dsh-windows-tray 1.0.2 → 1.0.4
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/bin/dsh-tray.js +0 -1
- package/build/build-tray-icons.ps1 +98 -98
- package/docs/system-tray-guide.md +133 -133
- package/install.ps1 +228 -184
- package/launcher/dsh-actions.ps1 +50 -50
- package/launcher/dsh-control.ps1 +40 -40
- package/launcher/dsh-lib.ps1 +50 -11
- package/launcher/dsh-menu.ps1 +93 -93
- package/launcher/dsh-tray.bat +10 -10
- package/launcher/dsh-tray.ps1 +429 -418
- package/launcher/dsh-update.ps1 +115 -115
- package/package.json +39 -36
- package/launcher/bh-menu.bat +0 -4
- package/launcher/bh-menu.ps1 +0 -56
- package/launcher/dsh-all-menu.bat +0 -4
- package/launcher/dsh-all-menu.ps1 +0 -106
package/launcher/dsh-lib.ps1
CHANGED
|
@@ -12,7 +12,7 @@ $URL = "http://127.0.0.1:$PORT"
|
|
|
12
12
|
$LOG_DIR = "$env:USERPROFILE\.dsh\logs"
|
|
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.
|
|
15
|
+
$TRAY_VERSION = '1.0.4' # 托盘版本 (与 npm 包版本同步, 用于日志/排查)
|
|
16
16
|
$TRAY_PKG = 'dsh-windows-tray' # npm 包名 (托盘自更新)
|
|
17
17
|
$TRAY_SCRIPT = Join-Path $PSScriptRoot 'dsh-tray.ps1'
|
|
18
18
|
|
|
@@ -89,6 +89,13 @@ function Start-Tui {
|
|
|
89
89
|
Write-Host "[tui] TUI 已在运行 (PID $($existing.ProcessId -join ',')), 不重复启动" -ForegroundColor Yellow
|
|
90
90
|
return
|
|
91
91
|
}
|
|
92
|
+
# 清理上次强杀残留的外壳 (node 已死但 pwsh -NoExit 窗口还在)
|
|
93
|
+
$stale = @(Get-TuiTree)
|
|
94
|
+
if ($stale.Count -gt 0) {
|
|
95
|
+
Write-Host '[tui] 清理上次残留的 TUI 外壳进程' -ForegroundColor DarkGray
|
|
96
|
+
foreach ($p in $stale) { & taskkill /PID $p.ProcessId /T /F 2>$null | Out-Null }
|
|
97
|
+
Start-Sleep -Milliseconds 500
|
|
98
|
+
}
|
|
92
99
|
$cmdLine = "& '$DSH_CMD' --profile dsh-tui"
|
|
93
100
|
if (Test-Path $WT) {
|
|
94
101
|
$args = "new-tab --title `"DSH TUI`" pwsh -NoExit -Command `"$cmdLine`""
|
|
@@ -103,21 +110,53 @@ function Start-Tui {
|
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
112
|
|
|
113
|
+
# TUI 进程树: 外壳 pwsh(-NoExit) → cmd(dsh.cmd) → node(bin.js), 三层命令行都含 dsh-tui
|
|
114
|
+
# 按进程名白名单过滤, 防止误杀命令行里恰好含 dsh-tui 的其他程序 (如 WindowsTerminal.exe 本体)
|
|
115
|
+
function Get-TuiTree {
|
|
116
|
+
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
|
|
117
|
+
$_.ProcessId -ne $PID -and $_.CommandLine -and
|
|
118
|
+
$_.CommandLine -match 'dsh-tui' -and $_.Name -match '^(node|cmd|pwsh|powershell)\.exe$'
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# 真正的 TUI 主进程 (node) —— 供状态显示 / 防重复启动
|
|
106
123
|
function Get-TuiProcesses {
|
|
107
|
-
Get-
|
|
108
|
-
Where-Object { $_.CommandLine -match 'dsh-tui' }
|
|
124
|
+
@(Get-TuiTree | Where-Object { $_.Name -eq 'node.exe' })
|
|
109
125
|
}
|
|
110
126
|
|
|
111
127
|
function Stop-Tui {
|
|
112
|
-
$
|
|
113
|
-
if ($
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
128
|
+
$tree = @(Get-TuiTree)
|
|
129
|
+
if ($tree.Count -eq 0) { Write-Host '[tui] TUI: 未在运行'; return }
|
|
130
|
+
# 从树根整树终止 (根 = 父进程不在树中的进程, 即 pwsh -NoExit 外壳)。
|
|
131
|
+
# 只杀 node 会导致外壳与窗口残留, 终端停在 raw mode 刷乱码且关不掉
|
|
132
|
+
$ids = @($tree | ForEach-Object { $_.ProcessId })
|
|
133
|
+
$roots = @($tree | Where-Object { $ids -notcontains $_.ParentProcessId })
|
|
134
|
+
foreach ($r in $roots) {
|
|
135
|
+
Write-Host "[tui] 停止 TUI 进程树 (根 PID $($r.ProcessId) $($r.Name))"
|
|
136
|
+
& taskkill /PID $r.ProcessId /T /F 2>$null | Out-Null
|
|
137
|
+
}
|
|
138
|
+
for ($i = 0; $i -lt 5; $i++) {
|
|
139
|
+
Start-Sleep -Milliseconds 600
|
|
140
|
+
if (@(Get-TuiTree).Count -eq 0) { break }
|
|
141
|
+
}
|
|
142
|
+
if (@(Get-TuiTree).Count -gt 0) { Write-Host '[tui] TUI 进程未完全退出,请稍后重试' -ForegroundColor Red }
|
|
143
|
+
else {
|
|
144
|
+
# 收尾: 关闭专为 TUI 开的 Windows Terminal 窗口。
|
|
145
|
+
# 强杀外壳退出码非 0, WT 默认 closeOnExit=graceful 不自动关标签, 会留死面板;
|
|
146
|
+
# 仅当该 WT 已无任何存活子进程时才关 — 用户在窗口里另开的标签仍在, 此时不动
|
|
147
|
+
$wts = @(Get-CimInstance Win32_Process -Filter "Name='WindowsTerminal.exe'" -ErrorAction SilentlyContinue |
|
|
148
|
+
Where-Object { $_.CommandLine -and $_.CommandLine -match 'dsh-tui' })
|
|
149
|
+
foreach ($w in $wts) {
|
|
150
|
+
$kids = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $_.ParentProcessId -eq $w.ProcessId })
|
|
151
|
+
if ($kids.Count -eq 0) {
|
|
152
|
+
Write-Host "[tui] 关闭 TUI 终端窗口 (PID $($w.ProcessId))"
|
|
153
|
+
& taskkill /PID $w.ProcessId /F 2>$null | Out-Null
|
|
154
|
+
} else {
|
|
155
|
+
Write-Host "[tui] 终端窗口 (PID $($w.ProcessId)) 还有其他标签页, 保留" -ForegroundColor DarkGray
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
Write-Host '[tui] TUI 已停止' -ForegroundColor Yellow
|
|
117
159
|
}
|
|
118
|
-
Start-Sleep -Seconds 1
|
|
119
|
-
if (@(Get-TuiProcesses).Count -gt 0) { Write-Host '[tui] TUI 进程未完全退出,请稍后重试' -ForegroundColor Red }
|
|
120
|
-
else { Write-Host '[tui] TUI 已停止' -ForegroundColor Yellow }
|
|
121
160
|
}
|
|
122
161
|
|
|
123
162
|
function Show-Status {
|
package/launcher/dsh-menu.ps1
CHANGED
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
# dsh (DeepSeek Harness) 启动器 —— 公司机部署 2026-08-14
|
|
2
|
-
# 用法:
|
|
3
|
-
# 交互菜单: 双击 dsh-menu.bat
|
|
4
|
-
# 非交互: dsh-menu.ps1 -Cmd start|stop|status
|
|
5
|
-
# 坑: 不要在本脚本内对 Start-Process 用 -RedirectStandardOutput
|
|
6
|
-
# (父进程 stdout 被重定向时挂起, 见 LEARNED #53)
|
|
7
|
-
param(
|
|
8
|
-
[string]$Cmd
|
|
9
|
-
)
|
|
10
|
-
$ErrorActionPreference = 'Stop'
|
|
11
|
-
|
|
12
|
-
$DSH_CMD = 'D:\Program Files (x86)\dsh\dsh.cmd'
|
|
13
|
-
$PORT = 3080
|
|
14
|
-
$URL = 'http://127.0.0.1:3080'
|
|
15
|
-
|
|
16
|
-
function Test-Port([int]$port) {
|
|
17
|
-
$client = New-Object System.Net.Sockets.TcpClient
|
|
18
|
-
try {
|
|
19
|
-
$async = $client.BeginConnect('127.0.0.1', $port, $null, $null)
|
|
20
|
-
if ($async.AsyncWaitHandle.WaitOne(500) -and $client.Connected) { return $true }
|
|
21
|
-
return $false
|
|
22
|
-
} catch { return $false }
|
|
23
|
-
finally { $client.Close() }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function Start-Dsh {
|
|
27
|
-
if (Test-Port $PORT) {
|
|
28
|
-
Write-Output "[dsh] 端口 $PORT 已监听, 服务已在运行, 不重复启动"
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
Start-Process -FilePath $DSH_CMD -ArgumentList 'web' -WindowStyle Hidden
|
|
32
|
-
Start-Sleep -Seconds 6
|
|
33
|
-
if (Test-Port $PORT) {
|
|
34
|
-
Write-Output "[dsh] 启动成功: $URL"
|
|
35
|
-
} else {
|
|
36
|
-
Start-Sleep -Seconds 5
|
|
37
|
-
if (Test-Port $PORT) {
|
|
38
|
-
Write-Output "[dsh] 启动成功: $URL"
|
|
39
|
-
} else {
|
|
40
|
-
Write-Output "[dsh] 启动失败: 端口 $PORT 未监听, 请手工运行 dsh web 查看报错"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function Stop-Dsh {
|
|
46
|
-
$pids = @((Get-NetTCPConnection -LocalPort $PORT -State Listen -ErrorAction SilentlyContinue).OwningProcess | Select-Object -Unique)
|
|
47
|
-
if ($pids.Count -eq 0) {
|
|
48
|
-
Write-Output '[dsh] 服务未在运行'
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
foreach ($p in $pids) {
|
|
52
|
-
Write-Output "[dsh] 停止进程 $p"
|
|
53
|
-
& taskkill /PID $p /T /F 2>$null | Out-Null
|
|
54
|
-
}
|
|
55
|
-
Start-Sleep -Seconds 2
|
|
56
|
-
if (Test-Port $PORT) { Write-Output '[dsh] 停止失败: 端口仍在监听' }
|
|
57
|
-
else { Write-Output '[dsh] 已停止' }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function Show-Status {
|
|
61
|
-
if (Test-Port $PORT) { Write-Output "[dsh] 运行中: $URL" }
|
|
62
|
-
else { Write-Output '[dsh] 未运行' }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
# ---------- 非交互模式 ----------
|
|
66
|
-
if ($Cmd) {
|
|
67
|
-
switch ($Cmd.ToLower()) {
|
|
68
|
-
'start' { Start-Dsh }
|
|
69
|
-
'stop' { Stop-Dsh }
|
|
70
|
-
'status' { Show-Status }
|
|
71
|
-
default { Write-Output '用法: dsh-menu.ps1 -Cmd start|stop|status' }
|
|
72
|
-
}
|
|
73
|
-
exit
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
# ---------- 交互菜单 ----------
|
|
77
|
-
while ($true) {
|
|
78
|
-
Write-Output ''
|
|
79
|
-
Write-Output '======== dsh 启动器 ========'
|
|
80
|
-
Show-Status
|
|
81
|
-
Write-Output '[1] 启动并打开浏览器'
|
|
82
|
-
Write-Output '[2] 停止'
|
|
83
|
-
Write-Output '[3] 状态'
|
|
84
|
-
Write-Output '[0] 退出'
|
|
85
|
-
$choice = Read-Host '请选择'
|
|
86
|
-
switch ($choice) {
|
|
87
|
-
'1' { Start-Dsh; Start-Process $URL; }
|
|
88
|
-
'2' { Stop-Dsh }
|
|
89
|
-
'3' { Show-Status }
|
|
90
|
-
'0' { exit }
|
|
91
|
-
default { Write-Output '无效选择' }
|
|
92
|
-
}
|
|
93
|
-
}
|
|
1
|
+
# dsh (DeepSeek Harness) 启动器 —— 公司机部署 2026-08-14
|
|
2
|
+
# 用法:
|
|
3
|
+
# 交互菜单: 双击 dsh-menu.bat
|
|
4
|
+
# 非交互: dsh-menu.ps1 -Cmd start|stop|status
|
|
5
|
+
# 坑: 不要在本脚本内对 Start-Process 用 -RedirectStandardOutput
|
|
6
|
+
# (父进程 stdout 被重定向时挂起, 见 LEARNED #53)
|
|
7
|
+
param(
|
|
8
|
+
[string]$Cmd
|
|
9
|
+
)
|
|
10
|
+
$ErrorActionPreference = 'Stop'
|
|
11
|
+
|
|
12
|
+
$DSH_CMD = 'D:\Program Files (x86)\dsh\dsh.cmd'
|
|
13
|
+
$PORT = 3080
|
|
14
|
+
$URL = 'http://127.0.0.1:3080'
|
|
15
|
+
|
|
16
|
+
function Test-Port([int]$port) {
|
|
17
|
+
$client = New-Object System.Net.Sockets.TcpClient
|
|
18
|
+
try {
|
|
19
|
+
$async = $client.BeginConnect('127.0.0.1', $port, $null, $null)
|
|
20
|
+
if ($async.AsyncWaitHandle.WaitOne(500) -and $client.Connected) { return $true }
|
|
21
|
+
return $false
|
|
22
|
+
} catch { return $false }
|
|
23
|
+
finally { $client.Close() }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function Start-Dsh {
|
|
27
|
+
if (Test-Port $PORT) {
|
|
28
|
+
Write-Output "[dsh] 端口 $PORT 已监听, 服务已在运行, 不重复启动"
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
Start-Process -FilePath $DSH_CMD -ArgumentList 'web' -WindowStyle Hidden
|
|
32
|
+
Start-Sleep -Seconds 6
|
|
33
|
+
if (Test-Port $PORT) {
|
|
34
|
+
Write-Output "[dsh] 启动成功: $URL"
|
|
35
|
+
} else {
|
|
36
|
+
Start-Sleep -Seconds 5
|
|
37
|
+
if (Test-Port $PORT) {
|
|
38
|
+
Write-Output "[dsh] 启动成功: $URL"
|
|
39
|
+
} else {
|
|
40
|
+
Write-Output "[dsh] 启动失败: 端口 $PORT 未监听, 请手工运行 dsh web 查看报错"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Stop-Dsh {
|
|
46
|
+
$pids = @((Get-NetTCPConnection -LocalPort $PORT -State Listen -ErrorAction SilentlyContinue).OwningProcess | Select-Object -Unique)
|
|
47
|
+
if ($pids.Count -eq 0) {
|
|
48
|
+
Write-Output '[dsh] 服务未在运行'
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
foreach ($p in $pids) {
|
|
52
|
+
Write-Output "[dsh] 停止进程 $p"
|
|
53
|
+
& taskkill /PID $p /T /F 2>$null | Out-Null
|
|
54
|
+
}
|
|
55
|
+
Start-Sleep -Seconds 2
|
|
56
|
+
if (Test-Port $PORT) { Write-Output '[dsh] 停止失败: 端口仍在监听' }
|
|
57
|
+
else { Write-Output '[dsh] 已停止' }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function Show-Status {
|
|
61
|
+
if (Test-Port $PORT) { Write-Output "[dsh] 运行中: $URL" }
|
|
62
|
+
else { Write-Output '[dsh] 未运行' }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# ---------- 非交互模式 ----------
|
|
66
|
+
if ($Cmd) {
|
|
67
|
+
switch ($Cmd.ToLower()) {
|
|
68
|
+
'start' { Start-Dsh }
|
|
69
|
+
'stop' { Stop-Dsh }
|
|
70
|
+
'status' { Show-Status }
|
|
71
|
+
default { Write-Output '用法: dsh-menu.ps1 -Cmd start|stop|status' }
|
|
72
|
+
}
|
|
73
|
+
exit
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# ---------- 交互菜单 ----------
|
|
77
|
+
while ($true) {
|
|
78
|
+
Write-Output ''
|
|
79
|
+
Write-Output '======== dsh 启动器 ========'
|
|
80
|
+
Show-Status
|
|
81
|
+
Write-Output '[1] 启动并打开浏览器'
|
|
82
|
+
Write-Output '[2] 停止'
|
|
83
|
+
Write-Output '[3] 状态'
|
|
84
|
+
Write-Output '[0] 退出'
|
|
85
|
+
$choice = Read-Host '请选择'
|
|
86
|
+
switch ($choice) {
|
|
87
|
+
'1' { Start-Dsh; Start-Process $URL; }
|
|
88
|
+
'2' { Stop-Dsh }
|
|
89
|
+
'3' { Show-Status }
|
|
90
|
+
'0' { exit }
|
|
91
|
+
default { Write-Output '无效选择' }
|
|
92
|
+
}
|
|
93
|
+
}
|
package/launcher/dsh-tray.bat
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
chcp 65001 >nul
|
|
3
|
-
rem DSH 系统托盘 - double click to start (blue whale = running, gray = stopped)
|
|
4
|
-
rem prefer pwsh (PowerShell 7), fallback to powershell 5.1
|
|
5
|
-
where pwsh >nul 2>nul
|
|
6
|
-
if %errorlevel%==0 (
|
|
7
|
-
start "" pwsh -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0dsh-tray.ps1"
|
|
8
|
-
) else (
|
|
9
|
-
start "" powershell -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0dsh-tray.ps1"
|
|
10
|
-
)
|
|
1
|
+
@echo off
|
|
2
|
+
chcp 65001 >nul
|
|
3
|
+
rem DSH 系统托盘 - double click to start (blue whale = running, gray = stopped)
|
|
4
|
+
rem prefer pwsh (PowerShell 7), fallback to powershell 5.1
|
|
5
|
+
where pwsh >nul 2>nul
|
|
6
|
+
if %errorlevel%==0 (
|
|
7
|
+
start "" pwsh -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0dsh-tray.ps1"
|
|
8
|
+
) else (
|
|
9
|
+
start "" powershell -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0dsh-tray.ps1"
|
|
10
|
+
)
|