Files
SuBlog/content/posts/dsh-launcher-intro/index.md
T

111 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: DSH Launcher — 一个把 DeepSeek Harness 装进桌面 GUI 的启动器
published: 2026-08-28
updated: 2026-08-28
description: 介绍 DSH Launcher,一个用 Wails v2 + Go + React 编写的跨平台桌面启动器(Windows / macOS / Linux),按「目录 + 版本」一键启动 DeepSeek Harness:实例管理、npm 版本查询、实时日志、插件市场,推 tag 自动构建发布三端安装包。
image: /posts/dsh-launcher-intro/img/cover.svg
tags: ['DSH', 'DeepSeek', 'Wails', 'Go', 'React', '桌面应用', 'CI/CD']
difficulty: 入门
---
## DSH Launcher 是什么
[DSH Launcher](https://github.com/wishesl/dsh-launcher) 是一个**跨平台桌面 GUI 启动器**(Windows / macOS / Linux),用来以「指定目录 + 指定版本」的方式启动 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/DeepSeek-Harness),并可视化地查询版本、管理实例、查看实时日志、安装插件。
DSH 的启动方式本质是一条 `npx -y @deepseek-ai/dsh@<版本> web` 命令(在某个工作目录里运行)。启动器把「选目录 + 选版本 + 启动/停止 + 看版本」封装成了开箱即用的图形界面。
![DSH Launcher 主页](/posts/dsh-launcher-intro/img/home.jpg)
![插件市场](/posts/dsh-launcher-intro/img/market.jpg)
## 为什么做这个
写这个启动器主要为了解决两个真实的痛点:
1. **npx 会优先命中启动目录里的本地 `node_modules` 副本**——哪怕 npm 上已经发了新版,只要目录里有旧版,`npx @deepseek-ai/dsh web` 就永远跑旧版。想升级得先搞清楚"我到底在跑哪一份",非常容易记混。
2. **版本查询 / 升级要敲一堆命令行**:`npm view @deepseek-ai/dsh versions`、`npx -y @deepseek-ai/dsh@latest web`……记不住,还容易敲错。
启动器的价值就一句话:**一个实例 = 一个目录 + 一个版本**,多个实例互不干扰,版本对照一目了然,再也不用手敲 npx 命令。
## 核心功能
| 功能 | 说明 |
|------|------|
| **实例管理** | 每个实例绑定一个目录和一个版本;卡片式列表,状态指示灯区分 启动中 / 运行中 / 已停止 / 异常退出,支持一键 启动 / 停止 / 删除 / 打开网页 |
| **版本查询** | 展示 npm `latest` / `next` dist-tag、全部版本历史与发布时间;本地实际版本通过读取目录 `node_modules/@deepseek-ai/dsh/package.json` 探测;官方 registry 优先,npmmirror 兜底 |
| **实时日志** | 启动日志流式回显到右侧常驻面板,自动识别 Web 地址、区分崩溃与正常退出、清理孤儿进程 |
| **插件市场** | 内置插件市场:发现 / 收藏 / 安装 / 卸载 DSH 插件,进度实时展示 |
| **系统托盘** | 点 ✕ 默认最小化到托盘而非退出,DSH 继续后台运行;托盘菜单可启动/停止各实例 |
| **单实例** | 重复启动 exe 只会把已运行实例的窗口唤回前台,不会开第二个窗口 |
| **自动发布** | 推 `v*` 标签即触发 GitHub Actions 构建三端产物并发布 Release |
## 两个有意思的设计
### 1. 服务态与进程态分离
最初版本里,「顶部显示 DSH 已就绪 / 打开按钮」完全依赖启动器自己管理的进程状态:进程输出里正则抓 URL → TCP 探测 → 标记 ready。这套逻辑有个隐患——一旦进程管理状态和真实情况脱节(比如快速停止/重启时的竞态、冷启动时持久化状态泄漏),顶部就永远显示"运行中"却没有打开按钮。
后来把它拆成了**两套互不影响的独立状态**:
| 状态 | 判定依据 | 驱动什么 |
|------|----------|----------|
| **服务态**(dsh 已启动) | 配置端口能正常访问 DSH 服务(后端 HTTP 探测,2s 兜底轮询 + 启停/保存/删除即时重查) | 顶部「已就绪」+ 打开按钮 |
| **进程态**(启动器在管理) | 启动器自己 spawn / kill 的进程生命周期 | 实例卡片的 启动 / 停止 按钮 |
好处:只要端口真的在服务,顶部就能打开——哪怕进程是被外部方式拉起来的;进程管理状态怎么折腾都不影响"能不能打开"。
### 2. 跨平台的进程管理抽象层
Wails v2 框架本身支持三端,但项目真正跨平台需要处理**进程树管理**这个平台差异最大的部分。抽象成了 `procattr_windows.go` / `procattr_unix.go` 两个文件:
| 能力 | Windows | macOS / Linux |
|------|---------|----------------|
| 启动 shell | `cmd /c`(解析 `.cmd` shim) | `sh -c` |
| 子进程属性 | `HideWindow`(不闪控制台) | `Setsid`(独立会话/进程组) |
| 进程树终止 | Job Object + `taskkill /T /F` | `kill(-pgid)` 组杀树 |
`dsh_process.go`、`install.go`、`market_ops.go`、`env.go` 全部改走统一的 `shellCommand()` / `killProcessTree()`,不再有散落的平台代码。
## 技术栈
| 层 | 技术 |
|----|------|
| 桌面壳 / 后端 | **Wails v2.15.0** + **Go 1.25**(Windows / macOS / Linux 三端) |
| 前端 | **React 18** + **TypeScript** + **Vite 3** |
| 系统托盘 | `fyne.io/systray`(独立 goroutine 跑消息循环,三端通用) |
| 单实例 | Wails `options.SingleInstanceLock` |
| CI / 发布 | GitHub Actions:三平台矩阵自动构建 + Releases |
## 多平台发布踩过的坑
自动化发布([workflow](https://github.com/wishesl/dsh-launcher/blob/master/.github/workflows/release.yml))流程是:**推 `v*` 标签 → 三平台并行构建 → 统一发 Release**。过程中踩了几个典型坑:
1. **Ubuntu 24.04 移除了 webkit2gtk-4.0**:Wails v2.10.2 默认仍要 4.0,24.04 只有 4.1。解法是升级 Wails 到 **v2.15.0**,构建时加 `-tags webkit2_41`,并补装 `libsoup-3.0-dev`。
2. **`macos-13`(Intel runner)已被 GitHub 下线**:macOS x64 产物改为在 arm64 runner 上用 `GOARCH=amd64` 交叉编译(Xcode 通用工具链原生支持)。
3. **Go 版本水涨船高**:Wails v2.15 要求 Go 1.25,CI 的 `setup-go` 也要跟着升。
## 快速上手
```bash
# 下载对应平台安装包(Releases 页)或自行构建
git clone https://github.com/wishesl/dsh-launcher.git
cd dsh-launcher
wails build # Windows / macOS
wails build -tags webkit2_41 # Linux(需 webkit2gtk-4.1 系统依赖)
```
打开应用后:
1. 左侧「实例」→「+ 添加实例」→ 选择 DSH 启动目录 → 选版本 → 启动方式选**本地副本**(官方推荐)→ 保存;
2. 卡片点「启动」,右侧日志面板自动弹出并实时滚动;
3. 顶部出现「DSH 已就绪 · 名称 · 地址」后点它即可在浏览器打开 DSH web;
4. 提示「本地副本未安装」时先点「安装到目录」,把该版本真实装进目录的 `node_modules`。
## 开源信息
- GitHub:[wishesl/dsh-launcher](https://github.com/wishesl/dsh-launcher)
- Releases(三平台安装包):<https://github.com/wishesl/dsh-launcher/releases>
- 技术选型参考:DSH 本质是 `npx @deepseek-ai/dsh@<版本> web` 跑在选定目录里,多实例互不干扰;一个目录对应一个版本,别混着用。
欢迎 Star、提 Issue,或者直接来提功能需求 🙂