🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

桌面外壳 ZgblKylin/dsh-gui

DeepSeek Harnessv0.0.1
ZgblKylin30 天前有提交已提供中文文档扫描:含敏感能力

把 Harness 网页界面装进轻量桌面窗口,支持多后端连接

30.6/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/7 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查未通过:缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add ZgblKylin/dsh-gui
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么[目录]—
dsh 原生插件 · other
暂无时间戳·判据 →
装得上吗[实装验证]✓
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
数据截至 2026/11/02·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]✓
活跃:最近一次提交在 30 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安装兼容性检查需源码安装

缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-gui @ 0.0.1
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/14 16:19:10

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件写入剪贴板(不计风险)
查看扫描证据(共 42 处,展示前 42 条)
  • 读取本地文件
    plugins/deep-whale/install.mjs:90
    if (!existsSync(manifestPath)) return
  • 读取本地文件
    plugins/deep-whale/install.mjs:93
    manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
  • 读取本地文件
    plugins/deep-whale/install.mjs:121
    if (!existsSync(scopeDir)) return
  • 读取本地文件
    plugins/deep-whale/install.mjs:123
    for (const entry of readdirSync(scopeDir)) {
  • 读取本地文件
    plugins/deep-whale/install.mjs:125
    if (!lstatSync(path).isSymbolicLink()) continue
  • 删除本地文件
    plugins/deep-whale/install.mjs:126
    rmSync(path, { force: true })
  • 读取本地文件
    plugins/deep-whale/install.mjs:130
    if (readdirSync(scopeDir).length === 0) rmSync(scopeDir, { recursive: true, force: true })
  • 删除本地文件递归
    plugins/deep-whale/install.mjs:130
    if (readdirSync(scopeDir).length === 0) rmSync(scopeDir, { recursive: true, force: true })
  • 读取本地文件
    plugins/dsh-pet/inject-config.mjs:42
    if (existsSync(jsonc)) return jsonc
  • 读取本地文件
    plugins/dsh-pet/inject-config.mjs:44
    return existsSync(legacy) ? legacy : jsonc
  • 读取本地文件
    plugins/dsh-pet/inject-config.mjs:67
    if (!existsSync(filePath)) {
  • 写入本地文件
    plugins/dsh-pet/inject-config.mjs:68
    writeFileSync(filePath, seed(), 'utf8')
  • 读取本地文件
    plugins/dsh-pet/inject-config.mjs:73
    cfg = parseUserConfig(readFileSync(filePath, 'utf8'))
  • 写入本地文件
    plugins/dsh-pet/inject-config.mjs:76
    writeFileSync(filePath, seed(), 'utf8')
  • 写入本地文件
    plugins/dsh-pet/inject-config.mjs:80
    writeFileSync(filePath, seed(), 'utf8')
  • 写入本地文件
    plugins/dsh-pet/inject-config.mjs:87
    writeFileSync(filePath, serialize({ ...cfg, pets: next }), 'utf8')
  • 读取本地文件
    plugins/dsh-pet/install.mjs:50
    const installed = existsSync(
  • 写入本地文件
    plugins/dsh-pet/install.mjs:60
    mkdirSync(dirname(target), { recursive: true })
  • 读取本地文件
    plugins/harness/browser-use.mjs:96
    return candidates.find((candidate) => existsSync(candidate))
  • 读取本地文件
    plugins/harness/wsl-workarounds.mjs:72
    return /microsoft/i.test(readFileSync('/proc/version', 'utf8'))
  • 读取本地文件
    plugins/harness/wsl-workarounds.mjs:97
    const current = existsSync(agentsFile) ? readFileSync(agentsFile, 'utf8') : ''
  • 读取本地文件
    plugins/harness/wsl-workarounds.mjs:97
    const current = existsSync(agentsFile) ? readFileSync(agentsFile, 'utf8') : ''
  • 写入本地文件
    plugins/harness/wsl-workarounds.mjs:99
    writeFileSync(agentsFile, `${withTrailingNewline(current)}\n${heading}\n\n${body}\n`, 'utf8')
  • 读取本地文件
    plugins/harness/wsl-workarounds.mjs:145
    if (!existsSync(target) && existsSync(source)) {
  • 写入本地文件
    plugins/harness/wsl-workarounds.mjs:146
    mkdirSync(targetDir, { recursive: true })
  • 写入本地文件
    plugins/harness/wsl-workarounds.mjs:147
    copyFileSync(source, target)
  • 读取本地文件
    plugins/harness/wsl-workarounds.mjs:157
    result.missingServices = COMPUTER_USE_SERVICES.filter((name) => !existsSync(join(unitDir, name)))
  • 删除本地文件
    src-tauri/src/update_script.mjs:38
    unlinkSync(SELF_PATH);
  • 写入本地文件
    src-tauri/src/update_script.mjs:52
    mkdirSync(GUI_DIR, { recursive: true });
  • 写入本地文件
    src-tauri/src/update_script.mjs:53
    appendFileSync(LOG_FILE, line, 'utf8');
  • 执行外部命令来自配置/变量
    src-tauri/src/update_script.mjs:71
    const result = spawnSync(command, args, {
  • 写入本地文件
    src-tauri/src/update_script.mjs:88
    mkdirSync(GUI_DIR, { recursive: true });
  • 执行外部命令来自配置/变量
    src-tauri/src/update_script.mjs:101
    const result = spawnSync(command, args, {
  • 读取本地文件
    src-tauri/src/update_script.mjs:113
    return readFileSync(outPath, 'utf8').trim();
  • 删除本地文件
    src-tauri/src/update_script.mjs:117
    try { unlinkSync(outPath); } catch { }
  • 删除本地文件
    src-tauri/src/update_script.mjs:118
    try { unlinkSync(errPath); } catch { }
  • 读取本地文件
    src-tauri/src/update_script.mjs:191
    if (!existsSync(exe)) {
  • 执行外部命令来自配置/变量
    src-tauri/src/update_script.mjs:196
    const child = spawn(exe, [], {
  • 读取本地文件
    src-tauri/src/update_script.mjs:220
    plan = JSON.parse(readFileSync(PLAN_FILE, 'utf8'));
  • 写入本地文件
    src-tauri/src/update_script.mjs:245
    writeFileSync(PLAN_FILE, JSON.stringify(plan, null, 2), 'utf8');
  • 读取本地文件
    src-tauri/src/update_script.mjs:252
    if (!existsSync(dir)) {
  • 写入剪贴板
    src-tauri/ui/app.js:3251
    navigator.clipboard.writeText(text).then(done, () => legacyCopy(text, done));
扫描于 2026/11/4(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 网页界面装入桌面窗口
  • 多后端连接

带集成 DeepSeek Harness 的 Tauri GUI,外加一些插件包

README

由 DeepSeek 最新模型翻译生成
dsh-gui 给 DeepSeek Harness Web UI 套的一层轻量桌面外壳。它不是重新打包的 harness——它运行 harness.json 选中的 dsh 命令行(默认 npm 运行时下是 @deepseek-ai/dsh registry 包,source 下是构建好的 deepseek-harness git 子模块),自己托管 Web 服务器,并在单个 webview 窗口里显示完整的 harness UI。除此之外不画任何东西:只有系统标题栏和边框。 它做什么 启动时,入口 exe 会: 1. 立刻打开一个无边框 webview 窗口,在里面显示加载页。harness 冷启动大约要 10 秒(耗时在 harness 内部的 CPU 计算上),所以窗口不会等它。 2. 启动 harness.json 选中的 dsh 命令行——也就是那个运行时的 bin.js——带上 web --port <port> --no-open,并把 DSH_HOME 固定到本仓库内的 ./.dsh。 3. 等到 harness 在 127.0.0.1:<port> 上对 GET / 返回 200,然后把带 token 的 URL 交给 shell 页面,由它在每个连接标签页一个子 webview 里托管 harness UI。
展开完整文档(README 剩余 1 节)
dsh-gui

给 DeepSeek Harness Web UI 套的一层轻量桌面外壳。它不是重新打包的 harness——它运行 harness.json 选中的 dsh 命令行(默认 npm 运行时下是 @deepseek-ai/dsh registry 包,source 下是构建好的 deepseek-harness git 子模块),自己托管 Web 服务器,并在单个 webview 窗口里显示完整的 harness UI。除此之外不画任何东西:只有系统标题栏和边框。

它做什么

启动时,入口 exe 会:

1. 立刻打开一个无边框 webview 窗口,在里面显示加载页。harness 冷启动大约要 10 秒(耗时在 harness 内部的 CPU 计算上),所以窗口不会等它。
2. 启动 harness.json 选中的 dsh 命令行——也就是那个运行时的 bin.js——带上 web --port <port> --no-open,并把 DSH_HOME 固定到本仓库内的 ./.dsh。
3. 等到 harness 在 127.0.0.1:<port> 上对 GET / 返回 200,然后把带 token 的 URL 交给 shell 页面,由它在每个连接标签页一个子 webview 里托管 harness UI。如果 harness 启动不了、或者一直没就绪,就在加载页上报告(窗口保持打开),而不是弹一个消息框。退出时 dsh-gui 会拆掉 harness 进程树;harness 跑在一个 kill-on-close 的 Windows job object 里,所以就算 dsh-gui 自己被杀了(任务管理器、关掉启动它的终端),内核也会强制执行这次拆除。

ui/ 页面只是一层薄壳:一个可任意拖拽的标题栏,带连接标签页(VSCode 风格,每个已连接的 DSH 后端一个)、一个 + 新建连接按钮,内侧还有一个汉堡(☰)菜单——标准的最小化 / 最大化 / 关闭控件排在它后面,汉堡菜单里放着标签页列表,加上新建连接 / 关闭当前连接,以及原有的关于 / 退出项。新建连接对话框支持本地后端(探测端口,或在上面启动内置 harness)、远程后端(VSCode Remote SSH 风格:加载前端,或通过 SSH 部署到另一台主机,再经 SSH 本地端口转发访问它),以及 Docker 后端(在运行中的容器里用 docker exec 启动 dsh,再用 docker exec -i 的 stdio 隧道桥接它的 loopback 端口——不需要端口映射)。连接相关的界面元素都在原生标题栏里——内嵌的 harness 页面不渲染这些。关于对话框显示 dsh-gui、deepseek-harness 子模块、whale-girl 应用图标子模块,以及 plugins/ 下每个插件包的版本(精确的 git tag,没有就用 commit 短哈希)、许可证和 GitHub 链接。harness UI 本身则继续像浏览器一样,通过纯 HTTP 和它自托管的服务器通信。

截图

dsh-gui screenshot

功能

GUI 外壳

- 无边框单窗口外壳——可任意拖拽的自定义标题栏,带
- 原生最小化 / 最大化 / 关闭控件;标题栏颜色跟随内嵌 harness 页面上报的浅色/深色主题。
- 先出窗口,harness 启动期间显示加载页 —— 进程一启动窗口就出现(只做 WebView2 初始化),显示一张加载卡片和已等待时长;harness 一应答,标签页 webview 立刻替换它。harness 出错也显示在那张卡片上,窗口全程可用(拖动、最小化、关闭)。
- 原生 Windows 11 标题栏交互 —— 悬停最大化按钮弹出系统 Snap Layouts 浮出菜单,双击标题栏切换最大化/还原,双击上边框纵向铺满/还原窗口(含拖动还原),左上角应用图标打开窗口控制菜单。
- VSCode 风格连接标签页 —— 每个已连接的 DSH 后端一个标签页,带关闭按钮,标签页与激活状态会持久化;关掉最后一个标签页会重新打开新建连接对话框。
- 新建连接(+) —— 本地后端(探测端口,或在端口上启动内置 harness)、远程后端(加载前端,或用 SSH 部署到另一台主机,再通过仅回环的 SSH 本地端口转发访问)、Docker 后端(用 docker exec -d 在运行中的容器里启动 dsh,再用 docker exec -i stdio 隧道桥接它的回环端口 —— 容器不需要 -p 端口映射;容器侧用户、工作目录和环境变量列表都可配置)。
- 汉堡菜单(☰) —— 标签页列表、新建连接 / 关闭当前连接、检查更新、关于 / 退出。
- 关于对话框 —— 版本(git tag 或 commit 短哈希)、许可证,以及 dsh-gui、harness、图标子模块和每个插件的 GitHub 链接。直接点击复制 URL;Ctrl+点击 用系统默认浏览器打开。
- 更新对话框 —— 检查仓库内每个项目是否有更新,让你选择更新哪些以及目标(最新 commit / 最新 tag),然后退出,在控制台窗口里更新,再重启。只要某行的 origin 是 GitHub 仓库,该行的模块名就链接到对应仓库的 Releases 列表页(https://github.com/<owner>/<repo>/releases)—— 链接在本地解析,所以首次绘制时就有,非 GitHub 或缺少 remote 时保持纯文本;和 changelog 标题一样,行名不可选中,所以直接点击会用系统默认浏览器打开。对话框推荐 AI 同步更新:每个可更新行和底栏都有 AI 更新 按钮,点击后回到项目主界面,在那里选中 dsh-gui 目录,选中「创造模式」(cordis) 预设,并预填一段提示词,要求智能体把子模块同步到所选目标,并在持久化的 .staging/dsh-gui 克隆里验证结果 —— staging WebUI 必须真的能加载,harness 更新还需额外做一次 computer-use 检查,确认 GUI 能启动并运行,第二阶段只改动这份检出你批准验证报告之后(底部按钮会列出每个可更新模块及其组合选择;发送前仍可更改预设)。每个 behind 行还有一个 更新日志 按钮:对于 tag 目标,它会显示本次更新引入的每一个 release 的官方 GitHub Release 说明——当前 checkout 与更新目标之间的所有 tag,最新的排在最前,所以落后好几个 release 的 checkout 能看到整个跨度,而不只是目标 tag 的说明——前提是远程是 GitHub 仓库且存在 release,否则它会请 dsh AI 总结当前 checkout 与更新目标之间的 commit——走运行中 harness 的 raw-LLM 路由(dsh-ai-update 插件,默认模型来自 Models 页面),这条路由不创建会话,所以这次运行永远不会出现在 DSH 会话列表里;当该路由不可用时,shell 会重试一次,然后回退到一次性 headless 模式(dsh --profile headless),其会话存储被重定向到临时目录——两条路径都不会持久化会话。markdown 结果渲染在一个可滚动、可选择的对话框中,其复制按钮复制原始源文本;来源行和任何失败消息(例如 GitHub-Release 获取错误)也可选择,所以问题可以原样复制到报告里。当更新日志来自真实的 GitHub release 时,对话框标题中的模块名会变成指向仓库 Releases 列表页面的链接(https://github.com/<owner>/<repo>/releases,绝不是每个 tag 的 /releases/tag/… 子页面);标题行不可选择,所以直接点击那里会打开系统默认浏览器。Ctrl+click 正文中的链接会在系统默认浏览器中打开 URL(直接点击仍是文本选择手势,因为 webview 链接无法自行弹出窗口)。

插件

- dsh-remote —— 多后端远程模式:连接标签页、新建连接对话框、本地后端启动,以及上文描述的 VSCode Remote SSH 风格部署/隧道(见 plugins/remote/dsh-remote/docs/)。
- dsh-sidebar-qa —— 选中对话文本 → 在专用的同工作区会话(❓追问·<主题>)中于右侧面板追问,不打断主对话;是 dsh-better-sidebar 的轻量消费者(见 plugins/better-sidebar/dsh-sidebar-qa/README.md;由 better-sidebar wrapper 安装,在 dsh-better-sidebar 之后)。
- dsh-better-sidebar —— 服务优先的侧边栏工作台(右侧边栏 + 底部面板):每会话资源管理器、CodeMirror 编辑器 + 文件查看器(图片/PDF/Markdown/HTML/代码)、真实终端(xterm.js + node-pty,支持重连回放;可选 terminal_ 模型工具,默认关闭)、Git 面板、内嵌浏览器,以及后台任务页面(见 plugins/better-sidebar/DSH-better-sidebar/README.md)。
- dsh-ai-update —— 更新对话框 AI 背后的浏览器半边桥接更新按钮:接收 shell 的 dsh-gui:ai-update 消息,回到新建会话首页,选中 dsh-gui 工作区,并把更新提示词预填进输入框——绝不直接创建会话,也绝不挑选预设(见 plugins/ai-update/dsh-ai-update/docs/)。
- dsh-deep-whale —— 鲸鱼娘皮肤系列;目前提供可热插拔的 maid-atelier 皮肤(浅色/深色宫殿背景、海军蓝蕾丝 UI 叠加层、Q 版侧边栏、内嵌资源)。

dsh-gui with the plugin features expanded

环境要求

- Windows 10/11,且装有 WebView2 Evergreen runtime(大多数现代 Windows / Edge 安装都自带)。
- Node.js ^22.19 || >=24.2,并且 npm 在 PATH 上(构建工具链和 shell 启动 bin.js 都要用)。>=24.2 这个下限是实打实的:harness 命令行入口依赖 import.meta.main,而 Node 从 22.18.0 / 24.2.0 起才提供它——在 24.0–24.1 上,bin.js 会直接以 0 退出、不启动,shell 则报 harness exited before becoming ready (status exit status: 0)。
- Rust 工具链(rustc/cargo),用于构建入口 exe。

构建时拉取的一切(npm 包、pnpm store、cargo crates)都落在 runtime root 或本地标准缓存里——从不全局安装 harness。

工具链是一个跨平台的 Node 命令行(scripts/dsh-gui.mjs),通过 npm scripts 暴露出来,所以同一套命令在 Windows、macOS 和 Linux(WSL)上都能用。

目录结构

安装涉及两个根目录。仓库根目录是 git checkout;runtime root 是它的父目录,存放 DSH home、工具链和入口二进制。DSH_GUI_ROOT 和 DSH_GUI_RUNTIME_ROOT 可以覆盖它们;单目录 checkout 也照样能用,因为此时两个根目录解析到同一个目录(见 docs/dsh-gui/nested-clone-layout.md)。在 Windows 上,把 runtime 放在工作区之外很重要:沙箱会给工作区目录树打上 Low 完整性标签,而从该目录树内的可执行文件启动的进程会继承这个标签(见 docs/dsh-gui/windows-acl-low-integrity-label.md)。

<runtime-root>/
├─ .dsh/ # (runtime) harness home: profiles/plugins/sessions
├─ .harness/ # npm-runtime install of @deepseek-ai/dsh
├─ .toolchain/ .pnpm-store/ # pinned pnpm and the shared package cache
├─ .staging/ # upgrade staging clone (scripts/staging.mjs)
├─ dsh-gui.exe # the Tauri shell entry binary (dsh-gui elsewhere)
└─ dsh-gui/ # the git checkout — also the DSH workspace ├─ harness.json # selects the dsh runtime (npm registry or source build) ├─ deepseek-harness/ # git submodule: the pinned harness checkout — version │ # source, built in place only in source mode ├─ package.json # npm scripts: setup / build / install:plugins / start / ... ├─ src-tauri/ # Tauri 外壳(Rust);cargo 产物在 target/<profile>/ │ ├─ ui/ # 无边框外壳页面:标题栏(连接标签页、 │ │ # + 按钮、汉堡菜单、窗口控制), │ │ # 新建连接对话框,以及关于对话框 │ └─ whale-icon/ # git 子模块:DeepSeek Harness 鲸鱼少女图标包 │ # (DeepSeekHarness-WhaleGirl.ico 是应用图标) ├─ scripts/ │ └─ dsh-gui.mjs # 每个 npm script 背后的跨平台命令行 ├─ presets/ # 智能体预设源码:每个 presets/<id>/ 目录各自持有 │ # 自己的 install.mjs;构建时把每个预设安装成 │ # @deepseek-ai/dsh-agent-preset 记录(见 presets/README.md) ├─ plugins/ # 插件包装层,预设风格:每个 plugins/<id>/ 持有 │ # 自己的 install.mjs,外加插件包/仓库检出 │ # (remote/dsh-remote 在树内;better-sidebar/ │ # DSH-better-sidebar、deep-whale/dsh-deep-whale 和 │ # dsh-web-ui/dsh-web-ui 是 git 子模块; │ # dsh-web-ui 从 npm 安装 dsh-web-ui-settings + │ # dsh-plugin-manager + dsh-skill-explorer + │ # dsh-usage + dsh-model-capabilities; │ # 见 plugins/README.md) └─ global_template.agents/ # 带版本管理的智能体配置模板:常驻加载的

文档和用户级技能(其中就有 review 技能);

构建时把缺失的文件填进

<runtime-root>/.dsh/.agents/,绝不覆盖已存在的

文件,外壳把 DSH_AGENTS_HOME 固定到这个

智能体配置主目录

npm scripts

下面每条命令都是 package.json 里的一项,转发给跨平台 Node 命令行——主生命周期走 scripts/dsh-gui.mjs,而 scripts/staging.mjs 和 scripts/harness.mjs 各自管自己的命令。表中标注的详细小节在下方展开。

| npm script | Command | 作用 |
| npm run setup / npm run build | node scripts/dsh-gui.mjs setup / node scripts/dsh-gui.mjs build | 一次性构建:工具链、harness 运行时、入口 exe、插件、预设、全局智能体模板(见 Build (one shot)) |
| npm run build:exe | node scripts/dsh-gui.mjs build --skip-harness | 只构建入口 exe,跳过 harness 运行时这一步 |
| npm run build:webui | node scripts/dsh-gui.mjs build --skip-exe | 只装 harness 运行时 + 插件/预设,跳过 cargo |
| npm run rebuild | node scripts/dsh-gui.mjs rebuild | 完整构建,禁用所有跳过逻辑:重新安装 runtime,重新安装每个插件(见 Build (one shot)) |
| npm run install:plugins / npm run plugins | node scripts/dsh-gui.mjs install | 运行 plugins/ 下每个插件的安装脚本(见 Adding plugins at runtime) |
| npm run test:scripts | node --test scripts/plugin-install.test.mjs | 插件安装快速路径与重装开关的单元测试 |
| npm run staging | node scripts/staging.mjs | staging-clone 工作流:ensure、sync、status、clean --yes(见 Validating an upgrade in the staging clone) |
| npm run harness | node scripts/harness.mjs | 前台 harness 后端,无 shell 窗口或浏览器(见 Run) |
| npm start | node scripts/dsh-gui.mjs run | 分离式桌面 shell(见 Run) |
| npm run shortcut | node scripts/dsh-gui.mjs shortcut | Windows 桌面快捷方式(见 System shortcut (Windows)) |

Build (one shot)
powershell
npm run setup

这条命令是幂等的,只写入 runtime 根目录下:

- 把 pnpm 11.7.0 引导安装到 .toolchain/(harness 锁定的确切版本),因此不需要系统安装 pnpm。
- 把 harness.json 选中的 dsh runtime 更新到最新(约定与限制见 docs/dsh-gui/harness-runtime.md):
- npm(仓库内置的 runtime):从 registry 把 @deepseek-ai/dsh@<version> 安装到 <runtime-root>/.harness/,其中 <version> 从子模块的 apps/cli/package.json 读取。deepseek-harness/ 下的任何内容都不编译。
- source:在 deepseek-harness/ 内运行 pnpm install --store-dir <runtime-root>/.pnpm-store --frozen-lockfile 和 pnpm run build(host lib + web dist/)。
- 无论选中哪个 runtime,包缓存都位于 <runtime-root>/.pnpm-store/,不在全局 store 里。
- 用 cargo build --release 编译入口 exe(默认 release),并复制到 runtime 根目录(Windows 上是 dsh-gui.exe,其他平台是 dsh-gui)。
- 运行 plugins/ 下每个插件的安装脚本 —— 每个 plugins/<id>/install.mjs 通常负责构建、安装并把插件包挂载到 web 配置档;dsh-web-ui/install.mjs 通过同一套配置档流水线安装它的四个 npm bundle(见 Adding plugins)。如果配置档里已经存在某个插件,且版本正好是锁定的版本 —— 已安装、已挂载、bundle 行已在 dsh.profile.bundles 中 —— 就跳过:dsh plugin add 否则会白白重新解析整个配置档依赖图。link: 安装只跳过那次依赖写入;包本身仍然会构建。
- 运行 presets/ 下每个智能体预设的安装脚本 —— 从 harness dsh-v0.1.7-rc.2 起,每个 presets/<id>/ 目录都会作为一个配置档补丁里的 @deepseek-ai/dsh-agent-preset 声明行,并出现在预设名册上(模式见 presets/README.md;已废弃的 .dsh/.agent-presets/ 目录不再被发现)。
- 填充全局智能体模板 —— global_template.agents/ 会合并进 <runtime-root>/.dsh/.agents/,也就是桌面外壳把 DSH_AGENTS_HOME 指向的智能体配置主目录,因此它常驻加载的文档和用户级技能(其中包括 review 技能)能到达每个会话。只写入缺失的文件:已安装的文档或技能绝不覆盖,所以你对已安装副本的修改在每次构建后都保留。

结果就是运行时根目录下的入口二进制(cargo 自己的输出留在 src-tauri\target\release\ 或 target\debug\)。

标志(在 -- 之后传入):--debug 走 cargo build 调试构建,--skip-harness 跳过 dsh 运行时安装/构建,--skip-exe 完全跳过 cargo(只处理运行时/插件 —— 在没有 Tauri 系统依赖的 Linux 上很有用),--force-harness 即使运行时已是最新也重新安装或重建(等同于 DSH_HARNESS_REBUILD=1),--rebuild 一次性关闭所有跳过(--force-harness 加上重新安装每个插件;插件脚本会看到 DSH_PLUGIN_REBUILD=1)。
示例:npm run build -- --debug。

npm run rebuild 等同于 npm run build -- --rebuild:改动插件源码、掩码,或手动改过配置档状态之后用它,或者某个插件虽然锁定版本已在配置档里、但仍需重新安装时用它。全局智能体模板依然绝不覆盖 —— 这条规则针对的是你的修改,不是「是否最新」。

打包/安装程序生成被有意禁用(src-tauri/tauri.conf.json 里的 bundle.active: false)。应用始终从这份安装运行,正因如此你才能 git pull 子模块或添加插件,并在下次启动时生效。

运行 powershell
npm start

or double-click the entry binary at the runtime root

npm start 以分离方式启动入口 exe:终端立刻返回,关掉终端也绝不会杀掉 dsh-gui(或它的 harness 子进程)。启动器和入口 exe 都把 DSH_HOME 指向运行时根目录的 .dsh。启动时会拒绝已被占用的回环端口,在 Windows 上还会核实监听者属于它刚启动的 Node 子进程,所以无关的 dsh web 进程绝不会被误认成本次安装的后端。

只想在前台运行同一个 harness 后端、不启动 Tauri 外壳也不打开浏览器,用: powershell
npm run harness

它使用相同的 web --port <port> --no-open 参数,从解析出的运行时的 CLI 启动,工作目录就是该运行时的目录(npm 模式下是 .harness/,source 模式下是 deepseek-harness/),并把 DSH_HOME 指向运行时根目录的 .dsh,把 DSH_AGENTS_HOME 指向 .dsh\.agents(构建时所用的智能体配置主目录从 global_template.agents/ 填充;没有这个固定点,harness 会读取机器级的 ~/.agents,用户级技能就永远进不了会话)。它的输出留在终端里;按 Ctrl+C 停止。

两条启动路径都遵循 $env:DSH_GUI_PORT(默认 3080)。在 npm start 下,harness 输出记录到 .dsh\gui\harness.log;dsh-gui 自己的状态行写入 .dsh\gui\gui.log,启动失败还会弹出消息框(GUI 应用没有控制台可打印)。

系统快捷方式(Windows) powershell
npm run shortcut # desktop shortcut
npm run shortcut -- "D:\x.lnk" # arbitrary location

应用图标

应用图标来自 src-tauri/whale-icon 子模块(fornarwhal/deepseek-whale-girl-icon,CC BY-NC-SA 4.0):DeepSeekHarness-WhaleGirl.ico(16–256 px,透明)。src-tauri/tauri.conf.json 的 bundle.icon 指向它,因此它作为 Windows exe 资源嵌入,并用作窗口/任务栏图标。shell UI 本身不保留图标副本——换图标就换子模块,然后重新构建: powershell
git submodule update --remote src-tauri/whale-icon
npm run build

更新 harness 子模块 powershell
git submodule update --remote deepseek-harness
npm run build # install the submodule's version, then rebuild exe + plugins

子模块始终是版本来源:在默认的 npm 运行时下,构建会按子模块 apps/cli/package.json 记录的版本安装 @deepseek-ai/dsh;在 source 下则就地编译检出内容——见 docs/dsh-gui/harness-runtime.md。

在 staging 克隆中验证升级

.staging/dsh-gui 是本检出内容的持久克隆,包含子模块;<runtime-root>/.staging/ 是该克隆的运行时根目录,带有自己的 DSH_HOME、工具链、pnpm store、harness 安装和构建输出。先让升级走一遍克隆,这样损坏的 harness 或插件修订就永远不会到达本检出所服务的安装: powershell
npm run staging -- ensure # first time only: create the clone
npm run staging -- sync # move the clone onto this checkout's revision
cd ..\.staging\dsh-gui # from the repository root
npm run build -- --skip-exe # dsh runtime install/build -> plugin installs -> presets

npm run staging -- status 会报告两个修订版本、子模块漂移,以及克隆的构建进度;npm run staging -- clean --yes 删除克隆及其运行时目录。
完整的升级工作流是 dsh-gui-update 技能;工作区本身记录在 docs/dsh-gui/upgrade-staging-workspace.md。

运行时添加插件

插件安装到运行时根目录 DSH_HOME 下的 web 配置档中(.dsh/)。目录结构与 presets/ 一致:每个 plugins/<id>/ 包装目录都包含一个 install.mjs,外加插件包或仓库检出——通常放在二级目录;对于 deep-whale 这类多包分发仓库,还要再深一层:

plugins/<id>/install.mjs # plugin: builds + installs + mounts; dsh-web-ui: preset + pet + settings bridge
plugins/<id>/<package>/ # the plugin package (in-tree, or a git submodule)

最简的 install.mjs 把共用流水线委托出去,只声明自己的 id、包目录和子模块提示: js
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { installPlugin } from '../../scripts/plugin-install.mjs'

const here = dirname(fileURLToPath(import.meta.url))
installPlugin({
id: 'my-plugin',
packageDir: join(here, 'dsh-my-plugin'),
})

npm run build(或 npm run setup,或 npm run install:plugins)会按目录名顺序运行每个 plugins//install.mjs。每个插件包装目录:

1. 就地构建包(用固定版本的 pnpm 工具链执行 pnpm install + pnpm run build)——没有 build 脚本的包按原样使用(预构建的 lib/);如果某个预构建分发包仍声明了 build 脚本,包装目录可以传 build: false 强制走这条路,
2. 固定配置档的 pnpm store(.dsh\profiles\web\pnpm-workspace.yaml),
3. 以 link: 依赖的形式把它装进 web 配置档,并且
4. 往 .dsh\profiles\web\cordis.patch.yml 追加一条 insert 条目,把它挂载进 web 组合(harness 只加载组合里的条目——单有依赖不会生效)。条目 id 来自插件 package.json 里的 dsh.gui.mountId 声明,默认取包名去掉开头的 dsh-。

声明了 dsh.bundle.patch 的插件(自带 cordis.patch.yml bundle 层,例如 dsh-ai-update)会自行挂载:dsh plugin add 把它协调进配置档的 dsh.profile.bundles 列表,它的补丁以 bundle 层的形式插入条目——不会为它写 cordis.patch.yml insert。如果某个旧版本在获得 bundle 声明之前是手动挂载的,共享安装器会在重装时移除匹配的旧 insert,避免 Loader 出现重复条目。

plugins/deep-whale 以免编译源码安装的方式安装它的 maid-atelier 皮肤:子模块检出自带预构建的 lib/,所以包装目录按原样链接它(build: false,不复制,不打补丁)。

plugins/dsh-web-ui 是部分例外:它的 install.mjs 通过 installNpmPlugin 安装四个 npm bundle,版本固定到子模块 tag(0.3.22)——@linxin666/dsh-client-ui-web-ui-settings(排在最前)、@linxin666/dsh-client-ui-skill-explorer、@linxin666/dsh-usage 和 @linxin666/dsh-client-ui-model-capabilities;这四个都声明了 dsh.bundle.patch,所以 dsh plugin add 会把它们各自协调进 dsh.profile.bundles(无需手动 cordis insert)。任务看板(@linxin666/dsh-client-ui-task-board)和会话归档管理器(@linxin666/dsh-session-archive)不再安装(见 dsh-web-ui/README.md);插件管理器标签页(@linxin666/dsh-client-ui-plugin-manager)也不再安装,因为固定版本 harness(dsh-v0.1.6-alpha.2)的官方 dsh-web-app bundle 自带 ui-plugin-manager loader 条目,会和上游包的 bundle patch id 冲突。这个桥接是必需的,因为 dsh-host-apiproxy 硬编码的设置允许列表不暴露第三方命名空间,所以没有它,依赖 webUiSettings 的插件的配置表单就是只读的。dsh-web-ui/ 只作为源码参考保留(v0.3.x 布局: packages/dsh-web-settings、packages/dsh-web-all);其他所有 dsh-web 包都保持不安装、不挂载。

之后重启 dsh-gui(或 harness)——插件集变更在启动时生效。其他任何 dsh plugin / --patch 工作流也能用——什么都跑不出这个仓库。

Linux / WSL

这套工具链是纯 Node,能在 Linux 上跑(比如 WSL 里):npm run setup、
npm run build、npm run install:plugins 在那里行为完全一样。harness、插件、dsh web 服务器都是跨平台的。在 Linux 上构建 Tauri 外壳还需要 Tauri 系统库
(libwebkit2gtk-4.1-dev、libgtk-3-dev、libayatana-appindicator3-dev、……)
以及一个显示环境才能运行;没有这些,就用 npm run build -- --skip-exe 只安装
dsh 运行时和插件。

故障排查

- 「dsh CLI is not installed」(npm run harness 路径会打印 @deepseek-ai/dsh CLI not found at ...)——运行 npm run setup;这条消息会列出解析到的运行时、入口路径和预期版本。
- 安装插件时出现 ERR_PNPM_UNEXPECTED_STORE——配置档的 pnpm store 没有被固定(某次安装运行的上下文里,home 变量解析出的默认 store 和之前用的不一样)。运行一次
npm run install:plugins——每个插件安装脚本都会先把
storeDir 写进 .dsh\profiles\web\pnpm-workspace.yaml,再重新添加它的 link: 依赖。
- 「failed to spawn harness (is node on PATH?)」——安装 Node ^22.19 或 =24.2(见上面的要求;24.0–24.1 无法启动 harness CLI)。
- 窗口空白 / 连接被拒绝——窗口停在加载卡片上(或显示 DeepSeek Harness 启动失败),说明 harness 没起来:看 .dsh\gui\harness.log(比如端口已被占用——设置 DSH_GUI_PORT)。卡片的计时器会一直走,直到 harness 响应,或者 90 秒就绪超时在那里报出失败。
- 「127.0.0.1:3080 is already in use」——关掉已有的 dsh-gui / dsh web 进程,或者设置另一个 $env:DSH_GUI_PORT。外壳会刻意拒绝附着到已有服务器,因为它可能用了另一个 DSH_HOME,那样看起来就像首次安装、会话缺失。
- 启动后毫无反应——窗口还没出现就失败了(找不到仓库根目录,比如 exe 被单独复制出来),会弹消息框报错;之后的所有问题都显示在窗口的加载页上。.dsh\gui\gui.log 保留历史记录,发生 panic 时会在 exe 旁边写入 dsh-gui-crash.log。

- A leftover node process after an old version crashed — current builds kill the harness via a Windows job object; kill strays once with taskkill /IM node.exe /F (check nothing else needs them first).
- WebView2 error — install the WebView2 Evergreen runtime.
- A storm of flashing console windows while cargo test (or a dev build)
runs — some spawn used a bare Command::new(...): when the process has no
console of its own (the shell is a windows_subsystem = "windows" binary, and
a test run started from a console-less host has none either), Windows gives
every console child — git above all — its own window, which flashes and
steals the foreground. Every spawn goes through console::hidden_command
(CREATE_NO_WINDOW); only the update launcher is deliberately visible, and the
spawns_go_through_hidden_command unit test fails on any new bare spawn.

License

This project (the dsh-gui shell) is released into the public domain under
The Unlicense. The deepseek-harness submodule retains its own
license.
预览分享卡片长什么样 ↓ZgblKylin/dsh-gui
分享 · DeepSeek Harness Hub
ZgblKylin/dsh-gui

把 Harness 网页界面装进轻量桌面窗口,支持多后端连接

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部