保姆级:从 0 到 1 用 Electron 封装 DeepSeek Harness

0 阅读3分钟

承接上篇:这个桌面应用是 AI 从规划到实现全程完成的,本篇把可复现的落地步骤完整贴出来——从同级 clone 到 2 个补丁到跑通,附 Windows 受限网络三坑。

上一篇《让AI把 DeepSeek Harness 做成桌面应用》,我讲了架构——进程内 Host + webserver + localhost 同源,零上游改动,而且这整个应用是 AI 从规划到实现全程完成的

这一篇直接上干货:怎么把 AI 写好的代码跑起来。从同级 clone、到 2 个补丁、到一键构建,全程照抄就能复现,文末还附上我在 Windows 受限网络下踩的 3 个坑。

如果你也想过给某个 agent 框架套个桌面壳,却被「Node 内部 API 在 Electron 下不可用」劝退过——这篇就是写给你的。


一、先交代:要 clone 什么

这个工程消费 deepseek-harness(dsh)和 dsh-market(插件市场),方式是同级目录源码引用(不是 git submodule)。构建前先把它们 clone 到本工程的同级目录:

# dsh:锁定 tag = dsh-v0.1.5-rc.2(与 patches/dsh-v0.1.5-rc.2/ 对应)
git clone --branch dsh-v0.1.5-rc.2 https://github.com/deepseek-ai/deepseek-harness.git ../deepseek-harness
git clone --branch v1.26.0    https://github.com/dsh-market/dsh-market.git         ../dsh-market

为什么同级目录而非 submodule?因为 @deepseek-ai/dsh(apps/cli)只暴露 bin,没有 main/exportsrunProfile 只能从源码 import。源码引用能直接 import 任意 host/client 包、能自建前端 dist、能紧跟上游迭代。

如果缺 ../dsh-marketcollect-dsh.mjs 会硬失败(打包产物需要把它物化为 dsh-dist/node_modules/dshmarket)。

二、自定义 desktop profile

dsh 用 profile 声明「组合哪些插件」。我们要的是「web 组合,微调为桌面」,所以新建一个 desktop profile:

profiles/desktop/
├── package.json          # dsh.profile.bundles = [dsh-base, dsh-web-app]
└── cordis.patch.yml      # 覆盖 web-runtime.printUrl: false(不打印 URL 行)

关键就两点:

  • dsh.profile.bundles = [dsh-base, dsh-web-app]——复用 web 组合,不 fork。
  • cordis.patch.yml 里把 web-runtimeprintUrl 关掉(桌面应用启动时不需要往终端打 URL),端口则由启动代码注入(--port 0)。

三、主进程挂起 dsh Host

桌面壳的主进程就干一件事:动态 import dsh 的 runProfile,把 Host 挂起来,就绪后建窗加载。

// src/main/host.ts
const { ctx, shutdown } = await runProfile('desktop', ['--port', '0']);
// --port 0 = OS 分配空闲端口;读 ctx.webServer.port 拿实际端口
const url = `http://127.0.0.1:${ctx.webServer.port}/`;

// src/main/windows.ts
win.loadURL(url);  // 渲染进程同源加载 dsh Web UI

托盘/通知这类桌面能力,主进程直接订阅 ctxsession/event(进程内,无需 HTTP/WS),触发 Electron Notification / Tray

四、两个补丁,为什么必须有

dsh 依赖 Node 内部 API(HMR、原生目录对话框),而 Electron 的 V8 是 -electron 分支,缺了关键符号,导致这些能力在 Electron 下不可用。所以构建前要打两个补丁:

补丁作用
dsh-disable-hmr.patchrunProfileDSH_DISABLE_HMR 开关,跳过依赖 --expose-internals 的 watch-only HMR
dsh-disable-native-picker.patch让目录选择器在 Electron 下强制用 browse(原生对话框 worker 用 electron.exe 启动会失败)

根因:dsh 的 loader 经 node-addon-require-builtin 原生模块获取 Node 内部 ESM loader,该模块依赖 Electron V8 缺失的 GetAlignedPointerFromEmbedderData 符号而失效。

补丁按 dsh 版本分目录存放(patches/<dsh-tag>/),升级 dsh 时新增对应目录即可。

五、完整命令清单(一键跑通)

# ① 本工程依赖(npm:Electron / Forge / Vite)
npm install

# ② dsh 依赖(在 ../deepseek-harness 下执行;pnpm 工程)
cd ../deepseek-harness && pnpm i

# ③ 一键构建 dsh:git apply 2 个补丁 → pnpm install → build host/client/web → build dsh-market
#    (幂等,--reverse --check 检测已应用则跳过)
npm run build:dsh

# ④ 开发模式:Vite 构建 + 启动 Electron,主进程挂起 dsh Host 并加载 Web UI
npm start

# ⑤ 打包:prepackage 自动 collect(pnpm deploy 物化 dsh 产物到 dsh-dist/)
npm run package

# ⑥ 出分发制品:Windows Squirrel 安装器 / 免安装 ZIP
npm run make

打包产物 out/DeepSeek Harness Desktop-win32-x64/ 已含 dsh(lib + node_modules + web dist + profile),exe 可直接运行。

六、目录结构对照

deepseek-harness-desktop/      # 本工程(Electron 桌面壳)
├── docs/                      # 产品概念设计
├── specs/                     # 规范文档(8 模块 + 201-dsh-market)
├── patches/                   # dsh 上游补丁(git apply)
├── scripts/                   # build-dsh.mjs / collect-dsh.mjs / fetch-runtime.mjs
├── profiles/desktop/          # 自定义 desktop profile
├── src/
│   ├── main/                  # index.ts / host.ts / windows.ts / tray.ts / notifications.ts ...
│   ├── preload/index.ts
│   └── renderer/renderer.ts   # 极薄渲染入口
├── forge.config.ts
└── resources/                 # 应用图标、托盘图标

七、Windows 受限网络的 3 个坑

在「GitHub 不可达 / Corepack 受限」的 Windows 环境,npm run package 会踩三个坑。均已验证解法:

坑 1:pnpm 版本不一致

deepseek-harness 锁定 pnpm@11.7.0,但 corepack pnpm --filter 在子工作区解析到旧版本报错:

This project is configured to use 11.7.0 of pnpm

解法——手工建一个调用 corepack 的 pnpm.cmd shim,放到用户可写目录并置于 PATH 前部:

$shimDir = "C:\Users\$env:USERNAME\AppData\Local\pnpm-shim"
New-Item -ItemType Directory -Path $shimDir -Force | Out-Null
$corepackCmd = (Get-Command corepack.cmd).Source
Set-Content -Path (Join-Path $shimDir "pnpm.cmd") -Value @"
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
call "$corepackCmd" pnpm %*
"@ -Encoding ASCII
$env:PATH = "$shimDir;$env:PATH"

坑 2:fetch-runtime 重复下载

首次下载被中断后,runtime/node/runtime/pnpm/ 已就位但版本戳没写,下次又重下。

解法——确认 runtime/node/node.exeruntime/pnpm/pnpm.exe 存在后,手写版本戳:

'{"node":"24.11.1","pnpm":"9.15.9","platform":"win32","arch":"x64"}' |
  Set-Content -Path .\runtime\.versions.json -Encoding ASCII -NoNewline

坑 3:Electron 二进制下载超时

electron-forge package 从 github.com 下载 Electron 二进制,受限网络时报 connect ETIMEDOUT

解法——改用 npmmirror 国内镜像:

$env:ELECTRON_MIRROR = "https://registry.npmmirror.com/-/binary/electron/"
$env:ELECTRON_CUSTOM_DIR = "v{{ version }}"
npm run package

三者叠加(pnpm shim 入 PATH + 版本戳就绪 + Electron 镜像)即可在受限 Windows 环境稳定跑通 npm run package / npm run make

八、写在最后

以上就是 Electron 封装 DeepSeek Harness 的完整落地路径:同级 clone → desktop profile → runProfile → 2 个补丁 → 一键构建。踩过的坑(Windows 受限网络三连)也都贴了报错原文和解法。

代码已开源github.com/fellow99/de…,欢迎 star 支持。

感谢各位关注,欢迎访问我的 GitHub 主页:fellow99.github.io/

下一篇预告:把同一个 dsh 搬上鸿蒙——Electron-on-HarmonyOS 运行时 + 6 个补丁,在鸿蒙真机上跑起 AI Agent。关注我,别错过。