Files
mnote/design/old/07-ai/process/7-64-recycle-codexmobile-embed-page-ai-v1.md
T

342 lines
13 KiB
Markdown
Raw Normal View History

2026-06-25 21:08:17 +08:00
# 7-64 [recycle] CodexMobile 嵌入 Page AI 方案 v1
> 创建时间:2026-06-23
>
> 当前状态:`RECYCLE`
>
> Owner07-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 runtime4457 行 JS+ Rust bridge~20000 行)维护成本高,且聊天体验不如 CodexMobile
- CodexMobile 直接对接 Codex CLICodexRelay 已解决 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 AFork 与精简(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 BMNote 集成(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,长期可选)