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.
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-Rvi759IS.js → AppLayout-6SPt_8Y_.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-DBhFxXYQ.js → Dashboard-Br7kCwKJ.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +1 -0
- package/src/assets/web-panel/assets/{index-uL0cZ8N_.js → index-tN-8TosE.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/a2a.js +380 -0
- package/src/commands/agent-network.js +785 -0
- package/src/commands/automation.js +654 -0
- package/src/commands/bi.js +348 -0
- package/src/commands/crosschain.js +218 -0
- package/src/commands/dao.js +565 -0
- package/src/commands/did-v2.js +620 -0
- package/src/commands/dlp.js +341 -0
- package/src/commands/economy.js +578 -0
- package/src/commands/evolution.js +391 -0
- package/src/commands/evomap.js +394 -0
- package/src/commands/federation.js +283 -0
- package/src/commands/hmemory.js +442 -0
- package/src/commands/inference.js +318 -0
- package/src/commands/lowcode.js +356 -0
- package/src/commands/marketplace.js +256 -0
- package/src/commands/perf.js +433 -0
- package/src/commands/pipeline.js +449 -0
- package/src/commands/plugin-ecosystem.js +517 -0
- package/src/commands/privacy.js +321 -0
- package/src/commands/reputation.js +261 -0
- package/src/commands/sandbox.js +401 -0
- package/src/commands/siem.js +246 -0
- package/src/commands/sla.js +259 -0
- package/src/commands/social.js +311 -0
- package/src/commands/sso.js +798 -0
- package/src/commands/stress.js +230 -0
- package/src/commands/terraform.js +245 -0
- package/src/commands/workflow.js +320 -0
- package/src/commands/zkp.js +562 -1
- package/src/index.js +21 -0
- package/src/lib/a2a-protocol.js +451 -0
- package/src/lib/agent-economy.js +479 -0
- package/src/lib/agent-network.js +1121 -0
- package/src/lib/app-builder.js +239 -0
- package/src/lib/automation-engine.js +948 -0
- package/src/lib/bi-engine.js +338 -0
- package/src/lib/cross-chain.js +345 -0
- package/src/lib/dao-governance.js +569 -0
- package/src/lib/did-v2-manager.js +1127 -0
- package/src/lib/dlp-engine.js +389 -0
- package/src/lib/evolution-system.js +453 -0
- package/src/lib/evomap-federation.js +177 -0
- package/src/lib/evomap-governance.js +276 -0
- package/src/lib/federation-hardening.js +259 -0
- package/src/lib/hierarchical-memory.js +481 -0
- package/src/lib/inference-network.js +330 -0
- package/src/lib/perf-tuning.js +734 -0
- package/src/lib/pipeline-orchestrator.js +928 -0
- package/src/lib/plugin-ecosystem.js +1109 -0
- package/src/lib/privacy-computing.js +427 -0
- package/src/lib/reputation-optimizer.js +299 -0
- package/src/lib/sandbox-v2.js +306 -0
- package/src/lib/siem-exporter.js +333 -0
- package/src/lib/skill-marketplace.js +325 -0
- package/src/lib/sla-manager.js +275 -0
- package/src/lib/social-graph-analytics.js +707 -0
- package/src/lib/sso-manager.js +841 -0
- package/src/lib/stress-tester.js +330 -0
- package/src/lib/terraform-manager.js +363 -0
- package/src/lib/workflow-engine.js +454 -1
- package/src/lib/zkp-engine.js +523 -20
- 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
|
+
}
|