← Back to PRs

#22313: fix: check plist existence before marking LaunchAgent missing

by MisterGuy420 open 2026-02-21 01:23 View on GitHub →
gateway cli size: S trusted-contributor
## Summary When `launchctl print` fails, the current code unconditionally sets `missingUnit: true`. This is incorrect when the LaunchAgent plist file exists but the service simply isn't loaded. Additionally, this PR now includes a complete `start()` implementation for all daemon backends, allowing OpenClaw to explicitly start services that are installed but not running. ## Problem The `readLaunchAgentRuntime` function in `src/daemon/launchd.ts` was marking LaunchAgents as `missingUnit: true` whenever `launchctl print` returned a non-zero exit code. However, a non-zero exit code can also mean "service not loaded" — which is different from "unit doesn't exist." This distinction matters for status reporting: when a user has installed the LaunchAgent but it's not currently running, we should not claim the unit is missing. Furthermore, there was no explicit `start()` path for daemon services. When a service wasn't loaded, users only received hints about how to start it manually. ## Changes ### 1. Fixed `missingUnit` detection - Check if plist file exists before setting `missingUnit` - Only mark as missing if the plist file also doesn't exist - Fixed `readLaunchAgentRuntime` to return `status: "stopped"` (not `"unknown"`) when plist exists but service is unloaded ### 2. Added `start()` method to GatewayService interface (`service.ts`) ### 3. Implemented `start()` for all three backends: - **launchd.ts**: `enable + bootstrap + kickstart` sequence with GUI domain error handling - **systemd.ts**: `systemctl start` - **schtasks.ts**: `schtasks /Run` ### 4. Updated `lifecycle-core.ts` - Now calls `service.start()` when service isn't loaded - Falls back to hints only on error ### 5. Updated `node-service.ts` - Added `start` wrapper with environment transformation ## Before vs After | State | Before | After | |-------|--------|-------| | Plist exists, service not loaded | `missingUnit: true` ❌ | `missingUnit: false` ✅ | | Plist doesn't exist | `missingUnit: true` ✅ | `missingUnit: true` ✅ | | Service not loaded | Hints only ❌ | Auto-start attempt ✅ | ## Testing - [x] `src/daemon/launchd.test.ts` — 13 tests pass - [x] `src/cli/daemon-cli/lifecycle-core.test.ts` — 2 tests pass - [x] `pnpm build && pnpm check && pnpm test` passes - [ ] Tested locally with OpenClaw instance ## AI-Assisted Contribution 🤖 - [x] This PR was created with AI assistance (Claude) - [x] Testing status: **lightly tested** — logic verified against existing tests, no new tests added - [x] I understand what this code does: it checks plist existence before determining if a LaunchAgent unit is truly missing, and adds explicit start capability for all daemon backends ## Related - Fixes #17763 - Supersedes #17784 (which had unrelated refactoring changes that caused merge conflicts) - Addresses the request in #22304 (this PR now incorporates the same complete fix) --- *Updated: Added `start()` implementation for all daemon backends (launchd, systemd, schtasks) and updated lifecycle-core to use explicit start instead of just hints.*

Most Similar PRs