agent-ecosystem/src/main/services/runtime/providerRuntimeEnv.ts

117 lines
3.5 KiB
TypeScript

import { ConfigManager } from '../infrastructure/ConfigManager';
import type { CliProviderId, TeamProviderId } from '@shared/types';
type RuntimeEnvProviderId = CliProviderId | TeamProviderId;
type AnthropicRuntimeBackendProviderId =
| 'anthropic'
| 'bedrock'
| 'vertex'
| 'foundry'
| 'claude-platform-aws';
const PROVIDER_ROUTING_ENV_KEYS = [
'CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST',
'CLAUDE_CODE_ENTRY_PROVIDER',
'CLAUDE_CODE_USE_OPENAI',
'CLAUDE_CODE_USE_BEDROCK',
'CLAUDE_CODE_USE_VERTEX',
'CLAUDE_CODE_USE_FOUNDRY',
'CLAUDE_CODE_USE_GEMINI',
] as const;
const BACKEND_SELECTION_ENV_KEYS = [
'CLAUDE_CODE_GEMINI_BACKEND',
'CLAUDE_CODE_CODEX_BACKEND',
] as const;
export function applyConfiguredRuntimeBackendsEnv(
env: NodeJS.ProcessEnv,
runtimeConfig = ConfigManager.getInstance().getConfig().runtime
): NodeJS.ProcessEnv {
for (const key of BACKEND_SELECTION_ENV_KEYS) {
env[key] = undefined;
}
env.CLAUDE_CODE_GEMINI_BACKEND = runtimeConfig.providerBackends.gemini;
env.CLAUDE_CODE_CODEX_BACKEND = runtimeConfig.providerBackends.codex;
return env;
}
function isTruthyEnvValue(value: string | undefined): boolean {
const normalized = value?.trim().toLowerCase();
return Boolean(normalized && normalized !== '0' && normalized !== 'false' && normalized !== 'no');
}
function resolveAnthropicRuntimeBackendFromEnv(
env: NodeJS.ProcessEnv
): AnthropicRuntimeBackendProviderId {
if (isTruthyEnvValue(env.CLAUDE_CODE_USE_BEDROCK)) {
return 'bedrock';
}
if (isTruthyEnvValue(env.CLAUDE_CODE_USE_VERTEX)) {
return 'vertex';
}
if (isTruthyEnvValue(env.CLAUDE_CODE_USE_FOUNDRY)) {
return 'foundry';
}
if (env.ANTHROPIC_AWS_WORKSPACE_ID?.trim()) {
return 'claude-platform-aws';
}
return 'anthropic';
}
function applyAnthropicRuntimeBackendEnv(
env: NodeJS.ProcessEnv,
backend: AnthropicRuntimeBackendProviderId
): void {
if (backend === 'bedrock') {
env.CLAUDE_CODE_USE_BEDROCK = '1';
} else if (backend === 'vertex') {
env.CLAUDE_CODE_USE_VERTEX = '1';
} else if (backend === 'foundry') {
env.CLAUDE_CODE_USE_FOUNDRY = '1';
}
}
export function applyProviderRuntimeEnv(
env: NodeJS.ProcessEnv,
providerId: RuntimeEnvProviderId | undefined
): NodeJS.ProcessEnv {
const resolvedProvider = resolveRuntimeProviderId(providerId);
const anthropicBackend =
resolvedProvider === 'anthropic' ? resolveAnthropicRuntimeBackendFromEnv(env) : 'anthropic';
for (const key of PROVIDER_ROUTING_ENV_KEYS) {
env[key] = undefined;
}
// Provider overrides must be positive pins. In dev and multimodel desktop
// flows the host process can already be routed to codex or gemini, and the
// child runtime reapplies settings.env after trust. Mark the env as
// host-managed and set the exact entry provider so anthropic teammates do not
// silently fall back into the host's current routing world.
env.CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST = '1';
env.CLAUDE_CODE_ENTRY_PROVIDER =
resolvedProvider === 'anthropic' ? anthropicBackend : resolvedProvider;
applyAnthropicRuntimeBackendEnv(env, anthropicBackend);
return env;
}
export function resolveRuntimeProviderId(
providerId: RuntimeEnvProviderId | undefined
): CliProviderId {
if (providerId === 'codex' || providerId === 'gemini' || providerId === 'opencode') {
return providerId;
}
return 'anthropic';
}
export function resolveTeamProviderId(providerId: TeamProviderId | undefined): TeamProviderId {
return providerId === 'codex' || providerId === 'gemini' || providerId === 'opencode'
? providerId
: 'anthropic';
}