Skip to content

chore: merge release/v0.1.8 back to main#726

Merged
mrcfps merged 4 commits intomainfrom
merge/release-v0.1.8-to-main
Mar 31, 2026
Merged

chore: merge release/v0.1.8 back to main#726
mrcfps merged 4 commits intomainfrom
merge/release-v0.1.8-to-main

Conversation

@lefarcen
Copy link
Copy Markdown
Collaborator

@lefarcen lefarcen commented Mar 31, 2026

Merge release/v0.1.8 hotfixes back into main.

Includes: #697, #700, #701

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
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread apps/desktop/main/services/launchd-bootstrap.ts
@mrcfps mrcfps merged commit 74e72b9 into main Mar 31, 2026
11 checks passed
@lefarcen lefarcen mentioned this pull request Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants