Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303#!/usr/bin/env node// Unified entry point for the Codex CLI.
import { spawn } from "node:child_process";import { existsSync, readFileSync, realpathSync } from "fs";import { createRequire } from "node:module";import { constants } from "node:os";import path from "path";import { fileURLToPath } from "url";
// __dirname equivalent in ESMconst __filename = fileURLToPath(import.meta.url);const __dirname = path.dirname(__filename);const require = createRequire(import.meta.url);const codexPackageRoot = realpathSync(path.join(__dirname, ".."));
const PLATFORM_PACKAGE_BY_TARGET = { "x86_64-unknown-linux-musl": "@openai/codex-linux-x64", "aarch64-unknown-linux-musl": "@openai/codex-linux-arm64", "x86_64-apple-darwin": "@openai/codex-darwin-x64", "aarch64-apple-darwin": "@openai/codex-darwin-arm64", "x86_64-pc-windows-msvc": "@openai/codex-win32-x64", "aarch64-pc-windows-msvc": "@openai/codex-win32-arm64",};
const { platform, arch } = process;
let targetTriple = null;switch (platform) { case "linux": case "android": switch (arch) { case "x64": targetTriple = "x86_64-unknown-linux-musl"; break; case "arm64": targetTriple = "aarch64-unknown-linux-musl"; break; default: break; } break; case "darwin": switch (arch) { case "x64": targetTriple = "x86_64-apple-darwin"; break; case "arm64": targetTriple = "aarch64-apple-darwin"; break; default: break; } break; case "win32": switch (arch) { case "x64": targetTriple = "x86_64-pc-windows-msvc"; break; case "arm64": targetTriple = "aarch64-pc-windows-msvc"; break; default: break; } break; default: break;}
if (!targetTriple) { throw new Error(`Unsupported platform: ${platform} (${arch})`);}
const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];if (!platformPackage) { throw new Error(`Unsupported target triple: ${targetTriple}`);}
function findCodexExecutable() { let vendorRoot; try { const packageJsonPath = require.resolve(`${platformPackage}/package.json`); vendorRoot = path.join(path.dirname(packageJsonPath), "vendor"); } catch { vendorRoot = path.join(__dirname, "..", "vendor"); }
const codexExecutable = path.join( vendorRoot, targetTriple, "bin", process.platform === "win32" ? "codex.exe" : "codex", ); if (existsSync(codexExecutable)) { return codexExecutable; }
const packageManager = detectPackageManager(); const updateCommand = packageManager === "bun" ? "bun install -g @openai/codex@latest" : packageManager === "pnpm" ? "pnpm add -g @openai/codex@latest" : packageManager === "vite-plus" ? "vp install -g @openai/codex@latest" : "npm install -g @openai/codex@latest"; throw new Error( `Missing optional dependency ${platformPackage}. Reinstall Codex: ${updateCommand}`, );}
const binaryPath = findCodexExecutable();
function isPnpmOwnedCodexInstall(nodeModulesDir) { if (!existsSync(path.join(nodeModulesDir, ".modules.yaml"))) { return false; }
try { return ( realpathSync(path.join(nodeModulesDir, "@openai", "codex")) === codexPackageRoot ); } catch { return false; }}
function isVitePlusOwnedCodexInstall(packagesDir) { if (path.basename(packagesDir) !== "packages") { return false; }
try { const metadata = JSON.parse( readFileSync(path.join(packagesDir, "@openai", "codex.json"), "utf8"), ); if (metadata.name !== "@openai/codex") { return false; }
// Vite+ records the active global installation in packages/@openai/codex.json. // Older installs have no ID or append a #-prefixed ID to the package name; // newer installs put the ID in a subdirectory of the package prefix. const installId = metadata.installId || ""; const installDir = installId.startsWith("#") ? path.join(packagesDir, `@openai/codex${installId}`) : path.join(packagesDir, "@openai/codex", installId); for (const nodeModulesDir of [ path.join(installDir, "lib", "node_modules"), path.join(installDir, "node_modules"), ]) { const packageRoot = path.join(nodeModulesDir, "@openai", "codex"); if ( existsSync(packageRoot) && realpathSync(packageRoot) === codexPackageRoot ) { return true; } } } catch { // Missing or unreadable ownership metadata must not prevent Codex starting. } return false;}
/** * Use heuristics to detect the package manager that was used to install Codex * in order to give the user a hint about how to update it. */function detectPackageManager() { // Package-manager ownership metadata can be several parents above the package. // Search ancestors of both the canonical package root and lexical entrypoint // because the package manager may link either path. const entrypointDir = path.dirname(path.resolve(process.argv[1])); for (const startDir of new Set([codexPackageRoot, entrypointDir])) { const filesystemRoot = path.parse(startDir).root; for ( let currentDir = startDir; currentDir !== filesystemRoot; currentDir = path.dirname(currentDir) ) { if (isVitePlusOwnedCodexInstall(currentDir)) { return "vite-plus"; } if (isPnpmOwnedCodexInstall(path.join(currentDir, "node_modules"))) { return "pnpm"; } }
if (isPnpmOwnedCodexInstall(path.join(filesystemRoot, "node_modules"))) { return "pnpm"; } }
const userAgent = process.env.npm_config_user_agent || ""; if (/\bbun\//.test(userAgent)) { return "bun"; }
const execPath = process.env.npm_execpath || ""; if (execPath.includes("bun")) { return "bun"; }
if ( __dirname.includes(".bun/install/global") || __dirname.includes(".bun\\install\\global") ) { return "bun"; }
return userAgent ? "npm" : null;}
const packageManager = detectPackageManager();const packageManagerEnvVar = packageManager === "bun" ? "CODEX_MANAGED_BY_BUN" : packageManager === "pnpm" ? "CODEX_MANAGED_BY_PNPM" : packageManager === "vite-plus" ? "CODEX_MANAGED_BY_VITE_PLUS" : "CODEX_MANAGED_BY_NPM";const env = { ...process.env, CODEX_MANAGED_PACKAGE_ROOT: codexPackageRoot,};delete env.CODEX_MANAGED_BY_NPM;delete env.CODEX_MANAGED_BY_BUN;delete env.CODEX_MANAGED_BY_PNPM;delete env.CODEX_MANAGED_BY_VITE_PLUS;env[packageManagerEnvVar] = "1";
// Replace Node so the native binary receives signals and exits directly.// Node exposes execve on Windows too, but calling it there is unsupported.if (process.platform !== "win32" && typeof process.execve === "function") { // Node ignores SIGXFSZ, and ignored signals survive execve. Installing a // handler lets execve restore the default disposition in the native binary. process.on("SIGXFSZ", () => {}); process.execve(binaryPath, [binaryPath, ...process.argv.slice(2)], env);}
// Older Node versions and Windows need a child process. Spawn asynchronously// so Node can forward signals while the native binary is executing.const child = spawn(binaryPath, process.argv.slice(2), { stdio: "inherit", env,});
child.on("error", (err) => { // Typically triggered when the binary is missing or not executable. // Re-throwing here will terminate the parent with a non-zero exit code // while still printing a helpful stack trace. // eslint-disable-next-line no-console console.error(err); process.exit(1);});
// Forward common termination signals to the child so that it shuts down// gracefully. In the handler we temporarily disable the default behavior of// exiting immediately; once the child has been signaled we simply wait for// its exit event which will in turn terminate the parent (see below).const forwardSignal = (signal) => { if (child.killed) { return; } try { child.kill(signal); } catch { /* ignore */ }};
["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => { process.on(sig, () => forwardSignal(sig));});
// When the child exits, mirror its termination reason in the parent so that// shell scripts and other tooling observe the correct exit status.// Wrap the lifetime of the child process in a Promise so that we can await// its termination in a structured way. The Promise resolves with an object// describing how the child exited: either via exit code or due to a signal.const childResult = await new Promise((resolve) => { child.on("exit", (code, signal) => { if (signal) { resolve({ type: "signal", signal }); } else { resolve({ type: "code", exitCode: code ?? 1 }); } });});
if (childResult.type === "signal") { // Preserve a failure exit code even if Node ignores the re-emitted signal. process.exitCode = 128 + constants.signals[childResult.signal]; process.removeAllListeners(childResult.signal); process.kill(process.pid, childResult.signal);} else { process.exit(childResult.exitCode);}