fix(proxy): preserve settings-profile launches for openai bridge

This commit is contained in:
Tam Nhu Tran committed 2026-04-28 11:42:56 -04:00
1 parent 1c91c46326
commit 3dcf150978
6 files changed
+216 -4

No files matched your search

@@ -299,7 +299,58 @@ describe('openai proxy request routing', () => {
expect(bodies[0]?.body).toMatchObject({
model: 'deepseek-reasoner',
reasoning_effort: 'high',
reasoning: { enabled: true, effort: 'high' },
});
expect((bodies[0]?.body as { reasoning?: unknown } | undefined)?.reasoning).toBeUndefined();
});
it('forwards adaptive thinking to openai-profile upstreams via reasoning_effort only', async () => {
const hits: string[] = [];
const bodies: Array<{ label: string; body: unknown }> = [];
const upstreamPort = await startMockUpstream('openai', hits, bodies);
const settingsPath = writeSettings('openai', {
ANTHROPIC_BASE_URL: 'https://api.openai.com/v1',
ANTHROPIC_AUTH_TOKEN: 'openai_token',
ANTHROPIC_MODEL: 'gpt-4.1',
});
fs.writeFileSync(
path.join(tempDir, '.ccs', 'config.json'),
JSON.stringify({ profiles: { openai: settingsPath } }, null, 2),
'utf8'
);
const profile: OpenAICompatProfileConfig = {
profileName: 'openai',
settingsPath,
baseUrl: `http://127.0.0.1:${upstreamPort}`,
apiKey: 'openai_token',
provider: 'openai',
model: 'gpt-4.1',
};
proxyServer = startOpenAICompatProxyServer({
profile,
port: 0,
authToken: 'test-proxy-token',
});
proxyPort = await waitForServerListening(proxyServer);
const response = await requestProxy({
model: 'gpt-4.1',
thinking: { type: 'adaptive' },
output_config: { effort: 'max' },
messages: [{ role: 'user', content: 'think adaptively' }],
});
expect(response.status).toBe(200);
expect(await response.json()).toMatchObject({
content: [{ type: 'text', text: 'Reply from openai' }],
});
expect(hits).toEqual(['openai']);
expect(bodies[0]?.body).toMatchObject({
model: 'gpt-4.1',
reasoning_effort: 'high',
});
expect((bodies[0]?.body as { reasoning?: unknown } | undefined)?.reasoning).toBeUndefined();
});
});
@@ -5,6 +5,7 @@ import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { mutateUnifiedConfig } from '../../../src/config/unified-config-loader';
import { stopOpenAICompatProxy } from '../../../src/proxy/proxy-daemon';
const BROWSER_PROMPT_SNIPPET = 'prefer the CCS MCP Browser tool';
setDefaultTimeout(30000);
@@ -30,6 +31,14 @@ function runCcs(args: string[], env: NodeJS.ProcessEnv): RunResult {
};
}
function readLaunchedArgs(argsLogPath: string): string[] {
return fs
.readFileSync(argsLogPath, 'utf8')
.split('\n')
.map((arg) => arg.trim())
.filter((arg) => arg.length > 0);
}
function reserveClosedPort(): number {
const server = Bun.serve({
port: 0,
@@ -167,7 +176,7 @@ exit 0
};
});
afterEach(() => {
afterEach(async () => {
if (devtoolsServer) {
devtoolsServer.kill();
devtoolsServer = undefined;
@@ -176,6 +185,7 @@ exit 0
return;
}
await stopOpenAICompatProxy();
fs.rmSync(tmpHome, { recursive: true, force: true });
});
@@ -257,6 +267,74 @@ exit 0
}
});
it('passes a sanitized settings copy for local OpenAI-compatible proxy launches', () => {
if (process.platform === 'win32') return;
fs.writeFileSync(
settingsPath,
JSON.stringify(
{
env: {
ANTHROPIC_BASE_URL: 'https://api.openai.com/v1',
ANTHROPIC_AUTH_TOKEN: 'profile-token',
ANTHROPIC_API_KEY: 'profile-api-key',
ANTHROPIC_MODEL: 'gpt-5.4',
CLAUDE_CODE_MAX_OUTPUT_TOKENS: '12345',
},
hooks: {
PreToolUse: [
{
matcher: 'Read',
hooks: [{ type: 'command', command: 'echo keep-profile-settings' }],
},
],
},
},
null,
2
) + '\n'
);
const result = runCcs(['glm', 'smoke'], baseEnv);
expect(result.status).toBe(0);
const launchedArgs = readLaunchedArgs(claudeArgsLogPath);
const settingsIndex = launchedArgs.indexOf('--settings');
expect(settingsIndex).toBeGreaterThanOrEqual(0);
const launchSettingsPath = launchedArgs[settingsIndex + 1];
expect(launchSettingsPath).toBeDefined();
expect(launchSettingsPath).not.toBe(settingsPath);
const persistedLaunchSettings = JSON.parse(
fs.readFileSync(launchSettingsPath as string, 'utf8')
) as {
env?: Record<string, string>;
hooks?: {
PreToolUse?: Array<{
matcher?: string;
hooks?: Array<{ command?: string }>;
}>;
};
};
expect(persistedLaunchSettings.env?.ANTHROPIC_BASE_URL).toBeUndefined();
expect(persistedLaunchSettings.env?.ANTHROPIC_AUTH_TOKEN).toBeUndefined();
expect(persistedLaunchSettings.env?.ANTHROPIC_API_KEY).toBeUndefined();
expect(persistedLaunchSettings.env?.ANTHROPIC_MODEL).toBe('gpt-5.4');
expect(
persistedLaunchSettings.hooks?.PreToolUse?.[0]?.hooks?.[0]?.command
).toBe('echo keep-profile-settings');
const launchedEnv = fs.readFileSync(claudeEnvLogPath, 'utf8');
expect(launchedEnv).toContain('stripAnthropic=');
expect(launchedEnv).toContain('anthropicBaseUrl=http://127.0.0.1:');
expect(launchedEnv).not.toContain('anthropicBaseUrl=https://api.openai.com/v1');
expect(launchedEnv).toContain('anthropicModel=gpt-5.4');
expect(launchedEnv).toContain('maxOutputTokens=12345');
});
it('does not auto-enable browser reuse for settings-profile launches from env overrides alone', async () => {
if (process.platform === 'win32') return;
@@ -651,6 +651,14 @@ describe('CLAUDECODE environment stripping', () => {
ANTHROPIC_MODEL: 'gpt-5.4',
CLAUDE_CODE_MAX_OUTPUT_TOKENS: '12345',
},
hooks: {
PreToolUse: [
{
matcher: 'Read',
hooks: [{ type: 'command', command: 'echo headless-bridge-hook' }],
},
],
},
},
null,
2
@@ -679,6 +687,32 @@ describe('CLAUDECODE environment stripping', () => {
expect(env.ANTHROPIC_AUTH_TOKEN).not.toBe('parent-routing-token');
expect(env.ANTHROPIC_MODEL).toBe('gpt-5.4');
expect(env.CLAUDE_CODE_MAX_OUTPUT_TOKENS).toBe('12345');
const args = spawnCalls[0].args;
const settingsIndex = args.indexOf('--settings');
expect(settingsIndex).toBeGreaterThanOrEqual(0);
const launchSettingsPath = args[settingsIndex + 1];
expect(launchSettingsPath).toBeDefined();
expect(launchSettingsPath).not.toBe(path.join(ccsDir, 'bridge.settings.json'));
const persistedLaunchSettings = JSON.parse(fs.readFileSync(launchSettingsPath, 'utf8')) as {
env?: Record<string, string>;
hooks?: {
PreToolUse?: Array<{
matcher?: string;
hooks?: Array<{ command?: string }>;
}>;
};
};
expect(persistedLaunchSettings.env?.ANTHROPIC_BASE_URL).toBeUndefined();
expect(persistedLaunchSettings.env?.ANTHROPIC_AUTH_TOKEN).toBeUndefined();
expect(persistedLaunchSettings.env?.ANTHROPIC_API_KEY).toBeUndefined();
expect(persistedLaunchSettings.env?.ANTHROPIC_MODEL).toBe('gpt-5.4');
expect(
persistedLaunchSettings.hooks?.PreToolUse?.[0]?.hooks?.[0]?.command
).toBe('echo headless-bridge-hook');
});
it('headless executor prepares image-analysis MCP and suppresses the legacy hook on healthy launches', async () => {