342 lines
13 KiB
Markdown
342 lines
13 KiB
Markdown
# 7-64 [recycle] CodexMobile 嵌入 Page AI 方案 v1
|
||||
|
|
|
|||
|
|
> 创建时间:2026-06-23
|
|||
|
|
>
|
|||
|
|
> 当前状态:`RECYCLE`
|
|||
|
|
>
|
|||
|
|
> Owner:07-ai / Page AI / CodexMobile embed
|
|||
|
|
>
|
|||
|
|
> 上位依据:
|
|||
|
|
> - `design/07-ai/done/7-62-page-ai-board-first-full-rewrite-v1.md`
|
|||
|
|
> - `design/07-ai/process/7-63-page-ai-board-first-productization-v1.md`
|
|||
|
|
> - `design/07-ai/done/7-38-page-ai-sidebar-runtime-owner-split-v1.md`
|
|||
|
|
|
|||
|
|
> 过时原因:已由 `design/07-ai/process/7-65-opencode-webui-embed-page-ai-v1.md` 替代;Page AI 新主路径只接入 opencode 官方 runtime + 官方 WebUI。
|
|||
|
|
|
|||
|
|
## 1. 核心结论
|
|||
|
|
|
|||
|
|
放弃 Board-first 和自研聊天 UI 两条路,改为 **fork CodexMobile 开源代码 → 精简 → iframe 嵌入 MNote**。
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
MNote Page AI = MNote 原生上下文 pills + CodexMobile 精简版 iframe
|
|||
|
|
CodexMobile = Vue 3 SPA + Express server → Codex CLI app-server (RPC)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
理由:
|
|||
|
|
- CodexMobile(`friuns2/codex-mobile`,MIT,675⭐)已提供成熟的聊天 UI、流式渲染、session 管理、plan/tool 卡片
|
|||
|
|
- 当前 MNote Page AI runtime(4457 行 JS)+ Rust bridge(~20000 行)维护成本高,且聊天体验不如 CodexMobile
|
|||
|
|
- CodexMobile 直接对接 Codex CLI,CodexRelay 已解决 DeepSeek 兼容,不需要 Board 中介
|
|||
|
|
- 嵌入方案比自研省 90% 代码量,比 Board-first 少一层依赖
|
|||
|
|
|
|||
|
|
## 2. CodexMobile 现状
|
|||
|
|
|
|||
|
|
### 2.1 仓库信息
|
|||
|
|
|
|||
|
|
| 项目 | 值 |
|
|||
|
|
|---|---|
|
|||
|
|
| 仓库 | `friuns2/codex-mobile` |
|
|||
|
|
| 协议 | MIT |
|
|||
|
|
| 星数 | 675 |
|
|||
|
|
| 语言 | TypeScript (Vue 3 + Express) |
|
|||
|
|
| 本地版本 | v0.1.90 (`/home/lix/.local/lib/node_modules/codexapp/`) |
|
|||
|
|
| 当前运行 | `codexapp --no-login --no-tunnel --no-open --port 5900` |
|
|||
|
|
|
|||
|
|
### 2.2 架构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
浏览器 Vue SPA (hash router)
|
|||
|
|
↓ fetch /codex-api/rpc
|
|||
|
|
Express httpServer.ts
|
|||
|
|
↓ codexAppServerBridge.ts
|
|||
|
|
Codex CLI app-server (RPC over HTTP)
|
|||
|
|
↓
|
|||
|
|
Codex CLI (实际 agent 执行)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.3 核心模块
|
|||
|
|
|
|||
|
|
| 模块 | 文件 | 作用 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| RPC 桥接 | `codexAppServerBridge.ts` (~2500行) | 代理所有 Codex RPC 调用 |
|
|||
|
|
| API 网关 | `codexGateway.ts` | 线程/消息/模型/账户等高层 API |
|
|||
|
|
| RPC 客户端 | `codexRpcClient.ts` | 底层 RPC 调用与错误处理 |
|
|||
|
|
| HTTP 服务 | `httpServer.ts` | Express + WebSocket + 静态文件 |
|
|||
|
|
| 聊天 UI | `ThreadConversation.vue` | 消息气泡、plan 卡片、工具卡片 |
|
|||
|
|
| 输入框 | `ThreadComposer.vue` | 消息输入、发送、模型选择 |
|
|||
|
|
| 侧边栏 | `SidebarThreadTree.vue` | 线程列表、搜索、切换 |
|
|||
|
|
| 布局 | `DesktopLayout.vue` | 整体布局框架 |
|
|||
|
|
|
|||
|
|
## 3. 精简方案
|
|||
|
|
|
|||
|
|
### 3.1 删除清单
|
|||
|
|
|
|||
|
|
| 删除模块 | 原因 |
|
|||
|
|
|---|---|
|
|||
|
|
| xterm 终端 (`terminalManager.ts`, `ThreadTerminalPanel.vue`) | MNote 不是终端 |
|
|||
|
|
| 文件浏览 (`localBrowseUi.ts`) | MNote 有 FileTree |
|
|||
|
|
| Firebase auth | MNote 用 SQLite control-plane |
|
|||
|
|
| Telegram bridge (`telegramThreadBridge.ts`) | 无关 |
|
|||
|
|
| Composio 集成 | 无关 |
|
|||
|
|
| OpenRouter proxy (`openRouterProxy.ts`) | 无关 |
|
|||
|
|
| Zen proxy (`zenProxy.ts`) | 无关 |
|
|||
|
|
| Custom endpoint proxy (`customEndpointProxy.ts`) | 无关 |
|
|||
|
|
| Free mode (`freeMode.ts`) | 无关 |
|
|||
|
|
| Skills routes/hub (`skillsRoutes.ts`, `SkillsHub.vue`, `SkillCard.vue`) | 无关 |
|
|||
|
|
| Review git (`reviewGit.ts`, `ReviewPane.vue`) | 无关 |
|
|||
|
|
| Automations (`AutomationsPanel.vue`) | 无关 |
|
|||
|
|
| Directory hub (`DirectoryHub.vue`) | 无关 |
|
|||
|
|
| Account menu (`AccountMenu.vue`) | 简化 |
|
|||
|
|
| Rate limit status (`RateLimitStatus.vue`) | 无关 |
|
|||
|
|
| Dictation (`useDictation.ts`) | 无关 |
|
|||
|
|
| GitHub skills sync (`useGithubSkillsSync.ts`) | 无关 |
|
|||
|
|
| API methods panel (`ApiMethodsPanel.vue`) | 调试工具 |
|
|||
|
|
| Pending request panel (`ThreadPendingRequestPanel.vue`) | 简化 |
|
|||
|
|
| Queued messages (`QueuedMessages.vue`) | 简化 |
|
|||
|
|
| Composer dropdowns (runtime/search/skill picker) | 简化 |
|
|||
|
|
| Header git branch dropdown | 无关 |
|
|||
|
|
|
|||
|
|
### 3.2 保留清单
|
|||
|
|
|
|||
|
|
| 保留模块 | 作用 |
|
|||
|
|
|---|---|
|
|||
|
|
| `codexAppServerBridge.ts` | 核心 RPC 桥接 |
|
|||
|
|
| `codexGateway.ts` | 高层 API |
|
|||
|
|
| `codexRpcClient.ts` | RPC 客户端 |
|
|||
|
|
| `httpServer.ts` | Express + WebSocket |
|
|||
|
|
| `authMiddleware.ts` | 简化为 MNote token 验证 |
|
|||
|
|
| `ThreadConversation.vue` | 聊天气泡 |
|
|||
|
|
| `ThreadComposer.vue` | 输入框 |
|
|||
|
|
| `SidebarThreadTree.vue` | 线程列表 |
|
|||
|
|
| `DesktopLayout.vue` | 布局 |
|
|||
|
|
| `ContentHeader.vue` | 顶部信息 |
|
|||
|
|
| `appServerDtos.ts` | 类型定义 |
|
|||
|
|
| `codexErrors.ts` | 错误处理 |
|
|||
|
|
| WebSocket 流式事件 | 实时消息 |
|
|||
|
|
|
|||
|
|
### 3.3 新增:postMessage Bridge
|
|||
|
|
|
|||
|
|
MNote 原生层与 CodexMobile iframe 之间的通信协议:
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// MNote → CodexMobile
|
|||
|
|
interface MnoteContextMessage {
|
|||
|
|
type: 'mnote:context';
|
|||
|
|
payload: {
|
|||
|
|
workspaceId: string;
|
|||
|
|
documentId: string;
|
|||
|
|
pageTitle: string;
|
|||
|
|
rootUri: string; // file:///...
|
|||
|
|
primaryTarget: {
|
|||
|
|
absolutePath: string;
|
|||
|
|
relativePath: string;
|
|||
|
|
};
|
|||
|
|
selection?: {
|
|||
|
|
text: string;
|
|||
|
|
};
|
|||
|
|
allowedRoots: Array<{
|
|||
|
|
rootUri: string;
|
|||
|
|
permission: 'read' | 'write';
|
|||
|
|
}>;
|
|||
|
|
knowledgeContext?: string; // LightRAG 查询结果
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CodexMobile → MNote
|
|||
|
|
interface CodexMobileEvent {
|
|||
|
|
type: 'codex:file-changed' | 'codex:session-update' | 'codex:ready';
|
|||
|
|
payload: {
|
|||
|
|
changedFiles?: string[];
|
|||
|
|
sessionId?: string;
|
|||
|
|
threadId?: string;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 4. 集成架构
|
|||
|
|
|
|||
|
|
### 4.1 整体拓扑
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌──────────────────────────────────────────────────┐
|
|||
|
|
│ MNote Rust SSR (localhost:3000) │
|
|||
|
|
│ │
|
|||
|
|
│ ┌─ Page AI Panel ──────────────────────────────┐ │
|
|||
|
|
│ │ ┌─ MNote 原生上下文 pills ──────────────────┐ │ │
|
|||
|
|
│ │ │ [当前页] [选区] [知识库] [allowed roots] │ │ │
|
|||
|
|
│ │ └────────────────────────────────────────────┘ │ │
|
|||
|
|
│ │ ┌─ iframe ───────────────────────────────────┐ │ │
|
|||
|
|
│ │ │ CodexMobile 精简版 SPA │ │ │
|
|||
|
|
│ │ │ (Vue 3, hash router, 独立端口 5900) │ │ │
|
|||
|
|
│ │ │ │ │ │
|
|||
|
|
│ │ │ ThreadConversation + ThreadComposer │ │ │
|
|||
|
|
│ │ └────────────────────────────────────────────┘ │ │
|
|||
|
|
│ │ postMessage ↑↓ │ │
|
|||
|
|
│ └────────────────────────────────────────────────┘ │
|
|||
|
|
│ │
|
|||
|
|
│ /codex-mobile/* → reverse proxy → 127.0.0.1:5900 │
|
|||
|
|
└──────────────────────────────────────────────────┘
|
|||
|
|
↓
|
|||
|
|
CodexMobile Express (5900)
|
|||
|
|
↓
|
|||
|
|
Codex CLI app-server (RPC)
|
|||
|
|
↓
|
|||
|
|
Codex CLI + CodexRelay → DeepSeek
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.2 Rust 侧改动
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// mnote-web routes/mod.rs 新增
|
|||
|
|
.route("/codex-mobile/{*path}", any(codex_mobile_proxy))
|
|||
|
|
|
|||
|
|
// codex_mobile_proxy: 反向代理到 127.0.0.1:5900
|
|||
|
|
// 仅对已认证 session 放行
|
|||
|
|
// 注入 X-Mnote-Workspace-Id / X-Mnote-Document-Id header
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.3 前端侧改动
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// sidebar-page-ai-runtime.js 精简为:
|
|||
|
|
// 1. 渲染上下文 pills(当前页、选区、知识库)
|
|||
|
|
// 2. 创建 iframe 指向 /codex-mobile/
|
|||
|
|
// 3. postMessage 监听与转发
|
|||
|
|
// 4. 文件变更事件 → watcher 刷新编辑器
|
|||
|
|
|
|||
|
|
// 删除:
|
|||
|
|
// - 所有 provider 选择逻辑 (Hermes/Reasonix/Chat-only)
|
|||
|
|
// - 所有 session/message/run 持久化逻辑
|
|||
|
|
// - 所有 Board bridge 调用
|
|||
|
|
// - 所有 workflow/worker 选择器
|
|||
|
|
// 预计从 4457 行精简到 ~300 行
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.4 Rust 侧删除
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
删除文件:
|
|||
|
|
- routes/page_ai_board.rs (317 行)
|
|||
|
|
- routes/page_ai_workflow.rs (967 行)
|
|||
|
|
- routes/hermes_client.rs 中 Page AI 相关部分
|
|||
|
|
|
|||
|
|
保留:
|
|||
|
|
- hermes_tools/doc.rs 中的 mnote.doc.* tools(非 Page AI 路径仍需要)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 5. 上下文注入机制
|
|||
|
|
|
|||
|
|
### 5.1 注入时机
|
|||
|
|
|
|||
|
|
1. Page AI 面板打开时:注入当前页路径、workspaceId、documentId
|
|||
|
|
2. 用户选中文本时:注入 selection
|
|||
|
|
3. 用户切换页面时:更新 primaryTarget
|
|||
|
|
4. 用户触发知识库查询时:注入 knowledgeContext
|
|||
|
|
|
|||
|
|
### 5.2 注入方式
|
|||
|
|
|
|||
|
|
MNote 通过 `iframe.contentWindow.postMessage()` 发送 `mnote:context`,CodexMobile 内新增 `useMnoteBridge.ts` composable 接收并注入到 Codex CLI 的 system prompt 中。
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// CodexMobile 侧新增 useMnoteBridge.ts
|
|||
|
|
// 监听 postMessage,将上下文追加到 thread/start 的 system prompt
|
|||
|
|
// ponytail: 最小实现,只做 system prompt 注入,不做 UI 展示
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.3 文件编辑闭环
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户输入 "把当前页的 A 替换为 B"
|
|||
|
|
→ Codex CLI 通过 CodexRelay 执行
|
|||
|
|
→ Codex CLI 直接编辑 primaryTarget.absolutePath
|
|||
|
|
→ 磁盘文件变化
|
|||
|
|
→ CodexMobile 检测文件变更 → postMessage 'codex:file-changed'
|
|||
|
|
→ MNote watcher 检测到文件变化 → 刷新 tiptap 编辑器
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 6. 实施计划
|
|||
|
|
|
|||
|
|
### Phase A:Fork 与精简(1天)
|
|||
|
|
|
|||
|
|
- [ ] `git clone https://github.com/friuns2/codex-mobile` 到 `/mnt/Data1T/mnote/codex-mobile/`
|
|||
|
|
- [ ] 删除 3.1 清单中的所有模块
|
|||
|
|
- [ ] 简化 `authMiddleware.ts`:接受 MNote session token
|
|||
|
|
- [ ] 新增 `useMnoteBridge.ts`:postMessage 监听
|
|||
|
|
- [ ] 验证 `npm run build` 通过
|
|||
|
|
- [ ] 验证精简版可独立运行(`codexapp --port 5900`)
|
|||
|
|
|
|||
|
|
### Phase B:MNote 集成(1天)
|
|||
|
|
|
|||
|
|
- [ ] Rust 新增 `/codex-mobile/{*path}` 反向代理路由
|
|||
|
|
- [ ] `sidebar-page-ai-runtime.js` 精简为上下文 pills + iframe + postMessage
|
|||
|
|
- [ ] 删除 `page_ai_board.rs`、`page_ai_workflow.rs`
|
|||
|
|
- [ ] 上下文 pills 实现(当前页、选区、知识库引用)
|
|||
|
|
- [ ] postMessage bridge 双向通信验证
|
|||
|
|
|
|||
|
|
### Phase C:闭环验证(0.5天)
|
|||
|
|
|
|||
|
|
- [ ] smoke:打开 Page AI → 看到 CodexMobile 聊天界面
|
|||
|
|
- [ ] smoke:发送消息 → 流式回复可见
|
|||
|
|
- [ ] smoke:编辑当前页 → 文件变化 → 编辑器刷新
|
|||
|
|
- [ ] smoke:刷新页面 → 会话恢复
|
|||
|
|
- [ ] smoke:上下文 pills 正确显示当前页信息
|
|||
|
|
|
|||
|
|
### Phase D:旧代码清理(后续)
|
|||
|
|
|
|||
|
|
- [ ] 归档 `sidebar-page-ai-runtime.js` 旧代码
|
|||
|
|
- [ ] 归档 `hermes_client.rs` Page AI 相关代码
|
|||
|
|
- [ ] 更新 `ARCHITECTURE.md` 和 `CURRENT_ARCHITECTURE.md`
|
|||
|
|
|
|||
|
|
## 7. 验收标准
|
|||
|
|
|
|||
|
|
### 7.1 聊天体验
|
|||
|
|
|
|||
|
|
- 用户可在 Page AI 面板中与 Codex 对话
|
|||
|
|
- 流式回复实时可见
|
|||
|
|
- 支持 stop / retry / copy
|
|||
|
|
- 刷新页面后会话历史可恢复
|
|||
|
|
- plan 卡片和工具调用卡片可见(含 plan 持久化补丁)
|
|||
|
|
|
|||
|
|
### 7.2 上下文注入
|
|||
|
|
|
|||
|
|
- 上下文 pills 显示当前页标题和路径
|
|||
|
|
- 选区内容自动注入
|
|||
|
|
- Codex 能读取和编辑当前页文件
|
|||
|
|
- 文件编辑后 MNote 编辑器自动刷新
|
|||
|
|
|
|||
|
|
### 7.3 代码精简
|
|||
|
|
|
|||
|
|
- `sidebar-page-ai-runtime.js` 从 4457 行精简到 <500 行
|
|||
|
|
- 删除 `page_ai_board.rs` 和 `page_ai_workflow.rs`(~1300 行)
|
|||
|
|
- 不再依赖 Agent Board 服务
|
|||
|
|
|
|||
|
|
## 8. 风险与处理
|
|||
|
|
|
|||
|
|
| 风险 | 处理 |
|
|||
|
|
|---|---|
|
|||
|
|
| CodexMobile 上游更新导致 fork 过时 | 定期 rebase,只保留精简 diff |
|
|||
|
|
| Codex CLI 不可用 | 降级提示 "Codex CLI 未运行",不伪装可用 |
|
|||
|
|
| iframe 跨域问题 | 同源反向代理(`/codex-mobile/*`),无跨域 |
|
|||
|
|
| postMessage 安全 | 验证 origin,只接受已知消息类型 |
|
|||
|
|
| plan 卡片刷新消失 | 已有 `codexmobile-plan-persist.js` 补丁,合入 fork |
|
|||
|
|
| npm 升级清空 codexapp 目录 | fork 到 MNote 仓库内,不依赖 npm 全局安装 |
|
|||
|
|
|
|||
|
|
## 9. 与旧方案的对比
|
|||
|
|
|
|||
|
|
| | Board-first (7-62/7-63) | CodexMobile 嵌入 (本方案) |
|
|||
|
|
|---|---|---|
|
|||
|
|
| MNote 代码量 | ~4457 JS + ~20000 Rust | ~500 JS + ~50 Rust |
|
|||
|
|
| 聊天 UI 成熟度 | 自研,持续打磨 | 675⭐ 开源验证 |
|
|||
|
|
| 依赖服务 | Agent Board (必须) | Codex CLI (必须) |
|
|||
|
|
| Agent 选择 | 通过 Board 间接 | 直接使用 Codex CLI |
|
|||
|
|
| Provider 适配 | Board 负责 | CodexRelay 负责 |
|
|||
|
|
| 上下文注入 | 自研 pills | 自研 pills + system prompt |
|
|||
|
|
| 维护负担 | Board bridge + 聊天 UI | 追上游 + postMessage bridge |
|
|||
|
|
| 差异化 | 上下文 pills | 上下文 pills + 成熟聊天体验 |
|
|||
|
|
|
|||
|
|
## 10. 非目标
|
|||
|
|
|
|||
|
|
- 不把 CodexMobile 的终端、文件浏览、Skills、Review 等功能带入 MNote
|
|||
|
|
- 不替换 MNote 的 FileTree、编辑器、知识库等核心功能
|
|||
|
|
- 不要求 CodexMobile 支持 MNote 特有的资源类型(mindmap、OnlyOffice)
|
|||
|
|
- 不实现 CodexMobile 与 MNote auth 的 SSO 统一(短期独立 auth,长期可选)
|