dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
One-click restart for DeepSeek Harness: a web button (plus a dsh_restart agent tool) hands the relaunch to a detached helper, the page reconnects by itself, and a recovery console shows the boot errors when the new host fails
English | 中文
A restart button for DeepSeek Harness. Installing or updating a plugin changes
host-side code, and only a fresh dsh web process picks it up — that used to
mean going back to a terminal. Now it is one click in the Web GUI: the page
reconnects on its own, and if the new process fails to boot, the error is
shown right there (an in-page overlay, plus a recovery console on its own port
that stays up even when DSH is dead).
An agent cannot restart DSH on its own. The dsh_restart tool only files a
request; it takes your click on 「批准并重启」 in the panel to actually run (see
"Human approval gate" below).
A self-restart is the moment things go wrong invisibly: the tab loses its
server, the new process crashes, and the only trace is in a terminal you already
left. dsh-restart handles all of that — and, now that agents can call tools,
one more: an agent restarting the host and telling you afterwards, which is
why a restart request waits for your click by default.
dsh_restart,
nothing is restarted. It files a request
(~/.dsh/dsh-restart/pending-approval.json, valid for 15 minutes by default),
which shows up in three places at once:
com.dsh.web, KeepAlive), the helper delegates with
launchctl kickstart -k and only observes (spawning our own host would race
the job for the port); otherwise it relaunches the exact same command
(argv / cwd / env / Node flags) after waiting for the port to be actually
released, instead of guessing with a fixed delay.http://127.0.0.1:3099 by default, CORS-open) keeps answering after DSH is
gone — showing phase, boot log, error lines and exit code, with a one-click
retry./api/dsh-restart/probe and reloads itself
as soon as the new host answers (autoReload, on by default).maxAttempts (default 2) automatic retries run before giving up.dsh web binds its port before the
plugin tree loads, so the helper only reports ready once the port answers,
the process is still alive, and the boot output carries no fatal line — held
over readyConfirmMs (4s) first, then watched for bootWatchMs (30s) so a
host that reports ready and dies seconds later is reclassified as a failed
restart instead of a silent success.dsh_restart_status (read-only): host pid/port/version/uptime/command,
helper phase and failure, recent restarts, the boot log's error lines, and
whether a restart request is currently waiting for you;dsh_restart: still requires confirm: true, but that flag is only the
caller describing itself — it proves nothing and unlocks nothing. With the
gate on, the tool files a request and tells the agent to send you to the
panel.dsh web boot mints a new launch token, so an
old tab's URL is refused with 401 once its cookie is gone. The page then swaps
the current process's token for a cookie in place (same authority as the
tab, no navigation) and reloads only after the tab really authenticates — a
restart ends with the app back on screen, not with an address to copy. If even
that fails it falls back to the clickable 「用新 token 地址打开」 link, so a
dead cookie can never turn a reload into the host's plain-text 401 page.GET /api/dsh-restart/goto always answers 303 with a
relative Location carrying the current process's token, so the one URL that
never expires is also the one that works on any authority.ERR_CONNECTION_REFUSED, where the browser has replaced the page and
no client-side recovery can run.~/.dsh/dsh-restart/history.json (a user-approved restart is
recorded as source: "user-approved"), logs under
~/.dsh/dsh-restart/logs/, config at ~/.dsh/dsh-restart.json (0600).Install the plugin (npm for releases, link: for local development, or from
GitHub):
dsh plugin --profile web add @zhengjunyao/dsh-restart # npm
dsh plugin --profile web add link:/path/to/dsh-restart # local development
dsh plugin --profile web add github:zhengjy01/dsh-restart
Expected: the command succeeds and @zhengjunyao/dsh-restart appears in
~/.dsh/profiles/web/package.json dependencies.
Restart DSH once so the host half loads — the last manual restart you need; from then on, restart from the panel.
Expected: the page reconnects and reloads itself; a restart icon appears in the left sidebar and a 「重启」 card in the settings page.
Screenshot slot 1 — what it looks like after install Capture: the settings card at 「设置 → 插件配置 → Web 插件 → 重启」, expanded (host pid, version, launch command, restart history, collapsed settings). Range: the whole card, ~680px wide. No redaction needed (it holds a local port and pid, no token). Suggested file:
docs/images/dsh-restart-01-panel.pngAfter adding it, replace this block with:
Screenshot slot 2 — sidebar entry in its shared row Capture: the sidebar's bottom row with this restart icon beside the other plugin entries (hovering shows the title 「重启 DSH」). Range: just that row, not the whole window. No redaction needed. Suggested file:
docs/images/dsh-restart-02-sidebar.pngAfter adding it:
Error: …
plus the log tail, with buttons to copy the error, ask the helper to retry, or
open the recovery console.dsh_restart → nothing restarts; it only files
a request, and its reply tells you to go confirm it.approvalTtlMinutes (15 by default) the
request expires.Screenshot slot 3 — the approval card (the star of this feature) Capture: the red-bordered card 「有一个会话请求重启 DSH(尚未执行)」 showing the reason, the requester, the countdown, and both buttons. Range: the card itself, including the countdown badge in its top-right corner. Redaction: mask the current user name or personal paths if they appear in the reason or requester. Suggested file:
docs/images/dsh-restart-03-approval-card.pngAfter adding it:
Screenshot slot 4 — sidebar badge and desktop notification Capture (a pair is fine): ① the red dot on the sidebar restart icon; ② the macOS notification 「DSH:有一个重启请求,等你确认」 in Notification Center. Redaction: mask any home path / user name inside the notification text. Suggested file:
docs/images/dsh-restart-04-badge-and-notification.pngAfter adding it:
Screenshot slot 5 — recovery console when a boot fails Capture: the in-page overlay with
Error: …and the log tail, or thehttp://127.0.0.1:3099recovery console. Redaction: mask absolute paths and the user name in the log. Suggested file:docs/images/dsh-restart-05-recovery-console.pngAfter adding it:
~/.dsh/dsh-restart.json (0600; seeded from the plugin row on first load, and
editable from the panel):
| Key | Default | Meaning |
|---|---|---|
enabled | true | Master switch (off = no routes, no tools) |
announceToAgent | true | Announce the plugin's tools and behaviour in the system prompt |
entry | sidebar | Entry point: sidebar / ball / both / off |
requireApproval | true | Must an agent-requested restart wait for your click? Turning it off restores the old "confirm and go" behaviour (not recommended — it removes the gate) |
approvalTtlMinutes | 15 | How long a pending request stays actionable (1–120; it expires after that) |
notifyOnApproval | true | Post a macOS desktop notification when a request is filed (darwin only) |
restartMode | auto | auto (delegate when launchd manages the host) / launchd (force; errors when no job) / helper (force self-relaunch) |
fallbackPort | 3099 | Recovery-console port (tries +1…+9 when busy) |
bootTimeoutMs | 120000 | How long a boot may take before the attempt fails |
maxAttempts | 2 | Launch attempts per restart request |
killGraceMs | 6000 | Grace period before the helper SIGKILLs the old host |
portFreeTimeoutMs | 25000 | How long to wait for the old host to release its port |
lingerMs | 4000 | How long the helper keeps its console up after readiness |
readyConfirmMs | 4000 | Stability window after the port answers before "ready" (0 = skip) |
bootWatchMs | 30000 | Window to reclassify a reported-ready-then-dead boot as a failure (0 = skip) |
logLines | 200 | Lines of boot log kept for display |
autoReload | true | Reload the page once the new host answers |
showOverlay | true | Show the full-screen overlay while restarting |
probeIntervalMs | 1200 | Reconnect probe interval |
historyLimit | 30 | Restart records kept |
All loopback-only (127.0.0.1 / ::1) and same-origin, matching the other dsh-*
panels — everything below lives under /api/dsh-restart/:
| Method | Path | Meaning |
|---|---|---|
| GET | /status | Host + helper + config + history + the pending request |
| GET | /probe | Tiny liveness probe (polled hard while reconnecting) |
| GET | /auth | This process's fresh token URL (deliberately cookie-free, still loopback-only) |
| GET | /goto | Bookmarkable entry: 303 to the current token (relative, so cookies stay on this authority) |
| POST | /restart | Hand over the restart; replies 202 before this process exits (the panel button uses this) |
| GET | /approval | The pending agent request + its deadline + the gate switch |
| POST | /approval | Your decision: { id, decision: "approve" | "deny" }. Approve consumes the request first, then hands over |
| GET | /logs | Boot-log tail + the lines that look like errors |
| GET | /history | Restart records |
| POST | /config | Patch the config / reset: true |
| GET | /helper | Live helper state, read through the host |
| POST | /helper/retry | Ask a failed helper to try again |
you click 「立即重启」 (a human click is the consent)
│ POST /api/dsh-restart/restart
▼
host (old process) ──writes pending-spec.json──▶ detached helper (zero deps)
│ replies 202, SIGTERMs itself ~0.7s later │
▼ │ waits for the port
process exits ─────────────────────────────────────────────┤
▼
spawns the new host with the same command
(stdout/stderr → logs/<stamp>-<pid>.log)
│
status.json ◀── phase/progress/errors ───┤
http://127.0.0.1:3099 ◀── recovery console┘
│
page polls /probe ──▶ new host answers ──▶ location.reload()┘
an agent calls dsh_restart (gate on)
│ files pending-approval.json only (no restart)
▼
sidebar badge + desktop notification + approval card
│ you click 「批准并重启」
▼
POST /api/dsh-restart/approval ──▶ consume the request (single-use) ──▶ the path above
(source=user-approved)
dsh.engines.dsh).launchctl kickstart -k; elsewhere
it falls back to relaunching the same command itself (the launchd path is
platform-guarded; Linux and Windows are untested). The desktop notification is
macOS-only as well (elsewhere it is silently skipped — the request itself is
still on disk).@deepseek-ai/dsh-*
peerDependencies ranges, which is what the plugin market displays. Since this version the peerDependencies range explicitly declares compatibility with DSH 0.2.0-rc.2 (^0.2.0-rc.2 is now included), so no compatibility warning appears on 0.2.0-rc.2. No functional change.pnpm test # 275 assertions across eight suites
smoke — config store round-trip and clamping, history, log tailing with error
detection, launch signature, host identity;approval — the gate: 0600 on disk, a newer request replaces an older one,
a mismatched id must not execute, approving is single-use (a replay does
nothing), denying leaves no residue, an expired request never restarts,
empty state is a no-op;helper — the real helper against fake hosts: crash capture + console + manual
retry, and a successful relaunch, plus the three readiness edges (crash after
the port answers, silent exit after the port answers, and an error-shaped
health-check noise line that must not be mistaken for a failure);routes — synthetic req/res across every route, including the
loopback/cross-site/method guards and the connection→token-URL wiring;handoff — end-to-end on fake ports: restart → old process really exits →
helper relaunches generation 2 → the port answers with a new pid;launchd — pid→job matching and observe mode (which must never spawn), plus
a boot-fatal line in the managed log that must not be reported as ready;selfheal — the real browser bundle with stubbed globals: a persisted "boot
failed" whose host recovered is cleared on mount, and a 401 page gets the
current token URL instead of a dead reload;client — the browser half's registration and rendering contract.dsh-doctor's job).launchctl kickstart -k com.dsh.web directly, and this plugin does not pretend
otherwise. It solves "a session restarted DSH and you only found out later",
and it makes every restart carry a record of who approved it.dsh_restart still asks for confirm: true: that is the caller's own
claim, meant to make an agent explain itself first. It is not your consent —
your consent is the click, and history.json records it as
source: "user-approved".MIT