fellow99 / deepseek-harness-desktop

목록에 있음

An Electron-based desktop wrapper for deepseek-harness.

main기타 소스 보기

설치

npm run build:dsh # ① git apply both patches under patches/ → ② pnpm install (if node_modules missing) → ③ build:lib:host + build:lib:client + build:web

이 명령은 GitHub 저장소 주소에서 생성됩니다. 실행 전에 업스트림 README와 소스를 검토하고 재현성이 필요하면 release 또는 commit을 고정하세요.

README

유지 관리자가 작성한 문서 스냅샷입니다.

GitHub에서 보기 ↗
커밋 d47e6dd동기화 2026. 8. 18.

中文 | English


DeepSeek Harness Desktop

An Electron-based desktop wrapper for deepseek-harness, providing deep desktop integration.

Status: ✅ Scaffold and dsh consumption complete — the main process runs runProfile('desktop') to host the dsh Host, and the renderer loads the dsh Web UI same-origin (tray/notification MVP capabilities pending). See docs/000-产品概念设计.md for details.


What is this

DeepSeek Harness (dsh) is an open-source agent harness by DeepSeek AI, built on an "everything is a plugin" architecture; its native entry is dsh web (a browser Web UI).

This project wraps the dsh Web UI in a native desktop shell with Electron, adding desktop capabilities such as tray and notifications while fully reusing the dsh frontend — making the agent harness run like a first-class desktop app. It is not a thin "wrap dsh web pointing at localhost" shell, but a first-class desktop application built on dsh's existing architecture.

Core design

dsh has completed its Host/Client split, and its webserver serves both the SPA dist and /api. The desktop shell therefore uses an in-process Host + webserver + localhost same-origin data plane:

┌─ Electron main process (Node.js, also hosts dsh Host)──────────────┐
│  runProfile('desktop', ['--port','0']) → { ctx, shutdown }          │
│    ├─ webserver   ← bound to 127.0.0.1:<free port>, serves dist+/api│
│    ├─ apiProxy    ← RPC gateway                                     │
│    └─ connection  ← already registered /api + WebSocket on webserver│
│  once ready: loadURL(`http://127.0.0.1:${ctx.webServer.port}/`)     │
│  ┌─ Tray / Notification: subscribe to ctx session/event             │
│  └─ Frameless window controls: thin IPC (min/max/close)             │
└───────────────▲────────────────────────────────────────────────────┘
                │ contextBridge: window.dsh (thin IPC, window controls)│
┌───────────────┴────────────────────────────────────────────────────┐
│ Renderer: loadURL('http://127.0.0.1:<port>/')  ← same-origin        │
│   standard dsh Web UI (WebApiClient: fetch /api + WS event stream)  │
└─────────────────────────────────────────────────────────────────────┘

Key point: the renderer loads localhost same-origin — zero CORS, zero auth, zero custom protocol, zero IPC carrier — reusing dsh's existing WebApiClient (HTTP uplink + WebSocket downlink), zero upstream changes.

Planned MVP features

  • ✅ System tray (quit / restore)
  • ✅ Native notifications
  • ✅ Frameless window / custom title bar
  • ✅ Clipboard image paste

(Deferred: global shortcut, launch at login, multiple windows; native file picker reuses dsh's standard frontend directory browser)

Target platforms & distribution

  • Platforms: Windows + Linux (macOS later)
  • Distribution: local packaging for personal use (Electron Forge make); no auto-update, code signing, or store distribution yet

Tech stack

  • Electron + Electron Forge (scaffolding & packaging)
  • deepseek-harness (a sibling directory of this project, not a submodule, referenced as ../deepseek-harness; consumed via local source reference)
  • TypeScript

Development

Integration approach

  • Source reference: dsh lives in a sibling directory (../deepseek-harness, not a submodule); we consume its build artifacts.
  • Host integration: src/main/host.ts dynamically imports dsh's runProfile (apps/cli build artifact), hosting the dsh Host in the main process (webserver bound to 127.0.0.1:<free port>), returning a { ctx, shutdown, port, url } handle.
  • Same-origin data plane: the renderer does loadURL(http://127.0.0.1:<port>/) to load the dsh Web UI same-origin, reusing WebApiClient (HTTP uplink + WebSocket downlink) — zero CORS, zero auth, zero new carrier.
  • desktop profile: profiles/desktop/ (dsh.profile.bundles = [dsh-base, dsh-web-app], with cordis.patch.yml overriding web-runtime.printUrl: false), copied to $DSH_HOME/profiles/desktop at runtime.

Build process (with patches)

dsh depends on Node internal APIs (HMR, native directory dialog) that are unavailable under Electron, so two patches must be applied before building. One command does it all (idempotent — --reverse --check detects already-applied and skips):

npm run build:dsh   # ① git apply both patches under patches/ → ② pnpm install (if node_modules missing) → ③ build:lib:host + build:lib:client + build:web
PatchPurpose
patches/dsh-disable-hmr.patchAdds a DSH_DISABLE_HMR switch to runProfile, skipping watch-only HMR (HMR depends on --expose-internals)
patches/dsh-disable-native-picker.patchForces directory-picker to use browse under Electron (the native dialog worker fails because it spawns electron.exe)

Electron compatibility root cause: dsh's loader obtains the Node internal ESM loader via the node-addon-require-builtin native module, which fails under Electron because Electron's V8 lacks the GetAlignedPointerFromEmbedderData symbol; in development the loader falls back to default ESM import, resolved by host.ts's ensureWorkspaceLinks linking workspace packages into dsh's root node_modules.

Start / package

npm install
npm start          # Development: Vite build + launch Electron, main process hosts dsh Host and loads its Web UI
npm run package    # Package: prepackage auto-collects (pnpm deploy materializes dsh artifacts into dsh-dist/, extraResource copies into resources/dsh-dist)

The packaged output out/DeepSeek Harness Desktop-win32-x64/ already includes dsh (lib + node_modules + web dist + profile); the exe runs dsh directly.

Directory structure

This project and deepseek-harness (dsh) live in sibling directories (not a submodule), integrated via source reference:

(sibling directories)
├── deepseek-harness-desktop/      # This project (Electron desktop shell)
│   ├── docs/                      # Product concept design
│   ├── specs/                     # Spec documents (as-built; see specs/README.md for index)
│   ├── patches/                   # dsh upstream patches (git apply, auto-applied by build:dsh)
│   │   ├── dsh-disable-hmr.patch
│   │   └── dsh-disable-native-picker.patch
│   ├── scripts/                   # Build scripts
│   │   ├── build-dsh.mjs          # apply patches + install deps + build dsh artifacts
│   │   └── collect-dsh.mjs        # collect dsh artifacts into dsh-dist/ (pnpm deploy + materialize)
│   ├── profiles/desktop/          # Custom desktop profile (dsh.profile.bundles + cordis.patch.yml)
│   ├── src/
│   │   ├── main/                  # Electron main process (= dsh Host host)
│   │   │   ├── index.ts           # single-instance lock → start host → create window → tray/notification/lifecycle
│   │   │   ├── host.ts            # runProfile('desktop') → { ctx, shutdown }; readiness determination
│   │   │   ├── windows.ts         # BrowserWindow, loadURL(localhost), frameless/security
│   │   │   ├── tray.ts            # system tray (quit/restore)
│   │   │   ├── notifications.ts   # subscribe to ctx session/event → native notifications
│   │   │   └── lifecycle.ts       # NO_PROXY/CA, crash handling
│   │   ├── preload/index.ts       # contextBridge: window.dsh (thin IPC)
│   │   └── renderer/renderer.ts   # minimal renderer entry (fallback loading page)
│   ├── forge.config.ts            # Electron Forge config (extraResource copies into resources/dsh-dist)
│   ├── vite.*.config.ts           # Vite configs (main/preload/renderer)
│   ├── index.html                 # renderer entry (Forge Vite convention: at project root)
│   └── resources/                 # app icon, tray icon
│
└── deepseek-harness/              # The wrapped host (dsh, source reference, not a submodule)
    ├── apps/                      # cli (dsh bin, profile-boot), web (Web frontend, build:web produces dist)
    ├── packages/                  # host / client / core / session workspace packages
    ├── vendor/                    # vendored cordis framework packages (cordis / loader / hmr / ...)
    └── native/                    # landlock-run native module (Linux sandbox, cut in MVP)

References

  • deepseek-harness (sibling directory ../deepseek-harness) — the wrapped host; its docs/ directory contains full architecture docs
  • opencode (desktop shell reference: packages/desktop/) — a similar "wrap an agent harness with Electron" use case

License

MIT © 2026 fellow99

프로젝트 파일 및 신호

표시된 항목은 디렉터리 스냅샷에서 감지된 공개 저장소 신호입니다.

테스트감지됨
문서감지됨

저장소 정보

언어
TypeScript
라이선스
MIT
마지막 업데이트
2026. 8. 18. 오후 3:49

신중하게 설치하기

소스 코드, 권한, 수명 주기 스크립트, 의존성 및 네트워크 접근을 검토하고 신뢰하지 않는 플러그인은 격리 환경에서 테스트하세요.