DSH × Tauri 桌面客户端

August 22, 2026 · View on GitHub

依据:根目录 dsh-tauri-plan.md(方案) 本文档将方案转成可执行计划:WBS 任务分解、依赖关系、验收标准、排期与风险登记。 配套落地文档:docs/01-architecture.md ~ docs/05-verification.md

1. 目标与范围

目标:把 dsh web(本地 HTTP 服务 + 浏览器 UI)包装成一个 macOS 桌面应用 —— 双击启动、自动拉起/关闭 dsh 服务端、原生 WebView 承载 UI,不修改 dsh 与前端任何代码。

v1 范围(继承方案 §7 决策):

决策点选择
运行时方案A:捆绑 Node runtime + dsh 依赖树(自包含、版本锁定)
目标平台macOS(首发) + Windows x64(已适配);Linux 仍预留
客户端壳壳 + 设置窗口 + 托盘 + 开机自启
分发方式个人自用,本地打包(macOS: dmg;Windows: NSIS),不做签名公证

明确不做(v1 不投入):

  • 签名 / 公证 / 上架
  • Linux 适配(仅代码层面预留)
  • bun --compile 单文件化(方案 C,列为后续优化,不阻塞主线)
  • 修改 dsh 源码、修改 dsh 前端

2. 交付物与仓库布局

代码仓库建议直接落在本工作区:

dsh/
├─ dsh-tauri-plan.md          # 方案(已有)
├─ docs/                      # 计划与落地文档(本次新增)
│  ├─ 00-project-plan.md      # 本文档:计划
│  ├─ 01-architecture.md      # 架构落地设计
│  ├─ 02-server-manager.md    # Server Manager 详细设计
│  ├─ 03-runtime-bundling.md  # 运行时捆绑(方案 A)构建管线
│  ├─ 04-tauri-shell.md       # Tauri v2 壳实现规格
│  └─ 05-verification.md      # 验收与验证清单
├─ plugins/                   # 插件工作区(已有,与本项目无关)
└─ dsh-desktop/               # 客户端工程(M0 起创建)
   ├─ package.json
   ├─ src/                    # 本地壳 UI(设置页/加载页,轻量 React)
   ├─ scripts/                # 构建期脚本(vendor-node / vendor-dsh / prune)
   ├─ src-tauri/
   │  ├─ Cargo.toml
   │  ├─ tauri.conf.json
   │  ├─ capabilities/
   │  ├─ resources/           # 构建期生成:dsh 安装树 + node 二进制
   │  └─ src/
   │     ├─ main.rs           # 生命周期编排、Tauri commands
   │     ├─ server.rs         # Server Manager
   │     ├─ window.rs         # WebView 导航、错误页、外链拦截
   │     ├─ settings.rs       # 设置持久化(DSH_HOME、端口策略等)
   │     └─ tray.rs           # 托盘
   └─ dist/                   # 壳 UI 构建产物

3. 已核实的关键事实(落地依据)

以下事实已在本机 @deepseek-ai/dsh@0.1.0-rc.8(安装于 /opt/homebrew/lib/node_modules/@deepseek-ai/dsh)上验证:

事实验证结果
版本0.1.0-rc.8,bin 入口 lib/bin.js
web 别名bin.js 硬编码:web = --profile web(L91-94)
启动命令node <dsh>/lib/bin.js web --port 0
URL 输出行dsh web: http://127.0.0.1:<port>(LOOPBACK_HOST 恒为 127.0.0.1);有 LAN 候选时行尾追加 (LAN: http://<ip>:<port>)
端口默认 3080;--port 0 由 OS 分配,实际端口读 ctx.webServer.port
profile 位置$DSH_HOME/profiles/web(DSH_HOME 默认 ~/.dsh,环境变量可覆盖)
前端 dist@deepseek-ai/dsh-web-frontend/dist,由 dsh-host-frontend-static 提供
依赖树体积342M,含 node-pty / sharp / koffi / landlock-run 等原生 .node 模块
信任边界loopback IP 字面量自带信任,无需 --trusted-host

4. WBS 任务分解

M0 验证(方案 §5 的 M0)

ID任务依赖产出验收标准
M0.1手动跑通 dsh web --port 0,确认 stdout URL 行与端口语义本机已装 dsh实测记录dsh web: http://127.0.0.1:<随机端口> 出现在 stdout;--port 0 生效
M0.2URL 解析原型:小脚本逐行读 stdout、正则提取 URLM0.1scripts/probe-dsh.mjs 原型能拿到端口并 TCP 连上;输出行带 LAN 后缀也能正确提取
M0.3Tauri v2 空壳工程初始化;主窗口加载外部 HTTP URL本机 Rust 工具链dsh-desktop/ 空壳npm run tauri dev 后窗口能显示 dsh UI
M0.4WebView 兼容性冒烟:SSE 流式输出、fetch、键盘输入M0.3冒烟记录对话流式渲染正常、方向键/复制粘贴正常

M0 全过 → 进入 M1。M0.1/M0.2 可用半天完成,M0.3/M0.4 半天到一天。

M1 最小可用(macOS)

ID任务依赖产出验收标准
M1.1工程骨架落地:tauri.conf.json 窗口配置、capabilities 最小集、main.rs 编排M0.3src-tauri 骨架主窗口先显示本地加载页,server 就绪后导航
M1.2Server Manager:spawn → stdout 解析 → TCP 健康检查M0.2, M1.1server.rs 核心启动时序全通;60s 超时与失败路径有明确表现(错误页)
M1.3退出清理:窗口关闭 → SIGTERM → 5s 宽限 → SIGKILL,按进程组清理M1.2生命周期代码退出后 pgrep -f "bin.js web" 无残留,无孤儿孙进程
M1.4崩溃重启:kill -9 子进程后自动重启(带退避),超限进错误页M1.2重启策略手动 kill -9 后服务自动恢复;连续崩溃 5 次后停在错误页
M1.5单实例:tauri-plugin-single-instance,二次启动聚焦已有窗口M1.1单实例代码双开时第二个实例不创建新服务端
M1.6加载/错误 UI:加载页、stderr 错误页(可查看日志)M1.2壳 UI 初版启动中显示加载页;启动失败显示原因与日志

M1 验收:双击启动 → 窗口显示 dsh UI;退出无残留进程;kill -9 自动恢复;双开只一个实例。M1 结束即有可用产品。

M2 打包分发

ID任务依赖产出验收标准
M2.1方案 A 资源捆绑脚本:vendor-dsh.mjs(npm i --omit=dev)+ vendor-node.mjs(下载平台 Node 二进制)M1.2resources/ 生成物 + 脚本构建期自动生成 resources/dsh/resources/node/,版本锁定并记录校验和
M2.2启动探测链:bundled → 用户配置 → 系统 PATHM1.2resolve_runtime()优先用捆绑运行时;缺失时按配置/PATH 降级,均有日志
M2.3dmg 打包 + 全新机器验证M2.1, M2.2target/release/bundle/dmg/*.dmg在干净用户/机器上装可跑,从 Finder 启动不依赖 PATH
M2.4体积裁剪(优化项):web profile bundle 闭包、prune 无用原生模块M2.1裁剪脚本体积 ≤ 250M(当前 342M,--omit=dev 先行);不破坏启动

M3 增强

ID任务依赖产出验收标准
M3.1托盘:状态显示(运行中/端口)、打开主界面、重启服务、退出M1.2tray.rs托盘菜单四项功能可用,状态与真实服务同步
M3.2设置窗口:DSH_HOME、端口策略(自动/固定)、日志查看、开机自启开关M1.6settings.rs + 设置页 UI四项设置可读写并持久化,重启后生效
M3.3开机自启(LoginItems)M3.2自启代码开关持久化,重启系统后自动启动客户端
M3.4升级策略:内置 dsh 版本随 app 发布;替换 app 后旧符号链接自愈验证M2.x发布检查清单覆盖安装后首次启动正常(依赖 dsh 自愈)
M3.5跨平台:进程组清理/路径/vendor/NSIS;Windows x64 实际适配,Linux 仍预留M1.x平台分支 + NSISWindows 按 docs/05 §4.5 可验收;Linux 仅脚本/cfg 预留

5. 依赖关系与关键路径

M0.1 → M0.2 ─┐
M0.3 → M0.4 ─┴→ M1.1 → M1.2 → M1.3 → M1.4 ─→ M1 验收
              │        └──→ M1.5(M1.1 后任意)
              └──────────→ M1.6(依赖 M1.2)
M1.2 ─→ M2.1 → M2.2 → M2.3 → M2.4(优化)
M1.2 ─→ M3.1 / M3.2(M3 与 M2 并行)
  • 关键路径:M0.3 → M1.1 → M1.2 → M1.3 → M1 验收 → M2.1 → M2.2 → M2.3
  • M3 与 M2 无强依赖,可在 M2.1 完成后并行推进。
  • M2.4(裁剪)与 M3.x 均为优化项,不阻塞主线。

6. 排期建议(个人项目,人日估算)

阶段人日建议节奏
M0 验证0.5 ~ 1第 1 周前半
M1 最小可用4 ~ 6第 1 周后半 ~ 第 2 周
M2 打包分发2 ~ 4第 3 周
M3 增强4 ~ 8第 4 ~ 5 周(与 M2.4 并行)
合计11 ~ 19 人日兼职约 4 ~ 6 周

估算假设:熟悉 Rust + Tauri;不熟悉则 M1.1/M1.3 各 +1 天。每阶段完成即按 docs/05-verification.md 过验收。

7. 风险登记(方案 §6 细化)

#风险触发信号缓解动作状态
R1体积 342M+M2.1 后 du 结果--omit=dev 先行;bundle 闭包裁剪(M2.4);评估 bun compile(方案 C)开放
R2macOS Sandbox 挡 ~/.dsh 写入沙箱开启时启动即失败默认不开沙箱;若上架再加 home-dir entitlement开放(决策:不开)
R3profiles/node_modules 符号链接指向 app bundle,升级/交替使用悬空覆盖安装后首次启动异常依赖 dsh 每次启动自愈;文档说明;M3.4 验证开放
R4子进程残留(孙进程/SSE)退出后 pgrep 有残留进程组 kill + 宽限升级(M1.3);SSE 关闭由 dsh 自身处理开放
R5启动超时/端口异常60s 未就绪错误页展示 stderr;M0.2 原型先行验证解析开放
R6WebView 兼容问题(SSE/键盘)M0.4 冒烟失败WKWebView 支持 SSE/fetch;键盘问题记录并绕过开放(预计低)
R7dsh 版本升级破坏兼容升级 dsh 后启动异常版本锁定 0.1.0-rc.8;升级随 app 发布、先验证再发开放
R8双开各起服务端连点两次图标单实例(M1.5);二次启动复用开放
R9Finder 启动 PATH 缺失导致 runtime 探测失败M2.3 全新机器验证捆绑运行时优先(M2.2 探测链)开放

8. Definition of Done

一个任务"完成"须满足:

  1. 实现符合对应落地文档(docs/01 ~ docs/04)的接口与行为约定;
  2. 通过 docs/05-verification.md 中该任务的验收步骤;
  3. 关键路径上有实测记录(命令 + 输出),非"目测可用";
  4. 不破坏已有验收项(回归:退出无残留、单实例、重启恢复);
  5. 涉及的行为决策(偏离本计划之处)回写本计划或方案文档。

9. 验收流程(每阶段出口)

阶段出口必过项(详见 05-verification.md)
M0脚本能拿到端口;空壳窗口显示 dsh UI;SSE 冒烟通过
M1双击即用;退出零残留;崩溃自愈;单实例
M2全新环境安装可跑;Finder 启动无 PATH 依赖;体积记录
M3托盘/设置/自启/日志全可用;覆盖安装正常