fix: move arg0 handling out of codex-linux-sandbox and into its own crate (#1697)
This commit is contained in:
20
codex-rs/Cargo.lock
generated
20
codex-rs/Cargo.lock
generated
@@ -605,6 +605,17 @@ dependencies = [
|
|||||||
"tree-sitter-bash",
|
"tree-sitter-bash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codex-arg0"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"codex-core",
|
||||||
|
"codex-linux-sandbox",
|
||||||
|
"dotenvy",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codex-chatgpt"
|
name = "codex-chatgpt"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@@ -628,11 +639,11 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
|
"codex-arg0",
|
||||||
"codex-chatgpt",
|
"codex-chatgpt",
|
||||||
"codex-common",
|
"codex-common",
|
||||||
"codex-core",
|
"codex-core",
|
||||||
"codex-exec",
|
"codex-exec",
|
||||||
"codex-linux-sandbox",
|
|
||||||
"codex-login",
|
"codex-login",
|
||||||
"codex-mcp-server",
|
"codex-mcp-server",
|
||||||
"codex-tui",
|
"codex-tui",
|
||||||
@@ -709,9 +720,9 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"codex-arg0",
|
||||||
"codex-common",
|
"codex-common",
|
||||||
"codex-core",
|
"codex-core",
|
||||||
"codex-linux-sandbox",
|
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"shlex",
|
"shlex",
|
||||||
@@ -761,7 +772,6 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"codex-common",
|
"codex-common",
|
||||||
"codex-core",
|
"codex-core",
|
||||||
"dotenvy",
|
|
||||||
"landlock",
|
"landlock",
|
||||||
"libc",
|
"libc",
|
||||||
"seccompiler",
|
"seccompiler",
|
||||||
@@ -799,8 +809,8 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
|
"codex-arg0",
|
||||||
"codex-core",
|
"codex-core",
|
||||||
"codex-linux-sandbox",
|
|
||||||
"mcp-types",
|
"mcp-types",
|
||||||
"mcp_test_support",
|
"mcp_test_support",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
@@ -826,10 +836,10 @@ dependencies = [
|
|||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"clap",
|
"clap",
|
||||||
"codex-ansi-escape",
|
"codex-ansi-escape",
|
||||||
|
"codex-arg0",
|
||||||
"codex-common",
|
"codex-common",
|
||||||
"codex-core",
|
"codex-core",
|
||||||
"codex-file-search",
|
"codex-file-search",
|
||||||
"codex-linux-sandbox",
|
|
||||||
"codex-login",
|
"codex-login",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ resolver = "2"
|
|||||||
members = [
|
members = [
|
||||||
"ansi-escape",
|
"ansi-escape",
|
||||||
"apply-patch",
|
"apply-patch",
|
||||||
|
"arg0",
|
||||||
"cli",
|
"cli",
|
||||||
"common",
|
"common",
|
||||||
"core",
|
"core",
|
||||||
|
|||||||
18
codex-rs/arg0/Cargo.toml
Normal file
18
codex-rs/arg0/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "codex-arg0"
|
||||||
|
version = { workspace = true }
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "codex_arg0"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1"
|
||||||
|
codex-core = { path = "../core" }
|
||||||
|
codex-linux-sandbox = { path = "../linux-sandbox" }
|
||||||
|
dotenvy = "0.15.7"
|
||||||
|
tokio = { version = "1", features = ["rt-multi-thread"] }
|
||||||
68
codex-rs/arg0/src/lib.rs
Normal file
68
codex-rs/arg0/src/lib.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
use std::future::Future;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
/// While we want to deploy the Codex CLI as a single executable for simplicity,
|
||||||
|
/// we also want to expose some of its functionality as distinct CLIs, so we use
|
||||||
|
/// the "arg0 trick" to determine which CLI to dispatch. This effectively allows
|
||||||
|
/// us to simulate deploying multiple executables as a single binary on Mac and
|
||||||
|
/// Linux (but not Windows).
|
||||||
|
///
|
||||||
|
/// When the current executable is invoked through the hard-link or alias named
|
||||||
|
/// `codex-linux-sandbox` we *directly* execute
|
||||||
|
/// [`codex_linux_sandbox::run_main`] (which never returns). Otherwise we:
|
||||||
|
///
|
||||||
|
/// 1. Use [`dotenvy::from_path`] and [`dotenvy::dotenv`] to modify the
|
||||||
|
/// environment before creating any threads.
|
||||||
|
/// 2. Construct a Tokio multi-thread runtime.
|
||||||
|
/// 3. Derive the path to the current executable (so children can re-invoke the
|
||||||
|
/// sandbox) when running on Linux.
|
||||||
|
/// 4. Execute the provided async `main_fn` inside that runtime, forwarding any
|
||||||
|
/// error. Note that `main_fn` receives `codex_linux_sandbox_exe:
|
||||||
|
/// Option<PathBuf>`, as an argument, which is generally needed as part of
|
||||||
|
/// constructing [`codex_core::config::Config`].
|
||||||
|
///
|
||||||
|
/// This function should be used to wrap any `main()` function in binary crates
|
||||||
|
/// in this workspace that depends on these helper CLIs.
|
||||||
|
pub fn arg0_dispatch_or_else<F, Fut>(main_fn: F) -> anyhow::Result<()>
|
||||||
|
where
|
||||||
|
F: FnOnce(Option<PathBuf>) -> Fut,
|
||||||
|
Fut: Future<Output = anyhow::Result<()>>,
|
||||||
|
{
|
||||||
|
// Determine if we were invoked via the special alias.
|
||||||
|
let argv0 = std::env::args().next().unwrap_or_default();
|
||||||
|
let exe_name = Path::new(&argv0)
|
||||||
|
.file_name()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
|
||||||
|
if exe_name == "codex-linux-sandbox" {
|
||||||
|
// Safety: [`run_main`] never returns.
|
||||||
|
codex_linux_sandbox::run_main();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This modifies the environment, which is not thread-safe, so do this
|
||||||
|
// before creating any threads/the Tokio runtime.
|
||||||
|
load_dotenv();
|
||||||
|
|
||||||
|
// Regular invocation – create a Tokio runtime and execute the provided
|
||||||
|
// async entry-point.
|
||||||
|
let runtime = tokio::runtime::Runtime::new()?;
|
||||||
|
runtime.block_on(async move {
|
||||||
|
let codex_linux_sandbox_exe: Option<PathBuf> = if cfg!(target_os = "linux") {
|
||||||
|
std::env::current_exe().ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
main_fn(codex_linux_sandbox_exe).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load env vars from ~/.codex/.env and `$(pwd)/.env`.
|
||||||
|
fn load_dotenv() {
|
||||||
|
if let Ok(codex_home) = codex_core::config::find_codex_home() {
|
||||||
|
dotenvy::from_path(codex_home.join(".env")).ok();
|
||||||
|
}
|
||||||
|
dotenvy::dotenv().ok();
|
||||||
|
}
|
||||||
@@ -18,12 +18,12 @@ workspace = true
|
|||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
clap_complete = "4"
|
clap_complete = "4"
|
||||||
|
codex-arg0 = { path = "../arg0" }
|
||||||
codex-chatgpt = { path = "../chatgpt" }
|
codex-chatgpt = { path = "../chatgpt" }
|
||||||
codex-core = { path = "../core" }
|
codex-core = { path = "../core" }
|
||||||
codex-common = { path = "../common", features = ["cli"] }
|
codex-common = { path = "../common", features = ["cli"] }
|
||||||
codex-exec = { path = "../exec" }
|
codex-exec = { path = "../exec" }
|
||||||
codex-login = { path = "../login" }
|
codex-login = { path = "../login" }
|
||||||
codex-linux-sandbox = { path = "../linux-sandbox" }
|
|
||||||
codex-mcp-server = { path = "../mcp-server" }
|
codex-mcp-server = { path = "../mcp-server" }
|
||||||
codex-tui = { path = "../tui" }
|
codex-tui = { path = "../tui" }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use clap::CommandFactory;
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use clap_complete::Shell;
|
use clap_complete::Shell;
|
||||||
use clap_complete::generate;
|
use clap_complete::generate;
|
||||||
|
use codex_arg0::arg0_dispatch_or_else;
|
||||||
use codex_chatgpt::apply_command::ApplyCommand;
|
use codex_chatgpt::apply_command::ApplyCommand;
|
||||||
use codex_chatgpt::apply_command::run_apply_command;
|
use codex_chatgpt::apply_command::run_apply_command;
|
||||||
use codex_cli::LandlockCommand;
|
use codex_cli::LandlockCommand;
|
||||||
@@ -92,7 +93,7 @@ struct LoginCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
codex_linux_sandbox::run_with_sandbox(|codex_linux_sandbox_exe| async move {
|
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
|
||||||
cli_main(codex_linux_sandbox_exe).await?;
|
cli_main(codex_linux_sandbox_exe).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ workspace = true
|
|||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
chrono = "0.4.40"
|
chrono = "0.4.40"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
|
codex-arg0 = { path = "../arg0" }
|
||||||
codex-core = { path = "../core" }
|
codex-core = { path = "../core" }
|
||||||
codex-common = { path = "../common", features = [
|
codex-common = { path = "../common", features = [
|
||||||
"cli",
|
"cli",
|
||||||
"elapsed",
|
"elapsed",
|
||||||
"sandbox_summary",
|
"sandbox_summary",
|
||||||
] }
|
] }
|
||||||
codex-linux-sandbox = { path = "../linux-sandbox" }
|
|
||||||
owo-colors = "4.2.0"
|
owo-colors = "4.2.0"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
shlex = "1.3.0"
|
shlex = "1.3.0"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
//! This allows us to ship a completely separate set of functionality as part
|
//! This allows us to ship a completely separate set of functionality as part
|
||||||
//! of the `codex-exec` binary.
|
//! of the `codex-exec` binary.
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use codex_arg0::arg0_dispatch_or_else;
|
||||||
use codex_common::CliConfigOverrides;
|
use codex_common::CliConfigOverrides;
|
||||||
use codex_exec::Cli;
|
use codex_exec::Cli;
|
||||||
use codex_exec::run_main;
|
use codex_exec::run_main;
|
||||||
@@ -24,7 +25,7 @@ struct TopCli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
codex_linux_sandbox::run_with_sandbox(|codex_linux_sandbox_exe| async move {
|
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
|
||||||
let top_cli = TopCli::parse();
|
let top_cli = TopCli::parse();
|
||||||
// Merge root-level overrides into inner CLI struct so downstream logic remains unchanged.
|
// Merge root-level overrides into inner CLI struct so downstream logic remains unchanged.
|
||||||
let mut inner = top_cli.inner;
|
let mut inner = top_cli.inner;
|
||||||
|
|||||||
@@ -14,15 +14,16 @@ path = "src/lib.rs"
|
|||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
codex-common = { path = "../common", features = ["cli"] }
|
codex-common = { path = "../common", features = ["cli"] }
|
||||||
codex-core = { path = "../core" }
|
codex-core = { path = "../core" }
|
||||||
dotenvy = "0.15.7"
|
libc = "0.2.172"
|
||||||
tokio = { version = "1", features = ["rt-multi-thread"] }
|
landlock = "0.4.1"
|
||||||
|
seccompiler = "0.5.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[target.'cfg(target_os = "linux")'.dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
tokio = { version = "1", features = [
|
tokio = { version = "1", features = [
|
||||||
"io-std",
|
"io-std",
|
||||||
@@ -31,8 +32,3 @@ tokio = { version = "1", features = [
|
|||||||
"rt-multi-thread",
|
"rt-multi-thread",
|
||||||
"signal",
|
"signal",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
libc = "0.2.172"
|
|
||||||
landlock = "0.4.1"
|
|
||||||
seccompiler = "0.5.0"
|
|
||||||
|
|||||||
@@ -4,72 +4,11 @@ mod landlock;
|
|||||||
mod linux_run_main;
|
mod linux_run_main;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub use linux_run_main::run_main;
|
pub fn run_main() -> ! {
|
||||||
|
linux_run_main::run_main();
|
||||||
use std::future::Future;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
/// Helper that consolidates the common boilerplate found in several Codex
|
|
||||||
/// binaries (`codex`, `codex-exec`, `codex-tui`) around dispatching to the
|
|
||||||
/// `codex-linux-sandbox` sub-command.
|
|
||||||
///
|
|
||||||
/// When the current executable is invoked through the hard-link or alias
|
|
||||||
/// named `codex-linux-sandbox` we *directly* execute [`run_main`](crate::run_main)
|
|
||||||
/// (which never returns). Otherwise we:
|
|
||||||
/// 1. Construct a Tokio multi-thread runtime.
|
|
||||||
/// 2. Derive the path to the current executable (so children can re-invoke
|
|
||||||
/// the sandbox) when running on Linux.
|
|
||||||
/// 3. Execute the provided async `main_fn` inside that runtime, forwarding
|
|
||||||
/// any error.
|
|
||||||
///
|
|
||||||
/// This function eliminates duplicated code across the various `main.rs`
|
|
||||||
/// entry-points.
|
|
||||||
pub fn run_with_sandbox<F, Fut>(main_fn: F) -> anyhow::Result<()>
|
|
||||||
where
|
|
||||||
F: FnOnce(Option<PathBuf>) -> Fut,
|
|
||||||
Fut: Future<Output = anyhow::Result<()>>,
|
|
||||||
{
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
// Determine if we were invoked via the special alias.
|
|
||||||
let argv0 = std::env::args().next().unwrap_or_default();
|
|
||||||
let exe_name = Path::new(&argv0)
|
|
||||||
.file_name()
|
|
||||||
.and_then(|s| s.to_str())
|
|
||||||
.unwrap_or("");
|
|
||||||
|
|
||||||
if exe_name == "codex-linux-sandbox" {
|
|
||||||
// Safety: [`run_main`] never returns.
|
|
||||||
crate::run_main();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This modifies the environment, which is not thread-safe, so do this
|
|
||||||
// before creating any threads/the Tokio runtime.
|
|
||||||
load_dotenv();
|
|
||||||
|
|
||||||
// Regular invocation – create a Tokio runtime and execute the provided
|
|
||||||
// async entry-point.
|
|
||||||
let runtime = tokio::runtime::Runtime::new()?;
|
|
||||||
runtime.block_on(async move {
|
|
||||||
let codex_linux_sandbox_exe: Option<PathBuf> = if cfg!(target_os = "linux") {
|
|
||||||
std::env::current_exe().ok()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
main_fn(codex_linux_sandbox_exe).await
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
pub fn run_main() -> ! {
|
pub fn run_main() -> ! {
|
||||||
panic!("codex-linux-sandbox is only supported on Linux");
|
panic!("codex-linux-sandbox is only supported on Linux");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load env vars from ~/.codex/.env and `$(pwd)/.env`.
|
|
||||||
fn load_dotenv() {
|
|
||||||
if let Ok(codex_home) = codex_core::config::find_codex_home() {
|
|
||||||
dotenvy::from_path(codex_home.join(".env")).ok();
|
|
||||||
}
|
|
||||||
dotenvy::dotenv().ok();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
codex-arg0 = { path = "../arg0" }
|
||||||
codex-core = { path = "../core" }
|
codex-core = { path = "../core" }
|
||||||
codex-linux-sandbox = { path = "../linux-sandbox" }
|
|
||||||
mcp-types = { path = "../mcp-types" }
|
mcp-types = { path = "../mcp-types" }
|
||||||
schemars = "0.8.22"
|
schemars = "0.8.22"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
use codex_arg0::arg0_dispatch_or_else;
|
||||||
use codex_mcp_server::run_main;
|
use codex_mcp_server::run_main;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
codex_linux_sandbox::run_with_sandbox(|codex_linux_sandbox_exe| async move {
|
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
|
||||||
run_main(codex_linux_sandbox_exe).await?;
|
run_main(codex_linux_sandbox_exe).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ anyhow = "1"
|
|||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
codex-ansi-escape = { path = "../ansi-escape" }
|
codex-ansi-escape = { path = "../ansi-escape" }
|
||||||
|
codex-arg0 = { path = "../arg0" }
|
||||||
codex-core = { path = "../core" }
|
codex-core = { path = "../core" }
|
||||||
codex-common = { path = "../common", features = [
|
codex-common = { path = "../common", features = [
|
||||||
"cli",
|
"cli",
|
||||||
@@ -26,7 +27,6 @@ codex-common = { path = "../common", features = [
|
|||||||
"sandbox_summary",
|
"sandbox_summary",
|
||||||
] }
|
] }
|
||||||
codex-file-search = { path = "../file-search" }
|
codex-file-search = { path = "../file-search" }
|
||||||
codex-linux-sandbox = { path = "../linux-sandbox" }
|
|
||||||
codex-login = { path = "../login" }
|
codex-login = { path = "../login" }
|
||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
crossterm = { version = "0.28.1", features = ["bracketed-paste"] }
|
crossterm = { version = "0.28.1", features = ["bracketed-paste"] }
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use codex_arg0::arg0_dispatch_or_else;
|
||||||
use codex_common::CliConfigOverrides;
|
use codex_common::CliConfigOverrides;
|
||||||
use codex_tui::Cli;
|
use codex_tui::Cli;
|
||||||
use codex_tui::run_main;
|
use codex_tui::run_main;
|
||||||
@@ -13,7 +14,7 @@ struct TopCli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
codex_linux_sandbox::run_with_sandbox(|codex_linux_sandbox_exe| async move {
|
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
|
||||||
let top_cli = TopCli::parse();
|
let top_cli = TopCli::parse();
|
||||||
let mut inner = top_cli.inner;
|
let mut inner = top_cli.inner;
|
||||||
inner
|
inner
|
||||||
|
|||||||
Reference in New Issue
Block a user