panrouter 1.7.2 → 1.9.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 +11 -16
- package/package.json +3 -3
- package/panrouter-tray.vbs +13 -0
- package/tray-daemon.ps1 +88 -145
- 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
|
+
// 直接调用 wscript,去掉多余的 cmd.exe 嵌套层级,更稳定
|
|
154
|
+
const child = spawn("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
|
// ─── 主流程 ──────────────────────────────────────────────────────────────
|
|
@@ -198,9 +192,10 @@ async function main() {
|
|
|
198
192
|
|
|
199
193
|
\x1b[33m托盘模式:\x1b[0m
|
|
200
194
|
在系统通知区(右下角)显示图标,右键菜单:
|
|
201
|
-
-
|
|
202
|
-
-
|
|
203
|
-
|
|
195
|
+
- 启动/重启服务
|
|
196
|
+
- 开关开机自启动
|
|
197
|
+
- 退出关闭服务器和托盘
|
|
198
|
+
左键点击图标显示菜单
|
|
204
199
|
|
|
205
200
|
\x1b[33m配置:\x1b[0m
|
|
206
201
|
代理运行在 http://127.0.0.1:50816
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "panrouter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.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,44 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
Pan Router
|
|
3
|
+
Pan Router 托盘守护脚本
|
|
4
|
+
去除了导致 UI 假死的同步网络请求,增加了稳定的重试和容错机制。
|
|
5
|
+
#>
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- NotifyIcon (右下角)
|
|
8
|
-
- 右键菜单 (开机自启动 / 退出)
|
|
9
|
-
- 健康检查 + 自动重启
|
|
7
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
8
|
+
Add-Type -AssemblyName System.Drawing
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
#>
|
|
10
|
+
# 抑制全局报错弹窗,防止后台服务因为意外报错直接死掉
|
|
11
|
+
$ErrorActionPreference = "SilentlyContinue"
|
|
14
12
|
|
|
15
|
-
$
|
|
13
|
+
$scriptPath = $MyInvocation.MyCommand.Path
|
|
14
|
+
$scriptDir = Split-Path $scriptPath -Parent
|
|
16
15
|
$serverPath = Join-Path $scriptDir "server.mjs"
|
|
17
|
-
$
|
|
18
|
-
$autostartName = "PanRouter"
|
|
16
|
+
$nodePath = (Get-Command node -ErrorAction SilentlyContinue).Source
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
# 如果系统环境没装 Node,直接静默退出
|
|
19
|
+
if (-not $nodePath) { Exit }
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
# ─── 核心功能:杀旧启新 ──────────────────────────────
|
|
22
|
+
function Restart-Server {
|
|
23
|
+
# 强制清理旧的代理进程
|
|
24
|
+
try {
|
|
25
|
+
wmic process where "name='node.exe'" get ProcessId,CommandLine /format:csv 2>$null | ForEach-Object {
|
|
26
|
+
if ($_ -match '(\d+),.*?server\.mjs') {
|
|
27
|
+
Stop-Process -Id $Matches[1] -Force -ErrorAction SilentlyContinue
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} catch {}
|
|
31
|
+
|
|
32
|
+
# 无黑框启动新进程
|
|
33
|
+
try {
|
|
34
|
+
Start-Process -FilePath $nodePath -ArgumentList "`"$serverPath`"" -WorkingDirectory $scriptDir -WindowStyle Hidden -PassThru -NoNewWindow | Out-Null
|
|
35
|
+
} catch {}
|
|
36
|
+
}
|
|
25
37
|
|
|
26
|
-
#
|
|
38
|
+
# 启动时立刻执行一次
|
|
39
|
+
Restart-Server
|
|
40
|
+
|
|
41
|
+
# ─── 绘制托盘图标 ────────────────────────────────────────
|
|
27
42
|
$bmp = New-Object System.Drawing.Bitmap(16, 16)
|
|
28
43
|
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
29
44
|
$g.SmoothingMode = 'HighQuality'
|
|
@@ -33,158 +48,86 @@ $g.FillEllipse($brush, 0, 0, 15, 15)
|
|
|
33
48
|
$font = New-Object System.Drawing.Font("Segoe UI", 8.5, [System.Drawing.FontStyle]::Bold)
|
|
34
49
|
$fg = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::White)
|
|
35
50
|
$g.DrawString("P", $font, $fg, 3, 1.5)
|
|
36
|
-
|
|
51
|
+
|
|
37
52
|
$hIcon = $bmp.GetHicon()
|
|
38
53
|
$icon = [System.Drawing.Icon]::FromHandle($hIcon)
|
|
39
|
-
$bmp.Dispose()
|
|
40
54
|
|
|
41
|
-
# ───
|
|
55
|
+
# ─── 初始化托盘对象 ──────────────────────────────────
|
|
42
56
|
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
43
57
|
$notifyIcon.Icon = $icon
|
|
44
|
-
$notifyIcon.Text = "Pan Router
|
|
58
|
+
$notifyIcon.Text = "Pan Router (端口: 50816)"
|
|
45
59
|
|
|
46
|
-
# ─── 右键菜单 (PS5.1 用 .GetNewClosure()) ─────────
|
|
47
60
|
$menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
48
61
|
|
|
49
|
-
|
|
50
|
-
$titleItem
|
|
62
|
+
# 标题 (不可点)
|
|
63
|
+
$titleItem = New-Object System.Windows.Forms.ToolStripMenuItem("Pan Router | :50816")
|
|
51
64
|
$titleItem.Enabled = $false
|
|
52
65
|
$titleItem.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
|
|
53
|
-
|
|
54
|
-
|
|
66
|
+
$menu.Items.Add($titleItem) | Out-Null
|
|
67
|
+
$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator)) | Out-Null
|
|
68
|
+
|
|
69
|
+
# 重启服务选项
|
|
70
|
+
$restartItem = New-Object System.Windows.Forms.ToolStripMenuItem("重启后台服务")
|
|
71
|
+
$restartItem.Add_Click({
|
|
72
|
+
Restart-Server
|
|
73
|
+
$notifyIcon.ShowBalloonTip(2000, "Pan Router", "后台代理服务已重新启动 ✓", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
74
|
+
})
|
|
75
|
+
$menu.Items.Add($restartItem) | Out-Null
|
|
76
|
+
|
|
77
|
+
# 开机自启动选项
|
|
78
|
+
$autoItem = New-Object System.Windows.Forms.ToolStripMenuItem("开机自启动")
|
|
79
|
+
try {
|
|
80
|
+
$regKey = Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name PanRouter -ErrorAction SilentlyContinue
|
|
81
|
+
$autoItem.Checked = ($regKey -ne $null)
|
|
82
|
+
} catch {}
|
|
55
83
|
|
|
56
|
-
$autoItem = New-Object System.Windows.Forms.ToolStripMenuItem
|
|
57
|
-
$autoItem.Text = "开机自启动"
|
|
58
|
-
$autoItem.Checked = Get-Autostart
|
|
59
84
|
$autoItem.Add_Click({
|
|
60
|
-
$
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Set-Autostart $false; $ai.Checked = $false
|
|
64
|
-
$ni.ShowBalloonTip(2000, "Pan Router", "开机自启动已关闭", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
85
|
+
if ($autoItem.Checked) {
|
|
86
|
+
reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /f 2>&1 | Out-Null
|
|
87
|
+
$autoItem.Checked = $false
|
|
65
88
|
} else {
|
|
66
|
-
|
|
67
|
-
$
|
|
89
|
+
$vbsPath = Join-Path $scriptDir "panrouter-tray.vbs"
|
|
90
|
+
$cmd = "wscript.exe //B //NoLogo `"$vbsPath`""
|
|
91
|
+
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /t REG_SZ /d $cmd /f 2>&1 | Out-Null
|
|
92
|
+
$autoItem.Checked = $true
|
|
68
93
|
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
94
|
+
})
|
|
95
|
+
$menu.Items.Add($autoItem) | Out-Null
|
|
96
|
+
$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator)) | Out-Null
|
|
72
97
|
|
|
73
|
-
|
|
74
|
-
$exitItem
|
|
98
|
+
# 退出选项
|
|
99
|
+
$exitItem = New-Object System.Windows.Forms.ToolStripMenuItem("退出")
|
|
75
100
|
$exitItem.Add_Click({
|
|
76
|
-
Write-Log "Exit clicked"
|
|
77
101
|
$notifyIcon.Visible = $false
|
|
102
|
+
try { wmic process where "name='node.exe'" get ProcessId,CommandLine /format:csv 2>$null | ForEach-Object { if ($_ -match '(\d+),.*?server\.mjs') { Stop-Process -Id $Matches[1] -Force -ErrorAction SilentlyContinue } } } catch {}
|
|
78
103
|
[System.Windows.Forms.Application]::Exit()
|
|
79
|
-
}
|
|
80
|
-
|
|
104
|
+
})
|
|
105
|
+
$menu.Items.Add($exitItem) | Out-Null
|
|
81
106
|
|
|
82
107
|
$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
108
|
|
|
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
|
-
# 左键: 状态
|
|
109
|
+
# ─── 优化左键操作 ────────────────────────────────────
|
|
155
110
|
$notifyIcon.Add_MouseClick({
|
|
156
111
|
if ($_.Button -eq [System.Windows.Forms.MouseButtons]::Left) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
# 健康检查 (30秒)
|
|
163
|
-
$healthTimer = New-Object System.Windows.Forms.Timer
|
|
164
|
-
$healthTimer.Interval = 30000
|
|
165
|
-
$healthTimer.Add_Tick({
|
|
166
|
-
if (-not (Test-Online)) {
|
|
167
|
-
Write-Log "Health FAILED, restarting..."
|
|
168
|
-
Start-Server
|
|
169
|
-
Start-Sleep -Seconds 3
|
|
170
|
-
if (Test-Online) { $notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器已自动重启", [System.Windows.Forms.ToolTipIcon]::Info) }
|
|
112
|
+
# 利用反射直接呼出右键菜单,取消容易卡死的网络检查
|
|
113
|
+
$mi = [System.Windows.Forms.NotifyIcon].GetMethod("ShowContextMenu", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Instance)
|
|
114
|
+
$mi.Invoke($notifyIcon, $null)
|
|
171
115
|
}
|
|
172
116
|
})
|
|
173
|
-
$healthTimer.Start()
|
|
174
117
|
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
Write-Log "Cleanup"
|
|
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
|
-
}
|
|
118
|
+
# ─── 启动与保持运行 ──────────────────────────────────
|
|
119
|
+
$notifyIcon.Visible = $true
|
|
187
120
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
121
|
+
# 使用 ApplicationContext 保持消息循环,防止脚本在执行完后意外释放托盘图标
|
|
122
|
+
$appContext = New-Object System.Windows.Forms.ApplicationContext
|
|
123
|
+
[System.Windows.Forms.Application]::Run($appContext)
|
|
124
|
+
|
|
125
|
+
# 清理资源
|
|
126
|
+
$notifyIcon.Visible = $false
|
|
127
|
+
$notifyIcon.Dispose()
|
|
128
|
+
$font.Dispose()
|
|
129
|
+
$fg.Dispose()
|
|
130
|
+
$brush.Dispose()
|
|
131
|
+
$g.Dispose()
|
|
132
|
+
[System.Runtime.InteropServices.Marshal]::DestroyIcon($hIcon)
|
|
133
|
+
$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
|
-
})();
|