cc-haha/docs/desktop/02-architecture.md
程序员阿江(Relakkes) 81845fbc49 fix: finalize Electron migration boundaries
Complete the Electron replacement boundary before merging by removing the renderer-side Tauri host fallback, tightening H5/browser access so only desktop navigation is tokenless, and moving desktop release publication to a tag-driven GitHub Actions matrix with a single final publish job.

Constraint: H5/browser capability access must not gain tokenless access through localhost or retired Tauri origins

Constraint: Desktop release artifacts must be built by GitHub Actions from version tags, not treated as local build outputs

Rejected: Keep localhost browser origins trusted for convenience | local browser contexts can access loopback services and must use the H5 token path

Rejected: Publish from each matrix job | partial releases can be created before all platforms finish

Confidence: high

Scope-risk: broad

Directive: Do not reintroduce Tauri origins or localhost browser origins into the trusted desktop origin set without a reviewed security design

Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/diagnostics-service.test.ts src/server/middleware/cors.test.ts

Tested: bun test scripts/pr/release-workflow.test.ts scripts/release-update-metadata.test.ts

Tested: bun run check:desktop

Tested: bun run check:native

Tested: git diff --check

Not-tested: bun run check:server is blocked by expired quarantine entries server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
2026-06-02 22:42:53 +08:00

16 KiB
Raw Blame History

架构设计

从 Electron host 到 CLI 子进程,桌面端的三层通信架构。


技术栈

前端

技术 版本 职责
React 18 UI 框架
Zustand 5 状态管理
Vite 6 构建工具
Tailwind CSS 4 样式
Shiki 4 代码高亮
Mermaid 11 图表渲染
marked + DOMPurify - Markdown 渲染
react-diff-viewer 4 Diff 展示
Lucide React - 图标

桌面层

技术 版本 职责
Electron - 跨平台桌面 host管理 Chromium renderer、窗口、IPC 和系统能力
electron-builder - macOS/Windows/Linux 打包与 release artifact 生成
electron-updater - 自动更新检查、下载与安装
node-pty - Electron main 中的终端 PTY runtime
desktop/src-tauri 资源目录 - 历史资源位置,暂存 sidecar 二进制、图标和 preview agent不再作为桌面 runtime

服务端

技术 职责
Bun 运行时 + 构建工具
Bun.serve HTTP/WebSocket 服务器

字体

  • Inter — 正文
  • Manrope — 标题
  • JetBrains Mono — 代码
  • Material Symbols Outlined — 图标

三层架构

┌─────────────────────────────────────┐
│         Electron Host (main/preload) │
│  ┌─────────────────────────────┐    │
│  │   Chromium Renderer         │    │  ← React 用户界面
│  └───────────┬─────────────────┘    │
│              │ HTTP + WebSocket      │
│  ┌───────────▼─────────────────┐    │
│  │   Server Sidecar (Bun)      │    │  ← API 服务 + 会话管理
│  │   Port: 动态分配             │    │
│  └───────────┬─────────────────┘    │
│              │ 子进程 spawn          │
│  ┌───────────▼─────────────────┐    │
│  │   CLI 子进程                 │    │  ← AI 对话 + 工具执行
│  └─────────────────────────────┘    │
│                                     │
│  ┌─────────────────────────────┐    │
│  │   Adapter Sidecar (可选)    │    │  ← Telegram/飞书接入
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

第一层Electron Host

职责窗口管理、Sidecar 进程编排、原生 API 桥接

核心文件位于 desktop/electron/,通过 preload 暴露 window.desktopHostrenderer 只调用 typed host contract不直接调用 Electron IPC。

能力 说明
runtime.getServerUrl 前端获取 Server Sidecar 的 HTTP 地址
dialogs.open/save 系统文件/目录选择与保存对话框
shell.open/openPath 外链与本地路径打开main 侧做 scheme/path allowlist
updates.check/download/install electron-updater 检查、下载、安装与 relaunch
terminal.* node-pty 会话创建、写入、resize、kill 和事件转发
preview.* WebContentsView 预览面板与 preview-agent message bridge
appMode.* 默认/便携模式和 CLAUDE_CONFIG_DIR 启动语义

状态管理

模块 说明
serverRuntime.ts server/adapters sidecar 生命周期、动态端口、日志和 env 注入
sidecarManager.ts sidecar 二进制定位、参数构造、子进程管理
windows.ts 窗口状态持久化、显示/聚焦、close-to-hide 生命周期
updater.ts 更新元数据归一化、下载进度和安装前状态校验

启动流程

  1. 应用启动时解析便携模式,必要时设置 CLAUDE_CONFIG_DIR
  2. 创建 BrowserWindow 并注入 preload hostdev 模式只允许 localhost rendererpackaged 模式加载 app.asar/dist/index.html
  3. renderer 调用 runtime.getServerUrl()Electron host 启动 claude-sidecar server --app-root ... --host 0.0.0.0 --port {dynamic}
  4. server 健康检查通过后React 继续加载设置、会话和 WebSocket。
  5. 修改 IM adapter 配置后renderer 调用 adapters.restartSidecar()host 重启 claude-sidecar adapters 并注入 ADAPTER_SERVER_URL

退出处理:窗口默认 close-to-hide明确退出、更新安装或 app mode restart 前会停止 server/adapters sidecar。

平台差异

  • macOSoverlay titlebar + 自定义菜单(关于、设置 Cmd+,、编辑、窗口)
  • Windowsset_decorations(false) 隐藏原生标题栏,前端自定义渲染 TitleBar + WindowControls

第二层Server Sidecar

职责HTTP REST API + WebSocket 网关 + 会话管理 + 协议代理

核心文件 src/server/(项目根目录),分层结构:

src/server/
├── index.ts              # 入口
├── server.ts             # HTTP 服务器
├── router.ts             # 路由注册
├── sessionManager.ts     # 会话管理器
├── api/                  # REST 路由层 (14 个模块)
├── services/             # 业务服务层 (14 个模块)
├── ws/                   # WebSocket 处理
├── proxy/                # API 代理Anthropic/OpenAI 协议转换)
├── middleware/            # auth、cors、errorHandler
└── config/               # Provider 预设

第三层CLI 子进程

职责AI 对话核心、工具执行、Agent 编排

Server 为每个 Session spawn 一个 CLI 子进程,通过 stdin/stdout JSON 通信。

Sidecar 构建

使用 Bun 编译为独立二进制(desktop/scripts/build-sidecars.ts

三种模式共用一个入口 desktop/sidecars/claude-sidecar.ts

  • server — 启动 HTTP/WS 服务
  • cli — 启动 CLI 子进程
  • adapters — 启动 IM 适配器(解析 --feishu/--telegram 参数,检查凭据后按需加载)

编译产物仍放置在 desktop/src-tauri/binaries/作为历史稳定资源目录Electron Builder 通过 filesasarUnpack 把它们打入 app.asar.unpacked


WebSocket 通信协议

连接地址

ws://127.0.0.1:{port}/ws/{sessionId}

前端通过 WebSocketManagerper-session 连接管理支持自动重连、消息队列缓冲、ping 心跳保活。

客户端 → 服务端

type 说明
user_message 用户消息(含 content, attachments
permission_response 权限审批requestId, allowed, rule
set_permission_mode 切换权限模式
stop_generation 停止生成
ping 心跳

服务端 → 客户端

type 说明
connected 连接成功
status 状态变更thinking/generating
content_start / content_delta 流式文本
thinking Extended Thinking
tool_use_complete 工具调用就绪
tool_result 工具执行结果
permission_request 权限请求
message_complete 消息完成(含 Token 统计)
error 错误通知
session_title_updated 标题更新
team_update / team_created / team_deleted 团队事件
task_update 任务变更
pong 心跳响应

连接管理

  • 心跳30s 间隔 ping/pong
  • 重连:指数退避 min(1000ms × 2^n, 30000ms),最多 10 次
  • 缓冲:未连接时消息暂存队列,恢复后自动发送

HTTP API

会话

方法 端点 说明
GET /api/sessions 列表(支持 project/limit/offset 筛选)
POST /api/sessions 创建
GET /api/sessions/:id/messages 历史消息
PATCH /api/sessions/:id 重命名
DELETE /api/sessions/:id 删除
GET /api/sessions/:id/git-info Git 信息
GET /api/sessions/:id/slash-commands 可用斜杠命令
GET /api/sessions/recent-projects 最近项目

模型与提供商

方法 端点 说明
GET/PUT /api/models/current 当前模型
GET /api/models 可用模型列表
GET/PUT /api/effort Effort 级别
GET/POST/PUT/DELETE /api/providers 提供商 CRUD
POST /api/providers/:id/activate 激活
POST /api/providers/:id/test 测试连接
GET /api/providers/presets 预设列表

定时任务

方法 端点 说明
GET/POST/PUT/DELETE /api/scheduled-tasks 任务 CRUD
POST /api/scheduled-tasks/:id/run 手动运行
GET /api/scheduled-tasks/runs 运行记录

其他

方法 端点 说明
GET /api/teams Agent 团队
GET /api/teams/:name/members/:agentId/transcript 成员转录
GET /api/agents Agent 定义
GET /api/skills 技能列表
GET/PUT /api/adapters 适配器配置
GET/PUT /api/settings/user 用户设置
GET/PUT /api/permissions/mode 权限模式
GET /api/tasks/lists CLI 任务清单
GET /health 健康检查

状态管理

使用 Zustand按领域拆分为 12 个 Store

Store 核心状态
chatStore per-session 消息、流式状态、权限请求、Token 统计
sessionStore 会话列表、activeSessionId、项目筛选
tabStore 标签页顺序localStorage 持久化)
settingsStore 权限模式、当前模型、effort、语言
providerStore Provider 列表、activeId
uiStore 主题、侧边栏、activeView、Toast、设置标签页
taskStore 定时任务、运行记录
teamStore 团队列表、成员转录轮询
agentStore Agent 定义列表
skillStore 技能元数据、详情
adapterStore 适配器配置、配对码
cliTaskStore per-session CLI 任务追踪

数据流

用户操作 → Component → Store → API/WebSocket → Server → Store → Component 重渲染

持久化

数据 存储
标签页状态 localStorage
语言偏好 localStorage
会话数据 Server JSONL (~/.claude/sessions/)
设置 Server API
适配器配置 ~/.claude/adapters.json

协议代理层

Server 内置代理层(src/server/proxy/),统一不同 AI 提供商的 API 格式。

支持格式

格式 典型提供商
anthropic Anthropic、OpenRouter、MiniMax
openai_chat OpenAI、DeepSeek、Ollama
openai_responses OpenAI Responses API

模型映射

每个 Provider 配置 4 个模型槽位:mainhaikusonnetopus,前端按槽位名调用,代理层自动映射为实际模型名。


适配器架构

适配器系统让 Telegram/飞书等 IM 平台接入 Claude Code。

IM 平台 → Adapter 进程 → HTTP + WebSocket → Server → CLI

共享模块 adapters/common/

模块 职责
config.ts 配置加载env > JSON > 默认值)
ws-bridge.ts WebSocket 桥接(心跳、重连、消息路由)
pairing.ts 用户配对6 位安全码、速率限制)
session-store.ts Chat → Session 映射持久化
message-buffer.ts 流式缓冲500ms / 200 字符阈值)
message-dedup.ts 消息去重
chat-queue.ts 同一 Chat 消息串行队列
http-client.ts HTTP 客户端

分片限制

  • Telegram: 4000 字符/消息
  • 飞书: 30000 字符/消息

项目目录结构

desktop/
├── src/                              # React 前端
│   ├── api/                         #   API 客户端 (15 个模块)
│   ├── components/
│   │   ├── layout/                  #   AppShell, Sidebar, TabBar, TitleBar,
│   │   │                            #   WindowControls, StatusBar, ContentRouter,
│   │   │                            #   ProjectFilter
│   │   ├── chat/                    #   ChatInput, MessageList, AssistantMessage,
│   │   │                            #   ToolCallBlock, ToolCallGroup, ThinkingBlock,
│   │   │                            #   PermissionDialog, CodeViewer, DiffViewer,
│   │   │                            #   ImageGalleryModal, StreamingIndicator ...
│   │   ├── shared/                  #   Button, Modal, Toast, Spinner,
│   │   │                            #   UpdateChecker, DirectoryPicker ...
│   │   ├── controls/                #   ModelSelector, PermissionModeSelector
│   │   ├── markdown/                #   MarkdownRenderer, MermaidRenderer
│   │   ├── skills/                  #   SkillList, SkillDetail
│   │   ├── tasks/                   #   TaskList, TaskRow, NewTaskModal,
│   │   │                            #   DayOfWeekPicker, PromptEditor ...
│   │   └── teams/                   #   TeamStatusBar
│   ├── pages/                       #   ActiveSession, EmptySession, Settings,
│   │                                #   AdapterSettings, AgentTeams, ScheduledTasks,
│   │                                #   ComputerUseSettings, ToolInspection ...
│   ├── stores/                      #   12 个 Zustand store
│   ├── types/                       #   TypeScript 类型 (9 个模块)
│   ├── hooks/                       #   useKeyboardShortcuts
│   ├── i18n/                        #   中/英 国际化
│   ├── config/                      #   providerPresets, spinnerVerbs
│   └── lib/                         #   desktopRuntime, cronDescribe, parseRunOutput
├── src-tauri/
│   ├── binaries/                    #   Electron 打包使用的 sidecar 二进制
│   ├── resources/preview-agent.js   #   预览页面注入脚本
│   ├── src/main.rs                  #   历史 Tauri 入口,非当前 runtime
│   ├── src/lib.rs                   #   历史 Tauri host非当前 runtime
│   ├── Cargo.toml
│   └── tauri.conf.json
├── electron/
│   ├── main.ts                      #   Electron main 入口
│   ├── preload.ts                   #   renderer host bridge
│   ├── preview-preload.ts           #   preview WebContentsView bridge
│   ├── ipc/                         #   channel registry + payload validators
│   └── services/                    #   dialogs, shell, updater, terminal, preview ...
├── sidecars/
│   └── claude-sidecar.ts            #   统一入口 (server/cli/adapters)
└── scripts/
    ├── build-sidecars.ts
    ├── build-macos-arm64.sh
    └── build-windows-x64.ps1

src/server/                           # 服务端(项目根目录)
├── api/                             #   REST 路由 (14 个模块)
├── services/                        #   业务服务 (14 个模块)
├── ws/                              #   WebSocket 处理
├── proxy/                           #   协议代理转换
├── middleware/                      #   auth, cors, errorHandler
└── config/                          #   Provider 预设

adapters/                             # IM 适配器
├── common/                          #   共享模块 (8 个)
├── telegram/                        #   Telegram Bot
└── feishu/                          #   飞书 Bot