neex 0.1.3 → 0.1.5
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 +225 -38
- package/dist/src/cli.js +493 -8
- package/dist/src/dev-runner.js +203 -0
- package/dist/src/index.js +3 -1
- package/dist/src/logger.js +36 -0
- package/dist/src/process-manager.js +330 -0
- package/dist/src/runner.js +112 -78
- package/dist/src/utils.js +10 -0
- package/dist/src/watcher.js +245 -0
- package/feet.txt +16 -0
- package/package.json +1 -1
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 -
|
|
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
|
|
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
|
|
@@ -53,8 +81,8 @@ function cli() {
|
|
|
53
81
|
// runx command: parallel execution by default (with alias 'p'), can run sequentially with -q
|
|
54
82
|
program
|
|
55
83
|
.command('runx <commands...>', { isDefault: true })
|
|
56
|
-
.alias('
|
|
57
|
-
.description('Run commands in parallel (default) or sequentially with -q. Alias:
|
|
84
|
+
.alias('px')
|
|
85
|
+
.description('Run commands in parallel (default) or sequentially with -q. Alias: px')
|
|
58
86
|
.option('-c, --no-color', 'Disable colored output')
|
|
59
87
|
.option('-t, --no-timing', 'Hide timing information')
|
|
60
88
|
.option('-p, --no-prefix', 'Hide command prefix')
|
|
@@ -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
|
-
//
|
|
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
|
|
146
|
+
isServerMode: true
|
|
115
147
|
});
|
|
116
148
|
}
|
|
117
149
|
catch (error) {
|
|
@@ -124,6 +156,438 @@ 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
|
+
// Process management commands
|
|
225
|
+
// Start command
|
|
226
|
+
program
|
|
227
|
+
.command('start <script>')
|
|
228
|
+
.description('Start a new process')
|
|
229
|
+
.option('-n, --name <name>', 'Process name')
|
|
230
|
+
.option('-i, --instances <number>', 'Number of instances', parseInt)
|
|
231
|
+
.option('--cwd <path>', 'Working directory')
|
|
232
|
+
.option('--env <env>', 'Environment variables (JSON string)')
|
|
233
|
+
.option('--no-autorestart', 'Disable auto-restart')
|
|
234
|
+
.option('--max-restarts <number>', 'Maximum restarts', parseInt)
|
|
235
|
+
.option('--restart-delay <ms>', 'Restart delay in ms', parseInt)
|
|
236
|
+
.option('--watch', 'Enable file watching')
|
|
237
|
+
.option('--ignore-watch <patterns...>', 'Patterns to ignore when watching')
|
|
238
|
+
.option('--max-memory <size>', 'Max memory before restart (e.g., 1G, 500M)')
|
|
239
|
+
.action(async (script, options) => {
|
|
240
|
+
try {
|
|
241
|
+
if (!processManager) {
|
|
242
|
+
processManager = new process_manager_js_1.ProcessManager();
|
|
243
|
+
}
|
|
244
|
+
const config = {
|
|
245
|
+
id: '',
|
|
246
|
+
name: options.name || path.basename(script, path.extname(script)),
|
|
247
|
+
script,
|
|
248
|
+
cwd: options.cwd,
|
|
249
|
+
env: options.env ? JSON.parse(options.env) : undefined,
|
|
250
|
+
instances: options.instances || 1,
|
|
251
|
+
autorestart: options.autorestart !== false,
|
|
252
|
+
max_restarts: options.maxRestarts || 10,
|
|
253
|
+
restart_delay: options.restartDelay || 1000,
|
|
254
|
+
watch: options.watch || false,
|
|
255
|
+
ignore_watch: options.ignoreWatch,
|
|
256
|
+
max_memory_restart: options.maxMemory
|
|
257
|
+
};
|
|
258
|
+
const id = await processManager.start(config);
|
|
259
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} Process started with ID: ${id}`));
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
if (error instanceof Error) {
|
|
263
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Start Error: ${error.message}`));
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown start error occurred`));
|
|
267
|
+
}
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
// Stop command
|
|
272
|
+
program
|
|
273
|
+
.command('stop <id>')
|
|
274
|
+
.description('Stop a process')
|
|
275
|
+
.action(async (id) => {
|
|
276
|
+
try {
|
|
277
|
+
if (!processManager) {
|
|
278
|
+
processManager = new process_manager_js_1.ProcessManager();
|
|
279
|
+
await processManager.load();
|
|
280
|
+
}
|
|
281
|
+
if (id === 'all') {
|
|
282
|
+
await processManager.stopAll();
|
|
283
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} All processes stopped`));
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
await processManager.stop(id);
|
|
287
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} Process ${id} stopped`));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (error instanceof Error) {
|
|
292
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Stop Error: ${error.message}`));
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown stop error occurred`));
|
|
296
|
+
}
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
// Restart command
|
|
301
|
+
program
|
|
302
|
+
.command('restart <id>')
|
|
303
|
+
.description('Restart a process')
|
|
304
|
+
.action(async (id) => {
|
|
305
|
+
try {
|
|
306
|
+
if (!processManager) {
|
|
307
|
+
processManager = new process_manager_js_1.ProcessManager();
|
|
308
|
+
await processManager.load();
|
|
309
|
+
}
|
|
310
|
+
if (id === 'all') {
|
|
311
|
+
await processManager.restartAll();
|
|
312
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} All processes restarted`));
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
await processManager.restart(id);
|
|
316
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} Process ${id} restarted`));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
if (error instanceof Error) {
|
|
321
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Restart Error: ${error.message}`));
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown restart error occurred`));
|
|
325
|
+
}
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
// Delete command
|
|
330
|
+
program
|
|
331
|
+
.command('delete <id>')
|
|
332
|
+
.description('Delete a process')
|
|
333
|
+
.action(async (id) => {
|
|
334
|
+
try {
|
|
335
|
+
if (!processManager) {
|
|
336
|
+
processManager = new process_manager_js_1.ProcessManager();
|
|
337
|
+
await processManager.load();
|
|
338
|
+
}
|
|
339
|
+
if (id === 'all') {
|
|
340
|
+
await processManager.deleteAll();
|
|
341
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} All processes deleted`));
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
await processManager.delete(id);
|
|
345
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} Process ${id} deleted`));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
if (error instanceof Error) {
|
|
350
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Delete Error: ${error.message}`));
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown delete error occurred`));
|
|
354
|
+
}
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
// List/Status command
|
|
359
|
+
program
|
|
360
|
+
.command('list')
|
|
361
|
+
.alias('status')
|
|
362
|
+
.alias('ls')
|
|
363
|
+
.description('List all processes')
|
|
364
|
+
.action(async () => {
|
|
365
|
+
try {
|
|
366
|
+
if (!processManager) {
|
|
367
|
+
processManager = new process_manager_js_1.ProcessManager();
|
|
368
|
+
await processManager.load();
|
|
369
|
+
}
|
|
370
|
+
const processes = await processManager.list();
|
|
371
|
+
if (processes.length === 0) {
|
|
372
|
+
console.log(chalk_1.default.yellow(`${figures_1.default.info} No processes found`));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
// Print table header
|
|
376
|
+
console.log('\n' + chalk_1.default.bold('Process Management Status'));
|
|
377
|
+
console.log(chalk_1.default.gray('─'.repeat(80)));
|
|
378
|
+
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')}`;
|
|
379
|
+
console.log(header);
|
|
380
|
+
console.log(chalk_1.default.gray('─'.repeat(80)));
|
|
381
|
+
// Print process information
|
|
382
|
+
processes.forEach(proc => {
|
|
383
|
+
const statusColor = proc.status === 'online' ? chalk_1.default.green :
|
|
384
|
+
proc.status === 'stopped' ? chalk_1.default.gray :
|
|
385
|
+
proc.status === 'errored' ? chalk_1.default.red : chalk_1.default.yellow;
|
|
386
|
+
const uptime = formatUptime(proc.uptime);
|
|
387
|
+
const pid = proc.pid ? proc.pid.toString() : '-';
|
|
388
|
+
const row = `${proc.id.padEnd(15)} ${proc.name.padEnd(20)} ${statusColor(proc.status.padEnd(10))} ${pid.padEnd(8)} ${uptime.padEnd(10)} ${proc.restarts}`;
|
|
389
|
+
console.log(row);
|
|
390
|
+
});
|
|
391
|
+
console.log(chalk_1.default.gray('─'.repeat(80)));
|
|
392
|
+
console.log(`\n${chalk_1.default.blue(`${figures_1.default.info} Total: ${processes.length} processes`)}`);
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
if (error instanceof Error) {
|
|
396
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} List Error: ${error.message}`));
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown list error occurred`));
|
|
400
|
+
}
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
// Logs command
|
|
405
|
+
program
|
|
406
|
+
.command('logs [id]')
|
|
407
|
+
.description('Show process logs')
|
|
408
|
+
.option('-f, --follow', 'Follow log output')
|
|
409
|
+
.option('-n, --lines <number>', 'Number of lines to show', parseInt)
|
|
410
|
+
.action(async (id, options) => {
|
|
411
|
+
try {
|
|
412
|
+
if (!processManager) {
|
|
413
|
+
processManager = new process_manager_js_1.ProcessManager();
|
|
414
|
+
await processManager.load();
|
|
415
|
+
}
|
|
416
|
+
if (id) {
|
|
417
|
+
const logs = await processManager.logs(id, options.lines || 100);
|
|
418
|
+
if (logs.length === 0) {
|
|
419
|
+
console.log(chalk_1.default.yellow(`${figures_1.default.info} No logs found for process ${id}`));
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
logs.forEach(log => console.log(log));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
console.log(chalk_1.default.yellow(`${figures_1.default.info} Please specify a process ID`));
|
|
427
|
+
}
|
|
428
|
+
if (options.follow) {
|
|
429
|
+
console.log(chalk_1.default.blue(`${figures_1.default.info} Following logs... Press Ctrl+C to stop`));
|
|
430
|
+
// TODO: Implement log following
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
if (error instanceof Error) {
|
|
435
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Logs Error: ${error.message}`));
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown logs error occurred`));
|
|
439
|
+
}
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
// Save command
|
|
444
|
+
program
|
|
445
|
+
.command('save')
|
|
446
|
+
.description('Save current process list')
|
|
447
|
+
.action(async () => {
|
|
448
|
+
try {
|
|
449
|
+
if (!processManager) {
|
|
450
|
+
processManager = new process_manager_js_1.ProcessManager();
|
|
451
|
+
await processManager.load();
|
|
452
|
+
}
|
|
453
|
+
await processManager.save();
|
|
454
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} Process list saved`));
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
if (error instanceof Error) {
|
|
458
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Save Error: ${error.message}`));
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown save error occurred`));
|
|
462
|
+
}
|
|
463
|
+
process.exit(1);
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
// Startup command (placeholder for system startup configuration)
|
|
467
|
+
program
|
|
468
|
+
.command('startup')
|
|
469
|
+
.description('Generate startup script')
|
|
470
|
+
.action(() => {
|
|
471
|
+
console.log(chalk_1.default.blue(`${figures_1.default.info} Startup script generation:`));
|
|
472
|
+
console.log('\nTo start neex processes on system boot, you can:');
|
|
473
|
+
console.log('1. Create a systemd service (Linux)');
|
|
474
|
+
console.log('2. Use launchd (macOS)');
|
|
475
|
+
console.log('3. Use Windows Service (Windows)');
|
|
476
|
+
console.log('\nExample systemd service file:');
|
|
477
|
+
console.log(chalk_1.default.gray(`
|
|
478
|
+
[Unit]
|
|
479
|
+
Description=Neex Process Manager
|
|
480
|
+
After=network.target
|
|
481
|
+
|
|
482
|
+
[Service]
|
|
483
|
+
Type=simple
|
|
484
|
+
User=your-user
|
|
485
|
+
WorkingDirectory=/path/to/your/project
|
|
486
|
+
ExecStart=/usr/local/bin/neex pm2 resurrect
|
|
487
|
+
Restart=always
|
|
488
|
+
|
|
489
|
+
[Install]
|
|
490
|
+
WantedBy=multi-user.target
|
|
491
|
+
`));
|
|
492
|
+
});
|
|
493
|
+
// Resurrect command (start saved processes)
|
|
494
|
+
program
|
|
495
|
+
.command('resurrect')
|
|
496
|
+
.description('Resurrect previously saved processes')
|
|
497
|
+
.action(async () => {
|
|
498
|
+
try {
|
|
499
|
+
if (!processManager) {
|
|
500
|
+
processManager = new process_manager_js_1.ProcessManager();
|
|
501
|
+
}
|
|
502
|
+
await processManager.load();
|
|
503
|
+
const processes = await processManager.list();
|
|
504
|
+
let started = 0;
|
|
505
|
+
for (const proc of processes) {
|
|
506
|
+
if (proc.status === 'stopped') {
|
|
507
|
+
try {
|
|
508
|
+
await processManager.restart(proc.id);
|
|
509
|
+
started++;
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
console.log(chalk_1.default.yellow(`${figures_1.default.warning} Failed to start ${proc.id}: ${error.message}`));
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} Resurrected ${started} processes`));
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
if (error instanceof Error) {
|
|
520
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Resurrect Error: ${error.message}`));
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown resurrect error occurred`));
|
|
524
|
+
}
|
|
525
|
+
process.exit(1);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
// Monit command (monitoring interface)
|
|
529
|
+
program
|
|
530
|
+
.command('monit')
|
|
531
|
+
.description('Launch monitoring interface')
|
|
532
|
+
.action(async () => {
|
|
533
|
+
try {
|
|
534
|
+
if (!processManager) {
|
|
535
|
+
processManager = new process_manager_js_1.ProcessManager();
|
|
536
|
+
await processManager.load();
|
|
537
|
+
}
|
|
538
|
+
console.log(chalk_1.default.blue(`${figures_1.default.info} Starting monitoring interface...`));
|
|
539
|
+
console.log(chalk_1.default.yellow(`${figures_1.default.warning} Press 'q' to quit, 'r' to refresh`));
|
|
540
|
+
// Simple monitoring loop
|
|
541
|
+
const monitorLoop = async () => {
|
|
542
|
+
console.clear();
|
|
543
|
+
console.log(chalk_1.default.bold.blue('Neex Process Monitor'));
|
|
544
|
+
console.log(chalk_1.default.gray('─'.repeat(80)));
|
|
545
|
+
const processes = await processManager.list();
|
|
546
|
+
if (processes.length === 0) {
|
|
547
|
+
console.log(chalk_1.default.yellow(`${figures_1.default.info} No processes running`));
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
processes.forEach(proc => {
|
|
551
|
+
const statusColor = proc.status === 'online' ? chalk_1.default.green :
|
|
552
|
+
proc.status === 'stopped' ? chalk_1.default.gray :
|
|
553
|
+
proc.status === 'errored' ? chalk_1.default.red : chalk_1.default.yellow;
|
|
554
|
+
console.log(`${statusColor('●')} ${proc.name} (${proc.id}) - ${statusColor(proc.status)}`);
|
|
555
|
+
console.log(` PID: ${proc.pid || 'N/A'} | Uptime: ${formatUptime(proc.uptime)} | Restarts: ${proc.restarts}`);
|
|
556
|
+
console.log();
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
console.log(chalk_1.default.gray('─'.repeat(80)));
|
|
560
|
+
console.log(chalk_1.default.blue(`Last updated: ${new Date().toLocaleTimeString()}`));
|
|
561
|
+
};
|
|
562
|
+
// Initial display
|
|
563
|
+
await monitorLoop();
|
|
564
|
+
// Set up keyboard input handling
|
|
565
|
+
process.stdin.setRawMode(true);
|
|
566
|
+
process.stdin.resume();
|
|
567
|
+
process.stdin.setEncoding('utf8');
|
|
568
|
+
const interval = setInterval(monitorLoop, 5000);
|
|
569
|
+
process.stdin.on('data', (key) => {
|
|
570
|
+
if (key.toString() === 'q' || key.toString() === '\u0003') { // 'q' or Ctrl+C
|
|
571
|
+
clearInterval(interval);
|
|
572
|
+
process.stdin.setRawMode(false);
|
|
573
|
+
console.log('\n' + chalk_1.default.green(`${figures_1.default.tick} Monitoring stopped`));
|
|
574
|
+
process.exit(0);
|
|
575
|
+
}
|
|
576
|
+
else if (key.toString() === 'r') {
|
|
577
|
+
monitorLoop();
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
catch (error) {
|
|
582
|
+
if (error instanceof Error) {
|
|
583
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Monit Error: ${error.message}`));
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} An unknown monit error occurred`));
|
|
587
|
+
}
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
127
591
|
program.parse(process.argv);
|
|
128
592
|
// Show help if no commands specified
|
|
129
593
|
if (program.args.length === 0) {
|
|
@@ -132,14 +596,35 @@ function cli() {
|
|
|
132
596
|
// Graceful shutdown handling
|
|
133
597
|
const handleSignal = (signal) => {
|
|
134
598
|
console.log(`\n${chalk_1.default.yellow(`${figures_1.default.warning} Received ${signal}. Cleaning up...`)}`);
|
|
599
|
+
if (devRunner && devRunner.isActive()) {
|
|
600
|
+
devRunner.stop();
|
|
601
|
+
}
|
|
135
602
|
if (cleanupRunner) {
|
|
136
603
|
cleanupRunner();
|
|
137
604
|
}
|
|
138
|
-
|
|
605
|
+
if (processManager) {
|
|
606
|
+
processManager.dispose();
|
|
607
|
+
}
|
|
139
608
|
setTimeout(() => process.exit(0), 500);
|
|
140
609
|
};
|
|
141
|
-
process.on('SIGINT', () => handleSignal('SIGINT'));
|
|
610
|
+
process.on('SIGINT', () => handleSignal('SIGINT'));
|
|
142
611
|
process.on('SIGTERM', () => handleSignal('SIGTERM'));
|
|
143
612
|
process.on('SIGQUIT', () => handleSignal('SIGQUIT'));
|
|
144
613
|
}
|
|
145
614
|
exports.default = cli;
|
|
615
|
+
// Helper function to format uptime
|
|
616
|
+
function formatUptime(seconds) {
|
|
617
|
+
if (seconds < 60) {
|
|
618
|
+
return `${seconds}s`;
|
|
619
|
+
}
|
|
620
|
+
else if (seconds < 3600) {
|
|
621
|
+
const minutes = Math.floor(seconds / 60);
|
|
622
|
+
const remainingSeconds = seconds % 60;
|
|
623
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
const hours = Math.floor(seconds / 3600);
|
|
627
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
628
|
+
return `${hours}h ${minutes}m`;
|
|
629
|
+
}
|
|
630
|
+
}
|