cloudburn 0.9.8 → 0.9.10

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.
Files changed (3) hide show
  1. package/README.md +4 -4
  2. package/dist/cli.js +92 -75
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -60,21 +60,21 @@ Use `discover` to run the same rules against live AWS resources.
60
60
 
61
61
  Run `cloudburn discover init` first. It automatically configures AWS Resource Explorer indexes, which CloudBurn uses as its live service catalog before it evaluates rules.
62
62
 
63
- By default, `cloudburn discover` runs against your active AWS region. You can pass `--region <region>` to target another region, or use `--region all` to run against all indexed regions through the AWS Resource Explorer aggregator.
63
+ By default, `cloudburn discover` runs against your active AWS region. You can pass `--region <region>` to target one explicit region.
64
64
 
65
65
  ```bash
66
66
  cloudburn discover init
67
67
  cloudburn discover
68
68
  cloudburn discover --region eu-central-1
69
- cloudburn discover --region all
70
69
  cloudburn discover --config .cloudburn.yml --enabled-rules CLDBRN-AWS-EBS-1
71
70
  cloudburn discover --service ec2,s3
72
- cloudburn discover list-enabled-regions --format text
71
+ cloudburn --debug discover --region eu-central-1
73
72
  cloudburn rules list
74
73
  cloudburn rules list --service ec2 --source discovery
75
74
  ```
76
75
 
77
- `cloudburn discover --region all` needs an AWS Resource Explorer aggregator and an unfiltered default view in the aggregator region.
76
+ The CLI targets one region per run. Multi-region discovery remains available through the SDK and still needs an AWS Resource Explorer aggregator plus an unfiltered default view in the aggregator region.
77
+ Use `--debug` to print SDK and provider execution tracing to `stderr` without changing the normal `stdout` format.
78
78
 
79
79
  ## Shell Completion
80
80
 
package/dist/cli.js CHANGED
@@ -557,6 +557,15 @@ var scanColumns = [
557
557
  { key: "column", header: "Column" },
558
558
  { key: "message", header: "Message" }
559
559
  ];
560
+ var diagnosticColumns = [
561
+ { key: "provider", header: "Provider" },
562
+ { key: "status", header: "Status" },
563
+ { key: "ruleId", header: "RuleId" },
564
+ { key: "source", header: "Source" },
565
+ { key: "service", header: "Service" },
566
+ { key: "region", header: "Region" },
567
+ { key: "message", header: "Message" }
568
+ ];
560
569
  var ruleListColumns = [
561
570
  { key: "ruleId", header: "RuleId" },
562
571
  { key: "provider", header: "Provider" },
@@ -637,8 +646,22 @@ ${regionsTable}`;
637
646
  case "rule-list":
638
647
  return renderRuleTable(response.rules, response.emptyMessage);
639
648
  case "scan-result": {
640
- const rows = projectScanRows(response.result);
641
- return rows.length === 0 ? "No findings." : renderAsciiTable(rows, scanColumns);
649
+ const findingRows = projectFindingRows(response.result);
650
+ const diagnosticRows = projectDiagnosticRows(response.result);
651
+ if (findingRows.length === 0 && diagnosticRows.length === 0) {
652
+ return "No findings.";
653
+ }
654
+ if (findingRows.length === 0) {
655
+ return `Diagnostics
656
+ ${renderAsciiTable(diagnosticRows, diagnosticColumns)}`;
657
+ }
658
+ if (diagnosticRows.length === 0) {
659
+ return renderAsciiTable(findingRows, scanColumns);
660
+ }
661
+ return `${renderAsciiTable(findingRows, scanColumns)}
662
+
663
+ Diagnostics
664
+ ${renderAsciiTable(diagnosticRows, diagnosticColumns)}`;
642
665
  }
643
666
  case "status":
644
667
  return renderAsciiTable(
@@ -655,34 +678,28 @@ ${regionsTable}`;
655
678
  );
656
679
  }
657
680
  };
658
- var projectScanRows = (result) => [
659
- ...flattenScanResult(result).map(({ finding, message, provider, ruleId, service, source }) => ({
660
- accountId: finding.accountId ?? "",
661
- message,
662
- path: finding.location?.path ?? "",
663
- provider,
664
- region: finding.region ?? "",
665
- resourceId: finding.resourceId,
666
- ruleId,
667
- service,
668
- source,
669
- column: finding.location?.column ?? "",
670
- line: finding.location?.line ?? ""
671
- })),
672
- ...getScanDiagnostics(result).map((diagnostic) => ({
673
- accountId: "",
674
- column: "",
675
- line: "",
676
- message: diagnostic.message,
677
- path: "",
678
- provider: diagnostic.provider,
679
- region: diagnostic.region ?? "",
680
- resourceId: "",
681
- ruleId: "",
682
- service: diagnostic.service,
683
- source: diagnostic.source
684
- }))
685
- ];
681
+ var projectFindingRows = (result) => flattenScanResult(result).map(({ finding, message, provider, ruleId, service, source }) => ({
682
+ accountId: finding.accountId ?? "",
683
+ message,
684
+ path: finding.location?.path ?? "",
685
+ provider,
686
+ region: finding.region ?? "",
687
+ resourceId: finding.resourceId,
688
+ ruleId,
689
+ service,
690
+ source,
691
+ column: finding.location?.column ?? "",
692
+ line: finding.location?.line ?? ""
693
+ }));
694
+ var projectDiagnosticRows = (result) => getScanDiagnostics(result).map((diagnostic) => ({
695
+ message: diagnostic.message,
696
+ provider: diagnostic.provider,
697
+ region: diagnostic.region ?? "",
698
+ ruleId: diagnostic.ruleId ?? "",
699
+ service: diagnostic.service,
700
+ source: diagnostic.source,
701
+ status: diagnostic.status
702
+ }));
686
703
  var inferColumns = (rows) => {
687
704
  const keys = Array.from(new Set(rows.flatMap((row) => Object.keys(row)))).sort(
688
705
  (left, right) => left.localeCompare(right)
@@ -1023,9 +1040,24 @@ var registerConfigCommand = (program) => {
1023
1040
  };
1024
1041
 
1025
1042
  // src/commands/discover.ts
1026
- import { assertValidAwsRegion, CloudBurnClient } from "@cloudburn/sdk";
1043
+ import { assertSupportedAwsRegion, CloudBurnClient } from "@cloudburn/sdk";
1027
1044
  import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
1028
1045
 
1046
+ // src/debug.ts
1047
+ var isDebugEnabled = (command) => {
1048
+ const options = command.optsWithGlobals();
1049
+ return options.debug === true;
1050
+ };
1051
+ var resolveCliDebugLogger = (command) => {
1052
+ if (!isDebugEnabled(command)) {
1053
+ return void 0;
1054
+ }
1055
+ return (message) => {
1056
+ process.stderr.write(`[debug] ${message}
1057
+ `);
1058
+ };
1059
+ };
1060
+
1029
1061
  // src/commands/config-options.ts
1030
1062
  import { builtInRuleMetadata } from "@cloudburn/sdk";
1031
1063
  import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
@@ -1119,18 +1151,25 @@ var buildInitializationStatusData = (result, message, format) => {
1119
1151
  };
1120
1152
  var parseAwsRegion = (value) => {
1121
1153
  try {
1122
- return assertValidAwsRegion(value);
1154
+ return assertSupportedAwsRegion(value);
1123
1155
  } catch (err) {
1124
1156
  throw new InvalidArgumentError3(err instanceof Error ? err.message : "Invalid AWS region.");
1125
1157
  }
1126
1158
  };
1127
- var parseDiscoverRegion = (value) => {
1128
- if (value === "all") {
1129
- return value;
1159
+ var resolveDiscoveryTarget = (region) => region === void 0 ? { mode: "current" } : { mode: "regions", regions: [region] };
1160
+ var resolveNestedRegionOption = (command, subcommandName) => {
1161
+ const rootCommand = command.parent?.parent;
1162
+ const rawArgs = rootCommand?.rawArgs ?? [];
1163
+ const subcommandIndex = rawArgs.lastIndexOf(subcommandName);
1164
+ if (subcommandIndex === -1) {
1165
+ return void 0;
1130
1166
  }
1131
- return parseAwsRegion(value);
1167
+ const optionIndex = rawArgs.indexOf("--region", subcommandIndex + 1);
1168
+ if (optionIndex === -1 || optionIndex + 1 >= rawArgs.length) {
1169
+ return void 0;
1170
+ }
1171
+ return rawArgs[optionIndex + 1];
1132
1172
  };
1133
- var resolveDiscoveryTarget = (region) => region === void 0 ? { mode: "current" } : region === "all" ? { mode: "all" } : { mode: "region", region };
1134
1173
  var toDiscoveryConfigOverride = (options) => {
1135
1174
  if (options.enabledRules === void 0 && options.disabledRules === void 0 && options.service === void 0) {
1136
1175
  return void 0;
@@ -1154,10 +1193,10 @@ var runCommand = async (action) => {
1154
1193
  };
1155
1194
  var registerDiscoverCommand = (program) => {
1156
1195
  const discoverCommand = setCommandExamples(
1157
- program.command("discover").description("Run a live AWS discovery").enablePositionalOptions().option(
1196
+ program.command("discover").description("Run a live AWS discovery").option(
1158
1197
  "--region <region>",
1159
- '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.',
1160
- parseDiscoverRegion
1198
+ "AWS region to discover. Defaults to the current AWS region from AWS_REGION when omitted.",
1199
+ parseAwsRegion
1161
1200
  ).option("--config <path>", "Explicit CloudBurn config file to load").option(
1162
1201
  "--enabled-rules <ruleIds>",
1163
1202
  "Comma-separated rule IDs to enable. When set, CloudBurn checks only these rules. By default, all rules are enabled.",
@@ -1172,7 +1211,8 @@ var registerDiscoverCommand = (program) => {
1172
1211
  parseDiscoveryServiceList
1173
1212
  ).option("--exit-code", "Exit with code 1 when findings exist").action(async (options, command) => {
1174
1213
  await runCommand(async () => {
1175
- const scanner = new CloudBurnClient();
1214
+ const debugLogger = resolveCliDebugLogger(command);
1215
+ const scanner = new CloudBurnClient({ debugLogger });
1176
1216
  const configOverride = toDiscoveryConfigOverride(options);
1177
1217
  const loadedConfig = await scanner.loadConfig(options.config);
1178
1218
  const discoveryOptions = {
@@ -1198,18 +1238,15 @@ var registerDiscoverCommand = (program) => {
1198
1238
  [
1199
1239
  "cloudburn discover",
1200
1240
  "cloudburn discover --region eu-central-1",
1201
- "cloudburn discover --region all",
1202
1241
  "cloudburn discover status",
1203
- "cloudburn discover list-enabled-regions",
1204
1242
  "cloudburn discover init"
1205
1243
  ]
1206
1244
  );
1207
1245
  discoverCommand.command("status").description("Show Resource Explorer status across all enabled AWS regions").action(async (_options, command) => {
1208
1246
  await runCommand(async () => {
1209
- const scanner = new CloudBurnClient();
1210
- const parentRegion = discoverCommand.opts().region;
1211
- const region = parentRegion === "all" ? void 0 : parentRegion;
1212
- const status = await scanner.getDiscoveryStatus({ region });
1247
+ const debugLogger = resolveCliDebugLogger(command);
1248
+ const scanner = new CloudBurnClient({ debugLogger });
1249
+ const status = await scanner.getDiscoveryStatus({ region: void 0 });
1213
1250
  const format = resolveOutputFormat(command);
1214
1251
  const rows = format === "json" ? status.regions.map((regionStatus) => ({
1215
1252
  ...regionStatus,
@@ -1252,28 +1289,6 @@ var registerDiscoverCommand = (program) => {
1252
1289
  format
1253
1290
  );
1254
1291
  process.stdout.write(`${output}
1255
- `);
1256
- return EXIT_CODE_OK;
1257
- });
1258
- });
1259
- discoverCommand.command("list-enabled-regions").description("List AWS regions with a local or aggregator Resource Explorer index").action(async (_options, command) => {
1260
- await runCommand(async () => {
1261
- const scanner = new CloudBurnClient();
1262
- const regions = await scanner.listEnabledDiscoveryRegions();
1263
- const format = resolveOutputFormat(command);
1264
- const output = renderResponse(
1265
- {
1266
- kind: "record-list",
1267
- columns: [
1268
- { key: "region", header: "Region" },
1269
- { key: "type", header: "Type" }
1270
- ],
1271
- emptyMessage: "No Resource Explorer indexes are enabled.",
1272
- rows: regions
1273
- },
1274
- format
1275
- );
1276
- process.stdout.write(`${output}
1277
1292
  `);
1278
1293
  return EXIT_CODE_OK;
1279
1294
  });
@@ -1284,9 +1299,9 @@ var registerDiscoverCommand = (program) => {
1284
1299
  parseAwsRegion
1285
1300
  ).action(async (options, command) => {
1286
1301
  await runCommand(async () => {
1287
- const scanner = new CloudBurnClient();
1288
- const parentRegion = discoverCommand.opts().region;
1289
- const region = options.region ?? (parentRegion === "all" ? void 0 : parentRegion);
1302
+ const debugLogger = resolveCliDebugLogger(command);
1303
+ const scanner = new CloudBurnClient({ debugLogger });
1304
+ const region = options.region ?? resolveNestedRegionOption(command, "init");
1290
1305
  const result = await scanner.initializeDiscovery({ region });
1291
1306
  const message = describeInitializationMessage(result);
1292
1307
  const format = resolveOutputFormat(command);
@@ -1304,7 +1319,8 @@ var registerDiscoverCommand = (program) => {
1304
1319
  });
1305
1320
  discoverCommand.command("supported-resource-types").description("List Resource Explorer supported AWS resource types").action(async (_options, command) => {
1306
1321
  await runCommand(async () => {
1307
- const scanner = new CloudBurnClient();
1322
+ const debugLogger = resolveCliDebugLogger(command);
1323
+ const scanner = new CloudBurnClient({ debugLogger });
1308
1324
  const resourceTypes = await scanner.listSupportedDiscoveryResourceTypes();
1309
1325
  const format = resolveOutputFormat(command);
1310
1326
  const output = renderResponse(
@@ -1434,7 +1450,8 @@ var registerScanCommand = (program) => {
1434
1450
  parseRuleIdList
1435
1451
  ).option("--service <services>", "Comma-separated services to include in the scan rule set.", parseIaCServiceList).option("--exit-code", "Exit with code 1 when findings exist").action(async (path, options, command) => {
1436
1452
  try {
1437
- const scanner = new CloudBurnClient2();
1453
+ const debugLogger = resolveCliDebugLogger(command);
1454
+ const scanner = new CloudBurnClient2({ debugLogger });
1438
1455
  const configOverride = toScanConfigOverride(options);
1439
1456
  const loadedConfig = await scanner.loadConfig(options.config);
1440
1457
  const scanPath = path ?? process.cwd();
@@ -1474,7 +1491,7 @@ var isCliEntrypoint = (moduleUrl, argvEntry = process.argv[1]) => {
1474
1491
  };
1475
1492
  var createProgram = () => {
1476
1493
  const program = createCliCommand();
1477
- program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.9.8").option("--format <format>", OUTPUT_FORMAT_OPTION_DESCRIPTION, parseOutputFormat);
1494
+ program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.9.10").option("--debug", "Write execution trace messages to stderr").option("--format <format>", OUTPUT_FORMAT_OPTION_DESCRIPTION, parseOutputFormat);
1478
1495
  configureCliHelp(program);
1479
1496
  registerCompletionCommand(program);
1480
1497
  registerConfigCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudburn",
3
- "version": "0.9.8",
3
+ "version": "0.9.10",
4
4
  "description": "Cloudburn CLI for cloud cost optimization",
5
5
  "homepage": "https://cloudburn.io/docs",
6
6
  "bugs": {
@@ -20,7 +20,7 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "commander": "^13.1.0",
23
- "@cloudburn/sdk": "0.21.0"
23
+ "@cloudburn/sdk": "0.21.2"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@biomejs/biome": "^2.4.6",