From 3bd331b97f2b6012c551d6eb04710fce5d473835 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sun, 11 Jan 2026 10:08:08 -0600 Subject: [PATCH] cleanup --- cli/src/connection.rs | 62 +++++++++++++++++++++++++++++++++++-------- src/daemon.ts | 19 +++++++++++++ 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/cli/src/connection.rs b/cli/src/connection.rs index bbb8d2c..bc50d32 100644 --- a/cli/src/connection.rs +++ b/cli/src/connection.rs @@ -172,20 +172,60 @@ pub fn ensure_daemon(session: &str, headed: bool) -> Result<(), String> { .find(|p| p.exists()) .ok_or("Daemon not found. Run from project directory or ensure daemon.js is alongside binary.")?; - let mut cmd = Command::new("node"); - cmd.arg(daemon_path) - .env("AGENT_BROWSER_DAEMON", "1") - .env("AGENT_BROWSER_SESSION", session); + // Spawn daemon as a fully detached background process + #[cfg(unix)] + { + use std::os::unix::process::CommandExt; + + let mut cmd = Command::new("node"); + cmd.arg(daemon_path) + .env("AGENT_BROWSER_DAEMON", "1") + .env("AGENT_BROWSER_SESSION", session); - if headed { - cmd.env("AGENT_BROWSER_HEADED", "1"); + if headed { + cmd.env("AGENT_BROWSER_HEADED", "1"); + } + + // Create new process group and session to fully detach + unsafe { + cmd.pre_exec(|| { + // Create new session (detach from terminal) + libc::setsid(); + Ok(()) + }); + } + + cmd.stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .map_err(|e| format!("Failed to start daemon: {}", e))?; } - cmd.stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn() - .map_err(|e| format!("Failed to start daemon: {}", e))?; + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + + let mut cmd = Command::new("node"); + cmd.arg(daemon_path) + .env("AGENT_BROWSER_DAEMON", "1") + .env("AGENT_BROWSER_SESSION", session); + + if headed { + cmd.env("AGENT_BROWSER_HEADED", "1"); + } + + // CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + const DETACHED_PROCESS: u32 = 0x00000008; + + cmd.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .map_err(|e| format!("Failed to start daemon: {}", e))?; + } for _ in 0..50 { if daemon_ready(session) { diff --git a/src/daemon.ts b/src/daemon.ts index e3b5fec..27c241a 100644 --- a/src/daemon.ts +++ b/src/daemon.ts @@ -228,6 +228,25 @@ export async function startDaemon(): Promise { process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); + process.on('SIGHUP', shutdown); + + // Handle unexpected errors - always cleanup + process.on('uncaughtException', (err) => { + console.error('Uncaught exception:', err); + cleanupSocket(); + process.exit(1); + }); + + process.on('unhandledRejection', (reason) => { + console.error('Unhandled rejection:', reason); + cleanupSocket(); + process.exit(1); + }); + + // Cleanup on normal exit + process.on('exit', () => { + cleanupSocket(); + }); // Keep process alive process.stdin.resume();