diff --git a/codex-cli/bin/codex.js b/codex-cli/bin/codex.js index df06dd36..d92d8f2f 100755 --- a/codex-cli/bin/codex.js +++ b/codex-cli/bin/codex.js @@ -1,154 +1,123 @@ #!/usr/bin/env node // Unified entry point for the Codex CLI. -/* - * Behavior - * ========= - * 1. By default we import the JavaScript implementation located in - * dist/cli.js. - * - * 2. Developers can opt-in to a pre-compiled Rust binary by setting the - * environment variable CODEX_RUST to a truthy value (`1`, `true`, etc.). - * When that variable is present we resolve the correct binary for the - * current platform / architecture and execute it via child_process. - * - * If the CODEX_RUST=1 is specified and there is no native binary for the - * current platform / architecture, an error is thrown. - */ -import fs from "fs"; import path from "path"; -import { fileURLToPath, pathToFileURL } from "url"; - -// Determine whether the user explicitly wants the Rust CLI. +import { fileURLToPath } from "url"; // __dirname equivalent in ESM const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -// For the @native release of the Node module, the `use-native` file is added, -// indicating we should default to the native binary. For other releases, -// setting CODEX_RUST=1 will opt-in to the native binary, if included. -const wantsNative = fs.existsSync(path.join(__dirname, "use-native")) || - (process.env.CODEX_RUST != null - ? ["1", "true", "yes"].includes(process.env.CODEX_RUST.toLowerCase()) - : false); +const { platform, arch } = process; -// Try native binary if requested. -if (wantsNative && process.platform !== 'win32') { - 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; - default: - break; - } - - if (!targetTriple) { - throw new Error(`Unsupported platform: ${platform} (${arch})`); - } - - const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`); - - // Use an asynchronous spawn instead of spawnSync so that Node is able to - // respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is - // executing. This allows us to forward those signals to the child process - // and guarantees that when either the child terminates or the parent - // receives a fatal signal, both processes exit in a predictable manner. - const { spawn } = await import("child_process"); - - const child = spawn(binaryPath, process.argv.slice(2), { - stdio: "inherit", - env: { ...process.env, CODEX_MANAGED_BY_NPM: "1" }, - }); - - 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; +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; } - try { - child.kill(signal); - } catch { - /* ignore */ + break; + case "darwin": + switch (arch) { + case "x64": + targetTriple = "x86_64-apple-darwin"; + break; + case "arm64": + targetTriple = "aarch64-apple-darwin"; + break; + default: + break; } - }; - - ["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") { - // Re-emit the same signal so that the parent terminates with the expected - // semantics (this also sets the correct exit code of 128 + n). - process.kill(process.pid, childResult.signal); - } else { - process.exit(childResult.exitCode); - } -} else { - // Fallback: execute the original JavaScript CLI. - - // Resolve the path to the compiled CLI bundle - const cliPath = path.resolve(__dirname, "../dist/cli.js"); - const cliUrl = pathToFileURL(cliPath).href; - - // Load and execute the CLI - try { - await import(cliUrl); - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - process.exit(1); - } + break; + case "win32": + switch (arch) { + case "x64": + targetTriple = "x86_64-pc-windows-msvc.exe"; + break; + case "arm64": + // We do not build this today, fall through... + default: + break; + } + break; + default: + break; } + +if (!targetTriple) { + throw new Error(`Unsupported platform: ${platform} (${arch})`); +} + +const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`); + +// Use an asynchronous spawn instead of spawnSync so that Node is able to +// respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is +// executing. This allows us to forward those signals to the child process +// and guarantees that when either the child terminates or the parent +// receives a fatal signal, both processes exit in a predictable manner. +const { spawn } = await import("child_process"); + +const child = spawn(binaryPath, process.argv.slice(2), { + stdio: "inherit", + env: { ...process.env, CODEX_MANAGED_BY_NPM: "1" }, +}); + +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") { + // Re-emit the same signal so that the parent terminates with the expected + // semantics (this also sets the correct exit code of 128 + n). + process.kill(process.pid, childResult.signal); +} else { + process.exit(childResult.exitCode); +} + diff --git a/codex-cli/scripts/install_native_deps.sh b/codex-cli/scripts/install_native_deps.sh index 353ffafd..6cf2faaf 100755 --- a/codex-cli/scripts/install_native_deps.sh +++ b/codex-cli/scripts/install_native_deps.sh @@ -2,13 +2,8 @@ # Install native runtime dependencies for codex-cli. # -# By default the script copies the sandbox binaries that are required at -# runtime. When called with the --full-native flag, it additionally -# bundles pre-built Rust CLI binaries so that the resulting npm package can run -# the native implementation when users set CODEX_RUST=1. -# # Usage -# install_native_deps.sh [--full-native] [--workflow-url URL] [CODEX_CLI_ROOT] +# install_native_deps.sh [--workflow-url URL] [CODEX_CLI_ROOT] # # The optional RELEASE_ROOT is the path that contains package.json. Omitting # it installs the binaries into the repository's own bin/ folder to support @@ -21,18 +16,14 @@ set -euo pipefail # ------------------ CODEX_CLI_ROOT="" -INCLUDE_RUST=0 # Until we start publishing stable GitHub releases, we have to grab the binaries # from the GitHub Action that created them. Update the URL below to point to the # appropriate workflow run: -WORKFLOW_URL="https://github.com/openai/codex/actions/runs/15981617627" +WORKFLOW_URL="https://github.com/openai/codex/actions/runs/16840150768" # rust-v0.20.0-alpha.2 while [[ $# -gt 0 ]]; do case "$1" in - --full-native) - INCLUDE_RUST=1 - ;; --workflow-url) shift || { echo "--workflow-url requires an argument"; exit 1; } if [ -n "$1" ]; then @@ -81,26 +72,20 @@ trap 'rm -rf "$ARTIFACTS_DIR"' EXIT # NB: The GitHub CLI `gh` must be installed and authenticated. gh run download --dir "$ARTIFACTS_DIR" --repo openai/codex "$WORKFLOW_ID" -# Decompress the artifacts for Linux sandboxing. -zstd -d "$ARTIFACTS_DIR/x86_64-unknown-linux-musl/codex-linux-sandbox-x86_64-unknown-linux-musl.zst" \ - -o "$BIN_DIR/codex-linux-sandbox-x64" - -zstd -d "$ARTIFACTS_DIR/aarch64-unknown-linux-musl/codex-linux-sandbox-aarch64-unknown-linux-musl.zst" \ - -o "$BIN_DIR/codex-linux-sandbox-arm64" - -if [[ "$INCLUDE_RUST" -eq 1 ]]; then - # x64 Linux - zstd -d "$ARTIFACTS_DIR/x86_64-unknown-linux-musl/codex-x86_64-unknown-linux-musl.zst" \ - -o "$BIN_DIR/codex-x86_64-unknown-linux-musl" - # ARM64 Linux - zstd -d "$ARTIFACTS_DIR/aarch64-unknown-linux-musl/codex-aarch64-unknown-linux-musl.zst" \ - -o "$BIN_DIR/codex-aarch64-unknown-linux-musl" - # x64 macOS - zstd -d "$ARTIFACTS_DIR/x86_64-apple-darwin/codex-x86_64-apple-darwin.zst" \ - -o "$BIN_DIR/codex-x86_64-apple-darwin" - # ARM64 macOS - zstd -d "$ARTIFACTS_DIR/aarch64-apple-darwin/codex-aarch64-apple-darwin.zst" \ - -o "$BIN_DIR/codex-aarch64-apple-darwin" -fi +# x64 Linux +zstd -d "$ARTIFACTS_DIR/x86_64-unknown-linux-musl/codex-x86_64-unknown-linux-musl.zst" \ + -o "$BIN_DIR/codex-x86_64-unknown-linux-musl" +# ARM64 Linux +zstd -d "$ARTIFACTS_DIR/aarch64-unknown-linux-musl/codex-aarch64-unknown-linux-musl.zst" \ + -o "$BIN_DIR/codex-aarch64-unknown-linux-musl" +# x64 macOS +zstd -d "$ARTIFACTS_DIR/x86_64-apple-darwin/codex-x86_64-apple-darwin.zst" \ + -o "$BIN_DIR/codex-x86_64-apple-darwin" +# ARM64 macOS +zstd -d "$ARTIFACTS_DIR/aarch64-apple-darwin/codex-aarch64-apple-darwin.zst" \ + -o "$BIN_DIR/codex-aarch64-apple-darwin" +# x64 Windows +zstd -d "$ARTIFACTS_DIR/x86_64-pc-windows-msvc/codex-x86_64-pc-windows-msvc.exe.zst" \ + -o "$BIN_DIR/codex-x86_64-pc-windows-msvc.exe" echo "Installed native dependencies into $BIN_DIR" diff --git a/codex-cli/scripts/stage_release.sh b/codex-cli/scripts/stage_release.sh index cd32ade6..bc2dee14 100755 --- a/codex-cli/scripts/stage_release.sh +++ b/codex-cli/scripts/stage_release.sh @@ -7,15 +7,8 @@ # Usage: # # --tmp