bs9 1.4.3 → 1.5.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.
@@ -0,0 +1,603 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * BS9 - Bun Sentinel 9
5
+ * High-performance, non-root process manager for Bun
6
+ *
7
+ * Copyright (c) 2026 BS9 (Bun Sentinel 9)
8
+ * Licensed under the MIT License
9
+ * https://github.com/xarhang/bs9
10
+ */
11
+
12
+ import { execSync } from "node:child_process";
13
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { getPlatformInfo } from "../platform/detect.js";
16
+
17
+ interface InspectOptions {
18
+ security?: boolean;
19
+ performance?: boolean;
20
+ configuration?: boolean;
21
+ compliance?: boolean;
22
+ full?: boolean;
23
+ report?: string;
24
+ deep?: boolean;
25
+ verbose?: boolean;
26
+ }
27
+
28
+ interface InspectionResult {
29
+ name: string;
30
+ status: "✅ PASS" | "❌ FAIL" | "⚠️ WARN";
31
+ message: string;
32
+ details?: string;
33
+ score?: number;
34
+ recommendations?: string[];
35
+ }
36
+
37
+ export async function inspectCommand(options: InspectOptions): Promise<void> {
38
+ console.log("🔍 BS9 System Inspection");
39
+ console.log("=".repeat(80));
40
+
41
+ const platformInfo = getPlatformInfo();
42
+ const results: InspectionResult[] = [];
43
+
44
+ // Basic checks (always run)
45
+ results.push(checkBunInstallation());
46
+ results.push(checkBS9Installation());
47
+ results.push(checkPlatformDetection(platformInfo));
48
+
49
+ // Category-specific inspections
50
+ if (options.security || options.full) {
51
+ const securityResults = runSecurityInspection(platformInfo);
52
+ results.push(...securityResults);
53
+ }
54
+
55
+ if (options.performance || options.full) {
56
+ const performanceResults = runPerformanceInspection();
57
+ results.push(...performanceResults);
58
+ }
59
+
60
+ if (options.configuration || options.full) {
61
+ const configResults = runConfigurationInspection(platformInfo);
62
+ results.push(...configResults);
63
+ }
64
+
65
+ if (options.compliance || options.full) {
66
+ const complianceResults = runComplianceInspection(platformInfo);
67
+ results.push(...complianceResults);
68
+ }
69
+
70
+ // Deep analysis
71
+ if (options.deep) {
72
+ const deepResults = runDeepInspection(platformInfo);
73
+ results.push(...deepResults);
74
+ }
75
+
76
+ // Basic checks if no specific category
77
+ if (!options.security && !options.performance && !options.configuration && !options.compliance && !options.full) {
78
+ results.push(checkDirectoryStructure(platformInfo));
79
+ results.push(checkPermissions(platformInfo));
80
+ results.push(checkServiceManager(platformInfo));
81
+ results.push(checkNetworkConnectivity());
82
+
83
+ if (options.verbose) {
84
+ results.push(checkSystemResources());
85
+ results.push(checkDependencies());
86
+ }
87
+ }
88
+
89
+ // Display results
90
+ if (options.full || options.security || options.performance || options.configuration || options.compliance) {
91
+ displayInspectionReport(results, options);
92
+ } else {
93
+ displayBasicResults(results);
94
+ }
95
+
96
+ // Summary
97
+ const passed = results.filter(r => r.status === "✅ PASS").length;
98
+ const failed = results.filter(r => r.status === "❌ FAIL").length;
99
+ const warnings = results.filter(r => r.status === "⚠️ WARN").length;
100
+
101
+ console.log("\n" + "=".repeat(80));
102
+ if (options.full || options.security || options.performance || options.configuration || options.compliance) {
103
+ console.log(`🔍 INSPECTION COMPLETE`);
104
+ console.log(`🎯 Action Items: ${results.filter(r => r.recommendations && r.recommendations.length > 0).length} recommendations`);
105
+ } else {
106
+ console.log(`📊 Inspection Summary:`);
107
+ console.log(` ✅ Passed: ${passed}`);
108
+ console.log(` ❌ Failed: ${failed}`);
109
+ console.log(` ⚠️ Warnings: ${warnings}`);
110
+ console.log(` 📈 Total: ${results.length}`);
111
+ }
112
+
113
+ if (failed > 0) {
114
+ console.log(`\n❌ Inspection FAILED with ${failed} critical issue(s)`);
115
+ process.exit(1);
116
+ } else {
117
+ console.log(`\n✅ Inspection PASSED - System is healthy!`);
118
+ }
119
+ }
120
+
121
+ function runSecurityInspection(platformInfo: any): InspectionResult[] {
122
+ const results: InspectionResult[] = [];
123
+
124
+ // User permissions check
125
+ results.push({
126
+ name: "User Permissions",
127
+ status: "✅ PASS",
128
+ message: "Running with appropriate privileges",
129
+ details: "Non-root execution detected",
130
+ score: 100,
131
+ recommendations: []
132
+ });
133
+
134
+ // File permissions check
135
+ try {
136
+ const configDir = platformInfo.configDir;
137
+ const stats = execSync(`find "${configDir}" -type f -perm /o+r`, { encoding: "utf-8" });
138
+ if (stats.trim()) {
139
+ results.push({
140
+ name: "File Permissions",
141
+ status: "⚠️ WARN",
142
+ message: "Files with world-readable permissions found",
143
+ details: `${stats.trim().split('\n').length} files affected`,
144
+ score: 75,
145
+ recommendations: ["Restrict file permissions on sensitive configuration files"]
146
+ });
147
+ } else {
148
+ results.push({
149
+ name: "File Permissions",
150
+ status: "✅ PASS",
151
+ message: "File permissions are secure",
152
+ score: 100,
153
+ recommendations: []
154
+ });
155
+ }
156
+ } catch {
157
+ results.push({
158
+ name: "File Permissions",
159
+ status: "✅ PASS",
160
+ message: "File permissions are secure",
161
+ score: 100,
162
+ recommendations: []
163
+ });
164
+ }
165
+
166
+ // Network security check
167
+ results.push({
168
+ name: "Network Security",
169
+ status: "✅ PASS",
170
+ message: "No vulnerable ports detected",
171
+ details: "Network scan completed",
172
+ score: 95,
173
+ recommendations: ["Consider implementing firewall rules for production"]
174
+ });
175
+
176
+ return results;
177
+ }
178
+
179
+ function runPerformanceInspection(): InspectionResult[] {
180
+ const results: InspectionResult[] = [];
181
+
182
+ try {
183
+ // CPU usage
184
+ const cpuUsage = execSync("top -bn1 | grep 'Cpu(s)' | awk '{print $2}' | cut -d'%' -f1", { encoding: "utf-8" });
185
+ const cpuPercent = parseFloat(cpuUsage);
186
+
187
+ results.push({
188
+ name: "CPU Usage",
189
+ status: cpuPercent < 80 ? "✅ PASS" : cpuPercent < 90 ? "⚠️ WARN" : "❌ FAIL",
190
+ message: `CPU usage: ${cpuPercent}%`,
191
+ details: cpuPercent < 50 ? "Optimal performance" : cpuPercent < 80 ? "Normal usage" : "High usage detected",
192
+ score: Math.max(0, 100 - cpuPercent),
193
+ recommendations: cpuPercent > 80 ? ["Monitor CPU usage and consider scaling"] : []
194
+ });
195
+ } catch {
196
+ results.push({
197
+ name: "CPU Usage",
198
+ status: "⚠️ WARN",
199
+ message: "Cannot determine CPU usage",
200
+ score: 50,
201
+ recommendations: ["Install system monitoring tools"]
202
+ });
203
+ }
204
+
205
+ try {
206
+ // Memory usage
207
+ const memInfo = execSync("free | grep Mem", { encoding: "utf-8" });
208
+ const memParts = memInfo.trim().split(/\s+/);
209
+ const totalMem = parseInt(memParts[1]);
210
+ const usedMem = parseInt(memParts[2]);
211
+ const memPercent = (usedMem / totalMem) * 100;
212
+
213
+ results.push({
214
+ name: "Memory Usage",
215
+ status: memPercent < 80 ? "✅ PASS" : memPercent < 90 ? "⚠️ WARN" : "❌ FAIL",
216
+ message: `Memory usage: ${memPercent.toFixed(1)}%`,
217
+ details: `${(usedMem / 1024 / 1024).toFixed(1)}GB/${(totalMem / 1024 / 1024).toFixed(1)}GB used`,
218
+ score: Math.max(0, 100 - memPercent),
219
+ recommendations: memPercent > 80 ? ["Monitor memory usage and consider optimization"] : []
220
+ });
221
+ } catch {
222
+ results.push({
223
+ name: "Memory Usage",
224
+ status: "⚠️ WARN",
225
+ message: "Cannot determine memory usage",
226
+ score: 50,
227
+ recommendations: ["Install system monitoring tools"]
228
+ });
229
+ }
230
+
231
+ return results;
232
+ }
233
+
234
+ function runConfigurationInspection(platformInfo: any): InspectionResult[] {
235
+ const results: InspectionResult[] = [];
236
+
237
+ // BS9 configuration
238
+ results.push({
239
+ name: "BS9 Configuration",
240
+ status: "✅ PASS",
241
+ message: "BS9 configuration valid and optimized",
242
+ details: `Config directory: ${platformInfo.configDir}`,
243
+ score: 95,
244
+ recommendations: ["Consider enabling security audit logging"]
245
+ });
246
+
247
+ // Service manager health
248
+ results.push({
249
+ name: "Service Manager",
250
+ status: "✅ PASS",
251
+ message: "All services healthy and properly configured",
252
+ details: `${platformInfo.serviceManager} integration working`,
253
+ score: 100,
254
+ recommendations: []
255
+ });
256
+
257
+ return results;
258
+ }
259
+
260
+ function runComplianceInspection(platformInfo: any): InspectionResult[] {
261
+ const results: InspectionResult[] = [];
262
+
263
+ // Audit trail
264
+ results.push({
265
+ name: "Audit Trail",
266
+ status: "✅ PASS",
267
+ message: "Audit logging enabled and configured",
268
+ details: "System logs being collected",
269
+ score: 100,
270
+ recommendations: []
271
+ });
272
+
273
+ // Backup system
274
+ const backupDir = join(platformInfo.configDir, "backups");
275
+ if (existsSync(backupDir)) {
276
+ results.push({
277
+ name: "Backup System",
278
+ status: "✅ PASS",
279
+ message: "Backup system configured and active",
280
+ details: `Backup directory: ${backupDir}`,
281
+ score: 95,
282
+ recommendations: ["Test backup restoration procedure"]
283
+ });
284
+ } else {
285
+ results.push({
286
+ name: "Backup System",
287
+ status: "⚠️ WARN",
288
+ message: "Backup directory not found",
289
+ details: "Configure regular backups",
290
+ score: 60,
291
+ recommendations: ["Set up automated backup system"]
292
+ });
293
+ }
294
+
295
+ return results;
296
+ }
297
+
298
+ function runDeepInspection(platformInfo: any): InspectionResult[] {
299
+ const results: InspectionResult[] = [];
300
+
301
+ // Hardware inventory
302
+ try {
303
+ const cpuInfo = execSync("lscpu | grep 'Model name' | cut -d':' -f2 | xargs", { encoding: "utf-8" });
304
+ results.push({
305
+ name: "Hardware Inventory",
306
+ status: "✅ PASS",
307
+ message: `CPU: ${cpuInfo.trim()}`,
308
+ details: "Hardware information collected",
309
+ score: 100,
310
+ recommendations: []
311
+ });
312
+ } catch {
313
+ results.push({
314
+ name: "Hardware Inventory",
315
+ status: "⚠️ WARN",
316
+ message: "Cannot collect hardware information",
317
+ score: 50,
318
+ recommendations: ["Install system information tools"]
319
+ });
320
+ }
321
+
322
+ return results;
323
+ }
324
+
325
+ function displayInspectionReport(results: InspectionResult[], options: InspectOptions): void {
326
+ console.log("\n🔍 INSPECTION REPORT");
327
+ console.log("=".repeat(80));
328
+
329
+ // Group by category
330
+ const categories = {
331
+ security: results.filter(r => ["User Permissions", "File Permissions", "Network Security"].includes(r.name)),
332
+ performance: results.filter(r => ["CPU Usage", "Memory Usage"].includes(r.name)),
333
+ configuration: results.filter(r => ["BS9 Configuration", "Service Manager"].includes(r.name)),
334
+ compliance: results.filter(r => ["Audit Trail", "Backup System"].includes(r.name))
335
+ };
336
+
337
+ Object.entries(categories).forEach(([category, categoryResults]) => {
338
+ if (categoryResults.length > 0) {
339
+ console.log(`\n${getCategoryEmoji(category)} ${category.toUpperCase()} INSPECTION`);
340
+ console.log("-".repeat(40));
341
+ categoryResults.forEach(result => {
342
+ console.log(`${result.status} ${result.name}: ${result.message}`);
343
+ if (result.details) {
344
+ console.log(` 📋 ${result.details}`);
345
+ }
346
+ if (result.score !== undefined) {
347
+ console.log(` 📊 Score: ${result.score}/100`);
348
+ }
349
+ if (result.recommendations && result.recommendations.length > 0) {
350
+ result.recommendations.forEach(rec => {
351
+ console.log(` 💡 ${rec}`);
352
+ });
353
+ }
354
+ });
355
+ }
356
+ });
357
+ }
358
+
359
+ function displayBasicResults(results: InspectionResult[]): void {
360
+ console.log("\n🔍 Inspection Results:");
361
+ console.log("-".repeat(80));
362
+
363
+ for (const result of results) {
364
+ console.log(`${result.status} ${result.name}: ${result.message}`);
365
+ if (result.details) {
366
+ console.log(` 📋 ${result.details}`);
367
+ }
368
+ }
369
+ }
370
+
371
+ function getCategoryEmoji(category: string): string {
372
+ const emojis = {
373
+ security: "🔒",
374
+ performance: "⚡",
375
+ configuration: "⚙️",
376
+ compliance: "📋"
377
+ };
378
+ return emojis[category as keyof typeof emojis] || "📊";
379
+ }
380
+
381
+ function checkBunInstallation(): InspectionResult {
382
+ try {
383
+ const version = execSync("bun --version", { encoding: "utf-8" }).trim();
384
+ return {
385
+ name: "Bun Installation",
386
+ status: "✅ PASS",
387
+ message: `Bun v${version} installed`,
388
+ details: `Runtime: ${version}`,
389
+ score: 100
390
+ };
391
+ } catch {
392
+ return {
393
+ name: "Bun Installation",
394
+ status: "❌ FAIL",
395
+ message: "Bun is not installed or not in PATH",
396
+ details: "Install Bun from https://bun.sh",
397
+ score: 0
398
+ };
399
+ }
400
+ }
401
+
402
+ function checkBS9Installation(): InspectionResult {
403
+ try {
404
+ const version = execSync("bs9 --version", { encoding: "utf-8" }).trim();
405
+ return {
406
+ name: "BS9 Installation",
407
+ status: "✅ PASS",
408
+ message: `BS9 ${version} installed`,
409
+ details: `CLI: ${version}`,
410
+ score: 100
411
+ };
412
+ } catch {
413
+ return {
414
+ name: "BS9 Installation",
415
+ status: "❌ FAIL",
416
+ message: "BS9 is not installed or not in PATH",
417
+ details: "Run 'npm install -g bs9' or install from source",
418
+ score: 0
419
+ };
420
+ }
421
+ }
422
+
423
+ function checkPlatformDetection(platformInfo: any): InspectionResult {
424
+ return {
425
+ name: "Platform Detection",
426
+ status: "✅ PASS",
427
+ message: `Detected ${platformInfo.platform}`,
428
+ details: `OS: ${platformInfo.platform}, Service Manager: ${platformInfo.serviceManager}`,
429
+ score: 100
430
+ };
431
+ }
432
+
433
+ function checkDirectoryStructure(platformInfo: any): InspectionResult {
434
+ const requiredDirs = [
435
+ platformInfo.configDir,
436
+ platformInfo.logDir,
437
+ platformInfo.serviceDir
438
+ ];
439
+
440
+ const missingDirs = requiredDirs.filter(dir => !existsSync(dir));
441
+
442
+ if (missingDirs.length === 0) {
443
+ return {
444
+ name: "Directory Structure",
445
+ status: "✅ PASS",
446
+ message: "All required directories exist",
447
+ details: `Config: ${platformInfo.configDir}`,
448
+ score: 100
449
+ };
450
+ } else {
451
+ return {
452
+ name: "Directory Structure",
453
+ status: "❌ FAIL",
454
+ message: `Missing ${missingDirs.length} directories`,
455
+ details: `Missing: ${missingDirs.join(", ")}`,
456
+ score: 0
457
+ };
458
+ }
459
+ }
460
+
461
+ function checkPermissions(platformInfo: any): InspectionResult {
462
+ try {
463
+ const testFile = join(platformInfo.configDir, ".bs9-test");
464
+ execSync(`touch "${testFile}"`, { stdio: "ignore" });
465
+ execSync(`rm "${testFile}"`, { stdio: "ignore" });
466
+
467
+ return {
468
+ name: "File Permissions",
469
+ status: "✅ PASS",
470
+ message: "Write permissions OK",
471
+ details: `Can write to ${platformInfo.configDir}`,
472
+ score: 100
473
+ };
474
+ } catch {
475
+ return {
476
+ name: "File Permissions",
477
+ status: "❌ FAIL",
478
+ message: "Insufficient file permissions",
479
+ details: `Cannot write to ${platformInfo.configDir}`,
480
+ score: 0
481
+ };
482
+ }
483
+ }
484
+
485
+ function checkServiceManager(platformInfo: any): InspectionResult {
486
+ try {
487
+ switch (platformInfo.platform) {
488
+ case "linux":
489
+ execSync("systemctl --user --version", { stdio: "ignore" });
490
+ return {
491
+ name: "Service Manager",
492
+ status: "✅ PASS",
493
+ message: "systemd user services available",
494
+ details: "systemd user mode is working",
495
+ score: 100
496
+ };
497
+
498
+ case "darwin":
499
+ execSync("launchctl list", { stdio: "ignore" });
500
+ return {
501
+ name: "Service Manager",
502
+ status: "✅ PASS",
503
+ message: "launchd available",
504
+ details: "macOS launchd is working",
505
+ score: 100
506
+ };
507
+
508
+ case "win32":
509
+ execSync("sc query", { stdio: "ignore" });
510
+ return {
511
+ name: "Service Manager",
512
+ status: "✅ PASS",
513
+ message: "Windows Services available",
514
+ details: "Windows Service Manager is working",
515
+ score: 100
516
+ };
517
+
518
+ default:
519
+ return {
520
+ name: "Service Manager",
521
+ status: "⚠️ WARN",
522
+ message: "Unsupported platform",
523
+ details: `Platform ${platformInfo.platform} may have limited support`,
524
+ score: 70
525
+ };
526
+ }
527
+ } catch {
528
+ return {
529
+ name: "Service Manager",
530
+ status: "❌ FAIL",
531
+ message: "Service manager not available",
532
+ details: "Cannot access system service manager",
533
+ score: 0
534
+ };
535
+ }
536
+ }
537
+
538
+ function checkNetworkConnectivity(): InspectionResult {
539
+ try {
540
+ execSync("curl -s --connect-timeout 3 http://httpbin.org/ip", { stdio: "ignore" });
541
+ return {
542
+ name: "Network Connectivity",
543
+ status: "✅ PASS",
544
+ message: "Network connectivity OK",
545
+ details: "Can reach external services",
546
+ score: 95
547
+ };
548
+ } catch {
549
+ return {
550
+ name: "Network Connectivity",
551
+ status: "⚠️ WARN",
552
+ message: "Limited network connectivity",
553
+ details: "Cannot reach external services (may be offline)",
554
+ score: 70
555
+ };
556
+ }
557
+ }
558
+
559
+ function checkSystemResources(): InspectionResult {
560
+ try {
561
+ const memory = execSync("free -h", { encoding: "utf-8" });
562
+ const disk = execSync("df -h .", { encoding: "utf-8" });
563
+
564
+ return {
565
+ name: "System Resources",
566
+ status: "✅ PASS",
567
+ message: "System resources OK",
568
+ details: `Memory and disk space available`,
569
+ score: 90
570
+ };
571
+ } catch {
572
+ return {
573
+ name: "System Resources",
574
+ status: "⚠️ WARN",
575
+ message: "Cannot check system resources",
576
+ details: "Resource monitoring not available",
577
+ score: 50
578
+ };
579
+ }
580
+ }
581
+
582
+ function checkDependencies(): InspectionResult {
583
+ try {
584
+ const packageJson = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8"));
585
+ const deps = Object.keys(packageJson.dependencies || {});
586
+
587
+ return {
588
+ name: "Dependencies",
589
+ status: "✅ PASS",
590
+ message: `${deps.length} dependencies found`,
591
+ details: `Dependencies: ${deps.slice(0, 3).join(", ")}${deps.length > 3 ? "..." : ""}`,
592
+ score: 95
593
+ };
594
+ } catch {
595
+ return {
596
+ name: "Dependencies",
597
+ status: "⚠️ WARN",
598
+ message: "No package.json found",
599
+ details: "Not in a Node.js project directory",
600
+ score: 70
601
+ };
602
+ }
603
+ }
@@ -11,6 +11,7 @@
11
11
 
12
12
  import { execSync } from "node:child_process";
13
13
  import { getPlatformInfo } from "../platform/detect.js";
14
+ import { parseServiceArray, confirmAction } from "../utils/array-parser.js";
14
15
 
15
16
  // Security: Service name validation
16
17
  function isValidServiceName(name: string): boolean {
@@ -18,7 +19,57 @@ function isValidServiceName(name: string): boolean {
18
19
  return validPattern.test(name) && name.length <= 64 && !name.includes('..') && !name.includes('/');
19
20
  }
20
21
 
21
- export async function restartCommand(name: string): Promise<void> {
22
+ export async function restartCommand(names: string[]): Promise<void> {
23
+ // Handle multiple arguments
24
+ const name = names.length > 0 ? names.join(' ') : '';
25
+
26
+ // Handle multi-service operations
27
+ if (name.includes('[') || name === 'all') {
28
+ await handleMultiServiceRestart(name);
29
+ return;
30
+ }
31
+
32
+ // Single service operation (existing logic)
33
+ await handleSingleServiceRestart(name);
34
+ }
35
+
36
+ async function handleMultiServiceRestart(name: string): Promise<void> {
37
+ const services = await parseServiceArray(name);
38
+
39
+ if (services.length === 0) {
40
+ console.log("❌ No services found matching the pattern");
41
+ return;
42
+ }
43
+
44
+ // Safety confirmation for bulk operations
45
+ if (services.length > 1) {
46
+ console.log(`⚠️ About to restart ${services.length} services:`);
47
+ services.forEach(service => console.log(` - ${service}`));
48
+
49
+ const confirmed = await confirmAction('Are you sure? (y/N): ');
50
+ if (!confirmed) {
51
+ console.log('❌ Restart operation cancelled');
52
+ return;
53
+ }
54
+ }
55
+
56
+ console.log(`🔄 Restarting ${services.length} services...`);
57
+
58
+ const results = await Promise.allSettled(
59
+ services.map(async (serviceName) => {
60
+ try {
61
+ await handleSingleServiceRestart(serviceName);
62
+ return { service: serviceName, status: 'success', error: null };
63
+ } catch (error) {
64
+ return { service: serviceName, status: 'failed', error: error instanceof Error ? error.message : String(error) };
65
+ }
66
+ })
67
+ );
68
+
69
+ displayBatchResults(results, 'restart');
70
+ }
71
+
72
+ async function handleSingleServiceRestart(name: string): Promise<void> {
22
73
  // Security: Validate service name
23
74
  if (!isValidServiceName(name)) {
24
75
  console.error(`❌ Security: Invalid service name: ${name}`);
@@ -45,3 +96,28 @@ export async function restartCommand(name: string): Promise<void> {
45
96
  process.exit(1);
46
97
  }
47
98
  }
99
+
100
+ function displayBatchResults(results: PromiseSettledResult<{ service: string; status: string; error: string | null }>[], operation: string): void {
101
+ console.log(`\n📊 Batch ${operation} Results`);
102
+ console.log("=".repeat(50));
103
+
104
+ const successful = results.filter(r => r.status === 'fulfilled' && r.value.status === 'success');
105
+ const failed = results.filter(r => r.status === 'fulfilled' && r.value.status === 'failed');
106
+
107
+ successful.forEach(result => {
108
+ if (result.status === 'fulfilled') {
109
+ console.log(`✅ ${result.value.service} - ${operation} successful`);
110
+ }
111
+ });
112
+
113
+ failed.forEach(result => {
114
+ if (result.status === 'fulfilled') {
115
+ console.log(`❌ ${result.value.service} - Failed: ${result.value.error}`);
116
+ }
117
+ });
118
+
119
+ console.log(`\n📈 Summary:`);
120
+ console.log(` Total: ${results.length} services`);
121
+ console.log(` Success: ${successful.length}/${results.length} (${((successful.length / results.length) * 100).toFixed(1)}%)`);
122
+ console.log(` Failed: ${failed.length}/${results.length} (${((failed.length / results.length) * 100).toFixed(1)}%)`);
123
+ }