🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

源码更新提醒 lmr233/dsh-git-update-notifier

DeepSeek Harnessv0.2.9
lmr23336 天前有提交已提供中文文档扫描:含敏感能力

每天首次启动 dsh 时用 git 检测上游提交并在 Web GUI 中询问是否更新适配源码 checkout,不改…

31.5/100
综合分
2
GitHub 星标
33
npm 周下载
—/5
用户评分

数据截至 2026/9/30 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-git-update-notifier
npm 源 · dsh-git-update-notifier 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么[目录]—
dsh 原生插件 · dev
暂无时间戳·判据 →
装得上吗[实装验证]—
曾在 dsh 0.2.1-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.2-rc.1,本站尚未在新版下复验
L5 · 运行时冒烟等级说明 →
数据截至 2026/10/28·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]—
更新放缓:最近一次提交在 36 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-git-update-notifier @ 0.2.9
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/12 11:37:02

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 55 处,展示前 46 条)
  • 读取本地文件
    lib/index.js:123
    const entries = readdirSync(dir)
  • 读取本地文件
    lib/index.js:129
    at = statSync(full).mtimeMs
  • 删除本地文件
    lib/index.js:138
    unlinkSync(stale.full)
  • 写入本地文件
    lib/index.js:164
    mkdirSync(dir, { recursive: true })
  • 写入本地文件
    lib/index.js:192
    writeFileSync(file, lines.join('\n'), 'utf8')
  • 读取本地文件
    lib/index.js:207
    return clip(readFileSync(target, 'utf8'), 256 * 1024)
  • 读取本地文件
    lib/index.js:248
    downloaded = statSync(part).size
  • 读取本地文件
    lib/index.js:255
    const parsed = JSON.parse(readFileSync(meta, 'utf8'))
  • 读取本地文件
    lib/index.js:266
    const parsed = JSON.parse(readFileSync(stateFile(), 'utf8'))
  • 写入本地文件
    lib/index.js:278
    mkdirSync(dirname(file), { recursive: true })
  • 写入本地文件
    lib/index.js:279
    writeFileSync(file, `${JSON.stringify(next, null, 2)}\n`, 'utf8')
  • 执行外部命令来自配置/变量
    lib/index.js:470
    child = spawn(command, args, {
  • 写入本地文件
    lib/index.js:1081
    writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8')
  • 删除本地文件
    lib/index.js:1245
    rmSync(file, { force: true })
  • 删除本地文件
    lib/index.js:1329
    rmSync(staged.file, { force: true })
  • 写入本地文件
    lib/index.js:1454
    writeFileSync(file, `${lines.join('\n')}\n`, 'utf8')
  • 删除本地文件
    lib/index.js:2225
    rmSync(file, { force: true })
  • 发起网络请求
    lib/client.js:91
    return fetch(ROUTE + '/status.json', { cache: 'no-store' })
  • 发起网络请求
    lib/client.js:97
    return fetch(ROUTE + path, { method: 'POST', headers: { 'content-type': 'application/json' } })
  • 发起网络请求
    lib/client.js:403
    fetch(ROUTE + '/progress.json', { cache: 'no-store' })
  • 发起网络请求
    lib/client.js:467
    fetch(ROUTE + '/rollback/targets.json', { cache: 'no-store' })
  • 发起网络请求
    lib/client.js:526
    fetch(ROUTE + '/diagnostics.json', { cache: 'no-store' })
  • 发起网络请求
    lib/registry.js:58
    const req = transport.request({
  • 发起网络请求
    lib/registry.js:84
    const connectReq = http.request({
  • 发起网络请求
    lib/registry.js:100
    const req = transport.request({
  • 读取本地文件
    lib/download.js:38
    const parsed = JSON.parse(readFileSync(metaPath, 'utf8'))
  • 写入本地文件
    lib/download.js:48
    writeFileSync(metaPath, `${JSON.stringify(meta)}\n`, 'utf8')
  • 读取本地文件
    lib/download.js:56
    return statSync(file).size
  • 发起网络请求
    lib/download.js:79
    const req = transport.request({
  • 发起网络请求
    lib/download.js:109
    const connectReq = http.request({
  • 发起网络请求
    lib/download.js:124
    const req = transport.request({
  • 写入本地文件
    lib/download.js:179
    mkdirSync(dirname(file), { recursive: true })
  • 删除本地文件
    lib/download.js:216
    rmSync(part, { force: true })
  • 删除本地文件
    lib/download.js:217
    rmSync(meta, { force: true })
  • 写入本地文件
    lib/download.js:234
    renameSync(part, file)
  • 删除本地文件
    lib/download.js:235
    rmSync(meta, { force: true })
  • 删除本地文件
    lib/download.js:239
    rmSync(part, { force: true })
  • 删除本地文件
    lib/download.js:240
    rmSync(meta, { force: true })
  • 写入本地文件
    lib/download.js:254
    const stream = createWriteStream(part, { flags: start > 0 ? 'a' : 'w' })
  • 写入本地文件
    lib/download.js:341
    renameSync(part, file)
  • 删除本地文件
    lib/download.js:342
    rmSync(meta, { force: true })
  • 读取本地文件
    lib/verify.js:40
    const stream = createReadStream(file)
  • 读取本地文件
    lib/verify.js:70
    const source = createReadStream(gzipFile)
  • 读取本地文件
    lib/selfcheck.js:50
    const parsed = JSON.parse(readFileSync(join(selfPackageDir(), 'package.json'), 'utf8'))
  • 读取本地文件
    lib/selfcheck.js:71
    return statSync(join(dir, '.git')) !== undefined
  • 读取本地文件
    lib/selfcheck.js:110
    const manifest = JSON.parse(readFileSync(join(profileRoot, 'package.json'), 'utf8'))
扫描于 2026/11/4(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 每日首次启动检测上游提交
  • WebGUI询问是否更新
  • 适配源码checkout
v0.2.82026-09-11
· v0.2.8 补上一个明显的盲区:插件一直在检测 dsh 本体,却检测不了自己。 · 新增 · 插件自身的更新检测:先判定「自己是怎么被装进来的」(git 检出 / github: 依赖 / npm 依赖 / 手工放进 node_modules 的副本),再据此决定去哪儿问版本 —— 而 GitHub 始终兜底,所以手工副本也能检测到新版。设置页显示「插件自身已是最新」或「插件自身有新版:X → Y(来源)」 · POST /plugin/update:按来源更新插件自己 —— git 检出走 fetch + 快进合并,npm / github 依赖走 npm install;手工副本明确拒绝并给出手动做法(不知道这份副本从哪来时,不动它) · DSH_GIT_UPDATE_NOTIFIER_GITHUB_API:指向镜像或本地 mock(测试据此离线验证) · 自身检查失败只记日志,绝不影响 dsh 本体的结论 · 变更 · HTTP 路由由 11 条增至 12 条(新增 POST /plugin/update) · npm test 增至 10 个脚本(新增 test/selfcheck.mjs) 完整变更:CHANGELOG.md 发布归档:docs/releases/v0.2.8.md 宿主端有改动:升级后需要重启 dsh web 才生效。
v0.2.72026-09-11
· v0.2.7 让「更新失败」这件事可检修:失败时留下完整现场,并且事后还找得回来。 · 新增 · 失败诊断日志:resolve / download / verify / install / fetch / merge 任一阶段失败,都把完整现场写进 $DSH_HOME/dsh-git-update-notifier-logs/update--.log —— 含插件版本、安装形态、包位置 / 仓库根 / 安装位置、本地与目标版本、代理、运行时、执行的命令、退出码、完整 stdout 与 stderr,以及(合并失败时)git status --short。只保留最近 20 份 · GET /diagnostics.json:取回最近一次失败的完整现场(进程内优先,磁盘兜底;只允许读日志目录内的文件) · 错误信息带上退出码:失败消息改为「失败(退出码 N):…」,命令与诊断文件路径写进状态,刷新页面后仍可见 · 设置页「查看完整报错」:任何阶段的失败都多出一行现场摘要与一个按钮,点开在页面内摊开完整输出直接复制(懒加载,不点不请求) · 变更 · HTTP 路由由 10 条增至 11 条(新增 GET /diagnostics.json) · 卸载说明补上日志目录 完整变更:CHANGELOG.md 发布归档:docs/releases/v0.2.7.md 宿主端有改动:升级后需要重启 dsh web 才生效。
v0.2.62026-09-11
· v0.2.6 补上更新流程最后一段的「出事了怎么办」:安装失败可以只重试安装,也可以回退到更新前。 · 新增 · 安装失败后的重试:把「准备安装包(下载 + 校验)」与「安装」拆成两步,安装失败时保留已校验的包与失败阶段标记。新增 POST /update/retry —— 只重做安装,不重新下载、不重新解析 registry;源码形态复用已抓取的 FETCH_HEAD 重做快进合并 · 安装失败后的回退:失败时把「更新前」那一刻的回退点正式记入状态(此前只在更新成功后才记录),因此失败后 POST /rollback 立刻可用 · 失败阶段可见:lastUpdate 增加 phase / canRetry / canRollback / attempt / needsFreshDownload;设置页给出「重试安装」与「回退到更新前」两个按钮,并写明这是第几次尝试 · 重试前重新校验:每次安装前重算哈希,确保装的就是当初校验通过的那份字节;本地包已被改动则丢弃并要求重新下载 · 变更 · HTTP 路由由 9 条增至 10 条(新增 POST /update/retry) · 源码形态的快进合并失败,提示改为「重试或回退」,而不是笼统的「请手动处理后重试」 完整变更:CHANGELOG.md 发布归档:docs/releases/v0.2.6.md 宿主端有改动:升级后需要重启 dsh web 才生效。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
dsh-git-update-notifier CI npm License: MIT powered by dsh 仓库: · MIT License dshV0.2.0已内置更新检查,此仓库不在更新,仅做纪念鉴于目前dsh更新频率高,但在目前的插件中并没有针对dsh更新推送的插件,所以制作了此插件用来检查并更新dsh,这是本人第一个作品,纯ai,如有意见可提出 每天本地时间 24 时检查一次 dsh 本体有没有更新(那时 dsh 没开着的话,下次启动时补检);有的话在 Web GUI 右下角弹一张卡片,由你决定是立即更新、延期,还是稍后再说。更新本身支持断点续传(中断了就从断点接着下)、安装前校验(sha512 / sha1 / 包内身份三层)、安装失败后的重试与回退,以及失败诊断(完整现场落盘,可展开复制)。它也检测自己有没有新版 —— 见插件自身的更新。当前版本 0.2.8。 为什么用 git,而不是查 npm 社区里已有多个“检查 dsh 更新”的插件(dsh-update-checker、dsh-auto-update 等),它们清一色对比 npm registry 上的 @deepseek-ai/dsh。
展开完整文档(README 剩余 1 节)
dsh-git-update-notifier

CI
npm
License: MIT
powered by dsh

仓库: · MIT License

dshV0.2.0已内置更新检查,此仓库不在更新,仅做纪念鉴于目前dsh更新频率高,但在目前的插件中并没有针对dsh更新推送的插件,所以制作了此插件用来检查并更新dsh,这是本人第一个作品,纯ai,如有意见可提出

每天本地时间 24 时检查一次 dsh 本体有没有更新(那时 dsh 没开着的话,下次启动时补检);有的话在 Web GUI 右下角弹一张卡片,由你决定是立即更新、延期,还是稍后再说。更新本身支持断点续传(中断了就从断点接着下)、安装前校验(sha512 / sha1 / 包内身份三层)、安装失败后的重试与回退,以及失败诊断(完整现场落盘,可展开复制)。它也检测自己有没有新版 —— 见插件自身的更新。当前版本 0.2.8。

为什么用 git,而不是查 npm

社区里已有多个“检查 dsh 更新”的插件(dsh-update-checker、dsh-auto-update 等),它们清一色对比 npm registry 上的 @deepseek-ai/dsh。这对本机不适用:

~/.dsh/profiles/node_modules/@deepseek-ai/dsh
→ Junction → \apps\cli # 只是指向源码 checkout 的链接

本机 dsh 本体跑的是源码 checkout(git remote 指向 deepseek-ai/deepseek-harness),所以:

- 查 npm 会误报——本地往往是 tag 之后又多走了若干提交的构建,npm 上并没有对应版本;
- “更新”若是执行 npm install,会往源码仓库里装包,破坏 pnpm workspace 的链接结构。

所以对源码形态,插件只做一件事:把这个 checkout 的提交推进到 origin/,全程不碰 npm、不碰 node_modules。

而对于 npx @deepseek-ai/dsh web 或 npm 安装出来的形态(拿到的是预构建包、没有 .git),则改用 npm registry 比对版本 —— 见支持的安装形态。

行为

| 时机 | 动作 |
| 每日本地 24 时 | 到点自动检查一次;若那时 dsh 没开着,启动后补检当天遗漏的一次。同一自然日内只检查一次 |
| 发现可更新 | 在 shell.overlay(整帧最上层,点击穿透)渲染询问卡片 |
| 点「立即更新」 | 先下载到本地(可断点续传)并校验,再安装:源码形态执行 git fetch + git merge --ff-only(只允许快进,绝不生成意外 merge commit);npx / npm 形态下载目标版本的 tarball、校验 integrity / shasum / 包内身份后,安装这份校验过的本地包 |
| 更新中被打断 | 下载进度落在 .part.json 里 —— 网络断开、主动「中断下载」、甚至 dsh 重启,下次都从断点继续;「中断并丢弃断点」才从头再来 |
| 安装阶段失败 | 保留已校验的包并记下“更新前”的回退点,设置页给出「重试安装」(复用本地包,不重新下载)与「回退到更新前」两个按钮,并标出这是第几次尝试 |
| 任一阶段失败 | 把完整现场写进 $DSH_HOME/dsh-git-update-notifier-logs/:命令、退出码、完整 stdout / stderr、形态与路径、(合并失败时)git status --short。设置页给出诊断文件路径与「查看完整报错」,点开即可复制去检修 |
| 插件自己有新版 | 设置页显示「插件自身有新版:X → Y(来源)」,并给出「更新插件」;更新的是宿主端代码,所以同样要重启 dsh web |
| 点「延期…」 | 选择 1 天 / 3 天 / 1 周 / 2 周 / 1 个月;到期前不再弹浮层卡片,设置页仍可查看与手动更新(上限一个月,超出按上限处理);延期期间可随时「取消延期」恢复提醒 |
| 点「稍后」 | 当天不再询问;次日 24 时(或次日启动时的补检)重新检查 |
| 检查失败(如断网) | 弹一张低调的失败卡片(带原因与「重新检查」),而不是静默无反应 |

卡片的展示内容随形态变化:源码形态显示分支、本地版本 → 上游版本(两端都取自各自的 package.json,上游那份从 FETCH_HEAD 读取;读不到时回退到提交号)、落后提交数与上游提交摘要;npx / npm 形态显示发布通道与 本地版本 → registry 版本。两种形态都会显示安装方式与包位置。

设置页也有一个入口

除浮层卡片外,插件还在设置 → 更新注册了一个常驻区块(settings.section 席位):

- 状态框:当前状态(有可用更新 / 已是最新 / 检查失败)、安装方式、分支与版本号(源码形态为「当前版本 → 上游版本」)或发布通道与版本(npx / npm 形态)、上次与下次检查时间、包位置;
- 常驻结果行:用一句人话写明最近一次检测的结论,例如「已是最新(0.1.5-rc.2 @ c291e7961)」或「发现新版本 0.1.5-rc.1 → 0.1.5-rc.2(上游领先 2 个提交)」。因此自动检测遇到「已是最新」时虽然不弹卡片,打开设置仍然看得见结论;
- 「手动检测更新」按钮:随时触发一次检查,不受「每天一次」限制;点击后按钮变「检测中…」,完成后在区块内写明结果(成功或失败),不会"点了没反应";
- 延期按钮(常驻):无论当前有没有更新都能点,可选 1 天 / 3 天 / 1 周 / 2 周 / 1 个月 —— 即使现在已是最新,也可以提前把未来一段时间的提醒压掉。期间不再弹浮层卡片(设置页显示「已延期至」),并可随时「取消延期」恢复提醒;
- 检测到可更新时,额外出现「立即更新」按钮。

浮层卡片只在"需要你决定"时冒出来,设置页区块则随时可查 —— 两者读的是同一份状态。

两半自检

插件的宿主半与客户端半生效条件不同:宿主半改动需要重启 dsh web,客户端半刷新页面即可。
两半各带一个 CODE_VERSION 常量(测试强制它们保持一致),版本对不上时设置页会直接说明原因:

插件两半版本不一致:宿主端(旧版,无版本标记),客户端 0.2.0。
宿主端的改动需要重启 dsh web 才生效(仅刷新页面不够)。

更新失败时的回退

更新是不可逆的写操作,因此做了三层保障:

1. 更新前记录回退点 —— 源码形态记提交号,npx / npm 形态记版本号;
2. 更新成功后,在 $DSH_HOME 落一个不依赖 dsh 运行的回退脚本(dsh-rollback.cmd / dsh-rollback.sh)—— dsh 若起不来,插件自己也跑不起来,所以回退入口必须落在 dsh 之外;
3. dsh 能正常启动时,设置页显示「上次更新:X → Y」,并提供常驻的「回退…」入口:点开后自选目标 —— 源码形态列出上游最近的若干版本号(逐个取各提交自己的 package.json),
npx / npm 形态列出已发布的历史版本 ——
选中后按钮写明「确认回退到 X」再执行;不选目标时回退到上面记录的更新前那一刻。
同时给出上面那个脚本的路径。
4. 安装失败也能回退:回退点不再只在更新成功后记录 —— 安装阶段(npm install / 快进合并)失败时同样把它正式记下来,所以那一刻就能直接「回退到更新前」,而不必等到"更新成功之后"。
失败现场还会保留已校验的包,供「重试安装」复用。

插件自身的更新

除了 dsh 本体,插件也检测自己有没有新版 —— 否则它就是个"只盯着别人的更新、自己却停在旧版"的工具。

难点在于它是怎么被装进来的:可能是 git 检出,可能是 profile 里声明的 github: 或 npm 依赖,也可能只是被手工复制进 node_modules 的一份副本。所以先判形态,再决定去哪儿问版本:

| 自身形态 | 判定依据 | 远端来源 | 「更新插件」的动作 |
| git 检出 | 自己所在目录(或上溯)有 .git | git 上游 | git fetch + merge --ff-only |
| npm 依赖 | 宿主清单里声明为本包(semver / ^ / ~) | npm registry | pnpm add / npm install(按宿主目录的包管理器) |
| github 依赖 | 宿主清单里声明为 github: / git URL | GitHub | 同上,spec 为 github:#v |
| 手工副本 | 目录在,但宿主清单里没有声明 | 只用 GitHub 兜底 | 不自动更新,给出手动做法 |

第三列的动作会先判定宿主目录归谁管:dsh 的 profile 是 pnpm 管理的,在那里跑 npm install 会打乱它的 node_modules 与 lockfile —— 所以有 pnpm-workspace.yaml / pnpm-lock.yaml 就走 pnpm add,否则才用 npm install。

关键在最后一行:手工副本没有任何声明可查,GitHub 的 release / tag 就成了唯一来源;而既然不知道这份副本从哪来,插件就不做自动替换,只如实说明并给出建议(例如 dsh plugin add github:lmr233/dsh-git-update-notifier)。

设置页会显示「插件自身已是最新(X)」或「插件自身有新版:X → Y(来源)」。这条检查与 dsh 本体的检查互不影响 —— 它失败只记一条日志。

支持的安装形态

插件会识别 dsh 的实际部署形态,据此选择检测源与更新动作:

| 形态 | 判定依据 | 检测源 | 更新动作 |
| 源码 checkout | 包位于含 .git 的仓库内 | git 上游提交 | git pull --ff-only |
| npx 缓存 | 路径含 _npx(npx @deepseek-ai/dsh web 的产物) | npm registry | 在缓存目录 npm install |
| npm 安装 | 其它 node_modules 安装 | npm registry | npm install(全局加 -g) |

为什么必须分开:源码形态比对 git 提交才是正确语义(查 npm 会误报,npm install 还会破坏 pnpm workspace 的链接结构);而 npx / npm 安装拿到的是预构建发布包、没有 .git,只能也应该用 registry 版本比对。

形态优先通过 process.argv[1](真正在跑的那份 bin 入口)识别 —— 它对源码启动、npx 启动、全局安装都成立;识别不到时才回退到 profile 里的 @deepseek-ai/dsh 链接。

npx 缓存的更新是原地的:缓存目录自带 package.json,在其中 npm install @deepseek-ai/dsh@ 之后,下次 npx @deepseek-ai/dsh web 会复用同一缓存目录并跑到新版本。

发布通道
dsh 处于开发者预览阶段,registry 上的 latest 常常不是最新 —— 实测有一段时间 latest = 0.1.5-rc.1 而 next 已是 0.1.5-rc.2。默认跟随 latest,需要跟进时可切换通道:

DSH_GIT_UPDATE_NOTIFIER_CHANNEL=next # latest | next | alpha

registry 镜像

网络受限时用镜像源(也能让测试指向本地 mock 以离线运行):

DSH_GIT_UPDATE_NOTIFIER_REGISTRY=https://registry.npmmirror.com

代理

Windows 上 git 不读取系统代理设置,所以开了代理的机器往往依然连不上 GitHub。本插件不修改你的任何 git 配置,而是自己探测代理,只在自身的 git 调用里以 -c http.proxy=... 生效:

探测顺序:

1. DSH_GIT_UPDATE_NOTIFIER_PROXY —— 显式指定;设成 none / off / direct 可彻底关闭代理探测
2. 标准环境变量 HTTPS_PROXY / HTTP_PROXY / ALL_PROXY
3. Windows 系统代理(Internet 选项注册表键)

细节:

- 每次检查都重新探测,所以你中途开关代理、换端口都能被立刻感知,无需重配置。
- 先试代理,失败自动回退直连(同一候选内还会退避重试以吸收网络抖动)。
- 系统代理可能是 127.0.0.1:7890,也可能是 http=a:1;https=b:2 的分列形式;后者优先挑 https,socks= 会映射成 socks5h://(DNS 也走代理)。
- 探测到代理时会在日志里打印一行,便于确认走的是哪条路。

安装

本包是一个组合包(bundle):package.json 里声明了 dsh.bundle.patch,profile 列出它时会应用 cordis.patch.yml 这一层。

从 npm 安装(推荐)

dsh plugin --profile web add dsh-git-update-notifier

从 registry 安装不只是省事:插件的自身更新检测会因为能查到 npm 上的版本而更可靠 —— GitHub 的匿名 API 每小时只有 60 次额度,共享出口 IP(校园网 / 公司网 / 运营商 NAT)很容易被耗尽,届时「插件市场」和别处的更新检测都会失败。

从 GitHub 安装

dsh plugin --profile web add github:lmr233/dsh-git-update-notifier

从本地目录安装

dsh plugin --profile web add

本包是纯手写 JavaScript、没有构建步骤,所以 git 安装拿到源码即可直接运行:不需要 TypeScript 插件那样的 prepare 构建,也不需要 pnpm 的 allowBuilds 授权。

装完重启 dsh web 并刷新页面。

手动安装

把整个包目录复制进 %USERPROFILE%\.dsh\profiles\web\node_modules\,然后往 %USERPROFILE%\.dsh\profiles\web\cordis.patch.yml 追加:

- insert:
- id: dsh-git-update-notifier
name: 'dsh-git-update-notifier'

不要直接在 ~/.dsh/profiles 下跑 npm install——那个目录没有 package.json,npm 会剪掉整个 node_modules。

改动宿主端代码后必须重启

宿主端的模块会被 Node 的 ESM 缓存持有,卸载再重新挂载插件并不会重新 import 模块(实测:把 insert 块删掉再加回来,插件确实卸载又加载了,但跑的还是旧代码)。所以 lib/index.js 的任何改动都需要重启 dsh web 才生效。客户端半(lib/client.js)不受此限,刷新页面即可。

验证配置层

dsh --profile web --dump-config # 应能看到 dsh-git-update-notifier 这一层

配置

全部通过环境变量覆盖,无需改代码:

| 变量 | 作用 |
| DSH_GIT_UPDATE_NOTIFIER_PROXY | 指定代理;none/off/direct 表示关闭探测 |
| DSH_GIT_UPDATE_NOTIFIER_ROOT | 直接指定 @deepseek-ai/dsh 包目录(独占:设置后不再自动探测其它候选) |
| DSH_GIT_UPDATE_NOTIFIER_CHANNEL | npm registry 的发布通道,默认 latest |
| DSH_GIT_UPDATE_NOTIFIER_REGISTRY | registry 基址,默认 https://registry.npmjs.org |
| DSH_GIT_UPDATE_NOTIFIER_GITHUB_API | GitHub API 基址,默认 https://api.github.com(指向镜像或本地 mock;插件自身的更新检测用它) |
| DSH_HOME | 决定状态文件位置(默认 ~/.dsh) |

checkout 是怎么找到的:读取 $DSH_HOME/profiles/node_modules/@deepseek-ai/dsh,对其 realpath(解析 junction / 符号链接),再逐级上溯到第一个含 .git 的目录。因此没有任何硬编码路径,换机器也能自动适配。找不到时才需要 DSH_GIT_UPDATE_NOTIFIER_ROOT。

状态文件

$DSH_HOME/dsh-git-update-notifier.json:

{
"status": "update-available",
"branch": "master",json
"localHead": "aa8262ec091698bae9a6b04773a6b5b06ad4aef2",
"remoteHead": "c291e7961a515f6d7af9304e7fd1d257929aef26",
"behind": 134,
"subjects": ["c291e7961a ..."],
"proxy": "http://127.0.0.1:7890",
"viaProxy": true,
"lastCheckDate": "2026-09-10",
"dismissed": false
}

删掉它即可让插件在下次启动时重新检查。

HTTP 路由(宿主端)

| 路由 | 方法 | 说明 |
| /dsh-git-update-notifier/status.json | GET | 当前检查快照 |
| /dsh-git-update-notifier/check | POST | 强制重新检查(无视“今天已检查过”) |
| /dsh-git-update-notifier/update | POST | 下载 + 校验 + 安装(源码形态为 git fetch + 快进合并) |
| /dsh-git-update-notifier/progress.json | GET | 更新在途进度与磁盘上的下载断点(供设置页轮询) |
| /dsh-git-update-notifier/download/cancel | POST | 中断下载:默认保留断点,?discard=1 连断点一起丢弃 |
| /dsh-git-update-notifier/dismiss | POST | 当天不再询问 |
| /dsh-git-update-notifier/snooze?days=N | POST | 延期 N 天(1–30,超出按上限;days=0 取消延期) |
| /dsh-git-update-notifier/rollback | POST | 回退:带 ?target= 回退到指定目标,不带则回退到更新前记录的点 |
| /dsh-git-update-notifier/plugin/update | POST | 更新插件自己:git 检出走快进合并,npm / github 依赖走 npm install;手工副本明确拒绝 |
| /dsh-git-update-notifier/diagnostics.json | GET | 最近一次失败的完整现场(命令、退出码、完整输出),或磁盘上诊断文件的内容 |
| /dsh-git-update-notifier/update/retry | POST | 安装失败后只重试安装:复用已校验的本地包(源码形态复用 FETCH_HEAD),不重新下载 |
| /dsh-git-update-notifier/rollback/targets.json | GET | 列出可选回退目标(源码形态逐个读取提交自己的版本号展示,npx / npm 形态取历史版本) |

上面所有写路由都要求来源是 loopback(127.0.0.1 / ::1),局域网里的其它客户端拿不到触发 git 操作的能力。

已知限制

- 校验依赖 registry 提供凭据。npm 官方的 dist 一定带 integrity(或至少 shasum);如果某个镜像两者都不给,插件会拒绝安装并说明原因,而不是“假装校验通过”。
- 断点只对同一版本、同一 URL 有效。目标版本变了(或 tarball 地址变了)就重新下载 —— 拼不同版本的字节没有意义。
- 更新后需要重新构建并重启才生效。git pull 只推进源码;dsh 运行的是构建产物,拉取后需自行 pnpm build:lib(或对应构建命令)并重启 dsh web。卡片在更新成功后会明确提示这一点。
- 只做快进合并。本地有未提交改动或分支已分叉时,快进合并会失败并在卡片上显示原因,不会尝试自动解决。
- 进程常驻不重启时按日定时触发。触发点是电脑本地时间每天 24 时(启动时补检当天遗漏的那次),不是“每次启动”;可用设置页的「手动检测更新」或直接 POST /check 随时触发。
- 需要 web profile:宿主端 inject: ['webServer'],在没有 web 服务的 profile 里会保持 PENDING。
- 代理自动探测目前覆盖 Windows(注册表)与环境变量;Linux 走环境变量,macOS 的系统代理(scutil --proxy)尚未覆盖。

卸载 sh
dsh plugin --profile web remove dsh-git-update-notifier

再删掉 $DSH_HOME/dsh-git-update-notifier.json、下载缓存目录 $DSH_HOME/dsh-git-update-notifier-downloads/(里面是已校验过的更新包与可能残留的 .part 断点),以及诊断日志目录 $DSH_HOME/dsh-git-update-notifier-logs/。

开发与验证 sh
npm test # 跑完所有离线测试(CI 里用的就是这个)
npm run check # 只做语法检查
npm run test:live # 真实上游:经系统代理抓取真实 GitHub(依赖网络,不进 CI)

也可以单独跑某一个:
node test/proxy-parse.mjs # 代理取值归一化(纯函数,不联网)
node test/download.mjs # 下载器:断点续传的几条现实路径(本地 http server 扮演 tarball 端点)
node test/verify.mjs # 包校验:篡改、身份不符、缺凭据(手写最小 UStar tar 构造用例)
node test/selfcheck.mjs # 插件自身:形态判定(普通 / pnpm / github: 声明)与 GitHub 查询回退
node test/client-render.mjs # 客户端:模块协议登记、导出形态、卡片各状态与按钮请求
node test/local-check.mjs # 宿主端:路由注册与 loopback 网关
node test/e2e-local-repo.mjs # 端到端:本地 bare 仓库验证检测→更新→失败时字段清空
test/download.mjs 同样不依赖外网:本地 http server 可以按指令“发一半就掐断”、“忽略 Range”、“谎报区间”、“慢慢发以便中途取消”,于是续传的每条分支都被真实走了一遍。

test/verify.mjs 手写最小 UStar tar + gzip 造出合法的 npm tarball,再精确地篡改一个字节、改掉版本号、抽掉校验凭据,验证每种坏包都会被拦下。

test/e2e-local-repo.mjs 不依赖外网:它自己造一个 bare 仓库与两个 clone,让 local 落后 upstream 一个提交,然后用假 ctx 驱动真实的 apply(),断言 behind=1、/update 真的推进了 HEAD、当天第二次启动只跳过不重复检查,以及抓取失败时成功态字段(behind/subjects/remoteHead)会被清空。

目录结构

dsh-git-update-notifier/
├── package.json # dsh.bundle.patch + dsh.client 声明 ├── cordis.patch.yml # 插入本插件行的组合层 ├── lib/ │ ├── index.js # 宿主端:代理探测、形态识别、检测、状态持久化、更新/回退/重试、诊断日志、HTTP 路由 │ ├── download.js # 带 Range 断点续传的下载器(跨进程断点、可取消) │ ├── verify.js # 更新包校验(sha512 / sha1 / 包内身份)与最小 tar 读取 │ ├── selfcheck.js # 插件自身的定位、安装形态判定与 GitHub 版本查询 │ ├── registry.js # registry 查询(dist-tags、单版本 manifest) │ ├── semver.js # 预发布排序的最小实现 │ └── client.js # 客户端:手写 bundle,shell.overlay 询问卡片 └── test/ # 可独立运行的验证脚本

两半都是纯手写 JavaScript,没有构建步骤;客户端只 require('react')(由外壳的模块表提供)。

版本与路线图

- 当前版本:0.2.9(发布归档:v0.2.9、v0.2.8、v0.2.7、v0.2.6、v0.2.5、v0.2.0、v0.1.0)
- 后续计划:ROADMAP.md
- 变更记录:CHANGELOG.md
预览分享卡片长什么样 ↓lmr233/dsh-git-update-notifier
分享 · DeepSeek Harness Hub
lmr233/dsh-git-update-notifier

每天首次启动 dsh 时用 git 检测上游提交并在 Web GUI 中询问是否更新适配源码 checkout,不改…

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部