neex 0.7.20 → 0.7.32

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 (58) hide show
  1. package/README.md +13 -31
  2. package/dist/bin/neex.d.ts +26 -0
  3. package/dist/bin/neex.d.ts.map +1 -0
  4. package/dist/src/build-manager.d.ts +41 -0
  5. package/dist/src/build-manager.d.ts.map +1 -0
  6. package/dist/src/build-manager.js +26 -24
  7. package/dist/src/cli.d.ts +2 -0
  8. package/dist/src/cli.d.ts.map +1 -0
  9. package/dist/src/commands/build-commands.d.ts +5 -0
  10. package/dist/src/commands/build-commands.d.ts.map +1 -0
  11. package/dist/src/commands/build-commands.js +2 -2
  12. package/dist/src/commands/dev-commands.d.ts +5 -0
  13. package/dist/src/commands/dev-commands.d.ts.map +1 -0
  14. package/dist/src/commands/dev-commands.js +5 -3
  15. package/dist/src/commands/index.d.ts +7 -0
  16. package/dist/src/commands/index.d.ts.map +1 -0
  17. package/dist/src/commands/init-commands.d.ts +2 -0
  18. package/dist/src/commands/init-commands.d.ts.map +1 -0
  19. package/dist/src/commands/init-commands.js +4 -4
  20. package/dist/src/commands/run-commands.d.ts +3 -0
  21. package/dist/src/commands/run-commands.d.ts.map +1 -0
  22. package/dist/src/commands/run-commands.js +26 -26
  23. package/dist/src/commands/server-commands.d.ts +3 -0
  24. package/dist/src/commands/server-commands.d.ts.map +1 -0
  25. package/dist/src/commands/server-commands.js +4 -2
  26. package/dist/src/commands/start-commands.d.ts +5 -0
  27. package/dist/src/commands/start-commands.d.ts.map +1 -0
  28. package/dist/src/commands/start-commands.js +2 -2
  29. package/dist/src/dev-manager.d.ts +51 -0
  30. package/dist/src/dev-manager.d.ts.map +1 -0
  31. package/dist/src/dev-manager.js +29 -21
  32. package/dist/src/index.d.ts +41 -0
  33. package/dist/src/index.d.ts.map +1 -0
  34. package/dist/src/index.js +15 -16
  35. package/dist/src/logger-manager.d.ts +4 -0
  36. package/dist/src/logger-manager.d.ts.map +1 -0
  37. package/dist/src/logger-manager.js +4 -4
  38. package/dist/src/logger.d.ts +34 -0
  39. package/dist/src/logger.d.ts.map +1 -0
  40. package/dist/src/logger.js +21 -15
  41. package/dist/src/runner.d.ts +21 -0
  42. package/dist/src/runner.d.ts.map +1 -0
  43. package/dist/src/runner.js +38 -25
  44. package/dist/src/start-manager.d.ts +49 -0
  45. package/dist/src/start-manager.d.ts.map +1 -0
  46. package/dist/src/start-manager.js +34 -29
  47. package/dist/src/types.d.ts +41 -0
  48. package/dist/src/types.d.ts.map +1 -0
  49. package/dist/src/utils.d.ts +2 -0
  50. package/dist/src/utils.d.ts.map +1 -0
  51. package/package.json +14 -9
  52. package/dist/src/cli-init.js +0 -1
  53. package/dist/src/commands/process-commands.js +0 -759
  54. package/dist/src/config.js +0 -59
  55. package/dist/src/dev-runner.js +0 -234
  56. package/dist/src/logger-process.js +0 -17
  57. package/dist/src/process-manager.js +0 -669
  58. package/dist/src/watcher.js +0 -245
@@ -1,669 +0,0 @@
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
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.ProcessManager = void 0;
30
- // src/process-manager.ts - Enhanced PM2-like process manager
31
- const child_process_1 = require("child_process");
32
- const fs = __importStar(require("fs"));
33
- const fs_1 = require("fs");
34
- const path = __importStar(require("path"));
35
- const events_1 = require("events");
36
- const chalk_1 = __importDefault(require("chalk"));
37
- const figures_1 = __importDefault(require("figures"));
38
- const logger_js_1 = __importDefault(require("./logger.js"));
39
- class ProcessManager extends events_1.EventEmitter {
40
- constructor(configPath) {
41
- super();
42
- this.processes = new Map();
43
- this.logStreams = new Map();
44
- this.isMonitoring = false;
45
- this.isDisposed = false;
46
- const baseDir = path.join(process.cwd(), '.neex');
47
- this.configPath = configPath || path.join(baseDir, 'processes.json');
48
- this.logDir = path.join(baseDir, 'logs');
49
- this.pidDir = path.join(baseDir, 'pids');
50
- this.ensureDirectories();
51
- this.startMonitoring();
52
- }
53
- async ensureDirectories() {
54
- try {
55
- const dirs = [
56
- path.dirname(this.configPath),
57
- this.logDir,
58
- this.pidDir
59
- ];
60
- for (const dir of dirs) {
61
- await fs_1.promises.mkdir(dir, { recursive: true });
62
- }
63
- }
64
- catch (error) {
65
- logger_js_1.default.printLine(`Failed to create directories: ${error.message}`, 'error');
66
- }
67
- }
68
- async saveConfig() {
69
- try {
70
- const config = Array.from(this.processes.values()).map(proc => {
71
- var _a, _b;
72
- return ({
73
- ...proc.config,
74
- status: proc.status,
75
- created_at: proc.created_at.toISOString(),
76
- started_at: (_a = proc.started_at) === null || _a === void 0 ? void 0 : _a.toISOString(),
77
- restarts: proc.restarts,
78
- last_restart: (_b = proc.last_restart) === null || _b === void 0 ? void 0 : _b.toISOString()
79
- });
80
- });
81
- await fs_1.promises.writeFile(this.configPath, JSON.stringify(config, null, 2));
82
- }
83
- catch (error) {
84
- logger_js_1.default.printLine(`Failed to save config: ${error.message}`, 'error');
85
- }
86
- }
87
- async load() {
88
- try {
89
- const data = await fs_1.promises.readFile(this.configPath, 'utf-8');
90
- const configs = JSON.parse(data);
91
- for (const config of configs) {
92
- if (config.id && !this.processes.has(config.id)) {
93
- const processInfo = {
94
- id: config.id,
95
- name: config.name,
96
- status: 'stopped',
97
- uptime: 0,
98
- restarts: config.restarts || 0,
99
- memory: 0,
100
- cpu: 0,
101
- created_at: config.created_at ? new Date(config.created_at) : new Date(),
102
- started_at: config.started_at ? new Date(config.started_at) : undefined,
103
- last_restart: config.last_restart ? new Date(config.last_restart) : undefined,
104
- config: {
105
- ...config,
106
- // Ensure required fields have defaults
107
- autorestart: config.autorestart !== false,
108
- max_restarts: config.max_restarts || 10,
109
- restart_delay: config.restart_delay || 1000,
110
- instances: config.instances || 1
111
- }
112
- };
113
- this.processes.set(config.id, processInfo);
114
- // Check if process is actually running
115
- if (config.pid) {
116
- const isRunning = await this.isProcessRunning(config.pid);
117
- if (isRunning) {
118
- processInfo.status = 'online';
119
- processInfo.pid = config.pid;
120
- processInfo.started_at = processInfo.started_at || new Date();
121
- }
122
- }
123
- }
124
- }
125
- }
126
- catch (error) {
127
- // Config file might not exist yet, that's fine
128
- }
129
- }
130
- async isProcessRunning(pid) {
131
- try {
132
- process.kill(pid, 0);
133
- return true;
134
- }
135
- catch (error) {
136
- return false;
137
- }
138
- }
139
- generateId(name) {
140
- let id = name.toLowerCase().replace(/[^a-z0-9-_]/g, '-');
141
- let counter = 0;
142
- while (this.processes.has(id)) {
143
- counter++;
144
- id = `${name.toLowerCase().replace(/[^a-z0-9-_]/g, '-')}-${counter}`;
145
- }
146
- return id;
147
- }
148
- setupLogStreams(processInfo) {
149
- const { config } = processInfo;
150
- // Close existing streams
151
- this.closeLogStreams(processInfo.id);
152
- // Setup default log paths
153
- if (!config.out_file) {
154
- config.out_file = path.join(this.logDir, `${processInfo.id}-out.log`);
155
- }
156
- if (!config.error_file) {
157
- config.error_file = path.join(this.logDir, `${processInfo.id}-error.log`);
158
- }
159
- try {
160
- const outStream = fs.createWriteStream(config.out_file, { flags: 'a' });
161
- const errStream = fs.createWriteStream(config.error_file, { flags: 'a' });
162
- this.logStreams.set(processInfo.id, { out: outStream, err: errStream });
163
- }
164
- catch (error) {
165
- logger_js_1.default.printLine(`Failed to create log streams for ${processInfo.id}: ${error.message}`, 'error');
166
- }
167
- }
168
- closeLogStreams(id) {
169
- var _a, _b;
170
- const streams = this.logStreams.get(id);
171
- if (streams) {
172
- (_a = streams.out) === null || _a === void 0 ? void 0 : _a.end();
173
- (_b = streams.err) === null || _b === void 0 ? void 0 : _b.end();
174
- this.logStreams.delete(id);
175
- }
176
- }
177
- async writePidFile(processInfo) {
178
- if (!processInfo.pid)
179
- return;
180
- const pidFile = path.join(this.pidDir, `${processInfo.id}.pid`);
181
- try {
182
- await fs_1.promises.writeFile(pidFile, processInfo.pid.toString());
183
- }
184
- catch (error) {
185
- logger_js_1.default.printLine(`Failed to write PID file: ${error.message}`, 'error');
186
- }
187
- }
188
- async removePidFile(id) {
189
- const pidFile = path.join(this.pidDir, `${id}.pid`);
190
- try {
191
- await fs_1.promises.unlink(pidFile);
192
- }
193
- catch (error) {
194
- // PID file might not exist, ignore
195
- }
196
- }
197
- parseCommand(script) {
198
- // Handle different script formats
199
- if (script.includes(' ')) {
200
- const parts = script.split(' ');
201
- return {
202
- command: parts[0],
203
- args: parts.slice(1)
204
- };
205
- }
206
- // Handle file extensions
207
- if (script.endsWith('.js') || script.endsWith('.mjs') || script.endsWith('.cjs')) {
208
- return {
209
- command: 'node',
210
- args: [script]
211
- };
212
- }
213
- if (script.endsWith('.ts') || script.endsWith('.mts') || script.endsWith('.cts')) {
214
- return {
215
- command: 'npx',
216
- args: ['ts-node', script]
217
- };
218
- }
219
- return {
220
- command: script,
221
- args: []
222
- };
223
- }
224
- async startProcess(processInfo) {
225
- const { config } = processInfo;
226
- try {
227
- processInfo.status = 'launching';
228
- processInfo.started_at = new Date();
229
- // Setup logging
230
- this.setupLogStreams(processInfo);
231
- // Parse command
232
- const { command, args } = this.parseCommand(config.script);
233
- const finalArgs = [...args, ...(config.args || [])];
234
- // Setup environment
235
- const env = {
236
- ...process.env,
237
- ...config.env,
238
- NEEX_PROCESS_ID: processInfo.id,
239
- NEEX_PROCESS_NAME: processInfo.name,
240
- NODE_ENV: process.env.NODE_ENV || 'production'
241
- };
242
- // Spawn process
243
- const childProcess = (0, child_process_1.spawn)(command, finalArgs, {
244
- cwd: config.cwd || process.cwd(),
245
- env,
246
- stdio: ['ignore', 'pipe', 'pipe'],
247
- detached: true // Detach the process to run independently
248
- });
249
- if (!childProcess.pid) {
250
- throw new Error(`Failed to start process: ${config.script}`);
251
- }
252
- processInfo.process = childProcess;
253
- processInfo.pid = childProcess.pid;
254
- processInfo.status = 'online';
255
- // Allow the parent process to exit independently of the child
256
- childProcess.unref();
257
- // Write PID file
258
- await this.writePidFile(processInfo);
259
- console.log(chalk_1.default.green(`${figures_1.default.tick} Process ${processInfo.name} (${processInfo.id}) started with PID ${processInfo.pid}`));
260
- this.emit('process:start', processInfo);
261
- // Setup logging
262
- const streams = this.logStreams.get(processInfo.id);
263
- if (childProcess.stdout && (streams === null || streams === void 0 ? void 0 : streams.out)) {
264
- childProcess.stdout.pipe(streams.out);
265
- childProcess.stdout.on('data', (data) => {
266
- var _a;
267
- const message = data.toString();
268
- if (config.time) {
269
- const timestamp = new Date().toISOString();
270
- (_a = streams.out) === null || _a === void 0 ? void 0 : _a.write(`[${timestamp}] ${message}`);
271
- }
272
- this.emit('process:log', {
273
- id: processInfo.id,
274
- type: 'stdout',
275
- data: message
276
- });
277
- });
278
- }
279
- if (childProcess.stderr && (streams === null || streams === void 0 ? void 0 : streams.err)) {
280
- childProcess.stderr.pipe(streams.err);
281
- childProcess.stderr.on('data', (data) => {
282
- var _a;
283
- const message = data.toString();
284
- if (config.time) {
285
- const timestamp = new Date().toISOString();
286
- (_a = streams.err) === null || _a === void 0 ? void 0 : _a.write(`[${timestamp}] ${message}`);
287
- }
288
- this.emit('process:log', {
289
- id: processInfo.id,
290
- type: 'stderr',
291
- data: message
292
- });
293
- });
294
- }
295
- // Handle process exit
296
- childProcess.on('exit', async (code, signal) => {
297
- processInfo.exit_code = code || undefined;
298
- processInfo.exit_signal = signal || undefined;
299
- processInfo.status = code === 0 ? 'stopped' : 'errored';
300
- processInfo.process = undefined;
301
- processInfo.pid = undefined;
302
- await this.removePidFile(processInfo.id);
303
- const exitMsg = signal
304
- ? `Process ${processInfo.name} killed by signal ${signal}`
305
- : `Process ${processInfo.name} exited with code ${code}`;
306
- console.log(chalk_1.default.yellow(`${figures_1.default.warning} ${exitMsg}`));
307
- this.emit('process:exit', { processInfo, code, signal });
308
- // Auto-restart if enabled and not manually stopped
309
- if (config.autorestart &&
310
- processInfo.status === 'errored' &&
311
- processInfo.restarts < (config.max_restarts || 10)) {
312
- const delay = config.restart_delay || 1000;
313
- console.log(chalk_1.default.blue(`${figures_1.default.info} Restarting ${processInfo.name} in ${delay}ms...`));
314
- setTimeout(async () => {
315
- try {
316
- await this.restart(processInfo.id);
317
- }
318
- catch (error) {
319
- console.error(chalk_1.default.red(`${figures_1.default.cross} Failed to restart ${processInfo.name}: ${error.message}`));
320
- }
321
- }, delay);
322
- }
323
- await this.saveConfig();
324
- });
325
- // Handle process error
326
- childProcess.on('error', (error) => {
327
- processInfo.status = 'errored';
328
- console.error(chalk_1.default.red(`${figures_1.default.cross} Process ${processInfo.name} error: ${error.message}`));
329
- this.emit('process:error', { processInfo, error });
330
- });
331
- await this.saveConfig();
332
- }
333
- catch (error) {
334
- processInfo.status = 'errored';
335
- console.error(chalk_1.default.red(`${figures_1.default.cross} Failed to start ${processInfo.name}: ${error.message}`));
336
- this.emit('process:error', { processInfo, error });
337
- throw error;
338
- }
339
- }
340
- startMonitoring() {
341
- if (this.isMonitoring || this.isDisposed)
342
- return;
343
- this.isMonitoring = true;
344
- this.monitoringInterval = setInterval(async () => {
345
- await this.updateProcessStats();
346
- }, 5000);
347
- }
348
- stopMonitoring() {
349
- if (this.monitoringInterval) {
350
- clearInterval(this.monitoringInterval);
351
- this.monitoringInterval = undefined;
352
- }
353
- this.isMonitoring = false;
354
- }
355
- async updateProcessStats() {
356
- for (const processInfo of this.processes.values()) {
357
- if (processInfo.status === 'online' && processInfo.pid) {
358
- try {
359
- // Check if process is still running
360
- const isRunning = await this.isProcessRunning(processInfo.pid);
361
- if (!isRunning) {
362
- processInfo.status = 'stopped';
363
- processInfo.process = undefined;
364
- processInfo.pid = undefined;
365
- await this.removePidFile(processInfo.id);
366
- continue;
367
- }
368
- // Update uptime
369
- if (processInfo.started_at) {
370
- processInfo.uptime = Math.floor((Date.now() - processInfo.started_at.getTime()) / 1000);
371
- }
372
- // Get memory and CPU usage (Unix/Linux only)
373
- if (process.platform !== 'win32') {
374
- try {
375
- const stats = await this.getProcessStats(processInfo.pid);
376
- processInfo.memory = stats.memory;
377
- processInfo.cpu = stats.cpu;
378
- }
379
- catch (error) {
380
- // Process might have died
381
- }
382
- }
383
- }
384
- catch (error) {
385
- // Process monitoring error
386
- }
387
- }
388
- }
389
- }
390
- async getProcessStats(pid) {
391
- return new Promise((resolve, reject) => {
392
- (0, child_process_1.exec)(`ps -o pid,rss,pcpu -p ${pid}`, (error, stdout) => {
393
- if (error) {
394
- resolve({ memory: 0, cpu: 0 });
395
- return;
396
- }
397
- const lines = stdout.trim().split('\n');
398
- if (lines.length < 2) {
399
- resolve({ memory: 0, cpu: 0 });
400
- return;
401
- }
402
- const stats = lines[1].trim().split(/\s+/);
403
- const memory = parseInt(stats[1]) * 1024; // Convert KB to bytes
404
- const cpu = parseFloat(stats[2]);
405
- resolve({ memory, cpu });
406
- });
407
- });
408
- }
409
- // Public API methods
410
- async start(config) {
411
- const id = this.generateId(config.name);
412
- const processInfo = {
413
- id,
414
- name: config.name,
415
- status: 'stopped',
416
- uptime: 0,
417
- restarts: 0,
418
- memory: 0,
419
- cpu: 0,
420
- created_at: new Date(),
421
- config: { ...config, id }
422
- };
423
- this.processes.set(id, processInfo);
424
- await this.startProcess(processInfo);
425
- return id;
426
- }
427
- async stop(id) {
428
- const processInfo = this.processes.get(id);
429
- if (!processInfo) {
430
- throw new Error(`Process ${id} not found`);
431
- }
432
- if (processInfo.status === 'stopped') {
433
- console.log(chalk_1.default.yellow(`${figures_1.default.info} Process ${id} is already stopped`));
434
- return;
435
- }
436
- processInfo.status = 'stopping';
437
- console.log(chalk_1.default.blue(`${figures_1.default.info} Stopping process ${processInfo.name} (${id})...`));
438
- if (processInfo.process && processInfo.pid) {
439
- try {
440
- // Try graceful shutdown first
441
- processInfo.process.kill('SIGTERM');
442
- // Wait for graceful shutdown
443
- await new Promise(resolve => setTimeout(resolve, 2000));
444
- // Force kill if still running
445
- if (processInfo.process && !processInfo.process.killed) {
446
- processInfo.process.kill('SIGKILL');
447
- }
448
- }
449
- catch (error) {
450
- // Process might already be dead
451
- }
452
- }
453
- processInfo.status = 'stopped';
454
- processInfo.process = undefined;
455
- processInfo.pid = undefined;
456
- await this.removePidFile(id);
457
- this.closeLogStreams(id);
458
- await this.saveConfig();
459
- console.log(chalk_1.default.green(`${figures_1.default.tick} Process ${processInfo.name} (${id}) stopped`));
460
- this.emit('process:stop', processInfo);
461
- }
462
- async restart(id) {
463
- const processInfo = this.processes.get(id);
464
- if (!processInfo) {
465
- throw new Error(`Process ${id} not found`);
466
- }
467
- console.log(chalk_1.default.blue(`${figures_1.default.info} Restarting process ${processInfo.name} (${id})...`));
468
- if (processInfo.status === 'online') {
469
- await this.stop(id);
470
- // Wait a bit before restarting
471
- await new Promise(resolve => setTimeout(resolve, 1000));
472
- }
473
- processInfo.restarts++;
474
- processInfo.last_restart = new Date();
475
- await this.startProcess(processInfo);
476
- }
477
- async delete(id) {
478
- const processInfo = this.processes.get(id);
479
- if (!processInfo) {
480
- throw new Error(`Process ${id} not found`);
481
- }
482
- if (processInfo.status === 'online') {
483
- await this.stop(id);
484
- }
485
- // Remove log files
486
- try {
487
- if (processInfo.config.out_file) {
488
- await fs_1.promises.unlink(processInfo.config.out_file);
489
- }
490
- if (processInfo.config.error_file) {
491
- await fs_1.promises.unlink(processInfo.config.error_file);
492
- }
493
- }
494
- catch (error) {
495
- // Log files might not exist
496
- }
497
- this.closeLogStreams(id);
498
- this.processes.delete(id);
499
- await this.saveConfig();
500
- console.log(chalk_1.default.green(`${figures_1.default.tick} Process ${processInfo.name} (${id}) deleted`));
501
- this.emit('process:delete', processInfo);
502
- }
503
- async list() {
504
- return Array.from(this.processes.values());
505
- }
506
- async logs(id, lines = 15) {
507
- const processInfo = this.processes.get(id);
508
- if (!processInfo) {
509
- throw new Error(`Process ${id} not found`);
510
- }
511
- return this.readLogFiles(processInfo.config, lines);
512
- }
513
- async readLogFiles(config, lines) {
514
- const logs = [];
515
- const readLastLines = async (filePath) => {
516
- try {
517
- const content = await fs_1.promises.readFile(filePath, 'utf-8');
518
- const fileLines = content.split('\n').filter(line => line.trim() !== '');
519
- return fileLines.slice(-lines);
520
- }
521
- catch (error) {
522
- return [];
523
- }
524
- };
525
- if (config.out_file) {
526
- const outLogs = await readLastLines(config.out_file);
527
- logs.push(...outLogs.map(line => `[OUT] ${line}`));
528
- }
529
- if (config.error_file) {
530
- const errLogs = await readLastLines(config.error_file);
531
- logs.push(...errLogs.map(line => `[ERR] ${line}`));
532
- }
533
- return logs.slice(-lines);
534
- }
535
- followLogs(id, onLogEntry) {
536
- const processInfo = this.processes.get(id);
537
- if (!processInfo) {
538
- logger_js_1.default.printLine(`Process ${id} not found for log following.`, 'error');
539
- return undefined;
540
- }
541
- const { config } = processInfo;
542
- const logFilesToWatch = [];
543
- // Resolve log file paths, defaulting if not specified (consistent with setupLogStreams)
544
- const outFile = config.out_file || path.join(this.logDir, `${processInfo.id}-out.log`);
545
- const errFile = config.error_file || path.join(this.logDir, `${processInfo.id}-error.log`);
546
- logFilesToWatch.push({ type: 'OUT', path: outFile });
547
- if (errFile !== outFile) { // Avoid watching the same file path twice
548
- logFilesToWatch.push({ type: 'ERR', path: errFile });
549
- }
550
- const lastReadSizes = {};
551
- const setupWatcherForFile = (filePath, type) => {
552
- try {
553
- const stats = fs.existsSync(filePath) ? fs.statSync(filePath) : { size: 0 };
554
- lastReadSizes[filePath] = stats.size;
555
- }
556
- catch (err) {
557
- lastReadSizes[filePath] = 0;
558
- // logger.printLine(`Could not get initial stats for ${filePath}: ${(err as Error).message}. Assuming size 0.`, 'debug');
559
- }
560
- const listener = (curr, prev) => {
561
- if (curr.mtimeMs <= prev.mtimeMs && curr.size === prev.size) {
562
- return;
563
- }
564
- const newSize = curr.size;
565
- let oldSize = lastReadSizes[filePath];
566
- // Fallback for oldSize, though it should be initialized
567
- if (typeof oldSize !== 'number')
568
- oldSize = 0;
569
- if (newSize > oldSize) {
570
- const stream = fs.createReadStream(filePath, {
571
- start: oldSize,
572
- end: newSize - 1,
573
- encoding: 'utf-8',
574
- });
575
- stream.on('data', (chunk) => {
576
- chunk.split('\n').forEach(line => {
577
- if (line.trim() !== '') {
578
- onLogEntry(`[${type}] ${line}`);
579
- }
580
- });
581
- });
582
- stream.on('error', (streamErr) => {
583
- logger_js_1.default.printLine(`Error reading log chunk for ${filePath}: ${streamErr.message}`, 'error');
584
- });
585
- lastReadSizes[filePath] = newSize;
586
- }
587
- else if (newSize < oldSize) {
588
- // File was truncated or replaced
589
- // logger.printLine(`Log file ${filePath} was truncated. Reading from start.`, 'debug');
590
- lastReadSizes[filePath] = 0;
591
- const stream = fs.createReadStream(filePath, {
592
- start: 0,
593
- end: newSize - 1,
594
- encoding: 'utf-8',
595
- });
596
- stream.on('data', (chunk) => {
597
- chunk.split('\n').forEach(line => {
598
- if (line.trim() !== '') {
599
- onLogEntry(`[${type}] ${line}`);
600
- }
601
- });
602
- });
603
- stream.on('error', (streamErr) => {
604
- logger_js_1.default.printLine(`Error reading truncated log file ${filePath}: ${streamErr.message}`, 'error');
605
- });
606
- lastReadSizes[filePath] = newSize;
607
- }
608
- };
609
- try {
610
- fs.watchFile(filePath, { persistent: true, interval: 500 }, listener);
611
- }
612
- catch (watchError) {
613
- logger_js_1.default.printLine(`Failed to watch log file ${filePath}: ${watchError.message}`, 'error');
614
- }
615
- };
616
- logFilesToWatch.forEach(fileToWatch => {
617
- if (fileToWatch.path) { // Ensure path is defined
618
- setupWatcherForFile(fileToWatch.path, fileToWatch.type);
619
- }
620
- });
621
- const stop = () => {
622
- logFilesToWatch.forEach(fileToWatch => {
623
- if (fileToWatch.path) { // Ensure path is defined before unwatching
624
- try {
625
- fs.unwatchFile(fileToWatch.path);
626
- }
627
- catch (unwatchError) {
628
- // logger.printLine(`Error unwatching ${fileToWatch.path}: ${(unwatchError as Error).message}`, 'debug');
629
- }
630
- }
631
- });
632
- };
633
- return { stop };
634
- }
635
- async save() {
636
- await this.saveConfig();
637
- console.log(chalk_1.default.green(`${figures_1.default.tick} Process configuration saved`));
638
- }
639
- async stopAll() {
640
- const promises = Array.from(this.processes.keys()).map(id => this.stop(id));
641
- await Promise.all(promises);
642
- }
643
- async restartAll() {
644
- const promises = Array.from(this.processes.keys()).map(id => this.restart(id));
645
- await Promise.all(promises);
646
- }
647
- async deleteAll() {
648
- await this.stopAll();
649
- this.processes.clear();
650
- await this.saveConfig();
651
- console.log(chalk_1.default.green(`${figures_1.default.tick} All processes deleted`));
652
- }
653
- getProcess(id) {
654
- return this.processes.get(id);
655
- }
656
- async dispose() {
657
- if (this.isDisposed)
658
- return;
659
- this.isDisposed = true;
660
- this.stopMonitoring();
661
- // Close all log streams
662
- for (const id of this.logStreams.keys()) {
663
- this.closeLogStreams(id);
664
- }
665
- // Stop all processes
666
- await this.stopAll();
667
- }
668
- }
669
- exports.ProcessManager = ProcessManager;