Files
dsh_shenxian/dsh-server-docs/04-调整方案/16-页面导航定稿-技能插件管理面三决策落地.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
   保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
   工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
   必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
   + ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
   ⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
   验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

12 KiB
Raw Blame History

16 · 页面导航定稿 + 插件三层归属模型(方案)

  • 日期:2026-09-10
  • 触发:多轮对齐后,用户对"技能/插件管理面"拍板,并给出插件三层归属模型
  • 结论一句话:技能/插件各独立入口 + 独立页面(skills.html 技能 + 新建 plugins.html 插件,仅 admin 管公共面)+「我的技能」走 dsh 设置页 section + 功能插件按「候选池投放→按需启用→重启生效」三层归属 + folder_plugins 废弃。
  • 状态:已封板,实施中 —— 阶段 0(bad6ed7)、阶段 1(6f86a5f)、阶段 2(b7fd85d)、页面 UI 优化(b97c9f8)、门户 SPA 化(9df64ac)已完成

TL;DR|结论:技能/插件管理面定稿:技能与插件各独立页面(skills.html / plugins.html,公共面仅 admin)+「我的技能」走实例设置页 section。 关键:功能插件三层归属:候选池投放 → 按需启用 → 重启生效;folder_plugins 废弃。 状态:✅ 已封板(阶段 0-2 已实施)


一、需求背景

档案 13「登录直达」覆盖了档案 11 建在 portal 页上的入口 → 登录成功后用户只在实例 UI 里,desktop.html / admin.html / skills.html 三个管理页在成功路径上根本不会被加载,导致用户(含 admin)看不到技能/插件入口。经多轮对齐,入口形态与插件归属最终定稿如下。

二、决策定稿

2.1 技能面(已拍板)

# 决策点 选定 说明
1 新页形态 技能/插件各独立入口 + 独立页面:skills.html(技能)+ 新建 plugins.html(插件) 「我的技能」不进门户页
2 共享技能启用 投放即生效 admin 投放共享技能 → 目标用户只读可见,不造开关(技能与插件启用语义不同)
3 「我的技能」入口 dsh 设置页自建 section 所有用户(含 admin)在实例设置里上传/管理自己的 skill,不跳页面、不回门户

2.2 插件面(三点归属模型 + 4 条补充,2026-09-10 用户提出)

三层归属:

  1. 现有 settings-plugins 管 dsh 自带插件(cordis 148 个),仅 admin 可访问(已收归)。
  2. 门户 plugins.html(独立页面) 管 系统外插件 = 功能插件,admin 投放,所有用户可用。
  3. 实例设置新增「功能插件」section,admin + 用户手动启用/禁用功能插件;启用时复制插件到该用户对应目录。

补充决策(用户答复):

  • 投放 = 只进候选池、默认禁用;用户启用后需重启该用户 dsh 实例才生效。
  • 支持批量设置启用/禁用,操作需点击确认弹窗,点弹窗确认按钮才执行重启实例。
  • 上传权限边界:上传插件 = 仅 admin(门户);普通用户只能上传 skill 自用,不能上传插件(用户侧「功能插件」section 只有启用/禁用,无上传入口)。
  • folder_plugins 历史作用见 §四。
  • 确认最佳方案后再实现(先方案、后代码)。

三、插件三层归属模型(定稿)

层 入口 对象 受众 动作
自带插件 dsh 设置 →「插件」(现有) dsh 自带 cordis 148 仅 admin 开关(已收归)
功能插件·投放 门户 plugins.html(独立页面) 系统外插件(业务 bundle) 仅 admin 上传 → 进候选池(默认禁用)
功能插件·启停 dsh 设置 →「功能插件」(新增) 候选池内的功能插件 admin + 用户 批量启用/禁用 → 确认弹窗 → 重启实例

流程:admin 投放(候选池,默认禁用)→ 用户在实例设置勾选启用 → 确认弹窗 → 复制插件到该用户 profile 目录 + 写 cordis patch → 重启该用户实例 → 生效。

四、folder_plugins 去留(已查明,建议废弃)

历史作用(源码实锤):

  • 档案 05 时期为"按工作目录启停用户自装插件"设计的预留机制。
  • 实现:folder_plugins 表按 workspace_id 存 plugin_id + enabled;POST /api/dsh/launch 时 renderPatch(enabled) 生成 cordis - insert: patch(dshs-runtime + 勾选插件),经 --patch 传给子 dsh。
  • 总开关 enablePatch 默认 false,线上未设 DSHS_ENABLE_PATCH(进程 env 已核实)→ 从未生效。真正让 portal-entry 生效的是 profile bundles(dsh plugin add),与 folder_plugins 无关。
  • 且"按工作目录"粒度与"每用户单实例"(ISOLATION=account)架构不匹配。

结论:已确认废弃 folder_plugins(含 folder_plugins 表、/api/plugins + /api/plugins/select 路由、desktop.html 插件面板),新模型直接走"复制 bundle 到 profile + 写 cordis patch + 重启"。

五、最终页面/入口地图(2026-09-10 SPA 化后)

门户管理面已合并为 portal.html SPA(hash 路由 + 顶栏面包屑,参考 mcn-work-shop 机制);desktop.html / skills.html / plugins.html 均重定向到 portal.html 对应路由。

侧 入口 受众 能力
门户 portal.html #/files(文件 + DSH 启动) 仅 admin 文件树浏览 / 上传 / 新建 / 在此启动 DSH
门户 portal.html #/keys(密钥管理) 仅 admin 全局 API 密钥增删 / 启用
门户 portal.html #/users(用户管理) 仅 admin 审批/禁用/删除
门户 portal.html #/skills(技能管理) 仅 admin 共享技能上传/替换/删除
门户 portal.html #/plugins(插件管理) 仅 admin 功能插件上传/替换/删除(候选池投放)
实例 dsh「设置」 「功能插件」section(新增) admin + 用户 批量启用/禁用候选池功能插件(无上传,确认弹窗 + 重启)
实例 dsh「设置」 「我的技能」section(新增) 所有用户 上传/管理自己的 skill(/api/skills/mine)
login.html — — 清理错位"技能管理"链接

六、分阶段实施计划

阶段 目标 主要改动 红线 / 验证
0 零风险清理 ✅ 消死链 + 废弃 folder_plugins 删 login.html 错位链接;废弃 folder_plugins(删 /api/plugins* 路由 + desktop.html 插件面板;admin.html role 守卫实测已存在无需改;表定义/renderPatch 死代码保留) 已验证:login/desktop 200、/api/plugins* 404、无残留;commit bad6ed7
1 技能/插件独立页面 ✅ admin 门户公共面 skills.html(共享技能 /api/skills/shared)+ 新建 plugins.html(功能插件 /api/plugins/business);移除「我的技能」+ 加 role 守卫(仅 admin);上传接入安全检测 + 替换策略;后续按 06 规范 Token + impeccable 做了 UI 优化、去退出登录 已验证:build 通过、端到端全通、表已建;commit 6f86a5f + b97c9f8
2 功能插件启停 section ✅ 实例侧启停 自建 client bundle @dsh-local/business-plugins(poc/business-plugins/)在 dsh 设置页注册「功能插件」section:候选池列表 + 批量勾选 + 确认弹窗(提示重启中断会话)+ 调 /api/plugins/mine/apply 启用/禁用 + 重启实例;门户加 CORS(仅 baseDomain 子域);启用=复制 tgz+手动解压到 profile node_modules+加 bundles(绕开 npm file: arborist bug) R2/R3;已验证:mine 链路全通、CORS 子域 204/非法拒绝、bundle host 面加载(marker);commit b7fd85d
3 「我的技能」section 实例侧个人技能 同自建 client 插件再注册「我的技能」section(或同一 bundle 两个 settings.section);上传表单调 /api/skills/mine;dsh-client-ui-skill 只读无上传,需自带表单 R3;测试走 R4
4 权限收口复核 对齐设计意图 确认新 section 开放范围、不误伤 guest 已禁 ui-settings-* 按 uid --dump-config 复核

七、实现细节(设计点定稿 + 技术要点)

7.1 功能插件设计点定稿(2026-09-10 用户拍板)

# 设计点 定稿
1 插件包格式 上传 tgz 文件(与 dsh plugin add 产物一致)
2 候选池存储 新建 business_plugins 表(元数据)+ tgz 存磁盘 .../plugins/<id>/
3 同名上传 替换策略(非覆盖):先删旧包再放新包,目标内容与新包完全一致、无旧残留;单版本,记 version
4 启用机制 编排器自实现解压 tgz + 写 profile dsh.profile.bundles(不调 dsh CLI)
5 禁用机制 保留包 + cordis patch disabled: true(不删 node_modules,便于快速重新启用) ⚠️ 更正(2026-09-12,档案 64 §9.2 实测):实现在 src/web/routes/business-plugins.ts L399-402 / L410-413 —— 启用 = pnpm add file:<tgz>,禁用 = pnpm remove <id>(真卸载,不是留包);因此任何「硬绑定 provider」的覆写段会在用户禁用后指向未注册的 provider(WEB_PROVIDER_CONFIGURED_MISSING,且 dsh 不回落)→ 已由档案 65 的「平台在 install/uninstall 后按当前 bundles 重算托管段」解决
6 上传安全检测 技能与插件上传都做:结构校验(技能=SKILL.md 必填;插件=package.json + dsh bundle 结构)+ 危险内容扫描(命令注入 / 网络外联 / 越权文件读写等)
7 投放范围 全员可见(先不做定向分组)

替换策略 vs 覆盖策略(差别关键):覆盖 = 同名文件直接覆盖,旧包里已删除的文件会残留;替换 = 先删旧内容再放新内容,保证目标目录内容与新包完全一致。与技能管理 v2(档案 11)「apply 全量替换(旧目录整删、缺失文件一并清除)」同语义。

7.2 技术要点

  1. 功能插件仓库(仅 admin 上传):门户新增上传 + 候选池(DB 表 + 磁盘 tgz);普通用户无上传入口。上传先过安全检测才进候选池。
  2. 技能上传:现有结构校验(档案 11 v2)之上,新增危险内容扫描。
  3. 分发(启用):复制 bundle 到 <root>/home/profiles/web/node_modules/<pkg> + 写进 dsh.profile.bundles。
  4. 生效:改 profile 后走 POST /api/dsh/restart 重启该用户实例(按 uid 选 pid,勿 pgrep -f '--profile')。
  5. 确认弹窗:批量勾选后弹确认(含「重启会中断当前会话」提示),点确认才执行复制 + 重启。
  6. 安全:功能插件 = 任意 node 代码,admin 投放即「给全员装代码」→ admin 可信为前提 + 上传安全检测 + 来源审计。
  7. 技能 vs 插件语义区分:技能「投放即生效」(只读共享层),插件「候选池默认禁用 + 按需启用」,勿混。

八、红线遵守

  • R1:本方案不触发 dsh 升级。
  • R2:改动限于门户静态页/编排器自建代码 + profile 层官方插件机制(dsh plugin add/bundles + cordis patch),不改 dsh 主程序与缓存。
  • R3:阶段 2/3 client 插件严格镜像官方 bundle(只导出 apply+inject,无 default)。
  • R4:验证用注册→admin approve→DELETE 模板,临时 admin session 标 poc-curl 用完即删,禁用真实账号做 API 登录测试。
  • UI:所有页面改动先读 06-工作台UI规范.md,冲突以其实测 Token 为准。

【保留说明(2026-09-11,档案 19 §C5 便宜版)】 folder_plugins 表结构与 repo 函数保留,但已无任何业务/路由调用(grep 实证:仅 src/db/* 自引用);workspaces 表仅被 enablePatch 分支使用,而 DEFAULT_ENABLE_PATCH=false 且生产 env 未覆盖 → 该分支在本部署不可达。保留原因:k8s/PG 路径未验证(档案 19 §C8),删除风险不对称。请勿往这两张表/该分支加新功能。