@cdot65/prisma-airs-cli 3.2.0 → 4.0.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/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
11
11
  [![Node 20+](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](https://nodejs.org/)
12
12
 
13
- **Full operational coverage over Palo Alto Prisma AIRS AI security — guardrail refinement, runtime scanning, AI red teaming, and model security.**
13
+ **Full operational coverage over Palo Alto Prisma AIRS AI security — guardrail refinement, runtime scanning, AI red teaming, AI Gateway, and model security.**
14
14
 
15
15
  > **[Read the full documentation](https://cdot65.github.io/prisma-airs-cli/)** — installation, configuration, architecture, CLI reference, and examples.
16
16
 
@@ -19,7 +19,10 @@
19
19
  - **Runtime Scanning** — scan prompts and responses against AIRS security profiles, single or bulk with CSV export
20
20
  - **Guardrail Optimization** — atomic CLI commands (`create`, `apply`, `eval`, `revert`) for custom topic guardrails, designed for autonomous agent loops (see [`AGENTS.md`](AGENTS.md))
21
21
  - **AI Red Teaming** — adversarial scanning with static, dynamic, and custom prompt set attack modes
22
+ - **[AI Gateway](https://cdot65.github.io/prisma-airs-cli/cli/aigateway/workspaces/)** — scoped/admin workspace management and workspace cost telemetry
22
23
  - **Model Security** — ML model supply chain scanning with security groups, rules, and violation tracking
24
+ - **Unified automation output** — every read command supports `pretty`, `table`, `markdown`, `csv`, `json`, and `yaml`, with pipe-safe stdout
25
+ - **Complete pagination** — consistent `--limit`, `--offset`, and `--all` traversal with a configurable safety cap
23
26
  - **`airs doctor`** — one-command diagnostics for environment, credentials, and API connectivity
24
27
  - **`airs config`** — manage `~/.prisma-airs/config.json` from the CLI (`list`, `get`, `set`, `unset`, `path`)
25
28
 
@@ -55,11 +58,40 @@ airs runtime topics revert --profile my-profile --name "Explosives"
55
58
  airs redteam scan --target <uuid> --name "Full Scan" --type STATIC
56
59
  airs redteam report <job-id>
57
60
 
61
+ # Red team custom target adapters
62
+ airs redteam adapter list --output json
63
+
64
+ # AI Gateway workspaces and cost telemetry
65
+ airs aigateway workspace list --all --output json
66
+ airs aigateway telemetry cost --workspace <workspace-slug> --days 30
67
+
58
68
  # Model security
59
69
  airs model-security scans create --config scan-config.json
70
+
71
+ # Pipe-safe read output and complete traversal
72
+ airs runtime profiles list --all --output json | jq '.[].profileName'
73
+ airs runtime topics list --all-versions --output markdown
60
74
  ```
61
75
 
62
- Bulk scans preserve one output row per input prompt in input order, including all eight runtime detector flags. Work is processed as sequential logical batches (`--batch-size 25` by default), with SDK requests capped at 20 prompts. Item-level state makes accepted and pending work resumable without duplicating CSV rows, and active jobs are locked against overlapping resumes. Runtime actions are exactly `allow`, `block`, or `failed`; failed or timed-out prompts make the command exit 1. Bulk scanning requires `@cdot65/prisma-airs-sdk` 0.13.2 or later.
76
+ Bulk scans preserve one output row per input prompt in input order, including all eight runtime detector flags. Work is processed as sequential logical batches (`--batch-size 25` by default), with SDK requests capped at 20 prompts. Item-level state makes accepted and pending work resumable without duplicating CSV rows, and active jobs are locked against overlapping resumes. Runtime actions are exactly `allow`, `block`, or `failed`; failed or timed-out prompts make the command exit 1. Version 4 uses `@cdot65/prisma-airs-sdk` 0.18.0 or later.
77
+
78
+ ## Read Output and Pagination
79
+
80
+ Read commands share one contract:
81
+
82
+ - Formats: `pretty`, `table`, `markdown`, `csv`, `json`, and `yaml`.
83
+ - JSON/YAML lists are bare arrays of complete normalized records; detail reads are complete objects.
84
+ - Table, Markdown, and CSV are stable human-oriented projections. CSV uses RFC 4180 quoting.
85
+ - Data is written to stdout; status, paging hints, warnings, and errors are written to stderr.
86
+ - Output precedence is command `--output`, global `--output`, `defaultOutput`/`PANW_CLI_OUTPUT`, then `pretty`.
87
+ - Paginated lists use `--limit`, `--offset`, and `--all`. Complete traversal is capped at 10,000 records by default; change it with `--max`, or use `--max 0` for no cap.
88
+ - Profile and topic lists return only the latest revision by default. Use `--all-versions` or `--revision` when historical revisions are needed.
89
+
90
+ ```bash
91
+ airs --output json runtime profiles list --all | jq '.[].profileName'
92
+ PANW_CLI_OUTPUT=yaml airs runtime topics get "My Topic"
93
+ airs model-security scans list --all --max 25000 --output csv > scans.csv
94
+ ```
63
95
 
64
96
  ## Documentation
65
97
 
@@ -69,12 +101,13 @@ The full guides, complete CLI reference, configuration, and architecture live on
69
101
  - **[Runtime Security](https://cdot65.github.io/prisma-airs-cli/runtime/overview/)** — scanning, profiles, topics, and DLP management
70
102
  - **[Guardrail Optimization](https://cdot65.github.io/prisma-airs-cli/runtime/guardrails/overview/)** — the agent-driven `topics create/apply/eval/revert` loop
71
103
  - **[AI Red Teaming](https://cdot65.github.io/prisma-airs-cli/redteam/overview/)** — static, dynamic, and custom adversarial scans
104
+ - **[AI Gateway](https://cdot65.github.io/prisma-airs-cli/cli/aigateway/workspaces/)** — workspace CRUD, two-plane authorization, and cost telemetry
72
105
  - **[Model Security](https://cdot65.github.io/prisma-airs-cli/model-security/overview/)** — ML model supply-chain scanning
73
106
  - **[CLI Reference](https://cdot65.github.io/prisma-airs-cli/cli/)** — every command, flag, and example
74
107
 
75
108
  ## Configuration
76
109
 
77
- Credentials come from environment variables or `~/.prisma-airs/config.json`. At minimum: `PANW_AI_SEC_API_KEY` (scanning) and `PANW_MGMT_CLIENT_ID` / `PANW_MGMT_CLIENT_SECRET` / `PANW_MGMT_TSG_ID` (management). See [`.env.example`](.env.example) and the [configuration guide](https://cdot65.github.io/prisma-airs-cli/getting-started/configuration/) for the full list.
110
+ Credentials come from environment variables or `~/.prisma-airs/config.json`. At minimum: `PANW_AI_SEC_API_KEY` (scanning) and `PANW_MGMT_CLIENT_ID` / `PANW_MGMT_CLIENT_SECRET` / `PANW_MGMT_TSG_ID` (management). Set `defaultOutput` in the config file or `PANW_CLI_OUTPUT` in the environment to choose a default read format. See [`.env.example`](.env.example) and the [configuration guide](https://cdot65.github.io/prisma-airs-cli/getting-started/configuration/) for the full list.
78
111
 
79
112
  ## License
80
113
 
@@ -21,7 +21,14 @@ var SdkManagementService = class {
21
21
  return { message: response.message };
22
22
  }
23
23
  async listTopics() {
24
- const response = await this.client.topics.list();
24
+ return this.client.topics.listAll();
25
+ }
26
+ async listLatestTopics(opts) {
27
+ const response = await this.client.topics.list({
28
+ latestOnly: true,
29
+ limit: opts?.limit,
30
+ offset: opts?.offset
31
+ });
25
32
  return response.custom_topics;
26
33
  }
27
34
  async getTopic(topicId) {
@@ -32,9 +39,9 @@ var SdkManagementService = class {
32
39
  }
33
40
  async getTopicByName(topicName) {
34
41
  const topics = await this.listTopics();
35
- const topic = topics.find((t) => t.topic_name === topicName);
36
- if (!topic) throw new Error(`Topic "${topicName}" not found`);
37
- return topic;
42
+ const matches = topics.filter((topic) => topic.topic_name === topicName);
43
+ if (matches.length === 0) throw new Error(`Topic "${topicName}" not found`);
44
+ return matches.reduce((latest, topic) => topic.revision > latest.revision ? topic : latest);
38
45
  }
39
46
  /**
40
47
  * Sets a single custom topic on a profile's topic-guardrails config.
@@ -53,7 +60,7 @@ var SdkManagementService = class {
53
60
  * it defaults to revision 0 (original content), not the latest.
54
61
  */
55
62
  async assignTopicsToProfile(profileName, topics, guardrailAction) {
56
- const { ai_profiles } = await this.client.profiles.list();
63
+ const ai_profiles = await this.client.profiles.listAll();
57
64
  const profile = ai_profiles.find((p) => p.profile_name === profileName);
58
65
  if (!profile?.profile_id) {
59
66
  throw new Error(`Profile "${profileName}" not found`);
@@ -102,7 +109,7 @@ var SdkManagementService = class {
102
109
  });
103
110
  }
104
111
  async getProfileTopics(profileName) {
105
- const { ai_profiles } = await this.client.profiles.list();
112
+ const ai_profiles = await this.client.profiles.listAll();
106
113
  const profile = ai_profiles.find((p) => p.profile_name === profileName);
107
114
  if (!profile?.profile_id) {
108
115
  throw new Error(`Profile "${profileName}" not found`);
@@ -170,6 +177,12 @@ var SdkManagementService = class {
170
177
  nextOffset: response.next_offset
171
178
  };
172
179
  }
180
+ async listAllProfiles(opts) {
181
+ const profiles = await this.client.profiles.listAll(opts);
182
+ return profiles.map(
183
+ (profile) => this.normalizeProfile(profile)
184
+ );
185
+ }
173
186
  async createProfile(request) {
174
187
  const response = await this.client.profiles.create(request);
175
188
  return this.normalizeProfile(response);
@@ -208,6 +221,11 @@ var SdkManagementService = class {
208
221
  nextOffset: raw.next_offset
209
222
  };
210
223
  }
224
+ async listAllApiKeys(opts) {
225
+ return (await this.client.apiKeys.listAll(opts)).map(
226
+ (key) => this.normalizeApiKey(key)
227
+ );
228
+ }
211
229
  async createApiKey(request) {
212
230
  const response = await this.client.apiKeys.create(request);
213
231
  return this.normalizeApiKey(response);
@@ -240,6 +258,11 @@ var SdkManagementService = class {
240
258
  nextOffset: raw.next_offset
241
259
  };
242
260
  }
261
+ async listAllCustomerApps(opts) {
262
+ return (await this.client.customerApps.listAll(opts)).map(
263
+ (app) => this.normalizeCustomerApp(app)
264
+ );
265
+ }
243
266
  async getCustomerApp(appName) {
244
267
  const response = await this.client.customerApps.get(appName);
245
268
  return this.normalizeCustomerApp(response);
@@ -253,11 +276,18 @@ var SdkManagementService = class {
253
276
  return this.normalizeCustomerApp(response);
254
277
  }
255
278
  async listConsumptionApps(opts) {
256
- const response = await this.client.dashboard.applicationsOverview({
257
- limit: opts?.limit ?? 100,
258
- offset: opts?.offset ?? 0
259
- });
260
- return (response.items ?? []).filter(
279
+ const limit = opts?.limit ?? 100;
280
+ let offset = opts?.offset ?? 0;
281
+ const items = [];
282
+ for (; ; ) {
283
+ const response = await this.client.dashboard.applicationsOverview({ limit, offset });
284
+ const page = response.items ?? [];
285
+ items.push(...page);
286
+ const total = response.pagination?.total_items;
287
+ offset += page.length;
288
+ if (page.length === 0 || page.length < limit || total != null && offset >= total) break;
289
+ }
290
+ return items.filter(
261
291
  (item) => typeof item.id === "string" && typeof item.name === "string"
262
292
  ).map((item) => ({
263
293
  appId: item.id,
@@ -525,6 +555,18 @@ var SdkModelSecurityService = class {
525
555
  groups: raw.security_groups.map(normalizeGroup)
526
556
  };
527
557
  }
558
+ async listAllGroups(opts = {}) {
559
+ const rows = await this.client.securityGroups.listAll({
560
+ source_types: opts.sourceTypes,
561
+ search_query: opts.searchQuery,
562
+ sort_field: opts.sortField,
563
+ sort_dir: opts.sortDir,
564
+ enabled_rules: opts.enabledRules,
565
+ limit: opts.limit,
566
+ max: opts.max
567
+ });
568
+ return rows.map((row) => normalizeGroup(row));
569
+ }
528
570
  async getGroup(uuid) {
529
571
  const response = await this.client.securityGroups.get(uuid);
530
572
  return normalizeGroup(response);
@@ -615,6 +657,15 @@ var SdkModelSecurityService = class {
615
657
  rules: raw.rules.map(normalizeRule)
616
658
  };
617
659
  }
660
+ async listAllRules(opts = {}) {
661
+ const rows = await this.client.securityRules.listAll({
662
+ source_type: opts.sourceType,
663
+ search_query: opts.searchQuery,
664
+ limit: opts.limit,
665
+ max: opts.max
666
+ });
667
+ return rows.map((row) => normalizeRule(row));
668
+ }
618
669
  async getRule(uuid) {
619
670
  const response = await this.client.securityRules.get(uuid);
620
671
  return normalizeRule(response);
@@ -641,6 +692,16 @@ var SdkModelSecurityService = class {
641
692
  scans: raw.scans.map(normalizeScan)
642
693
  };
643
694
  }
695
+ async listAllScans(opts = {}) {
696
+ const rows = await this.client.scans.listAll({
697
+ eval_outcomes: opts.evalOutcome === void 0 ? void 0 : [opts.evalOutcome],
698
+ source_types: opts.sourceType === void 0 ? void 0 : [opts.sourceType],
699
+ search_query: opts.search,
700
+ limit: opts.limit,
701
+ max: opts.max
702
+ });
703
+ return rows.map((row) => normalizeScan(row));
704
+ }
644
705
  async getScan(uuid) {
645
706
  const response = await this.client.scans.get(uuid);
646
707
  return normalizeScan(response);
@@ -743,6 +804,17 @@ var SdkModelSecurityService = class {
743
804
  models: raw.models.map(normalizeModel)
744
805
  };
745
806
  }
807
+ async listAllModels(opts = {}) {
808
+ const rows = await this.client.models.listAllModels({
809
+ search: opts.search,
810
+ search_query: opts.searchQuery,
811
+ sort_field: opts.sortField,
812
+ sort_order: opts.sortOrder,
813
+ limit: opts.limit,
814
+ max: opts.max
815
+ });
816
+ return rows.map((row) => normalizeModel(row));
817
+ }
746
818
  async getModel(uuid) {
747
819
  const response = await this.client.models.getModel(uuid);
748
820
  return normalizeModel(response);
@@ -1012,6 +1084,52 @@ function normalizeTargetDetail(raw) {
1012
1084
  metadata: sanitizeTargetMetadata(raw.target_metadata)
1013
1085
  };
1014
1086
  }
1087
+ function normalizeAdapterListItem(raw) {
1088
+ return {
1089
+ uuid: raw.uuid,
1090
+ name: raw.name,
1091
+ status: raw.status,
1092
+ createdAt: raw.created_at,
1093
+ updatedAt: raw.updated_at,
1094
+ createdByUserId: raw.created_by_user_id,
1095
+ targetCount: raw.target_count
1096
+ };
1097
+ }
1098
+ function normalizeAdapterVar(raw) {
1099
+ return {
1100
+ key: raw.key,
1101
+ value: raw.value,
1102
+ type: raw.type,
1103
+ isRedacted: raw.is_redacted
1104
+ };
1105
+ }
1106
+ function normalizeAdapterDetail(raw) {
1107
+ return {
1108
+ uuid: raw.uuid,
1109
+ tsgId: raw.tsg_id,
1110
+ name: raw.name,
1111
+ scriptB64: raw.script_b64,
1112
+ status: raw.status,
1113
+ description: raw.description,
1114
+ networkBrokerChannelUuid: raw.network_broker_channel_uuid,
1115
+ variables: (raw.variables ?? []).map(normalizeAdapterVar),
1116
+ targetCount: raw.target_count,
1117
+ createdAt: raw.created_at,
1118
+ updatedAt: raw.updated_at,
1119
+ createdByUserId: raw.created_by_user_id,
1120
+ updatedByUserId: raw.updated_by_user_id
1121
+ };
1122
+ }
1123
+ function preserveVariablesForUpdate(variables) {
1124
+ return variables.map((v) => ({
1125
+ key: v.key,
1126
+ value: v.isRedacted ? null : v.value ?? null,
1127
+ type: v.type
1128
+ }));
1129
+ }
1130
+ function toWireVariables(variables) {
1131
+ return variables.map((v) => ({ key: v.key, value: v.value, type: v.type }));
1132
+ }
1015
1133
  var SdkRedTeamService = class {
1016
1134
  client;
1017
1135
  constructor(opts) {
@@ -1122,29 +1240,14 @@ var SdkRedTeamService = class {
1122
1240
  return await this.client.targets.getTargetTemplates();
1123
1241
  }
1124
1242
  async listTargets() {
1125
- const all = [];
1126
- let skip = 0;
1127
- const limit = 100;
1128
- for (; ; ) {
1129
- const response = this.client.targets.list({ skip, limit });
1130
- const body = await response;
1131
- const data = body.data;
1132
- for (const t of data) {
1133
- all.push({
1134
- uuid: t.uuid,
1135
- name: t.name,
1136
- status: t.status,
1137
- targetType: t.target_type,
1138
- active: t.active
1139
- });
1140
- }
1141
- const pagination = body.pagination;
1142
- if (!pagination || all.length >= (pagination.total ?? data.length) || data.length < limit) {
1143
- break;
1144
- }
1145
- skip += limit;
1146
- }
1147
- return all;
1243
+ const targets = await this.client.targets.listAll({ limit: 100 });
1244
+ return targets.map((target) => ({
1245
+ uuid: target.uuid,
1246
+ name: target.name,
1247
+ status: String(target.status ?? ""),
1248
+ targetType: target.target_type == null ? void 0 : String(target.target_type),
1249
+ active: target.active
1250
+ }));
1148
1251
  }
1149
1252
  async getTarget(uuid) {
1150
1253
  const response = await this.client.targets.get(uuid);
@@ -1180,7 +1283,12 @@ var SdkRedTeamService = class {
1180
1283
  }
1181
1284
  async createScan(request) {
1182
1285
  let jobMetadata = {};
1183
- if (request.jobType === "STATIC" && request.categories) {
1286
+ if (request.jobType === "STATIC") {
1287
+ if (!request.categories) {
1288
+ throw new Error(
1289
+ "STATIC scans require categories. Pass categories explicitly or use the CLI default."
1290
+ );
1291
+ }
1184
1292
  jobMetadata = { categories: request.categories };
1185
1293
  } else if (request.jobType === "CUSTOM" && request.customPromptSets) {
1186
1294
  jobMetadata = {
@@ -1211,11 +1319,22 @@ var SdkRedTeamService = class {
1211
1319
  if (opts?.jobType) sdkOpts.job_type = opts.jobType;
1212
1320
  if (opts?.targetId) sdkOpts.target_id = opts.targetId;
1213
1321
  if (opts?.limit) sdkOpts.limit = opts.limit;
1322
+ if (opts?.offset !== void 0) sdkOpts.skip = opts.offset;
1214
1323
  const response = await this.client.scans.list(sdkOpts);
1215
1324
  return response.data.map(
1216
1325
  normalizeJob
1217
1326
  );
1218
1327
  }
1328
+ async listAllScans(opts) {
1329
+ const sdkOpts = {};
1330
+ if (opts?.status) sdkOpts.status = opts.status;
1331
+ if (opts?.jobType) sdkOpts.job_type = opts.jobType;
1332
+ if (opts?.targetId) sdkOpts.target_id = opts.targetId;
1333
+ if (opts?.limit) sdkOpts.limit = opts.limit;
1334
+ if (opts?.max !== void 0) sdkOpts.max = opts.max;
1335
+ const rows = await this.client.scans.listAll(sdkOpts);
1336
+ return rows.map((row) => normalizeJob(row));
1337
+ }
1219
1338
  async abortScan(jobId) {
1220
1339
  await this.client.scans.abort(jobId);
1221
1340
  }
@@ -1351,6 +1470,18 @@ var SdkRedTeamService = class {
1351
1470
  totalItems: pagination?.total_items
1352
1471
  };
1353
1472
  }
1473
+ async listAllChannels(opts = {}) {
1474
+ const limit = opts.limit ?? 100;
1475
+ const cap = opts.max === 0 ? Number.POSITIVE_INFINITY : opts.max ?? 1e4;
1476
+ const rows = [];
1477
+ for (let offset = 0; rows.length < cap; offset += limit) {
1478
+ const page = await this.listChannels({ ...opts, limit, offset });
1479
+ rows.push(...page.channels.slice(0, cap - rows.length));
1480
+ if (page.channels.length < limit || rows.length >= (page.totalItems ?? Number.POSITIVE_INFINITY))
1481
+ break;
1482
+ }
1483
+ return rows;
1484
+ }
1354
1485
  async getChannel(channelId) {
1355
1486
  const raw = await this.client.networkBroker.getChannel(channelId);
1356
1487
  return normalizeChannel(raw);
@@ -1408,6 +1539,95 @@ var SdkRedTeamService = class {
1408
1539
  totalItems: pagination?.total_items
1409
1540
  };
1410
1541
  }
1542
+ // -------------------------------------------------------------------------
1543
+ // Custom target adapters (SDK 0.16.0)
1544
+ // -------------------------------------------------------------------------
1545
+ async listAdapters(opts) {
1546
+ const sdkOpts = {};
1547
+ if (opts?.limit != null) sdkOpts.limit = opts.limit;
1548
+ if (opts?.offset != null) sdkOpts.skip = opts.offset;
1549
+ if (opts?.search) sdkOpts.search = opts.search;
1550
+ const raw = await this.client.adapters.list(sdkOpts);
1551
+ const pagination = raw.pagination;
1552
+ return {
1553
+ adapters: (raw.data ?? []).map(normalizeAdapterListItem),
1554
+ totalItems: pagination?.total_items
1555
+ };
1556
+ }
1557
+ async listAllAdapters(opts = {}) {
1558
+ const sdkOpts = {};
1559
+ if (opts.limit != null) sdkOpts.limit = opts.limit;
1560
+ if (opts.search) sdkOpts.search = opts.search;
1561
+ if (opts.max !== void 0) sdkOpts.max = opts.max;
1562
+ const rows = await this.client.adapters.listAll(sdkOpts);
1563
+ return rows.map((row) => normalizeAdapterListItem(row));
1564
+ }
1565
+ async getAdapter(uuid) {
1566
+ const raw = await this.client.adapters.get(uuid);
1567
+ return normalizeAdapterDetail(raw);
1568
+ }
1569
+ async createAdapter(request, validate) {
1570
+ const body = {
1571
+ name: request.name,
1572
+ script_b64: request.scriptB64,
1573
+ prompt: request.prompt
1574
+ };
1575
+ if (request.description !== void 0) body.description = request.description;
1576
+ if (request.networkBrokerChannelUuid !== void 0) {
1577
+ body.network_broker_channel_uuid = request.networkBrokerChannelUuid;
1578
+ }
1579
+ if (request.variables !== void 0) body.variables = toWireVariables(request.variables);
1580
+ const raw = await this.client.adapters.create(
1581
+ // biome-ignore lint/suspicious/noExplicitAny: body is assembled dynamically; SDK validates the shape
1582
+ body,
1583
+ validate === void 0 ? void 0 : { validate }
1584
+ );
1585
+ return normalizeAdapterDetail(raw);
1586
+ }
1587
+ async updateAdapter(uuid, overrides, validate) {
1588
+ const current = await this.getAdapter(uuid);
1589
+ const body = {
1590
+ name: overrides.name ?? current.name,
1591
+ script_b64: overrides.scriptB64 ?? current.scriptB64,
1592
+ prompt: overrides.prompt,
1593
+ variables: overrides.variables ? toWireVariables(overrides.variables) : preserveVariablesForUpdate(current.variables)
1594
+ };
1595
+ const description = overrides.description ?? current.description;
1596
+ if (description != null) body.description = description;
1597
+ const channel = overrides.networkBrokerChannelUuid ?? current.networkBrokerChannelUuid;
1598
+ if (channel != null) body.network_broker_channel_uuid = channel;
1599
+ const raw = await this.client.adapters.update(
1600
+ uuid,
1601
+ // biome-ignore lint/suspicious/noExplicitAny: body is assembled dynamically; SDK validates the shape
1602
+ body,
1603
+ validate === void 0 ? void 0 : { validate }
1604
+ );
1605
+ return normalizeAdapterDetail(raw);
1606
+ }
1607
+ async deleteAdapter(uuid) {
1608
+ await this.client.adapters.delete(uuid);
1609
+ }
1610
+ async validateAdapter(request) {
1611
+ let variables = request.variables ? toWireVariables(request.variables) : void 0;
1612
+ if (variables === void 0 && request.adapterUuid) {
1613
+ const adapter = await this.getAdapter(request.adapterUuid);
1614
+ variables = preserveVariablesForUpdate(adapter.variables);
1615
+ }
1616
+ const body = {
1617
+ script_b64: request.scriptB64,
1618
+ network_broker_channel_uuid: request.networkBrokerChannelUuid,
1619
+ prompt: request.prompt
1620
+ };
1621
+ if (variables !== void 0) body.variables = variables;
1622
+ if (request.adapterUuid !== void 0) body.adapter_uuid = request.adapterUuid;
1623
+ const raw = await this.client.adapters.validate(body);
1624
+ return {
1625
+ validated: raw.validated,
1626
+ stdout: raw.stdout,
1627
+ stderr: raw.stderr,
1628
+ traceback: raw.traceback
1629
+ };
1630
+ }
1411
1631
  };
1412
1632
 
1413
1633
  // src/airs/runtime.ts
@@ -2030,8 +2250,13 @@ var ConfigSchema = z.object({
2030
2250
  modelSecDataEndpoint: z.string().optional(),
2031
2251
  modelSecMgmtEndpoint: z.string().optional(),
2032
2252
  modelSecTokenEndpoint: z.string().optional(),
2253
+ // AI Gateway (endpoints only; creds shared with mgmt*)
2254
+ aiGwDataEndpoint: z.string().optional(),
2255
+ aiGwAdminEndpoint: z.string().optional(),
2256
+ aiGwTokenEndpoint: z.string().optional(),
2033
2257
  // Tuning
2034
2258
  scanConcurrency: z.coerce.number().int().min(1).max(20).default(5),
2259
+ defaultOutput: z.enum(["pretty", "table", "markdown", "csv", "json", "yaml"]).optional(),
2035
2260
  // Persistence
2036
2261
  dataDir: z.string().default("~/.prisma-airs/runs")
2037
2262
  });
@@ -2060,7 +2285,11 @@ function fromEnv() {
2060
2285
  modelSecDataEndpoint: env.PANW_MODEL_SEC_DATA_ENDPOINT,
2061
2286
  modelSecMgmtEndpoint: env.PANW_MODEL_SEC_MGMT_ENDPOINT,
2062
2287
  modelSecTokenEndpoint: env.PANW_MODEL_SEC_TOKEN_ENDPOINT,
2288
+ aiGwDataEndpoint: env.PANW_AI_GW_DATA_ENDPOINT,
2289
+ aiGwAdminEndpoint: env.PANW_AI_GW_ADMIN_ENDPOINT,
2290
+ aiGwTokenEndpoint: env.PANW_AI_GW_TOKEN_ENDPOINT,
2063
2291
  scanConcurrency: env.SCAN_CONCURRENCY,
2292
+ defaultOutput: env.PANW_CLI_OUTPUT,
2064
2293
  dataDir: env.DATA_DIR
2065
2294
  };
2066
2295
  }