cc-haha/docs/ui-clone/03-server-architecture.md
程序员阿江(Relakkes) 0c6d2754b4 feat: add Desktop UI backend server with full REST API and WebSocket support
Add complete server-side implementation for the Claude Code Desktop App UI:

- REST API: sessions, conversations, settings, models, scheduled tasks,
  search, agents, and status endpoints (9 modules, 30+ endpoints)
- WebSocket: real-time chat streaming with state transitions, ping/pong,
  permission request forwarding, and stop generation support
- Services: sessionService (JSONL read/write, CLI-compatible),
  settingsService (atomic writes), cronService, searchService (ripgrep),
  agentService (YAML management)
- Middleware: CORS (localhost-only), auth, unified error handling
- Tests: 180 tests (unit + E2E + business flow), all passing
- Docs: PRD, UI design spec, server architecture design

Non-invasive: all new code under src/server/, no changes to existing CLI code.
CLI/UI data interop: reads/writes the same JSONL/JSON files as the CLI.

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

273 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Claude Code Desktop App — 服务端架构设计
## 一、设计原则
1. **非侵入性**: 不修改现有 CLI 源代码,在 `src/server/` 下新建服务层
2. **数据一致性**: 读写与 CLI 完全相同的文件JSONL 会话、JSON 设置、Cron 任务)
3. **CLI/UI 互通**: UI 的操作落盘到 CLI 的文件系统CLI 的会话在 UI 可见
4. **渐进式**: 先实现核心 API再扩展高级功能
## 二、技术栈
| 层 | 技术 | 理由 |
|----|------|------|
| HTTP Server | Bun.serve() | 原项目已用 Bun零额外依赖 |
| WebSocket | Bun 原生 WebSocket | 已有 `ws` 依赖Bun 原生更高效 |
| 验证 | Zod v4 | 已在依赖中 |
| 测试 | bun:test | Bun 内置,无需额外依赖 |
| API 风格 | REST + WebSocket | REST 用于 CRUDWS 用于流式传输 |
## 三、目录结构
```
src/server/
├── index.ts # 服务器入口
├── router.ts # 路由注册
├── middleware/
│ ├── auth.ts # API Key 鉴权
│ ├── cors.ts # CORS 处理
│ └── errorHandler.ts # 统一错误处理
├── api/
│ ├── sessions.ts # 会话管理 API
│ ├── conversations.ts # 对话/消息 API
│ ├── settings.ts # 设置 API
│ ├── models.ts # 模型选择 API
│ ├── scheduled-tasks.ts # 定时任务 API
│ ├── search.ts # 搜索 API
│ ├── agents.ts # Agent 管理 API
│ ├── mcp.ts # MCP 服务器管理 API
│ └── status.ts # 状态与诊断 API
├── ws/
│ ├── handler.ts # WebSocket 连接管理
│ ├── chatStream.ts # 对话流式传输
│ └── events.ts # 事件类型定义
├── services/
│ ├── sessionService.ts # 会话服务(封装 sessionStorage
│ ├── conversationService.ts # 对话服务(封装 query engine
│ ├── settingsService.ts # 设置服务(封装 settings
│ ├── cronService.ts # 定时任务服务(封装 cronTasks
│ ├── searchService.ts # 搜索服务(封装 ripgrep
│ ├── agentService.ts # Agent 服务
│ └── mcpService.ts # MCP 服务
└── __tests__/
├── sessions.test.ts
├── conversations.test.ts
├── settings.test.ts
├── scheduled-tasks.test.ts
├── search.test.ts
└── e2e/
└── full-flow.test.ts
```
## 四、API 设计
### 4.1 会话管理 (Sessions)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/sessions` | 获取会话列表(支持分页、项目过滤) |
| GET | `/api/sessions/:id` | 获取会话详情(标题、消息数、时间) |
| POST | `/api/sessions` | 创建新会话 |
| DELETE | `/api/sessions/:id` | 删除会话 |
| PATCH | `/api/sessions/:id` | 更新会话(重命名) |
| GET | `/api/sessions/:id/messages` | 获取会话消息历史 |
**数据来源**: `~/.claude/projects/{proj}/{sid}.jsonl` (JSONL 格式)
**实现要点**:
- 调用 `loadMessageLogs()` 获取会话列表
- 调用 `loadTranscriptFile()` 解析 JSONL 消息
- 使用 `getProjectDir()` 定位项目目录
- 会话 ID 为 UUID v4
### 4.2 对话 (Conversations)
| 方法 | 路径 | 描述 |
|------|------|------|
| POST | `/api/sessions/:id/chat` | 发送消息(返回任务 ID |
| GET | `/api/sessions/:id/chat/status` | 获取对话状态 |
| POST | `/api/sessions/:id/chat/stop` | 停止生成 |
**WebSocket**: `ws://host:port/ws/chat/:sessionId`
- 发送消息 → 流式接收 AI 回复
- 实时推送工具调用进度
- 权限请求转发给前端
**WebSocket 消息格式**:
```typescript
// 客户端 → 服务器
type ClientMessage =
| { type: 'user_message'; content: string; attachments?: Attachment[] }
| { type: 'permission_response'; requestId: string; allowed: boolean }
| { type: 'stop_generation' }
// 服务器 → 客户端
type ServerMessage =
| { type: 'content_start'; blockType: 'text' | 'tool_use' }
| { type: 'content_delta'; text?: string; toolInput?: string }
| { type: 'tool_use_complete'; toolName: string; toolUseId: string }
| { type: 'tool_result'; toolUseId: string; content: any; isError: boolean }
| { type: 'permission_request'; requestId: string; toolName: string; input: any }
| { type: 'message_complete'; usage: Usage }
| { type: 'error'; message: string; code: string }
| { type: 'status'; state: 'thinking' | 'tool_executing' | 'idle' }
```
### 4.3 设置 (Settings)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/settings` | 获取合并后的设置 |
| GET | `/api/settings/user` | 获取用户级设置 |
| GET | `/api/settings/project` | 获取项目级设置 |
| PUT | `/api/settings/user` | 更新用户级设置 |
| PUT | `/api/settings/project` | 更新项目级设置 |
| GET | `/api/permissions/mode` | 获取当前权限模式 |
| PUT | `/api/permissions/mode` | 切换权限模式 |
**数据来源**: `~/.claude/settings.json` + `.claude/settings.json`
### 4.4 模型 (Models)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/models` | 获取可用模型列表 |
| GET | `/api/models/current` | 获取当前选中模型 |
| PUT | `/api/models/current` | 切换模型 |
| GET | `/api/effort` | 获取当前 Effort 等级 |
| PUT | `/api/effort` | 设置 Effort 等级 |
### 4.5 定时任务 (Scheduled Tasks)
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/scheduled-tasks` | 获取定时任务列表 |
| POST | `/api/scheduled-tasks` | 创建定时任务 |
| PUT | `/api/scheduled-tasks/:id` | 更新定时任务 |
| DELETE | `/api/scheduled-tasks/:id` | 删除定时任务 |
**数据来源**: `.claude/scheduled_tasks.json`
### 4.6 搜索 (Search)
| 方法 | 路径 | 描述 |
|------|------|------|
| POST | `/api/search` | 全局搜索ripgrep |
| POST | `/api/search/sessions` | 搜索会话历史 |
### 4.7 Agent 管理
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/agents` | 获取 Agent 定义列表 |
| GET | `/api/agents/:name` | 获取 Agent 详情 |
| POST | `/api/agents` | 创建 Agent 定义 |
| PUT | `/api/agents/:name` | 更新 Agent 定义 |
| DELETE | `/api/agents/:name` | 删除 Agent 定义 |
| GET | `/api/tasks` | 获取后台任务列表 |
| GET | `/api/tasks/:id` | 获取任务详情 |
### 4.8 MCP 服务器管理
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/mcp/servers` | 获取 MCP 服务器列表 |
| POST | `/api/mcp/servers` | 添加 MCP 服务器 |
| DELETE | `/api/mcp/servers/:name` | 移除 MCP 服务器 |
| GET | `/api/mcp/tools` | 获取 MCP 工具列表 |
### 4.9 状态与诊断
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/status` | 服务器状态(健康检查) |
| GET | `/api/status/diagnostics` | 系统诊断信息 |
| GET | `/api/status/usage` | Token 用量统计 |
| GET | `/api/status/user` | 用户信息 |
## 五、服务层设计
每个 Service 封装对现有工具函数的调用,提供统一的错误处理和数据转换:
```typescript
// 示例: SessionService
class SessionService {
// 列出所有会话
async listSessions(options: { project?: string; limit?: number; offset?: number }): Promise<SessionListResponse>
// 获取会话消息
async getSessionMessages(sessionId: string): Promise<Message[]>
// 创建新会话
async createSession(workDir: string): Promise<SessionInfo>
// 删除会话
async deleteSession(sessionId: string): Promise<void>
// 重命名会话
async renameSession(sessionId: string, title: string): Promise<void>
}
```
**关键实现**:
- `SessionService` 调用 `loadMessageLogs()``loadTranscriptFile()`
- `SettingsService` 调用 `getSettings()``updateSettingsForSource()`
- `CronService` 调用 `readCronTasks()``writeCronTasks()`
- `SearchService` 调用 ripgrep 流式搜索
- `ConversationService` 使用 `queryModelWithStreaming()` + `StreamingToolExecutor`
## 六、WebSocket 协议
### 连接流程
```
1. 客户端连接: ws://host:port/ws/chat/{sessionId}
Headers: Authorization: Bearer {apiKey}
2. 服务器确认: { type: 'connected', sessionId: '...' }
3. 客户端发送消息: { type: 'user_message', content: '...' }
4. 服务器流式响应:
{ type: 'status', state: 'thinking' }
{ type: 'content_start', blockType: 'text' }
{ type: 'content_delta', text: 'Let me...' }
{ type: 'content_delta', text: ' help you...' }
{ type: 'content_start', blockType: 'tool_use', toolName: 'Bash' }
{ type: 'tool_use_complete', toolName: 'Bash', toolUseId: '...' }
{ type: 'permission_request', requestId: '...', toolName: 'Bash', input: {...} }
5. 客户端批准: { type: 'permission_response', requestId: '...', allowed: true }
6. 服务器继续:
{ type: 'tool_result', toolUseId: '...', content: '...', isError: false }
{ type: 'content_delta', text: 'Done!' }
{ type: 'message_complete', usage: { input_tokens: 1000, output_tokens: 500 } }
{ type: 'status', state: 'idle' }
```
## 七、鉴权方案
- 本地运行: 简单 API Key 验证(从 .env 读取)
- Header: `Authorization: Bearer {ANTHROPIC_API_KEY}`
- WebSocket: 首次连接时通过 URL query 或首条消息验证
- 不做复杂的用户系统,依赖 Anthropic API Key 鉴权
## 八、测试策略
### 单元测试
- 每个 Service 独立测试
- Mock 文件系统操作
- 验证输入输出格式
### 集成测试
- 启动测试服务器
- 发送 HTTP 请求验证响应
- WebSocket 连接和消息流测试
### E2E 测试
- 完整对话流程(创建会话 → 发消息 → 收回复 → 停止 → 查看历史)
- 定时任务 CRUD
- 设置读写
- 搜索功能