diff --git a/CHANGELOG.md b/CHANGELOG.md index a1ce2c7bf..bbb63fe87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ ### Fixes - Discord/Telegram: add per-request retry policy with configurable delays and docs. +- macOS: prevent gateway launchd startup race where the app could kill a just-started gateway; avoid unnecessary `bootout` and ensure the job is enabled at login. Fixes #306. Thanks @gupsammy for PR #387. - Pairing: generate DM pairing codes with CSPRNG, expire pending codes after 1 hour, and avoid re-sending codes for already pending requests. - Pairing: lock + atomically write pairing stores with 0600 perms and stop logging pairing codes in provider logs. - Discord: include all inbound attachments in `MediaPaths`/`MediaUrls` (back-compat `MediaPath`/`MediaUrl` still first). diff --git a/apps/macos/Sources/Clawdbot/GatewayLaunchAgentManager.swift b/apps/macos/Sources/Clawdbot/GatewayLaunchAgentManager.swift index ee6b2e8e1..9038c1489 100644 --- a/apps/macos/Sources/Clawdbot/GatewayLaunchAgentManager.swift +++ b/apps/macos/Sources/Clawdbot/GatewayLaunchAgentManager.swift @@ -58,6 +58,18 @@ enum GatewayLaunchAgentManager { self.logger.error("launchd enable failed: gateway missing at \(gatewayBin)") return "Embedded gateway missing in bundle; rebuild via scripts/package-mac-app.sh" } + + // Check if service is already running - if so, skip bootout to avoid killing it + let alreadyRunning = await self.status() + if alreadyRunning { + self.logger.info("launchd service already running, skipping bootout") + // Still update plist in case config changed, but don't restart + self.writePlist(bundlePath: bundlePath, port: port) + // Ensure service is marked as enabled for auto-start on login + _ = await self.runLaunchctl(["enable", "gui/\(getuid())/\(gatewayLaunchdLabel)"]) + return nil + } + self.logger.info("launchd enable requested port=\(port)") self.writePlist(bundlePath: bundlePath, port: port) _ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(gatewayLaunchdLabel)"]) @@ -69,6 +81,8 @@ enum GatewayLaunchAgentManager { ? "Failed to bootstrap gateway launchd job" : bootstrap.output.trimmingCharacters(in: .whitespacesAndNewlines) } + // Ensure service is marked as enabled for auto-start on login + _ = await self.runLaunchctl(["enable", "gui/\(getuid())/\(gatewayLaunchdLabel)"]) // Note: removed redundant `kickstart -k` that caused race condition. // bootstrap already starts the job; kickstart -k would kill it immediately // and with KeepAlive=true, cause a restart loop with port conflicts.