Files
mnote/design/07-ai/process/7-65-opencode-webui-embed-page-ai-v1.md
T

38 KiB
Raw Blame History

状态补充(2026-06-25):本稿冻结为 opencode runtime / 官方 opencode WebUI iframe fallback 参考;Page AI 产品主线由 7-68-openhub-weknora-mnote-deep-fusion-v1.md 接管。官方 iframe 仅在 OpenHub AI 面板不可用、调试 opencode 原生行为或做回归对照时启用,不再作为默认产品路径。

7-65 [process] Page AI opencode WebUI embed v1

创建时间:2026-06-23

当前状态:FROZEN / 官方 opencode iframe fallbackOpenHub + WeKnora + MNote 深度融合为主线

Owner07-ai / Page AI / opencode WebUI embed

替代方案:

  • design/old/07-ai/process/7-62-recycle-page-ai-board-first-full-rewrite-v1.md
  • design/old/07-ai/process/7-63-recycle-page-ai-board-first-productization-v1.md
  • design/old/07-ai/process/7-64-recycle-codexmobile-embed-page-ai-v1.md

上位依据:

  • design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md
  • design/07-ai/done/7-38-page-ai-sidebar-runtime-owner-split-v1.md
  • design/07-ai/done/7-40-page-ai-context-envelope-and-run-receipt-v1.md
  • design/07-ai/done/7-50-lightrag-knowledge-rag-provider-v1.md

1. 核心结论

废弃 MNote Page AI 自研 provider 接入与 Board-first 产品壳后,本稿曾把 opencode 官方 runtime + 官方 WebUI 作为主线;截至 7-68,该方案降级为 fallback。

fallback MNote Page AI = MNote 宿主壳 + opencode 官方 WebUI iframe + MNote 打开/刷新/上下文集成
主线 Page AI          = MNote 宿主壳 + OpenHub AI 面板 + OpenHub FastAPI/Redis/opencode client + WeKnora
opencode              = agent runtime / HTTP server / OpenAPI / SSE / SDK / opencodego provider

MNote 不再直接维护 Reasonix / ZCode / Hermes / Chat-only / Board worker 作为页面 AI provider。它们可以继续存在于历史、调试或外部工作流边界,但不进入 Page AI 主路径。官方 opencode WebUI iframe 也不再进入默认产品主路径,只作为 fallback。

选择 opencode 的原因:

  • opencode web 官方提供本地 WebUI,不需要 MNote 自研完整聊天前端。
  • opencode serve 官方提供 headless HTTP server 和 OpenAPI,适合 MNote 做轻量 adapter。
  • @opencode-ai/sdk 覆盖 session、message、diff、permission、event,足够承接上下文注入与回写 receipt。
  • opencode 原生支持 SSE、权限审批、文件 diff、MCP、ACP、session export/import。
  • 本机已有 opencode 和 opencodego 订阅链路,provider/runtime/UI 属于同一生态,少一层兼容债。

2. 明确废弃

2.1 Page AI 主路径不再接入

  • Reasonix native session / Reasonix desktop bridge。
  • ZCode worker / Board worker selector。
  • Hermes profile / Hermes Web control surface。
  • Chat-only remote conversation。
  • Agent Board run/workflow 作为默认 Page AI 后端。
  • CodexMobile iframe 作为默认 Page AI 后端。

这些能力不删除历史代码,不立刻清理工具层,只从 Page AI 新主路径退出。

2.2 仍可保留的边界

  • mnote.doc.*mnote.block.*、旧 LightRAG legacy facade 等工具可继续作为 fallback/compat 边界;默认知识库工具应改向 WeKnora。
  • Agent Board 仍可作为外部 workflow/QA/review 系统,不再作为 Page AI 默认聊天后端。
  • CodexMobile 可保留为备选 spike 或体验对照,不作为当前实现目标。

3. 新系统边界

3.1 MNote 只做四件事

  1. 上下文:当前页、选区、页面标题、真实 .md 路径、workspaceId、allowed roots、WeKnora 引用;旧 LightRAG 只作为 legacy/fallback 口径保留。
  2. 授权:把 MNote local-first 文件权限转换为 opencode permission / external_directory / working directory。
  3. 嵌入:第一版通过 MNote 同源受登录态保护反代嵌入 opencode 官方 WebUInpm run dev:hot 默认拉起 opencode serve --hostname=127.0.0.1 --port 4096
  4. 回执:监听 opencode event/diff,触发 MNote watcher 刷新,记录 Page AI session binding。

3.2 opencode 负责完整 agent runtime

  • 聊天 UI。
  • 流式事件。
  • session 管理与恢复。
  • provider/model 调用。
  • tool call 展示。
  • permission ask/allow/deny。
  • 文件读写、patch、diff。
  • MCP / ACP / agent 配置。

3.3 集成形态:不是新的 Leptos island

当前 MNote 文档编辑器已经是 leptos_tiptap_island,但 Page AI 不应该再做一个重前端 island。Page AI 更像 VSCode 里的 Cline

VSCode workbench host  + Cline webview/extension
MNote web shell host   + opencode WebUI iframe/bridge

因此第一版形态是 mnote-web sidebar host runtime

  • 继续使用 rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js 作为宿主入口,但把它瘦身成 opencode host。
  • iframe 内尽量保留 opencode 官方 UI,包括消息、tool、permission、diff/session 页面。
  • iframe 外只放 MNote 必要宿主控件:当前页 context bar、打开变更、刷新当前页、授权状态、运行状态。
  • 不新建 Leptos island,不引入 React/Vue 到 MNote 主壳,不重写 opencode 消息 UI。

如果后续必须深度定制 opencode UI,也优先 fork opencode WebUI 的少量页面,而不是在 MNote 里复刻一套聊天前端。

4. 集成拓扑

MNote Rust SSR :3000
  └─ Page AI sidebar
      ├─ MNote host chrome
      │   [当前页] [选区] [可写目录] [知识库引用]
      │   [打开变更] [刷新当前页] [在主编辑区打开]
      └─ iframe http://127.0.0.1:4096/<project>/session
            ↓ first MVP direct localhost iframe
opencode web :4096
  ├─ 官方 WebUI
  ├─ 官方 HTTP server / OpenAPI
  ├─ SSE /event
  ├─ session/message/diff/permission APIs
  └─ opencodego / configured providers

第一版优先 iframe 官方 WebUI,不 fork、不精简、不重写样式。实测 opencode WebUI 使用根路径 /assets/session/global/health 等资源/API,子路径 /page-ai/opencode/ iframe 会产生 root path 错位;因此 MVP 采用 MNote 同源根路径反代 /<base64-project>/session/<sessionId>,让 iframe 内 location.pathname 与 opencode 官方 WebUI 预期保持一致,同时通过 MNote 登录态保护反代入口。/api/page-ai/opencode/* 负责 session binding、context 注入、status、diff/receipt adapter。只有 iframe/adapter 实测无法满足产品嵌入时,才考虑 fork WebUI。

4.1 MNote host chrome

Page AI 抽屉由 MNote 控制尺寸、开关、上下文和跨应用动作,opencode 只负责 AI 交互主体。

宿主控件最小集:

  • 当前页 pill:标题、相对路径、读写状态。
  • 选区 pill:有选区才展示,点击可重新注入上下文。
  • 变更 pill:来自 opencode diff/event,点击用 MNote 打开对应文件。
  • 刷新按钮:调用 window.__mnoteDocumentPaneRuntime.refreshPrimaryDocument()
  • 打开按钮:调用 window.__mnoteDocumentPaneRuntime.openResourceInActiveTab()

这些控件由 MNote 渲染,避免修改 opencode 官方页面结构。

4.2 MNote 打开 opencode 变更

opencode WebUI 里的 diff / changed file 默认按 opencode 自己的 UI 打开。MNote 需要额外提供宿主打开能力:

opencode diff/event
  → MNote adapter 归一化 changed files
  → Page AI host chrome 显示 changed file chips
  → 用户点击 chip
  → window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({ path })

第一版不强行改 opencode diff 内部点击行为;先在 iframe 外给 MNote-native changed file chips。这样即使 opencode DOM 改版,MNote 打开变更仍可用。

4.3 可选 bridge:只做宿主动作,不接管 UI

如果需要让 opencode 官方 UI 内部的文件链接也能用 MNote 打开,可在同源反代 HTML 中注入一个很小的 bridge 脚本:

opencode iframe click(file/diff link)
  → postMessage({ type: 'mnote:open-file', path })
  → parent MNote host 调用 openResourceInActiveTab

bridge 只允许三类消息:

  • mnote:open-file
  • mnote:refresh-file
  • mnote:session-ready

不通过 bridge 解析模型事件、不重绘消息、不替换 permission UI。DOM 选择器脆弱时立即退回 host chrome chips。

5. 最小实现

Phase Aruntime spike

  • 运行 opencode --versionopencode web --helpopencode serve --help;当前版本已升级到 1.17.9
  • 卸载 oh-my-openagent / oh-my-opencode 默认插件:opencode.jsonplugin 已为空,oh-my-openagent.jsonc 已移除并备份。
  • 启动/复用 opencode serve --hostname=127.0.0.1 --port=4096/global/health 返回 healthy。
  • 验证 WebUI 可打开、可进入 /mnt/Data1T/mnote project session、可真实回复。
  • 验证 opencodego/OmniRoute 模型可用:iframe 内真实回复 OPENCODE_MNOTE_IFRAME_OK_*
  • 验证当前工作目录指向 MNote workspace/session 返回 directory=/mnt/Data1T/mnote
  • 验证编辑一个 .md 文件后,/session/:id/diff 能返回文件 diffopencode-smoke-test.md 返回 modified diff。
  • /event SSE 只做了接口可达性探索,尚未接入持续事件消费。

Phase BMNote iframe embed

  • Rust 新增 /page-ai/opencode/{*path} 反向代理到 127.0.0.1:4096,并新增 /api/page-ai/opencode/status/api/page-ai/opencode/diff
  • 只允许已登录 MNote session 访问反代/API;未登录请求返回 401 page_ai_opencode_unauthorized
  • Page AI sidebar 精简为:MNote host chrome + iframe + basic status。
  • 尽量不改 opencode WebUI,保留官方页面布局、消息样式、tool/diff/permission UI。
  • iframe 容器与 host chrome 由 sidebar-page-ai-runtime.js + page-ai.css 承载,不新增 Page AI Leptos island。
  • WebUI 默认 iframe 使用 MNote 同源 /<base64-project>/session 反代;避免局域网浏览器访问自身 127.0.0.1:4096,同时保留 opencode 官方 URL 形态。

Phase B2MNote-native changed files

  • 建立 opencode sessionId ↔ MNote host 状态的持久 binding/api/page-ai/opencode/session 创建/复用 session,并落 SQLite/control-plane,按用户/session/workspace 区分。
  • 通过 /api/page-ai/opencode/events 代理 opencode /event,收到 session/message/diff/file 事件后触发有界 refresh。
  • 通过 /session/:id/diff 生成 changed file chips。
  • chip 点击走 window.__mnoteDocumentPaneRuntime.openResourceInActiveTab()
  • 当前页刷新按钮已走 refreshPrimaryDocument({ reason: 'page-ai-opencode-host' })changed files 命中当前页时由 event/diff refresh 链路自动触发刷新。

Phase Ccontext 注入

优先不修改 opencode WebUI,通过官方 API/SDK 注入上下文:

MNote open Page AI
  → create or resume opencode session
  → session.prompt(noReply=true, parts=[MNote context envelope])
  → iframe 打开对应 session

上下文 envelope 内容:

  • 当前页标题。
  • 当前页真实 Markdown 路径。
  • selection 文本。
  • allowed roots 与读写权限。
  • WeKnora 引用摘要;旧 LightRAG 引用只作为 legacy/fallback。
  • 当前任务约束:优先编辑 primaryTarget,禁止越权修改。

当前状态:done for MVP。host chrome 已展示当前页、真实 Markdown path(可定位时)、selection、allowed roots/writable 状态;打开 Page AI 时会调用 /api/page-ai/opencode/session,用 noReply=true 向 opencode session 注入 MNote context envelope,并把 iframe 打到绑定 session URL。2026-06-24 已按 opencode-chat 参考改回官方 WebUI iframe 主路径,MNote-native timeline 仅保留为 debug/receipt 边界。

Phase Dwriteback / receipt

  • 监听 opencode /event 或 SDK event.subscribe():当前通过 /api/page-ai/opencode/events 代理 /eventEventSource 收到相关事件后触发有界 refresh。
  • [partial] prompt / event 后通过绑定 session 的 /session/:id/diff 刷新 changed files;仍需继续核对 opencode 各类 file/diff event payload。
  • changed files 命中当前页时调用 refreshPrimaryDocument({ reason: 'page-ai-opencode-event' });手动刷新按钮保留。
  • 保存 MNote pageId ↔ opencode sessionId binding:当前已落 SQLite/control-plane,按用户/session/workspace 区分;浏览器刷新后可恢复同一 binding。
  • Page AI context bar 显示最近一次 changed files / runtime / error 摘要。

Phase E:可选 WebUI bridge

  • 只有 host chrome chips 体验不足时,才在反代层注入 mnote-opencode-bridge.js
  • bridge 只把 opencode UI 内部文件点击转成 postMessage
  • bridge 不解析/修改 opencode 消息流、tool UI、permission UI。
  • selector 失效时不阻塞主流程,回退 host chrome chips。

6. 安全与权限

  • opencode 只监听 127.0.0.1;MVP iframe 直连本机地址,同源反代/API 仍必须受 MNote 登录态保护。
  • 生产/长期运行必须设置 OPENCODE_SERVER_PASSWORD,或改为完整同源反代 + MNote 反代层隔离。
  • opencode working directory 优先指向当前 workspace root。
  • MNote allowed roots 映射到 opencode permission
    • 当前 workspace root:允许读,写按用户授权。
    • 当前页文件:允许读写。
    • workspace 外路径:默认 deny,必要时显式 external_directory
  • 不使用 --dangerously-skip-permissions 作为默认路径。

7. 验收标准

7.1 UI

  • Page AI 面板内显示 opencode 官方 WebUI。
  • 官方消息流、tool 卡片、permission 交互、diff/session UI 尽量原样保留。
  • MNote 只在 iframe 外展示 host chrome,不重做 opencode UI。
  • opencode 产生的 changed files 可以用 MNote 主编辑区或资源 tab 打开。

7.2 Runtime

  • 可创建/恢复 opencode session。
  • 可用 opencodego 模型完成真实回复。
  • 流式回复浏览器可见。
  • 权限审批走 opencode 原生机制。
  • 文件修改后 MNote 当前页面能刷新。

7.3 代码收敛

  • sidebar-page-ai-runtime.js 不再承载 Reasonix/ZCode/Hermes/Board provider 状态机。
  • 不新增 MNote 自研聊天 message store。
  • 不 fork opencode WebUI,除非 spike 证明 iframe 方案不可用。
  • 不新增 Page AI Leptos islandPage AI 是 mnote-web sidebar host runtime。

8. 与旧方案对比

方案 优点 主要问题 当前结论
Board-first 可接多 worker/workflow MNote 仍要维护产品壳和 Board adapter,聊天体验不成熟 废弃为主路径
CodexMobile embed Codex 体验强,贴近现有 Codex 体系 需要 fork/精简/修 bug,维护派生产品 备胎/对照
opencode WebUI embed 官方 runtime + 官方 WebUI + 官方 API,维护成本低 opencode 能力可能不如 Codex 先进,嵌入细节需实测 当前主路径

9. 非目标

  • 不重写 opencode WebUI。
  • 不把 opencode WebUI 拆成 MNote 原生组件。
  • 不同时接入 Reasonix/ZCode/Hermes/CodexMobile 多后端。
  • 不把 Agent Board 控制台嵌入 Page AI。
  • 不在第一版实现完整 MNote SSO 到 opencode;先由 MNote 反代保护。

10. 退出条件

只有出现以下任一情况,才重新启用 CodexMobile 或自研 UI 方案:

  • opencode WebUI 无法稳定 iframe/反代嵌入。
  • opencode session 无法通过 API 定位并打开指定 session。
  • opencode 无法可靠编辑 MNote workspace 文件。
  • opencode permission/diff/event 无法满足 MNote 最小安全闭环。
  • opencodego/provider 链路在真实使用中明显不稳定且短期不可修。

9. 2026-06-24 iframe 主路径验证记录

  • opencode --version1.17.9
  • opencode serve --hostname=127.0.0.1 --port 4096 --print-logs:真实可启动;官方 WebUI URL http://127.0.0.1:4096/L21udC9EYXRhMVQvbW5vdGU/session 可显示 Build anything
  • npm run dev:hot:真实拉起 mnote-web :3000opencode :4096
  • 浏览器 smoke:登录 mnote.e2e@example.com 后打开 Page AIiframe URL 为 http://127.0.0.1:3000/L21udC9EYXRhMVQvbW5vdGU/session,显示官方 opencode WebUI;截图 /tmp/mnote-page-ai-opencode-iframe.png
  • [partial] 发送真实消息:官方 WebUI 可输入并进入 Thinking/Stop 运行态;截图 /tmp/mnote-page-ai-opencode-send.png。本轮未等待到最终回复,不能声明 provider 回复完成。
  • binding 持久化 smoke:浏览器 reload 后仍恢复同一 session ses_106555512ffe5wDz9eMlP4v2i2
  • 静态检查:node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
  • Rust 检查:cd rust && cargo check -p mnote-web

剩余 gap

  • 在同源反代层补最小 postMessage bridgeopen-file / refresh-file / insert-text,不接管消息流。
  • [partial] 更完整核对 opencode /event 的 file/diff payload:已递归解析常见 changed/diff/file payload 并由 event 触发有界 refresh;仍需真实编辑文件后补最终证据。
  • 若要局域网访问,继续确认所有 opencode WebUI root-level API 路由都已被 MNote 登录态反代覆盖,避免直接暴露 4096。

10. 2026-06-25 bridge / receipt 补充记录

  • HTML 反代注入极小 bridge:只处理 insert-text / open-file / refresh-file / session-ready,不接管 opencode 消息流、tool UI、permission UI。
  • MNote host chrome 新增“插入当前页上下文”按钮,通过 postMessage 把当前页标题、路径、选区、allowed roots 插入 opencode 官方输入框。
  • 浏览器 smoke/tmp/mnote-page-ai-opencode-bridge-final.png,验证 iframe 内 window.__mnoteOpencodeBridgeInstalled === true,点击 host 按钮后官方输入框出现 MNote 当前页上下文标题:主页
  • postMessage smoke:模拟 iframe 发 open-file / refresh-file,确认 MNote host 调用 openResourceInActiveTab()refreshPrimaryDocument();输出见 tmp/mnote-opencode-postmessage-smoke.cjs 运行结果。
  • event receipt 强化:pageAiOpencodeNormalizeChangedFiles() 改为递归收集 changedFiles / files / diff / changes / edited / created / deleted / data / properties,事件到达时先更新 chips,再做有界 projection refresh。
  • 静态检查:node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
  • Rust 检查:cd rust && cargo check -p mnote-web

剩余 gap

  • 让 opencode 真实修改一个测试 Markdown 文件,等待 /event + /session/:id/diff 产出 changed file chips,再截图验证 chip 点击打开 MNote 主编辑区。
  • 若 opencode 官方 WebUI 后续 CSP 变化,需要把 inline bridge 改成 nonce/hash 或外部小脚本。

11. 2026-06-25 opencode 用户隔离与工作目录结论

11.1 当前结论

  • Page AI 工作目录应简化为“当前用户打开的根文件夹”:MNote 当前只能打开一个大的本地根目录,rootUri 对应的真实目录就是 opencode directory / project directory。
  • 切换根文件夹应视为新的 opencode session 边界:同一用户同一根目录内可按页面恢复 binding;根目录变化时默认新建 session,不把旧 session 带到新根目录。
  • MNote 自身 binding 已按 user_id + workspace_id + mnote_session_id + provider 隔离;mnote_session_id 内包含 workspace/page/directory,因此同一用户跨根目录不会复用同一 binding。
  • [partial] opencode 自身默认实例没有 MNote 用户概念;如果所有 MNote 用户共用一个 opencode serveopencode 的 session、permission、credential、account、skill、MCP、snapshot、tool-output 会共用同一套本机状态。

11.2 opencode 1.17.9 真实机制证据

本机 spike 使用临时环境启动:

HOME=/tmp/.../home \
XDG_CONFIG_HOME=/tmp/.../config \
XDG_DATA_HOME=/tmp/.../data \
XDG_CACHE_HOME=/tmp/.../cache \
XDG_STATE_HOME=/tmp/.../state \
opencode serve --port 4197 --hostname 127.0.0.1 --print-logs

观察结果:

  • 配置读取路径变为 $XDG_CONFIG_HOME/opencode/{config.json,opencode.json,opencode.jsonc}
  • 持久数据写入 $XDG_DATA_HOME/opencode/opencode.db
  • 日志写入 $XDG_DATA_HOME/opencode/log/opencode.log
  • 锁写入 $XDG_STATE_HOME/opencode/locks/*
  • 当前全局实例的默认持久库是 /home/lix/.local/share/opencode/opencode.db
  • opencode.db 内包含 sessionmessagepartpermissioncredentialaccountaccount_stateprojectworkspaceevent 等表;这些表没有 MNote 用户维度。
  • permission 只按 project_id + action + resource 唯一;共用实例会导致不同 MNote 用户在同一 project/directory 下共享 opencode 权限记忆。
  • session 表包含 directory / project_id / workspace_id / metadata,但不包含 MNote user_id;共用实例不能作为安全隔离边界。
  • opencode 会从当前用户 HOME/配置路径加载 skill/MCP;未隔离时可看到 /home/lix/.claude/home/lix/.agents/home/lix/.config/opencode/skill 等重复 skill 警告。

11.3 推荐隔离方案

第一版不要试图在单个 opencode server 内实现多用户隔离;改为 MNote 用户/根目录维度的 opencode runtime profile

MNote user + workspace/rootUri
  -> runtime profile id
  -> dedicated XDG_CONFIG_HOME / XDG_DATA_HOME / XDG_CACHE_HOME / XDG_STATE_HOME
  -> dedicated opencode serve process on 127.0.0.1:dynamic_port
  -> MNote 登录态反代 /page-ai/opencode/...

目录建议:

$MNOTE_DATA/users/<user-id>/opencode/<workspace-hash>/config/opencode/opencode.json
$MNOTE_DATA/users/<user-id>/opencode/<workspace-hash>/data/opencode/opencode.db
$MNOTE_DATA/users/<user-id>/opencode/<workspace-hash>/cache/opencode/
$MNOTE_DATA/users/<user-id>/opencode/<workspace-hash>/state/opencode/

配置策略:

  • provider/API key 可由 MNote 管理后写入每个 profile 的最小 opencode.json,或在本机单用户 dev 模式从 /home/lix/.config/opencode/opencode.json 复制 provider/model 白名单。
  • skill/MCP 默认不继承宿主 HOME 的全部内容;只显式安装 MNote 允许的 skill/MCP,例如 codegraphmempalace、后续 MNote MCP。
  • opencode 原生 permission 继续保留,但存储在该 profile 的独立 opencode.db 中。
  • MNote 的 allowed roots 仍作为上下文与反代边界;opencode 的工作目录固定为当前打开根目录。

11.4 session 策略

  • 同一 user_id + workspace_id/rootUri + pageAbsolutePath:优先恢复 MNote control-plane binding 指向的 opencode session。
  • rootUri 变化:默认新建 session;旧 binding 保留但不跨根目录复用。
  • pageAbsolutePath 变化:默认新建或按页面 binding 恢复,不把旧页面上下文继续注入到新页面。
  • opencode 原生 session 恢复只作为 profile 内部能力;MNote 是否恢复以 control-plane binding 为准。
  • 跨浏览器恢复依赖 SQLite control-plane binding + per-user opencode data profile,不依赖 sessionStorage

11.5 后续实现项

  • 新增 opencode runtime profile manager:按 MNote user/rootUri 分配 XDG 目录、端口、启动/健康检查、生命周期。
  • dev:hot 继续可一键启动,但默认只启动 dev 单用户 profile;多用户 profile 由后端按需拉起。
  • 反代不再只读 MNOTE_OPENCODE_BASE_URL 全局值,需按当前登录用户和 rootUri 解析到对应 profile base URL。
  • session binding metadata 写入 runtimeProfileIdrootUriprojectDirectoryopencodeDataDir,方便审计和恢复。
  • 增加最小测试:两个不同 MNote 用户同一 Markdown 根目录下创建 session,不共享 opencode opencode.db、permission、session list。

11.6 官方/社区多用户实现调研补充

本轮先核验豆包给出的项目名,再补充搜索到的真实社区线索。结论:没有发现可直接嵌入 MNote 的官方/社区“单 opencode 进程多 MNote 用户强隔离”实现;官方当前也把多用户 Web/serve 部署视为待增强能力。

豆包列表验真:

项目 验真结果 对 MNote 的价值
anomalyco/opencode-orchestrator 未找到公开仓库;真实相近项目是 agnusdei1207/opencode-orchestrator 后者是 opencode 多 agent 编排插件,不是多用户 runtime 隔离层
pRizz/opencode-cloud / gitea.com/pRizz/opencode-cloud 真实存在 Docker/container 级隔离参考;安全强但本地 MNote 开销偏大
oc-ext/ocx 未找到公开仓库 暂不可作为依据
daytonaio/daytona-opencode-plugin 未找到公开仓库 暂不可作为依据
anomalyco/openwork 未找到公开仓库 暂不可作为依据
lucentia/opencode-svip-proxy 未找到公开仓库 暂不可作为依据
kwickramasekara/opencode-chat 真实存在 VSCode WebView 嵌入参考:启动/复用 opencode serve、固定端口保留 localStorage、WebView proxy、剪贴板/键盘 bridge;不是多用户隔离实现

额外发现:

项目/线索 结论
官方 anomalyco/opencode issue #20067 open:请求 opencode web 支持 multi-user auth 与 per-user provider credentialsissue 描述确认共享 Web 实例会共享身份、session、provider credentials
官方 anomalyco/opencode issue #5784 closed:请求多租户 serve 下 MCP auth/config;说明多租户 MCP/资源隔离是社区真实痛点,但不是已可用的完整 MNote 用户隔离方案
官方 SECURITY.md 明确 opencode 不提供安全 sandboxserver mode 只支持 OPENCODE_SERVER_PASSWORD Basic Auth;需要真隔离时建议 Docker/VM
millerjes37/opencode-multiplexer 真实存在,是 opencode fork 的 multi-client server 支持;文档承认“知道 sessionID 即可交互”的 session hijacking 风险,session ownership 仍是 future enhancement;不适合作 MNote 多用户隔离底座
joeyism/opencode-multiplexer 真实存在,是多 session/多项目终端 dashboard,不是 Web 多用户隔离

对当前方案的修正:

  • 不应引入豆包描述的“单进程多租户 opencode”作为近期主路径;目前没有可靠公开实现,官方也还在 issue 层面。
  • 也不应一开始上 Docker/container;它解决安全隔离但会显著增加本地笔记软件的启动、资源和运维成本。
  • 近期最稳妥路线是 单 opencode 进程 + 单 MNote 当前用户/当前根目录 profile,先满足本机单用户和局域网同一登录用户;等 MNote 真正进入多用户同时在线场景,再升级为按需 profile manager。
  • 若要减少端口/生命周期复杂度,可先采用 opencode-chat 的轻量做法:固定一个 dev/local 端口、优先复用已存活 server、MNote 反代统一入口;不要提前实现多实例调度。
  • 多用户隔离仍必须作为设计约束保留:不能把共享 opencode opencode.db 声称为安全隔离,只能标为单用户/dev 模式。

更新后的分阶段建议:

  1. Phase MVP-local:一个 MNote 登录用户 + 一个当前根目录 + 一个 opencode server;工作目录固定为 rootUriMNote control-plane 做跨浏览器 session binding。
  2. Phase shared-device:为每个 MNote 用户准备独立 XDG profile,但不常驻多进程;登录/打开 Page AI 时按需启动,空闲回收。
  3. Phase SaaS/团队:再评估 opencode-cloud/Docker 或等待官方 multi-user auth/per-user credentials 落地;不要自己 fork 官方 opencode 做单进程多租户。

11.7 OpenHub 对照结论

xcl1989/OpenHub 是目前找到的最接近“opencode 多用户平台”的社区实现,README 明确主张:一个 opencode serve (:4096),后端按用户 workspace 通过 ?directory= 路由到不同目录,并在应用 SQLite 中维护 users、sessions、messages、permissions、skills、tools 等业务层权限。

可复用点:

  • 单 opencode server + per-user workspace:后端调用 /session/session/{id}/prompt_async/global/event 时统一带 directory=<user workspace>
  • 应用层 session ownershipOpenHub 自己用 SQLite 记录 conversation_sessions / messages / user_id,不把 opencode 原生 session 列表直接暴露给所有用户。
  • 应用层权限面板:模型权限、工具权限、skill 权限都在业务 DB 中维护,再同步/注入到用户 workspace。
  • per-user .opencode 目录:README 架构图显示每个 workspace 下有独立 .opencode/skills.opencode/tools
  • 单进程运维简单:固定 OPENCODE_BASE_URL=http://127.0.0.1:4096Basic Auth 保护后端到 opencode 的内部访问。

关键风险:

  • OpenHub 不是 opencode 原生多租户;它仍依赖一个全局 opencode server 和全局 opencode 数据库/credential/account 状态。
  • 隔离主要靠 directory 与 OpenHub 后端不暴露跨用户 session;如果绕过 OpenHub 直接访问 opencode,或知道别人的 session id,仍要依赖外层鉴权/反代拦截。
  • provider credentials 是 opencode server 全局配置;OpenHub 的用户模型/工具权限是应用层控制,不等于 opencode 内核 per-user credentials。
  • 它自研了聊天前端、消息库、知识/记忆/任务系统;这不符合 MNote 当前“尽量保留 opencode 官方 WebUI,不复刻消息 UI”的边界。

对 MNote 的启发:

  • OpenHub 证明“单 opencode serve + ?directory= 按用户工作区隔离”在产品上可跑,比一开始做多进程 profile manager 更轻。
  • MNote MVP 可以采用 OpenHub 的轻量隔离思路:固定一个本机 opencode server,所有请求由 MNote 登录态反代,MNote 后端只允许当前用户的 rootUri 作为 directory,并用 control-plane binding 限制 session ownership。
  • 但必须把这种模式标为 应用层隔离 / 单机可信 opencode 后端,不能标为强安全多租户。强隔离仍需后续 XDG profile 或 Docker。

更新后的推荐:

  1. 立即采用 OpenHub-lite:单 opencode serve、固定端口、MNote 反代、directory=rootUri、control-plane session ownership。
  2. 不复刻 OpenHub UI:仍保留 opencode 官方 WebUI iframeMNote 只做 host chrome、context、open/refresh、changed files。
  3. 补安全闸:所有 /api/page-ai/opencode/* 和 iframe 反代必须校验当前登录用户、rootUri、session binding;不允许前端任意传 directory 打开非当前 root。
  4. 后续 shared-device 再升级:当确实有多 MNote 用户同时使用同一机器时,再做 per-user XDG profile manager。

11.8 OpenHub 融合可行性评估

用户新判断:OpenHub 的前端、消息库、权限、记忆、文件、任务等功能与 MNote Page AI 长期目标高度重合,应评估是否直接融合,减少 MNote 自研量。

结论:可融合,但不建议整套 OpenHub 作为 MNote 新后端;推荐抽取 OpenHub Page-AI 子系统,形成 MNote 内的 OpenHub-lite

可最大化复用的部分:

  • React/AntD 聊天前端SmartQueryPage.jsxChatInputAssistantMessageToolCallQuestionFormHistoryDrawerDiffViewerFileManagerGitTimeMachine 等,可作为 Page AI 的 micro frontend,而不是继续维护当前简陋 host UI。
  • 消息库模型conversation_sessionsconversation_messages、图片、turn、opencode message id、归档、retry、last-turn delete 等,适合迁移到 MNote control-plane,替代 sessionStorage 和当前临时 binding。
  • opencode 单进程接入模式:后端按用户 workspace/rootUri 调 /session/session/{id}/prompt_async/global/event 并带 directory=,适合 MNote 当前“一个打开根目录”的简化模型。
  • 应用层权限面板:模型权限、工具权限、skill 权限、usage 统计可以映射到 MNote 用户体系;短期先只做 Page AI 所需的模型/工具/skill 白名单。
  • 任务/团队/记忆模块Smart Entity、Team、Memory、Scheduler 与 MNote 长期 agent 目标相关,但第一阶段只作为后续模块,不应阻塞 Page AI MVP。

不建议直接搬入的部分:

  • OpenHub 自带登录、用户管理、admin 页,与 MNote control-plane auth 重叠;应替换成 MNote 登录态。
  • OpenHub FastAPI 后端与 MNote Rust SSR/control-plane 双后端并存会增加部署复杂度;此判断已被 7-68 覆盖,当前第一阶段保留 OpenHub FastAPI/Redis/opencode client。
  • OpenHub 自研知识库/记忆/任务会与 MNote WeKnora、workspace、tree/file resource、control-plane 产生事实源冲突;旧 LightRAG 仅作为 legacy/fallback 参考。
  • OpenHub 不是 opencode 内核级强隔离,仍需 MNote 反代和 session ownership 限制。

推荐融合路线:

  1. Phase 1iframe micro frontend spike

    • 直接运行 OpenHub 前端的 Page-AI/Chat 子集,嵌入 MNote sidebar。
    • 后端 API 不直接用 OpenHub FastAPI,而是由 MNote 提供兼容 /api/query/stream/api/sessions/*/api/files/* 的最小 Rust adapter。
    • 目标是快速验证 UI/消息体验是否明显优于 opencode 官方 iframe。
  2. Phase 2:消息库迁移

    • 在 MNote control-plane 增加 OpenHub-like page_ai_sessions / page_ai_messages / page_ai_message_parts / page_ai_turns
    • 将 opencode session id、message id、tool calls、diff、reasoning、attachments、rootUri、pageAbsolutePath 统一持久化。
    • 替代当前临时 Page AI binding;支持跨浏览器、跨会话恢复。
  3. Phase 3UI 组件裁剪融合

    • 从 OpenHub 前端抽出 Chat shell、消息列表、工具调用、历史抽屉、Diff/File/GitTimeMachine 组件。
    • 去掉 Login/Admin/Knowledge/SmartEntity/Team 等非 Page AI 首屏模块。
    • 适配 MNote host chrome、当前页 context、changed file chip、MNote open/refresh。
  4. Phase 4:高级能力选择性引入

    • FileManager 映射 MNote resource/file tree。
    • GitTimeMachine 映射 MNote changed files / snapshot / restore 设计。
    • Memory/Skill/Tool permission 映射 MNote 用户权限和未来 MNote MCP。
    • Smart Entity/Team 作为 Page AI 后续 agent team,不进入当前 MVP。

技术判断:

  • 如果目标是“尽快有成熟 Page AI UI”,OpenHub 前端比 opencode 官方 iframe 更适合深度定制,因为它已经是普通 React/AntD 应用,消息、工具、历史、文件、diff 都在前端组件内。
  • 如果目标是“最少维护债”,opencode 官方 iframe 仍最省事,但 MNote 与页面/文件/权限/历史的融合会受 iframe 限制。
  • 当前更适合改为 OpenHub UI + MNote Rust adapter + opencode runtime:UI 和消息体验复用 OpenHub,用户/文件/权限/工作区真相仍归 MNoteagent runtime 仍归 opencode。

新的建议:

  • 7-65 当前 iframe 方案降级为 runtime spike 与 fallback。
  • 新增或接续设计 7-67-openhub-page-ai-fusion-v1,目标是用 OpenHub Chat 子系统替代当前 Page AI UI。
  • 第一阶段只做 Chat/Session/Message/Diff/File open 五件事,不引入 OpenHub 登录/admin/知识库/team。

11.9 OpenHub 知识库实现与 WeKnora 对照

用户最新判断:Page AI 要做深度融合;MNote 当前已放弃 LightRAG,知识库方向原计划是 WeKnora。因此需要单独核验 OpenHub 自带知识库是否能替代 WeKnora。

源码核验结论:OpenHub 自带知识库是轻量 SQLite 文本知识库,不是完整 RAG/知识库底座;适合复用 UI、API 形状和 prompt 注入链路,不建议替代 WeKnora。

OpenHub 知识库真实实现:

  • 数据表只有 knowledge_basesknowledge_sources:字段包括 scope=enterprise/userowner_idtitlesource_typecontenttags、统计字段;没有 chunk 表、embedding 表、向量库、图谱或 citation 表。
  • 上传解析支持 .md/.txt/.pdf/.docx/.xlsx/.csvPDF 走 PyMuPDF 文本抽取,DOCX 走 python-docx 段落抽取,表格转文本行;没有 OCR、版面恢复、图片解析或复杂文档结构保真。
  • chunker.py 存在 Markdown/文本/表格切块逻辑,但当前知识库主链没有把 chunk 持久化到 DB,检索与注入仍围绕整份 knowledge_sources.content
  • 检索分两层:DB 层用 LIKE 关键字筛出候选;服务层再对候选全文做 CJK/英文 token 的 BM25 + TF-IDF 重排;没有 embedding、semantic search、rerank model、hybrid vector search。
  • 注入方式是 prompt stuffing:小型个人知识库全量或近似全量注入,大型个人知识库取 2 条结果,企业知识库最多取 1 条结果,每条截取相关片段,总上下文默认限制约 1200 字符。
  • opencode 集成点是在发送用户问题前构造 <context>...</context>,并提示模型如果上下文不足就调用 knowledge_knowledge_search 工具继续查。
  • 前端 KnowledgeManager.jsx 和 admin 企业知识库 UI 可直接参考:列表、搜索、上传、添加、编辑、删除、统计、企业只读提示这些产品能力与 MNote 需要高度重合。

与 WeKnora 的关系:

  • WeKnora 应继续作为 MNote 长期知识库底座候选:负责文档解析、索引、检索、召回、引用、权限过滤与跨文档问答。
  • OpenHub 知识库不应替代 WeKnora;它更像“用户短记忆/轻量知识片段/企业公告文本”的 fallback。
  • 最优融合方式是 OpenHub Knowledge UI + MNote Rust 知识库 adapter + WeKnora provider:前端交互复用 OpenHub,后端接口形状兼容 OpenHub,但真正的 ingestion/search/citation 由 MNote 调 WeKnora。
  • OpenHub 的 knowledge_sources schema 可以作为 MNote control-plane 的 source registry 参考,但需要增加 workspace_id/root_uri/resource_id/source_uri/provider_doc_id/index_status/permission_scope/citation_locator 等 MNote 字段。
  • OpenHub 的 prompt 注入链路可以短期复用为 Page AI context block,但 WeKnora 命中结果必须带 citation/open-reference 映射,不能只塞纯文本。

对 7-67 深度融合设计的影响:

  1. Page AI 主 UI 继续选 OpenHub Chat 子系统,而不是官方 opencode iframe。
  2. Knowledge 模块第一阶段只迁移 UI 与 API contract,不迁移其 SQLite 文本检索为长期底座。
  3. MNote Rust adapter 提供 OpenHub-compatible /api/knowledge/*,内部走 WeKnora 或本地 fallback。
  4. 保留 OpenHub 轻量知识库作为“未配置 WeKnora 时的 local fallback / 用户手工短知识”,但不能称为默认知识库主线。
  5. 新设计稿应明确:OpenHub UI 负责交互,MNote control-plane 负责用户与权限,WeKnora 负责知识库索引与检索,opencode 负责 agent 执行。