# Claude Code 桌面端 — 架构设计
> 从 Tauri 窗口到 CLI 子进程,深入理解桌面端的三层通信架构。
技术栈 · 三层架构 · WebSocket 协议 · HTTP API · 状态管理 · 会话生命周期 · 协议转换 · 适配器架构

---
## 一、技术栈全景
Claude Code 桌面端的技术选型强调**轻量、高性能、跨平台**:
### 前端层
| 技术 | 版本 | 职责 |
|------|------|------|
| **React** | 18.3.1 | UI 框架,函数式组件 + Hooks |
| **Zustand** | 5.0.3 | 轻量级状态管理,零样板代码 |
| **Vite** | 6.0.7 | 构建工具,HMR 热更新 |
| **Tailwind CSS** | 4.0 | 原子化样式框架 |
| **Shiki** | 4.0.2 | VS Code 级别代码语法高亮 |
| **react-diff-viewer** | 4.2.0 | Diff 变更展示 |
| **marked** | 15.0.7 | Markdown 解析渲染 |
### 桌面层
| 技术 | 版本 | 职责 |
|------|------|------|
| **Tauri** | 2.x | 跨平台桌面框架(Rust) |
| **tauri-plugin-shell** | 2.x | Sidecar 进程管理 |
| **tauri-plugin-dialog** | 2.x | 原生文件选择对话框 |
| **tauri-plugin-process** | 2.x | 进程生命周期管理 |
### 服务端层
| 技术 | 职责 |
|------|------|
| **Bun** | JavaScript/TypeScript 运行时 + 构建工具 |
| **Bun.serve** | HTTP/WebSocket 服务器 |
| **Zod** | 请求参数校验 |
### 字体系统
桌面端自托管所有字体,无需 CDN 依赖:
- **Inter** (400-600) — 正文
- **Manrope** (400-800) — 标题
- **JetBrains Mono** — 代码
- **Material Symbols Outlined** — 图标
---
## 二、三层架构

桌面端采用**三层架构**设计,各层职责明确:
```
┌─────────────────────────────────┐
│ Tauri 主进程 (Rust) │
│ ┌───────────────────────────┐ │
│ │ WebView (React App) │ │ ← 用户看到的界面
│ │ Port 1420 (开发) / 内置 │ │
│ └───────────┬───────────────┘ │
│ │ HTTP + WebSocket │
│ ┌───────────▼───────────────┐ │
│ │ Sidecar: claude-server │ │ ← Bun 编译的服务端二进制
│ │ Port: 动态分配 │ │
│ └───────────┬───────────────┘ │
│ │ 子进程 spawn │
│ ┌───────────▼───────────────┐ │
│ │ CLI 子进程 (claude-cli) │ │ ← 实际执行 AI 对话和工具调用
│ │ stdin/stdout 通信 │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
```
### 第一层:Tauri 主进程
**职责**:窗口管理、进程编排、原生 API 桥接
关键文件:`desktop/src-tauri/src/lib.rs`
```rust
// 核心功能
fn reserve_local_port() -> u16 // 获取空闲端口
fn wait_for_server(port) -> bool // 等待服务就绪(超时 10 秒)
fn start_server_sidecar(port) // 启动 Server 后台进程
fn stop_server_sidecar() // 停止 Server 后台进程
// Tauri 命令(暴露给前端调用)
#[tauri::command]
fn get_server_url() -> String // 返回 http://127.0.0.1:{port}
```
启动流程:
1. Tauri 主进程启动
2. 调用 `reserve_local_port()` 获取空闲端口
3. 启动 `claude-server` sidecar 进程,传入端口号
4. 调用 `wait_for_server()` 轮询等待服务就绪
5. WebView 加载 React 应用
6. 前端调用 `get_server_url` 获取服务地址
### 第二层:Server Sidecar
**职责**:HTTP API + WebSocket 网关 + 会话管理 + 代理转换
关键文件:`src/server/index.ts`
```typescript
// 服务器启动
Bun.serve({
port: config.port,
fetch(req, server) {
// HTTP 路由 + WebSocket 升级
if (url.pathname.startsWith('/ws/')) {
server.upgrade(req, { data: { sessionId } })
return
}
return router.handle(req)
},
websocket: {
open(ws) { /* 连接建立 */ },
message(ws, msg) { /* 消息处理 */ },
close(ws) { /* 连接关闭 */ }
}
})
```
Server 提供两类服务:
- **HTTP REST API** — 会话管理、配置读写、模型操作
- **WebSocket** — 实时消息通道(对话流、权限请求、状态推送)
### 第三层:CLI 子进程
**职责**:AI 对话核心逻辑、工具执行、Agent 编排
Server 为每个 Session 创建一个 CLI 子进程:
```typescript
// 创建 CLI 子进程
const child = spawn('claude-cli', ['--session', sessionId], {
cwd: workDir,
env: { ...process.env, ANTHROPIC_AUTH_TOKEN: token }
})
// 通过 stdin/stdout 通信
child.stdin.write(JSON.stringify({ type: 'user_message', content }))
child.stdout.on('data', (chunk) => {
// 解析 stream_event,转发给 WebSocket 客户端
})
```
### Sidecar 构建
使用 Bun 将 TypeScript 编译为独立可执行文件:
```typescript
// desktop/scripts/build-sidecars.ts
// 支持 8 种目标平台:
const targets = [
'bun-darwin-arm64', // M1/M2 Mac
'bun-darwin-x64', // Intel Mac
'bun-windows-x64', // Windows x64
'bun-windows-arm64', // Windows ARM
'bun-linux-x64', // Linux x64
'bun-linux-arm64', // Linux ARM
'bun-linux-x64-musl', // Alpine Linux
'bun-linux-arm64-musl', // Alpine ARM
]
```
编译产物放置在 `desktop/src-tauri/binaries/`,Tauri 打包时自动包含。
---
## 三、WebSocket 通信协议

WebSocket 是桌面端与服务端实时通信的核心通道。
### 连接建立
```
ws://127.0.0.1:{port}/ws/{sessionId}
```
前端通过 `wsManager`(单例)管理所有 WebSocket 连接:
```typescript
// desktop/src/api/websocket.ts
class WebSocketManager {
private connections = new Map()
connect(sessionId: string): void {
const ws = new WebSocket(`${baseUrl}/ws/${sessionId}`)
// 自动重连(指数退避)
// 消息队列(连接未就绪时缓冲)
// 心跳 ping(30 秒间隔)
}
}
```
### 客户端 → 服务端消息
| type | 字段 | 说明 |
|------|------|------|
| `user_message` | `content`, `attachments?` | 用户发送消息 |
| `permission_response` | `requestId`, `allowed`, `rule?` | 权限审批响应 |
| `set_permission_mode` | `mode` | 切换权限模式 |
| `stop_generation` | — | 停止当前生成 |
| `ping` | — | 心跳保活 |
### 服务端 → 客户端消息
| type | 关键字段 | 说明 |
|------|----------|------|
| `connected` | `sessionId` | 连接成功确认 |
| `status` | `state`, `verb?`, `elapsed?` | 状态变更(thinking/generating) |
| `content_start` | `blockType` | 内容块开始(text/tool_use) |
| `content_delta` | `text?`, `toolInput?` | 增量内容(流式文本) |
| `thinking` | `text` | Extended Thinking 内容 |
| `tool_use_complete` | `toolName`, `input` | 工具调用准备就绪 |
| `tool_result` | `output`, `isError` | 工具执行结果 |
| `permission_request` | `toolName`, `input`, `requestId` | 权限请求 |
| `message_complete` | `usage: TokenUsage` | 消息完成(含 Token 统计) |
| `error` | `message`, `code` | 错误通知 |
| `session_title_updated` | `title` | 会话标题更新 |
| `team_update` | `team` | 团队状态变更 |
| `team_created` / `team_deleted` | `teamName` | 团队创建/删除 |
| `task_update` | `task` | 任务状态变更 |
| `pong` | — | 心跳响应 |
### 完整消息流示例
一条用户消息从发送到收到完整回复的流程:
```
用户输入 "写一个排序函数"
↓
ChatInput → chatStore.sendMessage()
↓
wsManager.send({ type: 'user_message', content: '...' })
↓
Server 收到 → 转发到 CLI 子进程
↓
CLI 开始处理,流式返回事件:
← { type: 'status', state: 'thinking' }
→ UI 显示"思考中..."动画
← { type: 'thinking', text: '让我分析...' }
→ ThinkingBlock 展示推理过程
← { type: 'content_start', blockType: 'text' }
→ 准备接收文本内容
← { type: 'content_delta', text: 'def sort(' }
← { type: 'content_delta', text: 'arr):\n' }
← { type: 'content_delta', text: ' return sorted(arr)' }
→ AssistantMessage 流式渲染,闪烁光标
← { type: 'content_start', blockType: 'tool_use' }
→ 准备接收工具调用
← { type: 'tool_use_complete', toolName: 'Write', input: {...} }
→ ToolCallBlock 展示文件写入操作
← { type: 'permission_request', toolName: 'Write', requestId: '...' }
→ PermissionDialog 弹出,等待用户审批
用户点击"允许"
↓
→ { type: 'permission_response', requestId: '...', allowed: true }
← { type: 'tool_result', output: '文件已写入' }
→ ToolResultBlock 展示结果
← { type: 'message_complete', usage: { input: 1200, output: 350 } }
→ 更新 Token 统计,清除流式状态
```
### 连接管理
**心跳保活**:每 30 秒发送 `ping`,服务端回复 `pong`
**自动重连**:指数退避策略
```
重连间隔 = min(1000ms × 2^(attempt-1), 30000ms)
最大重连次数 = 10
```
**消息缓冲**:WebSocket 未就绪时,消息暂存队列,连接恢复后自动发送。
---
## 四、HTTP API 端点
Server 提供完整的 RESTful API,供前端和适配器调用。
### 会话管理
| 方法 | 端点 | 说明 |
|------|------|------|
| `GET` | `/api/sessions` | 获取会话列表 |
| `POST` | `/api/sessions` | 创建新会话 |
| `GET` | `/api/sessions/:id` | 获取会话详情 |
| `PATCH` | `/api/sessions/:id` | 重命名会话 |
| `DELETE` | `/api/sessions/:id` | 删除会话 |
| `GET` | `/api/sessions/:id/messages` | 获取历史消息 |
| `GET` | `/api/sessions/:id/git-info` | 获取 Git 信息 |
| `GET` | `/api/sessions/:id/slash-commands` | 获取可用斜杠命令 |
| `GET` | `/api/sessions/recent-projects` | 最近项目列表 |
### 模型与提供商
| 方法 | 端点 | 说明 |
|------|------|------|
| `GET` | `/api/models` | 可用模型列表 |
| `GET` | `/api/models/current` | 当前使用的模型 |
| `POST` | `/api/models/:id/set-current` | 切换模型 |
| `GET/POST` | `/api/models/effort` | 获取/设置 Effort 级别 |
| `GET` | `/api/providers` | 提供商列表 |
| `POST` | `/api/providers` | 创建提供商 |
| `PUT` | `/api/providers/:id` | 更新提供商配置 |
| `DELETE` | `/api/providers/:id` | 删除提供商 |
| `POST` | `/api/providers/:id/activate` | 激活提供商 |
| `POST` | `/api/providers/:id/test` | 测试连接 |
| `GET` | `/api/providers/presets` | 获取预设列表 |
### 定时任务
| 方法 | 端点 | 说明 |
|------|------|------|
| `GET` | `/api/scheduled-tasks` | 任务列表 |
| `POST` | `/api/scheduled-tasks` | 创建任务 |
| `PUT` | `/api/scheduled-tasks/:id` | 更新任务 |
| `DELETE` | `/api/scheduled-tasks/:id` | 删除任务 |
| `POST` | `/api/scheduled-tasks/:id/run` | 手动运行 |
| `GET` | `/api/scheduled-tasks/runs` | 所有运行记录 |
| `GET` | `/api/scheduled-tasks/:id/runs` | 单任务运行记录 |
### Agent 团队
| 方法 | 端点 | 说明 |
|------|------|------|
| `GET` | `/api/teams` | 团队列表 |
| `GET` | `/api/teams/:name` | 团队详情 |
| `GET` | `/api/teams/:name/members/:agentId/transcript` | 成员转录 |
| `DELETE` | `/api/teams/:name` | 删除团队 |
### 其他
| 方法 | 端点 | 说明 |
|------|------|------|
| `GET` | `/health` | 健康检查 |
| `GET/POST` | `/api/settings/permission-mode` | 权限模式 |
| `GET` | `/api/agents` | Agent 定义列表 |
| `GET` | `/api/skills` | 技能列表 |
| `GET` | `/api/skills/detail` | 技能详情 |
| `GET/PUT` | `/api/adapters` | 适配器配置 |
---
## 五、状态管理

桌面端使用 **Zustand** 进行状态管理,按领域拆分为多个独立 Store。
### Store 架构
```
stores/
├── chatStore.ts # 聊天状态(消息、流式、权限) 20KB
├── sessionStore.ts # 会话列表、创建、删除
├── tabStore.ts # 标签页管理、顺序、持久化 5KB
├── settingsStore.ts # 全局设置(模型、权限、语言)
├── providerStore.ts # LLM 提供商配置
├── uiStore.ts # UI 状态(主题、Toast、模态框)
├── taskStore.ts # 定时任务管理
├── teamStore.ts # Agent 团队状态
├── agentStore.ts # Agent 定义管理
├── skillStore.ts # 技能库
├── adapterStore.ts # 适配器配置
└── cliTaskStore.ts # CLI 任务状态
```
### chatStore 核心设计
`chatStore` 是最复杂的 Store,为**每个 Session 维护独立状态**:
```typescript
type PerSessionState = {
messages: UIMessage[] // 消息列表
chatState: 'idle' | 'thinking' | ... // 对话状态
connectionState: ConnectionState // WebSocket 连接状态
streamingText: string // 流式文本缓冲
streamingToolInput: string // 工具输入流
activeToolUseId: string | null // 当前工具调用 ID
activeToolName: string | null // 当前工具名称
activeThinkingId: string | null // 当前思考块 ID
pendingPermission: PermissionRequest | null // 待决权限
tokenUsage: TokenUsage // Token 使用统计
elapsedSeconds: number // 经过时间
statusVerb: string // 随机状态动词
slashCommands: SlashCommand[] // 可用命令列表
}
```
关键方法:
| 方法 | 职责 |
|------|------|
| `connectToSession(id)` | 建立 WebSocket 连接 |
| `disconnectSession(id)` | 断开连接 |
| `sendMessage(id, content, attachments)` | 发送用户消息 |
| `respondToPermission(id, requestId, allowed, rule)` | 权限审批 |
| `stopGeneration(id)` | 停止生成 |
| `loadHistory(id)` | 加载历史消息 |
| `handleServerMessage(id, msg)` | 处理服务端消息 |
### 数据流向
```
用户操作
↓
Component (调用 Store 方法)
↓
Store (更新状态 + 调用 API/WebSocket)
↓
API 层 (HTTP 请求 / WebSocket 发送)
↓
Server 响应
↓
Store (更新状态)
↓
Component (自动重渲染)
```
### 持久化策略
| 数据 | 存储位置 | 说明 |
|------|----------|------|
| 打开的标签页 | `localStorage` | 页面刷新/重启后恢复 |
| 会话数据 | Server JSONL 文件 | `~/.claude/sessions/` |
| 设置 | Server API | 通过 HTTP 读写 |
| 适配器配置 | `~/.claude/adapters.json` | JSON 文件 |
| 会话映射 | `~/.claude/adapter-sessions.json` | 适配器 Chat→Session 映射 |
---
## 六、会话生命周期
一个会话从创建到恢复的完整流程:
### 创建
```
用户点击 + 新建会话
↓
sessionStore.createSession({ workDir })
↓
POST /api/sessions { workDir: '/path/to/project' }
↓
Server 创建 Session 记录 → 返回 { sessionId }
↓
tabStore.openTab(sessionId)
↓
chatStore.connectToSession(sessionId)
↓
WebSocket 连接 → 收到 { type: 'connected' }
```
### 交互
```
用户发送消息
↓
wsManager.send({ type: 'user_message', content })
↓
Server 转发到 CLI 子进程
↓
CLI 处理 → 流式返回 stream_event
↓
Server 转换为 ServerMessage → 推送到 WebSocket
↓
chatStore.handleServerMessage() → UI 更新
```
### 断开
```
用户关闭标签页 / 切换到其他标签
↓
chatStore.disconnectSession(sessionId)
↓
WebSocket 关闭
↓
CLI 子进程可能继续运行(后台任务)
```
### 恢复
```
应用启动 / 切回标签页
↓
tabStore.restoreTabs() // 从 localStorage 读取
↓
验证会话是否仍存在 (GET /api/sessions)
↓
chatStore.connectToSession(sessionId)
↓
chatStore.loadHistory(sessionId) // GET /api/sessions/:id/messages
↓
WebSocket 重连 → 恢复实时通信
```
### 会话持久化
Server 使用 **JSONL(JSON Lines)** 格式持久化会话数据:
```
~/.claude/sessions/
├── {sessionId}/
│ ├── messages.jsonl ← 流式追加,每行一条消息/事件
│ ├── metadata.json ← 会话元数据(workDir、标题、模型等)
│ └── ...
```
JSONL 格式的优势:
- **流式追加**:新消息直接 append,无需重写整个文件
- **崩溃恢复**:即使中途崩溃,已写入的行不会丢失
- **大会话支持**:按行读取,无需一次性加载整个 JSON
---
## 七、代理层协议转换
Server 内置了多协议代理层(Proxy),让不同 AI 提供商的 API 统一接入。
### 支持的 API 格式
| 格式 | 说明 | 典型提供商 |
|------|------|-----------|
| `anthropic` | Anthropic Messages API | Anthropic 官方、MiniMax |
| `openai_chat` | OpenAI Chat Completions | OpenAI、DeepSeek |
| `openai_responses` | OpenAI Responses API | OpenAI 新接口 |
### 协议转换流程
```
前端发送消息
↓
Server 接收
↓
判断当前 Provider 的 API 格式
↓
┌─────────────────────────────────┐
│ anthropic │ openai_chat/resp │
│ 直接透传 │ 转换消息格式 │
│ │ system → 系统消息 │
│ │ tool_use → 函数调用 │
│ │ tool_result → 函数结果│
└──────┬──────┴──────┬────────────┘
│ │
↓ ↓
Anthropic API OpenAI API
│ │
↓ ↓
直接返回 反向转换响应格式
│ │
└──────┬──────┘
↓
统一的 ServerMessage 格式
↓
WebSocket 推送给前端
```
### 模型映射
每个 Provider 可配置四个模型槽位:
| 槽位 | 用途 |
|------|------|
| `main` | 主对话模型 |
| `haiku` | 快速/轻量任务 |
| `sonnet` | 中等复杂度 |
| `opus` | 最高能力 |
例如 OpenRouter 配置:
```json
{
"apiFormat": "anthropic",
"baseUrl": "https://openrouter.ai/api/v1",
"modelMapping": {
"main": "anthropic/claude-sonnet-4-20250514",
"haiku": "anthropic/claude-haiku-4-5-20251001",
"opus": "anthropic/claude-opus-4-20250514"
}
}
```
---
## 八、适配器桥接架构

适配器系统让**任何 IM 平台**都可以接入 Claude Code,无需修改核心代码。
### 整体架构
```
┌──────────────┐ ┌──────────────┐
│ Telegram │ │ 飞书 │ ← IM 平台
│ grammy │ │ Lark SDK │
└──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
┌──────────────────────────────────┐
│ Adapter 进程 │ ← 平台适配层
│ ┌─────────────────────────────┐ │
│ │ common/ 共享模块 │ │
│ │ config │ ws-bridge │ │
│ │ pairing │ message-buffer │ │
│ │ dedup │ chat-queue │ │
│ │ session │ http-client │ │
│ └─────────────────────────────┘ │
└──────────────┬───────────────────┘
│ HTTP + WebSocket
▼
┌──────────────────────────────────┐
│ Desktop Server │ ← 服务端
│ POST /api/sessions │
│ WS /ws/{sessionId} │
└──────────────┬───────────────────┘
│
▼
┌──────────────────────────────────┐
│ CLI 子进程 │ ← AI 对话引擎
└──────────────────────────────────┘
```
### 四层架构
| 层级 | 职责 | 关键文件 |
|------|------|----------|
| **配置层** | 桌面端 UI 填写凭证 | `desktop/src/pages/AdapterSettings.tsx` |
| **存储层** | 配置读写和脱敏 | `src/server/services/adapterService.ts` |
| **适配层** | IM SDK 对接、消息路由 | `adapters/telegram/index.ts`, `adapters/feishu/index.ts` |
| **会话层** | WebSocket 桥接到 Server | `adapters/common/ws-bridge.ts` |
### 共享模块
所有适配器共用 `adapters/common/` 模块:
| 模块 | 职责 |
|------|------|
| `config.ts` | 配置加载(环境变量 > 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 客户端(创建会话、获取项目列表) |
### 接入新 IM 平台
得益于模块化设计,接入新平台只需三步:
**1. 创建适配器文件**
```typescript
// adapters/discord/index.ts
import { WsBridge } from '../common/ws-bridge.js'
import { MessageBuffer } from '../common/message-buffer.js'
import { loadConfig } from '../common/config.js'
import { isPaired, tryPair } from '../common/pairing.js'
```
**2. 实现消息接收**
```typescript
bot.on('message', async (msg) => {
// 去重 → 授权检查 → 会话管理 → WebSocket 桥接
if (!dedup.tryRecord(msg.id)) return
if (!isPaired('discord', msg.author.id, config)) {
tryPair(msg.content, { userId: msg.author.id, ... }, 'discord')
return
}
bridge.sendUserMessage(chatId, msg.content)
})
```
**3. 处理服务端事件**
```typescript
bridge.onServerMessage(chatId, async (msg) => {
switch (msg.type) {
case 'content_delta': /* 流式文本 */ break
case 'permission_request': /* 权限按钮 */ break
case 'message_complete': /* 完成 */ break
}
})
```
### 消息缓冲机制
为防止 IM 平台的消息频率限制,适配器使用 `MessageBuffer` 批量刷新:
```
content_delta: "def " ─┐
content_delta: "sort(" │→ 缓冲累积
content_delta: "arr):" │
↓ 500ms 或 200 字符
IM 平台: "def sort(arr): ▍" (编辑消息)
content_delta: "\n ..." ─┐
content_delta: "return" │→ 继续缓冲
↓
IM 平台: "def sort(arr):\n return ▍"
message_complete ↓
IM 平台: 最终完整文本(分片发送)
```
分片限制:
- Telegram: 4000 字符/消息
- 飞书: 30000 字符/消息
---
## 九、项目目录结构
```
desktop/
├── src/ # React 前端
│ ├── App.tsx # 根组件
│ ├── main.tsx # 入口
│ ├── components/
│ │ ├── layout/ # 布局:AppShell, Sidebar, TabBar
│ │ ├── chat/ # 聊天:MessageList, ChatInput, ToolCallBlock
│ │ ├── shared/ # 通用:Button, Modal, Toast, Spinner
│ │ ├── controls/ # 控件:ModelSelector, PermissionModeSelector
│ │ ├── markdown/ # MarkdownRenderer
│ │ ├── skills/ # 技能浏览
│ │ ├── tasks/ # 任务管理
│ │ └── teams/ # 团队视图
│ ├── pages/ # 页面:ActiveSession, Settings, ScheduledTasks
│ ├── stores/ # Zustand 状态
│ ├── api/ # HTTP + WebSocket 客户端
│ ├── types/ # TypeScript 类型定义
│ ├── hooks/ # 自定义 Hooks
│ ├── i18n/ # 国际化
│ ├── theme/ # 全局样式
│ ├── config/ # 提供商预设
│ └── lib/ # 工具库
├── src-tauri/ # Tauri Rust 后端
│ ├── src/lib.rs # 核心:端口管理、Sidecar 启动
│ ├── Cargo.toml # Rust 依赖
│ └── tauri.conf.json # Tauri 配置
├── sidecars/ # Sidecar 启动器
│ ├── server-launcher.ts # Server 进程启动
│ └── cli-launcher.ts # CLI 进程启动
├── scripts/
│ └── build-sidecars.ts # 跨平台编译脚本
├── vite.config.ts # Vite 构建配置
├── package.json # 依赖和脚本
└── tsconfig.json # TypeScript 配置
src/server/ # 服务端(独立于 desktop/)
├── index.ts # 入口:Bun.serve
├── router.ts # HTTP 路由
├── ws/ # WebSocket 处理
├── api/ # REST API 路由
├── services/ # 业务服务
├── proxy/ # 代理层(协议转换)
├── middleware/ # 中间件
├── config/ # 服务端配置
└── types/ # 类型定义
adapters/ # 外部 IM 适配器
├── common/ # 共享模块(9 个文件)
├── telegram/index.ts # Telegram Bot
├── feishu/index.ts # 飞书 Bot
└── package.json # 独立依赖
```
---
## 十、快速参考
### 关键端口
| 端口 | 用途 |
|------|------|
| 1420 | Vite 开发服务器 |
| 动态分配 | Server Sidecar(`reserve_local_port`) |
### 关键文件路径
| 文件 | 说明 |
|------|------|
| `~/.claude/adapters.json` | 适配器配置 |
| `~/.claude/adapter-sessions.json` | 适配器会话映射 |
| `~/.claude/sessions/` | 会话持久化数据 |
### 开发命令
```bash
# 前端开发
cd desktop && pnpm dev
# 编译 Sidecar
cd desktop && bun run build:sidecars
# Tauri 开发模式(前端 + 桌面框架)
cd desktop && pnpm tauri dev
# 构建发布包
cd desktop && pnpm tauri build
# 运行测试
cd desktop && pnpm test
```