DSH Desktop:把 DeepSeek Harness 变成真正的桌面应用

很多人以为,把一个开源的 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 会踩三个坑,均已验证解法:

  1. pnpm 版本不一致 :corepack pnpm --filter 在子工作区解析到旧版本报错。解法------手工建一个调用 corepack 的 pnpm.cmd shim 放到 PATH 前部。
  2. fetch-runtime 重复下载 :首次下载被中断后版本戳没写,下次又重下。解法------确认 runtime 下 node.exe / pnpm.exe 存在后,手写 .versions.json 版本戳。
  3. 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://fellow99.github.io/

相关推荐
hpoenixf2 小时前
从“研究日志”到自然对话:我们如何让 AI Agent 的回答真正流畅起来
agent
运维开发王义杰2 小时前
让 AI 写文档、建 Skill,算知识沉淀吗?
agent
坊钰3 小时前
【LangGraph框架入门级】2. 图核心概念:State、Node、Edge 与 Reducers
python·ai编程·langgraph
Rain的Java大神之路3 小时前
[特殊字符]8年Java老兵转型AI Agent:90%的人挂在同一个坑,根本不用学Python!(万字实战复盘,建议收藏)
java·数据库·人工智能·后端·python·spring·ai编程
程序猿DD3 小时前
不知道自己的软件好不好用?让Agent扮演“新用户”来试试!
agent
hpoenixf4 小时前
Agent 已经生成答案,为什么还需要一个独立的发布组件?
agent
guslegend4 小时前
工程地基、Provider 与 ReAct 循环:Agent 核心能力的设计、实现与验收
人工智能·agent·react·架构设计·spring ai
threerocks4 小时前
绝处求生!免费、低价渠道用上 Opus 5.5 全网最严谨白嫖与实战评测地图
aigc·openai·ai编程