fluxy-bot 0.5.34 → 0.5.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/bin/cli.js +84 -35
  2. package/package.json +1 -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';
@@ -81,16 +80,6 @@ WantedBy=multi-user.target
81
80
  `;
82
81
  }
83
82
 
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
83
  // ── UI helpers ──
95
84
 
96
85
  const c = {
@@ -215,10 +204,28 @@ function finalMessage(tunnelUrl, relayUrl) {
215
204
  ${c.pink}${c.bold}${link(relayUrl)}${c.reset}`);
216
205
  }
217
206
 
218
- console.log(`
207
+ if (os.platform() === 'linux') {
208
+ console.log(`
219
209
  ${c.dim}─────────────────────────────────${c.reset}
220
- ${c.dim}Press Ctrl+C to stop the bot${c.reset}
210
+
211
+ ${c.bold}${c.white}Commands:${c.reset}
212
+ ${c.dim}Status${c.reset} ${c.pink}fluxy daemon status${c.reset}
213
+ ${c.dim}Logs${c.reset} ${c.pink}fluxy daemon logs${c.reset}
214
+ ${c.dim}Restart${c.reset} ${c.pink}fluxy daemon restart${c.reset}
215
+ ${c.dim}Stop${c.reset} ${c.pink}fluxy daemon stop${c.reset}
216
+ ${c.dim}Update${c.reset} ${c.pink}fluxy update${c.reset}
221
217
  `);
218
+ } else {
219
+ console.log(`
220
+ ${c.dim}─────────────────────────────────${c.reset}
221
+
222
+ ${c.bold}${c.white}Commands:${c.reset}
223
+ ${c.dim}Status${c.reset} ${c.pink}fluxy status${c.reset}
224
+ ${c.dim}Update${c.reset} ${c.pink}fluxy update${c.reset}
225
+
226
+ ${c.dim}Press Ctrl+C to stop${c.reset}
227
+ `);
228
+ }
222
229
  }
223
230
 
224
231
  // ── Steps ──
@@ -381,6 +388,9 @@ async function init() {
381
388
 
382
389
  createConfig();
383
390
 
391
+ const isLinux = os.platform() === 'linux';
392
+ const hasSystemd = isLinux && (() => { try { execSync('systemctl --version', { stdio: 'ignore' }); return true; } catch { return false; } })();
393
+
384
394
  const steps = [
385
395
  'Creating config',
386
396
  'Installing cloudflared',
@@ -388,6 +398,7 @@ async function init() {
388
398
  'Connecting tunnel',
389
399
  'Verifying connection',
390
400
  'Preparing dashboard',
401
+ ...(hasSystemd ? ['Setting up auto-start daemon'] : []),
391
402
  ];
392
403
 
393
404
  const stepper = new Stepper(steps);
@@ -419,28 +430,32 @@ async function init() {
419
430
  await Promise.race([viteWarm, new Promise(r => setTimeout(r, 30_000))]);
420
431
  stepper.advance();
421
432
 
433
+ // Install systemd daemon on Linux
434
+ if (hasSystemd) {
435
+ child.removeAllListeners('exit');
436
+ child.kill('SIGTERM');
437
+ await new Promise((r) => setTimeout(r, 2000));
438
+ const nodePath = process.execPath;
439
+ const realHome = os.homedir();
440
+ const res = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
441
+ stdio: 'pipe',
442
+ env: { ...process.env, FLUXY_NODE_PATH: nodePath, FLUXY_REAL_HOME: realHome },
443
+ });
444
+ stepper.advance();
445
+ stepper.finish();
446
+ finalMessage(tunnelUrl, relayUrl);
447
+ if (res.status === 0) {
448
+ console.log(` ${c.blue}✔${c.reset} Daemon installed — Fluxy will auto-start on boot.`);
449
+ } else {
450
+ console.log(` ${c.yellow}⚠${c.reset} Daemon install failed. Run ${c.pink}fluxy daemon install${c.reset} manually.`);
451
+ }
452
+ console.log('');
453
+ process.exit(res.status ?? 0);
454
+ }
455
+
422
456
  stepper.finish();
423
457
  finalMessage(tunnelUrl, relayUrl);
424
458
 
425
- // Offer daemon install on Linux
426
- if (os.platform() === 'linux' && process.stdin.isTTY) {
427
- try {
428
- const answer = await askQuestion(` ${c.bold}Install as a system daemon (auto-start on boot)?${c.reset} ${c.dim}[Y/n]${c.reset} `);
429
- if (!answer || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
430
- console.log(`\n ${c.dim}Stopping foreground server...${c.reset}`);
431
- child.kill('SIGTERM');
432
- await new Promise((r) => setTimeout(r, 2000));
433
- const nodePath = process.execPath;
434
- const realHome = os.homedir();
435
- const result = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
436
- stdio: 'inherit',
437
- env: { ...process.env, FLUXY_NODE_PATH: nodePath, FLUXY_REAL_HOME: realHome },
438
- });
439
- process.exit(result.status ?? 0);
440
- }
441
- } catch {}
442
- }
443
-
444
459
  child.stdout.on('data', (d) => {
445
460
  process.stdout.write(` ${c.dim}${d.toString().trim()}${c.reset}\n`);
446
461
  });
@@ -456,9 +471,20 @@ async function start() {
456
471
  return init();
457
472
  }
458
473
 
474
+ const isLinux = os.platform() === 'linux';
475
+ const hasSystemd = isLinux && (() => { try { execSync('systemctl --version', { stdio: 'ignore' }); return true; } catch { return false; } })();
476
+ const needsDaemon = hasSystemd && !isServiceInstalled();
477
+
459
478
  banner();
460
479
 
461
- const steps = ['Loading config', 'Starting server', 'Connecting tunnel', 'Verifying connection', 'Preparing dashboard'];
480
+ const steps = [
481
+ 'Loading config',
482
+ 'Starting server',
483
+ 'Connecting tunnel',
484
+ 'Verifying connection',
485
+ 'Preparing dashboard',
486
+ ...(needsDaemon ? ['Setting up auto-start daemon'] : []),
487
+ ];
462
488
  const stepper = new Stepper(steps);
463
489
  stepper.start();
464
490
 
@@ -482,6 +508,29 @@ async function start() {
482
508
  await Promise.race([viteWarm, new Promise(r => setTimeout(r, 30_000))]);
483
509
  stepper.advance();
484
510
 
511
+ // Install systemd daemon on Linux if not already installed
512
+ if (needsDaemon) {
513
+ child.removeAllListeners('exit');
514
+ child.kill('SIGTERM');
515
+ await new Promise((r) => setTimeout(r, 2000));
516
+ const nodePath = process.execPath;
517
+ const realHome = os.homedir();
518
+ const res = spawnSync(process.execPath, [process.argv[1], 'daemon', 'install'], {
519
+ stdio: 'pipe',
520
+ env: { ...process.env, FLUXY_NODE_PATH: nodePath, FLUXY_REAL_HOME: realHome },
521
+ });
522
+ stepper.advance();
523
+ stepper.finish();
524
+ finalMessage(tunnelUrl, relayUrl);
525
+ if (res.status === 0) {
526
+ console.log(` ${c.blue}✔${c.reset} Daemon installed — Fluxy will auto-start on boot.`);
527
+ } else {
528
+ console.log(` ${c.yellow}⚠${c.reset} Daemon install failed. Run ${c.pink}fluxy daemon install${c.reset} manually.`);
529
+ }
530
+ console.log('');
531
+ process.exit(res.status ?? 0);
532
+ }
533
+
485
534
  stepper.finish();
486
535
  finalMessage(tunnelUrl, relayUrl);
487
536
 
@@ -500,14 +549,14 @@ async function status() {
500
549
  const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
501
550
  const res = await fetch(`http://localhost:${config.port}/api/health`);
502
551
  const data = await res.json();
503
- console.log(`\n ${c.blue}●${c.reset} Bot is running`);
552
+ console.log(`\n ${c.blue}●${c.reset} Fluxy is running`);
504
553
  console.log(` ${c.dim}Uptime: ${data.uptime}s${c.reset}`);
505
554
  if (config.relay?.url) {
506
555
  console.log(` ${c.dim}URL: ${c.reset}${c.pink}${link(config.relay.url)}${c.reset}`);
507
556
  }
508
557
  console.log(` ${c.dim}Config: ${CONFIG_PATH}${c.reset}\n`);
509
558
  } catch {
510
- console.log(`\n ${c.dim}●${c.reset} Bot is not running.\n`);
559
+ console.log(`\n ${c.dim}●${c.reset} Fluxy is not running.\n`);
511
560
  }
512
561
  }
513
562
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxy-bot",
3
- "version": "0.5.34",
3
+ "version": "0.5.38",
4
4
  "description": "Self-hosted, self-evolving AI agent with its own dashboard.",
5
5
  "type": "module",
6
6
  "license": "MIT",