panrouter 1.7.2 → 1.8.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.
- package/cli.mjs +7 -13
- package/package.json +3 -3
- package/panrouter-tray.vbs +13 -0
- package/tray-daemon.ps1 +65 -131
- package/daemon.mjs +0 -133
package/cli.mjs
CHANGED
|
@@ -141,16 +141,17 @@ async function startServer() {
|
|
|
141
141
|
// ─── 4. 以托盘模式启动 ──────────────────────────────────────────────────
|
|
142
142
|
|
|
143
143
|
function startTray() {
|
|
144
|
-
const
|
|
144
|
+
const vbsPath = path.join(__dirname, "panrouter-tray.vbs");
|
|
145
145
|
|
|
146
|
-
if (!fs.existsSync(
|
|
147
|
-
log("!!", "未找到
|
|
146
|
+
if (!fs.existsSync(vbsPath)) {
|
|
147
|
+
log("!!", "未找到 panrouter-tray.vbs", "red");
|
|
148
148
|
process.exit(1);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
log("..", "正在以托盘模式启动 Pan Router...", "yellow");
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
// cmd /c start /B → VBS 在新进程树中运行, 有 Window Station
|
|
154
|
+
const child = spawn("cmd.exe", ["/c", "start", "/B", "wscript.exe", "//B", "//NoLogo", vbsPath], {
|
|
154
155
|
cwd: __dirname,
|
|
155
156
|
stdio: "ignore",
|
|
156
157
|
windowsHide: true,
|
|
@@ -159,15 +160,8 @@ function startTray() {
|
|
|
159
160
|
});
|
|
160
161
|
child.unref();
|
|
161
162
|
|
|
162
|
-
log("OK", "Pan Router
|
|
163
|
-
console.log("");
|
|
164
|
-
console.log(" \x1b[33m实时查看日志:\x1b[0m");
|
|
165
|
-
console.log(" powershell -Command \"Get-Content -Wait -Tail 20 $env:TEMP\\panrouter-daemon.log\"");
|
|
166
|
-
console.log("");
|
|
167
|
-
console.log(" \x1b[33m查看全部日志:\x1b[0m");
|
|
168
|
-
console.log(" notepad %TEMP%\\panrouter-daemon.log");
|
|
169
|
-
console.log("");
|
|
170
|
-
console.log(" 若 10 秒后图标未出现,请查看日志并反馈");
|
|
163
|
+
log("OK", "Pan Router 托盘已启动", "green");
|
|
164
|
+
console.log(" 图标在右下角, 右键可退出");
|
|
171
165
|
}
|
|
172
166
|
|
|
173
167
|
// ─── 主流程 ──────────────────────────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "panrouter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"cli.mjs",
|
|
11
|
-
"daemon.mjs",
|
|
12
11
|
"server.mjs",
|
|
13
|
-
"tray-daemon.ps1"
|
|
12
|
+
"tray-daemon.ps1",
|
|
13
|
+
"panrouter-tray.vbs"
|
|
14
14
|
],
|
|
15
15
|
"license": "MIT"
|
|
16
16
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
' Pan Router Tray Launcher
|
|
2
|
+
' 由 cli.mjs --tray 通过 cmd /c start /B 启动
|
|
3
|
+
' 与 daemon 进程树无关, 独立 Window Station
|
|
4
|
+
|
|
5
|
+
Dim WshShell, FSO, ScriptDir
|
|
6
|
+
Set WshShell = CreateObject("WScript.Shell")
|
|
7
|
+
Set FSO = CreateObject("Scripting.FileSystemObject")
|
|
8
|
+
|
|
9
|
+
ScriptDir = FSO.GetParentFolderName(WScript.ScriptFullName)
|
|
10
|
+
|
|
11
|
+
' 后台隐藏启动 PS 托盘 (自包含: 启动 server + 图标 + 菜单)
|
|
12
|
+
' 0 = 隐藏窗口, False = 不等待返回
|
|
13
|
+
WshShell.Run "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -STA -File """ & ScriptDir & "\tray-daemon.ps1" & """", 0, False
|
package/tray-daemon.ps1
CHANGED
|
@@ -1,29 +1,45 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
Pan Router
|
|
4
|
-
|
|
5
|
-
由 panrouter-tray.vbs 启动。
|
|
6
|
-
服务器已经由 VBS 启动, PS 只负责:
|
|
7
|
-
- NotifyIcon (右下角)
|
|
8
|
-
- 右键菜单 (开机自启动 / 退出)
|
|
9
|
-
- 健康检查 + 自动重启
|
|
10
|
-
|
|
11
|
-
用法:
|
|
12
|
-
powershell -ExecutionPolicy Bypass -WindowStyle Hidden -STA -File tray-daemon.ps1
|
|
3
|
+
Pan Router 托盘 (自包含: 启动服务器 + 托盘图标 + 菜单)
|
|
4
|
+
⚠ 必须从 WScript.Shell.Run (VBS) 或正常控制台启动, 不能从 detached Node 进程启动
|
|
13
5
|
#>
|
|
14
6
|
|
|
15
|
-
$
|
|
7
|
+
$scriptPath = $MyInvocation.MyCommand.Path
|
|
8
|
+
$scriptDir = Split-Path $scriptPath -Parent
|
|
16
9
|
$serverPath = Join-Path $scriptDir "server.mjs"
|
|
17
10
|
$logFile = "$env:TEMP\panrouter-tray.log"
|
|
18
|
-
$
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
$now = Get-Date -Format "HH:mm:ss"
|
|
12
|
+
|
|
13
|
+
"$now === PanRouter Tray START ===" | Out-File $logFile
|
|
14
|
+
"$now PID=$pid" | Out-File -Append $logFile
|
|
15
|
+
"$now serverPath=$serverPath" | Out-File -Append $logFile
|
|
16
|
+
|
|
17
|
+
# ─── 杀掉旧 server ──────────────────────────────
|
|
18
|
+
try {
|
|
19
|
+
$old = wmic process where "name='node.exe'" get ProcessId,CommandLine /format:csv 2>$null
|
|
20
|
+
foreach ($line in $old) {
|
|
21
|
+
if ($line -match '(\d+),.*?server\.mjs') {
|
|
22
|
+
Stop-Process -Id $Matches[1] -Force -ErrorAction SilentlyContinue
|
|
23
|
+
"$now Killed old server PID=$($Matches[1])" | Out-File -Append $logFile
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} catch {}
|
|
27
|
+
|
|
28
|
+
# ─── 启动 server.mjs (用 node 完整路径) ─────────
|
|
29
|
+
try {
|
|
30
|
+
$nodePath = (Get-Command node).Source
|
|
31
|
+
"$now nodePath=$nodePath" | Out-File -Append $logFile
|
|
32
|
+
$p = Start-Process -FilePath $nodePath -ArgumentList "`"$serverPath`"" -WorkingDirectory $scriptDir -WindowStyle Hidden -PassThru -NoNewWindow
|
|
33
|
+
if ($p -and $p.Id) { "$now Server started PID=$($p.Id)" | Out-File -Append $logFile }
|
|
34
|
+
} catch {
|
|
35
|
+
"$now Server start FAILED: $_" | Out-File -Append $logFile
|
|
36
|
+
}
|
|
22
37
|
|
|
38
|
+
# ─── WinForms ────────────────────────────────────
|
|
23
39
|
Add-Type -AssemblyName System.Windows.Forms
|
|
24
40
|
Add-Type -AssemblyName System.Drawing
|
|
25
41
|
|
|
26
|
-
# ───
|
|
42
|
+
# ─── 图标 ────────────────────────────────────────
|
|
27
43
|
$bmp = New-Object System.Drawing.Bitmap(16, 16)
|
|
28
44
|
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
29
45
|
$g.SmoothingMode = 'HighQuality'
|
|
@@ -38,153 +54,71 @@ $hIcon = $bmp.GetHicon()
|
|
|
38
54
|
$icon = [System.Drawing.Icon]::FromHandle($hIcon)
|
|
39
55
|
$bmp.Dispose()
|
|
40
56
|
|
|
41
|
-
# ─── NotifyIcon
|
|
57
|
+
# ─── NotifyIcon ──────────────────────────────────
|
|
42
58
|
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
43
59
|
$notifyIcon.Icon = $icon
|
|
44
60
|
$notifyIcon.Text = "Pan Router | 端口 50816"
|
|
45
61
|
|
|
46
|
-
# ───
|
|
62
|
+
# ─── 菜单 ────────────────────────────────────────
|
|
47
63
|
$menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
48
|
-
|
|
49
|
-
$titleItem = New-Object System.Windows.Forms.ToolStripMenuItem
|
|
50
|
-
$titleItem.Text = "Pan Router - :50816"
|
|
51
|
-
$titleItem.Enabled = $false
|
|
52
|
-
$titleItem.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
|
|
53
|
-
[void]$menu.Items.Add($titleItem)
|
|
64
|
+
[void]$menu.Items.Add((New-Object System.Windows.Forms.ToolStripMenuItem("Pan Router - :50816") { Enabled = $false; Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) }))
|
|
54
65
|
[void]$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator))
|
|
55
66
|
|
|
56
|
-
$autoItem = New-Object System.Windows.Forms.ToolStripMenuItem
|
|
57
|
-
$autoItem.
|
|
58
|
-
$autoItem.Checked = Get-Autostart
|
|
67
|
+
$autoItem = New-Object System.Windows.Forms.ToolStripMenuItem("开机自启动")
|
|
68
|
+
try { $autoItem.Checked = (Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name PanRouter -ErrorAction Stop).PanRouter -ne $null } catch {}
|
|
59
69
|
$autoItem.Add_Click({
|
|
60
|
-
$
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
$ni.ShowBalloonTip(2000, "Pan Router", "开机自启动已关闭", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
70
|
+
if ($autoItem.Checked) {
|
|
71
|
+
reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /f 2>&1 | Out-Null
|
|
72
|
+
$autoItem.Checked = $false
|
|
73
|
+
$notifyIcon.ShowBalloonTip(2000, "Pan Router", "开机自启动已关闭", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
65
74
|
} else {
|
|
66
|
-
|
|
67
|
-
$
|
|
75
|
+
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /t REG_SZ /d "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -STA -File `"$scriptPath`"" /f 2>&1 | Out-Null
|
|
76
|
+
$autoItem.Checked = $true
|
|
77
|
+
$notifyIcon.ShowBalloonTip(2000, "Pan Router", "开机自启动已开启 ✓", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
68
78
|
}
|
|
69
79
|
}.GetNewClosure())
|
|
70
80
|
[void]$menu.Items.Add($autoItem)
|
|
71
81
|
[void]$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator))
|
|
72
82
|
|
|
73
|
-
$exitItem = New-Object System.Windows.Forms.ToolStripMenuItem
|
|
74
|
-
$exitItem.Text = "退出"
|
|
83
|
+
$exitItem = New-Object System.Windows.Forms.ToolStripMenuItem("退出")
|
|
75
84
|
$exitItem.Add_Click({
|
|
76
|
-
Write-Log "Exit clicked"
|
|
77
85
|
$notifyIcon.Visible = $false
|
|
86
|
+
try { $old = wmic process where "name='node.exe'" get ProcessId,CommandLine /format:csv 2>$null; foreach ($line in $old) { if ($line -match '(\d+),.*?server\.mjs') { Stop-Process -Id $Matches[1] -Force -ErrorAction SilentlyContinue } } } catch {}
|
|
78
87
|
[System.Windows.Forms.Application]::Exit()
|
|
79
88
|
}.GetNewClosure())
|
|
80
89
|
[void]$menu.Items.Add($exitItem)
|
|
81
90
|
|
|
82
91
|
$notifyIcon.ContextMenuStrip = $menu
|
|
83
|
-
$notifyIcon.Visible = $true
|
|
84
|
-
Write-Log "NotifyIcon visible with menu"
|
|
85
|
-
function Test-Online {
|
|
86
|
-
try {
|
|
87
|
-
$req = [System.Net.WebRequest]::Create("http://127.0.0.1:50816/health")
|
|
88
|
-
$req.Timeout = 1500
|
|
89
|
-
$resp = $req.GetResponse()
|
|
90
|
-
$resp.Close()
|
|
91
|
-
return $true
|
|
92
|
-
} catch { return $false }
|
|
93
|
-
}
|
|
94
92
|
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
# 杀旧的 server.mjs 进程
|
|
98
|
-
$old = wmic process where "name='node.exe'" get ProcessId,CommandLine /format:csv 2>$null
|
|
99
|
-
foreach ($line in $old) {
|
|
100
|
-
if ($line -match '(\d+),.*?server\.mjs') {
|
|
101
|
-
Stop-Process -Id $Matches[1] -Force -ErrorAction SilentlyContinue
|
|
102
|
-
Write-Log "Killed old server PID=$($Matches[1])"
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
} catch {}
|
|
106
|
-
try {
|
|
107
|
-
$p = Start-Process -FilePath "node" -ArgumentList "`"$serverPath`"" -WorkingDirectory $scriptDir -WindowStyle Hidden -PassThru -NoNewWindow
|
|
108
|
-
Write-Log "Server started PID=$($p.Id)"
|
|
109
|
-
} catch { Write-Log "Server start FAILED: $_" }
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
# ─── 开机自启动 ────────────────────────────────────
|
|
113
|
-
function Get-Autostart {
|
|
114
|
-
try { $val = Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name $autostartName -ErrorAction Stop; return $val.$autostartName -ne $null }
|
|
115
|
-
catch { return $false }
|
|
116
|
-
}
|
|
117
|
-
function Set-Autostart($enable) {
|
|
118
|
-
try {
|
|
119
|
-
if ($enable) {
|
|
120
|
-
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v $autostartName /t REG_SZ /d "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -STA -File `"$($MyInvocation.MyCommand.Path)`"" /f 2>&1 | Out-Null
|
|
121
|
-
Write-Log "Autostart ON"
|
|
122
|
-
} else {
|
|
123
|
-
reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v $autostartName /f 2>&1 | Out-Null
|
|
124
|
-
Write-Log "Autostart OFF"
|
|
125
|
-
}
|
|
126
|
-
} catch { Write-Log "Autostart error: $_" }
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
# ═══════════ 主流程 ═══════════
|
|
130
|
-
|
|
131
|
-
# 等待服务器就绪 (VBS 已启动 server, 但可能还没完全起来)
|
|
132
|
-
$ready = $false
|
|
133
|
-
for ($i = 0; $i -lt 20; $i++) {
|
|
134
|
-
Start-Sleep -Milliseconds 500
|
|
135
|
-
if (Test-Online) { $ready = $true; break }
|
|
136
|
-
}
|
|
137
|
-
Write-Log "Server online=$ready"
|
|
138
|
-
if ($ready) {
|
|
139
|
-
$notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器已就绪 ✓ (端口 50816)", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
140
|
-
} else {
|
|
141
|
-
Write-Log "Server not online, starting ourselves..."
|
|
142
|
-
Start-Server
|
|
143
|
-
for ($i = 0; $i -lt 20; $i++) {
|
|
144
|
-
Start-Sleep -Milliseconds 500
|
|
145
|
-
if (Test-Online) { $ready = $true; break }
|
|
146
|
-
}
|
|
147
|
-
if ($ready) {
|
|
148
|
-
$notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器已就绪 ✓", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
149
|
-
} else {
|
|
150
|
-
$notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器启动失败 ⚠", [System.Windows.Forms.ToolTipIcon]::Error)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
# 左键: 状态
|
|
93
|
+
# ─── 左键 ────────────────────────────────────────
|
|
155
94
|
$notifyIcon.Add_MouseClick({
|
|
156
95
|
if ($_.Button -eq [System.Windows.Forms.MouseButtons]::Left) {
|
|
157
|
-
|
|
158
|
-
|
|
96
|
+
try { $req = [System.Net.WebRequest]::Create("http://127.0.0.1:50816/health"); $req.Timeout = 1500; $resp = $req.GetResponse(); $resp.Close(); $notifyIcon.ShowBalloonTip(2000, "Pan Router", "运行正常 ✓ (端口 50816)", [System.Windows.Forms.ToolTipIcon]::Info) }
|
|
97
|
+
catch { $notifyIcon.ShowBalloonTip(2000, "Pan Router", "服务器未响应 ⚠", [System.Windows.Forms.ToolTipIcon]::Error) }
|
|
159
98
|
}
|
|
160
99
|
})
|
|
161
100
|
|
|
162
|
-
# 健康检查
|
|
101
|
+
# ─── 健康检查 ────────────────────────────────────
|
|
163
102
|
$healthTimer = New-Object System.Windows.Forms.Timer
|
|
164
103
|
$healthTimer.Interval = 30000
|
|
165
104
|
$healthTimer.Add_Tick({
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
Start-
|
|
170
|
-
if (Test-Online) { $notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器已自动重启", [System.Windows.Forms.ToolTipIcon]::Info) }
|
|
105
|
+
try { $req = [System.Net.WebRequest]::Create("http://127.0.0.1:50816/health"); $req.Timeout = 1500; $resp = $req.GetResponse(); $resp.Close() }
|
|
106
|
+
catch {
|
|
107
|
+
try { $old = wmic process where "name='node.exe'" get ProcessId,CommandLine /format:csv 2>$null; foreach ($line in $old) { if ($line -match '(\d+),.*?server\.mjs') { Stop-Process -Id $Matches[1] -Force -ErrorAction SilentlyContinue } } } catch {}
|
|
108
|
+
try { Start-Process -FilePath (Get-Command node).Source -ArgumentList "`"$serverPath`"" -WorkingDirectory $scriptDir -WindowStyle Hidden -NoNewWindow } catch {}
|
|
171
109
|
}
|
|
172
110
|
})
|
|
173
111
|
$healthTimer.Start()
|
|
112
|
+
"$now Health timer started" | Out-File -Append $logFile
|
|
174
113
|
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
$healthTimer.Stop()
|
|
179
|
-
try {
|
|
180
|
-
$old = wmic process where "name='node.exe'" get ProcessId,CommandLine /format:csv 2>$null
|
|
181
|
-
foreach ($line in $old) { if ($line -match '(\d+),.*?server\.mjs') { Stop-Process -Id $Matches[1] -Force -ErrorAction SilentlyContinue } }
|
|
182
|
-
} catch {}
|
|
183
|
-
$notifyIcon.Dispose()
|
|
184
|
-
[System.Runtime.InteropServices.Marshal]::DestroyIcon($hIcon)
|
|
185
|
-
$icon.Dispose()
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
Write-Log "Message loop"
|
|
114
|
+
# ═══ 进入消息循环 ═══
|
|
115
|
+
$notifyIcon.Visible = $true
|
|
116
|
+
"$now Icon visible, entering message loop" | Out-File -Append $logFile
|
|
189
117
|
[System.Windows.Forms.Application]::Run()
|
|
190
|
-
|
|
118
|
+
"$now Exited message loop" | Out-File -Append $logFile
|
|
119
|
+
|
|
120
|
+
# 清理
|
|
121
|
+
$healthTimer.Stop()
|
|
122
|
+
$notifyIcon.Dispose()
|
|
123
|
+
[System.Runtime.InteropServices.Marshal]::DestroyIcon($hIcon)
|
|
124
|
+
$icon.Dispose()
|
package/daemon.mjs
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Pan Router Daemon (v5)
|
|
5
|
-
*
|
|
6
|
-
* 1. spawn server.mjs (detached, hidden) — 正常
|
|
7
|
-
* 2. 用 VBS 启动 PS tray(解决 detached 进程无法创建通知图标的问题)
|
|
8
|
-
* 3. 保持存活,30s 健康检查
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { spawn, execSync } from "node:child_process";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
import path from "node:path";
|
|
14
|
-
import fs from "node:fs";
|
|
15
|
-
import http from "node:http";
|
|
16
|
-
|
|
17
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const serverPath = path.join(__dirname, "server.mjs");
|
|
19
|
-
const trayPsPath = path.join(__dirname, "tray-daemon.ps1");
|
|
20
|
-
const nodeExe = process.execPath;
|
|
21
|
-
const logPath = path.join(process.env.TEMP || "/tmp", "panrouter-daemon.log");
|
|
22
|
-
|
|
23
|
-
function log(msg) {
|
|
24
|
-
try { fs.appendFileSync(logPath, `${new Date().toISOString().slice(11,19)} ${msg}\n`); } catch {}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
log("=== Daemon v5 ===");
|
|
28
|
-
log(`nodeExe=${nodeExe}`);
|
|
29
|
-
|
|
30
|
-
// ─── 1. 杀旧 server ──────────────────────────────
|
|
31
|
-
try {
|
|
32
|
-
const out = execSync(
|
|
33
|
-
'wmic process where "name=\'node.exe\'" get ProcessId,CommandLine /format:csv 2>nul',
|
|
34
|
-
{ encoding: "utf8", windowsHide: true, timeout: 5000 }
|
|
35
|
-
);
|
|
36
|
-
for (const line of out.split("\n")) {
|
|
37
|
-
if (line.includes("server.mjs")) {
|
|
38
|
-
const m = line.match(/(\d+),.*?server\.mjs/);
|
|
39
|
-
if (m) { try { process.kill(parseInt(m[1]), "SIGKILL"); log(`Killed old PID=${m[1]}`); } catch {} }
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
} catch {}
|
|
43
|
-
|
|
44
|
-
// ─── 2. 启动 server.mjs ──────────────────────────
|
|
45
|
-
const server = spawn(nodeExe, [serverPath], {
|
|
46
|
-
cwd: __dirname, stdio: "ignore", windowsHide: true, detached: true, shell: false,
|
|
47
|
-
});
|
|
48
|
-
server.unref();
|
|
49
|
-
log(`Server spawned PID=${server.pid || "??"}`);
|
|
50
|
-
|
|
51
|
-
// ─── 3. 等就绪 ───────────────────────────────────
|
|
52
|
-
function isOnline() {
|
|
53
|
-
return new Promise(rs => {
|
|
54
|
-
try {
|
|
55
|
-
const req = http.get("http://127.0.0.1:50816/health", () => {});
|
|
56
|
-
req.on("response", () => { req.destroy(); rs(true); });
|
|
57
|
-
req.on("error", () => rs(false));
|
|
58
|
-
req.setTimeout(1500, () => { req.destroy(); rs(false); });
|
|
59
|
-
} catch { rs(false); }
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
(async () => {
|
|
64
|
-
let ready = false;
|
|
65
|
-
for (let i = 0; i < 20; i++) {
|
|
66
|
-
if (await isOnline()) { ready = true; break; }
|
|
67
|
-
await new Promise(r => setTimeout(r, 500));
|
|
68
|
-
}
|
|
69
|
-
log(`Server online=${ready}`);
|
|
70
|
-
|
|
71
|
-
// ─── 4. 用 VBS 启动 PS 托盘 ─────────────────────
|
|
72
|
-
log("Starting PS tray via VBS...");
|
|
73
|
-
|
|
74
|
-
if (fs.existsSync(trayPsPath)) {
|
|
75
|
-
log(`trayPsPath exists: ${trayPsPath}`);
|
|
76
|
-
|
|
77
|
-
// 创建临时的 VBS 启动器
|
|
78
|
-
const vbsContent = `Set WshShell = CreateObject("WScript.Shell")
|
|
79
|
-
Set FSO = CreateObject("Scripting.FileSystemObject")
|
|
80
|
-
' 写诊断日志
|
|
81
|
-
On Error Resume Next
|
|
82
|
-
Dim f : Set f = FSO.OpenTextFile("${(process.env.TEMP || "/tmp").replace(/\\/g, "\\\\")}\\panrouter-vbs.log", 2, True)
|
|
83
|
-
f.WriteLine Now & " VBS started"
|
|
84
|
-
f.Close
|
|
85
|
-
' 启动 PS 托盘
|
|
86
|
-
WshShell.Run "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -STA -File """ & "${trayPsPath.replace(/\\/g, "\\\\")}" & """", 0, False
|
|
87
|
-
If Err.Number <> 0 Then
|
|
88
|
-
Set f = FSO.OpenTextFile("${(process.env.TEMP || "/tmp").replace(/\\/g, "\\\\")}\\panrouter-vbs.log", 8, True)
|
|
89
|
-
f.WriteLine Now & " ERROR: " & Err.Description
|
|
90
|
-
f.Close
|
|
91
|
-
End If
|
|
92
|
-
`;
|
|
93
|
-
const vbsPath = path.join(process.env.TEMP || "/tmp", "panrouter-tray-launcher.vbs");
|
|
94
|
-
try {
|
|
95
|
-
fs.writeFileSync(vbsPath, vbsContent, "utf8");
|
|
96
|
-
log(`VBS written: ${vbsPath}`);
|
|
97
|
-
} catch (e) {
|
|
98
|
-
log(`VBS write FAILED: ${e.message}`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
log(`VBS content:\n${vbsContent}`);
|
|
102
|
-
|
|
103
|
-
// 先试 wscript //B
|
|
104
|
-
try {
|
|
105
|
-
const vbs = spawn("wscript.exe", ["//B", "//NoLogo", vbsPath], {
|
|
106
|
-
stdio: "ignore", windowsHide: true, shell: false,
|
|
107
|
-
});
|
|
108
|
-
vbs.unref();
|
|
109
|
-
log(`VBS spawned via wscript`);
|
|
110
|
-
} catch (e) {
|
|
111
|
-
log(`VBS via wscript FAILED: ${e.message}`);
|
|
112
|
-
}
|
|
113
|
-
} else {
|
|
114
|
-
log(`WARN: tray-daemon.ps1 NOT FOUND at ${trayPsPath}`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// ─── 5. 保持存活 ────────────────────────────────
|
|
118
|
-
log("Daemon running");
|
|
119
|
-
process.stdin.resume();
|
|
120
|
-
|
|
121
|
-
setInterval(() => {
|
|
122
|
-
isOnline().then(ok => {
|
|
123
|
-
if (!ok) {
|
|
124
|
-
log("Server offline, restarting...");
|
|
125
|
-
const s2 = spawn(nodeExe, [serverPath], {
|
|
126
|
-
cwd: __dirname, stdio: "ignore", windowsHide: true, detached: true, shell: false,
|
|
127
|
-
});
|
|
128
|
-
s2.unref();
|
|
129
|
-
log(`Server restarted PID=${s2.pid || "??"}`);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
}, 30000);
|
|
133
|
-
})();
|