DSH 更新检查插件

一个用于检查并安全更新 DSH (DeepSeek Harness) 的 Cordis 插件(静态 bundle 形态)。
截图 (Screenshots)
① 左下方浮动更新弹窗 —— 首次打开页面自动检查到新版本时,在左下部「设置」按钮上方弹出提示卡片,提供【更新到 {版本}】与【稍后】;点「稍后」后该版本不再重复弹出。v1.13.0 起点击更新为「安排更新」——退出 DSH 后由独立更新器自动完成安装。

② 设置 > 通用 · DSH 更新入口 —— 手动「检查更新 / 测试网络」,展示当前版本与最新版本;有新版本时提供【更新到】按钮(升级完成后重启 DSH 生效)。

更新机制(v1.13.0+,分离式两阶段更新器)
在 dsh 进程运行中直接执行 npm install -g 是危险的:Windows 会锁住已被 dsh 加载的
koffi 原生模块(koffi.node),npm 升级时包元数据更新成功、原生二进制却残留旧版,
导致 dsh 下次启动直接报 Mismatched native Koffi modules 崩溃(v1.12.0 及更早版本
的已知问题)。v1.13.0 起,更新流程改为两个阶段:
- 阶段一(DSH 进程内,即时返回):检查版本 → 把
lib/updater.cjs 模板实例化
(注入目标版本、全局根、本进程 PID 等任务配置)写入临时目录 → 以 detached
子进程启动独立更新器,UI 提示「退出 DSH 后将自动完成安装」。
- 阶段二(独立更新器,脱离 DSH 生命周期):轮询等待发起更新的 DSH 进程退出
(再加 3 秒宽限等文件锁释放)→ 执行
npm install -g @deepseek-ai/dsh@<精确版本> →
做三重校验:npm 退出码、对盘版本(磁盘 package.json 必须等于目标版本)、
koffi 完整性(koffi 主包/平台包声明版本与原生二进制内嵌版本一致)→
koffi 断链时自动按声明版本从 registry tarball(npm pack + 系统 tar)自愈 →
结果写入状态文件,DSH 下次启动时反馈(失败会弹卡片提示重试)。
另有防护网:每次「检查更新」时顺带做 koffi 完整性自检(只读文件、无副作用),
发现断链即弹「DSH 安装文件损坏」警告卡,提供【安排修复】一键走上述分离式流程
(repair 模式,仅校验并修复 koffi)。
安装(静态 bundle,推荐)
本仓库同时提供两种形态:
| 静态 bundle(推荐) | package.json + cordis.patch.yml + lib/ + client/ | 当前维护形态,dsh plugin add 后随 profile 层栈自动加载 |
动态插件(host.js + client.js) | 根目录两个文件 | 遗留,不再维护(v1.10.1 后冻结,仅作历史参考) |
dsh plugin --profile web add dsh-check-for-updates
dsh plugin --profile web add github:ShanHaiFish/dsh-check-for-updates
dsh plugin --profile web add "file:/absolute/path/to/dsh-check-for-updates"
npmjs: dsh-check-for-updates
Mac/Linux 上若路径含空格或非 ASCII(如本机 ...2026-DeepSeekHarness相关\...),建议先
复制到无空格的路径再 file: 安装。
无硬编码路径:bundle 版 host(lib/index.js)在运行时通过 require.resolve('@deepseek-ai/dsh/package.json')
与 process.execPath 探测 DSH 安装根/版本与 node/npm-cli,不写死任何本机路径,跨机器可用。
客户端:bundle 版在左下部「设置」上方浮动弹窗(首次打开自动检查 + 【更新到】/【稍后】),
通过 POST /dsh-update-check/api 调用 host。
功能特性
- ✅ 首次打开 Web 页面时自动触发一次更新检查
- ✅ 发现新版本时,在左下部"设置"按钮上方弹窗提示,并提供【更新到 {版本}】按钮
- ✅ 弹窗支持【稍后】关闭,同版本不再重复弹出
- ✅ 分离式两阶段更新:点击更新只「安排任务」,退出 DSH 后由独立进程完成安装,
从根本上避免 Windows 运行中升级导致 koffi 断链、dsh 无法启动
- ✅ 安装后三重校验:npm 退出码 / 对盘版本 / koffi 原生模块一致性
- ✅ koffi 断链自愈:自动从 registry tarball 恢复,修复失败会在下次启动时告知
- ✅ 启动自检:每次检查更新时校验 koffi 完整性,发现损坏弹警告卡一键安排修复
- ✅ 添加"测试网络"按钮用于调试
文件结构
lib/index.js - 主机端(静态 bundle):版本检查、API 路由、koffi 自检、更新任务安排
lib/updater.cjs - 分离式更新器模板(阶段二:等待退出 → 安装 → 校验/自愈 → 写状态)
lib/launcher.cjs - detached 启动辅助(参数防御性校验)
client/client.js - 客户端(静态 bundle):自动检查、更新弹窗、损坏警告卡、设置页「DSH 更新」入口
host.js / client.js - 遗留动态形态(v1.10.1 冻结,不再维护)
开发与验证(CI 自动化)
本地验证(无需安装依赖):
node --check client/client.js
node --check lib/index.js && node --check lib/updater.cjs && node --check lib/launcher.cjs
node test/smoke.cjs
node test/interactive.cjs
test/smoke.cjs - 结构冒烟:bundle 插件形态({ name, inject: ['slots'], apply })、
settings.general.item 注册、设置项组件渲染
test/interactive.cjs - 交互契约:模拟点击设置项各按钮(检查更新/测试网络/更新到),
验证与 host API(/dsh-update-check/api)契约的完整对齐,含「安排更新」语义与去重键
GitHub Actions 云端自动化:
.github/workflows/ci.yml - PR 与 push 到 main 时自动执行上述全部检查,通过才允许合并(发布前把关)
.github/workflows/release.yaml - 推送 v* 标签时自动发布:校验标签与版本一致 → 先跑测试 →
npm publish → 创建 GitHub Release。需在仓库 Settings → Secrets 配置 NPM_TOKEN
(npmjs access token,publish 权限)
发布前测试也可用 GitHub 预发布能力(不污染 npm latest):发 --prerelease Release 后,
测试者用 dsh plugin --profile web add github:ShanHaiFish/dsh-check-for-updates#<标签>
或 tarball URL 安装实测,通过后再升正式标签。
版本历史
每版发布同步归档至 GitHub Releases,
npm 发布记录见 dsh-check-for-updates on npm。
v1.13.2(2026-08-25 发布,当前版本)
自动化发布链路演练:本版本用于端到端验证 GitHub Actions 自动发布流程
(.github/workflows/release.yaml:推送 v* 标签 → 校验 tag/版本一致 → 语法与冒烟
测试 → npm publish → 自动创建 GitHub Release),无功能变更。全部产物由 CI 生成,
发布说明由 GitHub 按提交自动汇总。
v1.13.1(2026-08-25 发布)
修复(依据 bug-report-dsh-check-for-updates-settings-entry-missing.md):
- 补回「设置 > 通用 · DSH 更新」设置项入口:静态 bundle 化(
896caaa6)与
v1.13.0 重写(2ea843e)时遗漏了 settings.general.item 注册,用户无法在设置页
手动检查更新/测试网络。现于 client/client.js 导出声明 inject: ['slots'],
经 ctx.slots 注册 UpdateSettingsItem(React 组件,样式沿用 --dsw-alias-*
主题 token),完整对接 v1.13.0 host 契约:
- 挂载时只读当前版本(
get-current-version→{version}),不触发网络动作
- 【检查更新】→
check-for-updates(当前/最新版本、是否可更新、koffi 断链与
上次更新失败提示)
- 【测试网络】→
test-network(成功/失败内联反馈)
- 【更新到 {版本}】→
perform-update(v1.13.0「安排」语义:退出 DSH 后自动
完成安装),并把目标版本写入 sessionStorage,与浮动弹卡共用去重键
- 检测到 koffi 断链时同卡提供【安排修复】→
perform-repair
- 其余 v1.13.0 功能不变:自动检查、更新弹卡、koffi 警告卡、分离式两阶段更新。
v1.13.0(2026-08-24 发布)
修复与更新安全改造(依据 2026-08-24 排查报告):
- 修复启动页必现报错(R5):
client/client.js 的模块 factory 之前返回
{ __dshUpdateChecker, recheck } 普通对象,浏览器侧 cordis loader 要求插件形态
(函数或带 apply 方法的对象),导致每个用户安装后启动页必报
Failed to load plugins: invalid plugin, expect function or object with an "apply" method。
现返回 { name, apply },autoCheck() 移入 apply();调试句柄改挂
window.__dshUpdateChecker。功能本身不受影响(副作用在求值期已生效),
但报错必须修复。
- 分离式两阶段更新器(R1):更新不再在运行中的 dsh 进程内执行
npm install -g(Windows 下 koffi.node 被锁,npm 部分更新留下"元数据 3.1.6 +
二进制 3.1.5"断链,dsh 重启即崩 Mismatched native Koffi modules)。现在点击
更新只生成独立 updater 脚本并以 detached 子进程启动,等本 DSH 进程退出后
才开始安装(+3s 宽限),UI 话术改为「退出 DSH 后将自动完成安装」。
- 补齐校验(R2):旧版不检查 npm 退出码(网络失败仍报"更新成功")、README
声称的"安装后对盘校验"在代码中不存在。现在 updater 做退出码 / 对盘版本 /
koffi 完整性三重校验,koffi 断链时按主包声明版本拉官方 tarball 自动修复。
- 超时策略(R3):取消 180s 强杀(强杀 npm 是另一个损坏向量);独立更新器
给 npm 15 分钟上限,超时中止后仍执行完整性校验与自愈。
- 启动自检(R4):每次检查更新时附带 koffi 一致性自检(只读二进制查版本
字面量,无加载副作用);发现断链弹「安装文件损坏」警告卡,一键安排修复
(
perform-repair API)。上次更新失败的结果也会在下次启动时反馈。
- 版本号 semver 白名单校验、子进程一律「文件 + 字符串参数数组」(不经 shell)。
v1.12.0
修改内容:
- 首次打开自动检查更新(客户端):页面加载时自动触发一次
check-for-updates(模块级
autoStarted 标志保证每页只检查一次,不随 HMR 重复);自动检查失败不打扰用户。
- 设置按钮上方弹窗(客户端):检测到新版本且本会话未「稍后」过该版本时,在左下部「设置」
按钮上方(
shell.overlay 帧级浮动层,fixed 定位)弹出提示卡片,显示「有新版本可用」
「当前→最新版本」,并提供【更新到 {版本}】主按钮与【稍后】次级按钮。
- 点【更新到 {版本}】→ 复用 Host 的
perform-update 做精确版本安装 + 安装后对盘校验,
成功/失败均在弹窗内反馈;成功后自动关闭弹窗。
- 点【稍后】→ 该版本号记入
sessionStorage(dsh-updck-dismissed-version),同版本不再弹出;
出现更新版本则重新弹出。
- 状态订阅改多监听器(客户端):
updateState.onUpdate 单值回调改为 listeners: Set,
让「设置项」与「弹窗」两个组件同时实时刷新、互不覆盖。
- fetch 提供方 id 唯一化(主机端 v1.10.1):每次激活用唯一 id 注册
updck-curl-*,
避免与先前面动态包已注册的 updck-curl 冲突导致 Host 启动失败;若已有可用提供方则跳过注册。
- 弹窗配色沿用
--dsw-alias-* 语义化主题 token(带兜底),随亮/暗色与第三方主题自动适配。
说明:新增/改动均在既有 RPC(check-for-updates / perform-update / get-current-version /
test-network)之上完成,无需协议变更。
v1.11.0
修改内容:
- 主题适配:客户端 UI 不再使用硬编码颜色(
#007bff/#28a745/#dc3545 等),
全部改用 DSH 语义化主题 token --dsw-alias-*:
- 主按钮(更新):
var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary)),
文字 var(--dsw-alias-label-primary-foreground, var(--dsw-alias-bg-layer-1))
- 成功/有新版本:
state-success-primary;错误:state-error-primary;更新中:state-warn-primary
- 边框
border-l1/l2;表面 bg-layer-*/bg-module-platform;文字 label-primary/secondary/tertiary
- 对不确定存在的 token 使用
var(--a, fallback) 兜底,保证任意主题(含用户安装的主题插件、
亮/暗色)下与整体配色一致。
- 主机端无改动(仍 v1.10.0)。
v1.10.0
修改内容:
- 新增【更新】按钮自动执行升级(方案 B):
- 检测到新版本时,设置页出现蓝色【更新到 {版本}】按钮。
- 点击后 Host 端
perform-update 用 node + npm-cli.js(不经 cmd shell)执行
npm install -g --prefix <真实全局根> @deepseek-ai/dsh@<精确版本>。
- 显式
--prefix 从运行中的 package.json 程序化推导真实全局根,规避 .npmrc
中 prefix=/home/whaow/.npm-global 被 npm 在 Windows 误解析成不存在的 M:\... 的坑。
- 精确版本固定(用已校验的 semver 最高版本),不用
latest/next 标签,消除歧义。
- 前置守卫:仅当
compareVersions(目标, 当前) > 0 才执行;已是最新则 no-op。
- 安装后对盘校验:再次读取本地 package.json,版本必须等于目标才算成功。
- 超时保护(180s)自动
terminate;失败给出 npm 输出尾部可排查。
- 成功后提示「已更新到 X,请重启 DSH 生效」——不自动重启(进程内自杀式重启不稳定,
会终止当前会话/GUI),由用户重启加载新版本。
- 客户端新增:
updating 状态、更新按钮、更新结果信息、可展开的「执行日志」。
- 打开设置页依旧不触发任何网络/升级动作;无更新时不显示【更新】按钮。
稳定性保证:精确版本 + 显式程序化 --prefix + 前置守卫 + 安装后校验 + 超时/取消上界 + 不自动重启。
v1.9.0
修改内容:
- 只手动检查更新:打开设置页不再自动发起网络检查;挂载时仅本地读取并显示「当前版本」,只有点击「检查更新」才触发最新版本检查。
- 注册 curl/subprocess fetch 提供方(
updck-curl):本部署没有内置 fetch 提供方,web.fetch 原本永远报 no usable web provider is registered。插件用 subprocess 启动 curl.exe 抓取 npm registry,注册为 WebFetchProvider,让 web.fetch 真正可用。
- 最新版本不再取
/latest 标签:npm 的 latest 标签可能滞后(如仍指向 0.1.0-rc.7,而最新发布在 next 标签是 0.1.0-rc.8)。改为读取完整 packument https://registry.npmjs.org/@deepseek-ai/dsh 的 versions,用内置的小型 semver 比较器取实际发布的最高版本。
- 保留
web.search 作为最后兜底。
根因回顾:
tool-web 的 fetch: true 只注册模型侧 web_fetch 工具,并不注册 fetch 后端;fetch 后端必须由某插件调用 web.registerFetchProvider 注册——本部署没有,需插件自行提供。
/latest 返回的是 latest dist-tag,可能滞后于 next 上的新 rc 版本。
当前方案:
- 当前版本:
fs.resolve/readText 读取本地 @deepseek-ai/dsh/package.json
- 最新版本:curl 抓 npm packument → semver 取最高版本 → 失败降级
web.search
注意事项:
- 因为会
spawn curl(subprocess),安全审查判定为 BLOCK(120/300),需在插件安全白名单放行 updck 家族后运行。
- curl 不可用或 spawn 被拦截时,会降级
web.search(尽力而为)+ 显示错误提示。
v1.7.0
修改内容:
- 修复"当前版本: 0.0.0"问题 - 使用已验证的本地版本号
0.1.0-rc.7
- 修复"无法获取最新版本信息"问题
- 修改
C:\Users\whaow\.dsh\profiles\web\cordis.patch.yml 配置,启用 fetch: true(需要重启 dsh 生效)
- 最新版本通过
web.fetch 请求 https://registry.npmjs.org/@deepseek-ai/dsh/latest 获取
排查过程(已确认的根因):
web.fetch 被 DSH 默认配置 fetch: false 禁用(dsh-base/cordis.patch.yml)
web.search 返回的 snippet 全部为空,无法提取版本号
- 动态插件沙箱禁止
require 和 globalThis.fetch,只能使用 Cordis 服务
- 安全审查将"文件读取 + 网络访问"组合判定为数据外传路径(high),导致 ASK 审批
当前方案:
- 当前版本:写死为
0.1.0-rc.7(升级 DSH 后需手动更新此值)
- 最新版本:
web.fetch 请求 npm registry
注意事项:
- ⚠️ 需要在
cordis.patch.yml 中启用 fetch: true 并重启 DSH 后,web.fetch 才可用
- 重启前点击"测试网络"会显示 web.fetch 不可用
- 升级 DSH 后,记得更新
host.js 中的 CURRENT_VERSION 常量
v1.6.0
修改内容:
- 修复
no usable web provider is registered 错误
- 发现 DSH 配置中
fetch: false,web fetch 功能被禁用
- 改用
web.search() 搜索 npm 包版本信息
- 使用
fs 服务读取本地 package.json 获取当前版本
- 从搜索结果中提取版本号
根本原因:
- DSH 默认配置
fetch: false,禁用了 web fetch 功能
- 动态插件无法使用
require 或 globalThis.fetch
- 必须使用 Cordis 服务(
ctx.web, ctx.fs)
解决方案:
- 当前版本:使用
fs.resolve() + fs.readText() 读取本地 @deepseek-ai/dsh/package.json
- 最新版本:使用
web.search() 搜索 "@deepseek-ai/dsh npm version" 并提取版本号
注意事项:
- 搜索结果可能不准确,版本号提取依赖正则匹配
- 如果需要更准确的版本检测,需要在 DSH 配置中启用
fetch: true
v1.4.0
修改内容:
- 修复
no usable web provider is registered 错误
- 移除对
web 和 shell 服务的硬依赖(inject)
- 改用
globalThis.fetch 直接发起网络请求(Node.js 18+)
- 简化代码结构,移除复杂的 provider 注册逻辑
- 所有网络请求统一使用
fetchUrl() 封装函数
v1.3.0
修改内容:
- 修复
getCurrentVersion() 读取本地实际安装版本的 bug
- 注入
shell 服务,通过 node -e 命令读取本地 @deepseek-ai/dsh/package.json 的版本
getLatestVersion() 继续从 npm registry 获取最新版本
- 添加备用方案:本地读取失败时回退到 npm registry dist-tags
- 修复版本比较逻辑(之前 current 和 latest 都从 npm 获取,永远相同)
v1.2.0
修改内容:
- 移除
fs 依赖,简化代码
- 当前版本和最新版本都从 npm registry 获取
- 增强错误处理和日志输出
- 添加测试网络按钮用于调试
v1.1.0
修改内容:
- 增强
web.fetch 错误处理,添加详细日志
- 添加获取本地 DSH 版本功能(使用
ctx.fs)
- 添加备用方案:从 npm registry 获取当前版本
- 添加测试网络按钮用于调试
- 改进版本比较逻辑
v1.0.0
初始版本:
使用方法
⚠️ 以下为遗留动态形态(host.js/client.js,v1.10.1 冻结)的使用说明,仅作历史参考。
当前推荐使用上文的静态 bundle 安装方式,功能以 bundle 版为准。
安装插件
- 将
host.js 和 client.js 代码复制到 DSH 动态插件定义中
- 使用
cordis_define 定义插件
- 使用
cordis_run 运行插件
验证功能
- 打开 DSH Web 界面
- 进入 设置 > 通用
- 找到 DSH 更新 设置项
- 点击 测试网络 按钮验证网络连接
- 点击 检查更新 按钮验证功能
- 查看控制台日志获取详细信息
技术细节
⚠️ 本节描述遗留动态形态的接口;静态 bundle 版 host 通过 POST /dsh-update-check/api
(JSON {method})提供同名方法:check-for-updates / get-current-version / test-network /
perform-update(v1.13.0 起返回"已安排"而非同步安装)/ perform-repair(v1.13.0 新增)。
主机端 (host.js)
- 注入
web 服务
- 使用
web.fetch 从 npm registry 获取版本信息
- 注册以下 RPC 方法:
check-for-updates - 检查更新
get-current-version - 获取当前版本
test-network - 测试网络连接
客户端 (client.js)
- 注册设置项到
settings.general.item 槽位
- 使用
host.call 调用主机端方法
- 注册弹窗到
shell.overlay 槽位
- 添加"测试网络"按钮用于调试
WebFetchRequest 格式
{
url: 'https://registry.npmjs.org/@deepseek-ai/dsh/latest'
}
注意:不需要 method 和 headers 参数
WebFetchResult 格式
{
url: string,
statusCode: number,
body: {
kind: 'html' | 'text',
content: string
},
truncated: boolean
}
调试方法
1. 查看控制台日志
插件会在控制台输出详细的调试信息:
web.fetch 的返回结果
- 版本获取过程
- 错误信息和堆栈
2. 使用"测试网络"按钮
点击"测试网络"按钮可以:
- 验证
web.fetch 是否正常工作
- 检查 npm registry 是否可访问
- 查看返回的数据格式
3. 检查错误信息
如果出现错误,插件会显示:
- 用户友好的错误提示
- 详细的控制台日志
- 错误堆栈信息
后续优化
- 使用 semver 库:进行语义化版本比较
- 添加缓存机制:避免频繁请求 npm registry
- 定时检查:实现每天自动检查一次更新
- 改进错误处理:显示更友好的错误信息
- 添加更新日志:显示新版本的更新内容
许可证
MIT License