dsh-wsl-tray
为运行在 WSL 里的 DeepSeek Harness(DSH)提供 Windows 桌面快捷方式与系统托盘启动器。
- Stars
- 2
- Language
- TypeScript
- Created
- Aug 22, 2026
- Updated
- Sep 27, 2026
Introduction
dsh-wsl-tray
中文 | English
A DeepSeek Harness plugin for WSL deployments: it puts a Windows desktop shortcut and a system-tray launcher in front of the DSH web server running inside WSL.
Features
- Double-click the DeepSeek Harness desktop shortcut to start DSH in the
background (or reuse an already-running instance). The default browser opens
exactly once when DSH is ready (the built-in DSH browser-open is disabled by
--no-open; only the tray opens it). - A DSH fish tray icon appears. Right-click menu:
- 打开 DeepSeek Harness / open the DSH web page
- 重新生成桌面快捷方式 / recreate the desktop shortcut
- 重启 DSH 服务 / restart the DSH service
- 暂停守护进程 / 恢复守护进程 / pause or resume the watchdog
- 退出 / exit the tray icon
- Double-clicking the tray icon also opens the DSH web page.
- A watchdog daemon runs inside the tray: it probes the DSH URL on a
timer, restarts DSH when the probes fail, gives up after a bounded number of
consecutive failed restarts, and writes a full audit trail to
watchdog.log(see Watchdog below). - The plugin's own settings page (Settings → WSL Desktop & Tray) shows live status (tray files + watchdog state) and a button that recreates the desktop shortcut without touching WSL by hand. The page can also show the tail of the watchdog log.
- No console window is shown: the shortcut goes through
wscript.exe+ VBS and the entire chain is launched with window style 0.
Requirements
- DSH itself must be running inside WSL (
WSL_DISTRO_NAMEset,/mnt/caccessible). - Windows must be able to run
wscript.exe,powershell.exe, andwsl.exe. - DSH web 0.1.7-alpha.2 or newer (client-side settings sections + client bundle machinery).
Install
Once published to npm:
dsh plugin --profile web add dsh-wsl-tray
Or add it manually to a DSH web profile:
cd ~/.dsh/profiles/web
pnpm add dsh-wsl-tray
and add "dsh-wsl-tray" to package.json:
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-wsl-tray"
]
}
}
Restart dsh web and open Settings → WSL Desktop & Tray (中文界面为
设置 → WSL 桌面与托盘).
Running from a source checkout
The launcher never starts a checkout from src. A tsx/src host loads plugin
packages from lib (the default runtime resolution mode), so the same packages
end up twice in one process; symbols do not cross instances, and tool calls fail
with undefined state. start.sh therefore only ever launches a checkout's BUILD
OUTPUT:
cd ~/deepseek-harness
pnpm run build # required once, and again after every source change
Then set the checkout path in Settings → WSL Desktop & Tray (either the
repository root or its apps/cli directory works). With a path configured:
start.shrunsnode <checkout>/apps/cli/lib/bin.js web --no-openbefore anything else — neversrc, and never whicheverdshhappens to be onPATH.- When the build output is missing, the launcher writes the reason and the fix
to
~/.dsh/dsh-wsl-tray/start.logand exits instead of silently running a different install. - Export
DSH_WSL_TRAY_AUTO_BUILD=1in WSL to let the launcher runpnpm run builditself when the output is missing (a full build is slow, so this stays opt-in). - When the cli sources are newer than the build output, start.sh logs a
WARN ... newer than the build outputline, so a forgotten rebuild does not look like a change that did nothing.
Known limitation: the freshness check only scans <checkout>/apps/cli/src; a
change under packages/ is not detected, so keep pnpm run build in the loop.
Generated files
The plugin writes five generated files:
| File | Location |
|---|---|
dsh.ico | %USERPROFILE%\.dsh\dsh-wsl-tray\dsh.ico |
dsh-tray.ps1 | %USERPROFILE%\.dsh\dsh-wsl-tray\dsh-tray.ps1 |
dsh-tray.vbs | %USERPROFILE%\.dsh\dsh-wsl-tray\dsh-tray.vbs |
start.sh | ~/.dsh/dsh-wsl-tray/start.sh |
stop.sh | ~/.dsh/dsh-wsl-tray/stop.sh |
and creates:
%USERPROFILE%\Desktop\DeepSeek Harness.lnk
While the tray runs, the watchdog maintains two runtime files (both are shown on the settings page):
| File | Location |
|---|---|
watchdog.log | %USERPROFILE%\.dsh\dsh-wsl-tray\watchdog.log (rotated at 512 KB) |
watchdog-status.json | %USERPROFILE%\.dsh\dsh-wsl-tray\watchdog-status.json (latest tick) |
The shortcut points at wscript.exe, which runs dsh-tray.vbs; the VBS starts
the tray PowerShell hidden, and the tray starts start.sh inside WSL through
WScript.Shell.Run(..., 0, false).
Watchdog
The watchdog lives inside the tray helper (the one process that is deliberately independent of DSH), and answers the three questions a restart daemon has to:
- How liveness is judged — an HTTP
Invoke-WebRequestprobe of the DSH web URL everyprobeIntervalSec(default 10 s, probe timeout 3 s). Each probe records its status code or error text, so a refused connection (nothing listening), a timeout (hung server) and a bad status stay distinguishable in the log. DSH is only considered DOWN afterdownThreshold(3) consecutive failed probes. - Restart success & giving up — a restart is triggered (stop + start via
wsl.exe) and the watchdog waits up torestartWaitSec(180 s) for the URL to answer again: an answer = success, which resets the failure counter; an unanswered window = one failed restart. AftermaxRestartFailures(3) consecutive failures the watchdog pauses instead of looping forever. It resumes from the tray menu (恢复守护进程), or automatically as soon as DSH answers again. - Logging — every probe transition, restart trigger, success/failure and
pause/resume is appended to
watchdog.logwith a timestamp, level and the probe detail; the current state machine snapshot goes towatchdog-status.jsonevery tick. The settings page exposes both through/dsh-wsl-tray/watchdogand/dsh-wsl-tray/watchdog-log.
Phases: starting (initial boot grace) → probing (steady state) →
restarting (waiting after a restart) → backoff (cooldown) or paused
(give-up / manual pause). The tuning values above are baked into
dsh-tray.ps1; change them in src/artifacts.ts
(DEFAULT_WATCHDOG_CONFIG) and regenerate.
Install without npm publishing
If npm publishing is not an option, install the prebuilt tarball that is included in this repository:
cd ~/.dsh/profiles/web
pnpm add /path/to/dsh-wsl-tray-github/dist/dsh-wsl-tray-0.1.7.tgz
Then add "dsh-wsl-tray" to the profile bundle list as above.
How it works
- Hidden launch: shortcut →
wscript.exe→ VBS → hidden PowerShell tray. - DSH stays alive:
start.shruns DSH in the foreground of the hiddenwsl.exesession, so WSL does not recycle the process after the one-shot launcher exits. - Automatic browser open: a Windows-side timer in the tray polls the DSH
URL every 2 seconds and calls
Start-Process $webUrlonce DSH answers. - Watchdog: a second tray timer probes the URL every 10 seconds and runs the state machine described above.
- Regeneration: the settings page and the tray menu both run
dsh-tray.ps1 -Regenerate.
Development
npm install
npm run typecheck
npm test
npm run build
npm pack --dry-run
Known limitations
- Only enabled inside WSL; on non-WSL hosts the settings page reports that the feature is unavailable.
- The watchdog runs only while the tray icon is up: choosing 退出 stops the
watchdog and the DSH instance (via the generated
stop.sh: a PID file tracks the instancestart.shlaunched, then a pattern fallback coversbin.js weblaunches from source checkouts, npm global installs and npx). Add the shortcut to the Windows Startup folder if you want the watchdog to follow Windows boot. - A DSH web instance started any other way (different flags, another tool) is
not tracked by the PID file; stop it by hand (Windows Task Manager or
wsl --shutdown) if the pattern fallback does not reach it.