cloudburn 0.8.2 → 0.8.3
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/cli.js +210 -36
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -153,6 +153,13 @@ var buildCommandPath = (command) => {
|
|
|
153
153
|
}
|
|
154
154
|
return names.join(" ");
|
|
155
155
|
};
|
|
156
|
+
var getRootCommand = (command) => {
|
|
157
|
+
let current = command;
|
|
158
|
+
while (current.parent !== null) {
|
|
159
|
+
current = current.parent;
|
|
160
|
+
}
|
|
161
|
+
return current;
|
|
162
|
+
};
|
|
156
163
|
var visibleCommands = (command) => command.commands.filter((subcommand) => !subcommand._hidden);
|
|
157
164
|
var getCommandExamples = (command) => {
|
|
158
165
|
const { _cloudburnExamples } = command;
|
|
@@ -221,8 +228,9 @@ var formatCloudBurnHelp = (command, helper) => {
|
|
|
221
228
|
const cloudBurnHelp = helper;
|
|
222
229
|
const helpWidth = helper.helpWidth ?? 80;
|
|
223
230
|
const scenario = getHelpScenario(command);
|
|
224
|
-
const
|
|
225
|
-
const
|
|
231
|
+
const rootCommand = getRootCommand(command);
|
|
232
|
+
const localOptions = helper.visibleOptions(command).filter((option) => command.options.includes(option));
|
|
233
|
+
const globalOptions = helper.visibleGlobalOptions(command).filter((option) => rootCommand.options.includes(option));
|
|
226
234
|
const commands = helper.visibleCommands(command);
|
|
227
235
|
const examples = getCommandExamples(command);
|
|
228
236
|
const usageGuidance = getCommandUsageGuidance(command);
|
|
@@ -386,7 +394,7 @@ var setCommandUsageGuidance = (command, guidance) => {
|
|
|
386
394
|
};
|
|
387
395
|
|
|
388
396
|
// src/commands/completion.ts
|
|
389
|
-
var
|
|
397
|
+
var getRootCommand2 = (command) => {
|
|
390
398
|
let currentCommand = command;
|
|
391
399
|
while (currentCommand.parent !== null) {
|
|
392
400
|
currentCommand = currentCommand.parent;
|
|
@@ -441,14 +449,14 @@ var registerCompletionCommand = (program) => {
|
|
|
441
449
|
for (const shell of ["bash", "fish", "zsh"]) {
|
|
442
450
|
setCommandUsageGuidance(
|
|
443
451
|
completionCommand.command(shell).description(`Generate the autocompletion script for the ${shell} shell.`).option("--no-descriptions", "disable completion descriptions").action(function() {
|
|
444
|
-
const output = generateCompletionScript(shell,
|
|
452
|
+
const output = generateCompletionScript(shell, getRootCommand2(this));
|
|
445
453
|
process.stdout.write(output);
|
|
446
454
|
}),
|
|
447
455
|
getCompletionHelpText(shell)
|
|
448
456
|
);
|
|
449
457
|
}
|
|
450
458
|
program.command("__complete", { hidden: true }).argument("[words...]").allowUnknownOption().action(function(words) {
|
|
451
|
-
const suggestions = resolveCompletionSuggestions(createCompletionTree(
|
|
459
|
+
const suggestions = resolveCompletionSuggestions(createCompletionTree(getRootCommand2(this)), words ?? []);
|
|
452
460
|
if (suggestions.length === 0) {
|
|
453
461
|
return;
|
|
454
462
|
}
|
|
@@ -480,25 +488,26 @@ var categorize = (err) => {
|
|
|
480
488
|
if (!(err instanceof Error)) {
|
|
481
489
|
return { code: "RUNTIME_ERROR", message: "An unexpected error occurred." };
|
|
482
490
|
}
|
|
483
|
-
|
|
491
|
+
const code = "code" in err && typeof err.code === "string" ? err.code : void 0;
|
|
492
|
+
if (err.name === "CredentialsProviderError" || err.name === "ExpiredTokenException" || code === "CredentialsProviderError" || code === "ExpiredTokenException") {
|
|
484
493
|
return {
|
|
485
494
|
code: "CREDENTIALS_ERROR",
|
|
486
495
|
message: "AWS credentials not found or expired. Run 'aws sts get-caller-identity' to verify your session."
|
|
487
496
|
};
|
|
488
497
|
}
|
|
489
|
-
if (err.name.includes("AccessDenied")) {
|
|
498
|
+
if (err.name.includes("AccessDenied") || code?.includes("AccessDenied") === true) {
|
|
490
499
|
return {
|
|
491
500
|
code: "ACCESS_DENIED",
|
|
492
|
-
message: "Insufficient AWS permissions. Check your IAM role or policy."
|
|
501
|
+
message: sanitizeRuntimeErrorMessage(err.message).trim() || "Insufficient AWS permissions. Check your IAM role or policy."
|
|
493
502
|
};
|
|
494
503
|
}
|
|
495
504
|
if (err.code === "ENOENT") {
|
|
496
505
|
const path = err.path ?? "unknown";
|
|
497
506
|
return { code: "PATH_NOT_FOUND", message: `Path not found: ${path}` };
|
|
498
507
|
}
|
|
499
|
-
if (
|
|
508
|
+
if (code && isAwsDiscoveryErrorCode(code)) {
|
|
500
509
|
return {
|
|
501
|
-
code
|
|
510
|
+
code,
|
|
502
511
|
message: sanitizeRuntimeErrorMessage(err.message).trim() || "AWS Resource Explorer discovery failed."
|
|
503
512
|
};
|
|
504
513
|
}
|
|
@@ -526,6 +535,7 @@ var flattenScanResult = (result) => result.providers.flatMap(
|
|
|
526
535
|
)
|
|
527
536
|
);
|
|
528
537
|
var countScanResultFindings = (result) => flattenScanResult(result).length;
|
|
538
|
+
var getScanDiagnostics = (result) => result.diagnostics ?? [];
|
|
529
539
|
|
|
530
540
|
// src/formatters/output.ts
|
|
531
541
|
var DEFAULT_TABLE_WIDTH = 200;
|
|
@@ -574,6 +584,8 @@ var renderJson = (response) => {
|
|
|
574
584
|
switch (response.kind) {
|
|
575
585
|
case "document":
|
|
576
586
|
return JSON.stringify({ content: response.content, contentType: response.contentType }, null, 2);
|
|
587
|
+
case "discovery-status":
|
|
588
|
+
return JSON.stringify({ summary: response.summary, regions: response.rows }, null, 2);
|
|
577
589
|
case "record-list":
|
|
578
590
|
return JSON.stringify(response.rows, null, 2);
|
|
579
591
|
case "rule-list":
|
|
@@ -590,6 +602,9 @@ var renderText = (response) => {
|
|
|
590
602
|
switch (response.kind) {
|
|
591
603
|
case "document":
|
|
592
604
|
return response.content;
|
|
605
|
+
case "discovery-status":
|
|
606
|
+
return `${response.summaryText}
|
|
607
|
+
${renderTextRows(response.rows, response.columns, "No discovery status available.")}`;
|
|
593
608
|
case "record-list":
|
|
594
609
|
return renderTextRows(response.rows, response.columns, response.emptyMessage);
|
|
595
610
|
case "rule-list":
|
|
@@ -615,6 +630,19 @@ var renderTable = (response) => {
|
|
|
615
630
|
{ key: "Value", header: "Value" }
|
|
616
631
|
]
|
|
617
632
|
);
|
|
633
|
+
case "discovery-status": {
|
|
634
|
+
const summaryTable = renderAsciiTable(
|
|
635
|
+
Object.entries(response.summary).map(([field, value]) => ({ Field: field, Value: value })),
|
|
636
|
+
[
|
|
637
|
+
{ key: "Field", header: "Field" },
|
|
638
|
+
{ key: "Value", header: "Value" }
|
|
639
|
+
]
|
|
640
|
+
);
|
|
641
|
+
const regionsTable = response.rows.length === 0 ? "No discovery status available." : renderAsciiTable(response.rows, response.columns ?? inferColumns(response.rows));
|
|
642
|
+
return `${summaryTable}
|
|
643
|
+
|
|
644
|
+
${regionsTable}`;
|
|
645
|
+
}
|
|
618
646
|
case "record-list":
|
|
619
647
|
return response.rows.length === 0 ? response.emptyMessage : renderAsciiTable(response.rows, response.columns ?? inferColumns(response.rows));
|
|
620
648
|
case "rule-list":
|
|
@@ -638,19 +666,34 @@ var renderTable = (response) => {
|
|
|
638
666
|
);
|
|
639
667
|
}
|
|
640
668
|
};
|
|
641
|
-
var projectScanRows = (result) =>
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
669
|
+
var projectScanRows = (result) => [
|
|
670
|
+
...flattenScanResult(result).map(({ finding, message, provider, ruleId, service, source }) => ({
|
|
671
|
+
accountId: finding.accountId ?? "",
|
|
672
|
+
message,
|
|
673
|
+
path: finding.location?.path ?? "",
|
|
674
|
+
provider,
|
|
675
|
+
region: finding.region ?? "",
|
|
676
|
+
resourceId: finding.resourceId,
|
|
677
|
+
ruleId,
|
|
678
|
+
service,
|
|
679
|
+
source,
|
|
680
|
+
column: finding.location?.column ?? "",
|
|
681
|
+
line: finding.location?.line ?? ""
|
|
682
|
+
})),
|
|
683
|
+
...getScanDiagnostics(result).map((diagnostic) => ({
|
|
684
|
+
accountId: "",
|
|
685
|
+
column: "",
|
|
686
|
+
line: "",
|
|
687
|
+
message: diagnostic.message,
|
|
688
|
+
path: "",
|
|
689
|
+
provider: diagnostic.provider,
|
|
690
|
+
region: diagnostic.region ?? "",
|
|
691
|
+
resourceId: "",
|
|
692
|
+
ruleId: "",
|
|
693
|
+
service: diagnostic.service,
|
|
694
|
+
source: diagnostic.source
|
|
695
|
+
}))
|
|
696
|
+
];
|
|
654
697
|
var renderTextRows = (rows, columns, emptyMessage) => {
|
|
655
698
|
if (rows.length === 0) {
|
|
656
699
|
return emptyMessage;
|
|
@@ -822,6 +865,69 @@ var parseRuleIdList = (value) => {
|
|
|
822
865
|
};
|
|
823
866
|
|
|
824
867
|
// src/commands/discover.ts
|
|
868
|
+
var describeDiscoverySummary = (status) => {
|
|
869
|
+
const aggregatorSummary = status.aggregatorRegion ? ` Aggregator region: ${status.aggregatorRegion}.` : "";
|
|
870
|
+
const warningSummary = status.warning ? ` ${status.warning}` : "";
|
|
871
|
+
return `Coverage: ${status.coverage}. Indexed ${status.indexedRegionCount} of ${status.totalRegionCount} enabled regions.${aggregatorSummary}${warningSummary}`.trim();
|
|
872
|
+
};
|
|
873
|
+
var describeInitializationMessage = (result) => {
|
|
874
|
+
const baseMessage = result.indexType === "aggregator" ? result.aggregatorAction === "promoted" ? `Promoted the existing local Resource Explorer index in ${result.aggregatorRegion} to the aggregator.` : result.aggregatorAction === "created" ? `Configured ${result.aggregatorRegion} as the Resource Explorer aggregator.` : result.coverage === "full" ? `Resource Explorer aggregator already exists in ${result.aggregatorRegion}.` : `Resource Explorer aggregator already exists in ${result.aggregatorRegion}, but only ${result.observedStatus.indexedRegionCount} of ${result.observedStatus.totalRegionCount} regions are indexed.` : result.status === "EXISTING" ? `Local Resource Explorer setup already exists in ${result.aggregatorRegion}.` : `Local Resource Explorer setup created in ${result.aggregatorRegion}.`;
|
|
875
|
+
const indexSummaryParts = [];
|
|
876
|
+
if (result.createdIndexCount > 0) {
|
|
877
|
+
indexSummaryParts.push(
|
|
878
|
+
`Created ${result.createdIndexCount} ${result.createdIndexCount === 1 ? "index" : "indexes"}.`
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
if (result.reusedIndexCount > 0) {
|
|
882
|
+
indexSummaryParts.push(
|
|
883
|
+
`Reused ${result.reusedIndexCount} existing ${result.reusedIndexCount === 1 ? "index" : "indexes"}.`
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
const indexSummary = indexSummaryParts.length === 0 ? "" : ` ${indexSummaryParts.join(" ")}`;
|
|
887
|
+
const warnings = Array.from(new Set([result.warning, result.observedStatus.warning].filter(Boolean)));
|
|
888
|
+
const warning = warnings.length === 0 ? "" : ` ${warnings.join(" ")}`;
|
|
889
|
+
const convergenceNotice = result.verificationStatus === "timed_out" ? " Setup is still converging in AWS, so the observed coverage may still change." : "";
|
|
890
|
+
return `${baseMessage}${indexSummary}${warning}${convergenceNotice}`.trim();
|
|
891
|
+
};
|
|
892
|
+
var buildInitializationStatusData = (result, message, format) => {
|
|
893
|
+
const restrictedRegionCount = Math.max(
|
|
894
|
+
0,
|
|
895
|
+
result.observedStatus.totalRegionCount - result.observedStatus.accessibleRegionCount
|
|
896
|
+
);
|
|
897
|
+
if (format === "json") {
|
|
898
|
+
return {
|
|
899
|
+
aggregatorAction: result.aggregatorAction,
|
|
900
|
+
aggregatorRegion: result.aggregatorRegion,
|
|
901
|
+
coverage: result.coverage,
|
|
902
|
+
createdIndexCount: result.createdIndexCount,
|
|
903
|
+
indexType: result.indexType,
|
|
904
|
+
message,
|
|
905
|
+
observedStatus: result.observedStatus,
|
|
906
|
+
regions: result.regions,
|
|
907
|
+
reusedIndexCount: result.reusedIndexCount,
|
|
908
|
+
status: result.status,
|
|
909
|
+
taskId: result.taskId ?? "",
|
|
910
|
+
verificationStatus: result.verificationStatus,
|
|
911
|
+
...result.warning ? { warning: result.warning } : {}
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
return {
|
|
915
|
+
aggregatorAction: result.aggregatorAction,
|
|
916
|
+
aggregatorRegion: result.aggregatorRegion,
|
|
917
|
+
coverage: result.coverage,
|
|
918
|
+
createdIndexes: String(result.createdIndexCount),
|
|
919
|
+
details: "Run `cloudburn discover status` for per-region details.",
|
|
920
|
+
indexedRegions: result.regions.length === 0 ? "none" : result.regions.join(", "),
|
|
921
|
+
indexedSummary: `${result.observedStatus.indexedRegionCount} of ${result.observedStatus.totalRegionCount}`,
|
|
922
|
+
indexType: result.indexType,
|
|
923
|
+
message,
|
|
924
|
+
reusedIndexes: String(result.reusedIndexCount),
|
|
925
|
+
...restrictedRegionCount > 0 ? { restrictedRegions: String(restrictedRegionCount) } : {},
|
|
926
|
+
status: result.status,
|
|
927
|
+
taskId: result.taskId ?? "",
|
|
928
|
+
verificationStatus: result.verificationStatus
|
|
929
|
+
};
|
|
930
|
+
};
|
|
825
931
|
var parseAwsRegion = (value) => {
|
|
826
932
|
try {
|
|
827
933
|
return assertValidAwsRegion(value);
|
|
@@ -860,9 +966,17 @@ var registerDiscoverCommand = (program) => {
|
|
|
860
966
|
const discoverCommand = setCommandExamples(
|
|
861
967
|
program.command("discover").description("Run a live AWS discovery").enablePositionalOptions().option(
|
|
862
968
|
"--region <region>",
|
|
863
|
-
'Discovery region to use. Pass "all" to
|
|
969
|
+
'Discovery region to use. Defaults to the current AWS region from AWS_REGION; use this flag to override it. Pass "all" to check resources in all regions that are indexed in AWS Resource Explorer.',
|
|
864
970
|
parseDiscoverRegion
|
|
865
|
-
).option("--config <path>", "Explicit CloudBurn config file to load").option(
|
|
971
|
+
).option("--config <path>", "Explicit CloudBurn config file to load").option(
|
|
972
|
+
"--enabled-rules <ruleIds>",
|
|
973
|
+
"Comma-separated rule IDs to enable. When set, CloudBurn checks only these rules. By default, all rules are enabled.",
|
|
974
|
+
parseRuleIdList
|
|
975
|
+
).option(
|
|
976
|
+
"--disabled-rules <ruleIds>",
|
|
977
|
+
"Comma-separated rule IDs to disable. By default, all rules are enabled; use this to exclude specific rules.",
|
|
978
|
+
parseRuleIdList
|
|
979
|
+
).option("--exit-code", "Exit with code 1 when findings exist").action(async (options, command) => {
|
|
866
980
|
await runCommand(async () => {
|
|
867
981
|
const scanner = new CloudBurnClient();
|
|
868
982
|
const loadedConfig = await scanner.loadConfig(options.config);
|
|
@@ -891,10 +1005,64 @@ var registerDiscoverCommand = (program) => {
|
|
|
891
1005
|
"cloudburn discover",
|
|
892
1006
|
"cloudburn discover --region eu-central-1",
|
|
893
1007
|
"cloudburn discover --region all",
|
|
1008
|
+
"cloudburn discover status",
|
|
894
1009
|
"cloudburn discover list-enabled-regions",
|
|
895
1010
|
"cloudburn discover init"
|
|
896
1011
|
]
|
|
897
1012
|
);
|
|
1013
|
+
discoverCommand.command("status").description("Show Resource Explorer status across all enabled AWS regions").action(async (_options, command) => {
|
|
1014
|
+
await runCommand(async () => {
|
|
1015
|
+
const scanner = new CloudBurnClient();
|
|
1016
|
+
const parentRegion = discoverCommand.opts().region;
|
|
1017
|
+
const region = parentRegion === "all" ? void 0 : parentRegion;
|
|
1018
|
+
const status = await scanner.getDiscoveryStatus({ region });
|
|
1019
|
+
const format = resolveOutputFormat(command);
|
|
1020
|
+
const rows = format === "json" ? status.regions.map((regionStatus) => ({
|
|
1021
|
+
...regionStatus,
|
|
1022
|
+
notes: regionStatus.notes ?? ""
|
|
1023
|
+
})) : status.regions.map((regionStatus) => ({
|
|
1024
|
+
region: regionStatus.region,
|
|
1025
|
+
indexType: regionStatus.indexType === void 0 ? "" : regionStatus.isAggregator ? `${regionStatus.indexType} (active)` : regionStatus.indexType,
|
|
1026
|
+
notes: regionStatus.notes ?? "",
|
|
1027
|
+
status: regionStatus.status,
|
|
1028
|
+
viewStatus: regionStatus.viewStatus ?? ""
|
|
1029
|
+
}));
|
|
1030
|
+
const summary = format === "json" ? {
|
|
1031
|
+
accessibleRegionCount: status.accessibleRegionCount,
|
|
1032
|
+
coverage: status.coverage,
|
|
1033
|
+
indexedRegionCount: status.indexedRegionCount,
|
|
1034
|
+
totalRegionCount: status.totalRegionCount,
|
|
1035
|
+
...status.aggregatorRegion ? { aggregatorRegion: status.aggregatorRegion } : {},
|
|
1036
|
+
...status.warning ? { warning: status.warning } : {}
|
|
1037
|
+
} : {
|
|
1038
|
+
accessibleRegionCount: status.accessibleRegionCount,
|
|
1039
|
+
aggregatorRegion: status.aggregatorRegion ?? "",
|
|
1040
|
+
coverage: status.coverage,
|
|
1041
|
+
indexedRegionCount: status.indexedRegionCount,
|
|
1042
|
+
totalRegionCount: status.totalRegionCount,
|
|
1043
|
+
...status.warning ? { warning: status.warning } : {}
|
|
1044
|
+
};
|
|
1045
|
+
const output = renderResponse(
|
|
1046
|
+
{
|
|
1047
|
+
kind: "discovery-status",
|
|
1048
|
+
columns: [
|
|
1049
|
+
{ key: "region", header: "Region" },
|
|
1050
|
+
{ key: "indexType", header: "IndexType" },
|
|
1051
|
+
{ key: "status", header: "Status" },
|
|
1052
|
+
{ key: "viewStatus", header: "ViewStatus" },
|
|
1053
|
+
{ key: "notes", header: "Notes" }
|
|
1054
|
+
],
|
|
1055
|
+
rows,
|
|
1056
|
+
summary,
|
|
1057
|
+
summaryText: describeDiscoverySummary(status)
|
|
1058
|
+
},
|
|
1059
|
+
format
|
|
1060
|
+
);
|
|
1061
|
+
process.stdout.write(`${output}
|
|
1062
|
+
`);
|
|
1063
|
+
return EXIT_CODE_OK;
|
|
1064
|
+
});
|
|
1065
|
+
});
|
|
898
1066
|
discoverCommand.command("list-enabled-regions").description("List AWS regions with a local or aggregator Resource Explorer index").action(async (_options, command) => {
|
|
899
1067
|
await runCommand(async () => {
|
|
900
1068
|
const scanner = new CloudBurnClient();
|
|
@@ -917,24 +1085,22 @@ var registerDiscoverCommand = (program) => {
|
|
|
917
1085
|
return EXIT_CODE_OK;
|
|
918
1086
|
});
|
|
919
1087
|
});
|
|
920
|
-
discoverCommand.command("init").description("Set up AWS Resource Explorer for CloudBurn").option(
|
|
1088
|
+
discoverCommand.command("init").description("Set up AWS Resource Explorer for CloudBurn").option(
|
|
1089
|
+
"--region <region>",
|
|
1090
|
+
"Requested aggregator region to create or reuse during setup. This is the main Resource Explorer region that aggregates indexes from other regions when cross-region setup succeeds. Defaults to the current AWS region from AWS_REGION; use this flag to override it.",
|
|
1091
|
+
parseAwsRegion
|
|
1092
|
+
).action(async (options, command) => {
|
|
921
1093
|
await runCommand(async () => {
|
|
922
1094
|
const scanner = new CloudBurnClient();
|
|
923
1095
|
const parentRegion = discoverCommand.opts().region;
|
|
924
1096
|
const region = options.region ?? (parentRegion === "all" ? void 0 : parentRegion);
|
|
925
1097
|
const result = await scanner.initializeDiscovery({ region });
|
|
926
|
-
const message = result
|
|
1098
|
+
const message = describeInitializationMessage(result);
|
|
927
1099
|
const format = resolveOutputFormat(command);
|
|
928
1100
|
const output = renderResponse(
|
|
929
1101
|
{
|
|
930
1102
|
kind: "status",
|
|
931
|
-
data:
|
|
932
|
-
aggregatorRegion: result.aggregatorRegion,
|
|
933
|
-
message,
|
|
934
|
-
regions: result.regions,
|
|
935
|
-
status: result.status,
|
|
936
|
-
taskId: result.taskId ?? ""
|
|
937
|
-
},
|
|
1103
|
+
data: buildInitializationStatusData(result, message, format),
|
|
938
1104
|
text: message
|
|
939
1105
|
},
|
|
940
1106
|
format
|
|
@@ -1145,7 +1311,15 @@ var toScanConfigOverride = (options) => {
|
|
|
1145
1311
|
};
|
|
1146
1312
|
var registerScanCommand = (program) => {
|
|
1147
1313
|
setCommandExamples(
|
|
1148
|
-
program.command("scan").description("Run an autodetected static IaC scan").argument("[path]", "Terraform file, CloudFormation template, or directory to scan").option("--config <path>", "Explicit CloudBurn config file to load").option(
|
|
1314
|
+
program.command("scan").description("Run an autodetected static IaC scan").argument("[path]", "Terraform file, CloudFormation template, or directory to scan").option("--config <path>", "Explicit CloudBurn config file to load").option(
|
|
1315
|
+
"--enabled-rules <ruleIds>",
|
|
1316
|
+
"Comma-separated rule IDs to enable. When set, CloudBurn checks only these rules. By default, all rules are enabled.",
|
|
1317
|
+
parseRuleIdList
|
|
1318
|
+
).option(
|
|
1319
|
+
"--disabled-rules <ruleIds>",
|
|
1320
|
+
"Comma-separated rule IDs to disable. By default, all rules are enabled; use this to exclude specific rules.",
|
|
1321
|
+
parseRuleIdList
|
|
1322
|
+
).option("--exit-code", "Exit with code 1 when findings exist").action(async (path, options, command) => {
|
|
1149
1323
|
try {
|
|
1150
1324
|
const scanner = new CloudBurnClient2();
|
|
1151
1325
|
const loadedConfig = await scanner.loadConfig(options.config);
|
|
@@ -1174,7 +1348,7 @@ var registerScanCommand = (program) => {
|
|
|
1174
1348
|
// src/cli.ts
|
|
1175
1349
|
var createProgram = () => {
|
|
1176
1350
|
const program = createCliCommand();
|
|
1177
|
-
program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.8.
|
|
1351
|
+
program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.8.3").option("--format <format>", OUTPUT_FORMAT_OPTION_DESCRIPTION, parseOutputFormat);
|
|
1178
1352
|
configureCliHelp(program);
|
|
1179
1353
|
registerCompletionCommand(program);
|
|
1180
1354
|
registerDiscoverCommand(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudburn",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Cloudburn CLI for cloud cost optimization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"commander": "^13.1.0",
|
|
14
|
-
"@cloudburn/sdk": "0.13.
|
|
14
|
+
"@cloudburn/sdk": "0.13.2"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@biomejs/biome": "^2.4.6",
|