panrouter 6.3.4 → 6.3.6

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/pool-worker.mjs +87 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panrouter",
3
- "version": "6.3.4",
3
+ "version": "6.3.6",
4
4
  "description": "让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key",
5
5
  "type": "module",
6
6
  "bin": {
package/pool-worker.mjs CHANGED
@@ -145,6 +145,10 @@ function connectToHub() {
145
145
  ws.send(JSON.stringify({ type: "pong" }));
146
146
  break;
147
147
 
148
+ case "test":
149
+ handleTestRequest(msg);
150
+ break;
151
+
148
152
  case "upgrade":
149
153
  handleUpgrade();
150
154
  break;
@@ -266,6 +270,17 @@ async function doRestart() {
266
270
  // ─── 处理来自主控的请求 ─────────────────────────────────────────────────────
267
271
 
268
272
  function handleIncomingRequest(msg) {
273
+ // ponytail: 不阻塞请求,但如果 watchdog 标记了挂了就先重启
274
+ if (!_serverReady) {
275
+ isPortOpen(SERVER_PORT).then(open => {
276
+ if (!open) {
277
+ log(`9router 不在线,尝试重启`, "WARN");
278
+ killPort(SERVER_PORT);
279
+ trySpawn9router();
280
+ }
281
+ });
282
+ }
283
+
269
284
  const body = msg.body || "";
270
285
 
271
286
  const options = {
@@ -337,6 +352,29 @@ function safeSend(data) {
337
352
  }
338
353
  }
339
354
 
355
+ // ─── 节点连通性测试 ─────────────────────────────────────────────────────────
356
+
357
+ function handleTestRequest(msg) {
358
+ const testReq = http.request({
359
+ hostname: "127.0.0.1",
360
+ port: SERVER_PORT,
361
+ path: "/dashboard",
362
+ method: "GET",
363
+ timeout: 8000,
364
+ }, (res) => {
365
+ // 只要收到响应就算通
366
+ let body = "";
367
+ res.on("data", (c) => body += c);
368
+ res.on("end", () => {
369
+ safeSend({ type: "test_result", reqId: msg.reqId, ok: true, status: res.statusCode, detail: body.slice(0, 200) });
370
+ });
371
+ });
372
+ testReq.on("error", (err) => {
373
+ safeSend({ type: "test_result", reqId: msg.reqId, ok: false, error: err.message });
374
+ });
375
+ testReq.end();
376
+ }
377
+
340
378
  // ─── 心跳保活(每 25 秒发送 ping,避免 Cloudflare 闲置超时) ─────────────────
341
379
 
342
380
  function startHeartbeat() {
@@ -413,7 +451,7 @@ function ensureServer() {
413
451
  function _ensureServer() {
414
452
  const setupScript = path.join(__dirname, "setup-9router.cjs");
415
453
  if (!fs.existsSync(setupScript)) {
416
- log("找不到 setup-9router.cjs,直接尝试启动 9router", "WARN");
454
+ log("找不到 setup-9router.cjs,直接启动 9router", "WARN");
417
455
  trySpawn9router();
418
456
  poll9router();
419
457
  return;
@@ -423,19 +461,23 @@ function _ensureServer() {
423
461
  const child = spawn(process.execPath, [setupScript], {
424
462
  cwd: __dirname, stdio: "inherit",
425
463
  });
464
+ // 等安装完再启动 9router — 统一用 --tray 模式,setup 自己开的 cmd 窗口让它去
426
465
  child.on("exit", () => {
427
- log("部署脚本完成,正在启动 9router...");
466
+ log("安装完成,正在启动 9router...");
428
467
  trySpawn9router();
429
468
  });
430
-
431
469
  poll9router();
432
470
  }
433
471
 
434
472
  function trySpawn9router() {
435
473
  try {
436
- const r = spawn("npx", ["--yes", "9router"], {
437
- detached: true, stdio: "ignore", shell: true,
474
+ // ponytail: --tray 跳过 TTY 菜单(否则无 TTY 环境 3 秒自杀)
475
+ // --skip-update 跳过 npm registry 检查(手机网络不稳定)
476
+ // 不用 npx,9router 已被 setup-9router.cjs 全局安装
477
+ const r = spawn("9router", ["--tray", "--skip-update"], {
478
+ detached: true, stdio: ["ignore", "ignore", "pipe"], shell: true,
438
479
  });
480
+ r.stderr?.on("data", (d) => log(`9router 错误: ${d.toString().trim()}`, "ERR"));
439
481
  r.unref();
440
482
  } catch (e) {
441
483
  log(`启动 9router 失败: ${e.message}`, "ERR");
@@ -443,14 +485,47 @@ function trySpawn9router() {
443
485
  }
444
486
 
445
487
  async function poll9router() {
446
- for (let i = 0; ; i++) {
488
+ const deadline = Date.now() + 30000;
489
+ while (Date.now() < deadline) {
447
490
  if (await isPortOpen(SERVER_PORT)) {
448
491
  _serverReady = true;
449
492
  log(`9router 已就绪 (端口 ${SERVER_PORT})`, "OK");
450
- return;
493
+ return true;
494
+ }
495
+ await new Promise((r) => setTimeout(r, 1000));
496
+ }
497
+ log(`等待 9router 超时,继续后台重试`, "WARN");
498
+ return false;
499
+ }
500
+
501
+ // ponytail: 每隔 30 秒检查 9router 是否活着,死了就重启
502
+ let watchdogTimer = null;
503
+
504
+ function startWatchdog() {
505
+ if (watchdogTimer) return;
506
+ watchdogTimer = setInterval(async () => {
507
+ if (await isPortOpen(SERVER_PORT)) return;
508
+ log(`9router 无响应,正在重启...`, "WARN");
509
+ _serverReady = false;
510
+ killPort(SERVER_PORT);
511
+ trySpawn9router();
512
+ // 等待最多 15 秒
513
+ for (let i = 0; i < 15; i++) {
514
+ await new Promise((r) => setTimeout(r, 1000));
515
+ if (await isPortOpen(SERVER_PORT)) {
516
+ _serverReady = true;
517
+ log(`9router 已恢复`, "OK");
518
+ return;
519
+ }
451
520
  }
452
- if (i < 60) await new Promise((r) => setTimeout(r, 1000));
453
- else await new Promise((r) => setTimeout(r, 5000));
521
+ log(`9router 重启失败,下次再试`, "ERR");
522
+ }, 30000);
523
+ }
524
+
525
+ function stopWatchdog() {
526
+ if (watchdogTimer) {
527
+ clearInterval(watchdogTimer);
528
+ watchdogTimer = null;
454
529
  }
455
530
  }
456
531
 
@@ -488,6 +563,7 @@ function gracefulShutdown() {
488
563
 
489
564
  log("收到关机指令,正在安全退出...", "OFF");
490
565
 
566
+ stopWatchdog();
491
567
  stopHeartbeat();
492
568
  if (reconnectTimer) {
493
569
  clearTimeout(reconnectTimer);
@@ -528,11 +604,12 @@ export async function start() {
528
604
 
529
605
  log("节点看门狗已启动,等待公网入口...", "ON");
530
606
 
531
- // 等 9router 起来后检查状态
607
+ // 等 9router 起来后打开守护
532
608
  const checkReady = setInterval(async () => {
533
609
  if (_serverReady || (await isPortOpen(SERVER_PORT))) {
534
610
  _serverReady = true;
535
611
  clearInterval(checkReady);
612
+ startWatchdog();
536
613
  log("9router 已就绪,节点可正常处理请求", "ON");
537
614
  }
538
615
  }, 3000);