Skip to content

feat(desktop): macOS Tauri client + native collaboration features#252

Merged
haowei2000 merged 7 commits into
developfrom
feat/desktop-macos
Jul 18, 2026
Merged

feat(desktop): macOS Tauri client + native collaboration features#252
haowei2000 merged 7 commits into
developfrom
feat/desktop-macos

Conversation

@haowei2000

Copy link
Copy Markdown
Collaborator

Summary

Adds a macOS desktop client (Tauri v2) that hosts the existing React frontend and becomes the graphical home of the local ACP connector daemon, plus a batch of native collaboration / AI-ops features that only a same-machine app can offer. The red line holds: chat messages and permission decisions always go through the gateway — the desktop only displays richer local context and runs native pickers/notifications.

Note on scope: this branch is stacked on the PWA/Web-Push work, so the PR contains two commitsfeat(pwa,push) (6c254c14) and feat(desktop) (e6c5a081). If you'd rather land PWA separately first, say so and I'll split.

What's included

Foundation — Tauri shell (tray, close-to-hide, single-instance, ⌥⌘C, launch-at-login, native notifications); runtime-configurable gateway address + first-run ServerPicker (serverBase=null keeps browser behavior identical → web zero-regression); gateway user-scoped WS notification frames alongside Web Push (WKWebView has no Web Push).

Connector management — list/start/stop/restart/logs/delete + a supervisor that revives managed daemons; form-based config editor (structure-preserving toml_edit); agent picker with one-click npm install; native folder pickers; grid connector page; onboarding via gateway mint→redeem; bot↔connector linkage; open-agent-files locally or download.

Native features (this batch)

Feature
A2 agent-changes watch (FSEvents) + git status/diff/revert/open-PR (path-guarded)
A3 drag-to-grant: drop a Finder folder → allowed_roots + restart
A4 read-only audit timeline from the connector log
A1a informed approval: banner shows target branch/dirty/size
B5 cheers:// deep links + ⌥⌘K quick panel
B6 screenshot (screencapture -i) + Finder "Open With → Cheers", via the existing gateway upload path
B7 tray roster + dock badge (unread + pending approvals)
C8 per-connector health (process-group CPU/mem, hung detection)
C9 one-click ACP adapter update check + upgrade

Personal workbench plugins — user-installed (this-Mac) renderer plugins under ~/.cheers/plugins, no admin; install-time trust prompt.

Deferred (documented, not in this PR)

  • A1b native notification action-buttons — tauri-plugin-notification v2 can't render macOS action buttons; the workaround is a deprecated NSUserNotification API (flaky even when signed).
  • C10 local voice — native SFSpeechRecognizer/AVAudioEngine, main-thread-only, crash-prone.

Known tradeoff (LOW)

permission_context(path) is an ungated existence/size/git-branch oracle callable from the webview; CSP-mitigated (script-src 'self' → needs a separate XSS gadget to reach). Full fix = correlate to the pending approval; deferred as disproportionate. Documented in approval.rs.

Verification

server + apps/macos cargo check ✓ · 18 Rust unit tests ✓ · tsc ✓ · full tauri build (.app + .dmg) ✓ · adversarial review (5 dimensions, each finding verified — 3 fixed, 2 rejected).

⚠️ Needs live verification: every native/UI path (deep link, Finder open, dock badge, screencapture, FSEvents, tray) only works from a bundled .app, not tauri dev. Screen capture was unavailable during development, so none of the native behavior was driven live — reviewer should exercise the checklist on a built .app.

🤖 Generated with Claude Code

haowei2000 and others added 3 commits July 17, 2026 16:26
Mobile strategy phase 1 (docs/ROADMAP.md "Client Strategy & Boundaries"):
the web app becomes installable, and the two events that block or summon a
human — a pending permission card and an @mention — now reach the lock
screen and deep-link back to the exact message.

Gateway:
- infra/web_push.rs: pure-Rust RFC 8291 aes128gcm + RFC 8292 VAPID sender
  (p256/hkdf/aes-gcm, no openssl); pinned by the RFC 8291 Appendix A test
  vector. Hardened outbound client (no redirects, 5s/10s timeouts); 404/410
  self-cleans dead subscriptions; suspended/deleted users filtered.
- api/push.rs + migration 0050: VAPID key endpoint and per-user
  subscription registry (endpoint PK upsert, key validation at write time).
- Triggers: permission_request → approver set (owner + delegations, text
  truncated to notification size); @mention → human REST sends, bot
  post_message, and bridge send/done frames.
- Session-revocation parity: password change/reset, logout, and suspension
  drop the user's push subscriptions.
- Helm: optional secrets.vapidPrivateKey / gateway.vapidSubject; secret
  checksum annotation so enabling push actually rolls the gateway.

Frontend:
- vite-plugin-pwa (injectManifest) + manifest/icons/theme-color; precache
  kept to the real app shell (lazy heavyweights excluded, ~2.1 MB).
- src/sw.ts: push handler with focused-channel suppression (MessageChannel
  query), notificationclick routing that prefers a /chat window.
- src/lib/push.ts: subscribe lifecycle (active-SW readiness race, rollback
  on failed server registration), SW⇄page bridge, deep-link target parked
  until the channel's history is loaded, DM links filed under the viewer's
  personal workspace.
- Settings → Account: push toggle (hidden when unsupported/unconfigured);
  sign-out paths unsubscribe first.
- nginx (image + Helm chart): no-cache for sw.js, correct MIME + no-cache
  for manifest.webmanifest.

Verified on the kind stack: RFC vector unit test, 174 lib tests, live
mention → encrypted POST to push service → Gone cleanup, logout revocation,
SW activated + manifest installable in-browser.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…laboration features

A Tauri v2 desktop shell that hosts the existing React frontend and acts as the
graphical home of the local ACP connector daemon. Control plane stays on the
gateway: messages and permission decisions always go through the gateway API —
the desktop only displays richer local context and runs native pickers.

Foundation
- apps/macos Tauri shell: tray residency, close-to-hide, single-instance,
  ⌥⌘C toggle, launch-at-login, native notifications (WKWebView has no Web Push,
  so approvals/@mentions arrive over a user-scoped WS notification frame).
- Runtime-configurable gateway address (serverConfig.ts) + first-run ServerPicker;
  serverBase=null preserves the browser behavior exactly (web zero-regression).
- Gateway: spawn_notify_users_ws user-scoped frames alongside Web Push; Tauri
  origins allow-listed in CORS.

Connector management (M1/M2)
- List/start/stop/restart/logs/delete daemons + a supervisor that revives managed
  instances (macOS answer to the systemd-linger pitfall), with backoff.
- Form-based config editor (structure-preserving toml_edit), agent picker with
  brand icons + one-click npm install, native folder pickers for roots/cwd.
- Grid connector page; onboarding via gateway mint→redeem; bot↔connector linkage
  ("Set up on this Mac"); open agent files locally (same-machine) or download.

Native collaboration + AI-ops features
- A2 agent-changes watch (FSEvents) + git status/diff/revert/open-PR, path-guarded.
- A3 drag-to-grant: drop a Finder folder on a card → allowed_roots + restart.
- A4 read-only audit timeline parsed from the connector log.
- A1a informed approval: native banner shows the target's branch/dirty/size.
- B5 cheers:// deep links (route via push) + ⌥⌘K quick-panel window.
- B6 screenshot (screencapture -i) + Finder "Open With → Cheers", both uploaded
  through the existing gateway path; cold-start files survive via a pending stash.
- B7 tray roster + dock badge (unread + pending approvals).
- C8 per-connector health (process-group CPU/mem, hung detection).
- C9 one-click ACP adapter update check + upgrade.

Personal workbench plugins
- User-installed (this-Mac) renderer plugins under ~/.cheers/plugins, no admin —
  the middle tier between session-temp and admin-global; install-time trust prompt.

Deferred (documented): A1b native notification action-buttons (deprecated macOS
API) and C10 local voice (native-heavy). Known LOW tradeoff: permission_context
is an ungated metadata oracle, CSP-mitigated (see approval.rs).

Verified: server + tauri cargo check, 18 Rust unit tests, tsc, full tauri build
(.app + .dmg). Native/UI paths are needs-live-verify and only work from a bundled
.app (not tauri dev); adversarial review ran (3 findings fixed, 2 rejected).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@haowei2000
haowei2000 force-pushed the feat/desktop-macos branch from b720b0a to d90ee6a Compare July 18, 2026 03:44
haowei2000 and others added 4 commits July 18, 2026 11:45
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…notes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The remote-workspace file preview header wrapped its text buttons in a narrow
panel. Make Download / Save / Add-to-context icon-only with hover tooltips, and
replace the single 'Open in editor' dropdown with one brand-accent icon button
per available opener (Finder + installed editors). Filename shrinks first
(min-w-0), every action is shrink-0, so the row stays on one line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a Feature Tour section (EN + zh) covering the macOS desktop client
(connector management, native notifications, same-machine open-in-editor /
watch+diff / drag-to-grant / screenshot-send / tray+dock) and the installable
PWA, plus a matching feature card on the GitHub Pages landing page. Links to
Releases for the .dmg download.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@haowei2000
haowei2000 merged commit 8df9f79 into develop Jul 18, 2026
12 checks passed
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.

1 participant