mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 02:11:28 +00:00
feat(codex): add ccsxp runtime shortcut
This commit is contained in:
+2
-1
@@ -30,7 +30,8 @@
|
||||
"ccs-droid": "dist/bin/droid-runtime.js",
|
||||
"ccsd": "dist/bin/droid-runtime.js",
|
||||
"ccs-codex": "dist/bin/codex-runtime.js",
|
||||
"ccsx": "dist/bin/codex-runtime.js"
|
||||
"ccsx": "dist/bin/codex-runtime.js",
|
||||
"ccsxp": "dist/bin/ccsxp-runtime.js"
|
||||
},
|
||||
"files": [
|
||||
"dist/",
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
const { stripTargetFlag } = require('../targets/target-resolver');
|
||||
|
||||
process.env.CCS_INTERNAL_ENTRY_TARGET = 'codex';
|
||||
|
||||
// ccsxp is an opinionated shortcut for the built-in Codex-on-Codex route.
|
||||
// Strip user-supplied target overrides before forcing the shortcut target.
|
||||
const forwardedArgs = stripTargetFlag(process.argv.slice(2));
|
||||
process.argv.splice(2, process.argv.length - 2, 'codex', '--target', 'codex', ...forwardedArgs);
|
||||
require('../ccs');
|
||||
+22
-6
@@ -241,14 +241,26 @@ function resolveNativeClaudeLaunchArgs(
|
||||
}
|
||||
|
||||
function shouldPassthroughNativeCodexFlagCommand(args: string[]): boolean {
|
||||
return getNativeCodexPassthroughArgs(args) !== null;
|
||||
}
|
||||
|
||||
function getNativeCodexPassthroughArgs(args: string[]): string[] | null {
|
||||
const targetArgs = stripTargetFlag(args);
|
||||
if (targetArgs.length === 0) {
|
||||
return false;
|
||||
if (resolveTargetType(args) !== 'codex' || targetArgs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
resolveTargetType(args) === 'codex' && CODEX_NATIVE_PASSTHROUGH_FLAGS.has(targetArgs[0] || '')
|
||||
);
|
||||
const firstArg = targetArgs[0] || '';
|
||||
if (CODEX_NATIVE_PASSTHROUGH_FLAGS.has(firstArg)) {
|
||||
return targetArgs;
|
||||
}
|
||||
|
||||
const secondArg = targetArgs[1] || '';
|
||||
if (firstArg === 'codex' && CODEX_NATIVE_PASSTHROUGH_FLAGS.has(secondArg)) {
|
||||
return targetArgs.slice(1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function execNativeCodexFlagCommand(args: string[]): void {
|
||||
@@ -265,7 +277,11 @@ function execNativeCodexFlagCommand(args: string[]): void {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const targetArgs = stripTargetFlag(args);
|
||||
const targetArgs = getNativeCodexPassthroughArgs(args);
|
||||
if (!targetArgs) {
|
||||
console.error(fail('Native Codex passthrough args could not be resolved.'));
|
||||
process.exit(1);
|
||||
}
|
||||
const creds: TargetCredentials = {
|
||||
profile: 'default',
|
||||
baseUrl: '',
|
||||
|
||||
@@ -8,7 +8,7 @@ try {
|
||||
expandPath = require('../../dist/utils/helpers').expandPath;
|
||||
} catch (e) {
|
||||
// If helpers module doesn't exist or doesn't export expandPath, create a mock
|
||||
expandPath = function(p) {
|
||||
expandPath = function (p) {
|
||||
if (!p || typeof p !== 'string') return p;
|
||||
if (p.startsWith('~/')) {
|
||||
return path.join(os.homedir(), p.slice(2));
|
||||
@@ -76,7 +76,10 @@ describe('cross-platform', () => {
|
||||
describe('platform-specific behavior', () => {
|
||||
it('detects platform correctly', () => {
|
||||
const platform = os.platform();
|
||||
assert(['darwin', 'linux', 'win32'].includes(platform), 'Should be running on supported platform');
|
||||
assert(
|
||||
['darwin', 'linux', 'win32'].includes(platform),
|
||||
'Should be running on supported platform'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles path separators correctly', () => {
|
||||
@@ -133,6 +136,7 @@ describe('cross-platform', () => {
|
||||
assert(packageJson.bin.ccsd, 'bin field should specify ccsd command');
|
||||
assert(packageJson.bin['ccs-codex'], 'bin field should specify ccs-codex command');
|
||||
assert(packageJson.bin.ccsx, 'bin field should specify ccsx command');
|
||||
assert(packageJson.bin.ccsxp, 'bin field should specify ccsxp command');
|
||||
assert.notStrictEqual(
|
||||
packageJson.bin['ccs-droid'],
|
||||
packageJson.bin.ccs,
|
||||
@@ -153,6 +157,11 @@ describe('cross-platform', () => {
|
||||
packageJson.bin.ccsx,
|
||||
'ccsx should share the dedicated codex runtime entrypoint'
|
||||
);
|
||||
assert.notStrictEqual(
|
||||
packageJson.bin.ccsxp,
|
||||
packageJson.bin.ccsx,
|
||||
'ccsxp should use a dedicated shortcut entrypoint'
|
||||
);
|
||||
assert(
|
||||
fs.existsSync(path.join(__dirname, '..', '..', packageJson.bin['ccs-droid'])),
|
||||
'dedicated droid runtime entrypoint should exist'
|
||||
@@ -161,6 +170,10 @@ describe('cross-platform', () => {
|
||||
fs.existsSync(path.join(__dirname, '..', '..', packageJson.bin['ccs-codex'])),
|
||||
'dedicated codex runtime entrypoint should exist'
|
||||
);
|
||||
assert(
|
||||
fs.existsSync(path.join(__dirname, '..', '..', packageJson.bin.ccsxp)),
|
||||
'dedicated ccsxp shortcut entrypoint should exist'
|
||||
);
|
||||
assert(packageJson.scripts, 'package.json should have scripts field');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
|
||||
|
||||
const wrapperPath = require.resolve('../../../src/bin/ccsxp-runtime.ts');
|
||||
const ccsPath = require.resolve('../../../src/ccs.ts');
|
||||
|
||||
describe('ccsxp runtime wrapper', () => {
|
||||
const originalArgv = process.argv;
|
||||
const originalEntryTarget = process.env.CCS_INTERNAL_ENTRY_TARGET;
|
||||
|
||||
beforeEach(() => {
|
||||
delete require.cache[wrapperPath];
|
||||
delete require.cache[ccsPath];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.argv = originalArgv;
|
||||
|
||||
if (originalEntryTarget === undefined) {
|
||||
delete process.env.CCS_INTERNAL_ENTRY_TARGET;
|
||||
} else {
|
||||
process.env.CCS_INTERNAL_ENTRY_TARGET = originalEntryTarget;
|
||||
}
|
||||
|
||||
delete require.cache[wrapperPath];
|
||||
delete require.cache[ccsPath];
|
||||
});
|
||||
|
||||
it('prepends the built-in codex profile and target before loading CCS', () => {
|
||||
process.argv = ['node', wrapperPath, 'fix failing tests'];
|
||||
require.cache[ccsPath] = { exports: {} } as NodeJS.Module;
|
||||
|
||||
require(wrapperPath);
|
||||
|
||||
expect(process.env.CCS_INTERNAL_ENTRY_TARGET).toBe('codex');
|
||||
expect(process.argv.slice(2)).toEqual(['codex', '--target', 'codex', 'fix failing tests']);
|
||||
});
|
||||
|
||||
it('keeps flag-only invocations routed through the built-in codex profile shortcut', () => {
|
||||
process.argv = ['node', wrapperPath, '--version'];
|
||||
require.cache[ccsPath] = { exports: {} } as NodeJS.Module;
|
||||
|
||||
require(wrapperPath);
|
||||
|
||||
expect(process.argv.slice(2)).toEqual(['codex', '--target', 'codex', '--version']);
|
||||
});
|
||||
|
||||
it('strips user-supplied target overrides before forcing the codex shortcut target', () => {
|
||||
process.argv = ['node', wrapperPath, '--target', 'claude', '--version'];
|
||||
require.cache[ccsPath] = { exports: {} } as NodeJS.Module;
|
||||
|
||||
require(wrapperPath);
|
||||
|
||||
expect(process.argv.slice(2)).toEqual(['codex', '--target', 'codex', '--version']);
|
||||
});
|
||||
});
|
||||
@@ -40,6 +40,21 @@ function runCodexAlias(args: string[], env: NodeJS.ProcessEnv): RunResult {
|
||||
};
|
||||
}
|
||||
|
||||
function runCcsxpAlias(args: string[], env: NodeJS.ProcessEnv): RunResult {
|
||||
const ccsxpEntry = path.join(process.cwd(), 'src', 'bin', 'ccsxp-runtime.ts');
|
||||
const result = spawnSync(process.execPath, [ccsxpEntry, ...args], {
|
||||
encoding: 'utf8',
|
||||
env,
|
||||
timeout: 20000,
|
||||
});
|
||||
|
||||
return {
|
||||
status: result.status,
|
||||
stdout: result.stdout || '',
|
||||
stderr: result.stderr || '',
|
||||
};
|
||||
}
|
||||
|
||||
function readLoggedCodexCalls(logPath: string): string[][] {
|
||||
if (!fs.existsSync(logPath)) {
|
||||
return [];
|
||||
@@ -94,8 +109,9 @@ describe('codex runtime integration', () => {
|
||||
`#!/usr/bin/env node
|
||||
const fs = require('fs');
|
||||
const out = process.env.CCS_TEST_CODEX_ARGS_OUT;
|
||||
const cliArgs = process.argv.slice(2);
|
||||
if (out) {
|
||||
fs.appendFileSync(out, JSON.stringify(process.argv.slice(2)) + '\\n');
|
||||
fs.appendFileSync(out, JSON.stringify(cliArgs) + '\\n');
|
||||
}
|
||||
const envOut = process.env.CCS_TEST_CODEX_ENV_OUT;
|
||||
if (envOut) {
|
||||
@@ -110,11 +126,11 @@ if (envOut) {
|
||||
}) + '\\n'
|
||||
);
|
||||
}
|
||||
if (process.argv[2] === '--version' || process.argv[2] === '-v') {
|
||||
if (cliArgs.includes('--version') || cliArgs.includes('-v')) {
|
||||
process.stdout.write(process.env.CCS_TEST_CODEX_VERSION || 'codex-cli 0.118.0-alpha.3');
|
||||
process.exit(0);
|
||||
}
|
||||
if (process.argv[2] === '--help' || process.argv[2] === '-h') {
|
||||
if (cliArgs.includes('--help') || cliArgs.includes('-h')) {
|
||||
process.stdout.write(
|
||||
process.env.CCS_TEST_CODEX_HELP ||
|
||||
' -c, --config <key=value>\\n -p, --profile <CONFIG_PROFILE>\\n'
|
||||
@@ -244,10 +260,10 @@ process.exit(0);
|
||||
]);
|
||||
});
|
||||
|
||||
it('fails fast when native Codex reasoning overrides need unsupported --config support', () => {
|
||||
it('keeps ccsxp pinned to native Codex even when a user passes another --target override', () => {
|
||||
if (process.platform === 'win32') return;
|
||||
|
||||
const result = runCcs(['default', '--target', 'codex', '--effort', 'high', 'fix failing tests'], {
|
||||
const result = runCcsxpAlias(['--target', 'claude', '--version'], {
|
||||
...process.env,
|
||||
CI: '1',
|
||||
NO_COLOR: '1',
|
||||
@@ -255,9 +271,48 @@ process.exit(0);
|
||||
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(result.stdout).toContain('codex-cli 9.9.9-test');
|
||||
expect(readLoggedCodexCalls(codexArgsLogPath)).toEqual([['--version']]);
|
||||
});
|
||||
|
||||
it('passes ccs codex --target codex --version through to the native Codex binary', () => {
|
||||
if (process.platform === 'win32') return;
|
||||
|
||||
const result = runCcs(['codex', '--target', 'codex', '--version'], {
|
||||
...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',
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toContain('codex-cli 9.9.9-test');
|
||||
expect(readLoggedCodexCalls(codexArgsLogPath)).toEqual([['--version']]);
|
||||
});
|
||||
|
||||
it('fails fast when native Codex reasoning overrides need unsupported --config support', () => {
|
||||
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(1);
|
||||
expect(result.stderr).toContain('Codex CLI (codex-cli 9.9.9-test)');
|
||||
expect(result.stderr).toContain('does not advertise --config overrides');
|
||||
|
||||
Reference in New Issue
Block a user