chainlesschain 0.51.0 → 0.81.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.
Files changed (70) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AppLayout-Rvi759IS.js → AppLayout-6SPt_8Y_.js} +1 -1
  4. package/src/assets/web-panel/assets/{Dashboard-DBhFxXYQ.js → Dashboard-Br7kCwKJ.js} +2 -2
  5. package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +1 -0
  6. package/src/assets/web-panel/assets/{index-uL0cZ8N_.js → index-tN-8TosE.js} +2 -2
  7. package/src/assets/web-panel/index.html +2 -2
  8. package/src/commands/a2a.js +380 -0
  9. package/src/commands/agent-network.js +785 -0
  10. package/src/commands/automation.js +654 -0
  11. package/src/commands/bi.js +348 -0
  12. package/src/commands/crosschain.js +218 -0
  13. package/src/commands/dao.js +565 -0
  14. package/src/commands/did-v2.js +620 -0
  15. package/src/commands/dlp.js +341 -0
  16. package/src/commands/economy.js +578 -0
  17. package/src/commands/evolution.js +391 -0
  18. package/src/commands/evomap.js +394 -0
  19. package/src/commands/federation.js +283 -0
  20. package/src/commands/hmemory.js +442 -0
  21. package/src/commands/inference.js +318 -0
  22. package/src/commands/lowcode.js +356 -0
  23. package/src/commands/marketplace.js +256 -0
  24. package/src/commands/perf.js +433 -0
  25. package/src/commands/pipeline.js +449 -0
  26. package/src/commands/plugin-ecosystem.js +517 -0
  27. package/src/commands/privacy.js +321 -0
  28. package/src/commands/reputation.js +261 -0
  29. package/src/commands/sandbox.js +401 -0
  30. package/src/commands/siem.js +246 -0
  31. package/src/commands/sla.js +259 -0
  32. package/src/commands/social.js +311 -0
  33. package/src/commands/sso.js +798 -0
  34. package/src/commands/stress.js +230 -0
  35. package/src/commands/terraform.js +245 -0
  36. package/src/commands/workflow.js +320 -0
  37. package/src/commands/zkp.js +562 -1
  38. package/src/index.js +21 -0
  39. package/src/lib/a2a-protocol.js +451 -0
  40. package/src/lib/agent-economy.js +479 -0
  41. package/src/lib/agent-network.js +1121 -0
  42. package/src/lib/app-builder.js +239 -0
  43. package/src/lib/automation-engine.js +948 -0
  44. package/src/lib/bi-engine.js +338 -0
  45. package/src/lib/cross-chain.js +345 -0
  46. package/src/lib/dao-governance.js +569 -0
  47. package/src/lib/did-v2-manager.js +1127 -0
  48. package/src/lib/dlp-engine.js +389 -0
  49. package/src/lib/evolution-system.js +453 -0
  50. package/src/lib/evomap-federation.js +177 -0
  51. package/src/lib/evomap-governance.js +276 -0
  52. package/src/lib/federation-hardening.js +259 -0
  53. package/src/lib/hierarchical-memory.js +481 -0
  54. package/src/lib/inference-network.js +330 -0
  55. package/src/lib/perf-tuning.js +734 -0
  56. package/src/lib/pipeline-orchestrator.js +928 -0
  57. package/src/lib/plugin-ecosystem.js +1109 -0
  58. package/src/lib/privacy-computing.js +427 -0
  59. package/src/lib/reputation-optimizer.js +299 -0
  60. package/src/lib/sandbox-v2.js +306 -0
  61. package/src/lib/siem-exporter.js +333 -0
  62. package/src/lib/skill-marketplace.js +325 -0
  63. package/src/lib/sla-manager.js +275 -0
  64. package/src/lib/social-graph-analytics.js +707 -0
  65. package/src/lib/sso-manager.js +841 -0
  66. package/src/lib/stress-tester.js +330 -0
  67. package/src/lib/terraform-manager.js +363 -0
  68. package/src/lib/workflow-engine.js +454 -1
  69. package/src/lib/zkp-engine.js +523 -20
  70. package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +0 -1
@@ -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
  }
@@ -12,6 +12,19 @@ import {
12
12
  addTarget,
13
13
  exportLogs,
14
14
  getSIEMStats,
15
+ SIEM_FORMAT,
16
+ SIEM_TARGET_TYPE,
17
+ SIEM_SEVERITY,
18
+ SIEM_TARGET_STATUS,
19
+ SIEM_DEFAULT_BATCH_SIZE,
20
+ severityToCEF,
21
+ formatLog,
22
+ addTargetV2,
23
+ removeTarget,
24
+ setTargetStatus,
25
+ exportLogsV2,
26
+ getSIEMStatsV2,
27
+ listTargetsByStatus,
15
28
  } from "../lib/siem-exporter.js";
16
29
 
17
30
  export function registerSiemCommand(program) {
@@ -147,6 +160,239 @@ export function registerSiemCommand(program) {
147
160
  }
148
161
  }
149
162
 
163
+ await shutdown();
164
+ } catch (err) {
165
+ logger.error(`Failed: ${err.message}`);
166
+ process.exit(1);
167
+ }
168
+ });
169
+
170
+ // ═══════════════════════════════════════════════════════════════
171
+ // V2 Canonical Subcommands (Phase 51)
172
+ // ═══════════════════════════════════════════════════════════════
173
+
174
+ siem
175
+ .command("formats")
176
+ .description("List SIEM export formats (V2)")
177
+ .action(() => {
178
+ console.log(JSON.stringify(Object.values(SIEM_FORMAT), null, 2));
179
+ });
180
+
181
+ siem
182
+ .command("target-types")
183
+ .description("List SIEM target types (V2)")
184
+ .action(() => {
185
+ console.log(JSON.stringify(Object.values(SIEM_TARGET_TYPE), null, 2));
186
+ });
187
+
188
+ siem
189
+ .command("severities")
190
+ .description("List SIEM severity levels (V2)")
191
+ .action(() => {
192
+ console.log(JSON.stringify(Object.values(SIEM_SEVERITY), null, 2));
193
+ });
194
+
195
+ siem
196
+ .command("statuses")
197
+ .description("List SIEM target statuses (V2)")
198
+ .action(() => {
199
+ console.log(JSON.stringify(Object.values(SIEM_TARGET_STATUS), null, 2));
200
+ });
201
+
202
+ siem
203
+ .command("default-batch-size")
204
+ .description("Show default SIEM batch size (V2)")
205
+ .action(() => {
206
+ console.log(JSON.stringify({ batchSize: SIEM_DEFAULT_BATCH_SIZE }));
207
+ });
208
+
209
+ siem
210
+ .command("severity-cef <severity>")
211
+ .description("Map a SIEM severity to its CEF integer (0-10)")
212
+ .action((severity) => {
213
+ try {
214
+ console.log(JSON.stringify({ severity, cef: severityToCEF(severity) }));
215
+ } catch (err) {
216
+ logger.error(`Failed: ${err.message}`);
217
+ process.exit(1);
218
+ }
219
+ });
220
+
221
+ siem
222
+ .command("format-log <format>")
223
+ .description("Format a JSON log payload as cef|leef|json")
224
+ .option("-l, --log <json>", "Log object as JSON", "{}")
225
+ .action((format, options) => {
226
+ try {
227
+ const log = JSON.parse(options.log);
228
+ const result = formatLog(format, log);
229
+ console.log(
230
+ typeof result === "string" ? result : JSON.stringify(result, null, 2),
231
+ );
232
+ } catch (err) {
233
+ logger.error(`Failed: ${err.message}`);
234
+ process.exit(1);
235
+ }
236
+ });
237
+
238
+ siem
239
+ .command("add-target-v2")
240
+ .description("Add a SIEM target via canonical options (V2)")
241
+ .option("-t, --type <type>", "Target type")
242
+ .option("-u, --url <url>", "Target URL")
243
+ .option("-f, --format <fmt>", "Export format", "json")
244
+ .option("-c, --config <json>", "Config JSON", "{}")
245
+ .action(async (options) => {
246
+ try {
247
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
248
+ if (!ctx.db) {
249
+ logger.error("Database not available");
250
+ process.exit(1);
251
+ }
252
+ const db = ctx.db.getDatabase();
253
+ ensureSIEMTables(db);
254
+
255
+ let config = {};
256
+ try {
257
+ config = JSON.parse(options.config);
258
+ } catch (_err) {
259
+ logger.error("Invalid --config JSON");
260
+ process.exit(1);
261
+ }
262
+ const target = addTargetV2(db, {
263
+ type: options.type,
264
+ url: options.url,
265
+ format: options.format,
266
+ config,
267
+ });
268
+ console.log(JSON.stringify(target, null, 2));
269
+
270
+ await shutdown();
271
+ } catch (err) {
272
+ logger.error(`Failed: ${err.message}`);
273
+ process.exit(1);
274
+ }
275
+ });
276
+
277
+ siem
278
+ .command("remove-target <target-id>")
279
+ .description("Remove a SIEM target (V2)")
280
+ .action(async (targetId) => {
281
+ try {
282
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
283
+ if (!ctx.db) {
284
+ logger.error("Database not available");
285
+ process.exit(1);
286
+ }
287
+ const db = ctx.db.getDatabase();
288
+ ensureSIEMTables(db);
289
+
290
+ console.log(JSON.stringify(removeTarget(db, targetId), null, 2));
291
+
292
+ await shutdown();
293
+ } catch (err) {
294
+ logger.error(`Failed: ${err.message}`);
295
+ process.exit(1);
296
+ }
297
+ });
298
+
299
+ siem
300
+ .command("set-status <target-id> <status>")
301
+ .description("Transition a SIEM target's status (V2)")
302
+ .action(async (targetId, status) => {
303
+ try {
304
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
305
+ if (!ctx.db) {
306
+ logger.error("Database not available");
307
+ process.exit(1);
308
+ }
309
+ const db = ctx.db.getDatabase();
310
+ ensureSIEMTables(db);
311
+
312
+ console.log(
313
+ JSON.stringify(setTargetStatus(db, targetId, status), null, 2),
314
+ );
315
+
316
+ await shutdown();
317
+ } catch (err) {
318
+ logger.error(`Failed: ${err.message}`);
319
+ process.exit(1);
320
+ }
321
+ });
322
+
323
+ siem
324
+ .command("export-v2 <target-id>")
325
+ .description("Batch-export logs to a target (V2)")
326
+ .option("-l, --logs <json>", "Logs as JSON array", "[]")
327
+ .option("-b, --batch-size <n>", "Batch size override")
328
+ .action(async (targetId, options) => {
329
+ try {
330
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
331
+ if (!ctx.db) {
332
+ logger.error("Database not available");
333
+ process.exit(1);
334
+ }
335
+ const db = ctx.db.getDatabase();
336
+ ensureSIEMTables(db);
337
+
338
+ let logs = [];
339
+ try {
340
+ logs = JSON.parse(options.logs);
341
+ } catch (_err) {
342
+ logger.error("Invalid --logs JSON");
343
+ process.exit(1);
344
+ }
345
+ const r = exportLogsV2(db, {
346
+ targetId,
347
+ logs,
348
+ batchSize: options.batchSize ? Number(options.batchSize) : undefined,
349
+ });
350
+ console.log(JSON.stringify(r, null, 2));
351
+
352
+ await shutdown();
353
+ } catch (err) {
354
+ logger.error(`Failed: ${err.message}`);
355
+ process.exit(1);
356
+ }
357
+ });
358
+
359
+ siem
360
+ .command("stats-v2")
361
+ .description("Show extended SIEM stats (byFormat/byType/byStatus, V2)")
362
+ .action(async () => {
363
+ try {
364
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
365
+ if (!ctx.db) {
366
+ logger.error("Database not available");
367
+ process.exit(1);
368
+ }
369
+ const db = ctx.db.getDatabase();
370
+ ensureSIEMTables(db);
371
+
372
+ console.log(JSON.stringify(getSIEMStatsV2(), null, 2));
373
+
374
+ await shutdown();
375
+ } catch (err) {
376
+ logger.error(`Failed: ${err.message}`);
377
+ process.exit(1);
378
+ }
379
+ });
380
+
381
+ siem
382
+ .command("by-status <status>")
383
+ .description("List SIEM targets filtered by status (V2)")
384
+ .action(async (status) => {
385
+ try {
386
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
387
+ if (!ctx.db) {
388
+ logger.error("Database not available");
389
+ process.exit(1);
390
+ }
391
+ const db = ctx.db.getDatabase();
392
+ ensureSIEMTables(db);
393
+
394
+ console.log(JSON.stringify(listTargetsByStatus(status), null, 2));
395
+
150
396
  await shutdown();
151
397
  } catch (err) {
152
398
  logger.error(`Failed: ${err.message}`);