mirror of
https://github.com/openai/codex.git
synced 2026-11-02 19:43:24 +00:00
progress
This commit is contained in:
Generated
+1
@@ -586,6 +586,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"codex-core",
|
||||
"codex-exec",
|
||||
"directories",
|
||||
"libc",
|
||||
"nix 0.27.1",
|
||||
|
||||
@@ -32,6 +32,9 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
directories = "5"
|
||||
|
||||
# Re-use the codex-exec library for its CLI definition
|
||||
codex_exec = { package = "codex-exec", path = "../exec" }
|
||||
|
||||
# unix-only process helpers
|
||||
nix = { version = "0.27", optional = true, default-features = false, features = ["process"] }
|
||||
libc = { version = "0.2", optional = true }
|
||||
|
||||
+39
-21
@@ -47,17 +47,13 @@ enum Commands {
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct CreateCmd {
|
||||
/// A custom session identifier. When omitted, a random UUIDv4 is used.
|
||||
/// Session identifier. Generates a random UUIDv4 when omitted.
|
||||
#[arg(long)]
|
||||
id: Option<String>,
|
||||
|
||||
/// Path to the `codex-exec` binary. Defaults to relying on $PATH.
|
||||
#[arg(long, default_value = "codex-exec")]
|
||||
exec: String,
|
||||
|
||||
/// If set, terminate the agent when the CLI process exits ("attached" mode).
|
||||
#[arg(long)]
|
||||
kill_on_drop: bool,
|
||||
/// All flags following `create` are forwarded to `codex-exec`.
|
||||
#[clap(flatten)]
|
||||
exec_cli: codex_exec::Cli,
|
||||
}
|
||||
|
||||
impl CreateCmd {
|
||||
@@ -75,24 +71,46 @@ impl CreateCmd {
|
||||
let paths = store::paths_for(&id)?;
|
||||
store::materialise(&paths, &meta)?;
|
||||
|
||||
// Spawn the background agent and immediately detach.
|
||||
let mut child = spawn::spawn_agent(&self.exec, &id, &paths, self.kill_on_drop)?;
|
||||
// Convert exec_cli back into a Vec<String> so we can forward them verbatim.
|
||||
let exec_args = build_exec_args(&self.exec_cli);
|
||||
|
||||
if self.kill_on_drop {
|
||||
// Hold the handle for the lifetime of the CLI; when we drop at the end of
|
||||
// `run()` the agent will be terminated by the `kill_on_drop` setting.
|
||||
tokio::spawn(async move {
|
||||
let _ = child.wait().await;
|
||||
});
|
||||
}
|
||||
|
||||
// When not in kill_on_drop mode we *immediately* drop the handle so the agent can
|
||||
// outlive us.
|
||||
// Spawn the background agent and immediately detach – we never hold on to the
|
||||
// Child handle.
|
||||
let _child = spawn::spawn_agent(&paths, &exec_args)?;
|
||||
println!("{id}");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-serialize a `codex_exec::Cli` struct back into the exact CLI args.
|
||||
fn build_exec_args(cli: &codex_exec::Cli) -> Vec<String> {
|
||||
let mut args = Vec::new();
|
||||
|
||||
for path in &cli.images {
|
||||
args.push("--image".to_string());
|
||||
args.push(path.to_string_lossy().into_owned());
|
||||
}
|
||||
|
||||
if let Some(model) = &cli.model {
|
||||
args.push("--model".to_string());
|
||||
args.push(model.clone());
|
||||
}
|
||||
|
||||
if cli.skip_git_repo_check {
|
||||
args.push("--skip-git-repo-check".to_string());
|
||||
}
|
||||
|
||||
if cli.disable_response_storage {
|
||||
args.push("--disable-response-storage".to_string());
|
||||
}
|
||||
|
||||
if let Some(prompt) = &cli.prompt {
|
||||
args.push(prompt.clone());
|
||||
}
|
||||
|
||||
args
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// delete
|
||||
|
||||
@@ -141,7 +159,7 @@ impl LogsCmd {
|
||||
let file = tokio::fs::File::open(target).await?;
|
||||
|
||||
if self.follow {
|
||||
let mut reader = tokio::io::BufReader::new(file);
|
||||
let reader = tokio::io::BufReader::new(file);
|
||||
let mut lines = reader.lines();
|
||||
while let Some(line) = lines.next_line().await? {
|
||||
println!("{line}");
|
||||
|
||||
@@ -1,80 +1,75 @@
|
||||
//! Cross-platform helper to spawn a detached `codex-exec` agent.
|
||||
//! Cross-platform helper to spawn a fully-detached `codex-exec` process.
|
||||
|
||||
use crate::store::Paths;
|
||||
use anyhow::{Context, Result};
|
||||
use std::fs::OpenOptions;
|
||||
use tokio::process::{Child, Command};
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn spawn_agent(exec: &str, id: &str, paths: &Paths, kill_on_drop: bool) -> Result<Child> {
|
||||
use std::io;
|
||||
/// Spawn `codex-exec` with `exec_args`, redirecting stdio to the per-session log files and
|
||||
/// detaching the process group so it survives the parent CLI.
|
||||
pub fn spawn_agent(paths: &Paths, exec_args: &[String]) -> Result<Child> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::io;
|
||||
|
||||
// Prepare stdio handles first.
|
||||
let stdin = OpenOptions::new().read(true).open("/dev/null")?;
|
||||
let stdout = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&paths.stdout)?;
|
||||
let stderr = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&paths.stderr)?;
|
||||
// Prepare stdio handles first.
|
||||
let stdin = OpenOptions::new().read(true).open("/dev/null")?;
|
||||
let stdout = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&paths.stdout)?;
|
||||
let stderr = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&paths.stderr)?;
|
||||
|
||||
let mut cmd = Command::new(exec);
|
||||
cmd.arg("--job").arg(id)
|
||||
.stdin(stdin)
|
||||
.stdout(stdout)
|
||||
.stderr(stderr);
|
||||
let mut cmd = Command::new("codex-exec");
|
||||
cmd.args(exec_args)
|
||||
.stdin(stdin)
|
||||
.stdout(stdout)
|
||||
.stderr(stderr);
|
||||
|
||||
if kill_on_drop {
|
||||
cmd.kill_on_drop(true);
|
||||
}
|
||||
|
||||
// Detach: make a new session and ignore SIGHUP.
|
||||
unsafe {
|
||||
cmd.pre_exec(|| {
|
||||
unsafe {
|
||||
// setsid(2)
|
||||
// Detach from the controlling terminal: setsid + ignore SIGHUP.
|
||||
// SAFETY: calling an `unsafe` method (`pre_exec`). Runs in the parent process right
|
||||
// before fork; the closure then executes in the child.
|
||||
unsafe {
|
||||
cmd.pre_exec(|| {
|
||||
if libc::setsid() == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
libc::signal(libc::SIGHUP, libc::SIG_IGN);
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
let child = cmd.spawn().context("failed to spawn codex-exec")?;
|
||||
return Ok(child);
|
||||
}
|
||||
|
||||
let child = cmd.spawn().context("failed to spawn agent")?;
|
||||
Ok(child)
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn spawn_agent(exec: &str, id: &str, paths: &Paths, kill_on_drop: bool) -> Result<Child> {
|
||||
use std::os::windows::process::CommandExt;
|
||||
const DETACHED_PROCESS: u32 = 0x00000008;
|
||||
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
|
||||
|
||||
const DETACHED_PROCESS: u32 = 0x00000008;
|
||||
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
|
||||
let stdout = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&paths.stdout)?;
|
||||
let stderr = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&paths.stderr)?;
|
||||
|
||||
let stdout = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&paths.stdout)?;
|
||||
let stderr = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&paths.stderr)?;
|
||||
let mut cmd = Command::new("codex-exec");
|
||||
cmd.args(exec_args)
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(stdout)
|
||||
.stderr(stderr)
|
||||
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP);
|
||||
|
||||
let mut cmd = Command::new(exec);
|
||||
cmd.arg("--job").arg(id)
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(stdout)
|
||||
.stderr(stderr)
|
||||
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP);
|
||||
|
||||
if kill_on_drop {
|
||||
cmd.kill_on_drop(true);
|
||||
let child = cmd.spawn().context("failed to spawn codex-exec")?;
|
||||
return Ok(child);
|
||||
}
|
||||
|
||||
let child = cmd.spawn().context("failed to spawn agent")?;
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user