diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index b2b1b8cf..725a82c2 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use clap::Parser; use codex_cli::LandlockCommand; use codex_cli::SeatbeltCommand; @@ -66,17 +68,23 @@ struct ReplProto {} #[tokio::main] async fn main() -> anyhow::Result<()> { + let codex_linux_sandbox_exe: Option = if cfg!(target_os = "linux") { + std::env::current_exe().ok() + } else { + None + }; + let cli = MultitoolCli::parse(); match cli.subcommand { None => { - codex_tui::run_main(cli.interactive)?; + codex_tui::run_main(cli.interactive, codex_linux_sandbox_exe)?; } Some(Subcommand::Exec(exec_cli)) => { - codex_exec::run_main(exec_cli).await?; + codex_exec::run_main(exec_cli, codex_linux_sandbox_exe).await?; } Some(Subcommand::Mcp) => { - codex_mcp_server::run_main().await?; + codex_mcp_server::run_main(codex_linux_sandbox_exe).await?; } Some(Subcommand::Proto(proto_cli)) => { proto::run_main(proto_cli).await?; diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs index 2a3f4543..d643d006 100644 --- a/codex-rs/core/src/config.rs +++ b/codex-rs/core/src/config.rs @@ -98,6 +98,14 @@ pub struct Config { /// Collection of settings that are specific to the TUI. pub tui: Tui, + + /// Path to the `codex-linux-sandbox` executable. This must be set if + /// [`crate::exec::SandboxType::LinuxSeccomp`] is used. Note that this + /// cannot be set in the config file: it must be set in code via + /// [`ConfigOverrides`]. + /// + /// When this program is invoked, arg0 will be set to `codex-linux-sandbox`. + pub codex_linux_sandbox_exe: Option, } /// Base config deserialized from ~/.codex/config.toml. @@ -222,6 +230,7 @@ pub struct ConfigOverrides { pub disable_response_storage: Option, pub model_provider: Option, pub config_profile: Option, + pub codex_linux_sandbox_exe: Option, } impl Config { @@ -258,6 +267,7 @@ impl Config { disable_response_storage, model_provider, config_profile: config_profile_key, + codex_linux_sandbox_exe, } = overrides; let config_profile = match config_profile_key.or(cfg.profile) { @@ -359,6 +369,7 @@ impl Config { history, file_opener: cfg.file_opener.unwrap_or(UriBasedFileOpener::VsCode), tui: cfg.tui.unwrap_or_default(), + codex_linux_sandbox_exe, }; Ok(config) } @@ -699,6 +710,7 @@ disable_response_storage = true history: History::default(), file_opener: UriBasedFileOpener::VsCode, tui: Tui::default(), + codex_linux_sandbox_exe: None, }, o3_profile_config ); @@ -737,6 +749,7 @@ disable_response_storage = true history: History::default(), file_opener: UriBasedFileOpener::VsCode, tui: Tui::default(), + codex_linux_sandbox_exe: None, }; assert_eq!(expected_gpt3_profile_config, gpt3_profile_config); @@ -790,6 +803,7 @@ disable_response_storage = true history: History::default(), file_opener: UriBasedFileOpener::VsCode, tui: Tui::default(), + codex_linux_sandbox_exe: None, }; assert_eq!(expected_zdr_profile_config, zdr_profile_config); diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index e615de79..dbf01f02 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -3,6 +3,7 @@ mod event_processor; use std::io::IsTerminal; use std::path::Path; +use std::path::PathBuf; use std::sync::Arc; pub use cli::Cli; @@ -24,7 +25,7 @@ use tracing::error; use tracing::info; use tracing_subscriber::EnvFilter; -pub async fn run_main(cli: Cli) -> anyhow::Result<()> { +pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> anyhow::Result<()> { let Cli { images, model, @@ -69,6 +70,7 @@ pub async fn run_main(cli: Cli) -> anyhow::Result<()> { }, cwd: cwd.map(|p| p.canonicalize().unwrap_or(p)), model_provider: None, + codex_linux_sandbox_exe, }; let config = Config::load_with_overrides(overrides)?; // Print the effective configuration so users can see what Codex is using. diff --git a/codex-rs/exec/src/main.rs b/codex-rs/exec/src/main.rs index 3a40da23..3cb7bd0b 100644 --- a/codex-rs/exec/src/main.rs +++ b/codex-rs/exec/src/main.rs @@ -1,11 +1,19 @@ +use std::path::PathBuf; + use clap::Parser; use codex_exec::Cli; use codex_exec::run_main; #[tokio::main] async fn main() -> anyhow::Result<()> { + let codex_linux_sandbox_exe: Option = if cfg!(target_os = "linux") { + std::env::current_exe().ok() + } else { + None + }; + let cli = Cli::parse(); - run_main(cli).await?; + run_main(cli, codex_linux_sandbox_exe).await?; Ok(()) } diff --git a/codex-rs/mcp-server/src/codex_tool_config.rs b/codex-rs/mcp-server/src/codex_tool_config.rs index 2ddc00fb..d04a5c80 100644 --- a/codex-rs/mcp-server/src/codex_tool_config.rs +++ b/codex-rs/mcp-server/src/codex_tool_config.rs @@ -144,7 +144,10 @@ pub(crate) fn create_tool_for_codex_tool_call_param() -> Tool { impl CodexToolCallParam { /// Returns the initial user prompt to start the Codex conversation and the /// Config. - pub fn into_config(self) -> std::io::Result<(String, codex_core::config::Config)> { + pub fn into_config( + self, + codex_linux_sandbox_exe: Option, + ) -> std::io::Result<(String, codex_core::config::Config)> { let Self { prompt, model, @@ -167,6 +170,7 @@ impl CodexToolCallParam { sandbox_policy, disable_response_storage, model_provider: None, + codex_linux_sandbox_exe, }; let cfg = codex_core::config::Config::load_with_overrides(overrides)?; diff --git a/codex-rs/mcp-server/src/lib.rs b/codex-rs/mcp-server/src/lib.rs index e621f779..0f29eb78 100644 --- a/codex-rs/mcp-server/src/lib.rs +++ b/codex-rs/mcp-server/src/lib.rs @@ -2,6 +2,7 @@ #![deny(clippy::print_stdout, clippy::print_stderr)] use std::io::Result as IoResult; +use std::path::PathBuf; use mcp_types::JSONRPCMessage; use tokio::io::AsyncBufReadExt; @@ -24,7 +25,7 @@ use crate::message_processor::MessageProcessor; /// plenty for an interactive CLI. const CHANNEL_CAPACITY: usize = 128; -pub async fn run_main() -> IoResult<()> { +pub async fn run_main(codex_linux_sandbox_exe: Option) -> IoResult<()> { // Install a simple subscriber so `tracing` output is visible. Users can // control the log level with `RUST_LOG`. tracing_subscriber::fmt() @@ -61,7 +62,7 @@ pub async fn run_main() -> IoResult<()> { // Task: process incoming messages. let processor_handle = tokio::spawn({ - let mut processor = MessageProcessor::new(outgoing_tx.clone()); + let mut processor = MessageProcessor::new(outgoing_tx.clone(), codex_linux_sandbox_exe); async move { while let Some(msg) = incoming_rx.recv().await { match msg { diff --git a/codex-rs/mcp-server/src/main.rs b/codex-rs/mcp-server/src/main.rs index baef8587..8ce727e9 100644 --- a/codex-rs/mcp-server/src/main.rs +++ b/codex-rs/mcp-server/src/main.rs @@ -1,7 +1,15 @@ +use std::path::PathBuf; + use codex_mcp_server::run_main; #[tokio::main] async fn main() -> std::io::Result<()> { - run_main().await?; + let codex_linux_sandbox_exe: Option = if cfg!(target_os = "linux") { + std::env::current_exe().ok() + } else { + None + }; + + run_main(codex_linux_sandbox_exe).await?; Ok(()) } diff --git a/codex-rs/mcp-server/src/message_processor.rs b/codex-rs/mcp-server/src/message_processor.rs index 299523f9..bf6f42e5 100644 --- a/codex-rs/mcp-server/src/message_processor.rs +++ b/codex-rs/mcp-server/src/message_processor.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use crate::codex_tool_config::CodexToolCallParam; use crate::codex_tool_config::create_tool_for_codex_tool_call_param; @@ -28,15 +30,20 @@ use tokio::task; pub(crate) struct MessageProcessor { outgoing: mpsc::Sender, initialized: bool, + codex_linux_sandbox_exe: Option, } impl MessageProcessor { /// Create a new `MessageProcessor`, retaining a handle to the outgoing /// `Sender` so handlers can enqueue messages to be written to stdout. - pub(crate) fn new(outgoing: mpsc::Sender) -> Self { + pub(crate) fn new( + outgoing: mpsc::Sender, + codex_linux_sandbox_exe: Option, + ) -> Self { Self { outgoing, initialized: false, + codex_linux_sandbox_exe, } } @@ -339,7 +346,7 @@ impl MessageProcessor { let (initial_prompt, config): (String, CodexConfig) = match arguments { Some(json_val) => match serde_json::from_value::(json_val) { - Ok(tool_cfg) => match tool_cfg.into_config() { + Ok(tool_cfg) => match tool_cfg.into_config(self.codex_linux_sandbox_exe.clone()) { Ok(cfg) => cfg, Err(e) => { let result = CallToolResult { diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index f4391785..4ab68724 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -10,6 +10,7 @@ use codex_core::protocol::SandboxPolicy; use codex_core::util::is_inside_git_repo; use log_layer::TuiLogLayer; use std::fs::OpenOptions; +use std::path::PathBuf; use tracing_appender::non_blocking; use tracing_subscriber::EnvFilter; use tracing_subscriber::prelude::*; @@ -36,7 +37,7 @@ mod user_approval_widget; pub use cli::Cli; -pub fn run_main(cli: Cli) -> std::io::Result<()> { +pub fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> std::io::Result<()> { let (sandbox_policy, approval_policy) = if cli.full_auto { ( Some(SandboxPolicy::new_full_auto_policy()), @@ -61,6 +62,7 @@ pub fn run_main(cli: Cli) -> std::io::Result<()> { cwd: cli.cwd.clone().map(|p| p.canonicalize().unwrap_or(p)), model_provider: None, config_profile: cli.config_profile.clone(), + codex_linux_sandbox_exe, }; #[allow(clippy::print_stderr)] match Config::load_with_overrides(overrides) { diff --git a/codex-rs/tui/src/main.rs b/codex-rs/tui/src/main.rs index 531682da..08738ba2 100644 --- a/codex-rs/tui/src/main.rs +++ b/codex-rs/tui/src/main.rs @@ -1,10 +1,18 @@ +use std::path::PathBuf; + use clap::Parser; use codex_tui::Cli; use codex_tui::run_main; #[tokio::main] async fn main() -> std::io::Result<()> { + let codex_linux_sandbox_exe: Option = if cfg!(target_os = "linux") { + std::env::current_exe().ok() + } else { + None + }; + let cli = Cli::parse(); - run_main(cli)?; + run_main(cli, codex_linux_sandbox_exe)?; Ok(()) }