mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-26 10:13:27 +08:00
* fix: preserve standalone CLI commands across startup respawns Resolve launcher-bound runtime arguments at the CLI invocation owner. Consume a one-child parent PID signal in compiled runtimes while preserving the loose preload and detached worker lifetimes. * test: keep respawn transport guard independent of local names * test: include respawn owner dependencies in CLI fixtures
53 lines
1.9 KiB
JavaScript
53 lines
1.9 KiB
JavaScript
import { GATEWAY_SHUTDOWN_RESERVE_MS } from "./gateway-shutdown-budget.mjs";
|
|
|
|
const parentKey = Symbol.for("openclaw.cliRespawnParent");
|
|
export const CLI_RESPAWN_PARENT_ENV = "OPENCLAW_CLI_RESPAWN_PARENT";
|
|
// Compiled executables cannot run a preload. Consume its one-child equivalent
|
|
// before any detached worker can inherit it, and reject a different parent.
|
|
const inheritedParent = process.env[CLI_RESPAWN_PARENT_ENV];
|
|
delete process.env[CLI_RESPAWN_PARENT_ENV];
|
|
if (inheritedParent === String(process.ppid)) {
|
|
globalThis[parentKey] = { pid: process.ppid };
|
|
}
|
|
// Consume the private preload so later detached workers cannot inherit this contract.
|
|
export const CLI_PARENT_BOUND_PRELOAD = `data:text/javascript,${encodeURIComponent(`
|
|
globalThis[Symbol.for("openclaw.cliRespawnParent")] = { pid: process.ppid };
|
|
process.execArgv = process.execArgv.filter((arg, index, args) =>
|
|
arg !== import.meta.url && !(arg === "--import" && args[index + 1] === import.meta.url));
|
|
`)}`;
|
|
|
|
/** Only the respawn producer opts in; detached update drivers keep independent custody. */
|
|
export function getCliParentPid() {
|
|
return globalThis[parentKey]?.pid;
|
|
}
|
|
|
|
let terminating = false;
|
|
export function requestCliParentTermination(
|
|
signal = () => {
|
|
if (!process.emit("SIGTERM")) {
|
|
process.kill(process.pid, "SIGTERM");
|
|
}
|
|
},
|
|
) {
|
|
if (terminating) {
|
|
return;
|
|
}
|
|
terminating = true;
|
|
// Doctor's signal gate cancels work and settles rollback/restoration before exit.
|
|
setTimeout(() => process.kill(process.pid, "SIGKILL"), GATEWAY_SHUTDOWN_RESERVE_MS).unref();
|
|
signal();
|
|
}
|
|
|
|
export function watchCliRespawnParent() {
|
|
const parent = globalThis[parentKey];
|
|
if (!parent || parent.watching || !process.channel) {
|
|
return;
|
|
}
|
|
parent.watching = true;
|
|
process.once("disconnect", requestCliParentTermination);
|
|
process.channel.unref();
|
|
if (!process.connected) {
|
|
requestCliParentTermination();
|
|
}
|
|
}
|