Bundle
dsh-wx-desktop
DeepSeek Harness 桌面端:基于官方 DeepSeek Harness(@deepseek-ai/dsh)开发的独立 Electron 应用(内嵌启动本机 dsh,复用已配置 profile 与全部插件),同时可作为 DSH 插件挂载,在 Web GUI 提供「打开桌面端」入口。
- Source
- wangxilhy23
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 5 days ago
Readme
# dsh-wx-desktop — DeepSeek Harness 桌面端
把 DeepSeek Harness 装进一个原生桌面窗口:独立 Electron 应用,**自包含 dsh 运行时**——无需目标机器安装 Node、无需 dev checkout、无需全局 dsh。启动时自动拉起 dsh(复用你已配置的 web profile,因此已安装的全部插件开箱即用),并在原生窗口中渲染完整 Web GUI。
工程同时以 **DSH 插件** 形态存在:可挂载到 web profile,在浏览器 GUI 的侧栏增加「桌面端」入口,一键拉起桌面应用。
> **基于官方 DeepSeek Harness 开发**:本项目构建于官方 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`@deepseek-ai/dsh`)之上——dsh 内核、插件机制与 Web GUI 均来自官方仓库,桌面端只负责把官方 dsh 封装进原生窗口,并提供自包含运行时(当前内置 dsh `0.1.1-rc.2`)。
## 特性
- **完全自包含**:打包内置完整 `@deepseek-ai/dsh` 运行时(`runtime/dsh-runtime/`),用 **Electron 内嵌 Node**(`ELECTRON_RUN_AS_NODE` + `--expose-internals`)运行——**没有 Node 的机器也能启动**。
- **复用已运行 dsh 安装,杜绝 junction 冲突**:桌面端启动时先探测 `$DSH_HOME/profiles/node_modules` 的 junction 已被哪个 dsh 安装占用(例如正在运行的 web 端 dev checkout)。若已被占用,则**复用同一个安装**(`resolveActiveDshInstall`),避免两个不同版本的 dsh 互相抢占同一组 junction 导致 `EPERM` 崩溃;同时保证桌面端与 web 端加载**完全相同的 profile 与插件**。仅在没有其他 dsh 在运行时才回退到内置自包含 runtime。
- **内嵌启动 dsh**:`dsh web --port 0`,OS 自动分配端口,与已有 `127.0.0.1:3080` 服务互不冲突。
- **100% 插件兼容**:加载的是完整 dsh web GUI——host 插件在 dsh 进程内照常运行,client 插件(`dsh.client` 包)经 `window.__DSH_BOOT__` 图照常加载。零改动。
- **原生窗口体验**:应用菜单、窗口尺寸记忆由 OS 接管;锁定的 WebContents(`contextIsolation` + `sandbox`),外部链接走系统浏览器。
- **单实例**:二次启动聚焦已有窗口。
- **优雅关闭**:关窗 → dsh 子进程 SIGTERM → 超时强杀,不留残留进程;启动失败自动终止子进程(无孤儿)。
- **自定义品牌图标**:内置 DeepSeek 蓝色系鲸鱼图标(`build/icon.ico` / `build/icon.png`),用于安装程序、桌面快捷方式与任务栏。
## 快速开始(开发)
```bash
npm install # 安装 electron / electron-builder / tsdown / typescript
npm run install:dsh-runtime # 安装自包含 dsh 运行时(runtime/dsh-runtime/,约 220MB)
npm run dev # 构建 host+client 并启动桌面端
```
桌面端会按以下顺序定位 dsh 运行时(`src/electron/main.mjs` 内实现):
1. 环境变量 `DSH_DESKTOP_DSH`(显式覆盖,可指向 `bin.js`、dsh 仓库目录,或 PATH 上的 `dsh` 命令)
2. **已活跃的 dsh 安装**:读取 `$DSH_HOME/profiles/node_modules/@deepseek-ai/dsh` junction 目标。若该 junction 正被另一个 dsh 安装(例如正在运行的 web 端 dev checkout)占用,则复用那个安装——共享同一组 junction,避免 EPERM 冲突,并加载与 web 端一致的 profile(`resolveActiveDshInstall`)
3. **自包含运行时**:`runtime/dsh-runtime/`(开发)或 `resources/dsh-runtime/`(打包)——用 Electron 内嵌 Node 运行,**无需系统 Node**(无其他活跃安装时使用)
4. dev checkout 预构建 CLI `D:\deepseek-harness\apps\cli\lib\bin.js`(开发者便利,需要系统 node)
5. dev checkout 源码模式 / 任意 `dsh` 命令(PATH)
> **无需 Node**:自包含路径用 Electron 内嵌 Node(`process.execPath` + `ELECTRON_RUN_AS_NODE=1` + `--expose-internals`)运行预构建的 dsh `lib/bin.js`——目标机器不必安装 Node.js,也不需要 dev checkout。`DSH_DESKTOP_NODE` 可显式指定系统 node(仅开发回退用)。
>
> **启动超时**:默认 180s(首次运行需初始化 profile);用 `DSH_DESKTOP_BOOT_TIMEOUT_MS`(毫秒)覆盖。启动失败时,dsh 子进程会被自动终止(不残留孤儿进程),错误对话框会显示 dsh 最近的 stdout/stderr 以便排查。
>
> **profile**:默认复用用户 `~/.dsh`(含全部已装插件与历史会话);若不存在,dsh 自动初始化默认 profile(基础 GUI)。可用 `DSH_HOME` 指向其他位置。
## 作为 DSH 插件挂载
工程带 `cordis.patch.yml`(`dsh.bundle.patch`)与浏览器半(`dsh.client`),可安装进 web profile:
```bash
dsh plugin --profile web add link:E:\code\work\wxDshPlugin\dsh-wx-desktop
# 或从工程目录:
dsh plugin --profile web add link:.
```
挂载后刷新 `http://127.0.0.1:3080`,侧栏出现「桌面端」入口;点击会经 host 半的本地路由(`POST /dsh-wx-desktop/open`)拉起本机桌面应用。
桌面应用默认由 host 半通过**系统 node + 本包自带启动脚本**拉起(零 PATH 配置);如需指向已打包的 exe,在 profile 的 `cordis.patch.yml` 中给该行加配置:
```yaml
- id: ui-dsh-wx-desktop
name: dsh-wx-desktop
config:
desktopCommand: "D:\\path\\to\\DeepSeek Harness Desktop.exe"
```
## 构建与打包
```bash
npm run build # tsc (host 半) + tsdown (client bundle)
npm run install:dsh-runtime # 安装自包含 dsh 运行时(打包前置步骤)
npm run package # install:dsh-runtime + 构建 + electron-builder → dist/
npm run typecheck # host + client 两套 tsc --noEmit
```
打包产物(`dist/` 目录):
- `DeepSeek Harness Desktop-<version>-setup.exe` —— NSIS 安装程序(双击安装,含桌面快捷方式)
- `win-unpacked/` —— 免安装的解压即用目录
> 说明:打包时 `electron-builder.yml` 用 `extraMetadata.main: src/electron/main.mjs` 覆盖 Electron 入口(package.json 的 `main` 仍是 DSH 插件半 `lib/index.js`,两者互不干扰);`extraResources` 把 `runtime/dsh-runtime/` 装进 `resources/dsh-runtime/`。二进制下载走 npmmirror 镜像,可被 `ELECTRON_MIRROR` / `ELECTRON_BUILDER_BINARIES_MIRROR` 覆盖。`runtime/` 不入 git(220MB,可随时用脚本重建)。
## 工程结构
```
src/
index.ts host 半:注册 /dsh-wx-desktop/open 路由(回环源校验,spawn 桌面端)
client/index.ts 浏览器半:侧栏「桌面端」入口 + 启动状态(纯 DOM,自愈挂载)
electron/main.mjs Electron 主进程:解析 dsh、spawn、端口解析、窗口、生命周期
build/
icon.ico 应用图标(安装程序 / 快捷方式 / 任务栏,DeepSeek 蓝色系鲸鱼)
icon.png 图标源 PNG(256px)
runtime/dsh-runtime/ 自包含 @deepseek-ai/dsh 运行时(打包用,不入 git)
cordis.patch.yml 挂载插件行(dsh.bundle.patch)
scripts/dev.mjs 开发运行器:build → electron
scripts/package.mjs electron-builder 打包
scripts/install-dsh-runtime.mjs 安装自包含运行时(幂等)
```
## 与 web 端同时运行(junction 处理)
桌面端与 web 端共用同一 `$DSH_HOME`。dsh 启动时会维护 `$DSH_HOME/profiles/node_modules` 下的扁平依赖 junction,并让它们指向**当前运行中的 dsh 安装**的依赖闭包。若两个**不同版本**的 dsh(如桌面端内置 runtime 与 web 端 dev checkout)同时启动,后启动的一方会尝试 unlink 前一方正占用的 junction,在 Windows 上报 `EPERM` 而启动失败。
`resolveActiveDshInstall()` 通过读取 `@deepseek-ai/dsh` junction 的目标来识别当前活跃的 dsh 安装;若已有一个活跃安装,桌面端就复用同一个(而不是启动自己的内置 runtime)。这样:
- junction 指向一致,`healProfilesModuleFallback` 变成 no-op,不再互相抢占
- 桌面端加载的 profile / 插件与 web 端**完全一致**(所有已安装插件全部可见)
- 只有没有其他 dsh 在运行时,才回退到内置自包含 runtime(保持"无 Node 也能启动"的自包含设计)
## 常见问题
- **桌面端启动弹 "Error launching CrashSender.exe" / 0x80000003**:通常是 dsh 启动阶段因 junction 冲突失败,或 Electron 无法在 userData 目录创建 `SingletonLock`(多为运行环境权限受限)。先确认 web 端与桌面端是否共用同一 `$DSH_HOME`(本工程已通过复用活跃安装规避);若是权限问题,检查 `%APPDATA%\dsh-wx-desktop` 目录可写。
- **桌面端插件列表缺少某个插件**:确认该插件已安装到 `$DSH_HOME/profiles/web`,且桌面端与 web 端使用同一个 profile(本工程默认如此)。
## 说明与后续演进
- 当前采用「自包含内嵌 dsh + Electron 内嵌 Node + HTTP 加载」——这是本工程选择的稳妥落地路径。DSH 官方架构笔记(`2026-07-19-gui-layering-and-rpc-protocol.md`)预留了更深的形态:渲染进程用 `file://` 加载 dist、fetch 经 IPC 桥(`AbstractApiClient` 的 `doFetch` 子类 + host 侧 `toFetchHandler(api)` 同构点)。那是后续演进方向,不需要改动 DSH 本体即可接入。
- 安装包体积较大(自包含 dsh 运行时约 220MB):这是「无 Node 也能启动」的固有代价。如目标机器确定有 Node/dsh,可跳过 `install:dsh-runtime` 减小体积。
Install
dsh plugin --profile web add github:wangxilhy23/dsh-wx-desktop#c429a295847c290061f1370cc4bd829c6bd56b32
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-wx-desktop from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.