@cdot65/prisma-airs-cli 1.0.2 → 1.0.4

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.
@@ -30,34 +30,104 @@ function renderScanResult(result) {
30
30
  }
31
31
  console.log();
32
32
  }
33
+ /** Create a management service from config. */
34
+ async function createMgmtService() {
35
+ const config = await loadConfig();
36
+ return new SdkManagementService({
37
+ clientId: config.mgmtClientId,
38
+ clientSecret: config.mgmtClientSecret,
39
+ tsgId: config.mgmtTsgId,
40
+ tokenEndpoint: config.mgmtTokenEndpoint,
41
+ });
42
+ }
33
43
  export function registerRuntimeCommand(program) {
34
44
  const runtime = program
35
45
  .command('runtime')
36
46
  .description('Runtime prompt scanning against AIRS profiles');
37
- runtime
38
- .command('scan <prompt>')
39
- .description('Scan a single prompt against an AIRS security profile')
40
- .requiredOption('--profile <name>', 'Security profile name')
41
- .option('--response <text>', 'Response text to scan alongside the prompt')
42
- .action(async (prompt, opts) => {
47
+ // -----------------------------------------------------------------------
48
+ // runtime api-keys — API key management subcommands
49
+ // -----------------------------------------------------------------------
50
+ const apiKeys = runtime.command('api-keys').description('Manage AIRS API keys');
51
+ apiKeys
52
+ .command('list')
53
+ .description('List API keys')
54
+ .option('--limit <n>', 'Max results', '100')
55
+ .action(async (opts) => {
43
56
  try {
44
- const config = await loadConfig({});
45
- if (!config.airsApiKey) {
46
- renderError('PANW_AI_SEC_API_KEY is required');
47
- process.exit(1);
48
- }
49
- const service = new SdkRuntimeService(config.airsApiKey);
50
- console.log(chalk.bold.cyan('\n Prisma AIRS Runtime Scan'));
51
- console.log(chalk.dim(` Profile: ${opts.profile}`));
52
- console.log(chalk.dim(` Prompt: "${prompt.slice(0, 80)}${prompt.length > 80 ? '...' : ''}"`));
53
- const result = await service.scanPrompt(opts.profile, prompt, opts.response);
54
- renderScanResult(result);
57
+ renderRuntimeConfigHeader();
58
+ const service = await createMgmtService();
59
+ const result = await service.listApiKeys({
60
+ limit: Number.parseInt(opts.limit, 10),
61
+ });
62
+ renderApiKeyList(result.apiKeys);
63
+ }
64
+ catch (err) {
65
+ renderError(err instanceof Error ? err.message : String(err));
66
+ process.exit(1);
67
+ }
68
+ });
69
+ apiKeys
70
+ .command('create')
71
+ .description('Create a new API key')
72
+ .requiredOption('--config <path>', 'JSON file with API key configuration')
73
+ .action(async (opts) => {
74
+ try {
75
+ renderRuntimeConfigHeader();
76
+ const service = await createMgmtService();
77
+ const config = JSON.parse(fs.readFileSync(opts.config, 'utf-8'));
78
+ const key = await service.createApiKey(config);
79
+ console.log(` API key created: ${key.id}\n`);
80
+ renderApiKeyDetail(key);
81
+ }
82
+ catch (err) {
83
+ renderError(err instanceof Error ? err.message : String(err));
84
+ process.exit(1);
85
+ }
86
+ });
87
+ apiKeys
88
+ .command('regenerate <apiKeyId>')
89
+ .description('Regenerate an API key')
90
+ .requiredOption('--interval <n>', 'Rotation time interval')
91
+ .requiredOption('--unit <unit>', 'Rotation time unit (hours, days, months)')
92
+ .option('--updated-by <email>', 'Email of user performing regeneration')
93
+ .action(async (apiKeyId, opts) => {
94
+ try {
95
+ renderRuntimeConfigHeader();
96
+ const service = await createMgmtService();
97
+ const request = {
98
+ rotation_time_interval: Number.parseInt(opts.interval, 10),
99
+ rotation_time_unit: opts.unit,
100
+ };
101
+ if (opts.updatedBy)
102
+ request.updated_by = opts.updatedBy;
103
+ const key = await service.regenerateApiKey(apiKeyId, request);
104
+ console.log(` API key regenerated: ${key.id}\n`);
105
+ renderApiKeyDetail(key);
106
+ }
107
+ catch (err) {
108
+ renderError(err instanceof Error ? err.message : String(err));
109
+ process.exit(1);
110
+ }
111
+ });
112
+ apiKeys
113
+ .command('delete <apiKeyName>')
114
+ .description('Delete an API key')
115
+ .requiredOption('--updated-by <email>', 'Email of user performing deletion')
116
+ .action(async (apiKeyName, opts) => {
117
+ try {
118
+ renderRuntimeConfigHeader();
119
+ const service = await createMgmtService();
120
+ const result = await service.deleteApiKey(apiKeyName, opts.updatedBy);
121
+ console.log(` ${result.message}\n`);
55
122
  }
56
123
  catch (err) {
57
124
  renderError(err instanceof Error ? err.message : String(err));
58
125
  process.exit(1);
59
126
  }
60
127
  });
128
+ // -----------------------------------------------------------------------
129
+ // runtime bulk-scan — async bulk scanning
130
+ // -----------------------------------------------------------------------
61
131
  runtime
62
132
  .command('bulk-scan')
63
133
  .description('Scan multiple prompts via the async AIRS API')
@@ -117,40 +187,114 @@ export function registerRuntimeCommand(program) {
117
187
  process.exit(1);
118
188
  }
119
189
  });
120
- runtime
121
- .command('resume-poll <stateFile>')
122
- .description('Resume polling for a previously submitted bulk scan')
123
- .option('--output <file>', 'Output CSV file path')
124
- .action(async (stateFile, opts) => {
190
+ // -----------------------------------------------------------------------
191
+ // runtime customer-apps — customer app management subcommands
192
+ // -----------------------------------------------------------------------
193
+ const customerApps = runtime.command('customer-apps').description('Manage AIRS customer apps');
194
+ customerApps
195
+ .command('list')
196
+ .description('List customer apps')
197
+ .option('--limit <n>', 'Max results', '100')
198
+ .action(async (opts) => {
125
199
  try {
126
- const config = await loadConfig({});
127
- if (!config.airsApiKey) {
128
- renderError('PANW_AI_SEC_API_KEY is required');
129
- process.exit(1);
130
- }
131
- const state = await loadBulkScanState(stateFile);
132
- const service = new SdkRuntimeService(config.airsApiKey);
133
- console.log(chalk.bold.cyan('\n Prisma AIRS Resume Poll'));
134
- console.log(chalk.dim(` Profile: ${state.profile}`));
135
- console.log(chalk.dim(` Scan IDs: ${state.scanIds.length}`));
136
- console.log(chalk.dim(` Prompts: ${state.promptCount}\n`));
137
- console.log(chalk.dim(' Polling for results...'));
138
- const results = await service.pollResults(state.scanIds, undefined, {
139
- onRetry: (attempt, delayMs) => {
140
- console.log(chalk.yellow(` ⚠ Rate limited — retry ${attempt} in ${(delayMs / 1000).toFixed(0)}s...`));
141
- },
200
+ renderRuntimeConfigHeader();
201
+ const service = await createMgmtService();
202
+ const result = await service.listCustomerApps({
203
+ limit: Number.parseInt(opts.limit, 10),
142
204
  });
143
- const outputPath = opts.output ?? `${state.profile.replace(/\s+/g, '-')}-bulk-scan.csv`;
144
- const csv = SdkRuntimeService.formatResultsCsv(results);
145
- await writeFile(outputPath, csv, 'utf-8');
146
- const blocked = results.filter((r) => r.action === 'block').length;
147
- const allowed = results.filter((r) => r.action === 'allow').length;
148
- console.log(chalk.bold('\n Resume Poll Complete'));
149
- console.log(chalk.dim(' ─────────────────────────'));
150
- console.log(` Total: ${results.length}`);
151
- console.log(` Blocked: ${chalk.red(String(blocked))}`);
152
- console.log(` Allowed: ${chalk.green(String(allowed))}`);
153
- console.log(` Output: ${chalk.cyan(outputPath)}\n`);
205
+ renderCustomerAppList(result.apps);
206
+ }
207
+ catch (err) {
208
+ renderError(err instanceof Error ? err.message : String(err));
209
+ process.exit(1);
210
+ }
211
+ });
212
+ customerApps
213
+ .command('get <appName>')
214
+ .description('Get customer app details')
215
+ .action(async (appName) => {
216
+ try {
217
+ renderRuntimeConfigHeader();
218
+ const service = await createMgmtService();
219
+ const app = await service.getCustomerApp(appName);
220
+ renderCustomerAppDetail(app);
221
+ }
222
+ catch (err) {
223
+ renderError(err instanceof Error ? err.message : String(err));
224
+ process.exit(1);
225
+ }
226
+ });
227
+ customerApps
228
+ .command('update <appId>')
229
+ .description('Update a customer app')
230
+ .requiredOption('--config <path>', 'JSON file with app updates')
231
+ .action(async (appId, opts) => {
232
+ try {
233
+ renderRuntimeConfigHeader();
234
+ const service = await createMgmtService();
235
+ const config = JSON.parse(fs.readFileSync(opts.config, 'utf-8'));
236
+ const app = await service.updateCustomerApp(appId, config);
237
+ console.log(` Customer app updated: ${app.name}\n`);
238
+ renderCustomerAppDetail(app);
239
+ }
240
+ catch (err) {
241
+ renderError(err instanceof Error ? err.message : String(err));
242
+ process.exit(1);
243
+ }
244
+ });
245
+ customerApps
246
+ .command('delete <appName>')
247
+ .description('Delete a customer app')
248
+ .requiredOption('--updated-by <email>', 'Email of user performing deletion')
249
+ .action(async (appName, opts) => {
250
+ try {
251
+ renderRuntimeConfigHeader();
252
+ const service = await createMgmtService();
253
+ const app = await service.deleteCustomerApp(appName, opts.updatedBy);
254
+ console.log(` Customer app "${app.name}" deleted.\n`);
255
+ }
256
+ catch (err) {
257
+ renderError(err instanceof Error ? err.message : String(err));
258
+ process.exit(1);
259
+ }
260
+ });
261
+ // -----------------------------------------------------------------------
262
+ // runtime deployment-profiles — read-only listing
263
+ // -----------------------------------------------------------------------
264
+ const deploymentProfiles = runtime
265
+ .command('deployment-profiles')
266
+ .description('List AIRS deployment profiles');
267
+ deploymentProfiles
268
+ .command('list')
269
+ .description('List deployment profiles')
270
+ .option('--unactivated', 'Include unactivated profiles')
271
+ .action(async (opts) => {
272
+ try {
273
+ renderRuntimeConfigHeader();
274
+ const service = await createMgmtService();
275
+ const profiles = await service.listDeploymentProfiles({
276
+ unactivated: opts.unactivated,
277
+ });
278
+ renderDeploymentProfileList(profiles);
279
+ }
280
+ catch (err) {
281
+ renderError(err instanceof Error ? err.message : String(err));
282
+ process.exit(1);
283
+ }
284
+ });
285
+ // -----------------------------------------------------------------------
286
+ // runtime dlp-profiles — read-only listing
287
+ // -----------------------------------------------------------------------
288
+ const dlpProfiles = runtime.command('dlp-profiles').description('List AIRS DLP profiles');
289
+ dlpProfiles
290
+ .command('list')
291
+ .description('List DLP profiles')
292
+ .action(async () => {
293
+ try {
294
+ renderRuntimeConfigHeader();
295
+ const service = await createMgmtService();
296
+ const profiles = await service.listDlpProfiles();
297
+ renderDlpProfileList(profiles);
154
298
  }
155
299
  catch (err) {
156
300
  renderError(err instanceof Error ? err.message : String(err));
@@ -161,16 +305,6 @@ export function registerRuntimeCommand(program) {
161
305
  // runtime profiles — security profile CRUD subcommands
162
306
  // -----------------------------------------------------------------------
163
307
  const profiles = runtime.command('profiles').description('Manage AIRS security profiles');
164
- /** Create a management service from config. */
165
- async function createMgmtService() {
166
- const config = await loadConfig();
167
- return new SdkManagementService({
168
- clientId: config.mgmtClientId,
169
- clientSecret: config.mgmtClientSecret,
170
- tsgId: config.mgmtTsgId,
171
- tokenEndpoint: config.mgmtTokenEndpoint,
172
- });
173
- }
174
308
  profiles
175
309
  .command('list')
176
310
  .description('List security profiles')
@@ -260,25 +394,125 @@ export function registerRuntimeCommand(program) {
260
394
  // Register audit under profiles
261
395
  registerAuditCommand(profiles);
262
396
  // -----------------------------------------------------------------------
263
- // runtime topicscustom topic CRUD + guardrail generation subcommands
397
+ // runtime resume-pollresume polling for bulk scans
264
398
  // -----------------------------------------------------------------------
265
- const topics = runtime
266
- .command('topics')
267
- .description('Manage AIRS custom topics and guardrail generation');
268
- topics
269
- .command('list')
270
- .description('List custom topics')
271
- .option('--limit <n>', 'Max results', '100')
272
- .option('--offset <n>', 'Starting offset', '0')
273
- .action(async (opts) => {
399
+ runtime
400
+ .command('resume-poll <stateFile>')
401
+ .description('Resume polling for a previously submitted bulk scan')
402
+ .option('--output <file>', 'Output CSV file path')
403
+ .action(async (stateFile, opts) => {
274
404
  try {
275
- renderRuntimeConfigHeader();
276
- const service = await createMgmtService();
277
- const allTopics = await service.listTopics();
278
- // Client-side pagination since SDK returns all
279
- const offset = Number.parseInt(opts.offset, 10);
280
- const limit = Number.parseInt(opts.limit, 10);
281
- const page = allTopics.slice(offset, offset + limit);
405
+ const config = await loadConfig({});
406
+ if (!config.airsApiKey) {
407
+ renderError('PANW_AI_SEC_API_KEY is required');
408
+ process.exit(1);
409
+ }
410
+ const state = await loadBulkScanState(stateFile);
411
+ const service = new SdkRuntimeService(config.airsApiKey);
412
+ console.log(chalk.bold.cyan('\n Prisma AIRS Resume Poll'));
413
+ console.log(chalk.dim(` Profile: ${state.profile}`));
414
+ console.log(chalk.dim(` Scan IDs: ${state.scanIds.length}`));
415
+ console.log(chalk.dim(` Prompts: ${state.promptCount}\n`));
416
+ console.log(chalk.dim(' Polling for results...'));
417
+ const results = await service.pollResults(state.scanIds, undefined, {
418
+ onRetry: (attempt, delayMs) => {
419
+ console.log(chalk.yellow(` ⚠ Rate limited — retry ${attempt} in ${(delayMs / 1000).toFixed(0)}s...`));
420
+ },
421
+ });
422
+ const outputPath = opts.output ?? `${state.profile.replace(/\s+/g, '-')}-bulk-scan.csv`;
423
+ const csv = SdkRuntimeService.formatResultsCsv(results);
424
+ await writeFile(outputPath, csv, 'utf-8');
425
+ const blocked = results.filter((r) => r.action === 'block').length;
426
+ const allowed = results.filter((r) => r.action === 'allow').length;
427
+ console.log(chalk.bold('\n Resume Poll Complete'));
428
+ console.log(chalk.dim(' ─────────────────────────'));
429
+ console.log(` Total: ${results.length}`);
430
+ console.log(` Blocked: ${chalk.red(String(blocked))}`);
431
+ console.log(` Allowed: ${chalk.green(String(allowed))}`);
432
+ console.log(` Output: ${chalk.cyan(outputPath)}\n`);
433
+ }
434
+ catch (err) {
435
+ renderError(err instanceof Error ? err.message : String(err));
436
+ process.exit(1);
437
+ }
438
+ });
439
+ // -----------------------------------------------------------------------
440
+ // runtime scan — single prompt scanning
441
+ // -----------------------------------------------------------------------
442
+ runtime
443
+ .command('scan <prompt>')
444
+ .description('Scan a single prompt against an AIRS security profile')
445
+ .requiredOption('--profile <name>', 'Security profile name')
446
+ .option('--response <text>', 'Response text to scan alongside the prompt')
447
+ .action(async (prompt, opts) => {
448
+ try {
449
+ const config = await loadConfig({});
450
+ if (!config.airsApiKey) {
451
+ renderError('PANW_AI_SEC_API_KEY is required');
452
+ process.exit(1);
453
+ }
454
+ const service = new SdkRuntimeService(config.airsApiKey);
455
+ console.log(chalk.bold.cyan('\n Prisma AIRS Runtime Scan'));
456
+ console.log(chalk.dim(` Profile: ${opts.profile}`));
457
+ console.log(chalk.dim(` Prompt: "${prompt.slice(0, 80)}${prompt.length > 80 ? '...' : ''}"`));
458
+ const result = await service.scanPrompt(opts.profile, prompt, opts.response);
459
+ renderScanResult(result);
460
+ }
461
+ catch (err) {
462
+ renderError(err instanceof Error ? err.message : String(err));
463
+ process.exit(1);
464
+ }
465
+ });
466
+ // -----------------------------------------------------------------------
467
+ // runtime scan-logs — scan log query
468
+ // -----------------------------------------------------------------------
469
+ const scanLogs = runtime.command('scan-logs').description('Query AIRS scan logs');
470
+ scanLogs
471
+ .command('query')
472
+ .description('Query scan logs')
473
+ .requiredOption('--interval <n>', 'Time interval')
474
+ .requiredOption('--unit <unit>', 'Time unit (hours)')
475
+ .option('--filter <filter>', 'Filter: all, benign, threat', 'all')
476
+ .option('--page <n>', 'Page number', '1')
477
+ .option('--page-size <n>', 'Page size', '50')
478
+ .action(async (opts) => {
479
+ try {
480
+ renderRuntimeConfigHeader();
481
+ const service = await createMgmtService();
482
+ const result = await service.queryScanLogs({
483
+ timeInterval: Number.parseInt(opts.interval, 10),
484
+ timeUnit: opts.unit,
485
+ pageNumber: Number.parseInt(opts.page, 10),
486
+ pageSize: Number.parseInt(opts.pageSize, 10),
487
+ filter: opts.filter,
488
+ });
489
+ renderScanLogList(result.results, result.pageToken);
490
+ }
491
+ catch (err) {
492
+ renderError(err instanceof Error ? err.message : String(err));
493
+ process.exit(1);
494
+ }
495
+ });
496
+ // -----------------------------------------------------------------------
497
+ // runtime topics — custom topic CRUD + guardrail generation subcommands
498
+ // -----------------------------------------------------------------------
499
+ const topics = runtime
500
+ .command('topics')
501
+ .description('Manage AIRS custom topics and guardrail generation');
502
+ topics
503
+ .command('list')
504
+ .description('List custom topics')
505
+ .option('--limit <n>', 'Max results', '100')
506
+ .option('--offset <n>', 'Starting offset', '0')
507
+ .action(async (opts) => {
508
+ try {
509
+ renderRuntimeConfigHeader();
510
+ const service = await createMgmtService();
511
+ const allTopics = await service.listTopics();
512
+ // Client-side pagination since SDK returns all
513
+ const offset = Number.parseInt(opts.offset, 10);
514
+ const limit = Number.parseInt(opts.limit, 10);
515
+ const page = allTopics.slice(offset, offset + limit);
282
516
  renderTopicList(page);
283
517
  if (offset + limit < allTopics.length) {
284
518
  console.log(chalk.dim(` Showing ${page.length} of ${allTopics.length} topics\n`));
@@ -353,230 +587,5 @@ export function registerRuntimeCommand(program) {
353
587
  registerResumeCommand(topics);
354
588
  registerReportCommand(topics);
355
589
  registerListCommand(topics, 'runs');
356
- // -----------------------------------------------------------------------
357
- // runtime api-keys — API key management subcommands
358
- // -----------------------------------------------------------------------
359
- const apiKeys = runtime.command('api-keys').description('Manage AIRS API keys');
360
- apiKeys
361
- .command('list')
362
- .description('List API keys')
363
- .option('--limit <n>', 'Max results', '100')
364
- .action(async (opts) => {
365
- try {
366
- renderRuntimeConfigHeader();
367
- const service = await createMgmtService();
368
- const result = await service.listApiKeys({
369
- limit: Number.parseInt(opts.limit, 10),
370
- });
371
- renderApiKeyList(result.apiKeys);
372
- }
373
- catch (err) {
374
- renderError(err instanceof Error ? err.message : String(err));
375
- process.exit(1);
376
- }
377
- });
378
- apiKeys
379
- .command('create')
380
- .description('Create a new API key')
381
- .requiredOption('--config <path>', 'JSON file with API key configuration')
382
- .action(async (opts) => {
383
- try {
384
- renderRuntimeConfigHeader();
385
- const service = await createMgmtService();
386
- const config = JSON.parse(fs.readFileSync(opts.config, 'utf-8'));
387
- const key = await service.createApiKey(config);
388
- console.log(` API key created: ${key.id}\n`);
389
- renderApiKeyDetail(key);
390
- }
391
- catch (err) {
392
- renderError(err instanceof Error ? err.message : String(err));
393
- process.exit(1);
394
- }
395
- });
396
- apiKeys
397
- .command('regenerate <apiKeyId>')
398
- .description('Regenerate an API key')
399
- .requiredOption('--interval <n>', 'Rotation time interval')
400
- .requiredOption('--unit <unit>', 'Rotation time unit (hours, days, months)')
401
- .option('--updated-by <email>', 'Email of user performing regeneration')
402
- .action(async (apiKeyId, opts) => {
403
- try {
404
- renderRuntimeConfigHeader();
405
- const service = await createMgmtService();
406
- const request = {
407
- rotation_time_interval: Number.parseInt(opts.interval, 10),
408
- rotation_time_unit: opts.unit,
409
- };
410
- if (opts.updatedBy)
411
- request.updated_by = opts.updatedBy;
412
- const key = await service.regenerateApiKey(apiKeyId, request);
413
- console.log(` API key regenerated: ${key.id}\n`);
414
- renderApiKeyDetail(key);
415
- }
416
- catch (err) {
417
- renderError(err instanceof Error ? err.message : String(err));
418
- process.exit(1);
419
- }
420
- });
421
- apiKeys
422
- .command('delete <apiKeyName>')
423
- .description('Delete an API key')
424
- .requiredOption('--updated-by <email>', 'Email of user performing deletion')
425
- .action(async (apiKeyName, opts) => {
426
- try {
427
- renderRuntimeConfigHeader();
428
- const service = await createMgmtService();
429
- const result = await service.deleteApiKey(apiKeyName, opts.updatedBy);
430
- console.log(` ${result.message}\n`);
431
- }
432
- catch (err) {
433
- renderError(err instanceof Error ? err.message : String(err));
434
- process.exit(1);
435
- }
436
- });
437
- // -----------------------------------------------------------------------
438
- // runtime customer-apps — customer app management subcommands
439
- // -----------------------------------------------------------------------
440
- const customerApps = runtime.command('customer-apps').description('Manage AIRS customer apps');
441
- customerApps
442
- .command('list')
443
- .description('List customer apps')
444
- .option('--limit <n>', 'Max results', '100')
445
- .action(async (opts) => {
446
- try {
447
- renderRuntimeConfigHeader();
448
- const service = await createMgmtService();
449
- const result = await service.listCustomerApps({
450
- limit: Number.parseInt(opts.limit, 10),
451
- });
452
- renderCustomerAppList(result.apps);
453
- }
454
- catch (err) {
455
- renderError(err instanceof Error ? err.message : String(err));
456
- process.exit(1);
457
- }
458
- });
459
- customerApps
460
- .command('get <appName>')
461
- .description('Get customer app details')
462
- .action(async (appName) => {
463
- try {
464
- renderRuntimeConfigHeader();
465
- const service = await createMgmtService();
466
- const app = await service.getCustomerApp(appName);
467
- renderCustomerAppDetail(app);
468
- }
469
- catch (err) {
470
- renderError(err instanceof Error ? err.message : String(err));
471
- process.exit(1);
472
- }
473
- });
474
- customerApps
475
- .command('update <appId>')
476
- .description('Update a customer app')
477
- .requiredOption('--config <path>', 'JSON file with app updates')
478
- .action(async (appId, opts) => {
479
- try {
480
- renderRuntimeConfigHeader();
481
- const service = await createMgmtService();
482
- const config = JSON.parse(fs.readFileSync(opts.config, 'utf-8'));
483
- const app = await service.updateCustomerApp(appId, config);
484
- console.log(` Customer app updated: ${app.name}\n`);
485
- renderCustomerAppDetail(app);
486
- }
487
- catch (err) {
488
- renderError(err instanceof Error ? err.message : String(err));
489
- process.exit(1);
490
- }
491
- });
492
- customerApps
493
- .command('delete <appName>')
494
- .description('Delete a customer app')
495
- .requiredOption('--updated-by <email>', 'Email of user performing deletion')
496
- .action(async (appName, opts) => {
497
- try {
498
- renderRuntimeConfigHeader();
499
- const service = await createMgmtService();
500
- const app = await service.deleteCustomerApp(appName, opts.updatedBy);
501
- console.log(` Customer app "${app.name}" deleted.\n`);
502
- }
503
- catch (err) {
504
- renderError(err instanceof Error ? err.message : String(err));
505
- process.exit(1);
506
- }
507
- });
508
- // -----------------------------------------------------------------------
509
- // runtime deployment-profiles — read-only listing
510
- // -----------------------------------------------------------------------
511
- const deploymentProfiles = runtime
512
- .command('deployment-profiles')
513
- .description('List AIRS deployment profiles');
514
- deploymentProfiles
515
- .command('list')
516
- .description('List deployment profiles')
517
- .option('--unactivated', 'Include unactivated profiles')
518
- .action(async (opts) => {
519
- try {
520
- renderRuntimeConfigHeader();
521
- const service = await createMgmtService();
522
- const profiles = await service.listDeploymentProfiles({
523
- unactivated: opts.unactivated,
524
- });
525
- renderDeploymentProfileList(profiles);
526
- }
527
- catch (err) {
528
- renderError(err instanceof Error ? err.message : String(err));
529
- process.exit(1);
530
- }
531
- });
532
- // -----------------------------------------------------------------------
533
- // runtime dlp-profiles — read-only listing
534
- // -----------------------------------------------------------------------
535
- const dlpProfiles = runtime.command('dlp-profiles').description('List AIRS DLP profiles');
536
- dlpProfiles
537
- .command('list')
538
- .description('List DLP profiles')
539
- .action(async () => {
540
- try {
541
- renderRuntimeConfigHeader();
542
- const service = await createMgmtService();
543
- const profiles = await service.listDlpProfiles();
544
- renderDlpProfileList(profiles);
545
- }
546
- catch (err) {
547
- renderError(err instanceof Error ? err.message : String(err));
548
- process.exit(1);
549
- }
550
- });
551
- // -----------------------------------------------------------------------
552
- // runtime scan-logs — scan log query
553
- // -----------------------------------------------------------------------
554
- const scanLogs = runtime.command('scan-logs').description('Query AIRS scan logs');
555
- scanLogs
556
- .command('query')
557
- .description('Query scan logs')
558
- .requiredOption('--interval <n>', 'Time interval')
559
- .requiredOption('--unit <unit>', 'Time unit (hours)')
560
- .option('--filter <filter>', 'Filter: all, benign, threat', 'all')
561
- .option('--page <n>', 'Page number', '1')
562
- .option('--page-size <n>', 'Page size', '50')
563
- .action(async (opts) => {
564
- try {
565
- renderRuntimeConfigHeader();
566
- const service = await createMgmtService();
567
- const result = await service.queryScanLogs({
568
- timeInterval: Number.parseInt(opts.interval, 10),
569
- timeUnit: opts.unit,
570
- pageNumber: Number.parseInt(opts.page, 10),
571
- pageSize: Number.parseInt(opts.pageSize, 10),
572
- filter: opts.filter,
573
- });
574
- renderScanLogList(result.results, result.pageToken);
575
- }
576
- catch (err) {
577
- renderError(err instanceof Error ? err.message : String(err));
578
- process.exit(1);
579
- }
580
- });
581
590
  }
582
591
  //# sourceMappingURL=runtime.js.map