DSH plugin 本地调试:--patch 覆盖层怎么用、--dump-config 看配置、改了不生效怎么排查
DSH plugin 本地调试有两条通道:改源码阶段用 --patch 覆盖层挂载绝对路径的插件文件(改完重启即生效),要验证分发产物才用 dsh plugin --profile <name> add 装进 profile;两者验证的目标不同,混用会把「没挂载」误判成「代码有问题」。 无论你叫它 DSH插件 还是 DeepSeek插件,这两条通道的边界都一样。
DSH plugin 两条调试通道怎么选
判断标准只有一条:你要验证的是「加载」还是「安装」。(来源)
| 通道 | 命令 | 验证目标 | 生效方式 |
|---|---|---|---|
| 覆盖层 | pnpm dsh web --patch ./scratch-plugin/cordis.yml | 插件能否被加载、代码是否正确 | 改源码 + 重启 |
| profile 安装 | dsh plugin --profile demo add ./hello-plugin | 打包产物能否被安装 | 重新 add |
调试源码时永远先用覆盖层,因为它的反馈链路最短:不需要 package.json 齐备、不需要打包、不需要走 pnpm 安装。
用 --patch 覆盖层挂载 DSH plugin 源码
覆盖层的两条硬规则:插件路径必须绝对,patch 只贡献配置。 官方教程的写法分三步(来源):
- 取绝对路径:先
pwd拿到检出根,写进配置的必须是完整绝对路径。 - 写覆盖层配置:在
./scratch-plugin/cordis.yml里插入插件行:
- insert:
- id: hello
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'
- 带覆盖层启动:
pnpm dsh web --patch ./scratch-plugin/cordis.yml,预期插件随之加载、改完源码重启即生效。
为什么「写在 patch 里却没加载」多半不是语法问题:patch 不改变 loader 解析模块路径所用的 profile 目录,所以相对路径、软链、拼错的绝对路径都会静默失效。
用 --dump-config 看 DSH plugin 的配置树
不看配置树就调试,等于闭眼改代码。 dsh 提供两个不启动就能看合成结果的开关(来源):
- 看合成后的配置树:
dsh --profile demo --dump-config,预期输出含各层 patch 叠加结果。 - 看默认配置树:
dsh --profile demo --dump-default-config,用于与合成结果对照。
合成顺序决定了「谁压过谁」:
- 各 bundle 的 patch(按
dsh.profile.bundles顺序) - profile 自己的
cordis.patch.yml - 家目录的
$DSH_HOME/cordis.patch.yml --patch覆盖层(优先级最高)
越靠后越优先,所以覆盖层能压过 profile 配置。理解了这层顺序,就能解释「明明改了配置却不生效」。
按 Fiber 状态定位 DSH plugin 加载到哪一步
插件的生命周期是一个状态机,卡在哪一步就说明问题在哪一类。(来源)
PENDING ──▶ LOADING ──▶ ACTIVE
│
└──▶ FAILED ACTIVE ──▶ UNLOADING ──▶ DISPOSED
- 根本没进入状态机:加载器没解析到这个模块——回去查路径与 patch。
- 停在
LOADING或FAILED:依赖没就绪(inject里声明的服务不存在)或apply抛了异常。 ACTIVE但行为不对:插件加载成功,问题在注册逻辑或事件模式选择。- 卸载后还在跑:手动资源没交给
ctx.effect。
处置器的一个坑:逆序开始调用,但异步处置器并发执行、不保证逐个完成;有顺序依赖的清理必须合并进同一个 ctx.effect。
DSH plugin 改了不生效的五步排查
按成本从低到高查,通常第一步就命中。
- 确认 patch 被带上:启动命令里有没有
--patch,文件路径是否正确。 - 确认你的行进了配置树:
dsh --profile demo --dump-config搜索插件 id。 - 确认路径是绝对路径:相对路径不会报错,只会静默不加载。
- 确认源码已重新编译:跑的是入口产物还是源码,改的文件是否是真正入口引用的那个。
- 确认不是被上层覆盖:同一 id 是否在 profile 或家目录配置里被改写(合成顺序见上一节)。
DSH plugin 清理与卸载验证
调试阶段就要验证清理,否则问题会推迟到用户机器上爆发。 两条规则:通过 ctx 注册的东西(事件监听、工具、定时器)会在插件卸载时自动清理;手动资源(网络连接、文件句柄)必须包进 ctx.effect() 交出处置器(来源)。
验证手段:卸载插件后观察日志是否停止、服务是否仍可访问。服务类问题(如重复注册)见 服务重复注册的排查;插件压根没激活见 插件没激活的排查。
最后补几条容易忘的命令与前提:
- 源码运行需要先
pnpm run build(生产运行需要构建产物),再用pnpm dsh <args...>转发参数; - launcher 只解析自己的参数,第一个它不认识的 token 开始算应用参数——两者顺序不能颠倒;
web与headlessprofile 首次使用时从模板自动初始化,其他 profile 必须通过dsh plugin创建;- 无效命令、错误配置与启动失败都会以非零码退出——退出码非零就是真的失败,不要忽略。
环境本身没搭好的先看 开发环境搭建;调试通过后按 开发规范 过自检,再进 打包成 bundle。
常见问题
**DSH plugin 本地调试按目的选通道:验证「插件能否被加载」用 --patch 覆盖层**,改完源码重启即生效,不用打包;**验证「打包产物能否安装」用 dsh plugin --profile <name> add <包>**。覆盖层只贡献配置、不改变 loader 解析模块路径的 profile 目录,所以本地插件的路径必须写绝对路径(来源:dsh CLI README)。
先查三处:**① DSH plugin 的插件路径不是绝对路径**;**② 启动时没带上 --patch**(或 patch 文件路径写错);**③ 改的是源码但跑的是另一份构建产物**。用 --dump-config 打印合成后的配置树,就能确认你的 patch 行到底有没有进树(来源:dsh CLI README)。
**看 DSH plugin 的 Fiber 状态就能定位加载失败在哪一步**:PENDING → LOADING → ACTIVE,其中任一步出问题会转入 FAILED;卸载时走 ACTIVE → UNLOADING → DISPOSED。**卡在 LOADING/FAILED 通常是依赖(inject)没就绪或 apply 抛异常**,而不是模块没找到——模块问题的表现是插件根本没进入状态机(来源:官方「插件框架」)。
**DSH plugin 的配置合成顺序是:各 bundle 的 patch(按 dsh.profile.bundles 顺序)→ profile 自己的 cordis.patch.yml → 家目录的 $DSH_HOME/cordis.patch.yml → --patch 覆盖层**。越靠后越优先,所以覆盖层能压过 profile 配置;看清这一层关系才能解释「明明改了配置却不生效」(来源:dsh CLI README)。
**确认 DSH plugin 资源清理的方法是卸载一次插件,观察两类信号**:通过 ctx 注册的东西(事件监听、工具、定时器)应当自动清理;**手动资源必须包在 ctx.effect 的处置器里**才会释放。注意处置器是逆序开始调用,但异步处置器并发执行、不保证逐个完成——有顺序依赖的清理要合并进同一个 ctx.effect(来源:官方「插件框架」)。
相关术语
- --patch 覆盖层
- --patch 是 DSH plugin 的启动参数,把一份额外的 cordis.yml 叠加到现有配置树之上,用于挂载尚未发布的本地插件;层级最高、优先于 profile 与家目录配置,但不改变 loader 解析模块路径的 profile 目录。— https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/cli/README.md
- --dump-config
- --dump-config 是 DSH plugin 的调试开关,打印合成后的配置树(含各层 patch 叠加结果)而不真正启动,是确认插件是否被挂载的最快手段;--dump-default-config 则打印默认配置树。— https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/cli/README.md
- Fiber 状态
- Fiber 是 DSH plugin 在框架中的生命周期状态机:PENDING → LOADING → ACTIVE,任一步失败转 FAILED;卸载时 ACTIVE → UNLOADING → DISPOSED。定位加载问题先看插件停在哪个状态。— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/framework/index.zh.md
- profile
- profile 是 DSH plugin 的启动单元,是一叠有序的插件 bundle patch 层加上用户自己的覆盖;目录内含 package.json(含 dsh.profile.bundles)与 cordis.patch.yml。— https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/cli/README.md
来源
- dsh CLI README(profile 分层、--dump-config、插件安装)· deepseek-ai
- DeepSeek Harness 官方文档 - 你的第一个插件(--patch 覆盖层)· deepseek-ai
- DeepSeek Harness 官方文档 - 插件框架(Fiber 状态机)· deepseek-ai
- DeepSeek Harness 官方文档 - 服务与依赖· deepseek-ai