fluxy-bot 0.5.39 → 0.5.41

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/bin/cli.js +87 -19
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -81,6 +81,19 @@ WantedBy=multi-user.target
81
81
  `;
82
82
  }
83
83
 
84
+ function killAndWait(child, timeout = 10_000) {
85
+ return new Promise((resolve) => {
86
+ child.removeAllListeners('exit');
87
+ child.on('exit', () => resolve());
88
+ child.kill('SIGTERM');
89
+ // Force kill after timeout if SIGTERM doesn't work
90
+ setTimeout(() => {
91
+ try { child.kill('SIGKILL'); } catch {}
92
+ setTimeout(resolve, 500);
93
+ }, timeout);
94
+ });
95
+ }
96
+
84
97
  // ── UI helpers ──
85
98
 
86
99
  const c = {
@@ -433,9 +446,7 @@ async function init() {
433
446
 
434
447
  // Install systemd daemon on Linux
435
448
  if (hasSystemd) {
436
- child.removeAllListeners('exit');
437
- child.kill('SIGTERM');
438
- await new Promise((r) => setTimeout(r, 4000));
449
+ await killAndWait(child);
439
450
  const nodePath = process.execPath;
440
451
  const realHome = os.homedir();
441
452
  const res = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
@@ -472,6 +483,21 @@ async function start() {
472
483
  return init();
473
484
  }
474
485
 
486
+ // If daemon is already running, don't start a second instance
487
+ if (os.platform() === 'linux' && isServiceInstalled() && isServiceActive()) {
488
+ banner();
489
+ const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
490
+ console.log(`\n ${c.blue}●${c.reset} Fluxy is already running as a daemon.\n`);
491
+ if (config.relay?.url) {
492
+ console.log(` ${c.dim}URL:${c.reset} ${c.pink}${link(config.relay.url)}${c.reset}`);
493
+ }
494
+ console.log(` ${c.dim}Status:${c.reset} ${c.pink}fluxy daemon status${c.reset}`);
495
+ console.log(` ${c.dim}Logs:${c.reset} ${c.pink}fluxy daemon logs${c.reset}`);
496
+ console.log(` ${c.dim}Restart:${c.reset} ${c.pink}fluxy daemon restart${c.reset}`);
497
+ console.log(` ${c.dim}Stop:${c.reset} ${c.pink}fluxy daemon stop${c.reset}\n`);
498
+ return;
499
+ }
500
+
475
501
  const isLinux = os.platform() === 'linux';
476
502
  const hasSystemd = isLinux && (() => { try { execSync('systemctl --version', { stdio: 'ignore' }); return true; } catch { return false; } })();
477
503
  const needsDaemon = hasSystemd && !isServiceInstalled();
@@ -511,9 +537,7 @@ async function start() {
511
537
 
512
538
  // Install systemd daemon on Linux if not already installed
513
539
  if (needsDaemon) {
514
- child.removeAllListeners('exit');
515
- child.kill('SIGTERM');
516
- await new Promise((r) => setTimeout(r, 4000));
540
+ await killAndWait(child);
517
541
  const nodePath = process.execPath;
518
542
  const realHome = os.homedir();
519
543
  const res = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
@@ -546,17 +570,33 @@ async function start() {
546
570
  }
547
571
 
548
572
  async function status() {
549
- try {
550
- const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
551
- const res = await fetch(`http://localhost:${config.port}/api/health`);
552
- const data = await res.json();
553
- console.log(`\n ${c.blue}●${c.reset} Fluxy is running`);
554
- console.log(` ${c.dim}Uptime: ${data.uptime}s${c.reset}`);
555
- if (config.relay?.url) {
573
+ const config = fs.existsSync(CONFIG_PATH) ? JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')) : null;
574
+ const daemonRunning = os.platform() === 'linux' && isServiceInstalled() && isServiceActive();
575
+
576
+ // Try health endpoint
577
+ let healthOk = false;
578
+ let uptime = null;
579
+ if (config) {
580
+ try {
581
+ const res = await fetch(`http://localhost:${config.port}/api/health`);
582
+ const data = await res.json();
583
+ healthOk = true;
584
+ uptime = data.uptime;
585
+ } catch {}
586
+ }
587
+
588
+ if (healthOk) {
589
+ console.log(`\n ${c.blue}●${c.reset} Fluxy is running${daemonRunning ? ` ${c.dim}(daemon)${c.reset}` : ''}`);
590
+ if (uptime != null) console.log(` ${c.dim}Uptime: ${uptime}s${c.reset}`);
591
+ if (config?.relay?.url) {
556
592
  console.log(` ${c.dim}URL: ${c.reset}${c.pink}${link(config.relay.url)}${c.reset}`);
557
593
  }
558
594
  console.log(` ${c.dim}Config: ${CONFIG_PATH}${c.reset}\n`);
559
- } catch {
595
+ } else if (daemonRunning) {
596
+ console.log(`\n ${c.yellow}●${c.reset} Fluxy daemon is running but not responding yet.`);
597
+ console.log(` ${c.dim}It may still be starting up. Check logs:${c.reset}`);
598
+ console.log(` ${c.pink}fluxy daemon logs${c.reset}\n`);
599
+ } else {
560
600
  console.log(`\n ${c.dim}●${c.reset} Fluxy is not running.\n`);
561
601
  }
562
602
  }
@@ -586,16 +626,29 @@ async function update() {
586
626
 
587
627
  console.log(` ${c.dim}v${currentVersion} → v${latest.version}${c.reset}\n`);
588
628
 
629
+ const daemonWasRunning = os.platform() === 'linux' && isServiceInstalled() && isServiceActive();
630
+
589
631
  const steps = [
632
+ ...(daemonWasRunning ? ['Stopping daemon'] : []),
590
633
  'Downloading update',
591
634
  'Updating files',
592
635
  'Installing dependencies',
593
636
  'Building interface',
637
+ ...(daemonWasRunning ? ['Restarting daemon'] : []),
594
638
  ];
595
639
 
596
640
  const stepper = new Stepper(steps);
597
641
  stepper.start();
598
642
 
643
+ // Stop daemon before updating files
644
+ if (daemonWasRunning) {
645
+ try {
646
+ const cmd = needsSudo() ? `sudo systemctl stop ${SERVICE_NAME}` : `systemctl stop ${SERVICE_NAME}`;
647
+ execSync(cmd, { stdio: 'ignore' });
648
+ } catch {}
649
+ stepper.advance();
650
+ }
651
+
599
652
  // Download tarball
600
653
  const tarballUrl = latest.dist.tarball;
601
654
  const tmpDir = path.join(os.tmpdir(), `fluxy-update-${Date.now()}`);
@@ -670,17 +723,32 @@ async function update() {
670
723
  // Clean up
671
724
  fs.rmSync(tmpDir, { recursive: true, force: true });
672
725
 
726
+ // Restart daemon if it was running
727
+ if (daemonWasRunning) {
728
+ try {
729
+ const cmd = needsSudo() ? `sudo systemctl start ${SERVICE_NAME}` : `systemctl start ${SERVICE_NAME}`;
730
+ execSync(cmd, { stdio: 'ignore' });
731
+ } catch {}
732
+ stepper.advance();
733
+ }
734
+
673
735
  stepper.finish();
674
736
 
675
737
  console.log(`\n ${c.blue}${c.bold}✔ Updated to v${latest.version}${c.reset}\n`);
676
738
 
677
- // Auto-restart daemon if installed
678
- if (os.platform() === 'linux' && isServiceInstalled()) {
679
- try {
680
- execSync(`systemctl restart ${SERVICE_NAME}`, { stdio: 'ignore' });
739
+ if (daemonWasRunning) {
740
+ if (isServiceActive()) {
681
741
  console.log(` ${c.blue}✔${c.reset} Daemon restarted with new version.\n`);
742
+ } else {
743
+ console.log(` ${c.yellow}⚠${c.reset} Daemon may still be starting. Check ${c.pink}fluxy daemon status${c.reset}\n`);
744
+ }
745
+ } else if (os.platform() === 'linux' && isServiceInstalled()) {
746
+ try {
747
+ const cmd = needsSudo() ? `sudo systemctl start ${SERVICE_NAME}` : `systemctl start ${SERVICE_NAME}`;
748
+ execSync(cmd, { stdio: 'ignore' });
749
+ console.log(` ${c.blue}✔${c.reset} Daemon started with new version.\n`);
682
750
  } catch {
683
- console.log(` ${c.yellow}⚠${c.reset} Could not restart daemon. Run ${c.pink}fluxy daemon restart${c.reset} manually.\n`);
751
+ console.log(` ${c.dim}Run ${c.reset}${c.pink}fluxy daemon start${c.reset}${c.dim} to launch.${c.reset}\n`);
684
752
  }
685
753
  } else {
686
754
  console.log(` ${c.dim}Run ${c.reset}${c.pink}fluxy start${c.reset}${c.dim} to launch.${c.reset}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxy-bot",
3
- "version": "0.5.39",
3
+ "version": "0.5.41",
4
4
  "description": "Self-hosted, self-evolving AI agent with its own dashboard.",
5
5
  "type": "module",
6
6
  "license": "MIT",