fix(daemon): keep launchd KeepAlive while preserving restart hardening

This commit is contained in:
Gustavo Madeira Santana
2026-02-26 02:52:00 -05:00
parent b975711429
commit 4ebefe647a
3 changed files with 6 additions and 9 deletions

View File

@@ -10,7 +10,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Daemon/macOS launchd: forward proxy env vars into supervised service environments, switch LaunchAgent keepalive policy to crash-only with throttling, and harden restart sequencing to `print -> bootout -> wait old pid exit -> bootstrap -> kickstart`. (#27276) thanks @frankekn.
- Daemon/macOS launchd: forward proxy env vars into supervised service environments, keep LaunchAgent `KeepAlive=true` semantics, and harden restart sequencing to `print -> bootout -> wait old pid exit -> bootstrap -> kickstart`. (#27276) thanks @frankekn.
- Android/Node invoke: remove native gateway WebSocket `Origin` header to avoid false origin rejections, unify invoke command registry/policy/error parsing paths, and keep command availability checks centralized to reduce dispatcher/advertisement drift. (#27257) Thanks @obviyus.
- CI/Windows: shard the Windows `checks-windows` test lane into two matrix jobs and honor explicit shard index overrides in `scripts/test-parallel.mjs` to reduce CI critical-path wall time. (#27234) Thanks @joshavant.

View File

@@ -1,7 +1,5 @@
import fs from "node:fs/promises";
const LAUNCHD_THROTTLE_INTERVAL_SECONDS = 5;
const plistEscape = (value: string): string =>
value
.replaceAll("&", "&")
@@ -108,5 +106,5 @@ export function buildLaunchAgentPlist({
? `\n <key>Comment</key>\n <string>${plistEscape(comment.trim())}</string>`
: "";
const envXml = renderEnvDict(environment);
return `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n <dict>\n <key>Label</key>\n <string>${plistEscape(label)}</string>\n ${commentXml}\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n <key>ThrottleInterval</key>\n <integer>${LAUNCHD_THROTTLE_INTERVAL_SECONDS}</integer>\n <key>ProgramArguments</key>\n <array>${argsXml}\n </array>\n ${workingDirXml}\n <key>StandardOutPath</key>\n <string>${plistEscape(stdoutPath)}</string>\n <key>StandardErrorPath</key>\n <string>${plistEscape(stderrPath)}</string>${envXml}\n </dict>\n</plist>\n`;
return `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n <dict>\n <key>Label</key>\n <string>${plistEscape(label)}</string>\n ${commentXml}\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <true/>\n <key>ProgramArguments</key>\n <array>${argsXml}\n </array>\n ${workingDirXml}\n <key>StandardOutPath</key>\n <string>${plistEscape(stdoutPath)}</string>\n <key>StandardErrorPath</key>\n <string>${plistEscape(stderrPath)}</string>${envXml}\n </dict>\n</plist>\n`;
}

View File

@@ -185,7 +185,7 @@ describe("launchd install", () => {
expect(plist).toContain(`<string>${tmpDir}</string>`);
});
it("writes crash-only KeepAlive policy with throttle interval", async () => {
it("writes KeepAlive=true policy", async () => {
const env = createDefaultLaunchdEnv();
await installLaunchAgent({
env,
@@ -196,10 +196,9 @@ describe("launchd install", () => {
const plistPath = resolveLaunchAgentPlistPath(env);
const plist = state.files.get(plistPath) ?? "";
expect(plist).toContain("<key>KeepAlive</key>");
expect(plist).toContain("<key>SuccessfulExit</key>");
expect(plist).toContain("<false/>");
expect(plist).toContain("<key>ThrottleInterval</key>");
expect(plist).toContain("<integer>5</integer>");
expect(plist).toContain("<true/>");
expect(plist).not.toContain("<key>SuccessfulExit</key>");
expect(plist).not.toContain("<key>ThrottleInterval</key>");
});
it("restarts LaunchAgent with bootout-bootstrap-kickstart order", async () => {