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.
- package/package.json +1 -1
- package/src/commands/a2a.js +380 -0
- package/src/commands/bi.js +348 -0
- package/src/commands/crosschain.js +218 -0
- package/src/commands/dlp.js +341 -0
- package/src/commands/evomap.js +394 -0
- package/src/commands/federation.js +283 -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/privacy.js +321 -0
- package/src/commands/reputation.js +261 -0
- package/src/commands/siem.js +246 -0
- package/src/commands/sla.js +259 -0
- package/src/commands/stress.js +230 -0
- package/src/commands/terraform.js +245 -0
- package/src/commands/zkp.js +335 -0
- package/src/lib/a2a-protocol.js +451 -0
- package/src/lib/app-builder.js +239 -0
- package/src/lib/bi-engine.js +338 -0
- package/src/lib/cross-chain.js +345 -0
- package/src/lib/dlp-engine.js +389 -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/inference-network.js +330 -0
- package/src/lib/privacy-computing.js +427 -0
- package/src/lib/reputation-optimizer.js +299 -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/stress-tester.js +330 -0
- package/src/lib/terraform-manager.js +363 -0
- package/src/lib/zkp-engine.js +274 -0
package/src/commands/sla.js
CHANGED
|
@@ -21,6 +21,25 @@ import {
|
|
|
21
21
|
generateReport,
|
|
22
22
|
SLA_TERMS,
|
|
23
23
|
VIOLATION_SEVERITY,
|
|
24
|
+
// V2
|
|
25
|
+
SLA_STATUS_V2,
|
|
26
|
+
SLA_TIER_V2,
|
|
27
|
+
SLA_TERM_V2,
|
|
28
|
+
VIOLATION_SEVERITY_V2,
|
|
29
|
+
VIOLATION_STATUS_V2,
|
|
30
|
+
SLA_DEFAULT_MAX_ACTIVE_PER_ORG,
|
|
31
|
+
setMaxActiveSlasPerOrg,
|
|
32
|
+
getMaxActiveSlasPerOrg,
|
|
33
|
+
getActiveSlaCountForOrg,
|
|
34
|
+
createSLAV2,
|
|
35
|
+
setSLAStatus,
|
|
36
|
+
expireSLA,
|
|
37
|
+
autoExpireSLAs,
|
|
38
|
+
setViolationStatus,
|
|
39
|
+
acknowledgeViolation,
|
|
40
|
+
resolveViolation,
|
|
41
|
+
waiveViolation,
|
|
42
|
+
getSLAStatsV2,
|
|
24
43
|
} from "../lib/sla-manager.js";
|
|
25
44
|
|
|
26
45
|
function _dbFromCtx(ctx) {
|
|
@@ -349,4 +368,244 @@ export function registerSlaCommand(program) {
|
|
|
349
368
|
process.exit(1);
|
|
350
369
|
}
|
|
351
370
|
});
|
|
371
|
+
|
|
372
|
+
// ---------- V2 (Phase 61) ----------
|
|
373
|
+
const withDb = async (fn) => {
|
|
374
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
375
|
+
if (!ctx.db) {
|
|
376
|
+
logger.error("Database not available");
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
const db = ctx.db.getDatabase();
|
|
381
|
+
ensureSlaTables(db);
|
|
382
|
+
return await fn(db);
|
|
383
|
+
} finally {
|
|
384
|
+
await shutdown();
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
sla
|
|
389
|
+
.command("statuses")
|
|
390
|
+
.description("List SLA_STATUS_V2 values")
|
|
391
|
+
.action(() => {
|
|
392
|
+
console.log(JSON.stringify(Object.values(SLA_STATUS_V2), null, 2));
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
sla
|
|
396
|
+
.command("tier-names")
|
|
397
|
+
.description("List SLA_TIER_V2 values")
|
|
398
|
+
.action(() => {
|
|
399
|
+
console.log(JSON.stringify(Object.values(SLA_TIER_V2), null, 2));
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
sla
|
|
403
|
+
.command("term-names")
|
|
404
|
+
.description("List SLA_TERM_V2 values")
|
|
405
|
+
.action(() => {
|
|
406
|
+
console.log(JSON.stringify(Object.values(SLA_TERM_V2), null, 2));
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
sla
|
|
410
|
+
.command("severities")
|
|
411
|
+
.description("List VIOLATION_SEVERITY_V2 values")
|
|
412
|
+
.action(() => {
|
|
413
|
+
console.log(
|
|
414
|
+
JSON.stringify(Object.values(VIOLATION_SEVERITY_V2), null, 2),
|
|
415
|
+
);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
sla
|
|
419
|
+
.command("violation-statuses")
|
|
420
|
+
.description("List VIOLATION_STATUS_V2 values")
|
|
421
|
+
.action(() => {
|
|
422
|
+
console.log(JSON.stringify(Object.values(VIOLATION_STATUS_V2), null, 2));
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
sla
|
|
426
|
+
.command("default-max-active")
|
|
427
|
+
.description("Show SLA_DEFAULT_MAX_ACTIVE_PER_ORG")
|
|
428
|
+
.action(() => {
|
|
429
|
+
console.log(SLA_DEFAULT_MAX_ACTIVE_PER_ORG);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
sla
|
|
433
|
+
.command("max-active")
|
|
434
|
+
.description("Show current max active SLAs per org")
|
|
435
|
+
.action(() => {
|
|
436
|
+
console.log(getMaxActiveSlasPerOrg());
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
sla
|
|
440
|
+
.command("set-max-active <n>")
|
|
441
|
+
.description("Set per-org active-contract admission cap")
|
|
442
|
+
.action((n) => {
|
|
443
|
+
try {
|
|
444
|
+
const v = setMaxActiveSlasPerOrg(Number(n));
|
|
445
|
+
logger.success(`maxActiveSlasPerOrg=${v}`);
|
|
446
|
+
} catch (err) {
|
|
447
|
+
logger.error(`Failed: ${err.message}`);
|
|
448
|
+
process.exit(1);
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
sla
|
|
453
|
+
.command("active-count <org-id>")
|
|
454
|
+
.description("Show active SLA count for an org")
|
|
455
|
+
.action((orgId) => {
|
|
456
|
+
console.log(getActiveSlaCountForOrg(orgId));
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
sla
|
|
460
|
+
.command("create-v2 <org-id>")
|
|
461
|
+
.description("Create a V2 SLA contract (enforces per-org active cap)")
|
|
462
|
+
.option("-t, --tier <tier>", "gold|silver|bronze", "silver")
|
|
463
|
+
.option("-d, --duration <ms>", "Contract duration in ms", parseInt)
|
|
464
|
+
.option("-f, --fee <amount>", "Monthly fee", parseFloat)
|
|
465
|
+
.option("--json", "Output as JSON")
|
|
466
|
+
.action(async (orgId, options) => {
|
|
467
|
+
try {
|
|
468
|
+
await withDb((db) => {
|
|
469
|
+
const c = createSLAV2(db, {
|
|
470
|
+
orgId,
|
|
471
|
+
tier: options.tier,
|
|
472
|
+
duration: options.duration,
|
|
473
|
+
monthlyFee: options.fee,
|
|
474
|
+
});
|
|
475
|
+
if (options.json) {
|
|
476
|
+
console.log(JSON.stringify(c, null, 2));
|
|
477
|
+
} else {
|
|
478
|
+
logger.success(
|
|
479
|
+
`Created ${c.slaId.slice(0, 8)} [${c.tier}] → ${c.status}`,
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
} catch (err) {
|
|
484
|
+
logger.error(`Failed: ${err.message}`);
|
|
485
|
+
process.exit(1);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
sla
|
|
490
|
+
.command("set-status <sla-id> <status>")
|
|
491
|
+
.description("Transition SLA to a given status (state-machine guarded)")
|
|
492
|
+
.action(async (slaId, status) => {
|
|
493
|
+
try {
|
|
494
|
+
await withDb((db) => {
|
|
495
|
+
const c = setSLAStatus(db, slaId, status);
|
|
496
|
+
logger.success(`${c.slaId.slice(0, 8)} → ${c.status}`);
|
|
497
|
+
});
|
|
498
|
+
} catch (err) {
|
|
499
|
+
logger.error(`Failed: ${err.message}`);
|
|
500
|
+
process.exit(1);
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
sla
|
|
505
|
+
.command("expire <sla-id>")
|
|
506
|
+
.description("Expire an SLA (shortcut for set-status ... expired)")
|
|
507
|
+
.action(async (slaId) => {
|
|
508
|
+
try {
|
|
509
|
+
await withDb((db) => {
|
|
510
|
+
const c = expireSLA(db, slaId);
|
|
511
|
+
logger.success(`${c.slaId.slice(0, 8)} → ${c.status}`);
|
|
512
|
+
});
|
|
513
|
+
} catch (err) {
|
|
514
|
+
logger.error(`Failed: ${err.message}`);
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
sla
|
|
520
|
+
.command("auto-expire")
|
|
521
|
+
.description("Bulk-flip ACTIVE contracts past endDate to EXPIRED")
|
|
522
|
+
.option("--json", "Output as JSON")
|
|
523
|
+
.action(async (options) => {
|
|
524
|
+
try {
|
|
525
|
+
await withDb((db) => {
|
|
526
|
+
const flipped = autoExpireSLAs(db);
|
|
527
|
+
if (options.json) {
|
|
528
|
+
console.log(JSON.stringify(flipped, null, 2));
|
|
529
|
+
} else {
|
|
530
|
+
logger.success(`Auto-expired ${flipped.length} contract(s)`);
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
} catch (err) {
|
|
534
|
+
logger.error(`Failed: ${err.message}`);
|
|
535
|
+
process.exit(1);
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
sla
|
|
540
|
+
.command("set-violation-status <violation-id> <status>")
|
|
541
|
+
.description("Transition a violation (open→{acknowledged,resolved,waived})")
|
|
542
|
+
.option("--note <note>", "Attach a note")
|
|
543
|
+
.action(async (violationId, status, options) => {
|
|
544
|
+
try {
|
|
545
|
+
await withDb((db) => {
|
|
546
|
+
const v = setViolationStatus(db, violationId, status, {
|
|
547
|
+
note: options.note,
|
|
548
|
+
});
|
|
549
|
+
logger.success(`${v.violationId.slice(0, 8)} → ${v.v2Status}`);
|
|
550
|
+
});
|
|
551
|
+
} catch (err) {
|
|
552
|
+
logger.error(`Failed: ${err.message}`);
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
sla
|
|
558
|
+
.command("acknowledge-violation <violation-id>")
|
|
559
|
+
.description("Acknowledge a violation")
|
|
560
|
+
.option("--note <note>", "Attach a note")
|
|
561
|
+
.action(async (violationId, options) => {
|
|
562
|
+
try {
|
|
563
|
+
await withDb((db) => {
|
|
564
|
+
const v = acknowledgeViolation(db, violationId, options.note);
|
|
565
|
+
logger.success(`${v.violationId.slice(0, 8)} → ${v.v2Status}`);
|
|
566
|
+
});
|
|
567
|
+
} catch (err) {
|
|
568
|
+
logger.error(`Failed: ${err.message}`);
|
|
569
|
+
process.exit(1);
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
sla
|
|
574
|
+
.command("resolve-violation <violation-id>")
|
|
575
|
+
.description("Resolve a violation")
|
|
576
|
+
.option("--note <note>", "Attach a note")
|
|
577
|
+
.action(async (violationId, options) => {
|
|
578
|
+
try {
|
|
579
|
+
await withDb((db) => {
|
|
580
|
+
const v = resolveViolation(db, violationId, options.note);
|
|
581
|
+
logger.success(`${v.violationId.slice(0, 8)} → ${v.v2Status}`);
|
|
582
|
+
});
|
|
583
|
+
} catch (err) {
|
|
584
|
+
logger.error(`Failed: ${err.message}`);
|
|
585
|
+
process.exit(1);
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
sla
|
|
590
|
+
.command("waive-violation <violation-id>")
|
|
591
|
+
.description("Waive a violation")
|
|
592
|
+
.option("--note <note>", "Attach a note")
|
|
593
|
+
.action(async (violationId, options) => {
|
|
594
|
+
try {
|
|
595
|
+
await withDb((db) => {
|
|
596
|
+
const v = waiveViolation(db, violationId, options.note);
|
|
597
|
+
logger.success(`${v.violationId.slice(0, 8)} → ${v.v2Status}`);
|
|
598
|
+
});
|
|
599
|
+
} catch (err) {
|
|
600
|
+
logger.error(`Failed: ${err.message}`);
|
|
601
|
+
process.exit(1);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
sla
|
|
606
|
+
.command("stats-v2")
|
|
607
|
+
.description("Show aggregate V2 SLA stats (byStatus/byTier/violations)")
|
|
608
|
+
.action(() => {
|
|
609
|
+
console.log(JSON.stringify(getSLAStatsV2(), null, 2));
|
|
610
|
+
});
|
|
352
611
|
}
|
package/src/commands/stress.js
CHANGED
|
@@ -15,6 +15,22 @@ import {
|
|
|
15
15
|
analyzeBottlenecks,
|
|
16
16
|
generateCapacityPlan,
|
|
17
17
|
listLoadLevels,
|
|
18
|
+
// V2
|
|
19
|
+
RUN_STATUS_V2,
|
|
20
|
+
LEVEL_NAME_V2,
|
|
21
|
+
BOTTLENECK_KIND_V2,
|
|
22
|
+
BOTTLENECK_SEVERITY_V2,
|
|
23
|
+
STRESS_DEFAULT_MAX_CONCURRENT,
|
|
24
|
+
setMaxConcurrentTests,
|
|
25
|
+
getMaxConcurrentTests,
|
|
26
|
+
getActiveTestCount,
|
|
27
|
+
startStressTestV2,
|
|
28
|
+
completeStressTest,
|
|
29
|
+
stopStressTestV2,
|
|
30
|
+
failStressTest,
|
|
31
|
+
setRunStatus,
|
|
32
|
+
recommendLevelV2,
|
|
33
|
+
getStressStatsV2,
|
|
18
34
|
} from "../lib/stress-tester.js";
|
|
19
35
|
|
|
20
36
|
function _dbFromCtx(ctx) {
|
|
@@ -249,4 +265,218 @@ export function registerStressCommand(program) {
|
|
|
249
265
|
}
|
|
250
266
|
}
|
|
251
267
|
});
|
|
268
|
+
|
|
269
|
+
// ---------- V2 (Phase 59) ----------
|
|
270
|
+
const withDb = async (fn) => {
|
|
271
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
272
|
+
if (!ctx.db) {
|
|
273
|
+
logger.error("Database not available");
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
const db = ctx.db.getDatabase();
|
|
278
|
+
ensureStressTables(db);
|
|
279
|
+
return await fn(db);
|
|
280
|
+
} finally {
|
|
281
|
+
await shutdown();
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
stress
|
|
286
|
+
.command("run-statuses")
|
|
287
|
+
.description("List RUN_STATUS_V2 values")
|
|
288
|
+
.action(() => {
|
|
289
|
+
console.log(JSON.stringify(Object.values(RUN_STATUS_V2), null, 2));
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
stress
|
|
293
|
+
.command("level-names")
|
|
294
|
+
.description("List LEVEL_NAME_V2 values")
|
|
295
|
+
.action(() => {
|
|
296
|
+
console.log(JSON.stringify(Object.values(LEVEL_NAME_V2), null, 2));
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
stress
|
|
300
|
+
.command("bottleneck-kinds")
|
|
301
|
+
.description("List BOTTLENECK_KIND_V2 values")
|
|
302
|
+
.action(() => {
|
|
303
|
+
console.log(JSON.stringify(Object.values(BOTTLENECK_KIND_V2), null, 2));
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
stress
|
|
307
|
+
.command("bottleneck-severities")
|
|
308
|
+
.description("List BOTTLENECK_SEVERITY_V2 values")
|
|
309
|
+
.action(() => {
|
|
310
|
+
console.log(
|
|
311
|
+
JSON.stringify(Object.values(BOTTLENECK_SEVERITY_V2), null, 2),
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
stress
|
|
316
|
+
.command("default-max-concurrent")
|
|
317
|
+
.description("Show STRESS_DEFAULT_MAX_CONCURRENT")
|
|
318
|
+
.action(() => {
|
|
319
|
+
console.log(STRESS_DEFAULT_MAX_CONCURRENT);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
stress
|
|
323
|
+
.command("max-concurrent")
|
|
324
|
+
.description("Show current max concurrent test limit")
|
|
325
|
+
.action(() => {
|
|
326
|
+
console.log(getMaxConcurrentTests());
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
stress
|
|
330
|
+
.command("active-test-count")
|
|
331
|
+
.description("Show current active (RUNNING) test count")
|
|
332
|
+
.action(() => {
|
|
333
|
+
console.log(getActiveTestCount());
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
stress
|
|
337
|
+
.command("set-max-concurrent <n>")
|
|
338
|
+
.description("Set max concurrent test admission limit")
|
|
339
|
+
.action((n) => {
|
|
340
|
+
try {
|
|
341
|
+
const v = setMaxConcurrentTests(Number(n));
|
|
342
|
+
logger.success(`maxConcurrentTests=${v}`);
|
|
343
|
+
} catch (err) {
|
|
344
|
+
logger.error(`Failed: ${err.message}`);
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
stress
|
|
350
|
+
.command("start-v2")
|
|
351
|
+
.description("Start a V2 stress run (RUNNING, no metrics until complete)")
|
|
352
|
+
.option(
|
|
353
|
+
"-l, --level <level>",
|
|
354
|
+
"Load level (light|medium|heavy|extreme)",
|
|
355
|
+
"medium",
|
|
356
|
+
)
|
|
357
|
+
.option("-c, --concurrency <n>", "Override concurrency", parseInt)
|
|
358
|
+
.option("-r, --rps <n>", "Override requests per second", parseInt)
|
|
359
|
+
.option("-d, --duration <ms>", "Override duration in ms", parseInt)
|
|
360
|
+
.option("--json", "Output as JSON")
|
|
361
|
+
.action(async (options) => {
|
|
362
|
+
try {
|
|
363
|
+
await withDb((db) => {
|
|
364
|
+
const run = startStressTestV2(db, {
|
|
365
|
+
level: options.level,
|
|
366
|
+
concurrency: options.concurrency,
|
|
367
|
+
requestsPerSecond: options.rps,
|
|
368
|
+
duration: options.duration,
|
|
369
|
+
});
|
|
370
|
+
if (options.json) {
|
|
371
|
+
console.log(JSON.stringify(run, null, 2));
|
|
372
|
+
} else {
|
|
373
|
+
logger.success(
|
|
374
|
+
`Started ${run.testId.slice(0, 8)} [${run.loadLevel}] → ${run.status}`,
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
} catch (err) {
|
|
379
|
+
logger.error(`Failed: ${err.message}`);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
stress
|
|
385
|
+
.command("complete <test-id>")
|
|
386
|
+
.description("Complete a RUNNING run and compute metrics")
|
|
387
|
+
.option("--json", "Output as JSON")
|
|
388
|
+
.action(async (testId, options) => {
|
|
389
|
+
try {
|
|
390
|
+
await withDb((db) => {
|
|
391
|
+
const r = completeStressTest(db, testId);
|
|
392
|
+
if (options.json) {
|
|
393
|
+
console.log(JSON.stringify(r, null, 2));
|
|
394
|
+
} else {
|
|
395
|
+
logger.success(
|
|
396
|
+
`${r.testId.slice(0, 8)} → ${r.status} (tps=${r.result.tps})`,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
} catch (err) {
|
|
401
|
+
logger.error(`Failed: ${err.message}`);
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
stress
|
|
407
|
+
.command("stop-v2 <test-id>")
|
|
408
|
+
.description("Stop a RUNNING run (→ STOPPED)")
|
|
409
|
+
.action(async (testId) => {
|
|
410
|
+
try {
|
|
411
|
+
await withDb((db) => {
|
|
412
|
+
const r = stopStressTestV2(db, testId);
|
|
413
|
+
logger.success(`${r.testId.slice(0, 8)} → ${r.status}`);
|
|
414
|
+
});
|
|
415
|
+
} catch (err) {
|
|
416
|
+
logger.error(`Failed: ${err.message}`);
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
stress
|
|
422
|
+
.command("fail <test-id> <error-message>")
|
|
423
|
+
.description("Fail a RUNNING run with an error message (→ FAILED)")
|
|
424
|
+
.action(async (testId, errorMessage) => {
|
|
425
|
+
try {
|
|
426
|
+
await withDb((db) => {
|
|
427
|
+
const r = failStressTest(db, testId, errorMessage);
|
|
428
|
+
logger.success(
|
|
429
|
+
`${r.testId.slice(0, 8)} → ${r.status} (${r.errorMessage})`,
|
|
430
|
+
);
|
|
431
|
+
});
|
|
432
|
+
} catch (err) {
|
|
433
|
+
logger.error(`Failed: ${err.message}`);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
stress
|
|
439
|
+
.command("set-status <test-id> <status>")
|
|
440
|
+
.description("Transition run to a given status (state-machine guarded)")
|
|
441
|
+
.option("--error-message <msg>", "Attach error message (for failed)")
|
|
442
|
+
.action(async (testId, status, options) => {
|
|
443
|
+
try {
|
|
444
|
+
await withDb((db) => {
|
|
445
|
+
const patch = {};
|
|
446
|
+
if (options.errorMessage) patch.errorMessage = options.errorMessage;
|
|
447
|
+
const r = setRunStatus(db, testId, status, patch);
|
|
448
|
+
logger.success(`${r.testId.slice(0, 8)} → ${r.status}`);
|
|
449
|
+
});
|
|
450
|
+
} catch (err) {
|
|
451
|
+
logger.error(`Failed: ${err.message}`);
|
|
452
|
+
process.exit(1);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
stress
|
|
457
|
+
.command("recommend-level <target-rps>")
|
|
458
|
+
.description("Recommend the largest built-in level ≤ targetRps")
|
|
459
|
+
.option("--json", "Output as JSON")
|
|
460
|
+
.action((targetRps, options) => {
|
|
461
|
+
try {
|
|
462
|
+
const level = recommendLevelV2(Number(targetRps));
|
|
463
|
+
if (options.json) {
|
|
464
|
+
console.log(JSON.stringify(level, null, 2));
|
|
465
|
+
} else {
|
|
466
|
+
logger.log(
|
|
467
|
+
` ${chalk.cyan(level.name)} concurrency=${level.concurrency} rps=${level.requestsPerSecond} duration=${level.duration}ms`,
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
} catch (err) {
|
|
471
|
+
logger.error(`Failed: ${err.message}`);
|
|
472
|
+
process.exit(1);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
stress
|
|
477
|
+
.command("stats-v2")
|
|
478
|
+
.description("Show aggregate V2 stats (byStatus/byLevel/bottlenecks)")
|
|
479
|
+
.action(() => {
|
|
480
|
+
console.log(JSON.stringify(getStressStatsV2(), null, 2));
|
|
481
|
+
});
|
|
252
482
|
}
|