2025-07-28 08:31:24 -07:00
|
|
|
|
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.
|
fix: support special --codex-run-as-apply-patch arg (#1702)
This introduces some special behavior to the CLIs that are using the
`codex-arg0` crate where if `arg1` is `--codex-run-as-apply-patch`, then
it will run as if `apply_patch arg2` were invoked. This is important
because it means we can do things like:
```
SANDBOX_TYPE=landlock # or seatbelt for macOS
codex debug "${SANDBOX_TYPE}" -- codex --codex-run-as-apply-patch PATCH
```
which gives us a way to run `apply_patch` while ensuring it adheres to
the sandbox the user specified.
While it would be nice to use the `arg0` trick like we are currently
doing for `codex-linux-sandbox`, there is no way to specify the `arg0`
for the underlying command when running under `/usr/bin/sandbox-exec`,
so it will not work for us in this case.
Admittedly, we could have also supported this via a custom environment
variable (e.g., `CODEX_ARG0`), but since environment variables are
inherited by child processes, that seemed like a potentially leakier
abstraction.
This change, as well as our existing reliance on checking `arg0`, place
additional requirements on those who include `codex-core`. Its
`README.md` has been updated to reflect this.
While we could have just added an `apply-patch` subcommand to the
`codex` multitool CLI, that would not be sufficient for the standalone
`codex-exec` CLI, which is something that we distribute as part of our
GitHub releases for those who know they will not be using the TUI and
therefore prefer to use a slightly smaller executable:
https://github.com/openai/codex/releases/tag/rust-v0.10.0
To that end, this PR adds an integration test to ensure that the
`--codex-run-as-apply-patch` option works with the standalone
`codex-exec` CLI.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/1702).
* #1705
* #1703
* __->__ #1702
* #1698
* #1697
2025-07-28 09:26:44 -07:00
|
|
|
|
let mut args = std::env::args_os();
|
|
|
|
|
|
let argv0 = args.next().unwrap_or_default();
|
2025-07-28 08:31:24 -07:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
fix: support special --codex-run-as-apply-patch arg (#1702)
This introduces some special behavior to the CLIs that are using the
`codex-arg0` crate where if `arg1` is `--codex-run-as-apply-patch`, then
it will run as if `apply_patch arg2` were invoked. This is important
because it means we can do things like:
```
SANDBOX_TYPE=landlock # or seatbelt for macOS
codex debug "${SANDBOX_TYPE}" -- codex --codex-run-as-apply-patch PATCH
```
which gives us a way to run `apply_patch` while ensuring it adheres to
the sandbox the user specified.
While it would be nice to use the `arg0` trick like we are currently
doing for `codex-linux-sandbox`, there is no way to specify the `arg0`
for the underlying command when running under `/usr/bin/sandbox-exec`,
so it will not work for us in this case.
Admittedly, we could have also supported this via a custom environment
variable (e.g., `CODEX_ARG0`), but since environment variables are
inherited by child processes, that seemed like a potentially leakier
abstraction.
This change, as well as our existing reliance on checking `arg0`, place
additional requirements on those who include `codex-core`. Its
`README.md` has been updated to reflect this.
While we could have just added an `apply-patch` subcommand to the
`codex` multitool CLI, that would not be sufficient for the standalone
`codex-exec` CLI, which is something that we distribute as part of our
GitHub releases for those who know they will not be using the TUI and
therefore prefer to use a slightly smaller executable:
https://github.com/openai/codex/releases/tag/rust-v0.10.0
To that end, this PR adds an integration test to ensure that the
`--codex-run-as-apply-patch` option works with the standalone
`codex-exec` CLI.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/1702).
* #1705
* #1703
* __->__ #1702
* #1698
* #1697
2025-07-28 09:26:44 -07:00
|
|
|
|
let argv1 = args.next().unwrap_or_default();
|
|
|
|
|
|
if argv1 == "--codex-run-as-apply-patch" {
|
|
|
|
|
|
let patch_arg = args.next().and_then(|s| s.to_str().map(|s| s.to_owned()));
|
|
|
|
|
|
let exit_code = match patch_arg {
|
|
|
|
|
|
Some(patch_arg) => {
|
|
|
|
|
|
let mut stdout = std::io::stdout();
|
|
|
|
|
|
let mut stderr = std::io::stderr();
|
|
|
|
|
|
match codex_apply_patch::apply_patch(&patch_arg, &mut stdout, &mut stderr) {
|
|
|
|
|
|
Ok(()) => 0,
|
|
|
|
|
|
Err(_) => 1,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
None => {
|
|
|
|
|
|
eprintln!("Error: --codex-run-as-apply-patch requires a UTF-8 PATCH argument.");
|
|
|
|
|
|
1
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
std::process::exit(exit_code);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 08:31:24 -07:00
|
|
|
|
// 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();
|
|
|
|
|
|
}
|