dsh-hot-reload

devtested: passnpmMIT

Reloads an upgraded plugin inside the running dsh process instead of restarting it, and rolls back to the old version when a reload fails.

下载量 / Downloads
842
依赖体积 / node_modules
0.27 MB
测试时间 / Tested
2026-11-07
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-hot-reload

Sandbox test results

StageResult
1 install✓
2 list✓
3 mount✓
4 boot—

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

dsh-hot-reload

English | [中文](README-zh.md)

Live-reload upgraded DeepSeek Harness (dsh) plugins without restarting dsh.

dsh's built-in hot-reload (cordis-plugin-hmr) deliberately ignores

node_modules, so upgrading an installed plugin (dsh plugin add pkg@x)

normally requires a full dsh restart to take effect. This plugin closes that

gap: it watches your profile's pnpm-lock.yaml, and when an already-loaded

plugin package's version changes, it swaps the running plugin in place.

Behavior

On a plugin package upgrade, for each affected plugin:

  • Reload it live — invalidate the module caches, re-import the new code, and

re-instantiate the plugin fiber in place. dsh, your sessions, and every other

plugin keep running.

  • If the reload fails — you're left with the working old version, never a

dead plugin, plus a logged note that a manual dsh restart is needed to

pick up the new code. Two cases, both handled:

  • a failure while loading the new code (bad import, syntax error) is caught

before the running plugin is touched — the old plugin is never disturbed;

  • a failure while initializing it (the new apply throws, sync or async)

is rolled back — the old version is re-instantiated in place.

A version that failed is not retried automatically — retrying would tear

down the working plugin again on every later lockfile write. Install a

different version, or restart dsh, to pick the new code up.

Two cases produce no reload, by design:

  • Disabled plugin rows are skipped silently. A disabled plugin isn't

running, so there is nothing to swap — and re-enabling it makes dsh load the

new code anyway.

  • **A plugin that has no live fiber *yet*** (still importing, or it failed to

load earlier) is reported as no live fiber to reload right now and left

alone. Nothing was torn down, so this one is re-examined on every later

lockfile change, and it reloads by itself as soon as a running copy appears.

You are told once per version, not once per check — so if you still see

the same version reported after the plugin has had time to start, restart dsh.

Upgrading dsh-hot-reload itself

dsh-hot-reload can hot-reload itself too. When its own package is

upgraded, the running instance imports the new module, commits its own version

and persists it, closes its watcher, and then swaps its own fiber in place. The

new instance re-reads the state file and opens its own watcher — the old watcher

is closed before the new one opens, so there is never more than one live watcher.

To make that handoff — and a plain restart — safe, the plugin keeps its tracked

state in a file: profileDir/.dsh-hot-reload-state.json. It holds the committed

versions, the never-retried failedVersions, and the once-announced

noticedVersions. The file is written atomically — a .tmp file is written

and then renamed over the target, so a crash mid-write never leaves a truncated

file — and read back at startup, so a fresh instance inherits what the previous

one committed instead of treating already-loaded plugins as new and re-reloading

them.

This state file is required. If it cannot be written at startup, the plugin

refuses to start (it throws) rather than running with in-memory-only state that

a later self-reload or restart would lose.

It never restarts dsh for you — restarting is left to you (and your

supervisor, if any).

How you see what happened

The plugin writes every result to dsh's log. But dsh does not print its log to

your terminal, so those lines are easy to miss. Two extra places show you what

happened.

1. One line in your terminal, for every outcome. You get this in

every profile:

dsh-hot-reload: hot-reloaded some-plugin@1.2.0 (1 module(s))

2. A short pop-up message in the dsh web app. You get one when a reload

works. You also get one in every case where the new code did not load, so the

old code is still running:

  • the reload failed, and the old version was put back
  • the plugin has no running copy to swap out
  • the plugin turned off hot reload with dsh.hotReload: false
  • dsh did not provide the internal parts the reload needs

The second case is announced once per version, not once per check. That case

is retried on every later lockfile write — including writes for other packages —

so without this limit you would get the same pop-up over and over, with no way

to dismiss it. The other three are announced each time they happen.

The message slides in, stays a few seconds, then fades out. If one upgrade

reloads several plugins, the messages line up and show one after another.

The web part only loads in a profile that runs a web server. It sends the

messages over GET /dsh-hot-reload/events. A profile with no web server, such

as tui, still gets the terminal line and the log.

Messages are not saved. If no browser tab is open when a reload happens, that

message is gone. The log still has the record.

If you restart dsh with a tab open, the tab opens a new channel by itself and

pop-ups keep working. It tries for about three minutes. If it still cannot

connect after that, it writes one line to the browser console and stops trying;

reload the page to start again.

If you want every line in your terminal

The terminal line above covers every reload outcome — reloaded, failed, and

stale (not attempted — the old code is still running). To see everything else this plugin writes to the log (its warnings and

diagnostics), add dsh's console logger to your profile. It is a separate

package:

dsh plugin --profile web add @deepseek-ai/cordis-plugin-logger-console

Then add a row for it in that profile's cordis.patch.yml and restart dsh:

- insert:
    - id: logger-console
      name: '@deepseek-ai/cordis-plugin-logger-console'

This prints all dsh log lines, not only this plugin's.

**Note for full-screen

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::10,最后更新 2026-08-20

Links

GitHub · Homepage · npm

Same category