dsh-auto-update
A DeepSeek Harness (cordis) plugin that makes the harness update itself.
It periodically checks npm for a newer version of @deepseek-ai/dsh than the
currently installed one; when an update is found it downloads the tarball and
stages it; when the harness process exits, a detached helper process runs
npm install -g to replace the global installation with the new version; and
optionally it can relaunch dsh with the original startup arguments. By
default the web panel shows a small "Check for updates" pill in the bottom-right
corner (click to expand the full panel), which displays version status and
offers "Check for updates", "Update and restart", and "Cancel staged update".
How it works
- Install discovery — derives the global install root from
process.argv[1]
(the npm shim launches …/node_modules/@deepseek-ai/dsh/lib/bin.js); can also
be set explicitly via config.installRoot. If the harness is running from
source (pnpm dsh), auto-update is unavailable and logs a note.
- Version check — calls
npm view @deepseek-ai/dsh@<distTag> version --json,
reusing your configured registry / mirror / proxy. Includes a minimal built-in
semver comparison (handles prereleases such as 1.2.3-rc.1).
- Staging —
npm pack downloads the exact version's tarball into
$DSH_HOME/updates/ and writes pending-update.json (with from/to, install
root, process pid, whether to relaunch, etc.). This step never touches the
running installation.
- Applying — on the harness's
process.on('exit') it detaches
bin/apply-update.js: it waits for the old process to actually exit (on
Windows a running process locks native modules such as sharp/koffi/node-pty,
so installation must happen after exit) → npm install -g <tarball> → verifies
the installed version → records the result → optionally relaunches dsh <original args>.
Installation
Install the plugin in your profile directory (same as dsh-session-stats-panel):
# Local path install example (Windows / Linux / macOS)
corepack pnpm --dir "%USERPROFILE%\.dsh\profiles\web" add "C:\path\to\plugins\dsh-auto-update"
# Or Linux / macOS:
# corepack pnpm --dir "$HOME/.dsh/profiles/web" add "/path/to/plugins/dsh-auto-update"
Or, install directly from GitHub using the bundled manifest:
dsh plugin --profile web add github:a1113622001/dsh-auto-update
This plugin is distributed through its GitHub repository only and is not
published to the npm registry, so dsh plugin add dsh-auto-update (without
the github: prefix) fails with a missing-package error.
When installing manually, you must register the cordis.patch.yml
bundle at the repo root with the harness so the plugin starts with it.
dsh plugin add already carries that bundle, so no manual patching is needed —
the YAML block below is exactly the content of cordis.patch.yml.
Then append this to %USERPROFILE%\.dsh\profiles\web\cordis.patch.yml:
# dsh-auto-update: lets the harness update itself (check npm → stage → apply on exit)
- insert:
- id: auto-update
name: 'dsh-auto-update'
config:
distTag: latest
checkOnStart: true
checkIntervalMinutes: 240
autoStage: true
applyOnExit: true
relaunch: false
After saving, the running harness hot-reloads the host plugin (cordis.patch.yml
is watched); refresh the page once and the "Check for updates" pill appears in
the bottom-right corner (collapsed by default; click to expand the panel).
Configuration
Behavior details
- Safe defaults: by default it only "stages + applies on exit" — it never
kills the process mid-session. For a fully automatic loop set
relaunch to
true, or click "Update and restart" in the panel.
- Panel buttons:
/auto-update is a loopback-only HTTP channel mounted on
the harness web server; the browser calls status / check / stage /
cancel / applyRestart via POST /auto-update/<endpoint>. It is a plain
webServer prefix route, not a connection.rpc channel — on dsh ≥ 0.1.5 the
latter throws cannot get property "webServer" without inject from inside the
connection service and the route silently never mounts.
- Manual trigger:
applyRestart forces a check first, stages if needed, then
writes relaunch: true to the manifest; ~0.8s after responding it exits the
process, and the exit hook starts the apply process and relaunches.
- Crash tolerance: if the process is force-killed (no
exit event), the
staged manifest is preserved; on next start the plugin recovers it and
re-checks, then applies as usual.
- Update history:
$DSH_HOME/updates/update-history.jsonl records the outcome
of each apply; update-result.json holds the most recent result.
Verification / manual testing
# 1) Plugin syntax
node --check lib/index.js
node --check lib/updater.js
node --check bin/apply-update.js
# 2) Panel channel mounted (harness running; open the UI once to get the cookie)
curl -s -X POST http://127.0.0.1:3080/auto-update/status -H "content-type: application/json" -d '{"type":"client-request","rpcId":"t1","method":"status","payload":{}}'
# 3) Force a check
curl -s -X POST http://127.0.0.1:3080/auto-update/check -H "content-type: application/json" -d '{"type":"client-request","rpcId":"t2","method":"check","payload":{}}'
Known limitations
- Only supports harnesses installed as a global npm package (the
dsh CLI in
%APPDATA%\npm). Source runs (pnpm dsh / a dev checkout) are not updated.
- Applying happens after exit: if the process never exits gracefully (e.g. a
hard kill or power loss), the update is deferred to the next exit.
- On Windows, native modules (sharp/koffi/node-pty) can still hold file handles
open after the process exits, occasionally causing
EBUSY during
npm install -g. The helper waits until the recorded process has truly exited,
then retries with backoff, so a single lingering handle does not fail the update.
npm install -g requires write access to the npm global directory and cache
(normal user permissions are enough).
- If the plugin source directory is moved away, the
helper path recorded in a
staged manifest becomes stale; re-checking re-stages a fresh manifest.