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 一律写「远程服务器」。
3.8 KiB
3.8 KiB
40-共享技能层挂载修复(bundled-skills 进实例命名空间)(2026-09-11 落地)
背景与动机
档案 38a 的 P0:共享技能层「投了也发现不了」。
config.bundledSkillDir=<dataRoot>/bundled-skills(/var/lib/dshs/bundled-skills),orchestrator.ts已把它注入 envDSH_BUNDLED_SKILL_DIR(ALLOWED_ENV也放行)。- 但
@deepseek-ai/dsh-skill-filesystem是在实例内读盘:lib/index.js:84config.bundledSkillDir ?? process.env.DSH_BUNDLED_SKILL_DIR→resolve()→lib/index.js:181-183roots.push({ path: this.bundledSkillDir, source: "bundled" })→discoverRoot(root, …)。 - 而平台 bwrap 只注入 env、从未挂载该目录 → 实例内
ls= No such file or directory → 技能投进去也永远发现不了(档案 10/11 的共享技能层实际失效)。
用户决策
「bundled-skills 的调整确保用户访问权限不扩大就行」
→ 修挂载,但必须证明可见面不变。已按此口径实测(见「验证记录」)。
实现
src/supervisor/orchestrator.ts · spawnAsUser,在 '--bind', root, root 之后追加:
...(this.config.bundledSkillDir !== ''
? (['--ro-bind-try', this.config.bundledSkillDir, this.config.bundledSkillDir] as string[])
: []),
- 放在
--bind root root之后:bwrap 后写覆盖前写(与 profile 三文件同理)。 - 用
--ro-bind-try而非--ro-bind:目录若被误删,实例仍能起来(不崩)。 - 仅当
config.bundledSkillDir !== ''时追加,保留"未配置即不暴露"的语义。
验证记录
以平台原样 bwrap 参数 + 本行、以 guest 的 uid(100002)在生产机实跑。
| 检查 | 结果 |
|---|---|
mountinfo |
…/bundled-skills → 同名 ro,nosuid,nodev(精确叶子路径 + 只读) |
实例内 ls $DSH_BUNDLED_SKILL_DIR |
mcn-probe(探针技能可见) |
实例内 readFileSync($DSH_BUNDLED_SKILL_DIR + '/mcn-probe/SKILL.md') |
OK,读到 name: mcn-probe ← 即 skill-filesystem 的读取条件成立 |
实例内 realpathSync($DSH_BUNDLED_SKILL_DIR) |
/var/lib/dshs/bundled-skills |
| 挂载点可写 | touch = Read-only file system |
ls …/users/ |
1 项(只列用户自己) ← 可见面未扩大 |
读 dshs.db |
NO |
/etc 可见项 |
13(未被本改动影响) |
| 真实启动实例 | 正常打印 dsh web: http://127.0.0.1:41998/?token=… |
探针技能验证后已删除,宿主 bundled-skills/ 恢复为空。
事故 / 踩坑记录
- 光注入 env 没用:
DSH_BUNDLED_SKILL_DIR一直在ALLOWED_ENV+baseEnv里, 看上去"配置齐全",但 skill-filesystem 是实例内读盘,env 只决定"去哪儿找", 目录不挂载就是找不到。判据:插件在实例内resolve()+ 读盘的东西,必须真的出现在 命名空间里(通用化为本 skill 的铁律)。 - ⛔ 只能挂最内层路径。写成挂父目录(
--ro-bind <dataRoot> <dataRoot>)= 一次性把全部用户 home + DB + 凭据放进每个实例。改完必须逐字复核 args 并重跑可见面实测。
回滚 / 注意
- 回滚:
cp src/supervisor/orchestrator.ts.bak-<ts> src/supervisor/orchestrator.ts && npm run build && systemctl restart dshs - 副作用:
bundled-skills/内容对每个登录用户可读(设计意图)→ 该目录 禁止放内部文档 / 私有提示词 / 凭据;投放清单须按"全员可见"审一遍。 - 无 per-user 关闭能力:dsh 技能层无 disable/deny 机制,rank 600 的共享层内容用户关不掉
→ 只适合"全员基线技能";要可启停必须走 rank 400 用户层(
$DSH_HOME/skills,档案 41 方案)。 - 现状:机制已通,但尚未投放任何技能(投放属档案 41 范畴)。