@cdot65/prisma-airs-cli 4.2.1 → 4.3.0

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 +97 -27
  2. package/package.json +2 -2
package/dist/cli/index.js CHANGED
@@ -54,6 +54,7 @@ import {
54
54
 
55
55
  // src/airs/aigateway.ts
56
56
  import {
57
+ AIGatewayChartFiltersSchema,
57
58
  AIGatewayClient
58
59
  } from "@cdot65/prisma-airs-sdk";
59
60
  function toLimitArray(value) {
@@ -164,7 +165,7 @@ var SdkAiGatewayService = class {
164
165
  * Match a user-supplied ref against the workspace list so name | slug |
165
166
  * uuid all work. Unmatched refs pass through so the API's own error stands.
166
167
  */
167
- async resolveWorkspaceRef(ref, planes) {
168
+ async resolveWorkspaceRef(ref, planes, requireSlug = false) {
168
169
  for (const plane of planes) {
169
170
  let rows;
170
171
  try {
@@ -172,7 +173,8 @@ var SdkAiGatewayService = class {
172
173
  } catch {
173
174
  continue;
174
175
  }
175
- if (rows.some((w) => w.id === ref || w.slug === ref)) return ref;
176
+ const byRef = rows.find((w) => w.id === ref || w.slug === ref);
177
+ if (byRef) return requireSlug ? byRef.slug : ref;
176
178
  const byName = rows.filter((w) => w.name === ref);
177
179
  if (byName.length > 1) {
178
180
  throw new Error(
@@ -184,11 +186,15 @@ var SdkAiGatewayService = class {
184
186
  return ref;
185
187
  }
186
188
  async getTelemetryCost(opts) {
187
- const days = opts.days ?? 7;
188
- const workspaceSlug = await this.resolveWorkspaceRef(opts.workspaceSlug, ["data", "admin"]);
189
+ const { workspaceSlug: workspaceRef, days = 7, ...rawFilters } = opts;
190
+ const filters = AIGatewayChartFiltersSchema.parse(rawFilters);
191
+ if (!Number.isSafeInteger(days) || days <= 0)
192
+ throw new Error("Expected days to be a positive integer");
193
+ const workspaceSlug = await this.resolveWorkspaceRef(workspaceRef, ["data", "admin"], true);
189
194
  const raw = await this.client.telemetry.cost({
190
195
  workspaceSlug,
191
- days
196
+ days,
197
+ ...filters
192
198
  });
193
199
  return {
194
200
  workspaceSlug,
@@ -4543,6 +4549,52 @@ function registerAiGatewayInventory(root) {
4543
4549
  );
4544
4550
  }
4545
4551
 
4552
+ // src/cli/commands/aigateway/telemetry-filters.ts
4553
+ import { AIGatewayChartFiltersSchema as AIGatewayChartFiltersSchema2 } from "@cdot65/prisma-airs-sdk";
4554
+ function addChartFilterOptions(command) {
4555
+ return command.option("--trace-id <id>", "Filter by one trace ID").option("--metadata <json>", "Filter by string-valued metadata (JSON object)").option("--status-codes <codes>", "Comma-separated HTTP statuses (OR within the list)").option("--api-key-ids <ids>", "Comma-separated service key UUIDs (not secret key values)").option(
4556
+ "--ai-org-models <pairs>",
4557
+ "Comma-separated provider__model pairs, e.g. openai__gpt-5.6-terra"
4558
+ ).option("--total-units-min <n>", "Inclusive minimum total tokens (nonnegative integer)").option("--total-units-max <n>", "Inclusive maximum total tokens (nonnegative integer)").option("--cost-min <cents>", "Inclusive minimum cost in cents (fractional cents allowed)").option("--cost-max <cents>", "Inclusive maximum cost in cents (fractional cents allowed)");
4559
+ }
4560
+ function csv(value) {
4561
+ const values = value.split(",").map((entry) => entry.trim());
4562
+ if (values.some((entry) => entry.length === 0)) throw new CliUsageError("Empty CSV element");
4563
+ return values;
4564
+ }
4565
+ function decimal(value) {
4566
+ if (!/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(value)) {
4567
+ throw new CliUsageError("Expected a decimal number");
4568
+ }
4569
+ return Number(value);
4570
+ }
4571
+ function chartFiltersFrom(opts) {
4572
+ const filters = {};
4573
+ try {
4574
+ if (opts.traceId !== void 0) filters.traceId = opts.traceId;
4575
+ if (opts.metadata !== void 0) filters.metadata = parseJsonOption(opts.metadata);
4576
+ if (opts.statusCodes !== void 0)
4577
+ filters.statusCodes = csv(opts.statusCodes).map(parseIntegerOption);
4578
+ if (opts.apiKeyIds !== void 0) filters.apiKeyIds = csv(opts.apiKeyIds);
4579
+ if (opts.aiOrgModels !== void 0) filters.aiOrgModels = csv(opts.aiOrgModels);
4580
+ for (const key of ["totalUnitsMin", "totalUnitsMax"]) {
4581
+ if (opts[key] !== void 0) filters[key] = parseIntegerOption(opts[key]);
4582
+ }
4583
+ for (const key of ["costMin", "costMax"]) {
4584
+ if (opts[key] !== void 0) filters[key] = decimal(opts[key]);
4585
+ }
4586
+ } catch {
4587
+ throw new CliUsageError("Invalid chart filters: check JSON, CSV and numeric flag syntax");
4588
+ }
4589
+ const result = AIGatewayChartFiltersSchema2.safeParse(filters);
4590
+ if (!result.success) {
4591
+ throw new CliUsageError(
4592
+ "Invalid chart filters: check status codes, UUIDs, provider__model pairs, string metadata and inclusive nonnegative bounds"
4593
+ );
4594
+ }
4595
+ return result.data;
4596
+ }
4597
+
4546
4598
  // src/cli/commands/aigateway/telemetry.ts
4547
4599
  function addWindowOptions(command) {
4548
4600
  return addReadOutput(
@@ -4582,6 +4634,20 @@ function registerMetric(telemetry, name, description, method) {
4582
4634
  (opts) => runDetail(command, opts, (client) => client.telemetry[method](windowFrom(opts)))
4583
4635
  );
4584
4636
  }
4637
+ function registerFilteredMetric(telemetry, name, description) {
4638
+ const command = addChartFilterOptions(
4639
+ addWindowOptions(telemetry.command(name).description(description))
4640
+ );
4641
+ command.action(async (opts) => {
4642
+ let options;
4643
+ try {
4644
+ options = { ...windowFrom(opts), ...chartFiltersFrom(opts) };
4645
+ } catch (error) {
4646
+ failAiGateway(error);
4647
+ }
4648
+ await runDetail(command, opts, (client) => client.telemetry[name](options));
4649
+ });
4650
+ }
4585
4651
  function registerAiGatewayTelemetryReads(telemetry) {
4586
4652
  const cache = showHelpOnEmpty(telemetry.command("cache").description("Inspect cache telemetry"));
4587
4653
  const cacheSummary = addWindowOptions(cache.command("summary").description("Get cache totals"));
@@ -4622,7 +4688,7 @@ function registerAiGatewayTelemetryReads(telemetry) {
4622
4688
  })
4623
4689
  )
4624
4690
  );
4625
- registerMetric(telemetry, "latency", "Get latency telemetry", "latency");
4691
+ registerFilteredMetric(telemetry, "latency", "Get latency telemetry");
4626
4692
  const logs = showHelpOnEmpty(telemetry.command("logs").description("Inspect request logs"));
4627
4693
  const logsList = addWindowOptions(
4628
4694
  logs.command("list").description("List request logs").option("--page-size <n>", "Rows per response", "50").option("--status-code <code>", "Filter by HTTP status").option("--trace-id <id>", "Return one trace id")
@@ -4639,9 +4705,9 @@ function registerAiGatewayTelemetryReads(telemetry) {
4639
4705
  })
4640
4706
  )
4641
4707
  );
4642
- registerMetric(telemetry, "requests", "Get request count", "requests");
4708
+ registerFilteredMetric(telemetry, "requests", "Get request count");
4643
4709
  registerMetric(telemetry, "rescued-retries", "Get rescued retry telemetry", "rescuedRetries");
4644
- registerMetric(telemetry, "tokens", "Get token usage", "tokens");
4710
+ registerFilteredMetric(telemetry, "tokens", "Get token usage");
4645
4711
  registerMetric(telemetry, "user-trends", "Get user trends", "userTrends");
4646
4712
  registerMetric(telemetry, "users", "Get unique-user count", "users");
4647
4713
  }
@@ -4842,24 +4908,28 @@ function registerAiGatewayCommand(program) {
4842
4908
  workspace.command("delete <ref>", { hidden: true }).description("Deprecated compatibility command for archive").option("--force", "Skip confirmation prompt").action((ref, opts) => archiveWorkspace(ref, opts, true));
4843
4909
  const telemetry = aigateway.command("telemetry").description("AI Gateway runtime telemetry (data plane)").action(() => telemetry.outputHelp());
4844
4910
  registerAiGatewayTelemetryReads(telemetry);
4845
- const cost = telemetry.command("cost").description(
4846
- "Total and per-day spend for a workspace (API reports cents; pretty output shows dollars)"
4847
- ).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(
4848
- "after",
4849
- examples(
4850
- "airs aigateway telemetry cost --workspace ws-main-a-349e0e",
4851
- "airs aigateway telemetry cost --workspace ws-main-a-349e0e --days 30 --output json"
4911
+ const cost = addChartFilterOptions(
4912
+ telemetry.command("cost").description(
4913
+ "Total and per-day spend for a workspace (API reports cents; pretty output shows dollars)"
4914
+ ).requiredOption("--workspace <ref>", "Workspace slug, UUID, or unique display name").option("--days <n>", "Rolling window in days, counted back from now", "7").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText(
4915
+ "after",
4916
+ examples(
4917
+ "airs aigateway telemetry cost --workspace ws-main-a-349e0e",
4918
+ "airs aigateway telemetry cost --workspace ws-main-a-349e0e --days 30 --output json"
4919
+ )
4852
4920
  )
4853
4921
  ).action(async (opts) => {
4854
4922
  try {
4923
+ const days = parsePositiveInteger2(opts.days, "--days");
4924
+ const filters = chartFiltersFrom(opts);
4855
4925
  const fmt = await resolveOutput(cost, opts);
4856
4926
  if (fmt === "pretty") renderAiGatewayHeader();
4857
- const days = Number.parseInt(opts.days, 10);
4858
- if (!Number.isFinite(days) || days <= 0) {
4859
- usageError(`Invalid --days '${opts.days}'. Expected a positive integer`);
4860
- }
4861
4927
  const service = await createService();
4862
- const report = await service.getTelemetryCost({ workspaceSlug: opts.workspace, days });
4928
+ const report = await service.getTelemetryCost({
4929
+ workspaceSlug: opts.workspace,
4930
+ days,
4931
+ ...filters
4932
+ });
4863
4933
  renderCostReport(report, fmt);
4864
4934
  } catch (err) {
4865
4935
  failWithGrantHint(err);
@@ -6594,9 +6664,9 @@ function registerRedteamCommand(program) {
6594
6664
  try {
6595
6665
  renderRedteamHeader();
6596
6666
  const service = await createPromptSetService();
6597
- const csv = await service.downloadTemplate(uuid);
6667
+ const csv2 = await service.downloadTemplate(uuid);
6598
6668
  const outPath = opts.outputFile || `${uuid}-template.csv`;
6599
- fs2.writeFileSync(outPath, csv, "utf-8");
6669
+ fs2.writeFileSync(outPath, csv2, "utf-8");
6600
6670
  ui.success(`Template saved to ${outPath}`);
6601
6671
  } catch (err) {
6602
6672
  fail(err);
@@ -9047,8 +9117,8 @@ function parseCsvLine(line) {
9047
9117
  }
9048
9118
  return fields;
9049
9119
  }
9050
- function loadPrompts(csv, onWarning) {
9051
- const lines = csv.split(/\r?\n/);
9120
+ function loadPrompts(csv2, onWarning) {
9121
+ const lines = csv2.split(/\r?\n/);
9052
9122
  const nonEmpty = lines.filter((l) => l.trim() !== "");
9053
9123
  if (nonEmpty.length === 0) {
9054
9124
  throw new Error("CSV is empty");
@@ -9245,12 +9315,12 @@ function registerSampleCommand(parent) {
9245
9315
  });
9246
9316
  cmd.action(async (opts) => {
9247
9317
  resolveDeprecatedAliases(cmd, opts);
9248
- const csv = buildSampleCsv();
9318
+ const csv2 = buildSampleCsv();
9249
9319
  if (opts.outputFile) {
9250
- await writeFile4(opts.outputFile, csv, "utf-8");
9320
+ await writeFile4(opts.outputFile, csv2, "utf-8");
9251
9321
  ui.success(`Sample CSV written to ${opts.outputFile}`);
9252
9322
  } else {
9253
- process.stdout.write(csv);
9323
+ process.stdout.write(csv2);
9254
9324
  }
9255
9325
  });
9256
9326
  }
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.2.1",
4
+ "version": "4.3.0",
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",
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "license": "MIT",
59
59
  "dependencies": {
60
- "@cdot65/prisma-airs-sdk": "0.22.0",
60
+ "@cdot65/prisma-airs-sdk": "0.24.0",
61
61
  "@inquirer/prompts": "^8.3.0",
62
62
  "chalk": "^5.6.2",
63
63
  "commander": "^14.0.3",