repoburg 1.0.64 → 1.0.66
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/.rgignore +1 -0
- package/backend/.env +2 -2
- package/backend/dist/context-generation/context-generation.service.js +8 -8
- package/backend/dist/context-generation/context-generation.service.js.map +1 -1
- package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
- package/daemon/dist/api/services.js +1 -1
- package/package.json +1 -1
- package/platform-cli.js +12 -367
|
@@ -42,7 +42,7 @@ router.post('/:name/stop', async (req, res) => {
|
|
|
42
42
|
res.status(404).json({ error: message });
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
|
-
router.post('/:name/restart',
|
|
45
|
+
router.post('/:name/restart', async (req, res) => {
|
|
46
46
|
try {
|
|
47
47
|
const service = await serviceManager_1.serviceManager.restart(req.params.name);
|
|
48
48
|
res.json(service);
|
package/package.json
CHANGED
package/platform-cli.js
CHANGED
|
@@ -5,11 +5,9 @@ const axios = require('axios');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const fs = require('fs/promises');
|
|
7
7
|
const packageJson = require('./package.json');
|
|
8
|
-
const pm2 = require('pm2');
|
|
9
8
|
const { spawn } = require('child_process');
|
|
10
9
|
|
|
11
10
|
const DAEMON_BASE_URL = 'http://localhost:9998';
|
|
12
|
-
const DAEMON_PM2_NAME = 'repoburg-daemon';
|
|
13
11
|
|
|
14
12
|
// Uncomment the next line to use localhost for development
|
|
15
13
|
// const FRONTEND_BASE_URL ='http://localhost:3001';
|
|
@@ -35,38 +33,6 @@ const handleApiError = async (error) => {
|
|
|
35
33
|
process.exit(1);
|
|
36
34
|
};
|
|
37
35
|
|
|
38
|
-
const connectToPm2 = () => new Promise((resolve, reject) => {
|
|
39
|
-
pm2.connect(err => {
|
|
40
|
-
if (err) return reject(new Error(`Could not connect to pm2: ${err.message}`));
|
|
41
|
-
resolve();
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const startDaemon = async () => {
|
|
46
|
-
const { default: ora } = await import('ora');
|
|
47
|
-
const spinner = ora('Starting daemon process...').start();
|
|
48
|
-
try {
|
|
49
|
-
await connectToPm2();
|
|
50
|
-
await new Promise((resolve, reject) => {
|
|
51
|
-
pm2.start({
|
|
52
|
-
name: DAEMON_PM2_NAME,
|
|
53
|
-
script: 'repoburg-daemon',
|
|
54
|
-
}, (err) => {
|
|
55
|
-
pm2.disconnect();
|
|
56
|
-
if (err) {
|
|
57
|
-
const errMsg = err.message || err.msg;
|
|
58
|
-
return reject(new Error(`Error starting daemon with pm2: ${errMsg || err}`));
|
|
59
|
-
}
|
|
60
|
-
resolve();
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
spinner.succeed('Daemon process started via pm2.');
|
|
64
|
-
} catch (error) {
|
|
65
|
-
spinner.fail('Failed to start daemon.');
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
36
|
program
|
|
71
37
|
.name('repoburg')
|
|
72
38
|
.description('CLI to manage the Repoburg Platform Daemon and services.')
|
|
@@ -172,51 +138,8 @@ program
|
|
|
172
138
|
};
|
|
173
139
|
|
|
174
140
|
try {
|
|
175
|
-
// 1. Check daemon status
|
|
176
|
-
|
|
177
|
-
try {
|
|
178
|
-
authResponse = await axios.get(`${DAEMON_BASE_URL}/auth/status`);
|
|
179
|
-
} catch (error) {
|
|
180
|
-
if (error.request && !error.response) {
|
|
181
|
-
// console.log(chalk.yellow('Daemon not reachable. Attempting to start it now...'));
|
|
182
|
-
|
|
183
|
-
// TODO restore here once pm2 daemon bug is fixed
|
|
184
|
-
// await startDaemon();
|
|
185
|
-
//
|
|
186
|
-
// const pollForDaemonHealth = (timeout = 30000) => {
|
|
187
|
-
// const spinner = ora('Waiting for daemon to become healthy...').start();
|
|
188
|
-
// return new Promise((resolve, reject) => {
|
|
189
|
-
// const startTime = Date.now();
|
|
190
|
-
// const pollInterval = setInterval(async () => {
|
|
191
|
-
// if (Date.now() - startTime > timeout) {
|
|
192
|
-
// clearInterval(pollInterval);
|
|
193
|
-
// spinner.fail('Daemon health check timed out.');
|
|
194
|
-
// return reject(new Error('Daemon health check timed out. Check logs with `repoburg daemon logs`.'));
|
|
195
|
-
// }
|
|
196
|
-
// try {
|
|
197
|
-
// await axios.get(`${DAEMON_BASE_URL}/health`);
|
|
198
|
-
// clearInterval(pollInterval);
|
|
199
|
-
// spinner.succeed('Daemon is healthy.');
|
|
200
|
-
// resolve();
|
|
201
|
-
// } catch (e) {
|
|
202
|
-
// // ignore until timeout
|
|
203
|
-
// }
|
|
204
|
-
// }, 1000)
|
|
205
|
-
// });
|
|
206
|
-
// };
|
|
207
|
-
//
|
|
208
|
-
// await pollForDaemonHealth();
|
|
209
|
-
|
|
210
|
-
// try {
|
|
211
|
-
// authResponse = await axios.get(`${DAEMON_BASE_URL}/auth/status`);
|
|
212
|
-
// } catch (retryError) {
|
|
213
|
-
// console.error(chalk.red('Daemon started but is not responding correctly.'));
|
|
214
|
-
// return handleApiError(retryError);
|
|
215
|
-
// }
|
|
216
|
-
} else {
|
|
217
|
-
// return handleApiError(error);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
141
|
+
// 1. Check daemon status. This will fail if the daemon isn't running.
|
|
142
|
+
const authResponse = await axios.get(`${DAEMON_BASE_URL}/auth/status`);
|
|
220
143
|
|
|
221
144
|
if (authResponse.data.status !== 'authorized') {
|
|
222
145
|
console.log(chalk.yellow(`Authentication required. Status: ${authResponse.data.status}.`));
|
|
@@ -437,307 +360,29 @@ program
|
|
|
437
360
|
});
|
|
438
361
|
};
|
|
439
362
|
|
|
440
|
-
const pollForDaemonHealth = (timeout = 30000) => {
|
|
441
|
-
const spinner = ora('Waiting for daemon to become healthy...').start();
|
|
442
|
-
return new Promise((resolve, reject) => {
|
|
443
|
-
const startTime = Date.now();
|
|
444
|
-
const pollInterval = setInterval(async () => {
|
|
445
|
-
if (Date.now() - startTime > timeout) {
|
|
446
|
-
clearInterval(pollInterval);
|
|
447
|
-
spinner.fail('Daemon health check timed out.');
|
|
448
|
-
return reject(new Error('Daemon health check timed out.'));
|
|
449
|
-
}
|
|
450
|
-
try {
|
|
451
|
-
await axios.get(`${DAEMON_BASE_URL}/health`);
|
|
452
|
-
clearInterval(pollInterval);
|
|
453
|
-
spinner.succeed('Daemon is healthy.');
|
|
454
|
-
resolve();
|
|
455
|
-
} catch (e) {
|
|
456
|
-
// ignore until timeout
|
|
457
|
-
}
|
|
458
|
-
}, 1000)
|
|
459
|
-
});
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
let runningServices = [];
|
|
463
|
-
|
|
464
363
|
try {
|
|
465
|
-
|
|
466
|
-
const listSpinner = ora('Fetching list of running services...').start();
|
|
364
|
+
console.log(chalk.cyan('Stopping any running services before update...'));
|
|
467
365
|
try {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
366
|
+
// We don't care about the result, just that we tried to stop them.
|
|
367
|
+
await axios.post(`${DAEMON_BASE_URL}/services/remove-all`);
|
|
368
|
+
console.log(chalk.cyan('Services stopped.'));
|
|
471
369
|
} catch (error) {
|
|
472
|
-
|
|
473
|
-
|
|
370
|
+
if (error.request && !error.response) { // Daemon not running
|
|
371
|
+
console.log(chalk.yellow('Daemon not reachable, skipping service stop.'));
|
|
474
372
|
} else {
|
|
475
|
-
|
|
476
|
-
throw error;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// 2. Stop all running services
|
|
481
|
-
if (runningServices.length > 0) {
|
|
482
|
-
const stopSpinner = ora('Stopping running backend services...').start();
|
|
483
|
-
for (const name of runningServices) {
|
|
484
|
-
stopSpinner.text = `Stopping ${name}...`;
|
|
485
|
-
await axios.post(`${DAEMON_BASE_URL}/services/${name}/stop`);
|
|
373
|
+
console.warn(chalk.yellow('Could not stop services, continuing with update...'));
|
|
486
374
|
}
|
|
487
|
-
stopSpinner.succeed('All backend services stopped.');
|
|
488
375
|
}
|
|
489
|
-
|
|
490
|
-
// 3. Stop the daemon
|
|
491
|
-
const stopDaemonSpinner = ora('Stopping daemon...').start();
|
|
492
|
-
await new Promise((resolve, reject) => {
|
|
493
|
-
pm2.connect(err => {
|
|
494
|
-
if (err) return reject(err);
|
|
495
|
-
pm2.stop(DAEMON_PM2_NAME, (err) => {
|
|
496
|
-
pm2.disconnect();
|
|
497
|
-
const errMsg = err ? (err.message || err.msg) : null;
|
|
498
|
-
if (err && !(errMsg && errMsg.includes('not found'))) {
|
|
499
|
-
return reject(err);
|
|
500
|
-
}
|
|
501
|
-
resolve();
|
|
502
|
-
});
|
|
503
|
-
});
|
|
504
|
-
});
|
|
505
|
-
stopDaemonSpinner.succeed('Daemon stopped.');
|
|
506
|
-
|
|
507
|
-
// 4. Run update
|
|
376
|
+
|
|
508
377
|
await runCommand('npm', ['i', '-g', 'repoburg@latest']);
|
|
509
378
|
|
|
510
|
-
// 5. Start the daemon
|
|
511
|
-
const startDaemonSpinner = ora('Starting daemon...').start();
|
|
512
|
-
await new Promise((resolve, reject) => {
|
|
513
|
-
pm2.connect(err => {
|
|
514
|
-
if (err) return reject(err);
|
|
515
|
-
pm2.start({ name: DAEMON_PM2_NAME, script: 'repoburg-daemon' }, (err) => {
|
|
516
|
-
pm2.disconnect();
|
|
517
|
-
if (err) return reject(err);
|
|
518
|
-
resolve();
|
|
519
|
-
});
|
|
520
|
-
});
|
|
521
|
-
});
|
|
522
|
-
startDaemonSpinner.succeed('Daemon process started.');
|
|
523
|
-
await pollForDaemonHealth();
|
|
524
|
-
|
|
525
|
-
// 6. Restart services
|
|
526
|
-
if (runningServices.length > 0) {
|
|
527
|
-
const restartSpinner = ora('Restarting backend services...').start();
|
|
528
|
-
for (const name of runningServices) {
|
|
529
|
-
restartSpinner.text = `Restarting ${name}...`;
|
|
530
|
-
await axios.post(`${DAEMON_BASE_URL}/services/${name}/restart`);
|
|
531
|
-
}
|
|
532
|
-
restartSpinner.succeed('All backend services restarted.');
|
|
533
|
-
}
|
|
534
|
-
|
|
535
379
|
console.log(chalk.green.bold('\n✓ Repoburg update complete!'));
|
|
380
|
+
console.log(chalk.cyan('Please restart the Repoburg Daemon via the desktop application to apply the changes.'));
|
|
536
381
|
|
|
537
382
|
} catch (error) {
|
|
538
383
|
console.error(chalk.red('\nUpdate process failed:'), error.message);
|
|
539
|
-
console.error(chalk.yellow('Your services might be in a stopped state. Please check with `repoburg list` and `repoburg daemon status`.'));
|
|
540
|
-
process.exit(1);
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
// --- Daemon Management ---
|
|
545
|
-
const daemon = program.command('daemon')
|
|
546
|
-
.description('Manage the Repoburg Platform Daemon process (the service running on port 9998).');
|
|
547
|
-
|
|
548
|
-
daemon
|
|
549
|
-
.command('start')
|
|
550
|
-
.description('Start the daemon process in the background using pm2.')
|
|
551
|
-
.action(async () => {
|
|
552
|
-
const { default: chalk } = await import('chalk');
|
|
553
|
-
try {
|
|
554
|
-
await startDaemon();
|
|
555
|
-
console.log(chalk.cyan(`Run 'repoburg daemon status' to check its state.`));
|
|
556
|
-
} catch (error) {
|
|
557
|
-
console.error(chalk.red('An error occurred:'), error.message);
|
|
558
|
-
process.exit(1);
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
daemon
|
|
563
|
-
.command('status')
|
|
564
|
-
.description('Show the status of the daemon process.')
|
|
565
|
-
.action(async () => {
|
|
566
|
-
const { default: chalk } = await import('chalk');
|
|
567
|
-
const Table = require('cli-table3');
|
|
568
|
-
try {
|
|
569
|
-
await connectToPm2();
|
|
570
|
-
pm2.list((err, list) => {
|
|
571
|
-
if (err) {
|
|
572
|
-
console.error(chalk.red('Error listing pm2 processes:'), err.message || err);
|
|
573
|
-
pm2.disconnect();
|
|
574
|
-
process.exit(1);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
const daemonProcess = list.find(p => p.name === DAEMON_PM2_NAME);
|
|
578
|
-
if (!daemonProcess) {
|
|
579
|
-
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" is not running or not managed by pm2.`));
|
|
580
|
-
} else {
|
|
581
|
-
const table = new Table({
|
|
582
|
-
head: ['NAME', 'STATUS', 'PID', 'CPU', 'MEMORY'].map(h => chalk.cyan(h)),
|
|
583
|
-
colWidths: [20, 12, 8, 8, 15]
|
|
584
|
-
});
|
|
585
|
-
const status = daemonProcess.pm2_env.status;
|
|
586
|
-
const statusColor = status === 'online' ? chalk.green : chalk.yellow;
|
|
587
|
-
table.push([
|
|
588
|
-
daemonProcess.name,
|
|
589
|
-
statusColor(status),
|
|
590
|
-
daemonProcess.pid,
|
|
591
|
-
`${daemonProcess.monit.cpu || 0}%`,
|
|
592
|
-
`${(daemonProcess.monit.memory / 1024 / 1024).toFixed(1)} MB`
|
|
593
|
-
]);
|
|
594
|
-
console.log(table.toString());
|
|
595
|
-
}
|
|
596
|
-
pm2.disconnect();
|
|
597
|
-
});
|
|
598
|
-
} catch (error) {
|
|
599
|
-
console.error(chalk.red('An error occurred:'), error.message);
|
|
600
|
-
process.exit(1);
|
|
601
|
-
}
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
daemon
|
|
605
|
-
.command('stop')
|
|
606
|
-
.description('Stop the daemon process.')
|
|
607
|
-
.action(async () => {
|
|
608
|
-
const { default: chalk } = await import('chalk');
|
|
609
|
-
try {
|
|
610
|
-
await connectToPm2();
|
|
611
|
-
console.log(chalk.cyan(`Stopping daemon process "${DAEMON_PM2_NAME}"...`));
|
|
612
|
-
pm2.stop(DAEMON_PM2_NAME, (err) => {
|
|
613
|
-
const errMsg = err ? (err.message || err.msg) : null;
|
|
614
|
-
if (err) {
|
|
615
|
-
if (errMsg && errMsg.includes('not found')) {
|
|
616
|
-
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" not found or not running.`));
|
|
617
|
-
} else {
|
|
618
|
-
console.error(chalk.red('Error stopping daemon:'), errMsg || err);
|
|
619
|
-
pm2.disconnect();
|
|
620
|
-
process.exit(1);
|
|
621
|
-
}
|
|
622
|
-
} else {
|
|
623
|
-
console.log(chalk.green('✓ Daemon stopped.'));
|
|
624
|
-
}
|
|
625
|
-
pm2.disconnect();
|
|
626
|
-
});
|
|
627
|
-
} catch (error) {
|
|
628
|
-
console.error(chalk.red('An error occurred:'), error.message);
|
|
629
|
-
process.exit(1);
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
daemon
|
|
634
|
-
.command('restart')
|
|
635
|
-
.description('Restart the daemon process.')
|
|
636
|
-
.action(async () => {
|
|
637
|
-
const { default: chalk } = await import('chalk');
|
|
638
|
-
try {
|
|
639
|
-
await connectToPm2();
|
|
640
|
-
console.log(chalk.cyan(`Restarting daemon process "${DAEMON_PM2_NAME}"...`));
|
|
641
|
-
pm2.restart(DAEMON_PM2_NAME, (err) => {
|
|
642
|
-
const errMsg = err ? (err.message || err.msg) : null;
|
|
643
|
-
if (err) {
|
|
644
|
-
if (errMsg && errMsg.includes('not found')) {
|
|
645
|
-
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" not found. Use 'repoburg daemon start' instead.`));
|
|
646
|
-
} else {
|
|
647
|
-
console.error(chalk.red('Error restarting daemon:'), errMsg || err);
|
|
648
|
-
pm2.disconnect();
|
|
649
|
-
process.exit(1);
|
|
650
|
-
}
|
|
651
|
-
} else {
|
|
652
|
-
console.log(chalk.green('✓ Daemon restarted.'));
|
|
653
|
-
}
|
|
654
|
-
pm2.disconnect();
|
|
655
|
-
});
|
|
656
|
-
} catch (error) {
|
|
657
|
-
console.error(chalk.red('An error occurred:'), error.message);
|
|
658
|
-
process.exit(1);
|
|
659
|
-
}
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
daemon
|
|
663
|
-
.command('delete')
|
|
664
|
-
.alias('rm')
|
|
665
|
-
.description('Stop and delete the daemon process from pm2.')
|
|
666
|
-
.action(async () => {
|
|
667
|
-
const { default: chalk } = await import('chalk');
|
|
668
|
-
try {
|
|
669
|
-
await connectToPm2();
|
|
670
|
-
console.log(chalk.cyan(`Deleting daemon process "${DAEMON_PM2_NAME}" from pm2...`));
|
|
671
|
-
pm2.delete(DAEMON_PM2_NAME, (err) => {
|
|
672
|
-
const errMsg = err ? (err.message || err.msg) : null;
|
|
673
|
-
if (err) {
|
|
674
|
-
if (errMsg && errMsg.includes('not found')) {
|
|
675
|
-
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" not found.`));
|
|
676
|
-
} else {
|
|
677
|
-
console.error(chalk.red('Error deleting daemon:'), errMsg || err);
|
|
678
|
-
pm2.disconnect();
|
|
679
|
-
process.exit(1);
|
|
680
|
-
}
|
|
681
|
-
} else {
|
|
682
|
-
console.log(chalk.green('✓ Daemon process deleted from pm2.'));
|
|
683
|
-
}
|
|
684
|
-
pm2.disconnect();
|
|
685
|
-
});
|
|
686
|
-
} catch (error) {
|
|
687
|
-
console.error(chalk.red('An error occurred:'), error.message);
|
|
688
|
-
process.exit(1);
|
|
689
|
-
}
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
daemon
|
|
693
|
-
.command('logs')
|
|
694
|
-
.description('Display logs for the daemon process.')
|
|
695
|
-
.action(async () => {
|
|
696
|
-
const { default: chalk } = await import('chalk');
|
|
697
|
-
try {
|
|
698
|
-
await connectToPm2();
|
|
699
|
-
pm2.describe(DAEMON_PM2_NAME, async (err, description) => {
|
|
700
|
-
if (err) {
|
|
701
|
-
console.error(chalk.red('Error describing daemon process:'), err.message || err);
|
|
702
|
-
pm2.disconnect();
|
|
703
|
-
process.exit(1);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
if (!description || description.length === 0) {
|
|
707
|
-
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" not found.`));
|
|
708
|
-
pm2.disconnect();
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
const proc = description[0];
|
|
713
|
-
const outLogPath = proc.pm2_env.pm_out_log_path;
|
|
714
|
-
const errLogPath = proc.pm2_env.pm_err_log_path;
|
|
715
|
-
|
|
716
|
-
console.log(chalk.cyan(`--- Logs for ${DAEMON_PM2_NAME} ---`));
|
|
717
|
-
|
|
718
|
-
const readAndPrint = async (filePath, logType) => {
|
|
719
|
-
try {
|
|
720
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
721
|
-
console.log(chalk.bold.yellow(`\n--- ${logType} Log (${filePath}) ---\n`));
|
|
722
|
-
console.log(content || chalk.gray('(empty)'));
|
|
723
|
-
} catch (e) {
|
|
724
|
-
if (e.code !== 'ENOENT') {
|
|
725
|
-
throw e;
|
|
726
|
-
}
|
|
727
|
-
console.log(chalk.bold.yellow(`\n--- ${logType} Log (${filePath}) ---\n`));
|
|
728
|
-
console.log(chalk.gray('(not found)'));
|
|
729
|
-
}
|
|
730
|
-
};
|
|
731
|
-
|
|
732
|
-
await readAndPrint(outLogPath, 'Output');
|
|
733
|
-
await readAndPrint(errLogPath, 'Error');
|
|
734
|
-
|
|
735
|
-
pm2.disconnect();
|
|
736
|
-
});
|
|
737
|
-
} catch (error) {
|
|
738
|
-
console.error(chalk.red('An error occurred:'), error.message);
|
|
739
384
|
process.exit(1);
|
|
740
385
|
}
|
|
741
386
|
});
|
|
742
387
|
|
|
743
|
-
program.parse(process.argv);
|
|
388
|
+
program.parse(process.argv);
|