@yeaft/webchat-agent 0.0.36 → 0.0.38

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/connection.js +96 -13
  2. package/package.json +1 -1
package/connection.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import WebSocket from 'ws';
2
2
  import { execSync, execFile, spawn } from 'child_process';
3
- import { writeFileSync, mkdirSync } from 'fs';
3
+ import { writeFileSync, mkdirSync, existsSync, cpSync, rmSync } from 'fs';
4
4
  import { join } from 'path';
5
5
  import { platform } from 'os';
6
6
  import ctx from './context.js';
@@ -371,12 +371,13 @@ async function handleMessage(msg) {
371
371
  // 例如 C:/Users/x/myproject/node_modules/@yeaft/webchat-agent/cli.js → C:/Users/x/myproject
372
372
  const installDir = scriptPath.substring(0, nmIndex);
373
373
 
374
- // 判断全局安装 vs 局部安装:全局安装的 installDir 就是 npm global prefix
374
+ // 判断全局安装 vs 局部安装
375
+ // npm 全局 node_modules: prefix/lib/node_modules (Linux/macOS) 或 prefix/node_modules (Windows)
375
376
  const isGlobalInstall = await new Promise((resolve) => {
376
377
  execFile('npm', ['prefix', '-g'], { shell: true }, (err, stdout) => {
377
378
  if (err) { resolve(false); return; }
378
379
  const globalPrefix = stdout.toString().trim().replace(/\\/g, '/');
379
- resolve(installDir === globalPrefix);
380
+ resolve(installDir === globalPrefix || installDir === globalPrefix + '/lib');
380
381
  });
381
382
  });
382
383
 
@@ -459,18 +460,100 @@ async function handleMessage(msg) {
459
460
  console.log(`[Agent] Spawned upgrade script (PID wait for ${pid}, pm2=${isPm2}, dir=${installDir}): ${batPath}`);
460
461
  sendToServer({ type: 'upgrade_agent_ack', success: true, version: latestVersion, pendingRestart: true });
461
462
  } else {
462
- // Linux/macOS: rename 不受文件锁影响,直接升级
463
+ // Linux/macOS: 生成 detached shell 脚本,先停止服务再升级再启动
464
+ // 避免在升级过程中 systemd 不断重启已删除的旧版本
465
+ const pid = process.pid;
466
+ const configDir = getConfigDir();
467
+ mkdirSync(configDir, { recursive: true });
468
+ const shPath = join(configDir, 'upgrade.sh');
469
+ const isSystemd = existsSync(join(process.env.HOME || '', '.config', 'systemd', 'user', 'yeaft-agent.service'));
470
+ const isLaunchd = platform() === 'darwin' && existsSync(join(process.env.HOME || '', 'Library', 'LaunchAgents', 'com.yeaft.agent.plist'));
463
471
  const cwd = isGlobalInstall ? undefined : installDir;
464
- await new Promise((resolve, reject) => {
465
- execFile('npm', npmArgs, { cwd, stdio: 'pipe', shell: true }, (err) => {
466
- if (err) reject(err); else resolve();
467
- });
472
+
473
+ const shLines = [
474
+ '#!/bin/bash',
475
+ `PID=${pid}`,
476
+ `PKG="${pkgName}@${latestVersion}"`,
477
+ ...(cwd ? [`INSTALL_DIR="${cwd}"`] : []),
478
+ '',
479
+ '# Wait for current process to exit',
480
+ 'COUNT=0',
481
+ 'while kill -0 $PID 2>/dev/null; do',
482
+ ' COUNT=$((COUNT+1))',
483
+ ' if [ $COUNT -ge 30 ]; then',
484
+ ' echo "[Upgrade] Timeout waiting for PID $PID to exit"',
485
+ ' break',
486
+ ' fi',
487
+ ' sleep 2',
488
+ 'done',
489
+ '',
490
+ ];
491
+
492
+ // 停止服务管理器的自动重启
493
+ if (isSystemd) {
494
+ shLines.push(
495
+ '# Stop systemd service to prevent restart loop',
496
+ 'systemctl --user stop yeaft-agent 2>/dev/null',
497
+ 'sleep 1',
498
+ '',
499
+ );
500
+ } else if (isLaunchd) {
501
+ const plistPath = join(process.env.HOME || '', 'Library', 'LaunchAgents', 'com.yeaft.agent.plist');
502
+ shLines.push(
503
+ '# Unload launchd service to prevent restart loop',
504
+ `launchctl unload "${plistPath}" 2>/dev/null`,
505
+ 'sleep 1',
506
+ '',
507
+ );
508
+ }
509
+
510
+ // npm install
511
+ const npmCmd = isGlobalInstall
512
+ ? `npm install -g "$PKG"`
513
+ : `cd "$INSTALL_DIR" && npm install "$PKG"`;
514
+
515
+ shLines.push(
516
+ 'echo "[Upgrade] Installing $PKG..."',
517
+ npmCmd,
518
+ 'EXIT_CODE=$?',
519
+ 'if [ $EXIT_CODE -ne 0 ]; then',
520
+ ' echo "[Upgrade] npm install failed with exit code $EXIT_CODE"',
521
+ 'else',
522
+ ' echo "[Upgrade] Successfully installed $PKG"',
523
+ 'fi',
524
+ '',
525
+ );
526
+
527
+ // 重新启动服务
528
+ if (isSystemd) {
529
+ shLines.push(
530
+ '# Restart systemd service',
531
+ 'systemctl --user start yeaft-agent',
532
+ 'echo "[Upgrade] Service restarted via systemd"',
533
+ );
534
+ } else if (isLaunchd) {
535
+ const plistPath = join(process.env.HOME || '', 'Library', 'LaunchAgents', 'com.yeaft.agent.plist');
536
+ shLines.push(
537
+ '# Reload launchd service',
538
+ `launchctl load "${plistPath}"`,
539
+ 'echo "[Upgrade] Service restarted via launchd"',
540
+ );
541
+ }
542
+
543
+ // 清理脚本自身
544
+ shLines.push('', `rm -f "${shPath}"`);
545
+
546
+ writeFileSync(shPath, shLines.join('\n'), { mode: 0o755 });
547
+ const child = spawn('bash', [shPath], {
548
+ detached: true,
549
+ stdio: 'ignore',
468
550
  });
469
- console.log('[Agent] Upgrade successful, restarting...');
470
- sendToServer({ type: 'upgrade_agent_ack', success: true, version: latestVersion });
551
+ child.unref();
552
+ console.log(`[Agent] Spawned upgrade script: ${shPath}`);
553
+ sendToServer({ type: 'upgrade_agent_ack', success: true, version: latestVersion, pendingRestart: true });
471
554
  }
472
555
 
473
- // 清理并退出
556
+ // 清理并退出,让升级脚本接管
474
557
  setTimeout(() => {
475
558
  for (const [, term] of ctx.terminals) {
476
559
  if (term.pty) { try { term.pty.kill(); } catch {} }
@@ -488,8 +571,8 @@ async function handleMessage(msg) {
488
571
  ctx.ws.close();
489
572
  }
490
573
  clearTimeout(ctx.reconnectTimer);
491
- console.log('[Agent] Cleanup done, exiting...');
492
- process.exit(1);
574
+ console.log('[Agent] Cleanup done, exiting for upgrade...');
575
+ process.exit(0);
493
576
  }, 500);
494
577
  } catch (e) {
495
578
  console.error('[Agent] Upgrade failed:', e.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",