diff --git a/docs/system-architecture/target-adapters.md b/docs/system-architecture/target-adapters.md index 6d785128..a2b11eb4 100644 --- a/docs/system-architecture/target-adapters.md +++ b/docs/system-architecture/target-adapters.md @@ -136,7 +136,7 @@ export function resolveTargetType( // 4. Check profile config if (profileConfig?.target) { - // Persisted targets intentionally exclude runtime-only codex. + // Persisted targets are validated before profile configuration is saved. return profileConfig.target; } diff --git a/src/commands/api-command/help.ts b/src/commands/api-command/help.ts index 2da106d2..1c13d2e5 100644 --- a/src/commands/api-command/help.ts +++ b/src/commands/api-command/help.ts @@ -62,7 +62,7 @@ export async function showApiCommandHelp(writeLine: HelpWriter = console.log): P ` ${color('--1m / --no-1m', 'command')} Write or clear [1m] on compatible Claude mappings` ); writeLine( - ` ${color('--target ', 'command')} Default target: claude or droid (create)` + ` ${color('--target ', 'command')} Default target: claude, droid, or codex (create)` ); writeLine(` ${color('--register', 'command')} Register discovered orphan settings`); writeLine(` ${color('--json', 'command')} JSON output for discover command`); diff --git a/src/targets/target-metadata.ts b/src/targets/target-metadata.ts index 035ba432..00b2f60c 100644 --- a/src/targets/target-metadata.ts +++ b/src/targets/target-metadata.ts @@ -23,7 +23,7 @@ export const TARGET_METADATA: Record = { displayName: 'Codex CLI', runtimeAliases: ['ccs-codex', 'ccsx', 'ccsxp'], legacyAliasEnvVar: 'CCS_CODEX_ALIASES', - persistedTarget: false, + persistedTarget: true, }, } satisfies Record; diff --git a/tests/unit/api/profile-reader.test.ts b/tests/unit/api/profile-reader.test.ts index f677b2a1..c7bcc24f 100644 --- a/tests/unit/api/profile-reader.test.ts +++ b/tests/unit/api/profile-reader.test.ts @@ -56,7 +56,7 @@ describe('profile reader target sanitization', () => { } }); - it('normalizes legacy stored codex targets back to claude for profiles and variants', async () => { + it('normalizes legacy invalid stored targets back to claude for profiles and variants', async () => { const ccsDir = getScopedCcsDir(); fs.mkdirSync(ccsDir, { recursive: true }); fs.writeFileSync( @@ -64,12 +64,12 @@ describe('profile reader target sanitization', () => { JSON.stringify( { profiles: { demo: '~/.ccs/demo.settings.json' }, - profile_targets: { demo: 'codex' }, + profile_targets: { demo: 'glm' }, cliproxy: { routed: { provider: 'codex', settings: '~/.ccs/routed.settings.json', - target: 'codex', + target: 'glm', }, }, }, @@ -94,7 +94,7 @@ describe('profile reader target sanitization', () => { expect(result.variants[0]?.target).toBe('claude'); }); - it('normalizes unified stored codex targets back to claude for profiles and variants', async () => { + it('normalizes unified invalid stored targets back to claude for profiles and variants', async () => { const ccsDir = getScopedCcsDir(); fs.mkdirSync(ccsDir, { recursive: true }); process.env.CCS_UNIFIED_CONFIG = '1'; @@ -106,7 +106,7 @@ describe('profile reader target sanitization', () => { ' demo:', ' type: api', ' settings: ~/.ccs/demo.settings.json', - ' target: codex', + ' target: glm', 'cliproxy:', ' oauth_accounts: {}', ' providers: []', @@ -114,7 +114,7 @@ describe('profile reader target sanitization', () => { ' routed:', ' provider: codex', ' settings: ~/.ccs/routed.settings.json', - ' target: codex', + ' target: glm', '', ].join('\n'), 'utf8' diff --git a/tests/unit/commands/api-command-args.test.ts b/tests/unit/commands/api-command-args.test.ts index 83387831..9eebee5d 100644 --- a/tests/unit/commands/api-command-args.test.ts +++ b/tests/unit/commands/api-command-args.test.ts @@ -71,15 +71,15 @@ describe('api-command arg parser', () => { expect(parsed.target).toBeUndefined(); expect(parsed.errors).toEqual([ - 'Invalid --target value "invalid-target". Use: claude or droid', + 'Invalid --target value "invalid-target". Use: claude, droid, or codex', ]); }); - test('rejects runtime-only codex as a persisted API target value', () => { + test('accepts codex as a persisted API target value', () => { const parsed = parseApiCommandArgs(['my-api', '--target', 'codex']); - expect(parsed.target).toBeUndefined(); - expect(parsed.errors).toEqual(['Invalid --target value "codex". Use: claude or droid']); + expect(parsed.target).toBe('codex'); + expect(parsed.errors).toEqual([]); }); test('collects missing-value error for --target with no value', () => { diff --git a/tests/unit/commands/cliproxy-variant-args.test.ts b/tests/unit/commands/cliproxy-variant-args.test.ts index a810bceb..5e3f514c 100644 --- a/tests/unit/commands/cliproxy-variant-args.test.ts +++ b/tests/unit/commands/cliproxy-variant-args.test.ts @@ -26,11 +26,11 @@ describe('cliproxy variant arg parser', () => { expect(parsed.errors).toEqual(['Missing value for --target']); }); - test('rejects runtime-only codex as a persisted variant target value', () => { + test('accepts codex as a persisted variant target value', () => { const parsed = parseProfileArgs(['variant-a', '--target', 'codex']); - expect(parsed.target).toBeUndefined(); - expect(parsed.errors).toEqual(['Invalid --target value "codex". Use: claude or droid']); + expect(parsed.target).toBe('codex'); + expect(parsed.errors).toEqual([]); }); test('uses last --target value when repeated', () => { diff --git a/tests/unit/targets/target-resolver.test.ts b/tests/unit/targets/target-resolver.test.ts index 72bfbdaa..172da6e0 100644 --- a/tests/unit/targets/target-resolver.test.ts +++ b/tests/unit/targets/target-resolver.test.ts @@ -68,9 +68,9 @@ describe('resolveTargetType', () => { expect(resolveTargetType([], { target: 'invalid-target' as never })).toBe('claude'); }); - it('should ignore runtime-only codex target when it appears in persisted profile config', () => { + it('should use codex from persisted profile config', () => { process.argv = ['node', 'ccs']; - expect(resolveTargetType([], { target: 'codex' })).toBe('claude'); + expect(resolveTargetType([], { target: 'codex' })).toBe('codex'); }); it('should prioritize --target flag over profile config', () => { diff --git a/tests/unit/web-server/target-parse-routes.test.ts b/tests/unit/web-server/target-parse-routes.test.ts index b0446dab..d1942308 100644 --- a/tests/unit/web-server/target-parse-routes.test.ts +++ b/tests/unit/web-server/target-parse-routes.test.ts @@ -7,16 +7,16 @@ describe('route target parsing', () => { it('accepts valid target values', () => { expect(parseProfileTarget('claude')).toBe('claude'); expect(parseProfileTarget('DROID')).toBe('droid'); + expect(parseProfileTarget('codex')).toBe('codex'); expect(parseVariantTarget(' claude ')).toBe('claude'); expect(parseVariantTarget('droid')).toBe('droid'); + expect(parseVariantTarget(' codex ')).toBe('codex'); }); it('returns null for invalid target values', () => { expect(parseProfileTarget('glm')).toBeNull(); - expect(parseProfileTarget('codex')).toBeNull(); expect(parseProfileTarget('')).toBeNull(); expect(parseVariantTarget('factory')).toBeNull(); - expect(parseVariantTarget('codex')).toBeNull(); expect(parseVariantTarget(' ')).toBeNull(); });