chainlesschain 0.51.0 → 0.66.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.
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-Rvi759IS.js → AppLayout-6SPt_8Y_.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-DBhFxXYQ.js → Dashboard-Br7kCwKJ.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +1 -0
- package/src/assets/web-panel/assets/{index-uL0cZ8N_.js → index-tN-8TosE.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/agent-network.js +785 -0
- package/src/commands/automation.js +654 -0
- package/src/commands/dao.js +565 -0
- package/src/commands/did-v2.js +620 -0
- package/src/commands/economy.js +578 -0
- package/src/commands/evolution.js +391 -0
- package/src/commands/hmemory.js +442 -0
- package/src/commands/perf.js +433 -0
- package/src/commands/pipeline.js +449 -0
- package/src/commands/plugin-ecosystem.js +517 -0
- package/src/commands/sandbox.js +401 -0
- package/src/commands/social.js +311 -0
- package/src/commands/sso.js +798 -0
- package/src/commands/workflow.js +320 -0
- package/src/commands/zkp.js +227 -1
- package/src/index.js +21 -0
- package/src/lib/agent-economy.js +479 -0
- package/src/lib/agent-network.js +1121 -0
- package/src/lib/automation-engine.js +948 -0
- package/src/lib/dao-governance.js +569 -0
- package/src/lib/did-v2-manager.js +1127 -0
- package/src/lib/evolution-system.js +453 -0
- package/src/lib/hierarchical-memory.js +481 -0
- package/src/lib/perf-tuning.js +734 -0
- package/src/lib/pipeline-orchestrator.js +928 -0
- package/src/lib/plugin-ecosystem.js +1109 -0
- package/src/lib/sandbox-v2.js +306 -0
- package/src/lib/social-graph-analytics.js +707 -0
- package/src/lib/sso-manager.js +841 -0
- package/src/lib/workflow-engine.js +454 -1
- package/src/lib/zkp-engine.js +249 -20
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +0 -1
package/src/commands/sandbox.js
CHANGED
|
@@ -18,6 +18,23 @@ import {
|
|
|
18
18
|
getSandbox,
|
|
19
19
|
setQuota,
|
|
20
20
|
monitorBehavior,
|
|
21
|
+
// Phase 87 V2
|
|
22
|
+
SANDBOX_STATUS,
|
|
23
|
+
PERMISSION_TYPE,
|
|
24
|
+
RISK_LEVEL,
|
|
25
|
+
QUOTA_TYPE,
|
|
26
|
+
pauseSandboxV2,
|
|
27
|
+
resumeSandboxV2,
|
|
28
|
+
terminateSandboxV2,
|
|
29
|
+
setQuotaTyped,
|
|
30
|
+
enforcePermission,
|
|
31
|
+
checkQuotaV2,
|
|
32
|
+
getRiskLevel,
|
|
33
|
+
calculateRiskScore,
|
|
34
|
+
autoIsolate,
|
|
35
|
+
listIsolations,
|
|
36
|
+
filterAuditLog,
|
|
37
|
+
getSandboxStatsV2,
|
|
21
38
|
} from "../lib/sandbox-v2.js";
|
|
22
39
|
|
|
23
40
|
export function registerSandboxCommand(program) {
|
|
@@ -437,4 +454,388 @@ export function registerSandboxCommand(program) {
|
|
|
437
454
|
process.exit(1);
|
|
438
455
|
}
|
|
439
456
|
});
|
|
457
|
+
|
|
458
|
+
// ═════════════════════════════════════════════════════════════════
|
|
459
|
+
// Phase 87 — Agent Security Sandbox 2.0 subcommands
|
|
460
|
+
// ═════════════════════════════════════════════════════════════════
|
|
461
|
+
|
|
462
|
+
// sandbox statuses
|
|
463
|
+
sandbox
|
|
464
|
+
.command("statuses")
|
|
465
|
+
.description("List sandbox lifecycle status enum values (Phase 87)")
|
|
466
|
+
.option("--json", "Output as JSON")
|
|
467
|
+
.action((options) => {
|
|
468
|
+
const values = Object.values(SANDBOX_STATUS);
|
|
469
|
+
if (options.json) console.log(JSON.stringify(values, null, 2));
|
|
470
|
+
else values.forEach((v) => logger.log(` ${chalk.cyan(v)}`));
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// sandbox permission-types
|
|
474
|
+
sandbox
|
|
475
|
+
.command("permission-types")
|
|
476
|
+
.description("List permission type enum values (Phase 87)")
|
|
477
|
+
.option("--json", "Output as JSON")
|
|
478
|
+
.action((options) => {
|
|
479
|
+
const values = Object.values(PERMISSION_TYPE);
|
|
480
|
+
if (options.json) console.log(JSON.stringify(values, null, 2));
|
|
481
|
+
else values.forEach((v) => logger.log(` ${chalk.cyan(v)}`));
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// sandbox risk-levels
|
|
485
|
+
sandbox
|
|
486
|
+
.command("risk-levels")
|
|
487
|
+
.description("List risk level enum values (Phase 87)")
|
|
488
|
+
.option("--json", "Output as JSON")
|
|
489
|
+
.action((options) => {
|
|
490
|
+
const values = Object.values(RISK_LEVEL);
|
|
491
|
+
if (options.json) console.log(JSON.stringify(values, null, 2));
|
|
492
|
+
else values.forEach((v) => logger.log(` ${chalk.cyan(v)}`));
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// sandbox quota-types
|
|
496
|
+
sandbox
|
|
497
|
+
.command("quota-types")
|
|
498
|
+
.description("List quota type enum values (Phase 87)")
|
|
499
|
+
.option("--json", "Output as JSON")
|
|
500
|
+
.action((options) => {
|
|
501
|
+
const values = Object.values(QUOTA_TYPE);
|
|
502
|
+
if (options.json) console.log(JSON.stringify(values, null, 2));
|
|
503
|
+
else values.forEach((v) => logger.log(` ${chalk.cyan(v)}`));
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// sandbox pause <id>
|
|
507
|
+
sandbox
|
|
508
|
+
.command("pause")
|
|
509
|
+
.description("Pause a running sandbox (Phase 87)")
|
|
510
|
+
.argument("<id>", "Sandbox ID")
|
|
511
|
+
.option("--json", "Output as JSON")
|
|
512
|
+
.action(async (id, options) => {
|
|
513
|
+
try {
|
|
514
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
515
|
+
const db = ctx.db.getDatabase();
|
|
516
|
+
const r = pauseSandboxV2(db, id);
|
|
517
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
518
|
+
else
|
|
519
|
+
logger.log(
|
|
520
|
+
`${chalk.green("✓ Paused")} ${chalk.cyan(id)} (was ${chalk.yellow(r.previousStatus)})`,
|
|
521
|
+
);
|
|
522
|
+
await shutdown();
|
|
523
|
+
} catch (err) {
|
|
524
|
+
logger.error(`Failed: ${err.message}`);
|
|
525
|
+
process.exit(1);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// sandbox resume <id>
|
|
530
|
+
sandbox
|
|
531
|
+
.command("resume")
|
|
532
|
+
.description("Resume a paused sandbox (Phase 87)")
|
|
533
|
+
.argument("<id>", "Sandbox ID")
|
|
534
|
+
.option("--json", "Output as JSON")
|
|
535
|
+
.action(async (id, options) => {
|
|
536
|
+
try {
|
|
537
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
538
|
+
const db = ctx.db.getDatabase();
|
|
539
|
+
const r = resumeSandboxV2(db, id);
|
|
540
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
541
|
+
else logger.log(`${chalk.green("✓ Resumed")} ${chalk.cyan(id)}`);
|
|
542
|
+
await shutdown();
|
|
543
|
+
} catch (err) {
|
|
544
|
+
logger.error(`Failed: ${err.message}`);
|
|
545
|
+
process.exit(1);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
// sandbox terminate <id>
|
|
550
|
+
sandbox
|
|
551
|
+
.command("terminate")
|
|
552
|
+
.description(
|
|
553
|
+
"Terminate a sandbox (Phase 87 canonical; more explicit than destroy)",
|
|
554
|
+
)
|
|
555
|
+
.argument("<id>", "Sandbox ID")
|
|
556
|
+
.option("--reason <reason>", "Termination reason", "manual")
|
|
557
|
+
.option("--json", "Output as JSON")
|
|
558
|
+
.action(async (id, options) => {
|
|
559
|
+
try {
|
|
560
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
561
|
+
const db = ctx.db.getDatabase();
|
|
562
|
+
const r = terminateSandboxV2(db, id, options.reason);
|
|
563
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
564
|
+
else
|
|
565
|
+
logger.log(
|
|
566
|
+
`${chalk.green("✓ Terminated")} ${chalk.cyan(id)} reason=${chalk.yellow(r.reason)}`,
|
|
567
|
+
);
|
|
568
|
+
await shutdown();
|
|
569
|
+
} catch (err) {
|
|
570
|
+
logger.error(`Failed: ${err.message}`);
|
|
571
|
+
process.exit(1);
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// sandbox set-quota-typed <id> <type> <limit>
|
|
576
|
+
sandbox
|
|
577
|
+
.command("set-quota-typed")
|
|
578
|
+
.description("Set a single quota by type (Phase 87)")
|
|
579
|
+
.argument("<id>", "Sandbox ID")
|
|
580
|
+
.argument(
|
|
581
|
+
"<type>",
|
|
582
|
+
"Quota type (cpu_percent|memory_mb|disk_mb|network_kbps|process_count)",
|
|
583
|
+
)
|
|
584
|
+
.argument("<limit>", "Numeric limit")
|
|
585
|
+
.option("--json", "Output as JSON")
|
|
586
|
+
.action(async (id, type, limit, options) => {
|
|
587
|
+
try {
|
|
588
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
589
|
+
const db = ctx.db.getDatabase();
|
|
590
|
+
const r = setQuotaTyped(db, id, type, Number(limit));
|
|
591
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
592
|
+
else
|
|
593
|
+
logger.log(
|
|
594
|
+
`${chalk.green("✓ Quota set")} ${chalk.cyan(id)} ${type}=${chalk.yellow(limit)}`,
|
|
595
|
+
);
|
|
596
|
+
await shutdown();
|
|
597
|
+
} catch (err) {
|
|
598
|
+
logger.error(`Failed: ${err.message}`);
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// sandbox check-permission <id> <type> <target>
|
|
604
|
+
sandbox
|
|
605
|
+
.command("check-permission")
|
|
606
|
+
.description(
|
|
607
|
+
"Check whether a sandbox may perform a permission op (Phase 87)",
|
|
608
|
+
)
|
|
609
|
+
.argument("<id>", "Sandbox ID")
|
|
610
|
+
.argument(
|
|
611
|
+
"<type>",
|
|
612
|
+
"Permission type (filesystem|network|syscall|ipc|process)",
|
|
613
|
+
)
|
|
614
|
+
.argument("<target>", "Target (path / host / syscall name)")
|
|
615
|
+
.option("--mode <mode>", "File mode (read|write)", "read")
|
|
616
|
+
.option("--json", "Output as JSON")
|
|
617
|
+
.action(async (id, type, target, options) => {
|
|
618
|
+
try {
|
|
619
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
620
|
+
const db = ctx.db.getDatabase();
|
|
621
|
+
const sandbox = getSandbox(db, id);
|
|
622
|
+
if (!sandbox) {
|
|
623
|
+
logger.error(`Sandbox not found: ${id}`);
|
|
624
|
+
await shutdown();
|
|
625
|
+
process.exit(1);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
const r = enforcePermission(sandbox, {
|
|
629
|
+
type,
|
|
630
|
+
target,
|
|
631
|
+
mode: options.mode,
|
|
632
|
+
});
|
|
633
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
634
|
+
else
|
|
635
|
+
logger.log(
|
|
636
|
+
`${r.allowed ? chalk.green("allowed") : chalk.red("denied")} ${type} ${options.mode} ${target}`,
|
|
637
|
+
);
|
|
638
|
+
await shutdown();
|
|
639
|
+
} catch (err) {
|
|
640
|
+
logger.error(`Failed: ${err.message}`);
|
|
641
|
+
process.exit(1);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// sandbox check-quota <id> <type> [amount]
|
|
646
|
+
sandbox
|
|
647
|
+
.command("check-quota")
|
|
648
|
+
.description("Check quota availability for a type (Phase 87)")
|
|
649
|
+
.argument("<id>", "Sandbox ID")
|
|
650
|
+
.argument("<type>", "Quota type")
|
|
651
|
+
.argument("[amount]", "Amount to check", "0")
|
|
652
|
+
.option("--json", "Output as JSON")
|
|
653
|
+
.action(async (id, type, amount, options) => {
|
|
654
|
+
try {
|
|
655
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
656
|
+
const db = ctx.db.getDatabase();
|
|
657
|
+
const sandbox = getSandbox(db, id);
|
|
658
|
+
if (!sandbox) {
|
|
659
|
+
logger.error(`Sandbox not found: ${id}`);
|
|
660
|
+
await shutdown();
|
|
661
|
+
process.exit(1);
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
const r = checkQuotaV2(sandbox, type, Number(amount));
|
|
665
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
666
|
+
else
|
|
667
|
+
logger.log(
|
|
668
|
+
`${r.ok ? chalk.green("ok") : chalk.red("exceeded")} ${type}: ${r.current}/${r.limit ?? "∞"} (remaining ${r.remaining})`,
|
|
669
|
+
);
|
|
670
|
+
await shutdown();
|
|
671
|
+
} catch (err) {
|
|
672
|
+
logger.error(`Failed: ${err.message}`);
|
|
673
|
+
process.exit(1);
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// sandbox risk-score <id>
|
|
678
|
+
sandbox
|
|
679
|
+
.command("risk-score")
|
|
680
|
+
.description("Compute risk score + risk level for a sandbox (Phase 87)")
|
|
681
|
+
.argument("<id>", "Sandbox ID")
|
|
682
|
+
.option("--json", "Output as JSON")
|
|
683
|
+
.action(async (id, options) => {
|
|
684
|
+
try {
|
|
685
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
686
|
+
const db = ctx.db.getDatabase();
|
|
687
|
+
const r = calculateRiskScore(db, id);
|
|
688
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
689
|
+
else {
|
|
690
|
+
const color =
|
|
691
|
+
r.riskLevel === RISK_LEVEL.CRITICAL
|
|
692
|
+
? chalk.red
|
|
693
|
+
: r.riskLevel === RISK_LEVEL.HIGH
|
|
694
|
+
? chalk.magenta
|
|
695
|
+
: r.riskLevel === RISK_LEVEL.MEDIUM
|
|
696
|
+
? chalk.yellow
|
|
697
|
+
: chalk.green;
|
|
698
|
+
logger.log(
|
|
699
|
+
`risk=${color(r.riskLevel)} score=${chalk.cyan(r.riskScore)} patterns=${r.patterns.length} events=${r.totalEvents}`,
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
await shutdown();
|
|
703
|
+
} catch (err) {
|
|
704
|
+
logger.error(`Failed: ${err.message}`);
|
|
705
|
+
process.exit(1);
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// sandbox risk-level <score>
|
|
710
|
+
sandbox
|
|
711
|
+
.command("risk-level")
|
|
712
|
+
.description("Map a risk score to a RISK_LEVEL bucket (Phase 87)")
|
|
713
|
+
.argument("<score>", "Numeric score 0-100")
|
|
714
|
+
.option("--json", "Output as JSON")
|
|
715
|
+
.action((score, options) => {
|
|
716
|
+
const level = getRiskLevel(Number(score));
|
|
717
|
+
if (options.json)
|
|
718
|
+
console.log(JSON.stringify({ score: Number(score), level }, null, 2));
|
|
719
|
+
else logger.log(`score=${score} → ${chalk.cyan(level)}`);
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
// sandbox auto-isolate <id>
|
|
723
|
+
sandbox
|
|
724
|
+
.command("auto-isolate")
|
|
725
|
+
.description("Auto-isolate a sandbox: record + terminate (Phase 87)")
|
|
726
|
+
.argument("<id>", "Sandbox ID")
|
|
727
|
+
.option("--reason <reason>", "Isolation reason", "high-risk")
|
|
728
|
+
.option("--json", "Output as JSON")
|
|
729
|
+
.action(async (id, options) => {
|
|
730
|
+
try {
|
|
731
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
732
|
+
const db = ctx.db.getDatabase();
|
|
733
|
+
const entry = autoIsolate(db, id, options.reason);
|
|
734
|
+
if (options.json) console.log(JSON.stringify(entry, null, 2));
|
|
735
|
+
else
|
|
736
|
+
logger.log(
|
|
737
|
+
`${chalk.red("⊘ Isolated")} ${chalk.cyan(id)} reason=${chalk.yellow(entry.reason)} at ${entry.isolatedAt}`,
|
|
738
|
+
);
|
|
739
|
+
await shutdown();
|
|
740
|
+
} catch (err) {
|
|
741
|
+
logger.error(`Failed: ${err.message}`);
|
|
742
|
+
process.exit(1);
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
// sandbox isolations
|
|
747
|
+
sandbox
|
|
748
|
+
.command("isolations")
|
|
749
|
+
.description("List isolation records (Phase 87)")
|
|
750
|
+
.option("-s, --sandbox <id>", "Filter by sandbox ID")
|
|
751
|
+
.option("--reason <reason>", "Filter by reason")
|
|
752
|
+
.option("--json", "Output as JSON")
|
|
753
|
+
.action(async (options) => {
|
|
754
|
+
try {
|
|
755
|
+
await bootstrap({ verbose: program.opts().verbose });
|
|
756
|
+
const items = listIsolations({
|
|
757
|
+
sandboxId: options.sandbox,
|
|
758
|
+
reason: options.reason,
|
|
759
|
+
});
|
|
760
|
+
if (options.json) console.log(JSON.stringify(items, null, 2));
|
|
761
|
+
else if (items.length === 0) logger.info("No isolation records.");
|
|
762
|
+
else
|
|
763
|
+
items.forEach((i) =>
|
|
764
|
+
logger.log(
|
|
765
|
+
` ${chalk.cyan(i.sandboxId)} reason=${chalk.yellow(i.reason)} at=${i.isolatedAt}`,
|
|
766
|
+
),
|
|
767
|
+
);
|
|
768
|
+
await shutdown();
|
|
769
|
+
} catch (err) {
|
|
770
|
+
logger.error(`Failed: ${err.message}`);
|
|
771
|
+
process.exit(1);
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
// sandbox audit-filter <id>
|
|
776
|
+
sandbox
|
|
777
|
+
.command("audit-filter")
|
|
778
|
+
.description("Filter audit log by event types / time range (Phase 87)")
|
|
779
|
+
.argument("[id]", "Sandbox ID (omit for all)")
|
|
780
|
+
.option("-e, --events <types>", "Comma-separated event types")
|
|
781
|
+
.option("--from <iso>", "ISO timestamp lower bound")
|
|
782
|
+
.option("--to <iso>", "ISO timestamp upper bound")
|
|
783
|
+
.option("-l, --limit <n>", "Max entries to return", parseInt)
|
|
784
|
+
.option("--json", "Output as JSON")
|
|
785
|
+
.action(async (id, options) => {
|
|
786
|
+
try {
|
|
787
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
788
|
+
const db = ctx.db.getDatabase();
|
|
789
|
+
const filter = {};
|
|
790
|
+
if (options.events)
|
|
791
|
+
filter.eventTypes = options.events.split(",").map((s) => s.trim());
|
|
792
|
+
if (options.from || options.to)
|
|
793
|
+
filter.timeRange = { from: options.from, to: options.to };
|
|
794
|
+
if (options.limit) filter.limit = options.limit;
|
|
795
|
+
const entries = filterAuditLog(db, id, filter);
|
|
796
|
+
if (options.json) console.log(JSON.stringify(entries, null, 2));
|
|
797
|
+
else if (entries.length === 0)
|
|
798
|
+
logger.info("No matching audit entries.");
|
|
799
|
+
else
|
|
800
|
+
entries.forEach((e) =>
|
|
801
|
+
logger.log(
|
|
802
|
+
` ${chalk.gray(e.timestamp)} ${chalk.cyan(e.sandboxId)} ${chalk.yellow(e.action)}`,
|
|
803
|
+
),
|
|
804
|
+
);
|
|
805
|
+
await shutdown();
|
|
806
|
+
} catch (err) {
|
|
807
|
+
logger.error(`Failed: ${err.message}`);
|
|
808
|
+
process.exit(1);
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
// sandbox stats-v2
|
|
813
|
+
sandbox
|
|
814
|
+
.command("stats-v2")
|
|
815
|
+
.description(
|
|
816
|
+
"Extended V2 stats (byStatus / auditByAction / isolations) (Phase 87)",
|
|
817
|
+
)
|
|
818
|
+
.option("--json", "Output as JSON")
|
|
819
|
+
.action(async (options) => {
|
|
820
|
+
try {
|
|
821
|
+
await bootstrap({ verbose: program.opts().verbose });
|
|
822
|
+
const stats = getSandboxStatsV2();
|
|
823
|
+
if (options.json) console.log(JSON.stringify(stats, null, 2));
|
|
824
|
+
else {
|
|
825
|
+
logger.log(chalk.bold("Sandbox stats (Phase 87):"));
|
|
826
|
+
logger.log(` total: ${chalk.cyan(stats.totalSandboxes)}`);
|
|
827
|
+
logger.log(` by status:`);
|
|
828
|
+
for (const [k, v] of Object.entries(stats.byStatus))
|
|
829
|
+
logger.log(` ${k}: ${chalk.cyan(v)}`);
|
|
830
|
+
logger.log(` audit events: ${chalk.cyan(stats.auditEventCount)}`);
|
|
831
|
+
logger.log(
|
|
832
|
+
` isolations: total=${chalk.cyan(stats.isolations.total)}`,
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
await shutdown();
|
|
836
|
+
} catch (err) {
|
|
837
|
+
logger.error(`Failed: ${err.message}`);
|
|
838
|
+
process.exit(1);
|
|
839
|
+
}
|
|
840
|
+
});
|
|
440
841
|
}
|