feat: add mac companion app
This commit is contained in:
87
docs/clawdis-mac.md
Normal file
87
docs/clawdis-mac.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Clawdis macOS Companion (menu bar + XPC broker)
|
||||
|
||||
Author: steipete · Status: draft spec · Date: 2025-12-05
|
||||
|
||||
## Purpose
|
||||
- Single macOS menu-bar app named **Clawdis** that:
|
||||
- Shows native notifications for Clawdis/warelay events.
|
||||
- Owns TCC prompts (Notifications, Accessibility, Screen Recording).
|
||||
- Brokers privileged actions (screen capture, shell with elevated UI context) via XPC.
|
||||
- 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):
|
||||
- Dependency: `https://github.com/ChimeHQ/AsyncXPCConnection` (>=0.6.0).
|
||||
- Targets:
|
||||
- `ClawdisIPC` (shared Codable types + helpers).
|
||||
- `Clawdis` (LSUIElement MenuBarExtra app; embeds XPC listener and notifier).
|
||||
- `ClawdisCLI` (client that forms requests, talks XPC, prints JSON for scripts).
|
||||
- Bundle ID: `com.steipete.clawdis`; XPC service name: `com.steipete.clawdis.xpc`.
|
||||
- The CLI lives in the app bundle `Contents/Helpers/clawdis-mac`; dev symlink `bin/clawdis-mac` points there.
|
||||
- Node/TS layer calls the CLI; no direct XPC from Node.
|
||||
|
||||
## IPC contract (ClawdisIPC)
|
||||
- Codable enums; small payloads (<1 MB enforced in listener):
|
||||
|
||||
```
|
||||
enum Capability { notifications, accessibility, screenRecording }
|
||||
enum Request {
|
||||
notify(title, body, sound?)
|
||||
ensurePermissions([Capability], interactive: Bool)
|
||||
screenshot(displayID?, windowID?, format="png")
|
||||
runShell(command:[String], cwd?, env?, timeoutSec?, needsScreenRecording: Bool)
|
||||
status
|
||||
}
|
||||
struct Response { ok: Bool; message?: String; payload?: Data }
|
||||
```
|
||||
- Listener validates caller `auditToken` == same UID, rejects oversize/unknown cases.
|
||||
|
||||
## App UX (Clawdis)
|
||||
- MenuBarExtra icon only (LSUIElement; no Dock).
|
||||
- Menu items: Status, Test Notification, Permissions…, **Pause Clawdis** toggle (temporarily deny privileged actions/notifications without quitting), Quit.
|
||||
- Settings window (Trimmy-style tabs):
|
||||
- General: launch at login toggle, default sound, logging verbosity.
|
||||
- Permissions: live status + “Request” buttons for Notifications/Accessibility/Screen Recording; links to System Settings.
|
||||
- About: version, links, license.
|
||||
- Pause behavior: matches Trimmy’s “Auto Trim” toggle. When paused, XPC listener returns `ok=false, message="clawdis paused"` for actions that would touch TCC (notify/run/screenshot). 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 when `welcomeVersion` bumps or CLI/app version mismatch.
|
||||
|
||||
## Built-in services
|
||||
- NotificationManager: UNUserNotificationCenter primary; AppleScript `display notification` fallback; respects sound setting.
|
||||
- PermissionManager: checks/requests Notifications, Accessibility (AX), Screen Recording (capture probe); publishes changes for UI.
|
||||
- ScreenCaptureManager: window/display PNG capture; gated on permission.
|
||||
- ShellRunner: executes `Process` with timeout; rejects when `needsScreenRecording` and permission missing; returns stdout/stderr in payload.
|
||||
- XPCListener actor: routes Request → managers; logs via OSLog.
|
||||
|
||||
## CLI (`clawdis-mac`)
|
||||
- Subcommands (JSON out, non-zero exit on failure):
|
||||
- `notify --title --body [--sound]`
|
||||
- `ensure-permissions --cap accessibility --cap screenRecording [--interactive]`
|
||||
- `screenshot [--display-id N | --window-id N] [--out path]`
|
||||
- `run -- cmd args... [--cwd] [--env KEY=VAL] [--timeout 30] [--needs-screen-recording]`
|
||||
- `status`
|
||||
- Internals: builds Request, connects via AsyncXPCConnection, prints Response as JSON to stdout.
|
||||
|
||||
## Integration with warelay/Clawdis (Node/TS)
|
||||
- Add helper module that shells to `clawdis-mac`:
|
||||
- Prefer `ensure-permissions` before actions that need TCC.
|
||||
- Use `notify` for desktop toasts; fall back to JS notifier only if CLI missing or platform ≠ macOS.
|
||||
- Use `run` for tasks requiring privileged UI context (screen-recorded terminal runs, etc.).
|
||||
|
||||
## 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).
|
||||
|
||||
## Open questions / decisions
|
||||
- Where to place the dev symlink `bin/clawdis-mac` (repo root vs. `apps/macos/bin`)?
|
||||
- Should `runShell` support streaming stdout/stderr (XPC with AsyncSequence) or just buffered? (Start buffered; streaming later.)
|
||||
- Icon: reuse Clawdis lobster or new mac-specific glyph?
|
||||
- Sparkle updates: out of scope initially; add later if we ship signed builds.
|
||||
Reference in New Issue
Block a user