# 09-普通用户隐藏「模型」设置分区(角色化 profile patch) > 日期:2026-09-09 | 状态:已落地 | 类型:功能改造(会话 UI 角色裁剪) > 相关:档案 03(统一 KEY 管理员管控)、档案 05(portal-entry / settings.section 机制) ## 一、需求 普通用户在 dsh 设置面板不应出现「模型」分区(可配置 provider/key 的入口): 1. **管控一致性**:模型 KEY 由管理员统一管控(档案 03)——若普通用户能在自己实例里配 key,可绕过统一 key(用自己的 key); 2. **现状即故障感**:官方 web profile 的模型 provider 目录在此环境不可用(实测「加载提供方目录失败: settings are unavailable in this browser」),普通用户点进去看到报错,体验差; 3. **方案决策**:用户拍板 **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= 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 块内容: ```yaml # 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 实测) 1. guest profile 写 disable 块 → `dsh --dump-config`:`ui-settings-models` 行带 `disabled: true` + patched 注释 ✅ 2. 重启 guest 实例(POST /api/dsh/restart,port 37089→45255)→ 浏览器(沙箱外 Chrome + CDP + sid + token URL)设置面板:导航 = 通用设置/插件/Agent 预设(**无「模型」**)✅(截图 `guest-no-models.png`) 3. 还原 `[]` 不重启 → 5 次轮询模型分区不回来 → **live reload 对 client 增减无效**,需重启 ✅(反证生效时机) 4. ensure 脚本:幂等(已管理 → skip);wrote 分支演练(还原 [] → 跑 → 写入 disable 块 + dump 验证合入)✅ 5. admin profile 未动(dump 正常,模型分区保留) ## 五、新增普通用户 SOP(模型分区隐藏) ```bash # 1. 用户注册 → admin approve(role=active) # 2. 用户首次登录一次(dsh spawn,创建 profiles/web/)后: node /opt/dshs/ensure-role-profile-patch.cjs --restart # (--restart:kill 其实例 main,watchdog 拉起带 disable patch 的新实例) # 或用户此时实例未运行 → 下次 enter spawn 自动读 disable,无需 --restart ``` ## 六、后续 - P1「管理类插件化 / 编导工作区模板」落地时,评估把"角色化 profile patch 注入"收编为编排器原生(新用户 spawn 自动带),替代人工跑脚本; - admin 若后续也不需要「模型」分区(统一 key 走门户 /api/me/keys),同样机制对 admin profile 执行即可。 ## 七、回滚 ```bash # 恢复该用户 profile 默认空 patch + 重启实例: echo '[]' > /var/lib/dshs/users//home/profiles/web/cordis.patch.yml # (或先备份:cp cordis.patch.yml cordis.patch.yml.bak-) # 重启实例后「模型」分区回归 ```