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.
@@ -18,6 +18,21 @@ import {
18
18
  recordInvocation,
19
19
  listInvocations,
20
20
  getInvocationStats,
21
+ // V2 (Phase 65)
22
+ SERVICE_STATUS_V2,
23
+ INVOCATION_STATUS_V2,
24
+ PRICING_MODEL_V2,
25
+ MARKETPLACE_DEFAULT_MAX_CONCURRENT_INVOCATIONS,
26
+ setMaxConcurrentInvocations,
27
+ getMaxConcurrentInvocations,
28
+ getActiveInvocationCount,
29
+ beginInvocationV2,
30
+ startInvocation,
31
+ completeInvocationV2,
32
+ failInvocationV2,
33
+ timeoutInvocationV2,
34
+ setInvocationStatus,
35
+ getMarketplaceStatsV2,
21
36
  } from "../lib/skill-marketplace.js";
22
37
 
23
38
  function _dbFromCtx(ctx) {
@@ -323,4 +338,245 @@ export function registerMarketplaceCommand(program) {
323
338
  process.exit(1);
324
339
  }
325
340
  });
341
+
342
+ /* ── V2 (Phase 65) ───────────────────────────────────────── */
343
+
344
+ mp.command("service-statuses-v2")
345
+ .description("List V2 service statuses (frozen enum)")
346
+ .option("--json", "Output as JSON")
347
+ .action((options) => {
348
+ const values = Object.values(SERVICE_STATUS_V2);
349
+ if (options.json) console.log(JSON.stringify(values, null, 2));
350
+ else for (const v of values) logger.log(` ${chalk.cyan(v)}`);
351
+ });
352
+
353
+ mp.command("invocation-statuses-v2")
354
+ .description("List V2 invocation statuses (frozen enum)")
355
+ .option("--json", "Output as JSON")
356
+ .action((options) => {
357
+ const values = Object.values(INVOCATION_STATUS_V2);
358
+ if (options.json) console.log(JSON.stringify(values, null, 2));
359
+ else for (const v of values) logger.log(` ${chalk.cyan(v)}`);
360
+ });
361
+
362
+ mp.command("pricing-models")
363
+ .description("List V2 pricing models (frozen enum)")
364
+ .option("--json", "Output as JSON")
365
+ .action((options) => {
366
+ const values = Object.values(PRICING_MODEL_V2);
367
+ if (options.json) console.log(JSON.stringify(values, null, 2));
368
+ else for (const v of values) logger.log(` ${chalk.cyan(v)}`);
369
+ });
370
+
371
+ mp.command("default-max-concurrent")
372
+ .description("Show default per-service concurrency cap")
373
+ .action(() => {
374
+ logger.log(` ${MARKETPLACE_DEFAULT_MAX_CONCURRENT_INVOCATIONS}`);
375
+ });
376
+
377
+ mp.command("max-concurrent")
378
+ .description("Show current per-service concurrency cap")
379
+ .action(() => {
380
+ logger.log(` ${getMaxConcurrentInvocations()}`);
381
+ });
382
+
383
+ mp.command("active-invocation-count [service-id]")
384
+ .description(
385
+ "Show count of PENDING + RUNNING invocations (all services by default)",
386
+ )
387
+ .action((serviceId) => {
388
+ logger.log(` ${getActiveInvocationCount(serviceId)}`);
389
+ });
390
+
391
+ mp.command("set-max-concurrent <n>")
392
+ .description("Set the per-service concurrency cap")
393
+ .action((n) => {
394
+ try {
395
+ const v = setMaxConcurrentInvocations(Number(n));
396
+ logger.success(`Max concurrent invocations = ${v}`);
397
+ } catch (err) {
398
+ logger.error(`Failed: ${err.message}`);
399
+ process.exit(1);
400
+ }
401
+ });
402
+
403
+ mp.command("begin-v2 <service-id>")
404
+ .description("Begin a V2 invocation (creates PENDING, enforces cap)")
405
+ .option("-c, --caller <id>", "Caller ID")
406
+ .option("-i, --input <json>", "JSON-encoded input")
407
+ .option("--json", "Output as JSON")
408
+ .action(async (serviceId, options) => {
409
+ try {
410
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
411
+ const db = _dbFromCtx(ctx);
412
+ let input = null;
413
+ if (options.input) {
414
+ try {
415
+ input = JSON.parse(options.input);
416
+ } catch {
417
+ input = options.input;
418
+ }
419
+ }
420
+ const inv = beginInvocationV2(db, {
421
+ serviceId,
422
+ callerId: options.caller,
423
+ input,
424
+ });
425
+ if (options.json) console.log(JSON.stringify(inv, null, 2));
426
+ else {
427
+ logger.success(`Invocation queued [${inv.status}]`);
428
+ logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(inv.id)}`);
429
+ }
430
+ await shutdown();
431
+ } catch (err) {
432
+ logger.error(`Failed: ${err.message}`);
433
+ process.exit(1);
434
+ }
435
+ });
436
+
437
+ mp.command("start-invocation <invocation-id>")
438
+ .description("Start a V2 invocation (PENDING → RUNNING)")
439
+ .option("--json", "Output as JSON")
440
+ .action(async (invocationId, options) => {
441
+ try {
442
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
443
+ const db = _dbFromCtx(ctx);
444
+ const result = startInvocation(db, invocationId);
445
+ if (options.json) console.log(JSON.stringify(result, null, 2));
446
+ else logger.success(`Status = ${result.status}`);
447
+ await shutdown();
448
+ } catch (err) {
449
+ logger.error(`Failed: ${err.message}`);
450
+ process.exit(1);
451
+ }
452
+ });
453
+
454
+ mp.command("complete-invocation <invocation-id>")
455
+ .description("Complete a V2 invocation (RUNNING → SUCCESS)")
456
+ .option("-o, --output <json>", "JSON-encoded output")
457
+ .option("-d, --duration-ms <n>", "Duration in milliseconds", parseInt)
458
+ .option("--json", "Output as JSON")
459
+ .action(async (invocationId, options) => {
460
+ try {
461
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
462
+ const db = _dbFromCtx(ctx);
463
+ let output = null;
464
+ if (options.output) {
465
+ try {
466
+ output = JSON.parse(options.output);
467
+ } catch {
468
+ output = options.output;
469
+ }
470
+ }
471
+ const result = completeInvocationV2(db, invocationId, {
472
+ output,
473
+ durationMs: options.durationMs,
474
+ });
475
+ if (options.json) console.log(JSON.stringify(result, null, 2));
476
+ else logger.success(`Status = ${result.status}`);
477
+ await shutdown();
478
+ } catch (err) {
479
+ logger.error(`Failed: ${err.message}`);
480
+ process.exit(1);
481
+ }
482
+ });
483
+
484
+ mp.command("fail-invocation <invocation-id>")
485
+ .description("Fail a V2 invocation")
486
+ .option("-m, --message <msg>", "Error message")
487
+ .option("-d, --duration-ms <n>", "Duration in milliseconds", parseInt)
488
+ .action(async (invocationId, options) => {
489
+ try {
490
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
491
+ const db = _dbFromCtx(ctx);
492
+ const result = failInvocationV2(db, invocationId, options.message, {
493
+ durationMs: options.durationMs,
494
+ });
495
+ logger.success(`Status = ${result.status}`);
496
+ if (result.error) logger.log(` Error: ${result.error}`);
497
+ await shutdown();
498
+ } catch (err) {
499
+ logger.error(`Failed: ${err.message}`);
500
+ process.exit(1);
501
+ }
502
+ });
503
+
504
+ mp.command("timeout-invocation <invocation-id>")
505
+ .description("Timeout a V2 invocation")
506
+ .option("-d, --duration-ms <n>", "Duration in milliseconds", parseInt)
507
+ .action(async (invocationId, options) => {
508
+ try {
509
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
510
+ const db = _dbFromCtx(ctx);
511
+ const result = timeoutInvocationV2(db, invocationId, {
512
+ durationMs: options.durationMs,
513
+ });
514
+ logger.success(`Status = ${result.status}`);
515
+ await shutdown();
516
+ } catch (err) {
517
+ logger.error(`Failed: ${err.message}`);
518
+ process.exit(1);
519
+ }
520
+ });
521
+
522
+ mp.command("set-invocation-status <invocation-id> <status>")
523
+ .description("Set V2 invocation status via the state machine")
524
+ .option("-m, --message <msg>", "Error message (FAILED/TIMEOUT)")
525
+ .option("--json", "Output as JSON")
526
+ .action(async (invocationId, status, options) => {
527
+ try {
528
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
529
+ const db = _dbFromCtx(ctx);
530
+ const patch = options.message ? { error: options.message } : {};
531
+ const result = setInvocationStatus(db, invocationId, status, patch);
532
+ if (options.json) console.log(JSON.stringify(result, null, 2));
533
+ else logger.success(`Status = ${result.status}`);
534
+ await shutdown();
535
+ } catch (err) {
536
+ logger.error(`Failed: ${err.message}`);
537
+ process.exit(1);
538
+ }
539
+ });
540
+
541
+ mp.command("stats-v2")
542
+ .description("V2 aggregated stats")
543
+ .option("--json", "Output as JSON")
544
+ .action(async (options) => {
545
+ try {
546
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
547
+ _dbFromCtx(ctx);
548
+ const stats = getMarketplaceStatsV2();
549
+ if (options.json) console.log(JSON.stringify(stats, null, 2));
550
+ else {
551
+ logger.log(
552
+ ` ${chalk.bold("Services:")} ${stats.totalServices}`,
553
+ );
554
+ logger.log(
555
+ ` ${chalk.bold("Invocations:")} ${stats.totalInvocations} (active: ${stats.activeInvocations})`,
556
+ );
557
+ logger.log(
558
+ ` ${chalk.bold("Max concurrent:")} ${stats.maxConcurrentInvocations}`,
559
+ );
560
+ logger.log(` ${chalk.bold("Services by status:")}`);
561
+ for (const [k, v] of Object.entries(stats.servicesByStatus))
562
+ logger.log(` ${k.padEnd(12)} ${v}`);
563
+ logger.log(` ${chalk.bold("Services by pricing:")}`);
564
+ for (const [k, v] of Object.entries(stats.servicesByPricing))
565
+ logger.log(` ${k.padEnd(14)} ${v}`);
566
+ logger.log(` ${chalk.bold("Invocations by status:")}`);
567
+ for (const [k, v] of Object.entries(stats.invocationsByStatus))
568
+ logger.log(` ${k.padEnd(10)} ${v}`);
569
+ logger.log(
570
+ ` ${chalk.bold("Success rate:")} ${(stats.successRate * 100).toFixed(1)}%`,
571
+ );
572
+ logger.log(
573
+ ` ${chalk.bold("Avg duration:")} ${stats.avgDurationMs}ms`,
574
+ );
575
+ }
576
+ await shutdown();
577
+ } catch (err) {
578
+ logger.error(`Failed: ${err.message}`);
579
+ process.exit(1);
580
+ }
581
+ });
326
582
  }
@@ -23,6 +23,32 @@ import {
23
23
  dpPublish,
24
24
  heQuery,
25
25
  getPrivacyReport,
26
+ // V2 surface
27
+ FL_STATUS_V2,
28
+ MPC_STATUS_V2,
29
+ DP_MECHANISM_V2,
30
+ HE_SCHEME_V2,
31
+ MPC_PROTOCOL_V2,
32
+ PRIVACY_DEFAULT_MAX_ACTIVE_MPC_COMPUTATIONS,
33
+ setMaxActiveMpcComputations,
34
+ getMaxActiveMpcComputations,
35
+ getActiveMpcCount,
36
+ setPrivacyBudgetLimit,
37
+ getPrivacyBudgetLimit,
38
+ getPrivacyBudgetSpent,
39
+ resetPrivacyBudget,
40
+ createModelV2,
41
+ trainRoundV2,
42
+ aggregateRound,
43
+ failModelV2,
44
+ setFLStatusV2,
45
+ createComputationV2,
46
+ submitShareV2,
47
+ failComputation,
48
+ setMPCStatusV2,
49
+ dpPublishV2,
50
+ heQueryV2,
51
+ getPrivacyStatsV2,
26
52
  } from "../lib/privacy-computing.js";
27
53
 
28
54
  function _dbFromCtx(cmd) {
@@ -341,5 +367,300 @@ export function registerPrivacyCommand(program) {
341
367
  );
342
368
  });
343
369
 
370
+ /* ──────────────────────────────────────────────────────────
371
+ * V2 — Phase 91
372
+ * ────────────────────────────────────────────────────────── */
373
+
374
+ pc.command("fl-statuses-v2")
375
+ .description("List V2 FL statuses")
376
+ .option("--json", "JSON output")
377
+ .action((opts) => {
378
+ const v = Object.values(FL_STATUS_V2);
379
+ if (opts.json) return console.log(JSON.stringify(v, null, 2));
380
+ v.forEach((s) => console.log(s));
381
+ });
382
+
383
+ pc.command("mpc-statuses-v2")
384
+ .description("List V2 MPC statuses")
385
+ .option("--json", "JSON output")
386
+ .action((opts) => {
387
+ const v = Object.values(MPC_STATUS_V2);
388
+ if (opts.json) return console.log(JSON.stringify(v, null, 2));
389
+ v.forEach((s) => console.log(s));
390
+ });
391
+
392
+ pc.command("dp-mechanisms-v2")
393
+ .description("List V2 DP mechanisms")
394
+ .option("--json", "JSON output")
395
+ .action((opts) => {
396
+ const v = Object.values(DP_MECHANISM_V2);
397
+ if (opts.json) return console.log(JSON.stringify(v, null, 2));
398
+ v.forEach((s) => console.log(s));
399
+ });
400
+
401
+ pc.command("he-schemes-v2")
402
+ .description("List V2 HE schemes")
403
+ .option("--json", "JSON output")
404
+ .action((opts) => {
405
+ const v = Object.values(HE_SCHEME_V2);
406
+ if (opts.json) return console.log(JSON.stringify(v, null, 2));
407
+ v.forEach((s) => console.log(s));
408
+ });
409
+
410
+ pc.command("mpc-protocols-v2")
411
+ .description("List V2 MPC protocols")
412
+ .option("--json", "JSON output")
413
+ .action((opts) => {
414
+ const v = Object.values(MPC_PROTOCOL_V2);
415
+ if (opts.json) return console.log(JSON.stringify(v, null, 2));
416
+ v.forEach((s) => console.log(s));
417
+ });
418
+
419
+ pc.command("default-max-active-mpc")
420
+ .description("Show V2 default max active MPC computations")
421
+ .action(() =>
422
+ console.log(String(PRIVACY_DEFAULT_MAX_ACTIVE_MPC_COMPUTATIONS)),
423
+ );
424
+
425
+ pc.command("max-active-mpc")
426
+ .description("Show current max active MPC computations")
427
+ .action(() => console.log(String(getMaxActiveMpcComputations())));
428
+
429
+ pc.command("active-mpc-count")
430
+ .description("Show currently-active MPC computation count")
431
+ .action(() => console.log(String(getActiveMpcCount())));
432
+
433
+ pc.command("set-max-active-mpc <n>")
434
+ .description("Set max concurrent active MPC computations")
435
+ .action((n) => {
436
+ setMaxActiveMpcComputations(Number(n));
437
+ console.log(`max-active-mpc = ${getMaxActiveMpcComputations()}`);
438
+ });
439
+
440
+ pc.command("budget-limit")
441
+ .description("Show privacy budget limit")
442
+ .action(() => console.log(String(getPrivacyBudgetLimit())));
443
+
444
+ pc.command("budget-spent")
445
+ .description("Show privacy budget spent")
446
+ .action(() => console.log(String(getPrivacyBudgetSpent())));
447
+
448
+ pc.command("set-budget-limit <n>")
449
+ .description("Set privacy budget limit")
450
+ .action((n) => {
451
+ setPrivacyBudgetLimit(Number(n));
452
+ console.log(`budget-limit = ${getPrivacyBudgetLimit()}`);
453
+ });
454
+
455
+ pc.command("reset-budget")
456
+ .description("Reset privacy budget spent counter to 0")
457
+ .action(() => {
458
+ resetPrivacyBudget();
459
+ console.log("budget reset");
460
+ });
461
+
462
+ pc.command("create-model-v2 <name>")
463
+ .description("V2 create FL model (throws on invalid input)")
464
+ .option("-t, --total-rounds <n>", "Total rounds (default 10)")
465
+ .option("-l, --learning-rate <n>", "Learning rate (default 0.01)")
466
+ .option("-a, --architecture <arch>", "Architecture")
467
+ .option("-m, --model-type <type>", "Model type")
468
+ .option("--json", "JSON output")
469
+ .action((name, opts) => {
470
+ const db = _dbFromCtx(pc);
471
+ const m = createModelV2(db, {
472
+ name,
473
+ totalRounds: opts.totalRounds ? Number(opts.totalRounds) : undefined,
474
+ learningRate: opts.learningRate ? Number(opts.learningRate) : undefined,
475
+ architecture: opts.architecture,
476
+ modelType: opts.modelType,
477
+ });
478
+ if (opts.json) return console.log(JSON.stringify(m, null, 2));
479
+ console.log(`Model: ${m.id} (${m.name}, rounds=${m.total_rounds})`);
480
+ });
481
+
482
+ pc.command("train-round-v2 <model-id>")
483
+ .description("V2 train one FL round (auto initializing → training)")
484
+ .option("--json", "JSON output")
485
+ .action((id, opts) => {
486
+ const db = _dbFromCtx(pc);
487
+ const m = trainRoundV2(db, id);
488
+ if (opts.json) return console.log(JSON.stringify(m, null, 2));
489
+ console.log(
490
+ `Round ${m.current_round}/${m.total_rounds} status=${m.status} acc=${m.accuracy}`,
491
+ );
492
+ });
493
+
494
+ pc.command("aggregate-round <model-id>")
495
+ .description("Aggregate FL round (training → training next / completed)")
496
+ .option("--json", "JSON output")
497
+ .action((id, opts) => {
498
+ const db = _dbFromCtx(pc);
499
+ const m = aggregateRound(db, id);
500
+ if (opts.json) return console.log(JSON.stringify(m, null, 2));
501
+ console.log(`Model ${m.id} status=${m.status} round=${m.current_round}`);
502
+ });
503
+
504
+ pc.command("fail-model-v2 <model-id>")
505
+ .description("V2 fail FL model (any non-terminal → failed)")
506
+ .option("-e, --error <msg>", "Error message")
507
+ .option("--json", "JSON output")
508
+ .action((id, opts) => {
509
+ const db = _dbFromCtx(pc);
510
+ const m = failModelV2(db, id, { error: opts.error });
511
+ if (opts.json) return console.log(JSON.stringify(m, null, 2));
512
+ console.log(`Model ${m.id} failed: ${m.error_message || ""}`);
513
+ });
514
+
515
+ pc.command("set-fl-status <model-id> <status>")
516
+ .description("V2 state-machine guarded FL status setter")
517
+ .option("-a, --accuracy <n>")
518
+ .option("-l, --loss <n>")
519
+ .option("-e, --error <msg>")
520
+ .option("--json", "JSON output")
521
+ .action((id, status, opts) => {
522
+ const db = _dbFromCtx(pc);
523
+ const patch = {};
524
+ if (opts.accuracy !== undefined) patch.accuracy = Number(opts.accuracy);
525
+ if (opts.loss !== undefined) patch.loss = Number(opts.loss);
526
+ if (opts.error !== undefined) patch.errorMessage = opts.error;
527
+ const m = setFLStatusV2(db, id, status, patch);
528
+ if (opts.json) return console.log(JSON.stringify(m, null, 2));
529
+ console.log(`Model ${m.id} status=${m.status}`);
530
+ });
531
+
532
+ pc.command("create-computation-v2 <type>")
533
+ .description("V2 create MPC computation (throws on invalid input)")
534
+ .option("-p, --protocol <proto>", "Protocol (shamir|beaver|gmw)")
535
+ .option("-i, --participants <csv>", "Comma-separated participant IDs")
536
+ .option("-s, --shares-required <n>", "Shares required")
537
+ .option("--json", "JSON output")
538
+ .action((type, opts) => {
539
+ const db = _dbFromCtx(pc);
540
+ const ids = opts.participants
541
+ ? opts.participants
542
+ .split(",")
543
+ .map((s) => s.trim())
544
+ .filter(Boolean)
545
+ : [];
546
+ const c = createComputationV2(db, {
547
+ computationType: type,
548
+ protocol: opts.protocol,
549
+ participantIds: ids,
550
+ sharesRequired: opts.sharesRequired
551
+ ? Number(opts.sharesRequired)
552
+ : undefined,
553
+ });
554
+ if (opts.json) return console.log(JSON.stringify(c, null, 2));
555
+ console.log(
556
+ `Computation: ${c.id} protocol=${c.protocol} required=${c.shares_required}`,
557
+ );
558
+ });
559
+
560
+ pc.command("submit-share-v2 <comp-id>")
561
+ .description("V2 submit MPC share (state-guarded)")
562
+ .option("--json", "JSON output")
563
+ .action((id, opts) => {
564
+ const db = _dbFromCtx(pc);
565
+ const c = submitShareV2(db, id);
566
+ if (opts.json) return console.log(JSON.stringify(c, null, 2));
567
+ console.log(
568
+ `Comp ${c.id} shares=${c.shares_received}/${c.shares_required} status=${c.status}`,
569
+ );
570
+ });
571
+
572
+ pc.command("fail-computation <comp-id>")
573
+ .description("Fail MPC computation (any non-terminal → failed)")
574
+ .option("-e, --error <msg>")
575
+ .option("--json", "JSON output")
576
+ .action((id, opts) => {
577
+ const db = _dbFromCtx(pc);
578
+ const c = failComputation(db, id, { error: opts.error });
579
+ if (opts.json) return console.log(JSON.stringify(c, null, 2));
580
+ console.log(`Comp ${c.id} failed: ${c.error_message || ""}`);
581
+ });
582
+
583
+ pc.command("set-mpc-status <comp-id> <status>")
584
+ .description("V2 state-machine guarded MPC status setter")
585
+ .option("-h, --result-hash <hash>")
586
+ .option("-e, --error <msg>")
587
+ .option("--json", "JSON output")
588
+ .action((id, status, opts) => {
589
+ const db = _dbFromCtx(pc);
590
+ const patch = {};
591
+ if (opts.resultHash !== undefined) patch.resultHash = opts.resultHash;
592
+ if (opts.error !== undefined) patch.errorMessage = opts.error;
593
+ const c = setMPCStatusV2(db, id, status, patch);
594
+ if (opts.json) return console.log(JSON.stringify(c, null, 2));
595
+ console.log(`Comp ${c.id} status=${c.status}`);
596
+ });
597
+
598
+ pc.command("dp-publish-v2")
599
+ .description("V2 DP publish (throws on invalid input / exceeded budget)")
600
+ .requiredOption("-d, --data <n>", "Data value", Number)
601
+ .option("-e, --epsilon <n>", "Epsilon", Number)
602
+ .option("--delta <n>", "Delta", Number)
603
+ .option("-m, --mechanism <mech>", "Mechanism")
604
+ .option("-s, --sensitivity <n>", "Sensitivity", Number)
605
+ .option("--json", "JSON output")
606
+ .action((opts) => {
607
+ const db = _dbFromCtx(pc);
608
+ const r = dpPublishV2(db, {
609
+ data: opts.data,
610
+ epsilon: opts.epsilon,
611
+ delta: opts.delta,
612
+ mechanism: opts.mechanism,
613
+ sensitivity: opts.sensitivity,
614
+ });
615
+ if (opts.json) return console.log(JSON.stringify(r, null, 2));
616
+ console.log(
617
+ `Published: orig=${r.originalValue} noised=${r.noisedValue} ε=${r.epsilon}`,
618
+ );
619
+ });
620
+
621
+ pc.command("he-query-v2")
622
+ .description("V2 HE query (throws on invalid input)")
623
+ .requiredOption("-o, --operation <op>", "sum|product|mean|count")
624
+ .requiredOption("-d, --data <json>", "JSON array of numbers")
625
+ .option("-s, --scheme <scheme>", "Scheme")
626
+ .option("--json", "JSON output")
627
+ .action((opts) => {
628
+ let data;
629
+ try {
630
+ data = JSON.parse(opts.data);
631
+ } catch (_e) {
632
+ throw new Error("Invalid --data JSON");
633
+ }
634
+ const r = heQueryV2({
635
+ data,
636
+ operation: opts.operation,
637
+ scheme: opts.scheme,
638
+ });
639
+ if (opts.json) return console.log(JSON.stringify(r, null, 2));
640
+ console.log(
641
+ `Result: ${r.result} (${r.operation} over ${r.inputCount} items, scheme=${r.scheme})`,
642
+ );
643
+ });
644
+
645
+ pc.command("stats-v2")
646
+ .description("V2 all-enum-key zero-init stats")
647
+ .option("--json", "JSON output")
648
+ .action((opts) => {
649
+ const s = getPrivacyStatsV2();
650
+ if (opts.json) return console.log(JSON.stringify(s, null, 2));
651
+ console.log(
652
+ `Models: ${s.totalModels} Computations: ${s.totalComputations}`,
653
+ );
654
+ console.log(
655
+ `Active MPC: ${s.activeMpcCount}/${s.maxActiveMpcComputations}`,
656
+ );
657
+ console.log(
658
+ `Budget: ${s.budget.spent}/${s.budget.limit} (${s.budget.remaining} remaining)`,
659
+ );
660
+ console.log(
661
+ `Avg accuracy: ${s.avgAccuracy} Avg comp time ms: ${s.avgComputationTimeMs}`,
662
+ );
663
+ });
664
+
344
665
  program.addCommand(pc);
345
666
  }