程序员阿江(Relakkes) 3e3c2bc116 experiment(desktop): auto-spawn adapter sidecar on launch + restart on save
Wires the new claude-sidecar adapters mode into the actual desktop UX:

* On app launch, Tauri main process now spawns the adapter sidecar right
  after the server sidecar comes up. The sidecar reads ~/.claude/adapters.json
  and connects whichever of Feishu / Telegram has credentials configured;
  if neither does, it warns + skips + exits cleanly (treated as expected).
* When the user saves credentials in the existing AdapterSettings page,
  the frontend store invokes restart_adapters_sidecar after the PUT
  /api/adapters succeeds. Tauri kills the old child and spawns a new one,
  which picks up the fresh config and establishes the WebSocket connection
  to Feishu / Telegram immediately — no app restart needed.

End-to-end behavior is now: install app → open → configure credentials in
settings → click save → IM bot is live.

Implementation
==============

* desktop/src-tauri/capabilities/default.json: replace the stale
  binaries/claude-server allowlist with binaries/claude-sidecar across
  shell:allow-execute, shell:allow-spawn, plus a new shell:allow-kill
  entry needed for the restart path. (P2 changed externalBin to
  claude-sidecar but missed updating capabilities, which is why the prior
  bundle worked at all in dev mode but would have failed in production.)

* desktop/src-tauri/src/lib.rs:
  - new AdapterState that holds an Option<CommandChild>
  - start_adapters_sidecar() spawns `claude-sidecar adapters --feishu --telegram`
    with ADAPTER_SERVER_URL env var pointing at the dynamic server port
    (converted http://→ws:// since WsBridge does `new WebSocket(url)`
    directly without protocol translation)
  - spawn_and_track_adapters_sidecar() handles spawn + state insertion
  - stop_adapters_sidecar() kills + clears state
  - new #[tauri::command] restart_adapters_sidecar that calls stop+spawn
  - sidecar Terminated events are info-logged, not treated as errors,
    so the credential-missing path doesn't show up as a crash
  - setup() spawns the adapter sidecar after server startup completes
  - RunEvent::Exit cleanup also kills adapter sidecar

* desktop/src/stores/adapterStore.ts: after every successful PUT
  /api/adapters, dynamic-import @tauri-apps/api/core and call
  invoke('restart_adapters_sidecar'). Wrapped in try/catch so non-Tauri
  test environments fall through quietly. Triggers on every config
  change (including pairing code generation, paired-user removal) by
  design — keeps the rule simple and guarantees any save takes effect.

* desktop/src/pages/AdapterSettings.tsx: removed the stale "Server URL"
  text input. The field defaulted to ws://127.0.0.1:3456 but the actual
  server uses a dynamic port chosen at startup. Even when filled in
  correctly, loadConfig() in adapters/common/config.ts gives env var
  priority over file value, so this UI control had zero effect inside
  the desktop app. Standalone-mode adapter users can still edit the
  field directly in adapters.json if they need to.

Bundle size: unchanged at 88 MB .app / 37 MB DMG. The Rust changes
add only a few KB to the desktop main binary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 00:55:44 +08:00
..