panrouter 3.1.0 → 3.3.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.
Files changed (3) hide show
  1. package/cli.mjs +21 -8
  2. package/package.json +1 -1
  3. package/tray-daemon.ps1 +120 -95
package/cli.mjs CHANGED
@@ -91,7 +91,6 @@ async function startTray() {
91
91
  const psPath = path.join(__dirname, "tray-daemon.ps1");
92
92
  log("..", "正在后台启动代理...", "yellow");
93
93
 
94
- // 1. 由主程序直接启动 Node 代理,彻底杜绝 PowerShell 启动慢导致的超时
95
94
  try {
96
95
  if (process.platform === "win32") {
97
96
  execSync('taskkill /f /fi "WINDOWTITLE eq Pan Router*" >nul 2>&1', { stdio: "pipe" });
@@ -106,7 +105,6 @@ async function startTray() {
106
105
  });
107
106
  srv.unref();
108
107
 
109
- // 2. 验证端口(由于是直接用 Node 启动,这步通常 1 秒内就会通过)
110
108
  let ok = false;
111
109
  for (let i = 0; i < 15; i++) {
112
110
  if (await isPortOpen()) {
@@ -122,23 +120,38 @@ async function startTray() {
122
120
  log("!!", "服务启动超时,但仍将尝试加载托盘", "red");
123
121
  }
124
122
 
125
- // 3. 独立拉起系统托盘
126
123
  log("..", "正在加载系统托盘...", "yellow");
124
+
125
+ // 【核心修复】:增加 -STA 参数防止 UI 线程崩溃,增加 shell:true 确保一定能找到 powershell
127
126
  const tray = spawn("powershell.exe", [
128
127
  "-NoProfile",
128
+ "-STA",
129
129
  "-ExecutionPolicy", "Bypass",
130
130
  "-WindowStyle", "Hidden",
131
- "-File", psPath
131
+ "-File", `"${psPath}"`
132
132
  ], {
133
133
  cwd: __dirname,
134
- stdio: "ignore",
134
+ stdio: ['ignore', 'pipe', 'pipe'],
135
135
  windowsHide: true,
136
- detached: true,
136
+ shell: true,
137
137
  env: { ...process.env, PANROUTER_NODE: process.execPath }
138
138
  });
139
- tray.unref();
140
139
 
141
- console.log(" API 代理已立即可用。托盘图标稍后将在右下角显示。");
140
+ let psOutput = "";
141
+ tray.stdout.on("data", d => psOutput += d.toString());
142
+ tray.stderr.on("data", d => psOutput += d.toString());
143
+
144
+ // 监控 2.5 秒,如果闪退,直接打印错误
145
+ await new Promise(rs => setTimeout(rs, 2500));
146
+
147
+ if (tray.exitCode !== null) {
148
+ log("!!", "托盘进程未能驻留,发生闪退!", "red");
149
+ console.log(`\n\x1b[31m=== PowerShell 启动失败原因 ===\x1b[0m\n${psOutput || "(无报错输出,可能是被杀毒软件静默拦截)"}\n\x1b[31m===============================\x1b[0m\n`);
150
+ console.log("提示:如果代理可用,你可以暂时无视此错误。");
151
+ } else {
152
+ tray.unref();
153
+ console.log(" 托盘图标应该已在右下角显示。");
154
+ }
142
155
  }
143
156
 
144
157
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panrouter",
3
- "version": "3.1.0",
3
+ "version": "3.3.0",
4
4
  "description": "让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key",
5
5
  "type": "module",
6
6
  "bin": {
package/tray-daemon.ps1 CHANGED
@@ -1,107 +1,132 @@
1
1
  <#
2
2
  .SYNOPSIS
3
- Pan Router 托盘守护脚本 (极简直连版)
3
+ Pan Router 托盘守护脚本 (终极原生挂载版 + 日志诊断)
4
4
  #>
5
- $ErrorActionPreference = "SilentlyContinue"
5
+ $ErrorActionPreference = "Stop" # 遇到错误直接抛出,由 try-catch 捕获写日志
6
6
 
7
- Add-Type -AssemblyName System.Windows.Forms
8
- Add-Type -AssemblyName System.Drawing
9
- [System.Windows.Forms.Application]::EnableVisualStyles()
7
+ $logFile = "$env:TEMP\panrouter_tray.log"
8
+ "--- $(Get-Date -Format 'HH:mm:ss') 托盘脚本启动 ---" | Out-File $logFile
10
9
 
11
- $scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
12
- $serverPath = Join-Path $scriptDir "server.mjs"
13
-
14
- # 拿到 Node 路径
15
- $nodePath = $env:PANROUTER_NODE
16
- if ([string]::IsNullOrWhiteSpace($nodePath)) { $nodePath = "node" }
17
-
18
- # ─── 管理后台代理 ──────────────────────────────────────────
19
- function Start-Backend {
20
- # 杀掉旧代理
21
- Get-WmiObject Win32_Process -Filter "Name = 'node.exe'" |
22
- Where-Object CommandLine -match "server\.mjs" |
23
- ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
24
-
25
- # 启动新代理
26
- Start-Process -FilePath $nodePath -ArgumentList "`"$serverPath`"" -WorkingDirectory $scriptDir -WindowStyle Hidden
27
- }
28
-
29
- # 【核心修复】:不要盲目重启!检查端口如果已经通了,直接跳过耗时的杀进程步骤
30
- $portOpen = $false
31
- try {
32
- $tcp = New-Object System.Net.Sockets.TcpClient
33
- $ar = $tcp.BeginConnect("127.0.0.1", 50816, $null, $null)
34
- $portOpen = $ar.AsyncWaitHandle.WaitOne(500, $false)
35
- $tcp.Close()
36
- } catch {}
37
-
38
- if (-not $portOpen) {
39
- Start-Backend
40
- }
41
-
42
- # ─── 初始化托盘 ────────────────────────────────────────────
43
- $notifyIcon = New-Object System.Windows.Forms.NotifyIcon
44
- $notifyIcon.Text = "Pan Router (:50816)"
45
-
46
- # 绘图报错是导致图标出不来的元凶之一,这里做强兜底
47
10
  try {
48
- $bmp = New-Object System.Drawing.Bitmap(16, 16)
49
- $g = [System.Drawing.Graphics]::FromImage($bmp)
50
- $g.Clear([System.Drawing.Color]::FromArgb(0, 120, 215))
51
- $font = New-Object System.Drawing.Font("Arial", 8, [System.Drawing.FontStyle]::Bold)
52
- $g.DrawString("P", $font, [System.Drawing.Brushes]::White, 2, 1)
53
- $notifyIcon.Icon = [System.Drawing.Icon]::FromHandle($bmp.GetHicon())
54
- } catch {
55
- # 终极兜底:如果上面的画图代码因为 GDI 环境被拦,直接用系统自带的安全盾牌图标
56
- $notifyIcon.Icon = [System.Drawing.SystemIcons]::Shield
57
- }
58
-
59
- # ─── 菜单配置 ──────────────────────────────────────────────
60
- $menu = New-Object System.Windows.Forms.ContextMenuStrip
61
-
62
- $titleItem = $menu.Items.Add("Pan Router | :50816")
63
- $titleItem.Enabled = $false
64
- $menu.Items.Add("-") | Out-Null
65
-
66
- $restartItem = $menu.Items.Add("重启服务")
67
- $restartItem.Add_Click({
68
- Start-Backend
69
- $notifyIcon.ShowBalloonTip(2000, "Pan Router", "服务已重启", [System.Windows.Forms.ToolTipIcon]::Info)
70
- })
71
-
72
- $autoItem = $menu.Items.Add("开机自启动")
73
- try { $autoItem.Checked = (Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name PanRouter) -ne $null } catch {}
74
- $autoItem.Add_Click({
75
- if ($autoItem.Checked) {
76
- reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /f 2>&1 | Out-Null
77
- $autoItem.Checked = $false
78
- } else {
79
- $cmd = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`""
80
- reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /t REG_SZ /d $cmd /f 2>&1 | Out-Null
81
- $autoItem.Checked = $true
11
+ Add-Type -AssemblyName System.Windows.Forms
12
+ Add-Type -AssemblyName System.Drawing
13
+ [System.Windows.Forms.Application]::EnableVisualStyles()
14
+ "1. .NET 库加载成功" | Out-File $logFile -Append
15
+
16
+ $scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
17
+ $serverPath = Join-Path $scriptDir "server.mjs"
18
+
19
+ # 拿到 Node 路径
20
+ $nodePath = $env:PANROUTER_NODE
21
+ if ([string]::IsNullOrWhiteSpace($nodePath)) { $nodePath = "node" }
22
+ "2. Node 路径: $nodePath" | Out-File $logFile -Append
23
+
24
+ # ─── 管理后台代理 ──────────────────────────────────────────
25
+ function Start-Backend {
26
+ "-> 执行重启后台服务逻辑" | Out-File $logFile -Append
27
+ Get-WmiObject Win32_Process -Filter "Name = 'node.exe'" |
28
+ Where-Object CommandLine -match "server\.mjs" |
29
+ ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
30
+
31
+ Start-Process -FilePath $nodePath -ArgumentList "`"$serverPath`"" -WorkingDirectory $scriptDir -WindowStyle Hidden
82
32
  }
83
- })
84
-
85
- $menu.Items.Add("-") | Out-Null
86
-
87
- $exitItem = $menu.Items.Add("退出")
88
- $exitItem.Add_Click({
89
- $notifyIcon.Visible = $false
90
- Get-WmiObject Win32_Process -Filter "Name = 'node.exe'" | Where-Object CommandLine -match "server\.mjs" | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
91
- [System.Windows.Forms.Application]::Exit()
92
- })
93
33
 
94
- $notifyIcon.ContextMenuStrip = $menu
34
+ # 检查端口
35
+ $portOpen = $false
36
+ try {
37
+ $tcp = New-Object System.Net.Sockets.TcpClient
38
+ $ar = $tcp.BeginConnect("127.0.0.1", 50816, $null, $null)
39
+ $portOpen = $ar.AsyncWaitHandle.WaitOne(500, $false)
40
+ $tcp.Close()
41
+ "3. 端口探测完成,已开放状态: $portOpen" | Out-File $logFile -Append
42
+ } catch {
43
+ "3. 端口探测报错 (忽略并继续): $($_.Exception.Message)" | Out-File $logFile -Append
44
+ }
95
45
 
96
- # 左键点击直接弹出原生菜单(去除了那些花里胡哨的反射代码)
97
- $notifyIcon.Add_MouseClick({
98
- if ($_.Button -eq [System.Windows.Forms.MouseButtons]::Left) {
99
- $notifyIcon.ContextMenuStrip.Show([System.Windows.Forms.Cursor]::Position)
46
+ if (-not $portOpen) {
47
+ Start-Backend
100
48
  }
101
- })
102
49
 
103
- $notifyIcon.Visible = $true
50
+ # ─── 初始化托盘 ────────────────────────────────────────────
51
+ $notifyIcon = New-Object System.Windows.Forms.NotifyIcon
52
+ $notifyIcon.Text = "Pan Router (:50816)"
53
+
54
+ # 图标兜底处理
55
+ try {
56
+ $bmp = New-Object System.Drawing.Bitmap(16, 16)
57
+ $g = [System.Drawing.Graphics]::FromImage($bmp)
58
+ $g.Clear([System.Drawing.Color]::FromArgb(0, 120, 215))
59
+ $font = New-Object System.Drawing.Font("Arial", 8, [System.Drawing.FontStyle]::Bold)
60
+ $g.DrawString("P", $font, [System.Drawing.Brushes]::White, 2, 1)
61
+ $notifyIcon.Icon = [System.Drawing.Icon]::FromHandle($bmp.GetHicon())
62
+ "4. 自定义图标绘制成功" | Out-File $logFile -Append
63
+ } catch {
64
+ "4. 画图异常,改用原生盾牌图标: $($_.Exception.Message)" | Out-File $logFile -Append
65
+ $notifyIcon.Icon = [System.Drawing.SystemIcons]::Shield
66
+ }
104
67
 
105
- # 保持进程存活的最纯粹方式
106
- $ctx = New-Object System.Windows.Forms.ApplicationContext
107
- [System.Windows.Forms.Application]::Run($ctx)
68
+ # ─── 原生菜单配置 (ContextMenu) ────────────────────────────
69
+ # 【核心修复】:使用原生的 ContextMenu 代替极易失去焦点的 ContextMenuStrip
70
+ $menu = New-Object System.Windows.Forms.ContextMenu
71
+
72
+ $titleItem = New-Object System.Windows.Forms.MenuItem("Pan Router | :50816")
73
+ $titleItem.Enabled = $false
74
+ $menu.MenuItems.Add($titleItem) | Out-Null
75
+
76
+ $menu.MenuItems.Add("-") | Out-Null
77
+
78
+ $restartItem = New-Object System.Windows.Forms.MenuItem("重启服务")
79
+ $restartItem.Add_Click({
80
+ Start-Backend
81
+ $notifyIcon.ShowBalloonTip(2000, "Pan Router", "服务已重启", [System.Windows.Forms.ToolTipIcon]::Info)
82
+ })
83
+ $menu.MenuItems.Add($restartItem) | Out-Null
84
+
85
+ $autoItem = New-Object System.Windows.Forms.MenuItem("开机自启动")
86
+ try { $autoItem.Checked = (Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name PanRouter -ErrorAction SilentlyContinue) -ne $null } catch {}
87
+ $autoItem.Add_Click({
88
+ if ($autoItem.Checked) {
89
+ reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /f 2>&1 | Out-Null
90
+ $autoItem.Checked = $false
91
+ } else {
92
+ $cmd = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`""
93
+ reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /t REG_SZ /d $cmd /f 2>&1 | Out-Null
94
+ $autoItem.Checked = $true
95
+ }
96
+ })
97
+ $menu.MenuItems.Add($autoItem) | Out-Null
98
+
99
+ $menu.MenuItems.Add("-") | Out-Null
100
+
101
+ $exitItem = New-Object System.Windows.Forms.MenuItem("退出")
102
+ $exitItem.Add_Click({
103
+ $notifyIcon.Visible = $false
104
+ "-> 执行退出清理" | Out-File $logFile -Append
105
+ Get-WmiObject Win32_Process -Filter "Name = 'node.exe'" | Where-Object CommandLine -match "server\.mjs" | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
106
+ [System.Windows.Forms.Application]::Exit()
107
+ })
108
+ $menu.MenuItems.Add($exitItem) | Out-Null
109
+
110
+ $notifyIcon.ContextMenu = $menu
111
+ $notifyIcon.Visible = $true
112
+ "5. 托盘图标已绑定并设置为可见" | Out-File $logFile -Append
113
+
114
+ # ─── 隐形实体主窗口 ─────────────────────────────────────────
115
+ # 【核心修复】:挂载真实的 Form 窗口,防止 Windows 将该 PowerShell UI 线程当做垃圾回收掉
116
+ $form = New-Object System.Windows.Forms.Form
117
+ $form.ShowInTaskbar = $false
118
+ $form.WindowState = [System.Windows.Forms.FormWindowState]::Minimized
119
+ $form.Opacity = 0
120
+ $form.Add_Load({
121
+ $form.Hide()
122
+ "6. 实体窗口初始化完毕,正式进入消息挂起循环..." | Out-File $logFile -Append
123
+ })
124
+
125
+ [System.Windows.Forms.Application]::Run($form)
126
+
127
+ "7. 程序正常退出" | Out-File $logFile -Append
128
+ } catch {
129
+ "【致命报错】无法完成执行:" | Out-File $logFile -Append
130
+ $_.Exception.Message | Out-File $logFile -Append
131
+ $_.Exception.StackTrace | Out-File $logFile -Append
132
+ }