English | 简体中文
DSH-Desktop
Electron desktop shell for DeepSeek Harness (DSH). Uses Electron's embedded Node and bundled pnpm to install @deepseek-ai/dsh into ~/.dsh/runtime and serve the dsh web UI.
Download & Install
Prebuilt packages are published on GitHub Releases. First launch installs the DSH runtime (~1-2 min).
macOS
Requires macOS 13 (Ventura) or later.
- Download
DSH-Desktop-*.dmg from the latest release.
- Open the
.dmg and drag DSH-Desktop.app into /Applications.
- The app is unsigned, so Gatekeeper blocks the first launch. Right-click the app → Open and confirm, or run:
xattr -dr com.apple.quarantine /Applications/DSH-Desktop.app
Windows
Requires 64-bit Windows.
- Download
DSH-Desktop Setup *.exe (installer) or DSH-Desktop-*-win.zip (portable) from the latest release.
- Run the installer, or unzip the archive and launch
DSH-Desktop.exe.
- The build is unsigned, so SmartScreen may warn. Click More info → Run anyway.
How it works
Electron main process
├─ embedded Node (ELECTRON_RUN_AS_NODE) + bundled pnpm
├─ node/pnpm launchers (resources/runtime; repo-root runtime/ in dev)
├─ first launch: pnpm installs @deepseek-ai/dsh → ~/.dsh/runtime (upgradeable)
├─ spawn dsh web --host 127.0.0.1 --port
└─ BrowserWindow → http://127.0.0.1:
DSH is installed from npm at runtime, not shipped with the app. Upgrading DSH = detect a newer version on launch → click "Update" → optionally restart the web service (the desktop app stays open). No rebuild or re-signing.
After a successful boot, the web port is saved in web-port.json under Electron's user-data directory. Cold starts reuse it when available so origin-scoped browser preferences survive restarts. If another process occupies that port, a new loopback port is allocated; the app never attaches to that process. Plugins must still persist durable data on the host to survive a port change. Development and installed apps keep separate port records.
Develop
pnpm install
pnpm collect # collect pnpm and Electron launchers into runtime/
pnpm start # first launch installs @deepseek-ai/dsh (~1-2 min)
pnpm dev # alias for start
Dev and packaged builds both use the current Electron executable and the external ~/.dsh/runtime. No standalone Node binary is downloaded or shipped. Plugin commands find a small node launcher through PATH; the executable path is supplied at each launch, so moving or upgrading the app does not leave stale paths.
Tests (no Electron window or browser required):
pnpm test # build + both runners (see below)
pnpm test:unit # vitest, test/unit/** (no build needed)
pnpm test:integration # build + test/integration/**
pnpm typecheck
pnpm check # formatting + typecheck + build + all tests
test/ mirrors src/ so a failing file names the module it covers:
| Path | Runner | What it may import |
|---|
test/unit/**/*.test.ts | vitest | src/ directly; Electron via vi.mock |
test/integration/**/*.test.ts | vitest | src/ directly, multi-module flows |
test/integration/**/*.test.cjs | node:test | only built dist/ and scripts/ |
The .ts suites run under vitest; the .cjs suites are plain node:test
(see vitest.config.ts, which deliberately excludes them). The test script
builds fresh output first.
Package
pnpm dist:mac # macOS dmg + zip
pnpm dist:win # Windows nsis + zip (run on Windows)
Packaging recollects pnpm and the launchers. Windows builds require the matching MSVC Native Tools environment and Windows SDK for the small forwarding node.exe.
macOS artifacts are unsigned; Gatekeeper blocks first launch. Allow with:
xattr -dr com.apple.quarantine /Applications/DSH-Desktop.app
Runtime dependencies
- Node supplied by Electron; pnpm (latest) and command launchers collected by
scripts/collect-runtime.mjs
@deepseek-ai/dsh (npm latest), installed to ~/.dsh/runtime
Updating Electron
Run pnpm add -D -E electron@ and pnpm check, then launch and test the packaged app.
Launchers and native plugin builds use the running Electron's path and version. Keep the runAsNode fuse enabled. DSH may reject a new Node/V8 combination, and existing ABI-specific plugins may need rebuilding; verify the UI, terminals and plugin installation before release. The DSH installation is shared with the CLI, so do not rebuild the entire shared runtime for Electron.
Desktop plugin API
The shell injects window.dshDesktop into the DSH page (updates / seats / notify / overlays / plugins). Plugins should depend on that contract, not on Electron packaging code. See docs/desktop-api.md.
Our plugins
Companion DSH plugins live in DSH-Plugs.
Thanks to