@cdot65/prisma-airs-cli 3.3.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  sanitizeFilename,
21
21
  validateTopic,
22
22
  writeBackupFile
23
- } from "../chunk-2VIUZRPB.js";
23
+ } from "../chunk-W5YDJS7H.js";
24
24
 
25
25
  // src/cli/index.ts
26
26
  import "dotenv/config";
@@ -187,9 +187,15 @@ var SdkAiGatewayService = class {
187
187
  workspaceSlug,
188
188
  days,
189
189
  totalCents: raw.data.total,
190
+ totalUsd: raw.data.total / 100,
190
191
  avgCents: raw.data.avg,
192
+ avgUsd: raw.data.avg / 100,
191
193
  quotaExceeded: raw.data.isQuotaExceeded,
192
- records: raw.data.records.map((r) => ({ date: r.x, costCents: r.y }))
194
+ records: raw.data.records.map((r) => ({
195
+ date: r.x,
196
+ costCents: r.y,
197
+ costUsd: r.y / 100
198
+ }))
193
199
  };
194
200
  }
195
201
  /** Re-read after a write, falling back to the (partial) write response if the get fails. */
@@ -249,7 +255,11 @@ import { dump as yamlDump } from "js-yaml";
249
255
 
250
256
  // src/cli/renderer/common.ts
251
257
  import chalk2 from "chalk";
258
+ import { dump } from "js-yaml";
259
+ var CliUsageError = class extends Error {
260
+ };
252
261
  function fail(err) {
262
+ if (err instanceof CliUsageError) usageError(err.message);
253
263
  const message = err instanceof Error ? err.message : String(err);
254
264
  const status = err?.status ?? err?.statusCode;
255
265
  console.error(chalk2.red(`
@@ -267,44 +277,90 @@ function usageError(message) {
267
277
  `));
268
278
  process.exit(2);
269
279
  }
270
- var OUTPUT_FORMATS = ["pretty", "table", "csv", "json", "yaml"];
280
+ var OUTPUT_FORMATS = [
281
+ "pretty",
282
+ "table",
283
+ "markdown",
284
+ "csv",
285
+ "json",
286
+ "yaml"
287
+ ];
288
+ async function resolveOutput(command, opts, resolution = {}) {
289
+ const localIsExplicit = command.getOptionValueSource?.("output") === "cli";
290
+ let rootCommand = command;
291
+ while (rootCommand.parent) rootCommand = rootCommand.parent;
292
+ const globalIsExplicit = rootCommand.getOptionValueSource?.("output") === "cli";
293
+ const globalOutput = globalIsExplicit ? rootCommand.opts().output : void 0;
294
+ let configured;
295
+ try {
296
+ configured = (await loadConfig()).defaultOutput;
297
+ } catch (error) {
298
+ if (process.env.PANW_CLI_OUTPUT !== void 0) configured = process.env.PANW_CLI_OUTPUT;
299
+ else throw error;
300
+ }
301
+ const candidate = String(
302
+ localIsExplicit ? opts.output : globalOutput ?? configured ?? "pretty"
303
+ );
304
+ if (!OUTPUT_FORMATS.includes(candidate))
305
+ throw new CliUsageError(
306
+ `Invalid output format '${candidate}'. Expected: ${OUTPUT_FORMATS.join(", ")}`
307
+ );
308
+ const format = candidate;
309
+ const allowed = resolution.allowed ?? OUTPUT_FORMATS;
310
+ if (!allowed.includes(format))
311
+ throw new CliUsageError(
312
+ `Output format '${format}' is not supported here. Expected: ${allowed.join(", ")}`
313
+ );
314
+ return format;
315
+ }
316
+ function displayValue(value) {
317
+ if (value == null) return "";
318
+ return typeof value === "object" ? JSON.stringify(value) : String(value);
319
+ }
320
+ function csvCell(value) {
321
+ const text = displayValue(value);
322
+ return /[",\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
323
+ }
324
+ function markdownCell(value) {
325
+ return displayValue(value).replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r?\n/g, "<br>");
326
+ }
271
327
  function formatOutput(rows, columns, format) {
272
- if (rows.length === 0) return "";
273
- const vals = (row, key) => String(row[key] ?? "");
328
+ if (rows.length === 0) {
329
+ if (format === "json") return "[]";
330
+ if (format === "yaml") return "[]\n";
331
+ return "";
332
+ }
333
+ const projected = rows.map((row) => columns.map((column) => row[column.key]));
274
334
  switch (format) {
275
335
  case "json":
276
- return JSON.stringify(
277
- rows.map((r) => {
278
- const obj = {};
279
- for (const c of columns) obj[c.key] = r[c.key];
280
- return obj;
281
- }),
282
- null,
283
- 2
284
- );
285
- case "csv": {
286
- const header = columns.map((c) => c.label).join(",");
287
- const lines = rows.map(
288
- (r) => columns.map((c) => {
289
- const v = vals(r, c.key);
290
- return v.includes(",") || v.includes('"') ? `"${v.replace(/"/g, '""')}"` : v;
291
- }).join(",")
292
- );
293
- return [header, ...lines].join("\n");
294
- }
295
- case "yaml": {
296
- return rows.map((r) => columns.map((c) => `${c.key}: ${vals(r, c.key)}`).join("\n")).join("\n---\n");
336
+ return JSON.stringify(rows, null, 2);
337
+ case "yaml":
338
+ return dump(rows, { noRefs: true, lineWidth: -1 }).trimEnd();
339
+ case "csv":
340
+ return [
341
+ columns.map((column) => csvCell(column.label)).join(","),
342
+ ...projected.map((values) => values.map(csvCell).join(","))
343
+ ].join("\n");
344
+ case "markdown": {
345
+ const header = `| ${columns.map((column) => markdownCell(column.label)).join(" | ")} |`;
346
+ const divider = `| ${columns.map(() => "---").join(" | ")} |`;
347
+ return [
348
+ header,
349
+ divider,
350
+ ...projected.map((values) => `| ${values.map(markdownCell).join(" | ")} |`)
351
+ ].join("\n");
297
352
  }
298
353
  case "table": {
354
+ const values = projected.map((row) => row.map(displayValue));
299
355
  const widths = columns.map(
300
- (c) => Math.max(c.label.length, ...rows.map((r) => vals(r, c.key).length))
356
+ (column, index) => Math.max(column.label.length, ...values.map((row) => row[index].length))
301
357
  );
302
- const sep = widths.map((w) => "\u2500".repeat(w + 2)).join("\u253C");
303
- const header = columns.map((c, i) => ` ${c.label.padEnd(widths[i])} `).join("\u2502");
304
- const body = rows.map(
305
- (r) => columns.map((c, i) => ` ${vals(r, c.key).padEnd(widths[i])} `).join("\u2502")
358
+ const separator = widths.map((width) => "\u2500".repeat(width + 2)).join("\u253C");
359
+ const header = columns.map((column, index) => ` ${column.label.padEnd(widths[index])} `).join("\u2502");
360
+ const body = values.map(
361
+ (row) => row.map((value, index) => ` ${value.padEnd(widths[index])} `).join("\u2502")
306
362
  );
307
- return [header, sep, ...body].join("\n");
363
+ return [header, separator, ...body].join("\n");
308
364
  }
309
365
  default:
310
366
  return "";
@@ -403,6 +459,65 @@ ${INDENT}${chalk3.bold(label)}
403
459
  }
404
460
  };
405
461
 
462
+ // src/cli/renderer/view.ts
463
+ import { dump as dump2 } from "js-yaml";
464
+ function asRecord(item) {
465
+ return item;
466
+ }
467
+ function project(view, item) {
468
+ const source = asRecord(item);
469
+ return Object.fromEntries(
470
+ view.columns.map((column) => [column.key, column.get ? column.get(item) : source[column.key]])
471
+ );
472
+ }
473
+ function renderPageStatus(page) {
474
+ if (!page) return;
475
+ if (page.all) ui.status(`Showing all ${page.total ?? page.returned}`);
476
+ else if (page.total !== void 0)
477
+ ui.status(
478
+ `Showing ${page.returned} of ${page.total}${page.next !== void 0 ? ` (next --offset ${page.next})` : ""}`
479
+ );
480
+ else if (page.next !== void 0) ui.status(`Showing ${page.returned} (more available)`);
481
+ else ui.status(`Showing ${page.returned}`);
482
+ }
483
+ function emitList(view, items, format, opts = {}) {
484
+ if (format === "pretty") {
485
+ if (items.length === 0) ui.emptyList(view.name);
486
+ else view.pretty.list(items);
487
+ } else {
488
+ const rows = format === "json" || format === "yaml" ? items.map((item) => view.structured?.(item) ?? asRecord(item)) : items.map((item) => project(view, item));
489
+ const rendered = formatOutput(rows, view.columns, format);
490
+ if (rendered) console.log(rendered);
491
+ }
492
+ renderPageStatus(opts.page);
493
+ }
494
+ function emitDetail(view, item, format) {
495
+ if (format === "pretty") {
496
+ view.pretty.detail(item);
497
+ return;
498
+ }
499
+ const structured = view.structured?.(item) ?? asRecord(item);
500
+ if (format === "json") console.log(JSON.stringify(structured, null, 2));
501
+ else if (format === "yaml")
502
+ console.log(dump2(structured, { noRefs: true, lineWidth: -1 }).trimEnd());
503
+ else {
504
+ const rows = Object.entries(structured).map(([key, value]) => ({
505
+ key,
506
+ value: value != null && typeof value === "object" ? JSON.stringify(value) : value
507
+ }));
508
+ console.log(
509
+ formatOutput(
510
+ rows,
511
+ [
512
+ { key: "key", label: "Key" },
513
+ { key: "value", label: "Value" }
514
+ ],
515
+ format
516
+ )
517
+ );
518
+ }
519
+ }
520
+
406
521
  // src/cli/renderer/aigateway.ts
407
522
  function renderAiGatewayHeader() {
408
523
  ui.header("Prisma AIRS \u2014 AI Gateway", "Gateway workspace operations");
@@ -498,7 +613,17 @@ function renderWorkspaceDetail(workspace, format = "pretty") {
498
613
  }
499
614
  function renderCostReport(report, format = "pretty") {
500
615
  if (format !== "pretty") {
501
- console.log(format === "json" ? JSON.stringify(report, null, 2) : yamlDump(report));
616
+ emitDetail(
617
+ {
618
+ name: "cost report",
619
+ columns: [],
620
+ pretty: { list() {
621
+ }, detail() {
622
+ } }
623
+ },
624
+ report,
625
+ format
626
+ );
502
627
  return;
503
628
  }
504
629
  const dollars = (cents) => `$${(cents / 100).toFixed(2)}`;
@@ -597,60 +722,67 @@ function pageMeta(page, returned) {
597
722
  returned
598
723
  };
599
724
  }
600
- function emitList(page, fmt, header, toRow, columns, prettyLine) {
601
- const content = Array.isArray(page?.content) ? page.content : [];
602
- const rows = content.map(toRow);
603
- if (fmt === "json" || fmt === "yaml") {
604
- emitStructured({ items: rows, page: pageMeta(page, content.length) }, fmt);
605
- return;
606
- }
607
- if (content.length === 0) {
608
- ui.emptyList(header.toLowerCase());
609
- return;
610
- }
611
- if (fmt === "pretty") {
612
- ui.section(`${header}:`);
613
- for (const item of content) console.log(prettyLine(item));
614
- const meta = pageMeta(page, content.length);
615
- console.log();
616
- ui.dim(
617
- `page=${meta.number} size=${meta.size} returned=${meta.returned} total=${meta.total ?? "?"}`
618
- );
619
- console.log();
620
- return;
621
- }
622
- console.log(formatOutput(rows, columns, fmt));
725
+ function camelKey(key) {
726
+ return key.replace(/[_-]([a-z0-9])/g, (_, char) => char.toUpperCase());
623
727
  }
624
- function fieldsToObject(fields) {
625
- const obj = {};
626
- for (const f of fields) {
627
- if (f.value === void 0 || f.value === null || f.value === "") continue;
628
- obj[toKey(f.label)] = f.value;
728
+ function camelize(value) {
729
+ if (Array.isArray(value)) return value.map(camelize);
730
+ if (value && typeof value === "object") {
731
+ return Object.fromEntries(
732
+ Object.entries(value).map(([key, child]) => [
733
+ camelKey(key),
734
+ camelize(child)
735
+ ])
736
+ );
629
737
  }
630
- return obj;
631
- }
632
- function toKey(label) {
633
- return label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
738
+ return value;
634
739
  }
635
- function emitDetail(_item, fmt, fields, title) {
636
- if (fmt === "json" || fmt === "yaml") {
637
- emitStructured(fieldsToObject(fields), fmt);
638
- return;
639
- }
640
- if (fmt === "pretty") {
641
- ui.section(`${title}:`);
642
- const pairs = [];
643
- for (const f of fields) {
644
- if (f.value === void 0 || f.value === null || f.value === "") continue;
645
- pairs.push([f.label, f.value]);
740
+ function emitList2(page, fmt, header, toRow, columns, prettyLine) {
741
+ const content = Array.isArray(page?.content) ? page.content : [];
742
+ const meta = pageMeta(page, content.length);
743
+ const view = {
744
+ name: header.toLowerCase(),
745
+ columns: columns.map((column) => ({
746
+ ...column,
747
+ get: (item) => toRow(item)[column.key]
748
+ })),
749
+ structured: (item) => camelize(item),
750
+ pretty: {
751
+ list(items) {
752
+ ui.section(`${header}:`);
753
+ for (const item of items) console.log(prettyLine(item));
754
+ console.log();
755
+ },
756
+ detail() {
757
+ }
646
758
  }
647
- ui.keyValue(pairs);
648
- console.log();
649
- return;
650
- }
651
- const rows = [Object.fromEntries(fields.map((f) => [f.label, f.value ?? ""]))];
652
- const columns = fields.map((f) => ({ key: f.label, label: f.label }));
653
- console.log(formatOutput(rows, columns, fmt));
759
+ };
760
+ const total = typeof meta.total === "number" ? meta.total : void 0;
761
+ const number = Number(meta.number ?? 0);
762
+ const size = Number(meta.size ?? content.length);
763
+ const next = total !== void 0 && (number + 1) * size < total ? (number + 1) * size : void 0;
764
+ emitList(view, content, fmt, { page: { returned: content.length, total, next } });
765
+ }
766
+ function emitDetail2(item, fmt, fields, title) {
767
+ const view = {
768
+ name: title.toLowerCase(),
769
+ columns: [],
770
+ structured: (value) => camelize(value),
771
+ pretty: {
772
+ list() {
773
+ },
774
+ detail() {
775
+ ui.section(`${title}:`);
776
+ ui.keyValue(
777
+ fields.filter(
778
+ (field) => field.value !== void 0 && field.value !== null && field.value !== ""
779
+ ).map((field) => [field.label, field.value])
780
+ );
781
+ console.log();
782
+ }
783
+ }
784
+ };
785
+ emitDetail(view, item, fmt);
654
786
  }
655
787
  function ackObject(verb, item) {
656
788
  const out = { action: verb };
@@ -681,7 +813,7 @@ function emitIdAck(verb, id) {
681
813
  }
682
814
  var dlpFilteringProfiles = {
683
815
  renderList(page, fmt) {
684
- emitList(
816
+ emitList2(
685
817
  page,
686
818
  fmt,
687
819
  "Data Filtering Profiles",
@@ -711,7 +843,7 @@ var dlpFilteringProfiles = {
711
843
  );
712
844
  },
713
845
  renderGet(item, fmt) {
714
- emitDetail(
846
+ emitDetail2(
715
847
  item,
716
848
  fmt,
717
849
  [
@@ -740,7 +872,7 @@ var dlpFilteringProfiles = {
740
872
  };
741
873
  var dlpPatterns = {
742
874
  renderList(page, fmt) {
743
- emitList(
875
+ emitList2(
744
876
  page,
745
877
  fmt,
746
878
  "Data Patterns",
@@ -769,7 +901,7 @@ var dlpPatterns = {
769
901
  );
770
902
  },
771
903
  renderGet(item, fmt) {
772
- emitDetail(
904
+ emitDetail2(
773
905
  item,
774
906
  fmt,
775
907
  [
@@ -808,7 +940,7 @@ var dlpPatterns = {
808
940
  };
809
941
  var dlpProfiles = {
810
942
  renderList(page, fmt) {
811
- emitList(
943
+ emitList2(
812
944
  page,
813
945
  fmt,
814
946
  "Data Profiles",
@@ -837,7 +969,7 @@ var dlpProfiles = {
837
969
  );
838
970
  },
839
971
  renderGet(item, fmt) {
840
- emitDetail(
972
+ emitDetail2(
841
973
  item,
842
974
  fmt,
843
975
  [
@@ -865,7 +997,7 @@ var dlpProfiles = {
865
997
  };
866
998
  var dlpDictionaries = {
867
999
  renderList(page, fmt) {
868
- emitList(
1000
+ emitList2(
869
1001
  page,
870
1002
  fmt,
871
1003
  "Data Dictionaries",
@@ -894,7 +1026,7 @@ var dlpDictionaries = {
894
1026
  );
895
1027
  },
896
1028
  renderGet(item, fmt) {
897
- emitDetail(
1029
+ emitDetail2(
898
1030
  item,
899
1031
  fmt,
900
1032
  [
@@ -989,7 +1121,15 @@ function renderEvalTerminal(output) {
989
1121
 
990
1122
  // src/cli/renderer/modelsecurity.ts
991
1123
  import chalk7 from "chalk";
992
- import { dump as yamlDump3 } from "js-yaml";
1124
+ function resourceView(name, columns, pretty) {
1125
+ return { name, columns, pretty };
1126
+ }
1127
+ function structuredList(name, items, columns, format, pretty) {
1128
+ emitList(resourceView(name, columns, { list: pretty, detail: () => void 0 }), items, format);
1129
+ }
1130
+ function structuredDetail(name, item, format, pretty) {
1131
+ emitDetail(resourceView(name, [], { list: () => void 0, detail: pretty }), item, format);
1132
+ }
993
1133
  function renderModelSecurityHeader() {
994
1134
  ui.header("Prisma AIRS \u2014 Model Security", "ML model supply chain security");
995
1135
  }
@@ -1012,31 +1152,25 @@ function stateColor(state) {
1012
1152
  }
1013
1153
  }
1014
1154
  function renderGroupList(groups, format = "pretty") {
1015
- if (groups.length === 0) {
1016
- ui.emptyList("security groups");
1017
- return;
1018
- }
1019
1155
  if (format !== "pretty") {
1020
- const rows = groups.map((g) => ({
1021
- id: g.uuid,
1022
- name: g.name,
1023
- state: g.state,
1024
- sourceType: g.sourceType
1025
- }));
1026
- console.log(
1027
- formatOutput(
1028
- rows,
1029
- [
1030
- { key: "id", label: "ID" },
1031
- { key: "name", label: "Name" },
1032
- { key: "state", label: "State" },
1033
- { key: "sourceType", label: "Source Type" }
1034
- ],
1035
- format
1036
- )
1156
+ structuredList(
1157
+ "security groups",
1158
+ groups,
1159
+ [
1160
+ { key: "uuid", label: "ID" },
1161
+ { key: "name", label: "Name" },
1162
+ { key: "state", label: "State" },
1163
+ { key: "sourceType", label: "Source Type" }
1164
+ ],
1165
+ format,
1166
+ () => void 0
1037
1167
  );
1038
1168
  return;
1039
1169
  }
1170
+ if (groups.length === 0) {
1171
+ ui.emptyList("security groups");
1172
+ return;
1173
+ }
1040
1174
  ui.section("Security Groups:");
1041
1175
  for (const g of groups) {
1042
1176
  ui.dim(g.uuid);
@@ -1046,12 +1180,8 @@ function renderGroupList(groups, format = "pretty") {
1046
1180
  console.log();
1047
1181
  }
1048
1182
  function renderGroupDetail(group, format = "pretty") {
1049
- if (format === "json") {
1050
- console.log(JSON.stringify(group, null, 2));
1051
- return;
1052
- }
1053
- if (format === "yaml") {
1054
- console.log(yamlDump3(group));
1183
+ if (format !== "pretty") {
1184
+ structuredDetail("security group", group, format, () => void 0);
1055
1185
  return;
1056
1186
  }
1057
1187
  ui.section("Security Group Detail:");
@@ -1068,33 +1198,30 @@ function renderGroupDetail(group, format = "pretty") {
1068
1198
  console.log();
1069
1199
  }
1070
1200
  function renderRuleList(rules, format = "pretty") {
1071
- if (rules.length === 0) {
1072
- ui.emptyList("security rules");
1073
- return;
1074
- }
1075
1201
  if (format !== "pretty") {
1076
- const rows = rules.map((r) => ({
1077
- id: r.uuid,
1078
- name: r.name,
1079
- type: r.ruleType,
1080
- defaultState: r.defaultState,
1081
- sources: r.compatibleSources.join(", ")
1082
- }));
1083
- console.log(
1084
- formatOutput(
1085
- rows,
1086
- [
1087
- { key: "id", label: "ID" },
1088
- { key: "name", label: "Name" },
1089
- { key: "type", label: "Type" },
1090
- { key: "defaultState", label: "Default State" },
1091
- { key: "sources", label: "Sources" }
1092
- ],
1093
- format
1094
- )
1202
+ structuredList(
1203
+ "security rules",
1204
+ rules,
1205
+ [
1206
+ { key: "uuid", label: "ID" },
1207
+ { key: "name", label: "Name" },
1208
+ { key: "ruleType", label: "Type" },
1209
+ { key: "defaultState", label: "Default State" },
1210
+ {
1211
+ key: "compatibleSources",
1212
+ label: "Sources",
1213
+ get: (rule) => rule.compatibleSources.join(", ")
1214
+ }
1215
+ ],
1216
+ format,
1217
+ () => void 0
1095
1218
  );
1096
1219
  return;
1097
1220
  }
1221
+ if (rules.length === 0) {
1222
+ ui.emptyList("security rules");
1223
+ return;
1224
+ }
1098
1225
  ui.section("Security Rules:");
1099
1226
  for (const r of rules) {
1100
1227
  ui.dim(r.uuid);
@@ -1106,7 +1233,11 @@ function renderRuleList(rules, format = "pretty") {
1106
1233
  }
1107
1234
  console.log();
1108
1235
  }
1109
- function renderRuleDetail(rule) {
1236
+ function renderRuleDetail(rule, format = "pretty") {
1237
+ if (format !== "pretty") {
1238
+ structuredDetail("security rule", rule, format, () => void 0);
1239
+ return;
1240
+ }
1110
1241
  ui.section("Security Rule Detail:");
1111
1242
  ui.keyValue([
1112
1243
  ["UUID", rule.uuid],
@@ -1137,7 +1268,21 @@ function renderRuleDetail(rule) {
1137
1268
  }
1138
1269
  console.log();
1139
1270
  }
1140
- function renderRuleInstanceList(instances) {
1271
+ function renderRuleInstanceList(instances, format = "pretty") {
1272
+ if (format !== "pretty") {
1273
+ structuredList(
1274
+ "rule instances",
1275
+ instances,
1276
+ [
1277
+ { key: "uuid", label: "ID" },
1278
+ { key: "securityRuleUuid", label: "Rule ID" },
1279
+ { key: "state", label: "State" }
1280
+ ],
1281
+ format,
1282
+ () => void 0
1283
+ );
1284
+ return;
1285
+ }
1141
1286
  if (instances.length === 0) {
1142
1287
  ui.emptyList("rule instances");
1143
1288
  return;
@@ -1150,7 +1295,11 @@ function renderRuleInstanceList(instances) {
1150
1295
  }
1151
1296
  console.log();
1152
1297
  }
1153
- function renderRuleInstanceDetail(instance) {
1298
+ function renderRuleInstanceDetail(instance, format = "pretty") {
1299
+ if (format !== "pretty") {
1300
+ structuredDetail("rule instance", instance, format, () => void 0);
1301
+ return;
1302
+ }
1154
1303
  ui.section("Rule Instance Detail:");
1155
1304
  const pairs = [
1156
1305
  ["UUID", instance.uuid],
@@ -1175,37 +1324,26 @@ function renderRuleInstanceDetail(instance) {
1175
1324
  console.log();
1176
1325
  }
1177
1326
  function renderMsScanList(scans, format = "pretty") {
1178
- if (scans.length === 0) {
1179
- ui.emptyList("scans");
1180
- return;
1181
- }
1182
1327
  if (format !== "pretty") {
1183
- const rows = scans.map((s) => ({
1184
- id: s.uuid,
1185
- outcome: s.evalOutcome,
1186
- origin: s.scanOrigin,
1187
- modelUri: s.modelUri ?? "",
1188
- createdAt: s.createdAt,
1189
- passed: s.evalSummary?.rulesPassed ?? "",
1190
- failed: s.evalSummary?.rulesFailed ?? ""
1191
- }));
1192
- console.log(
1193
- formatOutput(
1194
- rows,
1195
- [
1196
- { key: "id", label: "ID" },
1197
- { key: "outcome", label: "Outcome" },
1198
- { key: "origin", label: "Origin" },
1199
- { key: "modelUri", label: "Model URI" },
1200
- { key: "passed", label: "Passed" },
1201
- { key: "failed", label: "Failed" },
1202
- { key: "createdAt", label: "Created" }
1203
- ],
1204
- format
1205
- )
1328
+ structuredList(
1329
+ "scans",
1330
+ scans,
1331
+ [
1332
+ { key: "uuid", label: "ID" },
1333
+ { key: "evalOutcome", label: "Outcome" },
1334
+ { key: "scanOrigin", label: "Origin" },
1335
+ { key: "modelUri", label: "Model URI" },
1336
+ { key: "createdAt", label: "Created" }
1337
+ ],
1338
+ format,
1339
+ () => void 0
1206
1340
  );
1207
1341
  return;
1208
1342
  }
1343
+ if (scans.length === 0) {
1344
+ ui.emptyList("scans");
1345
+ return;
1346
+ }
1209
1347
  ui.section("Model Security Scans:");
1210
1348
  for (const s of scans) {
1211
1349
  ui.dim(s.uuid);
@@ -1222,7 +1360,11 @@ function renderMsScanList(scans, format = "pretty") {
1222
1360
  }
1223
1361
  console.log();
1224
1362
  }
1225
- function renderMsScanDetail(scan) {
1363
+ function renderMsScanDetail(scan, format = "pretty") {
1364
+ if (format !== "pretty") {
1365
+ structuredDetail("scan", scan, format, () => void 0);
1366
+ return;
1367
+ }
1226
1368
  ui.section("Scan Detail:");
1227
1369
  const pairs = [
1228
1370
  ["UUID", scan.uuid],
@@ -1248,7 +1390,22 @@ function renderMsScanDetail(scan) {
1248
1390
  }
1249
1391
  console.log();
1250
1392
  }
1251
- function renderEvaluationList(evaluations) {
1393
+ function renderEvaluationList(evaluations, format = "pretty") {
1394
+ if (format !== "pretty") {
1395
+ structuredList(
1396
+ "evaluations",
1397
+ evaluations,
1398
+ [
1399
+ { key: "uuid", label: "ID" },
1400
+ { key: "ruleName", label: "Rule" },
1401
+ { key: "result", label: "Result" },
1402
+ { key: "ruleInstanceState", label: "State" }
1403
+ ],
1404
+ format,
1405
+ () => void 0
1406
+ );
1407
+ return;
1408
+ }
1252
1409
  if (evaluations.length === 0) {
1253
1410
  ui.emptyList("evaluations");
1254
1411
  return;
@@ -1262,7 +1419,11 @@ function renderEvaluationList(evaluations) {
1262
1419
  }
1263
1420
  console.log();
1264
1421
  }
1265
- function renderEvaluationDetail(evaluation) {
1422
+ function renderEvaluationDetail(evaluation, format = "pretty") {
1423
+ if (format !== "pretty") {
1424
+ structuredDetail("evaluation", evaluation, format, () => void 0);
1425
+ return;
1426
+ }
1266
1427
  ui.section("Evaluation Detail:");
1267
1428
  ui.keyValue([
1268
1429
  ["UUID", evaluation.uuid],
@@ -1275,7 +1436,22 @@ function renderEvaluationDetail(evaluation) {
1275
1436
  ]);
1276
1437
  console.log();
1277
1438
  }
1278
- function renderViolationList(violations) {
1439
+ function renderViolationList(violations, format = "pretty") {
1440
+ if (format !== "pretty") {
1441
+ structuredList(
1442
+ "violations",
1443
+ violations,
1444
+ [
1445
+ { key: "uuid", label: "ID" },
1446
+ { key: "ruleName", label: "Rule" },
1447
+ { key: "file", label: "File" },
1448
+ { key: "threat", label: "Threat" }
1449
+ ],
1450
+ format,
1451
+ () => void 0
1452
+ );
1453
+ return;
1454
+ }
1279
1455
  if (violations.length === 0) {
1280
1456
  ui.emptyList("violations");
1281
1457
  return;
@@ -1289,7 +1465,11 @@ function renderViolationList(violations) {
1289
1465
  }
1290
1466
  console.log();
1291
1467
  }
1292
- function renderViolationDetail(violation) {
1468
+ function renderViolationDetail(violation, format = "pretty") {
1469
+ if (format !== "pretty") {
1470
+ structuredDetail("violation", violation, format, () => void 0);
1471
+ return;
1472
+ }
1293
1473
  ui.section("Violation Detail:");
1294
1474
  ui.keyValue([
1295
1475
  ["UUID", violation.uuid],
@@ -1302,7 +1482,22 @@ function renderViolationDetail(violation) {
1302
1482
  ]);
1303
1483
  console.log();
1304
1484
  }
1305
- function renderFileList(files) {
1485
+ function renderFileList(files, format = "pretty") {
1486
+ if (format !== "pretty") {
1487
+ structuredList(
1488
+ "files",
1489
+ files,
1490
+ [
1491
+ { key: "uuid", label: "ID" },
1492
+ { key: "path", label: "Path" },
1493
+ { key: "type", label: "Type" },
1494
+ { key: "result", label: "Result" }
1495
+ ],
1496
+ format,
1497
+ () => void 0
1498
+ );
1499
+ return;
1500
+ }
1306
1501
  if (files.length === 0) {
1307
1502
  ui.emptyList("files");
1308
1503
  return;
@@ -1338,33 +1533,30 @@ function renderLabelValues(key, values) {
1338
1533
  console.log();
1339
1534
  }
1340
1535
  function renderModelList(models, format = "pretty") {
1341
- if (models.length === 0) {
1342
- ui.emptyList("models");
1343
- return;
1344
- }
1345
1536
  if (format !== "pretty") {
1346
- const rows = models.map((m) => ({
1347
- id: m.uuid,
1348
- name: m.name,
1349
- outcome: m.latestVersionOutcome ?? "",
1350
- formats: (m.latestVersionFormats ?? []).join(", "),
1351
- scanned: m.latestVersionScanTime ?? ""
1352
- }));
1353
- console.log(
1354
- formatOutput(
1355
- rows,
1356
- [
1357
- { key: "id", label: "ID" },
1358
- { key: "name", label: "Name" },
1359
- { key: "outcome", label: "Outcome" },
1360
- { key: "formats", label: "Formats" },
1361
- { key: "scanned", label: "Last Scan" }
1362
- ],
1363
- format
1364
- )
1537
+ structuredList(
1538
+ "models",
1539
+ models,
1540
+ [
1541
+ { key: "uuid", label: "ID" },
1542
+ { key: "name", label: "Name" },
1543
+ { key: "latestVersionOutcome", label: "Outcome" },
1544
+ {
1545
+ key: "latestVersionFormats",
1546
+ label: "Formats",
1547
+ get: (model) => (model.latestVersionFormats ?? []).join(", ")
1548
+ },
1549
+ { key: "latestVersionScanTime", label: "Last Scan" }
1550
+ ],
1551
+ format,
1552
+ () => void 0
1365
1553
  );
1366
1554
  return;
1367
1555
  }
1556
+ if (models.length === 0) {
1557
+ ui.emptyList("models");
1558
+ return;
1559
+ }
1368
1560
  ui.section("Models:");
1369
1561
  for (const m of models) {
1370
1562
  ui.dim(m.uuid);
@@ -1376,7 +1568,7 @@ function renderModelList(models, format = "pretty") {
1376
1568
  }
1377
1569
  function renderModelDetail(model, format = "pretty") {
1378
1570
  if (format !== "pretty") {
1379
- console.log(format === "json" ? JSON.stringify(model, null, 2) : yamlDump3(model));
1571
+ structuredDetail("model", model, format, () => void 0);
1380
1572
  return;
1381
1573
  }
1382
1574
  ui.section("Model Detail:");
@@ -1403,33 +1595,26 @@ function renderModelDetail(model, format = "pretty") {
1403
1595
  console.log();
1404
1596
  }
1405
1597
  function renderModelVersionList(versions, format = "pretty") {
1406
- if (versions.length === 0) {
1407
- ui.emptyList("versions");
1408
- return;
1409
- }
1410
1598
  if (format !== "pretty") {
1411
- const rows = versions.map((v) => ({
1412
- id: v.uuid,
1413
- revision: v.revision,
1414
- files: v.fileCount ?? "",
1415
- outcome: v.lastEvalOutcome ?? "",
1416
- scanned: v.latestScanTime ?? ""
1417
- }));
1418
- console.log(
1419
- formatOutput(
1420
- rows,
1421
- [
1422
- { key: "id", label: "ID" },
1423
- { key: "revision", label: "Revision" },
1424
- { key: "files", label: "Files" },
1425
- { key: "outcome", label: "Outcome" },
1426
- { key: "scanned", label: "Last Scan" }
1427
- ],
1428
- format
1429
- )
1599
+ structuredList(
1600
+ "versions",
1601
+ versions,
1602
+ [
1603
+ { key: "uuid", label: "ID" },
1604
+ { key: "revision", label: "Revision" },
1605
+ { key: "fileCount", label: "Files" },
1606
+ { key: "lastEvalOutcome", label: "Outcome" },
1607
+ { key: "latestScanTime", label: "Last Scan" }
1608
+ ],
1609
+ format,
1610
+ () => void 0
1430
1611
  );
1431
1612
  return;
1432
1613
  }
1614
+ if (versions.length === 0) {
1615
+ ui.emptyList("versions");
1616
+ return;
1617
+ }
1433
1618
  ui.section("Model Versions:");
1434
1619
  for (const v of versions) {
1435
1620
  ui.dim(v.uuid);
@@ -1441,7 +1626,7 @@ function renderModelVersionList(versions, format = "pretty") {
1441
1626
  }
1442
1627
  function renderModelVersionDetail(version, format = "pretty") {
1443
1628
  if (format !== "pretty") {
1444
- console.log(format === "json" ? JSON.stringify(version, null, 2) : yamlDump3(version));
1629
+ structuredDetail("model version", version, format, () => void 0);
1445
1630
  return;
1446
1631
  }
1447
1632
  ui.section("Model Version Detail:");
@@ -1473,31 +1658,12 @@ function renderModelVersionDetail(version, format = "pretty") {
1473
1658
  console.log();
1474
1659
  }
1475
1660
  function renderModelFileList(files, format = "pretty") {
1476
- if (files.length === 0) {
1477
- ui.emptyList("files");
1661
+ if (format !== "pretty") {
1662
+ renderFileList(files, format);
1478
1663
  return;
1479
1664
  }
1480
- if (format !== "pretty") {
1481
- const rows = files.map((f) => ({
1482
- id: f.uuid,
1483
- path: f.path,
1484
- type: f.type,
1485
- formats: f.formats.join(", "),
1486
- result: f.result
1487
- }));
1488
- console.log(
1489
- formatOutput(
1490
- rows,
1491
- [
1492
- { key: "id", label: "ID" },
1493
- { key: "path", label: "Path" },
1494
- { key: "type", label: "Type" },
1495
- { key: "formats", label: "Formats" },
1496
- { key: "result", label: "Result" }
1497
- ],
1498
- format
1499
- )
1500
- );
1665
+ if (files.length === 0) {
1666
+ ui.emptyList("files");
1501
1667
  return;
1502
1668
  }
1503
1669
  renderFileList(files);
@@ -1505,7 +1671,7 @@ function renderModelFileList(files, format = "pretty") {
1505
1671
 
1506
1672
  // src/cli/renderer/redteam.ts
1507
1673
  import chalk8 from "chalk";
1508
- import { dump as yamlDump4 } from "js-yaml";
1674
+ import { dump as yamlDump3 } from "js-yaml";
1509
1675
  function renderRedteamHeader() {
1510
1676
  ui.header("Prisma AIRS \u2014 AI Red Team", "Adversarial scan operations");
1511
1677
  }
@@ -1825,7 +1991,7 @@ function renderTargetDetail(target, format = "pretty") {
1825
1991
  if (format === "json") {
1826
1992
  console.log(JSON.stringify(target, null, 2));
1827
1993
  } else if (format === "yaml") {
1828
- console.log(yamlDump4(target));
1994
+ console.log(yamlDump3(target));
1829
1995
  }
1830
1996
  return;
1831
1997
  }
@@ -1857,7 +2023,7 @@ function renderPromptSetDetail(ps, format = "pretty", info) {
1857
2023
  if (format === "json") {
1858
2024
  console.log(JSON.stringify(payload, null, 2));
1859
2025
  } else if (format === "yaml") {
1860
- console.log(yamlDump4(payload));
2026
+ console.log(yamlDump3(payload));
1861
2027
  }
1862
2028
  return;
1863
2029
  }
@@ -1894,7 +2060,7 @@ function renderPromptList(prompts, format = "pretty") {
1894
2060
  if (format === "json") {
1895
2061
  console.log(JSON.stringify(prompts, null, 2));
1896
2062
  } else if (format === "yaml") {
1897
- console.log(yamlDump4(prompts));
2063
+ console.log(yamlDump3(prompts));
1898
2064
  }
1899
2065
  return;
1900
2066
  }
@@ -1917,7 +2083,7 @@ function renderPromptDetail(p, format = "pretty") {
1917
2083
  if (format === "json") {
1918
2084
  console.log(JSON.stringify(p, null, 2));
1919
2085
  } else if (format === "yaml") {
1920
- console.log(yamlDump4(p));
2086
+ console.log(yamlDump3(p));
1921
2087
  }
1922
2088
  return;
1923
2089
  }
@@ -1937,7 +2103,7 @@ function renderPropertyNames(names, format = "pretty") {
1937
2103
  if (format === "json") {
1938
2104
  console.log(JSON.stringify(names, null, 2));
1939
2105
  } else if (format === "yaml") {
1940
- console.log(yamlDump4(names));
2106
+ console.log(yamlDump3(names));
1941
2107
  } else {
1942
2108
  const rows = names.map((n) => ({ name: n }));
1943
2109
  console.log(formatOutput(rows, [{ key: "name", label: "Name" }], format));
@@ -1992,7 +2158,7 @@ function renderPropertyValues(payload, format = "pretty") {
1992
2158
  if (format === "json") {
1993
2159
  console.log(JSON.stringify(payload, null, 2));
1994
2160
  } else if (format === "yaml") {
1995
- console.log(yamlDump4(payload));
2161
+ console.log(yamlDump3(payload));
1996
2162
  }
1997
2163
  return;
1998
2164
  }
@@ -2023,7 +2189,7 @@ function renderInstanceDetail(inst, format = "pretty") {
2023
2189
  if (format === "json") {
2024
2190
  console.log(JSON.stringify(inst, null, 2));
2025
2191
  } else if (format === "yaml") {
2026
- console.log(yamlDump4(inst));
2192
+ console.log(yamlDump3(inst));
2027
2193
  }
2028
2194
  return;
2029
2195
  }
@@ -2041,7 +2207,7 @@ function renderRegistryCredentials(creds, format = "pretty") {
2041
2207
  if (format === "json") {
2042
2208
  console.log(JSON.stringify(creds, null, 2));
2043
2209
  } else if (format === "yaml") {
2044
- console.log(yamlDump4(creds));
2210
+ console.log(yamlDump3(creds));
2045
2211
  }
2046
2212
  return;
2047
2213
  }
@@ -2103,7 +2269,7 @@ function renderChannelList(channels, format = "pretty") {
2103
2269
  }
2104
2270
  function renderChannelDetail(channel, format = "pretty") {
2105
2271
  if (format !== "pretty") {
2106
- console.log(format === "json" ? JSON.stringify(channel, null, 2) : yamlDump4(channel));
2272
+ console.log(format === "json" ? JSON.stringify(channel, null, 2) : yamlDump3(channel));
2107
2273
  return;
2108
2274
  }
2109
2275
  ui.section("Channel Detail:");
@@ -2129,7 +2295,7 @@ function renderChannelDetail(channel, format = "pretty") {
2129
2295
  }
2130
2296
  function renderChannelStats(stats, format = "pretty") {
2131
2297
  if (format !== "pretty") {
2132
- console.log(format === "json" ? JSON.stringify(stats, null, 2) : yamlDump4(stats));
2298
+ console.log(format === "json" ? JSON.stringify(stats, null, 2) : yamlDump3(stats));
2133
2299
  return;
2134
2300
  }
2135
2301
  ui.section("Network Broker Stats:");
@@ -2147,7 +2313,7 @@ function renderChannelStats(stats, format = "pretty") {
2147
2313
  function renderLanguages(data, format = "pretty") {
2148
2314
  if (format !== "pretty") {
2149
2315
  if (format === "json" || format === "yaml") {
2150
- console.log(format === "json" ? JSON.stringify(data, null, 2) : yamlDump4(data));
2316
+ console.log(format === "json" ? JSON.stringify(data, null, 2) : yamlDump3(data));
2151
2317
  return;
2152
2318
  }
2153
2319
  console.log(
@@ -2256,7 +2422,7 @@ function renderAdapterList(adapters, format = "pretty", totalItems) {
2256
2422
  }
2257
2423
  function renderAdapterDetail(adapter, format = "pretty") {
2258
2424
  if (format !== "pretty") {
2259
- console.log(format === "json" ? JSON.stringify(adapter, null, 2) : yamlDump4(adapter));
2425
+ console.log(format === "json" ? JSON.stringify(adapter, null, 2) : yamlDump3(adapter));
2260
2426
  return;
2261
2427
  }
2262
2428
  ui.section("Adapter Detail:");
@@ -2289,7 +2455,7 @@ function renderAdapterDetail(adapter, format = "pretty") {
2289
2455
  }
2290
2456
  function renderAdapterValidation(result, format = "pretty") {
2291
2457
  if (format !== "pretty") {
2292
- console.log(format === "json" ? JSON.stringify(result, null, 2) : yamlDump4(result));
2458
+ console.log(format === "json" ? JSON.stringify(result, null, 2) : yamlDump3(result));
2293
2459
  return;
2294
2460
  }
2295
2461
  if (result.validated) {
@@ -2723,7 +2889,8 @@ function renderDeploymentProfileList(profiles, format = "pretty") {
2723
2889
  }
2724
2890
  function renderScanLogList(results, pageToken, format = "pretty") {
2725
2891
  if (results.length === 0) {
2726
- ui.emptyList("scan logs");
2892
+ if (format === "pretty") ui.emptyList("scan logs");
2893
+ else console.log(formatOutput([], [], format));
2727
2894
  return;
2728
2895
  }
2729
2896
  if (format !== "pretty") {
@@ -2856,7 +3023,7 @@ function scopeNameLooksUnrelated(name, scopeName) {
2856
3023
  function registerAiGatewayCommand(program) {
2857
3024
  const aigateway = program.command("aigateway").description("AI Gateway operations");
2858
3025
  const workspace = aigateway.command("workspace").description("Manage AI Gateway workspaces");
2859
- workspace.command("list").description("List workspaces (default: active workspaces you are scoped to)").option("--plane <plane>", "Plane to read from: data (scoped) or admin (whole tenant)").option("--status <status>", "Filter by lifecycle state: active or archived").option("--all", "Merge active + archived admin-plane reads (whole tenant, both states)").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").addHelpText(
3026
+ const workspaceList = workspace.command("list").description("List workspaces (default: active workspaces you are scoped to)").option("--plane <plane>", "Plane to read from: data (scoped) or admin (whole tenant)").option("--status <status>", "Filter by lifecycle state: active or archived").option("--all", "Merge active + archived admin-plane reads (whole tenant, both states)").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText(
2860
3027
  "after",
2861
3028
  examples(
2862
3029
  "airs aigateway workspace list",
@@ -2866,7 +3033,7 @@ function registerAiGatewayCommand(program) {
2866
3033
  )
2867
3034
  ).action(async (opts) => {
2868
3035
  try {
2869
- const fmt = opts.output;
3036
+ const fmt = await resolveOutput(workspaceList, opts);
2870
3037
  if (fmt === "pretty") renderAiGatewayHeader();
2871
3038
  const plane = parsePlane(opts.plane);
2872
3039
  const status = parseStatus(opts.status);
@@ -2887,7 +3054,7 @@ function registerAiGatewayCommand(program) {
2887
3054
  failWithGrantHint(err);
2888
3055
  }
2889
3056
  });
2890
- workspace.command("get <ref>").description("Get one workspace by UUID or slug (includes settings blocks)").option("--plane <plane>", "Plane to read from: data (scoped) or admin (whole tenant)").option("--output <format>", "Output format: pretty, json, yaml", "pretty").addHelpText(
3057
+ const workspaceGet = workspace.command("get <ref>").description("Get one workspace by UUID or slug (includes settings blocks)").option("--plane <plane>", "Plane to read from: data (scoped) or admin (whole tenant)").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText(
2891
3058
  "after",
2892
3059
  examples(
2893
3060
  "airs aigateway workspace get ws-main-a-349e0e",
@@ -2895,7 +3062,7 @@ function registerAiGatewayCommand(program) {
2895
3062
  )
2896
3063
  ).action(async (ref, opts) => {
2897
3064
  try {
2898
- const fmt = opts.output;
3065
+ const fmt = await resolveOutput(workspaceGet, opts);
2899
3066
  if (fmt === "pretty") renderAiGatewayHeader();
2900
3067
  const plane = parsePlane(opts.plane);
2901
3068
  const service = await createService();
@@ -2980,9 +3147,9 @@ function registerAiGatewayCommand(program) {
2980
3147
  }
2981
3148
  });
2982
3149
  const telemetry = aigateway.command("telemetry").description("AI Gateway runtime telemetry (data plane)");
2983
- telemetry.command("cost").description(
3150
+ const cost = telemetry.command("cost").description(
2984
3151
  "Total and per-day spend for a workspace (API reports cents; pretty output shows dollars)"
2985
- ).requiredOption("--workspace <slug>", "Workspace slug (not UUID), e.g. ws-main-a-349e0e").option("--days <n>", "Rolling window in days, counted back from now", "7").option("--output <format>", "Output format: pretty, json, yaml", "pretty").addHelpText(
3152
+ ).requiredOption("--workspace <slug>", "Workspace slug (not UUID), e.g. ws-main-a-349e0e").option("--days <n>", "Rolling window in days, counted back from now", "7").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText(
2986
3153
  "after",
2987
3154
  examples(
2988
3155
  "airs aigateway telemetry cost --workspace ws-main-a-349e0e",
@@ -2990,7 +3157,7 @@ function registerAiGatewayCommand(program) {
2990
3157
  )
2991
3158
  ).action(async (opts) => {
2992
3159
  try {
2993
- const fmt = opts.output;
3160
+ const fmt = await resolveOutput(cost, opts);
2994
3161
  if (fmt === "pretty") renderAiGatewayHeader();
2995
3162
  const days = Number.parseInt(opts.days, 10);
2996
3163
  if (!Number.isFinite(days) || days <= 0) {
@@ -3194,13 +3361,6 @@ function assertKnownKey(key) {
3194
3361
  usageError(`Unknown config key '${key}'. Valid keys: ${CONFIG_KEYS.join(", ")}`);
3195
3362
  }
3196
3363
  }
3197
- var LIST_FORMATS = ["pretty", "json", "yaml"];
3198
- function parseListFormat(value) {
3199
- if (!LIST_FORMATS.includes(value)) {
3200
- usageError(`Invalid --output '${value}'. Valid formats: ${LIST_FORMATS.join(", ")}`);
3201
- }
3202
- return value;
3203
- }
3204
3364
  var COLUMNS = [
3205
3365
  { key: "key", label: "Key" },
3206
3366
  { key: "value", label: "Value" },
@@ -3215,9 +3375,9 @@ function registerConfigCommand(program) {
3215
3375
  "airs config get mgmtTsgId"
3216
3376
  )
3217
3377
  );
3218
- config.command("list").description("Show effective configuration with per-key source (env/file/default)").option("--output <format>", "Output format: pretty, json, or yaml", "pretty").option("--reveal", "Show secret values in full").action(async (opts) => {
3378
+ const configList = config.command("list").description("Show effective configuration with per-key source (env/file/default)").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").option("--reveal", "Show secret values in full").action(async (opts) => {
3219
3379
  try {
3220
- const fmt = parseListFormat(opts.output);
3380
+ const fmt = await resolveOutput(configList, opts);
3221
3381
  const filePath = resolveConfigFilePath();
3222
3382
  const rows = buildConfigRows(await inspectConfig(), Boolean(opts.reveal));
3223
3383
  if (fmt === "pretty") {
@@ -3232,12 +3392,18 @@ function registerConfigCommand(program) {
3232
3392
  fail(err);
3233
3393
  }
3234
3394
  });
3235
- config.command("get <key>").description("Print a single effective config value").option("--reveal", "Show the real value of a secret key").action(async (key, opts) => {
3395
+ const configGet = config.command("get <key>").description("Print a single effective config value").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").option("--reveal", "Show the real value of a secret key").action(async (key, opts) => {
3236
3396
  try {
3237
3397
  assertKnownKey(key);
3238
3398
  const inspected = await inspectConfig();
3239
3399
  const entry = inspected[key];
3240
3400
  const raw = entry.value == null ? "" : String(entry.value);
3401
+ const fmt = await resolveOutput(configGet, opts);
3402
+ const value = raw !== "" && isSecretKey(key) && !opts.reveal ? maskSecret(raw) : raw;
3403
+ if (fmt !== "pretty") {
3404
+ console.log(formatOutput([{ key, value, source: entry.source }], COLUMNS, fmt));
3405
+ return;
3406
+ }
3241
3407
  if (raw !== "" && isSecretKey(key)) {
3242
3408
  if (opts.reveal) {
3243
3409
  ui.status(`Warning: printing secret value for '${key}'`);
@@ -3590,20 +3756,6 @@ var STATUS_KIND = {
3590
3756
  warn: "warn",
3591
3757
  fail: "error"
3592
3758
  };
3593
- var DOCTOR_FORMATS = ["pretty", "json", "yaml"];
3594
- function parseDoctorFormat(value) {
3595
- if (!DOCTOR_FORMATS.includes(value)) {
3596
- usageError(`Invalid --output '${value}'. Valid formats: ${DOCTOR_FORMATS.join(", ")}`);
3597
- }
3598
- return value;
3599
- }
3600
- function toYaml(checks) {
3601
- return checks.map((c) => {
3602
- const lines = [`name: ${c.name}`, `status: ${c.status}`, `detail: ${c.detail}`];
3603
- if (c.hint) lines.push(`hint: ${c.hint}`);
3604
- return lines.join("\n");
3605
- }).join("\n---\n");
3606
- }
3607
3759
  function renderPretty(checks) {
3608
3760
  ui.header("Doctor", "Prisma AIRS CLI preflight checks");
3609
3761
  for (const check of checks) {
@@ -3623,18 +3775,27 @@ function renderPretty(checks) {
3623
3775
  console.log("");
3624
3776
  }
3625
3777
  function registerDoctorCommand(program) {
3626
- program.command("doctor").description("Check credentials, config, and API connectivity (preflight)").option("--output <format>", "Output format: pretty, json, or yaml", "pretty").addHelpText(
3778
+ const doctor = program.command("doctor").description("Check credentials, config, and API connectivity (preflight)").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText(
3627
3779
  "after",
3628
3780
  examples("airs doctor", `airs doctor --output json | jq '.[] | select(.status != "pass")'`)
3629
3781
  ).action(async (opts) => {
3630
- const fmt = parseDoctorFormat(opts.output);
3782
+ const fmt = await resolveOutput(doctor, opts);
3631
3783
  const checks = await runDoctor();
3632
- if (fmt === "json") {
3633
- console.log(JSON.stringify(checks, null, 2));
3634
- } else if (fmt === "yaml") {
3635
- console.log(toYaml(checks));
3636
- } else {
3784
+ if (fmt === "pretty") {
3637
3785
  renderPretty(checks);
3786
+ } else {
3787
+ console.log(
3788
+ formatOutput(
3789
+ checks.map((check) => ({ ...check })),
3790
+ [
3791
+ { key: "name", label: "Name" },
3792
+ { key: "status", label: "Status" },
3793
+ { key: "detail", label: "Detail" },
3794
+ { key: "hint", label: "Hint" }
3795
+ ],
3796
+ fmt
3797
+ )
3798
+ );
3638
3799
  }
3639
3800
  process.exit(hasFailure(checks) ? 1 : 0);
3640
3801
  });
@@ -3669,27 +3830,29 @@ async function createService2() {
3669
3830
  function registerModelSecurityCommand(program) {
3670
3831
  const ms = program.command("model-security").description("AI Model Security operations \u2014 groups, rules, scans");
3671
3832
  const groups = ms.command("groups").description("Manage security groups");
3672
- groups.command("list").description("List security groups").option("--source-types <types>", "Filter by source types (comma-separated)").option("--search <query>", "Search by name or UUID").option("--sort-field <field>", "Sort field (created_at, updated_at)").option("--sort-dir <dir>", "Sort direction (asc, desc)").option("--enabled-rules <uuids>", "Filter by enabled rule UUIDs (comma-separated)").option("--limit <n>", "Max results", "20").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (opts) => {
3833
+ const groupsList = groups.command("list").description("List security groups").option("--source-types <types>", "Filter by source types (comma-separated)").option("--search <query>", "Search by name or UUID").option("--sort-field <field>", "Sort field (created_at, updated_at)").option("--sort-dir <dir>", "Sort direction (asc, desc)").option("--enabled-rules <uuids>", "Filter by enabled rule UUIDs (comma-separated)").option("--limit <n>", "Max results", "20").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (opts) => {
3673
3834
  try {
3674
- const fmt = opts.output;
3835
+ const fmt = await resolveOutput(groupsList, opts);
3675
3836
  if (fmt === "pretty") renderModelSecurityHeader();
3676
3837
  const service = await createService2();
3677
- const result = await service.listGroups({
3838
+ const listOptions = {
3678
3839
  sourceTypes: opts.sourceTypes ? opts.sourceTypes.split(",").map((s) => s.trim()) : void 0,
3679
3840
  searchQuery: opts.search,
3680
3841
  sortField: opts.sortField,
3681
3842
  sortDir: opts.sortDir,
3682
3843
  enabledRules: opts.enabledRules ? opts.enabledRules.split(",").map((s) => s.trim()) : void 0,
3683
- limit: Number.parseInt(opts.limit, 10)
3684
- });
3685
- renderGroupList(result.groups, fmt);
3844
+ limit: Number.parseInt(opts.limit, 10),
3845
+ skip: Number(opts.offset ?? 0)
3846
+ };
3847
+ const rows = opts.all ? await service.listAllGroups({ ...listOptions, max: Number(opts.max) }) : (await service.listGroups(listOptions)).groups;
3848
+ renderGroupList(rows, fmt);
3686
3849
  } catch (err) {
3687
3850
  fail(err);
3688
3851
  }
3689
3852
  });
3690
- groups.command("get <uuid>").description("Get security group details").option("--output <format>", "Output format: pretty, json, yaml", "pretty").action(async (uuid, opts) => {
3853
+ const groupsGet = groups.command("get <uuid>").description("Get security group details").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (uuid, opts) => {
3691
3854
  try {
3692
- const fmt = opts.output;
3855
+ const fmt = await resolveOutput(groupsGet, opts);
3693
3856
  if (fmt === "pretty") renderModelSecurityHeader();
3694
3857
  const service = await createService2();
3695
3858
  const group = await service.getGroup(uuid);
@@ -3877,24 +4040,26 @@ function registerModelSecurityCommand(program) {
3877
4040
  const ruleInstances = ms.command("rule-instances").description("Manage rule instances in groups");
3878
4041
  ruleInstances.command("list <groupUuid>").description("List rule instances in a security group").option("--security-rule-uuid <uuid>", "Filter by security rule UUID").option("--state <state>", "Filter by state (DISABLED, ALLOWING, BLOCKING)").option("--limit <n>", "Max results", "20").action(async (groupUuid, opts) => {
3879
4042
  try {
3880
- renderModelSecurityHeader();
4043
+ const fmt = opts.output;
4044
+ if (fmt === "pretty") renderModelSecurityHeader();
3881
4045
  const service = await createService2();
3882
4046
  const result = await service.listRuleInstances(groupUuid, {
3883
4047
  securityRuleUuid: opts.securityRuleUuid,
3884
4048
  state: opts.state,
3885
4049
  limit: Number.parseInt(opts.limit, 10)
3886
4050
  });
3887
- renderRuleInstanceList(result.ruleInstances);
4051
+ renderRuleInstanceList(result.ruleInstances, fmt);
3888
4052
  } catch (err) {
3889
4053
  fail(err);
3890
4054
  }
3891
4055
  });
3892
- ruleInstances.command("get <groupUuid> <instanceUuid>").description("Get rule instance details").action(async (groupUuid, instanceUuid) => {
4056
+ ruleInstances.command("get <groupUuid> <instanceUuid>").description("Get rule instance details").action(async (groupUuid, instanceUuid, opts) => {
3893
4057
  try {
3894
- renderModelSecurityHeader();
4058
+ const fmt = opts.output;
4059
+ if (fmt === "pretty") renderModelSecurityHeader();
3895
4060
  const service = await createService2();
3896
4061
  const instance = await service.getRuleInstance(groupUuid, instanceUuid);
3897
- renderRuleInstanceDetail(instance);
4062
+ renderRuleInstanceDetail(instance, fmt);
3898
4063
  } catch (err) {
3899
4064
  fail(err);
3900
4065
  }
@@ -3915,33 +4080,36 @@ function registerModelSecurityCommand(program) {
3915
4080
  }
3916
4081
  });
3917
4082
  const rules = ms.command("rules").description("Browse security rules");
3918
- rules.command("list").description("List available security rules").option("--source-type <type>", "Filter by source type").option("--search <query>", "Search by name or UUID").option("--limit <n>", "Max results", "20").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (opts) => {
4083
+ const rulesList = rules.command("list").description("List available security rules").option("--source-type <type>", "Filter by source type").option("--search <query>", "Search by name or UUID").option("--limit <n>", "Max results", "20").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (opts) => {
3919
4084
  try {
3920
- const fmt = opts.output;
4085
+ const fmt = await resolveOutput(rulesList, opts);
3921
4086
  if (fmt === "pretty") renderModelSecurityHeader();
3922
4087
  const service = await createService2();
3923
- const result = await service.listRules({
4088
+ const listOptions = {
3924
4089
  sourceType: opts.sourceType,
3925
4090
  searchQuery: opts.search,
3926
- limit: Number.parseInt(opts.limit, 10)
3927
- });
3928
- renderRuleList(result.rules, fmt);
4091
+ limit: Number.parseInt(opts.limit, 10),
4092
+ skip: Number(opts.offset ?? 0)
4093
+ };
4094
+ const rows = opts.all ? await service.listAllRules({ ...listOptions, max: Number(opts.max) }) : (await service.listRules(listOptions)).rules;
4095
+ renderRuleList(rows, fmt);
3929
4096
  } catch (err) {
3930
4097
  fail(err);
3931
4098
  }
3932
4099
  });
3933
- rules.command("get <uuid>").description("Get security rule details").action(async (uuid) => {
4100
+ rules.command("get <uuid>").description("Get security rule details").action(async (uuid, opts) => {
3934
4101
  try {
3935
- renderModelSecurityHeader();
4102
+ const fmt = opts.output;
4103
+ if (fmt === "pretty") renderModelSecurityHeader();
3936
4104
  const service = await createService2();
3937
4105
  const rule = await service.getRule(uuid);
3938
- renderRuleDetail(rule);
4106
+ renderRuleDetail(rule, fmt);
3939
4107
  } catch (err) {
3940
4108
  fail(err);
3941
4109
  }
3942
4110
  });
3943
4111
  const scans = ms.command("scans").description("Model security scan operations");
3944
- scans.command("list").description("List model security scans").option("--eval-outcome <outcome>", "Filter by eval outcome").option("--source-type <type>", "Filter by source type").option("--scan-origin <origin>", "Filter by scan origin").option("--search <query>", "Search scans").option("--limit <n>", "Max results", "20").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").addHelpText(
4112
+ const scansList = scans.command("list").description("List model security scans").option("--eval-outcome <outcome>", "Filter by eval outcome").option("--source-type <type>", "Filter by source type").option("--scan-origin <origin>", "Filter by scan origin").option("--search <query>", "Search scans").option("--limit <n>", "Max results", "20").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText(
3945
4113
  "after",
3946
4114
  examples(
3947
4115
  "airs model-security scans list",
@@ -3950,27 +4118,30 @@ function registerModelSecurityCommand(program) {
3950
4118
  )
3951
4119
  ).action(async (opts) => {
3952
4120
  try {
3953
- const fmt = opts.output;
4121
+ const fmt = await resolveOutput(scansList, opts);
3954
4122
  if (fmt === "pretty") renderModelSecurityHeader();
3955
4123
  const service = await createService2();
3956
- const result = await service.listScans({
4124
+ const listOptions = {
3957
4125
  evalOutcome: opts.evalOutcome,
3958
4126
  sourceType: opts.sourceType,
3959
4127
  scanOrigin: opts.scanOrigin,
3960
4128
  search: opts.search,
3961
- limit: Number.parseInt(opts.limit, 10)
3962
- });
3963
- renderMsScanList(result.scans, fmt);
4129
+ limit: Number.parseInt(opts.limit, 10),
4130
+ skip: Number(opts.offset ?? 0)
4131
+ };
4132
+ const rows = opts.all ? await service.listAllScans({ ...listOptions, max: Number(opts.max) }) : (await service.listScans(listOptions)).scans;
4133
+ renderMsScanList(rows, fmt);
3964
4134
  } catch (err) {
3965
4135
  fail(err);
3966
4136
  }
3967
4137
  });
3968
- scans.command("get <uuid>").description("Get scan details").action(async (uuid) => {
4138
+ scans.command("get <uuid>").description("Get scan details").action(async (uuid, opts) => {
3969
4139
  try {
3970
- renderModelSecurityHeader();
4140
+ const fmt = opts.output;
4141
+ if (fmt === "pretty") renderModelSecurityHeader();
3971
4142
  const service = await createService2();
3972
4143
  const scan = await service.getScan(uuid);
3973
- renderMsScanDetail(scan);
4144
+ renderMsScanDetail(scan, fmt);
3974
4145
  } catch (err) {
3975
4146
  fail(err);
3976
4147
  }
@@ -4046,27 +4217,28 @@ function registerModelSecurityCommand(program) {
4046
4217
  }
4047
4218
  });
4048
4219
  const models = ms.command("models").description("Browse the scanned model catalog (read-only)");
4049
- models.command("list").description("List models in the catalog").option("--search <text>", "Filter by search text").option("--search-query <text>", "Filter by model UUID or name").option("--sort-field <field>", "Sort field: created_at, updated_at").option("--sort-order <order>", "Sort order: asc, desc").option("--limit <n>", "Max results").option("--offset <n>", "Starting offset").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").addHelpText("after", examples("airs model-security models list")).action(async (opts) => {
4220
+ const modelsList = models.command("list").description("List models in the catalog").option("--search <text>", "Filter by search text").option("--search-query <text>", "Filter by model UUID or name").option("--sort-field <field>", "Sort field: created_at, updated_at").option("--sort-order <order>", "Sort order: asc, desc").option("--limit <n>", "Max results").option("--offset <n>", "Starting offset").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText("after", examples("airs model-security models list")).action(async (opts) => {
4050
4221
  try {
4051
- const fmt = opts.output;
4222
+ const fmt = await resolveOutput(modelsList, opts);
4052
4223
  if (fmt === "pretty") renderModelSecurityHeader();
4053
4224
  const service = await createService2();
4054
- const result = await service.listModels({
4225
+ const listOptions = {
4055
4226
  search: opts.search,
4056
4227
  searchQuery: opts.searchQuery,
4057
4228
  sortField: opts.sortField,
4058
4229
  sortOrder: opts.sortOrder,
4059
4230
  limit: opts.limit ? Number.parseInt(opts.limit, 10) : void 0,
4060
4231
  skip: opts.offset ? Number.parseInt(opts.offset, 10) : void 0
4061
- });
4062
- renderModelList(result.models, fmt);
4232
+ };
4233
+ const rows = opts.all ? await service.listAllModels({ ...listOptions, max: Number(opts.max) }) : (await service.listModels(listOptions)).models;
4234
+ renderModelList(rows, fmt);
4063
4235
  } catch (err) {
4064
4236
  fail(err);
4065
4237
  }
4066
4238
  });
4067
- models.command("get <uuid>").description("Get a model by UUID").option("--output <format>", "Output format: pretty, json, yaml", "pretty").action(async (uuid, opts) => {
4239
+ const modelsGet = models.command("get <uuid>").description("Get a model by UUID").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (uuid, opts) => {
4068
4240
  try {
4069
- const fmt = opts.output;
4241
+ const fmt = await resolveOutput(modelsGet, opts);
4070
4242
  if (fmt === "pretty") renderModelSecurityHeader();
4071
4243
  const service = await createService2();
4072
4244
  const model = await service.getModel(uuid);
@@ -4075,9 +4247,9 @@ function registerModelSecurityCommand(program) {
4075
4247
  fail(err);
4076
4248
  }
4077
4249
  });
4078
- models.command("versions <modelUuid>").description("List versions of a model").option("--sort-order <order>", "Sort order: asc, desc").option("--limit <n>", "Max results").option("--offset <n>", "Starting offset").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (modelUuid, opts) => {
4250
+ const modelVersions = models.command("versions <modelUuid>").description("List versions of a model").option("--sort-order <order>", "Sort order: asc, desc").option("--limit <n>", "Max results").option("--offset <n>", "Starting offset").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (modelUuid, opts) => {
4079
4251
  try {
4080
- const fmt = opts.output;
4252
+ const fmt = await resolveOutput(modelVersions, opts);
4081
4253
  if (fmt === "pretty") renderModelSecurityHeader();
4082
4254
  const service = await createService2();
4083
4255
  const result = await service.listModelVersions(modelUuid, {
@@ -4090,9 +4262,9 @@ function registerModelSecurityCommand(program) {
4090
4262
  fail(err);
4091
4263
  }
4092
4264
  });
4093
- models.command("version <uuid>").description("Get a model version by UUID").option("--output <format>", "Output format: pretty, json, yaml", "pretty").action(async (uuid, opts) => {
4265
+ const modelVersion = models.command("version <uuid>").description("Get a model version by UUID").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (uuid, opts) => {
4094
4266
  try {
4095
- const fmt = opts.output;
4267
+ const fmt = await resolveOutput(modelVersion, opts);
4096
4268
  if (fmt === "pretty") renderModelSecurityHeader();
4097
4269
  const service = await createService2();
4098
4270
  const version = await service.getModelVersion(uuid);
@@ -4101,9 +4273,9 @@ function registerModelSecurityCommand(program) {
4101
4273
  fail(err);
4102
4274
  }
4103
4275
  });
4104
- models.command("files <modelVersionUuid>").description("List files in a model version").option("--limit <n>", "Max results").option("--offset <n>", "Starting offset").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (modelVersionUuid, opts) => {
4276
+ const modelFiles = models.command("files <modelVersionUuid>").description("List files in a model version").option("--limit <n>", "Max results").option("--offset <n>", "Starting offset").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (modelVersionUuid, opts) => {
4105
4277
  try {
4106
- const fmt = opts.output;
4278
+ const fmt = await resolveOutput(modelFiles, opts);
4107
4279
  if (fmt === "pretty") renderModelSecurityHeader();
4108
4280
  const service = await createService2();
4109
4281
  const result = await service.listModelVersionFiles(modelVersionUuid, {
@@ -4352,8 +4524,12 @@ function parseAttackGoals(input) {
4352
4524
  return parsed;
4353
4525
  }
4354
4526
  function sliceClientSide(items, opts) {
4355
- const offset = opts.offset !== void 0 ? Number.parseInt(opts.offset, 10) : 0;
4356
- const limit = opts.limit !== void 0 ? Number.parseInt(opts.limit, 10) : void 0;
4527
+ if (opts.all) {
4528
+ const max = opts.max === void 0 ? 1e4 : Number(opts.max);
4529
+ return max === 0 ? items : items.slice(0, max);
4530
+ }
4531
+ const offset = opts.offset !== void 0 ? Number.parseInt(String(opts.offset), 10) : 0;
4532
+ const limit = opts.limit !== void 0 ? Number.parseInt(String(opts.limit), 10) : void 0;
4357
4533
  return items.slice(offset, limit === void 0 ? void 0 : offset + limit);
4358
4534
  }
4359
4535
  function parsePositiveInt(input, flag) {
@@ -4634,12 +4810,14 @@ function registerRedteamCommand(program) {
4634
4810
  const fmt = opts.output;
4635
4811
  if (fmt === "pretty") renderRedteamHeader();
4636
4812
  const service = await createService3();
4637
- const scans = await service.listScans({
4813
+ const listOptions = {
4638
4814
  status: opts.status,
4639
4815
  jobType: opts.type,
4640
4816
  targetId: opts.target,
4641
- limit: Number.parseInt(opts.limit, 10)
4642
- });
4817
+ limit: Number.parseInt(opts.limit, 10),
4818
+ offset: Number(opts.offset ?? 0)
4819
+ };
4820
+ const scans = opts.all ? await service.listAllScans({ ...listOptions, max: Number(opts.max) }) : await service.listScans(listOptions);
4643
4821
  renderScanList(scans, fmt);
4644
4822
  } catch (err) {
4645
4823
  fail(err);
@@ -5142,12 +5320,18 @@ function registerRedteamCommand(program) {
5142
5320
  fail(err);
5143
5321
  }
5144
5322
  });
5145
- const targetsBackup = targets.command("backup").description("Backup red team targets to local JSON/YAML files").option("--output-dir <path>", "Output directory").option("--output <format>", "Output format: json or yaml", "json").option("--name <targetName>", "Backup a single target by name");
5323
+ const targetsBackup = targets.command("backup").description("Backup red team targets to local JSON/YAML files").option("--output-dir <path>", "Output directory").option("--file-format <format>", "Backup file format: json or yaml", "json").option("--name <targetName>", "Backup a single target by name");
5324
+ registerDeprecatedAlias(targetsBackup, {
5325
+ oldFlag: "--output <format>",
5326
+ oldKey: "output",
5327
+ canonicalFlag: "--file-format",
5328
+ canonicalKey: "fileFormat"
5329
+ });
5146
5330
  registerDeprecatedAlias(targetsBackup, {
5147
5331
  oldFlag: "--format <format>",
5148
5332
  oldKey: "format",
5149
- canonicalFlag: "--output",
5150
- canonicalKey: "output"
5333
+ canonicalFlag: "--file-format",
5334
+ canonicalKey: "fileFormat"
5151
5335
  });
5152
5336
  targetsBackup.action(async (opts) => {
5153
5337
  resolveDeprecatedAliases(targetsBackup, opts);
@@ -5156,7 +5340,7 @@ function registerRedteamCommand(program) {
5156
5340
  const outputDir = resolveOutputDir(opts.outputDir, "targets");
5157
5341
  const results = await backupTargets({
5158
5342
  outputDir,
5159
- format: opts.output ?? "json",
5343
+ format: opts.fileFormat ?? "json",
5160
5344
  name: opts.name
5161
5345
  });
5162
5346
  renderBackupSummary(results, outputDir);
@@ -5221,11 +5405,13 @@ function registerRedteamCommand(program) {
5221
5405
  const fmt = opts.output;
5222
5406
  if (fmt === "pretty") renderRedteamHeader();
5223
5407
  const service = await createService3();
5224
- const { adapters, totalItems } = await service.listAdapters({
5408
+ const listOptions = {
5225
5409
  limit: opts.limit ? parsePositiveInt(opts.limit, "--limit") : void 0,
5226
5410
  offset: opts.offset ? Number.parseInt(opts.offset, 10) : void 0,
5227
5411
  search: opts.search
5228
- });
5412
+ };
5413
+ const result = opts.all ? { adapters: await service.listAllAdapters({ ...listOptions, max: Number(opts.max) }) } : await service.listAdapters(listOptions);
5414
+ const { adapters, totalItems } = result;
5229
5415
  renderAdapterList(adapters, fmt, totalItems);
5230
5416
  } catch (err) {
5231
5417
  fail(err);
@@ -5367,12 +5553,13 @@ function registerRedteamCommand(program) {
5367
5553
  const fmt = opts.output;
5368
5554
  if (fmt === "pretty") renderRedteamHeader();
5369
5555
  const service = await createService3();
5370
- const { channels: list } = await service.listChannels({
5556
+ const listOptions = {
5371
5557
  limit: opts.limit ? parsePositiveInt(opts.limit, "--limit") : void 0,
5372
5558
  offset: opts.offset ? Number.parseInt(opts.offset, 10) : void 0,
5373
5559
  search: opts.search,
5374
5560
  status: opts.status
5375
- });
5561
+ };
5562
+ const list = opts.all ? await service.listAllChannels({ ...listOptions, max: Number(opts.max) }) : (await service.listChannels(listOptions)).channels;
5376
5563
  renderChannelList(list, fmt);
5377
5564
  } catch (err) {
5378
5565
  fail(err);
@@ -5914,6 +6101,31 @@ async function loadBulkScanState(filePath) {
5914
6101
 
5915
6102
  // src/cli/pagination.ts
5916
6103
  var DEFAULT_PAGE_SIZE = 50;
6104
+ var DEFAULT_OFFSET_LIMIT = 100;
6105
+ var DEFAULT_MAX_ITEMS = 1e4;
6106
+ function registerListFlags(command, options) {
6107
+ const defaultLimit = options.dialect === "offset" ? DEFAULT_OFFSET_LIMIT : DEFAULT_PAGE_SIZE;
6108
+ return command.option("--limit <n>", `Items per page (default: ${defaultLimit})`, Number, defaultLimit).option("--offset <n>", "Item offset (default: 0)", Number, 0).option("--all", "Walk all pages").option("--max <n>", "Maximum items with --all; 0 removes the cap", Number, DEFAULT_MAX_ITEMS);
6109
+ }
6110
+ function integerFlag(name, value, minimum) {
6111
+ const parsed = Number(value);
6112
+ if (!Number.isSafeInteger(parsed) || parsed < minimum) {
6113
+ const qualifier = minimum === 1 ? "a positive integer" : "a non-negative integer";
6114
+ throw new CliUsageError(`${name} must be ${qualifier}`);
6115
+ }
6116
+ return parsed;
6117
+ }
6118
+ function resolveListParams(command, opts, options) {
6119
+ const defaultLimit = options.dialect === "offset" ? DEFAULT_OFFSET_LIMIT : DEFAULT_PAGE_SIZE;
6120
+ const limit = integerFlag("--limit", opts.limit ?? defaultLimit, 1);
6121
+ const offset = integerFlag("--offset", opts.offset ?? 0, 0);
6122
+ const max = integerFlag("--max", opts.max ?? DEFAULT_MAX_ITEMS, 0);
6123
+ const all = Boolean(opts.all);
6124
+ if (all && command.getOptionValueSource?.("offset") === "cli") {
6125
+ throw new CliUsageError("--all cannot be combined with --offset");
6126
+ }
6127
+ return { limit, offset, all, max };
6128
+ }
5917
6129
  function registerPageAliases(cmd, opts) {
5918
6130
  registerDeprecatedAlias(cmd, {
5919
6131
  oldFlag: `${opts.sizeFlag} <n>`,
@@ -6035,6 +6247,71 @@ function parseQuotedField(content, start, len) {
6035
6247
  return { value, nextIndex: i };
6036
6248
  }
6037
6249
 
6250
+ // src/cli/renderer/views/runtime.ts
6251
+ var profilesView = {
6252
+ name: "profiles",
6253
+ columns: [
6254
+ { key: "profileId", label: "ID" },
6255
+ { key: "profileName", label: "Name" },
6256
+ { key: "active", label: "Active" },
6257
+ { key: "revision", label: "Revision" }
6258
+ ],
6259
+ pretty: {
6260
+ list: (items) => renderProfileList(items, "pretty"),
6261
+ detail: renderProfileDetail
6262
+ }
6263
+ };
6264
+ var apiKeysView = {
6265
+ name: "API keys",
6266
+ columns: [
6267
+ { key: "id", label: "ID" },
6268
+ { key: "name", label: "Name" },
6269
+ { key: "last8", label: "Last 8" },
6270
+ { key: "expiresAt", label: "Expires" }
6271
+ ],
6272
+ pretty: {
6273
+ list: (items) => renderApiKeyList(items, "pretty"),
6274
+ detail: renderApiKeyDetail
6275
+ }
6276
+ };
6277
+ var customerAppsView = {
6278
+ name: "customer apps",
6279
+ columns: [
6280
+ { key: "id", label: "ID" },
6281
+ { key: "name", label: "Name" },
6282
+ { key: "description", label: "Description" }
6283
+ ],
6284
+ pretty: {
6285
+ list: (items) => renderCustomerAppList(items, "pretty"),
6286
+ detail: renderCustomerAppDetail
6287
+ }
6288
+ };
6289
+ var topicsView = {
6290
+ name: "topics",
6291
+ columns: [
6292
+ { key: "topic_id", label: "ID" },
6293
+ { key: "topic_name", label: "Name" },
6294
+ { key: "revision", label: "Revision" },
6295
+ { key: "description", label: "Description" }
6296
+ ],
6297
+ structured: (topic) => ({
6298
+ topicId: topic.topic_id,
6299
+ topicName: topic.topic_name,
6300
+ revision: topic.revision,
6301
+ active: topic.active,
6302
+ description: topic.description,
6303
+ examples: topic.examples,
6304
+ createdBy: topic.created_by,
6305
+ updatedBy: topic.updated_by,
6306
+ lastModifiedTs: topic.last_modified_ts,
6307
+ createdTs: topic.created_ts
6308
+ }),
6309
+ pretty: {
6310
+ list: (items) => renderTopicList(items, "pretty"),
6311
+ detail: renderTopicDetail
6312
+ }
6313
+ };
6314
+
6038
6315
  // src/cli/commands/dlp/dictionaries.ts
6039
6316
  import { readFile as readFile6 } from "fs/promises";
6040
6317
  import { basename as basename2 } from "path";
@@ -6048,6 +6325,9 @@ var SdkDictionariesService = class {
6048
6325
  async list(params) {
6049
6326
  return this.client.list(params);
6050
6327
  }
6328
+ async listAll(params) {
6329
+ return this.client.listAll(params);
6330
+ }
6051
6331
  async create(params) {
6052
6332
  return this.client.create(params);
6053
6333
  }
@@ -6155,20 +6435,22 @@ function register(dlp) {
6155
6435
  "--offset <n>",
6156
6436
  "Starting offset \u2014 rounds down to a page boundary",
6157
6437
  (v) => Number.parseInt(v, 10)
6158
- ).option("--sort <field,dir>", "(repeatable)", (v, p = []) => [...p, v]).option("--keywords", "Include keyword list in response").option("--include-keywords", "Alias for --keywords").option("--output <fmt>", "Output format", "pretty");
6438
+ ).option("--sort <field,dir>", "(repeatable)", (v, p = []) => [...p, v]).option("--keywords", "Include keyword list in response").option("--include-keywords", "Alias for --keywords").option("--output <fmt>", "Output format: pretty, table, markdown, csv, json, yaml");
6159
6439
  registerPageAliases(listCmd, { sizeFlag: "--size", sizeKey: "size" });
6160
6440
  listCmd.action(async (opts) => {
6161
6441
  try {
6162
6442
  const { page, size } = resolvePageParams(listCmd, opts);
6163
6443
  const includeKeywords = opts.keywords || opts.includeKeywords;
6444
+ const svc = new SdkDictionariesService();
6445
+ const params = {
6446
+ size,
6447
+ sort: opts.sort,
6448
+ keywords: includeKeywords ? true : void 0
6449
+ };
6450
+ const all = opts.all ? await svc.listAll({ ...params, max: Number(opts.max) }) : void 0;
6164
6451
  dlpDictionaries.renderList(
6165
- await new SdkDictionariesService().list({
6166
- page,
6167
- size,
6168
- sort: opts.sort,
6169
- keywords: includeKeywords ? true : void 0
6170
- }),
6171
- opts.output
6452
+ all ? { content: all, totalElements: all.length } : await svc.list({ ...params, page }),
6453
+ await resolveOutput(listCmd, opts)
6172
6454
  );
6173
6455
  } catch (err) {
6174
6456
  fail(err);
@@ -6189,12 +6471,12 @@ function register(dlp) {
6189
6471
  usageError(err instanceof Error ? err.message : String(err));
6190
6472
  }
6191
6473
  });
6192
- group.command("get <id>").option("--keywords", "").option("--include-keywords", "Alias for --keywords").option("--output <fmt>", "Output format", "pretty").action(async (id, opts) => {
6474
+ const getCmd = group.command("get <id>").option("--keywords", "").option("--include-keywords", "Alias for --keywords").option("--output <fmt>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (id, opts) => {
6193
6475
  try {
6194
6476
  const includeKeywords = opts.keywords || opts.includeKeywords;
6195
6477
  dlpDictionaries.renderGet(
6196
6478
  await new SdkDictionariesService().get(id, { includeKeywords }),
6197
- opts.output
6479
+ await resolveOutput(getCmd, opts)
6198
6480
  );
6199
6481
  } catch (err) {
6200
6482
  fail(err);
@@ -6255,6 +6537,9 @@ var SdkDataFilteringProfilesService = class {
6255
6537
  async list(params) {
6256
6538
  return this.client.list(params);
6257
6539
  }
6540
+ async listAll(params) {
6541
+ return this.client.listAll(params);
6542
+ }
6258
6543
  async get(id) {
6259
6544
  return this.client.get(id);
6260
6545
  }
@@ -6388,7 +6673,7 @@ function listFlags(cmd) {
6388
6673
  "--offset <n>",
6389
6674
  "Starting offset \u2014 rounds down to a page boundary",
6390
6675
  (v) => Number.parseInt(v, 10)
6391
- ).option("--sort <field,dir>", "Sort criteria (repeatable)", repeatable).option("--output <fmt>", "Output format", "pretty");
6676
+ ).option("--sort <field,dir>", "Sort criteria (repeatable)", repeatable).option("--output <fmt>", "Output format: pretty, table, markdown, csv, json, yaml");
6392
6677
  registerPageAliases(cmd, { sizeFlag: "--size", sizeKey: "size" });
6393
6678
  return cmd;
6394
6679
  }
@@ -6409,16 +6694,17 @@ function register2(dlp) {
6409
6694
  try {
6410
6695
  const { page, size } = resolvePageParams(listCmd, opts);
6411
6696
  const svc = new SdkDataFilteringProfilesService();
6412
- const r = await svc.list({ page, size, sort: opts.sort });
6413
- dlpFilteringProfiles.renderList(r, opts.output);
6697
+ const all = opts.all ? await svc.listAll({ size, sort: opts.sort, max: Number(opts.max) }) : void 0;
6698
+ const r = all ? { content: all, totalElements: all.length } : await svc.list({ page, size, sort: opts.sort });
6699
+ dlpFilteringProfiles.renderList(r, await resolveOutput(listCmd, opts));
6414
6700
  } catch (err) {
6415
6701
  fail(err);
6416
6702
  }
6417
6703
  });
6418
- group.command("get <id>").description("Get a filtering profile by id").option("--output <fmt>", "Output format", "pretty").action(async (id, opts) => {
6704
+ const getCmd = group.command("get <id>").description("Get a filtering profile by id").option("--output <fmt>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (id, opts) => {
6419
6705
  try {
6420
6706
  const svc = new SdkDataFilteringProfilesService();
6421
- dlpFilteringProfiles.renderGet(await svc.get(id), opts.output);
6707
+ dlpFilteringProfiles.renderGet(await svc.get(id), await resolveOutput(getCmd, opts));
6422
6708
  } catch (err) {
6423
6709
  fail(err);
6424
6710
  }
@@ -6514,6 +6800,9 @@ var SdkDataPatternsService = class {
6514
6800
  async list(params) {
6515
6801
  return this.client.list(params);
6516
6802
  }
6803
+ async listAll(params) {
6804
+ return this.client.listAll(params);
6805
+ }
6517
6806
  async create(body) {
6518
6807
  return this.client.create(body);
6519
6808
  }
@@ -6537,7 +6826,7 @@ function listFlags2(cmd) {
6537
6826
  "--offset <n>",
6538
6827
  "Starting offset \u2014 rounds down to a page boundary",
6539
6828
  (v) => Number.parseInt(v, 10)
6540
- ).option("--sort <field,dir>", "Sort criteria (repeatable)", repeatable).option("--output <fmt>", "Output format", "pretty");
6829
+ ).option("--sort <field,dir>", "Sort criteria (repeatable)", repeatable).option("--output <fmt>", "Output format: pretty, table, markdown, csv, json, yaml");
6541
6830
  registerPageAliases(cmd, { sizeFlag: "--size", sizeKey: "size" });
6542
6831
  return cmd;
6543
6832
  }
@@ -6559,9 +6848,10 @@ function register4(dlp) {
6559
6848
  try {
6560
6849
  const { page, size } = resolvePageParams(listCmd, opts);
6561
6850
  const svc = new SdkDataPatternsService();
6851
+ const result = opts.all ? await svc.listAll({ size, sort: opts.sort, max: Number(opts.max) }) : void 0;
6562
6852
  dlpPatterns.renderList(
6563
- await svc.list({ page, size, sort: opts.sort }),
6564
- opts.output
6853
+ result ? { content: result, totalElements: result.length } : await svc.list({ page, size, sort: opts.sort }),
6854
+ await resolveOutput(listCmd, opts)
6565
6855
  );
6566
6856
  } catch (err) {
6567
6857
  fail(err);
@@ -6579,11 +6869,11 @@ function register4(dlp) {
6579
6869
  usageError(err instanceof Error ? err.message : String(err));
6580
6870
  }
6581
6871
  });
6582
- group.command("get <id>").description("Get a data pattern by id").option("--output <fmt>", "Output format", "pretty").action(async (id, opts) => {
6872
+ const getCmd = group.command("get <id>").description("Get a data pattern by id").option("--output <fmt>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (id, opts) => {
6583
6873
  try {
6584
6874
  dlpPatterns.renderGet(
6585
6875
  await new SdkDataPatternsService().get(id),
6586
- opts.output
6876
+ await resolveOutput(getCmd, opts)
6587
6877
  );
6588
6878
  } catch (err) {
6589
6879
  fail(err);
@@ -6639,6 +6929,9 @@ var SdkDataProfilesService = class {
6639
6929
  async list(params) {
6640
6930
  return this.client.list(params);
6641
6931
  }
6932
+ async listAll(params) {
6933
+ return this.client.listAll(params);
6934
+ }
6642
6935
  async create(body) {
6643
6936
  return this.client.create(body);
6644
6937
  }
@@ -6659,7 +6952,7 @@ function listFlags3(cmd) {
6659
6952
  "--offset <n>",
6660
6953
  "Starting offset \u2014 rounds down to a page boundary",
6661
6954
  (v) => Number.parseInt(v, 10)
6662
- ).option("--sort <field,dir>", "Sort criteria (repeatable)", repeatable).option("--output <fmt>", "Output format", "pretty");
6955
+ ).option("--sort <field,dir>", "Sort criteria (repeatable)", repeatable).option("--output <fmt>", "Output format: pretty, table, markdown, csv, json, yaml");
6663
6956
  registerPageAliases(cmd, { sizeFlag: "--size", sizeKey: "size" });
6664
6957
  return cmd;
6665
6958
  }
@@ -6693,9 +6986,10 @@ function register5(dlp) {
6693
6986
  try {
6694
6987
  const { page, size } = resolvePageParams(listCmd, opts);
6695
6988
  const svc = new SdkDataProfilesService();
6989
+ const result = opts.all ? await svc.listAll({ size, sort: opts.sort, max: Number(opts.max) }) : void 0;
6696
6990
  dlpProfiles.renderList(
6697
- await svc.list({ page, size, sort: opts.sort }),
6698
- opts.output
6991
+ result ? { content: result, totalElements: result.length } : await svc.list({ page, size, sort: opts.sort }),
6992
+ await resolveOutput(listCmd, opts)
6699
6993
  );
6700
6994
  } catch (err) {
6701
6995
  fail(err);
@@ -6713,11 +7007,11 @@ function register5(dlp) {
6713
7007
  usageError(err instanceof Error ? err.message : String(err));
6714
7008
  }
6715
7009
  });
6716
- group.command("get <id>").description("Get a data profile by id").option("--output <fmt>", "Output format", "pretty").action(async (id, opts) => {
7010
+ const getCmd = group.command("get <id>").description("Get a data profile by id").option("--output <fmt>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (id, opts) => {
6717
7011
  try {
6718
7012
  dlpProfiles.renderGet(
6719
7013
  await new SdkDataProfilesService().get(id),
6720
- opts.output
7014
+ await resolveOutput(getCmd, opts)
6721
7015
  );
6722
7016
  } catch (err) {
6723
7017
  fail(err);
@@ -7365,15 +7659,26 @@ async function createMgmtService() {
7365
7659
  function registerRuntimeCommand(program) {
7366
7660
  const runtime = program.command("runtime").description("Runtime prompt scanning against AIRS profiles");
7367
7661
  const apiKeys = runtime.command("api-keys").description("Manage AIRS API keys");
7368
- apiKeys.command("list").description("List API keys").option("--limit <n>", "Max results", "100").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (opts) => {
7662
+ const apiKeysList = registerListFlags(apiKeys.command("list"), { dialect: "offset" }).description("List API keys").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (opts) => {
7369
7663
  try {
7370
- const fmt = opts.output;
7664
+ const fmt = await resolveOutput(apiKeysList, opts);
7665
+ const page = resolveListParams(apiKeysList, opts, { dialect: "offset" });
7371
7666
  if (fmt === "pretty") renderRuntimeConfigHeader();
7372
7667
  const service = await createMgmtService();
7668
+ if (page.all) {
7669
+ const items = await service.listAllApiKeys({ limit: page.limit, max: page.max });
7670
+ emitList(apiKeysView, items, fmt, {
7671
+ page: { returned: items.length, total: items.length, all: true }
7672
+ });
7673
+ return;
7674
+ }
7373
7675
  const result = await service.listApiKeys({
7374
- limit: Number.parseInt(opts.limit, 10)
7676
+ limit: page.limit,
7677
+ offset: page.offset
7678
+ });
7679
+ emitList(apiKeysView, result.apiKeys, fmt, {
7680
+ page: { returned: result.apiKeys.length, next: result.nextOffset }
7375
7681
  });
7376
- renderApiKeyList(result.apiKeys, fmt);
7377
7682
  } catch (err) {
7378
7683
  fail(err);
7379
7684
  }
@@ -7560,25 +7865,37 @@ function registerRuntimeCommand(program) {
7560
7865
  }
7561
7866
  });
7562
7867
  const customerApps = runtime.command("customer-apps").description("Manage AIRS customer apps");
7563
- customerApps.command("list").description("List customer apps").option("--limit <n>", "Max results", "100").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (opts) => {
7868
+ const customerAppsList = registerListFlags(customerApps.command("list"), { dialect: "offset" }).description("List customer apps").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (opts) => {
7564
7869
  try {
7565
- const fmt = opts.output;
7870
+ const fmt = await resolveOutput(customerAppsList, opts);
7871
+ const page = resolveListParams(customerAppsList, opts, { dialect: "offset" });
7566
7872
  if (fmt === "pretty") renderRuntimeConfigHeader();
7567
7873
  const service = await createMgmtService();
7874
+ if (page.all) {
7875
+ const items = await service.listAllCustomerApps({ limit: page.limit, max: page.max });
7876
+ emitList(customerAppsView, items, fmt, {
7877
+ page: { returned: items.length, total: items.length, all: true }
7878
+ });
7879
+ return;
7880
+ }
7568
7881
  const result = await service.listCustomerApps({
7569
- limit: Number.parseInt(opts.limit, 10)
7882
+ limit: page.limit,
7883
+ offset: page.offset
7884
+ });
7885
+ emitList(customerAppsView, result.apps, fmt, {
7886
+ page: { returned: result.apps.length, next: result.nextOffset }
7570
7887
  });
7571
- renderCustomerAppList(result.apps, fmt);
7572
7888
  } catch (err) {
7573
7889
  fail(err);
7574
7890
  }
7575
7891
  });
7576
- customerApps.command("get <appName>").description("Get customer app details").action(async (appName) => {
7892
+ const customerAppsGet = customerApps.command("get <appName>").description("Get customer app details").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (appName, opts) => {
7577
7893
  try {
7578
- renderRuntimeConfigHeader();
7894
+ const fmt = await resolveOutput(customerAppsGet, opts);
7895
+ if (fmt === "pretty") renderRuntimeConfigHeader();
7579
7896
  const service = await createMgmtService();
7580
7897
  const app = await service.getCustomerApp(appName);
7581
- renderCustomerAppDetail(app);
7898
+ emitDetail(customerAppsView, app, fmt);
7582
7899
  } catch (err) {
7583
7900
  fail(err);
7584
7901
  }
@@ -7605,14 +7922,14 @@ function registerRuntimeCommand(program) {
7605
7922
  fail(err);
7606
7923
  }
7607
7924
  });
7608
- customerApps.command("consumption").argument(
7925
+ const customerAppsConsumption = customerApps.command("consumption").argument(
7609
7926
  "[appName]",
7610
7927
  "Dashboard application name \u2014 the literal scan-payload metadata.app_name, as shown in the SCM AI Applications view (may differ from the SCM-registered customer-app name). Omit to report every dashboard bucket."
7611
7928
  ).description(
7612
7929
  "Show per-app token consumption + violation breakdown (SCM dashboard). Omit appName to scan all apps."
7613
- ).option("--time-interval <n>", "Window in days: 7, 30, or 60", "30").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (appName, opts) => {
7930
+ ).option("--time-interval <n>", "Window in days: 7, 30, or 60", "30").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (appName, opts) => {
7614
7931
  try {
7615
- const fmt = opts.output;
7932
+ const fmt = await resolveOutput(customerAppsConsumption, opts);
7616
7933
  const interval = Number.parseInt(opts.timeInterval, 10);
7617
7934
  if (interval !== 7 && interval !== 30 && interval !== 60) {
7618
7935
  usageError("--time-interval must be 7, 30, or 60 (the API rejects other values)");
@@ -7646,9 +7963,9 @@ function registerRuntimeCommand(program) {
7646
7963
  }
7647
7964
  });
7648
7965
  const deploymentProfiles = runtime.command("deployment-profiles").description("List AIRS deployment profiles");
7649
- deploymentProfiles.command("list").description("List deployment profiles").option("--unactivated", "Include unactivated profiles").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (opts) => {
7966
+ const deploymentProfilesList = deploymentProfiles.command("list").description("List deployment profiles").option("--unactivated", "Include unactivated profiles").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (opts) => {
7650
7967
  try {
7651
- const fmt = opts.output;
7968
+ const fmt = await resolveOutput(deploymentProfilesList, opts);
7652
7969
  if (fmt === "pretty") renderRuntimeConfigHeader();
7653
7970
  const service = await createMgmtService();
7654
7971
  const profiles2 = await service.listDeploymentProfiles({
@@ -7660,7 +7977,7 @@ function registerRuntimeCommand(program) {
7660
7977
  }
7661
7978
  });
7662
7979
  const profiles = runtime.command("profiles").description("Manage AIRS security profiles");
7663
- profiles.command("list").description("List security profiles").option("--limit <n>", "Max results", "100").option("--offset <n>", "Starting offset", "0").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").addHelpText(
7980
+ const profilesList = registerListFlags(profiles.command("list"), { dialect: "offset" }).description("List security profiles").option("--all-versions", "Include every profile revision").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText(
7664
7981
  "after",
7665
7982
  examples(
7666
7983
  "airs runtime profiles list",
@@ -7669,49 +7986,55 @@ function registerRuntimeCommand(program) {
7669
7986
  )
7670
7987
  ).action(async (opts) => {
7671
7988
  try {
7672
- const fmt = opts.output;
7673
- if (!OUTPUT_FORMATS.includes(fmt)) {
7674
- usageError(`Invalid output format "${fmt}". Valid: ${OUTPUT_FORMATS.join(", ")}`);
7675
- }
7989
+ const fmt = await resolveOutput(profilesList, opts);
7990
+ const page = resolveListParams(profilesList, opts, { dialect: "offset" });
7676
7991
  if (fmt === "pretty") renderRuntimeConfigHeader();
7677
7992
  const service = await createMgmtService();
7993
+ if (page.all) {
7994
+ const items = await service.listAllProfiles({
7995
+ limit: page.limit,
7996
+ latest: !opts.allVersions,
7997
+ max: page.max
7998
+ });
7999
+ emitList(profilesView, items, fmt, {
8000
+ page: { returned: items.length, total: items.length, all: true }
8001
+ });
8002
+ return;
8003
+ }
7678
8004
  const result = await service.listProfiles({
7679
- limit: Number.parseInt(opts.limit, 10),
7680
- offset: Number.parseInt(opts.offset, 10)
8005
+ limit: page.limit,
8006
+ offset: page.offset,
8007
+ latest: !opts.allVersions
8008
+ });
8009
+ emitList(profilesView, result.profiles, fmt, {
8010
+ page: {
8011
+ returned: result.profiles.length,
8012
+ next: result.nextOffset
8013
+ }
7681
8014
  });
7682
- renderProfileList(result.profiles, fmt);
7683
- if (fmt === "pretty" && result.nextOffset != null) {
7684
- ui.dim(`Next offset: ${result.nextOffset}`);
7685
- }
7686
8015
  } catch (err) {
7687
8016
  fail(err);
7688
8017
  }
7689
8018
  });
7690
- profiles.command("get <nameOrId>").description("Get a security profile by name or UUID").option("--output <format>", "Output format: pretty, json, yaml", "pretty").action(async (nameOrId, opts) => {
8019
+ const profilesGet = profiles.command("get <nameOrId>").description("Get a security profile by name or UUID").option("--revision <n>", "Select an exact revision", Number).option("--all-versions", "Return every matching revision").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (nameOrId, opts) => {
7691
8020
  try {
7692
- const fmt = opts.output;
7693
- if (fmt !== "pretty" && fmt !== "json" && fmt !== "yaml") {
7694
- usageError(`Invalid output format "${fmt}". Valid: pretty, json, yaml`);
7695
- }
8021
+ const fmt = await resolveOutput(profilesGet, opts);
7696
8022
  if (fmt === "pretty") renderRuntimeConfigHeader();
7697
8023
  const service = await createMgmtService();
7698
8024
  const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
7699
8025
  nameOrId
7700
8026
  );
7701
- const profile = isUuid ? await service.getProfile(nameOrId) : await service.getProfileByName(nameOrId);
7702
- if (fmt === "json") {
7703
- console.log(JSON.stringify(profile, null, 2));
7704
- } else if (fmt === "yaml") {
7705
- const lines = [`profileId: ${profile.profileId}`, `profileName: ${profile.profileName}`];
7706
- if (profile.revision != null) lines.push(`revision: ${profile.revision}`);
7707
- if (profile.active != null) lines.push(`active: ${profile.active}`);
7708
- if (profile.createdBy) lines.push(`createdBy: ${profile.createdBy}`);
7709
- if (profile.updatedBy) lines.push(`updatedBy: ${profile.updatedBy}`);
7710
- if (profile.lastModifiedTs) lines.push(`lastModifiedTs: ${profile.lastModifiedTs}`);
7711
- if (profile.policy) lines.push(`policy: ${JSON.stringify(profile.policy, null, 2)}`);
7712
- console.log(lines.join("\n"));
8027
+ if (opts.revision !== void 0 || opts.allVersions) {
8028
+ const profiles2 = (await service.listAllProfiles({ latest: false })).filter(
8029
+ (profile) => isUuid ? profile.profileId === nameOrId : profile.profileName === nameOrId
8030
+ );
8031
+ const selected = opts.revision === void 0 ? profiles2 : profiles2.filter((profile) => profile.revision === opts.revision);
8032
+ if (selected.length === 0) throw new Error(`Profile ${nameOrId} not found`);
8033
+ if (opts.allVersions) emitList(profilesView, selected, fmt);
8034
+ else emitDetail(profilesView, selected[0], fmt);
7713
8035
  } else {
7714
- renderProfileDetail(profile);
8036
+ const profile = isUuid ? await service.getProfile(nameOrId) : await service.getProfileByName(nameOrId);
8037
+ emitDetail(profilesView, profile, fmt);
7715
8038
  }
7716
8039
  } catch (err) {
7717
8040
  fail(err);
@@ -7994,12 +8317,12 @@ function registerRuntimeCommand(program) {
7994
8317
  }
7995
8318
  });
7996
8319
  const scanLogs = runtime.command("scan-logs").description("Query AIRS scan logs");
7997
- const scanLogsQuery = scanLogs.command("query").description("Query scan logs").requiredOption("--interval <n>", "Time interval").requiredOption("--unit <unit>", "Time unit (hours)").option("--filter <filter>", "Filter: all, benign, threat", "all").option("--limit <n>", "Max results per page (API page size)", "50").option("--offset <n>", "Starting offset \u2014 rounds down to a page boundary", "0").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty");
8320
+ const scanLogsQuery = scanLogs.command("query").description("Query scan logs").requiredOption("--interval <n>", "Time interval").requiredOption("--unit <unit>", "Time unit (hours)").option("--filter <filter>", "Filter: all, benign, threat", "all").option("--limit <n>", "Max results per page (API page size)", "50").option("--offset <n>", "Starting offset \u2014 rounds down to a page boundary", "0").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml");
7998
8321
  registerPageAliases(scanLogsQuery, { sizeFlag: "--page-size", sizeKey: "pageSize" });
7999
8322
  scanLogsQuery.action(async (opts) => {
8000
8323
  try {
8001
8324
  const { page, size } = resolvePageParams(scanLogsQuery, opts, { indexBase: 1 });
8002
- const fmt = opts.output;
8325
+ const fmt = await resolveOutput(scanLogsQuery, opts);
8003
8326
  if (fmt === "pretty") renderRuntimeConfigHeader();
8004
8327
  const service = await createMgmtService();
8005
8328
  const result = await service.queryScanLogs({
@@ -8036,52 +8359,47 @@ function registerRuntimeCommand(program) {
8036
8359
  }
8037
8360
  });
8038
8361
  registerEvalCommand(topics);
8039
- topics.command("get <nameOrId>").description("Get a custom topic by name or UUID").option("--output <format>", "Output format: pretty, json, yaml", "pretty").action(async (nameOrId, opts) => {
8362
+ const topicsGet = topics.command("get <nameOrId>").description("Get a custom topic by name or UUID").option("--revision <n>", "Select an exact revision", Number).option("--all-versions", "Return every matching revision").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (nameOrId, opts) => {
8040
8363
  try {
8041
- const fmt = opts.output;
8042
- if (fmt !== "pretty" && fmt !== "json" && fmt !== "yaml") {
8043
- usageError(`Invalid output format "${fmt}". Valid: pretty, json, yaml`);
8044
- }
8364
+ const fmt = await resolveOutput(topicsGet, opts);
8045
8365
  if (fmt === "pretty") renderRuntimeConfigHeader();
8046
8366
  const service = await createMgmtService();
8047
8367
  const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
8048
8368
  nameOrId
8049
8369
  );
8050
- const topic = isUuid ? await service.getTopic(nameOrId) : await service.getTopicByName(nameOrId);
8051
- if (fmt === "json") {
8052
- console.log(JSON.stringify(topic, null, 2));
8053
- } else if (fmt === "yaml") {
8054
- const lines = [`topic_id: ${topic.topic_id}`, `topic_name: ${topic.topic_name}`];
8055
- if (topic.revision != null) lines.push(`revision: ${topic.revision}`);
8056
- if (topic.description) lines.push(`description: ${topic.description}`);
8057
- if (topic.examples?.length) {
8058
- lines.push("examples:");
8059
- for (const ex of topic.examples) lines.push(` - ${ex}`);
8060
- }
8061
- if (topic.created_by) lines.push(`created_by: ${topic.created_by}`);
8062
- if (topic.updated_by) lines.push(`updated_by: ${topic.updated_by}`);
8063
- if (topic.last_modified_ts) lines.push(`last_modified_ts: ${topic.last_modified_ts}`);
8064
- console.log(lines.join("\n"));
8370
+ if (opts.revision !== void 0 || opts.allVersions) {
8371
+ const topics2 = (await service.listTopics()).filter(
8372
+ (topic) => isUuid ? topic.topic_id === nameOrId : topic.topic_name === nameOrId
8373
+ );
8374
+ const selected = opts.revision === void 0 ? topics2 : topics2.filter((topic) => topic.revision === opts.revision);
8375
+ if (selected.length === 0) throw new Error(`Topic ${nameOrId} not found`);
8376
+ if (opts.allVersions) emitList(topicsView, selected, fmt);
8377
+ else emitDetail(topicsView, selected[0], fmt);
8065
8378
  } else {
8066
- renderTopicDetail(topic);
8379
+ const topic = isUuid ? await service.getTopic(nameOrId) : await service.getTopicByName(nameOrId);
8380
+ emitDetail(topicsView, topic, fmt);
8067
8381
  }
8068
8382
  } catch (err) {
8069
8383
  fail(err);
8070
8384
  }
8071
8385
  });
8072
- topics.command("list").description("List custom topics").option("--limit <n>", "Max results", "100").option("--offset <n>", "Starting offset", "0").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (opts) => {
8386
+ const topicsList = registerListFlags(topics.command("list"), { dialect: "offset" }).description("List custom topics").option("--all-versions", "Include every topic revision").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").action(async (opts) => {
8073
8387
  try {
8074
- const fmt = opts.output;
8388
+ const fmt = await resolveOutput(topicsList, opts);
8389
+ const params = resolveListParams(topicsList, opts, { dialect: "offset" });
8075
8390
  if (fmt === "pretty") renderRuntimeConfigHeader();
8076
8391
  const service = await createMgmtService();
8077
- const allTopics = await service.listTopics();
8078
- const offset = Number.parseInt(opts.offset, 10);
8079
- const limit = Number.parseInt(opts.limit, 10);
8080
- const page = allTopics.slice(offset, offset + limit);
8081
- renderTopicList(page, fmt);
8082
- if (fmt === "pretty" && offset + limit < allTopics.length) {
8083
- ui.dim(`Showing ${page.length} of ${allTopics.length} topics`);
8084
- }
8392
+ const allTopics = opts.allVersions ? await service.listTopics() : await service.listLatestTopics(
8393
+ params.all ? { offset: 0, limit: params.max === 0 ? 1e4 : params.max } : { offset: params.offset, limit: params.limit }
8394
+ );
8395
+ const page = opts.allVersions && !params.all ? allTopics.slice(params.offset, params.offset + params.limit) : allTopics;
8396
+ emitList(topicsView, page, fmt, {
8397
+ page: params.all ? { returned: page.length, total: page.length, all: true } : {
8398
+ returned: page.length,
8399
+ total: opts.allVersions ? allTopics.length : void 0,
8400
+ next: opts.allVersions && params.offset + params.limit < allTopics.length ? params.offset + params.limit : void 0
8401
+ }
8402
+ });
8085
8403
  } catch (err) {
8086
8404
  fail(err);
8087
8405
  }
@@ -8283,6 +8601,27 @@ function installDebugLogger(logPath) {
8283
8601
  }
8284
8602
 
8285
8603
  // src/cli/program.ts
8604
+ var READ_COMMAND_NAMES = /* @__PURE__ */ new Set([
8605
+ "categories",
8606
+ "consumption",
8607
+ "evaluation",
8608
+ "evaluations",
8609
+ "files",
8610
+ "get",
8611
+ "languages",
8612
+ "list",
8613
+ "pypi-auth",
8614
+ "query",
8615
+ "registry-credentials",
8616
+ "report",
8617
+ "stats",
8618
+ "status",
8619
+ "values",
8620
+ "version",
8621
+ "versions",
8622
+ "violation",
8623
+ "violations"
8624
+ ]);
8286
8625
  function applyListDeleteAliases(cmd) {
8287
8626
  for (const sub of cmd.commands) {
8288
8627
  if (sub.name() === "list" && !sub.aliases().includes("ls")) sub.alias("ls");
@@ -8290,16 +8629,45 @@ function applyListDeleteAliases(cmd) {
8290
8629
  applyListDeleteAliases(sub);
8291
8630
  }
8292
8631
  }
8632
+ function applyReadContractFlags(cmd) {
8633
+ for (const sub of cmd.commands) {
8634
+ const flags = () => sub.options.map((option) => option.long);
8635
+ if ((sub.name() === "list" || sub.name() === "get") && !flags().includes("--output")) {
8636
+ sub.option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml");
8637
+ }
8638
+ if (sub.name() === "list" && (flags().includes("--limit") || flags().includes("--offset"))) {
8639
+ if (!flags().includes("--limit")) sub.option("--limit <n>", "Items per page", Number, 50);
8640
+ if (!flags().includes("--offset")) sub.option("--offset <n>", "Item offset", Number, 0);
8641
+ if (!flags().includes("--all")) sub.option("--all", "Walk all pages");
8642
+ if (!flags().includes("--max")) {
8643
+ sub.option("--max <n>", "Maximum items with --all; 0 removes the cap", Number, 1e4);
8644
+ }
8645
+ }
8646
+ applyReadContractFlags(sub);
8647
+ }
8648
+ }
8649
+ function applySortedHelp(cmd) {
8650
+ cmd.configureHelp({ sortOptions: true, sortSubcommands: true });
8651
+ for (const sub of cmd.commands) applySortedHelp(sub);
8652
+ }
8293
8653
  function buildProgram() {
8294
8654
  const here = dirname4(fileURLToPath(import.meta.url));
8295
8655
  const pkg = JSON.parse(readFileSync4(join4(here, "../../package.json"), "utf-8"));
8296
8656
  const program = new Command();
8297
8657
  program.name("airs").description(
8298
8658
  "CLI and library for Palo Alto Prisma AIRS \u2014 guardrail refinement, AI red teaming, model security scanning, profile audits"
8299
- ).version(pkg.version).option("--debug", "Log all AIRS/SCM API requests and responses to a JSONL file").option("--quiet", "Suppress status and decorative output (data and errors still print)");
8300
- program.hook("preAction", (_thisCommand, actionCommand) => {
8659
+ ).version(pkg.version).option("--debug", "Log all AIRS/SCM API requests and responses to a JSONL file").option("--output <format>", "Default output format for read commands").option("--quiet", "Suppress status and decorative output (data and errors still print)");
8660
+ program.hook("preAction", async (_thisCommand, actionCommand) => {
8301
8661
  const root = actionCommand.optsWithGlobals?.() ?? _thisCommand.opts();
8302
8662
  setQuiet(Boolean(root.quiet));
8663
+ if (READ_COMMAND_NAMES.has(actionCommand.name()) && actionCommand.options.some((option) => option.long === "--output")) {
8664
+ try {
8665
+ const format = await resolveOutput(actionCommand, actionCommand.opts());
8666
+ actionCommand.setOptionValueWithSource("output", format, "implied");
8667
+ } catch (error) {
8668
+ fail(error);
8669
+ }
8670
+ }
8303
8671
  if (root.debug) {
8304
8672
  const logPath = join4(homedir(), ".prisma-airs", `debug-api-${Date.now()}.jsonl`);
8305
8673
  installDebugLogger(logPath);
@@ -8314,6 +8682,8 @@ function buildProgram() {
8314
8682
  registerDoctorCommand(program);
8315
8683
  registerCompletionCommand(program);
8316
8684
  applyListDeleteAliases(program);
8685
+ applyReadContractFlags(program);
8686
+ applySortedHelp(program);
8317
8687
  return program;
8318
8688
  }
8319
8689