dsh-wx-desktop
deepseek harness 桌面端插件
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 19, 2026
- Updated
- Aug 19, 2026
Introduction
dsh-wx-desktop — DeepSeek Harness 桌面端
把 DeepSeek Harness 装进一个原生桌面窗口:独立 Electron 应用,自包含 dsh 运行时——无需目标机器安装 Node、无需 dev checkout、无需全局 dsh。启动时自动拉起 dsh(复用你已配置的 web profile,因此已安装的全部插件——vision-toolkit、ModLens、dsh-wx-skin 等——开箱即用),并在原生窗口中渲染完整 Web GUI。
工程同时以 DSH 插件 形态存在:可挂载到 web profile,在浏览器 GUI 的侧栏增加「桌面端」入口,一键拉起桌面应用。
特性
- 完全自包含:打包内置完整
@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),用于安装程序、桌面快捷方式与任务栏。
快速开始(开发)
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 内实现):
- 环境变量
DSH_DESKTOP_DSH(显式覆盖,可指向bin.js、dsh 仓库目录,或 PATH 上的dsh命令) - 已活跃的 dsh 安装:读取
$DSH_HOME/profiles/node_modules/@deepseek-ai/dshjunction 目标。若该 junction 正被另一个 dsh 安装(例如正在运行的 web 端 dev checkout)占用,则复用那个安装——共享同一组 junction,避免 EPERM 冲突,并加载与 web 端一致的 profile(resolveActiveDshInstall) - 自包含运行时:
runtime/dsh-runtime/(开发)或resources/dsh-runtime/(打包)——用 Electron 内嵌 Node 运行,无需系统 Node(无其他活跃安装时使用) - dev checkout 预构建 CLI
D:\deepseek-harness\apps\cli\lib\bin.js(开发者便利,需要系统 node) - dev checkout 源码模式 / 任意
dsh命令(PATH)
无需 Node:自包含路径用 Electron 内嵌 Node(
process.execPath+ELECTRON_RUN_AS_NODE=1+--expose-internals)运行预构建的 dshlib/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:
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 中给该行加配置:
- id: ui-dsh-wx-desktop
name: dsh-wx-desktop
config:
desktopCommand: "D:\\path\\to\\DeepSeek Harness Desktop.exe"
构建与打包
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 端完全一致(ModLens、vision-toolkit 等全部可见)
- 只有没有其他 dsh 在运行时,才回退到内置自包含 runtime(保持"无 Node 也能启动"的自包含设计)
常见问题
- 桌面端启动弹 "Error launching CrashSender.exe" / 0x80000003:通常是 dsh 启动阶段因 junction 冲突失败,或 Electron 无法在 userData 目录创建
SingletonLock(多为运行环境权限受限)。先确认 web 端与桌面端是否共用同一$DSH_HOME(本工程已通过复用活跃安装规避);若是权限问题,检查%APPDATA%\dsh-wx-desktop目录可写。 - 桌面端插件列表没有 ModLens:确认
$DSH_HOME/profiles/web/node_modules/@liustack/modlens已安装,且桌面端与 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减小体积。