@ganakailabs/cloudeval-cli 0.25.0 → 0.26.1
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/dist/{App-H46FRLWK.js → App-3VDZ4SKF.js} +2 -2
- package/dist/{Banner-7X2VHUVH.js → Banner-7BP5U7P4.js} +2 -2
- package/dist/{chunk-TISPT6EB.js → chunk-M5XGS3ZV.js} +1 -1
- package/dist/{chunk-LKVKOGVL.js → chunk-UUXJKGRN.js} +1 -1
- package/dist/cli.js +240 -17
- package/package.json +1 -1
- package/sbom.spdx.json +1 -1
|
@@ -38,10 +38,10 @@ import {
|
|
|
38
38
|
} from "./chunk-KBHRBGSX.js";
|
|
39
39
|
import {
|
|
40
40
|
Banner
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-M5XGS3ZV.js";
|
|
42
42
|
import {
|
|
43
43
|
CLI_VERSION
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-UUXJKGRN.js";
|
|
45
45
|
import {
|
|
46
46
|
raisedButtonStyle,
|
|
47
47
|
terminalTheme
|
package/dist/cli.js
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
} from "./chunk-KBHRBGSX.js";
|
|
37
37
|
import {
|
|
38
38
|
CLI_VERSION
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-UUXJKGRN.js";
|
|
40
40
|
|
|
41
41
|
// src/runtime/prepareInk.ts
|
|
42
42
|
import fs from "fs";
|
|
@@ -453,6 +453,8 @@ var cliCommands = [
|
|
|
453
453
|
"--wait-timeout",
|
|
454
454
|
"--poll-interval",
|
|
455
455
|
"--no-ai-summary",
|
|
456
|
+
"--ai-summary-mode",
|
|
457
|
+
"--ai-summary-profile",
|
|
456
458
|
"--ignore-dirty",
|
|
457
459
|
"--output",
|
|
458
460
|
"--format",
|
|
@@ -2398,6 +2400,10 @@ var parseGateConfig = (configText) => {
|
|
|
2398
2400
|
if (!configText || !/^\s*ci\s*:/m.test(configText) || !/^\s*gates\s*:/m.test(configText)) {
|
|
2399
2401
|
return void 0;
|
|
2400
2402
|
}
|
|
2403
|
+
const stringValue2 = (key) => {
|
|
2404
|
+
const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*([^\\s#]+)`, "m"));
|
|
2405
|
+
return match ? match[1].trim() : void 0;
|
|
2406
|
+
};
|
|
2401
2407
|
const numberValue2 = (key) => {
|
|
2402
2408
|
const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*([0-9]+(?:\\.[0-9]+)?)`, "m"));
|
|
2403
2409
|
return match ? Number(match[1]) : void 0;
|
|
@@ -2406,9 +2412,35 @@ var parseGateConfig = (configText) => {
|
|
|
2406
2412
|
const match = configText.match(new RegExp(`^\\s*${key}\\s*:\\s*(true|false)`, "im"));
|
|
2407
2413
|
return match ? match[1].toLowerCase() === "true" : void 0;
|
|
2408
2414
|
};
|
|
2415
|
+
const pillarScoreMins = {};
|
|
2416
|
+
const pillarBlock = configText.match(/^(\s*)pillars\s*:\s*$(?<body>(?:\n\s+[-\w]+\s*:\s*[0-9]+(?:\.[0-9]+)?\s*)+)/m);
|
|
2417
|
+
const pillarBody = pillarBlock?.groups?.body ?? "";
|
|
2418
|
+
for (const line of pillarBody.split("\n")) {
|
|
2419
|
+
const match = line.match(/^\s+([-\w]+)\s*:\s*([0-9]+(?:\.[0-9]+)?)/);
|
|
2420
|
+
if (match) {
|
|
2421
|
+
pillarScoreMins[match[1].replace(/-/g, "_").toLowerCase()] = Number(match[2]);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
for (const key of [
|
|
2425
|
+
"security",
|
|
2426
|
+
"reliability",
|
|
2427
|
+
"operational_excellence",
|
|
2428
|
+
"performance_efficiency",
|
|
2429
|
+
"cost_optimization"
|
|
2430
|
+
]) {
|
|
2431
|
+
const value = numberValue2(`${key}_score_min`);
|
|
2432
|
+
if (value !== void 0) {
|
|
2433
|
+
pillarScoreMins[key] = value;
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
const enforcement = stringValue2("enforcement")?.toLowerCase();
|
|
2409
2437
|
return {
|
|
2438
|
+
enforcement: enforcement === "warn" ? "warn" : "required",
|
|
2410
2439
|
overallScoreMin: numberValue2("overall_score_min") ?? 80,
|
|
2440
|
+
pillarScoreMin: numberValue2("pillar_score_min"),
|
|
2441
|
+
pillarScoreMins,
|
|
2411
2442
|
failOnHighRisk: booleanValue2("fail_on_high_risk") ?? true,
|
|
2443
|
+
failOnValidationErrors: booleanValue2("fail_on_validation_errors") ?? true,
|
|
2412
2444
|
maxMonthlyCost: numberValue2("max_monthly_cost")
|
|
2413
2445
|
};
|
|
2414
2446
|
};
|
|
@@ -2423,10 +2455,86 @@ var numberFrom = (...values) => {
|
|
|
2423
2455
|
}
|
|
2424
2456
|
return void 0;
|
|
2425
2457
|
};
|
|
2458
|
+
var normalizeKey = (value) => String(value ?? "").trim().replace(/[\s-]+/g, "_").toLowerCase();
|
|
2459
|
+
var firstRecord = (...values) => {
|
|
2460
|
+
for (const value of values) {
|
|
2461
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2462
|
+
return value;
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
return void 0;
|
|
2466
|
+
};
|
|
2467
|
+
var extractPillars = (waf) => {
|
|
2468
|
+
const fromArray = waf?.parsed?.score?.pillars;
|
|
2469
|
+
if (Array.isArray(fromArray)) {
|
|
2470
|
+
return fromArray.map((pillar) => {
|
|
2471
|
+
const record = asRecord(pillar);
|
|
2472
|
+
const id = normalizeKey(record.id ?? record.name ?? record.label);
|
|
2473
|
+
const score = numberFrom(record.score, record.value);
|
|
2474
|
+
if (!id || score === void 0) return void 0;
|
|
2475
|
+
return {
|
|
2476
|
+
id,
|
|
2477
|
+
label: String(record.label ?? record.name ?? id),
|
|
2478
|
+
score,
|
|
2479
|
+
passed: numberFrom(record.passed, record.passed_rules),
|
|
2480
|
+
warned: numberFrom(record.warned, record.warning_rules),
|
|
2481
|
+
failed: numberFrom(record.failed, record.failed_rules)
|
|
2482
|
+
};
|
|
2483
|
+
}).filter((pillar) => pillar !== void 0);
|
|
2484
|
+
}
|
|
2485
|
+
const scores = firstRecord(
|
|
2486
|
+
waf?.parsed?.pillar_scores,
|
|
2487
|
+
waf?.parsed?.score?.pillar_scores,
|
|
2488
|
+
waf?.raw?.pillar_scores
|
|
2489
|
+
);
|
|
2490
|
+
if (!scores) return [];
|
|
2491
|
+
return Object.entries(scores).map(([id, score]) => ({
|
|
2492
|
+
id: normalizeKey(id),
|
|
2493
|
+
label: id,
|
|
2494
|
+
score: numberFrom(score)
|
|
2495
|
+
})).filter((pillar) => pillar.score !== void 0);
|
|
2496
|
+
};
|
|
2497
|
+
var extractValidation = ({
|
|
2498
|
+
waf,
|
|
2499
|
+
preload,
|
|
2500
|
+
project
|
|
2501
|
+
}) => {
|
|
2502
|
+
const psRuleSummary = firstRecord(
|
|
2503
|
+
waf?.parsed?.validation_summary,
|
|
2504
|
+
waf?.raw?.validation_summary,
|
|
2505
|
+
waf?.validation_summary
|
|
2506
|
+
);
|
|
2507
|
+
const rules = Array.isArray(waf?.parsed?.rules) ? waf?.parsed?.rules : [];
|
|
2508
|
+
const failedRules = rules.filter(
|
|
2509
|
+
(rule) => ["fail", "failed", "error"].includes(String(rule?.status ?? rule?.outcome ?? "").toLowerCase())
|
|
2510
|
+
);
|
|
2511
|
+
const unitSummary = firstRecord(
|
|
2512
|
+
preload?.latest_payloads?.unit_tests?.summary,
|
|
2513
|
+
preload?.reports?.unit_tests?.metrics,
|
|
2514
|
+
project?.status?.unit_tests
|
|
2515
|
+
);
|
|
2516
|
+
return {
|
|
2517
|
+
psRule: {
|
|
2518
|
+
total: numberFrom(psRuleSummary?.total_rules, psRuleSummary?.totalRules, rules.length),
|
|
2519
|
+
passed: numberFrom(psRuleSummary?.passed_rules, psRuleSummary?.passedRules),
|
|
2520
|
+
failed: numberFrom(psRuleSummary?.failed_rules, psRuleSummary?.failedRules, failedRules.length),
|
|
2521
|
+
errors: numberFrom(psRuleSummary?.error_rules, psRuleSummary?.errorRules)
|
|
2522
|
+
},
|
|
2523
|
+
unitTests: {
|
|
2524
|
+
status: unitSummary?.status,
|
|
2525
|
+
total: numberFrom(unitSummary?.total_tests, unitSummary?.totalTests),
|
|
2526
|
+
passed: numberFrom(unitSummary?.passed_tests, unitSummary?.passedTests),
|
|
2527
|
+
failed: numberFrom(unitSummary?.failed_tests, unitSummary?.failedTests),
|
|
2528
|
+
skipped: numberFrom(unitSummary?.skipped_tests, unitSummary?.skippedTests)
|
|
2529
|
+
}
|
|
2530
|
+
};
|
|
2531
|
+
};
|
|
2426
2532
|
var evaluateGate = ({
|
|
2427
2533
|
configText,
|
|
2428
2534
|
waf,
|
|
2429
|
-
cost
|
|
2535
|
+
cost,
|
|
2536
|
+
preload,
|
|
2537
|
+
project
|
|
2430
2538
|
}) => {
|
|
2431
2539
|
const gateConfig = parseGateConfig(configText);
|
|
2432
2540
|
const overallScore = numberFrom(
|
|
@@ -2444,13 +2552,61 @@ var evaluateGate = ({
|
|
|
2444
2552
|
cost?.parsed?.total_spend?.amount,
|
|
2445
2553
|
cost?.raw?.total
|
|
2446
2554
|
);
|
|
2555
|
+
const currency = String(
|
|
2556
|
+
cost?.parsed?.totalSpend?.currency ?? cost?.parsed?.total_spend?.currency ?? cost?.raw?.currency ?? ""
|
|
2557
|
+
) || void 0;
|
|
2558
|
+
const savings = numberFrom(
|
|
2559
|
+
cost?.parsed?.estimatedSavings?.amount,
|
|
2560
|
+
cost?.parsed?.estimated_savings?.amount
|
|
2561
|
+
);
|
|
2562
|
+
const pillars = extractPillars(waf).map((pillar) => {
|
|
2563
|
+
const threshold = gateConfig?.pillarScoreMins[pillar.id] ?? gateConfig?.pillarScoreMin;
|
|
2564
|
+
return {
|
|
2565
|
+
...pillar,
|
|
2566
|
+
threshold,
|
|
2567
|
+
status: threshold !== void 0 && pillar.score < threshold ? "fail" : "pass"
|
|
2568
|
+
};
|
|
2569
|
+
});
|
|
2570
|
+
const validation = extractValidation({ waf, preload, project });
|
|
2571
|
+
const wellArchitected = {
|
|
2572
|
+
overall: {
|
|
2573
|
+
score: overallScore,
|
|
2574
|
+
threshold: gateConfig?.overallScoreMin,
|
|
2575
|
+
status: gateConfig && overallScore !== void 0 && overallScore < gateConfig.overallScoreMin ? "fail" : "pass"
|
|
2576
|
+
},
|
|
2577
|
+
pillars,
|
|
2578
|
+
risks: {
|
|
2579
|
+
high: highRisk,
|
|
2580
|
+
medium: numberFrom(waf?.parsed?.counts?.mediumRisk, waf?.parsed?.counts?.medium_count),
|
|
2581
|
+
critical: numberFrom(waf?.parsed?.counts?.criticalRisk, waf?.parsed?.counts?.critical_count)
|
|
2582
|
+
},
|
|
2583
|
+
topFindings: Array.isArray(waf?.parsed?.rules) ? waf.parsed.rules.slice(0, 5) : []
|
|
2584
|
+
};
|
|
2585
|
+
const costSummary = {
|
|
2586
|
+
monthly: {
|
|
2587
|
+
amount: monthlyCost,
|
|
2588
|
+
currency,
|
|
2589
|
+
threshold: gateConfig?.maxMonthlyCost,
|
|
2590
|
+
status: gateConfig?.maxMonthlyCost !== void 0 && monthlyCost !== void 0 && monthlyCost > gateConfig.maxMonthlyCost ? "fail" : "pass"
|
|
2591
|
+
},
|
|
2592
|
+
estimatedSavings: {
|
|
2593
|
+
amount: savings,
|
|
2594
|
+
currency
|
|
2595
|
+
},
|
|
2596
|
+
topServices: Array.isArray(cost?.parsed?.serviceGroups) ? cost.parsed.serviceGroups.slice(0, 5) : [],
|
|
2597
|
+
recommendations: Array.isArray(cost?.parsed?.recommendations) ? cost.parsed.recommendations.slice(0, 5) : []
|
|
2598
|
+
};
|
|
2447
2599
|
if (!gateConfig) {
|
|
2448
2600
|
return {
|
|
2449
2601
|
status: "warn",
|
|
2450
2602
|
reason: "ci.gates is not configured in .cloudeval/config.yaml.",
|
|
2603
|
+
enforcement: "warn",
|
|
2451
2604
|
overallScore,
|
|
2452
2605
|
highRisk,
|
|
2453
|
-
monthlyCost
|
|
2606
|
+
monthlyCost,
|
|
2607
|
+
wellArchitected,
|
|
2608
|
+
cost: costSummary,
|
|
2609
|
+
validation
|
|
2454
2610
|
};
|
|
2455
2611
|
}
|
|
2456
2612
|
const failures = [];
|
|
@@ -2462,16 +2618,34 @@ var evaluateGate = ({
|
|
|
2462
2618
|
if (gateConfig.failOnHighRisk && highRisk !== void 0 && highRisk > 0) {
|
|
2463
2619
|
failures.push(`${highRisk} high-risk architecture findings`);
|
|
2464
2620
|
}
|
|
2621
|
+
for (const pillar of pillars) {
|
|
2622
|
+
if (pillar.status === "fail") {
|
|
2623
|
+
failures.push(`${pillar.label} score ${pillar.score} is below ${pillar.threshold}`);
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
const failedPsRules = validation.psRule.failed ?? 0;
|
|
2627
|
+
const failedUnitTests = validation.unitTests.failed ?? 0;
|
|
2628
|
+
if (gateConfig.failOnValidationErrors && (failedPsRules > 0 || failedUnitTests > 0)) {
|
|
2629
|
+
failures.push(
|
|
2630
|
+
`validation has ${failedPsRules} failed PSRule checks and ${failedUnitTests} failed unit tests`
|
|
2631
|
+
);
|
|
2632
|
+
}
|
|
2465
2633
|
if (gateConfig.maxMonthlyCost !== void 0 && monthlyCost !== void 0 && monthlyCost > gateConfig.maxMonthlyCost) {
|
|
2466
2634
|
failures.push(`monthly cost ${monthlyCost} exceeds ${gateConfig.maxMonthlyCost}`);
|
|
2467
2635
|
}
|
|
2636
|
+
const wouldFail = failures.length > 0;
|
|
2468
2637
|
return {
|
|
2469
|
-
status:
|
|
2638
|
+
status: wouldFail && gateConfig.enforcement === "required" ? "fail" : wouldFail ? "warn" : "pass",
|
|
2639
|
+
enforcement: gateConfig.enforcement,
|
|
2640
|
+
wouldFail,
|
|
2470
2641
|
failures,
|
|
2471
2642
|
thresholds: gateConfig,
|
|
2472
2643
|
overallScore,
|
|
2473
2644
|
highRisk,
|
|
2474
|
-
monthlyCost
|
|
2645
|
+
monthlyCost,
|
|
2646
|
+
wellArchitected,
|
|
2647
|
+
cost: costSummary,
|
|
2648
|
+
validation
|
|
2475
2649
|
};
|
|
2476
2650
|
};
|
|
2477
2651
|
var safeFetch = async (input) => {
|
|
@@ -2526,6 +2700,7 @@ var waitForJob = async ({
|
|
|
2526
2700
|
baseUrl,
|
|
2527
2701
|
token,
|
|
2528
2702
|
userId,
|
|
2703
|
+
projectId,
|
|
2529
2704
|
jobId,
|
|
2530
2705
|
pollIntervalMs,
|
|
2531
2706
|
waitTimeoutMs
|
|
@@ -2533,11 +2708,14 @@ var waitForJob = async ({
|
|
|
2533
2708
|
const startedAt = Date.now();
|
|
2534
2709
|
let lastStatus;
|
|
2535
2710
|
for (; ; ) {
|
|
2536
|
-
const query =
|
|
2711
|
+
const query = new URLSearchParams({ project_id: projectId });
|
|
2712
|
+
if (userId) {
|
|
2713
|
+
query.set("user_id", userId);
|
|
2714
|
+
}
|
|
2537
2715
|
lastStatus = await fetchCloudEvalJson({
|
|
2538
2716
|
baseUrl,
|
|
2539
2717
|
authToken: token,
|
|
2540
|
-
path: `/jobs/${encodeURIComponent(jobId)}
|
|
2718
|
+
path: `/jobs/${encodeURIComponent(jobId)}?${query.toString()}`
|
|
2541
2719
|
});
|
|
2542
2720
|
const status = String(lastStatus.status ?? "unknown");
|
|
2543
2721
|
process.stderr.write(`github sync job ${jobId}: ${status}
|
|
@@ -2579,6 +2757,7 @@ var buildAiSummaryPrompt = (data) => [
|
|
|
2579
2757
|
`Well-Architected score: ${data.gate?.overallScore ?? "unknown"}`,
|
|
2580
2758
|
`High-risk findings: ${data.gate?.highRisk ?? "unknown"}`,
|
|
2581
2759
|
`Monthly cost: ${data.gate?.monthlyCost ?? "unknown"}`,
|
|
2760
|
+
`Validation: PSRule failed ${data.gate?.validation?.psRule?.failed ?? "unknown"}, unit tests failed ${data.gate?.validation?.unitTests?.failed ?? "unknown"}`,
|
|
2582
2761
|
Array.isArray(data.gate?.failures) && data.gate.failures.length ? `Gate failures: ${data.gate.failures.join("; ")}` : "Gate failures: none reported"
|
|
2583
2762
|
].join("\n");
|
|
2584
2763
|
var generateAiSummary = async ({
|
|
@@ -2587,6 +2766,8 @@ var generateAiSummary = async ({
|
|
|
2587
2766
|
user,
|
|
2588
2767
|
project,
|
|
2589
2768
|
model,
|
|
2769
|
+
mode,
|
|
2770
|
+
agentProfileId,
|
|
2590
2771
|
data
|
|
2591
2772
|
}) => {
|
|
2592
2773
|
const core = await import("./dist-CFLR5FML.js");
|
|
@@ -2613,9 +2794,10 @@ var generateAiSummary = async ({
|
|
|
2613
2794
|
name: String(data.projectId)
|
|
2614
2795
|
},
|
|
2615
2796
|
settings: {
|
|
2616
|
-
mode
|
|
2797
|
+
mode,
|
|
2617
2798
|
...model ? { model } : {}
|
|
2618
2799
|
},
|
|
2800
|
+
...mode === "agent" ? { agentProfileId: agentProfileId ?? "architecture" } : {},
|
|
2619
2801
|
completeAfterResponse: true,
|
|
2620
2802
|
responseCompletionGraceMs: 250,
|
|
2621
2803
|
streamIdleTimeoutMs: 3e4
|
|
@@ -2627,7 +2809,8 @@ var generateAiSummary = async ({
|
|
|
2627
2809
|
}
|
|
2628
2810
|
return {
|
|
2629
2811
|
enabled: true,
|
|
2630
|
-
mode
|
|
2812
|
+
mode,
|
|
2813
|
+
...mode === "agent" ? { agentProfileId: agentProfileId ?? "architecture" } : {},
|
|
2631
2814
|
...model ? { model } : {},
|
|
2632
2815
|
markdown: markdown.trim(),
|
|
2633
2816
|
threadId
|
|
@@ -2636,7 +2819,11 @@ var generateAiSummary = async ({
|
|
|
2636
2819
|
var buildMarkdownSummary = (data) => {
|
|
2637
2820
|
const gateStatus = String(data.gate?.status ?? "unknown").toUpperCase();
|
|
2638
2821
|
const score = data.gate?.overallScore ?? "unknown";
|
|
2639
|
-
const cost = data.gate?.
|
|
2822
|
+
const cost = data.gate?.cost?.monthly;
|
|
2823
|
+
const validation = data.gate?.validation;
|
|
2824
|
+
const pillarLines = Array.isArray(data.gate?.wellArchitected?.pillars) ? data.gate.wellArchitected.pillars.map(
|
|
2825
|
+
(pillar) => ` - ${pillar.label}: ${pillar.score}${pillar.threshold !== void 0 ? ` (min ${pillar.threshold})` : ""} - ${String(pillar.status ?? "unknown").toUpperCase()}`
|
|
2826
|
+
) : [];
|
|
2640
2827
|
const lines = [
|
|
2641
2828
|
"### CloudEval review",
|
|
2642
2829
|
"",
|
|
@@ -2646,8 +2833,31 @@ var buildMarkdownSummary = (data) => {
|
|
|
2646
2833
|
`- **Commit:** \`${String(data.commitSha ?? "unknown").slice(0, 12)}\``,
|
|
2647
2834
|
`- **Gate:** ${gateStatus}`,
|
|
2648
2835
|
`- **Well-Architected score:** ${score}`,
|
|
2649
|
-
`- **
|
|
2836
|
+
`- **Cost:** ${cost?.amount ?? "unknown"} ${cost?.currency ?? ""}`.trim(),
|
|
2837
|
+
`- **Validation:** PSRule ${validation?.psRule?.failed ?? "unknown"} failed, unit tests ${validation?.unitTests?.failed ?? "unknown"} failed`
|
|
2650
2838
|
];
|
|
2839
|
+
if (Array.isArray(data.gate?.failures) && data.gate.failures.length) {
|
|
2840
|
+
lines.push("", "#### Gate failures", "", ...data.gate.failures.map((failure) => `- ${failure}`));
|
|
2841
|
+
}
|
|
2842
|
+
if (pillarLines.length) {
|
|
2843
|
+
lines.push("", "#### Well-Architected drilldown", "", ...pillarLines);
|
|
2844
|
+
}
|
|
2845
|
+
if (cost?.amount !== void 0 || cost?.threshold !== void 0) {
|
|
2846
|
+
lines.push(
|
|
2847
|
+
"",
|
|
2848
|
+
"#### Cost drilldown",
|
|
2849
|
+
"",
|
|
2850
|
+
`- Cost: ${cost?.amount ?? "unknown"} ${cost?.currency ?? ""}${cost?.threshold !== void 0 ? ` (max ${cost.threshold})` : ""}`.trim()
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
if (validation) {
|
|
2854
|
+
lines.push(
|
|
2855
|
+
"",
|
|
2856
|
+
"#### Validation drilldown",
|
|
2857
|
+
"",
|
|
2858
|
+
`- Validation: PSRule ${validation.psRule?.failed ?? "unknown"} failed, unit tests ${validation.unitTests?.failed ?? "unknown"} failed`
|
|
2859
|
+
);
|
|
2860
|
+
}
|
|
2651
2861
|
if (data.aiSummary?.markdown) {
|
|
2652
2862
|
lines.push("", "## AI summary", "", data.aiSummary.markdown);
|
|
2653
2863
|
}
|
|
@@ -2657,7 +2867,7 @@ var registerReviewCommand = (program2, deps) => {
|
|
|
2657
2867
|
const command = addAuthOptions(
|
|
2658
2868
|
program2.command("review").description("Review the current GitHub-backed project from a pushed commit"),
|
|
2659
2869
|
deps.defaultBaseUrl
|
|
2660
|
-
).option("--project <id>", "CloudEval project id. If omitted, resolve by GitHub repo metadata.").option("--repo <owner/repo>", "GitHub repository. Defaults to git origin.").option("--ref <name>", "Git branch/ref. Defaults to current branch.").option("--commit-sha <sha>", "Commit SHA to sync/review. Defaults to local HEAD.").option("--source-root <path>", "GitHub source root used by the CloudEval project.").option("--config <path>", "Path to .cloudeval/config.yaml for gate thresholds.").option("--no-wait", "Submit GitHub sync and return without waiting for analysis.").option("--wait-timeout <ms>", "Maximum time to wait for GitHub sync.", "900000").option("--poll-interval <ms>", "Polling interval while waiting for GitHub sync.", "5000").option("--no-ai-summary", "Skip the AI-written review summary.").option("--ignore-dirty", "Review HEAD even if the local working tree has uncommitted changes.", false).option("--output <dir>", "Write review.json and review.md into a directory.").option("--quiet", "Accepted for CI parity; review output stays machine-readable.", false).option("--progress <mode>", "Accepted for CI parity; review does not stream progress.", "none").option("--model <model>", "Accepted for CI parity with ask/agent modes.").option("--format <format>", "Output format: text, json, ndjson, markdown", "text");
|
|
2870
|
+
).option("--project <id>", "CloudEval project id. If omitted, resolve by GitHub repo metadata.").option("--repo <owner/repo>", "GitHub repository. Defaults to git origin.").option("--ref <name>", "Git branch/ref. Defaults to current branch.").option("--commit-sha <sha>", "Commit SHA to sync/review. Defaults to local HEAD.").option("--source-root <path>", "GitHub source root used by the CloudEval project.").option("--config <path>", "Path to .cloudeval/config.yaml for gate thresholds.").option("--no-wait", "Submit GitHub sync and return without waiting for analysis.").option("--wait-timeout <ms>", "Maximum time to wait for GitHub sync.", "900000").option("--poll-interval <ms>", "Polling interval while waiting for GitHub sync.", "5000").option("--no-ai-summary", "Skip the AI-written review summary.").option("--ai-summary-mode <mode>", "AI summary mode: ask or agent.", "ask").option("--ai-summary-profile <profile-id>", "Agent Profile id when --ai-summary-mode agent is used.", "architecture").option("--ignore-dirty", "Review HEAD even if the local working tree has uncommitted changes.", false).option("--output <dir>", "Write review.json and review.md into a directory.").option("--quiet", "Accepted for CI parity; review output stays machine-readable.", false).option("--progress <mode>", "Accepted for CI parity; review does not stream progress.", "none").option("--model <model>", "Accepted for CI parity with ask/agent modes.").option("--format <format>", "Output format: text, json, ndjson, markdown", "text");
|
|
2661
2871
|
command.action(async (options, actionCommand) => {
|
|
2662
2872
|
try {
|
|
2663
2873
|
const cwd = process.cwd();
|
|
@@ -2690,6 +2900,7 @@ var registerReviewCommand = (program2, deps) => {
|
|
|
2690
2900
|
baseUrl: context.baseUrl,
|
|
2691
2901
|
token: context.token,
|
|
2692
2902
|
userId: context.user?.id,
|
|
2903
|
+
projectId,
|
|
2693
2904
|
jobId: extractJobId2(sync),
|
|
2694
2905
|
pollIntervalMs: parsePositiveInteger(
|
|
2695
2906
|
options.pollInterval,
|
|
@@ -2720,6 +2931,11 @@ var registerReviewCommand = (program2, deps) => {
|
|
|
2720
2931
|
token: context.token,
|
|
2721
2932
|
projectId
|
|
2722
2933
|
});
|
|
2934
|
+
const preload = context.user?.id ? await safeFetch({
|
|
2935
|
+
baseUrl: context.baseUrl,
|
|
2936
|
+
authToken: context.token,
|
|
2937
|
+
path: `/reports/preload/${encodeURIComponent(projectId)}?user_id=${encodeURIComponent(context.user.id)}&include_payload=true`
|
|
2938
|
+
}) : void 0;
|
|
2723
2939
|
const data = {
|
|
2724
2940
|
projectId,
|
|
2725
2941
|
repo,
|
|
@@ -2729,18 +2945,25 @@ var registerReviewCommand = (program2, deps) => {
|
|
|
2729
2945
|
sync: finalStatus ? { ...asRecord(sync), finalStatus } : sync,
|
|
2730
2946
|
reports: {
|
|
2731
2947
|
cost,
|
|
2732
|
-
waf
|
|
2948
|
+
waf,
|
|
2949
|
+
preload
|
|
2733
2950
|
},
|
|
2734
|
-
gate: evaluateGate({ configText, waf, cost })
|
|
2951
|
+
gate: evaluateGate({ configText, waf, cost, preload, project })
|
|
2735
2952
|
};
|
|
2736
2953
|
if (options.aiSummary !== false) {
|
|
2737
2954
|
try {
|
|
2955
|
+
const aiSummaryMode = String(options.aiSummaryMode ?? "ask").toLowerCase();
|
|
2956
|
+
if (!["ask", "agent"].includes(aiSummaryMode)) {
|
|
2957
|
+
throw new Error("--ai-summary-mode must be ask or agent.");
|
|
2958
|
+
}
|
|
2738
2959
|
data.aiSummary = await generateAiSummary({
|
|
2739
2960
|
baseUrl: context.baseUrl,
|
|
2740
2961
|
token: context.token,
|
|
2741
2962
|
user: context.user,
|
|
2742
2963
|
project,
|
|
2743
2964
|
model: options.model,
|
|
2965
|
+
mode: aiSummaryMode,
|
|
2966
|
+
agentProfileId: options.aiSummaryProfile,
|
|
2744
2967
|
data
|
|
2745
2968
|
});
|
|
2746
2969
|
} catch (error) {
|
|
@@ -14625,7 +14848,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
|
|
|
14625
14848
|
const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
|
|
14626
14849
|
const [{ render }, { App }] = await Promise.all([
|
|
14627
14850
|
import("ink"),
|
|
14628
|
-
import("./App-
|
|
14851
|
+
import("./App-3VDZ4SKF.js")
|
|
14629
14852
|
]);
|
|
14630
14853
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
14631
14854
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -14683,7 +14906,7 @@ program.command("chat").description("Start an interactive chat session").option(
|
|
|
14683
14906
|
const { assertSecureBaseUrl } = await import("./dist-CFLR5FML.js");
|
|
14684
14907
|
const [{ render }, { App }] = await Promise.all([
|
|
14685
14908
|
import("ink"),
|
|
14686
|
-
import("./App-
|
|
14909
|
+
import("./App-3VDZ4SKF.js")
|
|
14687
14910
|
]);
|
|
14688
14911
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
14689
14912
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -15437,7 +15660,7 @@ Error: ${errorMsg}
|
|
|
15437
15660
|
program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
|
|
15438
15661
|
const { render } = await import("ink");
|
|
15439
15662
|
const BannerPreview = React.lazy(async () => ({
|
|
15440
|
-
default: (await import("./Banner-
|
|
15663
|
+
default: (await import("./Banner-7BP5U7P4.js")).Banner
|
|
15441
15664
|
}));
|
|
15442
15665
|
render(
|
|
15443
15666
|
/* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(BannerPreview, { disable: false }) })
|
package/package.json
CHANGED
package/sbom.spdx.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
{
|
|
15
15
|
"SPDXID": "SPDXRef-Package-CloudEval-CLI",
|
|
16
16
|
"name": "CloudEval CLI",
|
|
17
|
-
"versionInfo": "0.
|
|
17
|
+
"versionInfo": "0.26.1",
|
|
18
18
|
"downloadLocation": "https://github.com/ganakailabs/cloudeval-cli",
|
|
19
19
|
"filesAnalyzed": false,
|
|
20
20
|
"licenseConcluded": "LicenseRef-CloudEval-CLI",
|