panrouter 2.0.1 → 3.1.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 +59 -101
- package/package.json +2 -3
- package/tray-daemon.ps1 +56 -115
- package/panrouter-tray.vbs +0 -13
package/cli.mjs
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Pan Router CLI — 一键安装 + 启动
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
3
|
import { execSync, spawn } from "node:child_process";
|
|
8
4
|
import http from "node:http";
|
|
9
5
|
import fs from "node:fs";
|
|
@@ -14,7 +10,6 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
14
10
|
const HOME = process.env.USERPROFILE || process.env.HOME;
|
|
15
11
|
const CLAUDE_DIR = path.join(HOME, ".claude");
|
|
16
12
|
const SETTINGS_PATH = path.join(CLAUDE_DIR, "settings.json");
|
|
17
|
-
const BACKUP_PATH = path.join(CLAUDE_DIR, "settings.json.panrouter.backup");
|
|
18
13
|
|
|
19
14
|
function log(label, msg, color = "") {
|
|
20
15
|
const colors = { green: "\x1b[32m", yellow: "\x1b[33m", red: "\x1b[31m", cyan: "\x1b[36m", reset: "\x1b[0m" };
|
|
@@ -22,46 +17,28 @@ function log(label, msg, color = "") {
|
|
|
22
17
|
console.log(`${c}[${label}]${colors.reset} ${msg}`);
|
|
23
18
|
}
|
|
24
19
|
|
|
25
|
-
function run(cmd) {
|
|
26
|
-
try {
|
|
27
|
-
execSync(cmd, { stdio: "inherit" });
|
|
28
|
-
return true;
|
|
29
|
-
} catch {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function runSilent(cmd) {
|
|
35
|
-
try {
|
|
36
|
-
execSync(cmd, { stdio: "pipe" });
|
|
37
|
-
return true;
|
|
38
|
-
} catch {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ─── 1. 安装与配置 ──────────────────────────────────────────
|
|
44
|
-
|
|
45
20
|
function installClaudeCode() {
|
|
46
21
|
log("..", "正在检查 Claude Code...", "yellow");
|
|
47
|
-
|
|
22
|
+
try {
|
|
23
|
+
execSync("claude --version", { stdio: "pipe" });
|
|
48
24
|
log("OK", "Claude Code 已就绪", "green");
|
|
49
25
|
return true;
|
|
26
|
+
} catch {
|
|
27
|
+
log("..", "正在安装 Claude Code...", "yellow");
|
|
28
|
+
try {
|
|
29
|
+
execSync("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
|
|
30
|
+
log("OK", "Claude Code 安装成功", "green");
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
log("!!", "Claude Code 安装失败", "red");
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
50
36
|
}
|
|
51
|
-
log("..", "正在安装 Claude Code...", "yellow");
|
|
52
|
-
if (!run("npm install -g @anthropic-ai/claude-code")) {
|
|
53
|
-
log("!!", "Claude Code 安装失败", "red");
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
log("OK", "Claude Code 安装成功", "green");
|
|
57
|
-
return true;
|
|
58
37
|
}
|
|
59
38
|
|
|
60
39
|
function writeConfig() {
|
|
61
|
-
log("..", "
|
|
40
|
+
log("..", "正在配置 Claude Code 路由...", "yellow");
|
|
62
41
|
if (!fs.existsSync(CLAUDE_DIR)) fs.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
63
|
-
if (fs.existsSync(SETTINGS_PATH)) fs.copyFileSync(SETTINGS_PATH, BACKUP_PATH);
|
|
64
|
-
|
|
65
42
|
const config = {
|
|
66
43
|
env: {
|
|
67
44
|
ANTHROPIC_BASE_URL: "http://127.0.0.1:50816",
|
|
@@ -73,7 +50,7 @@ function writeConfig() {
|
|
|
73
50
|
hasCompletedOnboarding: true,
|
|
74
51
|
};
|
|
75
52
|
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
76
|
-
log("OK", "
|
|
53
|
+
log("OK", "配置完成", "green");
|
|
77
54
|
}
|
|
78
55
|
|
|
79
56
|
async function isPortOpen() {
|
|
@@ -85,8 +62,6 @@ async function isPortOpen() {
|
|
|
85
62
|
});
|
|
86
63
|
}
|
|
87
64
|
|
|
88
|
-
// ─── 2. 启动服务(前台黑框) ────────────────────────────────
|
|
89
|
-
|
|
90
65
|
async function startServer() {
|
|
91
66
|
const serverPath = path.join(__dirname, "server.mjs");
|
|
92
67
|
try {
|
|
@@ -97,111 +72,94 @@ async function startServer() {
|
|
|
97
72
|
}
|
|
98
73
|
} catch {}
|
|
99
74
|
|
|
100
|
-
log("..", "
|
|
75
|
+
log("..", "正在启动代理...", "yellow");
|
|
101
76
|
if (process.platform === "win32") {
|
|
102
77
|
execSync(`start "Pan Router" cmd /c "node ${serverPath} & pause"`, { stdio: "pipe" });
|
|
103
78
|
} else {
|
|
104
|
-
|
|
105
|
-
child.unref();
|
|
79
|
+
spawn("node", [serverPath], { cwd: __dirname, stdio: "ignore", detached: true }).unref();
|
|
106
80
|
}
|
|
107
81
|
|
|
108
82
|
for (let i = 0; i < 15; i++) {
|
|
109
83
|
if (await isPortOpen()) break;
|
|
110
84
|
await new Promise(rs => setTimeout(rs, 1000));
|
|
111
85
|
}
|
|
112
|
-
log("OK", "Pan Router
|
|
86
|
+
log("OK", "Pan Router 运行中,可以执行 claude 命令了", "green");
|
|
113
87
|
}
|
|
114
88
|
|
|
115
|
-
// ─── 3. 托盘模式(后台静默) ────────────────────────────────
|
|
116
|
-
|
|
117
89
|
async function startTray() {
|
|
118
90
|
const serverPath = path.join(__dirname, "server.mjs");
|
|
119
|
-
const
|
|
91
|
+
const psPath = path.join(__dirname, "tray-daemon.ps1");
|
|
92
|
+
log("..", "正在后台启动代理...", "yellow");
|
|
120
93
|
|
|
121
|
-
|
|
94
|
+
// 1. 由主程序直接启动 Node 代理,彻底杜绝 PowerShell 启动慢导致的超时
|
|
95
|
+
try {
|
|
96
|
+
if (process.platform === "win32") {
|
|
97
|
+
execSync('taskkill /f /fi "WINDOWTITLE eq Pan Router*" >nul 2>&1', { stdio: "pipe" });
|
|
98
|
+
}
|
|
99
|
+
} catch {}
|
|
122
100
|
|
|
123
|
-
// 【核心修复1】:由 Node 主进程直接启动 Server,确保 API 百分百可用
|
|
124
101
|
const srv = spawn(process.execPath, [serverPath], {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
102
|
+
cwd: __dirname,
|
|
103
|
+
stdio: "ignore",
|
|
104
|
+
windowsHide: true,
|
|
105
|
+
detached: true
|
|
129
106
|
});
|
|
130
107
|
srv.unref();
|
|
131
108
|
|
|
132
|
-
//
|
|
133
|
-
process.env.PANROUTER_NODE_PATH = process.execPath;
|
|
134
|
-
|
|
135
|
-
// 启动托盘 UI
|
|
136
|
-
if (fs.existsSync(vbsPath)) {
|
|
137
|
-
const tray = spawn("wscript.exe", ["//B", "//NoLogo", vbsPath], {
|
|
138
|
-
cwd: __dirname,
|
|
139
|
-
stdio: "ignore",
|
|
140
|
-
windowsHide: true,
|
|
141
|
-
detached: true,
|
|
142
|
-
env: process.env // 传递环境变量
|
|
143
|
-
});
|
|
144
|
-
tray.unref();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// 验证端口
|
|
109
|
+
// 2. 验证端口(由于是直接用 Node 启动,这步通常 1 秒内就会通过)
|
|
148
110
|
let ok = false;
|
|
149
111
|
for (let i = 0; i < 15; i++) {
|
|
150
|
-
if (await isPortOpen()) {
|
|
112
|
+
if (await isPortOpen()) {
|
|
113
|
+
ok = true;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
151
116
|
await new Promise(rs => setTimeout(rs, 1000));
|
|
152
117
|
}
|
|
153
118
|
|
|
154
119
|
if (ok) {
|
|
155
|
-
log("OK", "
|
|
156
|
-
console.log(" 托盘图标应该已出现。如果系统原因导致图标没出来,代理依然可用。");
|
|
120
|
+
log("OK", "代理服务已就绪!(端口 50816)", "green");
|
|
157
121
|
} else {
|
|
158
|
-
log("!!", "
|
|
122
|
+
log("!!", "服务启动超时,但仍将尝试加载托盘", "red");
|
|
159
123
|
}
|
|
160
|
-
}
|
|
161
124
|
|
|
162
|
-
//
|
|
125
|
+
// 3. 独立拉起系统托盘
|
|
126
|
+
log("..", "正在加载系统托盘...", "yellow");
|
|
127
|
+
const tray = spawn("powershell.exe", [
|
|
128
|
+
"-NoProfile",
|
|
129
|
+
"-ExecutionPolicy", "Bypass",
|
|
130
|
+
"-WindowStyle", "Hidden",
|
|
131
|
+
"-File", psPath
|
|
132
|
+
], {
|
|
133
|
+
cwd: __dirname,
|
|
134
|
+
stdio: "ignore",
|
|
135
|
+
windowsHide: true,
|
|
136
|
+
detached: true,
|
|
137
|
+
env: { ...process.env, PANROUTER_NODE: process.execPath }
|
|
138
|
+
});
|
|
139
|
+
tray.unref();
|
|
163
140
|
|
|
164
|
-
|
|
165
|
-
console.log(`\n\x1b[36m=== Pan Router - Claude Code 代理 ===\x1b[0m\n`);
|
|
141
|
+
console.log(" API 代理已立即可用。托盘图标稍后将在右下角显示。");
|
|
166
142
|
}
|
|
167
143
|
|
|
168
144
|
async function main() {
|
|
169
145
|
const args = process.argv.slice(2);
|
|
170
|
-
|
|
171
146
|
if (args.includes("--help") || args.includes("-h")) {
|
|
172
147
|
console.log("用法:\n panrouter --server (带命令行窗口运行)\n panrouter --tray (后台隐藏运行 + 托盘)\n panrouter --tray-install (安装配置 + 托盘)");
|
|
173
148
|
return;
|
|
174
149
|
}
|
|
175
150
|
|
|
176
|
-
if (args.includes("--
|
|
177
|
-
|
|
178
|
-
if (!installClaudeCode()) process.exit(1);
|
|
179
|
-
writeConfig();
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
151
|
+
if (args.includes("--server") || args.includes("-s")) { await startServer(); return; }
|
|
152
|
+
if (args.includes("--tray") || args.includes("-t")) { await startTray(); return; }
|
|
182
153
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (args.includes("--tray") || args.includes("-t")) {
|
|
189
|
-
await startTray();
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
154
|
+
console.log(`\n\x1b[36m=== Pan Router - Claude Code ===\x1b[0m\n`);
|
|
155
|
+
if (!installClaudeCode()) return process.exit(1);
|
|
156
|
+
writeConfig();
|
|
192
157
|
|
|
193
158
|
if (args.includes("--tray-install") || args.includes("-ti")) {
|
|
194
|
-
printBanner();
|
|
195
|
-
if (!installClaudeCode()) process.exit(1);
|
|
196
|
-
writeConfig();
|
|
197
159
|
await startTray();
|
|
198
|
-
|
|
160
|
+
} else {
|
|
161
|
+
await startServer();
|
|
199
162
|
}
|
|
200
|
-
|
|
201
|
-
printBanner();
|
|
202
|
-
if (!installClaudeCode()) process.exit(1);
|
|
203
|
-
writeConfig();
|
|
204
|
-
await startServer();
|
|
205
163
|
}
|
|
206
164
|
|
|
207
165
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "panrouter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"cli.mjs",
|
|
11
11
|
"server.mjs",
|
|
12
|
-
"tray-daemon.ps1"
|
|
13
|
-
"panrouter-tray.vbs"
|
|
12
|
+
"tray-daemon.ps1"
|
|
14
13
|
],
|
|
15
14
|
"license": "MIT"
|
|
16
15
|
}
|
package/tray-daemon.ps1
CHANGED
|
@@ -1,166 +1,107 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
Pan Router 托盘守护脚本 (
|
|
3
|
+
Pan Router 托盘守护脚本 (极简直连版)
|
|
4
4
|
#>
|
|
5
|
-
|
|
6
5
|
$ErrorActionPreference = "SilentlyContinue"
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
14
|
-
Add-Type -AssemblyName System.Drawing
|
|
15
|
-
} catch {
|
|
16
|
-
"Failed to load assemblies: $($_.Exception.Message)" | Out-File $logFile -Append
|
|
17
|
-
Exit
|
|
18
|
-
}
|
|
7
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
8
|
+
Add-Type -AssemblyName System.Drawing
|
|
9
|
+
[System.Windows.Forms.Application]::EnableVisualStyles()
|
|
19
10
|
|
|
20
|
-
$
|
|
21
|
-
$scriptDir = Split-Path $scriptPath -Parent
|
|
11
|
+
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
|
|
22
12
|
$serverPath = Join-Path $scriptDir "server.mjs"
|
|
23
13
|
|
|
24
|
-
#
|
|
25
|
-
$nodePath = $env:
|
|
26
|
-
if ([string]::IsNullOrWhiteSpace($nodePath)) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
|
|
38
|
-
} catch {}
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
Start-Process -FilePath $nodePath -ArgumentList "`"$serverPath`"" -WorkingDirectory $scriptDir -WindowStyle Hidden
|
|
42
|
-
"Server process spawned successfully." | Out-File $logFile -Append
|
|
43
|
-
} catch {
|
|
44
|
-
"Server start error: $($_.Exception.Message)" | Out-File $logFile -Append
|
|
45
|
-
}
|
|
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
|
|
46
27
|
}
|
|
47
28
|
|
|
48
|
-
#
|
|
49
|
-
$
|
|
29
|
+
# 【核心修复】:不要盲目重启!检查端口如果已经通了,直接跳过耗时的杀进程步骤
|
|
30
|
+
$portOpen = $false
|
|
50
31
|
try {
|
|
51
|
-
$
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
break
|
|
56
|
-
}
|
|
57
|
-
}
|
|
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()
|
|
58
36
|
} catch {}
|
|
59
37
|
|
|
60
|
-
if (-not $
|
|
61
|
-
|
|
62
|
-
Restart-Server
|
|
38
|
+
if (-not $portOpen) {
|
|
39
|
+
Start-Backend
|
|
63
40
|
}
|
|
64
41
|
|
|
65
|
-
# ───
|
|
42
|
+
# ─── 初始化托盘 ────────────────────────────────────────────
|
|
43
|
+
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
44
|
+
$notifyIcon.Text = "Pan Router (:50816)"
|
|
45
|
+
|
|
46
|
+
# 绘图报错是导致图标出不来的元凶之一,这里做强兜底
|
|
66
47
|
try {
|
|
67
48
|
$bmp = New-Object System.Drawing.Bitmap(16, 16)
|
|
68
49
|
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
69
|
-
$g.
|
|
70
|
-
$
|
|
71
|
-
$
|
|
72
|
-
$
|
|
73
|
-
|
|
74
|
-
# 【修复1】改用通用系统字体,防止系统中缺少 Segoe UI 导致报错闪退
|
|
75
|
-
$font = New-Object System.Drawing.Font([System.Drawing.FontFamily]::GenericSansSerif, 8, [System.Drawing.FontStyle]::Bold)
|
|
76
|
-
$fg = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::White)
|
|
77
|
-
$g.DrawString("P", $font, $fg, 3, 2)
|
|
78
|
-
|
|
79
|
-
$hIcon = $bmp.GetHicon()
|
|
80
|
-
$icon = [System.Drawing.Icon]::FromHandle($hIcon)
|
|
81
|
-
"Custom icon drawn." | Out-File $logFile -Append
|
|
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())
|
|
82
54
|
} catch {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
$icon = [System.Drawing.SystemIcons]::Application
|
|
55
|
+
# 终极兜底:如果上面的画图代码因为 GDI 环境被拦,直接用系统自带的安全盾牌图标
|
|
56
|
+
$notifyIcon.Icon = [System.Drawing.SystemIcons]::Shield
|
|
86
57
|
}
|
|
87
58
|
|
|
88
|
-
# ───
|
|
89
|
-
# 【修复3】提供一个实质性的隐藏窗体,避免部分系统回收无窗体的托盘进程
|
|
90
|
-
$mainForm = New-Object System.Windows.Forms.Form
|
|
91
|
-
$mainForm.Text = "PanRouterHidden"
|
|
92
|
-
$mainForm.ShowInTaskbar = $false
|
|
93
|
-
$mainForm.WindowState = [System.Windows.Forms.FormWindowState]::Minimized
|
|
94
|
-
$mainForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedToolWindow
|
|
95
|
-
$mainForm.Opacity = 0
|
|
96
|
-
$mainForm.Add_Load({ $mainForm.Hide() })
|
|
97
|
-
|
|
98
|
-
# ─── 初始化托盘菜单 ────────────────────────────────────────
|
|
99
|
-
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
100
|
-
$notifyIcon.Icon = $icon
|
|
101
|
-
$notifyIcon.Text = "Pan Router (端口: 50816)"
|
|
102
|
-
|
|
59
|
+
# ─── 菜单配置 ──────────────────────────────────────────────
|
|
103
60
|
$menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
104
61
|
|
|
105
|
-
$titleItem =
|
|
62
|
+
$titleItem = $menu.Items.Add("Pan Router | :50816")
|
|
106
63
|
$titleItem.Enabled = $false
|
|
107
|
-
$
|
|
108
|
-
$menu.Items.Add($titleItem) | Out-Null
|
|
109
|
-
$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator)) | Out-Null
|
|
64
|
+
$menu.Items.Add("-") | Out-Null
|
|
110
65
|
|
|
111
|
-
$restartItem =
|
|
66
|
+
$restartItem = $menu.Items.Add("重启服务")
|
|
112
67
|
$restartItem.Add_Click({
|
|
113
|
-
|
|
114
|
-
$notifyIcon.ShowBalloonTip(2000, "Pan Router", "
|
|
68
|
+
Start-Backend
|
|
69
|
+
$notifyIcon.ShowBalloonTip(2000, "Pan Router", "服务已重启", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
115
70
|
})
|
|
116
|
-
$menu.Items.Add($restartItem) | Out-Null
|
|
117
|
-
|
|
118
|
-
$autoItem = New-Object System.Windows.Forms.ToolStripMenuItem("开机自启动")
|
|
119
|
-
try {
|
|
120
|
-
$regKey = Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name PanRouter
|
|
121
|
-
$autoItem.Checked = ($regKey -ne $null)
|
|
122
|
-
} catch {}
|
|
123
71
|
|
|
72
|
+
$autoItem = $menu.Items.Add("开机自启动")
|
|
73
|
+
try { $autoItem.Checked = (Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name PanRouter) -ne $null } catch {}
|
|
124
74
|
$autoItem.Add_Click({
|
|
125
75
|
if ($autoItem.Checked) {
|
|
126
76
|
reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /f 2>&1 | Out-Null
|
|
127
77
|
$autoItem.Checked = $false
|
|
128
78
|
} else {
|
|
129
|
-
$
|
|
130
|
-
$cmd = "wscript.exe //B //NoLogo `"$vbsPath`""
|
|
79
|
+
$cmd = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`""
|
|
131
80
|
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v PanRouter /t REG_SZ /d $cmd /f 2>&1 | Out-Null
|
|
132
81
|
$autoItem.Checked = $true
|
|
133
82
|
}
|
|
134
83
|
})
|
|
135
|
-
$menu.Items.Add($autoItem) | Out-Null
|
|
136
|
-
$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator)) | Out-Null
|
|
137
84
|
|
|
138
|
-
$
|
|
85
|
+
$menu.Items.Add("-") | Out-Null
|
|
86
|
+
|
|
87
|
+
$exitItem = $menu.Items.Add("退出")
|
|
139
88
|
$exitItem.Add_Click({
|
|
140
89
|
$notifyIcon.Visible = $false
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
} catch {}
|
|
144
|
-
$mainForm.Close()
|
|
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()
|
|
145
92
|
})
|
|
146
|
-
$menu.Items.Add($exitItem) | Out-Null
|
|
147
93
|
|
|
148
94
|
$notifyIcon.ContextMenuStrip = $menu
|
|
149
95
|
|
|
96
|
+
# 左键点击直接弹出原生菜单(去除了那些花里胡哨的反射代码)
|
|
150
97
|
$notifyIcon.Add_MouseClick({
|
|
151
98
|
if ($_.Button -eq [System.Windows.Forms.MouseButtons]::Left) {
|
|
152
|
-
$
|
|
153
|
-
$mi.Invoke($notifyIcon, $null)
|
|
99
|
+
$notifyIcon.ContextMenuStrip.Show([System.Windows.Forms.Cursor]::Position)
|
|
154
100
|
}
|
|
155
101
|
})
|
|
156
102
|
|
|
157
103
|
$notifyIcon.Visible = $true
|
|
158
104
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
[System.Windows.Forms.Application]::Run($mainForm)
|
|
163
|
-
|
|
164
|
-
"Exiting..." | Out-File $logFile -Append
|
|
165
|
-
$notifyIcon.Visible = $false
|
|
166
|
-
$notifyIcon.Dispose()
|
|
105
|
+
# 保持进程存活的最纯粹方式
|
|
106
|
+
$ctx = New-Object System.Windows.Forms.ApplicationContext
|
|
107
|
+
[System.Windows.Forms.Application]::Run($ctx)
|
package/panrouter-tray.vbs
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
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
|