dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
会话级的「按插件工具门控」插件(DeepSeek Harness 0.2.x)。
会话开始时弹出选择面板:列出所有已启用、且会注册工具的插件,每个插件一个标签(悬停显示它会注册哪些工具)。 被关掉的插件,其工具在这个会话内始终不会注入模型上下文,强行调用也会被拒绝;其它会话完全不受影响。
dsh.profile.bundles 与每个包的 cordis.patch.yml,得到「loader 行 → 它 mount 的包 → 该包源码里 tools.register({ name: '…' }) 的字面量」,再与运行时 ctx.tools.schemas() 求交集。不依赖插件加载顺序(插件作为 bundle 最后加载时也能正确归属宿主工具),宿主内置工具从 resources/app.asar 里读。归因结果只在内存里,不落盘。
tool-browser、billion-context),浏览器工具归到 tool-browser 而不是笼统一个桶。…/list-agents)只认它自己 mount 的那个模块,不会把同包兄弟模块的工具算进来;名字写在常量里(name: EXIT_PLAN_MODE)或由行配置指定(toolName: subagent_fork)的也能解析。toolName 配置优先,其次后声明的具体行先认领(tool-browser 胜于根行 dsh-builtin-browser)。agent-preset-registry 的 selectedDefault)。像 coding 这种不随 web-app 发布预设文件的模式,roster 由 bundle 补丁声明:改为只认挂在隔离 group 下的行(如 planning group 里的 plan-mode),其它预设文件整体跳过。compress / acp_status)。仍未命中的才进 (未识别)。askOnSessionStart: false 关闭)。标签 = 插件行名,悬停显示工具名列表,✓ 启用 / ✕ 禁用,另有「全部启用 / 全部禁用」「应用」「稍后再说」。
@deepseek-ai/dsh-tool-*,例如 tool-fs、tool-bash、tool-subagent)标记「待会话」:它们挂在会话的 agent scope 上,用虚线标签提前列出,第一条消息发出后按当前预设生效。所以官方内置插件的工具同样可以勾选/禁掉。@deepseek-ai/* 的插件行收进「官方内置插件 · N 个」一行,点一下展开/收起,避免几十个内置标签挤满面板;第三方插件始终平铺显示。needs-confirmation,面板先列出「将禁用 / 将重新启用」的插件,点「仍然应用」才落盘。$DSH_HOME/tool-gate.json(按 session id),并在 agent/created 时把两道闸门注册到该 agent 的 scoped ctx 上:
system-prompt/assemble 过滤掉被禁插件的工具 schema(模型看不到,也调用不了);tools.guard() 兜底:真被调用时返回 tool-gate: tool is masked because its plugin was disabled for this session (<tool>)。
闸门随 agent 销毁自动卸载。+ 按钮旁边)的「工具门控」按钮可重新打开面板,新会话、旧会话都能点到;面板显示当前关掉了几个插件。package.json 的 engines.node;CI 在 18 / 20 / 22 / 24 上跑测试。桌面端插件实际运行在 DSH 内置的 Electron/Node 上,无需另装 Node;只有在源码里跑测试或用 dsh CLI 时才需要本机 Node。engines 只是元数据:npm 默认只在不匹配时给出 EBADENGINE 警告,不会阻止安装;要硬拦截需在消费侧开启 engine-strict。dsh plugin --profile desktop add link:<本目录的绝对路径>
然后在 $DSH_HOME/profiles/desktop/package.json(DSH_HOME 默认是 ~/.dsh)的 dsh.profile.bundles 末尾确认有 @suntion/dsh-tool-gate(dsh plugin add 会自动追加),重启 DSH 生效。
开发时可复制一份临时 profile 试用:
dsh plugin --profile gate-test add link:<本目录的绝对路径>
dsh --profile gate-test --no-open --port 64001
cordis.patch.yml 中该行的 config)| 字段 | 默认 | 说明 |
|---|---|---|
askOnSessionStart | true | 新会话是否自动弹窗 |
file | $DSH_HOME/tool-gate.json | 状态文件路径 |
状态文件结构:
{
"version": 1,
"defaults": { "disabled": [], "updatedAt": "…" },
"sessions": {
"session-<uuid>": { "configured": true, "disabled": ["tool-browser", "billion-context"], "started": true, "updatedAt": "…" }
}
}
defaults 是「不再反复弹窗」的默认值;sessions.*.started 记录该会话是否已经有 agent(发过消息),用于判断改配置是否需要二次确认。
POST /tool-gate/api/state body { sessionId };POST /tool-gate/api/set body { sessionId, disabled: string[], asDefault?, confirm? }。
state 返回 { ok: true, value: { sessionId, groups, choice, started, askOnSessionStart } };set 在「会话已开始且与当前值有差异、又没带 confirm: true」时返回 409 { error: { code: "needs-confirmation", detail: { added, removed } } }。lib/index.js host 入口(name/inject/apply)
lib/registry.js 工具→插件归因(静态扫描 ∩ 运行时 schemas)
lib/scan/index.js 扫描 profile bundles / 包源码 / app.asar,得到 行→工具名
lib/scan/asar.js 只读 ASAR 解析(宿主内置插件源码在 app.asar 里)
lib/scan/patches.js cordis.patch.yml 行解析
lib/policy.js agent/created → scoped assemble 过滤 + tools.guard
lib/store.js $DSH_HOME/tool-gate.json 的读写(tmp+rename)
lib/gateway.js webServer 路由 /tool-gate/api
lib/client.js 手写 __ModuleLoader__ bundle:会话开始弹窗 + 右上角按钮
node test/smoke.mjs # 归因 / 门控 / 持久化(假 ctx)
node test/client-smoke.mjs # 客户端 bundle 的 slot 注册
端到端(真实 DSH web + 本地假模型 test/fake-model.cjs,Electron 驱动):
working_directory)→ 模型硬调该工具 → UI 显示 Error: tool-gate: tool is masked because its plugin was disabled for this session (working_directory)。working_directory)→ 调用正常执行。发布由 .github/workflows/release.yml 处理。发布 vX.Y.Z 格式的 GitHub Release 后,工作流会:
package.json 的 version 一致;@suntion/dsh-tool-gate@X.Y.Z 尚未发布;pnpm pack 生成 tarball,再用 npm publish 发布到 npm(pnpm 与 npm 共用同一个 registry,不需要分别发布)。发布到 npm 后工作流即结束,不再轮询 registry 做二次确认:npm publish 返回 0 就代表 tarball 已被接收,而 npm 会对新版本做自动审核,审核完成前 registry 一直对该版本返回 404(npm 网站上状态显示为「验证」,实测需要几分钟)。想看结果直接打开 https://www.npmjs.com/package/@suntion/dsh-tool-gate。
首次使用前,在仓库 Settings → Secrets and variables → Actions 中添加 NPM_TOKEN,token 需要拥有该包的发布权限。工作流也支持手动触发:填写已有的 Release tag 即可重试。