fluxy-bot 0.5.36 → 0.5.39
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 +81 -56
- package/package.json +1 -1
- package/workspace/backend/index.ts +11 -1
package/bin/cli.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawn, execSync, spawnSync } from 'child_process';
|
|
4
|
-
import readline from 'readline';
|
|
5
4
|
import fs from 'fs';
|
|
6
5
|
import path from 'path';
|
|
7
6
|
import os from 'os';
|
|
@@ -70,8 +69,9 @@ ExecStart=${nodePath} --import tsx/esm ${dataDir}/supervisor/index.ts
|
|
|
70
69
|
Restart=on-failure
|
|
71
70
|
RestartSec=5
|
|
72
71
|
Environment=HOME=${home}
|
|
73
|
-
Environment=NODE_ENV=
|
|
74
|
-
Environment=
|
|
72
|
+
Environment=NODE_ENV=development
|
|
73
|
+
Environment=NODE_PATH=${dataDir}/node_modules
|
|
74
|
+
Environment=PATH=${nodeBinDir}:${dataDir}/node_modules/.bin:/usr/local/bin:/usr/bin:/bin
|
|
75
75
|
StandardOutput=journal
|
|
76
76
|
StandardError=journal
|
|
77
77
|
SyslogIdentifier=fluxy
|
|
@@ -81,16 +81,6 @@ WantedBy=multi-user.target
|
|
|
81
81
|
`;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function askQuestion(query) {
|
|
85
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
86
|
-
return new Promise((resolve) => {
|
|
87
|
-
rl.question(query, (answer) => {
|
|
88
|
-
rl.close();
|
|
89
|
-
resolve(answer);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
84
|
// ── UI helpers ──
|
|
95
85
|
|
|
96
86
|
const c = {
|
|
@@ -215,16 +205,28 @@ function finalMessage(tunnelUrl, relayUrl) {
|
|
|
215
205
|
${c.pink}${c.bold}${link(relayUrl)}${c.reset}`);
|
|
216
206
|
}
|
|
217
207
|
|
|
218
|
-
|
|
208
|
+
if (os.platform() === 'linux') {
|
|
209
|
+
console.log(`
|
|
210
|
+
${c.dim}─────────────────────────────────${c.reset}
|
|
211
|
+
|
|
212
|
+
${c.bold}${c.white}Commands:${c.reset}
|
|
213
|
+
${c.dim}Status${c.reset} ${c.pink}fluxy daemon status${c.reset}
|
|
214
|
+
${c.dim}Logs${c.reset} ${c.pink}fluxy daemon logs${c.reset}
|
|
215
|
+
${c.dim}Restart${c.reset} ${c.pink}fluxy daemon restart${c.reset}
|
|
216
|
+
${c.dim}Stop${c.reset} ${c.pink}fluxy daemon stop${c.reset}
|
|
217
|
+
${c.dim}Update${c.reset} ${c.pink}fluxy update${c.reset}
|
|
218
|
+
`);
|
|
219
|
+
} else {
|
|
220
|
+
console.log(`
|
|
219
221
|
${c.dim}─────────────────────────────────${c.reset}
|
|
220
222
|
|
|
221
223
|
${c.bold}${c.white}Commands:${c.reset}
|
|
222
224
|
${c.dim}Status${c.reset} ${c.pink}fluxy status${c.reset}
|
|
223
|
-
${c.dim}Update${c.reset} ${c.pink}fluxy update${c.reset}
|
|
224
|
-
${c.dim}Daemon${c.reset} ${c.pink}fluxy daemon${c.reset}` : ''}
|
|
225
|
+
${c.dim}Update${c.reset} ${c.pink}fluxy update${c.reset}
|
|
225
226
|
|
|
226
|
-
${c.dim}Press Ctrl+C to stop
|
|
227
|
+
${c.dim}Press Ctrl+C to stop${c.reset}
|
|
227
228
|
`);
|
|
229
|
+
}
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
// ── Steps ──
|
|
@@ -387,6 +389,9 @@ async function init() {
|
|
|
387
389
|
|
|
388
390
|
createConfig();
|
|
389
391
|
|
|
392
|
+
const isLinux = os.platform() === 'linux';
|
|
393
|
+
const hasSystemd = isLinux && (() => { try { execSync('systemctl --version', { stdio: 'ignore' }); return true; } catch { return false; } })();
|
|
394
|
+
|
|
390
395
|
const steps = [
|
|
391
396
|
'Creating config',
|
|
392
397
|
'Installing cloudflared',
|
|
@@ -394,6 +399,7 @@ async function init() {
|
|
|
394
399
|
'Connecting tunnel',
|
|
395
400
|
'Verifying connection',
|
|
396
401
|
'Preparing dashboard',
|
|
402
|
+
...(hasSystemd ? ['Setting up auto-start daemon'] : []),
|
|
397
403
|
];
|
|
398
404
|
|
|
399
405
|
const stepper = new Stepper(steps);
|
|
@@ -425,28 +431,32 @@ async function init() {
|
|
|
425
431
|
await Promise.race([viteWarm, new Promise(r => setTimeout(r, 30_000))]);
|
|
426
432
|
stepper.advance();
|
|
427
433
|
|
|
434
|
+
// Install systemd daemon on Linux
|
|
435
|
+
if (hasSystemd) {
|
|
436
|
+
child.removeAllListeners('exit');
|
|
437
|
+
child.kill('SIGTERM');
|
|
438
|
+
await new Promise((r) => setTimeout(r, 4000));
|
|
439
|
+
const nodePath = process.execPath;
|
|
440
|
+
const realHome = os.homedir();
|
|
441
|
+
const res = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
|
|
442
|
+
stdio: 'pipe',
|
|
443
|
+
env: { ...process.env, FLUXY_NODE_PATH: nodePath, FLUXY_REAL_HOME: realHome },
|
|
444
|
+
});
|
|
445
|
+
stepper.advance();
|
|
446
|
+
stepper.finish();
|
|
447
|
+
finalMessage(tunnelUrl, relayUrl);
|
|
448
|
+
if (res.status === 0) {
|
|
449
|
+
console.log(` ${c.blue}✔${c.reset} Daemon installed — Fluxy will auto-start on boot.`);
|
|
450
|
+
} else {
|
|
451
|
+
console.log(` ${c.yellow}⚠${c.reset} Daemon install failed. Run ${c.pink}fluxy daemon install${c.reset} manually.`);
|
|
452
|
+
}
|
|
453
|
+
console.log('');
|
|
454
|
+
process.exit(res.status ?? 0);
|
|
455
|
+
}
|
|
456
|
+
|
|
428
457
|
stepper.finish();
|
|
429
458
|
finalMessage(tunnelUrl, relayUrl);
|
|
430
459
|
|
|
431
|
-
// Offer daemon install on Linux
|
|
432
|
-
if (os.platform() === 'linux' && process.stdin.isTTY) {
|
|
433
|
-
try {
|
|
434
|
-
const answer = await askQuestion(` ${c.bold}Install as a system daemon (auto-start on boot)?${c.reset} ${c.dim}[Y/n]${c.reset} `);
|
|
435
|
-
if (!answer || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
436
|
-
console.log(`\n ${c.dim}Stopping foreground server...${c.reset}`);
|
|
437
|
-
child.kill('SIGTERM');
|
|
438
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
439
|
-
const nodePath = process.execPath;
|
|
440
|
-
const realHome = os.homedir();
|
|
441
|
-
const result = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
|
|
442
|
-
stdio: 'inherit',
|
|
443
|
-
env: { ...process.env, FLUXY_NODE_PATH: nodePath, FLUXY_REAL_HOME: realHome },
|
|
444
|
-
});
|
|
445
|
-
process.exit(result.status ?? 0);
|
|
446
|
-
}
|
|
447
|
-
} catch {}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
460
|
child.stdout.on('data', (d) => {
|
|
451
461
|
process.stdout.write(` ${c.dim}${d.toString().trim()}${c.reset}\n`);
|
|
452
462
|
});
|
|
@@ -462,9 +472,20 @@ async function start() {
|
|
|
462
472
|
return init();
|
|
463
473
|
}
|
|
464
474
|
|
|
475
|
+
const isLinux = os.platform() === 'linux';
|
|
476
|
+
const hasSystemd = isLinux && (() => { try { execSync('systemctl --version', { stdio: 'ignore' }); return true; } catch { return false; } })();
|
|
477
|
+
const needsDaemon = hasSystemd && !isServiceInstalled();
|
|
478
|
+
|
|
465
479
|
banner();
|
|
466
480
|
|
|
467
|
-
const steps = [
|
|
481
|
+
const steps = [
|
|
482
|
+
'Loading config',
|
|
483
|
+
'Starting server',
|
|
484
|
+
'Connecting tunnel',
|
|
485
|
+
'Verifying connection',
|
|
486
|
+
'Preparing dashboard',
|
|
487
|
+
...(needsDaemon ? ['Setting up auto-start daemon'] : []),
|
|
488
|
+
];
|
|
468
489
|
const stepper = new Stepper(steps);
|
|
469
490
|
stepper.start();
|
|
470
491
|
|
|
@@ -488,28 +509,32 @@ async function start() {
|
|
|
488
509
|
await Promise.race([viteWarm, new Promise(r => setTimeout(r, 30_000))]);
|
|
489
510
|
stepper.advance();
|
|
490
511
|
|
|
512
|
+
// Install systemd daemon on Linux if not already installed
|
|
513
|
+
if (needsDaemon) {
|
|
514
|
+
child.removeAllListeners('exit');
|
|
515
|
+
child.kill('SIGTERM');
|
|
516
|
+
await new Promise((r) => setTimeout(r, 4000));
|
|
517
|
+
const nodePath = process.execPath;
|
|
518
|
+
const realHome = os.homedir();
|
|
519
|
+
const res = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
|
|
520
|
+
stdio: 'pipe',
|
|
521
|
+
env: { ...process.env, FLUXY_NODE_PATH: nodePath, FLUXY_REAL_HOME: realHome },
|
|
522
|
+
});
|
|
523
|
+
stepper.advance();
|
|
524
|
+
stepper.finish();
|
|
525
|
+
finalMessage(tunnelUrl, relayUrl);
|
|
526
|
+
if (res.status === 0) {
|
|
527
|
+
console.log(` ${c.blue}✔${c.reset} Daemon installed — Fluxy will auto-start on boot.`);
|
|
528
|
+
} else {
|
|
529
|
+
console.log(` ${c.yellow}⚠${c.reset} Daemon install failed. Run ${c.pink}fluxy daemon install${c.reset} manually.`);
|
|
530
|
+
}
|
|
531
|
+
console.log('');
|
|
532
|
+
process.exit(res.status ?? 0);
|
|
533
|
+
}
|
|
534
|
+
|
|
491
535
|
stepper.finish();
|
|
492
536
|
finalMessage(tunnelUrl, relayUrl);
|
|
493
537
|
|
|
494
|
-
// Offer daemon install on Linux if not already installed
|
|
495
|
-
if (os.platform() === 'linux' && !isServiceInstalled() && process.stdin.isTTY) {
|
|
496
|
-
try {
|
|
497
|
-
const answer = await askQuestion(` ${c.bold}Install as a system daemon (auto-start on boot)?${c.reset} ${c.dim}[Y/n]${c.reset} `);
|
|
498
|
-
if (!answer || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
499
|
-
console.log(`\n ${c.dim}Stopping foreground server...${c.reset}`);
|
|
500
|
-
child.kill('SIGTERM');
|
|
501
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
502
|
-
const nodePath = process.execPath;
|
|
503
|
-
const realHome = os.homedir();
|
|
504
|
-
const result = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
|
|
505
|
-
stdio: 'inherit',
|
|
506
|
-
env: { ...process.env, FLUXY_NODE_PATH: nodePath, FLUXY_REAL_HOME: realHome },
|
|
507
|
-
});
|
|
508
|
-
process.exit(result.status ?? 0);
|
|
509
|
-
}
|
|
510
|
-
} catch {}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
538
|
child.stdout.on('data', (d) => {
|
|
514
539
|
process.stdout.write(` ${c.dim}${d.toString().trim()}${c.reset}\n`);
|
|
515
540
|
});
|
package/package.json
CHANGED
|
@@ -37,6 +37,16 @@ app.use((_req, res) => {
|
|
|
37
37
|
res.status(404).json({ error: 'Not found' });
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
app.listen(PORT, () => {
|
|
40
|
+
const server = app.listen(PORT, () => {
|
|
41
41
|
console.log(`[backend] Listening on port ${PORT}`);
|
|
42
42
|
});
|
|
43
|
+
|
|
44
|
+
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
45
|
+
console.error(`[backend] Server error: ${err.message}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Keep process alive — prevent exit on unhandled rejection
|
|
50
|
+
process.on('unhandledRejection', (err) => {
|
|
51
|
+
console.error('[backend] Unhandled rejection:', err);
|
|
52
|
+
});
|