This commit is contained in:
Ryan Ragona
2025-04-26 07:05:19 -07:00
parent abf0198a49
commit b41f26f484
4 changed files with 137 additions and 22 deletions
+36 -12
View File
@@ -62,21 +62,21 @@ impl CreateCmd {
.id
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
// Persist basic metadata & directory skeleton *before* spawning the process.
let meta = store::SessionMeta {
id: id.clone(),
created_at: chrono::Utc::now(),
};
let paths = store::paths_for(&id)?;
store::materialise(&paths, &meta)?;
store::prepare_dirs(&paths)?;
// Convert exec_cli back into a Vec<String> so we can forward them verbatim.
let exec_args = build_exec_args(&self.exec_cli);
// Spawn the background agent and immediately detach – we never hold on to the
// Child handle.
let _child = spawn::spawn_agent(&paths, &exec_args)?;
// Spawn the background agent and immediately detach.
let child = spawn::spawn_agent(&paths, &exec_args)?;
// Record metadata (with PID) *after* successful spawn.
let meta = store::SessionMeta {
id: id.clone(),
pid: child.id().unwrap_or_default(),
created_at: chrono::Utc::now(),
};
store::write_meta(&paths, &meta)?;
println!("{id}");
Ok(())
}
@@ -200,10 +200,34 @@ pub struct ListCmd;
impl ListCmd {
pub async fn run(self) -> Result<()> {
use comfy_table::{Cell, Table};
use sysinfo::{SystemExt, Pid, PidExt};
let sessions = store::list_sessions()?;
let mut sys = sysinfo::System::new_all();
sys.refresh_processes();
let mut table = Table::new();
table.set_header(["ID", "PID", "STATUS", "CREATED"]);
for meta in sessions {
println!("{}\t{}", meta.id, meta.created_at);
let status: &str = if meta.pid == 0 {
"unknown"
} else if sys.process(Pid::from_u32(meta.pid)).is_some() {
"running"
} else {
"exited"
};
table.add_row([
Cell::new(&meta.id),
Cell::new(meta.pid),
Cell::new(status),
Cell::new(meta.created_at.to_rfc3339()),
]);
}
println!("{table}");
Ok(())
}
}
+11 -9
View File
@@ -1,7 +1,6 @@
//! Session bookkeeping – on-disk layout and simple helpers.
use anyhow::{Context, Result};
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
@@ -25,32 +24,35 @@ pub fn paths_for(id: &str) -> Result<Paths> {
}
fn base_dir() -> Result<PathBuf> {
let dirs = ProjectDirs::from("dev", "codex", "codex-session")
.context("unable to resolve data directory")?;
Ok(dirs.data_dir().to_owned())
// ~/.codex/sessions
let home = dirs::home_dir().context("could not resolve home directory")?;
Ok(home.join("codex").join("sessions"))
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SessionMeta {
pub id: String,
pub pid: u32,
pub created_at: chrono::DateTime<chrono::Utc>,
}
/// Create the on-disk directory structure and write metadata + empty log files.
pub fn materialise(paths: &Paths, meta: &SessionMeta) -> Result<()> {
/// Create directory & empty log files. Does **not** write metadata; caller should write that
/// once the child process has actually been spawned so we can record its PID.
pub fn prepare_dirs(paths: &Paths) -> Result<()> {
std::fs::create_dir_all(&paths.dir)?;
// Metadata (pretty-printed for manual inspection).
std::fs::write(&paths.meta, serde_json::to_vec_pretty(meta)?)?;
// Touch stdout/stderr so they exist even before the agent writes.
for p in [&paths.stdout, &paths.stderr] {
std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(p)?;
}
Ok(())
}
pub fn write_meta(paths: &Paths, meta: &SessionMeta) -> Result<()> {
std::fs::write(&paths.meta, serde_json::to_vec_pretty(meta)?)?;
Ok(())
}