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 CHANGED
@@ -141,16 +141,17 @@ async function startServer() {
141
141
  // ─── 4. 以托盘模式启动 ──────────────────────────────────────────────────
142
142
 
143
143
  function startTray() {
144
- const daemonPath = path.join(__dirname, "daemon.mjs");
144
+ const vbsPath = path.join(__dirname, "panrouter-tray.vbs");
145
145
 
146
- if (!fs.existsSync(daemonPath)) {
147
- log("!!", "未找到 daemon.mjs", "red");
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
- const child = spawn(process.execPath, [daemonPath], {
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 托盘服务已启动", "green");
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.7.2",
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
- panrouter-tray.vbs 启动。
6
- 服务器已经由 VBS 启动, PS 只负责:
7
- - NotifyIcon (右下角)
8
- - 右键菜单 (开机自启动 / 退出)
9
- - 健康检查 + 自动重启
7
+ Add-Type -AssemblyName System.Windows.Forms
8
+ Add-Type -AssemblyName System.Drawing
10
9
 
11
- 用法:
12
- powershell -ExecutionPolicy Bypass -WindowStyle Hidden -STA -File tray-daemon.ps1
13
- #>
10
+ # 抑制全局报错弹窗,防止后台服务因为意外报错直接死掉
11
+ $ErrorActionPreference = "SilentlyContinue"
14
12
 
15
- $scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
13
+ $scriptPath = $MyInvocation.MyCommand.Path
14
+ $scriptDir = Split-Path $scriptPath -Parent
16
15
  $serverPath = Join-Path $scriptDir "server.mjs"
17
- $logFile = "$env:TEMP\panrouter-tray.log"
18
- $autostartName = "PanRouter"
16
+ $nodePath = (Get-Command node -ErrorAction SilentlyContinue).Source
19
17
 
20
- function Write-Log($m) { "$(Get-Date -Format 'HH:mm:ss') $m" | Out-File -Append -Encoding utf8 $logFile }
21
- Write-Log "=== PanRouter Tray ==="
18
+ # 如果系统环境没装 Node,直接静默退出
19
+ if (-not $nodePath) { Exit }
22
20
 
23
- Add-Type -AssemblyName System.Windows.Forms
24
- Add-Type -AssemblyName System.Drawing
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
- $font.Dispose(); $fg.Dispose(); $brush.Dispose(); $g.Dispose()
51
+
37
52
  $hIcon = $bmp.GetHicon()
38
53
  $icon = [System.Drawing.Icon]::FromHandle($hIcon)
39
- $bmp.Dispose()
40
54
 
41
- # ─── NotifyIcon ────────────────────────────────────
55
+ # ─── 初始化托盘对象 ──────────────────────────────────
42
56
  $notifyIcon = New-Object System.Windows.Forms.NotifyIcon
43
57
  $notifyIcon.Icon = $icon
44
- $notifyIcon.Text = "Pan Router | 端口 50816"
58
+ $notifyIcon.Text = "Pan Router (端口: 50816)"
45
59
 
46
- # ─── 右键菜单 (PS5.1 用 .GetNewClosure()) ─────────
47
60
  $menu = New-Object System.Windows.Forms.ContextMenuStrip
48
61
 
49
- $titleItem = New-Object System.Windows.Forms.ToolStripMenuItem
50
- $titleItem.Text = "Pan Router - :50816"
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
- [void]$menu.Items.Add($titleItem)
54
- [void]$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator))
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
- $ni = $notifyIcon
61
- $ai = $autoItem
62
- if ($ai.Checked) {
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
- Set-Autostart $true; $ai.Checked = $true
67
- $ni.ShowBalloonTip(2000, "Pan Router", "开机自启动已开启 ", [System.Windows.Forms.ToolTipIcon]::Info)
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
- }.GetNewClosure())
70
- [void]$menu.Items.Add($autoItem)
71
- [void]$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator))
94
+ })
95
+ $menu.Items.Add($autoItem) | Out-Null
96
+ $menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator)) | Out-Null
72
97
 
73
- $exitItem = New-Object System.Windows.Forms.ToolStripMenuItem
74
- $exitItem.Text = "退出"
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
- }.GetNewClosure())
80
- [void]$menu.Items.Add($exitItem)
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
- function Start-Server {
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
- if (Test-Online) { $notifyIcon.ShowBalloonTip(2000, "Pan Router", "运行正常 ✓ (端口 50816)", [System.Windows.Forms.ToolTipIcon]::Info) }
158
- else { $notifyIcon.ShowBalloonTip(2000, "Pan Router", "服务器未响应 ⚠", [System.Windows.Forms.ToolTipIcon]::Error) }
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
- [System.Windows.Forms.Application]::ApplicationExit += {
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
- Write-Log "Message loop"
189
- [System.Windows.Forms.Application]::Run()
190
- Write-Log "Exited"
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
- })();