fix(infra): land #29078 from @cathrynlavery with restart fallback

Co-authored-by: Cathryn Lavery <cathryn@littlemight.com>
This commit is contained in:
Peter Steinberger
2026-02-27 22:04:46 +00:00
parent db67492a00
commit 4aa2dc6857
3 changed files with 31 additions and 52 deletions

View File

@@ -1,4 +1,5 @@
import { spawn } from "node:child_process";
import { triggerOpenClawRestart } from "./restart.js";
import { hasSupervisorHint } from "./supervisor-markers.js";
type RespawnMode = "spawned" | "supervised" | "disabled" | "failed";
@@ -21,29 +22,6 @@ function isLikelySupervisedProcess(env: NodeJS.ProcessEnv = process.env): boolea
return hasSupervisorHint(env);
}
/**
* Spawn a detached `launchctl kickstart -k` to force an immediate launchd
* restart, bypassing ThrottleInterval. The -k flag sends SIGTERM to the
* current process, so this MUST be non-blocking (spawn, not spawnSync) to
* avoid deadlocking — the gateway needs to be free to handle the signal
* and exit so launchd can start the replacement.
*/
function schedulelaunchdKickstart(label: string): boolean {
const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
const target = uid !== undefined ? `gui/${uid}/${label}` : label;
try {
const child = spawn("launchctl", ["kickstart", "-k", target], {
detached: true,
stdio: "ignore",
});
child.on("error", () => {}); // best-effort; suppress uncaught error event
child.unref();
return true;
} catch {
return false;
}
}
/**
* Attempt to restart this process with a fresh PID.
* - supervised environments (launchd/systemd): caller should exit and let supervisor restart
@@ -55,10 +33,16 @@ export function restartGatewayProcessWithFreshPid(): GatewayRespawnResult {
return { mode: "disabled" };
}
if (isLikelySupervisedProcess(process.env)) {
// On macOS under launchd, fire a detached kickstart so launchd restarts
// us immediately instead of waiting for ThrottleInterval (up to 60s).
// On macOS under launchd, actively kickstart the supervised service to
// bypass ThrottleInterval delays for intentional restarts.
if (process.platform === "darwin" && process.env.OPENCLAW_LAUNCHD_LABEL?.trim()) {
schedulelaunchdKickstart(process.env.OPENCLAW_LAUNCHD_LABEL.trim());
const restart = triggerOpenClawRestart();
if (!restart.ok) {
return {
mode: "failed",
detail: restart.detail ?? "launchctl kickstart failed",
};
}
}
return { mode: "supervised" };
}