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 一律写「远程服务器」。
5.0 KiB
5.0 KiB
09-普通用户隐藏「模型」设置分区(角色化 profile patch)
日期:2026-09-09 | 状态:已落地 | 类型:功能改造(会话 UI 角色裁剪) 相关:档案 03(统一 KEY 管理员管控)、档案 05(portal-entry / settings.section 机制)
一、需求
普通用户在 dsh 设置面板不应出现「模型」分区(可配置 provider/key 的入口):
- 管控一致性:模型 KEY 由管理员统一管控(档案 03)——若普通用户能在自己实例里配 key,可绕过统一 key(用自己的 key);
- 现状即故障感:官方 web profile 的模型 provider 目录在此环境不可用(实测「加载提供方目录失败: settings are unavailable in this browser」),普通用户点进去看到报错,体验差;
- 方案决策:用户拍板 B 方案 = 隐藏「模型」设置分区(admin 保留),比"保留分区+报错"(A)体验好;不选改官方文案(C,触碰官方 client)。
二、机制调研(实证)
| 项 | 结论 |
|---|---|
| client 插件装载 | profile bundles(package.json dsh.profile.bundles,如 base/web-app)→ 展开为 cordis 树,client 插件行形态 - id: ui-settings-models\n name: "@deepseek-ai/dsh-client-ui-settings-models"(id 是短 id 非包名,dsh --profile web --dump-config 可见) |
| cordis patch disable | dsh-app-boot applyEntryPatches:非 insert patch 按 id 找到目标行 → overrides(含 disabled: true)合入 → 该 client 插件不装载、分区消失。官方 telemetry patch 亦用 disabled: true(profile-boot) |
| patch 落点 | profile 层 $DSH_HOME/profiles/web/cordis.patch.yml(dsh 组合顺序:bundle 层 → 此文件 → --patch overlays)。当前生产实例 spawn 不带 --patch(enablePatch=false 且 enter 传 undefined),故 --patch overlay 通道未启用——disable 只能写 profile 层 |
| 生效时机 | 必须重启实例:client bundle 在实例启动时打包(同 v0.4.3 教训);patchReload: live 实测对 client 插件增减不生效(改文件后不重启,浏览器设置分区不变,5 次轮询确认) |
| dump 验证命令 | DSH_HOME=<home> dsh --profile web --dump-config → ui-settings-models 行出现 disabled: true 且注释 # == ... patched by .../cordis.patch.yml |
三、改动点
| 文件 | 内容 |
|---|---|
profiles/web/cordis.patch.yml(普通用户,guest 已写) |
默认 [] → 管理标记头 + disable 块(见下) |
/opt/dshs/ensure-role-profile-patch.cjs(新增,chmod 600) |
幂等工具:非 admin 用户检查/写入 disable 块;--restart 可 kill 实例由 watchdog 拉新。用法:node ensure-role-profile-patch.cjs [--restart] [username...](不带用户名 = 全部非 admin) |
disable 块内容:
# dshs role patch: 普通用户隐藏「模型」设置分区
# admin 保留;由 ensure-role-profile-patch.cjs 管理,勿手改
- id: ui-settings-models
name: "@deepseek-ai/dsh-client-ui-settings-models"
disabled: true
admin profile 不动(保留「模型」分区)。未来其他"角色化 UI 裁剪"沿用同一机制(在 ensure 脚本注册更多 disable 块 / 按 role 扩展)。
四、验证记录(guest 实测)
- guest profile 写 disable 块 →
dsh --dump-config:ui-settings-models行带disabled: true+ patched 注释 ✅ - 重启 guest 实例(POST /api/dsh/restart,port 37089→45255)→ 浏览器(沙箱外 Chrome + CDP + sid + token URL)设置面板:导航 = 通用设置/插件/Agent 预设(无「模型」)✅(截图
guest-no-models.png) - 还原
[]不重启 → 5 次轮询模型分区不回来 → live reload 对 client 增减无效,需重启 ✅(反证生效时机) - ensure 脚本:幂等(已管理 → skip);wrote 分支演练(还原 [] → 跑 → 写入 disable 块 + dump 验证合入)✅
- admin profile 未动(dump 正常,模型分区保留)
五、新增普通用户 SOP(模型分区隐藏)
# 1. 用户注册 → admin approve(role=active)
# 2. 用户首次登录一次(dsh spawn,创建 profiles/web/)后:
node /opt/dshs/ensure-role-profile-patch.cjs --restart <username>
# (--restart:kill 其实例 main,watchdog 拉起带 disable patch 的新实例)
# 或用户此时实例未运行 → 下次 enter spawn 自动读 disable,无需 --restart
六、后续
- P1「管理类插件化 / 编导工作区模板」落地时,评估把"角色化 profile patch 注入"收编为编排器原生(新用户 spawn 自动带),替代人工跑脚本;
- admin 若后续也不需要「模型」分区(统一 key 走门户 /api/me/keys),同样机制对 admin profile 执行即可。
七、回滚
# 恢复该用户 profile 默认空 patch + 重启实例:
echo '[]' > /var/lib/dshs/users/<id>/home/profiles/web/cordis.patch.yml
# (或先备份:cp cordis.patch.yml cordis.patch.yml.bak-<ts>)
# 重启实例后「模型」分区回归