@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.
- package/dist/{chunk-W5YDJS7H.js → chunk-LF6JPUA4.js} +9 -0
- package/dist/cli/index.js +220 -44
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1 -1
- package/package.json +2 -2
|
@@ -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-
|
|
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, "", "
|
|
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
|
-
|
|
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(
|
|
3287
|
+
const reqHeaders = redactHeaders(rawHeaders);
|
|
3120
3288
|
const loggedUrl = redactUrl(url);
|
|
3121
3289
|
let reqBody;
|
|
3122
|
-
if (
|
|
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
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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 ===
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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({
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
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
|
|
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
|
|
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
|
|
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
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.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.
|
|
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",
|