跳过主要内容
X

dsh-xswt-tauriapp (dsh-desktop-app)

xswt442-cmd/dsh-xswt-tauriapp/plugins/dsh-desktop-app

桌面外壳 dsh-xswt-tauriapp(一个 Tauri 写的 DeepSeek Harness 桌面壳)的安装引导:安装后首次启动 dsh 时,从发布页下载本平台安装包,先比对 SHA256SUMS、校验通过才落盘,再交给系统安装器打开;插件自己不安装任何东西,也不给 dsh 加界面。

安装

dsh plugin --profile web add github:xswt442-cmd/dsh-xswt-tauriapp

该插件位于仓库的 plugins/dsh-desktop-app 子路径下。

README

dsh-xswt-tauriapp

中文 | English

License: MIT DSH release DSH compatibility platform runtime compat downloads

DeepSeek Harness 的轻量 Tauri 桌面外壳,也是 dsh 的 desktop harness / runtime supervisor:Tauri 负责 dsh 的外围——窗口、进程、启动与复用、会话交接、菜单与托盘、快捷键、更新、外链与故障恢复;页面内容仍由 dsh 自己负责。外壳不修改 dsh 源码,不向 dsh 页面注入脚本、不读取它的 DOM、不覆盖它的样式或界面,也不介入 dsh 的会话、沙箱与权限模型。

功能

  • 服务复用与启动:在 30803129 中复用已在运行的 dsh web,否则在首个空闲端口启动一个;服务进程独立于外壳,关闭窗口不会停止它。
  • 启动 token 握手与会话交接:握手在 Rust 侧完成并校验,启动 token 不进入任何页面的 URL。
  • 启动时选择端口:默认值取扫描结果或上次手输的端口,也可以输入任意端口;可用性当场判定。
  • 更新提示:dsh 的更新按正式版 / RC / Alpha 分栏展示,外壳自身的更新按平台挑选安装包并校验发布页的 SHA256SUMS;两者各自记录「不再提示」。
  • 系统集成:macOS 使用系统菜单,Windows / Linux 使用托盘菜单;Ctrl/Cmd+RCtrl/Cmd+= - 0F12 只在自身窗口获得焦点时注册。
  • 站外链接交给系统默认程序打开,不接管应用窗口。

平台与兼容性

状态
Linux(deb / rpm / AppImage)支持,由 CI 产出;会话交接与首次导航已实测
Windows(NSIS 安装包)支持;安装包与 GUI 已在真机日常使用中验证,交接与首次导航均已实测
macOS(dmg)构建已接入,未签名;GUI 未实测
WSLg可运行;WebKitGTK 的 GPU 直通不稳,需设 WEBKIT_DISABLE_COMPOSITING_MODE=1WEBKIT_DISABLE_DMABUF_RENDERER=1。WSLg 没有状态栏宿主,托盘图标无处显示;快捷键可用,前提是走 X11 后端

首次导航发送 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),只会看到一句说明。它不静默安装,也不导入任何 harness API,因此不会成为 dsh 启动失败的原因。

使用 Release 产物

每个 release 附带 Windows 安装包、macOS dmg、deb、rpm 与 AppImage,以及 SHA256SUMS。deb 仅依赖 libwebkit2gtk-4.1-0libgtk-3-0

sudo apt install "./dsh-xswt-tauriapp_0.0.9_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_HOMEDSH 主目录,默认 ~/.dsh
DSH_BIN直接指定 dshlib/bin.js
DSH_NODE_BIN直接指定 node 可执行文件
DSH_TAURI_REGISTRY覆盖版本查询地址,默认 npm registry
DSH_SHELL_DEBUG非空时输出交接、导航与更新检查日志
DSH_SHELL_DEVTOOLS非空时在菜单中提供开发者工具(debug 构建默认提供)
DSH_SHELL_WAYLAND在 Linux 上不切换到 X11 后端(见「菜单、托盘与快捷键」)

工作原理

服务发现、握手与会话交接

步骤行为
端口段30803129
复用判定读取 $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 的鉴权模型决定的,外壳不做额外猜测。

端口选择

情况行为
已发现可复用的服务端口框预填它的端口,确认即复用;填别的端口会在新端口再起一个实例
没有可复用的服务默认 = 30803129 中第一个空闲端口;手输过的端口若仍空闲则优先
端口被别的程序占用当场提示,确认不会继续
端口上是 dsh,但本机无法接手其会话单独说明——例如在 Windows 侧启动的实例,或用了另一个 DSH_HOME
低于 1024直接拒绝,普通用户无法绑定

只有手输过的端口会被记住并作为下次的默认值;接受灰色默认值不算选择,默认值因此继续跟随「第一个空闲端口」。

候选端口取自 $DSH_HOME/launcher/logs/server-<port>.out.log文件名,而不是对整个端口段做扫描:没有日志的端口没有启动 token,握手不可能完成,探测它没有意义。这也让刻意选用的非标准端口(例如 9000)在下次启动时仍能被发现。

窗口模型与首次导航

窗口内容权限
bootstrap外壳自带页面:进度、更新弹窗、失败信息本地源,唯一被授予 IPC capability 的窗口
dshdsh 的原始界面远端源,不获得外壳 IPC

dsh 的会话 cookie 带 SameSite=Strict,由其他源的页面发起的导航不会携带它,这正是把外壳页面直接导航到 dsh 会停在 dsh 401 文本上的原因。外壳保留 dsh 的安全语义,只调整交接顺序:Rust 先完成握手并把 cookie 写入 cookie 存储,之后才以 http://127.0.0.1:<port>/ 创建 dsh 窗口。该窗口的首次导航由宿主发起,没有发起者页面,因此不是跨站请求。

菜单、托盘与快捷键

平台形态
macOS原生系统菜单(含「编辑」菜单,文本框快捷键依赖它)
Windows / Linux托盘菜单;不挂永久菜单栏,不占用 dsh 的高度

Tauri 的快捷键只能挂在菜单 accelerator 上,而 Windows / Linux 上挂在窗口的菜单就是可见菜单栏。这两个平台因此改用全局快捷键,并且只在自身窗口获得焦点期间注册、失焦即注销,不会长期占用整台机器的 Ctrl+R。桌面环境拒绝发放全局快捷键时,外壳照常启动,只是失去快捷键。

Linux 上另有一个前提:global-hotkey 通过 X11 抓键,而 Wayland 原生窗口的按键不经过 X 服务器,快捷键会注册成功但永不触发。外壳因此在有 DISPLAY 时默认使用 X11 后端(XWayland 在所有 Wayland 桌面上都存在),设置 DSH_SHELL_WAYLAND=1 可退出该行为;显式设置 GDK_BACKEND 时外壳不干预。

缩放由 Rust 调用原生 set_zoom 完成:WebView 自带的缩放热键在 macOS / Linux 上依赖向页面注入 polyfill,与本项目的不注入原则冲突。

外壳自身的更新

与 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

MIT

相关插件