dsh-windows-tray 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,146 +1,147 @@
1
- # dsh-windows-tray
2
-
3
- > DeepSeek Harness(dsh)的 Windows 系统托盘管家:**官方鲸鱼状态图标 + Web/TUI 双状态显示 + 一键启停**。
4
- > 纯 PowerShell + WinForms 实现,零依赖、无编译、不改 dsh 源码,卸载即还原。
5
-
6
- [![npm](https://img.shields.io/npm/v/dsh-windows-tray?style=flat-square)](https://www.npmjs.com/package/dsh-windows-tray)
7
- [![npm downloads](https://img.shields.io/npm/dm/dsh-windows-tray?style=flat-square)](https://www.npmjs.com/package/dsh-windows-tray)
8
- [![Platform](https://img.shields.io/badge/platform-Windows%2010%2F11-blue?style=flat-square)](https://www.npmjs.com/package/dsh-windows-tray)
9
- [![License](https://img.shields.io/npm/l/dsh-windows-tray?style=flat-square)](https://github.com/)
10
-
11
- ## 是什么
12
-
13
- dsh 默认在终端前台运行 `dsh web`——必须留着一个命令行窗口。本工具把它变成**一个标准的 Windows 托盘应用**:
14
-
15
- - 🐋 **状态一目了然**:品牌蓝鲸鱼 = Web 运行中;灰鲸鱼 = 未运行(图标由官方 favicon 渲染生成)
16
- - 📊 **DSH / TUI 双状态**:菜单顶部同屏显示 Web 与 TUI 各自的运行状态(含 PID),互不干扰
17
- - 🖱️ **零窗口操作**:双击开界面,右键通过 DSH / TUI 分组子菜单一键启停,不用再碰命令行
18
- - 👁️ **独立旁观者**:托盘是独立常驻进程,dsh 停止时它依然在(显示未运行 + 一键启动),退出托盘不影响 dsh
19
- - 🎨 **浅色圆角菜单**:白底、圆角、投影阴影,贴合现代桌面应用审美
20
-
21
- ## 功能特性
22
-
23
- | 交互 | 行为 |
24
- | :--- | :--- |
25
- | **图标** | 蓝鲸鱼 = Web 运行中(PID);灰鲸鱼 = 未运行 |
26
- | **悬停提示** | `Web UI 运行中 · 127.0.0.1:3088` / `Web UI 未运行 · 右键可启动` |
27
- | **双击** | 打开 Web UI(未运行先提示启动) |
28
- | **右键菜单** | 顶部 `Web UI 状态` / `TUI 状态` 双状态行;`Web UI ▸`(打开 Web UI / 启动 / 停止(带确认) / 重启);`TUI ▸`(启动 / 停止);打开日志目录 / `版本信息 ▸`(版本 / 检查更新 / 更新到最新版)/ 退出托盘 |
29
- | **菜单智能禁用** | Web 运行中禁用「启动 Web UI」;TUI 运行中禁用「启动 TUI」 |
30
- | **自更新** | 「版本信息 ▸」子菜单(位于退出托盘上方,非日常功能区):查看版本 / 检查更新 / 一键更新到最新版(自动重部署 + 重启托盘) |
31
- | **状态通知** | Web/TUI 启动与停止瞬间弹系统通知;托盘就绪弹一次提示 |
32
- | **开机自启** | 登录后托盘自动出现(启动文件夹快捷方式,安装时自动创建) |
33
-
34
- ## 适配与依赖
35
-
36
- | 项 | 说明 |
37
- | :--- | :--- |
38
- | **dsh 内核** | 适配 `@deepseek-ai/dsh` **v0.1.1-rc.2 系列**(共享核心 `@deepseek-ai/dsh-base` 同版本);dsh 升级不影响托盘 |
39
- | **Web 服务** | dsh web 默认监听 `127.0.0.1:3088`(即浏览器里的 Web UI)——托盘通过 TCP 探测该端口判断 Web 状态(端口被改时同步修改 `dsh-lib.ps1` 的 `$PORT` 即可) |
40
- | **TUI 插件** | 适配 `@deepseek-harness-tui/dsh-tui` **^0.9.2**(通过 dsh-profile `dsh-profile-dsh-tui` 挂载);托盘按进程命令行 `dsh-tui` 检测,与插件版本解耦 |
41
- | **Node.js** | ≥ 18(dsh 本体运行所需) |
42
- | **PowerShell** | 7(托盘常驻进程;缺省回退 Windows PowerShell 5.1) |
43
- | **运行时依赖** | **零 npm 依赖**——纯 PowerShell + WinForms + 系统 API;托盘不修改 dsh、不注入任何内容,纯旁观者 |
44
-
45
- ## 界面预览
46
-
47
- | 运行中(品牌蓝 `#4176E6`) | 未运行(中性灰 `#81858C`) |
48
- | :---: | :---: |
49
- | ![running](build/dsh-tray-running-preview.png) | ![stopped](build/dsh-tray-stopped-preview.png) |
50
-
51
- > 图标源 = dsh Web 前端官方 favicon(鲸鱼),多尺寸 16~256px;可随时用 `dsh-tray icons` 重新生成。
52
-
53
- ## 快速开始
54
-
55
- 前置:Windows 10/11 + [PowerShell 7](https://github.com/PowerShell/PowerShell/releases)(7.x;缺失时自动回退 Windows PowerShell 5.1)+ Node.js **≥ 18** + 已安装 dsh(任意位置)。
56
-
57
- ```powershell
58
- # 1. 全局安装
59
- npm i -g dsh-windows-tray
60
-
61
- # 2. 一键部署(自动探测 dsh/pwsh/node 路径 → 安装到 ~\.dsh\launcher
62
- # → 按本机路径改写配置 → 创建开机自启+桌面快捷方式 → 启动托盘)
63
- dsh-tray install
64
-
65
- # 3. 首次使用:把任务栏 ^ 溢出区里的鲸鱼拖到任务栏(一次性,Windows 会记住)
66
- ```
67
-
68
- 部署完成后即可双击鲸鱼打开 Web UI,右键管理一切。
69
-
70
- ## 常用命令
71
-
72
- | 命令 | 作用 |
73
- | :--- | :--- |
74
- | `dsh-tray` | 启动托盘(已有实例则提示,不重复启动) |
75
- | `dsh-tray install` | 一键部署 / 重新部署(可透传 `-DshRoot` 等参数) |
76
- | `dsh-tray icons` | 重新生成鲸鱼图标(内置 512px 源图,无需浏览器) |
77
- | `dsh-tray info` | 查看版本 / 安装位置 / 部署状态 |
78
- | `npm up -g dsh-windows-tray` | 命令行更新(更新后建议 `dsh-tray install` 重部署) |
79
-
80
- ## 如何更新
81
-
82
- 两种方式任选:
83
-
84
- **方式一:托盘一键更新(推荐)**——托盘菜单「版本信息 ▸」→「检查更新」对比 npm 最新版,发现新版后点「更新到最新版」:自动 `npm install -g` → 重新部署(含本机路径改写)→ 自动重启托盘,全程约 1 分钟,失败不影响当前托盘。
85
-
86
- **方式二:npm 标准流程**——与普通 npm 包一致:
87
-
88
- ```powershell
89
- npm up -g dsh-windows-tray # 拉取最新版
90
- dsh-tray install # 重新部署(自动改写本机路径 + 刷新快捷方式)
91
- dsh-tray # 重启托盘(或右键「退出托盘」后重新登录/双击快捷方式)
92
- ```
93
-
94
- > 部署目录固定为 `~\.dsh\launcher`,重装/更新都会覆盖旧文件,不会残留;`dsh-tray info` 可查看当前版本与部署状态。
95
-
96
- ## 卸载
97
-
98
- ```powershell
99
- npm rm -g dsh-windows-tray # ① 移除 npm 包本体
100
- # ② 删除快捷方式: 启动文件夹 + 桌面 各一个「DSH 系统托盘.lnk」
101
- # ③ 删除部署目录 ~\.dsh\launcher (如与 dsh 控制台共用, 确认无需保留再删)
102
- ```
103
-
104
- ## 工作原理
105
-
106
- ```
107
- 托盘(pwsh 常驻) ──2s 轮询──► 127.0.0.1:3088 (Web UI 端口)
108
- │ └──────────────► node 进程命令行含 'dsh-tui' (TUI 进程)
109
- ├── 菜单动作 ──► dsh-actions.ps1 (独立隐藏进程, 不卡 UI)
110
- ├── 自更新 ──► dsh-update.ps1 (npm 安装→重部署→重启)
111
- └── 单实例 ──► 命名互斥锁 (被强杀遗留的锁自动接管)
112
- ```
113
-
114
- - **状态检测**:Web 用 TCP 端口探测(物理单实例);TUI 按命令行匹配(多实例安全:全查全杀);TUI 检测 20s 节流,打开菜单时强制刷新
115
- - **动作隔离**:启动/停止/重启在独立进程执行,托盘 UI 永不卡顿
116
- - **日志**:`~\.dsh\logs\dsh-tray.log`(状态变化)+ `dsh-tray-actions.log`(动作)
117
- - **单点配置**:端口/路径等常量集中在 `launcher/dsh-lib.ps1`,安装时按本机自动改写——**包本身与机器无关**
118
-
119
- ## 配置
120
-
121
- | 项 | 位置 | 默认 |
122
- | :--- | :--- | :--- |
123
- | 监控端口 | `~\.dsh\launcher\dsh-lib.ps1` 顶部 `$PORT` | `3088` |
124
- | dsh 路径 | 同上(安装时自动改写) | 自动探测 |
125
- | 图标颜色 | `build/build-tray-icons.ps1`(运行蓝/停止灰) | `#4176E6` / `#81858C` |
126
-
127
- ## 常见问题
128
-
129
- | 问题 | 处理 |
130
- | :--- | :--- |
131
- | 任务栏看不到鲸鱼 | 点开 `^` 溢出区,把鲸鱼拖到任务栏(一次性) |
132
- | 停止 Web UI 会关掉浏览器界面 | 对——停止前有确认框;Web 与当前会话在 3088 端口上 |
133
- | 托盘退出了怎么唤回 | 双击桌面「DSH 系统托盘」快捷方式 / 重新登录(开机自启)/ `dsh-tray` |
134
- | 怎么更新到新版 | 托盘菜单「版本信息 ▸ → 更新到最新版」一键完成;或 `npm up -g dsh-windows-tray` + `dsh-tray install`(见「如何更新」) |
135
- | 检查更新失败 | 网络或 npm 不可用;托盘继续正常工作,稍后再试 |
136
- | dsh 内核升级了,托盘要动吗 | 不用——托盘只探测 3088 端口与 TUI 进程命令行,与内核版本解耦 |
137
- | 想彻底卸载 | `npm rm -g dsh-windows-tray` + 删除两个「DSH 系统托盘.lnk」+ 删除 `~\.dsh\launcher` |
138
-
139
- ## 文档
140
-
141
- - `docs/system-tray-guide.md` — 使用手册(安装部署 / 功能一览 / 更新 / 卸载 / 故障排查)
142
- - `docs/tray-dev-archive.md` — 开发归档(架构决策与历史记录)
143
-
144
- ## 许可
145
-
146
- MIT
1
+ # dsh-windows-tray
2
+
3
+ > DeepSeek Harness(dsh)的 Windows 系统托盘管家:**官方鲸鱼状态图标 + Web/TUI 双状态显示 + 一键启停**。
4
+ > 纯 PowerShell + WinForms 实现,零依赖、无编译、不改 dsh 源码,卸载即还原。
5
+
6
+ [![npm](https://img.shields.io/npm/v/dsh-windows-tray?style=flat-square)](https://www.npmjs.com/package/dsh-windows-tray)
7
+ [![npm downloads](https://img.shields.io/npm/dm/dsh-windows-tray?style=flat-square)](https://www.npmjs.com/package/dsh-windows-tray)
8
+ [![Platform](https://img.shields.io/badge/platform-Windows%2010%2F11-blue?style=flat-square)](https://www.npmjs.com/package/dsh-windows-tray)
9
+ [![License](https://img.shields.io/npm/l/dsh-windows-tray?style=flat-square)](https://github.com/)
10
+
11
+ ## 是什么
12
+
13
+ dsh 默认在终端前台运行 `dsh web`——必须留着一个命令行窗口。本工具把它变成**一个标准的 Windows 托盘应用**:
14
+
15
+ - 🐋 **状态一目了然**:品牌蓝鲸鱼 = Web 运行中;灰鲸鱼 = 未运行(图标由官方 favicon 渲染生成)
16
+ - 📊 **DSH / TUI 双状态**:菜单顶部同屏显示 Web 与 TUI 各自的运行状态(含 PID),互不干扰
17
+ - 🖱️ **零窗口操作**:双击开界面,右键通过 DSH / TUI 分组子菜单一键启停,不用再碰命令行
18
+ - 👁️ **独立旁观者**:托盘是独立常驻进程,dsh 停止时它依然在(显示未运行 + 一键启动),退出托盘不影响 dsh
19
+ - 🎨 **浅色圆角菜单**:白底、圆角、投影阴影,贴合现代桌面应用审美
20
+
21
+ ## 功能特性
22
+
23
+ | 交互 | 行为 |
24
+ | :--- | :--- |
25
+ | **图标** | 蓝鲸鱼 = Web 运行中(PID);灰鲸鱼 = 未运行 |
26
+ | **悬停提示** | `Web UI 运行中 · 127.0.0.1:3080` / `Web UI 未运行 · 右键可启动` |
27
+ | **双击** | 打开 Web UI(未运行先提示启动) |
28
+ | **右键菜单** | 顶部 `Web UI 状态` / `TUI 状态` 双状态行;`Web UI ▸`(打开 Web UI / 启动 / 停止(带确认) / 重启);`TUI ▸`(启动 / 停止);打开日志目录 / `版本信息 ▸`(版本 / 检查更新 / 更新到最新版)/ 退出托盘 |
29
+ | **菜单智能禁用** | Web 运行中禁用「启动 Web UI」;TUI 运行中禁用「启动 TUI」 |
30
+ | **自更新** | 「版本信息 ▸」子菜单(位于退出托盘上方,非日常功能区):查看版本 / 检查更新 / 一键更新到最新版(自动重部署 + 重启托盘) |
31
+ | **状态通知** | Web/TUI 启动与停止瞬间弹系统通知;托盘就绪弹一次提示 |
32
+ | **开机自启** | 登录后托盘自动出现 + dsh Web 静默拉起(两个启动文件夹快捷方式,安装时自动创建):「DSH 系统托盘.lnk」→ `dsh-tray.ps1`;「DeepSeek Harness (dsh).lnk」→ `dsh-boot.ps1` |
33
+
34
+ ## 适配与依赖
35
+
36
+ | 项 | 说明 |
37
+ | :--- | :--- |
38
+ | **dsh 内核** | **声明**(`package.json` → `dshCompatibility`):`range` = `>=0.1.1-rc.2 <0.2.0`,`testedVersions` = **0.1.1-rc.2 / 0.1.2-rc.1 / 0.1.5-rc.1 / 0.1.5-rc.2**;`install.ps1` 会做**软校验**(不在实测清单→黄字提醒,不阻断)。口径说明:本包是**外部工具**(不 import 任何 `@deepseek-ai` 代码),故不声明 `@deepseek-ai` peerDependencies。适配 `@deepseek-ai/dsh` **v0.1.1-rc.2 起系列**;**rc.1 起引入一次性 launch token 鉴权**(打开 Web UI 必须用启动横幅 `?token=` URL 首次签发持久 cookie)→ **打开/重启 Web UI 需托盘 ≥1.0.11**(自动解析 `web-menu.out.log` 的 token 并探活;无 token/失效回退裸 URL = 向后兼容);1.0.10 及更早在 rc.1+ 上打开的是裸 URL,会看到 `dsh web authentication required` 提示页 |
39
+ | **Web 服务** | dsh web 默认监听 `127.0.0.1:3080`(即浏览器里的 Web UI)——托盘通过 TCP 探测该端口判断 Web 状态(端口被改时同步修改 `dsh-lib.ps1` 的 `$PORT` 即可) |
40
+ | **TUI 插件** | 适配 `@deepseek-harness-tui/dsh-tui` **^0.9.2(含 0.10.0-beta.4,2026-09-03 实测兼容)**(通过 dsh-profile `dsh-profile-dsh-tui` 挂载);托盘按进程命令行 `dsh-tui` 检测,与插件版本解耦 |
41
+ | **Node.js** | ≥ 18(dsh 本体运行所需) |
42
+ | **PowerShell** | 7(托盘常驻进程;缺省回退 Windows PowerShell 5.1) |
43
+ | **运行时依赖** | **零 npm 依赖**——纯 PowerShell + WinForms + 系统 API;托盘不修改 dsh、不注入任何内容,纯旁观者 |
44
+
45
+ ## 界面预览
46
+
47
+ | 运行中(品牌蓝 `#4176E6`) | 未运行(中性灰 `#81858C`) |
48
+ | :---: | :---: |
49
+ | ![running](build/dsh-tray-running-preview.png) | ![stopped](build/dsh-tray-stopped-preview.png) |
50
+
51
+ > 图标源 = dsh Web 前端官方 favicon(鲸鱼),多尺寸 16~256px;可随时用 `dsh-tray icons` 重新生成。
52
+
53
+ ## 快速开始
54
+
55
+ 前置:Windows 10/11 + [PowerShell 7](https://github.com/PowerShell/PowerShell/releases)(7.x;缺失时自动回退 Windows PowerShell 5.1)+ Node.js **≥ 18** + 已安装 dsh(任意位置)。
56
+
57
+ ```powershell
58
+ # 1. 全局安装
59
+ npm i -g dsh-windows-tray
60
+
61
+ # 2. 一键部署(自动探测 dsh/pwsh/node 路径 → 安装到 ~\.dsh\launcher
62
+ # → 按本机路径改写配置 → 创建开机自启(托盘 + harness Web)+ 桌面快捷方式 → 启动托盘)
63
+ dsh-tray install
64
+
65
+ # 3. 首次使用:把任务栏 ^ 溢出区里的鲸鱼拖到任务栏(一次性,Windows 会记住)
66
+ ```
67
+
68
+ 部署完成后即可双击鲸鱼打开 Web UI,右键管理一切。
69
+
70
+ ## 常用命令
71
+
72
+ | 命令 | 作用 |
73
+ | :--- | :--- |
74
+ | `dsh-tray` | 启动托盘(已有实例则提示,不重复启动) |
75
+ | `dsh-tray install` | 一键部署 / 重新部署(可透传 `-DshRoot` 等参数) |
76
+ | `dsh-tray icons` | 重新生成鲸鱼图标(内置 512px 源图,无需浏览器) |
77
+ | `dsh-tray info` | 查看版本 / 安装位置 / 部署状态 |
78
+ | `npm up -g dsh-windows-tray` | 命令行更新(更新后建议 `dsh-tray install` 重部署) |
79
+
80
+ ## 如何更新
81
+
82
+ 两种方式任选:
83
+
84
+ **方式一:托盘一键更新(推荐)**——托盘菜单「版本信息 ▸」→「检查更新」对比 npm 最新版,发现新版后点「更新到最新版」:自动 `npm install -g` → 重新部署(含本机路径改写)→ 自动重启托盘,全程约 1 分钟,失败不影响当前托盘。
85
+
86
+ **方式二:npm 标准流程**——与普通 npm 包一致:
87
+
88
+ ```powershell
89
+ npm up -g dsh-windows-tray # 拉取最新版
90
+ dsh-tray install # 重新部署(自动改写本机路径 + 刷新快捷方式)
91
+ dsh-tray # 重启托盘(或右键「退出托盘」后重新登录/双击快捷方式)
92
+ ```
93
+
94
+ > 部署目录固定为 `~\.dsh\launcher`,重装/更新都会覆盖旧文件,不会残留;`dsh-tray info` 可查看当前版本与部署状态。
95
+
96
+ ## 卸载
97
+
98
+ ```powershell
99
+ npm rm -g dsh-windows-tray # ① 移除 npm 包本体
100
+ # ② 删除快捷方式: 「DSH 系统托盘.lnk」(启动文件夹 + 桌面) + 「DeepSeek Harness (dsh).lnk」(启动文件夹)
101
+ # ③ 删除部署目录 ~\.dsh\launcher (如与 dsh 控制台共用, 确认无需保留再删)
102
+ ```
103
+
104
+ ## 工作原理
105
+
106
+ ```
107
+ 托盘(pwsh 常驻) ──2s 轮询──► 127.0.0.1:3080 (Web UI 端口)
108
+ │ └──────────────► node 进程命令行含 'dsh-tui' (TUI 进程)
109
+ ├── 菜单动作 ──► dsh-actions.ps1 (独立隐藏进程, 不卡 UI)
110
+ ├── 自更新 ──► dsh-update.ps1 (npm 安装→重部署→重启)
111
+ └── 单实例 ──► 命名互斥锁 (被强杀遗留的锁自动接管)
112
+ ```
113
+
114
+ - **状态检测**:Web 用 TCP 端口探测(物理单实例);TUI 按命令行匹配(多实例安全:全查全杀);TUI 检测 20s 节流,打开菜单时强制刷新
115
+ - **动作隔离**:启动/停止/重启在独立进程执行,托盘 UI 永不卡顿
116
+ - **日志**:`~\.dsh\logs\dsh-windows-tray\`(`dsh-tray.log` 状态变化 / `dsh-tray-actions.log` 菜单动作 / `dsh-boot.log` 开机自启)
117
+ - **单点配置**:端口/路径等常量集中在 `launcher/dsh-lib.ps1`,安装时按本机自动改写——**包本身与机器无关**
118
+
119
+ ## 配置
120
+
121
+ | 项 | 位置 | 默认 |
122
+ | :--- | :--- | :--- |
123
+ | 监控端口 | `~\.dsh\launcher\dsh-lib.ps1` 顶部 `$PORT` | `3080` |
124
+ | dsh 路径 | 同上(安装时自动改写) | 自动探测 |
125
+ | 图标颜色 | `build/build-tray-icons.ps1`(运行蓝/停止灰) | `#4176E6` / `#81858C` |
126
+
127
+ ## 常见问题
128
+
129
+ | 问题 | 处理 |
130
+ | :--- | :--- |
131
+ | 任务栏看不到鲸鱼 | 点开 `^` 溢出区,把鲸鱼拖到任务栏(一次性) |
132
+ | 停止 Web UI 会关掉浏览器界面 | 对——停止前有确认框;Web 与当前会话在 3080 端口上 |
133
+ | 托盘退出了怎么唤回 | 双击桌面「DSH 系统托盘」快捷方式 / 重新登录(开机自启)/ `dsh-tray` |
134
+ | 怎么更新到新版 | 托盘菜单「版本信息 ▸ → 更新到最新版」一键完成;或 `npm up -g dsh-windows-tray` + `dsh-tray install`(见「如何更新」) |
135
+ | 检查更新失败 | 网络或 npm 不可用;托盘继续正常工作,稍后再试 |
136
+ | dsh 内核升级了,托盘要动吗 | 不用——托盘只探测 3080 端口与 TUI 进程命令行,与内核版本解耦 |
137
+ | 想彻底卸载 | `npm rm -g dsh-windows-tray` + 删除「DSH 系统托盘.lnk」与「DeepSeek Harness (dsh).lnk」+ 删除 `~\.dsh\launcher` |
138
+ | 开机后 dsh Web 没自动起 | 看 `~\.dsh\logs\dsh-windows-tray\dsh-boot.log`:末行 `boot ok (started)` = 正常;`boot FAILED` 后跟的错误即原因(隐藏窗口下日志是唯一线索),常见是 `~\.dsh\launcher` 被移动/删除 |
139
+
140
+ ## 文档
141
+
142
+ - `docs/system-tray-guide.md` — 使用手册(安装部署 / 功能一览 / 更新 / 卸载 / 故障排查)
143
+ - `docs/tray-dev-archive.md` — 开发归档(架构决策与历史记录)
144
+
145
+ ## 许可
146
+
147
+ MIT
package/bin/dsh-tray.js CHANGED
@@ -38,8 +38,13 @@ switch (cmd) {
38
38
  break;
39
39
 
40
40
  case 'icons':
41
+ // 2026-09-12 修 wart:脚本默认 -OutDir = $PSScriptRoot\icons(包内 build/icons),**不刷新已部署目录**;
42
+ // 已部署时显式指到 <deployDir>\icons,未部署则落包内 launcher\icons(与发货路径一致)。
41
43
  spawn(pwsh, ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File',
42
- path.join(root, 'build', 'build-tray-icons.ps1')],
44
+ path.join(root, 'build', 'build-tray-icons.ps1'),
45
+ '-OutDir', fs.existsSync(path.join(deployDir, 'dsh-tray.ps1'))
46
+ ? path.join(deployDir, 'icons')
47
+ : path.join(root, 'launcher', 'icons')],
43
48
  { stdio: 'inherit', windowsHide: true })
44
49
  .on('exit', (code) => process.exit(code == null ? 1 : code));
45
50
  break;
@@ -1,7 +1,9 @@
1
1
  # DSH 系统托盘(Windows)—— npm 包使用手册
2
2
 
3
3
  > 包名: **`dsh-windows-tray`** · 部署位置: `~\.dsh\launcher` · 技术栈: PowerShell 7 + WinForms,零 npm 依赖、纯旁观者、不改 dsh 源码。
4
- > 安装 / 更新 / 卸载全部走 npm 标准流程;开发归档见 [tray-dev-archive.md](tray-dev-archive.md),相关踩坑见 [powershell-pitfalls.md](powershell-pitfalls.md)。
4
+ > 安装 / 更新 / 卸载全部走 npm 标准流程;开发归档见 [tray-dev-archive.md](tray-dev-archive.md),相关踩坑见 [../../../docs/powershell-pitfalls.md](../../../docs/powershell-pitfalls.md)。
5
+ >
6
+ > **端口(2026-09-07 起)**:本包默认监控/使用端口为 **3080**。
5
7
 
6
8
  ---
7
9
 
@@ -14,7 +16,7 @@
14
16
  npm i -g dsh-windows-tray
15
17
 
16
18
  # 2. 一键部署:自动探测 dsh/pwsh/node 路径 → 安装到 ~\.dsh\launcher
17
- # → 按本机路径改写配置 → 创建开机自启 + 桌面快捷方式 → 启动托盘
19
+ # → 按本机路径改写配置 → 创建开机自启(托盘 + harness Web)+ 桌面快捷方式 → 启动托盘
18
20
  dsh-tray install
19
21
 
20
22
  # 3. 首次使用:把任务栏 ^ 溢出区里的鲸鱼拖到任务栏(一次性,Windows 会记住)
@@ -22,6 +24,8 @@ dsh-tray install
22
24
 
23
25
  ### 安装后必做:本机参数探测 → 确认 → 回填(不要信默认值)
24
26
 
27
+ > **v1.0.2 起 install 已自动完成本节的探测与回填**(探测运行中 web 进程的端口与数据目录 → 展示确认 → 回填全部读取点 + 自动建日志目录)。以下命令仅用于**排查**或手动核对;旧版本(≤1.0.1)部署的机器仍需按此手工适配一次。
28
+
25
29
  install 只自动改写 dsh/pwsh/node **路径**;**端口、数据目录按包默认值写入且不改**。与本机实际不一致时,症状是托盘永远灰鲸(探测错端口)或托盘/控制台启动的 web 落到空数据目录。先实测,人工确认数值,再回填到 §六 列出的**全部**填充点(只改一处会被另一处覆盖):
26
30
 
27
31
  ```powershell
@@ -48,21 +52,21 @@ Test-Path "$env:USERPROFILE\.dsh\settings.yaml" # True=默认目录;False=
48
52
  | 交互 | 行为 |
49
53
  | :--- | :--- |
50
54
  | **图标** | 🐋 品牌蓝鲸鱼 = Web 运行中;灰鲸鱼 = 未运行(官方 favicon 渲染,多尺寸 16~256px) |
51
- | **悬停提示** | `Web UI 运行中 · 127.0.0.1:3088` / `Web UI 未运行 · 右键可启动` |
55
+ | **悬停提示** | `Web UI 运行中 · 127.0.0.1:3080` / `Web UI 未运行 · 右键可启动` |
52
56
  | **双击** | 打开 Web UI(未运行时提示先启动) |
53
57
  | **右键菜单** | 顶部 `Web UI 状态` / `TUI 状态` 双状态行(含 PID);`Web UI ▸`(打开 Web UI / 启动 / 停止(带确认) / 重启);`TUI ▸`(启动 / 停止);打开日志目录 / `版本信息 ▸`(版本 / 检查更新 / 更新到最新版)/ 退出托盘 |
54
58
  | **菜单智能禁用** | Web 运行中禁用「启动 Web UI」;TUI 运行中禁用「启动 TUI」 |
55
59
  | **状态通知** | Web/TUI 启动与停止瞬间弹系统通知;托盘就绪弹一次提示 |
56
- | **开机自启** | 登录后托盘自动出现(启动文件夹快捷方式,安装时自动创建) |
60
+ | **开机自启** | 登录后托盘自动出现 + dsh Web 静默拉起(两个启动文件夹快捷方式,安装时自动创建):「DSH 系统托盘.lnk」→ `dsh-tray.ps1`;「DeepSeek Harness (dsh).lnk」→ `dsh-boot.ps1`(点源 `dsh-lib.ps1` → `Start-Dsh -OpenBrowser:$false`) |
57
61
 
58
- 托盘每 2 秒探测一次 `127.0.0.1:3088`(TCP 连接,500ms 超时,开销可忽略);菜单动作在独立隐藏进程执行,托盘 UI 永不卡顿。
62
+ 托盘每 2 秒探测一次 `127.0.0.1:3080`(TCP 连接,500ms 超时,开销可忽略);菜单动作在独立隐藏进程执行,托盘 UI 永不卡顿。
59
63
 
60
64
  ## 三、日常使用
61
65
 
62
66
  1. **自动跟随**:在 dsh 控制台(`~\.dsh\launcher\dsh-control.ps1`)执行 启动/重启 Web 时,托盘会自动拉起(幂等)。
63
67
  2. **手动唤起**:双击桌面「DSH 系统托盘」快捷方式;或 `dsh-tray`。
64
68
  3. **退出托盘**:右键 → 退出托盘(只退出托盘本身,不影响 dsh;下次登录/双击快捷方式可重新唤起)。
65
- 4. **取消自启**:删除启动文件夹里的「DSH 系统托盘.lnk」。
69
+ 4. **取消自启**:删除启动文件夹里的「DSH 系统托盘.lnk」与「DeepSeek Harness (dsh).lnk」(后者只关 dsh Web 的静默自启;托盘仍可从桌面快捷方式/控制台唤起)。
66
70
 
67
71
  ## 四、更新
68
72
 
@@ -85,7 +89,7 @@ dsh-tray # 重启托盘
85
89
 
86
90
  ```powershell
87
91
  npm rm -g dsh-windows-tray # 移除 npm 包本体
88
- # 删除两个「DSH 系统托盘.lnk」(启动文件夹 + 桌面)
92
+ # 删除三个「DSH 系统托盘.lnk」(启动文件夹 + 桌面)与启动文件夹里的「DeepSeek Harness (dsh).lnk」
89
93
  # 删除 ~\.dsh\launcher 目录(托盘部署文件;如与 dsh 控制台共用,确认无需保留再删)
90
94
  ```
91
95
 
@@ -95,7 +99,7 @@ npm rm -g dsh-windows-tray # 移除 npm 包本体
95
99
 
96
100
  | 项 | 位置 | 包默认 | 说明 |
97
101
  | :--- | :--- | :--- | :--- |
98
- | 监控端口 | ① `~\.dsh\launcher\dsh-lib.ps1` 顶部 `$PORT`;② `dsh-tray.ps1` 顶部 `param([int]$Port = …)` | `3088` | **两处必须一致**:托盘启动时先点源 lib 再执行 `$PORT = $Port`,**param 默认值会把 lib 改好的端口覆盖回去**(dsh-tray.ps1 第 27 行)——只改 lib 不改 param,托盘仍按默认端口探测 |
102
+ | 监控端口 | ① `~\.dsh\launcher\dsh-lib.ps1` 顶部 `$PORT`;② `dsh-tray.ps1` 顶部 `param([int]$Port = …)` | `3080` | **两处必须一致**:托盘启动时先点源 lib 再执行 `$PORT = $Port`,**param 默认值会把 lib 改好的端口覆盖回去**(dsh-tray.ps1 第 27 行)——只改 lib 不改 param,托盘仍按默认端口探测 |
99
103
  | dsh 路径 | `dsh-lib.ps1` 顶部 `$DSH_CMD / $NODE / $BIN` | 自动探测 | install 已改写,一般不用动 |
100
104
  | 数据目录 | `dsh-lib.ps1` `$WORKDIR` + `$env:DSH_HOME` | **不设置** | 不设 DSH_HOME 时 web 默认落 `~\.dsh`——自定义数据目录的机器(如 `D:\dsh`)必须在 lib 里补 `$env:DSH_HOME`,否则托盘/控制台启动的 web 指向空目录 |
101
105
  | 日志目录 | `dsh-lib.ps1` `$LOG_DIR`(`~\.dsh\logs`) | — | **目录须存在**:不存在时托盘 `Add-Content -ErrorAction SilentlyContinue` 静默吞日志,零日志不是没在记 |
@@ -107,7 +111,7 @@ npm rm -g dsh-windows-tray # 移除 npm 包本体
107
111
 
108
112
  | 项 | 包默认 | 笔记本实际(2026-08-27 实测) | 填充点 |
109
113
  | :--- | :--- | :--- | :--- |
110
- | Web 端口 | 3088 | **3080** | dsh-lib.ps1 `$PORT` + dsh-tray.ps1 param 默认值(两处) |
114
+ | Web 端口 | 3080 | **3080** | dsh-lib.ps1 `$PORT` + dsh-tray.ps1 param 默认值(两处) |
111
115
  | 数据目录 | 不设→`~\.dsh` | **D:\dsh** | dsh-lib.ps1 `$WORKDIR='D:\dsh'` + `$env:DSH_HOME='D:\dsh'` |
112
116
  | 日志目录 | `~\.dsh\logs`(可能不存在) | 已手动创建 | — |
113
117
  | 旧控制台 | — | `D:\dsh\launcher\dsh-control.ps1` 与托盘部署并存,端口同为 3080 | 无需合并 |
@@ -124,10 +128,11 @@ npm rm -g dsh-windows-tray # 移除 npm 包本体
124
128
  | 动作没反应 | 看 `~\.dsh\logs\dsh-tray-actions.log`(菜单动作)与 `dsh-tray.log`(状态变化)。 |
125
129
  | 重复实例 | 命名互斥锁单实例保护(被强杀遗留的锁自动接管),正常不会重复。 |
126
130
  | 检查更新失败 | 网络或 npm 不可用;托盘继续正常工作,稍后再试。 |
127
- | dsh 内核升级了,托盘要动吗 | 不用——托盘只探测 3088 端口与 TUI 进程命令行,与内核版本解耦。 |
131
+ | 开机后 dsh Web 没自动起 | 看 `~\.dsh\logs\dsh-windows-tray\dsh-boot.log`:末行 `boot ok (started)` = 正常;`boot FAILED` 后跟的错误即原因(自启是隐藏窗口,日志是唯一线索),常见是 `~\.dsh\launcher` 被移动/删除或 `dsh-boot.ps1` 被手工改名 |
132
+ | dsh 内核升级了,托盘要动吗 | 不用——托盘只探测 3080 端口与 TUI 进程命令行,与内核版本解耦。 |
128
133
 
129
134
  ## 八、已知边界
130
135
 
131
136
  - 通知弹窗受 Windows「通知设置/专注助手」控制,被系统屏蔽时图标与菜单不受影响。
132
137
  - 托盘进程是 `pwsh`,`taskkill /T` 杀进程树会连托盘一起结束(下次登录/快捷方式/控制台操作时恢复)。
133
- - 端口探测只区分 运行/停止:3088 被其他程序占用时会误判"运行中"。
138
+ - 端口探测只区分 运行/停止:3080 被其他程序占用时会误判"运行中"。
@@ -1,7 +1,9 @@
1
1
  # 系统托盘开发归档(v1.0.0 · 2026-08-27)
2
2
 
3
3
  > 托盘已发布为 npm 包 **`dsh-windows-tray`**,日常安装 / 使用 / 更新 / 卸载见 [system-tray-guide.md](system-tray-guide.md)。
4
- > 本文仅归档开发期的**架构决策与历史记录**(源码细节、文件清单不再维护);通用踩坑已归入 [powershell-pitfalls.md](powershell-pitfalls.md)。
4
+ > 本文仅归档开发期的**架构决策与历史记录**(源码细节、文件清单不再维护);通用踩坑已归入 [../../../docs/powershell-pitfalls.md](../../../docs/powershell-pitfalls.md)。
5
+ >
6
+ > **端口(2026-09-07 起)**:本包默认探测端口为 **3080**。
5
7
 
6
8
  ---
7
9
 
@@ -10,7 +12,7 @@
10
12
  | 决策 | 理由 |
11
13
  | :--- | :--- |
12
14
  | **独立常驻进程**(不嵌入 dsh) | 需求是"看到未启动状态"——进程内嵌图标会随 dsh 消失;旁观者进程才能表达 运行/停止 两态,且能重启已死的 dsh |
13
- | **端口探测**(每 2s TCP 连 127.0.0.1:3088,500ms 超时) | 不持有进程句柄;dsh 重启 PID 变化也不误判;对 dsh 实现零依赖 |
15
+ | **端口探测**(每 2s TCP 连 127.0.0.1:3080,500ms 超时) | 不持有进程句柄;dsh 重启 PID 变化也不误判;对 dsh 实现零依赖 |
14
16
  | **动作独立进程**(隐藏执行) | 菜单动作(含 taskkill、等待轮询)不阻塞托盘 UI 线程 |
15
17
  | **静态 .ico 图标资源** | 不依赖 dsh 运行时接口;缺失时 `New-FallbackIcon` 画纯色圆点兜底 |
16
18
  | **命名互斥锁单实例** | 替代 WMI 扫描,无竞态;被强杀遗留的锁自动接管(AbandonedMutexException) |
@@ -19,7 +21,7 @@
19
21
  进程模型:
20
22
 
21
23
  ```
22
- 托盘(pwsh) ──2s 轮询──► 端口 3088 (dsh web)
24
+ 托盘(pwsh) ──2s 轮询──► 端口 3080 (dsh web)
23
25
  │ └──────────────► node 进程命令行含 'dsh-tui' (TUI)
24
26
  ├── 菜单动作 ──► 独立隐藏进程 (带日志)
25
27
  └── 自更新 ──► npm install -g → 重部署 → 重启托盘
@@ -51,7 +53,7 @@ npm 上两个同思路项目(`dsh-tray`、`dsh-tray-launcher`,均一周内
51
53
 
52
54
  ## 5. 已知边界
53
55
 
54
- 1. 3088 被其他程序占用时托盘误判"运行中"(属端口问题,与托盘无关)
56
+ 1. 3080 被其他程序占用时托盘误判"运行中"(属端口问题,与托盘无关)
55
57
  2. 二态检测:只区分 运行/停止,无法区分"忙/卡死"(可扩展 HTTP 探测做三态)
56
58
  3. TUI 单实例是策略而非物理限制(见 §1 决策表)
57
59
  4. 单实例互斥锁在极端竞态下仍有极小双启动概率(影响可忽略)
package/install.ps1 CHANGED
@@ -1,4 +1,4 @@
1
- # install.ps1 —— DSH 系统托盘 一键部署(便携包内运行)
1
+ # install.ps1 —— DSH 系统托盘 一键部署(npm 包内运行;**唯一权威安装器**,无第二份副本)
2
2
  # 用法: pwsh -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 [-DshRoot <目录>] [-PwshPath <路径>] [-NodePath <路径>] [-Port <端口>] [-DshHome <数据目录>] [-Auto] [-NoTrayStart] [-NoShortcuts]
3
3
  # 流程: 探测本机路径 -> 探测端口/数据目录并确认 -> 安装到 ~\.dsh\launcher -> 回填全部读取点(含端口两处+DSH_HOME) -> 创建快捷方式 -> 启动托盘
4
4
  # v1.0.2: 端口/数据目录实测探测 + 展示确认 + 回填全部读取点 + 自动创建日志目录 (修: param 默认值覆盖 lib 端口 / DSH_HOME 不设 / 日志目录缺失静默丢日志)
@@ -91,7 +91,20 @@ if (-not $PwshPath -or -not (Test-Path $PwshPath)) { throw "未找到 pwsh (Powe
91
91
  # 版本校验: 托盘常驻进程依赖 PowerShell 7.x (5.1 只能降级运行)
92
92
  $pwshVer = & $PwshPath -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' 2>$null
93
93
  if ($pwshVer -notmatch '^7\.') { throw "pwsh 必须是 PowerShell 7.x (检测到: $pwshVer)。请安装 PowerShell 7 或用 -PwshPath 指定。" }
94
- Write-Host "[2/7] pwsh: $PwshPath (v$pwshVer)" -ForegroundColor Green
94
+ Write-Host "[2/7] pwsh: $PwshPath (v$pwshVer)" -ForegroundColor Green
95
+
96
+ # 内核兼容软校验(release-standards §5.5 / package.json dshCompatibility):
97
+ # 只提醒不阻断——本包是外部工具,内核超出实测范围时多数功能仍可用(探测/启停与版本解耦)
98
+ $tested = @('0.1.1-rc.2', '0.1.2-rc.1', '0.1.5-rc.1', '0.1.5-rc.2')
99
+ $corePkg = Join-Path $DshRoot 'node_modules\@deepseek-ai\dsh\package.json'
100
+ if (Test-Path $corePkg) {
101
+ $coreVer = (Get-Content $corePkg -Raw | ConvertFrom-Json).version
102
+ if ($tested -notcontains $coreVer) {
103
+ Write-Host "[warn] dsh 内核 $coreVer 不在本包实测清单($($tested -join ' / '))——功能大概率可用,异常请先对照 CHANGELOG 的 dshCompatibility 再报。" -ForegroundColor Yellow
104
+ } else {
105
+ Write-Host "[2/7] dsh 内核 $coreVer 在实测清单内" -ForegroundColor Green
106
+ }
107
+ }
95
108
 
96
109
  # 3) 探测 node
97
110
  if (-not $NodePath) {
@@ -115,7 +128,7 @@ if (-not $DshHome) {
115
128
  $probed = Find-DshHome
116
129
  if ($probed) { $DshHome = $probed }
117
130
  }
118
- if ($Port -eq 0) { $Port = 3088; Write-Host '警告: 未检测到运行中的 web 进程, 端口取包默认 3088 (如不符请用 -Port 指定)' -ForegroundColor Yellow }
131
+ if ($Port -eq 0) { $Port = 3080; Write-Host '警告: 未检测到运行中的 web 进程, 端口取包默认 3080 (如不符请用 -Port 指定)' -ForegroundColor Yellow }
119
132
  if (-not $DshHome) { $DshHome = Join-Path $env:USERPROFILE '.dsh'; Write-Host "警告: 未探测到数据目录 (无 settings.yaml), 取默认 $DshHome (如不符请用 -DshHome 指定)" -ForegroundColor Yellow }
120
133
  $Port = [int](Confirm-Value 'Web 端口' "$Port")
121
134
  $DshHome = Confirm-Value '数据目录' $DshHome
@@ -227,7 +240,21 @@ namespace DSH.Shortcut {
227
240
  New-Item -Path $nsKey -Force | Out-Null
228
241
  Set-ItemProperty -Path $nsKey -Name 'Enabled' -Value 1 -Type DWord
229
242
  Set-ItemProperty -Path $nsKey -Name 'ShowInActionCenter' -Value 1 -Type DWord
230
- Write-Host '快捷方式: 开机自启 + 桌面 + 开始菜单 已创建 (AUMID=DSH.WindowsTray, 通知已预授权)' -ForegroundColor Green
243
+ # harness (web) 开机自启 (2026-09-12): 与托盘同一战斗路径 —— dsh-boot.ps1 → dsh-lib Start-Dsh。
244
+ # 不走 dsh-menu.ps1 的原因: 它启动 node 时不重定向日志, dsh 0.1.2+ 的 launch token 落不到
245
+ # logs\dsh-windows-tray\web-menu.out.log, 托盘「打开 Web UI」只能回退裸 URL → 每次开机后见鉴权页。
246
+ # AUMID 与托盘同款: AUMID 由子进程继承, 开机时本脚本经 Ensure-DshTray 拉起的托盘通知身份不变。
247
+ $bootLnk = Join-Path ([Environment]::GetFolderPath('Startup')) 'DeepSeek Harness (dsh).lnk'
248
+ $boot = $ws.CreateShortcut($bootLnk)
249
+ $boot.TargetPath = $PwshPath
250
+ $boot.Arguments = '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "' + (Join-Path $dst 'dsh-boot.ps1') + '"'
251
+ $boot.WorkingDirectory = $dst
252
+ $boot.IconLocation = $icon
253
+ $boot.WindowStyle = 7
254
+ $boot.Description = 'DeepSeek Harness (dsh) - 开机静默启动 dsh Web'
255
+ $boot.Save()
256
+ [DSH.Shortcut.Helper]::SetAumid($bootLnk, 'DSH.WindowsTray')
257
+ Write-Host '快捷方式: 开机自启(托盘 + harness) + 桌面 + 开始菜单 已创建 (AUMID=DSH.WindowsTray, 通知已预授权)' -ForegroundColor Green
231
258
  }
232
259
 
233
260
  # 启动托盘
@@ -239,6 +266,6 @@ if (-not $NoTrayStart) {
239
266
  Write-Host ''
240
267
  Write-Host '部署完成! 常用命令:' -ForegroundColor Cyan
241
268
  Write-Host " 手动唤起托盘: 桌面「DSH 系统托盘」/ 控制台选项 5"
242
- Write-Host ' 取消自启: 删除启动文件夹里的「DSH 系统托盘.lnk」'
269
+ Write-Host ' 取消自启: 删除启动文件夹里的「DSH 系统托盘.lnk」与「DeepSeek Harness (dsh).lnk」'
243
270
  Write-Host ' 卸载: 删除快捷方式 + 删除 ~\.dsh\launcher'
244
271
  Write-Host " 验证: $logDir\dsh-tray.log 首行「托盘启动 (…端口 $Port)」" -ForegroundColor Cyan
@@ -0,0 +1,56 @@
1
+ # dsh-boot.ps1 -- DSH (DeepSeek Harness) logon autostart: bring up dsh web silently.
2
+ #
3
+ # Used by the Startup shortcut "DeepSeek Harness (dsh).lnk":
4
+ # pwsh.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File <this file>
5
+ #
6
+ # Why this file exists instead of calling dsh-menu.ps1 -Cmd start:
7
+ # dsh-menu.ps1 starts node WITHOUT redirecting stdout, so the dsh 0.1.2+ launch token
8
+ # never lands in logs\dsh-windows-tray\web-menu.out.log. The tray parses that file to
9
+ # open an ALREADY authorized Web UI tab; without it the tray falls back to the naked
10
+ # URL and the user gets the "authentication required" page after every boot.
11
+ # Routing through dsh-lib.ps1's Start-Dsh = the exact path the tray menu uses
12
+ # (hidden node, correct working dir, redirected logs, idempotent tray ensure).
13
+ #
14
+ # ASCII-ONLY ON PURPOSE: this file must stay parseable by Windows PowerShell 5.1 as well
15
+ # as pwsh 7. A no-BOM UTF-8 file with non-ASCII text is silently mis-parsed by 5.1
16
+ # (ParserError) -- that failure mode already cost one machine its autostart
17
+ # (see personal-docs dsh/docs/install-ledger.md, 2026-09-10 entry). Do not add non-ASCII here.
18
+ #
19
+ # Contract / boundaries:
20
+ # - exits 0 when web is up (started here or already running); exits 1 on any failure
21
+ # - every outcome is appended to dsh-boot.log (in the tray log dir once dsh-lib loads,
22
+ # else %TEMP%) -- hidden window means the log is the only diagnostic channel
23
+ # - idempotent: safe to run next to the tray's own autostart shortcut (dsh-lib reuses
24
+ # the existing listener; dsh-tray.ps1 is mutex-guarded)
25
+
26
+ $ErrorActionPreference = 'Stop'
27
+ $script:LogPath = Join-Path $env:TEMP 'dsh-boot.log'
28
+
29
+ function Write-BootLog([string]$Message) {
30
+ try {
31
+ Add-Content -LiteralPath $script:LogPath -Value ('[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Message) -ErrorAction SilentlyContinue
32
+ } catch { }
33
+ }
34
+
35
+ try {
36
+ $lib = Join-Path $PSScriptRoot 'dsh-lib.ps1'
37
+ if (-not (Test-Path -LiteralPath $lib)) { throw "dsh-lib.ps1 not found next to dsh-boot.ps1: $lib" }
38
+ . $lib
39
+ if ($LOG_DIR) { $script:LogPath = Join-Path $LOG_DIR 'dsh-boot.log' }
40
+
41
+ Write-BootLog ('boot begin (pid {0})' -f $PID)
42
+ $before = @(Get-WebPid)
43
+ Start-Dsh -OpenBrowser:$false
44
+ $after = @(Get-WebPid)
45
+ if ($after.Count -gt 0) {
46
+ $how = 'started'
47
+ if ($before.Count -gt 0) { $how = 'already running' }
48
+ Write-BootLog ('boot ok ({0}), web pid: {1}' -f $how, ($after -join ','))
49
+ } else {
50
+ Write-BootLog ('boot FAILED: port {0} not listening after start (see web-menu.err.log)' -f $PORT)
51
+ exit 1
52
+ }
53
+ } catch {
54
+ Write-BootLog ('boot FAILED: ' + $_.Exception.Message)
55
+ exit 1
56
+ }
@@ -13,7 +13,7 @@ while ($true) {
13
13
  Write-Host ' ┌──────────────────────────────┐'
14
14
  Write-Host ' │ DSH 控制台 │'
15
15
  Write-Host ' ├──────────────────────────────┤'
16
- Write-Host ' │ 1) 启动 / 打开 Web (3088) │'
16
+ Write-Host ' │ 1) 启动 / 打开 Web (3080) │'
17
17
  Write-Host ' │ 2) 重启 Web │'
18
18
  Write-Host ' │ 3) 启动 TUI(新终端窗口) │'
19
19
  Write-Host ' │ 4) 查看状态 │'
@@ -7,12 +7,12 @@ $DSH_CMD = 'D:\Program Files (x86)\dsh\dsh.cmd'
7
7
  $NODE = 'C:\Program Files\nodejs\node.exe'
8
8
  $BIN = 'D:\Program Files (x86)\dsh\node_modules\@deepseek-ai\dsh\lib\bin.js'
9
9
  $WORKDIR = 'D:\Program Files (x86)\dsh'
10
- $PORT = 3088
10
+ $PORT = 3080
11
11
  $URL = "http://127.0.0.1:$PORT"
12
12
  $LOG_DIR = Join-Path (Join-Path $env:USERPROFILE '.dsh\logs') 'dsh-windows-tray' # 本插件专属日志目录 (logs\<插件名>\, 边界规则见 release-standards §3.1)
13
13
  $WT = "$env:LOCALAPPDATA\Microsoft\WindowsApps\wt.exe"
14
14
  $PWSH = 'D:\Program Files (x86)\PowerShell-7.6.4\pwsh.exe'
15
- $TRAY_VERSION = '1.0.11' # 托盘版本 (与 npm 包版本同步, 用于日志/排查)
15
+ $TRAY_VERSION = '1.0.13' # 托盘版本 (与 npm 包版本同步, 用于日志/排查)
16
16
  $TRAY_PKG = 'dsh-windows-tray' # npm 包名 (托盘自更新)
17
17
  $TRAY_SCRIPT = Join-Path $PSScriptRoot 'dsh-tray.ps1'
18
18
 
@@ -75,6 +75,15 @@ function Initialize-LogDir {
75
75
  }
76
76
 
77
77
  function Get-WebPid {
78
+ # 优化(1.0.12): netstat 解析优先 (原 Get-NetTCPConnection 在本机 727 进程下 avg 1.3s,
79
+ # 直接拖慢右键菜单转态路径); 解析失败回退 CIM 查询
80
+ try {
81
+ $pids = @()
82
+ foreach ($row in (& netstat -ano -p tcp 2>$null)) {
83
+ if ($row -match ":$PORT\s+\S+\s+LISTENING\s+(\d+)\s*$") { $pids += $Matches[1] }
84
+ }
85
+ if ($pids.Count -gt 0) { return @($pids | Sort-Object -Unique) }
86
+ } catch { }
78
87
  @((Get-NetTCPConnection -LocalPort $PORT -State Listen -ErrorAction SilentlyContinue).OwningProcess | Select-Object -Unique)
79
88
  }
80
89
 
@@ -160,10 +169,12 @@ function Start-Tui {
160
169
 
161
170
  # TUI 进程树: 外壳 pwsh(-NoExit) → cmd(dsh.cmd) → node(bin.js), 三层命令行都含 dsh-tui
162
171
  # 按进程名白名单过滤, 防止误杀命令行里恰好含 dsh-tui 的其他程序 (如 WindowsTerminal.exe 本体)
172
+ # 优化(1.0.12): WQL 先按 Name 窄集 (原全量枚举会拉全部进程的 CommandLine);
173
+ # ⚠️ 本机实测任何 Win32_Process 查询固定 ~450-570ms (WinRM 不可用, DCOM/持久会话均无效)——
174
+ # **调用方 (托盘状态刷新) 必须在 ThreadJob 里异步跑, 禁止在 UI/菜单路径同步调用** (见 dsh-tray.ps1)
163
175
  function Get-TuiTree {
164
- Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
165
- $_.ProcessId -ne $PID -and $_.CommandLine -and
166
- $_.CommandLine -match 'dsh-tui' -and $_.Name -match '^(node|cmd|pwsh|powershell)\.exe$'
176
+ Get-CimInstance Win32_Process -Filter "Name='node.exe' OR Name='cmd.exe' OR Name='pwsh.exe' OR Name='powershell.exe'" -ErrorAction SilentlyContinue | Where-Object {
177
+ $_.ProcessId -ne $PID -and $_.CommandLine -and $_.CommandLine -match 'dsh-tui'
167
178
  }
168
179
  }
169
180
 
@@ -342,8 +353,7 @@ function Show-Status {
342
353
  # ---- 系统托盘 ----
343
354
 
344
355
  function Get-TrayProcesses {
345
- Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
346
- ($_.Name -eq 'pwsh.exe' -or $_.Name -eq 'powershell.exe') -and
356
+ Get-CimInstance Win32_Process -Filter "Name='pwsh.exe' OR Name='powershell.exe'" -ErrorAction SilentlyContinue | Where-Object {
347
357
  $_.CommandLine -and $_.CommandLine -match 'dsh-tray\.ps1' -and
348
358
  $_.ProcessId -ne $PID
349
359
  }
@@ -12,7 +12,7 @@
12
12
  # - TUI 状态独立检测: 菜单项按 TUI 是否运行自动禁用/启用
13
13
  # - 浅色圆角菜单(借鉴社区 dsh-tray 方案) + npm 自更新(借鉴 dsh-tray-launcher 方案)
14
14
  param(
15
- [int]$Port = 3088,
15
+ [int]$Port = 3080,
16
16
  [switch]$NoBalloon
17
17
  )
18
18
  $ErrorActionPreference = 'Stop'
@@ -354,31 +354,66 @@ $script:prevState = $null # $null=初始; $true=运行; $false=停止
354
354
  $script:prevTui = $null # TUI 状态 ($null=初始; $true=运行; $false=停止)
355
355
  $script:firstRun = $true # 首次刷新只设图标, 不弹变化通知
356
356
  $script:lastTuiCheck = 0 # TUI 检测节流 (常规 20s 一次, 打开菜单时强制)
357
+ $script:tuiJob = $null # 运行中的 TUI 检测任务 (1.0.12 异步化)
358
+ $script:tuiRefreshQueued = $false # 任务在跑期间又到期/强制刷新 → 完成后立即再刷
359
+
360
+ # ThreadJob 异步化 (1.0.12): 本机实测任何 Win32_Process WMI 查询固定 ~450-570ms
361
+ # (WinRM 不可用, WSMAN/DCOM 持久会话均无效) —— 若在菜单 Opening / 状态 tick 同步跑,
362
+ # 右键菜单会被 WinForms 卡住 (Opening 未返回不渲染)。TUI 检测改为后台任务,
363
+ # 结果由 2s 定时器在 UI 线程回收应用; ThreadJob 模块缺失时降级同步 (旧行为)。
364
+ Import-Module Microsoft.PowerShell.ThreadJob -ErrorAction SilentlyContinue
365
+ $script:threadJobOk = $null -ne (Get-Command Start-ThreadJob -ErrorAction SilentlyContinue)
366
+
367
+ function Apply-TuiStatus {
368
+ param([object[]]$tuiProcs)
369
+ $tuiRunning = $tuiProcs.Count -gt 0
370
+ if ($tuiRunning) {
371
+ $itemTuiStatus.Text = "TUI 状态: 运行中 (PID $(($tuiProcs.ProcessId) -join ','))"
372
+ } else {
373
+ $itemTuiStatus.Text = 'TUI 状态: 未运行'
374
+ }
375
+ if ($tuiRunning -ne $script:prevTui) {
376
+ $script:prevTui = $tuiRunning
377
+ $itemTuiStart.Enabled = -not $tuiRunning
378
+ $itemTuiStop.Enabled = $tuiRunning
379
+ if ($tuiRunning) {
380
+ Log "TUI: 运行中 (PID $(($tuiProcs.ProcessId) -join ','))"
381
+ } else {
382
+ Log 'TUI: 未运行'
383
+ }
384
+ }
385
+ }
357
386
 
358
387
  function Update-State {
359
388
  param([switch]$ForceTui)
360
389
  try {
361
- # TUI 状态独立检测 (不随 Web 状态提前返回); WMI 查询节流, 减少周期开销
362
- if ($ForceTui -or ([Environment]::TickCount - $script:lastTuiCheck) -gt 20000) {
363
- $script:lastTuiCheck = [Environment]::TickCount
364
- $tuiProcs = @(Get-TuiProcesses)
365
- $tuiRunning = $tuiProcs.Count -gt 0
366
- if ($tuiRunning) {
367
- $itemTuiStatus.Text = "TUI 状态: 运行中 (PID $(($tuiProcs.ProcessId) -join ','))"
368
- } else {
369
- $itemTuiStatus.Text = 'TUI 状态: 未运行'
390
+ # --- TUI 检测: 异步, 绝不阻塞 UI —— 先回收已完成任务, 再按需排程 ---
391
+ if ($script:threadJobOk) {
392
+ if ($script:tuiJob -and $script:tuiJob.State -ne 'Running') {
393
+ $tuiProcs = @(Receive-Job -Job $script:tuiJob -ErrorAction SilentlyContinue)
394
+ Remove-Job $script:tuiJob -Force -ErrorAction SilentlyContinue
395
+ $script:tuiJob = $null
396
+ $script:lastTuiCheck = [Environment]::TickCount
397
+ Apply-TuiStatus $tuiProcs
398
+ if ($script:tuiRefreshQueued) { $script:tuiRefreshQueued = $false; $script:lastTuiCheck = 0 }
370
399
  }
371
- if ($tuiRunning -ne $script:prevTui) {
372
- $script:prevTui = $tuiRunning
373
- $itemTuiStart.Enabled = -not $tuiRunning
374
- $itemTuiStop.Enabled = $tuiRunning
375
- if ($tuiRunning) {
376
- Log "TUI: 运行中 (PID $(($tuiProcs.ProcessId) -join ','))"
400
+ if ($ForceTui -or (([Environment]::TickCount - $script:lastTuiCheck) -gt 20000)) {
401
+ if (-not $script:tuiJob) {
402
+ $libPath = Join-Path $PSScriptRoot 'dsh-lib.ps1'
403
+ $script:tuiJob = Start-ThreadJob -ScriptBlock {
404
+ param($lib)
405
+ . $lib
406
+ @(Get-TuiProcesses)
407
+ } -ArgumentList $libPath
377
408
  } else {
378
- Log 'TUI: 未运行'
409
+ $script:tuiRefreshQueued = $true
379
410
  }
380
411
  }
412
+ } elseif ($ForceTui -or (([Environment]::TickCount - $script:lastTuiCheck) -gt 20000)) {
413
+ $script:lastTuiCheck = [Environment]::TickCount
414
+ Apply-TuiStatus (@(Get-TuiProcesses))
381
415
  }
416
+ # --- Web 状态: Test-Port ~11ms + 转态才查 PID (netstat ~42ms), 保持在 UI 线程 ---
382
417
  $running = Test-Port $PORT
383
418
  if ($running -eq $script:prevState -and -not $script:firstRun) { return } # 无变化
384
419
  $script:prevState = $running
@@ -426,5 +461,6 @@ $timer.Dispose()
426
461
  $menu.Dispose()
427
462
  $iconRun.Dispose(); $iconStop.Dispose()
428
463
  try { $mutex.ReleaseMutex() } catch { }
464
+ try { if ($script:tuiJob) { Remove-Job $script:tuiJob -Force -ErrorAction SilentlyContinue; $script:tuiJob = $null } } catch { }
429
465
  Log '托盘已退出'
430
466
 
package/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "dsh-windows-tray",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "DSH (DeepSeek Harness) Windows 系统托盘: 蓝/灰鲸鱼状态图标 + Web/TUI 一键启停控制。纯 PowerShell + WinForms, 零依赖。",
5
5
  "license": "MIT",
6
6
  "author": "jankin_lv",
7
- "keywords": [
7
+ "dshCompatibility": {
8
+ "testedVersions": ["0.1.1-rc.2", "0.1.2-rc.1", "0.1.5-rc.1", "0.1.5-rc.2"],
9
+ "range": ">=0.1.1-rc.2 <0.2.0",
10
+ "scope": "external-tool(本包不 import 任何 @deepseek-ai 代码:只拉起 dsh 的 bin.js web、探测 3080 端口、按进程命令行识别 TUI;故不声明 @deepseek-ai peerDependencies——那只会给用户刷 unmet peer 警告而买不到任何真实校验)",
11
+ "note": "dsh ≥0.1.2-rc.1 起 web 用一次性 launch token;托盘 ≥1.0.11 才会解析它(本包 1.0.13)"
12
+ },"keywords": [
8
13
  "dsh",
9
14
  "deepseek",
10
15
  "harness",