@cdot65/prisma-airs-cli 4.1.2 → 4.2.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.
@@ -2254,6 +2254,11 @@ var ConfigSchema = z.object({
2254
2254
  aiGwDataEndpoint: z.string().optional(),
2255
2255
  aiGwAdminEndpoint: z.string().optional(),
2256
2256
  aiGwTokenEndpoint: z.string().optional(),
2257
+ // Runtime inference has separate key authentication, never management OAuth.
2258
+ aiGwInferenceEndpoint: z.string().optional(),
2259
+ aiGwInferenceApiKey: z.string().optional(),
2260
+ aiGwInferenceModel: z.string().optional(),
2261
+ aiGwEmbeddingModel: z.string().optional(),
2257
2262
  // Tuning
2258
2263
  scanConcurrency: z.coerce.number().int().min(1).max(20).default(5),
2259
2264
  defaultOutput: z.enum(["pretty", "table", "markdown", "csv", "json", "yaml"]).optional(),
@@ -2288,6 +2293,10 @@ function fromEnv() {
2288
2293
  aiGwDataEndpoint: env.PANW_AI_GW_DATA_ENDPOINT,
2289
2294
  aiGwAdminEndpoint: env.PANW_AI_GW_ADMIN_ENDPOINT,
2290
2295
  aiGwTokenEndpoint: env.PANW_AI_GW_TOKEN_ENDPOINT,
2296
+ aiGwInferenceEndpoint: env.PANW_AI_GW_INFERENCE_ENDPOINT,
2297
+ aiGwInferenceApiKey: env.PANW_AI_GW_INFERENCE_API_KEY,
2298
+ aiGwInferenceModel: env.PANW_AI_GW_INFERENCE_MODEL,
2299
+ aiGwEmbeddingModel: env.PANW_AI_GW_EMBEDDING_MODEL,
2291
2300
  scanConcurrency: env.SCAN_CONCURRENCY,
2292
2301
  defaultOutput: env.PANW_CLI_OUTPUT,
2293
2302
  dataDir: env.DATA_DIR
package/dist/cli/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  sanitizeFilename,
21
21
  validateTopic,
22
22
  writeBackupFile
23
- } from "../chunk-W5YDJS7H.js";
23
+ } from "../chunk-LF6JPUA4.js";
24
24
 
25
25
  // src/cli/index.ts
26
26
  import "dotenv/config";
@@ -2970,6 +2970,168 @@ ${lines.map((l) => ` $ ${l}`).join("\n")}
2970
2970
  `;
2971
2971
  }
2972
2972
 
2973
+ // src/cli/commands/aigateway/inference.ts
2974
+ import { readFile } from "fs/promises";
2975
+ import {
2976
+ AIGatewayInferenceClient,
2977
+ ErrorType,
2978
+ GatewayInferenceInputCreateChatCompletionRequestSchema,
2979
+ GatewayInferenceInputCreateEmbeddingRequestSchema,
2980
+ GatewayInferenceInputCreateResponseSchema
2981
+ } from "@cdot65/prisma-airs-sdk";
2982
+ import { dump as dump3 } from "js-yaml";
2983
+ function integer(raw, flag) {
2984
+ const value = Number(raw);
2985
+ if (!Number.isSafeInteger(value) || value < 1)
2986
+ throw new CliUsageError(`${flag} must be a positive integer`);
2987
+ return value;
2988
+ }
2989
+ async function inferenceBody(kind, prompt, opts, defaultModel) {
2990
+ if (prompt !== void 0 && opts.file)
2991
+ throw new CliUsageError("Pass a prompt or --file, not both");
2992
+ if (prompt === void 0 && !opts.file) throw new CliUsageError("A prompt or --file is required");
2993
+ let body;
2994
+ if (opts.file) {
2995
+ let value;
2996
+ try {
2997
+ value = JSON.parse(await readFile(opts.file, "utf8"));
2998
+ } catch {
2999
+ throw new CliUsageError("--file must name a readable JSON request file");
3000
+ }
3001
+ if (!value || typeof value !== "object" || Array.isArray(value))
3002
+ throw new CliUsageError("--file must contain a JSON object");
3003
+ body = value;
3004
+ } else {
3005
+ body = kind === "chat" ? { messages: [{ role: "user", content: prompt }] } : { input: prompt };
3006
+ }
3007
+ body = { ...body };
3008
+ const model = opts.model ?? body.model ?? defaultModel;
3009
+ if (typeof model !== "string" || !model.trim())
3010
+ throw new CliUsageError(
3011
+ "Set --model, the request model, or the matching inference model config"
3012
+ );
3013
+ body.model = model;
3014
+ if (opts.stream !== void 0) body.stream = opts.stream;
3015
+ if (opts.maxTokens !== void 0)
3016
+ body[kind === "responses" ? "max_output_tokens" : "max_completion_tokens"] = integer(
3017
+ opts.maxTokens,
3018
+ "--max-tokens"
3019
+ );
3020
+ if (opts.dimensions !== void 0) body.dimensions = integer(opts.dimensions, "--dimensions");
3021
+ if (opts.encoding !== void 0) body.encoding_format = opts.encoding;
3022
+ if (kind === "responses" && body.store === void 0) body.store = false;
3023
+ return body;
3024
+ }
3025
+ function responseText(value) {
3026
+ if (!value || typeof value !== "object") return "";
3027
+ const record = value;
3028
+ if (record.type === "response.output_text.delta" && typeof record.delta === "string")
3029
+ return record.delta;
3030
+ if (Array.isArray(record.choices))
3031
+ return record.choices.map((choice) => choice.message?.content ?? choice.delta?.content ?? "").join("");
3032
+ if (Array.isArray(record.output))
3033
+ return record.output.flatMap((item) => Array.isArray(item.content) ? item.content : []).filter((part) => part.type === "output_text").map((part) => part.text).join("");
3034
+ return "";
3035
+ }
3036
+ function registerAiGatewayInference(parent) {
3037
+ const group = parent.command("inference").description("Call the gateway runtime (separate API key; no SCM OAuth required)");
3038
+ for (const kind of ["chat", "responses", "embeddings"]) {
3039
+ const command = group.command(`${kind} [prompt]`).description(
3040
+ `${kind === "embeddings" ? "Create embeddings" : "Generate a model response"} through your runtime gateway`
3041
+ ).option("--model <id>", "Provider-prefixed model ID; overrides request file and config").option("--endpoint <url>", "Runtime base URL including /v1; overrides config").option("--file <path>", "JSON request file (mutually exclusive with prompt)").option("--timeout <ms>", "Total deadline including streaming, in milliseconds", "60000").option(
3042
+ "--output <format>",
3043
+ "pretty, json or yaml; streaming json emits one chunk per line (JSONL)"
3044
+ );
3045
+ if (kind === "embeddings")
3046
+ command.option("--dimensions <n>", "Embedding dimensions").option("--encoding <format>", "float or base64");
3047
+ else
3048
+ command.option("--stream", "Stream text (pretty) or validated events (json/JSONL)").option("--max-tokens <n>", "Maximum completion/output tokens");
3049
+ command.action(async (prompt, opts) => {
3050
+ const controller = new AbortController();
3051
+ const interrupt = () => controller.abort(new Error("Inference interrupted"));
3052
+ const outputError = (error) => controller.abort(error);
3053
+ let stream;
3054
+ let failed = false;
3055
+ let failure;
3056
+ try {
3057
+ const format = await resolveOutput(command, opts, { allowed: ["pretty", "json", "yaml"] });
3058
+ const config = await loadConfig({ aiGwInferenceEndpoint: opts.endpoint });
3059
+ const body = await inferenceBody(
3060
+ kind,
3061
+ prompt,
3062
+ opts,
3063
+ kind === "embeddings" ? config.aiGwEmbeddingModel : config.aiGwInferenceModel
3064
+ );
3065
+ if (body.stream === true && format === "yaml")
3066
+ throw new CliUsageError("Streaming supports --output pretty or json (JSONL), not yaml");
3067
+ const schema = kind === "chat" ? GatewayInferenceInputCreateChatCompletionRequestSchema : kind === "responses" ? GatewayInferenceInputCreateResponseSchema : GatewayInferenceInputCreateEmbeddingRequestSchema;
3068
+ const parsed = schema.safeParse(body);
3069
+ if (!parsed.success)
3070
+ throw new CliUsageError(
3071
+ `Invalid inference request: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.code}`).join("; ")}`
3072
+ );
3073
+ const client = new AIGatewayInferenceClient({
3074
+ endpoint: config.aiGwInferenceEndpoint,
3075
+ apiKey: config.aiGwInferenceApiKey,
3076
+ timeoutMs: integer(opts.timeout ?? "60000", "--timeout"),
3077
+ numRetries: 0
3078
+ });
3079
+ process.once("SIGINT", interrupt);
3080
+ process.once("SIGTERM", interrupt);
3081
+ process.stdout.on("error", outputError);
3082
+ const write = (text) => new Promise((resolve2, reject) => {
3083
+ process.stdout.write(text, (error) => error ? reject(error) : resolve2());
3084
+ });
3085
+ const requestOptions = { signal: controller.signal };
3086
+ const result = kind === "chat" ? await client.createChatCompletion(
3087
+ GatewayInferenceInputCreateChatCompletionRequestSchema.parse(body),
3088
+ requestOptions
3089
+ ) : kind === "responses" ? await client.createResponse(
3090
+ GatewayInferenceInputCreateResponseSchema.parse(body),
3091
+ requestOptions
3092
+ ) : await client.createEmbedding(
3093
+ GatewayInferenceInputCreateEmbeddingRequestSchema.parse(body),
3094
+ requestOptions
3095
+ );
3096
+ if (Symbol.asyncIterator in result) {
3097
+ stream = result;
3098
+ for await (const event of result) {
3099
+ if (format === "json") await write(`${JSON.stringify(event)}
3100
+ `);
3101
+ else {
3102
+ const text = responseText(event);
3103
+ if (text) await write(text);
3104
+ }
3105
+ if ("type" in event && (event.type === "response.failed" || event.type === "response.incomplete"))
3106
+ throw new Error(`Gateway generation ended with ${event.type}`);
3107
+ }
3108
+ if (format === "pretty") await write("\n");
3109
+ } else {
3110
+ const text = format === "yaml" ? dump3(result, { noRefs: true }).trimEnd() : format === "pretty" && kind !== "embeddings" ? responseText(result) : JSON.stringify(result, null, 2);
3111
+ await write(`${text}
3112
+ `);
3113
+ if ("status" in result && (result.status === "failed" || result.status === "incomplete"))
3114
+ throw new Error(`Gateway generation ended with ${result.status}`);
3115
+ }
3116
+ } catch (error) {
3117
+ failed = true;
3118
+ failure = error;
3119
+ } finally {
3120
+ await stream?.cancel();
3121
+ controller.abort();
3122
+ process.removeListener("SIGINT", interrupt);
3123
+ process.removeListener("SIGTERM", interrupt);
3124
+ process.stdout.removeListener("error", outputError);
3125
+ }
3126
+ if (failed && failure?.code !== "EPIPE") {
3127
+ if (failure?.errorType === ErrorType.USER_REQUEST_PAYLOAD_ERROR)
3128
+ usageError(failure instanceof Error ? failure.message : "Invalid inference options");
3129
+ fail(failure);
3130
+ }
3131
+ });
3132
+ }
3133
+ }
3134
+
2973
3135
  // src/cli/commands/aigateway/inventory.ts
2974
3136
  import {
2975
3137
  AI_GATEWAY_DEPLOYMENT_STATUSES,
@@ -3036,7 +3198,7 @@ function isSensitiveKey(key) {
3036
3198
  function redactHeaders(headers) {
3037
3199
  const out = {};
3038
3200
  for (const [k, v] of Object.entries(headers)) {
3039
- out[k] = isSensitiveKey(k) ? MASK : v;
3201
+ out[k] = isSensitiveKey(k) || /^x-portkey-(config|metadata|forward-headers)$/i.test(k) ? MASK : v;
3040
3202
  }
3041
3203
  return out;
3042
3204
  }
@@ -3106,20 +3268,27 @@ function headersToRecord(headers) {
3106
3268
  }
3107
3269
  var KEEP_DEBUG_LOGS = 10;
3108
3270
  function installDebugLogger(logPath) {
3109
- mkdirSync(dirname(logPath), { recursive: true });
3110
- writeFileSync(logPath, "", "utf-8");
3271
+ mkdirSync(dirname(logPath), { recursive: true, mode: 448 });
3272
+ writeFileSync(logPath, "", { encoding: "utf8", mode: 384 });
3111
3273
  pruneDebugLogs(dirname(logPath), KEEP_DEBUG_LOGS);
3112
3274
  const originalFetch = globalThis.fetch;
3113
3275
  globalThis.fetch = async function debugFetch(input, init2) {
3114
3276
  const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
3115
- if (!isAirsUrl(url)) {
3277
+ const rawHeaders = headersToRecord(
3278
+ init2?.headers ?? (input instanceof Request ? input.headers : void 0)
3279
+ );
3280
+ const inference = Object.keys(rawHeaders).some(
3281
+ (name) => name.toLowerCase() === "x-portkey-api-key"
3282
+ );
3283
+ if (!isAirsUrl(url) && !inference) {
3116
3284
  return originalFetch(input, init2);
3117
3285
  }
3118
3286
  const method = init2?.method ?? (input instanceof Request ? input.method : "GET");
3119
- const reqHeaders = redactHeaders(headersToRecord(init2?.headers));
3287
+ const reqHeaders = redactHeaders(rawHeaders);
3120
3288
  const loggedUrl = redactUrl(url);
3121
3289
  let reqBody;
3122
- if (init2?.body) {
3290
+ if (inference) reqBody = "[BODY OMITTED]";
3291
+ else if (init2?.body) {
3123
3292
  try {
3124
3293
  reqBody = redactDeep(JSON.parse(String(init2.body)));
3125
3294
  } catch {
@@ -3134,7 +3303,7 @@ function installDebugLogger(logPath) {
3134
3303
  try {
3135
3304
  response = await originalFetch(input, init2);
3136
3305
  } catch (err) {
3137
- error = err instanceof Error ? err.message : String(err);
3306
+ error = inference ? "Runtime request failed" : err instanceof Error ? err.message : String(err);
3138
3307
  const entry2 = JSON.stringify({
3139
3308
  timestamp: ts2,
3140
3309
  durationMs: Date.now() - startMs,
@@ -3150,16 +3319,20 @@ function installDebugLogger(logPath) {
3150
3319
  response.headers.forEach((v, k) => {
3151
3320
  resHeaders[k] = v;
3152
3321
  });
3153
- const clone = response.clone();
3154
- try {
3155
- const text = await clone.text();
3322
+ if (inference || response.headers.get("content-type")?.includes("text/event-stream")) {
3323
+ resBody = "[BODY OMITTED]";
3324
+ } else {
3325
+ const clone = response.clone();
3156
3326
  try {
3157
- resBody = redactDeep(JSON.parse(text));
3327
+ const text = await clone.text();
3328
+ try {
3329
+ resBody = redactDeep(JSON.parse(text));
3330
+ } catch {
3331
+ resBody = text;
3332
+ }
3158
3333
  } catch {
3159
- resBody = text;
3334
+ resBody = "<unreadable>";
3160
3335
  }
3161
- } catch {
3162
- resBody = "<unreadable>";
3163
3336
  }
3164
3337
  const entry = JSON.stringify({
3165
3338
  timestamp: ts2,
@@ -3184,9 +3357,9 @@ function installDebugLogger(logPath) {
3184
3357
  }
3185
3358
 
3186
3359
  // src/cli/commands/aigateway/shared.ts
3187
- import { open, readFile, unlink } from "fs/promises";
3360
+ import { open, readFile as readFile2, unlink } from "fs/promises";
3188
3361
  import { AIGatewayClient as AIGatewayClient2 } from "@cdot65/prisma-airs-sdk";
3189
- import { dump as dump3, load } from "js-yaml";
3362
+ import { dump as dump4, load } from "js-yaml";
3190
3363
  async function defaultClientFactory() {
3191
3364
  const config = await loadConfig();
3192
3365
  return new AIGatewayClient2(aiGatewayClientOptions(config));
@@ -3210,7 +3383,7 @@ function addWriteOutput(command) {
3210
3383
  async function readGatewayRequest(path3) {
3211
3384
  let parsed;
3212
3385
  try {
3213
- const text = await readFile(path3, "utf8");
3386
+ const text = await readFile2(path3, "utf8");
3214
3387
  parsed = path3.endsWith(".yaml") || path3.endsWith(".yml") ? load(text) : JSON.parse(text);
3215
3388
  } catch (error) {
3216
3389
  throw new CliUsageError(
@@ -3267,7 +3440,7 @@ function renderGatewayList(items, format, label) {
3267
3440
  return;
3268
3441
  }
3269
3442
  if (format === "yaml") {
3270
- console.log(dump3(items, { noRefs: true, lineWidth: -1 }).trimEnd());
3443
+ console.log(dump4(items, { noRefs: true, lineWidth: -1 }).trimEnd());
3271
3444
  return;
3272
3445
  }
3273
3446
  if (items.length === 0) {
@@ -3285,7 +3458,7 @@ function renderGatewayDetail(value, format) {
3285
3458
  return;
3286
3459
  }
3287
3460
  if (format === "yaml") {
3288
- console.log(dump3(item, { noRefs: true, lineWidth: -1 }).trimEnd());
3461
+ console.log(dump4(item, { noRefs: true, lineWidth: -1 }).trimEnd());
3289
3462
  return;
3290
3463
  }
3291
3464
  const rows = Object.entries(item).map(([key, raw]) => ({
@@ -3401,7 +3574,7 @@ async function runSecretWrite(command, opts, prepare, write, prompt, settings =
3401
3574
  import {
3402
3575
  AISecSDKException,
3403
3576
  buildDottedObject,
3404
- ErrorType,
3577
+ ErrorType as ErrorType2,
3405
3578
  GatewayJsonObjectSchema,
3406
3579
  setDottedValue
3407
3580
  } from "@cdot65/prisma-airs-sdk";
@@ -3522,7 +3695,7 @@ function optionName(option) {
3522
3695
  }
3523
3696
  function asUsageError(error, prefix = "Invalid AI Gateway request") {
3524
3697
  if (error instanceof CliUsageError) return error;
3525
- if (error instanceof AISecSDKException && error.errorType === ErrorType.USER_REQUEST_PAYLOAD_ERROR) {
3698
+ if (error instanceof AISecSDKException && error.errorType === ErrorType2.USER_REQUEST_PAYLOAD_ERROR) {
3526
3699
  return new CliUsageError(`${prefix}: ${error.message}`);
3527
3700
  }
3528
3701
  return new CliUsageError(`${prefix}: ${schemaMessage(error)}`);
@@ -4535,6 +4708,7 @@ function scopeNameLooksUnrelated(name, scopeName) {
4535
4708
  function registerAiGatewayCommand(program) {
4536
4709
  const aigateway = program.command("aigateway").description("Manage and observe Prisma AIRS AI Gateway resources").action(() => aigateway.outputHelp());
4537
4710
  registerAiGatewayInventory(aigateway);
4711
+ registerAiGatewayInference(aigateway);
4538
4712
  const workspace = aigateway.command("workspaces").alias("workspace").description("Manage gateway workspaces").action(() => workspace.outputHelp());
4539
4713
  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(
4540
4714
  "after",
@@ -4813,7 +4987,7 @@ function registerCompletionCommand(program) {
4813
4987
  }
4814
4988
 
4815
4989
  // src/cli/commands/config.ts
4816
- import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
4990
+ import { mkdir, readFile as readFile3, writeFile } from "fs/promises";
4817
4991
  import { dirname as dirname2 } from "path";
4818
4992
  var CONFIG_KEYS = Object.keys(ConfigSchema.shape);
4819
4993
  var SECRET_PATTERN = /key|secret|token|password/i;
@@ -4837,7 +5011,7 @@ function buildConfigRows(inspected, reveal) {
4837
5011
  async function readConfigFileStrict(filePath) {
4838
5012
  let raw;
4839
5013
  try {
4840
- raw = await readFile2(filePath, "utf-8");
5014
+ raw = await readFile3(filePath, "utf-8");
4841
5015
  } catch {
4842
5016
  return { ok: true, data: {} };
4843
5017
  }
@@ -4973,7 +5147,7 @@ function registerConfigCommand(program) {
4973
5147
 
4974
5148
  // src/cli/commands/doctor.ts
4975
5149
  import { randomUUID } from "crypto";
4976
- import { readFile as readFile3 } from "fs/promises";
5150
+ import { readFile as readFile4 } from "fs/promises";
4977
5151
  import { init, Scanner } from "@cdot65/prisma-airs-sdk";
4978
5152
  var DOCTOR_TIMEOUT_MS = 5e3;
4979
5153
  var MIN_NODE_MAJOR = 20;
@@ -4993,7 +5167,7 @@ async function checkConfigFile(filePath) {
4993
5167
  const name = "Config file";
4994
5168
  let raw;
4995
5169
  try {
4996
- raw = await readFile3(filePath, "utf-8");
5170
+ raw = await readFile4(filePath, "utf-8");
4997
5171
  } catch {
4998
5172
  return {
4999
5173
  name,
@@ -7155,7 +7329,7 @@ function registerRedteamCommand(program) {
7155
7329
  // src/cli/commands/runtime.ts
7156
7330
  import { randomUUID as randomUUID4 } from "crypto";
7157
7331
  import * as fs5 from "fs";
7158
- import { readFile as readFile9 } from "fs/promises";
7332
+ import { readFile as readFile10 } from "fs/promises";
7159
7333
  import { basename as basename3, dirname as dirname3, join as join3, resolve as resolvePath } from "path";
7160
7334
  import chalk11 from "chalk";
7161
7335
 
@@ -7834,7 +8008,7 @@ var topicsView = {
7834
8008
  };
7835
8009
 
7836
8010
  // src/cli/commands/dlp/dictionaries.ts
7837
- import { readFile as readFile7 } from "fs/promises";
8011
+ import { readFile as readFile8 } from "fs/promises";
7838
8012
  import { basename as basename2 } from "path";
7839
8013
 
7840
8014
  // src/airs/dlp/dictionaries.ts
@@ -7873,7 +8047,7 @@ var SdkDictionariesService = class {
7873
8047
  };
7874
8048
 
7875
8049
  // src/cli/commands/dlp/patch.ts
7876
- import { readFile as readFile6 } from "fs/promises";
8050
+ import { readFile as readFile7 } from "fs/promises";
7877
8051
  function buildMergePatch(opts) {
7878
8052
  const out = {};
7879
8053
  for (const entry of opts.set ?? []) {
@@ -7915,7 +8089,7 @@ function coerceValue(raw) {
7915
8089
  async function parseBody(opts) {
7916
8090
  let raw;
7917
8091
  if (opts.bodyFile) {
7918
- raw = await readFile6(opts.bodyFile, "utf-8");
8092
+ raw = await readFile7(opts.bodyFile, "utf-8");
7919
8093
  } else if (opts.body === "-") {
7920
8094
  const chunks = [];
7921
8095
  for await (const chunk of opts.stdin ?? process.stdin) {
@@ -7936,7 +8110,7 @@ async function parseBody(opts) {
7936
8110
  // src/cli/commands/dlp/dictionaries.ts
7937
8111
  async function buildMetadata(opts) {
7938
8112
  if (opts.metadataFile) {
7939
- return JSON.parse(await readFile7(opts.metadataFile, "utf-8"));
8113
+ return JSON.parse(await readFile8(opts.metadataFile, "utf-8"));
7940
8114
  }
7941
8115
  if (!opts.name || !opts.category || !opts.region || !opts.file) {
7942
8116
  throw new Error("--name, --category, --region, and --file are required");
@@ -7981,7 +8155,7 @@ function register(dlp) {
7981
8155
  try {
7982
8156
  const metadata = await buildMetadata(opts);
7983
8157
  if (!opts.file) throw new Error("--file is required (multipart upload)");
7984
- const file = await readFile7(opts.file);
8158
+ const file = await readFile8(opts.file);
7985
8159
  const r = await new SdkDictionariesService().create({
7986
8160
  metadata,
7987
8161
  file,
@@ -8009,7 +8183,7 @@ function register(dlp) {
8009
8183
  try {
8010
8184
  const metadata = await buildMetadata(opts);
8011
8185
  if (!opts.file) throw new Error("--file is required (multipart upload)");
8012
- const file = await readFile7(opts.file);
8186
+ const file = await readFile8(opts.file);
8013
8187
  const r = await new SdkDictionariesService().replace(id, {
8014
8188
  metadata,
8015
8189
  file,
@@ -8492,7 +8666,10 @@ function writeFlags2(cmd) {
8492
8666
  }
8493
8667
  async function resolveWriteBody2(opts) {
8494
8668
  if (opts.body || opts.bodyFile) {
8495
- const body = await parseBody({ body: opts.body, bodyFile: opts.bodyFile });
8669
+ const body = await parseBody({
8670
+ body: opts.body,
8671
+ bodyFile: opts.bodyFile
8672
+ });
8496
8673
  if (!body) throw new Error("--body or --body-file was empty");
8497
8674
  return body;
8498
8675
  }
@@ -8500,7 +8677,7 @@ async function resolveWriteBody2(opts) {
8500
8677
  }
8501
8678
  function register5(dlp) {
8502
8679
  const group = dlp.command("profiles").description(
8503
- 'DLP data profiles. DELETE is not exposed by the DLP API. To remove a profile, patch with profile_status: "deleted".'
8680
+ "DLP data profiles. No supported DELETE; status-based retirement is not live-verified."
8504
8681
  );
8505
8682
  const listCmd = listFlags3(group.command("list").description("List data profiles"));
8506
8683
  listCmd.action(async (opts) => {
@@ -8569,14 +8746,13 @@ function register5(dlp) {
8569
8746
  usageError(err instanceof Error ? err.message : String(err));
8570
8747
  }
8571
8748
  });
8572
- group.command("delete <id>").description("Not supported \u2014 prints the patch idiom and exits 2").action((id) => {
8749
+ group.command("delete <id>").description("Not supported \u2014 explains the cleanup limitation and exits 2").action((id) => {
8573
8750
  usageError(
8574
8751
  `This DLP API has no DELETE for data profiles.
8575
- To soft-delete, fetch the profile to get its name + profile_type, then patch:
8576
-
8577
- airs runtime dlp profiles get ${id} --output json
8578
- airs runtime dlp profiles patch ${id} --set profile_status='"deleted"' \\
8579
- --set name='"<existing-name>"' --set profile_type='"<existing-type>"'`
8752
+ Status-based retirement is not live-verified: the latest owned-fixture
8753
+ PATCH/PUT returned HTTP 500; an advertised DELETE returned HTTP 501.
8754
+ No API request was sent by this command. Do not assume the profile was removed.
8755
+ Inspect current state with: airs runtime dlp profiles get ${id} --output json`
8580
8756
  );
8581
8757
  });
8582
8758
  }
@@ -8829,7 +9005,7 @@ function registerCreateCommand(parent) {
8829
9005
  }
8830
9006
 
8831
9007
  // src/cli/commands/topics-eval.ts
8832
- import { readFile as readFile8 } from "fs/promises";
9008
+ import { readFile as readFile9 } from "fs/promises";
8833
9009
 
8834
9010
  // src/core/prompt-loader.ts
8835
9011
  function parseCsvLine(line) {
@@ -8972,7 +9148,7 @@ function registerEvalCommand(parent) {
8972
9148
  resolveDeprecatedAliases(cmd, opts);
8973
9149
  try {
8974
9150
  const config = await loadConfig();
8975
- const csvContent = await readFile8(opts.prompts, "utf-8");
9151
+ const csvContent = await readFile9(opts.prompts, "utf-8");
8976
9152
  const { cases, intent } = loadPrompts(csvContent, (msg) => ui.status(`Warning: ${msg}`));
8977
9153
  if (!config.airsApiKey && !config.airsApiToken) {
8978
9154
  fail(new Error("PANW_AI_SEC_API_KEY or PANW_AI_SEC_API_TOKEN is required"));
@@ -9274,7 +9450,7 @@ function registerRuntimeCommand(program) {
9274
9450
  if (!config.airsApiKey && !config.airsApiToken) {
9275
9451
  fail(new Error("PANW_AI_SEC_API_KEY or PANW_AI_SEC_API_TOKEN is required"));
9276
9452
  }
9277
- const raw = await readFile9(opts.file, "utf-8");
9453
+ const raw = await readFile10(opts.file, "utf-8");
9278
9454
  const prompts = parseInputFile(raw, opts.file);
9279
9455
  if (prompts.length === 0) {
9280
9456
  usageError("No prompts found in input file");
package/dist/index.d.ts CHANGED
@@ -1657,6 +1657,10 @@ declare const ConfigSchema: z.ZodObject<{
1657
1657
  aiGwDataEndpoint: z.ZodOptional<z.ZodString>;
1658
1658
  aiGwAdminEndpoint: z.ZodOptional<z.ZodString>;
1659
1659
  aiGwTokenEndpoint: z.ZodOptional<z.ZodString>;
1660
+ aiGwInferenceEndpoint: z.ZodOptional<z.ZodString>;
1661
+ aiGwInferenceApiKey: z.ZodOptional<z.ZodString>;
1662
+ aiGwInferenceModel: z.ZodOptional<z.ZodString>;
1663
+ aiGwEmbeddingModel: z.ZodOptional<z.ZodString>;
1660
1664
  scanConcurrency: z.ZodDefault<z.ZodNumber>;
1661
1665
  defaultOutput: z.ZodOptional<z.ZodEnum<["pretty", "table", "markdown", "csv", "json", "yaml"]>>;
1662
1666
  dataDir: z.ZodDefault<z.ZodString>;
@@ -1683,6 +1687,10 @@ declare const ConfigSchema: z.ZodObject<{
1683
1687
  aiGwDataEndpoint?: string | undefined;
1684
1688
  aiGwAdminEndpoint?: string | undefined;
1685
1689
  aiGwTokenEndpoint?: string | undefined;
1690
+ aiGwInferenceEndpoint?: string | undefined;
1691
+ aiGwInferenceApiKey?: string | undefined;
1692
+ aiGwInferenceModel?: string | undefined;
1693
+ aiGwEmbeddingModel?: string | undefined;
1686
1694
  defaultOutput?: "json" | "yaml" | "pretty" | "table" | "markdown" | "csv" | undefined;
1687
1695
  }, {
1688
1696
  airsApiKey?: string | undefined;
@@ -1705,6 +1713,10 @@ declare const ConfigSchema: z.ZodObject<{
1705
1713
  aiGwDataEndpoint?: string | undefined;
1706
1714
  aiGwAdminEndpoint?: string | undefined;
1707
1715
  aiGwTokenEndpoint?: string | undefined;
1716
+ aiGwInferenceEndpoint?: string | undefined;
1717
+ aiGwInferenceApiKey?: string | undefined;
1718
+ aiGwInferenceModel?: string | undefined;
1719
+ aiGwEmbeddingModel?: string | undefined;
1708
1720
  scanConcurrency?: number | undefined;
1709
1721
  defaultOutput?: "json" | "yaml" | "pretty" | "table" | "markdown" | "csv" | undefined;
1710
1722
  dataDir?: string | undefined;
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  validateName,
19
19
  validateTopic,
20
20
  writeBackupFile
21
- } from "./chunk-W5YDJS7H.js";
21
+ } from "./chunk-LF6JPUA4.js";
22
22
  export {
23
23
  AirsScanService,
24
24
  SDK_ASYNC_BATCH_SIZE,
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.1.2",
4
+ "version": "4.2.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.20.0",
60
+ "@cdot65/prisma-airs-sdk": "0.21.0",
61
61
  "@inquirer/prompts": "^8.3.0",
62
62
  "chalk": "^5.6.2",
63
63
  "commander": "^14.0.3",