panrouter 1.6.1 → 1.7.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 (2) hide show
  1. package/daemon.mjs +27 -32
  2. package/package.json +1 -1
package/daemon.mjs CHANGED
@@ -1,14 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Pan Router Daemon (v4)
4
+ * Pan Router Daemon (v5)
5
5
  *
6
- * 一个 Node 进程同时:
7
- * 1. 隐藏启动 server.mjs(子进程)
8
- * 2. 隐藏启动 tray-daemon.ps1(子进程)
9
- * 3. 保持存活,守护两者
10
- *
11
- * 由 cli.mjs --tray 启动 (detached, 无窗口)。
6
+ * 1. spawn server.mjs (detached, hidden) — 正常
7
+ * 2. VBS 启动 PS tray(解决 detached 进程无法创建通知图标的问题)
8
+ * 3. 保持存活,30s 健康检查
12
9
  */
13
10
 
14
11
  import { spawn, execSync } from "node:child_process";
@@ -20,16 +17,15 @@ import http from "node:http";
20
17
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
18
  const serverPath = path.join(__dirname, "server.mjs");
22
19
  const trayPsPath = path.join(__dirname, "tray-daemon.ps1");
23
- const nodeExe = process.execPath; // ← 绝对路径!不依赖 PATH
20
+ const nodeExe = process.execPath;
24
21
  const logPath = path.join(process.env.TEMP || "/tmp", "panrouter-daemon.log");
25
22
 
26
23
  function log(msg) {
27
24
  try { fs.appendFileSync(logPath, `${new Date().toISOString().slice(11,19)} ${msg}\n`); } catch {}
28
25
  }
29
26
 
30
- log("=== Daemon v4 ===");
27
+ log("=== Daemon v5 ===");
31
28
  log(`nodeExe=${nodeExe}`);
32
- log(`serverPath=${serverPath}`);
33
29
 
34
30
  // ─── 1. 杀旧 server ──────────────────────────────
35
31
  try {
@@ -40,19 +36,14 @@ try {
40
36
  for (const line of out.split("\n")) {
41
37
  if (line.includes("server.mjs")) {
42
38
  const m = line.match(/(\d+),.*?server\.mjs/);
43
- if (m) { try { process.kill(parseInt(m[1]), "SIGKILL"); log(`Killed old server PID=${m[1]}`); } catch {} }
39
+ if (m) { try { process.kill(parseInt(m[1]), "SIGKILL"); log(`Killed old PID=${m[1]}`); } catch {} }
44
40
  }
45
41
  }
46
42
  } catch {}
47
43
 
48
44
  // ─── 2. 启动 server.mjs ──────────────────────────
49
- // 关键: 用 process.execPath (绝对路径), 不用 "node" (可能找不到 PATH)
50
45
  const server = spawn(nodeExe, [serverPath], {
51
- cwd: __dirname,
52
- stdio: "ignore",
53
- windowsHide: true,
54
- detached: true,
55
- shell: false,
46
+ cwd: __dirname, stdio: "ignore", windowsHide: true, detached: true, shell: false,
56
47
  });
57
48
  server.unref();
58
49
  log(`Server spawned PID=${server.pid || "??"}`);
@@ -77,29 +68,33 @@ function isOnline() {
77
68
  }
78
69
  log(`Server online=${ready}`);
79
70
 
80
- // ─── 4. 启动 PS 托盘 ────────────────────────────
71
+ // ─── 4. 用 VBS 启动 PS 托盘 ─────────────────────
72
+ // 关键: spawn 的 detached 进程没有 Window Station 访问权限,
73
+ // 无法创建 NotifyIcon。WScript.Shell.Run 0 是可靠的解决方式。
81
74
  if (fs.existsSync(trayPsPath)) {
82
- log("Starting PS tray...");
83
- const ps = spawn("powershell.exe", [
84
- "-NoProfile", "-ExecutionPolicy", "Bypass",
85
- "-WindowStyle", "Hidden",
86
- "-File", trayPsPath,
87
- ], {
88
- stdio: "ignore",
89
- windowsHide: true,
90
- shell: false,
75
+ // 创建临时的 VBS 启动器
76
+ const vbsContent = `Set WshShell = CreateObject("WScript.Shell")
77
+ WshShell.Run "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -STA -File """ & "${trayPsPath.replace(/\\/g, "\\\\")}" & """", 0, False
78
+ `;
79
+ const vbsPath = path.join(process.env.TEMP || "/tmp", "panrouter-tray-launcher.vbs");
80
+ try { fs.writeFileSync(vbsPath, vbsContent, "utf8"); } catch {}
81
+
82
+ log(`VBS launcher: ${vbsPath}`);
83
+
84
+ // wscript //B = 批处理模式, 无交互
85
+ const vbs = spawn("wscript.exe", ["//B", "//NoLogo", vbsPath], {
86
+ stdio: "ignore", windowsHide: true, shell: false,
91
87
  });
92
- ps.unref();
93
- log(`PS spawned PID=${ps.pid || "??"}`);
88
+ vbs.unref();
89
+ log(`VBS launched`);
94
90
  } else {
95
- log(`WARN: tray-daemon.ps1 not found at ${trayPsPath}`);
91
+ log(`WARN: ${trayPsPath} not found`);
96
92
  }
97
93
 
98
94
  // ─── 5. 保持存活 ────────────────────────────────
99
- log("Daemon running, keeping session alive");
95
+ log("Daemon running");
100
96
  process.stdin.resume();
101
97
 
102
- // 定时检查 server, 挂了就重启
103
98
  setInterval(() => {
104
99
  isOnline().then(ok => {
105
100
  if (!ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panrouter",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key",
5
5
  "type": "module",
6
6
  "bin": {