scripts-orchestrator 2.9.0 → 2.12.0

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.
@@ -4,7 +4,6 @@ import path from 'path';
4
4
  import { log } from './logger.js';
5
5
  import { HealthCheck } from './health-check.js';
6
6
 
7
-
8
7
  export class ProcessManager {
9
8
  constructor() {
10
9
  this.logger = log;
@@ -13,20 +12,39 @@ export class ProcessManager {
13
12
  this.logFolder = 'scripts-orchestrator-logs'; // Default log folder
14
13
  }
15
14
 
15
+ formatDuration(ms) {
16
+ if (ms < 1000) return `${ms}ms`;
17
+ const seconds = Math.floor(ms / 1000);
18
+ const minutes = Math.floor(seconds / 60);
19
+ const remainingSeconds = seconds % 60;
20
+ if (minutes > 0) {
21
+ return `${minutes}m ${remainingSeconds}s`;
22
+ }
23
+ return `${seconds}s`;
24
+ }
25
+
16
26
  setLogFolder(logFolder) {
17
27
  this.logFolder = logFolder;
18
28
  this.logger.verbose(`Log folder set to: ${logFolder}`);
19
29
  }
20
30
 
21
31
  getLogPath(command) {
22
- const baseDir = this.logFolder ? path.resolve(this.logFolder) : process.cwd();
32
+ const baseDir = this.logFolder
33
+ ? path.resolve(this.logFolder)
34
+ : process.cwd();
23
35
  const LOGS_DIR = path.join(baseDir, 'scripts-orchestrator-logs');
24
36
  // Use only the first word of the command for the log filename
25
37
  const logName = command.split(/\s+/)[0];
26
38
  return path.join(LOGS_DIR, `${logName}.log`);
27
39
  }
28
40
 
29
- addBackgroundProcess({ command, url, startedByScript, process_tracking, kill_command }) {
41
+ addBackgroundProcess({
42
+ command,
43
+ url,
44
+ startedByScript,
45
+ process_tracking,
46
+ kill_command,
47
+ }) {
30
48
  this.logger.verbose(`Adding background process: ${command} (${url})`);
31
49
  this.backgroundProcessesDetails.push({
32
50
  command,
@@ -37,8 +55,18 @@ export class ProcessManager {
37
55
  });
38
56
  }
39
57
 
40
- async runCommand({ cmd, logFile, background = false, healthCheck = null, kill_command = null, isRetry = false, env = null }) {
41
- const baseDir = this.logFolder ? path.resolve(this.logFolder) : process.cwd();
58
+ async runCommand({
59
+ cmd,
60
+ logFile,
61
+ background = false,
62
+ healthCheck = null,
63
+ kill_command = null,
64
+ isRetry = false,
65
+ env = null,
66
+ }) {
67
+ const baseDir = this.logFolder
68
+ ? path.resolve(this.logFolder)
69
+ : process.cwd();
42
70
  const LOGS_DIR = path.join(baseDir, 'scripts-orchestrator-logs');
43
71
  // Use only the first word of the command for the log filename
44
72
  const logName = cmd.split(/\s+/)[0];
@@ -54,7 +82,9 @@ export class ProcessManager {
54
82
  this.logger.verbose(`Clearing log file at ${LOG_FILE}`);
55
83
  fs.writeFileSync(LOG_FILE, ''); // Clear the log file
56
84
  } else {
57
- this.logger.verbose(`Appending to existing log file at ${LOG_FILE} (retry attempt)`);
85
+ this.logger.verbose(
86
+ `Appending to existing log file at ${LOG_FILE} (retry attempt)`,
87
+ );
58
88
  }
59
89
  } catch (error) {
60
90
  this.logger.error(`Failed to setup log file: ${error.message}`);
@@ -62,18 +92,21 @@ export class ProcessManager {
62
92
  }
63
93
 
64
94
  return new Promise((resolve) => {
95
+ const startTime = Date.now();
65
96
  // Build command with environment variables if provided
66
97
  let fullCommand = `npm run ${cmd}`;
67
98
  if (env && Object.keys(env).length > 0) {
68
- const envStr = Object.entries(env).map(([key, value]) => `${key}=${value}`).join(' ');
99
+ const envStr = Object.entries(env)
100
+ .map(([key, value]) => `${key}=${value}`)
101
+ .join(' ');
69
102
  fullCommand = `${envStr} npm run ${cmd}`;
70
103
  }
71
-
72
- this.logger.info(`Running: ${fullCommand}`);
73
-
104
+
105
+ this.logger.startTask(cmd, fullCommand);
106
+
74
107
  // Create isolated environment for each process
75
108
  const isolatedEnv = this.createIsolatedEnvironment({ command: cmd, env });
76
-
109
+
77
110
  const options = {
78
111
  shell: true,
79
112
  detached: background,
@@ -91,6 +124,7 @@ export class ProcessManager {
91
124
  const processInstance = spawn(fullCommand, [], options);
92
125
 
93
126
  processInstance.on('error', (error) => {
127
+ this.logger.stopTask(cmd);
94
128
  this.logger.error(`Failed to start process: ${error.message}`);
95
129
  //this.logger.verbose(`Process error details: ${JSON.stringify(error, null, 2)}`);
96
130
  resolve({ success: false, output: '' });
@@ -98,23 +132,29 @@ export class ProcessManager {
98
132
 
99
133
  if (background) {
100
134
  const processGroupId = processInstance.pid;
101
- this.logger.verbose(`Background process spawned with PID: ${processGroupId}`);
135
+ this.logger.verbose(
136
+ `Background process spawned with PID: ${processGroupId}`,
137
+ );
102
138
 
103
139
  // Track process exit for background processes
104
140
  let processExited = false;
105
141
  let processExitCode = null;
106
-
142
+
107
143
  processInstance.on('exit', (code, signal) => {
108
144
  processExited = true;
109
145
  processExitCode = code;
110
- this.logger.verbose(`Background process ${cmd} (PID: ${processGroupId}) exited with code: ${code}, signal: ${signal}`);
146
+ this.logger.verbose(
147
+ `Background process ${cmd} (PID: ${processGroupId}) exited with code: ${code}, signal: ${signal}`,
148
+ );
111
149
  });
112
150
 
113
151
  processInstance.stdout.on('data', (data) => {
114
152
  try {
115
153
  fs.appendFileSync(LOG_FILE, data.toString());
116
154
  } catch (error) {
117
- this.logger.error(`Failed to write to log file: ${error.message}`);
155
+ this.logger.error(
156
+ `Failed to write to log file: ${error.message}`,
157
+ );
118
158
  }
119
159
  });
120
160
 
@@ -122,7 +162,9 @@ export class ProcessManager {
122
162
  try {
123
163
  fs.appendFileSync(LOG_FILE, data.toString());
124
164
  } catch (error) {
125
- this.logger.error(`Failed to write to log file: ${error.message}`);
165
+ this.logger.error(
166
+ `Failed to write to log file: ${error.message}`,
167
+ );
126
168
  }
127
169
  });
128
170
 
@@ -134,37 +176,49 @@ export class ProcessManager {
134
176
  try {
135
177
  // First check if the process has already exited with an error
136
178
  if (processExited && processExitCode !== 0) {
137
- this.logger.error(`Background process ${cmd} exited with code ${processExitCode}`);
179
+ this.logger.stopTask(cmd);
180
+ this.logger.error(
181
+ `Background process ${cmd} exited with code ${processExitCode}`,
182
+ );
138
183
  let output = '';
139
184
  try {
140
185
  output = fs.readFileSync(LOG_FILE, 'utf8');
141
186
  this.logger.verbose(`Process output: ${output}`);
142
187
  } catch (error) {
143
- this.logger.error(`Failed to read log file: ${error.message}`);
188
+ this.logger.error(
189
+ `Failed to read log file: ${error.message}`,
190
+ );
144
191
  }
145
192
  return { success: false, output };
146
193
  }
147
-
148
- this.logger.verbose(`Verifying process ${processGroupId} (attempt ${attempt}/${maxAttempts})`);
194
+
195
+ this.logger.verbose(
196
+ `Verifying process ${processGroupId} (attempt ${attempt}/${maxAttempts})`,
197
+ );
149
198
  process.kill(processGroupId, 0);
150
199
  this.logger.verbose(`Process ${processGroupId} is running`);
151
-
200
+
152
201
  // Wait a bit more to ensure the process doesn't exit immediately after verification
153
202
  await new Promise((resolve) => setTimeout(resolve, 500));
154
-
203
+
155
204
  // Check again if the process exited during our wait
156
205
  if (processExited && processExitCode !== 0) {
157
- this.logger.error(`Background process ${cmd} exited with code ${processExitCode} shortly after starting`);
206
+ this.logger.stopTask(cmd);
207
+ this.logger.error(
208
+ `Background process ${cmd} exited with code ${processExitCode} shortly after starting`,
209
+ );
158
210
  let output = '';
159
211
  try {
160
212
  output = fs.readFileSync(LOG_FILE, 'utf8');
161
213
  this.logger.verbose(`Process output: ${output}`);
162
214
  } catch (error) {
163
- this.logger.error(`Failed to read log file: ${error.message}`);
215
+ this.logger.error(
216
+ `Failed to read log file: ${error.message}`,
217
+ );
164
218
  }
165
219
  return { success: false, output };
166
220
  }
167
-
221
+
168
222
  this.backgroundProcesses.push(processGroupId);
169
223
  this.backgroundProcessesDetails.push({
170
224
  command: cmd,
@@ -174,22 +228,31 @@ export class ProcessManager {
174
228
  startedByScript: true,
175
229
  kill_command,
176
230
  });
177
-
231
+
178
232
  this.logger.verbose(`Unreferencing process ${processGroupId}`);
179
233
  processInstance.unref();
180
-
234
+
235
+ this.logger.stopTask(cmd);
181
236
  this.logger.verbose(
182
237
  `Background process started: npm run ${cmd} (PGID: ${processGroupId})`,
183
238
  );
184
239
  return { success: true, output: '' };
185
240
  } catch (error) {
186
241
  if (attempt === maxAttempts) {
187
- this.logger.error(`Failed to start background process: npm run ${cmd}`);
188
- this.logger.verbose(`Final verification attempt failed: ${error.message}`);
242
+ this.logger.error(
243
+ `Failed to start background process: npm run ${cmd}`,
244
+ );
245
+ this.logger.verbose(
246
+ `Final verification attempt failed: ${error.message}`,
247
+ );
189
248
  return { success: false, output: '' };
190
249
  }
191
- this.logger.verbose(`Verification attempt ${attempt} failed: ${error.message}`);
192
- this.logger.verbose(`Waiting ${baseDelay * Math.pow(2, attempt - 1)}ms before next attempt`);
250
+ this.logger.verbose(
251
+ `Verification attempt ${attempt} failed: ${error.message}`,
252
+ );
253
+ this.logger.verbose(
254
+ `Waiting ${baseDelay * Math.pow(2, attempt - 1)}ms before next attempt`,
255
+ );
193
256
  await new Promise((resolve) =>
194
257
  setTimeout(resolve, baseDelay * Math.pow(2, attempt - 1)),
195
258
  );
@@ -204,7 +267,9 @@ export class ProcessManager {
204
267
  try {
205
268
  fs.appendFileSync(LOG_FILE, data.toString());
206
269
  } catch (error) {
207
- this.logger.error(`Failed to write to log file: ${error.message}`);
270
+ this.logger.error(
271
+ `Failed to write to log file: ${error.message}`,
272
+ );
208
273
  }
209
274
  });
210
275
 
@@ -212,7 +277,9 @@ export class ProcessManager {
212
277
  try {
213
278
  fs.appendFileSync(LOG_FILE, data.toString());
214
279
  } catch (error) {
215
- this.logger.error(`Failed to write to log file: ${error.message}`);
280
+ this.logger.error(
281
+ `Failed to write to log file: ${error.message}`,
282
+ );
216
283
  }
217
284
  });
218
285
 
@@ -224,17 +291,25 @@ export class ProcessManager {
224
291
  this.logger.error(`Failed to read log file: ${error.message}`);
225
292
  }
226
293
 
294
+ this.logger.stopTask(cmd);
295
+
296
+ const duration = Date.now() - startTime;
297
+ const durationStr = ` (${this.formatDuration(duration)})`;
298
+
227
299
  if (code !== 0) {
228
- this.logger.error(`Failed: npm run ${cmd} (exit code: ${code})`);
300
+ this.logger.error(
301
+ `Failed: npm run ${cmd} ❌${durationStr} (exit code: ${code})`,
302
+ );
229
303
  this.logger.verbose(`Process output: ${output}`);
230
304
  resolve({ success: false, output });
231
305
  } else {
232
- this.logger.success(`Completed: npm run ${cmd}`);
306
+ this.logger.success(`Completed: npm run ${cmd} ✅${durationStr}`);
233
307
  resolve({ success: true, output });
234
308
  }
235
309
  });
236
310
  }
237
311
  } catch (error) {
312
+ this.logger.stopTask(cmd);
238
313
  this.logger.error(`Failed to spawn process: ${error.message}`);
239
314
  //this.logger.verbose(`Spawn error details: ${JSON.stringify(error, null, 2)}`);
240
315
  resolve({ success: false, output: '' });
@@ -245,7 +320,7 @@ export class ProcessManager {
245
320
  createIsolatedEnvironment({ command, env = null }) {
246
321
  // Create a deep copy to avoid any reference sharing
247
322
  const baseEnv = JSON.parse(JSON.stringify(process.env));
248
-
323
+
249
324
  // Set standard environment variables
250
325
  const isolatedEnv = {
251
326
  ...baseEnv,
@@ -272,25 +347,37 @@ export class ProcessManager {
272
347
  // Remove any potentially problematic environment variables
273
348
  delete isolatedEnv.npm_lifecycle_event;
274
349
  delete isolatedEnv.npm_lifecycle_script;
275
-
350
+
276
351
  return isolatedEnv;
277
352
  }
278
353
 
279
354
  async cleanup() {
280
355
  try {
281
356
  this.logger.info('\nCleaning up background processes...');
282
-
357
+
283
358
  // Debug: Log the number of processes we're tracking
284
- this.logger.info(`- Found ${this.backgroundProcessesDetails.length} background processes to clean up`);
285
-
359
+ this.logger.info(
360
+ `- Found ${this.backgroundProcessesDetails.length} background processes to clean up`,
361
+ );
362
+
286
363
  // Debug: Log each process details
287
- this.backgroundProcessesDetails.forEach(({ command, pgid, url, startedByScript, kill_command }, index) => {
288
- this.logger.verbose(`- Process ${index + 1}: command=${command}, pgid=${pgid}, url=${url}, startedByScript=${startedByScript}, kill_command=${kill_command}`);
289
- });
290
-
364
+ this.backgroundProcessesDetails.forEach(
365
+ ({ command, pgid, url, startedByScript, kill_command }, index) => {
366
+ this.logger.verbose(
367
+ `- Process ${index + 1}: command=${command}, pgid=${pgid}, url=${url}, startedByScript=${startedByScript}, kill_command=${kill_command}`,
368
+ );
369
+ },
370
+ );
371
+
291
372
  const killPromises = this.backgroundProcessesDetails.map(
292
373
  async ({ command, pgid, url, startedByScript, kill_command }) => {
293
- await this.cleanupProcess({ command, pgid, url, startedByScript, kill_command });
374
+ await this.cleanupProcess({
375
+ command,
376
+ pgid,
377
+ url,
378
+ startedByScript,
379
+ kill_command,
380
+ });
294
381
  },
295
382
  );
296
383
 
@@ -304,33 +391,43 @@ export class ProcessManager {
304
391
 
305
392
  async cleanupCommand(commandName) {
306
393
  this.logger.info(`\nCleaning up processes for command: ${commandName}`);
307
-
394
+
308
395
  // Find processes for this specific command
309
396
  const commandProcesses = this.backgroundProcessesDetails.filter(
310
- ({ command }) => command === commandName
397
+ ({ command }) => command === commandName,
311
398
  );
312
-
399
+
313
400
  if (commandProcesses.length === 0) {
314
- this.logger.verbose(`- No background processes found for command: ${commandName}`);
401
+ this.logger.verbose(
402
+ `- No background processes found for command: ${commandName}`,
403
+ );
315
404
  return;
316
405
  }
317
-
318
- this.logger.verbose(`- Found ${commandProcesses.length} background processes for command: ${commandName}`);
319
-
406
+
407
+ this.logger.verbose(
408
+ `- Found ${commandProcesses.length} background processes for command: ${commandName}`,
409
+ );
410
+
320
411
  const killPromises = commandProcesses.map(
321
412
  async ({ command, pgid, url, startedByScript, kill_command }) => {
322
- await this.cleanupProcess({ command, pgid, url, startedByScript, kill_command });
323
- }
413
+ await this.cleanupProcess({
414
+ command,
415
+ pgid,
416
+ url,
417
+ startedByScript,
418
+ kill_command,
419
+ });
420
+ },
324
421
  );
325
422
 
326
423
  await Promise.allSettled(killPromises);
327
-
424
+
328
425
  // Remove the cleaned up processes from our tracking arrays
329
- this.backgroundProcesses = this.backgroundProcesses.filter(pgid =>
330
- !commandProcesses.some(proc => proc.pgid === pgid)
426
+ this.backgroundProcesses = this.backgroundProcesses.filter(
427
+ (pgid) => !commandProcesses.some((proc) => proc.pgid === pgid),
331
428
  );
332
429
  this.backgroundProcessesDetails = this.backgroundProcessesDetails.filter(
333
- ({ command }) => command !== commandName
430
+ ({ command }) => command !== commandName,
334
431
  );
335
432
  }
336
433
 
@@ -342,24 +439,40 @@ export class ProcessManager {
342
439
  return;
343
440
  }
344
441
 
345
- this.logger.verbose(`- Processing cleanup for ${command} (kill_command: ${kill_command})`);
442
+ this.logger.verbose(
443
+ `- Processing cleanup for ${command} (kill_command: ${kill_command})`,
444
+ );
346
445
 
347
446
  // Try custom kill command first if specified
348
447
  if (kill_command) {
349
448
  try {
350
- this.logger.verbose(`- Using custom kill command: npm run ${kill_command}`);
351
- const result = await this.runCommand({ cmd: kill_command, logFile: null, background: false });
449
+ this.logger.verbose(
450
+ `- Using custom kill command: npm run ${kill_command}`,
451
+ );
452
+ const result = await this.runCommand({
453
+ cmd: kill_command,
454
+ logFile: null,
455
+ background: false,
456
+ });
352
457
  if (result.success) {
353
- this.logger.verbose(`- Successfully killed ${command} using custom command`);
458
+ this.logger.verbose(
459
+ `- Successfully killed ${command} using custom command`,
460
+ );
354
461
  return;
355
462
  } else {
356
- this.logger.verbose('- Custom kill command failed, falling back to process signals');
463
+ this.logger.verbose(
464
+ '- Custom kill command failed, falling back to process signals',
465
+ );
357
466
  }
358
467
  } catch (error) {
359
- this.logger.verbose(`- Custom kill command error: ${error.message}, falling back`);
468
+ this.logger.verbose(
469
+ `- Custom kill command error: ${error.message}, falling back`,
470
+ );
360
471
  }
361
472
  } else {
362
- this.logger.verbose(`- No kill_command specified for ${command}, using process signals`);
473
+ this.logger.verbose(
474
+ `- No kill_command specified for ${command}, using process signals`,
475
+ );
363
476
  }
364
477
 
365
478
  try {
@@ -375,27 +488,36 @@ export class ProcessManager {
375
488
 
376
489
  // Cross-platform process termination
377
490
  const isWindows = process.platform === 'win32';
378
-
491
+
379
492
  if (isWindows) {
380
493
  // Windows: use taskkill to terminate process tree
381
494
  try {
382
- const killProcess = spawn('taskkill', ['/F', '/T', '/PID', pgid.toString()]);
495
+ const killProcess = spawn('taskkill', [
496
+ '/F',
497
+ '/T',
498
+ '/PID',
499
+ pgid.toString(),
500
+ ]);
383
501
  await new Promise((resolve) => {
384
502
  killProcess.on('close', resolve);
385
503
  });
386
- this.logger.verbose(`- Terminated background process: ${command} (PID: ${pgid})`);
504
+ this.logger.verbose(
505
+ `- Terminated background process: ${command} (PID: ${pgid})`,
506
+ );
387
507
  return;
388
508
  } catch (killError) {
389
- this.logger.verbose(`- Failed to use taskkill, falling back to process.kill: ${killError.message}`);
509
+ this.logger.verbose(
510
+ `- Failed to use taskkill, falling back to process.kill: ${killError.message}`,
511
+ );
390
512
  }
391
513
  }
392
-
514
+
393
515
  // Unix/Linux/macOS or Windows fallback: Try SIGTERM first
394
516
  process.kill(pgid, 'SIGTERM');
395
517
 
396
518
  await new Promise((resolve, reject) => {
397
519
  let timeout, checkInterval;
398
-
520
+
399
521
  timeout = setTimeout(() => {
400
522
  if (checkInterval) clearInterval(checkInterval);
401
523
  reject(new Error('Process termination timeout'));
@@ -415,26 +537,31 @@ export class ProcessManager {
415
537
  `- Terminated background process: ${command} (PGID: ${pgid})`,
416
538
  );
417
539
  } catch (error) {
418
- this.logger.verbose(`- Failed to terminate process group: ${error.message}`);
540
+ this.logger.verbose(
541
+ `- Failed to terminate process group: ${error.message}`,
542
+ );
419
543
  }
420
544
 
421
545
  // Check if the URL is still responding after termination attempt
422
546
  if (url) {
423
547
  try {
424
548
  const urlObj = new URL(url);
425
- const port = urlObj.port || (urlObj.protocol === 'https:' ? '443' : '80');
426
-
549
+ const port =
550
+ urlObj.port || (urlObj.protocol === 'https:' ? '443' : '80');
551
+
427
552
  // Use shared HTTP utility for cross-platform compatibility
428
553
  const urlResult = await HealthCheck.makeHttpRequest(url, 2000);
429
-
554
+
430
555
  if (urlResult.success && urlResult.statusCode === 200) {
431
- this.logger.verbose(`- URL ${url} is still responding after termination, finding process on port ${port}`);
432
-
556
+ this.logger.verbose(
557
+ `- URL ${url} is still responding after termination, finding process on port ${port}`,
558
+ );
559
+
433
560
  // Find and kill process using the port - cross-platform approach
434
561
  try {
435
562
  const isWindows = process.platform === 'win32';
436
563
  let findPortCmd, findPortArgs;
437
-
564
+
438
565
  if (isWindows) {
439
566
  // Windows: use netstat
440
567
  findPortCmd = 'netstat';
@@ -444,7 +571,7 @@ export class ProcessManager {
444
571
  findPortCmd = 'lsof';
445
572
  findPortArgs = ['-i', `:${port}`, '-t'];
446
573
  }
447
-
574
+
448
575
  const findProcess = spawn(findPortCmd, findPortArgs);
449
576
  const result = await new Promise((resolve) => {
450
577
  let output = '';
@@ -458,12 +585,15 @@ export class ProcessManager {
458
585
 
459
586
  if (result.code === 0 && result.output.trim()) {
460
587
  let pids = [];
461
-
588
+
462
589
  if (isWindows) {
463
590
  // Parse netstat output to find PIDs for the specific port
464
591
  const lines = result.output.split('\n');
465
592
  for (const line of lines) {
466
- if (line.includes(`:${port} `) && line.includes('LISTENING')) {
593
+ if (
594
+ line.includes(`:${port} `) &&
595
+ line.includes('LISTENING')
596
+ ) {
467
597
  const parts = line.trim().split(/\s+/);
468
598
  const pid = parts[parts.length - 1];
469
599
  if (pid && !isNaN(pid)) {
@@ -475,7 +605,7 @@ export class ProcessManager {
475
605
  // lsof output is already just PIDs
476
606
  pids = result.output.trim().split('\n');
477
607
  }
478
-
608
+
479
609
  for (const pid of pids) {
480
610
  try {
481
611
  if (isWindows) {
@@ -488,16 +618,22 @@ export class ProcessManager {
488
618
  // Unix/Linux/macOS: use process.kill
489
619
  process.kill(parseInt(pid), 'SIGKILL');
490
620
  }
491
- this.logger.verbose(`- Killed process (PID: ${pid}) using port ${port}`);
621
+ this.logger.verbose(
622
+ `- Killed process (PID: ${pid}) using port ${port}`,
623
+ );
492
624
  } catch (killError) {
493
625
  if (killError.code !== 'ESRCH') {
494
- this.logger.error(`- Failed to kill process (PID: ${pid}): ${killError.message}`);
626
+ this.logger.error(
627
+ `- Failed to kill process (PID: ${pid}): ${killError.message}`,
628
+ );
495
629
  }
496
630
  }
497
631
  }
498
632
  }
499
633
  } catch (portError) {
500
- this.logger.error(`- Failed to find process using port ${port}: ${portError.message}`);
634
+ this.logger.error(
635
+ `- Failed to find process using port ${port}: ${portError.message}`,
636
+ );
501
637
  }
502
638
  }
503
639
  } catch (error) {
@@ -508,10 +644,15 @@ export class ProcessManager {
508
644
  // Final attempt to kill the process group
509
645
  try {
510
646
  const isWindows = process.platform === 'win32';
511
-
647
+
512
648
  if (isWindows) {
513
649
  // Windows: force kill with taskkill
514
- const killProcess = spawn('taskkill', ['/F', '/T', '/PID', pgid.toString()]);
650
+ const killProcess = spawn('taskkill', [
651
+ '/F',
652
+ '/T',
653
+ '/PID',
654
+ pgid.toString(),
655
+ ]);
515
656
  await new Promise((resolve) => {
516
657
  killProcess.on('close', resolve);
517
658
  });
@@ -528,4 +669,4 @@ export class ProcessManager {
528
669
  }
529
670
 
530
671
  // For backward compatibility
531
- export const processManager = new ProcessManager();
672
+ export const processManager = new ProcessManager();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scripts-orchestrator",
3
- "version": "2.9.0",
3
+ "version": "2.12.0",
4
4
  "description": "A powerful script orchestrator for running parallel commands with dependency management, background processes, and health checks",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",