dsh-bundle-manager:DSH 第三方插件的运行时挂载管理器

前言

DeepSeek Harness(DSH)的理念是「一切皆插件」,但按官方链路,写进 dsh.profile.bundles 的 bundle 会随进程启动加载、长期驻留。想临时停用一个第三方插件,就得改 profile manifest、再重启。同时 DSH 有一条关键架构要求:可选第三方插件只能放在 dependencies,不得写入 dsh.profile.bundles——也就是说,官方静态层并没有给这类插件提供运行时启停的位置。

dsh-bundle-manager 补的就是这一环:它经 Loader API 在进程内挂载/卸载 bundle 行,即时生效、无需重启、不改写 profile manifest,并提供命名预设与自愈式失败回退。下面逐项介绍。

这是什么

dsh-bundle-manager 由 KaramachiA217 维护,MIT 许可证,定位是 DeepSeek Harness 可选第三方插件包的运行时挂载管理器。安装后它自己作为框架 bundle 进入官方静态层,并在设置页提供「插件挂载管理」区块;被管理的第三方插件则只放在 dependencies 里,由它在运行时挂载。

两条架构约束先交代清楚:

  1. 可选第三方插件只能放在 dependencies,不得写入 dsh.profile.bundles
  2. 本插件是唯一在运行时挂载这些插件的框架 bundle。

核心功能

草稿开关与一次性应用

在设置页的本地草稿中切换插件开关,点击 Save & Refresh 一次性应用整张表:主机侧 diff 出需要创建/移除的挂载并持久化,随后硬刷新页面,使客户端半部对齐。apply 走串行队列,单次上限 30 秒。

命名预设

可以把当前草稿组合保存为命名预设:未提交的切换会合并进快照,保存新名称的预设会自动激活。之后从下拉框切换预设即可;删除支持多选,带不可逆确认,并拒绝删除 default 预设与当前活动预设。

自愈式失败回退

坏插件不会拖垮其他插件:出错的插件落入 Failed 分组(含 kind、尝试次数、错误信息),其余插件继续工作。内置 20 秒挂载看门狗;启动时按分组并行,组数由 DSH_PM_BOOT_GROUPS 控制,取值 1–8。

安全机制

framework 白名单保护核心 bundle(标记为 framework-protected);注册表原子写入并保留 .bak 最近可用副本;支持自动旧路径迁移。

与官方静态层双轨共存(v0.5)

v0.5 引入了官方静态层(dsh.profile.bundles)与 bm 运行时层之间的可逆切换:

  • import-to-bm:把静态层里的插件导入 bm,交给运行时管理;
  • export-to-bundles / export-all-to-bundles:把 bm 管理的插件导出回官方静态层,后者可作为移除 bm 前的安全网批量导出全部。

被静态层覆盖的插件在 bm 中只读显示为 superseded-by-static。导入/导出会编辑 profile manifest,是「永不改写 manifest」规则的三个显式例外,且全部走 A 级冗余:原子写入 + .bm.bak 备份 + JSON.parse 回滚、一键批量回滚、依赖组提示、框架硬保护。

注意:轨道切换(导入/导出)需要重启;bm 运行时层自身的挂载/卸载是零重启。

卸载半程(v0.5)

卸载分两步:先由 bm 注销注册表行(只清 bm 自有文件,不动 manifest),再引导执行官方 dsh plugin remove pkg...(支持批量)。先清注册表行是为了保持可逆:若官方移除失败,包降级为休眠依赖(已安装、空闲),可在设置页重新开启(重写注册表行)恢复管理。另有响应式 GC,清理绕过 bm 被外部移除的包的注册表行,并记录一条可见的 failed 条目。

围栏 API

上述能力经围栏 API /bundle-manager/api(browser-trust fence)暴露,共十个接口:listapplypreset/savepreset/switchpreset/deleteimport-to-bmexport-to-bundlesexport-all-to-bundlesimport/rollbackuninstall

安装与启用

用官方 CLI 安装,一步完成依赖添加,随后 reconcile 会把包追加到 dsh.profile.bundles

dsh plugin --profile <profile-name> add dsh-bundle-manager

安装后 CLI 生成的 profile 布局形如:

{
  "dependencies": { "dsh-bundle-manager": "^0.5.3" },
  "dsh": { "profile": { "bundles": ["dsh-base", "dsh-web-app", "dsh-settings-ui", "dsh-bundle-manager"] } }
}

升级时重新运行并加 @latest

dsh plugin --profile <profile-name> add dsh-bundle-manager@latest

运行环境要求 Node >= 20;peerDependencies 包含 dsh-settings-ui >=0.3.0@deepseek-ai/cordis >=4.0.0-rc.0@deepseek-ai/dsh-host-webserver >=0.1.0-rc.0@deepseek-ai/dsh-client-runtime >=0.1.0-rc.0@deepseek-ai/dsh-client-ui-slots >=0.1.0-rc.0

本地开发与测试

本地开发时,先用 npm pack 构建 tarball,再用官方 CLI 安装本地包(或 file: 依赖),每次改动需重新打包:

npm pack
dsh plugin --profile <profile-name> add ./dsh-bundle-manager-<ver>.tgz

已知差异:在 rc.6 上 link: 挂载会因 ESM 解析失败,请改用 file: tarball。

测试与 CI 各有一条命令:

npm test     # 离线回归,210 断言,mock ctx 驱动围栏 API
npm run ci   # 5 步门禁:语法 + 测试 + 密钥扫描 + 净化 + 打包白名单

兼容性

  • dsh 0.1.0-rc.5:已在官方桌面 shell(仅框架 bundle)验证。
  • rc.6(2026-08-17 验证):与 rc.5 同上游提交 47f9438,运行时挂载/卸载、framework 白名单、失败隔离、预设、注册表持久化与客户端半部均零代码适配通过。
  • rc.7(2026-08-19 验证):bm 唯一使用的 kit 面 settings.section 在 rc.7 上不变;kit 锁定 npm 发布版 dsh-settings-ui@0.2.22,不依赖 kit 0.3.0 的 pluginCard/settingsScope

完整中文手册见仓库的 MANUAL.md。

适用场景与注意事项

适合的用户:

  • 需要频繁启停第三方插件、不想每次改 manifest 再重启的 DSH 用户;
  • 想把不同插件组合保存为预设、按任务一键切换的用户;
  • 想先在 bm 运行时层试用静态层插件、再决定是否固化(或反向导出)的用户。

使用前注意:

  1. 轨道切换(import-to-bm / export-to-bundles / export-all-to-bundles)需要重启,且是「永不改写 manifest」的仅有的三个例外;bm 运行时层的挂载/卸载零重启。
  2. apply 为串行队列、30 秒上限;预设删除是不可逆操作,default 与当前活动预设受保护。
  3. 家族单轨说明:其他第一方家族插件(mcp / search / proxy / skill / balance 等)保持 dependencies-only 单轨,由 bm 在运行时挂载,不参与导入/导出;双轨是面向外部用户的可选增强。
  4. 插件以当前 dsh 进程权限运行,安装前应检查源码与许可证。

小结

dsh-bundle-manager 把第三方插件的启停从「改 manifest + 重启」变成设置页里的一次点击,失败隔离与双轨共存让试错成本可控。如果你的 DSH profile 里插件较多,值得一试。

  • 社区目录页(独立站点,与 DeepSeek / 幻方无官方从属关系):https://www.skillhub.cn/plugins/KaramachiA217/dsh-bundle-manager
  • GitHub 仓库:https://github.com/KaramachiA217/dsh-bundle-manager
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye