Files
dsh_shenxian/dsh-server-docs/04-调整方案/10-共享技能只读部署-bundledSkillDir.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
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 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

9.3 KiB
Raw Blame History

10-共享技能只读部署(bundledSkillDir 层)

状态:已实施(机制生效);管理面见档案 11|2026-09-09|对象:dshs + dsh 0.1.2-rc.1 需求来源:能否上传 skill 让所有用户可用、只允许使用不允许修改?用 MCN V1.0 短视频脚本技能(290MB)做可行性分析 + 嵌套 subskill(browser-harness)能否在服务器运行。

TL;DR|结论:共享只读技能层:dsh 原生支持 bundledSkillDir(全员可用、不可修改)。 关键:实测结论:主技能放 bundled 只注册 1 个;6 个 subskill 要各自独立目录平铺才能成为可调技能。 状态:✅ 机制已实施(管理面见档案 11)

一句话结论

支持,且 dsh 原生设计了这一层:skill-filesystem 的 bundledSkillDir(env DSH_BUNDLED_SKILL_DIR,rank 600)就是"系统标准技能层"——所有实例共享、只读、不可改(trustedHost: true 直读 + root 属主 OS 权限兜底)。唯一卡点:dshs 编排器 spawn 子进程时 env 走白名单过滤(ALLOWED_ENV),新变量不会自动透传,需改编排器 2 处(自研代码,不触碰红线 2 的官方 dsh 主程序)。

需求判定

子问题 结论
dsh 是否支持上传 skill 全员共享 ✅ 支持,但无"上传"界面——机制是文件系统扫描 + env 注入指向共享只读目录
只允许使用、不允许修改 ✅ bundled 层设计即此语义(详见机制表)
MCN V1.0(290MB)能否直接放 ⚠️ 需裁剪(290MB 中 238MB 是 browser-harness 的 Windows venv,服务器 Linux 不可用)+ 拍平(见 subskill 结论)
嵌套 subskill(browser-harness 等)能否被识别 ⚠️ discoverRoot 只扫 root/<技能名>/SKILL.md 一层,subskills/ 内技能不会自动注册为独立技能;需拍平到 bundled 根目录(每个子技能目录含自己的 SKILL.md 即可独立注册)
browser-harness 能否在服务器运行 ⚠️ 机制上可行但工程量大:自带 envs 是 Windows 构建(Scripts/browser-harness.exe),Linux 需重建 venv + 装 headless Chrome + 依赖;且服务器为海外节点,直连抖音有风控/地区限制风险

机制调研表(源码级证据)

1. skill 装配链(谁在装载技能)

环节 位置 证据
注册插件 @deepseek-ai/dsh-agent-presets/presets/standard/agent.cordis.yml L83-88 skill-filesystem(dsh-skill-filesystem)+ tool-skill(dsh-tool-skill,提供技能目录/加载给 agent)
provider 实现 @deepseek-ai/dsh-skill-filesystem/lib/index.js apply(ctx, config) → ctx.skills.registerProvider(...)
env 来源 同文件 L84 config.bundledSkillDir ?? (includeDefaultRoots ? process.env.DSH_BUNDLED_SKILL_DIR : void 0) → preset 里无 config 块 → 走 env
只读语义 同文件 L142 get() parseSkillFile(locator.path, ctx, signal, candidate.source === "bundled") —— bundled 走 trustedHost 直读

2. 技能分层 rank(roots 构建,L166-190)

root 路径 source rank 修改权限
project-dsh <项目根>/.dsh/skills project-dsh 100 项目内
project-agents <项目根>/.agents/skills project-agents 200 项目内
custom config.customSkillDirs custom 300 配置方
user-dsh $DSH_HOME/skills user-dsh 400 用户自己(每人独立,改自己的)
user-agents $DSH_AGENTS_HOME 或 ~/.agents/skills user-agents 500 用户自己
bundled $DSH_BUNDLED_SKILL_DIR bundled 600 只读共享(trustedHost=true,无修改面)

rank 600 注释 trustedHost: true:读取绕过沙箱 fs 服务走 Node 原生直读;技能源为系统标准层,模型侧无可写入口。

3. 扫描粒度(决定 subskill 命运)

  • discoverRoot(root) = readdir(root.path) 单层 → 每个 entry:
    • 目录 → 要求 <entry>/SKILL.md 存在
    • .md 文件 → 直接视为技能
  • isPotentialSkillPath:相对 root 最多 2 段(root/<技能名>/SKILL.md),更深(如 root/主技能/subskills/子技能/SKILL.md)不被发现。
  • 结论:V1.0 主技能放 bundled 只注册「短视频工作台」一个;6 个 subskill 要成为独立可调技能必须各自独立目录平铺在 bundled 根。

4. 编排器 env 白名单(实施唯一卡点)

/opt/dshs/lib/supervisor/spawn.js L11-27:

const ALLOWED_ENV = new Set(['PATH','HOME','USER','TMP','TEMP','TMPDIR','SYSTEMROOT','SystemRoot',
  'PATHEXT','ProgramFiles','ProgramFiles(x86)','LANG','LC_ALL']);

orchestrator.js baseEnv()(L189-205)= {...scrubEnv(process.env), HOME: ws, DSH_HOME: home, DEEPSEEK_API_KEY}。

→ DSH_BUNDLED_SKILL_DIR 不在白名单 → 即使 systemd 里设了也会被 scrub 掉。必须:

  1. spawn.js ALLOWED_ENV 加 'DSH_BUNDLED_SKILL_DIR'
  2. orchestrator.js baseEnv 显式注入(建议读自身 config,如 DSH_BUNDLED_SKILL_DIR ?? '/opt/dshs/bundled-skills')
  3. src/*.ts 同步修改(保持与 lib 编译产物一致)

改的是自研编排器,非官方 dsh 主程序 → 不违反红线 2。

推荐实现方案(获批后执行)

步骤 操作 说明
1 服务器建共享目录 /opt/dshs/bundled-skills/(root:root 755) 用户进程只读,OS 权限兜底不可改
2 改编排器 2 文件(spawn.js 白名单 + orchestrator.js baseEnv)+ src 同步 重启 systemctl restart dshs
3 裁剪 V1.0 → 部署包(见下) 290MB → ~30MB
4 平铺部署:bundled 根下放 mcn-workstation/(主技能)+ 各 subskill 独立目录 主技能内 subskills/ 相对引用保留(V1.0 内目录不动);独立注册的子技能解决"按名调用"
5 重启 admin/guest 实例(client 面技能目录即时可见) 实例重启走主域 Host /api/dsh/restart
6 guest 浏览器实测:技能目录可见 → 发起会话让模型列出技能 → 确认只读 见档案 09 同款验证栈

V1.0 裁剪清单(290MB → 预计 <30MB)

内容 大小 处置
subskills/browser-harness/envs/ 238MB 删除(Windows venv,Linux 不可用;服务器按需重建)
mcn-work-shop/*.db.bak-* ~16MB 删除(4 份调试备份)
mcn-work-shop/ 其余(server.js/tmp/png/.mjs) ~4MB 视需求——调试工作台,建议不随技能分发(保留 SKILL.md 引用则需评估)
SKILL.md + references/ + references-add/ + scripts/ ~1.3MB 保留(核心)
6 个 subskill(除 browser-harness envs 外) ~25MB 保留 + 拍平

subskill / browser-harness 服务器可行性

识别(注册)

  • 每个 subskill 目录已含独立 SKILL.md(已逐一确认 6/6)→ 平铺到 bundled 根后可独立注册。
  • 主技能 SKILL.md 的「子技能索引」用相对路径 subskills/<名>/SKILL.md 引用 → 主技能加载后模型可读文件;但要"作为技能被调用"需独立注册(dsh 无宿主递归扫描机制,WorkBuddy 的软链/递归扫描不适用)。

运行(browser-harness)

检查项 服务器现状 结论
自带环境 envs/browser-harness/Scripts/browser-harness.exe(Windows) ❌ 不可直接用
Chrome 未安装(which 空) 需装 headless chromium + 系统库(libnss3 等)
Node v22.23.2 ✅ 可跑 node 型脚本
网络 47.77.182.89 海外节点 抖音直连风控/地区限制风险高(MCN 数据抓取核心场景)
RedFox API 路径 REDFOX_API_KEY 已具备 ✅ API 型数据(榜单/诊断)不受浏览器影响,优先走此路

→ 结论:browser-harness 服务器机制可行(Linux headless Chrome + 重建 venv),但投入大且抖音网页抓取在海外 IP 收益不确定;建议第一阶段不启用 browser-harness,抖音类技能优先走 RedFox API 子技能;网页交互类需求待业务验证后再评估。

方案对比

方案 机制 全员可用 只读 改动面 结论
A. bundledSkillDir(推荐) env 注入共享只读目录 ✅ ✅(原生 trustedHost + OS 权限) 编排器 2 文件 + 共享目录 正解,语义即"系统标准技能层"
B. 每用户 $DSH_HOME/skills 编排器建号时播种 ✅(复制分发) ❌ 用户可改自己副本 播种逻辑 + 同步机制 不合"不可改"
C. repository 插件全局注册 preset 注释提到"deployment registered globally" 未验证 未验证 机制不透明 不做

验证记录

  • 源码级证据全部取自行机服务器(2026-09-09 18:xx):装配链 / rank 常量 / discoverRoot 单层 / scrubEnv 白名单 / browser-harness envs 为 Windows venv。
  • 已实施(2026-09-09):spawn.ts ALLOWED_ENV 加变量 + orchestrator.ts baseEnv 注入 + 建 /var/lib/dshs/bundled-skills;admin dsh 实例 /proc/<pid>/environ 已含 DSH_BUNDLED_SKILL_DIR=/var/lib/dshs/bundled-skills。
  • 后续管理面(API+UI+个人技能)见档案 11。

回滚

  • 编排器改动 revert 2 文件 + systemctl restart dshs 即可(bundled 目录删除即技能消失;实例无需逐个回滚)。