5 Commits

Author SHA1 Message Date
程序员阿江(Relakkes)
e58ec4f649 experiment(desktop): add adapters mode to claude-sidecar (Feishu + Telegram)
The Feishu and Telegram IM adapters used to be standalone Bun processes
that the user had to launch manually with bun + .ts source on disk —
which meant they were effectively unreachable from the bundled desktop
app, since the user wouldn't have bun or the source tree.

This adds them as a third mode of the merged claude-sidecar binary:

  claude-sidecar adapters --app-root <path> [--feishu] [--telegram]

The launcher pre-checks credentials via the same `loadConfig()` the
adapters use internally, then conditionally `await import()`s each
enabled adapter whose creds are present. Adapters with missing creds
are warned and skipped, so a partially-configured user (e.g. only
Feishu set up, no Telegram bot token) still gets the working adapter
started cleanly instead of having Telegram's top-level
`process.exit(1)` kill the whole process.

Adapter source code is unchanged — the adapters still self-start at
top-level via Lark.WSClient.start() / grammy bot.start(). Their
SIGINT handlers also still register independently. The only thing
gating runtime is whether bun's static-import follows the dynamic
specifier into adapters/feishu/index.ts and adapters/telegram/index.ts,
which it does.

Bundle impact
=============

  metric          P0+P2 only   +adapters mode   delta
  claude-sidecar  66 MB        68 MB            +2 MB
  .app total      87 MB        88 MB            +1 MB
  .dmg            37 MB        37 MB            0 MB

Both adapter SDKs (@larksuiteoapi/node-sdk and grammy) statically
inline into the binary at a +2 MB cost, fully absorbed by DMG
compression. Compared to the alternative (a separate ~60 MB sidecar
binary per adapter, or even one combined ~60 MB adapter binary)
this is essentially free.

Verification
============

* `claude-sidecar server` regression test still passes (boots, /api/sessions
  → 200, CronScheduler runs)
* `claude-sidecar cli --version` returns 999.0.0-local
* `claude-sidecar adapters` (no flags) → exit 2 with usage error
* `claude-sidecar adapters --feishu --telegram` (no creds) → both warned and
  skipped, exit 1
* `claude-sidecar adapters --feishu` (FEISHU_APP_ID=cli_fake creds) →
  Feishu adapter boots, Lark client `client ready`, attempts API connect,
  fails with 400 from feishu API and gracefully retries (correct
  behavior — fake creds)
* `claude-sidecar adapters --telegram` (TELEGRAM_BOT_TOKEN=fake:token) →
  grammy bot.start() called, getMe API hits with 404, throws GrammyError
  (correct — fake creds)
* `bun test adapters/` → 299 pass / 0 fail
* `bun test src/` → 358 pass / 45 fail / 2 errors, identical to baseline

Scanner change
==============

desktop/scripts/scan-missing-imports.ts now also walks adapters/ in
addition to src/, so any future feature() gated stubs in the adapter
tree get auto-stubbed. As of this commit, adapters/ has 0 missing
imports — all clean.

Next step (UI integration, not done here)
==========================================

To actually wire this into the desktop UX, the Tauri main process needs:
- A "Configure IM adapters" settings page (App ID/Secret, bot token,
  allowed users) that writes ~/.claude/adapters.json
- A "Start/stop Feishu" / "Start/stop Telegram" toggle that spawns
  `claude-sidecar adapters --feishu` (or both) as a managed sidecar,
  monitors lifecycle, restarts on crash

The runtime infrastructure is now in place — that work is purely UI +
Rust spawn glue and can be done independently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 00:44:35 +08:00
程序员阿江(Relakkes)
3c5549e221 experiment(desktop): merge server + cli into one sidecar binary
Replaces the two ~65MB bun-compiled sidecar binaries with a single
~66MB merged binary. The bun runtime + shared dependency code (anthropic
SDK, MCP SDK, ws, undici, etc) was previously duplicated across both —
merging eliminates that duplication entirely.

Combined with the previous P0 commit (static-import inlining + drop
src/ + node_modules/ from Resources), this brings the macOS .app from
the original 435MB baseline down to 87MB (-80%), and the DMG from 113MB
to 37MB (-67%).

Final breakdown of the 87MB .app:
  Contents/MacOS/claude-sidecar         66MB   (was 57+57=114MB)
  Contents/MacOS/claude-code-desktop    18MB   (Tauri Rust main)
  Contents/Resources/icon.icns           2MB
  + plist + frameworks                  ~1MB

This is essentially the floor — bun runtime + Tauri main + minimum
overhead. Going lower would require swapping toolchains.

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

* desktop/sidecars/claude-sidecar.ts (new): single entrypoint that
  takes a positional mode argument ("server" or "cli") then dispatches
  via `await import('../../src/server/index.ts').startServer()` or
  `await import('../../src/entrypoints/cli.tsx')`. Same env / argv setup
  pattern as the old launchers.
* desktop/sidecars/server-launcher.ts + cli-launcher.ts: deleted.
* desktop/scripts/build-sidecars.ts: compiles only claude-sidecar now.
* desktop/src-tauri/tauri.conf.json: externalBin → ["binaries/claude-sidecar"]
* desktop/src-tauri/src/lib.rs: spawns sidecar with leading "server"
  positional arg.
* src/server/services/conversationService.ts resolveBundledCliPath /
  resolveCliArgs: when current process is claude-sidecar, reuses the
  same exe and spawns it with leading "cli" positional arg. Backward
  compat path for old claude-server / claude-cli pair preserved for the
  bin/claude-haha dev mode.

Verification
============

* claude-sidecar cli --version → 999.0.0-local (Claude Code) ✓
* claude-sidecar cli --help → full Commander spec ✓
* claude-sidecar server --port N → HTTP listening, CronScheduler running ✓
* All three above run in /tmp with no src/ or node_modules/ on disk
* bun test on src/ → 358 pass / 45 fail / 1 error, identical to baseline
  (44 fails are pre-existing on main, unrelated to this change)
* Full DMG round-trip via build-macos-arm64.sh succeeds; new .app
  installs cleanly in /Volumes/

Bundle size summary (vs original baseline)
==========================================

  metric              baseline   final      delta
  .app total          435 MB     87 MB      -348 MB (-80%)
  .dmg                113 MB     37 MB      -76 MB  (-67%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 00:10:47 +08:00
程序员阿江(Relakkes)
662485c3eb experiment(desktop): static-import sidecars + drop src/ + node_modules/ from bundle
Cuts the macOS .app from 435MB → 152MB (-65%) and the DMG from 113MB → 60MB
(-47%) by inlining src/server and src/entrypoints/cli into the bun-compiled
sidecar binaries instead of dynamic-importing them from disk at runtime.

Architectural change
====================

Before:
  desktop/sidecars/server-launcher.ts → bun build --compile (≈57MB shell)
    └─ at runtime: dynamic file:// import of <appRoot>/src/server/index.ts
       which transitively requires ALL of src/ + the entire root node_modules/
       to be shipped as Resource. tauri.conf.json copied 254M of node_modules
       and 47M of src/ into Contents/Resources/app/ on every build.

After:
  desktop/sidecars/server-launcher.ts → bun build --compile (≈65MB)
    └─ uses `await import('../../src/server/index.ts')` with a literal
       specifier so bun's bundler walks the whole graph statically and
       inlines everything into the binary.

  Same treatment for cli-launcher.ts → src/entrypoints/cli.tsx.

Resolver gymnastics
===================

This fork carries dozens of ant-internal feature() gated require/import
calls referencing modules that simply don't exist on disk
(cachedMicrocompact, devtools, proactive, coordinator, etc). Bun's resolver
walks the static dep graph BEFORE bun:bundle macro DCE, so even though
the dead branches never execute at runtime, they still fail to resolve
at compile time.

Two complementary mechanisms:

1. desktop/scripts/scan-missing-imports.ts walks src/, regex-greps every
   relative import / require / type-import specifier, and writes a Proxy
   noop stub for any target that doesn't exist on disk. Stubs are tagged
   with "@generated stub from scan-missing-imports" for idempotency. Text
   resources (.md / .txt / .json) get appropriate format-specific stubs.
   Runs as a pre-step inside build:sidecars.

2. desktop/scripts/build-sidecars.ts adds an `external: [...]` list for
   bare-package optional deps not in package.json (OTLP exporters,
   @aws-sdk/*, @anthropic-ai/{bedrock,vertex,foundry,mcpb}-sdk,
   @azure/identity, fflate, turndown, sharp, react-devtools-core).
   These remain runtime imports, fail benignly when their gating env
   var or feature flag is off.

Tauri side
==========

- desktop/src-tauri/tauri.conf.json: dropped all `resources` entries.
  Was 7 entries totaling ≈301MB. Now `{}`.
- desktop/src-tauri/src/lib.rs `resolve_app_root` no longer calls
  BaseDirectory::Resource (the app/ resource dir doesn't exist anymore);
  instead returns the directory of the current sidecar exe. The launchers
  still accept --app-root for backward compat with conversationService's
  CLI subprocess spawn.

Optimisations
=============

- bun build now uses minify whitespace+identifiers+syntax. Saved another
  ≈16MB across both binaries (server: 72MB→65MB, cli: 75MB→66MB).

Bonus fix
=========

src/services/remoteManagedSettings/index.ts had a typo importing
'./securityCheck.jsx' instead of '.js'. Bun's runtime resolver tolerated
it; bun build didn't.

Verification
============

- Both binaries boot successfully in /tmp with no src/ or node_modules/
  on disk. Verified `claude-cli --version` returns the build version,
  `claude-cli --help` prints the full Commander spec, and claude-server
  starts CronScheduler + listens on the requested port.
- bun test on src/ shows 358 pass / 45 fail / 2 errors vs main baseline
  of 359 / 44 / 2 — net 0 new failures (1 different flake direction).
  All 44 baseline failures pre-exist on main and are unrelated.
- Full DMG round-trip via build-macos-arm64.sh succeeds; new bundle
  installs cleanly in /Volumes/.

Bundle size summary
===================

  metric              baseline   after P0   delta
  Resources/app/      301 MB     0 MB       -301 MB
  MacOS/claude-server  57 MB     65 MB      +8 MB
  MacOS/claude-cli     57 MB     66 MB      +9 MB
  MacOS/claude-code-desktop  18 MB  18 MB   —
  ─────────────────────────────────────────────
  .app total          435 MB     152 MB     -283 MB (-65%)
  .dmg                113 MB     60 MB      -53 MB (-47%)

Generated stub files (173 of them under src/) are committed for
reproducibility — the scanner is idempotent and will re-create them
identically on every build, but tracking them avoids dirty working trees
on first compile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 00:05:39 +08:00
程序员阿江(Relakkes)
635a966c3e fix(desktop): unblock rollout with reliable session and IM flows
This folds together the desktop-side fixes needed before broader rollout.
Session resume no longer deadlocks waiting on init, Mermaid and inline image
output render inside chat, task and sub-agent state stay visible during
execution, local build/release paths are safer, and Feishu/Telegram now expose
lightweight mobile commands (/help, /status, /clear) without adding a new
adapter-specific protocol.

Constraint: Desktop releases must publish updater artifacts from non-draft GitHub releases
Constraint: IM commands need short, phone-friendly responses and low operational complexity
Rejected: Add a dedicated IM command API surface | re-used existing slash commands and session/task REST endpoints to keep adapters thin
Rejected: Wait for task_update push events in WebUI | added low-risk polling because the current frontend ignores that event path
Confidence: medium
Scope-risk: broad
Reversibility: clean
Directive: Keep IM command replies terse and mobile-first, and merge local fallback slash commands when server-provided lists are partial
Tested: cd desktop && bun x vitest run src/components/chat/MermaidRenderer.test.tsx src/components/markdown/MarkdownRenderer.test.tsx
Tested: cd desktop && bun x vitest run src/components/chat/composerUtils.test.ts src/pages/ActiveSession.test.tsx src/stores/chatStore.test.ts
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "SDK init arrives only after the first user turn" --timeout 60000
Tested: cd adapters && bun test common/ feishu/ telegram/
Tested: cd adapters && bunx tsc --noEmit
Not-tested: Full GitHub Actions release run on all three desktop platforms
Not-tested: Local DMG packaging end-to-end on Apple Silicon
Not-tested: Real Feishu/Telegram device sessions against a live adapter process
2026-04-10 16:41:59 +08:00
程序员阿江(Relakkes)
f9c42c3b40 feat: Tauri desktop app with sidecar, brand identity, and CORS fix
- Add Tauri sidecar architecture: Rust shell spawns claude-server binary,
  dynamic port allocation, health-check wait loop, graceful shutdown
- Fix CORS middleware to accept `tauri://localhost` and `https://tauri.localhost`
  origins from Tauri WebView, and add CORS headers to /health endpoint
- Enable native macOS window decorations (traffic lights) with Overlay title bar,
  add data-tauri-drag-region on sidebar for window dragging
- Conditionally apply desktop-only padding (44px for traffic lights) vs web (12px)
- Generate brand identity: light-background app icon, horizontal logo, full icon
  set (icns/ico/png) for Tauri bundle
- Add brand mark + GitHub link in sidebar, replace mascot SVG with app icon
  in EmptySession page
- Update README (zh/en) and docs hero image with new branding
- Add sidecar build scripts and launcher entry points
- Gitignore Rust target/, Tauri gen/, and brand-assets candidates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 16:07:38 +08:00