From 7138887a3b846ad7f75c44d5c2721cc2d0510048 Mon Sep 17 00:00:00 2001 From: 777genius Date: Tue, 12 May 2026 21:26:38 +0300 Subject: [PATCH] feat(runtime): detect Claude Platform on AWS safely --- .../runtime/providerRuntimeEnv.test.ts | 49 +++++++++++++++++++ .../services/runtime/providerRuntimeEnv.ts | 10 +++- .../services/team/TeamProvisioningService.ts | 2 + .../runtime/providerRuntimeEnv.test.ts | 19 +++++++ .../TeamProvisioningServicePrepare.test.ts | 14 ++++++ 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/main/services/runtime/providerRuntimeEnv.test.ts b/src/main/services/runtime/providerRuntimeEnv.test.ts index f3cf4444..ba155d94 100644 --- a/src/main/services/runtime/providerRuntimeEnv.test.ts +++ b/src/main/services/runtime/providerRuntimeEnv.test.ts @@ -35,9 +35,57 @@ describe('applyProviderRuntimeEnv', () => { expect(env.GOOGLE_CLOUD_PROJECT).toBe('project-1'); }); + it('preserves Claude Platform on AWS as an Anthropic runtime backend', () => { + const env: NodeJS.ProcessEnv = { + ANTHROPIC_AWS_WORKSPACE_ID: 'wrkspc_123', + AWS_PROFILE: 'cc', + AWS_REGION: 'us-west-2', + }; + + applyProviderRuntimeEnv(env, 'anthropic'); + + expect(env.CLAUDE_CODE_ENTRY_PROVIDER).toBe('claude-platform-aws'); + expect(env.ANTHROPIC_AWS_WORKSPACE_ID).toBe('wrkspc_123'); + expect(env.CLAUDE_CODE_USE_BEDROCK).toBeUndefined(); + expect(env.CLAUDE_CODE_USE_VERTEX).toBeUndefined(); + expect(env.CLAUDE_CODE_USE_FOUNDRY).toBeUndefined(); + expect(env.AWS_PROFILE).toBe('cc'); + expect(env.AWS_REGION).toBe('us-west-2'); + }); + + it('does not infer Claude Platform on AWS from AWS profile and region alone', () => { + const env: NodeJS.ProcessEnv = { + AWS_PROFILE: 'cc', + AWS_REGION: 'us-west-2', + }; + + applyProviderRuntimeEnv(env, 'anthropic'); + + expect(env.CLAUDE_CODE_ENTRY_PROVIDER).toBe('anthropic'); + expect(env.CLAUDE_CODE_USE_BEDROCK).toBeUndefined(); + expect(env.AWS_PROFILE).toBe('cc'); + expect(env.AWS_REGION).toBe('us-west-2'); + }); + + it('keeps Bedrock ahead of Claude Platform on AWS when both are configured', () => { + const env: NodeJS.ProcessEnv = { + CLAUDE_CODE_USE_BEDROCK: '1', + ANTHROPIC_AWS_WORKSPACE_ID: 'wrkspc_123', + AWS_PROFILE: 'cc', + AWS_REGION: 'us-east-1', + }; + + applyProviderRuntimeEnv(env, 'anthropic'); + + expect(env.CLAUDE_CODE_ENTRY_PROVIDER).toBe('bedrock'); + expect(env.CLAUDE_CODE_USE_BEDROCK).toBe('1'); + expect(env.ANTHROPIC_AWS_WORKSPACE_ID).toBe('wrkspc_123'); + }); + it('still strips Anthropic backend routing when Codex is selected', () => { const env: NodeJS.ProcessEnv = { CLAUDE_CODE_USE_BEDROCK: '1', + ANTHROPIC_AWS_WORKSPACE_ID: 'wrkspc_123', AWS_PROFILE: 'cc', }; @@ -45,6 +93,7 @@ describe('applyProviderRuntimeEnv', () => { expect(env.CLAUDE_CODE_ENTRY_PROVIDER).toBe('codex'); expect(env.CLAUDE_CODE_USE_BEDROCK).toBeUndefined(); + expect(env.ANTHROPIC_AWS_WORKSPACE_ID).toBe('wrkspc_123'); expect(env.AWS_PROFILE).toBe('cc'); }); }); diff --git a/src/main/services/runtime/providerRuntimeEnv.ts b/src/main/services/runtime/providerRuntimeEnv.ts index d21078b3..d2db5be6 100644 --- a/src/main/services/runtime/providerRuntimeEnv.ts +++ b/src/main/services/runtime/providerRuntimeEnv.ts @@ -4,7 +4,12 @@ import type { CliProviderId, TeamProviderId } from '@shared/types'; type RuntimeEnvProviderId = CliProviderId | TeamProviderId; -type AnthropicRuntimeBackendProviderId = 'anthropic' | 'bedrock' | 'vertex' | 'foundry'; +type AnthropicRuntimeBackendProviderId = + | 'anthropic' + | 'bedrock' + | 'vertex' + | 'foundry' + | 'claude-platform-aws'; const PROVIDER_ROUTING_ENV_KEYS = [ 'CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST', @@ -51,6 +56,9 @@ function resolveAnthropicRuntimeBackendFromEnv( if (isTruthyEnvValue(env.CLAUDE_CODE_USE_FOUNDRY)) { return 'foundry'; } + if (env.ANTHROPIC_AWS_WORKSPACE_ID?.trim()) { + return 'claude-platform-aws'; + } return 'anthropic'; } diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index acee6c48..b2f939ef 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -841,6 +841,8 @@ const DIRECT_TMUX_RESTART_ENV_KEYS = [ CLAUDE_TEAM_ANTHROPIC_AUTH_MODE_ENV, CLAUDE_TEAM_ANTHROPIC_API_KEY_HELPER_SETTINGS_PATH_ENV, 'ANTHROPIC_BASE_URL', + 'ANTHROPIC_AWS_WORKSPACE_ID', + 'ANTHROPIC_AWS_API_KEY', 'ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'GEMINI_BASE_URL', diff --git a/test/main/services/runtime/providerRuntimeEnv.test.ts b/test/main/services/runtime/providerRuntimeEnv.test.ts index 36060b46..4a27b8f1 100644 --- a/test/main/services/runtime/providerRuntimeEnv.test.ts +++ b/test/main/services/runtime/providerRuntimeEnv.test.ts @@ -35,6 +35,25 @@ describe('providerRuntimeEnv', () => { expect(result.CLAUDE_CODE_USE_OPENAI).toBeUndefined(); }); + it('pins Claude Platform on AWS only when the workspace id is explicit', () => { + const awsOnlyEnv: NodeJS.ProcessEnv = { + AWS_PROFILE: 'cc', + AWS_REGION: 'us-west-2', + }; + expect(applyProviderRuntimeEnv(awsOnlyEnv, 'anthropic').CLAUDE_CODE_ENTRY_PROVIDER).toBe( + 'anthropic' + ); + + const platformEnv: NodeJS.ProcessEnv = { + ANTHROPIC_AWS_WORKSPACE_ID: 'wrkspc_123', + AWS_PROFILE: 'cc', + AWS_REGION: 'us-west-2', + }; + expect(applyProviderRuntimeEnv(platformEnv, 'anthropic').CLAUDE_CODE_ENTRY_PROVIDER).toBe( + 'claude-platform-aws' + ); + }); + it('preserves gemini as a valid team provider id', () => { expect(resolveTeamProviderId('gemini')).toBe('gemini'); expect(resolveTeamProviderId('codex')).toBe('codex'); diff --git a/test/main/services/team/TeamProvisioningServicePrepare.test.ts b/test/main/services/team/TeamProvisioningServicePrepare.test.ts index 758e0c40..43f38d76 100644 --- a/test/main/services/team/TeamProvisioningServicePrepare.test.ts +++ b/test/main/services/team/TeamProvisioningServicePrepare.test.ts @@ -392,6 +392,20 @@ describe('TeamProvisioningService prepare/auth behavior', () => { expect(assignments).toContain("CODEX_HOME='/tmp/codex-connected-home'"); }); + it('preserves Claude Platform on AWS settings for direct tmux restart', () => { + const assignments = buildDirectTmuxRestartEnvAssignments( + { + ANTHROPIC_AWS_WORKSPACE_ID: 'wrkspc_123', + ANTHROPIC_AWS_API_KEY: 'aws-platform-key', + }, + 'anthropic' + ); + + expect(assignments).toContain("ANTHROPIC_AWS_WORKSPACE_ID='wrkspc_123'"); + expect(assignments).toContain("ANTHROPIC_AWS_API_KEY='aws-platform-key'"); + expect(assignments).toContain("CLAUDE_CODE_ENTRY_PROVIDER='anthropic'"); + }); + it('does not flatten Anthropic helper settings into non-Anthropic lead cross-provider args', async () => { const svc = new TeamProvisioningService(); const helperSettingsPath = path.join(tempRoot, 'team-runtime-auth', 'helper-settings.json');