#macOS app node doesn't support `system.run.prepare`, blocking all remote command execu

11 messages · Page 1 of 1 (latest)

livid tide
#

The macOS companion app (node mode) cannot execute shell commands from a remote gateway because it doesn't support system.run.prepare. This makes the app unable to perform basic remote automation tasks that the CLI node host handles without issue.

Environment

  • macOS app version: 2026.4.14 (2026041490)
  • Gateway version: 2026.4.14 (323493f)
  • Gateway mode: remote (VPS)
  • macOS version: 26.4.1
  • Node connection: paired and connected successfully

When using exec tool with host=node targeting the macOS app node, commands should execute in the app's UI context (WindowServer), leveraging its TCC permissions and native approval dialogs.
All exec attempts fail with:

node command not allowed: the node (platform: macOS 26.4.1) does not support "system.run.prepare"

Related Issues

  • Browser control also broken on macOS app node (transport layer failures)
  • Canvas authentication broken in remote mode (#60292, #56577)

According to docs (docs/platforms/mac/xpc.md, docs/platforms/mac/peekaboo.md), the macOS app is designed to execute commands in its UI context with native approval dialogs. The missing system.run.prepare support appears to be an implementation gap rather than an intentional limitation.

Question for maintainers: Is system.run.prepare support planned for the macOS app, or is there a different execution pathway we should be using?

silk oysterBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> ✅ Mark Solution

celest kayakBOT
#

I’m checking the repo now, because the docs say it should forward system.run, so the question is whether the current node capability gate just never advertises/implements system.run.prepare on macOS app nodes.

#

I checked current main, and this looks like a real implementation gap, not a config mistake.

  • exec host=node always calls system.run.prepare first, then system.run
  • the headless CLI node host implements system.run.prepare
  • the macOS companion app node currently exposes system.run, system.which, system.notify, and system.execApprovals.*, but not system.run.prepare

So right now there is no alternate exec path that bypasses system.run.prepare for the app node. That is why it fails before approvals even matter.

Best workaround today:

export OPENCLAW_NODE_EXEC_HOST=app
export OPENCLAW_NODE_EXEC_FALLBACK=0
openclaw node run --host <gateway-host> --port 18789 --display-name "Mac node"

That gives you the CLI node host for the exec pipeline, while still routing actual execution through the app’s UI/TCC context.

#

Docs:

So my read is:

  • planned/intended architecture: yes
  • working today via direct macOS app node: no
  • current reality: missing system.run.prepare support on the app node, so remote exec host=node against it is effectively broken

Also, changing exec approvals will not fix this one, because the gateway is rejecting the command as unsupported first.

If there is not already a dedicated GitHub issue for the missing app-node system.run.prepare support, I’d file one. This looks like a bug/docs mismatch: https://github.com/openclaw/openclaw/issues

livid tide
#

What if my CLI node is hosted within a launchd service? How should I export these two variables to ensure they remain active for all subsequent launchd runs?

celest kayakBOT
#

I want to make sure this advice is actually correct for a LaunchAgent, because OPENCLAW_NODE_EXEC_HOST is read very early during node-host startup. I’m checking whether ~/.openclaw/.env is loaded early enough, or whether this one really needs to live in the LaunchAgent plist environment.

#

If it’s running under launchd, a plain shell export is not enough, because future LaunchAgent restarts will not inherit your shell environment.

Best durable option:

  1. put these in ~/.openclaw/.env (or $OPENCLAW_STATE_DIR/.env if you use a custom state dir)
  2. restart the node service

Example:

OPENCLAW_NODE_EXEC_HOST=app
OPENCLAW_NODE_EXEC_FALLBACK=0

Then:

#
openclaw node restart

That’s the path I’d recommend, because:

  • OpenClaw loads the global ~/.openclaw/.env on startup
  • it survives reboots
  • it survives normal launchd restarts
  • it won’t get wiped by a future openclaw node install --force

If you really want it pinned in launchd itself, you can add them under:

~/Library/LaunchAgents/ai.openclaw.node.plist

inside: