fluxy-bot 0.5.40 → 0.5.42
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/bin/cli.js +72 -20
- package/package.json +1 -1
- package/supervisor/backend.ts +12 -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
|
|
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'], {
|
|
@@ -526,9 +537,7 @@ async function start() {
|
|
|
526
537
|
|
|
527
538
|
// Install systemd daemon on Linux if not already installed
|
|
528
539
|
if (needsDaemon) {
|
|
529
|
-
child
|
|
530
|
-
child.kill('SIGTERM');
|
|
531
|
-
await new Promise((r) => setTimeout(r, 4000));
|
|
540
|
+
await killAndWait(child);
|
|
532
541
|
const nodePath = process.execPath;
|
|
533
542
|
const realHome = os.homedir();
|
|
534
543
|
const res = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
|
|
@@ -561,17 +570,33 @@ async function start() {
|
|
|
561
570
|
}
|
|
562
571
|
|
|
563
572
|
async function status() {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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) {
|
|
571
592
|
console.log(` ${c.dim}URL: ${c.reset}${c.pink}${link(config.relay.url)}${c.reset}`);
|
|
572
593
|
}
|
|
573
594
|
console.log(` ${c.dim}Config: ${CONFIG_PATH}${c.reset}\n`);
|
|
574
|
-
}
|
|
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 {
|
|
575
600
|
console.log(`\n ${c.dim}●${c.reset} Fluxy is not running.\n`);
|
|
576
601
|
}
|
|
577
602
|
}
|
|
@@ -601,16 +626,29 @@ async function update() {
|
|
|
601
626
|
|
|
602
627
|
console.log(` ${c.dim}v${currentVersion} → v${latest.version}${c.reset}\n`);
|
|
603
628
|
|
|
629
|
+
const daemonWasRunning = os.platform() === 'linux' && isServiceInstalled() && isServiceActive();
|
|
630
|
+
|
|
604
631
|
const steps = [
|
|
632
|
+
...(daemonWasRunning ? ['Stopping daemon'] : []),
|
|
605
633
|
'Downloading update',
|
|
606
634
|
'Updating files',
|
|
607
635
|
'Installing dependencies',
|
|
608
636
|
'Building interface',
|
|
637
|
+
...(daemonWasRunning ? ['Restarting daemon'] : []),
|
|
609
638
|
];
|
|
610
639
|
|
|
611
640
|
const stepper = new Stepper(steps);
|
|
612
641
|
stepper.start();
|
|
613
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
|
+
|
|
614
652
|
// Download tarball
|
|
615
653
|
const tarballUrl = latest.dist.tarball;
|
|
616
654
|
const tmpDir = path.join(os.tmpdir(), `fluxy-update-${Date.now()}`);
|
|
@@ -685,18 +723,32 @@ async function update() {
|
|
|
685
723
|
// Clean up
|
|
686
724
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
687
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
|
+
|
|
688
735
|
stepper.finish();
|
|
689
736
|
|
|
690
737
|
console.log(`\n ${c.blue}${c.bold}✔ Updated to v${latest.version}${c.reset}\n`);
|
|
691
738
|
|
|
692
|
-
|
|
693
|
-
|
|
739
|
+
if (daemonWasRunning) {
|
|
740
|
+
if (isServiceActive()) {
|
|
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()) {
|
|
694
746
|
try {
|
|
695
|
-
const cmd = needsSudo() ? `sudo systemctl
|
|
696
|
-
execSync(cmd, { stdio: '
|
|
697
|
-
console.log(
|
|
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`);
|
|
698
750
|
} catch {
|
|
699
|
-
console.log(` ${c.
|
|
751
|
+
console.log(` ${c.dim}Run ${c.reset}${c.pink}fluxy daemon start${c.reset}${c.dim} to launch.${c.reset}\n`);
|
|
700
752
|
}
|
|
701
753
|
} else {
|
|
702
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
package/supervisor/backend.ts
CHANGED
|
@@ -25,7 +25,18 @@ export function spawnBackend(port: number): ChildProcess {
|
|
|
25
25
|
// Clear log file on each restart — only keeps current run
|
|
26
26
|
try { fs.writeFileSync(LOG_FILE, ''); } catch {}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// Wrap the backend in an inline loader that:
|
|
29
|
+
// 1. Dynamically imports the user's backend (tsx handles .ts compilation)
|
|
30
|
+
// 2. Adds a keepalive timer so the event loop never drains (fixes exit code 0 under systemd)
|
|
31
|
+
// 3. Catches and logs import errors instead of silently exiting
|
|
32
|
+
const backendUrl = 'file://' + backendPath.replace(/\\/g, '/');
|
|
33
|
+
const wrapper = [
|
|
34
|
+
`import('${backendUrl}')`,
|
|
35
|
+
` .catch(e => { console.error('[backend] Fatal:', e); process.exit(1); });`,
|
|
36
|
+
`setInterval(() => {}, 60000);`,
|
|
37
|
+
].join('\n');
|
|
38
|
+
|
|
39
|
+
child = spawn(process.execPath, ['--import', 'tsx/esm', '--input-type=module', '-e', wrapper], {
|
|
29
40
|
cwd: path.join(PKG_DIR, 'workspace'),
|
|
30
41
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
31
42
|
env: { ...process.env, BACKEND_PORT: String(port) },
|