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
16 KiB
架构设计
从 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.desktopHost,renderer 只调用 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 |
更新元数据归一化、下载进度和安装前状态校验 |
启动流程:
- 应用启动时解析便携模式,必要时设置
CLAUDE_CONFIG_DIR。 - 创建
BrowserWindow并注入 preload host;dev 模式只允许 localhost renderer,packaged 模式加载app.asar/dist/index.html。 - renderer 调用
runtime.getServerUrl(),Electron host 启动claude-sidecar server --app-root ... --host 0.0.0.0 --port {dynamic}。 - server 健康检查通过后,React 继续加载设置、会话和 WebSocket。
- 修改 IM adapter 配置后,renderer 调用
adapters.restartSidecar(),host 重启claude-sidecar adapters并注入ADAPTER_SERVER_URL。
退出处理:窗口默认 close-to-hide;明确退出、更新安装或 app mode restart 前会停止 server/adapters sidecar。
平台差异:
- macOS:overlay titlebar + 自定义菜单(关于、设置 Cmd+,、编辑、窗口)
- Windows:
set_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 通过 files 和 asarUnpack 把它们打入 app.asar.unpacked。
WebSocket 通信协议
连接地址
ws://127.0.0.1:{port}/ws/{sessionId}
前端通过 WebSocketManager(per-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 个模型槽位:main、haiku、sonnet、opus,前端按槽位名调用,代理层自动映射为实际模型名。
适配器架构
适配器系统让 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