fix(settings-profile): preserve non-routing env for Claude launches

Keep settings-profile Claude launches aligned with profile-scoped
non-routing env while still stripping routing/auth values.

- build claudeRuntimeEnvVars from filtered global + settings env
- preserve CLAUDE_CODE_MAX_OUTPUT_TOKENS and model defaults for
  nested settings-profile launches
- extend browser launch regression coverage to prove global_env
  cannot reintroduce routing/auth values
This commit is contained in:
Wooseong Kim
2026-04-22 14:08:37 +09:00
parent 1fd2fb83fb
commit 2191f0a6c7
2 changed files with 47 additions and 20 deletions
+5 -4
View File
@@ -80,7 +80,7 @@ import { handleError, runCleanup } from './errors';
import { tryHandleRootCommand } from './commands/root-command-router'; import { tryHandleRootCommand } from './commands/root-command-router';
// Import extracted utility functions // Import extracted utility functions
import { execClaude } from './utils/shell-executor'; import { execClaude, stripAnthropicRoutingEnv } from './utils/shell-executor';
import { isDeprecatedGlmtProfileName, normalizeDeprecatedGlmtEnv } from './utils/glmt-deprecation'; import { isDeprecatedGlmtProfileName, normalizeDeprecatedGlmtEnv } from './utils/glmt-deprecation';
import { maybeWarnAboutResumeLaneMismatch } from './auth/resume-lane-warning'; import { maybeWarnAboutResumeLaneMismatch } from './auth/resume-lane-warning';
import { createLogger } from './services/logging'; import { createLogger } from './services/logging';
@@ -1360,10 +1360,11 @@ async function main(): Promise<void> {
} }
// For Claude target launches that already pass `--settings`, keep runtime // For Claude target launches that already pass `--settings`, keep runtime
// env limited to CCS-managed flags so nested Team/subagent sessions do not // env free of ANTHROPIC routing/auth while preserving non-routing profile
// inherit profile-specific ANTHROPIC_* routing from the parent process. // env so nested Team/subagent sessions can still inherit model intent and
// other profile-scoped runtime flags.
const claudeRuntimeEnvVars: NodeJS.ProcessEnv = { const claudeRuntimeEnvVars: NodeJS.ProcessEnv = {
...globalEnv, ...stripAnthropicRoutingEnv({ ...globalEnv, ...settingsEnv }),
...(inheritedClaudeConfigDir ? { CLAUDE_CONFIG_DIR: inheritedClaudeConfigDir } : {}), ...(inheritedClaudeConfigDir ? { CLAUDE_CONFIG_DIR: inheritedClaudeConfigDir } : {}),
...webSearchEnv, ...webSearchEnv,
...imageAnalysisEnv, ...imageAnalysisEnv,
@@ -141,6 +141,7 @@ printf "%s\n" "$@" > "${claudeArgsLogPath}"
printf "anthropicApiKey=%s\n" "$ANTHROPIC_API_KEY" printf "anthropicApiKey=%s\n" "$ANTHROPIC_API_KEY"
printf "anthropicModel=%s\n" "$ANTHROPIC_MODEL" printf "anthropicModel=%s\n" "$ANTHROPIC_MODEL"
printf "anthropicSonnet=%s\n" "$ANTHROPIC_DEFAULT_SONNET_MODEL" printf "anthropicSonnet=%s\n" "$ANTHROPIC_DEFAULT_SONNET_MODEL"
printf "maxOutputTokens=%s\n" "$CLAUDE_CODE_MAX_OUTPUT_TOKENS"
} > "${claudeEnvLogPath}" } > "${claudeEnvLogPath}"
exit 0 exit 0
`, `,
@@ -197,6 +198,7 @@ exit 0
ANTHROPIC_AUTH_TOKEN: 'profile-token', ANTHROPIC_AUTH_TOKEN: 'profile-token',
ANTHROPIC_MODEL: 'gpt-5.4', ANTHROPIC_MODEL: 'gpt-5.4',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gpt-5.4', ANTHROPIC_DEFAULT_SONNET_MODEL: 'gpt-5.4',
CLAUDE_CODE_MAX_OUTPUT_TOKENS: '12345',
}, },
}, },
null, null,
@@ -204,23 +206,47 @@ exit 0
) + '\n' ) + '\n'
); );
const result = runCcs(['glm', 'smoke'], { const originalCcsHome = process.env.CCS_HOME;
...baseEnv, process.env.CCS_HOME = tmpHome;
ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/codex',
ANTHROPIC_AUTH_TOKEN: 'parent-routing-token',
ANTHROPIC_API_KEY: 'parent-api-key',
ANTHROPIC_MODEL: 'gpt-5.4',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gpt-5.4',
});
expect(result.status).toBe(0); try {
const launchedEnv = fs.readFileSync(claudeEnvLogPath, 'utf8'); mutateUnifiedConfig((config) => {
expect(launchedEnv).toContain('stripAnthropic=1'); config.global_env = {
expect(launchedEnv).toContain('anthropicBaseUrl='); enabled: true,
expect(launchedEnv).toContain('anthropicAuthToken='); env: {
expect(launchedEnv).toContain('anthropicApiKey='); ANTHROPIC_BASE_URL: 'http://127.0.0.1:9999/api/provider/global',
expect(launchedEnv).toContain('anthropicModel=gpt-5.4'); ANTHROPIC_AUTH_TOKEN: 'global-routing-token',
expect(launchedEnv).toContain('anthropicSonnet=gpt-5.4'); ANTHROPIC_API_KEY: 'global-api-key',
CLAUDE_CODE_MAX_OUTPUT_TOKENS: '54321',
},
};
});
const result = runCcs(['glm', 'smoke'], {
...baseEnv,
ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/codex',
ANTHROPIC_AUTH_TOKEN: 'parent-routing-token',
ANTHROPIC_API_KEY: 'parent-api-key',
ANTHROPIC_MODEL: 'gpt-5.4',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gpt-5.4',
});
expect(result.status).toBe(0);
const launchedEnv = fs.readFileSync(claudeEnvLogPath, 'utf8');
expect(launchedEnv).toContain('stripAnthropic=1');
expect(launchedEnv).toContain('anthropicBaseUrl=');
expect(launchedEnv).toContain('anthropicAuthToken=');
expect(launchedEnv).toContain('anthropicApiKey=');
expect(launchedEnv).toContain('anthropicModel=gpt-5.4');
expect(launchedEnv).toContain('anthropicSonnet=gpt-5.4');
expect(launchedEnv).toContain('maxOutputTokens=12345');
} finally {
if (originalCcsHome !== undefined) {
process.env.CCS_HOME = originalCcsHome;
} else {
delete process.env.CCS_HOME;
}
}
}); });
it('does not auto-enable browser reuse for settings-profile launches from env overrides alone', async () => { it('does not auto-enable browser reuse for settings-profile launches from env overrides alone', async () => {