repoburg 1.0.50 → 1.0.51
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/backend/dist/app.controller.d.ts +1 -0
- package/backend/dist/app.service.d.ts +1 -0
- package/backend/dist/app.service.js +9 -0
- package/backend/dist/app.service.js.map +1 -1
- package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
- package/daemon/dist/api/system.js +25 -0
- package/daemon/dist/index.js +2 -0
- package/package.json +3 -3
- package/platform-cli.js +414 -14
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const express_1 = require("express");
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const checkAuth_1 = require("../middleware/checkAuth");
|
|
6
|
+
const router = (0, express_1.Router)();
|
|
7
|
+
router.post('/update', checkAuth_1.checkAuth, (req, res) => {
|
|
8
|
+
try {
|
|
9
|
+
console.log('[DAEMON] Received request to start update process.');
|
|
10
|
+
// Using `spawn` with `detached: true` and `stdio: 'ignore'` allows the parent (daemon)
|
|
11
|
+
// to exit while the child (update script) continues running. `unref()` is crucial.
|
|
12
|
+
const child = (0, child_process_1.spawn)('repoburg', ['update'], {
|
|
13
|
+
detached: true,
|
|
14
|
+
stdio: 'ignore',
|
|
15
|
+
shell: true,
|
|
16
|
+
});
|
|
17
|
+
child.unref();
|
|
18
|
+
res.status(202).json({ message: 'Update process initiated. The daemon and services will restart shortly.' });
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error('Failed to spawn update process:', error);
|
|
22
|
+
res.status(500).json({ error: 'Failed to start update process.' });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
exports.default = router;
|
package/daemon/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const serviceManager_1 = require("./serviceManager");
|
|
|
12
12
|
const services_1 = __importDefault(require("./api/services"));
|
|
13
13
|
const auth_1 = __importDefault(require("./api/auth"));
|
|
14
14
|
const registry_1 = __importDefault(require("./api/registry"));
|
|
15
|
+
const system_1 = __importDefault(require("./api/system"));
|
|
15
16
|
const PORT = 9998;
|
|
16
17
|
async function main() {
|
|
17
18
|
await serviceManager_1.serviceManager.connect();
|
|
@@ -28,6 +29,7 @@ async function main() {
|
|
|
28
29
|
app.use('/services', services_1.default);
|
|
29
30
|
app.use('/auth', (0, auth_1.default)(wss));
|
|
30
31
|
app.use('/registry', registry_1.default);
|
|
32
|
+
app.use('/system', system_1.default);
|
|
31
33
|
app.get('/health', (req, res) => {
|
|
32
34
|
res.status(200).json({ status: 'ok' });
|
|
33
35
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repoburg",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.51",
|
|
4
4
|
"description": "A local AI-powered software developer assistant that runs on your own machine.",
|
|
5
5
|
"author": "Celal Ertug",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
],
|
|
17
17
|
"bin": {
|
|
18
18
|
"repoburg": "./platform-cli.js",
|
|
19
|
-
"repoburg-daemon": "daemon/dist/index.js"
|
|
20
|
-
"repoburg-backend": "backend/dist/main.js"
|
|
19
|
+
"repoburg-daemon": "daemon/dist/index.js"
|
|
21
20
|
},
|
|
22
21
|
"scripts": {
|
|
23
22
|
"postinstall": "node postinstall.js",
|
|
@@ -59,6 +58,7 @@
|
|
|
59
58
|
"libphonenumber-js": "^1.12.10",
|
|
60
59
|
"mermaid": "^11.10.1",
|
|
61
60
|
"open": "^10.1.0",
|
|
61
|
+
"ora": "^8.0.1",
|
|
62
62
|
"pm2": "^6.0.8",
|
|
63
63
|
"reflect-metadata": "^0.1.13",
|
|
64
64
|
"rxjs": "^7.8.1",
|
package/platform-cli.js
CHANGED
|
@@ -5,8 +5,11 @@ 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
|
+
const { spawn } = require('child_process');
|
|
8
10
|
|
|
9
11
|
const DAEMON_BASE_URL = 'http://localhost:9998';
|
|
12
|
+
const DAEMON_PM2_NAME = 'repoburg-daemon';
|
|
10
13
|
|
|
11
14
|
// Uncomment the next line to use localhost for development
|
|
12
15
|
// const FRONTEND_BASE_URL ='http://localhost:3001';
|
|
@@ -32,6 +35,37 @@ const handleApiError = async (error) => {
|
|
|
32
35
|
process.exit(1);
|
|
33
36
|
};
|
|
34
37
|
|
|
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
|
+
};
|
|
35
69
|
|
|
36
70
|
program
|
|
37
71
|
.name('repoburg')
|
|
@@ -83,6 +117,7 @@ program
|
|
|
83
117
|
.action(async (projectPath) => {
|
|
84
118
|
const { default: chalk } = await import('chalk');
|
|
85
119
|
const { default: open } = await import('open');
|
|
120
|
+
const { default: ora } = await import('ora');
|
|
86
121
|
|
|
87
122
|
const pollForAuth = (timeout = 300000) => { // 5 minutes timeout
|
|
88
123
|
process.stdout.write(chalk.yellow('Waiting for authorization...'));
|
|
@@ -115,24 +150,19 @@ program
|
|
|
115
150
|
};
|
|
116
151
|
|
|
117
152
|
const pollForServiceHealth = (port, timeout = 30000) => {
|
|
118
|
-
|
|
119
|
-
const spinner = setInterval(() => process.stdout.write('.'), 1000);
|
|
120
|
-
|
|
153
|
+
const spinner = ora(`Waiting for backend on port ${port} to be healthy...`).start();
|
|
121
154
|
return new Promise((resolve, reject) => {
|
|
122
155
|
const startTime = Date.now();
|
|
123
156
|
const pollInterval = setInterval(async () => {
|
|
124
157
|
if (Date.now() - startTime > timeout) {
|
|
125
158
|
clearInterval(pollInterval);
|
|
126
|
-
|
|
127
|
-
process.stdout.write('\n');
|
|
159
|
+
spinner.fail('Backend health check timed out.');
|
|
128
160
|
return reject(new Error('Backend health check timed out. Check logs with `repoburg logs <name>`.'));
|
|
129
161
|
}
|
|
130
162
|
try {
|
|
131
163
|
await axios.get(`http://localhost:${port}/health`);
|
|
132
164
|
clearInterval(pollInterval);
|
|
133
|
-
|
|
134
|
-
process.stdout.write('\n');
|
|
135
|
-
console.log(chalk.green(`✓ Backend is healthy.`));
|
|
165
|
+
spinner.succeed(`Backend is healthy.`);
|
|
136
166
|
resolve();
|
|
137
167
|
} catch (e) {
|
|
138
168
|
// ignore until timeout
|
|
@@ -142,18 +172,49 @@ program
|
|
|
142
172
|
};
|
|
143
173
|
|
|
144
174
|
try {
|
|
145
|
-
// 1. Check
|
|
175
|
+
// 1. Check daemon status and start if not running
|
|
146
176
|
let authResponse;
|
|
147
177
|
try {
|
|
148
178
|
authResponse = await axios.get(`${DAEMON_BASE_URL}/auth/status`);
|
|
149
179
|
} catch (error) {
|
|
150
180
|
if (error.request && !error.response) {
|
|
151
|
-
console.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
181
|
+
console.log(chalk.yellow('Daemon not reachable. Attempting to start it now...'));
|
|
182
|
+
|
|
183
|
+
await startDaemon();
|
|
184
|
+
|
|
185
|
+
const pollForDaemonHealth = (timeout = 30000) => {
|
|
186
|
+
const spinner = ora('Waiting for daemon to become healthy...').start();
|
|
187
|
+
return new Promise((resolve, reject) => {
|
|
188
|
+
const startTime = Date.now();
|
|
189
|
+
const pollInterval = setInterval(async () => {
|
|
190
|
+
if (Date.now() - startTime > timeout) {
|
|
191
|
+
clearInterval(pollInterval);
|
|
192
|
+
spinner.fail('Daemon health check timed out.');
|
|
193
|
+
return reject(new Error('Daemon health check timed out. Check logs with `repoburg daemon logs`.'));
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
await axios.get(`${DAEMON_BASE_URL}/health`);
|
|
197
|
+
clearInterval(pollInterval);
|
|
198
|
+
spinner.succeed('Daemon is healthy.');
|
|
199
|
+
resolve();
|
|
200
|
+
} catch (e) {
|
|
201
|
+
// ignore until timeout
|
|
202
|
+
}
|
|
203
|
+
}, 1000)
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
await pollForDaemonHealth();
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
authResponse = await axios.get(`${DAEMON_BASE_URL}/auth/status`);
|
|
211
|
+
} catch (retryError) {
|
|
212
|
+
console.error(chalk.red('Daemon started but is not responding correctly.'));
|
|
213
|
+
return handleApiError(retryError);
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
return handleApiError(error);
|
|
155
217
|
}
|
|
156
|
-
return handleApiError(error);
|
|
157
218
|
}
|
|
158
219
|
|
|
159
220
|
if (authResponse.data.status !== 'authorized') {
|
|
@@ -338,5 +399,344 @@ program
|
|
|
338
399
|
}
|
|
339
400
|
});
|
|
340
401
|
|
|
402
|
+
program
|
|
403
|
+
.command('update')
|
|
404
|
+
.description('Update the Repoburg platform to the latest version.')
|
|
405
|
+
.action(async () => {
|
|
406
|
+
const { default: chalk } = await import('chalk');
|
|
407
|
+
const { default: ora } = await import('ora');
|
|
408
|
+
|
|
409
|
+
const runCommand = (command, args) => {
|
|
410
|
+
return new Promise((resolve, reject) => {
|
|
411
|
+
const spinner = ora(`Running \`${command} ${args.join(' ')}\`...`).start();
|
|
412
|
+
const child = spawn(command, args, { stdio: 'pipe', shell: true });
|
|
413
|
+
let output = '';
|
|
414
|
+
|
|
415
|
+
child.stdout.on('data', (data) => {
|
|
416
|
+
output += data.toString();
|
|
417
|
+
});
|
|
418
|
+
child.stderr.on('data', (data) => {
|
|
419
|
+
output += data.toString();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
child.on('close', (code) => {
|
|
423
|
+
if (code === 0) {
|
|
424
|
+
spinner.succeed(`Command \`${command} ${args.join(' ')}\` finished.`);
|
|
425
|
+
resolve();
|
|
426
|
+
} else {
|
|
427
|
+
spinner.fail(`Command \`${command} ${args.join(' ')}\` failed with code ${code}.`);
|
|
428
|
+
console.error(chalk.gray(output));
|
|
429
|
+
reject(new Error(`Command failed`));
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
child.on('error', (err) => {
|
|
433
|
+
spinner.fail(`Failed to execute command.`);
|
|
434
|
+
reject(err);
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const pollForDaemonHealth = (timeout = 30000) => {
|
|
440
|
+
const spinner = ora('Waiting for daemon to become healthy...').start();
|
|
441
|
+
return new Promise((resolve, reject) => {
|
|
442
|
+
const startTime = Date.now();
|
|
443
|
+
const pollInterval = setInterval(async () => {
|
|
444
|
+
if (Date.now() - startTime > timeout) {
|
|
445
|
+
clearInterval(pollInterval);
|
|
446
|
+
spinner.fail('Daemon health check timed out.');
|
|
447
|
+
return reject(new Error('Daemon health check timed out.'));
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
await axios.get(`${DAEMON_BASE_URL}/health`);
|
|
451
|
+
clearInterval(pollInterval);
|
|
452
|
+
spinner.succeed('Daemon is healthy.');
|
|
453
|
+
resolve();
|
|
454
|
+
} catch (e) {
|
|
455
|
+
// ignore until timeout
|
|
456
|
+
}
|
|
457
|
+
}, 1000)
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
let runningServices = [];
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
// 1. Get list of running services
|
|
465
|
+
const listSpinner = ora('Fetching list of running services...').start();
|
|
466
|
+
try {
|
|
467
|
+
const response = await axios.get(`${DAEMON_BASE_URL}/services`);
|
|
468
|
+
runningServices = response.data.filter(s => s.status === 'running').map(s => s.name);
|
|
469
|
+
listSpinner.succeed(`Found ${runningServices.length} running services to restart later.`);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
if (error.request && !error.response) { // Daemon not running
|
|
472
|
+
listSpinner.warn('Daemon not reachable, assuming no services are running.');
|
|
473
|
+
} else {
|
|
474
|
+
listSpinner.fail('Failed to fetch services.');
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// 2. Stop all running services
|
|
480
|
+
if (runningServices.length > 0) {
|
|
481
|
+
const stopSpinner = ora('Stopping running backend services...').start();
|
|
482
|
+
for (const name of runningServices) {
|
|
483
|
+
stopSpinner.text = `Stopping ${name}...`;
|
|
484
|
+
await axios.post(`${DAEMON_BASE_URL}/services/${name}/stop`);
|
|
485
|
+
}
|
|
486
|
+
stopSpinner.succeed('All backend services stopped.');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// 3. Stop the daemon
|
|
490
|
+
const stopDaemonSpinner = ora('Stopping daemon...').start();
|
|
491
|
+
await new Promise((resolve, reject) => {
|
|
492
|
+
pm2.connect(err => {
|
|
493
|
+
if (err) return reject(err);
|
|
494
|
+
pm2.stop(DAEMON_PM2_NAME, (err) => {
|
|
495
|
+
pm2.disconnect();
|
|
496
|
+
const errMsg = err ? (err.message || err.msg) : null;
|
|
497
|
+
if (err && !(errMsg && errMsg.includes('not found'))) {
|
|
498
|
+
return reject(err);
|
|
499
|
+
}
|
|
500
|
+
resolve();
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
stopDaemonSpinner.succeed('Daemon stopped.');
|
|
505
|
+
|
|
506
|
+
// 4. Run update
|
|
507
|
+
await runCommand('npm', ['i', '-g', 'repoburg@latest']);
|
|
508
|
+
|
|
509
|
+
// 5. Start the daemon
|
|
510
|
+
const startDaemonSpinner = ora('Starting daemon...').start();
|
|
511
|
+
await new Promise((resolve, reject) => {
|
|
512
|
+
pm2.connect(err => {
|
|
513
|
+
if (err) return reject(err);
|
|
514
|
+
pm2.start({ name: DAEMON_PM2_NAME, script: 'repoburg-daemon' }, (err) => {
|
|
515
|
+
pm2.disconnect();
|
|
516
|
+
if (err) return reject(err);
|
|
517
|
+
resolve();
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
startDaemonSpinner.succeed('Daemon process started.');
|
|
522
|
+
await pollForDaemonHealth();
|
|
523
|
+
|
|
524
|
+
// 6. Restart services
|
|
525
|
+
if (runningServices.length > 0) {
|
|
526
|
+
const restartSpinner = ora('Restarting backend services...').start();
|
|
527
|
+
for (const name of runningServices) {
|
|
528
|
+
restartSpinner.text = `Restarting ${name}...`;
|
|
529
|
+
await axios.post(`${DAEMON_BASE_URL}/services/${name}/restart`);
|
|
530
|
+
}
|
|
531
|
+
restartSpinner.succeed('All backend services restarted.');
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
console.log(chalk.green.bold('\n✓ Repoburg update complete!'));
|
|
535
|
+
|
|
536
|
+
} catch (error) {
|
|
537
|
+
console.error(chalk.red('\nUpdate process failed:'), error.message);
|
|
538
|
+
console.error(chalk.yellow('Your services might be in a stopped state. Please check with `repoburg list` and `repoburg daemon status`.'));
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
// --- Daemon Management ---
|
|
544
|
+
const daemon = program.command('daemon')
|
|
545
|
+
.description('Manage the Repoburg Platform Daemon process (the service running on port 9998).');
|
|
546
|
+
|
|
547
|
+
daemon
|
|
548
|
+
.command('start')
|
|
549
|
+
.description('Start the daemon process in the background using pm2.')
|
|
550
|
+
.action(async () => {
|
|
551
|
+
const { default: chalk } = await import('chalk');
|
|
552
|
+
try {
|
|
553
|
+
await startDaemon();
|
|
554
|
+
console.log(chalk.cyan(`Run 'repoburg daemon status' to check its state.`));
|
|
555
|
+
} catch (error) {
|
|
556
|
+
console.error(chalk.red('An error occurred:'), error.message);
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
daemon
|
|
562
|
+
.command('status')
|
|
563
|
+
.description('Show the status of the daemon process.')
|
|
564
|
+
.action(async () => {
|
|
565
|
+
const { default: chalk } = await import('chalk');
|
|
566
|
+
const Table = require('cli-table3');
|
|
567
|
+
try {
|
|
568
|
+
await connectToPm2();
|
|
569
|
+
pm2.list((err, list) => {
|
|
570
|
+
if (err) {
|
|
571
|
+
console.error(chalk.red('Error listing pm2 processes:'), err.message || err);
|
|
572
|
+
pm2.disconnect();
|
|
573
|
+
process.exit(1);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const daemonProcess = list.find(p => p.name === DAEMON_PM2_NAME);
|
|
577
|
+
if (!daemonProcess) {
|
|
578
|
+
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" is not running or not managed by pm2.`));
|
|
579
|
+
} else {
|
|
580
|
+
const table = new Table({
|
|
581
|
+
head: ['NAME', 'STATUS', 'PID', 'CPU', 'MEMORY'].map(h => chalk.cyan(h)),
|
|
582
|
+
colWidths: [20, 12, 8, 8, 15]
|
|
583
|
+
});
|
|
584
|
+
const status = daemonProcess.pm2_env.status;
|
|
585
|
+
const statusColor = status === 'online' ? chalk.green : chalk.yellow;
|
|
586
|
+
table.push([
|
|
587
|
+
daemonProcess.name,
|
|
588
|
+
statusColor(status),
|
|
589
|
+
daemonProcess.pid,
|
|
590
|
+
`${daemonProcess.monit.cpu || 0}%`,
|
|
591
|
+
`${(daemonProcess.monit.memory / 1024 / 1024).toFixed(1)} MB`
|
|
592
|
+
]);
|
|
593
|
+
console.log(table.toString());
|
|
594
|
+
}
|
|
595
|
+
pm2.disconnect();
|
|
596
|
+
});
|
|
597
|
+
} catch (error) {
|
|
598
|
+
console.error(chalk.red('An error occurred:'), error.message);
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
daemon
|
|
604
|
+
.command('stop')
|
|
605
|
+
.description('Stop the daemon process.')
|
|
606
|
+
.action(async () => {
|
|
607
|
+
const { default: chalk } = await import('chalk');
|
|
608
|
+
try {
|
|
609
|
+
await connectToPm2();
|
|
610
|
+
console.log(chalk.cyan(`Stopping daemon process "${DAEMON_PM2_NAME}"...`));
|
|
611
|
+
pm2.stop(DAEMON_PM2_NAME, (err) => {
|
|
612
|
+
const errMsg = err ? (err.message || err.msg) : null;
|
|
613
|
+
if (err) {
|
|
614
|
+
if (errMsg && errMsg.includes('not found')) {
|
|
615
|
+
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" not found or not running.`));
|
|
616
|
+
} else {
|
|
617
|
+
console.error(chalk.red('Error stopping daemon:'), errMsg || err);
|
|
618
|
+
pm2.disconnect();
|
|
619
|
+
process.exit(1);
|
|
620
|
+
}
|
|
621
|
+
} else {
|
|
622
|
+
console.log(chalk.green('✓ Daemon stopped.'));
|
|
623
|
+
}
|
|
624
|
+
pm2.disconnect();
|
|
625
|
+
});
|
|
626
|
+
} catch (error) {
|
|
627
|
+
console.error(chalk.red('An error occurred:'), error.message);
|
|
628
|
+
process.exit(1);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
daemon
|
|
633
|
+
.command('restart')
|
|
634
|
+
.description('Restart the daemon process.')
|
|
635
|
+
.action(async () => {
|
|
636
|
+
const { default: chalk } = await import('chalk');
|
|
637
|
+
try {
|
|
638
|
+
await connectToPm2();
|
|
639
|
+
console.log(chalk.cyan(`Restarting daemon process "${DAEMON_PM2_NAME}"...`));
|
|
640
|
+
pm2.restart(DAEMON_PM2_NAME, (err) => {
|
|
641
|
+
const errMsg = err ? (err.message || err.msg) : null;
|
|
642
|
+
if (err) {
|
|
643
|
+
if (errMsg && errMsg.includes('not found')) {
|
|
644
|
+
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" not found. Use 'repoburg daemon start' instead.`));
|
|
645
|
+
} else {
|
|
646
|
+
console.error(chalk.red('Error restarting daemon:'), errMsg || err);
|
|
647
|
+
pm2.disconnect();
|
|
648
|
+
process.exit(1);
|
|
649
|
+
}
|
|
650
|
+
} else {
|
|
651
|
+
console.log(chalk.green('✓ Daemon restarted.'));
|
|
652
|
+
}
|
|
653
|
+
pm2.disconnect();
|
|
654
|
+
});
|
|
655
|
+
} catch (error) {
|
|
656
|
+
console.error(chalk.red('An error occurred:'), error.message);
|
|
657
|
+
process.exit(1);
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
daemon
|
|
662
|
+
.command('delete')
|
|
663
|
+
.alias('rm')
|
|
664
|
+
.description('Stop and delete the daemon process from pm2.')
|
|
665
|
+
.action(async () => {
|
|
666
|
+
const { default: chalk } = await import('chalk');
|
|
667
|
+
try {
|
|
668
|
+
await connectToPm2();
|
|
669
|
+
console.log(chalk.cyan(`Deleting daemon process "${DAEMON_PM2_NAME}" from pm2...`));
|
|
670
|
+
pm2.delete(DAEMON_PM2_NAME, (err) => {
|
|
671
|
+
const errMsg = err ? (err.message || err.msg) : null;
|
|
672
|
+
if (err) {
|
|
673
|
+
if (errMsg && errMsg.includes('not found')) {
|
|
674
|
+
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" not found.`));
|
|
675
|
+
} else {
|
|
676
|
+
console.error(chalk.red('Error deleting daemon:'), errMsg || err);
|
|
677
|
+
pm2.disconnect();
|
|
678
|
+
process.exit(1);
|
|
679
|
+
}
|
|
680
|
+
} else {
|
|
681
|
+
console.log(chalk.green('✓ Daemon process deleted from pm2.'));
|
|
682
|
+
}
|
|
683
|
+
pm2.disconnect();
|
|
684
|
+
});
|
|
685
|
+
} catch (error) {
|
|
686
|
+
console.error(chalk.red('An error occurred:'), error.message);
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
daemon
|
|
692
|
+
.command('logs')
|
|
693
|
+
.description('Display logs for the daemon process.')
|
|
694
|
+
.action(async () => {
|
|
695
|
+
const { default: chalk } = await import('chalk');
|
|
696
|
+
try {
|
|
697
|
+
await connectToPm2();
|
|
698
|
+
pm2.describe(DAEMON_PM2_NAME, async (err, description) => {
|
|
699
|
+
if (err) {
|
|
700
|
+
console.error(chalk.red('Error describing daemon process:'), err.message || err);
|
|
701
|
+
pm2.disconnect();
|
|
702
|
+
process.exit(1);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (!description || description.length === 0) {
|
|
706
|
+
console.log(chalk.yellow(`Daemon process "${DAEMON_PM2_NAME}" not found.`));
|
|
707
|
+
pm2.disconnect();
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
const proc = description[0];
|
|
712
|
+
const outLogPath = proc.pm2_env.pm_out_log_path;
|
|
713
|
+
const errLogPath = proc.pm2_env.pm_err_log_path;
|
|
714
|
+
|
|
715
|
+
console.log(chalk.cyan(`--- Logs for ${DAEMON_PM2_NAME} ---`));
|
|
716
|
+
|
|
717
|
+
const readAndPrint = async (filePath, logType) => {
|
|
718
|
+
try {
|
|
719
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
720
|
+
console.log(chalk.bold.yellow(`\n--- ${logType} Log (${filePath}) ---\n`));
|
|
721
|
+
console.log(content || chalk.gray('(empty)'));
|
|
722
|
+
} catch (e) {
|
|
723
|
+
if (e.code !== 'ENOENT') {
|
|
724
|
+
throw e;
|
|
725
|
+
}
|
|
726
|
+
console.log(chalk.bold.yellow(`\n--- ${logType} Log (${filePath}) ---\n`));
|
|
727
|
+
console.log(chalk.gray('(not found)'));
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
await readAndPrint(outLogPath, 'Output');
|
|
732
|
+
await readAndPrint(errLogPath, 'Error');
|
|
733
|
+
|
|
734
|
+
pm2.disconnect();
|
|
735
|
+
});
|
|
736
|
+
} catch (error) {
|
|
737
|
+
console.error(chalk.red('An error occurred:'), error.message);
|
|
738
|
+
process.exit(1);
|
|
739
|
+
}
|
|
740
|
+
});
|
|
341
741
|
|
|
342
742
|
program.parse(process.argv);
|