dsh-xswt-tauriapp
DiscoveredA lightweight Tauri desktop shell for DeepSeek Harness: windows, the dsh server process, launch and reuse, session handoff, menus and tray, shortcuts, updates, and fault recovery belong to the shell, while page content still belongs to dsh. A lightweight Tauri desktop shell for DeepSeek Harness: windows, the dsh server process, launch and reuse, the sessi
dsh-xswt-tauriapp
DeepSeek Harness 的轻量 Tauri 桌面外壳,也是 dsh 的 desktop harness / runtime supervisor:Tauri 负责 dsh 的外围——窗口、进程、启动与复用、会话交接、菜单与托盘、快捷键、更新、外链与故障恢复;页面内容仍由 dsh 自己负责。外壳不修改 dsh 源码,不向 dsh 页面注入脚本、不读取它的 DOM、不覆盖它的样式或界面,也不介入 dsh 的会话、沙箱与权限模型。
功能
- 服务复用与启动:在
3080–3129中复用已在运行的dsh web,否则在首个空闲端口启动一个;服务进程独立于外壳,关闭窗口不会停止它。 - 启动 token 握手与会话交接:握手在 Rust 侧完成并校验,启动 token 不进入任何页面的 URL。
- 启动时选择端口:默认值取扫描结果或上次手输的端口,也可以输入任意端口;可用性当场判定。
- 更新提示:dsh 的更新按正式版 / RC / Alpha 分栏展示,外壳自身的更新按平台挑选安装包并校验发布页的
SHA256SUMS;两者各自记录「不再提示」。 - 系统集成:macOS 使用系统菜单,Windows / Linux 使用托盘菜单;
Ctrl/Cmd+R、Ctrl/Cmd+=-0与F12只在自身窗口获得焦点时注册。 - 站外链接交给系统默认程序打开,不接管应用窗口。
平台与兼容性
| 项 | 状态 |
|---|---|
| Linux(deb / rpm / AppImage) | 支持,由 CI 产出;会话交接与首次导航已实测 |
| Windows(NSIS 安装包) | 支持;安装包与 GUI 已在真机日常使用中验证,交接与首次导航均已实测 |
| macOS(dmg) | 构建已接入,未签名;GUI 未实测 |
| WSLg | 可运行;WebKitGTK 的 GPU 直通不稳,需设 WEBKIT_DISABLE_COMPOSITING_MODE=1 与 WEBKIT_DISABLE_DMABUF_RENDERER=1。WSLg 没有状态栏宿主,托盘图标无处显示;快捷键可用,前提是走 X11 后端。WSLg 一律按 scale 1 渲染,与 Windows 的显示缩放无关,所以在 125% / 150% 的显示器上窗口比原生应用小:用 Ctrl/Cmd+= 缩放即可,因子会被记住,也可用 DSH_SHELL_ZOOM 固定 |
首次导航发送 SameSite=Strict cookie 的行为已在 Linux / WebKitGTK 与 Windows / WebView2 上实测确认;macOS 依赖其 WebView 对无发起者导航的同站判定,尚未实测。
获取
从插件市场安装
插件市场(awesome-dsh-plugin)的条目指向本仓库每个 release 附带的 dsh-xswt-tauriapp-plugin.tgz,它是安装引导而非安装包本体(源码见 plugins/dsh-desktop-app/):
dsh plugin --profile web add https://github.com/xswt442-cmd/dsh-xswt-tauriapp/releases/latest/download/dsh-xswt-tauriapp-plugin.tgz
装上后首次启动 dsh 时,引导会读取本仓库最新 release,按平台挑选安装包,先取 SHA256SUMS、校验通过才写盘,再交给系统安装器。已装好外壳的机器,以及没有桌面会话的机器(CI,或 Linux 上没有 DISPLAY),只会看到一句说明。引导自己读四个变量:DSH_TAURIAPP_MODE 只认 auto、notice、off 三个值,写成别的等于没写,仍按插件配置取;DSH_TAURIAPP_FORCE=1 让它在已经提示过一次之后仍然重新下载(其他值等于没设,notice 模式也仍然只给一句说明);DSH_TAURIAPP_NO_OPEN=1 使它停在即将交给系统安装器的那一刻,只把该执行的命令打印出来(同样只认 1);DSH_TAURIAPP_RELEASES_API 非空时覆盖发布接口地址。它的状态文件与下载的安装包都放在 $DSH_HOME 下——插件侧只要求该变量非空,不要求那个目录已经存在,这一点与外壳的 Rust 代码不同。它不静默安装,也不导入任何 harness API,因此不会成为 dsh 启动失败的原因。
使用 Release 产物
每个 release 附带 Windows 安装包、macOS dmg、deb、rpm 与 AppImage,以及 SHA256SUMS。deb 依赖 libwebkit2gtk-4.1-0、libgtk-3-0 与 libayatana-appindicator3-1(托盘用;Ubuntu 24.04 起 GTK3 的包名是 libgtk-3-0t64,它 Provides: libgtk-3-0,因此照装不误):
sudo apt install "./dsh-xswt-tauriapp_0.0.14_amd64.deb"
从源码构建
需要 Rust、Node 与 Tauri 的 Linux 系统依赖:
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev rpm
pnpm install
pnpm tauri build --bundles deb,rpm,appimage
使用
./src-tauri/target/release/dsh-xswt-tauriapp
外壳先显示 bootstrap 页面,同时完成服务发现与更新检查,随后打开 dsh 窗口。可用环境变量:
| 变量 | 作用 |
|---|---|
DSH_HOME | DSH 主目录:非空、且指向一个确实存在的目录时才采用,否则回落到 ~/.dsh(家目录取 HOME,Windows 上取 USERPROFILE) |
DSH_BIN | 非空时作为 dsh 的 lib/bin.js 的首选;它不存在时仍会继续尝试 $DSH_HOME/profiles、npm_config_prefix 与 PATH 里的候选 |
DSH_NODE_BIN | 非空时作为 node 可执行文件的候选,排在 PATH 之前;但拥有当前 dsh 安装的那个 node 若存在,会先于它被采用 |
DSH_TAURI_REGISTRY | 非空时覆盖 dsh 的版本查询地址,否则用 npm registry |
DSH_SHELL_RELEASES_API | 非空时覆盖外壳自身更新读取的 GitHub Releases API 地址,否则用本仓库的 releases/latest;供测试与镜像使用 |
DSH_SHELL_ALLOW_MULTIPLE | 只要被设置就允许同时运行多个外壳——空值与 0 同样算设置;默认第二次启动只把已在运行的那个提到前面 |
DSH_SHELL_DEBUG | 只要被设置就把交接、导航与更新检查的日志写到 stderr,空值也算设置;debug 构建无需设置,总是输出 |
DSH_SHELL_DEVTOOLS | 只要被设置就在发布构建里提供开发者工具,空值也算设置;debug 构建无需设置(见「菜单、托盘与快捷键」) |
DSH_SHELL_ZOOM | dsh 窗口的初始缩放因子,优先于记忆值:能解析成大于 0 的数字才生效(空值、0 与拼错的值一律忽略,回落记忆值),过界的值收敛到 0.3–3.0 |
DSH_SHELL_WAYLAND | 在 Linux 上只要被设置就不切换到 X11 后端——写成 0 同样是退出,习惯上置 1(见「菜单、托盘与快捷键」);显式设置了 GDK_BACKEND 时外壳同样不干预 |
工作原理
服务发现、握手与会话交接
| 步骤 | 行为 |
|---|---|
| 端口段 | 3080–3129 |
| 复用判定 | 读取 $DSH_HOME/launcher/logs/server-<port>.out.log 尾部的启动 token |
| 握手(Rust 侧) | GET /?token=… → 303 与会话 cookie → 携带 cookie 复请,校验页面含 DeepSeek Harness |
| 交接 | 把 cookie 写入 cookie 存储(补上服务未给出的 Domain),确认可读之后才创建 dsh 窗口 |
| 无认证服务 | 直接 GET / 返回 200 且含标记时同样采纳 |
| 启动 | node <dsh>/lib/bin.js web --port <p> --no-open,独立进程组,输出追加到上述日志 |
服务日志是启动 token 的唯一来源:在终端手工启动、日志未落在该路径的实例不会被识别,外壳会另起一个实例。这是 dsh 的鉴权模型决定的,外壳不做额外猜测。
端口选择
| 情况 | 行为 |
|---|---|
| 已发现可复用的服务 | 端口框预填它的端口,确认即复用;填别的端口会在新端口再起一个实例 |
| 没有可复用的服务 | 默认 = 3080–3129 中第一个空闲端口;手输过的端口若仍空闲则优先 |
| 端口被别的程序占用 | 当场提示,确认不会继续 |
| 端口上是 dsh,但本机无法接手其会话 | 单独说明——例如在 Windows 侧启动的实例,或用了另一个 DSH_HOME |
低于 1024 | 直接拒绝,普通用户无法绑定 |
只有手输过的端口会被记住并作为下次的默认值;接受灰色默认值不算选择,默认值因此继续跟随「第一个空闲端口」。
候选端口取自 $DSH_HOME/launcher/logs/server-<port>.out.log 的文件名,而不是对整个端口段做扫描:没有日志的端口没有启动 token,握手不可能完成,探测它没有意义。这也让刻意选用的非标准端口(例如 9000)在下次启动时仍能被发现。
窗口模型与首次导航
| 窗口 | 内容 | 权限 |
|---|---|---|
bootstrap | 外壳自带页面:进度、更新弹窗、失败信息 | 本地源,唯一被授予 IPC capability 的窗口 |
dsh | dsh 的原始界面 | 远端源,不获得外壳 IPC |
dsh 的会话 cookie 带 SameSite=Strict,由其他源的页面发起的导航不会携带它,这正是把外壳页面直接导航到 dsh 会停在 dsh 401 文本上的原因。外壳保留 dsh 的安全语义,只调整交接顺序:Rust 先完成握手并把 cookie 写入 cookie 存储,之后才以 http://127.0.0.1:<port>/ 创建 dsh 窗口。该窗口的首次导航由宿主发起,没有发起者页面,因此不是跨站请求。
dsh 页面里指向站外的链接交给系统的默认程序打开,但能交出去的只有 http:、https: 与 mailto::file: 会被拒绝,因为 explorer 与 open 不会跟递到手上的东西讲条件,命令线上的可执行文件会被直接运行,而 dsh 往磁盘写文件,于是一个指向文件的链接是一次启动而非一次浏览;解析不成 URL 的文本同样拒绝。被拒的链接只是留在原地失效,不会改到别处去打开,原因写进外壳日志(见「菜单、托盘与快捷键」的 DSH_SHELL_DEBUG)。
菜单、托盘与快捷键
| 平台 | 形态 |
|---|---|
| macOS | 原生系统菜单(含「编辑」菜单,文本框快捷键依赖它) |
| Windows / Linux | 托盘菜单;不挂永久菜单栏,不占用 dsh 的高度 |
Tauri 的快捷键只能挂在菜单 accelerator 上,而 Windows / Linux 上挂在窗口的菜单就是可见菜单栏。这两个平台因此改用全局快捷键,并且只在自身窗口获得焦点期间注册、失焦即注销,不会长期占用整台机器的 Ctrl+R。桌面环境拒绝发放全局快捷键时,外壳照常启动,只是失去快捷键。
Linux 上另有一个前提:global-hotkey 通过 X11 抓键,而 Wayland 原生窗口的按键不经过 X 服务器,快捷键会注册成功但永不触发。外壳因此在有 DISPLAY 时默认使用 X11 后端(XWayland 在所有 Wayland 桌面上都存在),设置 DSH_SHELL_WAYLAND 可退出该行为——判定只看有没有被设置,所以 0 也是退出,习惯上写 1;显式设置 GDK_BACKEND 时外壳同样不干预。
缩放由 Rust 调用原生 set_zoom 完成:WebView 自带的缩放热键在 macOS / Linux 上依赖向页面注入 polyfill,与本项目的不注入原则冲突。缩放因子会被记住(存于应用配置目录),重启后仍然生效;也可以用 DSH_SHELL_ZOOM 指定初值,此时环境变量优先——但生效的只是一个能解析成数字且大于 0 的值,其余(含空值与 0)一律被忽略,而不是把外壳卡住。
dsh 窗口的大小与位置同样会被记住(同一个配置目录里的 window.json),下次启动放回原处。记忆中的位置只在当前某块显示器仍能显示它时才采用:显示器拔掉之后那些坐标在屏幕外,恢复它就等于启动了一个看不见的窗口,所以此时回到居中。再次启动外壳时,第二次进程不会开第二个窗口,而是把已经在运行的那个提到前面(设置 DSH_SHELL_ALLOW_MULTIPLE 即可绕过,任何值都算,用于两个端口并排跑两个外壳);macOS 上从 Dock 图标重新激活(或再次 open)也会把窗口提到前面。
发布构建里没有 WebView 检查器:唯一的判定是 DSH_SHELL_DEVTOOLS 有没有被设置(任何值都算,包括空值),没设置时两个窗口都不启用检查器,「开发者工具」这一项也不会出现在菜单里,因此不会出现「菜单里有项、按下去却没检查器」;Windows / Linux 上 F12 照样会被注册,但按下去什么也不打开。debug 构建不用设置就带着检查器。日志的开关在同一处:打包后的 GUI 没有终端,所以交接失败与外链被拒这类原因在 debug 构建里直接写到 stderr,在发布构建里要设置 DSH_SHELL_DEBUG 才写(同样只看有没有被设置)。
外壳自身的更新
与 dsh 的更新是两条独立路径:dsh 来自 npm,外壳来自本仓库的 GitHub Releases,启动时并行检查。
| 情况 | 行为 |
|---|---|
| 有新版本,且该发布带本平台可安装的安装包 | 弹窗顶部提示「下载并安装」;下载后比对 SHA256SUMS,通过才交给系统安装器打开 |
| 有新版本,但该发布没有本平台的安装包 | 提示改为「打开发布页」,不猜、不下载别的东西 |
| 校验不通过 | 拒绝安装并说明期望与实际哈希,不落盘 |
发布里没有 SHA256SUMS | 直接拒绝下载未经校验的安装包 |
| 检查失败(断网、API 限流、还没有任何发布) | 静默,不提示;不确定有新版本不是打扰用户的理由 |
版本比较要求两边都能解析成 semver,并先剥离 tag 的 v 前缀。这里不复用 dsh 的比较函数:它的字符串回退适用于版本流,用于更新器则会把当前正在运行的版本当成新版本提示一次。
「不再提示」按外壳版本单独记录(dismissed-shell-updates.json),与 dsh 的忽略列表互不影响。
dsh 自身的更新
通道由版本字符串判定,不使用 npm dist-tag——后者自身可能指向一个 RC。
| 通道 | 判定 |
|---|---|
| 正式版 | 无预发布后缀,如 0.1.5 |
| RC | 预发布段以 rc 开头,如 0.1.5-rc.2 |
| Alpha | 预发布段以 alpha 开头,如 0.1.6-alpha.1 |
启动弹窗只在稳定度不低于已装版本的通道中选取候选:已装 RC 时只会被提示 RC 或正式版,Alpha 需在弹窗中主动选择。「不再提示此版本」把该版本写入应用配置目录,且仅抑制该版本。
开发与验证
cargo test --manifest-path crates/dsh-core/Cargo.toml
cargo test --manifest-path src-tauri/Cargo.toml --lib
node --test plugins/dsh-desktop-app/test/plugin.test.js
node scripts/check-docs.mjs
cargo run --example launch --manifest-path crates/dsh-core/Cargo.toml
crates/dsh-core 不依赖 GUI 工具链,可在缺少 libwebkit2gtk 的环境中独立构建与测试。examples/launch.rs 以与外壳相同的代码路径启动真实服务并输出准备好的会话(url= 与 cookie=);compat CI 用它验证握手,并确认启动 token 不进入 URL。
plugins/dsh-desktop-app/ 的市场引导插件是零依赖的纯 Node 模块,其测试全部运行在本机回环的替身发布服务器上:不访问 GitHub,也不打开任何安装包(DSH_TAURIAPP_NO_OPEN=1 使它停在即将交给系统安装器的那一刻)。
License
Comments
Loading…
Similar plugins
by cilis
Tauri 2 desktop application for Windows that detects (or installs) the global @deepseek-ai/dsh package, launches the DSH Web server and embeds it in its own window, with system tray management, login
★ 2
↓ 760/wk
MIT
Rust
Oct 4, 2026
dsh plugin --profile web add @lenorin/dsh-tauri-launcherby RAFOLIE
DeepSeek Harness desktop shell — Tauri v2, tray + native webchat + task-done toasts, single portable exe
★ 22
↓ 359/wk
MIT
TypeScript
Oct 5, 2026
dsh plugin --profile terminal add dsh-desktop-pluginby happpsee
Skill for packaging DeepSeek Harness into a Tauri 2 desktop app (macOS + Windows): dual-platform install playbook, China mirror bootstrap with a subagent timeout sentinel, admin-free Windows toolchain
★ 0
↓ 66/wk
Rust
Aug 16, 2026
dsh plugin --profile web add dsh-desktop-appDSH environment management: auto-installs a Rust/Tauri desktop host (downloaded per-OS from dsh-desktop-host releases, source build as fallback), one-click runtime updates, Windows platform compatibil
★ 0
↓ 133/wk
dsh plugin --profile web add dsh-desktop-toolsby huiliyi37
官方 DeepSeek Harness 的交互式终端 UI 插件:自研 ANSI 极简交互渲染、流式 Markdown/工具卡、16+ 主题、slash 命令与选择器、输入历史与本地偏好持久化、LSP 诊断、memory记忆,很丝滑的开发体验。
★ 286
↓ 630/wk
Apache-2.0
TypeScript
Sep 30, 2026
dsh plugin --profile terminal add @huiliyi37/dsh-tianshu-tuiby jianjianzhu
Full-page Harness console for the DeepSeek Harness Web GUI: sessions, plugins, market, MCP, skills and usage in one shell.
★ 0
MIT
JavaScript
Sep 20, 2026
dsh plugin --profile web add dsh-harness-ui