Files
ccs/src/bin/codex-runtime-router.ts
T

83 lines
3.5 KiB
TypeScript

/**
* Codex runtime router — testable logic for src/bin/codex-runtime.ts.
*
* All inter-module deps are resolved via require() at call-time so tests can
* inject stubs via require.cache before calling main().
*
* Routing:
* argv[2] === 'auth' → delegate to runCodexAuth(argv.slice(3)), exit with code
* else → resolve active profile, set CODEX_HOME, load ccs
* CCS manages the process lifecycle; entry MUST NOT
* call process.exit() when main returns -1.
*
* Return value contract:
* ≥ 0 → auth branch: caller should process.exit(code)
* -1 → CCS branch: CCS has taken over the process; caller must NOT exit
*/
process.env.CCS_INTERNAL_ENTRY_TARGET = 'codex';
/**
* Main entry-point for the ccsx / codex-runtime binary.
*
* @param argv - process.argv (or test-supplied equivalent)
* @returns ≥0 exit code for auth branch; -1 for CCS branch (no exit needed)
*/
export async function main(argv: string[]): Promise<number> {
const subcommand = argv[2];
// ── auth branch ─────────────────────────────────────────────────────────
if (subcommand === 'auth') {
const { runCodexAuth } = require('../codex-auth/codex-auth-router') as {
runCodexAuth: (args: string[]) => Promise<number>;
};
return runCodexAuth(argv.slice(3));
}
// ── non-auth branch: profile resolution ─────────────────────────────────
// F1: respect an explicit CODEX_HOME — ccsxp, user export, CI override, etc.
const explicit = (process.env.CODEX_HOME ?? '').trim();
if (!explicit) {
try {
const { resolveActiveProfile } = require('../codex-auth/resolve-active-profile') as {
resolveActiveProfile: (
env: NodeJS.ProcessEnv
) => { name: string; dir: string; source: string } | null;
};
const resolved = resolveActiveProfile(process.env);
if (resolved) {
process.env.CODEX_HOME = resolved.dir;
try {
const { ensureSharedConfigSymlink } = require('../codex-auth/codex-config-symlink') as {
ensureSharedConfigSymlink: (dir: string) => void;
};
ensureSharedConfigSymlink(resolved.dir);
} catch (symlinkErr) {
const msg = symlinkErr instanceof Error ? symlinkErr.message : String(symlinkErr);
process.stderr.write(
`[!] codex-auth: shared config symlink failed (${msg}), continuing\n`
);
}
}
} catch (resolverErr) {
const msg = resolverErr instanceof Error ? resolverErr.message : String(resolverErr);
if (resolverErr instanceof Error && resolverErr.name === 'CodexAuthProfileResolutionError') {
process.stderr.write(`[X] codex-auth: ${msg}\n`);
return 1;
}
// Resolver module threw unexpectedly — degrade to legacy mode.
process.stderr.write(`[!] codex-auth: profile resolution skipped (${msg})\n`);
}
}
// ── delegate to CCS ─────────────────────────────────────────────────────
// require() is evaluated AFTER env mutations above. CCS manages its own
// process lifecycle (spawns codex, pipes stdio, calls process.exit).
// Return -1 so the entry script knows NOT to call process.exit().
require('../ccs');
return -1; // CCS is in control — entry must not call process.exit()
}