Files
dsh_shenxian/dsh-server-docs/04-调整方案/101-能力管理-改名与tab分页与卡片三行.md
T
admin 3efd68517f chore: 并入已删除会话的在途成果(防丢失;原会话已删,未做功能验收)
**背景**:这些改动原属本工作区另外几个会话(T01/T02 等),**那些会话已被用户删除** ⇒
工作树里的成果处于"无主"状态,一次错误 checkout / 覆盖即**永久丢失** ⇒ 代入库保全。
口径遵循本项目**先例**(`39a1f2e` / `b617cdb`:**别人的活,代入库并在提交信息里注明**)。

**内容**:档案 101「能力管理」改名 + 页内 tab 分页|档案 102 语言切换搬入「用户设置」|
`07-实例UI分区登记表.md`|`scripts/find-ui*.mjs`(UI 元素定位工具)|`poc/portal-entry/`(0.5.3)|
`src/web/locale-pref.ts` + `home-files.ts`(语言偏好持久化)|`test/locale-pref.test.mjs`|
`package.json`|`BRIEF.md` / `INDEX.md` / `docs-manifest.json` / `03-路线图与待办.md` / 档案 100 增量。

**已做最小健全性检查**(⚠️ **未跑完整构建 / 单测** —— 那是原会话的验收职责,本次只求"不丢"):
- JSON 合法:`package.json` / `poc/business-plugins/package.json` / `docs-manifest.json` ✓
- 4 个 TS 文件 `{}`/`()` 配平 ✓;新增文件均非空 ✓
- 规模:13 文件改动 +340/−210,新增 12 条

**未 push**(按 §4 提交边界:用户说"提交",未说"推送")。
2026-09-15 21:17:12 +08:00

11 KiB
Raw Blame History

101 · 「能力管理」:分区改名 + 页内 tab 分页 + 插件卡片三行描述 + DeepSeek 改名

  • 日期:2026-09-15
  • 触发:用户三句话 —— ①「设置中 功能管理改为能力管理」②「能力管理页面改造为 tab可切换 插件和技能分别进行操作」③「插件卡片中增加中文用途描述 显示三行」④「内置 DeepSeek,去掉内置就叫 DeepSeek 就行」
  • 对象:poc/business-plugins(@dsh-local/business-plugins)client bundle 的「功能管理」section
  • 状态:✅ 已实施并投放(0.3.21 → 0.3.22)
  • 前置:档案 100(「我的技能」并入本 section)+ 交接单/archive/T01

TL;DR|结论:分区改名「能力管理」(label 级),页内改为 tab 分页(功能插件 / 我的技能,各自独立操作),插件卡片的中文用途描述从 1 行放宽到 3 行(.bp-plugDesc),并把「内置 DeepSeek → DeepSeek」。 关键:全部落在用户可见文案 / 版式这一层 —— 代码标识符、section id、包名、API 路径、词典键名一律不动(素材库 U10)。 不做:不改服务端;不动「我的技能」的机制(仍是 watch 即时生效 / 两阶段替换 / 锁定行无可点动作)。


一、四项改动

1.1 分区改名:功能管理 → 能力管理

位置 旧 新
section.label(zh) 功能管理 能力管理
section.label(en) Feature management Capability management

⛔ 只改 label:id: "business-plugins"、包名 @dsh-local/business-plugins、/api/plugins/*、词典键名、CSS 类名一律不动 —— 与档案 60(功能插件→功能管理)同一口径。

1.2 页内 tab 分页(插件 / 技能各自操作)

❙ 能力管理
┌ 功能插件 3 ┐┌ 我的技能 2 ┐          ← 下划线式 tab(复用 .pa-tabs/.pa-tab/.pa-tcnt),各带计数
┬────────────────────────────────
│ 【功能插件页】内存条 / 搜索·全选·清空 / 卡片网格 / 应用更改      ← 内容与改造前逐项一致
│ 【我的技能页】说明 + [+ 上传技能] / 上传面板 / 技能行列表
  • 实现:isPlugins = tab === "plugins"(useState("plugins"),默认落插件页 = 与改造前一致);插件块 if (isPlugins)、技能块 if (!isPlugins),四段门控成对。
  • 复用而非新造(U2):tab 直接用既有 .pa-tabs/.pa-tab/.pa-tcnt(下划线式;规范 §4.4「数据页用下划线式」),没有新增样式。
  • 计数:插件页签 = 候选池条目数,技能页签 = 列表条目数(含共享)—— 不点进去也知道各类有多少。
  • 技能页去掉重复标题:❙ 我的技能 竖条标题删掉(标题已由 tab 承担),只留「生效方式说明(左)+ 上传入口(右)」工具行(.bp-tabHead);避免同页出现两个同名标签。
  • 可访问性:role="tablist" / role="tab" / aria-selected。

1.3 插件卡片:中文用途描述 = 3 行

  • 新增 .bp-plugDesc:display:-webkit-box + -webkit-line-clamp:3 + -webkit-box-orient:vertical + overflow:hidden + 显式 white-space:normal。
  • ⚠️ white-space:normal 必须显式写 —— 旧版单行截断用 nowrap,不覆盖会让多行截断失效(一眼看不出来的静默回退)。
  • title 仍挂全文,hover 可看完整。
  • 为什么:单行 ellipsis 会把「这个插件到底干什么」截掉大半;候选池入库时已优先取官方目录的中文说明,值得多给两行。

1.4 「内置 DeepSeek」→「DeepSeek」

词典键 旧(zh) 新(zh)
ms.kindBuiltin 内置 DeepSeek DeepSeek
ms.optBuiltin 内置 DeepSeek(平台共享) DeepSeek
ms.builtinPickHint 内置 DeepSeek:平台内置通道,固定官方端点,无需填 API 地址。 DeepSeek:官方通道,固定官方端点,无需填 API 地址。
ms.tagBuiltin 内置 官方
ms.builtinHint 平台内置通道,无需端点 官方通道,无需端点
ms.catUnreadable / ms.catEmpty …当前只能使用内置 DeepSeek 或自定义提供方… …当前只能使用 DeepSeek 或自定义提供方…

en 侧同批(Built-in DeepSeek → DeepSeek;Built-in → Official)。

顺带修掉的误导:原选项名写的「(平台共享)」是错的 —— 该选项走的是内置通道(固定官方端点),但必须填用户自己的 API 密钥(keyAddSchema 里 apiKey 是 required + minLength:1;输入框 placeholder 也写着「输入你的 DeepSeek API 密钥」)。叫「平台共享」会让人以为不用填 key —— 这正是用户「添加模型也不能添加内置模型 没有对应KEY」困惑的来源。


二、与「平台共享模型」的关系(本次一并澄清,重要,别再混)

名称 是什么 谁的 要不要自己的 key 能删吗
平台共享模型 deepseek-main(页内卡片) admin 名下已启用的 key 条目(sharedKeyInfo().name = admin 启用列表里的第一条名) 平台 / admin ❌ 不要(用户不配 key 时回落到它) ❌ 只能开关(卡片上只有一个「停用/启用共享模型」,走 /api/me/models/shared)
DeepSeek(「新增提供方」下拉里的选项) 内置通道(固定官方端点)给用户自己加条目用的 provider 类型 用户自己 ✅ 要(否则 400) 用户可删自己的条目

⇒ 不是一回事:「平台共享模型 deepseek-main」是平台那条 key;「DeepSeek」是你自己新建条目时选的通道。另:官方 pi-ai 目录里的 deepseek 被后端显式排除(src/web/routes/auth.ts 注释原文:「平台已有『内置 DeepSeek』入口…再列一个同名选项只会让用户分不清哪个生效」)—— 所以下拉里不会出现第二个 DeepSeek。


三、实现落点

文件 改动
poc/business-plugins/lib/client.js ① zh/en:section.label 改名 + cap.tabPlugins 新词条 + ms.* 6 条措辞(内置→DeepSeek);② section 内新增 tab state + tab 条渲染 + 四段门控;③ 技能页工具行 .bp-tabHead(去重复标题);④ 插件卡片描述改 .bp-plugDesc;⑤ 注入 CSS:.bp-tabHead / .bp-plugDesc
poc/business-plugins/package.json 版本 0.3.21 → 0.3.22(含 ①②③④ 四项)+ description 追加本轮条目
scripts/verify-my-skills.mjs 新增 20 条断言:改名生效 / 旧名不再是 label / tab 条与两个页签 / 计数 / aria-selected / 门控成对(isPlugins 2 处 + !isPlugins 2 处)/ .bp-plugDesc 三件套 / title 仍在
scripts/verify-platform-admin-section.mjs 跟进改名:断言「条目名显示为 DeepSeek、不再有『内置 DeepSeek』」+ 3 处测试名/注释里的旧分区名

零服务端改动;未动官方包。


四、验收

# 口径 结果
A npm run verify ✅ 全绿(zh/en 各 367 键、引用键全部已声明;含新增 20 条断言)
B 产物 ✅ business-plugins-0.3.22.tgz 与服务器 sha256 一致(a5a55b41…)
C 投放 见 §五
D 06 §7 三段式 + 浏览器 见 §五

五、实施与投放记录

5.1 投放

产物:dsh-local-business-plugins-0.3.22.tgz  72,882 B  sha256 a5a55b41…(与服务器一致)
ssh bt-server 'cd /opt/dshs && node scripts/ensure-biz-plugins.cjs --all --restart'
  admin: 版本落后(0.3.21 → 0.3.22),升级中… ✓ bundles=7(含 @dsh-local/business-plugins: true)
  guest: 版本落后(0.3.21 → 0.3.22),升级中… ✓ bundles=6(含 @dsh-local/business-plugins: true)
  已停 0 个实例 scope(下次访问自动拉起)

⚠️ 同号覆盖说明:0.3.22 在本次投放前从未安装到任何 profile(两实例都是 0.3.21)⇒ 打包后(含 1.4 的措辞微调)覆盖同一版本号重发是安全的 —— 若已装过 0.3.22,则必须升到 0.3.23 (否则 pnpm 判定"无需更新",静默不生效,档案 18 踩坑 1)。

5.2 磁盘层(06 §7.3 ①)✅

实例 profile 版本 能力管理 cap.tabPlugins bp-plugDesc 旧 section.label: "功能管理"
admin(4092b965…) 0.3.22 3 3 3 0
guest(cce6d1cd…) 0.3.22 3 3 3 0

5.3 本地校验 ✅

npm run verify 全绿:zh/en 各 367 键、引用键全部已声明;verify-my-skills.mjs 新增 20 条 (改名生效 / 旧名不再是任何 label / tab 条与两个页签 / 计数 / aria-selected / 门控成对 (isPlugins 2 处 + !isPlugins 2 处)/ .bp-plugDesc 三件套 / title 仍在);verify-platform-admin-section.mjs 的「条目名 = DeepSeek」断言同步跟新。

5.4 ⚠️ 实例侧 / 浏览器验收:本轮未完成(原因与下一步写清)

阻塞点(客观,非"将就"):平台已切集群模式 ⇒

  1. 新用户按容量落 Worker w-106,不在 47 上 ⇒ 本机脚本「进实例 → 抓壳页 → 取 combo → 请求 bundle」 这条路径拿不到带新包的壳页(实测 comboHasBundle: false,47 上也无该用户目录);
  2. 47 → 106 无 SSH 路由(106 是经反向隧道连到 47 的)⇒ 从 47 侧探不到 106 上的 profile/实例。

已做到的:磁盘层(§5.2)已确认两实例 profile 里就是新代码 —— 而实例壳页的 combo 是对 profile/node_modules/**/client.js 的拼接,所以"服务端会返回新内容"由 §5.2 强推出,但未经端到端实测。

回头解决的条件:一旦拿到「新用户落在哪台 Worker / 该 Worker 的 SSH 或 agent 通道」, 就用档案 100 §8.4 那套(poc-ui-user.cjs + agent-browser)把三段式与视觉验收补上。

5.5 本轮附带的两个实测事实(高复用,已进 PLAYBOOK §9)

  1. 控制面库 = PostgreSQL(集群模式):DSHS_DB_URL=postgres://dshs:…@127.0.0.1:15432/dshs, DSHS_DEPLOY_MODE=cluster。/var/lib/dshs/dshs.db(SQLite)是回滚用的旧库、平台不读 ⇒ 临时用户审批写 SQLite = 白写(实测:register 201,但 login 403 pending_review);且 users 表在 PG 里的主键列是 id(不是 user_id),清理脚本别照抄 SQLite 时代的列名。
  2. 一次性用户的残留:41a9480e-5269-463d-acce-79aeee74ced7(pocuimz6rb)的 PG 行已删, 但其 home 目录留在 106 上(47 无该目录、无路由)⇒ 需在 106 侧 rm -rf 该用户目录才能彻底清干净。