DSH-Desktop
为DeepSeek Harness 打造的桌面端,没有打包DeepSeek Harness,核心可以独立更新
- Stars
- 1
- Language
- JavaScript
- Created
- Aug 15, 2026
- Updated
- Sep 4, 2026
Introduction
鲸港 WhaleHarbor
为 DeepSeek Harness 打造的桌面端体验
中文
鲸港 WhaleHarbor 把 DeepSeek Harness(DSH) 包装成一个原生桌面应用。它负责窗口、托盘、更新与桌面运行环境,同时完整保留官方 DSH 的智能体、模型、工具、会话与 Web UI。
显示品牌为「鲸港 WhaleHarbor」;包名与内部标识(
dsh-desktop、DSH_DESKTOP_*环境变量等)沿用历史命名,行为不变。
DSH 核心不打进安装包:目标机器首次启动时自动通过内置 pnpm 安装最新版 DSH,之后直接复用本机已有的完整安装——既保持轻量,又能随时更新到最新。
零依赖运行:DSH 核心直接跑在 Electron 内嵌的 Node 运行时上(ELECTRON_RUN_AS_NODE),核心的安装/更新用内置 pnpm——最终用户机器上不需要安装 node/npm。(注:MCP 子进程依赖用户 PATH 里的 node/npx,常规开发机均满足。)
DSH 来自哪个 npm 仓库? 核心
@deepseek-ai/dsh发布在官方 npmjs.org(https://registry.npmjs.org)。国内网络下默认使用 npmmirror 镜像(registry.npmmirror.com),探测不可用或安装失败时会自动回退 npmjs.org,也可用DSH_DESKTOP_NPM_REGISTRY指定。安装过程会显示实时进度条与已下载大小。
主要功能
沉浸式桌面窗口无边框(frameless)窗口,DSH Web UI 占满整个屏幕;最小化/最大化/关闭按钮以半透明方式融入界面右上角,不遮挡任何 DSH 内容。 |
系统托盘DeepSeek 鲸鱼托盘图标,右键「打开 / 退出」。开启「常驻通知栏」后,关闭窗口最小化到托盘,后台持续运行。 |
保持最新内置「核心」设置页显示 DSH 版本、一键检查更新,支持自动更新(新版本自动下载并提示重启)。 |
任务通知主任务完成、失败或需要确认时发送桌面通知(子任务完成不打扰);可选「阻止休眠」,任务运行期间防止系统睡眠。 |
继承终端 Profile自动加载终端里的环境变量(PATH 等)传给 DSH,MCP 服务等外部进程能正常找到可执行文件;macOS 从 Finder 启动时没有终端环境变量,此开关默认开启。 | |
与官方项目的关系
本项目基于 deepseek-ai/deepseek-harness 构建,并借助 Cordis 的插件化能力。
官方项目提供核心的智能体能力、插件系统与 Web UI;本项目负责:
- Electron 桌面应用封装(窗口、托盘、生命周期、崩溃重启、进程树清理)
- DSH 的自动安装、定位与更新
- 嵌入 DSH UI 的桌面插件(窗口控制条、设置页、更新徽章)
- Windows / macOS / Linux 安装包构建与 GitHub Actions 自动发布
如果你希望通过命令行运行 Harness,或参与核心功能开发,请优先查看官方仓库。
安装
从 GitHub Releases 下载对应平台安装包:
| 平台 | 格式 |
|---|---|
| Windows | NSIS 安装器(x64) |
| macOS | dmg(Apple Silicon / Intel) |
| Linux | AppImage · deb · rpm |
首次启动会自动安装最新版 DSH(约 250MB,由内置 pnpm 安装,通常几十秒)。最终用户无需安装 Node.js;从源码开发需要 Node ≥ 18。
从源码运行 / 开发
npm install # 安装依赖(首次)
npm start # 启动桌面应用
npm run icon # 重新生成鲸鱼图标(改配色/边距后)
打包
npm run pack # 打包目录到 dist/(快速验证)
npm run dist:win # Windows NSIS 安装器
npm run dist:mac # macOS dmg
npm run dist:linux # Linux AppImage + deb + rpm
GitHub Actions(.github/workflows/build-installers.yml)可在打 v* 标签或手动触发时,跨平台构建并自动发布到 Releases。
工作原理
npm start
└─ Electron 主进程
└─ 定位 DSH:应用托管目录 → node_modules → npm _npx 缓存(最新完整安装)
└─ 没有则 pnpm add @deepseek-ai/dsh@latest(内置 pnpm;registry 探测 + 失败重试/换镜像 + 实时进度条)
└─ 端口预检:被占用则弹「换端口并重试」面板(端口持久化)
└─ spawn: Electron 自身 (ELECTRON_RUN_AS_NODE=1) <dsh>/lib/bin.js --patch <desktop-plugin> --profile web --port X [--no-open]
└─ 桌面插件嵌入 DSH UI:窗口控制条 / 设置页 / 更新徽章
└─ DSH 打印 URL → 应用解析 → loadURL 到 frameless 窗口
└─ 启动/加载失败或插件未挂载 → 回到启动页(自带窗口控制条 + 重试/换端口/退出),
另注入兜底控制条,窗口永远可关闭
└─ 更新:先停 DSH 核心 → 安装新版本(防 Windows 下覆盖运行中文件导致崩溃)→ 重启核心
└─ 退出时 kill 整棵进程树;崩溃时一键重启
└─ 任务事件经本地桥转发 → 桌面通知(可开关)
插件扩展点(RPC 桥)
DSH 插件可以调用桌面壳的能力:桌面通知、托盘右键菜单、插件设置 KV、窗口/任务栏控制、壳事件订阅。壳在本地起一个 HTTP JSON-RPC 桥——只绑 127.0.0.1、每次启动随机端口 + 随机令牌,令牌只通过环境变量传给 DSH 进程,网页与无关本地进程无法调用。
Host 半部(运行在 DSH 进程内的插件)
从 process.env.DSH_DESKTOP_NOTIFY_PORT / DSH_DESKTOP_NOTIFY_TOKEN 读地址与令牌,POST http://127.0.0.1:<port>/,带 x-dsh-notify-token 头,body 为 { "method": "...", "params": { ... } };响应 { ok: true, ... } 或 { ok: false, error }。网络失败按 1.5s~20s 退避重试几次;收到 ok:false 说明对端壳无此方法(旧版本),不必重试。
| 方法 | 参数 | 说明 |
|---|---|---|
bridge.register | plugin, eventPort?, events? | 注册插件(notify/tray/window.* 前先注册;settings.* 不要求)。eventPort 是插件自起的 127.0.0.1 HTTP 端口,壳把托盘点击与订阅事件回投到它(POST {event, data},同一令牌);events 可订阅 window.visibility(显示/聚焦/最小化快照)、core.lifecycle(starting/ready/restarting/exited) |
notify.show | kind?, title?, body?, force? | 桌面通知。kind=done/error/approval 有默认文案;force:true 绕过「窗口聚焦时不弹」抑制(用户显式动作的回执用);受设置「任务通知」总开关约束 |
tray.setMenu | plugin, items | 贡献托盘右键菜单分区([{id, label, enabled?}],≤10 项),点击回投 {event:"tray.click", id} |
settings.get / settings.set | plugin, key? / plugin, key, value | 插件设置 KV(value 限 string/number/boolean,null 删除该 key),持久化在壳的设置文件里 |
window.* | 见左列 | window.progress {value}(任务栏进度:-1 清除 / 0..1 确定 / >1 不确定)、window.flash {flag}(闪烁,窗口获焦自动停)、window.badge {text}(macOS Dock / Linux 角标数)、window.overlay {dataUrl}(Windows 任务栏角标图)、window.alwaysOnTop {flag}、window.show / window.hide / window.minimize |
float.window.* | create / state / move / close / closeAll | 插件浮窗(桌面宠物、迷你状态条等):创建小型透明置顶悬浮窗——永不抢焦点、不进任务栏;state 下行推送 JSON 状态(宠物页面 __dshFloat.onState 接收),页面点击经 __dshFloat.send 回投;核心重启/退出自动清理,限额每插件 3 个,设置「允许插件浮窗」可整体关闭 |
最小示例(注册 + 托盘菜单 + 通知):
// DSH 插件 Host 半部的 apply(ctx) 里
const port = process.env.DSH_DESKTOP_NOTIFY_PORT;
const token = process.env.DSH_DESKTOP_NOTIFY_TOKEN;
if (!port || !token) return; // 非桌面壳环境(裸 dsh 命令行),静默跳过
const call = (method, params = {}) => fetch(`http://127.0.0.1:${port}/`, {
method: "POST",
headers: { "content-type": "application/json", "x-dsh-notify-token": token },
body: JSON.stringify({ method, params })
}).then((r) => r.json()).catch(() => {});
call("bridge.register", { plugin: "my-plugin", eventPort: myEventServerPort, events: ["core.lifecycle"] });
call("tray.setMenu", { plugin: "my-plugin", items: [{ id: "hello", label: "打个招呼" }] });
call("notify.show", { kind: "done", title: "任务完成" });
托盘点击与订阅的事件会 POST 到你上报的 eventPort(同一个令牌头),自行起个小 HTTP 服务接收即可。
Client 半部(运行在 DSH 页面里的插件)
桌面壳给页面注入 window.dshDesktop(普通浏览器直接访问同一页面时不存在,用前判空):
windowAction(action, params)—— 与window.*RPC 同一实现pluginSettingsGet(plugin, key)/pluginSettingsSet(plugin, key, value)—— 同一 KVonShellEvent(cb)—— 接收壳事件
设置页
插件需要设置 UI 时,用核心 Cordis 的 settings.section 槽挂一个整页(桌面版设置区自身就是这么挂的):
// 客户端插件,需 exports.inject = ["slots"]
ctx.slots.register({ name: "settings.section", id: "my-plugin", order: 50, label: "我的插件" }, MySettingsPage);
持久化用上面的插件设置 KV(Host 侧 settings.get/set,页面侧 dshDesktop.pluginSettingsGet/Set)。壳刻意不提供行级声明式设置项——整页槽能覆盖全部场景。
环境变量(可选)
| 变量 | 作用 |
|---|---|
DSH_DESKTOP_PORT | 指定端口(默认 3080) |
DSH_DESKTOP_HOME | 覆盖 DSH_HOME(默认 ~/.dsh) |
DSH_DESKTOP_NPM_REGISTRY | npm 镜像(默认 npmmirror) |
DSH_DESKTOP_NPM_CACHE | npm 缓存目录 |
DSH_DESKTOP_SPEC | DSH npm 规格(默认 @deepseek-ai/dsh@latest) |
DSH_DESKTOP_TIMEOUT | 启动看门狗超时秒数(默认 1800s) |
DSH_DESKTOP_INSTALL_ESTIMATE_MB | 安装进度条估算总大小(默认 250MB) |
DSH_DESKTOP_PNPM_VERSION | 构建期打包的内置 pnpm 版本(默认 10.33.0) |
常见问题
- 端口冲突:应用启动前会预检端口。若 3080 被占用(如浏览器里开着另一个 DSH),会弹出「端口已被占用」面板,可直接换一个端口并重试(端口会记住);或设
DSH_DESKTOP_PORT换端口。 - 首次安装慢/卡住:DSH 核心由内置 pnpm 安装(解析+下载通常几十秒),启动页有进度条 + 已下载大小。若网络下载慢,设镜像:
$env:npm_config_registry = "https://registry.npmmirror.com"。 - 启动失败看不到原因:所有启动/崩溃错误都会显示在启动页的错误面板(含最近日志),提供「重试 / 换端口并重试 / 退出」。
- 点更新没反应或崩溃:更新会先停掉 DSH 核心再安装新版本(避免 Windows 下覆盖运行中文件导致崩溃),失败时可选择「用当前版本继续」。
- 常驻通知栏看不到托盘图标:开启「常驻通知栏」后立即创建托盘图标(无需先点一次关闭)。
- electron 下载超时(GitHub 不可达):
npm install前设$env:ELECTRON_MIRROR = "https://npmmirror.com/mirrors/electron/"。 - 任务通知不弹:在「设置 → 桌面版」确认「任务通知」已开启;另外应用窗口有焦点且可见时不弹(你正在看着界面,任务状态已内联显示),只在后台/最小化/藏托盘时才通知。
- 通知安全:任务通知走本机回环 HTTP 桥(
127.0.0.1),每次启动使用随机端口 + 随机令牌,网页和无关本地进程无法伪造或刷屏通知。 - macOS 报"已损坏,无法打开" / "无法验证开发者":当前未签名,这是 macOS Gatekeeper 对下载的未签名 App 的拦截(Apple Silicon 上 arm64 版最常显示"已损坏")。不是包坏了,壳有完整的 x64 和 arm64 版本(release 里的
*-arm64.dmg)。临时绕过:右键应用 → 打开,或终端执行xattr -dr com.apple.quarantine "/Applications/DeepSeek Harness Desktop.app"。要彻底解决需给 macOS 包签名+公证(见下文「macOS 签名与公证」)。 - macOS 签名与公证:
- 不买账号也能签(自制自签名证书):仓库 Secrets 配
CSC_LINK(.p12 的 base64)+CSC_KEY_PASSWORD+CSC_NAME(证书名)。本仓库已附一个生成好的自签名证书(见工作流注释)。注意自签名证书只在本机/信任它的 Mac 上免提示打开,其他用户仍需右键 → 打开。 - 彻底解决(付费 Apple Developer 账号):Developer ID 证书 + 公证凭据
APPLE_ID/APPLE_APP_SPECIFIC_PASSWORD/APPLE_TEAM_ID(或APPLE_API_KEY/APPLE_API_KEY_ID/APPLE_API_ISSUER),配齐后 GitHub Actions 自动签名并 notarytool 公证,所有 Mac 双击即开。 - 什么都没配时:
scripts/mac-sign.js自动做 ad-hoc 自签名,"已损坏" → "无法验证开发者"(右键 → 打开 可用)。
- 不买账号也能签(自制自签名证书):仓库 Secrets 配
- 与浏览器里的 DSH 共用数据:默认共用
~/.dsh,会话互通。
目录结构
dsh-desktop/
├── main.js # Electron 主进程:生命周期、IPC、托盘、设置、通知桥
├── preload.js # contextBridge:暴露 dshDesktop.* 给 DSH 页面
├── splash.html # 启动页
├── dsh-desktop-plugin/ # 嵌入 DSH UI 的桌面插件(窗口控制条/设置页/更新徽章)
│ ├── client.js # 浏览器端:Slot UI
│ └── index.js # Host 端:任务事件 → 通知桥
├── scripts/ # 图标生成 / exe 图标嵌入 / 插件市场、pnpm 拉取
├── build/ # 鲸鱼 SVG 与各平台图标
├── .github/workflows/ # GitHub Actions 跨平台构建发布
├── AGENTS.md # 面向 AI agent 的开发指南(含踩坑记录)
└── LICENSE # MIT
License
本项目遵循 MIT License。
本项目是基于 DeepSeek Harness 构建的社区桌面版本,并非 DeepSeek 官方产品。