很多人以为,把一个开源的 AI agent 框架做成桌面应用,只有两条路:要么 fork 上游、动一堆架构代码;要么退而求其次,套个浏览器壳充数。
其实还有第三条路------DSH Desktop 用「核心架构零改动」的方式,把 DeepSeek Harness 变成了一个真正独立的桌面应用。
这篇文章是 DSH Desktop 的完整介绍:它是什么、怎么设计的、已经能做什么、怎么把它跑起来。
一、DSH Desktop 是什么
先交代背景。DeepSeek Harness(dsh)是 DeepSeek AI 开源的 agent harness(智能体运行框架),采用「一切皆插件」的架构------模型适配器、工具注册表、会话日志、甚至 agent 主循环本身,全是可替换的插件。
它的原生入口是 dsh web,一个跑在浏览器里的 Web UI。功能没问题,但它始终活在浏览器标签页里:没有托盘、没有原生通知、没有独立的窗口管理------怎么看都不像一个"应用"。
DSH Desktop 要做的,就是把这个 Web UI 装进原生桌面壳,补齐桌面能力,让它像一个真正的桌面应用那样运行。
关键的一点:它不是「包一层 dsh web 指向 localhost」的粗壳。那种做法只是套了个浏览器,进程生命周期、托盘、通知全都和 dsh 内部状态脱节。
DSH Desktop 是按 dsh 现有架构实现的一个真正的桌面应用------dsh 作为宿主跑在 Electron 主进程里,桌面能力直接订阅它的内部事件。
二、核心设计:进程内 Host + webserver + 同源数据面
DSH Desktop 的核心设计,浓缩成一句话:
在 Electron 主进程里动态 import dsh 的 runProfile('desktop'),把 dsh 的 Host(webserver + apiProxy + connection)挂起来;就绪后,渲染进程 loadURL 到那个 localhost 端口,跑的就是未经修改的标准 dsh Web UI。
架构长这样:
┌─ Electron 主进程(Node.js,也承载 dsh Host)─────────────────┐
│ runProfile('desktop', ['--port','0']) → { ctx, shutdown } │
│ ├─ webserver ← 绑定 127.0.0.1:<空闲端口>,服务 dist + /api│
│ ├─ apiProxy ← RPC 网关 │
│ └─ connection ← 已把 /api + WebSocket 注册到 webserver │
│ 就绪后 loadURL(`http://127.0.0.1:${ctx.webServer.port}/`) │
│ ┌─ Tray / Notification:订阅 ctx 的 session/event │
│ └─ 常规标题栏(系统原生 min/max/close) │
└──────────────▲───────────────────────────────────────────────┘
│ (无窗口控制 IPC;渲染层走 HTTP/WS 同源) │
┌──────────────┴───────────────────────────────────────────────┐
│ 渲染进程:loadURL('http://127.0.0.1:<port>/') ← 同源 │
│ 标准 dsh Web UI(WebApiClient:fetch /api + WS 事件流) │
└──────────────────────────────────────────────────────────────┘
这里有一条决定性的架构事实:dsh 已经完成了 Host/Client 分层,而且它的 webserver 同时服务 SPA 前端页面和 /api 接口。
这意味着渲染进程只要加载 http://127.0.0.1:<port>/,它和 /api 就是同源的------fetch('/api/xxx') 和 WebSocket('/api/events.mux') 统统不需要 CORS、不需要鉴权、不需要自定义协议。
核心代码就几行:
ts
// src/main/host.ts ------ 挂起 dsh Host,一行调用
const { ctx, shutdown } = await runProfile('desktop', ['--port', '0']);
// --port 0 = 让 OS 分配空闲端口,避免 3080 被占
const url = `http://127.0.0.1:${ctx.webServer.port}/`;
// src/main/windows.ts ------ 渲染进程同源加载
win.loadURL(url);
托盘 / 通知这类桌面能力怎么接?主进程直接订阅 ctx 的 session/event ------因为 Host 就跑在同一个进程里,不需要经过 HTTP/WebSocket,进程内直接拿到事件,触发 Electron 的 Notification / Tray。
这一套设计换来了四个"零":零 CORS、零鉴权、零自定义协议、零新增 IPC 载体 ------复用 dsh 现成的 WebApiClient(HTTP 上行 + WebSocket 下行),核心架构零改动。
三、已经能做什么
目前 DSH Desktop 已实现的桌面能力:
- ✅ 系统托盘(退出 / 唤回)
- ✅ 原生通知
- ✅ 常规 Windows 标题栏(系统原生 min/max/close)
- ✅ 剪贴板图片粘贴
- ✅ 窗口状态持久化(最大化 / 位置尺寸,重启恢复)
- ✅ F11 全屏切换
暂缓的能力:全局快捷键唤起、开机自启、多窗口;原生文件选择沿用 dsh 标准前端的目录浏览。
四、技术栈与平台
- Electron + Electron Forge:脚手架与打包
- deepseek-harness (
dsh):被封装的宿主,采用同级目录源码引用(非 submodule) - dsh-market :内置的可视化插件市场(npm 包名
dshmarket) - TypeScript
平台方面,Electron 三端通用:Windows + Linux + macOS。Windows 出 Squirrel 安装器,macOS 出 DMG,Linux 出 deb/rpm,另外三个平台都出免安装 ZIP。
目前先本地打包自用,暂不做自动更新、代码签名和商店分发。
五、怎么跑起来
从克隆到构建,核心就一条命令链。先把两个同级依赖 clone 下来(dsh 和 dsh-market,均为源码引用、非 submodule),再执行:
bash
# ① 本工程依赖(npm:Electron / Forge / Vite)
npm install
# ② dsh 依赖(在 ../deepseek-harness 下执行;pnpm 工程)
cd ../deepseek-harness && pnpm i
# ③ 一键构建 dsh:git apply 补丁 → pnpm install → build host/client/web → build dsh-market
npm run build:dsh
# ④ 开发模式:Vite 构建 + 启动 Electron
npm start
# ⑤ 打包 / 出分发制品
npm run package
npm run make
打包产物 out/DSH Desktop-win32-x64/ 已包含 dsh(lib + node_modules + web dist + profile),exe 可直接运行。
三个补丁,为什么必须有
dsh 依赖 Node 内部 API(HMR、原生目录对话框),而 Electron 的 V8 缺了关键符号,导致这些能力在 Electron 下不可用。所以构建前要打三个补丁:
| 补丁 | 作用 |
|---|---|
dsh-disable-hmr.patch |
给 runProfile 加开关,跳过依赖内部 API 的 watch-only HMR |
dsh-disable-native-picker.patch |
让目录选择器在 Electron 下强制用 browse |
dsh-disable-welcome-notice.patch |
移除首启的版本声明和 API Key 引导,直接进入应用 |
根因:dsh 的 loader 通过原生模块获取 Node 内部 ESM loader,该模块依赖 Electron V8 缺失的符号而失效。
补丁按 dsh 版本分目录存放(patches/<dsh-tag>/),升级 dsh 时新增对应目录即可。
六、Windows 受限网络的三坑
在「GitHub 不可达 / Corepack 受限」的 Windows 环境下,npm run package 会踩三个坑,均已验证解法:
- pnpm 版本不一致 :
corepack pnpm --filter在子工作区解析到旧版本报错。解法------手工建一个调用 corepack 的pnpm.cmdshim 放到 PATH 前部。 - fetch-runtime 重复下载 :首次下载被中断后版本戳没写,下次又重下。解法------确认 runtime 下 node.exe / pnpm.exe 存在后,手写
.versions.json版本戳。 - Electron 二进制下载超时 :从 GitHub 下载 Electron 二进制报
ETIMEDOUT。解法------改用 npmmirror 国内镜像。
三者叠加,即可在受限 Windows 环境稳定跑通构建。详细的报错原文和命令,之前《保姆级》那篇已经展开写过,这里不重复。
七、写在最后
DSH Desktop 用「进程内 Host + webserver + 同源数据面」这一套设计,把一个 agent harness 变成了一个真正的桌面应用。它的灵魂只有一句话:抓住「dsh webserver 同源服务页面和 API」这个事实,换来零 CORS、零鉴权、零自定义协议、核心架构零改动。
如果你也在折腾 agent harness 的桌面化,这个思路可以直接抄。
代码已开源 ,Windows / Linux / macOS 都能跑,欢迎 star 支持,也欢迎提 issue 一起完善。
- GitHub:https://github.com/fellow99/dsh-desktop
- 下载地址:https://github.com/fellow99/dsh-desktop/releases/
- 项目主页:https://fellow99.github.io/dsh-desktop-website/
感谢各位关注,欢迎访问我的 GitHub 主页:https://fellow99.github.io/