@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.
- package/dist/cli/index.js +97 -27
- 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
|
-
|
|
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 =
|
|
188
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
4708
|
+
registerFilteredMetric(telemetry, "requests", "Get request count");
|
|
4643
4709
|
registerMetric(telemetry, "rescued-retries", "Get rescued retry telemetry", "rescuedRetries");
|
|
4644
|
-
|
|
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 =
|
|
4846
|
-
"
|
|
4847
|
-
|
|
4848
|
-
"
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
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({
|
|
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
|
|
6667
|
+
const csv2 = await service.downloadTemplate(uuid);
|
|
6598
6668
|
const outPath = opts.outputFile || `${uuid}-template.csv`;
|
|
6599
|
-
fs2.writeFileSync(outPath,
|
|
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(
|
|
9051
|
-
const lines =
|
|
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
|
|
9318
|
+
const csv2 = buildSampleCsv();
|
|
9249
9319
|
if (opts.outputFile) {
|
|
9250
|
-
await writeFile4(opts.outputFile,
|
|
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(
|
|
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.
|
|
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.
|
|
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",
|