平台共享模型改为「管理员逐用户授权」+ 品牌中文名改「能力网络」+ 修掉跨机模型落地/语言偏好静默失效

三条线合并入库(同一次部署批次,源码与生产此前已一致):

一、档案 138 · 平台共享模型:管理员逐用户授权(默认关闭)
  用户口径原文:「admin 设置的共享模型,需要 admin 在用户列表中开启(新增选项,默认关闭),
  用户才能在会话中使用(以及在设置的模型设置页面展示)」。
  · DB 迁移 v11:新增 users.shared_model_granted(DEFAULT 0 = 默认关闭)。
    ⚠️ 刻意**不**复用 v6 的 shared_model_enabled —— 那是用户侧偏好(用户能自己关,默认 1),
    而本需求要的是**管理员门禁**;共用一列则用户点一下就给自己授权,门禁形同不存在。
    生效 = granted ∧ enabled(server.ts#sharedLandingRows)。
  · 新路由 POST /api/admin/users/:id/models/shared(requireAdmin)+ 审计 shared_model_grant
    + **尽力而为**重启该用户实例(租约被占/实例未运行都不算失败)。
  · admin 用户列表新增「共享模型」列;用户侧 /api/me/keys 增 shared.granted / sharedModelGranted;
    插件 0.3.24:未授权时「平台共享模型」整块不渲染。

二、顺带修掉一个既有真缺陷:平台侧写用户 home 必须走 UserFs(用户卷在 worker 上)
  landModels 原先 join(owner.home_dir, …) + 本机 fs ⇒ 对「实例不在控制面本机」的用户
  读到空串(**不报错**)⇒ 模型落地一直是**静默空操作**(托管清单还被清空)
  ——即档案 87 的模型设置页对 guest 这类用户**从未生效**。
  · UserFs 新增 readHomeFile/writeHomeFile + 文件名白名单(settings.yaml / .credentials.yaml)
  · worker agent 新增 /fs/home-read /fs/home-write(只认白名单裸文件名)
  · landModels 改走 userFs(与"文件面"同一份按归属路由 ⇒ 落地与实例必然同机)
  · 同轮把 /api/me/locale(档案 102 语言偏好)也改成同一套(原先同样失效)
  · home-files.ts 抽出 backupHomeFile(备份留平台侧,命名规则逐字不变)

三、档案 139 · 品牌中文名:能力枢纽 → 能力网络(其他语言仍 CapabilityNet)
  落点四处:i18n 中文词条 / admin.html 顶栏 / favicon.svg 的 title+aria-label / design.css 注释;
  test/i18n-brand.test.mjs 期望值同步。档案 137 顶部加"后续"指针,不改历史。

验证(全部真机实测):
  · 红腿:未授权 → 106 上 guest 的 .credentials.yaml refs 变空(共享 key 被撤)
  · 绿腿:授权 → key 回来 + 托管清单恢复 ["DEEPSEEK_API_KEY"]
  · admin 列表带出 sharedModelGranted;用户侧 granted 随授权翻面(true/shared ↔ false/none)
  · 开关两次均 200(不再假失败);插件实装 0.3.24 且含 gating 字符串
  · 语言偏好:106 上 settings.yaml 出现 locale.preference=en(属主=实例属主,既有段逐字保留)
  · 本机 npm test 226 tests / 225 pass / 0 fail / 1 skipped;四个 verify 脚本全绿

部署:47 推 51 个 lib 产物、106 推 12 个(lib/ 是 gitignore ⇒ 回滚点物化在
/opt/dsh/backups/seq138b-20260919-125345/,逐文件对账 0 不一致;先 106 后 47);
插件 business-plugins 0.3.24(两机 artifacts 与本机 pack md5 一致)。
This commit is contained in:
admin committed 2026-09-19 13:51:01 +08:00
1 parent 971ccc3703
commit c2b7c5ef71
30 files changed
+770 -70

No files matched your search

+6 -1
View File
@@ -2919,7 +2919,12 @@ window.__ModuleLoader__.load({
jsxRuntime.jsx("h1", { className: "pa-hd", children: t("ms.title") }),
jsxRuntime.jsx("p", { className: "pa-sub", children: t("ms.sub") }),
// ── admin 配的「平台共享模型」(口径②:也列在这里、用户可开关)──
// ── admin 配的「平台共享模型」(档案 138:**管理员逐用户开启后才有这一块**)──
// 判据 = `shared.granted`(后端 `users.shared_model_granted`,默认关闭)。
// 未授权 ⇒ 整块不渲染(用户口径:「admin 开启,用户才能使用 + 才在设置页展示」)。
// ⚠️ 与 `sharedOn`(`sharedModelEnabled`,用户自己的偏好)**不是一回事**:
// granted 决定"有没有",sharedOn 决定"你自己要不要用"。
!shared.granted ? null :
jsxRuntime.jsxs("div", { className: "pa-box", style: { marginBottom: 16 }, children: [
jsxRuntime.jsxs("div", { className: "pa-card-h", children: [
jsxRuntime.jsx("span", { children: t("ms.shared") }),
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@dsh-local/business-plugins",
"version": "0.3.23",
"version": "0.3.24",
"description": "能力管理(原「功能管理」/「功能插件」)section for dsh web profile — v0.3.23(2026-09-15):**撤除「偏好设置」分区**(用户:「把偏好设置中的 语言切换功能放到用户设置中,去掉偏好设置这个栏目」)。语言切换搬到 `@dsh-local/portal-entry` 的「用户设置」分区(id `user-settings`,order 103)—— 语言是**用户级偏好**,与「账号 / 退出登录」同区即可,再单开一个分区就是**重复入口**。撤掉:`PreferencesSection` 整段、`ctx.slots.inject` 里 id `preferences` order 99 的注册、zh/en 各 3 条 `pref.*` 词条 ⇒ 本 section 现只剩 **2 个分区**(能力管理 101 + 模型设置 100)。配套:`verify-platform-admin-section.mjs` 分区数断言 3 → 2 并新增「preferences 已撤除」断言;语言切换的功能断言移到新脚本 `scripts/verify-portal-entry.mjs`。|v0.3.22(2026-09-15):**分区改名「能力管理」+ 页内 tab 分页(插件 / 技能)+ 插件卡片中文用途描述三行**。① **改名**(用户:「设置中 功能管理改为能力管理」)—— **只改 label 级**(zh `能力管理` / en `Capability management`),代码标识符 / section id / 包名 / API 路径一律不动(素材库 U10「术语统一只改用户可见那层」);② **页内 tab**(用户:「能力管理页面改造为 tab可切换 插件和技能分别进行操作」)—— 复用既有 `.pa-tabs/.pa-tab/.pa-tcnt`(下划线式;规范 §4.4「数据页用下划线式」),两个页签各带**计数**;**插件页内容原样**(内存条 / 工具行 / 卡片网格 / 应用更改),**技能页**去掉重复的分组标题(标题已由 tab 承担)只留「生效方式说明 + 上传入口」工具行;两页**各自独立操作**,默认落插件页(与改造前一致);③ **插件卡片用途描述 = 3 行**(用户:「插件卡片中增加中文用途描述 显示三行」)—— 新增 `.bp-plugDesc`(`-webkit-line-clamp:3` + 显式 `white-space:normal`,否则继承单行截断的 nowrap),单行 ellipsis 会把「这个插件干什么」截得看不全;`title` 仍挂全文。④ **「内置 DeepSeek」→「DeepSeek」**(用户:「内置 DeepSeek,去掉内置就叫 DeepSeek 就行」)—— 仍只动**用户可见文案**:行标题 `ms.kindBuiltin`、新增下拉选项 `ms.optBuiltin`(原「内置 DeepSeek(平台共享)」,那个「(平台共享)」是**误导**:该选项走内置通道但**要填用户自己的 key**)、选择提示 `ms.builtinPickHint`、目录不可用时的兜底文案(`ms.catUnreadable`/`ms.catEmpty`)、以及种类小标 `ms.tagBuiltin`「内置」→「官方」+ `ms.builtinHint`「平台内置通道」→「官方通道」;⛔ **词典键名与代码标识符一律不动**(U10)。|v0.3.21(2026-09-15):**实例内「我的技能」分组**(档案 100 / 交接单 T01)—— 用户在同一页管理「功能插件 + 我的技能」两类功能。① **形态**:并入既有「功能管理」section 内**分组**,**不新开 section**(依据用户口径「不要分开管理」+ 档案 60 的分区命名);**仅入口层合并、机制层独立** —— 技能是 watch 驱动(启停**立即生效、无需重启**)而插件要重启实例,两条 API(`/api/skills/mine*` vs `/api/plugins/mine*`)与两套落盘位置一律不动(档案 16 §2.1 决策 2)。② **技能行**:技能名(截断三件套)+ 来源徽章(共享/我的)+ 状态(● 已启用 / ○ 已停用)+ 事实(文件数 · 体积,`tabular-nums`)+ 动作(启用/停用、删除);沿用官方卡片行语汇(动作区 `margin-left:auto` + `nowrap`)⇒ **结构上不可能换行**。③ **平台共享技能**(`source:'shared'`、`locked:true`)显示 🔒只读并**不渲染任何动作按钮** —— 与后端 `assertNotShared()` 的 409 双向一致(前端不给注定失败的按钮,后端仍兜底)。④ **上传**:虚线拖拽区 + 点击选文件(`label` 包隐藏 `input`,无需 ref),客户端先校 `.zip` / ≤150MB;**同名走两阶段**(先暂存 → 页内弹窗确认,写明「替换将删除旧技能全部文件、不可恢复」+ 旧/新文件数对照 → `apply`),与门户同语义。⑤ **删除**同样必须过页内确认(不可逆);启用/停用**可逆故不弹窗**。⑥ 失败一律**行内展示后端中文原文**(如「「x」是平台共享技能…请改用其他技能名」),不自己造词、不裸露状态码。⑦ zh/en 全量词条(`msk.*`);`scripts/verify-my-skills.mjs` 把「不换行 / 危险操作必须二次确认 / 锁定行无按钮 / tab 分页」钉成机械断言并入 `npm run verify`。|v0.3.19(2026-09-14):**内存口径最终版 —— 基础 MIN、最多浮动到 MAX**(用户:「改成基础 min 最大可以浮动Line truncated
"type": "module",
"main": "lib/index.js",