panrouter 1.7.1 → 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 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
+ // 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,9 @@ 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(" 图标在右下角, 右键可退出");
165
+ }
171
166
 
172
167
  // ─── 主流程 ──────────────────────────────────────────────────────────────
173
168
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panrouter",
3
- "version": "1.7.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
- $scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
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
- $autostartName = "PanRouter"
19
-
20
- function Write-Log($m) { "$(Get-Date -Format 'HH:mm:ss') $m" | Out-File -Append -Encoding utf8 $logFile }
21
- Write-Log "=== PanRouter Tray ==="
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
- # ─── 右键菜单 (PS5.1 用 .GetNewClosure()) ─────────
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.Text = "开机自启动"
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
- $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)
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
- Set-Autostart $true; $ai.Checked = $true
67
- $ni.ShowBalloonTip(2000, "Pan Router", "开机自启动已开启 ✓", [System.Windows.Forms.ToolTipIcon]::Info)
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
- 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
- # 左键: 状态
93
+ # ─── 左键 ────────────────────────────────────────
155
94
  $notifyIcon.Add_MouseClick({
156
95
  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) }
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
- # 健康检查 (30秒)
101
+ # ─── 健康检查 ────────────────────────────────────
163
102
  $healthTimer = New-Object System.Windows.Forms.Timer
164
103
  $healthTimer.Interval = 30000
165
104
  $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) }
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
- [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
- }
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
- Write-Log "Exited"
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
- })();