DeepSeek Harness 插件开发与发布:dsh-plugin 话题约定与官方流程

2026-08-16

写插件的教程满地都是,写完之后怎么让别人装上却常常没人讲。DeepSeek Harness 这边有一份专门的文档 docs/user/develop/basic/publish.md(截至我们采集时 183 行),把「分发」这件事拆成了两个互不重叠的概念,还顺手记下了一条从 GitHub 安装时的坑。这篇就沿着那份文档和 apps/cli/reference/README.md 走一遍。

先把限定说在前面:deepseek-harness 仓库建立于 2026-08-13,我们的采集与快照时间是 2026-08-16,前后只差三天;根 package.jsonversion0.1.0-rc.5,GitHub 上一个 Release 都没有,README 自述处于开发者预览阶段并明确写了未来会出现破坏兼容性的变更。下面提到的每一条命令、字段名与默认值都要带着这层限定读。

dsh-plugin 这个话题,是约定不是机制

截至 2026-08-16,deepseek-ai/deepseek-harness 仓库自己挂着四个 GitHub topics:ai-agentscordisdshdsh-plugin。很自然会以为 dsh-plugin 是某种插件注册渠道。

我们把这条约定的来源找了一遍:README.md:40README.zh.md:40CONTRIBUTING.md:15CONTRIBUTING.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:249packages/skill/skill/README.md:9packages/extensions/README.md:5packages/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.jsoncordis.patch.ymlindex.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-cordispackages/sdk/server 的包名是 @deepseek-ai/dsh-sdk-jsonrpc-server。写 add / remove 参数时以 package.json 里的 name 为准,不要照着目录名敲。

四层叠加:最容易写错的是「整行替换」

配置层叠顺序在两处都有记录:docs/user/develop/basic/publish.md:116-119apps/cli/reference/README.md:9。四层,后面的层覆盖前面的:

顺序
1profile manifest 里 dsh.profile.bundles 列出的每个 bundle patch,按列表顺序(@deepseek-ai/dsh-base 最先)
2profile 自己的 cordis.patch.yml
3home 级 $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 官方仓库(github.com/deepseek-ai/deepseek-harness)的 README、docs/ 下的 架构与子系统文档、以及 packages/ 下的源码整理,核对日 2026-08-16,对应仓库快照 47f9438(版本 0.1.0-rc.5)。 本文内容为仓库源码与文档口径,我们没有安装、也没有运行过这个项目, 因此不涉及界面外观、操作手感与运行速度的任何描述。 该仓库建立于 2026-08-13,README 自述处于开发者预览阶段并明确说明未来会有破坏兼容性的变更, 文中出现的命令、配置与默认值随时可能变动,请以仓库最新内容为准。 安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。