diff --git a/src/auth/profile-detector.ts b/src/auth/profile-detector.ts index d2c63087..2c402d18 100644 --- a/src/auth/profile-detector.ts +++ b/src/auth/profile-detector.ts @@ -130,10 +130,27 @@ class ProfileDetector { // Handle composite variants if ('type' in variant && variant.type === 'composite') { const composite = variant as CompositeVariantConfig; + + // Defensive: check for missing tiers or default_tier + if (!composite.tiers || !composite.default_tier) { + console.warn( + `[!] Warning: Composite variant '${profileName}' has missing tiers or default_tier` + ); + return null; + } + + const defaultTierConfig = composite.tiers[composite.default_tier]; + if (!defaultTierConfig) { + console.warn( + `[!] Warning: Composite variant '${profileName}' missing config for default tier '${composite.default_tier}'` + ); + return null; + } + return { type: 'cliproxy', name: profileName, - provider: composite.tiers[composite.default_tier].provider as CLIProxyProfileName, + provider: defaultTierConfig.provider as CLIProxyProfileName, settingsPath: composite.settings, port: composite.port, isComposite: true, diff --git a/src/cliproxy/config/env-builder.ts b/src/cliproxy/config/env-builder.ts index 38b7cc28..f3d5dfae 100644 --- a/src/cliproxy/config/env-builder.ts +++ b/src/cliproxy/config/env-builder.ts @@ -461,17 +461,31 @@ export function getCompositeEnvVars( } const validPort = validatePort(port); - const defaultModel = tiers[defaultTier].model; - return { + // Defensive: handle missing tiers gracefully + const opusModel = tiers.opus?.model; + const sonnetModel = tiers.sonnet?.model; + const haikuModel = tiers.haiku?.model; + const defaultModel = tiers[defaultTier]?.model; + + // If default tier is missing, we cannot proceed meaningfully + if (!defaultModel) { + throw new Error(`Missing model for default tier '${defaultTier}'`); + } + + const env: Record = { ...globalEnv, ...additionalEnvVars, // Root URL — CLIProxyAPI routes based on model name in request body ANTHROPIC_BASE_URL: `http://127.0.0.1:${validPort}`, ANTHROPIC_AUTH_TOKEN: getEffectiveApiKey(), ANTHROPIC_MODEL: defaultModel, - ANTHROPIC_DEFAULT_OPUS_MODEL: tiers.opus.model, - ANTHROPIC_DEFAULT_SONNET_MODEL: tiers.sonnet.model, - ANTHROPIC_DEFAULT_HAIKU_MODEL: tiers.haiku.model, }; + + // Only set tier env vars if the tier exists + if (opusModel) env.ANTHROPIC_DEFAULT_OPUS_MODEL = opusModel; + if (sonnetModel) env.ANTHROPIC_DEFAULT_SONNET_MODEL = sonnetModel; + if (haikuModel) env.ANTHROPIC_DEFAULT_HAIKU_MODEL = haikuModel; + + return env; } diff --git a/src/cliproxy/executor/env-resolver.ts b/src/cliproxy/executor/env-resolver.ts index 1af44fa7..3d0a449f 100644 --- a/src/cliproxy/executor/env-resolver.ts +++ b/src/cliproxy/executor/env-resolver.ts @@ -136,9 +136,9 @@ export function buildClaudeEnvironment(config: ProxyChainConfig): Record 0) { compositeTierThinking = tierThinking; }