use crate::agent::OnSpawnHook;
use crate::process_store::{ProcessEntry, ProcessStore};
use std::sync::Arc;
pub struct RegisterOptions<'a> {
pub provider: &'a str,
pub model: &'a str,
pub command: &'a str,
pub prompt_preview: Option<&'a str>,
pub session_id: Option<&'a str>,
pub session_name: Option<&'a str>,
pub root: Option<&'a str>,
}
#[derive(Debug, Clone, Default)]
pub struct RegisterOptionsOwned {
pub provider: String,
pub model: String,
pub command: String,
pub prompt_preview: Option<String>,
pub session_id: Option<String>,
pub session_name: Option<String>,
pub root: Option<String>,
}
impl RegisterOptionsOwned {
pub(crate) fn as_borrowed(&self) -> RegisterOptions<'_> {
RegisterOptions {
provider: &self.provider,
model: &self.model,
command: &self.command,
prompt_preview: self.prompt_preview.as_deref(),
session_id: self.session_id.as_deref(),
session_name: self.session_name.as_deref(),
root: self.root.as_deref(),
}
}
}
pub struct ProcessRegistration {
proc_id: String,
env_vars: Vec<(String, String)>,
}
impl ProcessRegistration {
pub fn proc_id(&self) -> &str {
&self.proc_id
}
pub fn env_vars(&self) -> &[(String, String)] {
&self.env_vars
}
pub fn on_spawn_hook(&self) -> OnSpawnHook {
let proc_id = self.proc_id.clone();
Arc::new(move |pid: u32| {
if let Ok(mut pstore) = ProcessStore::load() {
pstore.update_pid(&proc_id, pid);
let _ = pstore.save();
}
})
}
pub fn update_status(&self, status: &str, exit_code: Option<i32>) {
if let Ok(mut pstore) = ProcessStore::load() {
pstore.update_status(&self.proc_id, status, exit_code);
let _ = pstore.save();
}
}
}
pub(crate) fn build_env_vars(proc_id: &str, opts: &RegisterOptions<'_>) -> Vec<(String, String)> {
let mut env_vars = Vec::with_capacity(6);
if let Some(sid) = opts.session_id {
env_vars.push(("ZAG_SESSION_ID".to_string(), sid.to_string()));
}
env_vars.push(("ZAG_PROCESS_ID".to_string(), proc_id.to_string()));
env_vars.push(("ZAG_PROVIDER".to_string(), opts.provider.to_string()));
env_vars.push(("ZAG_MODEL".to_string(), opts.model.to_string()));
if let Some(r) = opts.root {
env_vars.push(("ZAG_ROOT".to_string(), r.to_string()));
}
if let Some(name) = opts.session_name {
env_vars.push(("ZAG_SESSION_NAME".to_string(), name.to_string()));
}
env_vars
}
pub(crate) fn build_entry(
proc_id: &str,
opts: &RegisterOptions<'_>,
parent_process_id: Option<String>,
parent_session_id: Option<String>,
) -> ProcessEntry {
ProcessEntry {
id: proc_id.to_string(),
pid: std::process::id(),
session_id: opts.session_id.map(String::from),
provider: opts.provider.to_string(),
model: opts.model.to_string(),
command: opts.command.to_string(),
prompt: opts.prompt_preview.map(String::from),
started_at: chrono::Utc::now().to_rfc3339(),
status: "running".to_string(),
exit_code: None,
exited_at: None,
root: opts.root.map(String::from),
parent_process_id,
parent_session_id,
}
}
pub fn register(opts: RegisterOptions<'_>) -> ProcessRegistration {
let proc_id = uuid::Uuid::new_v4().to_string();
let parent_process_id = std::env::var("ZAG_PROCESS_ID").ok();
let parent_session_id = std::env::var("ZAG_SESSION_ID").ok();
let entry = build_entry(&proc_id, &opts, parent_process_id, parent_session_id);
let env_vars = build_env_vars(&proc_id, &opts);
if let Ok(mut pstore) = ProcessStore::load() {
pstore.add(entry);
let _ = pstore.save();
}
ProcessRegistration { proc_id, env_vars }
}
#[cfg(test)]
#[path = "process_registration_tests.rs"]
mod tests;