UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

96 lines (95 loc) 2.71 kB
import { spawn } from "node:child_process"; //#region packages/agent-core/src/harness/env/kill-tree.ts const DEFAULT_GRACE_MS = 3e3; const MAX_GRACE_MS = 6e4; /** * Best-effort process-tree termination with graceful shutdown. * - Windows: use taskkill /T to include descendants. Sends SIGTERM-equivalent * first (without /F), then force-kills if process survives. * - Unix: send SIGTERM to process group first, wait grace period, then SIGKILL. * * When the child was spawned with `detached: false`, pass `detached: false` to * skip the Unix `process.kill(-pid, ...)` group-kill. That avoids signaling the * gateway's own process group. */ function killProcessTree(pid, opts) { if (!Number.isFinite(pid) || pid <= 0) return; if (process.platform === "win32") { if (opts?.force === true) { signalProcessTreeWindows(pid, "SIGKILL"); return; } killProcessTreeWindows(pid, normalizeGraceMs(opts?.graceMs)); return; } const useGroupKill = opts?.detached !== false; if (opts?.force === true) { signalProcessTreeUnix(pid, "SIGKILL", useGroupKill); return; } const graceMs = normalizeGraceMs(opts?.graceMs); signalProcessTreeUnix(pid, "SIGTERM", useGroupKill); setTimeout(() => { if (!(useGroupKill ? isProcessAlive(-pid) || isProcessAlive(pid) : isProcessAlive(pid))) return; signalProcessTreeUnix(pid, "SIGKILL", useGroupKill); }, graceMs).unref(); } function signalProcessTree(pid, signal, opts) { if (!Number.isFinite(pid) || pid <= 0) return; if (process.platform === "win32") { signalProcessTreeWindows(pid, signal); return; } signalProcessTreeUnix(pid, signal, opts?.detached !== false); } function normalizeGraceMs(value) { if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_GRACE_MS; return Math.max(0, Math.min(MAX_GRACE_MS, Math.floor(value))); } function isProcessAlive(pid) { try { process.kill(pid, 0); return true; } catch { return false; } } function signalProcessTreeUnix(pid, signal, useGroupKill) { if (useGroupKill) try { process.kill(-pid, signal); return; } catch {} try { process.kill(pid, signal); } catch {} } function runTaskkill(args) { try { spawn("taskkill", args, { stdio: "ignore", detached: true, windowsHide: true }); } catch {} } function killProcessTreeWindows(pid, graceMs) { signalProcessTreeWindows(pid, "SIGTERM"); setTimeout(() => { if (!isProcessAlive(pid)) return; signalProcessTreeWindows(pid, "SIGKILL"); }, graceMs).unref(); } function signalProcessTreeWindows(pid, signal) { runTaskkill(signal === "SIGKILL" ? [ "/F", "/T", "/PID", String(pid) ] : [ "/T", "/PID", String(pid) ]); } //#endregion export { signalProcessTree as n, killProcessTree as t };