@ganakailabs/cloudeval-cli 0.27.3 → 0.28.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/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/{App-7QUH5EKH.js → App-FRLV34U4.js} +2 -2
- package/dist/{Banner-AWNB7NZK.js → Banner-IFLO2NC6.js} +2 -2
- package/dist/{chunk-QUKE5J4T.js → chunk-L5ICTZHW.js} +1 -1
- package/dist/{chunk-O4KUS7UM.js → chunk-XDMPAWK2.js} +1 -1
- package/dist/cli.js +694 -55
- package/package.json +1 -1
- package/sbom.spdx.json +4 -4
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -235,7 +235,7 @@ This notice is not a substitute for legal review before public or enterprise dis
|
|
|
235
235
|
| safer-buffer | 2.1.2 | MIT | Nikita Skovoroda | https://github.com/ChALkeR/safer-buffer#readme |
|
|
236
236
|
| scheduler | 0.23.2 | MIT | NOASSERTION | https://reactjs.org/ |
|
|
237
237
|
| semver | 7.7.3 | ISC | GitHub Inc. | https://github.com/npm/node-semver#readme |
|
|
238
|
-
| shell-quote | 1.8.
|
|
238
|
+
| shell-quote | 1.8.4 | MIT | James Halliday | https://github.com/ljharb/shell-quote |
|
|
239
239
|
| signal-exit | 3.0.7 | ISC | Ben Coe | https://github.com/tapjs/signal-exit |
|
|
240
240
|
| skin-tone | 2.0.0 | MIT | Sindre Sorhus | https://github.com/sindresorhus/skin-tone#readme |
|
|
241
241
|
| slice-ansi | 5.0.0 | MIT | NOASSERTION | https://github.com/chalk/slice-ansi#readme |
|
|
@@ -38,10 +38,10 @@ import {
|
|
|
38
38
|
} from "./chunk-USSCB2ZU.js";
|
|
39
39
|
import {
|
|
40
40
|
Banner
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-L5ICTZHW.js";
|
|
42
42
|
import {
|
|
43
43
|
CLI_VERSION
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-XDMPAWK2.js";
|
|
45
45
|
import {
|
|
46
46
|
raisedButtonStyle,
|
|
47
47
|
terminalTheme
|
package/dist/cli.js
CHANGED
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
} from "./chunk-USSCB2ZU.js";
|
|
40
40
|
import {
|
|
41
41
|
CLI_VERSION
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-XDMPAWK2.js";
|
|
43
43
|
|
|
44
44
|
// src/runtime/prepareInk.ts
|
|
45
45
|
import fs from "fs";
|
|
@@ -609,6 +609,7 @@ var cliCommands = [
|
|
|
609
609
|
"--wait",
|
|
610
610
|
"--poll-interval",
|
|
611
611
|
"--wait-timeout",
|
|
612
|
+
"--progress",
|
|
612
613
|
"--location",
|
|
613
614
|
...outputOptions,
|
|
614
615
|
...authOptions,
|
|
@@ -2842,10 +2843,14 @@ var namedAmount = (record) => numberFrom(
|
|
|
2842
2843
|
record.amount,
|
|
2843
2844
|
record.monthly_cost,
|
|
2844
2845
|
record.monthlyCost,
|
|
2846
|
+
record.monthly_cost_estimate,
|
|
2847
|
+
record.monthlyCostEstimate,
|
|
2845
2848
|
record.cost,
|
|
2846
2849
|
record.value
|
|
2847
2850
|
);
|
|
2848
|
-
var namedLabel = (record, fallback) => String(
|
|
2851
|
+
var namedLabel = (record, fallback) => String(
|
|
2852
|
+
record.name ?? record.resource_name ?? record.resourceName ?? record.service ?? record.label ?? record.category ?? fallback
|
|
2853
|
+
);
|
|
2849
2854
|
var mermaidLabel = (value) => value.replace(/"/g, "'");
|
|
2850
2855
|
var joinReadableList = (values) => {
|
|
2851
2856
|
if (values.length <= 1) {
|
|
@@ -2869,6 +2874,7 @@ var costServiceRows = (services, currency) => (Array.isArray(services) ? service
|
|
|
2869
2874
|
...rowCurrency ? { currency: rowCurrency } : {}
|
|
2870
2875
|
};
|
|
2871
2876
|
}).filter((service) => service !== void 0);
|
|
2877
|
+
var sortedPositiveCostRows = (rows) => rows.filter((row) => row.amount > 0).sort((left, right) => right.amount - left.amount);
|
|
2872
2878
|
var reconcileCostServiceRows = (services, totalAmount, currency) => {
|
|
2873
2879
|
const total = numberFrom(totalAmount);
|
|
2874
2880
|
if (total === void 0 || services.length === 0) {
|
|
@@ -2897,7 +2903,80 @@ var reconcileCostServiceRows = (services, totalAmount, currency) => {
|
|
|
2897
2903
|
}
|
|
2898
2904
|
return services;
|
|
2899
2905
|
};
|
|
2906
|
+
var compactCostRowsForChart = (rows, totalAmount, currency, options = {}) => {
|
|
2907
|
+
const maxRows = options.maxRows ?? 5;
|
|
2908
|
+
const remainderLabel = options.remainderLabel ?? "Unallocated";
|
|
2909
|
+
const total = numberFrom(totalAmount);
|
|
2910
|
+
const positiveRows = sortedPositiveCostRows(rows);
|
|
2911
|
+
const selected = positiveRows.slice(0, maxRows);
|
|
2912
|
+
const collapsed = positiveRows.slice(maxRows);
|
|
2913
|
+
const collapsedSum = Number(
|
|
2914
|
+
collapsed.reduce((sum, row) => sum + row.amount, 0).toFixed(3)
|
|
2915
|
+
);
|
|
2916
|
+
const result = [...selected];
|
|
2917
|
+
if (collapsedSum > 1e-3) {
|
|
2918
|
+
result.push({
|
|
2919
|
+
name: "Other",
|
|
2920
|
+
amount: collapsedSum,
|
|
2921
|
+
...currency ? { currency } : {}
|
|
2922
|
+
});
|
|
2923
|
+
}
|
|
2924
|
+
const represented = result.reduce((sum, row) => sum + row.amount, 0);
|
|
2925
|
+
if (total !== void 0) {
|
|
2926
|
+
const delta = Number((total - represented).toFixed(3));
|
|
2927
|
+
if (delta > 1e-3) {
|
|
2928
|
+
result.push({
|
|
2929
|
+
name: remainderLabel,
|
|
2930
|
+
amount: delta,
|
|
2931
|
+
...currency ? { currency } : {}
|
|
2932
|
+
});
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
return result;
|
|
2936
|
+
};
|
|
2900
2937
|
var markdownLink = (label, url) => url ? `[${label}](${url})` : label;
|
|
2938
|
+
var openInCloudEvalLines = (links) => {
|
|
2939
|
+
if (!links) {
|
|
2940
|
+
return [];
|
|
2941
|
+
}
|
|
2942
|
+
const reports = asRecord(links.reports);
|
|
2943
|
+
const downloads = asRecord(links.downloads);
|
|
2944
|
+
const entries = [
|
|
2945
|
+
["Project preview", links.project],
|
|
2946
|
+
["Architecture report", reports.architecture],
|
|
2947
|
+
["Cost report", reports.cost],
|
|
2948
|
+
["Validation details", reports.validation],
|
|
2949
|
+
["Download PDF", downloads.pdf],
|
|
2950
|
+
["Workflow run", links.workflowRun],
|
|
2951
|
+
["Download review artifacts", downloads.reviewArtifacts]
|
|
2952
|
+
].filter((entry) => typeof entry[1] === "string" && entry[1].length > 0);
|
|
2953
|
+
return entries.map(([label, url]) => `- ${markdownLink(label, url)}`);
|
|
2954
|
+
};
|
|
2955
|
+
var monthlyCostImpactLines = (currentAmount, savingsAmount, currency) => {
|
|
2956
|
+
const current = numberFrom(currentAmount);
|
|
2957
|
+
const savings = numberFrom(savingsAmount);
|
|
2958
|
+
if (current === void 0 || current <= 0 || savings === void 0 || savings <= 0) {
|
|
2959
|
+
return [];
|
|
2960
|
+
}
|
|
2961
|
+
const optimized = Math.max(current - savings, 0);
|
|
2962
|
+
const savingsPercent = current > 0 ? savings / current * 100 : void 0;
|
|
2963
|
+
const yMax = Math.max(current, optimized, savings);
|
|
2964
|
+
return [
|
|
2965
|
+
"| Metric | Amount |",
|
|
2966
|
+
"| --- | ---: |",
|
|
2967
|
+
`| Current monthly cost | **${formatMonthlyMoney(current, currency)}** |`,
|
|
2968
|
+
`| Potential savings | **${formatMonthlyMoney(savings, currency)}${savingsPercent !== void 0 ? ` (${trimNumber(savingsPercent, 1)}%)` : ""}** |`,
|
|
2969
|
+
`| Optimized monthly cost | **${formatMonthlyMoney(optimized, currency)}** |`,
|
|
2970
|
+
"",
|
|
2971
|
+
"```mermaid",
|
|
2972
|
+
"xychart-beta",
|
|
2973
|
+
' title "Monthly cost impact"',
|
|
2974
|
+
' x-axis ["Current", "Optimized"]',
|
|
2975
|
+
` y-axis "${currency ? `${currency}/mo` : "monthly cost"}" 0 --> ${trimNumber(yMax, 3)}`,
|
|
2976
|
+
` bar [${trimNumber(current, 3)}, ${trimNumber(optimized, 3)}]`,
|
|
2977
|
+
"```"
|
|
2978
|
+
];
|
|
2979
|
+
};
|
|
2901
2980
|
var extractPillars = (waf) => {
|
|
2902
2981
|
const fromArray = waf?.parsed?.score?.pillars;
|
|
2903
2982
|
if (Array.isArray(fromArray)) {
|
|
@@ -3125,6 +3204,16 @@ var evaluateGate = ({
|
|
|
3125
3204
|
currency
|
|
3126
3205
|
},
|
|
3127
3206
|
topServices: (Array.isArray(cost?.parsed?.serviceGroups) ? cost.parsed.serviceGroups : entriesAsNamedRecords(cost?.report?.processed?.cost_by_service_family)).slice(0, 5),
|
|
3207
|
+
topResources: (firstArray(
|
|
3208
|
+
cost?.report?.raw?.resource_estimates,
|
|
3209
|
+
cost?.report?.raw?.resourceEstimates,
|
|
3210
|
+
cost?.raw?.resource_estimates,
|
|
3211
|
+
cost?.raw?.resourceEstimates,
|
|
3212
|
+
cost?.parsed?.resourceEstimates,
|
|
3213
|
+
cost?.parsed?.resource_estimates,
|
|
3214
|
+
preload?.latest_payloads?.cost?.raw?.resource_estimates,
|
|
3215
|
+
preload?.latest_payloads?.cost?.raw?.resourceEstimates
|
|
3216
|
+
) ?? []).slice(0, 20),
|
|
3128
3217
|
recommendations: (Array.isArray(cost?.parsed?.recommendations) ? cost.parsed.recommendations : Array.isArray(cost?.report?.processed?.optimization_recommendations) ? cost.report.processed.optimization_recommendations : []).slice(0, 5)
|
|
3129
3218
|
};
|
|
3130
3219
|
if (!gateConfig) {
|
|
@@ -3481,7 +3570,14 @@ var buildMarkdownSummary = (data) => {
|
|
|
3481
3570
|
cost?.amount,
|
|
3482
3571
|
cost?.currency
|
|
3483
3572
|
);
|
|
3484
|
-
const
|
|
3573
|
+
const resourceCostRows = compactCostRowsForChart(
|
|
3574
|
+
costServiceRows(data.gate?.cost?.topResources, cost?.currency),
|
|
3575
|
+
cost?.amount,
|
|
3576
|
+
cost?.currency,
|
|
3577
|
+
{ maxRows: 5, remainderLabel: "Unallocated" }
|
|
3578
|
+
);
|
|
3579
|
+
const positiveResourceCosts = resourceCostRows.filter((resource) => resource.amount > 0);
|
|
3580
|
+
const openLinks = openInCloudEvalLines(data.links);
|
|
3485
3581
|
const architectureLines = architectureSignalLines({
|
|
3486
3582
|
architecture,
|
|
3487
3583
|
costServices,
|
|
@@ -3505,6 +3601,9 @@ var buildMarkdownSummary = (data) => {
|
|
|
3505
3601
|
`- **Ref**: \`${ref}\``,
|
|
3506
3602
|
`- **Commit**: \`${commit}\``
|
|
3507
3603
|
];
|
|
3604
|
+
if (openLinks.length) {
|
|
3605
|
+
lines.push("", "#### Open in CloudEval", "", ...openLinks);
|
|
3606
|
+
}
|
|
3508
3607
|
if (data.aiSummary?.markdown) {
|
|
3509
3608
|
lines.push("", "#### AI summary", "", data.aiSummary.markdown);
|
|
3510
3609
|
}
|
|
@@ -3528,11 +3627,29 @@ var buildMarkdownSummary = (data) => {
|
|
|
3528
3627
|
}
|
|
3529
3628
|
if (cost?.amount !== void 0 || cost?.threshold !== void 0) {
|
|
3530
3629
|
const costLines = [];
|
|
3531
|
-
|
|
3630
|
+
const impactLines = monthlyCostImpactLines(
|
|
3631
|
+
cost?.amount,
|
|
3632
|
+
data.gate?.cost?.estimatedSavings?.amount,
|
|
3633
|
+
cost?.currency ?? data.gate?.cost?.estimatedSavings?.currency
|
|
3634
|
+
);
|
|
3635
|
+
if (impactLines.length) {
|
|
3636
|
+
costLines.push(...impactLines);
|
|
3637
|
+
} else if (data.gate?.cost?.estimatedSavings?.amount !== void 0) {
|
|
3532
3638
|
costLines.push(
|
|
3533
3639
|
`- Estimated savings: **${formatMonthlyMoney(data.gate.cost.estimatedSavings.amount, data.gate.cost.estimatedSavings.currency)}**`
|
|
3534
3640
|
);
|
|
3535
3641
|
}
|
|
3642
|
+
if (positiveResourceCosts.length) {
|
|
3643
|
+
costLines.push(
|
|
3644
|
+
"",
|
|
3645
|
+
"```mermaid",
|
|
3646
|
+
"pie title Monthly cost by resource",
|
|
3647
|
+
...positiveResourceCosts.map(
|
|
3648
|
+
(resource) => ` "${mermaidLabel(resource.name)}" : ${trimNumber(resource.amount, 3)}`
|
|
3649
|
+
),
|
|
3650
|
+
"```"
|
|
3651
|
+
);
|
|
3652
|
+
}
|
|
3536
3653
|
if (costServices.length) {
|
|
3537
3654
|
costLines.push(
|
|
3538
3655
|
"",
|
|
@@ -3543,17 +3660,6 @@ var buildMarkdownSummary = (data) => {
|
|
|
3543
3660
|
)
|
|
3544
3661
|
);
|
|
3545
3662
|
}
|
|
3546
|
-
if (positiveCostServices.length) {
|
|
3547
|
-
costLines.push(
|
|
3548
|
-
"",
|
|
3549
|
-
"```mermaid",
|
|
3550
|
-
"pie title Monthly cost by service",
|
|
3551
|
-
...positiveCostServices.map(
|
|
3552
|
-
(service) => ` "${mermaidLabel(service.name)}" : ${trimNumber(service.amount, 3)}`
|
|
3553
|
-
),
|
|
3554
|
-
"```"
|
|
3555
|
-
);
|
|
3556
|
-
}
|
|
3557
3663
|
if (costLines.length) {
|
|
3558
3664
|
lines.push(
|
|
3559
3665
|
"",
|
|
@@ -3673,18 +3779,58 @@ var registerReviewCommand = (program2, deps) => {
|
|
|
3673
3779
|
}),
|
|
3674
3780
|
readConfigText(cwd, options)
|
|
3675
3781
|
]);
|
|
3782
|
+
const frontendBaseUrl = resolveFrontendBaseUrl({ apiBaseUrl: context.baseUrl });
|
|
3783
|
+
const projectUrl = buildFrontendUrl({
|
|
3784
|
+
baseUrl: frontendBaseUrl,
|
|
3785
|
+
target: "project",
|
|
3786
|
+
projectId,
|
|
3787
|
+
view: "preview",
|
|
3788
|
+
layout: "architecture"
|
|
3789
|
+
});
|
|
3790
|
+
const workflowRunUrl = githubWorkflowRunUrl();
|
|
3676
3791
|
const data = {
|
|
3677
3792
|
projectId,
|
|
3678
3793
|
project: {
|
|
3679
3794
|
id: projectId,
|
|
3680
3795
|
name: String(project?.name ?? projectId),
|
|
3681
|
-
url:
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3796
|
+
url: projectUrl
|
|
3797
|
+
},
|
|
3798
|
+
links: {
|
|
3799
|
+
project: projectUrl,
|
|
3800
|
+
reports: {
|
|
3801
|
+
architecture: buildFrontendUrl({
|
|
3802
|
+
baseUrl: frontendBaseUrl,
|
|
3803
|
+
target: "reports",
|
|
3804
|
+
projectId,
|
|
3805
|
+
tab: "architecture",
|
|
3806
|
+
reportType: "architecture"
|
|
3807
|
+
}),
|
|
3808
|
+
cost: buildFrontendUrl({
|
|
3809
|
+
baseUrl: frontendBaseUrl,
|
|
3810
|
+
target: "reports",
|
|
3811
|
+
projectId,
|
|
3812
|
+
tab: "cost",
|
|
3813
|
+
reportType: "cost"
|
|
3814
|
+
}),
|
|
3815
|
+
validation: buildFrontendUrl({
|
|
3816
|
+
baseUrl: frontendBaseUrl,
|
|
3817
|
+
target: "reports",
|
|
3818
|
+
projectId,
|
|
3819
|
+
tab: "validation",
|
|
3820
|
+
reportType: "unit_tests"
|
|
3821
|
+
})
|
|
3822
|
+
},
|
|
3823
|
+
downloads: {
|
|
3824
|
+
pdf: buildFrontendUrl({
|
|
3825
|
+
baseUrl: frontendBaseUrl,
|
|
3826
|
+
target: "reports",
|
|
3827
|
+
projectId,
|
|
3828
|
+
downloadPdf: true,
|
|
3829
|
+
pdfVerbosity: "full"
|
|
3830
|
+
}),
|
|
3831
|
+
...workflowRunUrl ? { reviewArtifacts: workflowRunUrl } : {}
|
|
3832
|
+
},
|
|
3833
|
+
...workflowRunUrl ? { workflowRun: workflowRunUrl } : {}
|
|
3688
3834
|
},
|
|
3689
3835
|
repo,
|
|
3690
3836
|
ref,
|
|
@@ -3738,12 +3884,13 @@ var registerReviewCommand = (program2, deps) => {
|
|
|
3738
3884
|
filesWritten
|
|
3739
3885
|
});
|
|
3740
3886
|
if (data.gate.status === "fail") {
|
|
3741
|
-
process.
|
|
3887
|
+
process.exitCode = 1;
|
|
3888
|
+
return;
|
|
3742
3889
|
}
|
|
3743
|
-
process.
|
|
3890
|
+
process.exitCode = 0;
|
|
3744
3891
|
} catch (error) {
|
|
3745
3892
|
console.error(error?.message ?? "Review failed");
|
|
3746
|
-
process.
|
|
3893
|
+
process.exitCode = 1;
|
|
3747
3894
|
}
|
|
3748
3895
|
});
|
|
3749
3896
|
};
|
|
@@ -7692,6 +7839,51 @@ var isSuccessfulJobStatus = (value) => ["completed", "succeeded"].includes(norma
|
|
|
7692
7839
|
var compactRecord = (value) => Object.fromEntries(
|
|
7693
7840
|
Object.entries(value).filter(([, item]) => item !== void 0)
|
|
7694
7841
|
);
|
|
7842
|
+
var basenameFromPath = (value) => {
|
|
7843
|
+
const trimmed = value?.trim();
|
|
7844
|
+
if (!trimmed) {
|
|
7845
|
+
return void 0;
|
|
7846
|
+
}
|
|
7847
|
+
const normalized = trimmed.replace(/\\/g, "/");
|
|
7848
|
+
return normalized.split("/").filter(Boolean).pop() ?? trimmed;
|
|
7849
|
+
};
|
|
7850
|
+
var isBackendTempLocation = (value) => {
|
|
7851
|
+
const trimmed = value?.trim();
|
|
7852
|
+
if (!trimmed) {
|
|
7853
|
+
return false;
|
|
7854
|
+
}
|
|
7855
|
+
const normalized = trimmed.replace(/\\/g, "/").toLowerCase();
|
|
7856
|
+
const basename = basenameFromPath(trimmed) ?? "";
|
|
7857
|
+
return normalized.startsWith("/tmp/") || normalized.startsWith("/private/tmp/") || normalized.includes("/var/folders/") || normalized.includes("/appdata/local/temp/") || normalized.includes("/windows/temp/") || /^tmp[\w.-]*\.json$/i.test(basename);
|
|
7858
|
+
};
|
|
7859
|
+
var displayTemplateLocation = (rawLocation, context) => {
|
|
7860
|
+
const trimmed = rawLocation?.trim();
|
|
7861
|
+
if (!trimmed) {
|
|
7862
|
+
return void 0;
|
|
7863
|
+
}
|
|
7864
|
+
if (isBackendTempLocation(trimmed)) {
|
|
7865
|
+
return basenameFromPath(context?.templatePath);
|
|
7866
|
+
}
|
|
7867
|
+
return trimmed;
|
|
7868
|
+
};
|
|
7869
|
+
var sanitizeTemplateLocationFields = (value, context) => {
|
|
7870
|
+
if (Array.isArray(value)) {
|
|
7871
|
+
return value.map((item) => sanitizeTemplateLocationFields(item, context));
|
|
7872
|
+
}
|
|
7873
|
+
const record = recordValue(value);
|
|
7874
|
+
if (!record) {
|
|
7875
|
+
return value;
|
|
7876
|
+
}
|
|
7877
|
+
return Object.fromEntries(
|
|
7878
|
+
Object.entries(record).flatMap(([key, item]) => {
|
|
7879
|
+
if (["file_path", "filePath", "location"].includes(key) && typeof item === "string") {
|
|
7880
|
+
const publicLocation = displayTemplateLocation(item, context);
|
|
7881
|
+
return publicLocation === void 0 ? [] : [[key, publicLocation]];
|
|
7882
|
+
}
|
|
7883
|
+
return [[key, sanitizeTemplateLocationFields(item, context)]];
|
|
7884
|
+
})
|
|
7885
|
+
);
|
|
7886
|
+
};
|
|
7695
7887
|
var arrayValue = (value) => Array.isArray(value) ? value : [];
|
|
7696
7888
|
var firstString = (record, fields) => {
|
|
7697
7889
|
for (const field of fields) {
|
|
@@ -7702,6 +7894,283 @@ var firstString = (record, fields) => {
|
|
|
7702
7894
|
}
|
|
7703
7895
|
return void 0;
|
|
7704
7896
|
};
|
|
7897
|
+
var publicValidationText = (value) => value?.replace(/\bPSRule(?:\s+for\s+Azure)?\b/gi, "validation rules").replace(/\bARM\s+TTK\b/gi, "template validation");
|
|
7898
|
+
var sanitizeTemplateOperationText = (value) => {
|
|
7899
|
+
if (typeof value === "string") {
|
|
7900
|
+
return publicValidationText(value) ?? value;
|
|
7901
|
+
}
|
|
7902
|
+
if (Array.isArray(value)) {
|
|
7903
|
+
return value.map(sanitizeTemplateOperationText);
|
|
7904
|
+
}
|
|
7905
|
+
const record = recordValue(value);
|
|
7906
|
+
if (record) {
|
|
7907
|
+
return Object.fromEntries(
|
|
7908
|
+
Object.entries(record).map(([key, item]) => [
|
|
7909
|
+
key,
|
|
7910
|
+
sanitizeTemplateOperationText(item)
|
|
7911
|
+
])
|
|
7912
|
+
);
|
|
7913
|
+
}
|
|
7914
|
+
return value;
|
|
7915
|
+
};
|
|
7916
|
+
var firstNumber = (record, fields) => {
|
|
7917
|
+
for (const field of fields) {
|
|
7918
|
+
const value = record?.[field];
|
|
7919
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
7920
|
+
return value;
|
|
7921
|
+
}
|
|
7922
|
+
if (typeof value === "string" && value.trim()) {
|
|
7923
|
+
const parsed = Number(value);
|
|
7924
|
+
if (Number.isFinite(parsed)) {
|
|
7925
|
+
return parsed;
|
|
7926
|
+
}
|
|
7927
|
+
}
|
|
7928
|
+
}
|
|
7929
|
+
return void 0;
|
|
7930
|
+
};
|
|
7931
|
+
var firstArray2 = (record, fields) => {
|
|
7932
|
+
for (const field of fields) {
|
|
7933
|
+
const value = record?.[field];
|
|
7934
|
+
if (Array.isArray(value)) {
|
|
7935
|
+
return value;
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
return [];
|
|
7939
|
+
};
|
|
7940
|
+
var normalizeProgressItem = (value, context) => {
|
|
7941
|
+
const record = recordValue(value) ?? {};
|
|
7942
|
+
const passed = typeof record.passed === "boolean" ? record.passed : void 0;
|
|
7943
|
+
return compactRecord({
|
|
7944
|
+
name: firstString(record, [
|
|
7945
|
+
"name",
|
|
7946
|
+
"test_name",
|
|
7947
|
+
"testName",
|
|
7948
|
+
"rule_name",
|
|
7949
|
+
"ruleName",
|
|
7950
|
+
"item",
|
|
7951
|
+
"current_item",
|
|
7952
|
+
"display_name",
|
|
7953
|
+
"displayName"
|
|
7954
|
+
]),
|
|
7955
|
+
status: firstString(record, ["status", "outcome", "result"]) ?? (passed === void 0 ? void 0 : passed ? "Pass" : "Fail"),
|
|
7956
|
+
passed,
|
|
7957
|
+
category: firstString(record, ["category", "test_category", "testCategory"]),
|
|
7958
|
+
severity: firstString(record, ["severity", "level"]),
|
|
7959
|
+
message: publicValidationText(firstString(record, ["message", "description"])),
|
|
7960
|
+
recommendation: publicValidationText(
|
|
7961
|
+
firstString(record, ["recommendation", "remediation"])
|
|
7962
|
+
),
|
|
7963
|
+
location: displayTemplateLocation(
|
|
7964
|
+
firstString(record, ["file_path", "filePath", "location"]),
|
|
7965
|
+
context
|
|
7966
|
+
),
|
|
7967
|
+
durationMs: firstNumber(record, ["duration_ms", "durationMs"]),
|
|
7968
|
+
documentationUrl: firstString(record, [
|
|
7969
|
+
"documentation_url",
|
|
7970
|
+
"documentationUrl",
|
|
7971
|
+
"help_url",
|
|
7972
|
+
"helpUrl"
|
|
7973
|
+
])
|
|
7974
|
+
});
|
|
7975
|
+
};
|
|
7976
|
+
var operationFromResult = (result) => {
|
|
7977
|
+
const record = recordValue(result);
|
|
7978
|
+
if (!record) {
|
|
7979
|
+
return void 0;
|
|
7980
|
+
}
|
|
7981
|
+
if ("test_results" in record || "total_tests" in record) {
|
|
7982
|
+
return "template_test";
|
|
7983
|
+
}
|
|
7984
|
+
if ("filtered_results" in record || "results" in record || "summary" in record) {
|
|
7985
|
+
return "template_validate";
|
|
7986
|
+
}
|
|
7987
|
+
return void 0;
|
|
7988
|
+
};
|
|
7989
|
+
var progressEventFromStatus = (input) => {
|
|
7990
|
+
const statusRecord = recordValue(input.status);
|
|
7991
|
+
return compactRecord({
|
|
7992
|
+
phase: input.phase,
|
|
7993
|
+
jobId: input.jobId,
|
|
7994
|
+
operation: firstString(statusRecord, ["operation", "operation_type", "operationType"]),
|
|
7995
|
+
status: firstString(statusRecord, ["status", "state"]),
|
|
7996
|
+
progress: firstNumber(statusRecord, [
|
|
7997
|
+
"progress",
|
|
7998
|
+
"progress_percent",
|
|
7999
|
+
"progressPercent",
|
|
8000
|
+
"percentage",
|
|
8001
|
+
"percent"
|
|
8002
|
+
]),
|
|
8003
|
+
completed: firstNumber(statusRecord, [
|
|
8004
|
+
"completed_items",
|
|
8005
|
+
"completedItems",
|
|
8006
|
+
"completed_tests",
|
|
8007
|
+
"completedTests",
|
|
8008
|
+
"completed_rules",
|
|
8009
|
+
"completedRules"
|
|
8010
|
+
]),
|
|
8011
|
+
total: firstNumber(statusRecord, [
|
|
8012
|
+
"total_items",
|
|
8013
|
+
"totalItems",
|
|
8014
|
+
"total_tests",
|
|
8015
|
+
"totalTests",
|
|
8016
|
+
"total_rules",
|
|
8017
|
+
"totalRules"
|
|
8018
|
+
]),
|
|
8019
|
+
currentItem: firstString(statusRecord, [
|
|
8020
|
+
"current_item",
|
|
8021
|
+
"currentItem",
|
|
8022
|
+
"current_test",
|
|
8023
|
+
"currentTest",
|
|
8024
|
+
"current_rule",
|
|
8025
|
+
"currentRule"
|
|
8026
|
+
]),
|
|
8027
|
+
message: publicValidationText(
|
|
8028
|
+
firstString(statusRecord, ["message", "detail", "description"])
|
|
8029
|
+
),
|
|
8030
|
+
items: firstArray2(statusRecord, [
|
|
8031
|
+
"recent_events",
|
|
8032
|
+
"recentEvents",
|
|
8033
|
+
"events",
|
|
8034
|
+
"progress_events",
|
|
8035
|
+
"progressEvents"
|
|
8036
|
+
]).map((item) => normalizeProgressItem(item, input.context)).filter((item) => item.name || item.message),
|
|
8037
|
+
elapsedMs: input.elapsedMs
|
|
8038
|
+
});
|
|
8039
|
+
};
|
|
8040
|
+
var failedStatus = (status) => ["fail", "failed", "error"].includes(String(status ?? "").trim().toLowerCase());
|
|
8041
|
+
var passedStatus = (status) => ["pass", "passed", "success", "succeeded"].includes(
|
|
8042
|
+
String(status ?? "").trim().toLowerCase()
|
|
8043
|
+
);
|
|
8044
|
+
var progressItemsFromDetails = (details, nameFields, context) => {
|
|
8045
|
+
const targetLocation = (target) => {
|
|
8046
|
+
const name = firstString(target, ["name", "id"]);
|
|
8047
|
+
const type = firstString(target, ["type"]);
|
|
8048
|
+
if (name && type) {
|
|
8049
|
+
return `${name} (${type})`;
|
|
8050
|
+
}
|
|
8051
|
+
return name ?? type;
|
|
8052
|
+
};
|
|
8053
|
+
return details.filter((detail) => failedStatus(firstString(detail, ["status", "outcome", "result"]))).map((detail) => {
|
|
8054
|
+
const evidence = recordValue(detail.evidence);
|
|
8055
|
+
return compactRecord({
|
|
8056
|
+
name: firstString(detail, nameFields),
|
|
8057
|
+
status: firstString(detail, ["status", "outcome", "result"]),
|
|
8058
|
+
category: firstString(detail, ["category", "test_category", "testCategory"]),
|
|
8059
|
+
severity: firstString(detail, ["severity", "level"]),
|
|
8060
|
+
message: publicValidationText(
|
|
8061
|
+
firstString(detail, ["message", "description"]) ?? firstString(evidence, ["description", "synopsis"])
|
|
8062
|
+
),
|
|
8063
|
+
recommendation: publicValidationText(
|
|
8064
|
+
firstString(detail, ["recommendation", "remediation"]) ?? firstString(evidence, ["recommendation", "remediation"])
|
|
8065
|
+
),
|
|
8066
|
+
location: displayTemplateLocation(
|
|
8067
|
+
firstString(detail, ["file_path", "filePath", "location"]),
|
|
8068
|
+
context
|
|
8069
|
+
) ?? targetLocation(recordValue(detail.target)),
|
|
8070
|
+
durationMs: firstNumber(detail, ["duration_ms", "durationMs"]),
|
|
8071
|
+
documentationUrl: firstString(detail, [
|
|
8072
|
+
"documentation_url",
|
|
8073
|
+
"documentationUrl",
|
|
8074
|
+
"help_url",
|
|
8075
|
+
"helpUrl"
|
|
8076
|
+
]) ?? firstString(evidence, [
|
|
8077
|
+
"documentation_url",
|
|
8078
|
+
"documentationUrl",
|
|
8079
|
+
"help_url",
|
|
8080
|
+
"helpUrl"
|
|
8081
|
+
])
|
|
8082
|
+
});
|
|
8083
|
+
}).filter((item) => item.name || item.message);
|
|
8084
|
+
};
|
|
8085
|
+
var resultProgressSummary = (result, operation, context) => {
|
|
8086
|
+
const resultRecord = recordValue(result) ?? {};
|
|
8087
|
+
const summary = recordValue(resultRecord.summary) ?? {};
|
|
8088
|
+
const detectedOperation = operationFromResult(result);
|
|
8089
|
+
const resolvedOperation = detectedOperation ?? operation;
|
|
8090
|
+
const operationText = String(resolvedOperation ?? "").toLowerCase();
|
|
8091
|
+
if (detectedOperation === "template_test" || operationText.includes("test")) {
|
|
8092
|
+
const details2 = normalizeTemplateTestDetails(result, context);
|
|
8093
|
+
const passed2 = firstNumber(resultRecord, ["passed_tests", "passedTests"]) ?? details2.filter((detail) => detail.passed === true || passedStatus(firstString(detail, ["status"]))).length;
|
|
8094
|
+
const failed2 = firstNumber(resultRecord, ["failed_tests", "failedTests"]) ?? details2.filter((detail) => detail.passed === false || failedStatus(firstString(detail, ["status"]))).length;
|
|
8095
|
+
const skipped = firstNumber(resultRecord, ["skipped_tests", "skippedTests"]) ?? 0;
|
|
8096
|
+
return {
|
|
8097
|
+
operation: resolvedOperation,
|
|
8098
|
+
progress: 100,
|
|
8099
|
+
message: `Template tests complete: ${passed2} passed, ${failed2} failed, ${skipped} skipped`,
|
|
8100
|
+
items: progressItemsFromDetails(details2, ["test_name", "testName", "name"], context)
|
|
8101
|
+
};
|
|
8102
|
+
}
|
|
8103
|
+
const details = normalizeTemplateValidationDetails(result);
|
|
8104
|
+
const passed = firstNumber(summary, ["passed_rules", "passedRules"]) ?? details.filter((detail) => passedStatus(firstString(detail, ["status"]))).length;
|
|
8105
|
+
const failed = firstNumber(summary, ["failed_rules", "failedRules"]) ?? details.filter((detail) => failedStatus(firstString(detail, ["status"]))).length;
|
|
8106
|
+
const total = firstNumber(summary, ["total_rules", "totalRules"]) ?? details.length;
|
|
8107
|
+
return {
|
|
8108
|
+
operation: resolvedOperation ?? "template_validate",
|
|
8109
|
+
progress: 100,
|
|
8110
|
+
message: `Validation complete: ${passed} passed, ${failed} failed across ${total} checks`,
|
|
8111
|
+
items: progressItemsFromDetails(details, ["rule_id", "rule_name", "ruleName", "name"], context)
|
|
8112
|
+
};
|
|
8113
|
+
};
|
|
8114
|
+
var formatTemplateProgressEvent = (event, command) => {
|
|
8115
|
+
const appendItemDetails = (lines2, item) => {
|
|
8116
|
+
const metadata = [
|
|
8117
|
+
item.category ? `category: ${item.category}` : void 0,
|
|
8118
|
+
item.severity ? `severity: ${item.severity}` : void 0,
|
|
8119
|
+
item.location ? `location: ${item.location}` : void 0,
|
|
8120
|
+
typeof item.durationMs === "number" ? `duration: ${item.durationMs}ms` : void 0
|
|
8121
|
+
].filter(Boolean);
|
|
8122
|
+
if (metadata.length) {
|
|
8123
|
+
lines2.push(` ${metadata.join(" | ")}`);
|
|
8124
|
+
}
|
|
8125
|
+
if (item.name && item.message) {
|
|
8126
|
+
lines2.push(` message: ${item.message}`);
|
|
8127
|
+
}
|
|
8128
|
+
if (item.recommendation) {
|
|
8129
|
+
lines2.push(` recommendation: ${item.recommendation}`);
|
|
8130
|
+
}
|
|
8131
|
+
if (item.documentationUrl) {
|
|
8132
|
+
lines2.push(` docs: ${item.documentationUrl}`);
|
|
8133
|
+
}
|
|
8134
|
+
};
|
|
8135
|
+
if (event.phase === "submitted") {
|
|
8136
|
+
return [`${command} job ${event.jobId} submitted`];
|
|
8137
|
+
}
|
|
8138
|
+
if (event.phase === "result") {
|
|
8139
|
+
const lines2 = event.message ? [event.message] : [`${command} job ${event.jobId} complete`];
|
|
8140
|
+
for (const item of event.items ?? []) {
|
|
8141
|
+
const status2 = item.status ? `${item.status} ` : "";
|
|
8142
|
+
lines2.push(` - ${status2}${item.name ?? item.message ?? "item"}`);
|
|
8143
|
+
appendItemDetails(lines2, item);
|
|
8144
|
+
}
|
|
8145
|
+
return lines2;
|
|
8146
|
+
}
|
|
8147
|
+
const status = event.status ? event.status.toUpperCase() : "RUNNING";
|
|
8148
|
+
const progress = typeof event.progress === "number" ? ` ${Math.round(event.progress)}%` : "";
|
|
8149
|
+
const completed = typeof event.completed === "number" && typeof event.total === "number" ? ` (${event.completed}/${event.total})` : "";
|
|
8150
|
+
const current = event.currentItem ? ` current: ${event.currentItem}` : "";
|
|
8151
|
+
const message = event.message ? ` ${event.message}` : "";
|
|
8152
|
+
const lines = [
|
|
8153
|
+
`${command} job ${event.jobId}: ${status}${progress}${completed}${current}${message}`
|
|
8154
|
+
];
|
|
8155
|
+
for (const item of event.items ?? []) {
|
|
8156
|
+
const statusText = item.status ? `${item.status} ` : "";
|
|
8157
|
+
const passedText = item.status || item.passed === void 0 ? "" : item.passed ? "Pass " : "Fail ";
|
|
8158
|
+
lines.push(` - ${statusText}${passedText}${item.name ?? item.message ?? "item"}`);
|
|
8159
|
+
appendItemDetails(lines, item);
|
|
8160
|
+
}
|
|
8161
|
+
return lines;
|
|
8162
|
+
};
|
|
8163
|
+
var templateProgressEventKey = (event) => JSON.stringify({
|
|
8164
|
+
phase: event.phase,
|
|
8165
|
+
operation: event.operation,
|
|
8166
|
+
status: event.status,
|
|
8167
|
+
progress: event.progress,
|
|
8168
|
+
completed: event.completed,
|
|
8169
|
+
total: event.total,
|
|
8170
|
+
currentItem: event.currentItem,
|
|
8171
|
+
message: event.message,
|
|
8172
|
+
items: event.items
|
|
8173
|
+
});
|
|
7705
8174
|
var unwrapTemplateOperationResult = (value) => {
|
|
7706
8175
|
const record = recordValue(value);
|
|
7707
8176
|
if (!record) {
|
|
@@ -7713,9 +8182,9 @@ var unwrapTemplateOperationResult = (value) => {
|
|
|
7713
8182
|
}
|
|
7714
8183
|
const nestedResult = recordValue(result.result);
|
|
7715
8184
|
if (nestedResult) {
|
|
7716
|
-
return nestedResult;
|
|
8185
|
+
return sanitizeTemplateOperationText(nestedResult);
|
|
7717
8186
|
}
|
|
7718
|
-
return result;
|
|
8187
|
+
return sanitizeTemplateOperationText(result);
|
|
7719
8188
|
};
|
|
7720
8189
|
var resolvedOperationResult = (value) => recordValue(unwrapTemplateOperationResult(value));
|
|
7721
8190
|
var validationResults = (result) => {
|
|
@@ -7747,9 +8216,15 @@ var normalizeTemplateValidationDetails = (result) => validationResults(recordVal
|
|
|
7747
8216
|
pillar: firstString(row, ["pillar"]),
|
|
7748
8217
|
...Object.keys(target).length ? { target } : {},
|
|
7749
8218
|
evidence: compactRecord({
|
|
7750
|
-
description:
|
|
7751
|
-
|
|
7752
|
-
|
|
8219
|
+
description: publicValidationText(
|
|
8220
|
+
firstString(row, ["description", "message"]) ?? firstString(info, ["description"])
|
|
8221
|
+
),
|
|
8222
|
+
synopsis: publicValidationText(
|
|
8223
|
+
firstString(row, ["synopsis"]) ?? firstString(info, ["synopsis"])
|
|
8224
|
+
),
|
|
8225
|
+
recommendation: publicValidationText(
|
|
8226
|
+
firstString(row, ["recommendation", "remediation"])
|
|
8227
|
+
),
|
|
7753
8228
|
documentation_url: firstString(row, [
|
|
7754
8229
|
"documentation_url",
|
|
7755
8230
|
"documentationUrl",
|
|
@@ -7776,7 +8251,7 @@ var withTemplateValidationDetails = (value) => {
|
|
|
7776
8251
|
details: normalizeTemplateValidationDetails(result)
|
|
7777
8252
|
});
|
|
7778
8253
|
};
|
|
7779
|
-
var normalizeTemplateTestDetails = (result) => arrayValue(recordValue(result)?.test_results).map((item) => {
|
|
8254
|
+
var normalizeTemplateTestDetails = (result, context) => arrayValue(recordValue(result)?.test_results).map((item) => {
|
|
7780
8255
|
const row = recordValue(item) ?? {};
|
|
7781
8256
|
const passed = typeof row.passed === "boolean" ? row.passed : void 0;
|
|
7782
8257
|
return compactRecord({
|
|
@@ -7786,17 +8261,23 @@ var normalizeTemplateTestDetails = (result) => arrayValue(recordValue(result)?.t
|
|
|
7786
8261
|
status: passed === void 0 ? firstString(row, ["status"]) : passed ? "Pass" : "Fail",
|
|
7787
8262
|
passed,
|
|
7788
8263
|
severity: firstString(row, ["severity", "level"]),
|
|
7789
|
-
message: firstString(row, ["message", "description"]),
|
|
7790
|
-
recommendation:
|
|
8264
|
+
message: publicValidationText(firstString(row, ["message", "description"])),
|
|
8265
|
+
recommendation: publicValidationText(
|
|
8266
|
+
firstString(row, ["recommendation", "remediation"])
|
|
8267
|
+
),
|
|
7791
8268
|
duration_ms: typeof row.duration_ms === "number" ? row.duration_ms : typeof row.durationMs === "number" ? row.durationMs : void 0,
|
|
7792
|
-
file_path:
|
|
8269
|
+
file_path: displayTemplateLocation(
|
|
8270
|
+
firstString(row, ["file_path", "filePath"]),
|
|
8271
|
+
context
|
|
8272
|
+
)
|
|
7793
8273
|
});
|
|
7794
8274
|
});
|
|
7795
|
-
var withTemplateTestDetails = (value) => {
|
|
7796
|
-
const
|
|
7797
|
-
if (!
|
|
8275
|
+
var withTemplateTestDetails = (value, context) => {
|
|
8276
|
+
const rawResult = resolvedOperationResult(value);
|
|
8277
|
+
if (!rawResult) {
|
|
7798
8278
|
return value;
|
|
7799
8279
|
}
|
|
8280
|
+
const result = recordValue(sanitizeTemplateLocationFields(rawResult, context)) ?? rawResult;
|
|
7800
8281
|
const original = recordValue(value);
|
|
7801
8282
|
const jobFields = original && ("jobId" in original || "status" in original) ? compactRecord({
|
|
7802
8283
|
submitted: original.submitted,
|
|
@@ -7812,7 +8293,7 @@ var withTemplateTestDetails = (value) => {
|
|
|
7812
8293
|
failed_tests: result.failed_tests,
|
|
7813
8294
|
skipped_tests: result.skipped_tests
|
|
7814
8295
|
}),
|
|
7815
|
-
details: normalizeTemplateTestDetails(result)
|
|
8296
|
+
details: normalizeTemplateTestDetails(result, context)
|
|
7816
8297
|
});
|
|
7817
8298
|
};
|
|
7818
8299
|
var getTemplateValidationJobStatus = async (input) => fetchCloudEvalJson({
|
|
@@ -7834,10 +8315,34 @@ var waitForTemplateValidationResult = async (input) => {
|
|
|
7834
8315
|
}
|
|
7835
8316
|
const waitTimeoutMs = Math.max(1, input.waitTimeoutMs ?? 6e5);
|
|
7836
8317
|
const pollIntervalMs = Math.max(500, input.pollIntervalMs ?? 2500);
|
|
8318
|
+
const progressContext = {
|
|
8319
|
+
templatePath: input.templatePath,
|
|
8320
|
+
parametersPath: input.parametersPath
|
|
8321
|
+
};
|
|
7837
8322
|
const deadline = Date.now() + waitTimeoutMs;
|
|
8323
|
+
const startedAt = Date.now();
|
|
8324
|
+
const elapsedMs = () => Date.now() - startedAt;
|
|
8325
|
+
await input.onProgress?.(
|
|
8326
|
+
progressEventFromStatus({
|
|
8327
|
+
phase: "submitted",
|
|
8328
|
+
jobId,
|
|
8329
|
+
status: input.submitted,
|
|
8330
|
+
elapsedMs: elapsedMs(),
|
|
8331
|
+
context: progressContext
|
|
8332
|
+
})
|
|
8333
|
+
);
|
|
7838
8334
|
let status;
|
|
7839
8335
|
for (; ; ) {
|
|
7840
8336
|
status = await getTemplateValidationJobStatus({ ...input, jobId });
|
|
8337
|
+
await input.onProgress?.(
|
|
8338
|
+
progressEventFromStatus({
|
|
8339
|
+
phase: "status",
|
|
8340
|
+
jobId,
|
|
8341
|
+
status,
|
|
8342
|
+
elapsedMs: elapsedMs(),
|
|
8343
|
+
context: progressContext
|
|
8344
|
+
})
|
|
8345
|
+
);
|
|
7841
8346
|
if (isTerminalJobStatus3(status)) {
|
|
7842
8347
|
break;
|
|
7843
8348
|
}
|
|
@@ -7853,13 +8358,32 @@ var waitForTemplateValidationResult = async (input) => {
|
|
|
7853
8358
|
`Template validation job ${jobId} ended with status ${normalizedStatus(status) || "unknown"}.`
|
|
7854
8359
|
);
|
|
7855
8360
|
}
|
|
8361
|
+
const result = sanitizeTemplateLocationFields(
|
|
8362
|
+
unwrapTemplateOperationResult(
|
|
8363
|
+
await getTemplateValidationJobResult({ ...input, jobId })
|
|
8364
|
+
),
|
|
8365
|
+
progressContext
|
|
8366
|
+
);
|
|
8367
|
+
const statusEvent = progressEventFromStatus({
|
|
8368
|
+
phase: "status",
|
|
8369
|
+
jobId,
|
|
8370
|
+
status,
|
|
8371
|
+
elapsedMs: elapsedMs(),
|
|
8372
|
+
context: progressContext
|
|
8373
|
+
});
|
|
8374
|
+
await input.onProgress?.(
|
|
8375
|
+
compactRecord({
|
|
8376
|
+
...statusEvent,
|
|
8377
|
+
phase: "result",
|
|
8378
|
+
...resultProgressSummary(result, statusEvent.operation, progressContext),
|
|
8379
|
+
elapsedMs: elapsedMs()
|
|
8380
|
+
})
|
|
8381
|
+
);
|
|
7856
8382
|
return {
|
|
7857
8383
|
submitted: input.submitted,
|
|
7858
8384
|
jobId,
|
|
7859
8385
|
status,
|
|
7860
|
-
result
|
|
7861
|
-
await getTemplateValidationJobResult({ ...input, jobId })
|
|
7862
|
-
)
|
|
8386
|
+
result
|
|
7863
8387
|
};
|
|
7864
8388
|
};
|
|
7865
8389
|
var templateTestRequestBody = async (files, options) => {
|
|
@@ -9234,7 +9758,7 @@ var mcpToolDefinitions = [
|
|
|
9234
9758
|
},
|
|
9235
9759
|
wait: {
|
|
9236
9760
|
type: "boolean",
|
|
9237
|
-
description: "Poll an async validation job until results are ready.",
|
|
9761
|
+
description: "Poll an async validation job until results are ready. When the MCP call includes _meta.progressToken, wait progress is emitted as notifications/progress.",
|
|
9238
9762
|
default: false
|
|
9239
9763
|
},
|
|
9240
9764
|
pollIntervalMs: {
|
|
@@ -9291,7 +9815,7 @@ var mcpToolDefinitions = [
|
|
|
9291
9815
|
verbose: { type: "boolean", default: false },
|
|
9292
9816
|
wait: {
|
|
9293
9817
|
type: "boolean",
|
|
9294
|
-
description: "Poll an async template test job until results are ready.",
|
|
9818
|
+
description: "Poll an async template test job until results are ready. When the MCP call includes _meta.progressToken, wait progress is emitted as notifications/progress.",
|
|
9295
9819
|
default: false
|
|
9296
9820
|
},
|
|
9297
9821
|
pollIntervalMs: {
|
|
@@ -10753,7 +11277,7 @@ var buildToolHandlers = (serverOptions) => {
|
|
|
10753
11277
|
});
|
|
10754
11278
|
return withEnvelope({ command: "projects graph sync-runs", data });
|
|
10755
11279
|
});
|
|
10756
|
-
handlers.set("template_validate", async (args) => {
|
|
11280
|
+
handlers.set("template_validate", async (args, context) => {
|
|
10757
11281
|
const config = await resolveInvocationConfig(serverOptions, args);
|
|
10758
11282
|
const auth = await resolveAuth(config, { requireUser: true });
|
|
10759
11283
|
const templatePath = stringValue(args.templatePath);
|
|
@@ -10786,14 +11310,17 @@ var buildToolHandlers = (serverOptions) => {
|
|
|
10786
11310
|
userId: auth.user.id,
|
|
10787
11311
|
submitted,
|
|
10788
11312
|
pollIntervalMs: numberValue(args.pollIntervalMs),
|
|
10789
|
-
waitTimeoutMs: numberValue(args.waitTimeoutMs)
|
|
11313
|
+
waitTimeoutMs: numberValue(args.waitTimeoutMs),
|
|
11314
|
+
templatePath,
|
|
11315
|
+
parametersPath: stringValue(args.parametersPath),
|
|
11316
|
+
onProgress: context?.sendProgress ? (event) => context.sendProgress(event, "validate template") : void 0
|
|
10790
11317
|
}) : submitted;
|
|
10791
11318
|
return withEnvelope({
|
|
10792
11319
|
command: "validate template",
|
|
10793
11320
|
data: booleanValue(args.details) ? withTemplateValidationDetails(data) : data
|
|
10794
11321
|
});
|
|
10795
11322
|
});
|
|
10796
|
-
handlers.set("template_test", async (args) => {
|
|
11323
|
+
handlers.set("template_test", async (args, context) => {
|
|
10797
11324
|
const config = await resolveInvocationConfig(serverOptions, args);
|
|
10798
11325
|
const auth = await resolveAuth(config, { requireUser: true });
|
|
10799
11326
|
const templatePath = stringValue(args.templatePath);
|
|
@@ -10818,11 +11345,17 @@ var buildToolHandlers = (serverOptions) => {
|
|
|
10818
11345
|
userId: auth.user.id,
|
|
10819
11346
|
submitted,
|
|
10820
11347
|
pollIntervalMs: numberValue(args.pollIntervalMs),
|
|
10821
|
-
waitTimeoutMs: numberValue(args.waitTimeoutMs)
|
|
11348
|
+
waitTimeoutMs: numberValue(args.waitTimeoutMs),
|
|
11349
|
+
templatePath,
|
|
11350
|
+
parametersPath: stringValue(args.parametersPath),
|
|
11351
|
+
onProgress: context?.sendProgress ? (event) => context.sendProgress(event, "validate tests") : void 0
|
|
10822
11352
|
}) : submitted;
|
|
10823
11353
|
return withEnvelope({
|
|
10824
11354
|
command: "validate tests",
|
|
10825
|
-
data: withTemplateTestDetails(data
|
|
11355
|
+
data: withTemplateTestDetails(data, {
|
|
11356
|
+
templatePath,
|
|
11357
|
+
parametersPath: stringValue(args.parametersPath)
|
|
11358
|
+
})
|
|
10826
11359
|
});
|
|
10827
11360
|
});
|
|
10828
11361
|
handlers.set("template_parse", async (args) => {
|
|
@@ -12013,6 +12546,34 @@ var serveMcpServer = async (options) => {
|
|
|
12013
12546
|
transport: outputTransport
|
|
12014
12547
|
});
|
|
12015
12548
|
};
|
|
12549
|
+
const progressTokenFromParams = (params) => {
|
|
12550
|
+
const meta = isObject(params?._meta) ? params?._meta : void 0;
|
|
12551
|
+
const token = meta?.progressToken;
|
|
12552
|
+
if (typeof token === "string") {
|
|
12553
|
+
return token;
|
|
12554
|
+
}
|
|
12555
|
+
if (typeof token === "number" && Number.isFinite(token)) {
|
|
12556
|
+
return token;
|
|
12557
|
+
}
|
|
12558
|
+
return void 0;
|
|
12559
|
+
};
|
|
12560
|
+
const sendTemplateProgress = (progressToken, event, command) => {
|
|
12561
|
+
if (progressToken === void 0) {
|
|
12562
|
+
return;
|
|
12563
|
+
}
|
|
12564
|
+
const progress = typeof event.progress === "number" ? event.progress : typeof event.completed === "number" ? event.completed : event.phase === "result" ? 100 : 0;
|
|
12565
|
+
const total = typeof event.progress === "number" ? 100 : typeof event.total === "number" ? event.total : event.phase === "result" ? 100 : void 0;
|
|
12566
|
+
send({
|
|
12567
|
+
jsonrpc: "2.0",
|
|
12568
|
+
method: "notifications/progress",
|
|
12569
|
+
params: {
|
|
12570
|
+
progressToken,
|
|
12571
|
+
progress,
|
|
12572
|
+
...total === void 0 ? {} : { total },
|
|
12573
|
+
message: formatTemplateProgressEvent(event, command).join("\n")
|
|
12574
|
+
}
|
|
12575
|
+
});
|
|
12576
|
+
};
|
|
12016
12577
|
const handleRequest = async (request) => {
|
|
12017
12578
|
debug("request received", {
|
|
12018
12579
|
id: request.id,
|
|
@@ -12096,9 +12657,23 @@ var serveMcpServer = async (options) => {
|
|
|
12096
12657
|
`Tool has no handler: ${name}`
|
|
12097
12658
|
);
|
|
12098
12659
|
}
|
|
12660
|
+
const progressToken = progressTokenFromParams(request.params);
|
|
12661
|
+
let lastProgressKey;
|
|
12099
12662
|
const startedAt = Date.now();
|
|
12100
12663
|
try {
|
|
12101
|
-
const envelope = await handler(args
|
|
12664
|
+
const envelope = await handler(args, {
|
|
12665
|
+
progressToken,
|
|
12666
|
+
sendProgress: (event, command) => {
|
|
12667
|
+
if (event.phase === "status") {
|
|
12668
|
+
const key = templateProgressEventKey(event);
|
|
12669
|
+
if (key === lastProgressKey) {
|
|
12670
|
+
return;
|
|
12671
|
+
}
|
|
12672
|
+
lastProgressKey = key;
|
|
12673
|
+
}
|
|
12674
|
+
sendTemplateProgress(progressToken, event, command);
|
|
12675
|
+
}
|
|
12676
|
+
});
|
|
12102
12677
|
debug("tool call completed", { tool: name, ok: envelope.ok });
|
|
12103
12678
|
await options.telemetry?.track("cli.mcp.tool", {
|
|
12104
12679
|
command: "mcp",
|
|
@@ -13137,6 +13712,47 @@ var registerAgentsCommand = (program2, deps) => {
|
|
|
13137
13712
|
};
|
|
13138
13713
|
|
|
13139
13714
|
// src/validateCommand.ts
|
|
13715
|
+
var normalizeTemplateProgressMode = (value) => {
|
|
13716
|
+
if (value === true) {
|
|
13717
|
+
return "stderr";
|
|
13718
|
+
}
|
|
13719
|
+
if (value === void 0 || value === false || value === null) {
|
|
13720
|
+
return "none";
|
|
13721
|
+
}
|
|
13722
|
+
const mode = String(value).trim().toLowerCase();
|
|
13723
|
+
if (mode === "auto" || mode === "stderr" || mode === "ndjson" || mode === "none") {
|
|
13724
|
+
return mode;
|
|
13725
|
+
}
|
|
13726
|
+
throw new Error("--progress must be one of: auto, stderr, ndjson, none");
|
|
13727
|
+
};
|
|
13728
|
+
var createTemplateProgressReporter = (command, progress) => {
|
|
13729
|
+
const requestedMode = normalizeTemplateProgressMode(progress);
|
|
13730
|
+
const mode = requestedMode === "auto" ? process.stderr.isTTY ? "stderr" : "none" : requestedMode;
|
|
13731
|
+
if (mode === "none") {
|
|
13732
|
+
return void 0;
|
|
13733
|
+
}
|
|
13734
|
+
let lastStatusKey;
|
|
13735
|
+
return (event) => {
|
|
13736
|
+
if (event.phase === "status") {
|
|
13737
|
+
const key = templateProgressEventKey(event);
|
|
13738
|
+
if (key === lastStatusKey) {
|
|
13739
|
+
return;
|
|
13740
|
+
}
|
|
13741
|
+
lastStatusKey = key;
|
|
13742
|
+
}
|
|
13743
|
+
if (mode === "ndjson") {
|
|
13744
|
+
process.stderr.write(
|
|
13745
|
+
`${JSON.stringify({ type: "template_progress", command, ...event })}
|
|
13746
|
+
`
|
|
13747
|
+
);
|
|
13748
|
+
return;
|
|
13749
|
+
}
|
|
13750
|
+
for (const line of formatTemplateProgressEvent(event, command)) {
|
|
13751
|
+
process.stderr.write(`${line}
|
|
13752
|
+
`);
|
|
13753
|
+
}
|
|
13754
|
+
};
|
|
13755
|
+
};
|
|
13140
13756
|
var addCommon5 = (command, deps) => addAuthOptions(command, deps.defaultBaseUrl).requiredOption("--template-file <path>", "Cloud template JSON file").option("--parameters-file <path>", "Optional parameters JSON file").option("--format <format>", "Output format: text, json, ndjson, markdown", "text").option("--output <file>", "Output file");
|
|
13141
13757
|
var parsePositiveInteger2 = (value, optionName = "--max-results") => {
|
|
13142
13758
|
if (!value) {
|
|
@@ -13158,7 +13774,11 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
13158
13774
|
"--rule <id>",
|
|
13159
13775
|
"Run a specific validation check id; repeat for multiple checks",
|
|
13160
13776
|
collectRule
|
|
13161
|
-
).option("--category <name>", "Validation category filter").option("--pillar <name>", "Architecture pillar filter").option("--min-severity <level>", "Minimum severity level").option("--max-results <count>", "Maximum validation results").option("--project <id>", "Project id for saved validation results").option("--save-report", "Persist validation results when a project is provided", false).option("--details", "Include frontend-style per-check evidence details", false).option("--wait", "Poll an async validation job until results are ready", false).option("--poll-interval <ms>", "Polling interval when --wait is set", "2500").option("--wait-timeout <ms>", "Maximum time to wait when --wait is set", "600000").
|
|
13777
|
+
).option("--category <name>", "Validation category filter").option("--pillar <name>", "Architecture pillar filter").option("--min-severity <level>", "Minimum severity level").option("--max-results <count>", "Maximum validation results").option("--project <id>", "Project id for saved validation results").option("--save-report", "Persist validation results when a project is provided", false).option("--details", "Include frontend-style per-check evidence details", false).option("--wait", "Poll an async validation job until results are ready", false).option("--poll-interval <ms>", "Polling interval when --wait is set", "2500").option("--wait-timeout <ms>", "Maximum time to wait when --wait is set", "600000").option(
|
|
13778
|
+
"--progress [mode]",
|
|
13779
|
+
"Progress events while waiting: auto, stderr, ndjson, none",
|
|
13780
|
+
"none"
|
|
13781
|
+
).action(async (options, command) => {
|
|
13162
13782
|
try {
|
|
13163
13783
|
const context = requireAuthUser(await resolveAuthContext(options, command, deps));
|
|
13164
13784
|
const submitted = await validateTemplate({
|
|
@@ -13188,6 +13808,12 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
13188
13808
|
waitTimeoutMs: parsePositiveInteger2(
|
|
13189
13809
|
options.waitTimeout,
|
|
13190
13810
|
"--wait-timeout"
|
|
13811
|
+
),
|
|
13812
|
+
templatePath: options.templateFile,
|
|
13813
|
+
parametersPath: options.parametersFile,
|
|
13814
|
+
onProgress: createTemplateProgressReporter(
|
|
13815
|
+
"validate template",
|
|
13816
|
+
options.progress
|
|
13191
13817
|
)
|
|
13192
13818
|
}) : submitted;
|
|
13193
13819
|
const outputData = options.details ? withTemplateValidationDetails(data) : data;
|
|
@@ -13225,7 +13851,11 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
13225
13851
|
process.exit(1);
|
|
13226
13852
|
}
|
|
13227
13853
|
});
|
|
13228
|
-
addCommon5(validate.command("tests").description("Run cloud template test checks"), deps).option("--test <name>", "Run a specific template test; repeat for multiple tests", collectRule).option("--skip-test <name>", "Skip a specific template test; repeat for multiple tests", collectRule).option("--category <name>", "Template test category").option("--group <name>", "Template test group; repeat for multiple groups", collectRule).option("--verbose", "Request verbose template test output", false).option("--wait", "Poll an async template test job until results are ready", false).option("--poll-interval <ms>", "Polling interval when --wait is set", "2500").option("--wait-timeout <ms>", "Maximum time to wait when --wait is set", "600000").
|
|
13854
|
+
addCommon5(validate.command("tests").description("Run cloud template test checks"), deps).option("--test <name>", "Run a specific template test; repeat for multiple tests", collectRule).option("--skip-test <name>", "Skip a specific template test; repeat for multiple tests", collectRule).option("--category <name>", "Template test category").option("--group <name>", "Template test group; repeat for multiple groups", collectRule).option("--verbose", "Request verbose template test output", false).option("--wait", "Poll an async template test job until results are ready", false).option("--poll-interval <ms>", "Polling interval when --wait is set", "2500").option("--wait-timeout <ms>", "Maximum time to wait when --wait is set", "600000").option(
|
|
13855
|
+
"--progress [mode]",
|
|
13856
|
+
"Progress events while waiting: auto, stderr, ndjson, none",
|
|
13857
|
+
"none"
|
|
13858
|
+
).action(async (options, command) => {
|
|
13229
13859
|
try {
|
|
13230
13860
|
const context = requireAuthUser(await resolveAuthContext(options, command, deps));
|
|
13231
13861
|
const submitted = await testTemplate({
|
|
@@ -13252,11 +13882,20 @@ var registerValidateCommand = (program2, deps) => {
|
|
|
13252
13882
|
waitTimeoutMs: parsePositiveInteger2(
|
|
13253
13883
|
options.waitTimeout,
|
|
13254
13884
|
"--wait-timeout"
|
|
13885
|
+
),
|
|
13886
|
+
templatePath: options.templateFile,
|
|
13887
|
+
parametersPath: options.parametersFile,
|
|
13888
|
+
onProgress: createTemplateProgressReporter(
|
|
13889
|
+
"validate tests",
|
|
13890
|
+
options.progress
|
|
13255
13891
|
)
|
|
13256
13892
|
}) : submitted;
|
|
13257
13893
|
await writeFormattedOutput({
|
|
13258
13894
|
command: "validate tests",
|
|
13259
|
-
data: withTemplateTestDetails(data
|
|
13895
|
+
data: withTemplateTestDetails(data, {
|
|
13896
|
+
templatePath: options.templateFile,
|
|
13897
|
+
parametersPath: options.parametersFile
|
|
13898
|
+
}),
|
|
13260
13899
|
format: options.format,
|
|
13261
13900
|
output: options.output
|
|
13262
13901
|
});
|
|
@@ -15626,7 +16265,7 @@ program.command("tui").description("Open the CloudEval Terminal UI").option(
|
|
|
15626
16265
|
const { assertSecureBaseUrl } = await import("./dist-PEYJDO7A.js");
|
|
15627
16266
|
const [{ render }, { App }] = await Promise.all([
|
|
15628
16267
|
import("ink"),
|
|
15629
|
-
import("./App-
|
|
16268
|
+
import("./App-FRLV34U4.js")
|
|
15630
16269
|
]);
|
|
15631
16270
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
15632
16271
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -15684,7 +16323,7 @@ program.command("chat").description("Start an interactive chat session").option(
|
|
|
15684
16323
|
const { assertSecureBaseUrl } = await import("./dist-PEYJDO7A.js");
|
|
15685
16324
|
const [{ render }, { App }] = await Promise.all([
|
|
15686
16325
|
import("ink"),
|
|
15687
|
-
import("./App-
|
|
16326
|
+
import("./App-FRLV34U4.js")
|
|
15688
16327
|
]);
|
|
15689
16328
|
const baseUrl = await resolveBaseUrl(options, command);
|
|
15690
16329
|
assertSecureBaseUrl(baseUrl);
|
|
@@ -16438,7 +17077,7 @@ Error: ${errorMsg}
|
|
|
16438
17077
|
program.command("banner").description("Preview the startup banner and terminal capabilities").action(async () => {
|
|
16439
17078
|
const { render } = await import("ink");
|
|
16440
17079
|
const BannerPreview = React.lazy(async () => ({
|
|
16441
|
-
default: (await import("./Banner-
|
|
17080
|
+
default: (await import("./Banner-IFLO2NC6.js")).Banner
|
|
16442
17081
|
}));
|
|
16443
17082
|
render(
|
|
16444
17083
|
/* @__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.28.1",
|
|
18
18
|
"downloadLocation": "https://github.com/ganakailabs/cloudeval-cli",
|
|
19
19
|
"filesAnalyzed": false,
|
|
20
20
|
"licenseConcluded": "LicenseRef-CloudEval-CLI",
|
|
@@ -2354,9 +2354,9 @@
|
|
|
2354
2354
|
"summary": "The semantic version parser used by npm."
|
|
2355
2355
|
},
|
|
2356
2356
|
{
|
|
2357
|
-
"SPDXID": "SPDXRef-Package-shell-quote-1.8.
|
|
2357
|
+
"SPDXID": "SPDXRef-Package-shell-quote-1.8.4",
|
|
2358
2358
|
"name": "shell-quote",
|
|
2359
|
-
"versionInfo": "1.8.
|
|
2359
|
+
"versionInfo": "1.8.4",
|
|
2360
2360
|
"downloadLocation": "https://github.com/ljharb/shell-quote",
|
|
2361
2361
|
"filesAnalyzed": false,
|
|
2362
2362
|
"licenseConcluded": "MIT",
|
|
@@ -3847,7 +3847,7 @@
|
|
|
3847
3847
|
{
|
|
3848
3848
|
"spdxElementId": "SPDXRef-Package-CloudEval-CLI",
|
|
3849
3849
|
"relationshipType": "DEPENDS_ON",
|
|
3850
|
-
"relatedSpdxElement": "SPDXRef-Package-shell-quote-1.8.
|
|
3850
|
+
"relatedSpdxElement": "SPDXRef-Package-shell-quote-1.8.4"
|
|
3851
3851
|
},
|
|
3852
3852
|
{
|
|
3853
3853
|
"spdxElementId": "SPDXRef-Package-CloudEval-CLI",
|