DeepSeek Harness 插件开发与发布:dsh-plugin 话题约定与官方流程
写插件的教程满地都是,写完之后怎么让别人装上却常常没人讲。DeepSeek Harness 这边有一份专门的文档 docs/user/develop/basic/publish.md(截至我们采集时 183 行),把「分发」这件事拆成了两个互不重叠的概念,还顺手记下了一条从 GitHub 安装时的坑。这篇就沿着那份文档和 apps/cli/reference/README.md 走一遍。
先把限定说在前面:deepseek-harness 仓库建立于 2026-08-13,我们的采集与快照时间是 2026-08-16,前后只差三天;根 package.json 里 version 是 0.1.0-rc.5,GitHub 上一个 Release 都没有,README 自述处于开发者预览阶段并明确写了未来会出现破坏兼容性的变更。下面提到的每一条命令、字段名与默认值都要带着这层限定读。
dsh-plugin 这个话题,是约定不是机制
截至 2026-08-16,deepseek-ai/deepseek-harness 仓库自己挂着四个 GitHub topics:ai-agents、cordis、dsh、dsh-plugin。很自然会以为 dsh-plugin 是某种插件注册渠道。
我们把这条约定的来源找了一遍:README.md:40、README.zh.md:40、CONTRIBUTING.md:15、CONTRIBUTING.zh.md:15 这四处都有同一句文字建议——给插件仓库加上 dsh-plugin 话题,方便被发现。除此之外,我们没有在仓库里找到任何读取或校验这个 topic 的代码,也没有找到与之配套的发现或索引实现。
所以它目前的性质是「作者侧的自愿标注」:加了能被搜到,不加也不影响插件被安装和加载。真正决定一个包能不能被 dsh 识别为扩展的,是下面要说的 manifest 字段。
顺带记一处名字上容易撞车的差异,只陈述、不延伸:仓库内部的一份 Agent Note .agents/notes/implemented/simplification/2026-08-09-remove-repository-plugin.md:17 记录了 @deepseek-ai/dsh-repository-plugin 包、.dsh-plugin 编写格式以及 dsh-plugin-prepare 可执行文件被移除;我们遍历 packages/**/package.json 找 name 含 repository-plugin 的包,结果是空列表。而 docs/subsystems/skills.md:13 与 :249、packages/skill/skill/README.md:9、packages/extensions/README.md:5、packages/extensions/tool-cordis/README.md:19 这几处文档仍在描述 repository plugin。两边口径不一致,以我们实读的仓库状态为准。也就是说,GitHub 话题里的 dsh-plugin 和那个已被记为移除的 .dsh-plugin 编写格式,是两件不同的东西。
bundle 与 profile:一个是你写的,一个是用户启的
docs/user/develop/basic/publish.md:13-14 给了两个定义:
- **bundle(组合包)**是一个 npm 包,在
package.json里声明dsh.bundle,回答「这个包贡献了什么」——一个 patch 文件。 - **profile(配置档)**是
$DSH_HOME/profiles/<name>下的一个目录,在package.json里声明dsh.profile,回答「哪些 bundle 按什么顺序组成这套环境」。
文档在 :16 用一句断言把两者钉死:bundle 是你编写并分发的东西,profile 是用户用 dsh --profile <name> 启动的东西,「Nothing is both」——没有任何东西同时是两者。
bundle 的目录是三件套(:26-31):package.json、cordis.patch.yml、index.js。manifest 里的关键字段就一行(:42):
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
漏了这一行会怎样?文档 :64 写得很直白:没有 dsh.bundle 声明的包仍然可以安装,但只作为普通依赖存在,dsh plugin 会打印一条警告,并且不激活任何配置层。这是排查「装上了却没生效」时第一个该看的地方——先确认包的 package.json 里有没有这行声明,而不是先去翻插件代码。
安装、验证、移除
文档给的三条命令(:80、:106-107、:110)原样是:
dsh plugin --profile demo add ./hello-plugin
dsh --profile demo --dump-config
dsh --profile demo
dsh plugin --profile demo remove dsh-hello-plugin
首次使用时会初始化 profile,并以 @deepseek-ai/dsh-base 作为第一个 bundle(:83)。
apps/cli/reference/README.md:43 补了一条对理解这条命令很关键的说明:dsh plugin --profile <name> <args...> 实际上是把参数转发给 profile 目录下的 pnpm,所以 add / remove / why / update 这些 pnpm 动词原样可用;也因此 pnpm 必须在 PATH 上。每次成功执行之后,它会按已安装状态去对账 dsh.profile.bundles。
这一条对 Windows 用户尤其要留意:既然 dsh plugin 是把参数转发给 pnpm,那「pnpm 必须在 PATH 上」就是这条命令的硬前提,先在终端里确认 pnpm 能被调起再谈别的。另外要区分清楚一件事——docs/user/guide/python-sdk.md:102 里那句「不支持 Windows agent」,说的是 Python SDK 示例组合因为持久 PTY 后端需要 POSIX 终端底座,与这里的插件发布流程不是同一件事,别把两条限制混在一起。
写命令时还有个细节容易翻车:目录名不等于包名。同一份事实里就有两个现成的例子——packages/extensions/ui-cordis 的包名是 @deepseek-ai/dsh-client-ui-cordis,packages/sdk/server 的包名是 @deepseek-ai/dsh-sdk-jsonrpc-server。写 add / remove 参数时以 package.json 里的 name 为准,不要照着目录名敲。
四层叠加:最容易写错的是「整行替换」
配置层叠顺序在两处都有记录:docs/user/develop/basic/publish.md:116-119 与 apps/cli/reference/README.md:9。四层,后面的层覆盖前面的:
| 顺序 | 层 |
|---|---|
| 1 | profile manifest 里 dsh.profile.bundles 列出的每个 bundle patch,按列表顺序(@deepseek-ai/dsh-base 最先) |
| 2 | profile 自己的 cordis.patch.yml |
| 3 | home 级 $DSH_HOME/cordis.patch.yml |
| 4 | 每个 --patch <path> overlay,按 argv 顺序 |
apps/cli/reference/README.md:9 还补了一句:home 级那一层「outranks the per-profile layer」——排在 profile 自己那层之后,因此优先级更高。
真正的坑在 :123 这句语义上:「Later layers win per row, and a patch replaces a row’s entire config value rather than deep-merging keys.」覆盖是按行生效的,而且一个 patch 是把该行的整个 config 值替换掉,不是按 key 深合并。文档在 :125 直接给了结论:覆盖某一行时,必须把这一行需要的每个 key 都重写一遍,不能只写你想改的那个。
这条语义决定了一类症状的判定方式:如果你只在自己的 patch 里写了一个字段,结果发现这一行上原本存在的其它字段全都没了——先别怀疑插件,去比对同一行在更靠前的层里原本有哪些 key,再把它们补齐重写。dsh --profile demo --dump-config 就是给这一步用的,先看合并后的结果再动手。反过来,如果 --dump-config 里这一行的 key 都在、值也是你写的,那问题就不在叠加语义上,得往别处查。
从 GitHub 装:那条构建脚本陷阱
docs/user/develop/basic/publish.md:153-178 单独用一节写了这件事。git 安装取到的是源码而不是构建产物,TypeScript 包因此会缺 lib/ 而加载失败。文档说两边各要做一件事:
- 作者侧:提供一个自包含的
prepare脚本。 - 用户侧:因为 pnpm ≥10 默认拒绝执行 git 依赖的
prepare,需要在 profile 的pnpm-workspace.yaml里写allowBuilds: { dsh-hello-plugin: true },然后重跑add。
关于这条授权意味着什么,文档 :173 的定性值得原样引一遍:这是「permission to execute the package’s code on your machine at install time, outside any sandbox the agent runs under」——在安装时于你的机器上执行该包的代码,且不在 agent 运行所处的任何沙箱之内。同一处建议只对自己信任源码的包开这个口子,并 pin 到具体 commit。
文档还给了两个不需要这条授权的分发方式(:177-178):发布到 npm(lib/ 在 pnpm publish 时构建),或者用 pnpm pack 打出 tarball,让用户 dsh plugin add ./xxx.tgz。如果你是作者,这两条路能让用户少开一次执行权限。
以上命令与配置片段均照抄自仓库文档,未经实测,以官方文档与 --help 的实际输出为准。
回到起点
把这几件事串起来:dsh-plugin 话题解决的是「被找到」,dsh.bundle 声明解决的是「被识别」,profile 与四层叠加解决的是「被组合进某套环境」,prepare / npm / tarball 解决的是「装得上」。四件事各管一段,缺哪一段都会表现为「我的插件没生效」,但排查入口完全不同。
最后再提醒一次版本状态:本文引用的所有字段名与命令都取自 0.1.0-rc.5 快照,该仓库没有任何 Release,README 自述会有破坏兼容性的变更,别把这些写进无人值守的脚本里就不管了。
延伸阅读
- 从头读起:DeepSeek Harness 是什么:建仓三天、13 万 star 的 Agent 框架
- 本专题共 45 篇,完整分组目录见专题页
- DeepSeek Harness 注册了几个工具:README 五个、源码七个
- DeepSeek Harness 的 Agent 轮次上限:包默认 256、出厂组合配 64
本文依据 DeepSeek Harness 官方仓库(github.com/deepseek-ai/deepseek-harness)的 README、docs/ 下的
架构与子系统文档、以及 packages/ 下的源码整理,核对日 2026-08-16,对应仓库快照 47f9438(版本 0.1.0-rc.5)。
本文内容为仓库源码与文档口径,我们没有安装、也没有运行过这个项目,
因此不涉及界面外观、操作手感与运行速度的任何描述。
该仓库建立于 2026-08-13,README 自述处于开发者预览阶段并明确说明未来会有破坏兼容性的变更,
文中出现的命令、配置与默认值随时可能变动,请以仓库最新内容为准。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。