@cdot65/prisma-airs-cli 3.2.0 → 3.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 CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  sanitizeFilename,
21
21
  validateTopic,
22
22
  writeBackupFile
23
- } from "../chunk-TTBN7YHC.js";
23
+ } from "../chunk-2VIUZRPB.js";
24
24
 
25
25
  // src/cli/index.ts
26
26
  import "dotenv/config";
@@ -45,16 +45,207 @@ import { dirname as dirname4, join as join4 } from "path";
45
45
  import { fileURLToPath } from "url";
46
46
  import { Command } from "commander";
47
47
 
48
- // src/cli/examples.ts
49
- function examples(...lines) {
50
- return `
51
- Examples:
52
- ${lines.map((l) => ` $ ${l}`).join("\n")}
53
- `;
48
+ // src/airs/aigateway.ts
49
+ import {
50
+ AIGatewayClient
51
+ } from "@cdot65/prisma-airs-sdk";
52
+ function toLimitArray(value) {
53
+ if (Array.isArray(value)) return value;
54
+ if (value !== null && typeof value === "object") return [value];
55
+ return [];
56
+ }
57
+ function normalizeWorkspace(raw) {
58
+ return {
59
+ id: raw.id,
60
+ slug: raw.slug,
61
+ name: raw.name,
62
+ icon: raw.icon,
63
+ description: raw.description,
64
+ createdAt: raw.created_at,
65
+ lastUpdatedAt: raw.last_updated_at,
66
+ isDefault: Boolean(raw.is_default),
67
+ status: raw.status,
68
+ scopeName: raw.scope_name
69
+ };
70
+ }
71
+ function normalizeWorkspaceDetail(raw) {
72
+ return {
73
+ ...normalizeWorkspace(raw),
74
+ defaults: raw.defaults,
75
+ usageLimits: toLimitArray(raw.usage_limits),
76
+ rateLimits: toLimitArray(raw.rate_limits),
77
+ securitySettings: raw.security_settings,
78
+ dataPlaneSecuritySettings: raw.data_plane_security_settings,
79
+ settings: raw.settings
80
+ };
54
81
  }
82
+ function aiGatewayGrantHint(err) {
83
+ const status = err?.status ?? err?.statusCode;
84
+ if (status !== 403) return void 0;
85
+ const message = err instanceof Error ? err.message : String(err);
86
+ const grant = message.includes("AB03") ? "the service account is missing a workspace-scope grant (data plane, /ai_gw/v2)" : "the service account is missing a tenant-root admin grant (admin plane, /ai_gw/admin/v2)";
87
+ return `${grant}. SCM Access Management edits the existing role row by default \u2014 use "Add Role" so the account ends up with both role rows, not one row moved.`;
88
+ }
89
+ var SdkAiGatewayService = class {
90
+ client;
91
+ constructor(opts) {
92
+ this.client = new AIGatewayClient(opts);
93
+ }
94
+ async listWorkspaces(options) {
95
+ const response = await this.client.workspaces.list(options);
96
+ return response.data.map(normalizeWorkspace);
97
+ }
98
+ async listAllWorkspaces() {
99
+ const [active, archived] = await Promise.all([
100
+ this.client.workspaces.list({ plane: "admin" }),
101
+ this.client.workspaces.list({ plane: "admin", status: "archived" })
102
+ ]);
103
+ return [
104
+ ...active.data,
105
+ ...archived.data
106
+ ].map(normalizeWorkspace);
107
+ }
108
+ async getWorkspace(workspaceRef, options) {
109
+ try {
110
+ const raw = await this.client.workspaces.get(workspaceRef, options);
111
+ return normalizeWorkspaceDetail(raw);
112
+ } catch (err) {
113
+ const status = err.statusCode;
114
+ if (status !== 404) throw err;
115
+ const resolved = await this.resolveWorkspaceRef(workspaceRef, [
116
+ options?.plane ?? "data",
117
+ "admin"
118
+ ]);
119
+ if (resolved === workspaceRef) throw err;
120
+ const raw = await this.client.workspaces.get(resolved, options);
121
+ return normalizeWorkspaceDetail(raw);
122
+ }
123
+ }
124
+ async createWorkspace(request) {
125
+ const body = {
126
+ name: request.name,
127
+ scope_name: request.scopeName
128
+ };
129
+ if (request.description !== void 0) body.description = request.description;
130
+ if (request.icon !== void 0) body.icon = request.icon;
131
+ if (request.defaults !== void 0) body.defaults = request.defaults;
132
+ if (request.users !== void 0) body.users = request.users;
133
+ if (request.usageLimits !== void 0) body.usage_limits = request.usageLimits;
134
+ if (request.rateLimits !== void 0) body.rate_limits = request.rateLimits;
135
+ const created = await this.client.workspaces.create(body);
136
+ return this.refetchAfterWrite(created.id, created);
137
+ }
138
+ async updateWorkspace(workspaceRef, request) {
139
+ const ref = await this.resolveWorkspaceRef(workspaceRef, ["admin"]);
140
+ const body = {};
141
+ if (request.name !== void 0) body.name = request.name;
142
+ if (request.description !== void 0) body.description = request.description;
143
+ if (request.icon !== void 0) body.icon = request.icon;
144
+ if (request.defaults !== void 0) body.defaults = request.defaults;
145
+ if (request.usageLimits !== void 0) body.usage_limits = request.usageLimits;
146
+ if (request.rateLimits !== void 0) body.rate_limits = request.rateLimits;
147
+ await this.client.workspaces.update(ref, body);
148
+ return this.getWorkspace(ref, { plane: "admin" });
149
+ }
150
+ async deleteWorkspace(workspaceRef) {
151
+ const ref = await this.resolveWorkspaceRef(workspaceRef, ["admin"]);
152
+ await this.client.workspaces.delete(ref);
153
+ }
154
+ /**
155
+ * The API accepts only a UUID or slug as a workspace ref — a display name
156
+ * gets a misleading 400 AB01 ("No update fields provided") on writes.
157
+ * Match a user-supplied ref against the workspace list so name | slug |
158
+ * uuid all work. Unmatched refs pass through so the API's own error stands.
159
+ */
160
+ async resolveWorkspaceRef(ref, planes) {
161
+ for (const plane of planes) {
162
+ let rows;
163
+ try {
164
+ rows = await this.listWorkspaces({ plane });
165
+ } catch {
166
+ continue;
167
+ }
168
+ if (rows.some((w) => w.id === ref || w.slug === ref)) return ref;
169
+ const byName = rows.filter((w) => w.name === ref);
170
+ if (byName.length > 1) {
171
+ throw new Error(
172
+ `workspace name '${ref}' is ambiguous (${byName.map((w) => w.slug).join(", ")}) \u2014 use a slug or UUID`
173
+ );
174
+ }
175
+ if (byName.length === 1) return byName[0].slug;
176
+ }
177
+ return ref;
178
+ }
179
+ async getTelemetryCost(opts) {
180
+ const days = opts.days ?? 7;
181
+ const workspaceSlug = await this.resolveWorkspaceRef(opts.workspaceSlug, ["data", "admin"]);
182
+ const raw = await this.client.telemetry.cost({
183
+ workspaceSlug,
184
+ days
185
+ });
186
+ return {
187
+ workspaceSlug,
188
+ days,
189
+ totalCents: raw.data.total,
190
+ avgCents: raw.data.avg,
191
+ quotaExceeded: raw.data.isQuotaExceeded,
192
+ records: raw.data.records.map((r) => ({ date: r.x, costCents: r.y }))
193
+ };
194
+ }
195
+ /** Re-read after a write, falling back to the (partial) write response if the get fails. */
196
+ async refetchAfterWrite(workspaceRef, writeResponse) {
197
+ try {
198
+ return await this.getWorkspace(workspaceRef, { plane: "admin" });
199
+ } catch {
200
+ return normalizeWorkspaceDetail(writeResponse);
201
+ }
202
+ }
203
+ };
55
204
 
56
- // src/cli/renderer/ui.ts
57
- import chalk3 from "chalk";
205
+ // src/config/client-options.ts
206
+ function runtimeInitOptions(config) {
207
+ return {
208
+ apiKey: config.airsApiKey,
209
+ apiToken: config.airsApiToken,
210
+ apiEndpoint: config.airsApiEndpoint,
211
+ numRetries: config.airsNumRetries
212
+ };
213
+ }
214
+ function redTeamClientOptions(config) {
215
+ return {
216
+ clientId: config.mgmtClientId,
217
+ clientSecret: config.mgmtClientSecret,
218
+ tsgId: config.mgmtTsgId,
219
+ dataEndpoint: config.redTeamDataEndpoint,
220
+ mgmtEndpoint: config.redTeamMgmtEndpoint,
221
+ tokenEndpoint: config.redTeamTokenEndpoint ?? config.mgmtTokenEndpoint,
222
+ networkBrokerEndpoint: config.redTeamNetworkBrokerEndpoint
223
+ };
224
+ }
225
+ function aiGatewayClientOptions(config) {
226
+ return {
227
+ clientId: config.mgmtClientId,
228
+ clientSecret: config.mgmtClientSecret,
229
+ tsgId: config.mgmtTsgId,
230
+ dataEndpoint: config.aiGwDataEndpoint,
231
+ adminEndpoint: config.aiGwAdminEndpoint,
232
+ tokenEndpoint: config.aiGwTokenEndpoint ?? config.mgmtTokenEndpoint
233
+ };
234
+ }
235
+ function modelSecurityClientOptions(config) {
236
+ return {
237
+ clientId: config.mgmtClientId,
238
+ clientSecret: config.mgmtClientSecret,
239
+ tsgId: config.mgmtTsgId,
240
+ dataEndpoint: config.modelSecDataEndpoint,
241
+ mgmtEndpoint: config.modelSecMgmtEndpoint,
242
+ tokenEndpoint: config.modelSecTokenEndpoint ?? config.mgmtTokenEndpoint
243
+ };
244
+ }
245
+
246
+ // src/cli/renderer/aigateway.ts
247
+ import chalk4 from "chalk";
248
+ import { dump as yamlDump } from "js-yaml";
58
249
 
59
250
  // src/cli/renderer/common.ts
60
251
  import chalk2 from "chalk";
@@ -121,6 +312,7 @@ function formatOutput(rows, columns, format) {
121
312
  }
122
313
 
123
314
  // src/cli/renderer/ui.ts
315
+ import chalk3 from "chalk";
124
316
  var INDENT = " ";
125
317
  var quietMode = false;
126
318
  function setQuiet(quiet) {
@@ -211,6 +403,118 @@ ${INDENT}${chalk3.bold(label)}
211
403
  }
212
404
  };
213
405
 
406
+ // src/cli/renderer/aigateway.ts
407
+ function renderAiGatewayHeader() {
408
+ ui.header("Prisma AIRS \u2014 AI Gateway", "Gateway workspace operations");
409
+ }
410
+ function statusColor(status) {
411
+ switch (status.toLowerCase()) {
412
+ case "active":
413
+ return chalk4.green;
414
+ case "archived":
415
+ return chalk4.yellow;
416
+ default:
417
+ return chalk4.dim;
418
+ }
419
+ }
420
+ function statusLabel(status) {
421
+ return status ?? "unknown";
422
+ }
423
+ function renderWorkspaceList(workspaces, format = "pretty") {
424
+ if (workspaces.length === 0) {
425
+ ui.emptyList("workspaces");
426
+ return;
427
+ }
428
+ if (format !== "pretty") {
429
+ const rows = workspaces.map((w) => ({
430
+ id: w.id,
431
+ slug: w.slug,
432
+ name: w.name,
433
+ status: statusLabel(w.status),
434
+ isDefault: w.isDefault,
435
+ scopeName: w.scopeName ?? ""
436
+ }));
437
+ console.log(
438
+ formatOutput(
439
+ rows,
440
+ [
441
+ { key: "id", label: "ID" },
442
+ { key: "slug", label: "Slug" },
443
+ { key: "name", label: "Name" },
444
+ { key: "status", label: "Status" },
445
+ { key: "isDefault", label: "Default" },
446
+ { key: "scopeName", label: "Scope" }
447
+ ],
448
+ format
449
+ )
450
+ );
451
+ return;
452
+ }
453
+ ui.section("AI Gateway Workspaces:");
454
+ for (const w of workspaces) {
455
+ ui.dim(w.id);
456
+ const status = statusColor(statusLabel(w.status))(statusLabel(w.status));
457
+ const dflt = w.isDefault ? chalk4.cyan(" default") : "";
458
+ console.log(` ${w.name} ${chalk4.dim(w.slug)} ${status}${dflt}`);
459
+ if (w.scopeName) console.log(` ${chalk4.dim(`scope: ${w.scopeName}`)}`);
460
+ console.log();
461
+ }
462
+ }
463
+ function renderWorkspaceDetail(workspace, format = "pretty") {
464
+ if (format !== "pretty") {
465
+ console.log(format === "json" ? JSON.stringify(workspace, null, 2) : yamlDump(workspace));
466
+ return;
467
+ }
468
+ ui.section("Workspace Detail:");
469
+ const pairs = [
470
+ ["ID", workspace.id],
471
+ ["Slug", workspace.slug],
472
+ ["Name", workspace.name],
473
+ ["Status", statusColor(statusLabel(workspace.status))(statusLabel(workspace.status))],
474
+ ["Default", workspace.isDefault ? "yes" : "no"]
475
+ ];
476
+ if (workspace.description != null) pairs.push(["Description", workspace.description]);
477
+ if (workspace.scopeName != null) pairs.push(["Scope", workspace.scopeName]);
478
+ if (workspace.createdAt != null) pairs.push(["Created", workspace.createdAt]);
479
+ if (workspace.lastUpdatedAt != null) pairs.push(["Updated", workspace.lastUpdatedAt]);
480
+ ui.keyValue(pairs);
481
+ if (workspace.defaults && Object.keys(workspace.defaults).length > 0) {
482
+ ui.section("Defaults:");
483
+ console.log(chalk4.dim(JSON.stringify(workspace.defaults, null, 2)));
484
+ }
485
+ if (workspace.usageLimits.length > 0) {
486
+ ui.section("Usage Limits:");
487
+ console.log(chalk4.dim(JSON.stringify(workspace.usageLimits, null, 2)));
488
+ }
489
+ if (workspace.rateLimits.length > 0) {
490
+ ui.section("Rate Limits:");
491
+ console.log(chalk4.dim(JSON.stringify(workspace.rateLimits, null, 2)));
492
+ }
493
+ if (workspace.securitySettings && Object.keys(workspace.securitySettings).length > 0) {
494
+ ui.section("Security Settings:");
495
+ ui.keyValue(Object.entries(workspace.securitySettings).map(([k, v]) => [k, v]));
496
+ }
497
+ console.log();
498
+ }
499
+ function renderCostReport(report, format = "pretty") {
500
+ if (format !== "pretty") {
501
+ console.log(format === "json" ? JSON.stringify(report, null, 2) : yamlDump(report));
502
+ return;
503
+ }
504
+ const dollars = (cents) => `$${(cents / 100).toFixed(2)}`;
505
+ ui.section(`Cost \u2014 ${report.workspaceSlug} (last ${report.days}d):`);
506
+ ui.keyValue([
507
+ ["Total", dollars(report.totalCents)],
508
+ ["Daily average", dollars(report.avgCents)]
509
+ ]);
510
+ if (report.quotaExceeded) ui.warn("Telemetry quota exceeded \u2014 data may be truncated");
511
+ if (report.records.length > 0) {
512
+ ui.section("Per day:");
513
+ ui.keyValue(report.records.map((r) => [r.date, dollars(r.costCents)]));
514
+ }
515
+ console.log();
516
+ }
517
+
214
518
  // src/cli/renderer/backup.ts
215
519
  function renderBackupHeader() {
216
520
  ui.header("Prisma AIRS \u2014 Backup & Restore");
@@ -252,17 +556,17 @@ function renderRestoreSummary(results) {
252
556
  }
253
557
 
254
558
  // src/cli/renderer/dlp.ts
255
- import chalk4 from "chalk";
256
- import { dump as yamlDump } from "js-yaml";
257
- function statusColor(status) {
559
+ import chalk5 from "chalk";
560
+ import { dump as yamlDump2 } from "js-yaml";
561
+ function statusColor2(status) {
258
562
  switch (status) {
259
563
  case "active":
260
- return chalk4.green(status);
564
+ return chalk5.green(status);
261
565
  case "deleted":
262
566
  case "disabled":
263
- return chalk4.yellow(status);
567
+ return chalk5.yellow(status);
264
568
  default:
265
- return status ? chalk4.dim(status) : chalk4.dim("\u2014");
569
+ return status ? chalk5.dim(status) : chalk5.dim("\u2014");
266
570
  }
267
571
  }
268
572
  function ts(ms) {
@@ -279,7 +583,7 @@ function emitStructured(payload, fmt) {
279
583
  return;
280
584
  }
281
585
  if (fmt === "yaml") {
282
- console.log(yamlDump(payload));
586
+ console.log(yamlDump2(payload));
283
587
  return;
284
588
  }
285
589
  console.log(JSON.stringify(payload, null, 2));
@@ -398,11 +702,11 @@ var dlpFilteringProfiles = {
398
702
  { key: "version", label: "Version" }
399
703
  ],
400
704
  (it) => {
401
- const dir = it.direction ? chalk4.dim(` dir:${it.direction}`) : "";
402
- const sev = it.log_severity ? chalk4.dim(` sev:${it.log_severity}`) : "";
403
- const ver = it.version != null ? chalk4.dim(` v${it.version}`) : "";
404
- return ` ${chalk4.dim(it.id)}
405
- ${it.name} ${chalk4.cyan(it.type ?? "")}${dir}${sev}${ver}`;
705
+ const dir = it.direction ? chalk5.dim(` dir:${it.direction}`) : "";
706
+ const sev = it.log_severity ? chalk5.dim(` sev:${it.log_severity}`) : "";
707
+ const ver = it.version != null ? chalk5.dim(` v${it.version}`) : "";
708
+ return ` ${chalk5.dim(it.id)}
709
+ ${it.name} ${chalk5.cyan(it.type ?? "")}${dir}${sev}${ver}`;
406
710
  }
407
711
  );
408
712
  },
@@ -457,10 +761,10 @@ var dlpPatterns = {
457
761
  { key: "version", label: "Version" }
458
762
  ],
459
763
  (it) => {
460
- const tech = it.detection_config?.technique ? chalk4.dim(` ${it.detection_config.technique}`) : "";
461
- const ver = it.version != null ? chalk4.dim(` v${it.version}`) : "";
462
- return ` ${chalk4.dim(it.id)}
463
- ${it.name} ${chalk4.cyan(it.type ?? "")} ${statusColor(it.status)}${tech}${ver}`;
764
+ const tech = it.detection_config?.technique ? chalk5.dim(` ${it.detection_config.technique}`) : "";
765
+ const ver = it.version != null ? chalk5.dim(` v${it.version}`) : "";
766
+ return ` ${chalk5.dim(it.id)}
767
+ ${it.name} ${chalk5.cyan(it.type ?? "")} ${statusColor2(it.status)}${tech}${ver}`;
464
768
  }
465
769
  );
466
770
  },
@@ -525,10 +829,10 @@ var dlpProfiles = {
525
829
  { key: "version", label: "Version" }
526
830
  ],
527
831
  (it) => {
528
- const ptype = it.profile_type ? chalk4.dim(` ${it.profile_type}`) : "";
529
- const ver = it.version != null ? chalk4.dim(` v${it.version}`) : "";
530
- return ` ${chalk4.dim(it.id)}
531
- ${it.name} ${chalk4.cyan(it.type ?? "")} ${statusColor(it.profile_status)}${ptype}${ver}`;
832
+ const ptype = it.profile_type ? chalk5.dim(` ${it.profile_type}`) : "";
833
+ const ver = it.version != null ? chalk5.dim(` v${it.version}`) : "";
834
+ return ` ${chalk5.dim(it.id)}
835
+ ${it.name} ${chalk5.cyan(it.type ?? "")} ${statusColor2(it.profile_status)}${ptype}${ver}`;
532
836
  }
533
837
  );
534
838
  },
@@ -582,10 +886,10 @@ var dlpDictionaries = {
582
886
  { key: "version", label: "Version" }
583
887
  ],
584
888
  (it) => {
585
- const kw = Array.isArray(it.keywords) ? chalk4.dim(` ${it.keywords.length} kw`) : "";
586
- const ver = it.version != null ? chalk4.dim(` v${it.version}`) : "";
587
- return ` ${chalk4.dim(it.id)}
588
- ${it.name} ${chalk4.cyan(it.type ?? "")} ${statusColor(it.status)}${kw}${ver}`;
889
+ const kw = Array.isArray(it.keywords) ? chalk5.dim(` ${it.keywords.length} kw`) : "";
890
+ const ver = it.version != null ? chalk5.dim(` v${it.version}`) : "";
891
+ return ` ${chalk5.dim(it.id)}
892
+ ${it.name} ${chalk5.cyan(it.type ?? "")} ${statusColor2(it.status)}${kw}${ver}`;
589
893
  }
590
894
  );
591
895
  },
@@ -627,7 +931,7 @@ var dlpDictionaries = {
627
931
  };
628
932
 
629
933
  // src/cli/renderer/eval.ts
630
- import chalk5 from "chalk";
934
+ import chalk6 from "chalk";
631
935
  function buildEvalOutput(profile, topic, intent, metrics, results) {
632
936
  const fps = results.filter((r) => !r.testCase.expectedTriggered && r.actualTriggered).map((r) => ({ prompt: r.testCase.prompt, expected: false, actual: true }));
633
937
  const fns = results.filter((r) => r.testCase.expectedTriggered && !r.actualTriggered).map((r) => ({ prompt: r.testCase.prompt, expected: true, actual: false }));
@@ -651,7 +955,7 @@ function buildEvalOutput(profile, topic, intent, metrics, results) {
651
955
  };
652
956
  }
653
957
  function renderEvalTerminal(output) {
654
- const coverageColor = output.metrics.coverage >= 0.9 ? chalk5.green : output.metrics.coverage >= 0.7 ? chalk5.yellow : chalk5.red;
958
+ const coverageColor = output.metrics.coverage >= 0.9 ? chalk6.green : output.metrics.coverage >= 0.7 ? chalk6.yellow : chalk6.red;
655
959
  ui.header("Eval Results");
656
960
  ui.keyValue([
657
961
  ["Profile", output.profile],
@@ -684,8 +988,8 @@ function renderEvalTerminal(output) {
684
988
  }
685
989
 
686
990
  // src/cli/renderer/modelsecurity.ts
687
- import chalk6 from "chalk";
688
- import { dump as yamlDump2 } from "js-yaml";
991
+ import chalk7 from "chalk";
992
+ import { dump as yamlDump3 } from "js-yaml";
689
993
  function renderModelSecurityHeader() {
690
994
  ui.header("Prisma AIRS \u2014 Model Security", "ML model supply chain security");
691
995
  }
@@ -696,15 +1000,15 @@ function stateColor(state) {
696
1000
  case "ALLOWING":
697
1001
  case "PASSED":
698
1002
  case "SUCCESS":
699
- return chalk6.green;
1003
+ return chalk7.green;
700
1004
  case "BLOCKED":
701
1005
  case "BLOCKING":
702
1006
  case "FAILED":
703
- return chalk6.red;
1007
+ return chalk7.red;
704
1008
  case "DISABLED":
705
- return chalk6.dim;
1009
+ return chalk7.dim;
706
1010
  default:
707
- return chalk6.yellow;
1011
+ return chalk7.yellow;
708
1012
  }
709
1013
  }
710
1014
  function renderGroupList(groups, format = "pretty") {
@@ -736,8 +1040,8 @@ function renderGroupList(groups, format = "pretty") {
736
1040
  ui.section("Security Groups:");
737
1041
  for (const g of groups) {
738
1042
  ui.dim(g.uuid);
739
- const color = g.state === "ACTIVE" ? chalk6.green : chalk6.yellow;
740
- console.log(` ${g.name} ${color(g.state)} source: ${chalk6.dim(g.sourceType)}`);
1043
+ const color = g.state === "ACTIVE" ? chalk7.green : chalk7.yellow;
1044
+ console.log(` ${g.name} ${color(g.state)} source: ${chalk7.dim(g.sourceType)}`);
741
1045
  }
742
1046
  console.log();
743
1047
  }
@@ -747,15 +1051,15 @@ function renderGroupDetail(group, format = "pretty") {
747
1051
  return;
748
1052
  }
749
1053
  if (format === "yaml") {
750
- console.log(yamlDump2(group));
1054
+ console.log(yamlDump3(group));
751
1055
  return;
752
1056
  }
753
1057
  ui.section("Security Group Detail:");
754
- const color = group.state === "ACTIVE" ? chalk6.green : chalk6.yellow;
1058
+ const color = group.state === "ACTIVE" ? chalk7.green : chalk7.yellow;
755
1059
  ui.keyValue([
756
1060
  ["UUID", group.uuid],
757
1061
  ["Name", group.name],
758
- ["Description", group.description || chalk6.dim("(none)")],
1062
+ ["Description", group.description || chalk7.dim("(none)")],
759
1063
  ["Source Type", group.sourceType],
760
1064
  ["State", color(group.state)],
761
1065
  ["Created", group.createdAt],
@@ -795,10 +1099,10 @@ function renderRuleList(rules, format = "pretty") {
795
1099
  for (const r of rules) {
796
1100
  ui.dim(r.uuid);
797
1101
  console.log(
798
- ` ${r.name} type: ${chalk6.dim(r.ruleType)} default: ${chalk6.dim(r.defaultState)}`
1102
+ ` ${r.name} type: ${chalk7.dim(r.ruleType)} default: ${chalk7.dim(r.defaultState)}`
799
1103
  );
800
- console.log(` ${chalk6.dim(r.description)}`);
801
- console.log(` Sources: ${r.compatibleSources.map((s) => chalk6.dim(s)).join(", ")}`);
1104
+ console.log(` ${chalk7.dim(r.description)}`);
1105
+ console.log(` Sources: ${r.compatibleSources.map((s) => chalk7.dim(s)).join(", ")}`);
802
1106
  }
803
1107
  console.log();
804
1108
  }
@@ -827,8 +1131,8 @@ function renderRuleDetail(rule) {
827
1131
  if (rule.editableFields.length > 0) {
828
1132
  ui.section("Editable Fields:");
829
1133
  for (const f of rule.editableFields) {
830
- console.log(` ${f.displayName} (${chalk6.dim(f.attributeName)}): ${f.displayType}`);
831
- if (f.description) console.log(` ${chalk6.dim(f.description)}`);
1134
+ console.log(` ${f.displayName} (${chalk7.dim(f.attributeName)}): ${f.displayType}`);
1135
+ if (f.description) console.log(` ${chalk7.dim(f.description)}`);
832
1136
  }
833
1137
  }
834
1138
  console.log();
@@ -906,13 +1210,13 @@ function renderMsScanList(scans, format = "pretty") {
906
1210
  for (const s of scans) {
907
1211
  ui.dim(s.uuid);
908
1212
  console.log(
909
- ` ${stateColor(s.evalOutcome)(s.evalOutcome)} ${chalk6.dim(s.scanOrigin)} ${chalk6.dim(s.createdAt)}`
1213
+ ` ${stateColor(s.evalOutcome)(s.evalOutcome)} ${chalk7.dim(s.scanOrigin)} ${chalk7.dim(s.createdAt)}`
910
1214
  );
911
- if (s.modelUri) console.log(` ${chalk6.dim(s.modelUri)}`);
1215
+ if (s.modelUri) console.log(` ${chalk7.dim(s.modelUri)}`);
912
1216
  if (s.evalSummary) {
913
1217
  const { rulesPassed, rulesFailed, totalRules } = s.evalSummary;
914
1218
  console.log(
915
- ` Rules: ${chalk6.green(`${rulesPassed} passed`)} ${chalk6.red(`${rulesFailed} failed`)} / ${totalRules} total`
1219
+ ` Rules: ${chalk7.green(`${rulesPassed} passed`)} ${chalk7.red(`${rulesFailed} failed`)} / ${totalRules} total`
916
1220
  );
917
1221
  }
918
1222
  }
@@ -934,7 +1238,7 @@ function renderMsScanDetail(scan) {
934
1238
  const { rulesPassed, rulesFailed, totalRules } = scan.evalSummary;
935
1239
  pairs.push([
936
1240
  "Rules",
937
- `${chalk6.green(`${rulesPassed} passed`)} ${chalk6.red(`${rulesFailed} failed`)} / ${totalRules} total`
1241
+ `${chalk7.green(`${rulesPassed} passed`)} ${chalk7.red(`${rulesFailed} failed`)} / ${totalRules} total`
938
1242
  ]);
939
1243
  }
940
1244
  ui.keyValue(pairs);
@@ -953,7 +1257,7 @@ function renderEvaluationList(evaluations) {
953
1257
  for (const e of evaluations) {
954
1258
  ui.dim(e.uuid);
955
1259
  console.log(
956
- ` ${e.ruleName} ${stateColor(e.result)(e.result)} ${chalk6.dim(e.ruleInstanceState)}`
1260
+ ` ${e.ruleName} ${stateColor(e.result)(e.result)} ${chalk7.dim(e.ruleInstanceState)}`
957
1261
  );
958
1262
  }
959
1263
  console.log();
@@ -979,9 +1283,9 @@ function renderViolationList(violations) {
979
1283
  ui.section("Violations:");
980
1284
  for (const v of violations) {
981
1285
  ui.dim(v.uuid);
982
- console.log(` ${chalk6.red(v.ruleName)} ${chalk6.dim(v.file)}`);
1286
+ console.log(` ${chalk7.red(v.ruleName)} ${chalk7.dim(v.file)}`);
983
1287
  console.log(` ${v.description}`);
984
- console.log(` Threat: ${chalk6.dim(v.threat)}`);
1288
+ console.log(` Threat: ${chalk7.dim(v.threat)}`);
985
1289
  }
986
1290
  console.log();
987
1291
  }
@@ -989,7 +1293,7 @@ function renderViolationDetail(violation) {
989
1293
  ui.section("Violation Detail:");
990
1294
  ui.keyValue([
991
1295
  ["UUID", violation.uuid],
992
- ["Rule", chalk6.red(violation.ruleName)],
1296
+ ["Rule", chalk7.red(violation.ruleName)],
993
1297
  ["Description", violation.ruleDescription],
994
1298
  ["State", violation.ruleInstanceState],
995
1299
  ["File", violation.file],
@@ -1005,8 +1309,8 @@ function renderFileList(files) {
1005
1309
  }
1006
1310
  ui.section("Scanned Files:");
1007
1311
  for (const f of files) {
1008
- const color = f.result === "SUCCESS" ? chalk6.green : f.result === "SKIPPED" ? chalk6.yellow : chalk6.red;
1009
- const formats = f.formats.length > 0 ? chalk6.dim(` [${f.formats.join(", ")}]`) : "";
1312
+ const color = f.result === "SUCCESS" ? chalk7.green : f.result === "SKIPPED" ? chalk7.yellow : chalk7.red;
1313
+ const formats = f.formats.length > 0 ? chalk7.dim(` [${f.formats.join(", ")}]`) : "";
1010
1314
  console.log(` ${color(f.result)} ${f.type} ${f.path}${formats}`);
1011
1315
  }
1012
1316
  console.log();
@@ -1064,15 +1368,15 @@ function renderModelList(models, format = "pretty") {
1064
1368
  ui.section("Models:");
1065
1369
  for (const m of models) {
1066
1370
  ui.dim(m.uuid);
1067
- const outcome = m.latestVersionOutcome ? stateColor(m.latestVersionOutcome)(m.latestVersionOutcome) : chalk6.dim("unscanned");
1068
- const formats = m.latestVersionFormats && m.latestVersionFormats.length > 0 ? chalk6.dim(` [${m.latestVersionFormats.join(", ")}]`) : "";
1371
+ const outcome = m.latestVersionOutcome ? stateColor(m.latestVersionOutcome)(m.latestVersionOutcome) : chalk7.dim("unscanned");
1372
+ const formats = m.latestVersionFormats && m.latestVersionFormats.length > 0 ? chalk7.dim(` [${m.latestVersionFormats.join(", ")}]`) : "";
1069
1373
  console.log(` ${m.name} ${outcome}${formats}`);
1070
1374
  console.log();
1071
1375
  }
1072
1376
  }
1073
1377
  function renderModelDetail(model, format = "pretty") {
1074
1378
  if (format !== "pretty") {
1075
- console.log(format === "json" ? JSON.stringify(model, null, 2) : yamlDump2(model));
1379
+ console.log(format === "json" ? JSON.stringify(model, null, 2) : yamlDump3(model));
1076
1380
  return;
1077
1381
  }
1078
1382
  ui.section("Model Detail:");
@@ -1129,7 +1433,7 @@ function renderModelVersionList(versions, format = "pretty") {
1129
1433
  ui.section("Model Versions:");
1130
1434
  for (const v of versions) {
1131
1435
  ui.dim(v.uuid);
1132
- const outcome = v.lastEvalOutcome ? stateColor(v.lastEvalOutcome)(v.lastEvalOutcome) : chalk6.dim("unscanned");
1436
+ const outcome = v.lastEvalOutcome ? stateColor(v.lastEvalOutcome)(v.lastEvalOutcome) : chalk7.dim("unscanned");
1133
1437
  const files = v.fileCount != null ? ` files: ${v.fileCount}` : "";
1134
1438
  console.log(` ${v.revision} ${outcome}${files}`);
1135
1439
  console.log();
@@ -1137,7 +1441,7 @@ function renderModelVersionList(versions, format = "pretty") {
1137
1441
  }
1138
1442
  function renderModelVersionDetail(version, format = "pretty") {
1139
1443
  if (format !== "pretty") {
1140
- console.log(format === "json" ? JSON.stringify(version, null, 2) : yamlDump2(version));
1444
+ console.log(format === "json" ? JSON.stringify(version, null, 2) : yamlDump3(version));
1141
1445
  return;
1142
1446
  }
1143
1447
  ui.section("Model Version Detail:");
@@ -1200,45 +1504,45 @@ function renderModelFileList(files, format = "pretty") {
1200
1504
  }
1201
1505
 
1202
1506
  // src/cli/renderer/redteam.ts
1203
- import chalk7 from "chalk";
1204
- import { dump as yamlDump3 } from "js-yaml";
1507
+ import chalk8 from "chalk";
1508
+ import { dump as yamlDump4 } from "js-yaml";
1205
1509
  function renderRedteamHeader() {
1206
1510
  ui.header("Prisma AIRS \u2014 AI Red Team", "Adversarial scan operations");
1207
1511
  }
1208
1512
  function severityColor(severity) {
1209
1513
  switch (severity.toUpperCase()) {
1210
1514
  case "CRITICAL":
1211
- return chalk7.red;
1515
+ return chalk8.red;
1212
1516
  case "HIGH":
1213
- return chalk7.magenta;
1517
+ return chalk8.magenta;
1214
1518
  case "MEDIUM":
1215
- return chalk7.yellow;
1519
+ return chalk8.yellow;
1216
1520
  case "LOW":
1217
- return chalk7.cyan;
1521
+ return chalk8.cyan;
1218
1522
  default:
1219
- return chalk7.dim;
1523
+ return chalk8.dim;
1220
1524
  }
1221
1525
  }
1222
- function statusColor2(status) {
1526
+ function statusColor3(status) {
1223
1527
  switch (status) {
1224
1528
  case "COMPLETED":
1225
- return chalk7.green;
1529
+ return chalk8.green;
1226
1530
  case "RUNNING":
1227
- return chalk7.blue;
1531
+ return chalk8.blue;
1228
1532
  case "QUEUED":
1229
1533
  case "INIT":
1230
- return chalk7.yellow;
1534
+ return chalk8.yellow;
1231
1535
  case "FAILED":
1232
1536
  case "ABORTED":
1233
- return chalk7.red;
1537
+ return chalk8.red;
1234
1538
  case "PARTIALLY_COMPLETE":
1235
- return chalk7.yellow;
1539
+ return chalk8.yellow;
1236
1540
  default:
1237
- return chalk7.white;
1541
+ return chalk8.white;
1238
1542
  }
1239
1543
  }
1240
1544
  function activeState(active) {
1241
- return statusColor2(active ? "COMPLETED" : "FAILED")(active ? "active" : "inactive");
1545
+ return statusColor3(active ? "COMPLETED" : "FAILED")(active ? "active" : "inactive");
1242
1546
  }
1243
1547
  function renderScanStatus(job) {
1244
1548
  ui.section("Scan Status:");
@@ -1248,7 +1552,7 @@ function renderScanStatus(job) {
1248
1552
  ["Type", job.jobType]
1249
1553
  ];
1250
1554
  if (job.targetName) pairs.push(["Target", job.targetName]);
1251
- pairs.push(["Status", statusColor2(job.status)(job.status)]);
1555
+ pairs.push(["Status", statusColor3(job.status)(job.status)]);
1252
1556
  if (job.total != null && job.completed != null) {
1253
1557
  pairs.push(["Progress", `${job.completed}/${job.total}`]);
1254
1558
  }
@@ -1291,9 +1595,9 @@ function renderScanList(jobs, format = "pretty") {
1291
1595
  for (const job of jobs) {
1292
1596
  ui.dim(job.uuid);
1293
1597
  console.log(
1294
- ` ${job.name} ${statusColor2(job.status)(job.status)} ${job.jobType}${job.score != null ? ` score: ${job.score}` : ""}`
1598
+ ` ${job.name} ${statusColor3(job.status)(job.status)} ${job.jobType}${job.score != null ? ` score: ${job.score}` : ""}`
1295
1599
  );
1296
- if (job.createdAt) console.log(` ${chalk7.dim(job.createdAt)}`);
1600
+ if (job.createdAt) console.log(` ${chalk8.dim(job.createdAt)}`);
1297
1601
  console.log();
1298
1602
  }
1299
1603
  }
@@ -1308,7 +1612,7 @@ function renderStaticReport(report) {
1308
1612
  for (const s of report.severityBreakdown) {
1309
1613
  const color = severityColor(s.severity);
1310
1614
  console.log(
1311
- ` ${color(s.severity.padEnd(10))} ${chalk7.red(`${s.successful} bypassed`)} ${chalk7.green(`${s.failed} blocked`)}`
1615
+ ` ${color(s.severity.padEnd(10))} ${chalk8.red(`${s.successful} bypassed`)} ${chalk8.green(`${s.failed} blocked`)}`
1312
1616
  );
1313
1617
  }
1314
1618
  }
@@ -1386,10 +1690,10 @@ function renderAttackList(attacks, options) {
1386
1690
  }
1387
1691
  ui.section("Attacks:");
1388
1692
  for (const a of attacks) {
1389
- const sev = a.severity ? severityColor(a.severity)(a.severity.padEnd(10)) : chalk7.dim("N/A".padEnd(10));
1390
- const result = a.successful ? chalk7.red("BYPASSED") : chalk7.green("BLOCKED");
1693
+ const sev = a.severity ? severityColor(a.severity)(a.severity.padEnd(10)) : chalk8.dim("N/A".padEnd(10));
1694
+ const result = a.successful ? chalk8.red("BYPASSED") : chalk8.green("BLOCKED");
1391
1695
  const label = a.subCategoryDisplayName ?? a.subCategory ?? "\u2014";
1392
- console.log(` ${sev} ${result} ${label}${a.category ? chalk7.dim(` [${a.category}]`) : ""}`);
1696
+ console.log(` ${sev} ${result} ${label}${a.category ? chalk8.dim(` [${a.category}]`) : ""}`);
1393
1697
  }
1394
1698
  if (options?.footnote) ui.dim(options.footnote);
1395
1699
  console.log();
@@ -1409,11 +1713,11 @@ function renderCustomAttackList(attacks) {
1409
1713
  }
1410
1714
  ui.section("Custom Attacks:");
1411
1715
  for (const a of attacks) {
1412
- const result = a.threat ? chalk7.red("THREAT") : chalk7.green("SAFE");
1716
+ const result = a.threat ? chalk8.red("THREAT") : chalk8.green("SAFE");
1413
1717
  const prompt = a.promptText.length > 80 ? `${a.promptText.substring(0, 77)}...` : a.promptText;
1414
- const asrStr = a.asr != null ? chalk7.dim(` ASR: ${a.asr.toFixed(1)}%`) : "";
1718
+ const asrStr = a.asr != null ? chalk8.dim(` ASR: ${a.asr.toFixed(1)}%`) : "";
1415
1719
  console.log(` ${result}${asrStr} ${prompt}`);
1416
- if (a.goal) console.log(` ${chalk7.dim(a.goal)}`);
1720
+ if (a.goal) console.log(` ${chalk8.dim(a.goal)}`);
1417
1721
  }
1418
1722
  console.log();
1419
1723
  }
@@ -1460,11 +1764,11 @@ function renderCategories(categories) {
1460
1764
  ui.section("Attack Categories:");
1461
1765
  for (const c of categories) {
1462
1766
  console.log(
1463
- ` ${chalk7.bold(c.displayName)} ${chalk7.cyan(`(${c.id})`)}${c.description ? chalk7.dim(` \u2014 ${c.description}`) : ""}`
1767
+ ` ${chalk8.bold(c.displayName)} ${chalk8.cyan(`(${c.id})`)}${c.description ? chalk8.dim(` \u2014 ${c.description}`) : ""}`
1464
1768
  );
1465
1769
  for (const sc of c.subCategories) {
1466
1770
  console.log(
1467
- ` ${chalk7.dim("\u2022")} ${sc.displayName} ${chalk7.cyan(`(${sc.id})`)}${sc.description ? chalk7.dim(` \u2014 ${sc.description}`) : ""}`
1771
+ ` ${chalk8.dim("\u2022")} ${sc.displayName} ${chalk8.cyan(`(${sc.id})`)}${sc.description ? chalk8.dim(` \u2014 ${sc.description}`) : ""}`
1468
1772
  );
1469
1773
  }
1470
1774
  console.log();
@@ -1521,7 +1825,7 @@ function renderTargetDetail(target, format = "pretty") {
1521
1825
  if (format === "json") {
1522
1826
  console.log(JSON.stringify(target, null, 2));
1523
1827
  } else if (format === "yaml") {
1524
- console.log(yamlDump3(target));
1828
+ console.log(yamlDump4(target));
1525
1829
  }
1526
1830
  return;
1527
1831
  }
@@ -1553,7 +1857,7 @@ function renderPromptSetDetail(ps, format = "pretty", info) {
1553
1857
  if (format === "json") {
1554
1858
  console.log(JSON.stringify(payload, null, 2));
1555
1859
  } else if (format === "yaml") {
1556
- console.log(yamlDump3(payload));
1860
+ console.log(yamlDump4(payload));
1557
1861
  }
1558
1862
  return;
1559
1863
  }
@@ -1590,7 +1894,7 @@ function renderPromptList(prompts, format = "pretty") {
1590
1894
  if (format === "json") {
1591
1895
  console.log(JSON.stringify(prompts, null, 2));
1592
1896
  } else if (format === "yaml") {
1593
- console.log(yamlDump3(prompts));
1897
+ console.log(yamlDump4(prompts));
1594
1898
  }
1595
1899
  return;
1596
1900
  }
@@ -1600,11 +1904,11 @@ function renderPromptList(prompts, format = "pretty") {
1600
1904
  }
1601
1905
  ui.section("Prompts:");
1602
1906
  for (const p of prompts) {
1603
- const status = p.active ? chalk7.green("active") : chalk7.dim("inactive");
1907
+ const status = p.active ? chalk8.green("active") : chalk8.dim("inactive");
1604
1908
  const text = p.prompt.length > 80 ? `${p.prompt.substring(0, 77)}...` : p.prompt;
1605
- console.log(` ${chalk7.dim(p.uuid)} ${status}`);
1909
+ console.log(` ${chalk8.dim(p.uuid)} ${status}`);
1606
1910
  console.log(` ${text}`);
1607
- if (p.goal) console.log(` ${chalk7.dim(`Goal: ${p.goal}`)}`);
1911
+ if (p.goal) console.log(` ${chalk8.dim(`Goal: ${p.goal}`)}`);
1608
1912
  }
1609
1913
  console.log();
1610
1914
  }
@@ -1613,7 +1917,7 @@ function renderPromptDetail(p, format = "pretty") {
1613
1917
  if (format === "json") {
1614
1918
  console.log(JSON.stringify(p, null, 2));
1615
1919
  } else if (format === "yaml") {
1616
- console.log(yamlDump3(p));
1920
+ console.log(yamlDump4(p));
1617
1921
  }
1618
1922
  return;
1619
1923
  }
@@ -1621,7 +1925,7 @@ function renderPromptDetail(p, format = "pretty") {
1621
1925
  const pairs = [
1622
1926
  ["UUID", p.uuid],
1623
1927
  ["Set UUID", p.promptSetId],
1624
- ["Status", p.active ? chalk7.green("active") : chalk7.dim("inactive")],
1928
+ ["Status", p.active ? chalk8.green("active") : chalk8.dim("inactive")],
1625
1929
  ["Prompt", p.prompt]
1626
1930
  ];
1627
1931
  if (p.goal) pairs.push(["Goal", p.goal]);
@@ -1633,7 +1937,7 @@ function renderPropertyNames(names, format = "pretty") {
1633
1937
  if (format === "json") {
1634
1938
  console.log(JSON.stringify(names, null, 2));
1635
1939
  } else if (format === "yaml") {
1636
- console.log(yamlDump3(names));
1940
+ console.log(yamlDump4(names));
1637
1941
  } else {
1638
1942
  const rows = names.map((n) => ({ name: n }));
1639
1943
  console.log(formatOutput(rows, [{ key: "name", label: "Name" }], format));
@@ -1653,7 +1957,7 @@ function renderPropertyNames(names, format = "pretty") {
1653
1957
  function renderAuthValidation(result) {
1654
1958
  ui.section("Auth Validation:");
1655
1959
  const pairs = [
1656
- ["Validated", result.validated ? chalk7.green("yes") : chalk7.red("no")]
1960
+ ["Validated", result.validated ? chalk8.green("yes") : chalk8.red("no")]
1657
1961
  ];
1658
1962
  if (result.tokenPreview) pairs.push(["Token", result.tokenPreview]);
1659
1963
  if (result.expiresIn != null) pairs.push(["Expires In", `${result.expiresIn}s`]);
@@ -1671,7 +1975,7 @@ function renderTargetTemplates(templates) {
1671
1975
  function renderEulaStatus(status) {
1672
1976
  ui.section("EULA Status:");
1673
1977
  const pairs = [
1674
- ["Accepted", status.isAccepted ? chalk7.green("yes") : chalk7.red("no")]
1978
+ ["Accepted", status.isAccepted ? chalk8.green("yes") : chalk8.red("no")]
1675
1979
  ];
1676
1980
  if (status.acceptedAt) pairs.push(["Accepted At", status.acceptedAt]);
1677
1981
  if (status.acceptedByUserId) pairs.push(["Accepted By", status.acceptedByUserId]);
@@ -1688,7 +1992,7 @@ function renderPropertyValues(payload, format = "pretty") {
1688
1992
  if (format === "json") {
1689
1993
  console.log(JSON.stringify(payload, null, 2));
1690
1994
  } else if (format === "yaml") {
1691
- console.log(yamlDump3(payload));
1995
+ console.log(yamlDump4(payload));
1692
1996
  }
1693
1997
  return;
1694
1998
  }
@@ -1709,7 +2013,7 @@ function renderInstanceResponse(resp) {
1709
2013
  if (resp.tenantId) pairs.push(["Tenant ID", resp.tenantId]);
1710
2014
  if (resp.appId) pairs.push(["App ID", resp.appId]);
1711
2015
  if (resp.isSuccess != null) {
1712
- pairs.push(["Success", resp.isSuccess ? chalk7.green("yes") : chalk7.red("no")]);
2016
+ pairs.push(["Success", resp.isSuccess ? chalk8.green("yes") : chalk8.red("no")]);
1713
2017
  }
1714
2018
  ui.keyValue(pairs);
1715
2019
  console.log();
@@ -1719,7 +2023,7 @@ function renderInstanceDetail(inst, format = "pretty") {
1719
2023
  if (format === "json") {
1720
2024
  console.log(JSON.stringify(inst, null, 2));
1721
2025
  } else if (format === "yaml") {
1722
- console.log(yamlDump3(inst));
2026
+ console.log(yamlDump4(inst));
1723
2027
  }
1724
2028
  return;
1725
2029
  }
@@ -1737,7 +2041,7 @@ function renderRegistryCredentials(creds, format = "pretty") {
1737
2041
  if (format === "json") {
1738
2042
  console.log(JSON.stringify(creds, null, 2));
1739
2043
  } else if (format === "yaml") {
1740
- console.log(yamlDump3(creds));
2044
+ console.log(yamlDump4(creds));
1741
2045
  }
1742
2046
  return;
1743
2047
  }
@@ -1751,13 +2055,13 @@ function renderRegistryCredentials(creds, format = "pretty") {
1751
2055
  function channelStatusColor(status) {
1752
2056
  switch (status.toUpperCase()) {
1753
2057
  case "ONLINE":
1754
- return chalk7.green;
2058
+ return chalk8.green;
1755
2059
  case "DRAFT":
1756
- return chalk7.yellow;
2060
+ return chalk8.yellow;
1757
2061
  case "OFFLINE":
1758
- return chalk7.red;
2062
+ return chalk8.red;
1759
2063
  default:
1760
- return chalk7.white;
2064
+ return chalk8.white;
1761
2065
  }
1762
2066
  }
1763
2067
  function renderChannelList(channels, format = "pretty") {
@@ -1791,7 +2095,7 @@ function renderChannelList(channels, format = "pretty") {
1791
2095
  ui.section("Network Broker Channels:");
1792
2096
  for (const c of channels) {
1793
2097
  if (c.uuid) ui.dim(c.uuid);
1794
- const status = c.status ? channelStatusColor(c.status)(c.status) : chalk7.dim("unknown");
2098
+ const status = c.status ? channelStatusColor(c.status)(c.status) : chalk8.dim("unknown");
1795
2099
  const clients = c.connectedClientsCount != null ? ` clients: ${c.connectedClientsCount}` : "";
1796
2100
  console.log(` ${c.name ?? "(unnamed)"} ${status}${clients}`);
1797
2101
  console.log();
@@ -1799,7 +2103,7 @@ function renderChannelList(channels, format = "pretty") {
1799
2103
  }
1800
2104
  function renderChannelDetail(channel, format = "pretty") {
1801
2105
  if (format !== "pretty") {
1802
- console.log(format === "json" ? JSON.stringify(channel, null, 2) : yamlDump3(channel));
2106
+ console.log(format === "json" ? JSON.stringify(channel, null, 2) : yamlDump4(channel));
1803
2107
  return;
1804
2108
  }
1805
2109
  ui.section("Channel Detail:");
@@ -1825,7 +2129,7 @@ function renderChannelDetail(channel, format = "pretty") {
1825
2129
  }
1826
2130
  function renderChannelStats(stats, format = "pretty") {
1827
2131
  if (format !== "pretty") {
1828
- console.log(format === "json" ? JSON.stringify(stats, null, 2) : yamlDump3(stats));
2132
+ console.log(format === "json" ? JSON.stringify(stats, null, 2) : yamlDump4(stats));
1829
2133
  return;
1830
2134
  }
1831
2135
  ui.section("Network Broker Stats:");
@@ -1843,7 +2147,7 @@ function renderChannelStats(stats, format = "pretty") {
1843
2147
  function renderLanguages(data, format = "pretty") {
1844
2148
  if (format !== "pretty") {
1845
2149
  if (format === "json" || format === "yaml") {
1846
- console.log(format === "json" ? JSON.stringify(data, null, 2) : yamlDump3(data));
2150
+ console.log(format === "json" ? JSON.stringify(data, null, 2) : yamlDump4(data));
1847
2151
  return;
1848
2152
  }
1849
2153
  console.log(
@@ -1869,7 +2173,7 @@ function renderLanguages(data, format = "pretty") {
1869
2173
  }
1870
2174
  ui.section("Languages:");
1871
2175
  for (const l of data.languages) {
1872
- console.log(` ${chalk7.dim(l.code)} ${l.name}`);
2176
+ console.log(` ${chalk8.dim(l.code)} ${l.name}`);
1873
2177
  }
1874
2178
  console.log();
1875
2179
  }
@@ -1903,45 +2207,140 @@ function renderErrorLogs(logs, format = "pretty") {
1903
2207
  }
1904
2208
  ui.section("Target-Profile Error Logs:");
1905
2209
  for (const l of logs) {
1906
- const type = l.errorType ? chalk7.red(l.errorType) : chalk7.dim("error");
2210
+ const type = l.errorType ? chalk8.red(l.errorType) : chalk8.dim("error");
1907
2211
  console.log(
1908
- ` ${chalk7.dim(l.createdAt)} ${type}${l.errorSource ? ` (${l.errorSource})` : ""}`
2212
+ ` ${chalk8.dim(l.createdAt)} ${type}${l.errorSource ? ` (${l.errorSource})` : ""}`
1909
2213
  );
1910
2214
  if (l.errorMessage) console.log(` ${l.errorMessage}`);
1911
- if (l.jobId) console.log(` ${chalk7.dim(`job: ${l.jobId}`)}`);
2215
+ if (l.jobId) console.log(` ${chalk8.dim(`job: ${l.jobId}`)}`);
2216
+ console.log();
2217
+ }
2218
+ }
2219
+ function renderAdapterList(adapters, format = "pretty", totalItems) {
2220
+ if (adapters.length === 0) {
2221
+ ui.emptyList("adapters");
2222
+ return;
2223
+ }
2224
+ if (format !== "pretty") {
2225
+ const rows = adapters.map((a) => ({
2226
+ uuid: a.uuid,
2227
+ name: a.name,
2228
+ status: a.status,
2229
+ targets: a.targetCount ?? "",
2230
+ updated: a.updatedAt ?? ""
2231
+ }));
2232
+ console.log(
2233
+ formatOutput(
2234
+ rows,
2235
+ [
2236
+ { key: "uuid", label: "UUID" },
2237
+ { key: "name", label: "Name" },
2238
+ { key: "status", label: "Status" },
2239
+ { key: "targets", label: "Targets" },
2240
+ { key: "updated", label: "Updated" }
2241
+ ],
2242
+ format
2243
+ )
2244
+ );
2245
+ return;
2246
+ }
2247
+ ui.section("Custom Target Adapters:");
2248
+ for (const a of adapters) {
2249
+ ui.dim(a.uuid);
2250
+ const status = a.status === "ACTIVE" ? chalk8.green(a.status) : chalk8.yellow(a.status);
2251
+ const targets = a.targetCount != null ? ` targets: ${a.targetCount}` : "";
2252
+ console.log(` ${a.name} ${status}${targets}`);
1912
2253
  console.log();
1913
2254
  }
2255
+ if (totalItems !== void 0) ui.dim(`${totalItems} total`);
2256
+ }
2257
+ function renderAdapterDetail(adapter, format = "pretty") {
2258
+ if (format !== "pretty") {
2259
+ console.log(format === "json" ? JSON.stringify(adapter, null, 2) : yamlDump4(adapter));
2260
+ return;
2261
+ }
2262
+ ui.section("Adapter Detail:");
2263
+ const pairs = [
2264
+ ["UUID", adapter.uuid],
2265
+ ["Name", adapter.name],
2266
+ [
2267
+ "Status",
2268
+ adapter.status === "ACTIVE" ? chalk8.green(adapter.status) : chalk8.yellow(adapter.status)
2269
+ ],
2270
+ ["Script", `${adapter.scriptB64.length} base64 chars`]
2271
+ ];
2272
+ if (adapter.description != null) pairs.push(["Description", adapter.description]);
2273
+ if (adapter.networkBrokerChannelUuid != null)
2274
+ pairs.push(["Broker Channel", adapter.networkBrokerChannelUuid]);
2275
+ if (adapter.targetCount != null) pairs.push(["Targets", adapter.targetCount]);
2276
+ if (adapter.createdAt != null) pairs.push(["Created", adapter.createdAt]);
2277
+ if (adapter.updatedAt != null) pairs.push(["Updated", adapter.updatedAt]);
2278
+ ui.keyValue(pairs);
2279
+ if (adapter.variables.length > 0) {
2280
+ ui.section("Variables:");
2281
+ ui.keyValue(
2282
+ adapter.variables.map((v) => [
2283
+ `${v.key} (${v.type})`,
2284
+ v.isRedacted ? chalk8.dim("(redacted)") : v.value ?? ""
2285
+ ])
2286
+ );
2287
+ }
2288
+ console.log();
2289
+ }
2290
+ function renderAdapterValidation(result, format = "pretty") {
2291
+ if (format !== "pretty") {
2292
+ console.log(format === "json" ? JSON.stringify(result, null, 2) : yamlDump4(result));
2293
+ return;
2294
+ }
2295
+ if (result.validated) {
2296
+ ui.success("Adapter script validated");
2297
+ } else {
2298
+ ui.error("Adapter script validation FAILED");
2299
+ }
2300
+ if (result.stdout) {
2301
+ ui.section("stdout:");
2302
+ console.log(result.stdout);
2303
+ }
2304
+ if (result.stderr) {
2305
+ ui.section("stderr:");
2306
+ console.log(chalk8.red(result.stderr));
2307
+ }
2308
+ if (result.traceback) {
2309
+ ui.section("traceback:");
2310
+ console.log(chalk8.red(result.traceback));
2311
+ }
2312
+ console.log();
1914
2313
  }
1915
2314
 
1916
2315
  // src/cli/renderer/runtime.ts
1917
- import chalk8 from "chalk";
2316
+ import chalk9 from "chalk";
1918
2317
  function renderScanProgress(job) {
1919
2318
  if (job.total != null && job.completed != null && job.total > 0) {
1920
2319
  const pct = Math.round(job.completed / job.total * 100);
1921
2320
  const bar = "\u2588".repeat(Math.round(pct / 5)) + "\u2591".repeat(20 - Math.round(pct / 5));
1922
2321
  process.stdout.write(
1923
- `\r ${statusColor3(job.status)(job.status)} ${bar} ${pct}% (${job.completed}/${job.total})`
2322
+ `\r ${statusColor4(job.status)(job.status)} ${bar} ${pct}% (${job.completed}/${job.total})`
1924
2323
  );
1925
2324
  } else {
1926
- process.stdout.write(`\r ${statusColor3(job.status)(job.status)}...`);
2325
+ process.stdout.write(`\r ${statusColor4(job.status)(job.status)}...`);
1927
2326
  }
1928
2327
  }
1929
- function statusColor3(status) {
2328
+ function statusColor4(status) {
1930
2329
  switch (status) {
1931
2330
  case "COMPLETED":
1932
- return chalk8.green;
2331
+ return chalk9.green;
1933
2332
  case "RUNNING":
1934
- return chalk8.blue;
2333
+ return chalk9.blue;
1935
2334
  case "QUEUED":
1936
2335
  case "INIT":
1937
- return chalk8.yellow;
2336
+ return chalk9.yellow;
1938
2337
  case "FAILED":
1939
2338
  case "ABORTED":
1940
- return chalk8.red;
2339
+ return chalk9.red;
1941
2340
  case "PARTIALLY_COMPLETE":
1942
- return chalk8.yellow;
2341
+ return chalk9.yellow;
1943
2342
  default:
1944
- return chalk8.white;
2343
+ return chalk9.white;
1945
2344
  }
1946
2345
  }
1947
2346
  function renderRuntimeConfigHeader() {
@@ -1971,8 +2370,8 @@ function renderProfileList(profiles, format = "pretty") {
1971
2370
  ui.section("Security Profiles:");
1972
2371
  for (const p of profiles) {
1973
2372
  ui.dim(p.profileId);
1974
- const status = p.active ? chalk8.green("active") : chalk8.yellow("inactive");
1975
- const rev = p.revision != null ? chalk8.dim(` rev:${p.revision}`) : "";
2373
+ const status = p.active ? chalk9.green("active") : chalk9.yellow("inactive");
2374
+ const rev = p.revision != null ? chalk9.dim(` rev:${p.revision}`) : "";
1976
2375
  console.log(` ${p.profileName} ${status}${rev}`);
1977
2376
  }
1978
2377
  console.log();
@@ -1982,7 +2381,7 @@ function renderProfileDetail(profile) {
1982
2381
  const pairs = [
1983
2382
  ["ID", profile.profileId],
1984
2383
  ["Name", profile.profileName],
1985
- ["Status", profile.active ? chalk8.green("active") : chalk8.yellow("inactive")]
2384
+ ["Status", profile.active ? chalk9.green("active") : chalk9.yellow("inactive")]
1986
2385
  ];
1987
2386
  if (profile.revision != null) pairs.push(["Revision", profile.revision]);
1988
2387
  if (profile.createdBy) pairs.push(["Created", profile.createdBy]);
@@ -2086,8 +2485,8 @@ function renderTopicList(topics, format = "pretty") {
2086
2485
  ui.section("Custom Topics:");
2087
2486
  for (const t of topics) {
2088
2487
  ui.dim(String(t.topic_id));
2089
- const rev = t.revision != null ? chalk8.dim(` rev:${t.revision}`) : "";
2090
- const desc = t.description ? chalk8.dim(` \u2014 ${t.description.slice(0, 80)}`) : "";
2488
+ const rev = t.revision != null ? chalk9.dim(` rev:${t.revision}`) : "";
2489
+ const desc = t.description ? chalk9.dim(` \u2014 ${t.description.slice(0, 80)}`) : "";
2091
2490
  console.log(` ${t.topic_name}${rev}${desc}`);
2092
2491
  }
2093
2492
  console.log();
@@ -2145,8 +2544,8 @@ function renderApiKeyList(keys, format = "pretty") {
2145
2544
  ui.section("API Keys:");
2146
2545
  for (const k of keys) {
2147
2546
  ui.dim(k.id);
2148
- const last8 = k.last8 ? chalk8.dim(` key: \u2026${k.last8}`) : "";
2149
- const expires = k.expiresAt ? chalk8.dim(` expires: ${k.expiresAt}`) : "";
2547
+ const last8 = k.last8 ? chalk9.dim(` key: \u2026${k.last8}`) : "";
2548
+ const expires = k.expiresAt ? chalk9.dim(` expires: ${k.expiresAt}`) : "";
2150
2549
  console.log(` ${k.name}${last8}${expires}`);
2151
2550
  }
2152
2551
  console.log();
@@ -2191,7 +2590,7 @@ function renderCustomerAppList(apps, format = "pretty") {
2191
2590
  ui.section("Customer Apps:");
2192
2591
  for (const a of apps) {
2193
2592
  if (a.id) ui.dim(a.id);
2194
- const desc = a.description ? chalk8.dim(` \u2014 ${a.description.slice(0, 80)}`) : "";
2593
+ const desc = a.description ? chalk9.dim(` \u2014 ${a.description.slice(0, 80)}`) : "";
2195
2594
  console.log(` ${a.name}${desc}`);
2196
2595
  }
2197
2596
  console.log();
@@ -2315,9 +2714,9 @@ function renderDeploymentProfileList(profiles, format = "pretty") {
2315
2714
  const name = p.raw.dp_name ?? p.raw.profile_name ?? p.raw.name ?? "unknown";
2316
2715
  const status = p.raw.status;
2317
2716
  const authCode = p.raw.auth_code;
2318
- const statusColor4 = status === "active" ? chalk8.green : chalk8.dim;
2717
+ const statusColor5 = status === "active" ? chalk9.green : chalk9.dim;
2319
2718
  console.log(
2320
- ` ${name}${status ? ` ${statusColor4(status)}` : ""}${authCode ? ` ${chalk8.dim(authCode)}` : ""}`
2719
+ ` ${name}${status ? ` ${statusColor5(status)}` : ""}${authCode ? ` ${chalk9.dim(authCode)}` : ""}`
2321
2720
  );
2322
2721
  }
2323
2722
  console.log();
@@ -2357,10 +2756,10 @@ function renderScanLogList(results, pageToken, format = "pretty") {
2357
2756
  const profile = r.profile_name;
2358
2757
  const ts2 = r.received_ts ?? r.timestamp;
2359
2758
  const scanId = r.scan_id;
2360
- const actionColor = action === "block" ? chalk8.red : chalk8.green;
2759
+ const actionColor = action === "block" ? chalk9.red : chalk9.green;
2361
2760
  if (scanId) ui.dim(scanId);
2362
2761
  console.log(
2363
- ` ${ts2 ? chalk8.dim(ts2) : ""} ${action ? actionColor(action) : ""} ${profile ? `[${profile}]` : ""} ${app ?? ""}`
2762
+ ` ${ts2 ? chalk9.dim(ts2) : ""} ${action ? actionColor(action) : ""} ${profile ? `[${profile}]` : ""} ${app ?? ""}`
2364
2763
  );
2365
2764
  }
2366
2765
  if (pageToken) {
@@ -2370,20 +2769,256 @@ function renderScanLogList(results, pageToken, format = "pretty") {
2370
2769
  console.log();
2371
2770
  }
2372
2771
 
2373
- // src/cli/commands/completion.ts
2374
- var COMPLETION_SHELLS = ["bash", "zsh", "fish"];
2375
- function collectCompletionNodes(root, path3 = []) {
2376
- const words = [];
2377
- for (const sub of root.commands) {
2378
- words.push(sub.name(), ...sub.aliases());
2772
+ // src/cli/confirm.ts
2773
+ async function confirmOrAbort(message, force, options = {}) {
2774
+ if (force) return;
2775
+ const interactive = options.isTTY ?? process.stdout.isTTY === true;
2776
+ if (!interactive) {
2777
+ usageError(
2778
+ `refusing to ${options.action ?? "proceed"} without --force in non-interactive mode`
2779
+ );
2379
2780
  }
2380
- for (const opt of root.options) {
2381
- if (!opt.hidden && opt.long) words.push(opt.long);
2781
+ const prompt = options.promptFn ?? (await import("@inquirer/prompts")).confirm;
2782
+ const confirmed = await prompt({ message, default: false });
2783
+ if (!confirmed) {
2784
+ ui.info("Aborted");
2785
+ process.exit(0);
2382
2786
  }
2383
- words.push("--help");
2384
- const node = { path: path3.join(" "), words: [...new Set(words)] };
2385
- const children = root.commands.flatMap(
2386
- (sub) => collectCompletionNodes(sub, [...path3, sub.name()])
2787
+ }
2788
+
2789
+ // src/cli/examples.ts
2790
+ function examples(...lines) {
2791
+ return `
2792
+ Examples:
2793
+ ${lines.map((l) => ` $ ${l}`).join("\n")}
2794
+ `;
2795
+ }
2796
+
2797
+ // src/cli/commands/aigateway.ts
2798
+ async function createService() {
2799
+ const config = await loadConfig();
2800
+ return new SdkAiGatewayService(aiGatewayClientOptions(config));
2801
+ }
2802
+ function failWithGrantHint(err) {
2803
+ const hint = aiGatewayGrantHint(err);
2804
+ if (hint) ui.warn(`403: ${hint}`);
2805
+ fail(err);
2806
+ }
2807
+ function parsePlane(value) {
2808
+ if (value === void 0) return void 0;
2809
+ if (value !== "data" && value !== "admin") {
2810
+ usageError(`Invalid --plane '${value}'. Valid planes: data, admin`);
2811
+ }
2812
+ return value;
2813
+ }
2814
+ function parseStatus(value) {
2815
+ if (value === void 0) return void 0;
2816
+ if (value !== "active" && value !== "archived") {
2817
+ usageError(`Invalid --status '${value}'. Valid statuses: active, archived`);
2818
+ }
2819
+ return value;
2820
+ }
2821
+ function parseJsonFlag(raw, flag) {
2822
+ if (raw === void 0) return void 0;
2823
+ try {
2824
+ return JSON.parse(raw);
2825
+ } catch {
2826
+ throw new Error(`${flag} must be valid JSON`);
2827
+ }
2828
+ }
2829
+ function buildWorkspaceWriteRequest(opts) {
2830
+ const out = {};
2831
+ for (const key of ["name", "description", "icon"]) {
2832
+ if (opts[key] !== void 0) out[key] = opts[key];
2833
+ }
2834
+ const defaults = parseJsonFlag(opts.defaults, "--defaults");
2835
+ const metadata = parseJsonFlag(opts.metadata, "--metadata");
2836
+ if (defaults !== void 0 || metadata !== void 0) {
2837
+ out.defaults = {
2838
+ ...typeof defaults === "object" && defaults !== null ? defaults : {},
2839
+ ...metadata !== void 0 ? { metadata } : {}
2840
+ };
2841
+ }
2842
+ if (opts.users !== void 0) {
2843
+ out.users = opts.users.split(",").map((u) => u.trim()).filter(Boolean);
2844
+ }
2845
+ const usage = parseJsonFlag(opts.usageLimits, "--usage-limits");
2846
+ if (usage !== void 0) out.usageLimits = usage;
2847
+ const rate = parseJsonFlag(opts.rateLimits, "--rate-limits");
2848
+ if (rate !== void 0) out.rateLimits = rate;
2849
+ return out;
2850
+ }
2851
+ function scopeNameLooksUnrelated(name, scopeName) {
2852
+ const nameToken = name.toLowerCase().replace(/[^a-z0-9]/g, "");
2853
+ if (nameToken.length < 4) return false;
2854
+ return !scopeName.toLowerCase().replace(/[^a-z0-9]/g, "").includes(nameToken);
2855
+ }
2856
+ function registerAiGatewayCommand(program) {
2857
+ const aigateway = program.command("aigateway").description("AI Gateway operations");
2858
+ const workspace = aigateway.command("workspace").description("Manage AI Gateway workspaces");
2859
+ 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, csv, json, yaml", "pretty").addHelpText(
2860
+ "after",
2861
+ examples(
2862
+ "airs aigateway workspace list",
2863
+ "airs aigateway workspace list --plane admin",
2864
+ "airs aigateway workspace list --plane admin --status archived",
2865
+ "airs aigateway workspace list --all --output json"
2866
+ )
2867
+ ).action(async (opts) => {
2868
+ try {
2869
+ const fmt = opts.output;
2870
+ if (fmt === "pretty") renderAiGatewayHeader();
2871
+ const plane = parsePlane(opts.plane);
2872
+ const status = parseStatus(opts.status);
2873
+ if (opts.all && (plane !== void 0 || status !== void 0)) {
2874
+ usageError("--all already merges admin-plane active + archived; drop --plane/--status");
2875
+ }
2876
+ const service = await createService();
2877
+ const workspaces = opts.all ? await service.listAllWorkspaces() : await service.listWorkspaces(
2878
+ plane !== void 0 || status !== void 0 ? { plane, status } : void 0
2879
+ );
2880
+ renderWorkspaceList(workspaces, fmt);
2881
+ if (fmt === "pretty" && !opts.all && plane !== "admin") {
2882
+ ui.status(
2883
+ "Data-plane list shows only active workspaces you are scoped to \u2014 use --plane admin or --all for the whole tenant."
2884
+ );
2885
+ }
2886
+ } catch (err) {
2887
+ failWithGrantHint(err);
2888
+ }
2889
+ });
2890
+ workspace.command("get <ref>").description("Get one workspace by UUID or slug (includes settings blocks)").option("--plane <plane>", "Plane to read from: data (scoped) or admin (whole tenant)").option("--output <format>", "Output format: pretty, json, yaml", "pretty").addHelpText(
2891
+ "after",
2892
+ examples(
2893
+ "airs aigateway workspace get ws-main-a-349e0e",
2894
+ "airs aigateway workspace get 16f7e90d-382a-4e78-b577-1b01eb5f8297 --plane admin --output json"
2895
+ )
2896
+ ).action(async (ref, opts) => {
2897
+ try {
2898
+ const fmt = opts.output;
2899
+ if (fmt === "pretty") renderAiGatewayHeader();
2900
+ const plane = parsePlane(opts.plane);
2901
+ const service = await createService();
2902
+ const workspace2 = await service.getWorkspace(
2903
+ ref,
2904
+ plane !== void 0 ? { plane } : void 0
2905
+ );
2906
+ renderWorkspaceDetail(workspace2, fmt);
2907
+ } catch (err) {
2908
+ failWithGrantHint(err);
2909
+ }
2910
+ });
2911
+ workspace.command("create").description("Create a workspace (admin plane)").requiredOption("--name <name>", "Display name").requiredOption(
2912
+ "--scope-name <scope>",
2913
+ "SCM role scope granting data-plane access (e.g. ws_production_bx7qw0) \u2014 not derived from --name"
2914
+ ).option("--description <text>", "Workspace description").option("--icon <icon>", "Workspace icon").option("--metadata <json>", "Sugar for defaults.metadata (flat string map)").option("--defaults <json>", "Workspace defaults object").option("--users <ids>", "Comma-separated user ids to seed the workspace with").option("--usage-limits <json>", "Usage-limit policies \u2014 a JSON ARRAY of policy objects").option("--rate-limits <json>", "Rate-limit policies \u2014 a JSON ARRAY of policy objects").option("--output <format>", "Output format: pretty, json, yaml", "pretty").addHelpText(
2915
+ "after",
2916
+ examples(
2917
+ "airs aigateway workspace create --name Production --scope-name ws_production_bx7qw0",
2918
+ `airs aigateway workspace create --name Production --scope-name ws_production_bx7qw0 --metadata '{"env":"production"}' --rate-limits '[{"type":"requests","unit":"rpm","value":100}]'`
2919
+ )
2920
+ ).action(async (opts) => {
2921
+ try {
2922
+ const fmt = opts.output;
2923
+ if (fmt === "pretty") renderAiGatewayHeader();
2924
+ if (scopeNameLooksUnrelated(opts.name, opts.scopeName)) {
2925
+ ui.warn(
2926
+ `--scope-name '${opts.scopeName}' shares no token with --name '${opts.name}'. A workspace created with a scope nobody holds will not appear in data-plane lists.`
2927
+ );
2928
+ }
2929
+ const service = await createService();
2930
+ const workspace2 = await service.createWorkspace({
2931
+ ...buildWorkspaceWriteRequest(opts),
2932
+ name: opts.name,
2933
+ scopeName: opts.scopeName
2934
+ });
2935
+ ui.success(`Workspace created: ${workspace2.id}`);
2936
+ renderWorkspaceDetail(workspace2, fmt);
2937
+ } catch (err) {
2938
+ failWithGrantHint(err);
2939
+ }
2940
+ });
2941
+ workspace.command("update <ref>").description("Update a workspace (admin plane, partial patch)").option("--name <name>", "New display name").option("--description <text>", "New description").option("--icon <icon>", "New icon").option("--metadata <json>", "Sugar for defaults.metadata (flat string map)").option("--defaults <json>", "Workspace defaults object").option("--usage-limits <json>", "Usage-limit policies \u2014 a JSON ARRAY of policy objects").option("--rate-limits <json>", "Rate-limit policies \u2014 a JSON ARRAY of policy objects").option("--output <format>", "Output format: pretty, json, yaml", "pretty").addHelpText(
2942
+ "after",
2943
+ examples(
2944
+ `airs aigateway workspace update ws-produc-985697 --description 'Production workloads, us-east'`
2945
+ )
2946
+ ).action(async (ref, opts) => {
2947
+ try {
2948
+ const fmt = opts.output;
2949
+ if (fmt === "pretty") renderAiGatewayHeader();
2950
+ const request = buildWorkspaceWriteRequest(opts);
2951
+ if (Object.keys(request).length === 0) {
2952
+ usageError(
2953
+ "Specify at least one of --name --description --icon --metadata --defaults --usage-limits --rate-limits"
2954
+ );
2955
+ }
2956
+ const service = await createService();
2957
+ const workspace2 = await service.updateWorkspace(ref, request);
2958
+ ui.success(`Workspace updated: ${workspace2.id}`);
2959
+ renderWorkspaceDetail(workspace2, fmt);
2960
+ } catch (err) {
2961
+ failWithGrantHint(err);
2962
+ }
2963
+ });
2964
+ workspace.command("delete <ref>").description("Archive a workspace (soft delete \u2014 there is no hard delete)").option("--force", "Skip confirmation prompt").addHelpText("after", examples("airs aigateway workspace delete ws-produc-985697 --force")).action(async (ref, opts) => {
2965
+ try {
2966
+ renderAiGatewayHeader();
2967
+ await confirmOrAbort(
2968
+ `Archive workspace ${ref}? (soft delete \u2014 the row remains under --status archived)`,
2969
+ Boolean(opts.force),
2970
+ { action: `archive workspace ${ref}` }
2971
+ );
2972
+ const service = await createService();
2973
+ await service.deleteWorkspace(ref);
2974
+ ui.success(`Workspace archived: ${ref}`);
2975
+ ui.status(
2976
+ "This is a soft delete \u2014 the workspace remains visible via `workspace list --plane admin --status archived`. A `get` on it now answers 404; that is expected."
2977
+ );
2978
+ } catch (err) {
2979
+ failWithGrantHint(err);
2980
+ }
2981
+ });
2982
+ const telemetry = aigateway.command("telemetry").description("AI Gateway runtime telemetry (data plane)");
2983
+ telemetry.command("cost").description(
2984
+ "Total and per-day spend for a workspace (API reports cents; pretty output shows dollars)"
2985
+ ).requiredOption("--workspace <slug>", "Workspace slug (not UUID), e.g. ws-main-a-349e0e").option("--days <n>", "Rolling window in days, counted back from now", "7").option("--output <format>", "Output format: pretty, json, yaml", "pretty").addHelpText(
2986
+ "after",
2987
+ examples(
2988
+ "airs aigateway telemetry cost --workspace ws-main-a-349e0e",
2989
+ "airs aigateway telemetry cost --workspace ws-main-a-349e0e --days 30 --output json"
2990
+ )
2991
+ ).action(async (opts) => {
2992
+ try {
2993
+ const fmt = opts.output;
2994
+ if (fmt === "pretty") renderAiGatewayHeader();
2995
+ const days = Number.parseInt(opts.days, 10);
2996
+ if (!Number.isFinite(days) || days <= 0) {
2997
+ usageError(`Invalid --days '${opts.days}'. Expected a positive integer`);
2998
+ }
2999
+ const service = await createService();
3000
+ const report = await service.getTelemetryCost({ workspaceSlug: opts.workspace, days });
3001
+ renderCostReport(report, fmt);
3002
+ } catch (err) {
3003
+ failWithGrantHint(err);
3004
+ }
3005
+ });
3006
+ }
3007
+
3008
+ // src/cli/commands/completion.ts
3009
+ var COMPLETION_SHELLS = ["bash", "zsh", "fish"];
3010
+ function collectCompletionNodes(root, path3 = []) {
3011
+ const words = [];
3012
+ for (const sub of root.commands) {
3013
+ words.push(sub.name(), ...sub.aliases());
3014
+ }
3015
+ for (const opt of root.options) {
3016
+ if (!opt.hidden && opt.long) words.push(opt.long);
3017
+ }
3018
+ words.push("--help");
3019
+ const node = { path: path3.join(" "), words: [...new Set(words)] };
3020
+ const children = root.commands.flatMap(
3021
+ (sub) => collectCompletionNodes(sub, [...path3, sub.name()])
2387
3022
  );
2388
3023
  return [node, ...children];
2389
3024
  }
@@ -2653,39 +3288,6 @@ function registerConfigCommand(program) {
2653
3288
  import { randomUUID } from "crypto";
2654
3289
  import { readFile as readFile2 } from "fs/promises";
2655
3290
  import { init, Scanner } from "@cdot65/prisma-airs-sdk";
2656
-
2657
- // src/config/client-options.ts
2658
- function runtimeInitOptions(config) {
2659
- return {
2660
- apiKey: config.airsApiKey,
2661
- apiToken: config.airsApiToken,
2662
- apiEndpoint: config.airsApiEndpoint,
2663
- numRetries: config.airsNumRetries
2664
- };
2665
- }
2666
- function redTeamClientOptions(config) {
2667
- return {
2668
- clientId: config.mgmtClientId,
2669
- clientSecret: config.mgmtClientSecret,
2670
- tsgId: config.mgmtTsgId,
2671
- dataEndpoint: config.redTeamDataEndpoint,
2672
- mgmtEndpoint: config.redTeamMgmtEndpoint,
2673
- tokenEndpoint: config.redTeamTokenEndpoint ?? config.mgmtTokenEndpoint,
2674
- networkBrokerEndpoint: config.redTeamNetworkBrokerEndpoint
2675
- };
2676
- }
2677
- function modelSecurityClientOptions(config) {
2678
- return {
2679
- clientId: config.mgmtClientId,
2680
- clientSecret: config.mgmtClientSecret,
2681
- tsgId: config.mgmtTsgId,
2682
- dataEndpoint: config.modelSecDataEndpoint,
2683
- mgmtEndpoint: config.modelSecMgmtEndpoint,
2684
- tokenEndpoint: config.modelSecTokenEndpoint ?? config.mgmtTokenEndpoint
2685
- };
2686
- }
2687
-
2688
- // src/cli/commands/doctor.ts
2689
3291
  var DOCTOR_TIMEOUT_MS = 5e3;
2690
3292
  var MIN_NODE_MAJOR = 20;
2691
3293
  function checkNodeVersion(version = process.version) {
@@ -2883,6 +3485,50 @@ async function checkManagementAuth(probe, hasCreds, timeoutMs = DOCTOR_TIMEOUT_M
2883
3485
  };
2884
3486
  }
2885
3487
  }
3488
+ async function checkAiGatewayApi(probe, hasCreds, timeoutMs = DOCTOR_TIMEOUT_MS) {
3489
+ const name = "AI Gateway API";
3490
+ if (!hasCreds) {
3491
+ return {
3492
+ name,
3493
+ status: "warn",
3494
+ detail: "skipped \u2014 management credentials not configured",
3495
+ hint: "Set PANW_MGMT_CLIENT_ID, PANW_MGMT_CLIENT_SECRET, PANW_MGMT_TSG_ID"
3496
+ };
3497
+ }
3498
+ try {
3499
+ const result = await withTimeout(probe(), timeoutMs);
3500
+ if (result === TIMED_OUT) {
3501
+ return {
3502
+ name,
3503
+ status: "fail",
3504
+ detail: `timed out after ${timeoutMs}ms \u2014 network unreachable or endpoint not responding`,
3505
+ hint: "Check network connectivity and PANW_AI_GW_DATA_ENDPOINT"
3506
+ };
3507
+ }
3508
+ return {
3509
+ name,
3510
+ status: "pass",
3511
+ detail: `endpoint reachable (${result} workspace${result === 1 ? "" : "s"} in scope)`
3512
+ };
3513
+ } catch (err) {
3514
+ const status = httpStatus(err);
3515
+ const message = errMessage(err);
3516
+ if (status === 403) {
3517
+ return {
3518
+ name,
3519
+ status: "warn",
3520
+ detail: `endpoint reachable, but access denied (HTTP 403): ${message}`,
3521
+ hint: aiGatewayGrantHint(err)
3522
+ };
3523
+ }
3524
+ return {
3525
+ name,
3526
+ status: "fail",
3527
+ detail: status !== void 0 ? `AI Gateway API error (HTTP ${status}): ${message}` : `network unreachable: ${message}`,
3528
+ hint: "Verify credentials and PANW_AI_GW_DATA_ENDPOINT"
3529
+ };
3530
+ }
3531
+ }
2886
3532
  async function defaultScannerProbe() {
2887
3533
  const config = await loadConfig();
2888
3534
  init(runtimeInitOptions(config));
@@ -2900,6 +3546,12 @@ async function defaultMgmtProbe() {
2900
3546
  const topics = await service.listTopics();
2901
3547
  return topics.length;
2902
3548
  }
3549
+ async function defaultAiGwProbe() {
3550
+ const config = await loadConfig();
3551
+ const service = new SdkAiGatewayService(aiGatewayClientOptions(config));
3552
+ const workspaces = await service.listWorkspaces();
3553
+ return workspaces.length;
3554
+ }
2903
3555
  async function runDoctor(deps = {}) {
2904
3556
  const configFilePath = deps.configFilePath ?? resolveConfigFilePath();
2905
3557
  const inspect = deps.inspect ?? (() => inspectConfig(configFilePath));
@@ -2923,7 +3575,12 @@ async function runDoctor(deps = {}) {
2923
3575
  mgmtCreds.status === "pass",
2924
3576
  timeoutMs
2925
3577
  );
2926
- return [node, configFile, scannerCreds, mgmtCreds, scannerApi, mgmtAuth];
3578
+ const aiGwApi = await checkAiGatewayApi(
3579
+ deps.aiGwProbe ?? defaultAiGwProbe,
3580
+ mgmtCreds.status === "pass",
3581
+ timeoutMs
3582
+ );
3583
+ return [node, configFile, scannerCreds, mgmtCreds, scannerApi, mgmtAuth, aiGwApi];
2927
3584
  }
2928
3585
  function hasFailure(checks) {
2929
3586
  return checks.some((c) => c.status === "fail");
@@ -3005,7 +3662,7 @@ function run(bin, args, label) {
3005
3662
  });
3006
3663
  });
3007
3664
  }
3008
- async function createService() {
3665
+ async function createService2() {
3009
3666
  const config = await loadConfig();
3010
3667
  return new SdkModelSecurityService(modelSecurityClientOptions(config));
3011
3668
  }
@@ -3016,7 +3673,7 @@ function registerModelSecurityCommand(program) {
3016
3673
  try {
3017
3674
  const fmt = opts.output;
3018
3675
  if (fmt === "pretty") renderModelSecurityHeader();
3019
- const service = await createService();
3676
+ const service = await createService2();
3020
3677
  const result = await service.listGroups({
3021
3678
  sourceTypes: opts.sourceTypes ? opts.sourceTypes.split(",").map((s) => s.trim()) : void 0,
3022
3679
  searchQuery: opts.search,
@@ -3034,7 +3691,7 @@ function registerModelSecurityCommand(program) {
3034
3691
  try {
3035
3692
  const fmt = opts.output;
3036
3693
  if (fmt === "pretty") renderModelSecurityHeader();
3037
- const service = await createService();
3694
+ const service = await createService2();
3038
3695
  const group = await service.getGroup(uuid);
3039
3696
  renderGroupDetail(group, fmt);
3040
3697
  } catch (err) {
@@ -3044,7 +3701,7 @@ function registerModelSecurityCommand(program) {
3044
3701
  groups.command("create").description("Create a security group").requiredOption("--config <path>", "JSON file with group configuration").action(async (opts) => {
3045
3702
  try {
3046
3703
  renderModelSecurityHeader();
3047
- const service = await createService();
3704
+ const service = await createService2();
3048
3705
  const config = JSON.parse(fs.readFileSync(opts.config, "utf-8"));
3049
3706
  const group = await service.createGroup({
3050
3707
  name: config.name,
@@ -3061,7 +3718,7 @@ function registerModelSecurityCommand(program) {
3061
3718
  groups.command("update <uuid>").description("Update a security group").option("--name <name>", "New name").option("--description <desc>", "New description").action(async (uuid, opts) => {
3062
3719
  try {
3063
3720
  renderModelSecurityHeader();
3064
- const service = await createService();
3721
+ const service = await createService2();
3065
3722
  const request = {};
3066
3723
  if (opts.name) request.name = opts.name;
3067
3724
  if (opts.description) request.description = opts.description;
@@ -3075,7 +3732,7 @@ function registerModelSecurityCommand(program) {
3075
3732
  groups.command("delete <uuid>").description("Delete a security group").action(async (uuid) => {
3076
3733
  try {
3077
3734
  renderModelSecurityHeader();
3078
- const service = await createService();
3735
+ const service = await createService2();
3079
3736
  const { confirmed, state } = await service.deleteGroupAndVerify(uuid);
3080
3737
  if (confirmed) {
3081
3738
  ui.success(`Group ${uuid} deleted.`);
@@ -3105,7 +3762,7 @@ function registerModelSecurityCommand(program) {
3105
3762
  if (!useUv && !hasBin("python3")) {
3106
3763
  fail(new Error("Neither uv nor python3 found on PATH. Install one first."));
3107
3764
  }
3108
- const service = await createService();
3765
+ const service = await createService2();
3109
3766
  const auth = await service.getPyPIAuth();
3110
3767
  const pkg = `model-security-client[${extras}]`;
3111
3768
  const steps = useUv ? [
@@ -3149,7 +3806,7 @@ function registerModelSecurityCommand(program) {
3149
3806
  labels.command("add <scanUuid>").description("Add labels to a scan").requiredOption("--labels <json>", "JSON array of {key, value} labels").action(async (scanUuid, opts) => {
3150
3807
  try {
3151
3808
  renderModelSecurityHeader();
3152
- const service = await createService();
3809
+ const service = await createService2();
3153
3810
  const parsed = JSON.parse(opts.labels);
3154
3811
  await service.addLabels(scanUuid, parsed);
3155
3812
  ui.success("Labels added.");
@@ -3160,7 +3817,7 @@ function registerModelSecurityCommand(program) {
3160
3817
  labels.command("set <scanUuid>").description("Replace all labels on a scan").requiredOption("--labels <json>", "JSON array of {key, value} labels").action(async (scanUuid, opts) => {
3161
3818
  try {
3162
3819
  renderModelSecurityHeader();
3163
- const service = await createService();
3820
+ const service = await createService2();
3164
3821
  const parsed = JSON.parse(opts.labels);
3165
3822
  await service.setLabels(scanUuid, parsed);
3166
3823
  ui.success("Labels set.");
@@ -3171,7 +3828,7 @@ function registerModelSecurityCommand(program) {
3171
3828
  labels.command("delete <scanUuid>").description("Delete labels from a scan by key").requiredOption("--keys <keys>", "Comma-separated label keys to delete").action(async (scanUuid, opts) => {
3172
3829
  try {
3173
3830
  renderModelSecurityHeader();
3174
- const service = await createService();
3831
+ const service = await createService2();
3175
3832
  const keys = opts.keys.split(",").map((k) => k.trim());
3176
3833
  await service.deleteLabels(scanUuid, keys);
3177
3834
  ui.success("Labels deleted.");
@@ -3182,7 +3839,7 @@ function registerModelSecurityCommand(program) {
3182
3839
  labels.command("keys").description("List available label keys").option("--limit <n>", "Max results", "20").action(async (opts) => {
3183
3840
  try {
3184
3841
  renderModelSecurityHeader();
3185
- const service = await createService();
3842
+ const service = await createService2();
3186
3843
  const result = await service.getLabelKeys({
3187
3844
  limit: Number.parseInt(opts.limit, 10)
3188
3845
  });
@@ -3194,7 +3851,7 @@ function registerModelSecurityCommand(program) {
3194
3851
  labels.command("values <key>").description("List values for a label key").option("--limit <n>", "Max results", "20").action(async (key, opts) => {
3195
3852
  try {
3196
3853
  renderModelSecurityHeader();
3197
- const service = await createService();
3854
+ const service = await createService2();
3198
3855
  const result = await service.getLabelValues(key, {
3199
3856
  limit: Number.parseInt(opts.limit, 10)
3200
3857
  });
@@ -3206,7 +3863,7 @@ function registerModelSecurityCommand(program) {
3206
3863
  ms.command("pypi-auth").description("Get PyPI authentication URL for Google Artifact Registry").action(async () => {
3207
3864
  try {
3208
3865
  renderModelSecurityHeader();
3209
- const service = await createService();
3866
+ const service = await createService2();
3210
3867
  const auth = await service.getPyPIAuth();
3211
3868
  ui.section("PyPI Authentication");
3212
3869
  ui.keyValue([
@@ -3221,7 +3878,7 @@ function registerModelSecurityCommand(program) {
3221
3878
  ruleInstances.command("list <groupUuid>").description("List rule instances in a security group").option("--security-rule-uuid <uuid>", "Filter by security rule UUID").option("--state <state>", "Filter by state (DISABLED, ALLOWING, BLOCKING)").option("--limit <n>", "Max results", "20").action(async (groupUuid, opts) => {
3222
3879
  try {
3223
3880
  renderModelSecurityHeader();
3224
- const service = await createService();
3881
+ const service = await createService2();
3225
3882
  const result = await service.listRuleInstances(groupUuid, {
3226
3883
  securityRuleUuid: opts.securityRuleUuid,
3227
3884
  state: opts.state,
@@ -3235,7 +3892,7 @@ function registerModelSecurityCommand(program) {
3235
3892
  ruleInstances.command("get <groupUuid> <instanceUuid>").description("Get rule instance details").action(async (groupUuid, instanceUuid) => {
3236
3893
  try {
3237
3894
  renderModelSecurityHeader();
3238
- const service = await createService();
3895
+ const service = await createService2();
3239
3896
  const instance = await service.getRuleInstance(groupUuid, instanceUuid);
3240
3897
  renderRuleInstanceDetail(instance);
3241
3898
  } catch (err) {
@@ -3245,7 +3902,7 @@ function registerModelSecurityCommand(program) {
3245
3902
  ruleInstances.command("update <groupUuid> <instanceUuid>").description("Update a rule instance").requiredOption("--config <path>", "JSON file with rule instance updates").action(async (groupUuid, instanceUuid, opts) => {
3246
3903
  try {
3247
3904
  renderModelSecurityHeader();
3248
- const service = await createService();
3905
+ const service = await createService2();
3249
3906
  const config = JSON.parse(fs.readFileSync(opts.config, "utf-8"));
3250
3907
  const instance = await service.updateRuleInstance(groupUuid, instanceUuid, {
3251
3908
  state: config.state,
@@ -3262,7 +3919,7 @@ function registerModelSecurityCommand(program) {
3262
3919
  try {
3263
3920
  const fmt = opts.output;
3264
3921
  if (fmt === "pretty") renderModelSecurityHeader();
3265
- const service = await createService();
3922
+ const service = await createService2();
3266
3923
  const result = await service.listRules({
3267
3924
  sourceType: opts.sourceType,
3268
3925
  searchQuery: opts.search,
@@ -3276,7 +3933,7 @@ function registerModelSecurityCommand(program) {
3276
3933
  rules.command("get <uuid>").description("Get security rule details").action(async (uuid) => {
3277
3934
  try {
3278
3935
  renderModelSecurityHeader();
3279
- const service = await createService();
3936
+ const service = await createService2();
3280
3937
  const rule = await service.getRule(uuid);
3281
3938
  renderRuleDetail(rule);
3282
3939
  } catch (err) {
@@ -3295,7 +3952,7 @@ function registerModelSecurityCommand(program) {
3295
3952
  try {
3296
3953
  const fmt = opts.output;
3297
3954
  if (fmt === "pretty") renderModelSecurityHeader();
3298
- const service = await createService();
3955
+ const service = await createService2();
3299
3956
  const result = await service.listScans({
3300
3957
  evalOutcome: opts.evalOutcome,
3301
3958
  sourceType: opts.sourceType,
@@ -3311,7 +3968,7 @@ function registerModelSecurityCommand(program) {
3311
3968
  scans.command("get <uuid>").description("Get scan details").action(async (uuid) => {
3312
3969
  try {
3313
3970
  renderModelSecurityHeader();
3314
- const service = await createService();
3971
+ const service = await createService2();
3315
3972
  const scan = await service.getScan(uuid);
3316
3973
  renderMsScanDetail(scan);
3317
3974
  } catch (err) {
@@ -3321,7 +3978,7 @@ function registerModelSecurityCommand(program) {
3321
3978
  scans.command("create").description("Create a model security scan").requiredOption("--config <path>", "JSON file with scan configuration").action(async (opts) => {
3322
3979
  try {
3323
3980
  renderModelSecurityHeader();
3324
- const service = await createService();
3981
+ const service = await createService2();
3325
3982
  const config = JSON.parse(fs.readFileSync(opts.config, "utf-8"));
3326
3983
  const scan = await service.createScan(config);
3327
3984
  ui.success(`Scan created: ${scan.uuid}`);
@@ -3333,7 +3990,7 @@ function registerModelSecurityCommand(program) {
3333
3990
  scans.command("evaluations <scanUuid>").description("List rule evaluations for a scan").option("--limit <n>", "Max results", "20").action(async (scanUuid, opts) => {
3334
3991
  try {
3335
3992
  renderModelSecurityHeader();
3336
- const service = await createService();
3993
+ const service = await createService2();
3337
3994
  const result = await service.getEvaluations(scanUuid, {
3338
3995
  limit: Number.parseInt(opts.limit, 10)
3339
3996
  });
@@ -3345,7 +4002,7 @@ function registerModelSecurityCommand(program) {
3345
4002
  scans.command("evaluation <uuid>").description("Get evaluation details").action(async (uuid) => {
3346
4003
  try {
3347
4004
  renderModelSecurityHeader();
3348
- const service = await createService();
4005
+ const service = await createService2();
3349
4006
  const evaluation = await service.getEvaluation(uuid);
3350
4007
  renderEvaluationDetail(evaluation);
3351
4008
  } catch (err) {
@@ -3355,7 +4012,7 @@ function registerModelSecurityCommand(program) {
3355
4012
  scans.command("violations <scanUuid>").description("List violations for a scan").option("--limit <n>", "Max results", "20").action(async (scanUuid, opts) => {
3356
4013
  try {
3357
4014
  renderModelSecurityHeader();
3358
- const service = await createService();
4015
+ const service = await createService2();
3359
4016
  const result = await service.getViolations(scanUuid, {
3360
4017
  limit: Number.parseInt(opts.limit, 10)
3361
4018
  });
@@ -3367,7 +4024,7 @@ function registerModelSecurityCommand(program) {
3367
4024
  scans.command("violation <uuid>").description("Get violation details").action(async (uuid) => {
3368
4025
  try {
3369
4026
  renderModelSecurityHeader();
3370
- const service = await createService();
4027
+ const service = await createService2();
3371
4028
  const violation = await service.getViolation(uuid);
3372
4029
  renderViolationDetail(violation);
3373
4030
  } catch (err) {
@@ -3377,7 +4034,7 @@ function registerModelSecurityCommand(program) {
3377
4034
  scans.command("files <scanUuid>").description("List scanned files").option("--type <type>", "Filter by file type").option("--result <result>", "Filter by result").option("--limit <n>", "Max results", "20").action(async (scanUuid, opts) => {
3378
4035
  try {
3379
4036
  renderModelSecurityHeader();
3380
- const service = await createService();
4037
+ const service = await createService2();
3381
4038
  const result = await service.getFiles(scanUuid, {
3382
4039
  type: opts.type,
3383
4040
  result: opts.result,
@@ -3393,7 +4050,7 @@ function registerModelSecurityCommand(program) {
3393
4050
  try {
3394
4051
  const fmt = opts.output;
3395
4052
  if (fmt === "pretty") renderModelSecurityHeader();
3396
- const service = await createService();
4053
+ const service = await createService2();
3397
4054
  const result = await service.listModels({
3398
4055
  search: opts.search,
3399
4056
  searchQuery: opts.searchQuery,
@@ -3411,7 +4068,7 @@ function registerModelSecurityCommand(program) {
3411
4068
  try {
3412
4069
  const fmt = opts.output;
3413
4070
  if (fmt === "pretty") renderModelSecurityHeader();
3414
- const service = await createService();
4071
+ const service = await createService2();
3415
4072
  const model = await service.getModel(uuid);
3416
4073
  renderModelDetail(model, fmt);
3417
4074
  } catch (err) {
@@ -3422,7 +4079,7 @@ function registerModelSecurityCommand(program) {
3422
4079
  try {
3423
4080
  const fmt = opts.output;
3424
4081
  if (fmt === "pretty") renderModelSecurityHeader();
3425
- const service = await createService();
4082
+ const service = await createService2();
3426
4083
  const result = await service.listModelVersions(modelUuid, {
3427
4084
  sortOrder: opts.sortOrder,
3428
4085
  limit: opts.limit ? Number.parseInt(opts.limit, 10) : void 0,
@@ -3437,7 +4094,7 @@ function registerModelSecurityCommand(program) {
3437
4094
  try {
3438
4095
  const fmt = opts.output;
3439
4096
  if (fmt === "pretty") renderModelSecurityHeader();
3440
- const service = await createService();
4097
+ const service = await createService2();
3441
4098
  const version = await service.getModelVersion(uuid);
3442
4099
  renderModelVersionDetail(version, fmt);
3443
4100
  } catch (err) {
@@ -3448,7 +4105,7 @@ function registerModelSecurityCommand(program) {
3448
4105
  try {
3449
4106
  const fmt = opts.output;
3450
4107
  if (fmt === "pretty") renderModelSecurityHeader();
3451
- const service = await createService();
4108
+ const service = await createService2();
3452
4109
  const result = await service.listModelVersionFiles(modelVersionUuid, {
3453
4110
  limit: opts.limit ? Number.parseInt(opts.limit, 10) : void 0,
3454
4111
  skip: opts.offset ? Number.parseInt(opts.offset, 10) : void 0
@@ -3464,25 +4121,8 @@ function registerModelSecurityCommand(program) {
3464
4121
  import * as fs2 from "fs";
3465
4122
  import * as path from "path";
3466
4123
 
3467
- // src/cli/confirm.ts
3468
- async function confirmOrAbort(message, force, options = {}) {
3469
- if (force) return;
3470
- const interactive = options.isTTY ?? process.stdout.isTTY === true;
3471
- if (!interactive) {
3472
- usageError(
3473
- `refusing to ${options.action ?? "proceed"} without --force in non-interactive mode`
3474
- );
3475
- }
3476
- const prompt = options.promptFn ?? (await import("@inquirer/prompts")).confirm;
3477
- const confirmed = await prompt({ message, default: false });
3478
- if (!confirmed) {
3479
- ui.info("Aborted");
3480
- process.exit(0);
3481
- }
3482
- }
3483
-
3484
4124
  // src/cli/deprecated-flags.ts
3485
- import chalk9 from "chalk";
4125
+ import chalk10 from "chalk";
3486
4126
  import { Option } from "commander";
3487
4127
  var ALIASES = /* @__PURE__ */ new WeakMap();
3488
4128
  function registerDeprecatedAlias(cmd, alias) {
@@ -3497,7 +4137,7 @@ function resolveDeprecatedAliases(cmd, opts) {
3497
4137
  const oldValue = opts[alias.oldKey];
3498
4138
  if (oldValue === void 0) continue;
3499
4139
  console.error(
3500
- chalk9.yellow(
4140
+ chalk10.yellow(
3501
4141
  ` \u26A0 ${alias.oldFlag.split(" ")[0]} is deprecated and will be removed in v4 \u2014 use ${alias.canonicalFlag}`
3502
4142
  )
3503
4143
  );
@@ -3681,7 +4321,7 @@ async function restoreTargets(opts) {
3681
4321
  }
3682
4322
 
3683
4323
  // src/cli/commands/redteam.ts
3684
- async function createService2() {
4324
+ async function createService3() {
3685
4325
  const config = await loadConfig();
3686
4326
  return new SdkRedTeamService(redTeamClientOptions(config));
3687
4327
  }
@@ -3689,6 +4329,14 @@ async function createPromptSetService() {
3689
4329
  const config = await loadConfig();
3690
4330
  return new SdkPromptSetService(redTeamClientOptions(config));
3691
4331
  }
4332
+ function buildDefaultCategories(categories) {
4333
+ return Object.fromEntries(
4334
+ categories.map((category) => [
4335
+ category.id,
4336
+ category.subCategories.map((subCategory) => subCategory.id).filter((id) => id !== "MULTI_TURN")
4337
+ ])
4338
+ );
4339
+ }
3692
4340
  function parseAttackGoals(input) {
3693
4341
  const trimmed = input.trim();
3694
4342
  const raw = trimmed.startsWith("[") ? trimmed : fs2.readFileSync(trimmed, "utf-8");
@@ -3721,8 +4369,11 @@ var VALID_TARGET_PROVIDERS = [
3721
4369
  "DATABRICKS",
3722
4370
  "BEDROCK",
3723
4371
  "REST",
3724
- "STREAMING"
4372
+ "STREAMING",
4373
+ "WEBSOCKET",
4374
+ "CUSTOM_TARGET_ADAPTER"
3725
4375
  ];
4376
+ var REST_PROVIDERS = /* @__PURE__ */ new Set(["REST", "STREAMING", "WEBSOCKET", "HUGGING_FACE"]);
3726
4377
  function buildTargetScaffold(provider, templates) {
3727
4378
  const key = provider.toUpperCase();
3728
4379
  if (!VALID_TARGET_PROVIDERS.includes(key)) {
@@ -3730,21 +4381,92 @@ function buildTargetScaffold(provider, templates) {
3730
4381
  `Unknown provider "${provider}". Valid providers: ${VALID_TARGET_PROVIDERS.join(", ")}`
3731
4382
  );
3732
4383
  }
4384
+ if (key === "CUSTOM_TARGET_ADAPTER") {
4385
+ return {
4386
+ name: "",
4387
+ target_type: "AGENT",
4388
+ connection_type: "CUSTOM_TARGET_ADAPTER",
4389
+ api_endpoint_type: "NETWORK_BROKER",
4390
+ network_broker_channel_uuid: "<channel-uuid>",
4391
+ adapter_uuid: "<adapter-uuid>",
4392
+ // adapter_variable_overrides is an ARRAY of {key, value, type} objects.
4393
+ adapter_variable_overrides: [],
4394
+ target_background: { use_case: "" },
4395
+ additional_context: {}
4396
+ };
4397
+ }
4398
+ if (REST_PROVIDERS.has(key)) {
4399
+ const tpl = templates[key] ?? {};
4400
+ return {
4401
+ name: "",
4402
+ target_type: "APPLICATION",
4403
+ connection_type: "CUSTOM",
4404
+ api_endpoint_type: "PUBLIC",
4405
+ response_mode: key === "STREAMING" ? "STREAMING" : key === "WEBSOCKET" ? "WEBSOCKET" : "REST",
4406
+ auth_type: "HEADERS",
4407
+ auth_config: {
4408
+ auth_header: { Authorization: "Bearer <token>" }
4409
+ },
4410
+ connection_params: {
4411
+ api_endpoint: tpl.url ?? "",
4412
+ request_headers: { "Content-Type": "application/json" },
4413
+ request_json: tpl.request_json ?? { messages: [{ role: "user", content: "{INPUT}" }] },
4414
+ response_json: tpl.response_json ?? { choices: [{ message: { content: "{RESPONSE}" } }] },
4415
+ response_key: "choices.0.message.content"
4416
+ },
4417
+ target_background: {},
4418
+ additional_context: {}
4419
+ };
4420
+ }
3733
4421
  return {
3734
4422
  name: "",
3735
4423
  target_type: "APPLICATION",
3736
- connection_params: templates[key] ?? {},
4424
+ connection_type: key,
4425
+ api_endpoint_type: "PUBLIC",
4426
+ response_mode: "REST",
4427
+ auth_type: "HEADERS",
4428
+ auth_config: {
4429
+ auth_header: { Authorization: "Bearer <token>" }
4430
+ },
4431
+ connection_params: {
4432
+ target_connection_config: templates[key] ?? {}
4433
+ },
3737
4434
  target_background: {},
3738
- additional_context: {},
3739
- target_metadata: {}
4435
+ additional_context: {}
3740
4436
  };
3741
4437
  }
4438
+ function resolveScriptB64(opts) {
4439
+ if (opts.scriptFile !== void 0 && opts.scriptB64 !== void 0) {
4440
+ throw new Error("--script-file and --script-b64 are mutually exclusive");
4441
+ }
4442
+ if (opts.scriptB64 !== void 0) return opts.scriptB64;
4443
+ if (opts.scriptFile !== void 0) {
4444
+ return Buffer.from(fs2.readFileSync(opts.scriptFile, "utf-8")).toString("base64");
4445
+ }
4446
+ throw new Error("one of --script-file or --script-b64 is required");
4447
+ }
4448
+ function parseAdapterVariables(input) {
4449
+ let parsed;
4450
+ try {
4451
+ parsed = JSON.parse(input);
4452
+ } catch (err) {
4453
+ throw new Error(`--variables: invalid JSON (${err instanceof Error ? err.message : err})`);
4454
+ }
4455
+ if (!Array.isArray(parsed) || !parsed.every(
4456
+ (v) => v !== null && typeof v === "object" && typeof v.key === "string" && (v.type === "VAR" || v.type === "SECRET")
4457
+ )) {
4458
+ throw new Error(
4459
+ '--variables: expected a JSON array of { "key": string, "value"?: string|null, "type": "VAR"|"SECRET" }'
4460
+ );
4461
+ }
4462
+ return parsed;
4463
+ }
3742
4464
  function registerRedteamCommand(program) {
3743
4465
  const redteam = program.command("redteam").description("AI Red Team scan operations");
3744
4466
  redteam.command("abort <jobId>").description("Abort a running scan").action(async (jobId) => {
3745
4467
  try {
3746
4468
  renderRedteamHeader();
3747
- const service = await createService2();
4469
+ const service = await createService3();
3748
4470
  await service.abortScan(jobId);
3749
4471
  ui.success(`Scan ${jobId} aborted.`);
3750
4472
  } catch (err) {
@@ -3754,7 +4476,7 @@ function registerRedteamCommand(program) {
3754
4476
  redteam.command("categories").description("List available attack categories").action(async () => {
3755
4477
  try {
3756
4478
  renderRedteamHeader();
3757
- const service = await createService2();
4479
+ const service = await createService3();
3758
4480
  const categories = await service.getCategories();
3759
4481
  renderCategories(categories);
3760
4482
  } catch (err) {
@@ -3765,7 +4487,7 @@ function registerRedteamCommand(program) {
3765
4487
  eula.command("status").description("Check EULA acceptance status").action(async () => {
3766
4488
  try {
3767
4489
  renderRedteamHeader();
3768
- const service = await createService2();
4490
+ const service = await createService3();
3769
4491
  const status = await service.getEulaStatus();
3770
4492
  renderEulaStatus(status);
3771
4493
  } catch (err) {
@@ -3775,7 +4497,7 @@ function registerRedteamCommand(program) {
3775
4497
  eula.command("content").description("Display EULA content").action(async () => {
3776
4498
  try {
3777
4499
  renderRedteamHeader();
3778
- const service = await createService2();
4500
+ const service = await createService3();
3779
4501
  const content = await service.getEulaContent();
3780
4502
  renderEulaContent(content);
3781
4503
  } catch (err) {
@@ -3793,7 +4515,7 @@ function registerRedteamCommand(program) {
3793
4515
  resolveDeprecatedAliases(eulaAccept, opts);
3794
4516
  try {
3795
4517
  renderRedteamHeader();
3796
- const service = await createService2();
4518
+ const service = await createService3();
3797
4519
  const content = await service.getEulaContent();
3798
4520
  if (!opts.force) {
3799
4521
  renderEulaContent(content);
@@ -3811,7 +4533,7 @@ function registerRedteamCommand(program) {
3811
4533
  instances.command("create").description("Create an instance").requiredOption("--tsg-id <id>", "TSG ID").requiredOption("--tenant-id <id>", "Tenant ID").requiredOption("--app-id <id>", "App ID").requiredOption("--region <region>", "Region").action(async (opts) => {
3812
4534
  try {
3813
4535
  renderRedteamHeader();
3814
- const service = await createService2();
4536
+ const service = await createService3();
3815
4537
  const result = await service.createInstance({
3816
4538
  tsgId: opts.tsgId,
3817
4539
  tenantId: opts.tenantId,
@@ -3827,7 +4549,7 @@ function registerRedteamCommand(program) {
3827
4549
  try {
3828
4550
  const fmt = opts.output;
3829
4551
  if (fmt === "pretty") renderRedteamHeader();
3830
- const service = await createService2();
4552
+ const service = await createService3();
3831
4553
  const result = await service.getInstance(tenantId);
3832
4554
  renderInstanceDetail(result, fmt);
3833
4555
  } catch (err) {
@@ -3837,7 +4559,7 @@ function registerRedteamCommand(program) {
3837
4559
  instances.command("update <tenantId>").description("Update an instance").requiredOption("--tsg-id <id>", "TSG ID").requiredOption("--app-id <id>", "App ID").requiredOption("--region <region>", "Region").action(async (tenantId, opts) => {
3838
4560
  try {
3839
4561
  renderRedteamHeader();
3840
- const service = await createService2();
4562
+ const service = await createService3();
3841
4563
  const result = await service.updateInstance(tenantId, {
3842
4564
  tsgId: opts.tsgId,
3843
4565
  tenantId,
@@ -3852,7 +4574,7 @@ function registerRedteamCommand(program) {
3852
4574
  instances.command("delete <tenantId>").description("Delete an instance").action(async (tenantId) => {
3853
4575
  try {
3854
4576
  renderRedteamHeader();
3855
- const service = await createService2();
4577
+ const service = await createService3();
3856
4578
  const result = await service.deleteInstance(tenantId);
3857
4579
  renderInstanceResponse(result);
3858
4580
  ui.success(`Instance ${tenantId} deleted.`);
@@ -3864,7 +4586,7 @@ function registerRedteamCommand(program) {
3864
4586
  devices.command("create <tenantId>").description("Create devices for an instance").requiredOption("--config <path>", "JSON file with device request").action(async (tenantId, opts) => {
3865
4587
  try {
3866
4588
  renderRedteamHeader();
3867
- const service = await createService2();
4589
+ const service = await createService3();
3868
4590
  const config = JSON.parse(fs2.readFileSync(opts.config, "utf-8"));
3869
4591
  const result = await service.createDevices(tenantId, config);
3870
4592
  ui.success("Devices created:");
@@ -3876,7 +4598,7 @@ function registerRedteamCommand(program) {
3876
4598
  devices.command("update <tenantId>").description("Update devices for an instance (PATCH)").requiredOption("--config <path>", "JSON file with device request").action(async (tenantId, opts) => {
3877
4599
  try {
3878
4600
  renderRedteamHeader();
3879
- const service = await createService2();
4601
+ const service = await createService3();
3880
4602
  const config = JSON.parse(fs2.readFileSync(opts.config, "utf-8"));
3881
4603
  const result = await service.updateDevices(tenantId, config);
3882
4604
  ui.success("Devices updated:");
@@ -3888,7 +4610,7 @@ function registerRedteamCommand(program) {
3888
4610
  devices.command("delete <tenantId>").description("Delete devices by serial numbers").requiredOption("--serial-numbers <list>", "Comma-separated serial numbers").action(async (tenantId, opts) => {
3889
4611
  try {
3890
4612
  renderRedteamHeader();
3891
- const service = await createService2();
4613
+ const service = await createService3();
3892
4614
  const result = await service.deleteDevices(tenantId, opts.serialNumbers);
3893
4615
  ui.success("Devices deleted:");
3894
4616
  console.log(JSON.stringify(result, null, 2));
@@ -3900,7 +4622,7 @@ function registerRedteamCommand(program) {
3900
4622
  try {
3901
4623
  const fmt = opts.output;
3902
4624
  if (fmt === "pretty") renderRedteamHeader();
3903
- const service = await createService2();
4625
+ const service = await createService3();
3904
4626
  const creds = await service.getRegistryCredentials();
3905
4627
  renderRegistryCredentials(creds, fmt);
3906
4628
  } catch (err) {
@@ -3911,7 +4633,7 @@ function registerRedteamCommand(program) {
3911
4633
  try {
3912
4634
  const fmt = opts.output;
3913
4635
  if (fmt === "pretty") renderRedteamHeader();
3914
- const service = await createService2();
4636
+ const service = await createService3();
3915
4637
  const scans = await service.listScans({
3916
4638
  status: opts.status,
3917
4639
  jobType: opts.type,
@@ -4124,7 +4846,7 @@ function registerRedteamCommand(program) {
4124
4846
  redteam.command("report <jobId>").description("View scan report").option("--attacks", "Include attack list", false).option("--severity <level>", "Filter attacks by severity").option("--limit <n>", "Max attacks to show", "20").action(async (jobId, opts) => {
4125
4847
  try {
4126
4848
  renderRedteamHeader();
4127
- const service = await createService2();
4849
+ const service = await createService3();
4128
4850
  const job = await service.getScan(jobId);
4129
4851
  renderScanStatus(job);
4130
4852
  if (job.jobType === "CUSTOM") {
@@ -4184,7 +4906,18 @@ function registerRedteamCommand(program) {
4184
4906
  const customPromptSets = opts.promptSets ? opts.promptSets.split(",").map((s) => s.trim()) : void 0;
4185
4907
  try {
4186
4908
  renderRedteamHeader();
4187
- const service = await createService2();
4909
+ const service = await createService3();
4910
+ if (opts.type === "STATIC" && !categories) {
4911
+ const defaultCategories = buildDefaultCategories(await service.getCategories());
4912
+ const categoryCount = Object.values(defaultCategories).reduce(
4913
+ (total, subCategories) => total + subCategories.length,
4914
+ 0
4915
+ );
4916
+ categories = defaultCategories;
4917
+ ui.status(
4918
+ `No --categories given \u2014 defaulting to all ${categoryCount} categories (MULTI_TURN excluded). Pass --categories to narrow the scan.`
4919
+ );
4920
+ }
4188
4921
  ui.status(`Creating ${opts.type} scan "${opts.name}"...`);
4189
4922
  const job = await service.createScan({
4190
4923
  name: opts.name,
@@ -4218,7 +4951,7 @@ function registerRedteamCommand(program) {
4218
4951
  redteam.command("status <jobId>").description("Check scan status").action(async (jobId) => {
4219
4952
  try {
4220
4953
  renderRedteamHeader();
4221
- const service = await createService2();
4954
+ const service = await createService3();
4222
4955
  const job = await service.getScan(jobId);
4223
4956
  renderScanStatus(job);
4224
4957
  } catch (err) {
@@ -4237,7 +4970,7 @@ function registerRedteamCommand(program) {
4237
4970
  try {
4238
4971
  const fmt = opts.output;
4239
4972
  if (fmt === "pretty") renderRedteamHeader();
4240
- const service = await createService2();
4973
+ const service = await createService3();
4241
4974
  const list = await service.listTargets();
4242
4975
  renderTargetList(sliceClientSide(list, opts), fmt);
4243
4976
  } catch (err) {
@@ -4248,7 +4981,7 @@ function registerRedteamCommand(program) {
4248
4981
  try {
4249
4982
  const fmt = opts.output;
4250
4983
  if (fmt === "pretty") renderRedteamHeader();
4251
- const service = await createService2();
4984
+ const service = await createService3();
4252
4985
  const target = await service.getTarget(uuid);
4253
4986
  renderTargetDetail(target, fmt);
4254
4987
  } catch (err) {
@@ -4258,7 +4991,7 @@ function registerRedteamCommand(program) {
4258
4991
  targets.command("create").description("Create a new red team target").requiredOption("--config <path>", "JSON file with target configuration").option("--validate", "Validate target connection before saving").action(async (opts) => {
4259
4992
  try {
4260
4993
  renderRedteamHeader();
4261
- const service = await createService2();
4994
+ const service = await createService3();
4262
4995
  const config = JSON.parse(fs2.readFileSync(opts.config, "utf-8"));
4263
4996
  const target = await service.createTarget(
4264
4997
  config,
@@ -4273,7 +5006,7 @@ function registerRedteamCommand(program) {
4273
5006
  targets.command("update <uuid>").description("Update a red team target").requiredOption("--config <path>", "JSON file with target updates").option("--validate", "Validate target connection before saving").action(async (uuid, opts) => {
4274
5007
  try {
4275
5008
  renderRedteamHeader();
4276
- const service = await createService2();
5009
+ const service = await createService3();
4277
5010
  const config = JSON.parse(fs2.readFileSync(opts.config, "utf-8"));
4278
5011
  const target = await service.updateTarget(
4279
5012
  uuid,
@@ -4292,7 +5025,7 @@ function registerRedteamCommand(program) {
4292
5025
  action: `delete target ${uuid}`
4293
5026
  });
4294
5027
  renderRedteamHeader();
4295
- const service = await createService2();
5028
+ const service = await createService3();
4296
5029
  await service.deleteTarget(uuid);
4297
5030
  ui.success(`Target ${uuid} deleted.`);
4298
5031
  } catch (err) {
@@ -4302,7 +5035,7 @@ function registerRedteamCommand(program) {
4302
5035
  targets.command("probe").description("Test target connection without saving").requiredOption("--config <path>", "JSON file with connection params").action(async (opts) => {
4303
5036
  try {
4304
5037
  renderRedteamHeader();
4305
- const service = await createService2();
5038
+ const service = await createService3();
4306
5039
  const config = JSON.parse(fs2.readFileSync(opts.config, "utf-8"));
4307
5040
  const result = await service.probeTarget(config);
4308
5041
  ui.dim("Probe result:");
@@ -4314,7 +5047,7 @@ function registerRedteamCommand(program) {
4314
5047
  targets.command("profile <uuid>").description("View target profile").action(async (uuid) => {
4315
5048
  try {
4316
5049
  renderRedteamHeader();
4317
- const service = await createService2();
5050
+ const service = await createService3();
4318
5051
  const profile = await service.getTargetProfile(uuid);
4319
5052
  ui.dim("Target Profile:");
4320
5053
  console.log(JSON.stringify(profile, null, 2));
@@ -4325,7 +5058,7 @@ function registerRedteamCommand(program) {
4325
5058
  targets.command("update-profile <uuid>").description("Update target profile").requiredOption("--config <path>", "JSON file with profile updates").action(async (uuid, opts) => {
4326
5059
  try {
4327
5060
  renderRedteamHeader();
4328
- const service = await createService2();
5061
+ const service = await createService3();
4329
5062
  const config = JSON.parse(fs2.readFileSync(opts.config, "utf-8"));
4330
5063
  const result = await service.updateTargetProfile(uuid, config);
4331
5064
  ui.success("Profile updated:");
@@ -4337,7 +5070,7 @@ function registerRedteamCommand(program) {
4337
5070
  targets.command("validate-auth").description("Validate target auth credentials").requiredOption("--auth-type <type>", "Auth type: HEADERS, BASIC_AUTH, OAUTH2").requiredOption("--config <path>", "JSON file with auth_config").option("--target-id <uuid>", "Existing target UUID").action(async (opts) => {
4338
5071
  try {
4339
5072
  renderRedteamHeader();
4340
- const service = await createService2();
5073
+ const service = await createService3();
4341
5074
  const authConfig = JSON.parse(fs2.readFileSync(opts.config, "utf-8"));
4342
5075
  const result = await service.validateTargetAuth({
4343
5076
  authType: opts.authType,
@@ -4351,7 +5084,7 @@ function registerRedteamCommand(program) {
4351
5084
  });
4352
5085
  targets.command("metadata").description("Get target field metadata").action(async () => {
4353
5086
  try {
4354
- const service = await createService2();
5087
+ const service = await createService3();
4355
5088
  const metadata = await service.getTargetMetadata();
4356
5089
  console.log(JSON.stringify(metadata, null, 2));
4357
5090
  } catch (err) {
@@ -4383,7 +5116,7 @@ function registerRedteamCommand(program) {
4383
5116
  }
4384
5117
  try {
4385
5118
  renderRedteamHeader();
4386
- const service = await createService2();
5119
+ const service = await createService3();
4387
5120
  const templates = await service.getTargetTemplates();
4388
5121
  const scaffold = buildTargetScaffold(provider, templates);
4389
5122
  fs2.writeFileSync(outputPath, `${JSON.stringify(scaffold, null, 2)}
@@ -4402,7 +5135,7 @@ function registerRedteamCommand(program) {
4402
5135
  targets.command("templates").description("Get provider-specific target templates").action(async () => {
4403
5136
  try {
4404
5137
  renderRedteamHeader();
4405
- const service = await createService2();
5138
+ const service = await createService3();
4406
5139
  const templates = await service.getTargetTemplates();
4407
5140
  renderTargetTemplates(templates);
4408
5141
  } catch (err) {
@@ -4456,7 +5189,7 @@ function registerRedteamCommand(program) {
4456
5189
  try {
4457
5190
  const fmt = opts.output;
4458
5191
  if (fmt === "pretty") renderRedteamHeader();
4459
- const service = await createService2();
5192
+ const service = await createService3();
4460
5193
  const { logs } = await service.getTargetProfileErrorLogs(targetId, {
4461
5194
  limit: opts.limit ? parsePositiveInt(opts.limit, "--limit") : void 0,
4462
5195
  offset: opts.offset ? Number.parseInt(opts.offset, 10) : void 0,
@@ -4467,13 +5200,173 @@ function registerRedteamCommand(program) {
4467
5200
  fail(err);
4468
5201
  }
4469
5202
  });
5203
+ const adapter = redteam.command("adapter").description("Manage custom target adapters (scripted targets run via the network broker)");
5204
+ async function assertChannelOnline(service, channelUuid) {
5205
+ let status;
5206
+ try {
5207
+ status = (await service.getChannel(channelUuid)).status;
5208
+ } catch {
5209
+ return;
5210
+ }
5211
+ if (status && status !== "ONLINE") {
5212
+ fail(
5213
+ new Error(
5214
+ `network broker channel ${channelUuid} is ${status} \u2014 adapter validation requires an ONLINE channel (network broker v1.4.0+). Check 'airs redteam network-broker channels list'.`
5215
+ )
5216
+ );
5217
+ }
5218
+ }
5219
+ adapter.command("list").description("List custom target adapters").option("--limit <n>", "Max results").option("--offset <n>", "Starting offset").option("--search <text>", "Filter by search text").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (opts) => {
5220
+ try {
5221
+ const fmt = opts.output;
5222
+ if (fmt === "pretty") renderRedteamHeader();
5223
+ const service = await createService3();
5224
+ const { adapters, totalItems } = await service.listAdapters({
5225
+ limit: opts.limit ? parsePositiveInt(opts.limit, "--limit") : void 0,
5226
+ offset: opts.offset ? Number.parseInt(opts.offset, 10) : void 0,
5227
+ search: opts.search
5228
+ });
5229
+ renderAdapterList(adapters, fmt, totalItems);
5230
+ } catch (err) {
5231
+ fail(err);
5232
+ }
5233
+ });
5234
+ adapter.command("get <uuid>").description("Get a custom target adapter").option("--output <format>", "Output format: pretty, json, yaml", "pretty").action(async (uuid, opts) => {
5235
+ try {
5236
+ const fmt = opts.output;
5237
+ if (fmt === "pretty") renderRedteamHeader();
5238
+ const service = await createService3();
5239
+ renderAdapterDetail(await service.getAdapter(uuid), fmt);
5240
+ } catch (err) {
5241
+ fail(err);
5242
+ }
5243
+ });
5244
+ adapter.command("create").description("Create a custom target adapter").requiredOption("--name <name>", "Adapter name").requiredOption(
5245
+ "--prompt <text>",
5246
+ "Sample prompt used to exercise the adapter during validation (not stored)"
5247
+ ).option("--script-file <path>", "Path to the adapter script (encoded to base64 for you)").option("--script-b64 <b64>", "Adapter script, already base64-encoded").option("--description <text>", "Adapter description").option("--channel <uuid>", "Network broker channel UUID (required to activate)").option("--variables <json>", "JSON array of { key, value, type: VAR|SECRET }").option("--draft", "Save as DRAFT without running the validation script").option("--output <format>", "Output format: pretty, json, yaml", "pretty").addHelpText(
5248
+ "after",
5249
+ examples(
5250
+ `airs redteam adapter create --name my-adapter --script-file ./adapter.py --channel 550e8400-... --prompt 'Hello' --variables '[{"key":"endpoint","value":"http://agent.svc:8080","type":"VAR"}]'`,
5251
+ "airs redteam adapter create --name my-adapter --script-file ./adapter.py --prompt Hello --draft"
5252
+ )
5253
+ ).action(async (opts) => {
5254
+ try {
5255
+ const fmt = opts.output;
5256
+ if (fmt === "pretty") renderRedteamHeader();
5257
+ const scriptB64 = resolveScriptB64(opts);
5258
+ const variables = opts.variables ? parseAdapterVariables(opts.variables) : void 0;
5259
+ const service = await createService3();
5260
+ if (!opts.draft && opts.channel) await assertChannelOnline(service, opts.channel);
5261
+ const created = await service.createAdapter(
5262
+ {
5263
+ name: opts.name,
5264
+ scriptB64,
5265
+ prompt: opts.prompt,
5266
+ description: opts.description,
5267
+ networkBrokerChannelUuid: opts.channel,
5268
+ variables
5269
+ },
5270
+ opts.draft ? false : void 0
5271
+ );
5272
+ ui.success(`Adapter created: ${created.uuid}`);
5273
+ renderAdapterDetail(created, fmt);
5274
+ } catch (err) {
5275
+ fail(err);
5276
+ }
5277
+ });
5278
+ adapter.command("update <uuid>").description(
5279
+ "Update a custom target adapter (read-modify-write; variables preserved unless --variables)"
5280
+ ).requiredOption(
5281
+ "--prompt <text>",
5282
+ "Sample validation prompt \u2014 required on every update because upstream never stores it"
5283
+ ).option("--name <name>", "New adapter name").option("--script-file <path>", "New adapter script file (encoded to base64 for you)").option("--script-b64 <b64>", "New adapter script, already base64-encoded").option("--description <text>", "New description").option("--channel <uuid>", "New network broker channel UUID").option(
5284
+ "--variables <json>",
5285
+ "REPLACES the whole variable set \u2014 omitted keys are deleted upstream. Omit this flag to preserve stored variables."
5286
+ ).option("--draft", "Save as DRAFT without re-running the validation script").option("--output <format>", "Output format: pretty, json, yaml", "pretty").addHelpText(
5287
+ "after",
5288
+ examples(
5289
+ `airs redteam adapter update 550e8400-... --description 'new description' --prompt 'Hello'`
5290
+ )
5291
+ ).action(async (uuid, opts) => {
5292
+ try {
5293
+ const fmt = opts.output;
5294
+ if (fmt === "pretty") renderRedteamHeader();
5295
+ const scriptB64 = opts.scriptFile !== void 0 || opts.scriptB64 !== void 0 ? resolveScriptB64(opts) : void 0;
5296
+ const variables = opts.variables ? parseAdapterVariables(opts.variables) : void 0;
5297
+ const service = await createService3();
5298
+ if (!opts.draft && opts.channel) await assertChannelOnline(service, opts.channel);
5299
+ const updated = await service.updateAdapter(
5300
+ uuid,
5301
+ {
5302
+ prompt: opts.prompt,
5303
+ name: opts.name,
5304
+ scriptB64,
5305
+ description: opts.description,
5306
+ networkBrokerChannelUuid: opts.channel,
5307
+ variables
5308
+ },
5309
+ opts.draft ? false : void 0
5310
+ );
5311
+ ui.success(`Adapter updated: ${updated.uuid}`);
5312
+ renderAdapterDetail(updated, fmt);
5313
+ } catch (err) {
5314
+ fail(err);
5315
+ }
5316
+ });
5317
+ adapter.command("delete <uuid>").description("Delete a custom target adapter").option("--force", "Skip confirmation prompt").action(async (uuid, opts) => {
5318
+ try {
5319
+ renderRedteamHeader();
5320
+ await confirmOrAbort(`Delete adapter ${uuid}?`, Boolean(opts.force), {
5321
+ action: `delete adapter ${uuid}`
5322
+ });
5323
+ const service = await createService3();
5324
+ await service.deleteAdapter(uuid);
5325
+ ui.success(`Adapter ${uuid} deleted.`);
5326
+ } catch (err) {
5327
+ fail(err);
5328
+ }
5329
+ });
5330
+ adapter.command("validate").description("Run an adapter script end-to-end through the broker channel without saving").requiredOption("--channel <uuid>", "Network broker channel UUID (must be ONLINE)").requiredOption("--prompt <text>", "Sample prompt to send through the adapter").option("--script-file <path>", "Path to the adapter script (encoded to base64 for you)").option("--script-b64 <b64>", "Adapter script, already base64-encoded").option(
5331
+ "--variables <json>",
5332
+ "JSON array of { key, value, type } \u2014 the FULL set the script needs"
5333
+ ).option(
5334
+ "--adapter <uuid>",
5335
+ "Existing adapter: resolves redacted/null variable values from its stored secrets (and supplies its variable set when --variables is omitted)"
5336
+ ).option("--output <format>", "Output format: pretty, json, yaml", "pretty").addHelpText(
5337
+ "after",
5338
+ examples(
5339
+ `airs redteam adapter validate --script-file ./adapter.py --channel 550e8400-... --prompt 'Hello' --variables '[{"key":"endpoint","value":"http://agent.svc:8080","type":"VAR"}]'`,
5340
+ `airs redteam adapter validate --script-file ./adapter.py --channel 550e8400-... --prompt 'Hello' --adapter 660e8400-...`
5341
+ )
5342
+ ).action(async (opts) => {
5343
+ try {
5344
+ const fmt = opts.output;
5345
+ if (fmt === "pretty") renderRedteamHeader();
5346
+ const scriptB64 = resolveScriptB64(opts);
5347
+ const variables = opts.variables ? parseAdapterVariables(opts.variables) : void 0;
5348
+ const service = await createService3();
5349
+ await assertChannelOnline(service, opts.channel);
5350
+ const result = await service.validateAdapter({
5351
+ scriptB64,
5352
+ networkBrokerChannelUuid: opts.channel,
5353
+ prompt: opts.prompt,
5354
+ variables,
5355
+ adapterUuid: opts.adapter
5356
+ });
5357
+ renderAdapterValidation(result, fmt);
5358
+ if (!result.validated) process.exitCode = 1;
5359
+ } catch (err) {
5360
+ fail(err);
5361
+ }
5362
+ });
4470
5363
  const networkBroker = redteam.command("network-broker").description("Manage red team network broker channels");
4471
5364
  const channels = networkBroker.command("channels").description("Manage network broker channels");
4472
5365
  channels.command("list").description("List network broker channels").option("--limit <n>", "Max results").option("--offset <n>", "Starting offset").option("--search <text>", "Filter by search text").option("--status <status...>", "Filter by status (ONLINE, OFFLINE, DRAFT)").option("--output <format>", "Output format: pretty, table, csv, json, yaml", "pretty").action(async (opts) => {
4473
5366
  try {
4474
5367
  const fmt = opts.output;
4475
5368
  if (fmt === "pretty") renderRedteamHeader();
4476
- const service = await createService2();
5369
+ const service = await createService3();
4477
5370
  const { channels: list } = await service.listChannels({
4478
5371
  limit: opts.limit ? parsePositiveInt(opts.limit, "--limit") : void 0,
4479
5372
  offset: opts.offset ? Number.parseInt(opts.offset, 10) : void 0,
@@ -4489,7 +5382,7 @@ function registerRedteamCommand(program) {
4489
5382
  try {
4490
5383
  const fmt = opts.output;
4491
5384
  if (fmt === "pretty") renderRedteamHeader();
4492
- const service = await createService2();
5385
+ const service = await createService3();
4493
5386
  const channel = await service.getChannel(channelId);
4494
5387
  renderChannelDetail(channel, fmt);
4495
5388
  } catch (err) {
@@ -4499,7 +5392,7 @@ function registerRedteamCommand(program) {
4499
5392
  channels.command("create").description("Create a network broker channel").requiredOption("--name <name>", "Channel name").option("--description <text>", "Channel description").action(async (opts) => {
4500
5393
  try {
4501
5394
  renderRedteamHeader();
4502
- const service = await createService2();
5395
+ const service = await createService3();
4503
5396
  const channel = await service.createChannel({
4504
5397
  name: opts.name,
4505
5398
  description: opts.description
@@ -4516,7 +5409,7 @@ function registerRedteamCommand(program) {
4516
5409
  usageError("Specify --name and/or --description to update");
4517
5410
  }
4518
5411
  renderRedteamHeader();
4519
- const service = await createService2();
5412
+ const service = await createService3();
4520
5413
  const channel = await service.updateChannel(channelId, {
4521
5414
  name: opts.name,
4522
5415
  description: opts.description
@@ -4531,7 +5424,7 @@ function registerRedteamCommand(program) {
4531
5424
  try {
4532
5425
  const fmt = opts.output;
4533
5426
  if (fmt === "pretty") renderRedteamHeader();
4534
- const service = await createService2();
5427
+ const service = await createService3();
4535
5428
  const stats = await service.getChannelStats();
4536
5429
  renderChannelStats(stats, fmt);
4537
5430
  } catch (err) {
@@ -4542,7 +5435,7 @@ function registerRedteamCommand(program) {
4542
5435
  try {
4543
5436
  const fmt = opts.output;
4544
5437
  if (fmt === "pretty") renderRedteamHeader();
4545
- const service = await createService2();
5438
+ const service = await createService3();
4546
5439
  const data = await service.getLanguages(Boolean(opts.management));
4547
5440
  renderLanguages(data, fmt);
4548
5441
  } catch (err) {
@@ -4556,7 +5449,7 @@ import { randomUUID as randomUUID4 } from "crypto";
4556
5449
  import * as fs5 from "fs";
4557
5450
  import { readFile as readFile8 } from "fs/promises";
4558
5451
  import { basename as basename3, dirname as dirname2, join as join2, resolve as resolvePath } from "path";
4559
- import chalk10 from "chalk";
5452
+ import chalk11 from "chalk";
4560
5453
 
4561
5454
  // src/cli/builders/profile-builder.ts
4562
5455
  function parseList(value) {
@@ -6373,14 +7266,14 @@ function registerSampleCommand(parent) {
6373
7266
 
6374
7267
  // src/cli/commands/runtime.ts
6375
7268
  function renderScanResult(result) {
6376
- const actionColor = result.action === "block" ? chalk10.red : chalk10.green;
7269
+ const actionColor = result.action === "block" ? chalk11.red : chalk11.green;
6377
7270
  ui.header("Scan Result");
6378
7271
  ui.keyValue([
6379
7272
  ["Action", actionColor(result.action.toUpperCase())],
6380
7273
  ["Category", result.category],
6381
- ["Triggered", result.triggered ? chalk10.red("yes") : chalk10.green("no")],
6382
- ["Scan ID", chalk10.dim(result.scanId)],
6383
- ["Report ID", chalk10.dim(result.reportId)]
7274
+ ["Triggered", result.triggered ? chalk11.red("yes") : chalk11.green("no")],
7275
+ ["Scan ID", chalk11.dim(result.scanId)],
7276
+ ["Report ID", chalk11.dim(result.reportId)]
6384
7277
  ]);
6385
7278
  const flags = Object.entries(result.detections).filter(([, v]) => v);
6386
7279
  if (flags.length > 0) {
@@ -6649,10 +7542,10 @@ function registerRuntimeCommand(program) {
6649
7542
  ui.header("Bulk Scan Complete");
6650
7543
  ui.keyValue([
6651
7544
  ["Total", results.length],
6652
- ["Blocked", chalk10.red(String(blocked))],
6653
- ["Allowed", chalk10.green(String(allowed))],
6654
- ["Failed", chalk10.red(String(failed))],
6655
- ["Output", chalk10.cyan(outputPath)]
7545
+ ["Blocked", chalk11.red(String(blocked))],
7546
+ ["Allowed", chalk11.green(String(allowed))],
7547
+ ["Failed", chalk11.red(String(failed))],
7548
+ ["Output", chalk11.cyan(outputPath)]
6656
7549
  ]);
6657
7550
  if (failed > 0) {
6658
7551
  ui.error(`${failed} prompt(s) failed; successful results were preserved.`);
@@ -7061,10 +7954,10 @@ function registerRuntimeCommand(program) {
7061
7954
  ui.header("Resume Poll Complete");
7062
7955
  ui.keyValue([
7063
7956
  ["Total", results.length],
7064
- ["Blocked", chalk10.red(String(blocked))],
7065
- ["Allowed", chalk10.green(String(allowed))],
7066
- ["Failed", chalk10.red(String(failed))],
7067
- ["Output", chalk10.cyan(outputPath)]
7957
+ ["Blocked", chalk11.red(String(blocked))],
7958
+ ["Allowed", chalk11.green(String(allowed))],
7959
+ ["Failed", chalk11.red(String(failed))],
7960
+ ["Output", chalk11.cyan(outputPath)]
7068
7961
  ]);
7069
7962
  if (failed > 0) {
7070
7963
  ui.error(`${failed} prompt(s) failed; successful results were preserved.`);
@@ -7416,6 +8309,7 @@ function buildProgram() {
7416
8309
  registerRuntimeCommand(program);
7417
8310
  registerRedteamCommand(program);
7418
8311
  registerModelSecurityCommand(program);
8312
+ registerAiGatewayCommand(program);
7419
8313
  registerConfigCommand(program);
7420
8314
  registerDoctorCommand(program);
7421
8315
  registerCompletionCommand(program);