Merged
Conversation
The OpenClaw launchd plist was missing OPENCLAW_GATEWAY_TOKEN, causing all channels to be stuck in 'connecting' on fresh installs where no openclaw.json existed yet to provide the token fallback. Added parity test verifying BOTH plists contain the token.
…ts (#700) * fix(desktop): use port 50789 to avoid global openclaw port conflict Users with 'openclaw install' have a global ai.openclaw.gateway launchd service on port 18789 with KeepAlive=true. When Nexu also used 18789, launchd race conditions caused token mismatch or crash loops. Changed default to 50789 (alongside controller:50800 and web:50810). findFreePort() still handles further conflicts. * fix(desktop): detect and recover from port theft after openclaw launch After starting the openclaw launchd service, verify the port listener PID matches our service PID. If a competing service (e.g. global ai.openclaw.gateway with KeepAlive=true) grabbed the port, bootout our openclaw, find a new free port, regenerate both openclaw and controller plists with the new port, and restart both services. * test: add port theft detection and recovery scenarios Scenario 27: competing service steals port after launch → bootstrap detects PID mismatch and reassigns to next free port. Scenario 28: our openclaw owns the port → no reassignment needed. * fix: align CI probe and dev-launchd with new default port 50789 - desktop-ci-check.mjs: update health probe URL from 18789 to 50789 - dev-launchd.sh: update OPENCLAW_PORT default to 50789 so cleanup no longer kills a user's unrelated global openclaw on 18789 * fix: update desktop-stop-smoke default port to 50789 * ci: retrigger CI checks * fix(desktop): use net.connect for port detection, fix recovery flow - Replace lsof-based port detection with net.connect — lsof is blocked by macOS hardened runtime in packaged Electron apps, silently returning empty results even when ports are occupied. - Add structured logging via env.log callback so bootstrap diagnostics appear in cold-start.log (console.log is lost in packaged mode). - Fix recovery: add waitForExit after bootout before re-bootstrapping to prevent launchd race conditions. Verified: packaged app correctly detects port 50789 occupied by global openclaw, auto-assigns 50790, both services coexist. * fix(desktop): pass --port to openclaw gateway and revert default to 18789 The openclaw plist ProgramArguments never included --port, so openclaw always bound to its hardcoded default 18789 regardless of what findFreePort allocated. When another service (ClawX, global openclaw) occupied 18789, our openclaw crashed on bind (EADDRINUSE) even though bootstrap had correctly detected the conflict and assigned a new port. Now passing --port explicitly in ProgramArguments. Also reverted the default port back to 18789 since dynamic port allocation handles conflicts correctly. * fix: revert default port to 18789, fix tests for net.createServer detection - Reverted all 50789 port changes back to 18789 (scripts, CI, tests) - Updated detectPortOccupier to use net.createServer().listen() instead of net.connect (avoids conflict with probePort mock) - Updated plist-generator tests for --port in ProgramArguments - Updated port conflict scenario tests (15/16/27/28) to mock createServer instead of lsof/createConnection - All 624 tests pass * chore: remove accidentally committed files * test(e2e): add openclaw port conflict resilience scenario Simulates a global openclaw (or ClawX) occupying port 18789 before Nexu launches. Verifies: - Nexu detects the conflict and auto-assigns an alternative port - Controller comes up healthy - OpenClaw runs on 18790+ instead of crashing - The blocker on 18789 is NOT killed (coexistence) * chore(ci): send Feishu notification after desktop E2E results Green card on pass, red card on failure. Shows test mode, source, channel, and links to the CI run for details. * chore(ci): notify Feishu on desktop E2E failure Only sends on failure (not success). Card includes: - Trigger source: PR number, branch, commit SHA - Who triggered it - Test mode/source/channel - Link to CI logs * chore(ci): send Feishu notification on both E2E pass and fail * fix(desktop): retry launchctl bootstrap on Input/output error When launchd has stale state for a service label (e.g. after repeated bootout/bootstrap during port conflict recovery), bootstrap fails with 'Input/output error (code 5)'. Now detects this error, bootout to clear the stale registration, waits 1s, and retries once. * fix(desktop): address port conflict PR review feedback 1. Recovery uses bootoutAndWaitForExit (captures PID before bootout) instead of separate bootout + waitForExit without knownPid. 2. Attach path adds token validation: checks launchd service env OPENCLAW_GATEWAY_TOKEN matches expected token before attaching. Prevents attaching to a global openclaw or ClawX on the same port. 3. E2E openclaw port conflict: controller readiness is now a hard fail. Port discovery uses runtime-ports.json instead of hardcoded range, so any valid auto-assigned port is accepted. * test(e2e): add post-launch port theft recovery scenario Scenario: start app normally on 18789, kill openclaw, occupy 18789 with a blocker, force-quit and re-launch. Verifies the app detects the stolen port on cold start, auto-assigns a new port, and recovers. This covers the post-launch recovery path that unit tests can't easily simulate (requires real launchd timing). * fix(e2e): use dynamic openclaw port instead of hardcoded 18789 The openclaw port may be auto-assigned to 18790+ when 18789 is occupied. Updated: - packaged-e2e.mjs: read openclawPort from controller readiness - run-e2e.sh: read from runtime-ports.json, scan port range for diagnostics and port-free checks
…se-v0.1.8-to-main # Conflicts: # apps/desktop/main/services/plist-generator.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 65f8f8ca57
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
mrcfps
approved these changes
Mar 31, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge
release/v0.1.8hotfixes back into main.Includes: #697, #700, #701