@cdot65/prisma-airs-cli 4.0.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.
Files changed (2) hide show
  1. package/dist/cli/index.js +65 -34
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -287,7 +287,10 @@ var OUTPUT_FORMATS = [
287
287
  ];
288
288
  async function resolveOutput(command, opts, resolution = {}) {
289
289
  const localIsExplicit = command.getOptionValueSource?.("output") === "cli";
290
- const root = command.parent ? command.optsWithGlobals() : command.opts();
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;
291
294
  let configured;
292
295
  try {
293
296
  configured = (await loadConfig()).defaultOutput;
@@ -295,7 +298,9 @@ async function resolveOutput(command, opts, resolution = {}) {
295
298
  if (process.env.PANW_CLI_OUTPUT !== void 0) configured = process.env.PANW_CLI_OUTPUT;
296
299
  else throw error;
297
300
  }
298
- const candidate = String(localIsExplicit ? opts.output : root.output ?? configured ?? "pretty");
301
+ const candidate = String(
302
+ localIsExplicit ? opts.output : globalOutput ?? configured ?? "pretty"
303
+ );
299
304
  if (!OUTPUT_FORMATS.includes(candidate))
300
305
  throw new CliUsageError(
301
306
  `Invalid output format '${candidate}'. Expected: ${OUTPUT_FORMATS.join(", ")}`
@@ -320,7 +325,11 @@ function markdownCell(value) {
320
325
  return displayValue(value).replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r?\n/g, "<br>");
321
326
  }
322
327
  function formatOutput(rows, columns, format) {
323
- if (rows.length === 0) return format === "json" ? "[]" : "";
328
+ if (rows.length === 0) {
329
+ if (format === "json") return "[]";
330
+ if (format === "yaml") return "[]\n";
331
+ return "";
332
+ }
324
333
  const projected = rows.map((row) => columns.map((column) => row[column.key]));
325
334
  switch (format) {
326
335
  case "json":
@@ -2880,7 +2889,8 @@ function renderDeploymentProfileList(profiles, format = "pretty") {
2880
2889
  }
2881
2890
  function renderScanLogList(results, pageToken, format = "pretty") {
2882
2891
  if (results.length === 0) {
2883
- ui.emptyList("scan logs");
2892
+ if (format === "pretty") ui.emptyList("scan logs");
2893
+ else console.log(formatOutput([], [], format));
2884
2894
  return;
2885
2895
  }
2886
2896
  if (format !== "pretty") {
@@ -3013,7 +3023,7 @@ function scopeNameLooksUnrelated(name, scopeName) {
3013
3023
  function registerAiGatewayCommand(program) {
3014
3024
  const aigateway = program.command("aigateway").description("AI Gateway operations");
3015
3025
  const workspace = aigateway.command("workspace").description("Manage AI Gateway workspaces");
3016
- 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(
3017
3027
  "after",
3018
3028
  examples(
3019
3029
  "airs aigateway workspace list",
@@ -3023,7 +3033,7 @@ function registerAiGatewayCommand(program) {
3023
3033
  )
3024
3034
  ).action(async (opts) => {
3025
3035
  try {
3026
- const fmt = opts.output;
3036
+ const fmt = await resolveOutput(workspaceList, opts);
3027
3037
  if (fmt === "pretty") renderAiGatewayHeader();
3028
3038
  const plane = parsePlane(opts.plane);
3029
3039
  const status = parseStatus(opts.status);
@@ -3044,7 +3054,7 @@ function registerAiGatewayCommand(program) {
3044
3054
  failWithGrantHint(err);
3045
3055
  }
3046
3056
  });
3047
- 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(
3048
3058
  "after",
3049
3059
  examples(
3050
3060
  "airs aigateway workspace get ws-main-a-349e0e",
@@ -3052,7 +3062,7 @@ function registerAiGatewayCommand(program) {
3052
3062
  )
3053
3063
  ).action(async (ref, opts) => {
3054
3064
  try {
3055
- const fmt = opts.output;
3065
+ const fmt = await resolveOutput(workspaceGet, opts);
3056
3066
  if (fmt === "pretty") renderAiGatewayHeader();
3057
3067
  const plane = parsePlane(opts.plane);
3058
3068
  const service = await createService();
@@ -3820,9 +3830,9 @@ async function createService2() {
3820
3830
  function registerModelSecurityCommand(program) {
3821
3831
  const ms = program.command("model-security").description("AI Model Security operations \u2014 groups, rules, scans");
3822
3832
  const groups = ms.command("groups").description("Manage security groups");
3823
- 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) => {
3824
3834
  try {
3825
- const fmt = opts.output;
3835
+ const fmt = await resolveOutput(groupsList, opts);
3826
3836
  if (fmt === "pretty") renderModelSecurityHeader();
3827
3837
  const service = await createService2();
3828
3838
  const listOptions = {
@@ -3840,9 +3850,9 @@ function registerModelSecurityCommand(program) {
3840
3850
  fail(err);
3841
3851
  }
3842
3852
  });
3843
- 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) => {
3844
3854
  try {
3845
- const fmt = opts.output;
3855
+ const fmt = await resolveOutput(groupsGet, opts);
3846
3856
  if (fmt === "pretty") renderModelSecurityHeader();
3847
3857
  const service = await createService2();
3848
3858
  const group = await service.getGroup(uuid);
@@ -4070,9 +4080,9 @@ function registerModelSecurityCommand(program) {
4070
4080
  }
4071
4081
  });
4072
4082
  const rules = ms.command("rules").description("Browse security rules");
4073
- 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) => {
4074
4084
  try {
4075
- const fmt = opts.output;
4085
+ const fmt = await resolveOutput(rulesList, opts);
4076
4086
  if (fmt === "pretty") renderModelSecurityHeader();
4077
4087
  const service = await createService2();
4078
4088
  const listOptions = {
@@ -4099,7 +4109,7 @@ function registerModelSecurityCommand(program) {
4099
4109
  }
4100
4110
  });
4101
4111
  const scans = ms.command("scans").description("Model security scan operations");
4102
- 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(
4103
4113
  "after",
4104
4114
  examples(
4105
4115
  "airs model-security scans list",
@@ -4108,7 +4118,7 @@ function registerModelSecurityCommand(program) {
4108
4118
  )
4109
4119
  ).action(async (opts) => {
4110
4120
  try {
4111
- const fmt = opts.output;
4121
+ const fmt = await resolveOutput(scansList, opts);
4112
4122
  if (fmt === "pretty") renderModelSecurityHeader();
4113
4123
  const service = await createService2();
4114
4124
  const listOptions = {
@@ -4207,9 +4217,9 @@ function registerModelSecurityCommand(program) {
4207
4217
  }
4208
4218
  });
4209
4219
  const models = ms.command("models").description("Browse the scanned model catalog (read-only)");
4210
- 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) => {
4211
4221
  try {
4212
- const fmt = opts.output;
4222
+ const fmt = await resolveOutput(modelsList, opts);
4213
4223
  if (fmt === "pretty") renderModelSecurityHeader();
4214
4224
  const service = await createService2();
4215
4225
  const listOptions = {
@@ -4226,9 +4236,9 @@ function registerModelSecurityCommand(program) {
4226
4236
  fail(err);
4227
4237
  }
4228
4238
  });
4229
- 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) => {
4230
4240
  try {
4231
- const fmt = opts.output;
4241
+ const fmt = await resolveOutput(modelsGet, opts);
4232
4242
  if (fmt === "pretty") renderModelSecurityHeader();
4233
4243
  const service = await createService2();
4234
4244
  const model = await service.getModel(uuid);
@@ -4237,9 +4247,9 @@ function registerModelSecurityCommand(program) {
4237
4247
  fail(err);
4238
4248
  }
4239
4249
  });
4240
- 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) => {
4241
4251
  try {
4242
- const fmt = opts.output;
4252
+ const fmt = await resolveOutput(modelVersions, opts);
4243
4253
  if (fmt === "pretty") renderModelSecurityHeader();
4244
4254
  const service = await createService2();
4245
4255
  const result = await service.listModelVersions(modelUuid, {
@@ -4252,9 +4262,9 @@ function registerModelSecurityCommand(program) {
4252
4262
  fail(err);
4253
4263
  }
4254
4264
  });
4255
- 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) => {
4256
4266
  try {
4257
- const fmt = opts.output;
4267
+ const fmt = await resolveOutput(modelVersion, opts);
4258
4268
  if (fmt === "pretty") renderModelSecurityHeader();
4259
4269
  const service = await createService2();
4260
4270
  const version = await service.getModelVersion(uuid);
@@ -4263,9 +4273,9 @@ function registerModelSecurityCommand(program) {
4263
4273
  fail(err);
4264
4274
  }
4265
4275
  });
4266
- 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) => {
4267
4277
  try {
4268
- const fmt = opts.output;
4278
+ const fmt = await resolveOutput(modelFiles, opts);
4269
4279
  if (fmt === "pretty") renderModelSecurityHeader();
4270
4280
  const service = await createService2();
4271
4281
  const result = await service.listModelVersionFiles(modelVersionUuid, {
@@ -7912,14 +7922,14 @@ function registerRuntimeCommand(program) {
7912
7922
  fail(err);
7913
7923
  }
7914
7924
  });
7915
- customerApps.command("consumption").argument(
7925
+ const customerAppsConsumption = customerApps.command("consumption").argument(
7916
7926
  "[appName]",
7917
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."
7918
7928
  ).description(
7919
7929
  "Show per-app token consumption + violation breakdown (SCM dashboard). Omit appName to scan all apps."
7920
- ).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) => {
7921
7931
  try {
7922
- const fmt = opts.output;
7932
+ const fmt = await resolveOutput(customerAppsConsumption, opts);
7923
7933
  const interval = Number.parseInt(opts.timeInterval, 10);
7924
7934
  if (interval !== 7 && interval !== 30 && interval !== 60) {
7925
7935
  usageError("--time-interval must be 7, 30, or 60 (the API rejects other values)");
@@ -7953,9 +7963,9 @@ function registerRuntimeCommand(program) {
7953
7963
  }
7954
7964
  });
7955
7965
  const deploymentProfiles = runtime.command("deployment-profiles").description("List AIRS deployment profiles");
7956
- 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) => {
7957
7967
  try {
7958
- const fmt = opts.output;
7968
+ const fmt = await resolveOutput(deploymentProfilesList, opts);
7959
7969
  if (fmt === "pretty") renderRuntimeConfigHeader();
7960
7970
  const service = await createMgmtService();
7961
7971
  const profiles2 = await service.listDeploymentProfiles({
@@ -8307,12 +8317,12 @@ function registerRuntimeCommand(program) {
8307
8317
  }
8308
8318
  });
8309
8319
  const scanLogs = runtime.command("scan-logs").description("Query AIRS scan logs");
8310
- 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");
8311
8321
  registerPageAliases(scanLogsQuery, { sizeFlag: "--page-size", sizeKey: "pageSize" });
8312
8322
  scanLogsQuery.action(async (opts) => {
8313
8323
  try {
8314
8324
  const { page, size } = resolvePageParams(scanLogsQuery, opts, { indexBase: 1 });
8315
- const fmt = opts.output;
8325
+ const fmt = await resolveOutput(scanLogsQuery, opts);
8316
8326
  if (fmt === "pretty") renderRuntimeConfigHeader();
8317
8327
  const service = await createMgmtService();
8318
8328
  const result = await service.queryScanLogs({
@@ -8591,6 +8601,27 @@ function installDebugLogger(logPath) {
8591
8601
  }
8592
8602
 
8593
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
+ ]);
8594
8625
  function applyListDeleteAliases(cmd) {
8595
8626
  for (const sub of cmd.commands) {
8596
8627
  if (sub.name() === "list" && !sub.aliases().includes("ls")) sub.alias("ls");
@@ -8629,7 +8660,7 @@ function buildProgram() {
8629
8660
  program.hook("preAction", async (_thisCommand, actionCommand) => {
8630
8661
  const root = actionCommand.optsWithGlobals?.() ?? _thisCommand.opts();
8631
8662
  setQuiet(Boolean(root.quiet));
8632
- if ((actionCommand.name() === "list" || actionCommand.name() === "get") && actionCommand.options.some((option) => option.long === "--output")) {
8663
+ if (READ_COMMAND_NAMES.has(actionCommand.name()) && actionCommand.options.some((option) => option.long === "--output")) {
8633
8664
  try {
8634
8665
  const format = await resolveOutput(actionCommand, actionCommand.opts());
8635
8666
  actionCommand.setOptionValueWithSource("output", format, "implied");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cdot65/prisma-airs-cli",
3
3
  "packageManager": "pnpm@10.6.5",
4
- "version": "4.0.0",
4
+ "version": "4.0.1",
5
5
  "description": "CLI and library for Palo Alto Prisma AIRS — guardrail refinement, AI red teaming, model security scanning, profile audits",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",