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,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
  }
@@ -0,0 +1,433 @@
1
+ /**
2
+ * `cc perf` — CLI surface for Phase 22 性能自动调优.
3
+ *
4
+ * Real os.cpus/freemem + process.memoryUsage sampling → SQLite ring buffer.
5
+ * 5 built-in rules, hysteresis + cooldown tracking. CLI reports
6
+ * recommendations but NEVER auto-applies actions.
7
+ */
8
+
9
+ import { Command } from "commander";
10
+
11
+ import {
12
+ ALERT_LEVELS,
13
+ RECOMMENDATION_STATUS,
14
+ BUILTIN_RULES,
15
+ ensurePerfTables,
16
+ getPerfConfig,
17
+ setPerfConfig,
18
+ collectSample,
19
+ listSamples,
20
+ getLatestSample,
21
+ clearHistory,
22
+ listRules,
23
+ getRule,
24
+ setRuleEnabled,
25
+ evaluateRules,
26
+ listRecommendations,
27
+ applyRecommendation,
28
+ dismissRecommendation,
29
+ listHistory,
30
+ getAlerts,
31
+ getPerfStats,
32
+ getPerformanceReport,
33
+ } from "../lib/perf-tuning.js";
34
+
35
+ function _dbFromCtx(cmd) {
36
+ const root = cmd?.parent?.parent ?? cmd?.parent;
37
+ return root?._db;
38
+ }
39
+
40
+ function _json(v) {
41
+ console.log(JSON.stringify(v, null, 2));
42
+ }
43
+
44
+ function _fmtTs(ts) {
45
+ if (!ts) return "—";
46
+ return new Date(ts).toISOString();
47
+ }
48
+
49
+ export function registerPerfCommand(program) {
50
+ const perf = new Command("perf")
51
+ .description("Performance monitoring & auto-tuning (Phase 22)")
52
+ .hook("preAction", (thisCmd) => {
53
+ const db = _dbFromCtx(thisCmd);
54
+ if (db) ensurePerfTables(db);
55
+ });
56
+
57
+ /* ── Catalogs ─────────────────────────────────────── */
58
+
59
+ perf
60
+ .command("levels")
61
+ .description("List alert levels")
62
+ .option("--json", "JSON output")
63
+ .action((opts) => {
64
+ const rows = Object.values(ALERT_LEVELS);
65
+ if (opts.json) return _json(rows);
66
+ for (const r of rows) console.log(` ${r}`);
67
+ });
68
+
69
+ perf
70
+ .command("rule-statuses")
71
+ .description("List recommendation statuses")
72
+ .option("--json", "JSON output")
73
+ .action((opts) => {
74
+ const rows = Object.values(RECOMMENDATION_STATUS);
75
+ if (opts.json) return _json(rows);
76
+ for (const r of rows) console.log(` ${r}`);
77
+ });
78
+
79
+ perf
80
+ .command("rules")
81
+ .description("List 5 built-in tuning rules")
82
+ .option("--json", "JSON output")
83
+ .action((opts) => {
84
+ const db = _dbFromCtx(perf);
85
+ const rules = db ? listRules(db) : BUILTIN_RULES.map((r) => ({ ...r }));
86
+ if (opts.json) return _json(rules);
87
+ for (const r of rules) {
88
+ const flag = r.enabled === false ? "[off] " : " ";
89
+ console.log(
90
+ `${flag}${r.id.padEnd(20)} ${r.severity.padEnd(8)} ${r.name}`,
91
+ );
92
+ console.log(
93
+ ` cond=${r.condition.metric} ${r.condition.op} ${r.condition.value}` +
94
+ ` hyst=${r.consecutiveRequired}x cooldown=${Math.round(r.cooldownMs / 1000)}s`,
95
+ );
96
+ console.log(` action=${r.action}`);
97
+ }
98
+ });
99
+
100
+ perf
101
+ .command("rule-show")
102
+ .argument("<ruleId>", "Rule id")
103
+ .description("Show a rule with live state")
104
+ .option("--json", "JSON output")
105
+ .action((ruleId, opts) => {
106
+ const db = _dbFromCtx(perf);
107
+ const r = getRule(db, ruleId);
108
+ if (!r) {
109
+ console.error(`Unknown rule: ${ruleId}`);
110
+ process.exit(1);
111
+ }
112
+ if (opts.json) return _json(r);
113
+ console.log(`Rule: ${r.id} — ${r.name}`);
114
+ console.log(` enabled: ${r.enabled}`);
115
+ console.log(` severity: ${r.severity}`);
116
+ console.log(
117
+ ` condition: ${r.condition.metric} ${r.condition.op} ${r.condition.value}`,
118
+ );
119
+ console.log(` action: ${r.action}`);
120
+ console.log(
121
+ ` hysteresis: ${r.consecutiveRequired}x cooldown: ${r.cooldownMs}ms`,
122
+ );
123
+ console.log(` consecutive: ${r.consecutiveCount}`);
124
+ console.log(` totalTriggered: ${r.totalTriggered}`);
125
+ console.log(` lastTriggeredAt: ${_fmtTs(r.lastTriggeredAt)}`);
126
+ });
127
+
128
+ perf
129
+ .command("rule-enable")
130
+ .argument("<ruleId>", "Rule id")
131
+ .description("Enable a rule")
132
+ .option("--json", "JSON output")
133
+ .action((ruleId, opts) => {
134
+ const db = _dbFromCtx(perf);
135
+ const r = setRuleEnabled(db, ruleId, true);
136
+ if (opts.json) return _json(r);
137
+ if (!r.updated) {
138
+ console.error(`Failed: ${r.reason}`);
139
+ process.exit(1);
140
+ }
141
+ console.log(`Enabled ${ruleId}`);
142
+ });
143
+
144
+ perf
145
+ .command("rule-disable")
146
+ .argument("<ruleId>", "Rule id")
147
+ .description("Disable a rule")
148
+ .option("--json", "JSON output")
149
+ .action((ruleId, opts) => {
150
+ const db = _dbFromCtx(perf);
151
+ const r = setRuleEnabled(db, ruleId, false);
152
+ if (opts.json) return _json(r);
153
+ if (!r.updated) {
154
+ console.error(`Failed: ${r.reason}`);
155
+ process.exit(1);
156
+ }
157
+ console.log(`Disabled ${ruleId}`);
158
+ });
159
+
160
+ /* ── Sampling ─────────────────────────────────────── */
161
+
162
+ perf
163
+ .command("collect")
164
+ .description("Collect one performance sample now")
165
+ .option("--slow-queries <n>", "Feed slowQueries counter (int)", (v) =>
166
+ parseInt(v, 10),
167
+ )
168
+ .option("--json", "JSON output")
169
+ .action((opts) => {
170
+ const db = _dbFromCtx(perf);
171
+ const s = collectSample(db, { slowQueries: opts.slowQueries });
172
+ if (opts.json) return _json(s);
173
+ console.log(`Sample @ ${_fmtTs(s.ts)}`);
174
+ console.log(` cpu: ${s.cpuPercent}% mem: ${s.memoryPercent}%`);
175
+ console.log(
176
+ ` heap: ${s.heapPercent}% (${s.heapUsed}/${s.heapTotal}) rss: ${s.rss}`,
177
+ );
178
+ console.log(` load1: ${s.load1} loadPerCore: ${s.loadPerCore}`);
179
+ });
180
+
181
+ perf
182
+ .command("metrics")
183
+ .description("Show the latest sample (collect one if none exists)")
184
+ .option("--json", "JSON output")
185
+ .action((opts) => {
186
+ const db = _dbFromCtx(perf);
187
+ let s = getLatestSample(db);
188
+ if (!s) s = collectSample(db);
189
+ if (opts.json) return _json(s);
190
+ console.log(`Latest @ ${_fmtTs(s.ts)}`);
191
+ console.log(
192
+ ` cpu=${s.cpuPercent}% mem=${s.memoryPercent}% heap=${s.heapPercent}%`,
193
+ );
194
+ console.log(` load1=${s.load1} loadPerCore=${s.loadPerCore}`);
195
+ });
196
+
197
+ perf
198
+ .command("samples")
199
+ .description("List recent samples (ring buffer)")
200
+ .option("--limit <n>", "Max rows", (v) => parseInt(v, 10), 20)
201
+ .option("--since-ms <ms>", "Only samples newer than N ms ago", (v) =>
202
+ parseInt(v, 10),
203
+ )
204
+ .option("--json", "JSON output")
205
+ .action((opts) => {
206
+ const db = _dbFromCtx(perf);
207
+ const rows = listSamples(db, {
208
+ limit: opts.limit,
209
+ sinceMs: opts.sinceMs,
210
+ });
211
+ if (opts.json) return _json(rows);
212
+ for (const r of rows)
213
+ console.log(
214
+ ` ${_fmtTs(r.ts)} cpu=${r.cpuPercent}% mem=${r.memoryPercent}% heap=${r.heapPercent}% load/c=${r.loadPerCore}`,
215
+ );
216
+ console.log(`(${rows.length} rows)`);
217
+ });
218
+
219
+ perf
220
+ .command("clear-history")
221
+ .description("Wipe the ring buffer")
222
+ .option("--json", "JSON output")
223
+ .action((opts) => {
224
+ const db = _dbFromCtx(perf);
225
+ const r = clearHistory(db);
226
+ if (opts.json) return _json(r);
227
+ console.log(`Cleared ${r.cleared} samples`);
228
+ });
229
+
230
+ /* ── Evaluation ───────────────────────────────────── */
231
+
232
+ perf
233
+ .command("evaluate")
234
+ .description("Run the rule engine against the latest sample")
235
+ .option("--collect", "Collect a fresh sample before evaluating")
236
+ .option("--slow-queries <n>", "Feed slowQueries counter (int)", (v) =>
237
+ parseInt(v, 10),
238
+ )
239
+ .option("--json", "JSON output")
240
+ .action((opts) => {
241
+ const db = _dbFromCtx(perf);
242
+ if (opts.collect) collectSample(db, { slowQueries: opts.slowQueries });
243
+ const r = evaluateRules(db);
244
+ if (opts.json) return _json(r);
245
+ console.log(`Evaluated @ ${_fmtTs(r.evaluatedAt)}`);
246
+ console.log(
247
+ `Sample: cpu=${r.sample.cpuPercent}% mem=${r.sample.memoryPercent}% heap=${r.sample.heapPercent}% load/c=${r.sample.loadPerCore}`,
248
+ );
249
+ console.log(`Triggered: ${r.triggered.length}`);
250
+ for (const t of r.triggered)
251
+ console.log(
252
+ ` ✱ ${t.ruleId} [${t.severity}] ${t.metric}=${t.value} (> ${t.threshold}) → ${t.recommendationId}`,
253
+ );
254
+ console.log(`Skipped: ${r.skipped.length}`);
255
+ for (const s of r.skipped) console.log(` · ${s.ruleId} — ${s.reason}`);
256
+ });
257
+
258
+ perf
259
+ .command("recommendations")
260
+ .description("List recommendations")
261
+ .option("--status <s>", "Filter: pending|applied|dismissed")
262
+ .option("--limit <n>", "Max rows", (v) => parseInt(v, 10), 50)
263
+ .option("--json", "JSON output")
264
+ .action((opts) => {
265
+ const db = _dbFromCtx(perf);
266
+ const rows = listRecommendations(db, {
267
+ status: opts.status,
268
+ limit: opts.limit,
269
+ });
270
+ if (opts.json) return _json(rows);
271
+ for (const r of rows)
272
+ console.log(
273
+ ` [${r.status.padEnd(10)}] ${r.id} ${r.ruleId} ${r.metric}=${r.metricValue} (> ${r.threshold})`,
274
+ );
275
+ console.log(`(${rows.length} rows)`);
276
+ });
277
+
278
+ perf
279
+ .command("apply")
280
+ .argument("<id>", "Recommendation id")
281
+ .description("Mark a recommendation as applied")
282
+ .option("-n, --note <note>", "Optional note")
283
+ .option("--json", "JSON output")
284
+ .action((id, opts) => {
285
+ const db = _dbFromCtx(perf);
286
+ const r = applyRecommendation(db, id, { note: opts.note });
287
+ if (opts.json) return _json(r);
288
+ if (!r.applied) {
289
+ console.error(`Failed: ${r.reason}`);
290
+ process.exit(1);
291
+ }
292
+ console.log(`Applied ${id} @ ${_fmtTs(r.appliedAt)}`);
293
+ });
294
+
295
+ perf
296
+ .command("dismiss")
297
+ .argument("<id>", "Recommendation id")
298
+ .description("Dismiss a pending recommendation")
299
+ .option("-n, --note <note>", "Optional note")
300
+ .option("--json", "JSON output")
301
+ .action((id, opts) => {
302
+ const db = _dbFromCtx(perf);
303
+ const r = dismissRecommendation(db, id, { note: opts.note });
304
+ if (opts.json) return _json(r);
305
+ if (!r.dismissed) {
306
+ console.error(`Failed: ${r.reason}`);
307
+ process.exit(1);
308
+ }
309
+ console.log(`Dismissed ${id}`);
310
+ });
311
+
312
+ /* ── History / alerts / stats ─────────────────────── */
313
+
314
+ perf
315
+ .command("history")
316
+ .description("Tuning action history")
317
+ .option("--rule-id <id>", "Filter by rule id")
318
+ .option("--limit <n>", "Max rows", (v) => parseInt(v, 10), 50)
319
+ .option("--json", "JSON output")
320
+ .action((opts) => {
321
+ const db = _dbFromCtx(perf);
322
+ const rows = listHistory(db, { ruleId: opts.ruleId, limit: opts.limit });
323
+ if (opts.json) return _json(rows);
324
+ for (const r of rows)
325
+ console.log(
326
+ ` ${_fmtTs(r.createdAt)} ${r.ruleId.padEnd(20)} ${r.action}`,
327
+ );
328
+ console.log(`(${rows.length} rows)`);
329
+ });
330
+
331
+ perf
332
+ .command("alerts")
333
+ .description("Current threshold violations against latest sample")
334
+ .option("--json", "JSON output")
335
+ .action((opts) => {
336
+ const db = _dbFromCtx(perf);
337
+ const rows = getAlerts(db);
338
+ if (opts.json) return _json(rows);
339
+ if (rows.length === 0) {
340
+ console.log("No alerts.");
341
+ return;
342
+ }
343
+ for (const a of rows)
344
+ console.log(` [${a.level}] ${a.metric}=${a.value} > ${a.threshold}`);
345
+ });
346
+
347
+ perf
348
+ .command("config")
349
+ .description("Show or update performance config")
350
+ .option("--max-samples <n>", "Ring buffer size", (v) => parseInt(v, 10))
351
+ .option(
352
+ "--interval-ms <n>",
353
+ "Intended sample interval (informational)",
354
+ (v) => parseInt(v, 10),
355
+ )
356
+ .option(
357
+ "--threshold <metric=value>",
358
+ "Override threshold (repeatable)",
359
+ (val, prev = {}) => {
360
+ const [k, v] = val.split("=");
361
+ prev[k] = parseFloat(v);
362
+ return prev;
363
+ },
364
+ )
365
+ .option("--json", "JSON output")
366
+ .action((opts) => {
367
+ const db = _dbFromCtx(perf);
368
+ const patch = {};
369
+ if (opts.maxSamples != null) patch.maxSamples = opts.maxSamples;
370
+ if (opts.intervalMs != null) patch.sampleIntervalMs = opts.intervalMs;
371
+ if (opts.threshold) patch.thresholds = opts.threshold;
372
+ const cfg =
373
+ Object.keys(patch).length > 0
374
+ ? setPerfConfig(db, patch)
375
+ : getPerfConfig(db);
376
+ if (opts.json) return _json(cfg);
377
+ console.log(`maxSamples: ${cfg.maxSamples}`);
378
+ console.log(`sampleIntervalMs: ${cfg.sampleIntervalMs}`);
379
+ console.log("thresholds:");
380
+ for (const [k, v] of Object.entries(cfg.thresholds))
381
+ console.log(` ${k.padEnd(16)} ${v}`);
382
+ });
383
+
384
+ perf
385
+ .command("stats")
386
+ .description("Aggregate perf stats")
387
+ .option("--json", "JSON output")
388
+ .action((opts) => {
389
+ const db = _dbFromCtx(perf);
390
+ const s = getPerfStats(db);
391
+ if (opts.json) return _json(s);
392
+ console.log(`Samples: ${s.samples}`);
393
+ console.log(
394
+ `Rules: ${s.rules.enabled}/${s.rules.total} enabled, triggered ${s.rules.triggered}x`,
395
+ );
396
+ console.log(
397
+ `Recommendations: pending=${s.recommendations.pending} applied=${s.recommendations.applied} dismissed=${s.recommendations.dismissed} total=${s.recommendations.total}`,
398
+ );
399
+ console.log(`History: ${s.historyEntries}`);
400
+ console.log(
401
+ `Averages: cpu=${s.averages.cpuPercent ?? "—"}% mem=${s.averages.memoryPercent ?? "—"}% heap=${s.averages.heapPercent ?? "—"}%`,
402
+ );
403
+ });
404
+
405
+ perf
406
+ .command("report")
407
+ .description("Full performance report (sample + alerts + stats + pending)")
408
+ .option("--json", "JSON output")
409
+ .action((opts) => {
410
+ const db = _dbFromCtx(perf);
411
+ const r = getPerformanceReport(db);
412
+ if (opts.json) return _json(r);
413
+ console.log(`Report @ ${_fmtTs(r.generatedAt)}`);
414
+ if (r.sample) {
415
+ console.log(
416
+ `Sample: cpu=${r.sample.cpuPercent}% mem=${r.sample.memoryPercent}% heap=${r.sample.heapPercent}% load/c=${r.sample.loadPerCore}`,
417
+ );
418
+ } else {
419
+ console.log("Sample: (none — run `cc perf collect`)");
420
+ }
421
+ console.log(`Alerts: ${r.alerts.length}`);
422
+ for (const a of r.alerts)
423
+ console.log(` [${a.level}] ${a.metric}=${a.value} > ${a.threshold}`);
424
+ console.log(
425
+ `Pending recommendations: ${r.pendingRecommendations.length}`,
426
+ );
427
+ for (const p of r.pendingRecommendations)
428
+ console.log(` ${p.id} ${p.ruleId} ${p.description}`);
429
+ console.log(`Recent history: ${r.recentHistory.length}`);
430
+ });
431
+
432
+ program.addCommand(perf);
433
+ }