From 70dd17c784deca9b4169fa57b8a22724c45a5927 Mon Sep 17 00:00:00 2001 From: 777genius Date: Thu, 23 Apr 2026 20:08:17 +0300 Subject: [PATCH] fix(runtime): honor stored Anthropic API key state --- src/main/index.ts | 1 + .../runtime/ProviderConnectionService.ts | 11 +++- .../runtime/ProviderConnectionService.test.ts | 53 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/main/index.ts b/src/main/index.ts index 08f1ec86..6de4da2c 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1016,6 +1016,7 @@ async function initializeServices(): Promise { ); const mcpInstallService = new McpInstallService(mcpAggregator, extensionsRuntimeAdapter); const apiKeyService = new ApiKeyService(); + providerConnectionService.setApiKeyService(apiKeyService); await apiKeyService.syncProcessEnv(RUNTIME_MANAGED_API_KEY_ENV_VARS); // warmup() and ensureInstalled() are deferred to after window creation // (did-finish-load handler) to avoid thread pool contention at startup. diff --git a/src/main/services/runtime/ProviderConnectionService.ts b/src/main/services/runtime/ProviderConnectionService.ts index 13c98862..6db0be6f 100644 --- a/src/main/services/runtime/ProviderConnectionService.ts +++ b/src/main/services/runtime/ProviderConnectionService.ts @@ -88,7 +88,7 @@ export class ProviderConnectionService { null; constructor( - private readonly apiKeyService = new ApiKeyService(), + private apiKeyService = new ApiKeyService(), private readonly configManager = ConfigManager.getInstance() ) {} @@ -107,6 +107,10 @@ export class ProviderConnectionService { this.codexModelCatalogFeature = feature; } + setApiKeyService(apiKeyService: ApiKeyService): void { + this.apiKeyService = apiKeyService; + } + getConfiguredAuthMode(providerId: CliProviderId): CliProviderAuthMode | null { if (providerId === 'anthropic') { return this.configManager.getConfig().providerConnections.anthropic.authMode; @@ -263,6 +267,11 @@ export class ProviderConnectionService { return null; } + const storedKey = await this.apiKeyService.lookupPreferred('ANTHROPIC_API_KEY'); + if (storedKey?.value.trim()) { + return null; + } + return ( 'Anthropic API key mode is enabled, but no ANTHROPIC_API_KEY is configured. ' + 'Add a stored/environment API key or switch Anthropic auth mode back to Auto or OAuth.' diff --git a/test/main/services/runtime/ProviderConnectionService.test.ts b/test/main/services/runtime/ProviderConnectionService.test.ts index f663faee..308029f5 100644 --- a/test/main/services/runtime/ProviderConnectionService.test.ts +++ b/test/main/services/runtime/ProviderConnectionService.test.ts @@ -126,6 +126,59 @@ describe('ProviderConnectionService', () => { expect(issue).toContain('ANTHROPIC_API_KEY'); }); + it('treats a stored Anthropic API key as configured even when env is empty', async () => { + const lookupPreferred = vi.fn().mockResolvedValue({ + envVarName: 'ANTHROPIC_API_KEY', + value: 'stored-key', + }); + const { ProviderConnectionService } = + await import('@main/services/runtime/ProviderConnectionService'); + + const service = new ProviderConnectionService( + { + lookupPreferred, + } as never, + { + getConfig: () => createConfig('api_key'), + } as never + ); + + const issue = await service.getConfiguredConnectionIssue({}, 'anthropic'); + + expect(lookupPreferred).toHaveBeenCalledWith('ANTHROPIC_API_KEY'); + expect(issue).toBeNull(); + }); + + it('can swap to the shared API key service after construction', async () => { + const staleApiKeyService = { + lookupPreferred: vi.fn().mockResolvedValue(null), + }; + const sharedApiKeyService = { + lookupPreferred: vi.fn().mockResolvedValue({ + envVarName: 'ANTHROPIC_API_KEY', + value: 'shared-key', + }), + }; + const { ProviderConnectionService } = + await import('@main/services/runtime/ProviderConnectionService'); + + const service = new ProviderConnectionService( + staleApiKeyService as never, + { + getConfig: () => createConfig('api_key'), + } as never + ); + + expect(await service.getConfiguredConnectionIssue({}, 'anthropic')).toContain( + 'Anthropic API key mode is enabled' + ); + + service.setApiKeyService(sharedApiKeyService as never); + + expect(await service.getConfiguredConnectionIssue({}, 'anthropic')).toBeNull(); + expect(sharedApiKeyService.lookupPreferred).toHaveBeenCalledWith('ANTHROPIC_API_KEY'); + }); + it('prefers stored API key status over environment detection for Anthropic', async () => { getCachedShellEnvMock.mockReturnValue({ ANTHROPIC_API_KEY: 'shell-key',