Historically, Codex CLI has treated `apply_patch` (and its sometimes misspelling, `applypatch`) as a "virtual CLI," intercepting it when it appears as the first arg to `command` for the `"container.exec", `"shell"`, or `"local_shell"` tools. This approach has a known limitation where if, say, the model created a Python script that runs `apply_patch` and then tried to run the Python script, we have no insight as to what the model is trying to do and the Python Script would fail because `apply_patch` was never really on the `PATH`. One way to solve this problem is to require users to install an `apply_patch` executable alongside the `codex` executable (or at least put it someplace where Codex can discover it). Though to keep Codex CLI as a standalone executable, we exploit "the arg0 trick" where we create a temporary directory with an entry named `apply_patch` and prepend that directory to the `PATH` for the duration of the invocation of Codex. - On UNIX, `apply_patch` is a symlink to `codex`, which now changes its behavior to behave like `apply_patch` if arg0 is `apply_patch` (or `applypatch`) - On Windows, `apply_patch.bat` is a batch script that runs `codex --codex-run-as-apply-patch %*`, as Codex also changes its behavior if the first argument is `--codex-run-as-apply-patch`.
60 lines
1.8 KiB
Rust
60 lines
1.8 KiB
Rust
use std::io::Read;
|
|
use std::io::Write;
|
|
|
|
pub fn main() -> ! {
|
|
let exit_code = run_main();
|
|
std::process::exit(exit_code);
|
|
}
|
|
|
|
/// We would prefer to return `std::process::ExitCode`, but its `exit_process()`
|
|
/// method is still a nightly API and we want main() to return !.
|
|
pub fn run_main() -> i32 {
|
|
// Expect either one argument (the full apply_patch payload) or read it from stdin.
|
|
let mut args = std::env::args_os();
|
|
let _argv0 = args.next();
|
|
|
|
let patch_arg = match args.next() {
|
|
Some(arg) => match arg.into_string() {
|
|
Ok(s) => s,
|
|
Err(_) => {
|
|
eprintln!("Error: apply_patch requires a UTF-8 PATCH argument.");
|
|
return 1;
|
|
}
|
|
},
|
|
None => {
|
|
// No argument provided; attempt to read the patch from stdin.
|
|
let mut buf = String::new();
|
|
match std::io::stdin().read_to_string(&mut buf) {
|
|
Ok(_) => {
|
|
if buf.is_empty() {
|
|
eprintln!("Usage: apply_patch 'PATCH'\n echo 'PATCH' | apply-patch");
|
|
return 2;
|
|
}
|
|
buf
|
|
}
|
|
Err(err) => {
|
|
eprintln!("Error: Failed to read PATCH from stdin.\n{err}");
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Refuse extra args to avoid ambiguity.
|
|
if args.next().is_some() {
|
|
eprintln!("Error: apply_patch accepts exactly one argument.");
|
|
return 2;
|
|
}
|
|
|
|
let mut stdout = std::io::stdout();
|
|
let mut stderr = std::io::stderr();
|
|
match crate::apply_patch(&patch_arg, &mut stdout, &mut stderr) {
|
|
Ok(()) => {
|
|
// Flush to ensure output ordering when used in pipelines.
|
|
let _ = stdout.flush();
|
|
0
|
|
}
|
|
Err(_) => 1,
|
|
}
|
|
}
|