dsh-windows-tray 1.0.0

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.
@@ -0,0 +1,4 @@
1
+ @echo off
2
+ chcp 65001 > nul
3
+ powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0bh-menu.ps1"
4
+ pause
@@ -0,0 +1,56 @@
1
+ # dsh 黑洞模拟器启动器 —— 网页版交互黑洞(oseiskar/black-hole)
2
+ # 用法: 双击 bh-menu.bat 或 powershell -File bh-menu.ps1 -Cmd start|stop|status
3
+ param([string]$Cmd)
4
+ $ErrorActionPreference = 'Stop'
5
+ chcp 65001 > $null
6
+
7
+ $PORT = 8137
8
+ $URL = 'http://127.0.0.1:8137'
9
+ $DIR = "$env:USERPROFILE\.dsh\gen-wallpaper\bh-shader"
10
+
11
+ function Test-Port([int]$port) {
12
+ $c = New-Object System.Net.Sockets.TcpClient
13
+ try { if ($c.BeginConnect('127.0.0.1', $port, $null, $null).AsyncWaitHandle.WaitOne(500) -and $c.Connected) { return $true } } catch {}
14
+ finally { $c.Close() }
15
+ return $false
16
+ }
17
+
18
+ function Start-BH {
19
+ if (Test-Port $PORT) { Write-Output "[bh] 服务已在运行: $URL"; Start-Process $URL; return }
20
+ Start-Process node -ArgumentList 'serve.mjs' -WorkingDirectory $DIR -WindowStyle Hidden
21
+ Start-Sleep -Seconds 3
22
+ if (Test-Port $PORT) { Write-Output "[bh] 启动成功: $URL"; Start-Process $URL }
23
+ else { Write-Output "[bh] 启动失败, 请手工运行: cd $DIR; node serve.mjs" }
24
+ }
25
+
26
+ function Stop-BH {
27
+ $pids = @((Get-NetTCPConnection -LocalPort $PORT -State Listen -ErrorAction SilentlyContinue).OwningProcess | Select-Object -Unique)
28
+ if ($pids.Count -eq 0) { Write-Output '[bh] 服务未运行'; return }
29
+ foreach ($p in $pids) { Write-Output "[bh] 停止进程 $p"; & taskkill /PID $p /F 2>$null | Out-Null }
30
+ Write-Output '[bh] 已停止'
31
+ }
32
+
33
+ if ($Cmd) {
34
+ switch ($Cmd.ToLower()) {
35
+ 'start' { Start-BH }
36
+ 'stop' { Stop-BH }
37
+ 'status'{ if (Test-Port $PORT) { Write-Output "[bh] 运行中: $URL" } else { Write-Output '[bh] 未运行' } }
38
+ default { Write-Output '用法: bh-menu.ps1 -Cmd start|stop|status' }
39
+ }
40
+ exit
41
+ }
42
+ while ($true) {
43
+ Write-Output ''
44
+ Write-Output '======== 黑洞模拟器 ========'
45
+ if (Test-Port $PORT) { Write-Output '状态: 运行中' } else { Write-Output '状态: 未运行' }
46
+ Write-Output '[1] 启动并打开浏览器'
47
+ Write-Output '[2] 停止'
48
+ Write-Output '[0] 退出'
49
+ $choice = Read-Host '请选择'
50
+ switch ($choice) {
51
+ '1' { Start-BH }
52
+ '2' { Stop-BH }
53
+ '0' { exit }
54
+ default { Write-Output '无效选择' }
55
+ }
56
+ }
@@ -0,0 +1,50 @@
1
+ # dsh-actions.ps1 —— 托盘菜单动作执行器 (由 dsh-tray.ps1 以独立隐藏进程拉起)
2
+ # 用法: pwsh -NoProfile -File dsh-actions.ps1 -Action start|stop|restart|tui-start|tui-stop
3
+ # 动作在独立进程执行, 托盘 UI 线程保持响应; 日志写入 ~/.dsh/logs/dsh-tray-actions.log
4
+ param(
5
+ [Parameter(Mandatory = $true)]
6
+ [ValidateSet('start', 'stop', 'restart', 'tui-start', 'tui-stop')]
7
+ [string]$Action
8
+ )
9
+ $ErrorActionPreference = 'Stop'
10
+
11
+ . (Join-Path $PSScriptRoot 'dsh-lib.ps1')
12
+
13
+ $logFile = Join-Path $LOG_DIR 'dsh-tray-actions.log'
14
+ function Log([string]$msg) {
15
+ $line = ('[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $msg)
16
+ Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue
17
+ }
18
+
19
+ try {
20
+ switch ($Action) {
21
+ 'start' {
22
+ Log '启动 DSH (来自系统托盘)'
23
+ Start-Dsh -OpenBrowser:$false
24
+ Log '启动动作完成'
25
+ }
26
+ 'stop' {
27
+ Log '停止 DSH (来自系统托盘)'
28
+ Stop-Dsh
29
+ Log '停止动作完成'
30
+ }
31
+ 'restart' {
32
+ Log '重启 DSH (来自系统托盘)'
33
+ Restart-Dsh
34
+ Log '重启动作完成'
35
+ }
36
+ 'tui-start' {
37
+ Log '启动 TUI (来自系统托盘)'
38
+ Start-Tui
39
+ Log 'TUI 启动动作完成'
40
+ }
41
+ 'tui-stop' {
42
+ Log '停止 TUI (来自系统托盘)'
43
+ Stop-Tui
44
+ Log 'TUI 停止动作完成'
45
+ }
46
+ }
47
+ } catch {
48
+ Log ("动作 {0} 失败: {1}" -f $Action, $_.Exception.Message)
49
+ exit 1
50
+ }
@@ -0,0 +1,4 @@
1
+ @echo off
2
+ chcp 65001 > nul
3
+ powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0dsh-all-menu.ps1"
4
+ pause
@@ -0,0 +1,106 @@
1
+ # dsh 一体化控制台 —— 同时管理 dsh web (3080) 和黑洞模拟器 (8137)
2
+ # 用法: 双击 dsh-一体化菜单.lnk / dsh-all-menu.bat 进入交互菜单
3
+ # 命令行: dsh-all-menu.ps1 -Cmd all-start|all-stop|dsh-start|dsh-stop|bh-start|bh-stop|status|open
4
+ param([string]$Cmd)
5
+ $ErrorActionPreference = 'Stop'
6
+ chcp 65001 > $null
7
+
8
+ $DshMenu = "$PSScriptRoot\dsh-menu.ps1"
9
+ $BhMenu = "$PSScriptRoot\bh-menu.ps1"
10
+ $DshUrl = 'http://127.0.0.1:3080'
11
+ $BhUrl = 'http://127.0.0.1:8137'
12
+
13
+ function Test-Port([int]$port) {
14
+ $c = New-Object System.Net.Sockets.TcpClient
15
+ try { if ($c.BeginConnect('127.0.0.1', $port, $null, $null).AsyncWaitHandle.WaitOne(500) -and $c.Connected) { return $true } } catch {}
16
+ finally { $c.Close() }
17
+ return $false
18
+ }
19
+
20
+ function StatusLine {
21
+ $d = if (Test-Port 3080) { "运行中: $DshUrl" } else { '未运行' }
22
+ $b = if (Test-Port 8137) { "运行中: $BhUrl" } else { '未运行' }
23
+ Write-Host " [dsh] $d" -ForegroundColor $(if (Test-Port 3080) { 'Green' } else { 'Red' })
24
+ Write-Host " [bh ] $b" -ForegroundColor $(if (Test-Port 8137) { 'Green' } else { 'Red' })
25
+ }
26
+
27
+ function All-Start {
28
+ Write-Host '启动 dsh web ...' -ForegroundColor Cyan
29
+ & powershell -NoProfile -ExecutionPolicy Bypass -File $DshMenu -Cmd start | Out-Host
30
+ Write-Host '启动 黑洞模拟器 ...' -ForegroundColor Cyan
31
+ & powershell -NoProfile -ExecutionPolicy Bypass -File $BhMenu -Cmd start | Out-Host
32
+ }
33
+ function All-Stop {
34
+ Write-Host '停止 dsh web ...' -ForegroundColor Yellow
35
+ & powershell -NoProfile -ExecutionPolicy Bypass -File $DshMenu -Cmd stop | Out-Host
36
+ Write-Host '停止 黑洞模拟器 ...' -ForegroundColor Yellow
37
+ & powershell -NoProfile -ExecutionPolicy Bypass -File $BhMenu -Cmd stop | Out-Host
38
+ }
39
+ function Pause-Back { Read-Host '按回车键返回菜单' | Out-Null }
40
+
41
+ function Show-Detail {
42
+ Write-Host ' ── dsh web ──' -ForegroundColor Cyan
43
+ if (Test-Port 3080) {
44
+ $p = (Get-NetTCPConnection -LocalPort 3080 -State Listen | Select-Object -First 1).OwningProcess
45
+ Write-Host " 状态: 运行中 PID: $p" -ForegroundColor Green
46
+ Write-Host " 地址: $DshUrl" -ForegroundColor White
47
+ } else { Write-Host ' 状态: 未运行' -ForegroundColor Red }
48
+ Write-Host ' ── 黑洞模拟器 ──' -ForegroundColor Cyan
49
+ if (Test-Port 8137) {
50
+ $p = (Get-NetTCPConnection -LocalPort 8137 -State Listen | Select-Object -First 1).OwningProcess
51
+ Write-Host " 状态: 运行中 PID: $p" -ForegroundColor Green
52
+ Write-Host " 地址: $BhUrl" -ForegroundColor White
53
+ } else { Write-Host ' 状态: 未运行' -ForegroundColor Red }
54
+ }
55
+
56
+ if ($Cmd) {
57
+ switch ($Cmd.ToLower()) {
58
+ 'all-start' { All-Start }
59
+ 'all-stop' { All-Stop }
60
+ 'dsh-start' { & powershell -NoProfile -ExecutionPolicy Bypass -File $DshMenu -Cmd start | Out-Host }
61
+ 'dsh-stop' { & powershell -NoProfile -ExecutionPolicy Bypass -File $DshMenu -Cmd stop | Out-Host }
62
+ 'bh-start' { & powershell -NoProfile -ExecutionPolicy Bypass -File $BhMenu -Cmd start | Out-Host }
63
+ 'bh-stop' { & powershell -NoProfile -ExecutionPolicy Bypass -File $BhMenu -Cmd stop | Out-Host }
64
+ 'status' { StatusLine }
65
+ 'open' { if (Test-Port 3080) { Start-Process $DshUrl } else { Write-Host '[dsh] 未运行' -ForegroundColor Red }
66
+ if (Test-Port 8137) { Start-Process $BhUrl } else { Write-Host '[bh] 未运行' -ForegroundColor Red } }
67
+ default { Write-Output '用法: dsh-all-menu.ps1 -Cmd all-start|all-stop|dsh-start|dsh-stop|bh-start|bh-stop|status|open' }
68
+ }
69
+ exit
70
+ }
71
+
72
+ while ($true) {
73
+ Clear-Host
74
+ Write-Host ''
75
+ Write-Host ' ╔═══════════════════════════════════════╗' -ForegroundColor Cyan
76
+ Write-Host ' ║ dsh 一体化控制台 v1 ║' -ForegroundColor Cyan
77
+ Write-Host ' ╚═══════════════════════════════════════╝' -ForegroundColor Cyan
78
+ Write-Host ''
79
+ StatusLine
80
+ Write-Host ''
81
+ Write-Host ' [1] 全部启动 (dsh + 黑洞) 并打开浏览器' -ForegroundColor White
82
+ Write-Host ' [2] 全部停止' -ForegroundColor White
83
+ Write-Host ' [3] 只启动 dsh [4] 只停止 dsh' -ForegroundColor White
84
+ Write-Host ' [5] 只启动黑洞 [6] 只停止黑洞' -ForegroundColor White
85
+ Write-Host ' [7] 服务详情 (PID/端口)' -ForegroundColor White
86
+ Write-Host ' [8] 打开 dsh 页面 [9] 打开黑洞页面' -ForegroundColor White
87
+ Write-Host ' [0] 退出' -ForegroundColor White
88
+ Write-Host ''
89
+ $choice = Read-Host ' 请选择'
90
+ Write-Host ''
91
+ switch ($choice) {
92
+ '1' { All-Start; Pause-Back }
93
+ '2' { All-Stop; Pause-Back }
94
+ '3' { & powershell -NoProfile -ExecutionPolicy Bypass -File $DshMenu -Cmd start | Out-Host; Pause-Back }
95
+ '4' { & powershell -NoProfile -ExecutionPolicy Bypass -File $DshMenu -Cmd stop | Out-Host; Pause-Back }
96
+ '5' { & powershell -NoProfile -ExecutionPolicy Bypass -File $BhMenu -Cmd start | Out-Host; Pause-Back }
97
+ '6' { & powershell -NoProfile -ExecutionPolicy Bypass -File $BhMenu -Cmd stop | Out-Host; Pause-Back }
98
+ '7' { Show-Detail; Pause-Back }
99
+ '8' { if (Test-Port 3080) { Start-Process $DshUrl } else { Write-Host ' [dsh] 未运行, 请先启动' -ForegroundColor Red }
100
+ Pause-Back }
101
+ '9' { if (Test-Port 8137) { Start-Process $BhUrl } else { Write-Host ' [bh] 未运行, 请先启动' -ForegroundColor Red }
102
+ Pause-Back }
103
+ '0' { Write-Host ' 再见!' -ForegroundColor Cyan; exit }
104
+ default { Write-Host ' 无效选择, 请重试' -ForegroundColor Red; Start-Sleep -Milliseconds 800 }
105
+ }
106
+ }
@@ -0,0 +1,10 @@
1
+ @echo off
2
+ chcp 65001 >nul
3
+ rem DSH 控制台 - double click to open menu
4
+ rem prefer pwsh (PowerShell 7), fallback to powershell 5.1
5
+ where pwsh >nul 2>nul
6
+ if %errorlevel%==0 (
7
+ pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0dsh-control.ps1"
8
+ ) else (
9
+ powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0dsh-control.ps1"
10
+ )
@@ -0,0 +1,40 @@
1
+ # DSH 控制台 —— 启动/重启 Web + 启动 TUI + 状态查看 + 系统托盘
2
+ # 部署:2026-08-26;双击桌面「DSH 控制台」或 dsh-control.bat
3
+ # 坑:本脚本由用户独立窗口运行(非 web 子进程),杀/重启 web 安全(PITFALLS §12.2)
4
+ # 2026-08-28: 启动/停止/重启/托盘逻辑抽到 dsh-lib.ps1(与系统托盘共享同一份代码)
5
+ $ErrorActionPreference = 'Stop'
6
+ chcp 65001 > $null
7
+
8
+ . (Join-Path $PSScriptRoot 'dsh-lib.ps1')
9
+
10
+ while ($true) {
11
+ Clear-Host
12
+ Write-Host ''
13
+ Write-Host ' ┌──────────────────────────────┐'
14
+ Write-Host ' │ DSH 控制台 │'
15
+ Write-Host ' ├──────────────────────────────┤'
16
+ Write-Host ' │ 1) 启动 / 打开 Web (3088) │'
17
+ Write-Host ' │ 2) 重启 Web │'
18
+ Write-Host ' │ 3) 启动 TUI(新终端窗口) │'
19
+ Write-Host ' │ 4) 查看状态 │'
20
+ Write-Host ' │ 5) 启动系统托盘 │'
21
+ Write-Host ' │ 0) 退出 │'
22
+ Write-Host ' └──────────────────────────────┘'
23
+ $c = Read-Host ' 请选择'
24
+ switch ($c) {
25
+ '1' { Start-Dsh }
26
+ '2' { Restart-Dsh }
27
+ '3' { Start-Tui }
28
+ '4' { Show-Status }
29
+ '5' {
30
+ if (Ensure-DshTray) {
31
+ Write-Host '[tray] 系统托盘已在运行(蓝鲸=运行中 / 灰鲸=未运行,右键可控制)' -ForegroundColor Green
32
+ } else {
33
+ Write-Host '[tray] 系统托盘已启动(蓝鲸=运行中 / 灰鲸=未运行,右键可控制)' -ForegroundColor Green
34
+ }
35
+ }
36
+ '0' { Write-Host ' 再见!'; break }
37
+ default { Write-Host " 无效选择: $c" -ForegroundColor Red }
38
+ }
39
+ if ($c -ne '0') { Write-Host ''; Read-Host ' 按回车返回菜单' }
40
+ }
@@ -0,0 +1,165 @@
1
+ # dsh-lib.ps1 —— DSH 启动/停止/重启/托盘 公共库
2
+ # 被 dsh-control.ps1 (控制台) 与 dsh-tray.ps1 (系统托盘) 点源引用 (dot-source)
3
+ # 所有路径/端口/进程管理逻辑的单一事实来源: 修改此处, 两个入口同时生效
4
+ $ErrorActionPreference = 'Stop'
5
+
6
+ $DSH_CMD = 'D:\Program Files (x86)\dsh\dsh.cmd'
7
+ $NODE = 'C:\Program Files\nodejs\node.exe'
8
+ $BIN = 'D:\Program Files (x86)\dsh\node_modules\@deepseek-ai\dsh\lib\bin.js'
9
+ $WORKDIR = 'D:\Program Files (x86)\dsh'
10
+ $PORT = 3088
11
+ $URL = "http://127.0.0.1:$PORT"
12
+ $LOG_DIR = "$env:USERPROFILE\.dsh\logs"
13
+ $WT = "$env:LOCALAPPDATA\Microsoft\WindowsApps\wt.exe"
14
+ $PWSH = 'D:\Program Files (x86)\PowerShell-7.6.4\pwsh.exe'
15
+ $TRAY_VERSION = '1.0.0' # 托盘版本 (与 npm 包版本同步, 用于日志/排查)
16
+ $TRAY_PKG = 'dsh-windows-tray' # npm 包名 (托盘自更新)
17
+ $TRAY_SCRIPT = Join-Path $PSScriptRoot 'dsh-tray.ps1'
18
+
19
+ function Test-Port([int]$port) {
20
+ $client = New-Object System.Net.Sockets.TcpClient
21
+ try {
22
+ $async = $client.BeginConnect('127.0.0.1', $port, $null, $null)
23
+ if ($async.AsyncWaitHandle.WaitOne(500) -and $client.Connected) { return $true }
24
+ return $false
25
+ } catch { return $false }
26
+ finally { $client.Close() }
27
+ }
28
+
29
+ function Get-WebPid {
30
+ @((Get-NetTCPConnection -LocalPort $PORT -State Listen -ErrorAction SilentlyContinue).OwningProcess | Select-Object -Unique)
31
+ }
32
+
33
+ function Start-Dsh {
34
+ param([switch]$OpenBrowser = $true)
35
+ $trayStarted = -not (Ensure-DshTray) # 先保证系统托盘在(幂等)
36
+ $pids = @(Get-WebPid)
37
+ if ($pids.Count -gt 0) {
38
+ Write-Host "[dsh] 已在运行: $URL (PID $($pids -join ','))" -ForegroundColor Green
39
+ if ($trayStarted) { Write-Host '[tray] 系统托盘已自动启动(右键可控制 DSH)' -ForegroundColor DarkGray }
40
+ if ($OpenBrowser) { Start-Process $URL }
41
+ return
42
+ }
43
+ Write-Host "[dsh] 启动中 (端口 $PORT)..."
44
+ $out = Join-Path $LOG_DIR 'web-menu.out.log'
45
+ $err = Join-Path $LOG_DIR 'web-menu.err.log'
46
+ Start-Process -FilePath $NODE -ArgumentList "`"$BIN`"",'web','--host','127.0.0.1','--port',"$PORT",'--no-open' `
47
+ -WorkingDirectory $WORKDIR -WindowStyle Hidden `
48
+ -RedirectStandardOutput $out -RedirectStandardError $err | Out-Null
49
+ for ($i = 0; $i -lt 12; $i++) {
50
+ Start-Sleep -Seconds 1
51
+ if (Test-Port $PORT) { break }
52
+ }
53
+ if (Test-Port $PORT) {
54
+ Write-Host "[dsh] 启动成功: $URL" -ForegroundColor Green
55
+ if ($trayStarted) { Write-Host '[tray] 系统托盘已自动启动(右键可控制 DSH)' -ForegroundColor DarkGray }
56
+ Start-Sleep -Seconds 2
57
+ if ($OpenBrowser) { Start-Process $URL }
58
+ } else {
59
+ Write-Host "[dsh] 启动失败: 12 秒内端口 $PORT 未监听" -ForegroundColor Red
60
+ Write-Host "[dsh] 错误日志: $err(末尾 10 行)"
61
+ Get-Content $err -Tail 10 -ErrorAction SilentlyContinue
62
+ }
63
+ }
64
+
65
+ function Stop-Dsh {
66
+ $pids = @(Get-WebPid)
67
+ if ($pids.Count -eq 0) { Write-Host '[dsh] 未在运行'; return }
68
+ foreach ($p in $pids) {
69
+ Write-Host "[dsh] 停止进程 $p"
70
+ & taskkill /PID $p /T /F 2>$null | Out-Null
71
+ }
72
+ for ($i = 0; $i -lt 10; $i++) {
73
+ Start-Sleep -Seconds 1
74
+ if (-not (Test-Port $PORT)) { break }
75
+ }
76
+ if (Test-Port $PORT) { Write-Host '[dsh] 端口未释放,请稍后重试' -ForegroundColor Red }
77
+ else { Write-Host '[dsh] 已停止' -ForegroundColor Yellow }
78
+ }
79
+
80
+ function Restart-Dsh {
81
+ Stop-Dsh
82
+ Start-Sleep -Seconds 1
83
+ Start-Dsh
84
+ }
85
+
86
+ function Start-Tui {
87
+ $existing = @(Get-TuiProcesses)
88
+ if ($existing.Count -gt 0) {
89
+ Write-Host "[tui] TUI 已在运行 (PID $($existing.ProcessId -join ',')), 不重复启动" -ForegroundColor Yellow
90
+ return
91
+ }
92
+ $cmdLine = "& '$DSH_CMD' --profile dsh-tui"
93
+ if (Test-Path $WT) {
94
+ $args = "new-tab --title `"DSH TUI`" pwsh -NoExit -Command `"$cmdLine`""
95
+ Start-Process $WT -ArgumentList $args
96
+ Write-Host '[tui] 已在 Windows Terminal 新标签页启动 DSH TUI' -ForegroundColor Green
97
+ } elseif (Test-Path $PWSH) {
98
+ Start-Process $PWSH -ArgumentList '-NoExit','-Command',$cmdLine
99
+ Write-Host '[tui] 已在新 PowerShell 窗口启动 DSH TUI' -ForegroundColor Green
100
+ } else {
101
+ Start-Process 'powershell' -ArgumentList '-NoExit','-Command',$cmdLine
102
+ Write-Host '[tui] 已在新 PowerShell 窗口启动 DSH TUI' -ForegroundColor Green
103
+ }
104
+ }
105
+
106
+ function Get-TuiProcesses {
107
+ Get-CimInstance Win32_Process -Filter "Name='node.exe'" -ErrorAction SilentlyContinue |
108
+ Where-Object { $_.CommandLine -match 'dsh-tui' }
109
+ }
110
+
111
+ function Stop-Tui {
112
+ $tui = @(Get-TuiProcesses)
113
+ if ($tui.Count -eq 0) { Write-Host '[tui] TUI: 未在运行'; return }
114
+ foreach ($p in $tui) {
115
+ Write-Host "[tui] 停止 TUI 进程 $($p.ProcessId)"
116
+ & taskkill /PID $p.ProcessId /T /F 2>$null | Out-Null
117
+ }
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
+ }
122
+
123
+ function Show-Status {
124
+ $pids = @(Get-WebPid)
125
+ if ($pids.Count -gt 0) {
126
+ Write-Host "[dsh] Web: 运行中 $URL (PID $($pids -join ','))" -ForegroundColor Green
127
+ } else {
128
+ Write-Host '[dsh] Web: 未运行' -ForegroundColor Yellow
129
+ }
130
+ $tui = @(Get-TuiProcesses)
131
+ if ($tui.Count -gt 0) {
132
+ Write-Host "[tui] TUI: 运行中 (PID $($tui.ProcessId -join ','))" -ForegroundColor Green
133
+ } else {
134
+ Write-Host '[tui] TUI: 未运行(按 3 启动)' -ForegroundColor Yellow
135
+ }
136
+ $tray = @(Get-TrayProcesses)
137
+ if ($tray.Count -gt 0) {
138
+ Write-Host "[tray] 托盘: 运行中 (PID $($tray.ProcessId -join ','))" -ForegroundColor Green
139
+ } else {
140
+ Write-Host '[tray] 托盘: 未运行(按 5 启动)' -ForegroundColor Yellow
141
+ }
142
+ }
143
+
144
+ # ---- 系统托盘 ----
145
+
146
+ function Get-TrayProcesses {
147
+ Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
148
+ ($_.Name -eq 'pwsh.exe' -or $_.Name -eq 'powershell.exe') -and
149
+ $_.CommandLine -and $_.CommandLine -match 'dsh-tray\.ps1' -and
150
+ $_.ProcessId -ne $PID
151
+ }
152
+ }
153
+
154
+ # 确保托盘在运行(幂等): 已在运行返回 $true; 刚拉起返回 $false
155
+ function Ensure-DshTray {
156
+ $existing = @(Get-TrayProcesses)
157
+ if ($existing.Count -gt 0) { return $true }
158
+ $args = @('-NoProfile','-ExecutionPolicy','Bypass','-WindowStyle','Hidden','-File',$TRAY_SCRIPT)
159
+ if (Test-Path $PWSH) {
160
+ Start-Process -FilePath $PWSH -ArgumentList $args -WindowStyle Hidden | Out-Null
161
+ } else {
162
+ Start-Process -FilePath 'powershell.exe' -ArgumentList $args -WindowStyle Hidden | Out-Null
163
+ }
164
+ return $false
165
+ }
@@ -0,0 +1,11 @@
1
+ @echo off
2
+ chcp 65001 >nul
3
+ rem dsh (DeepSeek Harness) launcher - double click to open menu
4
+ rem prefer pwsh (PowerShell 7), fallback to powershell 5.1
5
+ where pwsh >nul 2>nul
6
+ if %errorlevel%==0 (
7
+ pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0dsh-menu.ps1"
8
+ ) else (
9
+ powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0dsh-menu.ps1"
10
+ )
11
+ pause
@@ -0,0 +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
+ }
@@ -0,0 +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
+ )