dsh-bundle-dedup-guard
已验证dsh-bundle-dedup-guard · v0.1.0 · MIT
DSH plugin guard: checks profile bundle lists for duplicate loader entries on every plugin load, preventing the 'duplicate loader entry id' boot crash caused by listing an aggregate bundle and its sub-plugins together — and the `dsh plugin` reconcile recu
安装
dsh plugin add dsh-bundle-dedup-guard 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
标签
作者
说明文档
dsh-bundle-dedup-guard
DeepSeek Harness 插件守卫:每次装载插件时检查 profile 的 bundles 列表是否存在重复 loader 条目。
当 profile 的 dsh.profile.bundles 同时列出聚合包(其 patch 一次性插入全部子插件条目,
如 @linxin666/dsh-web-ui-all)和它的子插件时,loader 会对同一 loader 条目 id
收到两次 insert。EntryGroup.update 在任何插件条目创建之前就抛
duplicate loader entry id: <id>,整个 profile 启动失败。本插件让这类问题不再无声发生。
为什么存在
2026-08-18 事故:web profile 同时列出了 @linxin666/dsh-web-ui-all(聚合 13 个子插件
为一个 patch)和全部 13 个子插件。每个子插件 id 被 insert 两次,第一个被撞上的是
duplicate loader entry id: ui-dsh-aionui-panel。只修 bundles 列表还不够——
dsh plugin 的 reconcilePlugins 会在每次 pnpm 操作后把 dependencies 里所有声明了
dsh.bundle 的包按依赖顺序追加回 bundles,一小时后子插件被重新写回,下次启动又崩。
完整事故记录:docs/KNOWN-ISSUE-bundle-duplicate.md。
工作原理
loader 的故障链路是:cordis-plugin-include 的 applyEntryPatches 平铺累加各 bundle
的 insert 条目不去重,随后 cordis-plugin-loader 的 EntryGroup.update 按 id 去重、
碰到第一个重复就抛错——此时任何插件条目都还没创建。本插件用纯 Node 复刻这套
「平铺累加 + 按 id 去重」语义,报告违规 id、来源(哪个 bundle/patch 插入的)与修复指引。
每次装载插件都检查
| 时机 | 触发 | 说明 |
|---|---|---|
| 启动装载 | 插件 apply | 每次启动体检一次 |
| loader 事件 | loader/entry-init / loader/partial-dispose |
运行时热装载/新增插件,800ms 防抖复查 |
| 清单文件变化 | fs.watch 监听 profile 目录 |
dsh plugin add、插件市场安装、手改 package.json / cordis.patch.yml 的瞬间立刻警告,无需等重启 |
报告内容
- 重复 loader 条目 id——每个被多个来源插入的 id 及完整来源链(如
ui-dsh-aionui-panel: @linxin666/dsh-web-ui-all ← @linxin666/dsh-client-ui-aionui-panel)。 - 未解析 bundle——列在 bundles 里但解析不到(loader 同样会 loud fail)。
- 无 patch 的包——列了但没有
dsh.bundle.patch段(按 loader 契约属配置错误)。 - reconcile 预测警告——
dependencies里声明了dsh.bundle但不在 bundles 列表的包。dsh plugin的 reconcile 会在下次装/更新插件时把它追加进 bundles;若它是聚合包已覆盖的 子插件,将重新造成崩溃。警告会点名已被覆盖的 id。处理:把这些包移入devDependencies(reconcile 只读dependencies)。
报告落盘 $DSH_HOME/dsh-bundle-dedup-guard/reports/<profile>-<时间戳>.json 与
<profile>.latest.json。
已知限制
loader 在创建任何插件条目之前就去重,所以「启动即崩溃」的场景下进程内检查无法运行 (插件树根本没装载)。此时请用下面的独立 CLI:纯读盘,崩溃状态下也能跑。
安装
作为 profile bundle(开发期推荐):
- 在 profile 的
package.jsondependencies加:"dsh-bundle-dedup-guard": "link:F:/path/to/dsh-bundle-dedup-guard" dsh.profile.bundles加"dsh-bundle-dedup-guard"(放最前面即可)。- 在 profile 的
node_modules建立链接(dsh plugin add会自动做)。
从 npm:
dsh plugin --profile web add dsh-bundle-dedup-guard
用法
插件自动检查,无需交互。手动诊断(含启动已崩溃时):
# 检查全部 profile(DSH_HOME 缺省 ~/.dsh)
node bin/check.mjs
# 指定 profile
node bin/check.mjs --profile web
# 指定清单文件(如修复前的备份,测试用)
node bin/check.mjs --manifest <path-to-package.json>
# 机器可读 JSON、不落盘报告
node bin/check.mjs --profile web --json --no-write
退出码:0 = 健康;1 = 发现重复 / 未解析 bundle / 无 patch bundle(可做 CI 门禁)。
修复重复
编辑 dsh.profile.bundles,每个 id 只保留一个来源。最常见的形态是「聚合包 + 子包」:
- 保留聚合包(如
@linxin666/dsh-web-ui-all) - 删除单独列出的子包条目
- 同时把子插件从
dependencies移入devDependencies——否则dsh plugin的 reconcile 会在下次装/更新插件时把它们重新追加回 bundles(2026-08-18 复发的根因)
然后重跑 node bin/check.mjs --profile <name> 直到绿灯,再重启。
开发
npm test # node --test,零依赖
npm run check # 对本机 profile 运行守卫
lib/check.mjs— 检查核心(纯 Node,无第三方依赖)index.mjs— Cordis 插件入口(apply + 监听器)bin/check.mjs— 独立 CLI(无需装载插件树)test/— 带 fixture profile 的单元测试
License
MIT