🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

桌面客户端 lijian-ui/dsh-desktop

DeepSeek Harnessv0.2.0
lijian-ui60 天前有提交已提供中文文档扫描:含敏感能力

把官方 dsh 网页版装进独立窗口,双击即用

35/100
综合分
5
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/18 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查未通过:要求 dsh 0.1.1-rc.2,而最新版为 0.2.1-rc.5;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add lijian-ui/dsh-desktop
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · market
装得上吗
实装验证未通过(download_interrupted),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 60 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
⚠︎ 实装验证未通过(download_interrupted · 2026/9/29) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

要求 dsh 0.1.1-rc.2,而最新版为 0.2.1-rc.5;仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-desktop @ 0.2.0
✓Node 引擎未声明 engines.node
✗dsh CLI 依赖要求 0.1.1-rc.2 · 最新 0.2.1-rc.5 不兼容
✓入口文件main/exports/bin 已声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/3 17:55:30

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量
查看扫描证据(共 36 处,展示前 29 条)
  • 读取本地文件
    src/main/config.ts:72
    if (fs.existsSync(file)) {
  • 读取本地文件
    src/main/config.ts:73
    const raw = fs.readFileSync(file, 'utf-8');
  • 读取密钥环境变量
    src/main/config.ts:92
    const apiKey = process.env.DEEPSEEK_API_KEY || fileConfig.apiKey;
  • 执行外部命令来自配置/变量
    src/main/dsh-process.ts:209
    const child = spawn(command, dshArgs, {
  • 读取本地文件
    src/main/dsh-process.ts:418
    if (fs.existsSync(binDir)) {
  • 读取本地文件
    src/main/dsh-process.ts:424
    if (fs.existsSync(pnpmEntry)) {
  • 写入本地文件
    src/main/dsh-process.ts:433
    fs.mkdirSync(shimDir, { recursive: true })
  • 读取本地文件
    src/main/dsh-process.ts:436
    if (!fs.existsSync(shimPath)) {
  • 写入本地文件
    src/main/dsh-process.ts:441
    fs.writeFileSync(shimPath, `@${nodeArg} "${script}" %*\r\n`, 'utf8')
  • 写入本地文件
    src/main/dsh-process.ts:448
    fs.writeFileSync(shimPath, scriptBody, 'utf8')
  • 写入本地文件
    src/main/dsh-process.ts:449
    fs.chmodSync(shimPath, 0o755)
  • 读取本地文件
    src/main/dsh-process.ts:455
    if (fs.existsSync(pnpmBin) && !paths.has(pnpmBin)) paths.add(pnpmBin)
  • 读取本地文件
    src/main/dsh-process.ts:535
    if (fs.existsSync(c)) return c;
  • 读取本地文件
    src/main/dsh-process.ts:584
    if (!fs.existsSync(officecliFile)) {
  • 写入本地文件
    src/main/dsh-process.ts:594
    fs.chmodSync(officecliFile, 0o755);
  • 执行外部命令来自配置/变量
    src/main/dsh-process.ts:777
    const out = execFileSync(nodePath, ['--version'], {
  • 读取本地文件
    src/main/profile-init.ts:106
    const raw = fs.readFileSync(path.join(profileDir, 'package.json'), 'utf-8');
  • 写入本地文件
    src/main/profile-init.ts:145
    fs.writeFileSync(path.join(profileDir, 'package.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
  • 读取本地文件
    src/main/profile-init.ts:155
    if (!fs.existsSync(source)) {
  • 读取本地文件
    src/main/profile-init.ts:158
    if (fs.existsSync(linkPath) || fs.lstatSync(linkPath, { throwIfNoEntry: false })) {
  • 删除本地文件
    src/main/profile-init.ts:164
    fs.unlinkSync(linkPath);
  • 删除本地文件
    src/main/profile-init.ts:167
    fs.rmSync(linkPath, { recursive: true, force: true });
  • 写入本地文件
    src/main/profile-init.ts:170
    fs.mkdirSync(path.dirname(linkPath), { recursive: true });
  • 写入本地文件
    src/main/profile-init.ts:183
    fs.mkdirSync(profileDir, { recursive: true });
  • 读取本地文件
    src/main/profile-init.ts:193
    if (!fs.existsSync(p)) fs.writeFileSync(p, content, 'utf-8');
  • 写入本地文件
    src/main/profile-init.ts:205
    fs.writeFileSync(path.join(profileDir, 'package.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
  • 读取本地文件
    src/main/profile-init.ts:240
    if (fs.existsSync(skillDir)) return null;
  • 读取本地文件
    src/main/tray.ts:42
    if (fs.existsSync(iconPath)) {
  • 读取本地文件
    src/main/window.ts:38
    return fs.existsSync(iconPath) ? iconPath : undefined;
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/cordis-plugin-group@deepseek-ai/cordis-plugin-hmr@deepseek-ai/cordis-plugin-include@deepseek-ai/cordis-plugin-loader@deepseek-ai/cordis-plugin-timer@deepseek-ai/dsh@deepseek-ai/dsh-agent@deepseek-ai/dsh-agent-default-model@deepseek-ai/dsh-agent-presets@deepseek-ai/dsh-anonymous-user-id@deepseek-ai/dsh-api-gateway
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 独立窗口运行
  • 双击即用
  • 基于官方打包

基于官方 Deepseek Harness 打包的桌面客户端,方便直接安装使用

README

由 DeepSeek 最新模型翻译生成
dsh-desktop(DeepSeek Harness 桌面端) 基于官方 npm 包 @deepseek-ai/dsh 的 Electron 桌面壳。方案 A 的核心思路: Electron 主进程通过子进程启动官方 dsh web,再把其本地 HTTP 页面加载进窗口,从而把官方 WebUI 包装成一个独立桌面应用。 原则:不引用、不改动官方 dsh 任何源码,仅消费官方 npm 包,跟随其 npm update 升级。 架构概览 flowchart TB subgraph Main["Electron 主进程(我们的代码,零原生模块)"] Mgr["DshManagerspawn 官方 dsh + 端口解析崩溃自动重启"] Win["BrowserWindow加载 http://127.0.0.1:port"] Tray["系统托盘显示窗口 / 重启 / 退出"] Menu["中文菜单栏重启 / 关于 / 退出"] end subgraph Child["官方 dsh 子进程(系统 Node 运行)"] Cli["@deepseek-ai/dsh CLIdsh web --host 127.0.0.1 --port 0"] Http["HTTP 服务 + Agent 核心+ 前端静态资源"] end
展开完整文档(README 剩余 1 节)
dsh-desktop(DeepSeek Harness 桌面端)

基于官方 npm 包 @deepseek-ai/dsh 的 Electron 桌面壳。方案 A 的核心思路: Electron 主进程通过子进程启动官方 dsh web,再把其本地 HTTP 页面加载进窗口,从而把官方 WebUI 包装成一个独立桌面应用。

原则:不引用、不改动官方 dsh 任何源码,仅消费官方 npm 包,跟随其 npm update 升级。

架构概览

flowchart TB
subgraph Main["Electron 主进程(我们的代码,零原生模块)"]
Mgr["DshManagerspawn 官方 dsh + 端口解析崩溃自动重启"]
Win["BrowserWindow加载 http://127.0.0.1:port"]
Tray["系统托盘显示窗口 / 重启 / 退出"]
Menu["中文菜单栏重启 / 关于 / 退出"]
end

subgraph Child["官方 dsh 子进程(系统 Node 运行)"]
Cli["@deepseek-ai/dsh CLIdsh web --host 127.0.0.1 --port 0"]
Http["HTTP 服务 + Agent 核心+ 前端静态资源"]
end

Mgr -- "spawn(系统 Node,承担原生模块 / Node 版本)" --> Cli
Cli -- "stdout 解析端口" --> Mgr
Cli --> Http
Mgr -- "端口就绪" --> Win
Win -- "关闭 → 隐藏到托盘" --> Tray
Tray -- "显示主窗口" --> Win
Menu -- "重启 dsh 服务" --> Mgr

- 官方主包只暴露 CLI,没有可 import 的运行时 API,因此采用进程外 spawn。
- 原生模块(node-pty / koffi)与 Node 版本要求全部由系统 Node 跑的官方子进程承担, Electron 侧无需 electron-rebuild,无 ABI 负担。
- 打包版:dsh 及其全部依赖由 asarUnpack 解包到真实文件系统,主进程用「系统 Node 绝对路径 + dsh/lib/bin.js」启动子进程(见「打包版如何找到系统 Node」)。
- 后续若官方发布 embed/SDK,可演进到方案 B(file:// + IPC 桥接),前端代码无需改动。

环境要求

| 依赖 | 版本要求 | 说明 |
| 系统 Node.js | ^22.19.0 \|\| >=24.0.0 | dsh 官方的硬性要求,由子进程使用 |
| npm | 随 Node 自带 | 用于安装依赖 |
| Electron | ^43.0.0(开发依赖) | 仅负责窗口与显示 |

注意:Electron 内置的 Node 版本不满足 dsh 要求,但无所谓——
dsh 跑在独立的系统 Node 子进程里,Electron 只负责显示页面。

打包版如何找到 Node 运行时

v0.2.0 起内置精简 Node 运行时(resources/node-runtime/),打包版无需用户预装 Node,开箱即用。桌面端按以下顺序解析 Node 可执行文件:

1. 内置 Node 运行时(打包版自带,优先)——resources/node-runtime/node.exe(Win)或 bin/node(macOS)
2. config.json 显式配置 nodePath(自定义时用)——例如 macOS: { "nodePath": "/usr/local/bin/node" }

3. 自动探测(nvm → Homebrew → 官方路径),且每个候选都会做版本校验
(dsh 要求 ^22.19 || >=24),不满足的旧版(如系统 v18)自动跳过

内置 Node 由 scripts/fetch-node.cjs 从 node/ 官方发行包精简生成到 vendor/,打包时经 electron-builder.yml 的 extraResources 打进安装包。
开发模式(npm run dev)无内置目录,走 2/3 步探测。

若探测失败且未配置,启动会报错提示。打包版同时要求 dsh 及其全部依赖被 asarUnpack 解包(electron-builder.yml 已配置 /node_modules/),因为系统 Node 无法读取 asar 压缩包内的文件。

安装与运行

进入项目目录
cd electron-app

安装依赖(会同时装上官方 @deepseek-ai/dsh 与 Electron)
npm install

开发模式:先编译 TypeScript,再启动 Electron
npm run dev

或分两步
npm run build # 编译 src -> dist
npm start # 启动 electron .

首次启动会:
1. 主进程 spawn 官方 dsh web --host 127.0.0.1 --port 0;
2. 从子进程 stdout 解析实际端口(系统自动分配,避免冲突);
3. 创建窗口加载 http://127.0.0.1:,看到的就是官方 WebUI。

macOS 未签名应用安装说明

当前版本未做 Apple 开发者签名与公证,首次安装/启动时 macOS Gatekeeper 会拦截,常见报错与解决方法如下:

| 报错提示 | 原因 | 解决方法 |
| 「无法验证开发者」/「Apple 无法检查其是否包含恶意软件」 | Gatekeeper 拦截未签名应用 | 右键应用图标 → 选择「打开」→ 弹窗中再点「打开」(仅首次,之后可直接双击) |
| 「已损坏,无法打开。您应该将它移到废纸篓」 | 下载文件被加了隔离标记(quarantine) | 终端执行 xattr -dr com.apple.quarantine "/Applications/DeepSeek Harness 桌面端.app" |
| 「打不开,因为来自身份不明的开发者」 | 同上 | 系统设置 → 隐私与安全性 → 点击「仍要打开」 |

以上均只需在首次处理一次;应用本体功能不受影响,仅跳过 Gatekeeper 检查。
说明:未签名应用在部分系统设置下可能仍提示,此时请检查「系统设置 → 隐私与安全性 → 安全性」,确认「允许从以下位置下载的应用」中选择了「App Store 和被认可的开发者」之外的选项。

配置

API Key
推荐用环境变量注入(最安全,不写入文件):

export DEEPSEEK_API_KEY="你的密钥"
npm run dev

或在项目根创建 config.json(已被 .gitignore 忽略,切勿提交):

{
"apiKey": "你的密钥", "host": "127.0.0.1", "port": 0,
"extraArgs": []
}

配置优先级:环境变量 > config.json > 内置默认。

其他参数
- host:监听地址,默认 127.0.0.1(仅本机,不暴露网络)。
- port:传 0 让系统分配空闲端口;也可固定(如 3080)。
- extraArgs:需要原样透传给 dsh web 的额外命令行参数数组。
- nodePath:系统 Node.js 绝对路径(打包版必须,见下文「打包版如何找到系统 Node」)。

config.json 的读取位置

桌面端按顺序查找 config.json,找到即用、不合并:

| 场景 | 路径 |
| 开发模式(npm run dev) | 项目根:electron-app/config.json |
| 打包版 Windows | %APPDATA%\dsh-desktop\config.json,即 C:\Users\\AppData\Roaming\dsh-desktop\config.json |
| 打包版 macOS | ~/Library/Application Support/dsh-desktop/config.json |

目录名取 Electron 的 app.getName()(打包后为 package.json 的 name 字段,即 dsh-desktop;注意不是 productName「DeepSeek Harness 桌面端」——该值只配在 electron-builder.yml,不会进入打包后 app.asar 的 package.json),文件不存在时忽略,全部走默认值/环境变量。

目录结构

electron-app/
├── package.json # 依赖与脚本(含官方 @deepseek-ai/dsh、electron-builder) ├── tsconfig.json # TypeScript 配置(CommonJS 输出到 dist/) ├── electron-builder.yml # 打包配置(重点:npmRebuild:false + asarUnpack 原生模块) ├── .npmrc # 国内镜像源(npmmirror + Electron 二进制镜像) ├── .gitignore ├── README.md ├── scripts/ │ ├── build-native.cjs # 打包前置:物化 koffi 原生二进制(best-effort) │ ├── generate-icon.cjs # 图标生成:官方 SVG → build/icon.ico / icon.png(依赖 sharp) │ ├── publish-lib.mjs # 发布公共模块:产物扫描、版本解析、发布说明加载 │ └── publish-github.mjs # 发版脚本:gh CLI 创建 GitHub Release 并上传产物 └── src/ ├── main/ # 主进程代码(Node) │ ├── index.ts # 入口:生命周期、IPC、菜单/托盘串联、错误兜底 │ ├── dsh-process.ts # 核心:DshManager(spawn + 端口冲突重试 + 崩溃自动重启) │ ├── window.ts # 创建 BrowserWindow、关闭→托盘拦截、加载失败兜底错误页、按端口重加载 │ ├── menu.ts # 中文应用菜单(文件/编辑/视图/窗口/帮助 + 重启/关于/退出) │ ├── tray.ts # 系统托盘(显示窗口/重启 dsh/退出,关闭窗口后常驻入口) │ ├── config.ts # 配置读取与 dsh 环境变量组装 │ └── log.ts # 统一日志工具 └── preload/ # 预加载脚本(方案 A 预留桌面集成接口) └── index.ts # 暴露 window.dshDesktop(平台/版本/打开外部链接/重试)

健壮性设计

桌面端在「启动」与「存活」两个维度做了容错,避免白屏或静默崩溃:

| 场景 | 行为 |
| 端口冲突重试 | 若显式配置了固定端口且该端口被占用,自动顺延端口重试(最多 10 次)后再失败。默认 --port 0 由系统分配,不会冲突。 |
| 子进程崩溃自动重启 | dsh 子进程运行期间异常退出时,按指数退避(1s→2s→4s…)自动重启,最多 5 次;重启成功后窗口无缝刷新到新端口。 |
| 加载失败兜底 | 页面加载超时或 did-fail-load(如 dsh 崩溃)时,渲染中文错误页,提供「重新连接」按钮,点击即重启 dsh。 |
| 启动彻底失败 | 首次启动即失败且无窗口时,弹窗提示后退出;有窗口时展示错误页,而非白屏。 |
| 关闭 → 系统托盘 | 点窗口右上角 X(或 Cmd+W)不退出应用,隐藏到系统托盘常驻;托盘菜单「显示主窗口」/双击托盘图标恢复。托盘不可用(个别 Linux 桌面)时关闭窗口直接退出,避免窗口丢失。 |
| 主动退出 | 托盘或菜单「退出」显式结束应用;退出时按进程树 tree-kill,dsh 拉起的孙进程一并清理。 |

重试/重启入口有三处:菜单「文件 → 重启 dsh 服务」、错误页「重新连接」按钮、macOS Dock 重建。
窗口恢复入口有三处:托盘菜单/双击、macOS Dock 激活、二次启动实例聚焦。

打包与原生模块分发

生产分发使用 electron-builder。核心难点是原生模块(node-pty / koffi)随包正确分发,我们的处理原则是「不让 Electron 重编译、只负责解包」:

1. npmRebuild: false(关键)原生模块由系统 Node 运行的 dsh 子进程加载,绝不能让 electron-rebuild 把它们编译成 Electron 内置 Node 的 ABI,否则子进程一加载就崩。配置见 electron-builder.yml。
2. asarUnpack 解包原生目录 Node 无法从 asar 压缩包内加载 .node / .dll / .exe,必须把 node_modules/node-pty/、node_modules/koffi/ 等解包到真实文件系统。
3. build:native 物化 koffi 二进制 koffi 不在 npm 包内附带预编译二进制(运行时自下载到临时目录)。scripts/build-native.cjs 在打包前主动把它物化进 node_modules/koffi/win32_x64/koffi.node,随包分发,避免打包后首次运行依赖联网。该步骤失败不阻断打包(运行时仍可自下载)。

打包命令

开发调试用的免安装目录(验证打包结构)
npm run pack

Windows 安装包(NSIS .exe)
npm run build:electron:win

macOS 双架构(同时产出 x64 + arm64)
npm run build:electron:mac

其他平台 / 自定义参数:透传给 electron-builder
npm run build:electron -- --linux

build:electron 是主命令(build:native → tsc → electron-builder),平台参数通过 -- 透传:Windows 用 --win,macOS 双架构用 --mac --x64 --arm64。

产物输出到 dist-electron/(已在 .gitignore 忽略)。

发布到 GitHub Release

1. 先打包(产出 dist-electron/ 下的安装包 + latest.yml / latest-mac.yml 更新元数据)
npm run build:electron:win

2. 发布(创建/更新 GitHub Release 并上传全部产物)
npm run release:github

- 前置:安装并登录 gh CLI(winget install --id GitHub.cli && gh auth login)。
- tag:自动取 package.json 的 version,生成 v{version}(如 v0.2.0)。
- 发布说明:默认文案;在项目根创建 RELEASE_NOTES.md 即可自定义(Markdown 全文作为 Release Notes)。
- 重复发布同版本:脚本检测到已存在的 release 时会更新说明并 --clobber 覆盖同名附件。
- 产物范围:dist-electron/ 顶层所有 .exe / .dmg / .AppImage / .deb / .zip / .yml / .blockmap(排除 builder- 调试文件)。

自动更新

v0.2.0 起内置 electron-updater,走 GitHub Release 通道:

- 打包时(publish 已配置 GitHub provider)生成 latest.yml(Windows)/ latest-mac.yml(macOS)
- 客户端启动 60 秒后自动检查,发现新版本自动下载,下载完弹窗「立即重启 / 稍后」
- 帮助菜单「检查更新」可手动触发;每小时定时检查
- 发布时务必保证 latest.yml + .zip(macOS 更新用 zip,不是 dmg)+ .blockmap 都上传,否则对应平台无法增量更新(release:github 的产物范围已覆盖)

注意事项

- 自定义图标:官方 Harness 黑色鲸鱼图标已由 scripts/generate-icon.cjs 自动生成到 build/icon.ico(多尺寸)与 build/icon.png,无需手工维护。
- 国内镜像:.npmrc 已配置 npmmirror 与 Electron 二进制镜像,Electron 下载不受影响。
- 首次打包耗时:npm install 会下载 Electron 与官方 dsh 的原生依赖,建议使用国内源。
- dev-preview 风险:@deepseek-ai/dsh 处于 rc 阶段,版本升级可能带来破坏性变更,建议锁定版本并在升级后回归测试(端口解析正则依赖其 stdout 格式)。

已知限制与后续演进

- 此方案不是终态:官方 webserver 注释已预留 file:// + IPC 的桌面形态。
待官方发布正式的 embed/SDK 后,可将传输层从 HTTP 替换为 IPC,前端代码无需改动。
- 端口:当前通过 loopback HTTP 通信,本机任意进程都能访问该端口。方案 B 可消除此面。
- dev-preview 风险:@deepseek-ai/dsh 处于 rc 阶段,版本升级可能带来破坏性变更,建议锁定版本并在升级后回归测试(端口解析正则依赖其 stdout 格式)。
预览分享卡片长什么样 ↓lijian-ui/dsh-desktop
分享 · DeepSeek Harness Hub
lijian-ui/dsh-desktop

把官方 dsh 网页版装进独立窗口,双击即用

这是你的仓库?认领条目

回到顶部