panrouter 1.1.0 → 1.1.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/tray-manager.ps1 +179 -193
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panrouter",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key",
5
5
  "type": "module",
6
6
  "bin": {
package/tray-manager.ps1 CHANGED
@@ -6,232 +6,218 @@
6
6
  右键菜单: 状态, 开机自启动开关, 退出
7
7
 
8
8
  用法:
9
- powershell -STA -File tray-manager.ps1 -ServerPath "server.mjs"
9
+ powershell -ExecutionPolicy Bypass -STA -File tray-manager.ps1 -ServerPath "server.mjs"
10
10
  #>
11
11
 
12
12
  param(
13
13
  [string]$ServerPath
14
14
  )
15
15
 
16
- # ─── STA 检查 ──────────────────────────────────
17
- if ([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne "STA") {
18
- $r = [System.Windows.Forms.MessageBox]::Show(
19
- "Pan Router 需要 STA 模式运行。`n是否自动以 STA 模式重新启动?",
20
- "Pan Router",
21
- "YesNo",
22
- "Warning"
23
- )
24
- if ($r -eq "Yes") {
25
- powershell -STA -File $MyInvocation.MyCommand.Path -ServerPath $ServerPath
26
- }
27
- exit 1
28
- }
16
+ # ─── 日志辅助 ──────────────────────────────────
17
+ $logFile = "$env:TEMP\panrouter-tray.log"
18
+ function Write-Log { param([string]$m) "$([DateTime]::Now.ToString('HH:mm:ss')) $m" | Out-File -Append -Encoding utf8 $logFile }
29
19
 
30
- Add-Type -AssemblyName System.Windows.Forms
31
- Add-Type -AssemblyName System.Drawing
20
+ try {
21
+ Write-Log "==== Pan Router Tray Started ===="
22
+ Write-Log "ServerPath: $ServerPath"
23
+ Write-Log "PSVersion: $($PSVersionTable.PSVersion)"
32
24
 
33
- # ─── 常量 ──────────────────────────────────────
34
- $scriptPath = $MyInvocation.MyCommand.Path
35
- $autostartName = "PanRouter"
36
- $runKeyPath = "Software\Microsoft\Windows\CurrentVersion\Run"
25
+ Add-Type -AssemblyName System.Windows.Forms
26
+ Add-Type -AssemblyName System.Drawing
37
27
 
38
- # npm 全局安装目录作为图标标题提示
39
- $pkgDir = Split-Path (Split-Path $ServerPath -Parent) -Parent
40
- $pkgName = Split-Path $pkgDir -Leaf
41
- $tooltipText = "Pan Router`n端口 50816 | 运行中"
28
+ # ─── 常量 ──────────────────────────────────
29
+ $scriptPath = $MyInvocation.MyCommand.Path
30
+ $autostartName = "PanRouter"
31
+ $runKeyPath = "Software\Microsoft\Windows\CurrentVersion\Run"
42
32
 
43
- # ─── 图标生成 ──────────────────────────────────
44
- function New-TrayIcon {
33
+ # ─── 生成图标 ──────────────────────────────
45
34
  $bmp = New-Object System.Drawing.Bitmap(16, 16)
46
35
  $g = [System.Drawing.Graphics]::FromImage($bmp)
47
36
  $g.SmoothingMode = 'HighQuality'
48
37
  $g.Clear([System.Drawing.Color]::Transparent)
49
-
50
- # 蓝色圆底
51
38
  $brush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(0, 120, 215))
52
39
  $g.FillEllipse($brush, 0, 0, 15, 15)
53
-
54
- # 白色 "P" 字
55
40
  $font = New-Object System.Drawing.Font("Segoe UI", 8.5, [System.Drawing.FontStyle]::Bold)
56
41
  $fg = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::White)
57
42
  $g.DrawString("P", $font, $fg, 3, 1.5)
58
- $font.Dispose()
59
- $fg.Dispose()
60
- $brush.Dispose()
61
- $g.Dispose()
62
-
43
+ $font.Dispose(); $fg.Dispose(); $brush.Dispose(); $g.Dispose()
63
44
  $hIcon = $bmp.GetHicon()
64
45
  $icon = [System.Drawing.Icon]::FromHandle($hIcon)
65
46
  $bmp.Dispose()
66
- return $icon, $hIcon
67
- }
47
+ Write-Log "Icon created"
48
+
49
+ # ─── NotifyIcon ────────────────────────────
50
+ $notifyIcon = New-Object System.Windows.Forms.NotifyIcon
51
+ $notifyIcon.Icon = $icon
52
+ $notifyIcon.Text = "Pan Router`n端口 50816 | 运行中"
53
+ $notifyIcon.Visible = $true
54
+ Write-Log "NotifyIcon created and visible"
55
+
56
+ # ─── 用 UseShellExecute 启动服务器(避免输出缓冲死锁) ──
57
+ function Start-ServerNohup {
58
+ param([string]$ServerPath)
59
+ $serverDir = Split-Path $ServerPath -Parent
60
+ try {
61
+ $psi = New-Object System.Diagnostics.ProcessStartInfo
62
+ $psi.FileName = "node"
63
+ $psi.Arguments = "`"$ServerPath`""
64
+ $psi.WorkingDirectory = $serverDir
65
+ $psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
66
+ $psi.CreateNoWindow = $true
67
+ # UseShellExecute=$true 避免重定向缓冲死锁
68
+ $psi.UseShellExecute = $true
69
+ $proc = [System.Diagnostics.Process]::Start($psi)
70
+ Write-Log "Server started PID=$($proc.Id)"
71
+ return $proc
72
+ } catch {
73
+ Write-Log "Server start FAILED: $_"
74
+ return $null
75
+ }
76
+ }
68
77
 
69
- # ─── 启动隐藏的 Node 服务器 ─────────────────────
70
- function Start-ServerHidden {
71
- param([string]$ServerPath)
72
-
73
- $serverDir = Split-Path $ServerPath -Parent
74
- $psi = New-Object System.Diagnostics.ProcessStartInfo
75
- $psi.FileName = "node"
76
- $psi.Arguments = "`"$ServerPath`""
77
- $psi.WorkingDirectory = $serverDir
78
- $psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
79
- $psi.CreateNoWindow = $true
80
- $psi.UseShellExecute = $false
81
- $psi.RedirectStandardOutput = $true
82
- $psi.RedirectStandardError = $true
83
-
84
- try {
85
- $proc = [System.Diagnostics.Process]::Start($psi)
86
- return $proc
87
- } catch {
88
- return $null
78
+ # ─── 杀掉旧进程 (wmic 兼容 PS5.1) ──────────
79
+ function Stop-OldServer {
80
+ try {
81
+ $old = Get-WmiObject Win32_Process -Filter "Name='node.exe'" -ErrorAction Stop |
82
+ Where-Object { $_.CommandLine -match "server\.mjs" }
83
+ foreach ($p in $old) {
84
+ Write-Log "Killing old server PID=$($p.ProcessId)"
85
+ Stop-Process -Id $p.ProcessId -Force -ErrorAction SilentlyContinue
86
+ }
87
+ } catch { Write-Log "Stop-OldServer: $_" }
89
88
  }
90
- }
91
89
 
92
- # ─── 检查服务器是否在线 ─────────────────────────
93
- function Test-ServerOnline {
94
- try {
95
- $req = [System.Net.HttpWebRequest]::Create("http://127.0.0.1:50816/health")
96
- $req.Method = "GET"
97
- $req.Timeout = 1500
98
- $resp = $req.GetResponse()
99
- $resp.Close()
100
- return $true
101
- } catch {
102
- return $false
90
+ # ─── 健康检查 ──────────────────────────────
91
+ function Test-Online {
92
+ try {
93
+ $req = [System.Net.WebRequest]::Create("http://127.0.0.1:50816/health")
94
+ $req.Timeout = 1500
95
+ $resp = $req.GetResponse()
96
+ $resp.Close()
97
+ return $true
98
+ } catch { return $false }
103
99
  }
104
- }
105
100
 
106
- # ─── 杀掉旧 Pan Router 进程 (WMI 方式, 兼容 PS5.1) ──────────
107
- function Stop-OldServer {
108
- try {
109
- $existing = Get-CimInstance -ClassName Win32_Process -Filter "Name='node.exe'" -ErrorAction SilentlyContinue |
110
- Where-Object { $_.CommandLine -match "server\.mjs" }
111
- foreach ($p in $existing) {
112
- $pid = $p.ProcessId
113
- Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue
114
- }
115
- } catch {}
116
- }
101
+ # ─── 启动流程 ──────────────────────────────
102
+ Stop-OldServer
103
+ $serverProcess = Start-ServerNohup -ServerPath $ServerPath
117
104
 
118
- # ═══════════════════════════════════════════════
119
- # 主流程
120
- # ═══════════════════════════════════════════════
121
-
122
- # 1. 杀掉旧服务器
123
- Stop-OldServer
124
-
125
- # 2. 生成图标
126
- $icon, $hIcon = New-TrayIcon
127
-
128
- # 3. 创建 NotifyIcon
129
- $notifyIcon = New-Object System.Windows.Forms.NotifyIcon
130
- $notifyIcon.Icon = $icon
131
- $notifyIcon.Text = $tooltipText
132
- $notifyIcon.Visible = $true
133
-
134
- # 4. 启动隐藏服务器进程
135
- $serverProcess = Start-ServerHidden -ServerPath $ServerPath
136
-
137
- # 5. 等待服务器就绪(后台检查,不阻塞 UI)
138
- $checkTimer = New-Object System.Windows.Forms.Timer
139
- $checkTimer.Interval = 500
140
- $checkCount = 0
141
- $checkTimer.Add_Tick({
142
- $checkCount++
143
- if (Test-ServerOnline) {
144
- $checkTimer.Stop()
145
- $notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器已就绪 (端口 50816)", "Info")
146
- } elseif ($checkCount -ge 10) {
147
- $checkTimer.Stop()
148
- $notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器启动可能较慢,尝试访问中...", "Warning")
105
+ # 前台等待服务器就绪(超时 10 秒)
106
+ $ready = $false
107
+ for ($i = 0; $i -lt 20; $i++) {
108
+ Start-Sleep -Milliseconds 500
109
+ if (Test-Online) { $ready = $true; break }
149
110
  }
150
- })
151
- $checkTimer.Start()
152
-
153
- # 6. 左键点击: 弹气泡提示状态
154
- $notifyIcon.Add_MouseClick({
155
- param($sender, $e)
156
- if ($e.Button -eq [System.Windows.Forms.MouseButtons]::Left) {
157
- $online = Test-ServerOnline
158
- if ($online) {
159
- $notifyIcon.ShowBalloonTip(3000, "Pan Router", "运行正常 ✓ (端口 50816)", "Info")
160
- } else {
161
- $notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器未响应 ⚠", "Error")
162
- }
111
+ Write-Log "Server ready=$ready"
112
+ if ($ready) {
113
+ $notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器已就绪 (端口 50816)", [System.Windows.Forms.ToolTipIcon]::Info)
163
114
  }
164
- })
165
-
166
- # 7. 右键菜单
167
- $menu = New-Object System.Windows.Forms.ContextMenuStrip
168
-
169
- # 标题行
170
- $titleItem = New-Object System.Windows.Forms.ToolStripMenuItem
171
- $titleItem.Text = "Pan Router - :50816"
172
- $titleItem.Enabled = $false
173
- $titleItem.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
174
- $menu.Items.Add($titleItem)
175
- $menu.Items.Add("-")
176
-
177
- # 开机自启动
178
- $autoStartItem = New-Object System.Windows.Forms.ToolStripMenuItem
179
- $autoStartItem.Text = "开机自启动"
180
- $runKey = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey($runKeyPath, $true)
181
- $autoStartItem.Checked = ($runKey.GetValue($autostartName) -ne $null)
182
- $runKey.Close()
183
- $autoStartItem.Add_Click({
115
+
116
+ # ─── 后台定时健康检查 ──────────────────────
117
+ $healthTimer = New-Object System.Windows.Forms.Timer
118
+ $healthTimer.Interval = 30000
119
+ $healthTimer.Add_Tick({
120
+ if (-not (Test-Online)) {
121
+ Write-Log "Health check FAILED, restarting..."
122
+ Stop-OldServer
123
+ $script:serverProcess = Start-ServerNohup -ServerPath $ServerPath
124
+ Start-Sleep -Seconds 3
125
+ if (Test-Online) {
126
+ $notifyIcon.ShowBalloonTip(3000, "Pan Router", "服务器已自动重启", [System.Windows.Forms.ToolTipIcon]::Info)
127
+ }
128
+ }
129
+ })
130
+ $healthTimer.Start()
131
+ Write-Log "Health timer started"
132
+
133
+ # ─── 左键: 状态气泡 ────────────────────────
134
+ $notifyIcon.Add_MouseClick({
135
+ if ($_.Button -eq [System.Windows.Forms.MouseButtons]::Left) {
136
+ $online = Test-Online
137
+ if ($online) {
138
+ $notifyIcon.ShowBalloonTip(2000, "Pan Router", "运行正常 ✓ (端口 50816)", [System.Windows.Forms.ToolTipIcon]::Info)
139
+ } else {
140
+ $notifyIcon.ShowBalloonTip(2000, "Pan Router", "服务器未响应 ⚠", [System.Windows.Forms.ToolTipIcon]::Error)
141
+ }
142
+ }
143
+ })
144
+
145
+ # ─── 右键菜单 ──────────────────────────────
146
+ $menu = New-Object System.Windows.Forms.ContextMenuStrip
147
+
148
+ $titleItem = New-Object System.Windows.Forms.ToolStripMenuItem
149
+ $titleItem.Text = "Pan Router - :50816"
150
+ $titleItem.Enabled = $false
151
+ $titleItem.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
152
+ $menu.Items.Add($titleItem)
153
+ $menu.Items.Add("-")
154
+
155
+ $autoStartItem = New-Object System.Windows.Forms.ToolStripMenuItem
156
+ $autoStartItem.Text = "开机自启动"
184
157
  $rk = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey($runKeyPath, $true)
185
- if ($autoStartItem.Checked) {
186
- $rk.DeleteValue($autostartName, $false)
187
- $autoStartItem.Checked = $false
188
- $notifyIcon.ShowBalloonTip(2000, "Pan Router", "开机自启动已关闭", "Info")
189
- } else {
190
- $cmd = "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -STA -File `"$scriptPath`" -ServerPath `"$ServerPath`""
191
- $rk.SetValue($autostartName, $cmd)
192
- $autoStartItem.Checked = $true
193
- $notifyIcon.ShowBalloonTip(2000, "Pan Router", "开机自启动已开启 ✓", "Info")
194
- }
158
+ $autoStartItem.Checked = ($rk.GetValue($autostartName) -ne $null)
195
159
  $rk.Close()
196
- })
197
- $menu.Items.Add($autoStartItem)
198
-
199
- $menu.Items.Add("-")
200
-
201
- # 退出
202
- $exitItem = New-Object System.Windows.Forms.ToolStripMenuItem
203
- $exitItem.Text = "退出"
204
- $exitItem.Add_Click({
205
- $notifyIcon.Visible = $false
206
- [System.Windows.Forms.Application]::Exit()
207
- })
208
- $menu.Items.Add($exitItem)
209
-
210
- $notifyIcon.ContextMenuStrip = $menu
211
-
212
- # 8. 退出清理
213
- $cleanup = {
214
- try {
215
- if ($checkTimer) { $checkTimer.Stop(); $checkTimer.Dispose() }
216
- if ($serverProcess -and !$serverProcess.HasExited) {
217
- $serverProcess.Kill()
218
- $serverProcess.WaitForExit(3000)
219
- $serverProcess.Dispose()
160
+ $autoStartItem.Add_Click({
161
+ $rk2 = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey($runKeyPath, $true)
162
+ if ($autoStartItem.Checked) {
163
+ $rk2.DeleteValue($autostartName, $false)
164
+ $autoStartItem.Checked = $false
165
+ $notifyIcon.ShowBalloonTip(2000, "Pan Router", "开机自启动已关闭", [System.Windows.Forms.ToolTipIcon]::Info)
166
+ Write-Log "Autostart OFF"
167
+ } else {
168
+ $cmd = "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -STA -File `"$scriptPath`" -ServerPath `"$ServerPath`""
169
+ $rk2.SetValue($autostartName, $cmd)
170
+ $autoStartItem.Checked = $true
171
+ $notifyIcon.ShowBalloonTip(2000, "Pan Router", "开机自启动已开启 ✓", [System.Windows.Forms.ToolTipIcon]::Info)
172
+ Write-Log "Autostart ON"
220
173
  }
221
- # 再补一刀: 确保无残留
222
- Stop-OldServer
223
- } catch {}
224
- }
174
+ $rk2.Close()
175
+ })
176
+ $menu.Items.Add($autoStartItem)
177
+ $menu.Items.Add("-")
178
+
179
+ $exitItem = New-Object System.Windows.Forms.ToolStripMenuItem
180
+ $exitItem.Text = "退出"
181
+ $exitItem.Add_Click({
182
+ Write-Log "Exit clicked"
183
+ $notifyIcon.Visible = $false
184
+ [System.Windows.Forms.Application]::Exit()
185
+ })
186
+ $menu.Items.Add($exitItem)
187
+
188
+ $notifyIcon.ContextMenuStrip = $menu
189
+
190
+ # ─── 退出清理 ──────────────────────────────
191
+ $cleanup = {
192
+ try {
193
+ Write-Log "Cleanup started"
194
+ $healthTimer.Stop()
195
+ $healthTimer.Dispose()
196
+ if (-not $serverProcess.HasExited) {
197
+ $serverProcess.Kill()
198
+ $serverProcess.WaitForExit(3000)
199
+ $serverProcess.Dispose()
200
+ Write-Log "Server killed"
201
+ }
202
+ Stop-OldServer
203
+ Write-Log "Cleanup done"
204
+ } catch { Write-Log "Cleanup error: $_" }
205
+ }
225
206
 
226
- [System.Windows.Forms.Application]::ApplicationExit += {
227
- & $cleanup
228
- $notifyIcon.Dispose()
229
- [System.Runtime.InteropServices.Marshal]::DestroyIcon($hIcon)
230
- $icon.Dispose()
231
- }
207
+ [System.Windows.Forms.Application]::ApplicationExit += {
208
+ & $cleanup
209
+ $notifyIcon.Dispose()
210
+ [System.Runtime.InteropServices.Marshal]::DestroyIcon($hIcon)
211
+ $icon.Dispose()
212
+ }
232
213
 
233
- # 9. 运行消息循环
234
- [System.Windows.Forms.Application]::Run()
214
+ Write-Log "Entering message loop"
215
+ [System.Windows.Forms.Application]::Run()
216
+ Write-Log "Message loop exited"
217
+ & $cleanup
235
218
 
236
- # 10. 循环结束后再次清理
237
- & $cleanup
219
+ } catch {
220
+ $errMsg = "UNHANDLED ERROR: $_"
221
+ Write-Log $errMsg
222
+ try { [System.Windows.Forms.MessageBox]::Show($errMsg, "Pan Router Error") } catch {}
223
+ }