dsh-update-checkerDeepSeek Harness plugin
DeepSeek Harness 主程序与插件更新管理:npm/GitHub 双源 semver 比对、多语言横幅、一键更新(主程序自动备份/校验/回滚,插件临时目录安装)、更新后看门狗重启。Update management for DeepSeek Harness and its plugins: dual-source semver checks, locale banner, one-click updates with backup/rollback, watchdog restart.
- Stars
- 13
- Forks
- 3
- License
- MIT
- Last commit
- Sep 3, 2026
- Latest release
- v1.4.20
Overview
DeepSeek Harness 主程序与插件更新管理:npm/GitHub 双源 semver 比对、多语言横幅、一键更新(主程序自动备份/校验/回滚,插件临时目录安装)、更新后看门狗重启。Update management for DeepSeek Harness and its plugins: dual-source semver checks, locale banner, one-click updates with backup/rollback, watchdog restart.
Original README
Cached from the project repository on Sep 3, 2026. This is source content, separate from the Agents.md review above.
dsh-update-checker
English | 中文
A permanent Cordis plugin for the DeepSeek Harness Web GUI that auto-checks for new DeepSeek Harness releases and installed third-party plugin updates (the former standalone dsh-plugin-checker was merged in v1.1.0), asks the user, and one-click updates with success/failure feedback.
Features
- Full update lifecycle — check, backup, update, rollback, and restart, all in one plugin.
- Main program check — compares the installed
@deepseek-ai/dshagainst the npm latest (full packument, stable-first, semver-aware — pre-release builds likealpha/beta/rcare skipped unless you enable theallowPrereleasesetting, so the checker never auto-promotes the harness into an unintended pre-release channel). - Third-party plugin check — scans installed non-official plugins (layout-agnostic, incl. pnpm-hoisted
node_modules), cross-compares each against npm + GitHub (target = higher version); local tools with no publish source go toignored. When a plugin name has multiple copies, the one in the composition-owning profile'snode_moduleswins (the rest are listed ascopies), and each plugin can be excluded from prompts (excludedPlugins, re-enableable in the settings page). - Working GitHub channel — dedicated HTTPS client for GitHub domains (tolerates self-signed local proxies; the npm registry still uses strict TLS), with redirects, size caps and timeouts; codeload tarballs are validated before install.
- In-GUI banner — locale-aware (zh/en follows the DSH UI language), states update / up-to-date / failure, with a suppression flag and a change brief (vX→vY + risk level + release notes when available).
- One-click update with safety — main program: dry-run guard (abort if the plan contains
remove) → snapshot backup (version manifests + amain-snapshotcopy of the@deepseek-aitree for offline rollback) → layout-adaptive install (in-place or-g) → post-install checkinstalled==latest; plugins: temp-dir install + copy, dependency version reconciliation, auto--allow-scriptsfor native deps on npm ≥ 12. Updates (and rollbacks) persist to the profilepackage.json+ lockfile (pnpm install --lockfile-only/npm install --package-lock-only), so a later install never silently reverts the plugin — no more "same plugin keeps asking for the same update" loops. - Real rollback — main program via
POST /rollback, plugins viaPOST /plugin-rollback;GET /backups.jsonlists both. - Restart with watchdog — launcher derived from the current process argv, kill by PID + port, recovery confirmed by port listening + an HTTP 200 probe (
GET /restart-status.json). - Write-route security — all write routes require
{ "confirm": true }and a loopback source (127.0.0.1/::1), so LAN clients can't trigger update/restart/rollback. - Zero-config portability — profile dir /
$DSH_HOME/ composition file / deploy root are all derived from the plugin's own install location; works on any machine without editing code.
Host & Client
- Host (
lib/index.js) — HTTP routes:status.json(check),suppress,update(withdrypreview),rollback,backups.json,restart,restart-status.json,plugins.json,plugin-update,plugin-rollback,plugin-exclude. - Client (
lib/client.js) — renders two banners in the rootshell.overlayslot: a core banner (main-program update state) and a plugin banner (updatable plugins with single / update-all buttons). Both check on page load, then every 6 hours; the settings page ("检查更新") adds rollback buttons.
Install & mount
The package is a profile bundle (its manifest declares dsh.bundle.patch).
bash# 1) put the package into $DSH_HOME/profiles/node_modules/ so the profile can resolve it. # ⚠️ Never run `npm install` directly inside $DSH_HOME/profiles — it has no # package.json and npm would prune the whole node_modules (data loss). # Safe option A — install in a temp dir, then copy only this package: npm i dsh-update-checker --prefix <temp-dir> --no-save cp -r <temp-dir>/node_modules/dsh-update-checker $DSH_HOME/profiles/node_modules/ # Safe option B — copy the package directory manually (git clone or tarball). # 2) add the row to $DSH_HOME/profiles/web/cordis.patch.yml
yaml# $DSH_HOME/profiles/web/cordis.patch.yml - insert: - id: dsh-update-checker name: 'dsh-update-checker'
Then let patch HMR apply it (or restart dsh web) and reload the page.
Step-by-step guide with troubleshooting (中文): docs/INSTALL.md.
Configuration & portability
All paths are auto-detected at runtime — nothing is hardcoded:
- Plugin / profile dir — derived from the plugin's own install location (
import.meta.url). $DSH_HOME— the parent of theprofilesroot (state, backups, restart log live there).- Composition file — defaults to
$DSH_HOME/profiles/web/cordis.patch.yml. - Deployment root — junction
realpathfirst, thenDSH_DEPLOY_ROOT, thenprocess.cwd(), then the npm global prefix (parent ofnpm root -g's output; v1.4.9+ coversnpm -ginstalls).- systemd /
npm -gescape hatch: if auto-detection ever misses your setup, setDSH_DEPLOY_ROOTto the directory that containsnode_modules/@deepseek-ai/dsh(e.g.<npm prefix>/libon Linux).
- systemd /
- Node / npm executables —
resolveNodeExe()finds the real Node:DSH_UC_NODE_EXEoverride →npm_node_execpath→process.execPathwhen it is Node → common install dirs →PATH. This is what makes DSH Desktop (Electron, whereprocess.execPathiselectron.exe) able to run npm for plugin updates. If your Desktop build bundles Node elsewhere, setDSH_UC_NODE_EXEto it. - Restart launcher — self-adapting: probes common launcher names under the deployment root; the web port is read from the running
webServer.port.
Platform & install-layout support
- Detection (checks) — layout-agnostic, works on any machine.
- One-click update & restart — tuned for the layout they were developed on:
- Windows only — the restart flow spawns PowerShell.
- Main-program update adapts: in-place
npm installwhen the deploy root has apackage.json,npm install -gotherwise; both run the dry-run guard and re-read the installed version afterwards. - Plugin updates — temp-dir install + copy, npm 11/12+ compatible.
- Other platforms/layouts: banners and version checks still work, but the update/restart buttons need code adaptation. Linux/macOS support is the natural next step.
Notes
- Host code changes require a service restart (the loader caches imported modules); client changes are picked up by HMR and apply on the next page refresh.
- Update/rollback/restart/suppress/settings routes are guarded by
{ "confirm": true }and a loopback-source check (127.0.0.1/::1). - Before
npm install, a backup (deploymentpackage.json+package-lock.json+ both @deepseek-ai version manifests +backup-meta.json+ amain-snapshotcopy of the@deepseek-aiframework tree) is written to$DSH_HOME/dsh-update-checker-backups/<timestamp>/; both main-program and plugin rollback routes are provided, and main-program rollback restores from themain-snapshotwhen present instead of re-installing from the registry.
Changelog
-
v1.4.20 — Main-program update robustness: stale-lockfile reify fix + frontend-dist verify via realpath (issue #14):
- Stale-lockfile reify fix: when the target version is already declared in
package-lock.json/node_modules/.package-lock.json(a leftover from a failed or partial update) but the physically installed@deepseek-aitree is still older, npm's reify trusted the lockfile and skipped re-installing, so the update ended inE_VERSION: update did not reach <target> (installed=<old>)and rolled back — a perpetual "fake update" loop. The worker now detects this mismatch (lockfile-declared version ≠ physical version, and physical ≠ target) and deletes both stale lockfiles before installing, forcing npm to re-resolve and really re-install the target version. - Frontend-dist verify via realpath (#14): the post-install integrity check reads
dsh-web-frontend/dist/index.html; it now resolves that directory throughrealpath(following junction / pnpm-hoisted install layouts) so a legitimately installed frontend is not mis-flagged, and if it still cannot be read it reports the exact path tried instead of a bare "unreadable" — previously the update rolled back withintegrity check failed: dsh-web-frontend dist/index.html unreadable.
- Stale-lockfile reify fix: when the target version is already declared in
-
v1.4.19 — Follow pre-release builds when no stable release exists + clear npx-cache-layout warning (issue #14):
- No-stable fallback:
pickMainLatestnow returns the highest published version (including pre-releases) when there are no stable@deepseek-ai/dshreleases, instead of returningnulland failing the status check with "no stable version; enable allowPrerelease". The main framework currently ships onlyrc/alphabuilds, so the stable-only default made the checker pointless. - Stable-first kept when a stable exists: if any stable release is present, the checker still prefers the highest stable and only follows pre-releases when
allowPrereleaseis on (preserving the v1.4.17 incident guard). - npx-cache-layout warning (#14): when the resolved deploy root is an npm
npxcache path (.../_npx/...), the status check reports a clear note and the main-framework update route refuses withE_NPX_CACHE, pointing to the official local install ornpm i -g @deepseek-ai/dsh, instead of silently installing to the wrong place and failing the post-install integrity check.
- No-stable fallback:
-
v1.4.18 — Monorepo-subpackage false updates (#13) + dark-mode primary-button contrast (#11, from PR #12):
- Monorepo subpackage detection (#13):
parseGhReporeturnsnullwhen the npmrepositorycarriesdirectory(e.g.packages/dsh-weknora), so a monorepo subpackage is checked against npm only. Previously the repo root's latest release tag (e.g.v0.7.2) was treated as the update target and the update then failed on the missing rootpackage.json(ENOENT). - Fail-safe GitHub provenance (#13):
fetchGhPkgNamenow distinguishes "confirmed the repo root has nopackage.json" (HTTP 404 →hasRootPkg:false, treated as not belonging) from a transient/unknown error (hasRootPkg:null, still trusted), so a real repo isn't wrongly suppressed by a network blip. - Homebrew npm-cli layout (#13):
getNpmCligained the../libexec/lib/node_modules/npm/bin/npm-cli.jscandidate, so macOS Homebrew Node ≥ 22 resolves npm and plugin updates work on the npm channel. - GitHub→npm fallback on a missing root package.json (#13):
isGhFallbackablenow acceptsENOENT/ENOPKG, so a GitHub tarball with no rootpackage.jsonfalls back to the npm channel with a clear error instead of hard-failing. - Dark-mode primary buttons (#11, picking up PR #12):
.dsh-update-btn-primary/.dsh-plugin-btn-primaryuse--dsw-alias-button-primary-fill+--dsw-alias-label-primary-foregroundinstead of--dsw-alias-brand-primary+color:#fff, so dark mode is no longer white-on-white.
- Monorepo subpackage detection (#13):
Development
lib/index.js— Host half: plain ESM, Node built-ins only, no build step; pure helpers exported as named ESM exports for unit testing.lib/client.js— Client half: plain JS (window.__ModuleLoader__), requires onlyreact, no build step.- Tests:
npm test(Node ≥ 20 built-in test runner, no third-party deps). scripts/restart-service.ps1— manual restart helper (run with-ExecutionPolicy Bypass).
License
MIT