fix(gateway): detect launchd supervision via XPC_SERVICE_NAME

On macOS, launchd sets XPC_SERVICE_NAME on managed processes but does
not set LAUNCH_JOB_LABEL or LAUNCH_JOB_NAME. Without checking
XPC_SERVICE_NAME, isLikelySupervisedProcess() returns false for
launchd-managed gateways, causing restartGatewayProcessWithFreshPid()
to fork a detached child instead of returning "supervised". The
detached child holds the gateway lock while launchd simultaneously
respawns the original process (KeepAlive=true), leading to an infinite
lock-timeout / restart loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dimatu
2026-02-19 02:54:18 +00:00
committed by Peter Steinberger
parent f84adcbe88
commit cf796e2a22
2 changed files with 11 additions and 0 deletions

View File

@@ -108,6 +108,16 @@ describe("restartGatewayProcessWithFreshPid", () => {
expect(spawnMock).not.toHaveBeenCalled();
});
it("returns supervised when XPC_SERVICE_NAME is set by launchd", () => {
clearSupervisorHints();
setPlatform("darwin");
process.env.XPC_SERVICE_NAME = "ai.openclaw.gateway";
const result = restartGatewayProcessWithFreshPid();
expect(result.mode).toBe("supervised");
expect(triggerOpenClawRestartMock).not.toHaveBeenCalled();
expect(spawnMock).not.toHaveBeenCalled();
});
it("spawns detached child with current exec argv", () => {
delete process.env.OPENCLAW_NO_RESPAWN;
clearSupervisorHints();

View File

@@ -1,6 +1,7 @@
const LAUNCHD_SUPERVISOR_HINT_ENV_VARS = [
"LAUNCH_JOB_LABEL",
"LAUNCH_JOB_NAME",
"XPC_SERVICE_NAME",
"OPENCLAW_LAUNCHD_LABEL",
] as const;