neex 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <a href="https://github.com/Neexjs">
3
3
  <picture>
4
4
  <source media="(prefers-color-scheme: dark)" srcset="https://neex.storage.c2.liara.space/Neex.png">
5
- <img alt="Nextpress logo" src="https://neex.storage.c2.liara.space/Neex.png" height="150" style="border-radius: 12px;">
5
+ <img alt="Neex logo" src="https://neex.storage.c2.liara.space/Neex.png" height="150" style="border-radius: 12px;">
6
6
  </picture>
7
7
  </a>
8
8
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  [![NPM version](https://img.shields.io/npm/v/neex.svg?style=for-the-badge&labelColor=000000&color=0066FF&borderRadius=8)](https://www.npmjs.com/package/neex)
14
14
  [![Download Count](https://img.shields.io/npm/dt/neex.svg?style=for-the-badge&labelColor=000000&color=0066FF&borderRadius=8)](https://www.npmjs.com/package/neex)
15
- [![MIT License](https://img.shields.io/badge/license-MIT-0066FF.svg?style=for-the-badge&labelColor=000000&borderRadius=8)](https://github.com/Nextpress-cc/neex/blob/main/LICENSE)
15
+ [![MIT License](https://img.shields.io/badge/license-MIT-0066FF.svg?style=for-the-badge&labelColor=000000&borderRadius=8)](https://github.com/neexjs/blob/main/LICENSE)
16
16
  [![GitHub](https://img.shields.io/badge/GitHub-Neex-0066FF.svg?style=for-the-badge&logo=github&labelColor=000000&logoWidth=20&borderRadius=8)](https://github.com/Neexjs)
17
17
  </div>
18
18
 
package/dist/src/cli.js CHANGED
@@ -1,20 +1,48 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
- // src/cli.ts - Updated version
29
+ // src/cli.ts - Enhanced version with Nodemon and PM2 functionality
7
30
  const commander_1 = require("commander");
8
31
  const index_js_1 = require("./index.js");
32
+ const dev_runner_js_1 = require("./dev-runner.js");
33
+ const process_manager_js_1 = require("./process-manager.js");
9
34
  const chalk_1 = __importDefault(require("chalk"));
10
35
  const figures_1 = __importDefault(require("figures"));
36
+ const path = __importStar(require("path"));
11
37
  const { version } = require('../../package.json');
12
38
  function cli() {
13
39
  const program = new commander_1.Command();
14
40
  let cleanupRunner = null;
41
+ let devRunner = null;
42
+ let processManager = null;
15
43
  program
16
44
  .name('neex')
17
- .description('Professional script runner with beautiful colored output')
45
+ .description('Professional script runner with nodemon and PM2 functionality')
18
46
  .version(version);
19
47
  // Main command for sequential execution (similar to run-s)
20
48
  program
@@ -63,6 +91,8 @@ function cli() {
63
91
  .option('-m, --minimal', 'Use minimal output format')
64
92
  .option('-x, --max-parallel <number>', 'Maximum number of parallel processes', parseInt)
65
93
  .option('-q, --sequential', 'Run commands sequentially instead of in parallel')
94
+ .option('--retry <count>', 'Number of times to retry a failed command', parseInt)
95
+ .option('--retry-delay <ms>', 'Delay in milliseconds between retries', parseInt)
66
96
  .action(async (commands, options) => {
67
97
  try {
68
98
  await (0, index_js_1.run)(commands, {
@@ -74,6 +104,8 @@ function cli() {
74
104
  stopOnError: options.stopOnError,
75
105
  printOutput: options.output,
76
106
  minimalOutput: options.minimal,
107
+ retry: options.retry,
108
+ retryDelay: options.retryDelay,
77
109
  registerCleanup: (cleanup) => { cleanupRunner = cleanup; }
78
110
  });
79
111
  }
@@ -87,7 +119,7 @@ function cli() {
87
119
  process.exit(1);
88
120
  }
89
121
  });
90
- // Add a new servers command specifically optimized for running web servers
122
+ // Servers command specifically optimized for running web servers
91
123
  program
92
124
  .command('servers <commands...>')
93
125
  .alias('srv')
@@ -111,7 +143,7 @@ function cli() {
111
143
  printOutput: true,
112
144
  registerCleanup: (cleanup) => { cleanupRunner = cleanup; },
113
145
  groupOutput: options.groupOutput,
114
- isServerMode: true // Special flag for server mode formatting
146
+ isServerMode: true
115
147
  });
116
148
  }
117
149
  catch (error) {
@@ -124,6 +156,439 @@ function cli() {
124
156
  process.exit(1);
125
157
  }
126
158
  });
159
+ // Watch command (Nodemon functionality)
160
+ program
161
+ .command('watch <commands...>')
162
+ .alias('w')
163
+ .description('Run commands with file watching (nodemon functionality)')
164
+ .option('-c, --no-color', 'Disable colored output')
165
+ .option('-t, --no-timing', 'Hide timing information')
166
+ .option('-p, --no-prefix', 'Hide command prefix')
167
+ .option('-s, --stop-on-error', 'Stop on first error')
168
+ .option('-o, --no-output', 'Hide command output')
169
+ .option('-m, --minimal', 'Use minimal output format')
170
+ .option('-w, --watch <paths...>', 'Paths to watch (default: current directory)')
171
+ .option('-i, --ignore <patterns...>', 'Patterns to ignore')
172
+ .option('-e, --ext <extensions...>', 'File extensions to watch (default: js,mjs,json,ts,tsx,jsx)')
173
+ .option('-d, --delay <ms>', 'Delay before restart in milliseconds', parseInt)
174
+ .option('--clear', 'Clear console on restart')
175
+ .option('--verbose', 'Verbose output')
176
+ .option('--signal <signal>', 'Signal to send to processes on restart', 'SIGTERM')
177
+ .action(async (commands, options) => {
178
+ try {
179
+ console.log(chalk_1.default.blue(`${figures_1.default.info} Starting development server with file watching...`));
180
+ const watchPaths = options.watch || ['./'];
181
+ const ignorePatterns = options.ignore || [
182
+ 'node_modules/**',
183
+ '.git/**',
184
+ '*.log',
185
+ 'dist/**',
186
+ 'build/**',
187
+ 'coverage/**',
188
+ '.nyc_output/**',
189
+ '*.tmp',
190
+ '*.temp'
191
+ ];
192
+ const extensions = options.ext || ['js', 'mjs', 'json', 'ts', 'tsx', 'jsx'];
193
+ devRunner = new dev_runner_js_1.DevRunner({
194
+ parallel: false,
195
+ color: options.color,
196
+ showTiming: options.timing,
197
+ prefix: options.prefix,
198
+ stopOnError: options.stopOnError,
199
+ printOutput: options.output,
200
+ minimalOutput: options.minimal,
201
+ watch: watchPaths,
202
+ ignore: ignorePatterns,
203
+ ext: extensions,
204
+ delay: options.delay || 1000,
205
+ clearConsole: options.clear,
206
+ verbose: options.verbose,
207
+ signal: options.signal,
208
+ restartOnChange: true,
209
+ groupOutput: false,
210
+ isServerMode: false // Added missing property
211
+ });
212
+ await devRunner.start(commands);
213
+ }
214
+ catch (error) {
215
+ if (error instanceof Error) {
216
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Watch Error: ${error.message}`));
217
+ }
218
+ else {
219
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown watch error occurred`));
220
+ }
221
+ process.exit(1);
222
+ }
223
+ });
224
+ // PM2-like process management commands
225
+ const pm2Group = program.command('pm2').description('Process management commands (PM2 alternative)');
226
+ // Start command
227
+ pm2Group
228
+ .command('start <script>')
229
+ .description('Start a new process')
230
+ .option('-n, --name <name>', 'Process name')
231
+ .option('-i, --instances <number>', 'Number of instances', parseInt)
232
+ .option('--cwd <path>', 'Working directory')
233
+ .option('--env <env>', 'Environment variables (JSON string)')
234
+ .option('--no-autorestart', 'Disable auto-restart')
235
+ .option('--max-restarts <number>', 'Maximum restarts', parseInt)
236
+ .option('--restart-delay <ms>', 'Restart delay in ms', parseInt)
237
+ .option('--watch', 'Enable file watching')
238
+ .option('--ignore-watch <patterns...>', 'Patterns to ignore when watching')
239
+ .option('--max-memory <size>', 'Max memory before restart (e.g., 1G, 500M)')
240
+ .action(async (script, options) => {
241
+ try {
242
+ if (!processManager) {
243
+ processManager = new process_manager_js_1.ProcessManager();
244
+ }
245
+ const config = {
246
+ id: '',
247
+ name: options.name || path.basename(script, path.extname(script)),
248
+ script,
249
+ cwd: options.cwd,
250
+ env: options.env ? JSON.parse(options.env) : undefined,
251
+ instances: options.instances || 1,
252
+ autorestart: options.autorestart !== false,
253
+ max_restarts: options.maxRestarts || 10,
254
+ restart_delay: options.restartDelay || 1000,
255
+ watch: options.watch || false,
256
+ ignore_watch: options.ignoreWatch,
257
+ max_memory_restart: options.maxMemory
258
+ };
259
+ const id = await processManager.start(config);
260
+ console.log(chalk_1.default.green(`${figures_1.default.tick} Process started with ID: ${id}`));
261
+ }
262
+ catch (error) {
263
+ if (error instanceof Error) {
264
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Start Error: ${error.message}`));
265
+ }
266
+ else {
267
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown start error occurred`));
268
+ }
269
+ process.exit(1);
270
+ }
271
+ });
272
+ // Stop command
273
+ pm2Group
274
+ .command('stop <id>')
275
+ .description('Stop a process')
276
+ .action(async (id) => {
277
+ try {
278
+ if (!processManager) {
279
+ processManager = new process_manager_js_1.ProcessManager();
280
+ await processManager.load();
281
+ }
282
+ if (id === 'all') {
283
+ await processManager.stopAll();
284
+ console.log(chalk_1.default.green(`${figures_1.default.tick} All processes stopped`));
285
+ }
286
+ else {
287
+ await processManager.stop(id);
288
+ console.log(chalk_1.default.green(`${figures_1.default.tick} Process ${id} stopped`));
289
+ }
290
+ }
291
+ catch (error) {
292
+ if (error instanceof Error) {
293
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Stop Error: ${error.message}`));
294
+ }
295
+ else {
296
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown stop error occurred`));
297
+ }
298
+ process.exit(1);
299
+ }
300
+ });
301
+ // Restart command
302
+ pm2Group
303
+ .command('restart <id>')
304
+ .description('Restart a process')
305
+ .action(async (id) => {
306
+ try {
307
+ if (!processManager) {
308
+ processManager = new process_manager_js_1.ProcessManager();
309
+ await processManager.load();
310
+ }
311
+ if (id === 'all') {
312
+ await processManager.restartAll();
313
+ console.log(chalk_1.default.green(`${figures_1.default.tick} All processes restarted`));
314
+ }
315
+ else {
316
+ await processManager.restart(id);
317
+ console.log(chalk_1.default.green(`${figures_1.default.tick} Process ${id} restarted`));
318
+ }
319
+ }
320
+ catch (error) {
321
+ if (error instanceof Error) {
322
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Restart Error: ${error.message}`));
323
+ }
324
+ else {
325
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown restart error occurred`));
326
+ }
327
+ process.exit(1);
328
+ }
329
+ });
330
+ // Delete command
331
+ pm2Group
332
+ .command('delete <id>')
333
+ .description('Delete a process')
334
+ .action(async (id) => {
335
+ try {
336
+ if (!processManager) {
337
+ processManager = new process_manager_js_1.ProcessManager();
338
+ await processManager.load();
339
+ }
340
+ if (id === 'all') {
341
+ await processManager.deleteAll();
342
+ console.log(chalk_1.default.green(`${figures_1.default.tick} All processes deleted`));
343
+ }
344
+ else {
345
+ await processManager.delete(id);
346
+ console.log(chalk_1.default.green(`${figures_1.default.tick} Process ${id} deleted`));
347
+ }
348
+ }
349
+ catch (error) {
350
+ if (error instanceof Error) {
351
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Delete Error: ${error.message}`));
352
+ }
353
+ else {
354
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown delete error occurred`));
355
+ }
356
+ process.exit(1);
357
+ }
358
+ });
359
+ // List/Status command
360
+ pm2Group
361
+ .command('list')
362
+ .alias('status')
363
+ .alias('ls')
364
+ .description('List all processes')
365
+ .action(async () => {
366
+ try {
367
+ if (!processManager) {
368
+ processManager = new process_manager_js_1.ProcessManager();
369
+ await processManager.load();
370
+ }
371
+ const processes = await processManager.list();
372
+ if (processes.length === 0) {
373
+ console.log(chalk_1.default.yellow(`${figures_1.default.info} No processes found`));
374
+ return;
375
+ }
376
+ // Print table header
377
+ console.log('\n' + chalk_1.default.bold('Process Management Status'));
378
+ console.log(chalk_1.default.gray('─'.repeat(80)));
379
+ const header = `${chalk_1.default.bold('ID'.padEnd(15))} ${chalk_1.default.bold('Name'.padEnd(20))} ${chalk_1.default.bold('Status'.padEnd(10))} ${chalk_1.default.bold('PID'.padEnd(8))} ${chalk_1.default.bold('Uptime'.padEnd(10))} ${chalk_1.default.bold('Restarts')}`;
380
+ console.log(header);
381
+ console.log(chalk_1.default.gray('─'.repeat(80)));
382
+ // Print process information
383
+ processes.forEach(proc => {
384
+ const statusColor = proc.status === 'online' ? chalk_1.default.green :
385
+ proc.status === 'stopped' ? chalk_1.default.gray :
386
+ proc.status === 'errored' ? chalk_1.default.red : chalk_1.default.yellow;
387
+ const uptime = formatUptime(proc.uptime);
388
+ const pid = proc.pid ? proc.pid.toString() : '-';
389
+ const row = `${proc.id.padEnd(15)} ${proc.name.padEnd(20)} ${statusColor(proc.status.padEnd(10))} ${pid.padEnd(8)} ${uptime.padEnd(10)} ${proc.restarts}`;
390
+ console.log(row);
391
+ });
392
+ console.log(chalk_1.default.gray('─'.repeat(80)));
393
+ console.log(`\n${chalk_1.default.blue(`${figures_1.default.info} Total: ${processes.length} processes`)}`);
394
+ }
395
+ catch (error) {
396
+ if (error instanceof Error) {
397
+ console.error(chalk_1.default.red(`${figures_1.default.cross} List Error: ${error.message}`));
398
+ }
399
+ else {
400
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown list error occurred`));
401
+ }
402
+ process.exit(1);
403
+ }
404
+ });
405
+ // Logs command
406
+ pm2Group
407
+ .command('logs [id]')
408
+ .description('Show process logs')
409
+ .option('-f, --follow', 'Follow log output')
410
+ .option('-n, --lines <number>', 'Number of lines to show', parseInt)
411
+ .action(async (id, options) => {
412
+ try {
413
+ if (!processManager) {
414
+ processManager = new process_manager_js_1.ProcessManager();
415
+ await processManager.load();
416
+ }
417
+ if (id) {
418
+ const logs = await processManager.logs(id, options.lines || 100);
419
+ if (logs.length === 0) {
420
+ console.log(chalk_1.default.yellow(`${figures_1.default.info} No logs found for process ${id}`));
421
+ }
422
+ else {
423
+ logs.forEach(log => console.log(log));
424
+ }
425
+ }
426
+ else {
427
+ console.log(chalk_1.default.yellow(`${figures_1.default.info} Please specify a process ID`));
428
+ }
429
+ if (options.follow) {
430
+ console.log(chalk_1.default.blue(`${figures_1.default.info} Following logs... Press Ctrl+C to stop`));
431
+ // TODO: Implement log following
432
+ }
433
+ }
434
+ catch (error) {
435
+ if (error instanceof Error) {
436
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Logs Error: ${error.message}`));
437
+ }
438
+ else {
439
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown logs error occurred`));
440
+ }
441
+ process.exit(1);
442
+ }
443
+ });
444
+ // Save command
445
+ pm2Group
446
+ .command('save')
447
+ .description('Save current process list')
448
+ .action(async () => {
449
+ try {
450
+ if (!processManager) {
451
+ processManager = new process_manager_js_1.ProcessManager();
452
+ await processManager.load();
453
+ }
454
+ await processManager.save();
455
+ console.log(chalk_1.default.green(`${figures_1.default.tick} Process list saved`));
456
+ }
457
+ catch (error) {
458
+ if (error instanceof Error) {
459
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Save Error: ${error.message}`));
460
+ }
461
+ else {
462
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown save error occurred`));
463
+ }
464
+ process.exit(1);
465
+ }
466
+ });
467
+ // Startup command (placeholder for system startup configuration)
468
+ pm2Group
469
+ .command('startup')
470
+ .description('Generate startup script')
471
+ .action(() => {
472
+ console.log(chalk_1.default.blue(`${figures_1.default.info} Startup script generation:`));
473
+ console.log('\nTo start neex processes on system boot, you can:');
474
+ console.log('1. Create a systemd service (Linux)');
475
+ console.log('2. Use launchd (macOS)');
476
+ console.log('3. Use Windows Service (Windows)');
477
+ console.log('\nExample systemd service file:');
478
+ console.log(chalk_1.default.gray(`
479
+ [Unit]
480
+ Description=Neex Process Manager
481
+ After=network.target
482
+
483
+ [Service]
484
+ Type=simple
485
+ User=your-user
486
+ WorkingDirectory=/path/to/your/project
487
+ ExecStart=/usr/local/bin/neex pm2 resurrect
488
+ Restart=always
489
+
490
+ [Install]
491
+ WantedBy=multi-user.target
492
+ `));
493
+ });
494
+ // Resurrect command (start saved processes)
495
+ pm2Group
496
+ .command('resurrect')
497
+ .description('Resurrect previously saved processes')
498
+ .action(async () => {
499
+ try {
500
+ if (!processManager) {
501
+ processManager = new process_manager_js_1.ProcessManager();
502
+ }
503
+ await processManager.load();
504
+ const processes = await processManager.list();
505
+ let started = 0;
506
+ for (const proc of processes) {
507
+ if (proc.status === 'stopped') {
508
+ try {
509
+ await processManager.restart(proc.id);
510
+ started++;
511
+ }
512
+ catch (error) {
513
+ console.log(chalk_1.default.yellow(`${figures_1.default.warning} Failed to start ${proc.id}: ${error.message}`));
514
+ }
515
+ }
516
+ }
517
+ console.log(chalk_1.default.green(`${figures_1.default.tick} Resurrected ${started} processes`));
518
+ }
519
+ catch (error) {
520
+ if (error instanceof Error) {
521
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Resurrect Error: ${error.message}`));
522
+ }
523
+ else {
524
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown resurrect error occurred`));
525
+ }
526
+ process.exit(1);
527
+ }
528
+ });
529
+ // Monit command (monitoring interface)
530
+ pm2Group
531
+ .command('monit')
532
+ .description('Launch monitoring interface')
533
+ .action(async () => {
534
+ try {
535
+ if (!processManager) {
536
+ processManager = new process_manager_js_1.ProcessManager();
537
+ await processManager.load();
538
+ }
539
+ console.log(chalk_1.default.blue(`${figures_1.default.info} Starting monitoring interface...`));
540
+ console.log(chalk_1.default.yellow(`${figures_1.default.warning} Press 'q' to quit, 'r' to refresh`));
541
+ // Simple monitoring loop
542
+ const monitorLoop = async () => {
543
+ console.clear();
544
+ console.log(chalk_1.default.bold.blue('Neex Process Monitor'));
545
+ console.log(chalk_1.default.gray('─'.repeat(80)));
546
+ const processes = await processManager.list();
547
+ if (processes.length === 0) {
548
+ console.log(chalk_1.default.yellow(`${figures_1.default.info} No processes running`));
549
+ }
550
+ else {
551
+ processes.forEach(proc => {
552
+ const statusColor = proc.status === 'online' ? chalk_1.default.green :
553
+ proc.status === 'stopped' ? chalk_1.default.gray :
554
+ proc.status === 'errored' ? chalk_1.default.red : chalk_1.default.yellow;
555
+ console.log(`${statusColor('●')} ${proc.name} (${proc.id}) - ${statusColor(proc.status)}`);
556
+ console.log(` PID: ${proc.pid || 'N/A'} | Uptime: ${formatUptime(proc.uptime)} | Restarts: ${proc.restarts}`);
557
+ console.log();
558
+ });
559
+ }
560
+ console.log(chalk_1.default.gray('─'.repeat(80)));
561
+ console.log(chalk_1.default.blue(`Last updated: ${new Date().toLocaleTimeString()}`));
562
+ };
563
+ // Initial display
564
+ await monitorLoop();
565
+ // Set up keyboard input handling
566
+ process.stdin.setRawMode(true);
567
+ process.stdin.resume();
568
+ process.stdin.setEncoding('utf8');
569
+ const interval = setInterval(monitorLoop, 5000);
570
+ process.stdin.on('data', (key) => {
571
+ if (key.toString() === 'q' || key.toString() === '\u0003') { // 'q' or Ctrl+C
572
+ clearInterval(interval);
573
+ process.stdin.setRawMode(false);
574
+ console.log('\n' + chalk_1.default.green(`${figures_1.default.tick} Monitoring stopped`));
575
+ process.exit(0);
576
+ }
577
+ else if (key.toString() === 'r') {
578
+ monitorLoop();
579
+ }
580
+ });
581
+ }
582
+ catch (error) {
583
+ if (error instanceof Error) {
584
+ console.error(chalk_1.default.red(`${figures_1.default.cross} Monit Error: ${error.message}`));
585
+ }
586
+ else {
587
+ console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown monit error occurred`));
588
+ }
589
+ process.exit(1);
590
+ }
591
+ });
127
592
  program.parse(process.argv);
128
593
  // Show help if no commands specified
129
594
  if (program.args.length === 0) {
@@ -132,14 +597,35 @@ function cli() {
132
597
  // Graceful shutdown handling
133
598
  const handleSignal = (signal) => {
134
599
  console.log(`\n${chalk_1.default.yellow(`${figures_1.default.warning} Received ${signal}. Cleaning up...`)}`);
600
+ if (devRunner && devRunner.isActive()) {
601
+ devRunner.stop();
602
+ }
135
603
  if (cleanupRunner) {
136
604
  cleanupRunner();
137
605
  }
138
- // Give cleanup a moment, then exit
606
+ if (processManager) {
607
+ processManager.dispose();
608
+ }
139
609
  setTimeout(() => process.exit(0), 500);
140
610
  };
141
- process.on('SIGINT', () => handleSignal('SIGINT')); // Ctrl+C
611
+ process.on('SIGINT', () => handleSignal('SIGINT'));
142
612
  process.on('SIGTERM', () => handleSignal('SIGTERM'));
143
613
  process.on('SIGQUIT', () => handleSignal('SIGQUIT'));
144
614
  }
145
615
  exports.default = cli;
616
+ // Helper function to format uptime
617
+ function formatUptime(seconds) {
618
+ if (seconds < 60) {
619
+ return `${seconds}s`;
620
+ }
621
+ else if (seconds < 3600) {
622
+ const minutes = Math.floor(seconds / 60);
623
+ const remainingSeconds = seconds % 60;
624
+ return `${minutes}m ${remainingSeconds}s`;
625
+ }
626
+ else {
627
+ const hours = Math.floor(seconds / 3600);
628
+ const minutes = Math.floor((seconds % 3600) / 60);
629
+ return `${hours}h ${minutes}m`;
630
+ }
631
+ }