mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 02:11:28 +00:00
fix: probe Codex config override support directly
This commit is contained in:
@@ -5,6 +5,7 @@ import { escapeShellArg } from '../utils/shell-executor';
|
||||
import type { TargetBinaryInfo } from './target-adapter';
|
||||
|
||||
const CODEX_CONFIG_OVERRIDE_FEATURE = 'config-overrides';
|
||||
const CODEX_CONFIG_OVERRIDE_PROBE_ARGS = ['-c', 'model="gpt-5"', '--version'];
|
||||
|
||||
function runCodexProbe(codexPath: string, args: string[]): string | undefined {
|
||||
const isWindows = process.platform === 'win32';
|
||||
@@ -49,9 +50,25 @@ export function readCodexVersion(codexPath: string): string | undefined {
|
||||
return runCodexProbe(codexPath, ['--version'])?.trim();
|
||||
}
|
||||
|
||||
function codexHelpAdvertisesConfigOverrides(helpText: string | undefined): boolean {
|
||||
if (!helpText) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return /(^|\n)\s*-c,\s*--config\b/m.test(helpText) || helpText.includes('--config <key=value>');
|
||||
}
|
||||
|
||||
function codexSupportsConfigOverrideProbe(codexPath: string): boolean {
|
||||
return !!runCodexProbe(codexPath, CODEX_CONFIG_OVERRIDE_PROBE_ARGS)?.trim();
|
||||
}
|
||||
|
||||
function detectCodexFeatures(codexPath: string): readonly string[] {
|
||||
if (codexSupportsConfigOverrideProbe(codexPath)) {
|
||||
return [CODEX_CONFIG_OVERRIDE_FEATURE];
|
||||
}
|
||||
|
||||
const helpText = runCodexProbe(codexPath, ['--help']);
|
||||
return helpText?.includes('--config <key=value>') ? [CODEX_CONFIG_OVERRIDE_FEATURE] : [];
|
||||
return codexHelpAdvertisesConfigOverrides(helpText) ? [CODEX_CONFIG_OVERRIDE_FEATURE] : [];
|
||||
}
|
||||
|
||||
export function detectCodexCli(): string | null {
|
||||
|
||||
@@ -72,4 +72,56 @@ describe('codex-detector', () => {
|
||||
|
||||
execFileSyncSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('falls back to a direct -c probe when help text omits the config flag', () => {
|
||||
const fakeCodex = path.join(tmpDir, 'codex');
|
||||
fs.writeFileSync(fakeCodex, '');
|
||||
process.env.CCS_CODEX_PATH = fakeCodex;
|
||||
|
||||
const execFileSyncSpy = spyOn(childProcess, 'execFileSync').mockImplementation((command, args) => {
|
||||
const joinedArgs = Array.isArray(args) ? args.join(' ') : '';
|
||||
|
||||
if (joinedArgs.includes('--help')) {
|
||||
return 'Codex CLI\n';
|
||||
}
|
||||
|
||||
if (joinedArgs.includes('-c') && joinedArgs.includes('--version')) {
|
||||
return 'codex-cli 0.119.0-alpha.1';
|
||||
}
|
||||
|
||||
return 'codex-cli 0.119.0-alpha.1';
|
||||
});
|
||||
|
||||
const info = getCodexBinaryInfo();
|
||||
|
||||
expect(info?.features).toContain('config-overrides');
|
||||
|
||||
execFileSyncSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('still detects support from broader help text when the direct probe fails', () => {
|
||||
const fakeCodex = path.join(tmpDir, 'codex');
|
||||
fs.writeFileSync(fakeCodex, '');
|
||||
process.env.CCS_CODEX_PATH = fakeCodex;
|
||||
|
||||
const execFileSyncSpy = spyOn(childProcess, 'execFileSync').mockImplementation((command, args) => {
|
||||
const joinedArgs = Array.isArray(args) ? args.join(' ') : '';
|
||||
|
||||
if (joinedArgs.includes('--help')) {
|
||||
return 'Codex CLI\n -c, --config <CONFIG_OVERRIDE>\n';
|
||||
}
|
||||
|
||||
if (joinedArgs.includes('-c') && joinedArgs.includes('--version')) {
|
||||
throw new Error('unsupported');
|
||||
}
|
||||
|
||||
return 'codex-cli 0.119.0-alpha.1';
|
||||
});
|
||||
|
||||
const info = getCodexBinaryInfo();
|
||||
|
||||
expect(info?.features).toContain('config-overrides');
|
||||
|
||||
execFileSyncSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -126,7 +126,17 @@ if (envOut) {
|
||||
}) + '\\n'
|
||||
);
|
||||
}
|
||||
if (cliArgs.includes('--version') || cliArgs.includes('-v')) {
|
||||
const configFlagIndex = cliArgs.findIndex((arg) => arg === '-c' || arg === '--config');
|
||||
if (process.env.CCS_TEST_CODEX_CONFIG_OVERRIDE_STATUS === 'unsupported' && configFlagIndex !== -1) {
|
||||
process.stderr.write('codex: unknown option --config\\n');
|
||||
process.exit(1);
|
||||
}
|
||||
if (
|
||||
cliArgs.includes('--version') ||
|
||||
cliArgs.includes('-v') ||
|
||||
(configFlagIndex !== -1 &&
|
||||
(cliArgs[configFlagIndex + 2] === '--version' || cliArgs[configFlagIndex + 2] === '-v'))
|
||||
) {
|
||||
process.stdout.write(process.env.CCS_TEST_CODEX_VERSION || 'codex-cli 0.118.0-alpha.3');
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -324,6 +334,7 @@ process.exit(0);
|
||||
CCS_HOME: tmpHome,
|
||||
CCS_CODEX_PATH: fakeCodexPath,
|
||||
CCS_TEST_CODEX_ARGS_OUT: codexArgsLogPath,
|
||||
CCS_TEST_CODEX_CONFIG_OVERRIDE_STATUS: 'unsupported',
|
||||
CCS_TEST_CODEX_VERSION: 'codex-cli 9.9.9-test',
|
||||
CCS_TEST_CODEX_HELP: ' -p, --profile <CONFIG_PROFILE>\\n',
|
||||
}
|
||||
@@ -333,7 +344,31 @@ process.exit(0);
|
||||
expect(result.stderr).toContain('Codex CLI (codex-cli 9.9.9-test)');
|
||||
expect(result.stderr).toContain('does not advertise --config overrides');
|
||||
const calls = readLoggedCodexCalls(codexArgsLogPath);
|
||||
expect(calls).toEqual([['--help'], ['--version']]);
|
||||
expect(calls).toEqual([['-c', 'model="gpt-5"', '--version'], ['--help'], ['--version']]);
|
||||
});
|
||||
|
||||
it('accepts native Codex reasoning overrides when the direct -c probe succeeds', () => {
|
||||
if (process.platform === 'win32') return;
|
||||
|
||||
const result = runCcs(
|
||||
['default', '--target', 'codex', '--effort', 'high', 'fix failing tests'],
|
||||
{
|
||||
...process.env,
|
||||
CI: '1',
|
||||
NO_COLOR: '1',
|
||||
CCS_HOME: tmpHome,
|
||||
CCS_CODEX_PATH: fakeCodexPath,
|
||||
CCS_TEST_CODEX_ARGS_OUT: codexArgsLogPath,
|
||||
CCS_TEST_CODEX_VERSION: 'codex-cli 9.9.9-test',
|
||||
CCS_TEST_CODEX_HELP: ' -p, --profile <CONFIG_PROFILE>\\n',
|
||||
}
|
||||
);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(readLoggedCodexCalls(codexArgsLogPath)).toEqual([
|
||||
['-c', 'model="gpt-5"', '--version'],
|
||||
['-c', 'model_reasoning_effort="high"', 'fix failing tests'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('reports unsupported generic settings profiles before Codex install guidance', () => {
|
||||
|
||||
@@ -68,6 +68,13 @@ describe('Codex settings bridge launch', () => {
|
||||
fs.writeFileSync(
|
||||
fakeCodexPath,
|
||||
`#!/bin/sh
|
||||
if [ "$1" = "-c" ] || [ "$1" = "--config" ]; then
|
||||
if [ "$3" = "--version" ] || [ "$3" = "-v" ]; then
|
||||
echo "codex-cli 0.118.0-alpha.3"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "--version" ]; then
|
||||
echo "codex-cli 0.118.0-alpha.3"
|
||||
exit 0
|
||||
|
||||
@@ -20,14 +20,29 @@ const testRoot = path.join(os.tmpdir(), `ccs-codex-dashboard-test-${Date.now()}`
|
||||
const codexHome = path.join(testRoot, '.codex-home');
|
||||
const codexStubPath = path.join(testRoot, 'codex');
|
||||
|
||||
function writeCodexStub(options?: { helpText?: string; version?: string }) {
|
||||
function writeCodexStub(options?: {
|
||||
helpText?: string;
|
||||
version?: string;
|
||||
supportsConfigOverrides?: boolean;
|
||||
}) {
|
||||
const helpText =
|
||||
options?.helpText ?? ' -c, --config <key=value>\n -p, --profile <CONFIG_PROFILE>\n';
|
||||
const version = options?.version ?? 'codex-cli 0.118.0-alpha.3';
|
||||
const supportsConfigOverrides = options?.supportsConfigOverrides ?? true;
|
||||
|
||||
fs.writeFileSync(
|
||||
codexStubPath,
|
||||
`#!/bin/sh
|
||||
if [ "$1" = "-c" ] || [ "$1" = "--config" ]; then
|
||||
if [ "${supportsConfigOverrides ? '1' : '0'}" != "1" ]; then
|
||||
printf '%s\\n' 'codex: unknown option --config' >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$3" = "--version" ] || [ "$3" = "-v" ]; then
|
||||
printf '%s\\n' "${version}"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
if [ "$1" = "--version" ]; then
|
||||
printf '%s\\n' "${version}"
|
||||
exit 0
|
||||
@@ -272,7 +287,10 @@ requires_openai_auth = true
|
||||
});
|
||||
|
||||
it('warns when active profile is missing, config overrides are unavailable, or risky fields exist', async () => {
|
||||
writeCodexStub({ helpText: ' -p, --profile <CONFIG_PROFILE>\n' });
|
||||
writeCodexStub({
|
||||
helpText: ' -p, --profile <CONFIG_PROFILE>\n',
|
||||
supportsConfigOverrides: false,
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(codexHome, 'config.toml'),
|
||||
`profile = "missing-profile"
|
||||
|
||||
Reference in New Issue
Block a user