From 9ee6e6f3422f77a7e851264585dfa2abbccdb775 Mon Sep 17 00:00:00 2001 From: dedrisian-oai Date: Sun, 28 Sep 2025 11:46:15 -0700 Subject: [PATCH 1/5] Improve update nudge (#4405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the update nudge larger and adds a link to see latest release: Screenshot 2025-09-28 at 11 19 05 AM --- codex-rs/tui/src/lib.rs | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index d82fa19acc..e959973b22 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -273,6 +273,8 @@ async fn run_ratatui_app( // within the TUI scrollback. Building spans keeps styling consistent. #[cfg(not(debug_assertions))] if let Some(latest_version) = updates::get_upgrade_version(&config) { + use crate::history_cell::padded_emoji; + use crate::history_cell::with_border_with_inner_width; use ratatui::style::Stylize as _; use ratatui::text::Line; @@ -280,16 +282,27 @@ async fn run_ratatui_app( let exe = std::env::current_exe()?; let managed_by_npm = std::env::var_os("CODEX_MANAGED_BY_NPM").is_some(); - let mut lines: Vec> = Vec::new(); - lines.push(Line::from(vec![ - "✨⬆️ Update available!".bold().cyan(), - " ".into(), - format!("{current_version} -> {latest_version}.").into(), - ])); + let mut content_lines: Vec> = vec![ + Line::from(vec![ + padded_emoji("✨").bold().cyan(), + "Update available!".bold().cyan(), + " ".into(), + format!("{current_version} -> {latest_version}.").bold(), + ]), + Line::from(""), + Line::from("See full release notes:"), + Line::from(""), + Line::from( + "https://github.com/openai/codex/releases/latest" + .cyan() + .underlined(), + ), + Line::from(""), + ]; if managed_by_npm { let npm_cmd = "npm install -g @openai/codex@latest"; - lines.push(Line::from(vec![ + content_lines.push(Line::from(vec![ "Run ".into(), npm_cmd.cyan(), " to update.".into(), @@ -298,19 +311,22 @@ async fn run_ratatui_app( && (exe.starts_with("/opt/homebrew") || exe.starts_with("/usr/local")) { let brew_cmd = "brew upgrade codex"; - lines.push(Line::from(vec![ + content_lines.push(Line::from(vec![ "Run ".into(), brew_cmd.cyan(), " to update.".into(), ])); } else { - lines.push(Line::from(vec![ + content_lines.push(Line::from(vec![ "See ".into(), - "https://github.com/openai/codex/releases/latest".cyan(), - " for the latest releases and installation options.".into(), + "https://github.com/openai/codex".cyan().underlined(), + " for installation options.".into(), ])); } + let viewport_width = tui.terminal.viewport_area.width as usize; + let inner_width = viewport_width.saturating_sub(4).max(1); + let mut lines = with_border_with_inner_width(content_lines, inner_width); lines.push("".into()); tui.insert_history_lines(lines); } From 43615becf0cee2e05e61af0b077869dda0ba7d13 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Sun, 28 Sep 2025 14:35:14 -0700 Subject: [PATCH 2/5] chore: move pre_main_hardening() utility into its own crate (#4403) --- codex-rs/Cargo.lock | 9 +++++++- codex-rs/Cargo.toml | 6 ++++-- codex-rs/cli/Cargo.toml | 10 +-------- codex-rs/cli/src/main.rs | 10 +-------- codex-rs/process-hardening/Cargo.toml | 21 +++++++++++++++++++ .../src/lib.rs} | 16 ++++++++++++++ 6 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 codex-rs/process-hardening/Cargo.toml rename codex-rs/{cli/src/pre_main_hardening.rs => process-hardening/src/lib.rs} (84%) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index eb97878072..00d95eb4e9 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -692,12 +692,12 @@ dependencies = [ "codex-exec", "codex-login", "codex-mcp-server", + "codex-process-hardening", "codex-protocol", "codex-protocol-ts", "codex-responses-api-proxy", "codex-tui", "ctor 0.5.0", - "libc", "owo-colors", "predicates", "pretty_assertions", @@ -949,6 +949,13 @@ dependencies = [ "wiremock", ] +[[package]] +name = "codex-process-hardening" +version = "0.0.0" +dependencies = [ + "libc", +] + [[package]] name = "codex-protocol" version = "0.0.0" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index af111c6f19..ddd7ed51a3 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -16,6 +16,7 @@ members = [ "mcp-server", "mcp-types", "ollama", + "process-hardening", "protocol", "protocol-ts", "rmcp-client", @@ -49,10 +50,11 @@ codex-login = { path = "login" } codex-mcp-client = { path = "mcp-client" } codex-mcp-server = { path = "mcp-server" } codex-ollama = { path = "ollama" } +codex-process-hardening = { path = "process-hardening" } codex-protocol = { path = "protocol" } -codex-rmcp-client = { path = "rmcp-client" } codex-protocol-ts = { path = "protocol-ts" } codex-responses-api-proxy = { path = "responses-api-proxy" } +codex-rmcp-client = { path = "rmcp-client" } codex-tui = { path = "tui" } codex-utils-readiness = { path = "utils/readiness" } core_test_support = { path = "core/tests/common" } @@ -83,8 +85,8 @@ dirs = "6" dotenvy = "0.15.7" env-flags = "0.1.1" env_logger = "0.11.5" -eventsource-stream = "0.2.3" escargot = "0.5" +eventsource-stream = "0.2.3" futures = "0.3" icu_decimal = "2.0.0" icu_locale_core = "2.0.0" diff --git a/codex-rs/cli/Cargo.toml b/codex-rs/cli/Cargo.toml index e61285c822..094a35b42c 100644 --- a/codex-rs/cli/Cargo.toml +++ b/codex-rs/cli/Cargo.toml @@ -25,6 +25,7 @@ codex-core = { workspace = true } codex-exec = { workspace = true } codex-login = { workspace = true } codex-mcp-server = { workspace = true } +codex-process-hardening = { workspace = true } codex-protocol = { workspace = true } codex-protocol-ts = { workspace = true } codex-responses-api-proxy = { workspace = true } @@ -43,15 +44,6 @@ tokio = { workspace = true, features = [ tracing = { workspace = true } tracing-subscriber = { workspace = true } -[target.'cfg(target_os = "linux")'.dependencies] -libc = { workspace = true } - -[target.'cfg(target_os = "android")'.dependencies] -libc = { workspace = true } - -[target.'cfg(target_os = "macos")'.dependencies] -libc = { workspace = true } - [dev-dependencies] assert_cmd = { workspace = true } predicates = { workspace = true } diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 871966b0a7..7dcfdae2f3 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -23,7 +23,6 @@ use std::path::PathBuf; use supports_color::Stream; mod mcp_cmd; -mod pre_main_hardening; use crate::mcp_cmd::McpCli; use crate::proto::ProtoCli; @@ -213,14 +212,7 @@ fn pre_main_hardening() { }; if secure_mode == "1" { - #[cfg(any(target_os = "linux", target_os = "android"))] - crate::pre_main_hardening::pre_main_hardening_linux(); - - #[cfg(target_os = "macos")] - crate::pre_main_hardening::pre_main_hardening_macos(); - - #[cfg(windows)] - crate::pre_main_hardening::pre_main_hardening_windows(); + codex_process_hardening::pre_main_hardening(); } // Always clear this env var so child processes don't inherit it. diff --git a/codex-rs/process-hardening/Cargo.toml b/codex-rs/process-hardening/Cargo.toml new file mode 100644 index 0000000000..7294b6e268 --- /dev/null +++ b/codex-rs/process-hardening/Cargo.toml @@ -0,0 +1,21 @@ +[package] +edition = "2024" +name = "codex-process-hardening" +version = { workspace = true } + +[lib] +name = "codex_process_hardening" +path = "src/lib.rs" + +[lints] +workspace = true + +[dependencies] +[target.'cfg(target_os = "linux")'.dependencies] +libc = { workspace = true } + +[target.'cfg(target_os = "android")'.dependencies] +libc = { workspace = true } + +[target.'cfg(target_os = "macos")'.dependencies] +libc = { workspace = true } diff --git a/codex-rs/cli/src/pre_main_hardening.rs b/codex-rs/process-hardening/src/lib.rs similarity index 84% rename from codex-rs/cli/src/pre_main_hardening.rs rename to codex-rs/process-hardening/src/lib.rs index a75cb3561c..a787b4097d 100644 --- a/codex-rs/cli/src/pre_main_hardening.rs +++ b/codex-rs/process-hardening/src/lib.rs @@ -1,3 +1,19 @@ +/// This is designed to be called pre-main() (using `#[ctor::ctor]`) to perform +/// various process hardening steps, such as +/// - disabling core dumps +/// - disabling ptrace attach on Linux and macOS. +/// - removing dangerous environment variables such as LD_PRELOAD and DYLD_* +pub fn pre_main_hardening() { + #[cfg(any(target_os = "linux", target_os = "android"))] + pre_main_hardening_linux(); + + #[cfg(target_os = "macos")] + pre_main_hardening_macos(); + + #[cfg(windows)] + pre_main_hardening_windows(); +} + #[cfg(any(target_os = "linux", target_os = "android"))] const PRCTL_FAILED_EXIT_CODE: i32 = 5; From 7407469791857c9ec40a1af3c60e7d70c9799554 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Sun, 28 Sep 2025 15:13:44 -0700 Subject: [PATCH 3/5] chore: lower logging level from error to info for MCP startup (#4412) --- codex-rs/core/src/mcp_connection_manager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codex-rs/core/src/mcp_connection_manager.rs b/codex-rs/core/src/mcp_connection_manager.rs index 015ca8c6bb..a488791c2b 100644 --- a/codex-rs/core/src/mcp_connection_manager.rs +++ b/codex-rs/core/src/mcp_connection_manager.rs @@ -108,7 +108,7 @@ impl McpClientAdapter { params: mcp_types::InitializeRequestParams, startup_timeout: Duration, ) -> Result { - tracing::error!( + info!( "new_stdio_client use_rmcp_client: {use_rmcp_client} program: {program:?} args: {args:?} env: {env:?} params: {params:?} startup_timeout: {startup_timeout:?}" ); if use_rmcp_client { From eb9ece042a1c1cb287c9f839debcb356262a04a5 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Sun, 28 Sep 2025 15:13:56 -0700 Subject: [PATCH 4/5] chore: remove responses-api-proxy from the multitool --- codex-rs/Cargo.lock | 5 ++--- codex-rs/Cargo.toml | 1 - codex-rs/cli/Cargo.toml | 1 - codex-rs/cli/src/main.rs | 11 ----------- codex-rs/responses-api-proxy/Cargo.toml | 6 +++--- codex-rs/responses-api-proxy/src/main.rs | 18 ++++++++---------- 6 files changed, 13 insertions(+), 29 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 00d95eb4e9..fc0eb20a88 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -695,7 +695,6 @@ dependencies = [ "codex-process-hardening", "codex-protocol", "codex-protocol-ts", - "codex-responses-api-proxy", "codex-tui", "ctor 0.5.0", "owo-colors", @@ -996,13 +995,13 @@ version = "0.0.0" dependencies = [ "anyhow", "clap", - "codex-arg0", + "codex-process-hardening", + "ctor 0.5.0", "libc", "reqwest", "serde", "serde_json", "tiny_http", - "tokio", "zeroize", ] diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index ddd7ed51a3..862f06aa17 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -53,7 +53,6 @@ codex-ollama = { path = "ollama" } codex-process-hardening = { path = "process-hardening" } codex-protocol = { path = "protocol" } codex-protocol-ts = { path = "protocol-ts" } -codex-responses-api-proxy = { path = "responses-api-proxy" } codex-rmcp-client = { path = "rmcp-client" } codex-tui = { path = "tui" } codex-utils-readiness = { path = "utils/readiness" } diff --git a/codex-rs/cli/Cargo.toml b/codex-rs/cli/Cargo.toml index 094a35b42c..618f4b7b03 100644 --- a/codex-rs/cli/Cargo.toml +++ b/codex-rs/cli/Cargo.toml @@ -28,7 +28,6 @@ codex-mcp-server = { workspace = true } codex-process-hardening = { workspace = true } codex-protocol = { workspace = true } codex-protocol-ts = { workspace = true } -codex-responses-api-proxy = { workspace = true } codex-tui = { workspace = true } ctor = { workspace = true } owo-colors = { workspace = true } diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 7dcfdae2f3..5c95df0afa 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -1,4 +1,3 @@ -use anyhow::Context; use clap::CommandFactory; use clap::Parser; use clap_complete::Shell; @@ -15,7 +14,6 @@ use codex_cli::login::run_logout; use codex_cli::proto; use codex_common::CliConfigOverrides; use codex_exec::Cli as ExecCli; -use codex_responses_api_proxy::Args as ResponsesApiProxyArgs; use codex_tui::AppExitInfo; use codex_tui::Cli as TuiCli; use owo_colors::OwoColorize; @@ -87,10 +85,6 @@ enum Subcommand { /// Internal: generate TypeScript protocol bindings. #[clap(hide = true)] GenerateTs(GenerateTsCommand), - - /// Internal: run the responses API proxy. - #[clap(hide = true)] - ResponsesApiProxy(ResponsesApiProxyArgs), } #[derive(Debug, Parser)] @@ -339,11 +333,6 @@ async fn cli_main(codex_linux_sandbox_exe: Option) -> anyhow::Result<() Some(Subcommand::GenerateTs(gen_cli)) => { codex_protocol_ts::generate_ts(&gen_cli.out_dir, gen_cli.prettier.as_deref())?; } - Some(Subcommand::ResponsesApiProxy(args)) => { - tokio::task::spawn_blocking(move || codex_responses_api_proxy::run_main(args)) - .await - .context("responses-api-proxy blocking task panicked")??; - } } Ok(()) diff --git a/codex-rs/responses-api-proxy/Cargo.toml b/codex-rs/responses-api-proxy/Cargo.toml index b66473b195..651019a816 100644 --- a/codex-rs/responses-api-proxy/Cargo.toml +++ b/codex-rs/responses-api-proxy/Cargo.toml @@ -8,7 +8,7 @@ name = "codex_responses_api_proxy" path = "src/lib.rs" [[bin]] -name = "responses-api-proxy" +name = "codex-responses-api-proxy" path = "src/main.rs" [lints] @@ -17,11 +17,11 @@ workspace = true [dependencies] anyhow = { workspace = true } clap = { workspace = true, features = ["derive"] } -codex-arg0 = { workspace = true } +codex-process-hardening = { workspace = true } +ctor = { workspace = true } libc = { workspace = true } reqwest = { workspace = true, features = ["blocking", "json", "rustls-tls"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tiny_http = { workspace = true } -tokio = { workspace = true } zeroize = { workspace = true } diff --git a/codex-rs/responses-api-proxy/src/main.rs b/codex-rs/responses-api-proxy/src/main.rs index eaa534d981..c4568d76aa 100644 --- a/codex-rs/responses-api-proxy/src/main.rs +++ b/codex-rs/responses-api-proxy/src/main.rs @@ -1,14 +1,12 @@ -use anyhow::Context; use clap::Parser; -use codex_arg0::arg0_dispatch_or_else; use codex_responses_api_proxy::Args as ResponsesApiProxyArgs; -pub fn main() -> anyhow::Result<()> { - arg0_dispatch_or_else(|_codex_linux_sandbox_exe| async move { - let args = ResponsesApiProxyArgs::parse(); - tokio::task::spawn_blocking(move || codex_responses_api_proxy::run_main(args)) - .await - .context("responses-api-proxy blocking task panicked")??; - Ok(()) - }) +#[ctor::ctor] +fn pre_main() { + codex_process_hardening::pre_main_hardening(); +} + +pub fn main() -> anyhow::Result<()> { + let args = ResponsesApiProxyArgs::parse(); + codex_responses_api_proxy::run_main(args) } From 32e31039678dfc572633393f9baa21a3d13d33af Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Sun, 28 Sep 2025 15:13:56 -0700 Subject: [PATCH 5/5] feat: build codex-responses-api-proxy for all platforms as part of the GitHub Release --- .github/dotslash-config.json | 28 ++++++++++++++++++++++++++ .github/workflows/rust-release.yml | 4 +++- codex-cli/scripts/build_npm_package.py | 7 ++----- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/.github/dotslash-config.json b/.github/dotslash-config.json index 8566e03490..5e28cdf207 100644 --- a/.github/dotslash-config.json +++ b/.github/dotslash-config.json @@ -27,6 +27,34 @@ "path": "codex.exe" } } + }, + "codex-responses-api-proxy": { + "platforms": { + "macos-aarch64": { + "regex": "^codex-responses-api-proxy-aarch64-apple-darwin\\.zst$", + "path": "codex-responses-api-proxy" + }, + "macos-x86_64": { + "regex": "^codex-responses-api-proxy-x86_64-apple-darwin\\.zst$", + "path": "codex-responses-api-proxy" + }, + "linux-x86_64": { + "regex": "^codex-responses-api-proxy-x86_64-unknown-linux-musl\\.zst$", + "path": "codex-responses-api-proxy" + }, + "linux-aarch64": { + "regex": "^codex-responses-api-proxy-aarch64-unknown-linux-musl\\.zst$", + "path": "codex-responses-api-proxy" + }, + "windows-x86_64": { + "regex": "^codex-responses-api-proxy-x86_64-pc-windows-msvc\\.exe\\.zst$", + "path": "codex-responses-api-proxy.exe" + }, + "windows-aarch64": { + "regex": "^codex-responses-api-proxy-aarch64-pc-windows-msvc\\.exe\\.zst$", + "path": "codex-responses-api-proxy.exe" + } + } } } } diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index c808216d96..345fd091a7 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -97,7 +97,7 @@ jobs: sudo apt install -y musl-tools pkg-config - name: Cargo build - run: cargo build --target ${{ matrix.target }} --release --bin codex + run: cargo build --target ${{ matrix.target }} --release --bin codex --bin codex-responses-api-proxy - name: Stage artifacts shell: bash @@ -107,8 +107,10 @@ jobs: if [[ "${{ matrix.runner }}" == windows* ]]; then cp target/${{ matrix.target }}/release/codex.exe "$dest/codex-${{ matrix.target }}.exe" + cp target/${{ matrix.target }}/release/codex-responses-api-proxy.exe "$dest/codex-responses-api-proxy-${{ matrix.target }}.exe" else cp target/${{ matrix.target }}/release/codex "$dest/codex-${{ matrix.target }}" + cp target/${{ matrix.target }}/release/codex-responses-api-proxy "$dest/codex-responses-api-proxy-${{ matrix.target }}" fi - if: ${{ matrix.runner == 'windows-11-arm' }} diff --git a/codex-cli/scripts/build_npm_package.py b/codex-cli/scripts/build_npm_package.py index 0401f10385..bedb1c2438 100755 --- a/codex-cli/scripts/build_npm_package.py +++ b/codex-cli/scripts/build_npm_package.py @@ -158,11 +158,8 @@ def stage_sources(staging_dir: Path, version: str) -> None: out.write("\n") -def install_native_binaries(staging_dir: Path, workflow_url: str | None) -> None: - cmd = ["./scripts/install_native_deps.py"] - if workflow_url: - cmd.extend(["--workflow-url", workflow_url]) - cmd.append(str(staging_dir)) +def install_native_binaries(staging_dir: Path, workflow_url: str) -> None: + cmd = ["./scripts/install_native_deps.py", "--workflow-url", workflow_url, str(staging_dir)] subprocess.check_call(cmd, cwd=CODEX_CLI_ROOT)