# 7-71 MNote 统一 AI 管理面板与 Pi Lab 功能接入设计 v1 状态:process Owner:07-ai / mnote-web / control-plane / Page AI 日期:2026-07-04 ## 1. 结论 应该做 MNote 原生统一 AI 管理面板,但顺序不是先堆 UI,而是先把 control-plane 合同、持久化和 effective config API 做成唯一真相,再让 **Pi Rust Page AI / LightRAG** 消费同一套配置。 文件夹授权必须只有一套:当前 `/admin/access-policy` 与 `/user/access-policy` 管理的 `directory_grants` 同时就是 MNote 工作区访问授权和 AI `allowed roots` 的事实源。AI 管理面板可以把它纳入同一个信息架构,但不能再维护第二套 AI-only allowed roots。 OpenHub 的 admin 面板(历史)证明了正确产品结构:provider/model、skills、tools、MCP、目录权限、知识库、历史、用量、健康状态必须先有管理面,聊天 UI 只展示用户可用的有效配置。MNote 不能把 OpenHub admin 作为 MNote AI 的长期真相。**2026-07-18:OpenHub 已不再是默认 Page AI 主线;Pi Rust 为默认入口。** MNote AI 管理中心应落在 Turso/libSQL control-plane。上位收口见 checklist 21。 ## 2. 调研依据 ### 2.1 OpenHub 源码: - `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/pages/AdminPage.jsx` - `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/components/UserSettingsDrawer.jsx` - `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/components/HistoryDrawer.jsx` - `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/services/api.js` - `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-backend/app/api/admin.py` 可复用的产品模型: - 管理后台按区段管理:用户、模型配置、工具权限、技能、目录权限、知识库、用量、系统健康。 - 用户设置抽屉只显示管理员已允许的有效配置:模型、技能、MCP、目录、工具、用量。 - 权限是三层:admin global policy -> user override -> effective policy。 - 历史记录是左侧抽屉,支持新建、刷新、归档、分页/滚动。 不应直接搬的部分: - opencode 服务管理、飞书渠道、WeKnora 配置导出。 - React/AntD 组件主链。 - OpenHub SQLite 表作为 MNote 真相层。 ### 2.2 Pi / Pi Web UI 源码: - `/mnt/Data1T/tmp/pi-web-ui-shot/node_modules/@earendil-works/pi-web-ui/src/` - `/mnt/Data1T/tmp/pi-web-ui-shot/node_modules/@earendil-works/pi-agent-core/dist/` - `rust/crates/mnote-web/src/routes/page_ai_pi.rs` - `rust/crates/mnote-web/browser/sidebar-page-ai-pi-lab-runtime.js` 可复用的合同: - `SessionData`: `id/title/model/thinkingLevel/messages/createdAt/lastModified` - `SessionMetadata`: `id/title/lastModified/messageCount/usage/thinkingLevel/preview` - `AgentEvent`: `agent_start/end`、`message_update/end`、`tool_execution_start/update/end` - `ThinkingLevel`: `off/minimal/low/medium/high/xhigh` - Provider/model 设置形态:SettingsDialog、ModelSelector、ProvidersModelsTab、CustomProvider。 不能照搬的部分: - IndexedDB 是 Pi standalone browser app 的存储,不是 MNote 真相层。 - Pi 原始 `bash/read/write/edit` 仍必须禁用。 - Pi web-ui 是 Lit Web Components;MNote 当前 Pi Lab 保持原生 runtime,不 iframe、不让 Pi UI 接管 provider key 或工具权限。 ### 2.3 MNote 当前底座 已有: - `directory_grants`:当前文件夹授权事实。 - `ai_policies`:已有 `allowed_roots_json / model_policy_json / quota_json`,但无 HTTP 管理 API 和 UI。 - `ai_runtime_runs / ai_runtime_events`:已有跨 provider run/event journal。 - `ai_external_conversation_bindings`:已有 MNote session 与外部 provider conversation 绑定。 - `/admin/access-policy` 与 `/user/access-policy`:已有文件夹授权面板。 缺口: - Pi Lab session 仍在内存 HashMap,重启丢失。 - Pi Lab receipt 仍在内存 + JSONL adapter,无法按用户/工作区查询。 - provider/model/skills/MCP/tools 的 MNote-native 管理 API 和 UI 不存在。 - Pi Lab UI 的历史、设置、模型选择还没有接入真实配置源。 ## 3. 目标架构 ```text MNote AI 管理中心 /admin/ai admin global policy /user/ai user effective settings | v Turso/libSQL control-plane directory_grants ai_policies ai_runtime_runs / ai_runtime_events ai_external_conversation_bindings ai_provider_configs / ai_model_catalog / ai_model_grants ai_tool_policies / ai_skill_registry / ai_mcp_servers ai_tool_events / ai_file_patches | v MNote AI Facade APIs effective config allowed roots model/provider catalog skills/tools/MCP policy LightRAG facade session history / receipts | +--> OpenHub iframe/default Page AI +--> Pi Lab native drawer +--> future native agents ``` OpenHub 继续保持默认 Page AI 入口。MNote 管理中心不替换 OpenHub admin,而是成为 MNote 侧 AI 权限和配置的真相层;需要同步给 OpenHub 时,通过 provider identity sync / OpenHub admin API 做下游同步。 ## 4. 管理面板信息架构 ### 4.1 Admin:`/admin/ai` 沿用现有 `/admin/access-policy` 的 SSR 页面方式,新增 AI 管理中心。左侧区段建议: 1. `Overview` - AI runtime 状态、OpenHub 状态、Pi Lab 状态、LightRAG 状态、最近错误。 2. `Providers & Models` - Omniroute/OpenAI/Anthropic/Google/local providers。 - API key/base URL/认证状态。 - 模型可见性、默认 Build/Plan/Task 模型、failover chain。 - 默认保留 `omniroute/freefirst` 作为 Pi Lab dev/default model。 3. `Access Scopes` - 直接复用当前 `/admin/access-policy`、`/user/access-policy` 文件夹授权 UI 和既有授权 API,不新增 AI-only 授权目录。 - `directory_grants` 是 MNote 与 AI 共用的 allowed roots 事实源。 - `AiAccessScope.allowed_roots`、OpenHub allowedRoots、Pi Lab allowed roots 都从 `directory_grants` 派生。 - 支持 read/write、recursive、capabilities、workspace/user 维度。 - 该区段只提供授权摘要、effective scope 预览和进入现有授权页的入口;若后续内嵌编辑,也必须调用同一套 `directory_grants` command/API。 4. `Tools` - MNote-owned tools 全局策略:allow/ask/deny。 - 默认 deny raw bash/read/write/edit。 - Pi 只显示并使用 MNote facade tools。 5. `Skills` - MNote builtin skills、Codex/Acontext skills、OpenHub/opencode skills 的 registry。 - 全局启停,用户可在授权范围内开关。 6. `MCP` - MCP server registry、启动命令、allowed roots、network policy、env/secret 引用。 - 默认不让 Pi 直接接管 raw MCP;Pi 通过 MNote facade 调用。 7. `Knowledge` - LightRAG 是唯一默认 provider。 - 管理 source registry、index 状态、citation/open-reference 映射。 8. `Sessions & Receipts` - Page AI / OpenHub / Pi Lab / native agent 历史。 - tool events、deny reason、diff summary、file version、citation count。 9. `Usage & Quota` - 按用户、workspace、provider、model 统计。 - token、cost、request count、失败率。 ### 4.2 User:`/user/ai` 用户设置只显示 effective config: - 可用模型:来自 admin allowed models;用户只能隐藏或选择默认,不提升权限。 - 可用 skills/tools/MCP:只能在 admin 允许范围内开关或降级。 - 目录权限:查看由 `directory_grants` 派生的 allowed roots,并进入 `/user/access-policy` 请求或调整授权;不保存 AI 私有目录配置。 - 历史记录:自己的 Page AI / Pi Lab sessions。 - 用量:自己的 quota 与调用统计。 ### 4.3 Pi Lab 抽屉接入 Pi Lab 顶部工具栏: - 历史按钮:打开 Pi Lab history drawer,数据来自 MNote `/api/page-ai/pi/sessions` 或统一 `/api/page-ai/sessions?provider=pi`。 - 设置按钮:打开 MNote-native user AI settings overlay;管理员可跳到 `/admin/ai`。 - 模型选择:读取 effective model options,不再硬编码单个 select。 - Build/Plan:读取 agent mode policy,决定可用模型、thinkingLevel、tool policy。 - 工具/引用/diff:继续只显示 MNote facade 返回的 receipt 和 citation。 ## 5. 数据合同 ### 5.1 复用现有表 - `directory_grants`:MNote 与 AI 共用的唯一文件夹授权事实源,也是 allowed roots 派生源。 - `ai_policies`:workspace/user 的模型、配额、工具、skills/MCP 策略汇总层;不得作为第二套 allowed roots 真相。历史字段 `allowed_roots_json` 只能视为缓存/兼容快照,写入时必须由 `directory_grants` 派生。 - `ai_runtime_runs`:统一 AI session/run 元数据。Pi Lab 不新建独立 session 真相表,先复用它。 - `ai_runtime_events`:统一 message/tool/runtime event journal。 - `ai_external_conversation_bindings`:OpenHub remote conversation / future external provider binding。 - `user_ui_preferences`:前端折叠状态、默认 tab 等低风险 UI preference。 ### 5.2 授权目录单一真相规则 - 不新增 `ai_allowed_roots`、`pi_allowed_roots`、`openhub_allowed_roots` 或任何同义表。 - 不允许 Pi Lab、OpenHub、AI 管理页或 session 设置单独写一份目录授权 JSON。 - 所有目录授权变更统一写入 `directory_grants`,沿用现有管理员/用户权限边界、审计和路径校验。 - AI effective config 按 user/workspace 读取 `directory_grants`,生成 `AiAccessScope.allowed_roots`。 - Pi Lab 创建 session/run 时记录 effective scope 快照,但每次工具执行仍按当前 `directory_grants` 重新校验。 - OpenHub `allowedRoots` 是 MNote 下发的下游投影,不能反向成为授权真相。 - `ai_policies.allowed_roots_json` 只能作为不可独立编辑的兼容快照、迁移输入或缓存;满足迁移条件后应删除或改名,避免误用和双写。 ### 5.3 新增或规范化表 第一阶段优先最小新增: ```sql ai_tool_events( id, user_id, workspace_id, session_id, run_id, provider, provider_session_id, tool_name, allowed, deny_reason, root_uri, page_path, normalized_file_path, diff_summary, citation_count, before_file_version, after_file_version, payload_json, created_at, deleted_at ) ``` ```sql ai_file_patches( id, user_id, workspace_id, session_id, run_id, tool_event_id, root_uri, relative_path, before_file_version, after_file_version, patch_summary_json, created_at ) ``` 第二阶段再规范化: - `ai_provider_configs` - `ai_model_catalog` - `ai_model_grants` - `ai_tool_policies` - `ai_skill_registry` - `ai_skill_grants` - `ai_mcp_servers` - `ai_mcp_grants` - `ai_usage_logs` - `ai_model_failover_chains` 说明:不要急着新增 `ai_provider_sessions`。MNote 已有 `ai_runtime_runs / ai_runtime_events`,Pi Lab history 可以先落这里;只有当 provider 原生 thread 与 MNote run/event journal 差异太大时,再加 provider-specific session detail 表。 ## 6. API 合同 ### 6.1 管理 API ```text GET /api/ai-admin/overview GET /api/ai-admin/providers PUT /api/ai-admin/providers/{provider_id} GET /api/ai-admin/models PUT /api/ai-admin/models/{provider_id}/{model_id}/policy GET /api/ai-admin/access-scopes GET /api/ai-admin/tools PUT /api/ai-admin/tools/{tool_name}/policy GET /api/ai-admin/skills PUT /api/ai-admin/skills/{skill_id}/policy GET /api/ai-admin/mcp PUT /api/ai-admin/mcp/{server_id}/policy GET /api/ai-admin/knowledge GET /api/ai-admin/sessions GET /api/ai-admin/receipts GET /api/ai-admin/usage ``` ### 6.2 用户有效配置 API ```text GET /api/ai-settings/effective GET /api/ai-settings/models PUT /api/ai-settings/models/{provider_id}/{model_id} GET /api/ai-settings/tools PUT /api/ai-settings/tools/{tool_name} GET /api/ai-settings/skills PUT /api/ai-settings/skills/{skill_id} GET /api/ai-settings/mcp GET /api/ai-settings/access-scopes GET /api/ai-settings/usage ``` `GET /api/ai-admin/access-scopes` 与 `GET /api/ai-settings/access-scopes` 只读返回由 `directory_grants` 计算的 effective scope,并返回现有授权管理页入口。授权写入继续使用现有 access-policy / `directory_grants` command/API;不在 AI admin route 中定义第二套 grants 写语义。 ### 6.3 Pi Lab history API 可以做成 Pi-specific facade,但底层写 `ai_runtime_runs/events`: ```text GET /api/page-ai/pi/sessions GET /api/page-ai/pi/sessions/{session_id} PUT /api/page-ai/pi/sessions/{session_id}/title DELETE /api/page-ai/pi/sessions/{session_id} GET /api/page-ai/pi/sessions/{session_id}/events ``` 响应参考 Pi `SessionMetadata`: ```json { "id": "pi_lab_...", "title": "当前页解释", "model": "omniroute/freefirst", "thinkingLevel": "medium", "lastModified": "2026-07-04T14:20:00Z", "messageCount": 8, "usage": {"input": 1200, "output": 400, "totalTokens": 1600}, "preview": "帮我解释当前页面的逻辑..." } ``` ## 7. 实施顺序 ### P0:合同与只读聚合 - 新增设计和 smoke baseline。 - 增加 `/api/ai-settings/effective` 只读 API。 - 聚合已有 `directory_grants`、`ai_policies`、agent descriptors、Pi defaults。 - 明确 `effective.allowedRoots` 只从 `directory_grants` 派生;若 `ai_policies.allowed_roots_json` 存在,只作为兼容只读快照或迁移源,不参与新增写入。 - Pi Lab model selector 改为消费 effective models,但仍保留 `omniroute/freefirst` fallback。 验收: - OpenHub 默认入口不变。 - Pi Lab 打开后能显示来自 MNote effective config 的模型、allowed roots、tool policy。 ### P1:Pi Lab history 入库 - Pi Lab start/send/end 写 `ai_runtime_runs / ai_runtime_events`。 - 历史 drawer 读 MNote DB,不读 IndexedDB、不读内存 HashMap。 - 现有 JSONL receipt 保留为 debug fallback。 验收: - 重启 mnote-web 后 Pi Lab 历史仍可见。 - 同一 user/workspace 隔离生效。 ### P2:Receipt 与 patch 入库 - 新增 `ai_tool_events / ai_file_patches`。 - `mnote.local_file.read/patch`、LightRAG query、reference.open、tool_receipt.write 全部写入。 - Pi Lab receipt rail 改读 DB event stream。 验收: - allowed roots deny、diff summary、file version、citation count 可在管理面板查询。 - patch 后 watcher refresh 仍不新增轮询。 ### P3:MNote AI 管理中心首版 - `/admin/ai` 先做四个可用 tab:Overview、Providers & Models、Access Scopes、Sessions & Receipts。 - `/user/ai` 做 Models、Access、History、Usage。 - 复用 `/admin/access-policy` / `/user/access-policy` 逻辑,不拆第二套授权事实;AI 面板里的 Access Scopes 本质上是该授权页面的 AI 视角入口。 验收: - 管理员可设置 Pi Lab 默认模型和可见模型。 - 用户只能选择管理员允许的模型。 - 文件夹授权仍同步 OpenHub,不破坏 OpenHub。 ### P4:Skills / MCP / Tools / LightRAG 管理 - Skills registry 与 grants。 - MCP registry 与 sandbox policy。 - MNote-owned tools allow/ask/deny。 - LightRAG source/index/status 管理。 验收: - Pi Lab settings 能显示 skills/MCP/tools effective 状态。 - Pi 仍不能直接启用 raw bash/read/write/edit。 - LightRAG 仍是唯一默认 knowledge provider。 ## 8. 关键决策 1. **MNote 管理中心是必需的**,但第一步是 control-plane + effective API,不是先做完整 UI。 2. **OpenHub admin 只作为参考和下游同步对象**,不作为 MNote AI 真相层。 3. **文件夹授权只保留一套**:`directory_grants` 同时服务 MNote 工作区访问、OpenHub `allowedRoots` 和 Pi Lab `AiAccessScope.allowed_roots`;AI 管理页只提供同源视图和现有授权入口。 4. **Pi IndexedDB schema 可参考,不能作为 MNote 持久化方案**。 5. **Pi Lab history 先复用 `ai_runtime_runs/events`**,避免新增重复 session 表。 6. **receipt/patch 必须入 Turso/libSQL**,JSONL 只保留 debug/crash recovery。 7. **skills/MCP/tools 先管权限,再接 UI**;否则会把未受控工具暴露给 Pi。 8. **secrets 不进前端、不硬编码**;provider API key 应只存 secret reference/env key,后续再接正式 secret store。 ## 9. 风险 - 直接把 OpenHub admin 嵌进 MNote 会形成第二个 iframe 管理系统,和 Pi Lab 原生化目标冲突。 - 先做 Pi UI settings 会把配置写进临时 DOM/内存,后续仍要重接 Turso。 - 如果不先统一 history,Pi Lab 与 OpenHub 会话会分裂,用户无法判断哪个入口产生了修改。 - MCP 管理若过早放开,容易绕过 AiAccessScope;必须默认只注册、只显示,不默认启用。 - Model/provider secret 管理不能复用 Codex auth 作为产品实现;Codex auth 只可作为 dev smoke 输入。 ## 10. 下一步最小可执行设计稿 建议下一张执行稿拆成: `7-72-page-ai-pi-lab-history-and-effective-config-control-plane-v1` 范围只做: - `/api/ai-settings/effective` - Pi Lab model options 读取 effective config - Pi Lab session 写入 `ai_runtime_runs/events` - Pi Lab history drawer 读 DB - receipt 仍保留 JSONL,但设计 migration 到 `ai_tool_events` - smoke 覆盖重启后历史存在、用户隔离、OpenHub 默认入口不变 做完 7-72 后,再做 `/admin/ai` 首版 UI。这样不会在 UI 上先行制造第二套事实源。 --- ## 11. 实施进度(2026-07-04) ### P0: 合同与只读聚合 — 进度 | 项 | 状态 | 证据 | |---|---|---| | 新增设计和 smoke baseline | ✅ 已完成 | `design/07-ai/process/7-71-*` 设计文档、`scripts/task-ai-management-control-plane-static-smoke.js` 静态 smoke(27/27 通过) | | `/api/ai-settings/effective` 只读 API | ✅ 已实现 | `routes/ai_settings.rs::effective_settings` — GET-only,读 `directory_grants` 和 `ai_policies.model_policy_json/quota_json` | | `/api/ai-settings/access-scopes` 用户只读 API | ✅ 已实现 | `routes/ai_settings.rs::user_access_scopes` — GET-only | | `/api/ai-admin/access-scopes` 管理员只读 API | ✅ 已实现 | `routes/ai_settings.rs::admin_access_scopes` — GET-only,管理员鉴权 | | 聚合 `directory_grants`、`ai_policies`、agent descriptors、Pi defaults | ✅ 已实现 | effective 响应聚合 providers、models、tool_catalog、lightrag_provider、access_policy_links | | `effective.allowedRoots` 只从 `directory_grants` 派生 | ✅ 已验证 | `load_active_directory_grants` 读取 `list_directory_grants_for_actor`;`ai_policies.allowed_roots_json` 不被读取 | | Pi Lab model selector 消费 effective models | ✅ 已实现 | `effective_settings` 响应含 models、default_model;Pi Lab 前端消费 | | 静态 smoke 覆盖:路由注册、source of truth、无写操作 | ✅ 27/27 通过 | `scripts/task-ai-management-control-plane-static-smoke.js` | ### P1: Pi Lab history 入库 — 进度 | 项 | 状态 | 证据 | |---|---|---| | Pi Lab start/send/end 写 `ai_runtime_runs / ai_runtime_events` | ✅ 已实现 | `page_ai_pi.rs::persist_upsert_run` / `persist_append_event` | | 历史 drawer 读 MNote DB | ✅ 已实现 | `list_sessions` 读 `control_plane.list_ai_runtime_runs`;`get_session_history` / `get_session_events` 分别读 run 和 events | | 现有 JSONL receipt 保留为 debug fallback | ✅ 已保留 | `PI_LAB_RECEIPT_STORE` JSONL 静态存储仍在 | | Pi Lab session 路由注册 | ✅ 已验证 | `GET /api/page-ai/pi/sessions`、`GET .../{session_id}`、`GET .../{session_id}/events` | | Pi Lab session 按 pi_lab/pi profile/acp_runtime 过滤 | ✅ 已验证 | `list_sessions` 过滤 `r.profile == PI_LAB_PROFILE && r.acp_runtime == PI_LAB_ACP_RUNTIME` | ### P2: Receipt 与 patch 入库 — 已完成 | 项 | 状态 | 证据 | |---|---|---| | `ai_tool_events / ai_file_patches` migration | ✅ | `control-plane/migrations/009-ai-tool-events-file-patches.sql` | | SQLite 与 Turso/libSQL store | ✅ | `append/list_ai_tool_event`、`append/list_ai_file_patch` | | Pi tool receipt 入 control-plane | ✅ | `page_ai_pi.rs::write_receipt` | | patch 关联写入 file patch journal | ✅ | receipt id 作为 `tool_event_id` | | JSONL 降级为 debug fallback | ✅ | 仅 control-plane 写失败后追加 | | 管理 API 查询 receipts / patches | ✅ | `/api/ai-settings/receipts`、`/api/ai-admin/receipts` | ### P3: MNote AI 管理中心首版 — 管理员写管理完成 | 项 | 状态 | 证据 | |---|---|---| | `/admin/ai` 路由与入口 | ✅ 已实现 | `gateway.rs::admin_ai_entry`,鉴权后跳转,管理员模式 | | `/user/ai` 路由与入口 | ✅ 已实现 | `gateway.rs::user_ai_entry`,鉴权后跳转,用户模式 | | 账户菜单独立 AI 管理入口 | ✅ 已实现 | `sidebar-workspace-runtime.js` `mnote-account-ai-management`,按角色跳 `/admin/ai` 或 `/user/ai` | | AI 管理页面 SSR | ✅ 已实现 | `ssr/pages/ai_admin.rs::AiManagementPage` — 对齐 OpenHub Admin 左侧分页、白色 Card 壳、表格、抽屉与分组权限面板;管理 Overview、模型、服务、用量、工具、Skills/MCP、目录、知识库、会话与监控 | | Access Scopes 只读展示 | ✅ 已验证 | 页面脚本只 GET access-scopes 数据,无 POST/PUT/DELETE,无写表单 | | Sessions & Receipts 区域 | ✅ 已接真实数据 | 展示 Pi session、tool receipt、deny reason、diff summary、patch 数量 | | `/admin/ai` 管理写操作(provider/model 编辑等) | ✅ 已实现 | `GET/PUT /api/ai-admin/settings`;模型配置浏览器保存与刷新持久化通过 | | `/user/ai` 用户设置 | ❌ 未实现 | 当前 `/user/ai` 复用 `AiManagementPage` 用户模式,仅有只读视图;逐用户模型/工具/Skill/MCP override 已在管理员用户抽屉完成 | ### P4: Skills / MCP / Tools / LightRAG 管理 — 部分完成 - Skills、MCP 与工具策略已通过原子管理 API 和 SSR 管理 UI 写入 `ai_policies.model_policy_json` - MCP 强制 `facadeOnly=true`、`sandbox=true`,secret 仅接受 `env://` / `secret://` - MNote-owned tools 已支持 allow/ask/deny - LightRAG source/index/status 管理 UI 未实现 - provider/skill/MCP 专用规范化表未实现;出现独立查询、授权或生命周期需求时再迁移 ### OpenHub / Pi Lab 独立入口状态 | 项 | 状态 | |---|---| | OpenHub `/page-ai/openhub/ai` 未删除 | ✅ | | OpenHub `/page-ai/openhub/admin` 未删除 | ✅ | | OpenHub status API 未删除 | ✅ | | Pi Lab shell `/page-ai/pi` 未删除 | ✅ | | Pi Lab start/send/abort/events 路由未删除 | ✅ | ### 验证命令占位 ```bash # 静态 smoke(只读代码审计) node scripts/task-ai-management-control-plane-static-smoke.js # 运行时 smoke(需要 mnote-web 运行中) # curl -s http://localhost:3000/api/ai-settings/effective | jq '.sourceOfTruth' # curl -s http://localhost:3000/api/ai-settings/access-scopes | jq '.sourceOfTruth' # curl -s http://localhost:3000/api/ai-admin/access-scopes | jq '.sourceOfTruth' # curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/admin/ai # curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/user/ai # curl -s http://localhost:3000/api/page-ai/pi/sessions | jq '.schema' # curl -s http://localhost:3000/api/ai-settings/receipts | jq '{receiptCount, patchCount}' ``` ### 运行时与浏览器证据 - 隔离运行端口:`127.0.0.1:33171` - `task-pi-lab-mock-api-smoke.js`:通过 - `task-pi-lab-browser-smoke.js`:通过 - Pi Lab 截图:`tmp/7-71-pi-lab-browser-final.png` - AI 管理页截图:`tmp/7-71-ai-admin-final.png` - 模型写管理截图:`tmp/7-71-ai-admin-openhub-like-ui-final/models.png` - Skills/MCP 写管理截图:`tmp/7-71-ai-admin-openhub-like-ui-final/skills-mcp.png` - OpenHub-like 逐用户配置抽屉截图:`tmp/7-71-ai-admin-openhub-like-ui-final/users.png` - 管理页实测摘要:会话 1、回执 3、文件补丁 1 - OpenHub 仍为默认 Page AI,Pi Lab 继续使用独立按钮、独立抽屉与独立 session - `mnote-e2e` 已通过 control-plane `role=admin` 验证管理员访问和写入;浏览器 smoke 使用 dev seed 仅为临时验收账号准备,不写第二套授权目录 ### P4: Skills / MCP / Tools 管理 — 验收结果 | 项 | 状态 | 证据 | |---|---|---| | admin 原子策略 GET/PUT | ✅ 通过 | `/api/ai-admin/settings`,统一保存模型、工具、Skills、MCP | | 浏览器保存与刷新持久化 | ✅ 通过 | `scripts/task-ai-management-browser-smoke.js` | | secretRef-only 检查 | ✅ 通过 (3 项) | Section 9: 无硬编码 api_key/secret,仅 env 引用 | | allowed roots 不可写检查 | ✅ 通过 (4 项) | Section 10: 无写路由、无写函数、无写表单 | | MCP facade-only 检查 | ✅ 通过 (3 项) | Section 11: Pi 仅暴露 mnote. 工具,MCP 经 facade 管控 | ### P5: OpenHub 式逐用户 AI 配置 — 已完成 - OpenHub Admin 的真实用户管理入口为用户表格 + 模型/工具/Skill/目录权限操作;MCP 仅展示用户工作区 `.opencode/mcp` 文件,不存在 Admin per-user MCP 策略。 - MNote 新增用户列表与逐用户 AI 策略 API,用户 override 继续落 `ai_policies(user_id, workspace_id=NULL)`。 - 当前 `ai_policies` 没有系统全局行,因此暂以 `MNOTE_ADMIN_USER_IDS` 首个用户作为全局策略 owner;迁移到规范化 policy 表时移除此兼容约定。 - 本轮已把 MNote `/admin/ai` 用户配置抽屉改成 OpenHub-like 体验:模型按 provider 分组、工具按 risk level 分组、Skill/MCP 使用启停开关、目录权限只读并跳转统一授权管理页。 - effective 合并规则: - 模型只能从管理员 allowed models 中选择; - 工具只能降权,不能从 deny/ask 提升到 allow; - Skill/MCP 只能关闭或启用全局已允许项; - MCP 始终保持 facade-only + sandbox; - allowed roots 完全不参与该合并,继续只读 `directory_grants`。 - 浏览器截图:`tmp/7-71-ai-admin-openhub-like-ui-final/users.png`。 - 浏览器 smoke 同时以普通用户 `ai-user` 登录,确认 effective API 不返回被禁用模型和 Skill。 ### 本轮 smoke 结果 静态 smoke:**47 通过,0 失败**。浏览器 smoke 验证模型、工具、Skills/MCP 保存、OpenHub Admin 独立入口保留、OpenHub-like 用户抽屉、逐用户 override、刷新持久化及 effective 投影全部通过。 ``` $ node scripts/task-ai-management-control-plane-static-smoke.js 通过: 47 失败: 0 跳过: 0 $ MNOTE_AI_ADMIN_OUTPUT_DIR=tmp/7-71-ai-admin-openhub-like-ui-final \ node scripts/task-ai-management-browser-smoke.js ok: true ``` ### 后续范围 - `/user/ai` 用户模型/Skills/MCP 偏好 - LightRAG source/index/status 写管理 - provider/skill/MCP 专用规范化表迁移