fix(codex): pass ccsx resume through to native codex

Closes #1287
This commit is contained in:
Kai (Tam Nhu) Tran
2026-05-18 09:45:20 -04:00
committed by GitHub
parent 5a7c2202f4
commit 5a54c1b536
7 changed files with 43 additions and 9 deletions
+4
View File
@@ -14,6 +14,10 @@
* **docker:** `ghcr.io/kaitranntt/ccs-dashboard:latest` Docker image is deprecated — migrate to `ghcr.io/kaitranntt/ccs:latest` (CCS + CLIProxy). The legacy image continues publishing for 2 more releases and emits a startup warning. See [#1251](https://github.com/kaitranntt/ccs/issues/1251).
### Bug Fixes
* **codex:** Pass `ccsx resume` through to native Codex instead of treating `resume` as a CCS profile.
## [7.79.1](https://github.com/kaitranntt/ccs/compare/v7.79.0...v7.79.1) (2026-05-14)
### Hotfixes
+1
View File
@@ -273,6 +273,7 @@ src/
### Native Codex Runtime Target
- Dedicated runtime entrypoints: `ccs-codex` and `ccsx` resolve through `src/bin/codex-runtime.ts`, while `ccsxp` resolves through `src/bin/ccsxp-runtime.ts`; all three set `CCS_INTERNAL_ENTRY_TARGET=codex` before delegating to `src/targets/target-resolver.ts`.
- Native Codex passthrough: `ccsx --help`, `ccsx --version`, and `ccsx resume ...` short-circuit before CCS profile detection, so Codex diagnostics and session resume behavior stay aligned with the upstream Codex CLI. CCS-owned `ccsx auth` remains the managed Codex profile namespace.
- Provider shortcut behavior: `ccsxp` strips user-supplied `--target` overrides and prepends `--config model_provider="cliproxy"` so it behaves like native Codex plus the CLIProxy provider recipe. The stricter CCS-managed bridge remains available explicitly through `ccs codex --target codex`. It pins `CODEX_HOME` to native `~/.codex` by default so inherited launcher state does not send history/config writes to a nonstandard Codex root; `CCSXP_CODEX_HOME` is the explicit override. On launch, CCS repairs the native `[model_providers.cliproxy]` stanza in `config.toml`, preserves a valid custom `base_url`, reads that provider's configured `env_key` (default `CLIPROXY_API_KEY`), and injects the effective CLIProxy auth token into that key for the child Codex process.
- Implicit Codex launches such as `ccs --target codex` and `ccsxp` use native Codex default mode even when the CCS default profile is a Claude account. Explicit unsupported profiles such as `ccs work --target codex` still fail fast with native-vs-pool guidance.
- `argv[0]` alias mapping still exists in `src/targets/target-resolver.ts` for same-binary/custom alias scenarios, but the built-in npm bins above do not depend on that map at runtime.
+2 -1
View File
@@ -1,6 +1,6 @@
# CCS Project Roadmap
Last Updated: 2026-05-09
Last Updated: 2026-05-18
Forward-looking roadmap documenting current priorities, GitHub issues, and future feature plans.
@@ -41,6 +41,7 @@ All major modularization work is complete. The codebase evolved from monolithic
### Recent Fixes
- **2026-05-18**: **#1287** `ccsx resume` now passes through to the native Codex `resume` subcommand before CCS profile detection, preserving Codex session continuation while keeping `ccsx auth` on the managed Codex profile namespace.
- **2026-05-09**: **#1199** Existing Claude auth accounts now have dashboard-visible Shared Resources controls separate from History Sync. Accounts exposes a dedicated Resources action for `shared` vs `profile-local`, `/shared` now inventories commands, skills, agents, plugins, and `settings.json`, plugin directories without docs show factual directory contents, and shared settings content is inspectable read-only through the localhost-gated shared-content API.
- **2026-05-07**: **#760** Codex GPT fast mode is now a first-class CLIProxy model tuning suffix. CCS accepts `gpt-5.4-fast`, `gpt-5.4-high-fast`, and equivalent canonicalized forms in raw env configs, CLI variant creation, and the dashboard model picker; runtime requests now send the base upstream model with `reasoning.effort` plus `service_tier: "priority"` instead of leaking the suffixed alias to CLIProxy upstream routing.
- **2026-05-07**: **#1103** GitHub Copilot is now treated as a deprecated compatibility bridge. The dashboard moves Copilot out of the active Identity & Access section and into Deprecated, quick setup no longer offers `ghcp` for new onboarding, CLI/help/config copy marks Copilot as deprecated, and existing `ccs copilot` / `ghcp` compatibility paths remain available for current setups.
@@ -515,6 +515,7 @@ ccs-codex
ccsx
→ dist/bin/codex-runtime.js
CCS_INTERNAL_ENTRY_TARGET=codex
→ passes native Codex diagnostics and `resume` through before CCS profile detection
ccsxp
→ dist/bin/ccsxp-runtime.js
+12 -5
View File
@@ -43,7 +43,7 @@ export interface DispatcherBootstrap {
* - setGlobalConfigDir (--config-dir flag)
* - Cloud-sync warnings
* - Completion short-circuit via tryHandleRootCommand
* - Codex native passthrough via execNativeCodexFlagCommand
* - Codex native passthrough via execNativeCodexCommand
*
* Adapter registration (registerTarget calls) stays in main() because it is
* singleton wiring with no dependency on the parsed args.
@@ -141,9 +141,9 @@ export async function bootstrapAndParseEarlyCli(rawArgs: string[]): Promise<Disp
return { args, isCompletionCommand, browserLaunchOverride: undefined, exitNow: true };
}
if (shouldPassthroughNativeCodexFlagCommand(args)) {
const { execNativeCodexFlagCommand } = await import('./target-executor');
execNativeCodexFlagCommand(args);
if (shouldPassthroughNativeCodexCommand(args)) {
const { execNativeCodexCommand } = await import('./target-executor');
execNativeCodexCommand(args);
return { args, isCompletionCommand, browserLaunchOverride: undefined, exitNow: true };
}
@@ -175,6 +175,7 @@ export const CODEX_RUNTIME_REASONING_LEVELS = new Set([
]);
export const CODEX_NATIVE_PASSTHROUGH_FLAGS = new Set(['--help', '-h', '--version', '-v']);
export const CODEX_NATIVE_PASSTHROUGH_SUBCOMMANDS = new Set(['resume']);
export const NATIVE_CLAUDE_EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'] as const;
@@ -314,7 +315,7 @@ export function shouldNormalizeNativeClaudeEffort(
// ========== Native Codex Passthrough ==========
export function shouldPassthroughNativeCodexFlagCommand(args: string[]): boolean {
export function shouldPassthroughNativeCodexCommand(args: string[]): boolean {
return getNativeCodexPassthroughArgs(args) !== null;
}
@@ -328,11 +329,17 @@ export function getNativeCodexPassthroughArgs(args: string[]): string[] | null {
if (CODEX_NATIVE_PASSTHROUGH_FLAGS.has(firstArg)) {
return targetArgs;
}
if (CODEX_NATIVE_PASSTHROUGH_SUBCOMMANDS.has(firstArg)) {
return targetArgs;
}
const secondArg = targetArgs[1] || '';
if (firstArg === 'codex' && CODEX_NATIVE_PASSTHROUGH_FLAGS.has(secondArg)) {
return targetArgs.slice(1);
}
if (firstArg === 'codex' && CODEX_NATIVE_PASSTHROUGH_SUBCOMMANDS.has(secondArg)) {
return targetArgs.slice(1);
}
return null;
}
+3 -3
View File
@@ -3,7 +3,7 @@
* and top-level profile dispatcher (Phase E switch).
*
* Extracted from src/ccs.ts (lines 291-295, 394-426).
* Handles direct execution of native Codex flag passthrough (--help, --version, etc.)
* Handles direct execution of native Codex passthrough commands (--help, resume, etc.)
* before the main profile dispatch loop runs.
*
* dispatchProfile() collapses the 6-branch switch in main() to a single call.
@@ -29,9 +29,9 @@ export interface ProfileError extends Error {
suggestions?: string[];
}
// ========== Native Codex Flag Command Executor ==========
// ========== Native Codex Command Executor ==========
export function execNativeCodexFlagCommand(args: string[]): void {
export function execNativeCodexCommand(args: string[]): void {
const adapter = getTarget('codex');
if (!adapter) {
console.error(fail('Target adapter not found for "codex"'));
@@ -498,6 +498,26 @@ process.exit(0);
});
}
it('passes ccsx resume straight through to the native Codex binary', () => {
if (process.platform === 'win32') return;
const result = runCodexAlias(['resume', '--help'], {
...process.env,
CI: '1',
NO_COLOR: '1',
CCS_HOME: tmpHome,
CODEX_HOME: path.join(tmpHome, '.codex'),
CCS_CODEX_PATH: fakeCodexPath,
CCS_TEST_CODEX_ARGS_OUT: codexArgsLogPath,
CCS_TEST_CODEX_HELP: 'Resume a previous interactive session',
});
expect(result.status).toBe(0);
expect(result.stdout).toContain('Resume a previous interactive session');
expect(result.stderr).not.toContain('Profile not found: resume');
expect(readLoggedCodexCalls(codexArgsLogPath)).toEqual([['resume', '--help']]);
});
it('strips nested Codex session env from passthrough launches while keeping CODEX_HOME', () => {
if (process.platform === 'win32') return;