UnknowCao/dsh-dock

dsh-dock

DeepSeek Harness 的 Windows 桌面启动器:一键鲸鱼图标 + 侧栏「更多」菜单(设置 / 重启/刷新 / 优雅完全退出),冷启动卡片支持可滚动的多 DSH 候选选择。

Uncategorizeddeepseek-harnessdeepseek-harness-desktopdeepseek-harness-plugin

🌐 中文 · English

DSH Dock (dsh-dock)

"Double-click the desktop whale, get full-screen DSH — then it lives in your notification area: status at a glance, restart in one click, no terminal, no npx dsh web, no digging through the process list for port 3080."

Collapses "open a terminal → type the command → paste the token → press F11 → hunt the process list to kill the server" into: double-click the whale to open, a tray whale that reports status, one-click server restart, and a two-step full exit. Single-file native exe, no build step on install, zero dependencies, loopback-only traffic.

Results and live tests · Quick start · What it can do · Security boundaries · Verification and testing


Tray whale right-click menu: Open DSH · Restart Server · ☑ Start on boot · Full Exit

After the window opens the whale settles into the notification area: hover for server status, left-click to reopen, right-click for restart / autostart / exit — "restart the server", several times a day, shrinks from a process-list hunt to one click.


What problem does it solve

If you use DeepSeek Harness daily, this probably repeats every day: open a terminal → npx dsh web --no-open → copy the token URL → paste it into a browser → press F11. Want a restart? Get-NetTCPConnection -LocalPort 3080 to find the PID, Stop-Process, then do the whole thing again.

The problem isn't that any single step is hard — it's that every step needs your hands. "Restart" happens several times a day, yet it's buried in the process list.

DSH Dock collapses the ritual into three entry points plus one resident status seat:

  • One black whale icon on the desktop: server running → opens full-screen directly; server down → starts it silently and opens the window once ready; after opening, the whale settles into the system tray (can be turned off in the settings page).
  • The tray whale (status entry, v0.4.0): hover shows "running / starting / stopped"; left-click reopens instantly; right-click menu = Open DSH · Restart Server · ☑ Start on boot · Full Exit.
  • One "Whale Bay" menu in the sidebar: Surface (reload the UI, server untouched) / Migrate (restart the server — it always returns) / To the Bay (full exit, two-step confirm) — three actions borrowed from the whale's behavior repertoire, with the functional legend on hover. Settings is not in the menu — it stays at the native sidebar-footer entry, where the dsh-dock settings page (tray residency / start on boot) lives inside the Settings dialog.
  • Exit cleans up after itself: Full Exit → the page closes itself → the server stops gracefully (whole-tree dispose) → sessions persist in real time; double-click the whale next time and pick up where you left off.

All plugin chrome (menu, settings page, exit overlay) follows the harness display language and re-renders live on a language switch (中文 menu shown in menu.png).


Results and live tests

Every image and number in this section is a real run artifact; the re-capture recipes live in the repo: docs/screenshots/CAPTURE.md.

Desktop app (double-click to launch)

Desktop "DSH Harness" app icon (black whale)

After install, a "DSH Harness.exe" app appears on the desktop — double-clicking it is the whole entry (not a shortcut): server running → silent full-screen window; server down → the whale card takes over. The icon is the black whale (compiled into the exe; the shot above is the embedded 512×512 frame of assets/dsh-dock.ico, transparent background).

Sidebar "Whale Bay" menu (pops upward on click)

As of v0.5 the sidebar footer gains a three-wave icon button, "Whale Bay" (鲸湾) — the whale's rest-and-reset bay. It opens three actions borrowed from the whale's behavior repertoire: Surface (reload the UI — same effect as Ctrl+Shift+R, server untouched), Migrate (restart the server; it always returns), and To the Bay (full exit; two-step accidental-click guard, auto-disarms after 4 seconds). The menu keeps the metaphor pure — the functional legend rides the native hover tooltip and aria-label — and its icons (wind / migratory loop / moon) compose one small seascape with the wave trigger. The menu renders as a plain neighbor entry in the sidebar footer slot — no native DOM is moved or hidden — so it coexists with every other sidebar plugin (including dynamic Cordis plugins); its labels follow the harness display language live (中文: 换气 / 洄游 / 归湾).

Menu openAfter clicking "To the Bay" once
Sidebar menuTo the Bay two-step confirmation

Both are real v0.5 captures: the open Whale Bay menu (Surface / Migrate / To the Bay) and the armed two-step state (确认归湾?) after one click on "To the Bay". 中文 menu shown in menu.png. Re-capture in one command: node scripts/capture-whale-menu.mjs --en.

Tray whale (resident status entry, v0.4.0)

After opening the window the whale stays in the notification area: hover tooltip shows the server state; left-click reopens instantly; Restart Server / ☑ Start on boot live on the right-click menu; the tray's "Full Exit" is a single click (the right-click itself is the intent; the sidebar keeps its two-step guard). A named mutex guarantees at most one icon at any time — whichever of double-click / boot autostart / tray arrives first stays.

Tray right-click menu

Tray right-click menu (top to bottom): Open DSH · Restart Server · ☑ Start on boot · Full Exit (single click).

Settings page ("DSH Dock(启动器)" section in the Settings dialog, v0.4.0)

Settings page

All plugin settings in one place: the native "Settings" entry at the sidebar bottom → the "DSH Dock(启动器)" section in the left list → two toggles (Tray residency / Start on boot, each with a description). Their state stays in sync with the tray's right-click checkmark in real time.

Cold-start card (double-click the whale while the server is down)

Cold-start card

A dark translucent rounded glass card; the centered black whale is a breathing light (brightness pulsing 35%↔100% on a cosine curve — no progress bar, no caption). On failure the whale stops breathing, the text turns red, and the log path plus a close button appear.

Screenshot provenance: the card is a real render of the prebuilt launcher (assets/dsh-dock-launcher.exe) under a demo config (temporary launcher.ini pointing at the real server on 3080 + a dead token line, read-only probing, no real log writes), captured in the "connecting" state. Re-capturable via pwsh -File scripts/capture-demo-card.ps1.

Several DSH installs? Pick on the cold card (since v0.3.3)

Multi-DSH candidate picker

With several dsh installs (global / npx cache / source checkouts), the cold-start card draws its own candidate list (version · source · path) and starts the one you pick; no click for 10 seconds auto-uses the last successful choice (or the highest version); switching never rewrites a single file; with zero candidates it offers a one-click npx fetch. Re-capturable via pwsh -File scripts/capture-multidsh.ps1.

Measured numbers (this machine)

This machine = Windows 11 + 360 AV; method in Verification and testing. Engine-related timings will differ on other machines; the plugin's own overhead stays ~0.75s.

ScenarioMeasuredNotes
Server running, double-click the desktop appEdge new process ~0.6–1.5sNative exe direct (launch ~100ms + local HTTP verify ~10ms), no wscript layer
Server down, double-click the desktop appBranded card in ~0.3s, window opens when the server is readyCard and launcher are the same native exe (prebuilt and committed — no compile on install), no PowerShell engine wait
Server cold start to port ready~10–13sdsh's own startup time (measured 10.6s / 13.2s)
"Full Exit" click to process actually gonemeasured 3.6–4.8s/launcher/api/stop asks ctx.appExit to dispose the whole tree, then treats the host assigning process.exitCode as the "dispose finished" signal and exits itself after a 0.25s flush — cost = real dispose time + 0.25s (measured 2026-09-14: dispose 3.87s / 2.68s → 4.76s / 3.55s total; the old build was a flat 7.0s watchdog); a host that never signals falls back to a 6s ceiling; hosts without appExit fall back to the hard kill

Quick start

Prerequisites: Windows 10/11 + the DSH web profile + Edge (falls back to the default browser if missing). No Node setup needed — the installer auto-detects a usable Node (≥ 22.19).

dsh plugin --profile web add github:UnknowCao/dsh-dock#v0.8.0

After restarting the server there is nothing to do: on activation the plugin extracts its parameters from the environment (its own listening port, Node path, start command); within ~2s the desktop gets "DSH Harness.exe" (black whale icon) and the sidebar gets the "Whale Bay" menu. Daily routine: double-click the whale to open, exit from the menu. "Full Exit" asks the host to dispose the whole Cordis tree and treats the process.exitCode the host assigns as the "disposal finished" signal, then finishes itself (cost = real dispose time + 0.25s, see stop.log); a host that never signals falls back to a 6s ceiling, and only hosts without ctx.appExit fall back to the legacy hard kill.

Upgrading from v0.4? v0.5 touches only the sidebar menu: renamed to "Whale Bay", narrowed to Surface (reload) / Migrate (restart the server) / To the Bay (full exit), each with a hover legend (Settings returns to the native sidebar-footer entry; the settings page itself is unchanged); fixes the page freeze when another plugin (e.g. the Cordis panel badge) also occupies the sidebar footer slot; menu labels now follow the harness language live. Tray, restart, autostart and the settings page are unchanged.

For customization (rename / different port), tell the agent:

Rename the DSH desktop app to XX / install it on port 3081

Uninstall: delete the desktop DSH Harness.exe and ~\.dsh\launcher\; remove the dependency and bundle entries from the profile. Uninstalling never deletes files automatically.


What it can do

CapabilityDeliversTrigger
One-click launch · fast pathServer running: silently opens full-screen (dedicated Edge app window, no card flash)Double-click the desktop app
One-click launch · cold pathBranded card (breathing whale) → silent start → window on readyDouble-click the desktop app
Dedicated app windowOwn Edge profile: not a tab in your main browser, 30-day login stateAutomatic
Sidebar "Whale Bay" menu (v0.5 redesign)Surface (reload the UI) / Migrate (restart the server) / To the Bay (full exit, two-step confirm) behind one wave button in the sidebar footer, each with a hover legend; popup is a React portal"Whale Bay" at the sidebar bottom
Coexists with other sidebar plugins (v0.5)Renders as a plain neighbor entry — zero DOM relocation, no hidden native triggers; the footer slot stacks vertically so full-row entries (e.g. the Cordis panel badge) and Whale Bay each get their row; no more page freezeAutomatic
Follows the UI language (v0.5)Menu / settings page / exit overlay strings register into the harness locale; a language switch re-renders live (≤ v0.4 ignored it)Harness language setting
Exit closes the windowBrief page linger, window closes, server stops silently, sessions persist in real timeMenu "To the Bay" ×2
Guarded / race-freeTwo-step Full Exit; double-click right after exit waits for the old process to die before cold-starting; single-instance lock on cold start; port-occupied-but-not-ready fails loudly instead of hangingAutomatic
Single file · no build · zero depsThe desktop entry is the native exe itself (not a shortcut); prebuilt and committed — installs need no csc; runtime talks to loopback onlyAutomatic
Install on activation~2s after activation the full suite lands, all parameters (port/Node/start command) auto-detected from the environment; content-compare idempotency, refreshed automatically on plugin upgradedsh plugin add + restart
Pick which DSH to startWith several dsh installs (global / npx cache / source), the cold-start card shows a picker; no click for 10s auto-uses the last successful version (or the highest); switching never rewrites any file; zero-candidate case offers a one-click npx fetchDouble-click the whale (multi-candidate)
Tray residency · status entry (v0.4.0)After opening the window the whale stays in the notification area: hover shows "running / starting / stopped" status, left-click reopens instantly; closing the window never stops the server; a named mutex guarantees at most one icon everAutomatic (on by default, toggle in the settings page)
One-click server restart (v0.4.0)Kill the listener → wait the port dead → start again → auto-open on ready (replaces the old ~15s "exit and double-click" ritual with one click)Tray right-click or Whale Bay menu "Migrate"
Boot autostart (v0.4.0, off by default)Checking writes an HKCU Run entry: after login the whale sits in the tray with the server preheated in the background, no window; click the tray to open instantlyTray right-click "Start on boot" or the settings page
Plugin settings page (v0.4.0)A proper section inside the Settings dialog (same seat other plugins' settings pages use): tray residency / boot autostartSettings dialog → "DSH Dock(启动器)"

Compared to doing it by hand

ScenarioBy handdsh-dock
OpenTerminal npx dsh web + grab the token + paste into a browserDouble-click the icon — token grabbed, full-screen window opened
Window shapeLost among browser tabsDedicated app window, straight to full screen (own profile)
Reload the UI— (restart the whole server)One "Surface" click in the Whale Bay menu; server untouched
Restart the serverFull exit → wait for the port to die → double-click (~15s ritual)Tray right-click or Whale Bay menu "Migrate" — old windows close first, one new one opens on ready
ExitHunt the process list for the PID, then killSidebar two-step "To the Bay" (graceful stop + window close) or one tray click
Double-click againMay spawn a second serverIdempotent: already running → just open the window

How it differs from other launcher approaches

The dsh-plugin ecosystem already has several desktop clients and launchers (see the indexes at the bottom). dsh-dock's bet: not another program — a plugin inside the dsh you already have.

Dimensiondsh-dock's take
ShapeA DSH plugin: one dsh plugin add command, rides your existing dsh, no second runtime
UpgradesUpgrading the plugin upgrades it; the launcher suite refreshes itself on every activation (content-compared, idempotent)
Exit / restartVia host web routes: graceful exit disposes the whole tree; restart runs in the independent tray process; sessions persist in real time
In-UI controlThe Whale Bay sidebar menu + a settings page inside the Settings dialog, same surface as the Web UI, coexisting with other plugins
Multiple dsh versionsPick right on the cold-start card (global / npx cache / source checkouts)
Trust surfaceZero install hooks, a single prebuilt exe, loopback-only traffic (see Security boundaries)

In one line: if you already use dsh's Web UI and want "desktop double-click + tray residency + a clean exit" rather than yet another client — this is it.

More plugins in the ecosystem indexes: dsh-plugin topic · Oh-My-DSH · awesome-deepseek-harness.


Security boundaries

  • Loopback only: the exit endpoint /launcher/api/stop accepts local/trusted-origin + same-origin requests only; cross-site requests get a flat 403 — nothing exposed externally
  • Never touches session data: exit relies on DSH's real-time persistence; the plugin does not read or write sessions in $DSH_HOME
  • No credentials read or sent: the token only flows between the local log and the local Edge launch arguments
  • No wrong kills: exit uses "own-PID kill + netstat fallback"; if the manifest points at some other process, it never acts
  • Zero lifecycle scripts on install: package.json has no preinstall/install hooks — installing runs no arbitrary code; if a same-named desktop file exists that is not this plugin's, it refuses to overwrite and reports loudly
  • Full Exit has a two-step confirmation; startup failures surface the reason plus the log path
  • No resident background service (as of v0.4.0): the double-click chain remains a single native exe; the tray residency is a visible, user-toggleable whale process (on by default) with loopback-only traffic and no credential access. Turning "tray residency" off means a double-click no longer leaves a resident tray (back to the v0.3.x short-lived process model) — with two caveats: start-on-boot IS exe --boot (silent residency + server preheat), and turning the toggle off does not evict a tray that is already resident (it disappears at the next full exit + relaunch)
  • Autostart writes only the per-user registry: boot autostart is one dsh-dock value under HKCU\Software\Microsoft\Windows\CurrentVersion\Run (pointing at the suite exe with --boot) — no admin rights needed, removing it deletes the entry
  • Settings routes share the same fence: /launcher/api/settings/{get,set} goes through the same loopback/same-origin trust check as the exit and restart endpoints; cross-site gets a 403

Known limitations (stated up front, not defects)

  • Windows only (macOS/Linux planned)
  • Manually started server + a fresh Edge profile: no token in the log to grab — log in once with a token URL (login state lasts 30 days)
  • "Full Exit" from an ordinary browser tab may be blocked from auto-closing → an overlay explains it; close manually once. Windows opened by the desktop app close automatically
  • The web chrome (menu / settings page / exit overlay) follows the harness display language live; the native tray exe is not localized yet (Chinese labels; OS-driven context menu styling)
  • Sidebar "Migrate" (restart server) needs the resident tray to execute (the host cannot restart its own death); when no tray is present it prompts you to double-click the whale first
  • Tray "Restart Server" deliberately closes every dsh-dock window before booting — their tokens died with the old process, so a refresh cannot resurrect them

File structure

dsh-dock/
├── index.js                 # Host: launcher_install tool + exit/settings/restart routes + baked launcher.ini/start-server.cmd + desktop exe placement
├── client.js                # Client: sidebar "Whale Bay" menu (plain neighbor entry + React portals) + "DSH Dock(启动器)" settings page in the Settings dialog + exit auto-close
├── cordis.patch.yml         # bundle patch: inserts the plugin row into the composition
├── package.json             # package manifest (name: dsh-dock)
├── regen-launcher.mjs       # maintenance: regenerate the launcher suite outside the harness
├── candidates.mjs           # shared: multi-DSH detection + suite materialization (activation + T2 refresh)
├── dsh-dock-refresh.mjs     # cold-start refresh script (T2, run by the exe via node; deployed next to candidates.json)
├── dsh-dock-v0.8.0-release-notes.md  # v0.8.0 release notes (adaptive tray pacing / exactly-once restart / publish-gate fixes)
├── dsh-dock-v0.7.0-release-notes.md  # v0.7.0 release notes (user-pointed extra install paths)
├── dsh-dock-v0.6.0-release-notes.md  # v0.6.0 release notes (adaptive candidate discovery)
├── dsh-dock-v0.5.0-release-notes.md  # v0.5.0 release notes (Whale Bay redesign / coexistence / live locale)
├── dsh-dock-v0.4.0-release-notes.md  # v0.4.0 release notes (the tray/restart/autostart/settings story)
├── src/
│   └── DshDockLauncher.cs   # the launcher's single implementation: fast-path silent window + cold card + health gate + exit races + single-instance lock + tray
├── assets/
│   ├── dsh-dock-launcher.exe # prebuilt launcher (output of scripts/build-launcher.ps1, committed — installs need no compiler)
│   ├── dsh-dock.ico         # desktop app whale icon (compiled into the exe)
│   └── whale.png            # card whale source bitmap (whiteness remap)
├── docs/
│   └── screenshots/
│       ├── desktop-shortcut.png    # desktop app icon close-up (black whale 512×512, transparent)
│       ├── menu.png / menu-en.png  # Whale Bay menu captures (v0.5, scripted)
│       ├── menu-exit-armed.png     # "Full Exit" two-step confirmation state
│       ├── tray-menu.png           # tray right-click menu, real capture
│       ├── settings-section.png    # the "DSH Dock(启动器)" section inside the Settings dialog
│       ├── cold-card.png           # cold-start card, real capture (re-recorded under the demo config)
│       ├── cold-card-multidsh.png  # the multi-DSH candidate picker, real capture
│       └── CAPTURE.md              # re-capture recipes (manual menu shots + scripted runs)
├── scripts/
│   ├── build-launcher.ps1        # compile src/DshDockLauncher.cs → assets/dsh-dock-launcher.exe (dev-time)
│   ├── capture-demo-card.ps1     # cold-card re-capture: demo ini + dead token → burst-shoot, pick the brightest whale phase
│   ├── capture-multidsh.ps1      # multi-DSH picker re-capture
│   ├── capture-whale-menu.mjs    # Whale Bay menu + settings page one-command re-capture (CDP-driven Edge, incl. language switch + restore)
│   └── verify-health-gate.ps1    # regression: a dead token (401) must never open a window
├── test/
│   ├── candidates.smoke.mjs      # offline unit tests: candidate scanning (M1 adaptive + M2 extra roots)
│   └── exit-path.smoke.mjs       # offline unit tests: exit orchestration (signal / ceiling / throw / exactly-once)
├── LICENSE
├── README.md                # Chinese README (primary)
└── README.en.md             # this file

On install, generated under ~\.dsh\launcher\ (runtime artifacts, not committed):