chainlesschain 0.66.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.
@@ -15,6 +15,21 @@ import {
15
15
  detectAnomaly,
16
16
  predictTrend,
17
17
  listTemplates,
18
+ // Phase 95 V2
19
+ CHART_TYPE,
20
+ ANOMALY_METHOD,
21
+ REPORT_FORMAT,
22
+ REPORT_STATUS,
23
+ DASHBOARD_LAYOUT,
24
+ nlQueryV2,
25
+ detectAnomalyV2,
26
+ predictTrendV2,
27
+ recommendChart,
28
+ createDashboardV2,
29
+ updateReportStatus,
30
+ getReportStatus,
31
+ getReportStatusHistory,
32
+ getBIStatsV2,
18
33
  } from "../lib/bi-engine.js";
19
34
 
20
35
  export function registerBiCommand(program) {
@@ -216,6 +231,339 @@ export function registerBiCommand(program) {
216
231
  }
217
232
  });
218
233
 
234
+ // ─── Phase 95 V2 subcommands ──────────────────────────────
235
+
236
+ // bi chart-types
237
+ bi.command("chart-types")
238
+ .description("List supported chart types (V2)")
239
+ .option("--json", "Output as JSON")
240
+ .action((options) => {
241
+ const types = Object.values(CHART_TYPE);
242
+ if (options.json) {
243
+ console.log(JSON.stringify(types, null, 2));
244
+ } else {
245
+ logger.log(chalk.bold("Chart Types:"));
246
+ for (const t of types) logger.log(` ${chalk.cyan(t)}`);
247
+ }
248
+ });
249
+
250
+ // bi anomaly-methods
251
+ bi.command("anomaly-methods")
252
+ .description("List supported anomaly detection methods (V2)")
253
+ .option("--json", "Output as JSON")
254
+ .action((options) => {
255
+ const methods = Object.values(ANOMALY_METHOD);
256
+ if (options.json) {
257
+ console.log(JSON.stringify(methods, null, 2));
258
+ } else {
259
+ logger.log(chalk.bold("Anomaly Methods:"));
260
+ for (const m of methods) logger.log(` ${chalk.cyan(m)}`);
261
+ }
262
+ });
263
+
264
+ // bi report-formats
265
+ bi.command("report-formats")
266
+ .description("List supported report formats (V2)")
267
+ .option("--json", "Output as JSON")
268
+ .action((options) => {
269
+ const formats = Object.values(REPORT_FORMAT);
270
+ if (options.json) {
271
+ console.log(JSON.stringify(formats, null, 2));
272
+ } else {
273
+ logger.log(chalk.bold("Report Formats:"));
274
+ for (const f of formats) logger.log(` ${chalk.cyan(f)}`);
275
+ }
276
+ });
277
+
278
+ // bi report-statuses
279
+ bi.command("report-statuses")
280
+ .description("List canonical report statuses (V2)")
281
+ .option("--json", "Output as JSON")
282
+ .action((options) => {
283
+ const statuses = Object.values(REPORT_STATUS);
284
+ if (options.json) {
285
+ console.log(JSON.stringify(statuses, null, 2));
286
+ } else {
287
+ logger.log(chalk.bold("Report Statuses:"));
288
+ for (const s of statuses) logger.log(` ${chalk.cyan(s)}`);
289
+ }
290
+ });
291
+
292
+ // bi dashboard-layouts
293
+ bi.command("dashboard-layouts")
294
+ .description("List supported dashboard layouts (V2)")
295
+ .option("--json", "Output as JSON")
296
+ .action((options) => {
297
+ const layouts = Object.values(DASHBOARD_LAYOUT);
298
+ if (options.json) {
299
+ console.log(JSON.stringify(layouts, null, 2));
300
+ } else {
301
+ logger.log(chalk.bold("Dashboard Layouts:"));
302
+ for (const l of layouts) logger.log(` ${chalk.cyan(l)}`);
303
+ }
304
+ });
305
+
306
+ // bi query-v2 <question>
307
+ bi.command("query-v2 <question>")
308
+ .description("Heuristic NL→SQL with intent detection (V2)")
309
+ .option("--schema <json>", "Schema hint as JSON, e.g. '{\"tables\":[...]}'")
310
+ .option("--json", "Output as JSON")
311
+ .action((question, options) => {
312
+ try {
313
+ const schema = options.schema ? JSON.parse(options.schema) : undefined;
314
+ const result = nlQueryV2({ query: question, schema });
315
+ if (options.json) {
316
+ console.log(JSON.stringify(result, null, 2));
317
+ } else {
318
+ logger.success(`Intent: ${chalk.cyan(result.intent)}`);
319
+ logger.log(` ${chalk.bold("SQL:")} ${result.sql}`);
320
+ logger.log(` ${chalk.bold("Table:")} ${result.table}`);
321
+ logger.log(` ${chalk.bold("Visual:")} ${result.visualization}`);
322
+ if (result.aggregate)
323
+ logger.log(` ${chalk.bold("Agg:")} ${result.aggregate}`);
324
+ if (result.limit !== null)
325
+ logger.log(` ${chalk.bold("Limit:")} ${result.limit}`);
326
+ }
327
+ } catch (err) {
328
+ logger.error(`Failed: ${err.message}`);
329
+ process.exit(1);
330
+ }
331
+ });
332
+
333
+ // bi anomaly-v2
334
+ bi.command("anomaly-v2")
335
+ .description("Anomaly detection with method choice (V2)")
336
+ .option("--data <json>", "Data as JSON array of numbers")
337
+ .option("--method <m>", "Method: z_score|iqr", "z_score")
338
+ .option("--threshold <n>", "Threshold (z_score σ or iqr multiplier)")
339
+ .option("--json", "Output as JSON")
340
+ .action((options) => {
341
+ try {
342
+ const data = options.data ? JSON.parse(options.data) : [];
343
+ const threshold =
344
+ options.threshold !== undefined
345
+ ? parseFloat(options.threshold)
346
+ : undefined;
347
+ const result = detectAnomalyV2({
348
+ data,
349
+ method: options.method,
350
+ threshold,
351
+ });
352
+ if (options.json) {
353
+ console.log(JSON.stringify(result, null, 2));
354
+ } else {
355
+ logger.log(chalk.bold(`Method: ${result.method}`));
356
+ logger.log(` ${chalk.bold("Threshold:")} ${result.threshold}`);
357
+ if (result.method === "iqr") {
358
+ logger.log(
359
+ ` ${chalk.bold("Q1/Q3/IQR:")} ${result.q1} / ${result.q3} / ${result.iqr}`,
360
+ );
361
+ logger.log(
362
+ ` ${chalk.bold("Bounds:")} [${result.lowerBound}, ${result.upperBound}]`,
363
+ );
364
+ } else {
365
+ logger.log(` ${chalk.bold("Mean:")} ${result.mean}`);
366
+ logger.log(` ${chalk.bold("Std:")} ${result.std}`);
367
+ }
368
+ logger.log(
369
+ ` ${chalk.bold("Anomalies:")} ${result.anomalies.length}`,
370
+ );
371
+ }
372
+ } catch (err) {
373
+ logger.error(`Failed: ${err.message}`);
374
+ process.exit(1);
375
+ }
376
+ });
377
+
378
+ // bi predict-v2
379
+ bi.command("predict-v2")
380
+ .description("Trend prediction with r² confidence (V2)")
381
+ .option("--data <json>", "Data as JSON array of numbers")
382
+ .option("--periods <n>", "Number of periods to predict", "3")
383
+ .option("--json", "Output as JSON")
384
+ .action((options) => {
385
+ try {
386
+ const data = options.data ? JSON.parse(options.data) : [];
387
+ const result = predictTrendV2({
388
+ data,
389
+ periods: parseInt(options.periods, 10),
390
+ });
391
+ if (options.json) {
392
+ console.log(JSON.stringify(result, null, 2));
393
+ } else {
394
+ logger.log(chalk.bold(`Trend: ${chalk.cyan(result.trend)}`));
395
+ logger.log(` ${chalk.bold("Slope:")} ${result.slope}`);
396
+ logger.log(
397
+ ` ${chalk.bold("R²:")} ${result.r2} (${result.confidence})`,
398
+ );
399
+ logger.log(
400
+ ` ${chalk.bold("Predictions:")} ${result.predictions.join(", ")}`,
401
+ );
402
+ }
403
+ } catch (err) {
404
+ logger.error(`Failed: ${err.message}`);
405
+ process.exit(1);
406
+ }
407
+ });
408
+
409
+ // bi recommend-chart
410
+ bi.command("recommend-chart")
411
+ .description("Recommend chart type from intent or data shape (V2)")
412
+ .option("--intent <text>", "User intent description")
413
+ .option("--shape <json>", "Data shape hint as JSON")
414
+ .option("--json", "Output as JSON")
415
+ .action((options) => {
416
+ try {
417
+ const shape = options.shape ? JSON.parse(options.shape) : undefined;
418
+ const chart = recommendChart({
419
+ intent: options.intent,
420
+ dataShape: shape,
421
+ });
422
+ if (options.json) {
423
+ console.log(JSON.stringify({ chart }, null, 2));
424
+ } else {
425
+ logger.log(`Recommended chart: ${chalk.cyan(chart)}`);
426
+ }
427
+ } catch (err) {
428
+ logger.error(`Failed: ${err.message}`);
429
+ process.exit(1);
430
+ }
431
+ });
432
+
433
+ // bi dashboard-v2 <name>
434
+ bi.command("dashboard-v2 <name>")
435
+ .description("Create dashboard with validated layout (V2)")
436
+ .option("--widgets <json>", "Widgets as JSON array", "[]")
437
+ .option("--layout <spec>", "Layout: grid|flow|tabs OR JSON object")
438
+ .option("--json", "Output as JSON")
439
+ .action(async (name, options) => {
440
+ try {
441
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
442
+ if (!ctx.db) {
443
+ logger.error("Database not available");
444
+ process.exit(1);
445
+ }
446
+ const db = ctx.db.getDatabase();
447
+ ensureBITables(db);
448
+
449
+ const widgets = JSON.parse(options.widgets);
450
+ let layout;
451
+ if (options.layout) {
452
+ try {
453
+ layout = JSON.parse(options.layout);
454
+ } catch (_err) {
455
+ layout = options.layout;
456
+ }
457
+ }
458
+ const dashboard = createDashboardV2(db, { name, widgets, layout });
459
+ if (options.json) {
460
+ console.log(JSON.stringify(dashboard, null, 2));
461
+ } else {
462
+ logger.success(`Dashboard created: ${chalk.cyan(name)}`);
463
+ logger.log(` ${chalk.bold("ID:")} ${dashboard.id}`);
464
+ logger.log(` ${chalk.bold("Layout:")} ${dashboard.layout.type}`);
465
+ }
466
+ await shutdown();
467
+ } catch (err) {
468
+ logger.error(`Failed: ${err.message}`);
469
+ process.exit(1);
470
+ }
471
+ });
472
+
473
+ // bi set-report-status <report-id> <status>
474
+ bi.command("set-report-status <report-id> <status>")
475
+ .description("Update report status with validated transition (V2)")
476
+ .option("--json", "Output as JSON")
477
+ .action(async (reportId, status, options) => {
478
+ try {
479
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
480
+ if (!ctx.db) {
481
+ logger.error("Database not available");
482
+ process.exit(1);
483
+ }
484
+ const db = ctx.db.getDatabase();
485
+ ensureBITables(db);
486
+
487
+ const result = updateReportStatus(db, { reportId, status });
488
+ if (options.json) {
489
+ console.log(JSON.stringify(result, null, 2));
490
+ } else {
491
+ logger.success(
492
+ `${chalk.cyan(reportId)}: ${result.previous} → ${chalk.bold(result.status)}`,
493
+ );
494
+ }
495
+ await shutdown();
496
+ } catch (err) {
497
+ logger.error(`Failed: ${err.message}`);
498
+ process.exit(1);
499
+ }
500
+ });
501
+
502
+ // bi report-status <report-id>
503
+ bi.command("report-status <report-id>")
504
+ .description("Show current report status (V2)")
505
+ .option("--json", "Output as JSON")
506
+ .action((reportId, options) => {
507
+ const status = getReportStatus(reportId);
508
+ if (options.json) {
509
+ console.log(JSON.stringify({ reportId, status }, null, 2));
510
+ } else {
511
+ logger.log(`${chalk.cyan(reportId)}: ${chalk.bold(status)}`);
512
+ }
513
+ });
514
+
515
+ // bi report-history <report-id>
516
+ bi.command("report-history <report-id>")
517
+ .description("Show report status transition history (V2)")
518
+ .option("--json", "Output as JSON")
519
+ .action((reportId, options) => {
520
+ const hist = getReportStatusHistory(reportId);
521
+ if (options.json) {
522
+ console.log(JSON.stringify(hist, null, 2));
523
+ } else if (hist.length === 0) {
524
+ logger.info("No transitions recorded");
525
+ } else {
526
+ logger.log(chalk.bold(`History (${hist.length}):`));
527
+ for (const h of hist) {
528
+ logger.log(` ${chalk.gray(h.at)} ${h.from} → ${chalk.cyan(h.to)}`);
529
+ }
530
+ }
531
+ });
532
+
533
+ // bi stats-v2
534
+ bi.command("stats-v2")
535
+ .description("BI platform statistics (V2)")
536
+ .option("--json", "Output as JSON")
537
+ .action(async (options) => {
538
+ try {
539
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
540
+ if (!ctx.db) {
541
+ logger.error("Database not available");
542
+ process.exit(1);
543
+ }
544
+ const db = ctx.db.getDatabase();
545
+ ensureBITables(db);
546
+
547
+ const stats = getBIStatsV2(db);
548
+ if (options.json) {
549
+ console.log(JSON.stringify(stats, null, 2));
550
+ } else {
551
+ logger.log(chalk.bold("BI Stats (V2):"));
552
+ logger.log(` Dashboards: ${stats.dashboards}`);
553
+ logger.log(
554
+ ` Reports: ${stats.reports.total} byStatus=${JSON.stringify(stats.reports.byStatus)}`,
555
+ );
556
+ logger.log(` Scheduled: ${stats.scheduled}`);
557
+ logger.log(` Templates: ${stats.templates}`);
558
+ logger.log(` Chart types: ${stats.chartTypes}`);
559
+ }
560
+ await shutdown();
561
+ } catch (err) {
562
+ logger.error(`Failed: ${err.message}`);
563
+ process.exit(1);
564
+ }
565
+ });
566
+
219
567
  // bi templates
220
568
  bi.command("templates")
221
569
  .description("List available BI templates")
@@ -27,6 +27,24 @@ import {
27
27
  listMessages,
28
28
  estimateFee,
29
29
  getCrossChainStats,
30
+ // V2
31
+ BRIDGE_STATUS_V2,
32
+ SWAP_STATUS_V2,
33
+ MESSAGE_STATUS_V2,
34
+ CHAIN_ID_V2,
35
+ CROSSCHAIN_DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS,
36
+ setMaxActiveBridgesPerAddress,
37
+ getMaxActiveBridgesPerAddress,
38
+ getActiveBridgeCount,
39
+ configureChainV2,
40
+ getChainConfigV2,
41
+ listChainsV2,
42
+ bridgeAssetV2,
43
+ setBridgeStatusV2,
44
+ setSwapStatusV2,
45
+ setMessageStatusV2,
46
+ autoExpireSwapsV2,
47
+ getCrossChainStatsV2,
30
48
  } from "../lib/cross-chain.js";
31
49
 
32
50
  function _dbFromCtx(cmd) {
@@ -378,5 +396,205 @@ export function registerCrossChainCommand(program) {
378
396
  console.log(`Messages: ${stats.messages.total}`);
379
397
  });
380
398
 
399
+ /* ══════════════════════════════════════════════════
400
+ * Phase 89 — Cross-Chain V2 subcommands
401
+ * ══════════════════════════════════════════════════ */
402
+
403
+ cc.command("bridge-statuses-v2")
404
+ .description("List V2 bridge statuses")
405
+ .option("--json", "JSON output")
406
+ .action((opts) => {
407
+ const values = Object.values(BRIDGE_STATUS_V2);
408
+ if (opts.json) return console.log(JSON.stringify(values, null, 2));
409
+ for (const v of values) console.log(` ${v}`);
410
+ });
411
+
412
+ cc.command("swap-statuses-v2")
413
+ .description("List V2 swap statuses")
414
+ .option("--json", "JSON output")
415
+ .action((opts) => {
416
+ const values = Object.values(SWAP_STATUS_V2);
417
+ if (opts.json) return console.log(JSON.stringify(values, null, 2));
418
+ for (const v of values) console.log(` ${v}`);
419
+ });
420
+
421
+ cc.command("message-statuses-v2")
422
+ .description("List V2 message statuses")
423
+ .option("--json", "JSON output")
424
+ .action((opts) => {
425
+ const values = Object.values(MESSAGE_STATUS_V2);
426
+ if (opts.json) return console.log(JSON.stringify(values, null, 2));
427
+ for (const v of values) console.log(` ${v}`);
428
+ });
429
+
430
+ cc.command("chain-ids-v2")
431
+ .description("List V2 chain IDs")
432
+ .option("--json", "JSON output")
433
+ .action((opts) => {
434
+ const values = Object.values(CHAIN_ID_V2);
435
+ if (opts.json) return console.log(JSON.stringify(values, null, 2));
436
+ for (const v of values) console.log(` ${v}`);
437
+ });
438
+
439
+ cc.command("default-max-active-bridges")
440
+ .description("Show default max active bridges per address")
441
+ .action(() => {
442
+ console.log(` ${CROSSCHAIN_DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS}`);
443
+ });
444
+
445
+ cc.command("max-active-bridges")
446
+ .description("Show current max active bridges per address")
447
+ .action(() => {
448
+ console.log(` ${getMaxActiveBridgesPerAddress()}`);
449
+ });
450
+
451
+ cc.command("active-bridge-count [address]")
452
+ .description("Active (non-terminal) bridge count, optionally by address")
453
+ .action((address) => {
454
+ console.log(` ${getActiveBridgeCount(address)}`);
455
+ });
456
+
457
+ cc.command("set-max-active-bridges <n>")
458
+ .description("Set max active bridges per address (positive integer)")
459
+ .action((n) => {
460
+ setMaxActiveBridgesPerAddress(parseFloat(n));
461
+ console.log(
462
+ ` Max active bridges per address = ${getMaxActiveBridgesPerAddress()}`,
463
+ );
464
+ });
465
+
466
+ cc.command("configure-chain <chain-id>")
467
+ .description("Configure a supported chain (rpcUrl, contract, enabled)")
468
+ .option("--rpc-url <url>", "RPC URL")
469
+ .option("--contract <addr>", "Contract address")
470
+ .option("--disabled", "Set enabled=false")
471
+ .option("--json", "JSON output")
472
+ .action((chainId, opts) => {
473
+ const cfg = configureChainV2({
474
+ chainId,
475
+ rpcUrl: opts.rpcUrl,
476
+ contractAddress: opts.contract,
477
+ enabled: !opts.disabled,
478
+ });
479
+ if (opts.json) return console.log(JSON.stringify(cfg, null, 2));
480
+ console.log(` Configured ${cfg.chainId} (enabled=${cfg.enabled})`);
481
+ });
482
+
483
+ cc.command("chain-config <chain-id>")
484
+ .description("Show chain config (or 'not configured')")
485
+ .option("--json", "JSON output")
486
+ .action((chainId, opts) => {
487
+ const cfg = getChainConfigV2(chainId);
488
+ if (opts.json) return console.log(JSON.stringify(cfg, null, 2));
489
+ if (!cfg) return console.log(" not configured");
490
+ console.log(
491
+ ` ${cfg.chainId} enabled=${cfg.enabled} rpc=${cfg.rpcUrl ?? "-"} contract=${cfg.contractAddress ?? "-"}`,
492
+ );
493
+ });
494
+
495
+ cc.command("list-chains-v2")
496
+ .description("List chains enriched with V2 config")
497
+ .option("--json", "JSON output")
498
+ .action((opts) => {
499
+ const chains = listChainsV2();
500
+ if (opts.json) return console.log(JSON.stringify(chains, null, 2));
501
+ for (const c of chains) {
502
+ console.log(
503
+ ` ${c.id.padEnd(12)} ${c.symbol.padEnd(6)} enabled=${c.enabled} rpc=${c.rpcUrl ?? "-"}`,
504
+ );
505
+ }
506
+ });
507
+
508
+ cc.command("bridge-v2 <from-chain> <to-chain> <amount>")
509
+ .description(
510
+ "Create a bridge (V2: throws on error, enforces per-address cap)",
511
+ )
512
+ .option("--asset <name>", "Asset symbol", "native")
513
+ .option("--sender <addr>", "Sender address")
514
+ .option("--recipient <addr>", "Recipient address")
515
+ .option("--json", "JSON output")
516
+ .action((fromChain, toChain, amount, opts) => {
517
+ const r = bridgeAssetV2(_dbFromCtx(cc), {
518
+ fromChain,
519
+ toChain,
520
+ asset: opts.asset,
521
+ amount: parseFloat(amount),
522
+ senderAddress: opts.sender,
523
+ recipientAddress: opts.recipient,
524
+ });
525
+ if (opts.json) return console.log(JSON.stringify(r, null, 2));
526
+ console.log(` Bridge created: ${r.bridgeId} fee=${r.fee}`);
527
+ });
528
+
529
+ cc.command("set-bridge-status <bridge-id> <status>")
530
+ .description("Set bridge status with state-machine guard")
531
+ .option("--lock-tx <hash>", "Lock tx hash")
532
+ .option("--mint-tx <hash>", "Mint tx hash")
533
+ .option("--message <msg>", "Error message")
534
+ .option("--json", "JSON output")
535
+ .action((bridgeId, status, opts) => {
536
+ const b = setBridgeStatusV2(_dbFromCtx(cc), bridgeId, status, {
537
+ lockTxHash: opts.lockTx,
538
+ mintTxHash: opts.mintTx,
539
+ errorMessage: opts.message,
540
+ });
541
+ if (opts.json) return console.log(JSON.stringify(b, null, 2));
542
+ console.log(` ${b.id} ${b.status}`);
543
+ });
544
+
545
+ cc.command("set-swap-status <swap-id> <status>")
546
+ .description("Set swap status with state-machine guard")
547
+ .option("--claim-tx <hash>", "Claim tx hash")
548
+ .option("--refund-tx <hash>", "Refund tx hash")
549
+ .option("--json", "JSON output")
550
+ .action((swapId, status, opts) => {
551
+ const s = setSwapStatusV2(_dbFromCtx(cc), swapId, status, {
552
+ claimTxHash: opts.claimTx,
553
+ refundTxHash: opts.refundTx,
554
+ });
555
+ if (opts.json) return console.log(JSON.stringify(s, null, 2));
556
+ console.log(` ${s.id} ${s.status}`);
557
+ });
558
+
559
+ cc.command("set-message-status <message-id> <status>")
560
+ .description("Set message status with state-machine guard")
561
+ .option("--source-tx <hash>", "Source tx hash")
562
+ .option("--dest-tx <hash>", "Destination tx hash")
563
+ .option("--json", "JSON output")
564
+ .action((messageId, status, opts) => {
565
+ const m = setMessageStatusV2(_dbFromCtx(cc), messageId, status, {
566
+ sourceTxHash: opts.sourceTx,
567
+ destinationTxHash: opts.destTx,
568
+ });
569
+ if (opts.json) return console.log(JSON.stringify(m, null, 2));
570
+ console.log(` ${m.id} ${m.status} retries=${m.retries}`);
571
+ });
572
+
573
+ cc.command("auto-expire-swaps")
574
+ .description("Bulk-flip past-deadline swaps to EXPIRED")
575
+ .option("--json", "JSON output")
576
+ .action((opts) => {
577
+ const expired = autoExpireSwapsV2(_dbFromCtx(cc));
578
+ if (opts.json) return console.log(JSON.stringify(expired, null, 2));
579
+ console.log(` Expired ${expired.length} swap(s)`);
580
+ });
581
+
582
+ cc.command("stats-v2")
583
+ .description("V2 cross-chain statistics (all-enum-key)")
584
+ .option("--json", "JSON output")
585
+ .action((opts) => {
586
+ const s = getCrossChainStatsV2();
587
+ if (opts.json) return console.log(JSON.stringify(s, null, 2));
588
+ console.log(
589
+ ` Bridges: ${s.totalBridges} (active ${s.activeBridges}, volume ${s.totalBridgeVolume}, fees ${s.totalFees})`,
590
+ );
591
+ console.log(` Swaps: ${s.totalSwaps}`);
592
+ console.log(` Messages: ${s.totalMessages}`);
593
+ console.log(` Configured chains: ${s.configuredChains}`);
594
+ console.log(
595
+ ` Max active bridges / addr: ${s.maxActiveBridgesPerAddress}`,
596
+ );
597
+ });
598
+
381
599
  program.addCommand(cc);
382
600
  }