deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
以"零额外安装"为设计原则:仅凭一个官方插件与 Windows 原生机制,让 DeepSeek Harness Web UI 获得桌面 App 式的一键启动体验。
不额外安装任何东西。以官方 DeepSeek Harness 为中心,通过官方支持的插件机制,尝试拼出类似桌面端的体验。
--app 独立窗口、系统应用注册,这些官方浏览器能力直接借用,不为小众需求自造轮子;同时也为"想深度适配浏览器生态"的用户保留了路径(安装为应用后任务栏/开始菜单/可固定全部由系统管理)。一句话:用最小的插件代价,换取最接近原生的桌面体验;不装软件,只用官方的东西。
前置要求:
dsh plugin命令依赖 pnpm。若提示pnpm is not recognized,先安装:npm install -g pnpm(或启用 Node 自带的 corepack:
corepack enable pnpm)
# 方式一:npm
npm install -g dsh-native-launcher
dsh plugin --profile web add dsh-native-launcher
# 方式二:源码安装
git clone https://github.com/ingleav626-art/dsh-native-launcher
dsh plugin --profile web add <path-to-repo>
# 重启 dsh web 后生效
安装后重启:桌面出现快捷方式,右下角托盘出现图标。双击快捷方式即用。
安装为应用(推荐,一次性):普通标签页打开 http://127.0.0.1:3080 → 视觉中心出现安装提示框 → 点「安装」→ 弹出浏览器安装提示 → 确认。装完后:
⋯ → 更多工具 → 应用 → 将此站点安装为应用DSH WebUI,官方 DSH 图标),双击即用;幂等——已存在则跳过,指向错误/图标变更自动重建,设置页可强制重建openMode 回退(--app 独立窗口 / --new-window / 默认)--app-id/--app 参数),每次启动都会验证浏览器进程是否真的起来——没起来自动换下一条路径,最终回退默认方式打开,不再"点了没反应"tray-notify.log;页面关闭时 host 自动补发(2s 确认窗口);等待批准 / 回答 / 计划审阅也会通知(pendingInteraction 检测)appExit,含持久化 flush);有任务则驻留到完成再退出;20s 防抖 + 2s 二次确认防误杀Q:任务完成了但没收到通知?
A:通知主通道是托盘原生通知(不依赖浏览器权限)。若托盘也没弹:先确认托盘图标在(重启 dsh 会自动拉起/换新托盘),再看 ~/.dsh-webui-launcher/tray-notify.log 是否有失败原因。另注意 Windows 会静默屏蔽短时间内的连续通知(同 tag 几秒内重复时尤其明显)——设置页"发送测试通知"请间隔几秒再点。
Q:关掉窗口后服务退出了,但我不想让它退?
A:把配置 closeToExit 改为 false——关窗后服务常驻(手动用托盘"退出 WebUI"才退出)。
Q:任务还在跑,我关了窗口,任务会丢吗? A:不会。有任务在跑时服务会驻留,任务跑完(且仍无窗口)才自动退出;任务完成还会弹托盘通知。
Q:改了端口,启动打开的还是旧页面?
A:改 port 后已安装的旧 PWA 仍指向旧端口,启动会自动回退 --app 模式(功能可用);清理旧应用请到 edge://apps 手动卸载。
Q:卸载重装了浏览器应用(PWA),通知没了? A:托盘通知不受影响;浏览器兜底通知需要在通知设置页重新点一次"请求权限"。
Q:托盘图标不见了? A:重启 dsh 会自动重新拉起(含旧托盘自动换新);仍不行就任务管理器结束残留的 PowerShell 托盘进程(含 pwsh)再重启。
Q:一个任务会收到两条通知(托盘+浏览器)? A:不会。托盘是主通道,浏览器通知只在其未送达时兜底(3 秒确认),正常情况下只弹一条。
dsh plugin --profile web remove dsh-native-launcher # 1. 移除插件(profile 依赖 + bundle)
然后手动清理生成物(插件只负责生成,不负责回收):
Remove-Item "$env:USERPROFILE\.dsh-webui-launcher" -Recurse -Force # 2. 启动脚本/托盘/图标/日志
Remove-Item "$env:USERPROFILE\Desktop\DSH WebUI.lnk" # 3. 桌面快捷方式
edge://apps → DSH WebUI → 卸载v0.1 基础(桌面化外壳):
pwahelper.exe 与旧版 msedge.exe --app-id;窗口检测按 app_id + 端口 URL(任意 host) 双锚点,域名 / 应用名 / 标题均可变v0.2 新增(可靠性 + 关闭语义):
tray-notify.logpagehide + fetch keepalive 上报),host 检查任务——无任务则走官方 appExit 优雅退出(持久化 flush);有任务则驻留到完成,任务完成且仍无窗口时自动退出;20s 防抖 + 2s 二次确认避免刷新/闪断/重开竞态误杀,多窗口全部关闭才算安装为应用后自动获得(浏览器原生能力,无需本插件代码):任务栏独立图标、无地址栏独立窗口、开始菜单条目、可固定任务栏、应用级关闭——与桌面 App 一致的窗口体验。
桌面快捷方式(DSH WebUI.lnk)
│ wscript.exe launcher.vbs(隐藏窗口,无黑窗)
▼
launch.cmd TCP 端口探测 (127.0.0.1:<port>)
├─ 已监听 → 拉起托盘 → open-webui.ps1(打开已装应用/浏览器,不重复启动)
└─ 未监听 → 拉起托盘 → set DSH_LAUNCHER=1 && dsh --profile web(静默启动)
│
▼
插件 apply(任意启动方式都会执行)
├─ 拉起系统托盘(Mutex 单实例 + 版本自更新)
├─ 注册 PWA 路由(manifest + 官方图标)
├─ 注册设置页 "WebUI 启动器" section
├─ 注册通知/关闭语义(见下)
└─ 检测 DSH_LAUNCHER=1 → loader.await() 就绪
→ webServer.port 就绪 → 打开 WebUI
open-webui.ps1 打开链路(多路探测,命中一个即启动):
| 优先级 | 方式 | 说明 |
|---|---|---|
| 0 | 已运行检测 → 聚焦现有窗口 | 按 app_id / 端口 URL(任意 host)匹配 pwahelper/msedge 进程;已在运行则 Win32 聚焦,绝不新开 |
| 0 | --app-id=<app_id> |
host 启动时扫描 Edge 已安装应用(Manifest Resources + Preferences 按站点 URL 匹配),部署自适应;冷启动后验证进程是否真的出现 |
| 0b | AppsFolder(explorer shell:AppsFolder\<AUMID>) |
Windows 已注册应用列表,按站点 host 前缀 + 名称匹配 |
| 1-2 | PWA 快捷方式扫描 | 开始菜单 / 任务栏 / 桌面(浏览器 exe + --app-id 特征),避免自我递归 |
| 3 | Chromium Web Applications 目录 | 旧结构 internal manifest 匹配 |
| 4 | --app / --new-window / 默认 |
未安装应用时的浏览器回退 |
通知链路(可靠主通道):
回合结束(agent 实时窗口判定,防重放)
├─ client(页面开着):规则过滤 → 上报 host → 写 tray-notify.json
│ └─ 3s 后查 tray-acked:托盘已消费 → 浏览器不弹;未消费 → 浏览器兜底
└─ host 兜底(2s 确认窗口):无客户端在线才补写(页面全关场景)
└─ 托盘 Timer 1.5s 轮询 → PowerShell 原生 Toast(AUMID 注册)
├─ 成功 → 删除队列文件
└─ 失败 → 记 tray-notify.log + BalloonTip + 提示音
等待批准 / 回答 / 计划审阅(pendingInteraction)走同一通道。
关闭语义(桌面 App 行为):
页面加载 → online 登记(per-tab clientId)
页面关闭/刷新 → pagehide + fetch keepalive → offline 上报
→ 全部客户端离线 → 20s 防抖(刷新/重连可取消)
→ 任务空闲 → 2s 二次确认 → appExit(0) 官方优雅退出(持久化 flush)
→ 任务在跑 → 驻留;任务完成且仍无客户端 → 自动退出
cordis.patch.yml(或 profile 的 patch 层覆盖):
- id: native-launcher
config:
# 快捷方式双击后执行的启动命令(cmd 中运行,依赖 PATH 里的 dsh)
launchCommand: dsh --profile web
# 是否自动打开浏览器(仅快捷方式启动且带 DSH_LAUNCHER=1 时)
autoOpen: true
# 快捷方式名称(不含扩展名)
shortcutName: DSH WebUI
# 快捷方式已存在时是否强制覆盖
force: false
# 端口探测端口(需与 webserver 端口一致)
port: 3080
# 是否启用系统托盘
tray: true
# 打开方式:app(--app 独立窗口,默认)| new-window(独立窗口)| default(浏览器默认行为)
openMode: app
# 关闭语义(桌面应用行为):所有页面窗口关闭后,无任务则优雅退出服务;有任务则驻留到完成
closeToExit: true
生成物(用户目录 ~/.dsh-webui-launcher/):
| 文件 | 作用 |
|---|---|
launcher.vbs |
wscript 入口:隐藏窗口调起 cmd |
launch.cmd |
TCP 端口探测 + 启动/直连 + 拉起托盘 |
open-webui.ps1 |
多路探测打开已安装应用 / 浏览器(已运行→聚焦,未运行→启动) |
tray.ps1 |
NotifyIcon 托盘(单实例 Mutex;打开 / 退出 WebUI;Toast 通知轮询;版本标记) |
tray-version.txt |
托盘脚本版本标记(apply 用它做托盘自更新) |
tray-notify.json |
通知队列文件(host 写 → 托盘轮询弹 Toast → 消费删除) |
dsh-webui.ico |
快捷方式 / 托盘图标(官方 DSH 图标) |
native-launcher.log |
插件运行日志(启动/快捷方式/托盘/通知/关闭语义诊断) |
tray-notify.log |
托盘 Toast 失败原因 / tick 错误(排查用) |
pwa-scan.log |
PWA 应用扫描诊断日志(每次启动重写,排查用) |
| 方案 | 形态 | 亮点 | 短板 |
|---|---|---|---|
| dsh-native-launcher(本插件) | 标准 dsh 插件 + Windows 自带机制 | 轻量(零重型依赖)、托盘、已装应用优先打开、可靠通知、关窗自动退出、安装引导、设置页、端口直连、静默启动、官方图标 | 仅 Windows |
| jenokagong/dsh-webui-launcher | 纯 bat | 控制台可最小化恢复、快速启动(~2s) | 关窗=停服务、无托盘、无端口直连 |
| LvienOeria 插件 | dsh 插件 | 幂等 state hash、配置 .bak 备份、坏配置大声报错 | 不支持 Windows,无托盘/快捷方式 |
| zhanweipan 启动器 | Electron | 一键部署、版本管理、多实例、日志面板 | 重型桌面端,与轻量定位相悖 |
| Hllojjh 托盘 | Python 托盘 | 单实例互斥、只停自己进程树、外部占用识别、二次确认 | 依赖 Python 运行时 |
| Ruler4396 启动器 | WebView2 | 服务驻留三模式、关窗即停 | 依赖 WebView2 运行时 |
差异化:同为 dsh 插件的方案里,LvienOeria 不支持 Windows、jenokagong 无托盘,而重型桌面端(Electron/WebView2/Python)都要求额外安装运行时——与"不额外安装任何东西"的理念相悖。本插件是"纯 Windows 原生 + 纯插件"的最小代价路线:桌面"应用窗口"走浏览器原生 PWA 机制——用户安装 PWA 后快捷方式直接打开已安装应用(任务栏独立、官方图标、可固定),而非自造浏览器壳——同样是"任务栏独立应用",成本比 Electron/WebView2 低一个数量级,且零运行时依赖。
lib/notification-host.js)与 client 完成检测/设置(lib/notification-client.js)为其原样构建产物(v0.1.2 同步:含等待确认通知),通过本插件包内模块挂载(含 Settings > 通知 设置页)本插件将其浏览器通知升级为托盘通知更可靠。favicon.svg),仅用于非商业开源插件场景MIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。