mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-05 18:16:28 +00:00
fix(runtime): strip inherited Anthropic routing env selectively
This commit is contained in:
@@ -26,6 +26,28 @@ export function stripAnthropicEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ANTHROPIC_ROUTING_ENV_KEYS = new Set([
|
||||||
|
'ANTHROPIC_BASE_URL',
|
||||||
|
'ANTHROPIC_AUTH_TOKEN',
|
||||||
|
'ANTHROPIC_API_KEY',
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip inherited Anthropic routing/auth env while preserving model intent.
|
||||||
|
* Used for nested settings-profile Claude launches where `--settings` already
|
||||||
|
* defines the provider transport and the parent process should only lend model
|
||||||
|
* defaults or effort hints.
|
||||||
|
*/
|
||||||
|
export function stripAnthropicRoutingEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
||||||
|
const result: NodeJS.ProcessEnv = {};
|
||||||
|
for (const key of Object.keys(env)) {
|
||||||
|
if (!ANTHROPIC_ROUTING_ENV_KEYS.has(key)) {
|
||||||
|
result[key] = env[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strip Claude Code nested-session guard env var from a process environment.
|
* Strip Claude Code nested-session guard env var from a process environment.
|
||||||
*
|
*
|
||||||
@@ -137,14 +159,18 @@ export function execClaude(
|
|||||||
const webSearchEnv = getWebSearchHookEnv();
|
const webSearchEnv = getWebSearchHookEnv();
|
||||||
const claudeLaunchEnv = getClaudeLaunchEnvOverrides();
|
const claudeLaunchEnv = getClaudeLaunchEnvOverrides();
|
||||||
|
|
||||||
// For account/default profiles, strip ANTHROPIC_* from parent env to prevent
|
// Strip inherited ANTHROPIC_* when the launch should not reuse parent routing.
|
||||||
// stale proxy config (e.g., from prior CLIProxy sessions) from interfering
|
// Account/default profiles need full isolation from prior proxy sessions.
|
||||||
// with native Claude API routing. Settings-based profiles explicitly inject
|
// Settings profiles can selectively strip only routing/auth when `--settings`
|
||||||
// their own ANTHROPIC_* values, so they don't need this protection.
|
// already carries the provider source of truth but the parent model intent
|
||||||
|
// should still flow into nested Team/subagent launches.
|
||||||
const profileType = envVars?.CCS_PROFILE_TYPE;
|
const profileType = envVars?.CCS_PROFILE_TYPE;
|
||||||
const baseEnv =
|
const stripInheritedAnthropicEnv = profileType === 'account' || profileType === 'default';
|
||||||
profileType === 'account' || profileType === 'default'
|
const stripInheritedAnthropicRoutingEnv = envVars?.CCS_STRIP_INHERITED_ANTHROPIC_ENV === '1';
|
||||||
? stripAnthropicEnv(process.env)
|
const baseEnv = stripInheritedAnthropicEnv
|
||||||
|
? stripAnthropicEnv(process.env)
|
||||||
|
: stripInheritedAnthropicRoutingEnv
|
||||||
|
? stripAnthropicRoutingEnv(process.env)
|
||||||
: process.env;
|
: process.env;
|
||||||
|
|
||||||
// Prepare environment (merge with base env if envVars provided)
|
// Prepare environment (merge with base env if envVars provided)
|
||||||
|
|||||||
@@ -336,6 +336,37 @@ describe('CLAUDECODE environment stripping', () => {
|
|||||||
expect(normalizeSpy).toHaveBeenCalledWith(instancePath);
|
expect(normalizeSpy).toHaveBeenCalledWith(instancePath);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('execClaude strips inherited ANTHROPIC routing env but keeps model intent for settings-profile Claude launches', () => {
|
||||||
|
process.env.ANTHROPIC_BASE_URL = 'http://127.0.0.1:8317/api/provider/codex';
|
||||||
|
process.env.ANTHROPIC_AUTH_TOKEN = 'ccs-internal-managed';
|
||||||
|
process.env.ANTHROPIC_API_KEY = 'stale-api-key';
|
||||||
|
process.env.ANTHROPIC_MODEL = 'gpt-5.4';
|
||||||
|
process.env.ANTHROPIC_DEFAULT_OPUS_MODEL = 'gpt-5.4';
|
||||||
|
process.env.ANTHROPIC_DEFAULT_SONNET_MODEL = 'gpt-5.4';
|
||||||
|
process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = 'gpt-5.4-mini';
|
||||||
|
process.env.ANTHROPIC_SMALL_FAST_MODEL = 'gpt-5-codex-mini';
|
||||||
|
|
||||||
|
execClaude('claude', ['--help'], {
|
||||||
|
CCS_PROFILE_TYPE: 'settings',
|
||||||
|
CCS_STRIP_INHERITED_ANTHROPIC_ENV: '1',
|
||||||
|
CLAUDE_CONFIG_DIR: path.join(os.tmpdir(), 'ccs-settings-profile-instance'),
|
||||||
|
CCS_WEBSEARCH_SKIP: '1',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(spawnCalls.length).toBeGreaterThan(0);
|
||||||
|
const env = spawnCalls[0].options?.env as NodeJS.ProcessEnv;
|
||||||
|
expect(env.CCS_PROFILE_TYPE).toBe('settings');
|
||||||
|
expect(env.CLAUDE_CONFIG_DIR).toContain('ccs-settings-profile-instance');
|
||||||
|
expect(env.ANTHROPIC_BASE_URL).toBeUndefined();
|
||||||
|
expect(env.ANTHROPIC_AUTH_TOKEN).toBeUndefined();
|
||||||
|
expect(env.ANTHROPIC_API_KEY).toBeUndefined();
|
||||||
|
expect(env.ANTHROPIC_MODEL).toBe('gpt-5.4');
|
||||||
|
expect(env.ANTHROPIC_DEFAULT_OPUS_MODEL).toBe('gpt-5.4');
|
||||||
|
expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe('gpt-5.4');
|
||||||
|
expect(env.ANTHROPIC_DEFAULT_HAIKU_MODEL).toBe('gpt-5.4-mini');
|
||||||
|
expect(env.ANTHROPIC_SMALL_FAST_MODEL).toBe('gpt-5-codex-mini');
|
||||||
|
});
|
||||||
|
|
||||||
it('headless executor spawn path strips CLAUDECODE before spawn', async () => {
|
it('headless executor spawn path strips CLAUDECODE before spawn', async () => {
|
||||||
writeConfigWithAutoUpdatePreference(false);
|
writeConfigWithAutoUpdatePreference(false);
|
||||||
process.env.CLAUDECODE = 'nested';
|
process.env.CLAUDECODE = 'nested';
|
||||||
|
|||||||
Reference in New Issue
Block a user