diff --git a/cli/src/connection.rs b/cli/src/connection.rs index 0079f85..dc4de8c 100644 --- a/cli/src/connection.rs +++ b/cli/src/connection.rs @@ -159,7 +159,13 @@ fn is_daemon_running(session: &str) -> bool { if let Ok(pid_str) = fs::read_to_string(&pid_path) { if let Ok(pid) = pid_str.trim().parse::() { unsafe { - return libc::kill(pid, 0) == 0; + if libc::kill(pid, 0) == 0 { + return true; + } + // EPERM means the process exists but we lack permission to + // signal it (e.g. inside a macOS sandbox). Only ESRCH means + // the process is genuinely gone. + return std::io::Error::last_os_error().raw_os_error() != Some(libc::ESRCH); } } } diff --git a/cli/src/main.rs b/cli/src/main.rs index 4df940e..c56a423 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -170,7 +170,11 @@ fn run_session(args: &[String], session: &str, json_mode: bool) { if let Ok(pid_str) = fs::read_to_string(&pid_path) { if let Ok(pid) = pid_str.trim().parse::() { #[cfg(unix)] - let running = unsafe { libc::kill(pid as i32, 0) == 0 }; + let running = unsafe { + libc::kill(pid as i32, 0) == 0 + || std::io::Error::last_os_error().raw_os_error() + != Some(libc::ESRCH) + }; #[cfg(windows)] let running = unsafe { let handle = diff --git a/src/daemon.ts b/src/daemon.ts index c47a7ff..c5faff4 100644 --- a/src/daemon.ts +++ b/src/daemon.ts @@ -262,7 +262,12 @@ export function isDaemonRunning(session?: string): boolean { // Check if process exists (works on both Unix and Windows) process.kill(pid, 0); return true; - } catch { + } catch (err: unknown) { + // EPERM means the process exists but we lack permission to signal it + // (e.g. caller is inside a macOS sandbox). Only ESRCH means it's gone. + if (err instanceof Error && (err as NodeJS.ErrnoException).code === 'EPERM') { + return true; + } // Process doesn't exist, clean up stale files cleanupSocket(session); return false;