Back to home

AEicn

dsh-desktop

将 DeepSeek Harness 封装为 Windows 便携桌面应用。 WebView2 原生窗口,内置 Node 与 dsh(构建时打包、运行时零下载)。

Stars
1
Language
C#
Created
Aug 14, 2026
Updated
Aug 14, 2026

Introduction

DeepSeek Harness 桌面版(DshDesktop)

DeepSeek Harness 封装为 Windows 便携桌面应用。 WebView2 原生窗口,内置 Node 与 dsh(构建时打包、运行时零下载)。

✨ 核心功能(保持精简)

功能说明
免装 Node内置独立 Node 运行时(构建时打包到 runtime/node,目标机器零依赖)
内置 dsh CLI完整打包 @deepseek-ai/dsh 及全部插件(runtime/dsh,离线可用)
无边框窗口自绘 36px 标题栏(⋯ 菜单 + 窗口控制)、标题栏颜色跟随 harness 外观设置(设置页「外观」:浅色/深色/跟随系统)、Win11 圆角、边缘拖拽调整大小(悬停显示缩放光标);⋯ 菜单与托盘菜单(含二级菜单)为 Win11 圆角
系统托盘常驻关闭默认隐藏到托盘;菜单(颜色跟随主题):显示 / 检查更新 / 日志 / 退出
退出即清理Job Object(KILL_ON_JOB_CLOSE)托管 harness 进程树,正常/崩溃/强杀均无孤儿进程
便携版数据跟随 exe(data/ 目录),拷到 U 盘即用
跟随官方更新检测 dsh 新版本 → 同意后 npm overlay 安装(staging 原子切换)→ 失败自动回退内置版
文件预览「文件」标签页(与 对话/轨迹 同级):VSCode 风格文件树 + 单击预览(文本/图片/HTML)+「本会话修改」次级页(按轮次分组显示本会话改过的文件)
任务/权限提醒会话任务跑完弹托盘通知(点击回到窗口);agent 请求权限(需审批)弹警告通知;agent 询问抉择选项(ask_user_question)时弹提醒(携带问题文本,点击回到窗口作答);子代理任务不通知、30 秒去重
余额显示对话底部 dock:余额 ¥X(15 分钟刷新,查询失败自动隐藏),悬停仅提示点击跳转用量页面(不展示充值明细),点击用系统默认浏览器打开用量页面

🚀 快速开始

使用成品(免构建)

项目根目录 dist\ 即为便携版:

  1. 进入 dist\,双击 DshDesktop.exe
  2. 零下载启动(内置 Node + dsh 已打包)
  3. 数据生成在 dist\data\,随目录移动即"便携"

从源码构建

需要:.NET SDK 9.0+ + 网络(首次打包内置运行时)

git clone https://github.com/AEicn/dsh-desktop.git && cd dsh-desktop

# 1) 打包内置运行时(Node + dsh,构建机执行一次)
powershell -ExecutionPolicy Bypass -File scripts/fetch-runtime.ps1

# 2) 构建并生成 dist\(自动复制 runtime/assets,清理中间副本)
powershell -ExecutionPolicy Bypass -File scripts/make-dist.ps1
# 或加 -KeepCache 保留 runtime 缓存加速重建

# 3) 更新应用并保留用户数据(dist\data 重建前移出、重建后移回)
#    本地工具(不入库,见 .gitignore);克隆仓库的用户直接跑 make-dist.ps1 即可
powershell -ExecutionPolicy Bypass -File scripts/update-dist.ps1
powershell -ExecutionPolicy Bypass -File scripts/release.ps1

📁 项目文件说明(每个文件的作用)

源码(src/)

文件作用
Program.cs入口:单实例互斥、全局异常捕获、启动主窗体
Paths.cs便携路径解析:exe 可写 → 数据跟随 exe;否则回退用户目录。定义 RootDir/RuntimeDir/DshHome/WebView2DataDir/LogsDir
AppConfig.cs配置加载/保存(data/config.json):workspace、端口、更新开关、托盘开关
Log.cs简单日志器:追加写 logs/app.log(UTF-8 无 BOM)
NodeRuntime.cs内置 Node 检查:runtime/node/node.exe 存在性 + 版本自检(构建时打包,运行时零下载)
DshBundler.cs内置 dsh 管理:内置版(只读基线)+ overlay 更新(data/agent,staging 原子切换)+ 版本查询
HarnessLauncher.cs启动 dsh --profile web --port <free> 子进程(内置 node 直调 bin.js),等待 HTTP 就绪;HarnessInstance 持有进程 + Job
ProcessJob.csWindows Job Object 封装(KILL_ON_JOB_CLOSE):应用退出即终止整个进程树
WindowChrome.cs无边框窗口外观骨架:Win11 圆角开关、WS_THICKFRAME 清除(白边根源)、WM_NCHITTEST 8 方向 resize 命中、菜单弹窗圆角
MainForm.cs主窗体:无边框 + 自绘标题栏(拖动/最小化/最大化/关闭/菜单)、WebView2 加载 harness、导航 origin 校验、更新检查 UI;DarkMenuRenderer 深色菜单
TrayIcon.cs系统托盘:深色菜单(显示/检查更新/日志/退出)、首次隐藏提示一次、自绘鲸鱼图标兜底
Updater.cs更新器:查询 npm registry 最新版 → 比对 → 调用 DshBundler 更新 → 结果回传
PluginSync.cs配套插件同步:把 assets/plugins/ 复制到便携 DSH_HOME 的 web profile 并全量重建 cordis.patch.yml(幂等,自动清理旧条目)
ShellEndpoint.cs桌面壳事件端点:监听 127.0.0.1 随机端口,接收宿主插件(dsh-notify 提醒、dsh-appearance 外观同步)POST 的事件,弹托盘通知或切换标题栏/菜单主题
Security.csWebView2 导航 origin 校验(仅允许本地 harness origin)

配套插件(assets/plugins/)

目录作用
dsh-files/文件树宿主插件(运行在 harness 内):注册 /api/dsh-files/list(目录列表)与 /api/dsh-files/read(读文件,类型判定)路由,仅回环可访问
dsh-files-client/文件树客户端插件(注入 Web UI):「文件」标签页 —— VSCode 风格懒加载文件树 + 单击预览(文本/图片/HTML iframe)+「本会话修改」次级页(session.history 提取 tool/call 按轮次分组);会话 cwd 从 session.list RPC 获取
dsh-notify/提醒宿主插件:监听 harness 事件流(apiProxy host 流:任务跑完 running→false;mux 流:approval/requested 权限请求、question/requested 抉择询问)→ POST 桌面壳事件端点(DSH_DESKTOP_SHELL_URL);子代理任务不通知、30 秒去重,权限请求逐条提醒,询问携带问题标题/正文逐条提醒
dsh-appearance/外观同步宿主插件:切换事件(settings/updated/settings/document-updated)只作为检查信号——延迟一拍读 ui-theme 命名空间的当前值(以持久化值为唯一事实,不信任事件载荷)→ 值变化时 POST 桌面壳 {type:"appearance", preference};永久 2 秒轮询兜底自愈;桌面壳据此给标题栏与菜单配色,「跟随系统」读 Windows 应用模式
dsh-balance/余额宿主插件:读 DSH_HOME/.credentials.yamlDEEPSEEK_API_KEY → 查询官方 /user/balanceGET /api/dsh-balance/query(仅回环)
dsh-balance-client/余额客户端插件:注册 conversation.composer.dock(对话底部统计栏),显示「余额 ¥X」(15 分钟刷新,查询失败不显示),悬停仅提示点击跳转用量页面(不展示充值明细),点击跳转 usage 页面(桌面壳转系统浏览器)

构建脚本(scripts/)

文件作用
fetch-runtime.ps1构建时打包:下载 Node 24 官方 zip → runtime/node;内置 npm 安装 @deepseek-ai/dshruntime/dsh;精简(删 PDB/非 win32 二进制/C++ 源码,注意保留 koffi/src 等 JS 入口)
make-dist.ps1一键构建:确保 runtime 存在 → dotnet publish → 生成根目录 dist\ → 清理 %TEMP% 构建目录(项目树零中间产物)
update-dist.ps1本地更新工具(不入库,见 .gitignore):调 make-dist.ps1 -KeepCache 重建 dist\,重建前后把 dist\data 移出/移回,更新应用不丢用户数据(会话/配置/凭据/WebView2 档案)
release.ps1本地发布工具(不入库,见 .gitignore):commit 全部改动 → fetch 集成远端(rebase/合并,本地优先)→ push → 强制打 v<版本> tag 并推送(触发 CI 构建 dist 并生成 GitHub Release);版本只读 DshDesktop.csproj<Version>,可用 -Message 自定义提交信息、-Token 提供 GitHub PAT;首次运行自动初始化 git(init/remote/分支/身份)

其他

文件作用
DshDesktop.csproj项目定义:WinForms + WebView2、ApplicationIcon(exe 鲸鱼图标)、发布时复制 runtime/assets、排除非源码目录防误编译、中间产物重定向到 %TEMP%
Directory.Build.propsMSBuild 属性(Sdk.props 前导入):NuGet restore 文件重定向到 %TEMP%,项目根零 obj/
app.manifestDPI 感知、Win10/11 兼容、asInvoker
assets/icon.icoDeepSeek 鲸鱼图标(窗口/托盘/exe)
docs/ARCHITECTURE.md架构总览
docs/RULES.md开发与构建规则(必须遵守的约定)
.github/workflows/build.ymlCI:tag 触发 → fetch-runtime → make-dist → 打包 zip 发布

📁 运行期目录(dist\ 内自动生成)

dist\
  DshDesktop.exe            # 主程序
  runtime\node\             # 内置 Node
  runtime\dsh\              # 内置 dsh(离线)
  assets\plugins\           # 配套插件(同步到 web profile)
  data\
    dsh-home\               # 便携 DSH_HOME(会话/配置/凭据)
    agent\                  # 更新 overlay(已更新的 dsh)
    webview2\               # WebView2 用户数据
    config.json             # 应用配置
  logs\                     # 日志

⚙️ 配置(data/config.json)

{
  "workspace": "C:\\Users\\you\\Projects",
  "port": 0,
  "useBundledDsh": true,
  "startupTimeoutSeconds": 120,
  "minimizeToTray": true,
  "autoCheckUpdates": true
}
  • 标题栏与菜单颜色跟随 harness 设置页的「外观」选项(浅色 / 深色 / 跟随系统),切换后实时生效
  • appearancePreference(内部缓存):最近一次 harness 外观偏好,启动即按它渲染配色(无需等待同步);由应用自动读写,无需手改

环境变量:

  • DSH_DESKTOP_PORTABLE=1 强制便携模式
  • DSH_DEBUG_PORT=9333 WebView2 远程调试(开发用)
  • DSH_NODE_MIRROR / NPM_CONFIG_REGISTRY 构建时镜像(可选)
  • DEEPSEEK_API_KEY / DEEPSEEK_API_BASE 余额查询(默认读 DSH_HOME/.credentials.yaml

🔄 更新机制

  1. 启动后(或菜单「检查 dsh 更新…」)查询 npm registry 最新版
  2. 发现新版 → 询问是否更新
  3. 同意后:npm 安装到 data/agent-staging → 校验 → 原子切换为 data/agent(不碰内置包)
  4. 失败保留当前版本;启动时 overlay 优先、内置兜底;重启生效

🛡️ 安全设计

  • 导航围栏:WebView2 仅允许本地 harness origin,外部链接交给系统浏览器
  • 路由围栏:插件路由仅接受回环(127.0.0.1/::1)请求
  • 进程隔离:harness 进程树挂入 Job Object,退出即清理

📄 License

MIT — 见 LICENSE