diff --git a/CHANGELOG.md b/CHANGELOG.md index 53ab2e5ee7d..80c00a9f732 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ Docs: https://docs.openclaw.ai - Synology Chat/rate-limit env parsing: honor `SYNOLOGY_RATE_LIMIT=0` as an explicit value while still falling back to the default limit for malformed env values instead of partially parsing them. Landed from contributor PR #39197 by @scoootscooob. Thanks @scoootscooob. - Voice-call/OpenAI Realtime STT config defaults: honor explicit `vadThreshold: 0` and `silenceDurationMs: 0` instead of silently replacing them with defaults. Landed from contributor PR #39196 by @scoootscooob. Thanks @scoootscooob. - Voice-call/OpenAI TTS speed config: honor explicit `speed: 0` instead of silently replacing it with the default speed. Landed from contributor PR #39318 by @ql-wade. Thanks @ql-wade. +- launchd/runtime PID parsing: reject `pid <= 0` from `launchctl print` so the daemon state parser no longer treats kernel/non-running sentinel values as real process IDs. Landed from contributor PR #39281 by @mvanhorn. Thanks @mvanhorn. - Cron/file permission hardening: enforce owner-only (`0600`) cron store/backup/run-log files and harden cron store + run-log directories to `0700`, including pre-existing directories from older installs. (#36078) Thanks @aerelune. - Gateway/remote WS break-glass hostname support: honor `OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1` for `ws://` hostname URLs (not only private IP literals) across onboarding validation and runtime gateway connection checks, while still rejecting public IP literals and non-unicast IPv6 endpoints. (#36930) Thanks @manju-rn. - Routing/binding lookup scalability: pre-index route bindings by channel/account and avoid full binding-list rescans on channel-account cache rollover, preventing multi-second `resolveAgentRoute` stalls in large binding configurations. (#36915) Thanks @songchenghao. diff --git a/src/daemon/launchd.test.ts b/src/daemon/launchd.test.ts index ca94f8b5602..d1c808b2add 100644 --- a/src/daemon/launchd.test.ts +++ b/src/daemon/launchd.test.ts @@ -102,6 +102,26 @@ describe("launchd runtime parsing", () => { lastExitReason: "exited", }); }); + + it("does not set pid when pid = 0", () => { + const output = ["state = running", "pid = 0"].join("\n"); + const info = parseLaunchctlPrint(output); + expect(info.pid).toBeUndefined(); + expect(info.state).toBe("running"); + }); + + it("sets pid for positive values", () => { + const output = ["state = running", "pid = 1234"].join("\n"); + const info = parseLaunchctlPrint(output); + expect(info.pid).toBe(1234); + }); + + it("does not set pid for negative values", () => { + const output = ["state = waiting", "pid = -1"].join("\n"); + const info = parseLaunchctlPrint(output); + expect(info.pid).toBeUndefined(); + expect(info.state).toBe("waiting"); + }); }); describe("launchctl list detection", () => { diff --git a/src/daemon/launchd.ts b/src/daemon/launchd.ts index 5326413b73d..b859e9ad482 100644 --- a/src/daemon/launchd.ts +++ b/src/daemon/launchd.ts @@ -128,7 +128,7 @@ export function parseLaunchctlPrint(output: string): LaunchctlPrintInfo { const pidValue = entries.pid; if (pidValue) { const pid = Number.parseInt(pidValue, 10); - if (Number.isFinite(pid)) { + if (Number.isFinite(pid) && pid > 0) { info.pid = pid; } }