OpenClaw macOS-Companion (Menüleiste + Gateway-Broker)

Die macOS-App ist der Menüleisten-Companion für OpenClaw. Sie verwaltet Berechtigungen, steuert/verbindet sich mit dem Gateway lokal (launchd oder manuell) und stellt macOS-Funktionen als Node dem Agenten zur Verfügung.

Was sie kann

  • Zeigt native Benachrichtigungen und Status in der Menüleiste.
  • Verwaltet TCC-Prompts (Benachrichtigungen, Bedienungshilfen, Bildschirmaufnahme, Mikrofon, Spracherkennung, Automation/AppleScript).
  • Startet oder verbindet sich mit dem Gateway (lokal oder remote).
  • Stellt macOS-spezifische Tools bereit (Canvas, Kamera, Bildschirmaufnahme, system.run).
  • Startet den lokalen Node-Host-Service im Remote-Modus (launchd) und stoppt ihn im Lokal-Modus.
  • Hostet optional PeekabooBridge für UI-Automatisierung.
  • Installiert die globale CLI (openclaw) via npm/pnpm auf Anfrage (Bun wird für die Gateway-Runtime nicht empfohlen).

Lokal vs. Remote-Modus

  • Lokal (Standard): Die App verbindet sich mit einem laufenden lokalen Gateway, falls vorhanden; andernfalls aktiviert sie den launchd-Service über openclaw gateway install.
  • Remote: Die App verbindet sich über SSH/Tailscale mit einem Gateway und startet niemals einen lokalen Prozess. Die App startet den lokalen Node-Host-Service, damit das Remote-Gateway diesen Mac erreichen kann. Die App startet das Gateway nicht als Child-Prozess.

Launchd-Steuerung

Die App verwaltet einen benutzerspezifischen LaunchAgent mit dem Label ai.openclaw.gateway (oder ai.openclaw.<profile> bei Verwendung von --profile/OPENCLAW_PROFILE; Legacy com.openclaw.* wird weiterhin entladen).

launchctl kickstart -k gui/$UID/ai.openclaw.gateway
launchctl bootout gui/$UID/ai.openclaw.gateway

Ersetze das Label durch ai.openclaw.<profile> bei Verwendung eines benannten Profils.

Falls der LaunchAgent nicht installiert ist, aktiviere ihn über die App oder führe openclaw gateway install aus.

Node-Funktionen (Mac)

Die macOS-App präsentiert sich als Node. Gängige Befehle:

  • Canvas: canvas.present, canvas.navigate, canvas.eval, canvas.snapshot, canvas.a2ui.*
  • Kamera: camera.snap, camera.clip
  • Bildschirm: screen.record
  • System: system.run, system.notify

Der Node meldet eine permissions-Map, damit Agenten wissen, was erlaubt ist.

Node-Service + App-IPC:

  • Wenn der Headless-Node-Host-Service läuft (Remote-Modus), verbindet er sich per Gateway-WS als Node.
  • system.run wird in der macOS-App (UI/TCC-Kontext) über einen lokalen Unix-Socket ausgeführt; Prompts + Output bleiben in der App.

Diagramm (SCI):

Gateway -> Node Service (WS)
                 |  IPC (UDS + Token + HMAC + TTL)
                 v
             Mac App (UI + TCC + system.run)

Exec-Genehmigungen (system.run)

system.run wird durch Exec-Genehmigungen in der macOS-App gesteuert (Einstellungen → Exec-Genehmigungen). Sicherheit + Ask + Allowlist werden lokal auf dem Mac gespeichert in:

~/.openclaw/exec-approvals.json

Beispiel:

{
  "version": 1,
  "defaults": {
    "security": "deny",
    "ask": "on-miss"
  },
  "agents": {
    "main": {
      "security": "allowlist",
      "ask": "on-miss",
      "allowlist": [{ "pattern": "/opt/homebrew/bin/rg" }]
    }
  }
}

Hinweise:

  • allowlist-Einträge sind Glob-Patterns für aufgelöste Binary-Pfade.
  • Roher Shell-Befehlstext mit Shell-Steuerungs- oder Expansionssyntax (&&, ||, ;, |, `, $, <, >, (, )) wird als Allowlist-Miss behandelt und erfordert explizite Genehmigung (oder das Allowlisten der Shell-Binary).
  • Die Wahl von „Immer erlauben” im Prompt fügt den Befehl zur Allowlist hinzu.
  • system.run-Umgebungsüberschreibungen werden gefiltert (entfernt PATH, DYLD_*, LD_*, NODE_OPTIONS, PYTHON*, PERL*, RUBYOPT, SHELLOPTS, PS4) und dann mit der App-Umgebung zusammengeführt.
  • Bei Shell-Wrappern (bash|sh|zsh ... -c/-lc) werden anfragespezifische Umgebungsüberschreibungen auf eine kleine explizite Allowlist reduziert (TERM, LANG, LC_*, COLORTERM, NO_COLOR, FORCE_COLOR).
  • Bei Always-Allow-Entscheidungen im Allowlist-Modus werden bekannte Dispatch-Wrapper (env, nice, nohup, stdbuf, timeout) mit inneren ausführbaren Pfaden statt Wrapper-Pfaden persistiert. Wenn das Unwrapping nicht sicher ist, wird kein Allowlist-Eintrag automatisch persistiert.

Die App registriert das openclaw:// URL-Schema für lokale Aktionen.

openclaw://agent

Löst eine Gateway-agent-Anfrage aus.

open 'openclaw://agent?message=Hello%20from%20deep%20link'

Query-Parameter:

  • message (erforderlich)
  • sessionKey (optional)
  • thinking (optional)
  • deliver / to / channel (optional)
  • timeoutSeconds (optional)
  • key (optionaler Unattended-Mode-Schlüssel)

Sicherheit:

  • Ohne key fragt die App zur Bestätigung.
  • Ohne key erzwingt die App ein kurzes Nachrichtenlimit für den Bestätigungsprompt und ignoriert deliver / to / channel.
  • Mit einem gültigen key läuft der Run unbeaufsichtigt (gedacht für persönliche Automatisierungen).

Onboarding-Ablauf (typisch)

  1. Installiere und starte OpenClaw.app.
  2. Schließe die Berechtigungs-Checkliste ab (TCC-Prompts).
  3. Stelle sicher, dass der Lokal-Modus aktiv ist und das Gateway läuft.
  4. Installiere die CLI, wenn du Terminal-Zugang möchtest.

Platzierung des State-Verzeichnisses (macOS)

Vermeide es, dein OpenClaw-State-Verzeichnis in iCloud oder andere Cloud-synchronisierte Ordner zu legen. Synchronisierte Pfade können Latenz hinzufügen und gelegentlich Dateisperren-/Sync-Konflikte bei Sessions und Zugangsdaten verursachen.

Bevorzuge einen lokalen, nicht synchronisierten State-Pfad wie:

OPENCLAW_STATE_DIR=~/.openclaw

Wenn openclaw doctor State unter einem der folgenden Pfade erkennt:

  • ~/Library/Mobile Documents/com~apple~CloudDocs/...
  • ~/Library/CloudStorage/...

wird es warnen und empfehlen, auf einen lokalen Pfad umzuziehen.

Build & Entwicklungsworkflow (nativ)

  • cd apps/macos && swift build
  • swift run OpenClaw (oder Xcode)
  • App paketieren: scripts/package-mac-app.sh

Gateway-Konnektivität debuggen (macOS CLI)

Verwende die Debug-CLI, um denselben Gateway-WebSocket-Handshake und dieselbe Discovery-Logik auszuführen, die die macOS-App nutzt, ohne die App zu starten.

cd apps/macos
swift run openclaw-mac connect --json
swift run openclaw-mac discover --timeout 3000 --json

Connect-Optionen:

  • --url <ws://host:port>: Konfiguration überschreiben
  • --mode <local|remote>: aus Konfiguration auflösen (Standard: Konfiguration oder local)
  • --probe: frischen Health-Probe erzwingen
  • --timeout <ms>: Request-Timeout (Standard: 15000)
  • --json: strukturierte Ausgabe zum Vergleichen

Discovery-Optionen:

  • --include-local: Gateways einschließen, die als „lokal” gefiltert würden
  • --timeout <ms>: gesamtes Discovery-Zeitfenster (Standard: 2000)
  • --json: strukturierte Ausgabe zum Vergleichen

Tipp: Vergleiche mit openclaw gateway discover --json, um zu sehen, ob die Discovery-Pipeline der macOS-App (NWBrowser + Tailnet DNS-SD Fallback) sich von der dns-sd-basierten Discovery der Node-CLI unterscheidet.

Remote-Verbindungs-Plumbing (SSH-Tunnel)

Wenn die macOS-App im Remote-Modus läuft, öffnet sie einen SSH-Tunnel, damit lokale UI-Komponenten mit einem Remote-Gateway kommunizieren können, als wäre es auf localhost.

Control-Tunnel (Gateway-WebSocket-Port)

  • Zweck: Health-Checks, Status, Web-Chat, Konfiguration und andere Control-Plane-Aufrufe.
  • Lokaler Port: der Gateway-Port (Standard 18789), immer stabil.
  • Remote-Port: derselbe Gateway-Port auf dem Remote-Host.
  • Verhalten: kein zufälliger lokaler Port; die App nutzt einen bestehenden gesunden Tunnel oder startet ihn bei Bedarf neu.
  • SSH-Aufbau: ssh -N -L <local>:127.0.0.1:<remote> mit BatchMode + ExitOnForwardFailure + Keepalive-Optionen.
  • IP-Meldung: Der SSH-Tunnel nutzt Loopback, daher sieht das Gateway die Node-IP als 127.0.0.1. Verwende Direct (ws/wss) Transport, wenn die echte Client-IP angezeigt werden soll (siehe macOS-Fernzugriff).

Für Setup-Schritte siehe macOS-Fernzugriff. Für Protokoll-Details siehe Gateway-Protokoll.

Verwandte Doku