8.9 KiB
8.9 KiB
summary, read_when
| summary | read_when | ||
|---|---|---|---|
| Spec for the Clawdis macOS companion menu bar app and local broker (control socket + PeekabooBridge) |
|
Clawdis macOS Companion (menu bar + local broker)
Author: steipete · Status: draft spec · Date: 2025-12-05
Purpose
- Single macOS menu-bar app named Clawdis that:
- Shows native notifications for Clawdis/clawdis events.
- Owns TCC prompts (Notifications, Accessibility, Screen Recording, Automation/AppleScript, Microphone, Speech Recognition).
- Brokers privileged actions via local IPC:
- Clawdis control socket (app-specific actions like notify/run)
- PeekabooBridge socket (
bridge.sock) for UI automation brokering (consumed bypeekaboo; seedocs/mac/peekaboo.md)
- Provides a tiny CLI (
clawdis-mac) that talks to the app; Node/TS shells out to it.
- Replace the separate notifier helper pattern (Oracle) with a built-in notifier.
- Offer a first-run experience similar to VibeTunnel’s onboarding (permissions + CLI install).
High-level design
- SwiftPM package in
apps/macos/(macOS 15+, Swift 6). - Targets:
ClawdisIPC(shared Codable types + helpers for app-specific commands).Clawdis(LSUIElement MenuBarExtra app; hosts control socket + optional PeekabooBridgeHost).ClawdisCLI(clawdis-mac; prints text by default,--jsonfor scripts).
- Bundle ID:
com.steipete.clawdis. - The CLI lives in the app bundle
Contents/Helpers/clawdis-mac; dev symlinkbin/clawdis-macpoints there. - Node/TS layer calls the CLI; no direct privileged API calls from Node.
Note: docs/mac/xpc.md describes an aspirational long-term Mach/XPC architecture. The current direction for UI automation is PeekabooBridge (socket-based).
IPC contract (ClawdisIPC)
- Codable enums; small payloads (<1 MB enforced in listener):
enum Capability { notifications, accessibility, screenRecording, appleScript, microphone, speechRecognition }
enum Request {
notify(title, body, sound?)
ensurePermissions([Capability], interactive: Bool)
runShell(command:[String], cwd?, env?, timeoutSec?, needsScreenRecording: Bool)
status
}
struct Response { ok: Bool; message?: String; payload?: Data }
- The control-socket server rejects oversize/unknown cases and validates the caller by code signature TeamID (with a
DEBUG-only same-UID escape hatch controlled byCLAWDIS_ALLOW_UNSIGNED_SOCKET_CLIENTS=1).
UI automation is not part of ClawdisIPC.Request:
- UI automation is handled via the separate PeekabooBridge socket and is surfaced by the
peekabooCLI (seedocs/mac/peekaboo.md).
App UX (Clawdis)
- MenuBarExtra icon only (LSUIElement; no Dock).
- Menu items: Status, Permissions…, Pause Clawdis toggle (temporarily deny privileged actions/notifications without quitting), Quit.
- Settings window (Trimmy-style tabs):
- General: launch at login toggle and debug/visibility toggles (no per-user default sound; pass sounds per notification via CLI).
- Permissions: live status + “Request” buttons for Notifications/Accessibility/Screen Recording; links to System Settings.
- Debug (when enabled): PID/log links, restart/reveal app shortcuts, manual test notification.
- About: version, links, license.
- Pause behavior: matches Trimmy’s “Auto Trim” toggle. When paused, the broker returns
ok=false, message="clawdis paused"for actions that would touch TCC. State is persisted (UserDefaults) and surfaced in menu and status view. - Onboarding (VibeTunnel-inspired): Welcome → What it does → Install CLI (shows
ln -s .../clawdis-mac /usr/local/bin) → Permissions checklist with live status → Test notification → Done. Re-show whenwelcomeVersionbumps or CLI/app version mismatch.
Built-in services
- NotificationManager: UNUserNotificationCenter primary; AppleScript
display notificationfallback; respects the--soundvalue on each request. - PermissionManager: checks/requests Notifications, Accessibility (AX), Screen Recording (capture probe); publishes changes for UI.
- UI automation + capture: provided by PeekabooBridgeHost when enabled (see
docs/mac/peekaboo.md). - ShellExecutor: executes
Processwith timeout; rejects whenneedsScreenRecordingand permission missing; returns stdout/stderr in payload. - ControlSocketServer actor: routes Request → managers; logs via OSLog.
CLI (clawdis-mac)
- Subcommands (text by default;
--jsonfor machine output; non-zero exit on failure):notify --title --body [--sound] [--priority passive|active|timeSensitive] [--delivery system|overlay|auto]ensure-permissions --cap accessibility --cap screenRecording [--interactive]- UI automation + capture: use
peekaboo …(Clawdis hosts PeekabooBridge; seedocs/mac/peekaboo.md) run -- cmd args... [--cwd] [--env KEY=VAL] [--timeout 30] [--needs-screen-recording]status
- Sounds: supply any macOS alert name with
--soundper notification; omit the flag to use the system default. There is no longer a persisted “default sound” in the app UI. - Priority:
timeSensitiveis best-effort and falls back toactiveunless the app is signed with the Time Sensitive Notifications entitlement. - Delivery:
overlayandautoshow an in-app toast panel (bypasses Notification Center/Focus). - Internals:
- For app-specific commands (
notify,ensure-permissions,run,status): buildClawdisIPC.Request, send over the control socket. - UI automation is intentionally not exposed via
clawdis-mac; it lives behind PeekabooBridge and is surfaced by thepeekabooCLI.
- For app-specific commands (
Integration with clawdis/Clawdis (Node/TS)
- Add helper module that shells to
clawdis-mac:- Prefer
ensure-permissionsbefore actions that need TCC. - Use
notifyfor desktop toasts; fall back to JS notifier only if CLI missing or platform ≠ macOS. - Use
runfor tasks requiring privileged UI context (screen-recorded terminal runs, etc.). - For UI automation, shell out to
peekaboo …(text by default; add--jsonfor structured output) and rely on PeekabooBridge host selection (Peekaboo.app → Clawdis.app → local).
- Prefer
Deep links (URL scheme)
Clawdis (the macOS app) registers a URL scheme for triggering local actions from anywhere (browser, Shortcuts, CLI, etc.).
Scheme:
clawdis://…
clawdis://agent
Triggers a Gateway agent request (same machinery as WebChat/agent runs).
Example:
open 'clawdis://agent?message=Hello%20from%20deep%20link'
Query parameters:
message(required): the agent prompt (URL-encoded).sessionKey(optional): explicit session key to use.thinking(optional):off|minimal|low|medium|high(or omit for default).deliver(optional):true|false(default: false).to/channel(optional): forwarded to the Gatewayagentmethod (only meaningful withdeliver=true).timeoutSeconds(optional): timeout hint forwarded to the Gateway.key(optional): unattended mode key (see below).
Safety/guardrails:
- Disabled by default; enable in Clawdis → Settings → Debug (“Allow URL scheme (agent)”).
- Without
key, Clawdis prompts with a confirmation dialog before invoking the agent. - With
key=<value>, Clawdis runs without prompting (intended for personal automations).- The current key is shown in Debug Settings and stored locally in UserDefaults.
Notes:
- In local mode, Clawdis will start the local Gateway if needed before issuing the request.
- In remote mode, Clawdis will use the configured remote tunnel/endpoint.
Permissions strategy
- All TCC prompts originate from the app bundle; CLI and Node stay headless.
- Permission checks are idempotent; onboarding surfaces missing grants and provides one-click request buttons.
Build & dev workflow (native)
cd native && swift build(debug) /swift build -c release.- Run app for dev:
swift run Clawdis(or Xcode scheme). - Package app + helper:
swift build -c release && swift package --allow-writing-to-directory ../dist(tbd exact script). - Tests: add Swift Testing suites under
apps/macos/Tests(especially IPC round-trips and permission probing fakes).
Icon pipeline
- Source asset lives at
apps/macos/Icon.icon(glass .icon bundle). - Regenerate the bundled icns via
scripts/build_icon.sh(uses ictool/icontool + sips), which outputs toapps/macos/Sources/Clawdis/Resources/Clawdis.icnsby default. OverrideDEST_ICNSto change the target. The script also writes intermediate renders toapps/macos/build/icon/.
Open questions / decisions
- Where to place the dev symlink
bin/clawdis-mac(repo root vs.apps/macos/bin)? - Should
runShellsupport streaming stdout/stderr (IPC with AsyncSequence) or just buffered? (Start buffered; streaming later.) - Icon: reuse Clawdis lobster or new mac-specific glyph?
- Sparkle updates: bundled via Sparkle; release builds point at
https://raw.githubusercontent.com/steipete/clawdis/main/appcast.xmland enable auto-checks, while debug builds leave the feed blank and disable checks.