dsh-quick-restart
One-click restart for the DeepSeek Harness Web UI — restarts the whole dsh web process so freshly installed, updated, or reconfigured plugins and settings take effect immediately. The Edge/PWA window stays open and reconnects on its own.
中文说明见 README.zh.md。
Why it needs an external helper
DSH has no official "restart the process" API. The only official way to stop it is the graceful path behind ctx.appExit() (dispose the root fiber tree, close the listening socket, let Node exit normally — never process.exit(), which can trip a libuv assertion on Windows).
Restarting itself is the hard part: when DSH runs under a supervisor (e.g. Start-DSH-GUI.ps1), that supervisor puts the process into a Windows Job Object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. Everything inside that job — including plain spawn({ detached: true }) children — dies with it. So the thing that brings DSH back must live outside the job:
- the helper is created through WMI
Win32_Process.Create (its parent becomes WmiPrvSE.exe, so it escapes the job),
- the plugin verifies that parent before touching DSH: if the helper cannot be confirmed alive outside the job, the restart is aborted and DSH is left untouched.
What you get
- Sidebar entry (
sidebar.footer.action, next to Settings) plus a full page under Settings → Quick Restart.
- Explicit state machine:
idle → checking → waiting-for-tasks → (confirming-force-restart) → preparing-helper → stopping → starting → waiting-for-ready → succeeded | failed.
- Task-aware: waits for running agents / background jobs / subagents / workflows instead of killing them; on timeout it asks you before a force restart, and only then cancels work.
- Nothing dies silently: helper launch failure, port-not-released, or service-not-ready all abort with a readable reason in the panel and in the state file.
- Multi-instance safe: state, lock and log files are keyed by
port-pid; process kills are verified by PID + creation time + port ownership (never "kill every node.exe").
- Result survives a page reload: a new instance recognises its own birth record (
newInstance.pid === self) and reports "this instance was started by Quick Restart".
Install
The plugin installs as a profile bundle (the dsh.bundle.patch entry adds it to the profile's cordis.patch.yml):
// <DSH_HOME>/profiles/web/package.json
"dependencies": { "dsh-quick-restart": "link:D:/path/to/dsh-quick-restart" }
# <DSH_HOME>/profiles/web/cordis.patch.yml
- insert:
- id: dsh-quick-restart
name: dsh-quick-restart
Then restart DSH once manually (that is the bootstrapping problem this plugin solves afterwards). A page reload is enough for client-half changes — DSH re-hashes plugin bundles and serves the new revision.
Configuration
| Field | Default | Meaning |
|---|
enabled | true | Show/enable the entry |
gracefulShutdownTimeoutMs | 30000 | How long to wait for running tasks (1000–3600000) |
startupTimeoutMs | 60000 | How long to wait for the service to come back (5000–1800000) |
reconnectEnabled | true | Read the real result back after reconnecting |
requireConfirmationWhenBusy | true | Ask before restarting while tasks run |
Values are clamped/validated; unlimited waits are rejected on purpose.
Development
npm run typecheck # tsc --noEmit
npm run build # type stripping + copy client bundle & helper into lib/
npm test # 20 unit tests (state machine, locks, recipes, redaction, birth record)
npm run test:launch # helper launch regression (fake new instance, no real DSH)
npm run e2e # full restart against a throwaway instance
src/ is the single source of truth; lib/ is a committed build artifact (DSH loads lib/index.js, and git installs must work without a build step).
Limitations (by design)
- DSH has no global drain gate, so new work can still arrive while waiting for tasks.
ctx.workflowEngine has no list() — workflows are only observable through events.
- Environment variables whose names look like credentials are not forwarded across the restart (only their names are reported).
- Credential-like values must live in
$DSH_HOME/.credentials.yaml, not in the environment.
- Windows-only (Job Object / WMI /
Get-NetTCPConnection semantics).
License
MIT