dsh-plugin-update-checker
Adds a "Check updates" tab to the Built-in plugins settings: one row per installed bundle, with its latest npm version and an update button. Each row also reports DSH-runtime compatibility. The registry is read through the .npmrc chain (environment, then profile, then user, first hit wins); with not
Install
dsh plugin --profile web add dsh-plugin-update-checker
Sandbox test
This plugin has not been tested yet. Submit a test →
README
dsh-plugin-update-checker
[中文](./README.zh-CN.md) · [English](./README.md)
一个 DeepSeek Harness 插件:在 内置插件(Built-in plugins) 设置分区中增加一个 检查更新(Check updates) 按钮。点击后,它会扫描每个已安装插件的 (1) npm 上的最新发布版本 与 (2) 与当前运行 DSH 运行时的兼容性,并以每个插件一行的形式展示结果。
它与只读的插件清单页并列,本身不提供任何配置。它负责汇报,并对有可用更新的插件提供一个 更新 按钮。启用、停用、卸载插件仍然在右侧栏的 插件(Plugins) 页面或通过 dsh plugin 完成。
「更新」按钮怎么工作:由主机侧驱动 app 自己的插件管理器,一次更新是两次调用——先 removeBundle,再 installBundle(<name>@<version>)。单次调用不够:dsh 0.2.1 的管理器对「已经是 profile 依赖」的包直接拒绝 add(already-installed,在 pnpm 启动前抛出),而更新恰好就是这个形状。装完后会从磁盘读回实际版本,所以「管理器报告成功但什么都没写」会如实报成失败,而不是假的版本变更。有一个包例外:本插件不能卸载自己——卸载会把正在响应这次请求的路由一起卸掉,请用 dsh install-plugin dsh-plugin-update-checker@<version> 更新。
✨ 它做什么
检查项 · 数据来源 · 说明
更新可用性 · npm 注册表(latest tag 与已安装版本对比) · 仅针对发布到注册表的包。通过 tarball / git / 本地路径安装的包不会发布到注册表,因此显示为 不受支持(Unsupported),而绝不会错误地显示 已是最新。
兼容性 · 插件的 @deepseek-ai/dsh peerDependencies 与当前 DSH 版本对比 · 复用 @deepseek-ai/dsh-app-boot 的 evaluatePluginCompatibility——也就是启动器在启动时用的同一个判断函数。若在 compatibility.json 中授予了精确版本豁免,则显示为 兼容(已豁免)*。
兼容性与更新可用性是独立计算的——注册表慢或不可达,永远不会掩盖不兼容的 peer 依赖;反之,纯本地包仍然会得到兼容性结论。
扫描用哪个注册表:不再写死 registry.npmjs.org。主机自己读配置(环境变量 → profile 的 .npmrc → 用户 ~/.npmrc,取第一个命中的),再并发探测、取最先成功的那个。规则分三种:什么都没配 → 竞速「启动器同款镜像 + 公共注册表」;配的是公共默认值(npm 会自动往 ~/.npmrc 写 registry=https://registry.npmjs.org/)→ 保留它并把镜像挂在后面,两者皆公共、无泄露风险;配的是其它地址(公司内部源、私有源)→ 只用它一个,绝不与任何公共注册表竞速,否则会把内部包名泄露给第三方。国内网络下差别很大——实测同一台机器上公共注册表约 11s、镜像约 0.2s;竞速保证慢的那个即使排在第一也不会拖住快的。清单里显式声明了 publishConfig.registry 的包仍以该注册表为准。获胜注册表随行返回,点「更新」时沿用它——从哪读到版本,就从哪安装。
宿主半身只声明了webServer,而remote是浏览器侧服务;当前 cordis 版本里访问未声明的服务会直接抛错(cannot get property ... without inject),所以宿主不能去问插件管理器要注册表——上面这套读配置的方式正是为此设计的。
📸 位置
打开 设置 → 内置插件。该分区现在有两个标签页:
- 插件清单(Plugin list) —— 只读清单(由 DSH 自带)。
- 检查更新(Check updates) —— 本插件。展示当前 DSH 版本、检查更新 按钮,以及结果表格(插件 / 已安装版本 / 最新版本 / 兼容性 / 更新)。
检查是按需触发的:点击按钮,主机扫描,表格填充。某个清单损坏或注册表不可达,只会隔离到对应那一行——绝不会让整张报表空白。
📦 安装
方式 A —— 从 npm 安装(推荐)
dsh install-plugin dsh-plugin-update-checker
从注册表安装对这个插件尤其值得:用 github: 安装会被 profile 锁文件钉在某个
commit 上,重跑同样的 spec 不会拿到新版本,面板也只能把它报成 不受支持
(它只会比对注册表版本)。0.1.4 之后按普通版本号解析,面板自带的「更新」按钮就能用。
方式 B —— 从本 GitHub 仓库直接安装
1. 打开右侧栏 插件(Plugins) 页面 → 添加插件。
2. 在来源框里填入下面任意一种安装描述符(启动器通过 installBundle 解析,和 GUI 用的是同一个原语):
github:windrover/dsh-plugin-update-checker # 或完整 URL: https://github.com/windrover/dsh-plugin-update-checker
3. 重启以加载新 bundle:
dsh check # 铁律 #1:重启前先预检 dsh restart # 重新加载 web profile,新 bundle 随之加载
4. 打开 设置 → 内置插件 → 检查更新 → 检查更新。
检查按钮发起的 fetch('/api/plugin-update-checker/scan') 在已认证的浏览器会话内执行,无需手动 token。
本仓库是 公开 的,所以从 GitHub 直接安装无需额外认证。在registry.npmjs.org不可达的网络下,启动器会自动改用registry.npmmirror.com。
方式 B —— 本地链接(用于开发 / 改这个插件)
1. 把包 clone 到 profile 能链接的位置:
# 例如放在你的 DSH 工作区里 git clone https://github.com/windrover/dsh-plugin-update-checker dsh-plugin-update-checker
2. 链接进 profile:在 profile 清单中加入 link: 依赖与 bundles 条目:
// ~/.dsh/profiles/<你的 profile>/package.json
{
"dependencies": {
"dsh-plugin-update-checker": "link:/abs/path/to/dsh-plugin-update-checker"
},
"dsh": { "profile": { "bundles": [ "…", "dsh-plugin-update-checker" ] } }
}
然后安装。本 profile 下 npm install 会拒绝 link: 协议——改用 pnpm(profile 是 pnpm workspace,这也是 dsh 底层用的):
cd ~/.dsh/profiles/web pnpm install --registry=https://registry.npmmirror.com # link: 依赖不联网
dsh 的 wire_link_plugin_deps 会自动把插件 import 的 @deepseek-ai/* 软链进它自己的 node_modules,但不会处理普通 npm 包——所以要把那唯一一条真依赖手动预链(link 插件的已知坑:link 插件解析不到没提升到它树里的包):
mkdir -p dsh-plugin-update-checker/node_modules
ln -sfn ~/.npm/_npx/<active-dsh-hash>/node_modules/semver \
dsh-plugin-update-checker/node_modules/semver
3. 重启并打开(dsh check && dsh restart,再走方式 A 同样的设置路径)。
🔌 实现方式
一个包,两端:
- 主机(Host)(
lib/index.js→lib/host/update-checker.js)注册两条路由。POST /api/plugin-update-checker/scan从磁盘遍历 profile 的 bundles,读取每个清单、评估 peer 兼容性、探测注册表。POST /api/plugin-update-checker/update接收{ name, version, registry? },通过宿主pluginManager服务执行更新(lib/host/update.js):先试installBundle,管理器回答already-installed就removeBundle后再装一次,最后读回落盘版本。cordis.patch.yml添加 Loader 行。 - 浏览器(Browser)(
lib/client.js)向内置插件分区的settings.plugins.tab槽注册一个标签页,并用react(require("react"))和 DSH 已暴露的设计 token 渲染结果。它只与主机的两条路由通信,自己不再碰remote.pluginManager。包的id等于包name——这是dsh-client-modules强制的契约。
🧪 测试
npm test # 下面全部按顺序跑
test/client-contract.test.mjs 锁定浏览器半边的契约(id == 包名、唯一一个 settings.plugins.tab 注册、可渲染的 body 组件)。test/update.test.mjs 用假管理器锁定更新编排(先卸后装、拒绝自更新、哪种失败会中断流程)。test/update-route.test.mjs 在一次性 profile 上端到端跑路由,包括「管理器说 applied、磁盘却没动」这一种。test/layering.test.mjs 保证主机拆分保持单向依赖、ctx 只出现在入口模块。
📝 局限
- 只汇报与更新,不启停 —— 不会主动启用/停用/卸载插件;更新里的那次卸载是管理器对这一个包的两步操作。更新失败按行回报,不自动重试。
- 不能更新自己 —— 「更新」按钮会给出明确拒绝;请在终端执行
dsh install-plugin dsh-plugin-update-checker@<version>后重启。 - 只报告运行时,从不更新它 —— DSH 运行时那一行在任何载体下都没有「更新」按钮。桌面 App 里显示 由 App 更新 并指向菜单栏;CLI / web 宿主保留 有更新,并给出
npm install -g @deepseek-ai/dsh@<version>命令。刻意写明「谁来更新」:一个本面板无法兑现的按钮,看起来就是个坏掉的控件。 - 注册表可达性 —— 注册表检查需要能访问所配置注册表的网络;离线或防火墙后的主机每个包显示 未知(Unknown)。
- 本地 / git / tarball 包 —— 不发布注册表版本,其更新状态为 不受支持;兼容性仍由本地清单评估。
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::5,最后更新 2026-11-02