@robinmordasiewicz/f5xc-xcsh 1.0.90-2601021717 → 1.0.90-2601022257

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/completions/_xcsh CHANGED
@@ -44,7 +44,7 @@ _xcsh() {
44
44
  'data_intelligence:Data Intelligence API'
45
45
  'ddos:Volumetric attack mitigation and traffic scrubbing.'
46
46
  'dns:Zones, record types, and load balancing.'
47
- 'generative_ai:AI assistant queries and feedback collection.'
47
+ 'generative_ai:AI assistant queries and feedback'
48
48
  'login:Authentication and session management'
49
49
  'managed_kubernetes:Cluster RBAC, pod security, and container registries.'
50
50
  'marketplace:Add-on services, connectors, and TPM policies.'
@@ -113,9 +113,6 @@ _xcsh() {
113
113
  'ddos-protect:Alias for ddos'
114
114
  'dns-zone:Alias for dns'
115
115
  'zones:Alias for dns'
116
- 'ai:Alias for generative_ai'
117
- 'genai:Alias for generative_ai'
118
- 'assistant:Alias for generative_ai'
119
116
  'mk8s:Alias for managed_kubernetes'
120
117
  'appstack:Alias for managed_kubernetes'
121
118
  'k8s-mgmt:Alias for managed_kubernetes'
@@ -193,6 +190,9 @@ _xcsh() {
193
190
  (completion)
194
191
  _values 'command' 'bash:Generate bash completion script' 'zsh:Generate zsh completion script' 'fish:Generate fish completion script'
195
192
  ;;
193
+ (generative_ai)
194
+ _values 'command' 'query:Query the AI assistant' 'chat:Interactive AI chat mode' 'feedback:Submit feedback for AI responses' 'eval:RBAC testing mode commands'
195
+ ;;
196
196
  (login)
197
197
  _values 'command' 'banner:Display xcsh banner with logo' 'profile:Manage saved connection profiles' 'context:Manage default namespace context'
198
198
  ;;
@@ -6,7 +6,7 @@ _xcsh_completions() {
6
6
  local cur prev words cword
7
7
  _init_completion || return
8
8
 
9
- local commands="admin_console_and_ui api authentication bigip billing_and_usage blindfold bot_and_threat_defense cdn ce_management certificates cloud_infrastructure cloudstatus completion container_services data_and_privacy_security data_intelligence ddos dns generative_ai login managed_kubernetes marketplace network network_security nginx_one object_storage observability rate_limiting secops_and_incident_response service_mesh shape sites statistics support telemetry_and_insights tenant_and_identity threat_campaign users virtual vpm_and_node_management waf console-ui ui-assets static-components apisec api-discovery authn oidc sso f5-bigip irule ltm billing-usage quotas usage-tracking bf encrypt secrets threat-defense tpm shape-bot cache content ce-mgmt edge-management ce-lifecycle cert certs ssl tls cloud infra provider vk8s containers workloads data-privacy pii sensitive-data lma di intelligence insights dos ddos-protect dns-zone zones ai genai assistant mk8s appstack k8s-mgmt market addons extensions net routing bgp netsec nfw nginx nms nginx-plus storage s3 buckets obs monitoring synth ratelimit throttle policer secops incident-response mitigation mesh svc-mesh shape-sec safeap site deployment stats metrics logs tickets help-desk telemetry ti tenant-identity idm user-settings threats campaigns threat-intel user accounts iam lb loadbalancer vhost vpm nodes node-mgmt firewall appfw help quit exit clear history"
9
+ local commands="admin_console_and_ui api authentication bigip billing_and_usage blindfold bot_and_threat_defense cdn ce_management certificates cloud_infrastructure cloudstatus completion container_services data_and_privacy_security data_intelligence ddos dns generative_ai login managed_kubernetes marketplace network network_security nginx_one object_storage observability rate_limiting secops_and_incident_response service_mesh shape sites statistics support telemetry_and_insights tenant_and_identity threat_campaign users virtual vpm_and_node_management waf console-ui ui-assets static-components apisec api-discovery authn oidc sso f5-bigip irule ltm billing-usage quotas usage-tracking bf encrypt secrets threat-defense tpm shape-bot cache content ce-mgmt edge-management ce-lifecycle cert certs ssl tls cloud infra provider vk8s containers workloads data-privacy pii sensitive-data lma di intelligence insights dos ddos-protect dns-zone zones mk8s appstack k8s-mgmt market addons extensions net routing bgp netsec nfw nginx nms nginx-plus storage s3 buckets obs monitoring synth ratelimit throttle policer secops incident-response mitigation mesh svc-mesh shape-sec safeap site deployment stats metrics logs tickets help-desk telemetry ti tenant-identity idm user-settings threats campaigns threat-intel user accounts iam lb loadbalancer vhost vpm nodes node-mgmt firewall appfw help quit exit clear history"
10
10
  local actions="list get create delete replace apply status patch add-labels remove-labels"
11
11
  local builtins="help quit exit clear history context ctx"
12
12
  local global_flags="--help -h --version -v --no-color --output -o --namespace -ns --spec"
@@ -34,6 +34,14 @@ _xcsh_completions() {
34
34
  COMPREPLY=($(compgen -W "bash zsh fish" -- "${cur}"))
35
35
  return 0
36
36
  ;;
37
+ generative_ai)
38
+ COMPREPLY=($(compgen -W "query chat feedback eval" -- "${cur}"))
39
+ return 0
40
+ ;;
41
+ generative_ai/eval)
42
+ COMPREPLY=($(compgen -W "query feedback" -- "${cur}"))
43
+ return 0
44
+ ;;
37
45
  login)
38
46
  COMPREPLY=($(compgen -W "banner profile context" -- "${cur}"))
39
47
  return 0
@@ -40,7 +40,7 @@ complete -c xcsh -n "__fish_use_subcommand" -a "data_and_privacy_security" -d 'P
40
40
  complete -c xcsh -n "__fish_use_subcommand" -a "data_intelligence" -d 'Data Intelligence API'
41
41
  complete -c xcsh -n "__fish_use_subcommand" -a "ddos" -d 'Volumetric attack mitigation and traffic scrubbing.'
42
42
  complete -c xcsh -n "__fish_use_subcommand" -a "dns" -d 'Zones, record types, and load balancing.'
43
- complete -c xcsh -n "__fish_use_subcommand" -a "generative_ai" -d 'AI assistant queries and feedback collection.'
43
+ complete -c xcsh -n "__fish_use_subcommand" -a "generative_ai" -d 'AI assistant queries and feedback'
44
44
  complete -c xcsh -n "__fish_use_subcommand" -a "login" -d 'Authentication and session management'
45
45
  complete -c xcsh -n "__fish_use_subcommand" -a "managed_kubernetes" -d 'Cluster RBAC, pod security, and container registries.'
46
46
  complete -c xcsh -n "__fish_use_subcommand" -a "marketplace" -d 'Add-on services, connectors, and TPM policies.'
@@ -111,9 +111,6 @@ complete -c xcsh -n "__fish_use_subcommand" -a "dos" -d 'Alias for ddos'
111
111
  complete -c xcsh -n "__fish_use_subcommand" -a "ddos-protect" -d 'Alias for ddos'
112
112
  complete -c xcsh -n "__fish_use_subcommand" -a "dns-zone" -d 'Alias for dns'
113
113
  complete -c xcsh -n "__fish_use_subcommand" -a "zones" -d 'Alias for dns'
114
- complete -c xcsh -n "__fish_use_subcommand" -a "ai" -d 'Alias for generative_ai'
115
- complete -c xcsh -n "__fish_use_subcommand" -a "genai" -d 'Alias for generative_ai'
116
- complete -c xcsh -n "__fish_use_subcommand" -a "assistant" -d 'Alias for generative_ai'
117
114
  complete -c xcsh -n "__fish_use_subcommand" -a "mk8s" -d 'Alias for managed_kubernetes'
118
115
  complete -c xcsh -n "__fish_use_subcommand" -a "appstack" -d 'Alias for managed_kubernetes'
119
116
  complete -c xcsh -n "__fish_use_subcommand" -a "k8s-mgmt" -d 'Alias for managed_kubernetes'
@@ -180,6 +177,12 @@ complete -c xcsh -n "__fish_seen_subcommand_from cloudstatus" -a "maintenance" -
180
177
  complete -c xcsh -n "__fish_seen_subcommand_from completion" -a "bash" -d 'Generate bash completion script'
181
178
  complete -c xcsh -n "__fish_seen_subcommand_from completion" -a "zsh" -d 'Generate zsh completion script'
182
179
  complete -c xcsh -n "__fish_seen_subcommand_from completion" -a "fish" -d 'Generate fish completion script'
180
+ complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "query" -d 'Query the AI assistant'
181
+ complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "chat" -d 'Interactive AI chat mode'
182
+ complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "feedback" -d 'Submit feedback for AI responses'
183
+ complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "eval" -d 'RBAC testing mode commands'
184
+ complete -c xcsh -n "__fish_seen_subcommand_from generative_ai; and __fish_seen_subcommand_from eval" -a "query" -d 'Eval mode AI query'
185
+ complete -c xcsh -n "__fish_seen_subcommand_from generative_ai; and __fish_seen_subcommand_from eval" -a "feedback" -d 'Eval mode feedback submission'
183
186
  complete -c xcsh -n "__fish_seen_subcommand_from login" -a "banner" -d 'Display xcsh banner with logo'
184
187
  complete -c xcsh -n "__fish_seen_subcommand_from login" -a "profile" -d 'Manage saved connection profiles'
185
188
  complete -c xcsh -n "__fish_seen_subcommand_from login; and __fish_seen_subcommand_from profile" -a "list" -d 'List all saved profiles'
@@ -353,16 +356,6 @@ complete -c xcsh -n "__fish_seen_subcommand_from dns" -a "status" -d 'Get resour
353
356
  complete -c xcsh -n "__fish_seen_subcommand_from dns" -a "patch" -d 'Patch a resource'
354
357
  complete -c xcsh -n "__fish_seen_subcommand_from dns" -a "add-labels" -d 'Add labels to a resource'
355
358
  complete -c xcsh -n "__fish_seen_subcommand_from dns" -a "remove-labels" -d 'Remove labels from a resource'
356
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "list" -d 'List resources'
357
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "get" -d 'Get a specific resource'
358
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "create" -d 'Create a new resource'
359
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "delete" -d 'Delete a resource'
360
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "replace" -d 'Replace a resource'
361
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "apply" -d 'Apply configuration from file'
362
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "status" -d 'Get resource status'
363
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "patch" -d 'Patch a resource'
364
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "add-labels" -d 'Add labels to a resource'
365
- complete -c xcsh -n "__fish_seen_subcommand_from generative_ai" -a "remove-labels" -d 'Remove labels from a resource'
366
359
  complete -c xcsh -n "__fish_seen_subcommand_from managed_kubernetes" -a "list" -d 'List resources'
367
360
  complete -c xcsh -n "__fish_seen_subcommand_from managed_kubernetes" -a "get" -d 'Get a specific resource'
368
361
  complete -c xcsh -n "__fish_seen_subcommand_from managed_kubernetes" -a "create" -d 'Create a new resource'
package/dist/index.js CHANGED
@@ -46630,8 +46630,8 @@ function getLogoModeFromEnv(envPrefix) {
46630
46630
  var CLI_NAME = "xcsh";
46631
46631
  var CLI_FULL_NAME = "F5 Distributed Cloud Shell";
46632
46632
  function getVersion() {
46633
- if ("v1.0.90-2601021717") {
46634
- return "v1.0.90-2601021717";
46633
+ if ("v1.0.90-2601022257") {
46634
+ return "v1.0.90-2601022257";
46635
46635
  }
46636
46636
  if (process.env.XCSH_VERSION) {
46637
46637
  return process.env.XCSH_VERSION;
@@ -47025,9 +47025,9 @@ var APIError = class extends Error {
47025
47025
 
47026
47026
  // src/api/client.ts
47027
47027
  var DEFAULT_RETRY_CONFIG = {
47028
- maxRetries: 3,
47029
- initialDelayMs: 1e3,
47030
- maxDelayMs: 1e4,
47028
+ maxRetries: 2,
47029
+ initialDelayMs: 500,
47030
+ maxDelayMs: 5e3,
47031
47031
  backoffMultiplier: 2,
47032
47032
  jitter: true
47033
47033
  };
@@ -47069,7 +47069,7 @@ var APIClient = class {
47069
47069
  constructor(config) {
47070
47070
  this.serverUrl = config.serverUrl.replace(/\/+$/, "");
47071
47071
  this.apiToken = config.apiToken ?? "";
47072
- this.timeout = config.timeout ?? 3e4;
47072
+ this.timeout = config.timeout ?? 15e3;
47073
47073
  this.debug = config.debug ?? false;
47074
47074
  this.retryConfig = {
47075
47075
  ...DEFAULT_RETRY_CONFIG,
@@ -47176,13 +47176,20 @@ var APIClient = class {
47176
47176
  }
47177
47177
  /**
47178
47178
  * Check if an error is retryable
47179
+ *
47180
+ * Permanent network errors (connection refused, DNS not found) are NOT retried
47181
+ * since retrying won't help if the server is unreachable.
47179
47182
  */
47180
47183
  isRetryableError(error) {
47181
47184
  if (error instanceof APIError) {
47182
47185
  return RETRYABLE_STATUS_CODES.has(error.statusCode);
47183
47186
  }
47184
47187
  if (error instanceof Error) {
47185
- return error.name === "AbortError" || error.message.includes("fetch failed") || error.message.includes("network") || error.message.includes("ECONNREFUSED") || error.message.includes("ENOTFOUND") || error.message.includes("ETIMEDOUT");
47188
+ const message = error.message.toLowerCase();
47189
+ if (message.includes("econnrefused") || message.includes("enotfound") || message.includes("ehostunreach") || message.includes("enetunreach")) {
47190
+ return false;
47191
+ }
47192
+ return error.name === "AbortError" || message.includes("etimedout") || message.includes("econnreset") || message.includes("socket hang up");
47186
47193
  }
47187
47194
  return false;
47188
47195
  }
@@ -147143,10 +147150,1076 @@ var completionDomain = {
147143
147150
  subcommands: /* @__PURE__ */ new Map()
147144
147151
  };
147145
147152
 
147153
+ // src/domains/generative_ai/client.ts
147154
+ var GenAIClient = class {
147155
+ constructor(apiClient) {
147156
+ this.apiClient = apiClient;
147157
+ }
147158
+ /**
147159
+ * Query the AI assistant
147160
+ *
147161
+ * @param namespace - The namespace context for the query
147162
+ * @param query - The natural language query
147163
+ * @returns The AI assistant response with query_id and response data
147164
+ */
147165
+ async query(namespace, query) {
147166
+ const request = {
147167
+ current_query: query,
147168
+ namespace
147169
+ };
147170
+ const response = await this.apiClient.post(
147171
+ `/api/gen-ai/namespaces/${namespace}/query`,
147172
+ request
147173
+ );
147174
+ if (!response.ok) {
147175
+ throw new Error(
147176
+ `GenAI query failed: ${response.statusCode} - ${JSON.stringify(response.data)}`
147177
+ );
147178
+ }
147179
+ return response.data;
147180
+ }
147181
+ /**
147182
+ * Submit feedback for a query
147183
+ *
147184
+ * @param request - The feedback request with query_id and feedback type
147185
+ */
147186
+ async feedback(request) {
147187
+ const response = await this.apiClient.post(
147188
+ `/api/gen-ai/namespaces/${request.namespace}/query_feedback`,
147189
+ request
147190
+ );
147191
+ if (!response.ok) {
147192
+ throw new Error(
147193
+ `GenAI feedback failed: ${response.statusCode} - ${JSON.stringify(response.data)}`
147194
+ );
147195
+ }
147196
+ }
147197
+ /**
147198
+ * Query the AI assistant in eval mode (for RBAC testing)
147199
+ *
147200
+ * @param namespace - The namespace context for the query
147201
+ * @param query - The natural language query
147202
+ * @returns The AI assistant response
147203
+ */
147204
+ async evalQuery(namespace, query) {
147205
+ const request = {
147206
+ current_query: query,
147207
+ namespace
147208
+ };
147209
+ const response = await this.apiClient.post(
147210
+ `/api/gen-ai/namespaces/${namespace}/eval_query`,
147211
+ request
147212
+ );
147213
+ if (!response.ok) {
147214
+ throw new Error(
147215
+ `GenAI eval query failed: ${response.statusCode} - ${JSON.stringify(response.data)}`
147216
+ );
147217
+ }
147218
+ return response.data;
147219
+ }
147220
+ /**
147221
+ * Submit feedback for an eval query
147222
+ *
147223
+ * @param request - The feedback request with query_id and feedback type
147224
+ */
147225
+ async evalFeedback(request) {
147226
+ const response = await this.apiClient.post(
147227
+ `/api/gen-ai/namespaces/${request.namespace}/eval_query_feedback`,
147228
+ request
147229
+ );
147230
+ if (!response.ok) {
147231
+ throw new Error(
147232
+ `GenAI eval feedback failed: ${response.statusCode} - ${JSON.stringify(response.data)}`
147233
+ );
147234
+ }
147235
+ }
147236
+ };
147237
+ var cachedClient = null;
147238
+ function getGenAIClient(apiClient) {
147239
+ if (!cachedClient) {
147240
+ cachedClient = new GenAIClient(apiClient);
147241
+ }
147242
+ return cachedClient;
147243
+ }
147244
+
147245
+ // src/domains/generative_ai/types.ts
147246
+ function getResponseType(response) {
147247
+ if (response.generic_response) return "generic_response";
147248
+ if (response.explain_log) return "explain_log";
147249
+ if (response.gen_dashboard_filter) return "gen_dashboard_filter";
147250
+ if (response.list_response) return "list_response";
147251
+ if (response.site_analysis_response) return "site_analysis_response";
147252
+ if (response.widget_response) return "widget_response";
147253
+ return null;
147254
+ }
147255
+ var FEEDBACK_TYPE_MAP = {
147256
+ other: "OTHER",
147257
+ inaccurate: "INACCURATE_DATA",
147258
+ irrelevant: "IRRELEVANT_CONTENT",
147259
+ poor_format: "POOR_FORMAT",
147260
+ slow: "SLOW_RESPONSE"
147261
+ };
147262
+ function getValidFeedbackTypes() {
147263
+ return Object.keys(FEEDBACK_TYPE_MAP);
147264
+ }
147265
+
147266
+ // src/domains/generative_ai/response-renderer.ts
147267
+ function renderResponse(response) {
147268
+ const lines = [];
147269
+ const responseType = getResponseType(response);
147270
+ switch (responseType) {
147271
+ case "generic_response":
147272
+ lines.push(...renderGenericResponse(response.generic_response));
147273
+ break;
147274
+ case "explain_log":
147275
+ lines.push(...renderExplainLog(response.explain_log));
147276
+ break;
147277
+ case "gen_dashboard_filter":
147278
+ lines.push(
147279
+ ...renderDashboardFilter(response.gen_dashboard_filter)
147280
+ );
147281
+ break;
147282
+ case "list_response":
147283
+ lines.push(...renderListResponse(response.list_response));
147284
+ break;
147285
+ case "site_analysis_response":
147286
+ lines.push(...renderSiteAnalysis(response.site_analysis_response));
147287
+ break;
147288
+ case "widget_response":
147289
+ lines.push(...renderWidgetResponse(response.widget_response));
147290
+ break;
147291
+ default:
147292
+ lines.push("No response content.");
147293
+ }
147294
+ if (response.follow_up_queries && response.follow_up_queries.length > 0) {
147295
+ lines.push("");
147296
+ lines.push("Suggested follow-up questions:");
147297
+ response.follow_up_queries.forEach((q, i) => {
147298
+ lines.push(` ${i + 1}. ${q}`);
147299
+ });
147300
+ }
147301
+ return lines;
147302
+ }
147303
+ function renderGenericResponse(response) {
147304
+ const lines = [];
147305
+ if (response.error) {
147306
+ lines.push(`Error: ${response.error}`);
147307
+ return lines;
147308
+ }
147309
+ if (response.text) {
147310
+ lines.push(...response.text.split("\n"));
147311
+ }
147312
+ if (response.links && response.links.length > 0) {
147313
+ lines.push("");
147314
+ lines.push("Related links:");
147315
+ for (const link3 of response.links) {
147316
+ lines.push(` - ${link3.title}: ${link3.url}`);
147317
+ }
147318
+ }
147319
+ return lines;
147320
+ }
147321
+ function renderExplainLog(response) {
147322
+ const lines = [];
147323
+ lines.push("=== Security Event Analysis ===");
147324
+ lines.push("");
147325
+ if (response.summary) {
147326
+ lines.push("Summary:");
147327
+ lines.push(` ${response.summary}`);
147328
+ lines.push("");
147329
+ }
147330
+ if (response.action) {
147331
+ lines.push(`Action Taken: ${response.action}`);
147332
+ }
147333
+ if (response.accuracy) {
147334
+ lines.push(`Accuracy: ${response.accuracy}`);
147335
+ }
147336
+ if (response.violations && response.violations.length > 0) {
147337
+ lines.push("");
147338
+ lines.push("Violations Detected:");
147339
+ for (const v of response.violations) {
147340
+ lines.push(` - ${v.name}: ${v.description}`);
147341
+ }
147342
+ }
147343
+ if (response.threat_campaigns && response.threat_campaigns.length > 0) {
147344
+ lines.push("");
147345
+ lines.push("Threat Campaigns:");
147346
+ for (const campaign of response.threat_campaigns) {
147347
+ lines.push(` - ${campaign}`);
147348
+ }
147349
+ }
147350
+ if (response.request_details) {
147351
+ lines.push("");
147352
+ lines.push("Request Details:");
147353
+ lines.push(` ${JSON.stringify(response.request_details, null, 2)}`);
147354
+ }
147355
+ return lines;
147356
+ }
147357
+ function renderDashboardFilter(response) {
147358
+ const lines = [];
147359
+ lines.push("=== Dashboard Filter ===");
147360
+ lines.push("");
147361
+ if (response.filter_expression) {
147362
+ lines.push("Filter Expression:");
147363
+ lines.push(` ${response.filter_expression}`);
147364
+ }
147365
+ if (response.dashboard_context) {
147366
+ lines.push("");
147367
+ lines.push("Dashboard Context:");
147368
+ lines.push(` ${response.dashboard_context}`);
147369
+ }
147370
+ return lines;
147371
+ }
147372
+ function renderListResponse(response) {
147373
+ const lines = [];
147374
+ if (response.formatted_list) {
147375
+ lines.push(response.formatted_list);
147376
+ return lines;
147377
+ }
147378
+ if (response.items && response.items.length > 0) {
147379
+ if (response.total_count !== void 0) {
147380
+ lines.push(`Total: ${response.total_count} items`);
147381
+ lines.push("");
147382
+ }
147383
+ for (const item of response.items) {
147384
+ lines.push(JSON.stringify(item, null, 2));
147385
+ lines.push("");
147386
+ }
147387
+ } else {
147388
+ lines.push("No items found.");
147389
+ }
147390
+ return lines;
147391
+ }
147392
+ function renderSiteAnalysis(response) {
147393
+ const lines = [];
147394
+ lines.push("=== Site Analysis ===");
147395
+ lines.push("");
147396
+ if (response.site_name) {
147397
+ lines.push(`Site: ${response.site_name}`);
147398
+ }
147399
+ if (response.health_status) {
147400
+ lines.push(`Health: ${response.health_status}`);
147401
+ }
147402
+ if (response.metrics) {
147403
+ lines.push("");
147404
+ lines.push("Metrics:");
147405
+ for (const [key, value] of Object.entries(response.metrics)) {
147406
+ lines.push(` ${key}: ${JSON.stringify(value)}`);
147407
+ }
147408
+ }
147409
+ if (response.recommendations && response.recommendations.length > 0) {
147410
+ lines.push("");
147411
+ lines.push("Recommendations:");
147412
+ for (const rec of response.recommendations) {
147413
+ lines.push(` - ${rec}`);
147414
+ }
147415
+ }
147416
+ return lines;
147417
+ }
147418
+ function renderWidgetResponse(response) {
147419
+ const lines = [];
147420
+ if (response.display_type) {
147421
+ lines.push(`Widget Type: ${response.display_type}`);
147422
+ lines.push("");
147423
+ }
147424
+ if (response.table) {
147425
+ for (const row of response.table.rows) {
147426
+ const cells = row.cells.map((cell) => {
147427
+ let value = cell.value;
147428
+ if (cell.properties?.status_style) {
147429
+ value = `[${cell.properties.status_style}] ${value}`;
147430
+ }
147431
+ return value;
147432
+ });
147433
+ lines.push(cells.join(" | "));
147434
+ }
147435
+ }
147436
+ if (response.links && response.links.length > 0) {
147437
+ lines.push("");
147438
+ lines.push("Links:");
147439
+ for (const link3 of response.links) {
147440
+ lines.push(` - ${link3.title}: ${link3.url}`);
147441
+ }
147442
+ }
147443
+ return lines;
147444
+ }
147445
+
147446
+ // src/domains/generative_ai/query.ts
147447
+ var lastQueryState = {
147448
+ namespace: "",
147449
+ lastQueryId: null,
147450
+ lastQuery: null,
147451
+ followUpQueries: [],
147452
+ isActive: false
147453
+ };
147454
+ function getLastQueryState() {
147455
+ return lastQueryState;
147456
+ }
147457
+ function updateLastQueryState(partial) {
147458
+ lastQueryState = { ...lastQueryState, ...partial };
147459
+ }
147460
+ function clearLastQueryState() {
147461
+ lastQueryState = {
147462
+ namespace: "",
147463
+ lastQueryId: null,
147464
+ lastQuery: null,
147465
+ followUpQueries: [],
147466
+ isActive: false
147467
+ };
147468
+ }
147469
+ function parseQueryArgs(args, session) {
147470
+ const { options, remainingArgs } = parseDomainOutputFlags(
147471
+ args,
147472
+ session.getOutputFormat()
147473
+ );
147474
+ let spec = false;
147475
+ let namespace = session.getNamespace();
147476
+ const questionParts = [];
147477
+ let i = 0;
147478
+ while (i < remainingArgs.length) {
147479
+ const arg = remainingArgs[i];
147480
+ if (arg === "--spec") {
147481
+ spec = true;
147482
+ } else if (arg === "--namespace" || arg === "-ns") {
147483
+ if (i + 1 < remainingArgs.length) {
147484
+ namespace = remainingArgs[i + 1] ?? namespace;
147485
+ i++;
147486
+ }
147487
+ } else {
147488
+ questionParts.push(arg ?? "");
147489
+ }
147490
+ i++;
147491
+ }
147492
+ return {
147493
+ format: options.format,
147494
+ noColor: options.noColor,
147495
+ spec,
147496
+ namespace,
147497
+ question: questionParts.join(" ")
147498
+ };
147499
+ }
147500
+ var queryCommand = {
147501
+ name: "query",
147502
+ description: "Send a natural language query to the F5 Distributed Cloud AI assistant. Ask about load balancers, WAF configurations, site status, security events, or any platform topic. Returns AI-generated responses with optional follow-up suggestions. Use --namespace to specify the context for namespace-scoped resources.",
147503
+ descriptionShort: "Query the AI assistant",
147504
+ descriptionMedium: "Send natural language queries to the AI assistant for help with F5 XC platform operations, configurations, and troubleshooting.",
147505
+ usage: "<question> [--namespace <ns>]",
147506
+ aliases: ["ask", "q"],
147507
+ async execute(args, session) {
147508
+ const { format, noColor, spec, namespace, question } = parseQueryArgs(
147509
+ args,
147510
+ session
147511
+ );
147512
+ if (spec) {
147513
+ const cmdSpec = getCommandSpec("generative_ai query");
147514
+ if (cmdSpec) {
147515
+ return successResult([formatSpec(cmdSpec)]);
147516
+ }
147517
+ }
147518
+ if (!question.trim()) {
147519
+ return errorResult(
147520
+ "Please provide a question. Usage: ai query <question>"
147521
+ );
147522
+ }
147523
+ const apiClient = session.getAPIClient();
147524
+ if (!apiClient) {
147525
+ return errorResult(
147526
+ "Not connected to API. Please configure connection first."
147527
+ );
147528
+ }
147529
+ if (!session.isTokenValidated()) {
147530
+ return errorResult(
147531
+ "Not authenticated. Please check your API token."
147532
+ );
147533
+ }
147534
+ try {
147535
+ const client = getGenAIClient(apiClient);
147536
+ const response = await client.query(namespace, question);
147537
+ updateLastQueryState({
147538
+ namespace,
147539
+ lastQueryId: response.query_id,
147540
+ lastQuery: question,
147541
+ followUpQueries: response.follow_up_queries ?? [],
147542
+ isActive: true
147543
+ });
147544
+ if (format === "none") {
147545
+ return successResult([]);
147546
+ }
147547
+ if (format === "json" || format === "yaml" || format === "tsv") {
147548
+ return successResult(
147549
+ formatDomainOutput(response, { format, noColor })
147550
+ );
147551
+ }
147552
+ const lines = renderResponse(response);
147553
+ return successResult(lines);
147554
+ } catch (error) {
147555
+ const message = error instanceof Error ? error.message : String(error);
147556
+ return errorResult(`Query failed: ${message}`);
147557
+ }
147558
+ }
147559
+ };
147560
+
147561
+ // src/domains/generative_ai/chat.ts
147562
+ import * as readline from "readline";
147563
+ function parseChatArgs(args, session) {
147564
+ const { remainingArgs } = parseDomainOutputFlags(
147565
+ args,
147566
+ session.getOutputFormat()
147567
+ );
147568
+ let spec = false;
147569
+ let namespace = session.getNamespace();
147570
+ let i = 0;
147571
+ while (i < remainingArgs.length) {
147572
+ const arg = remainingArgs[i];
147573
+ if (arg === "--spec") {
147574
+ spec = true;
147575
+ } else if (arg === "--namespace" || arg === "-ns") {
147576
+ if (i + 1 < remainingArgs.length) {
147577
+ namespace = remainingArgs[i + 1] ?? namespace;
147578
+ i++;
147579
+ }
147580
+ }
147581
+ i++;
147582
+ }
147583
+ return { spec, namespace };
147584
+ }
147585
+ function showChatHelp() {
147586
+ return [
147587
+ "",
147588
+ "=== AI Chat Commands ===",
147589
+ "",
147590
+ " /exit, /quit, /q - Exit chat mode",
147591
+ " /help, /h - Show this help",
147592
+ " /clear, /c - Clear conversation context",
147593
+ " /feedback <type> - Submit feedback for last response",
147594
+ " Types: positive, negative",
147595
+ " 1, 2, 3... - Select a follow-up question by number",
147596
+ "",
147597
+ "Just type your question to query the AI assistant.",
147598
+ ""
147599
+ ];
147600
+ }
147601
+ async function handleFeedback(input, session) {
147602
+ const state = getLastQueryState();
147603
+ if (!state.lastQueryId || !state.lastQuery) {
147604
+ return ["No previous query to provide feedback for."];
147605
+ }
147606
+ const parts = input.split(/\s+/);
147607
+ const feedbackType = parts[1]?.toLowerCase();
147608
+ if (!feedbackType) {
147609
+ return [
147610
+ "Usage: /feedback <positive|negative>",
147611
+ " Optional: /feedback negative <type> [comment]",
147612
+ " Types: other, inaccurate, irrelevant, poor_format, slow"
147613
+ ];
147614
+ }
147615
+ const apiClient = session.getAPIClient();
147616
+ if (!apiClient) {
147617
+ return ["Not connected to API."];
147618
+ }
147619
+ try {
147620
+ const client = getGenAIClient(apiClient);
147621
+ if (feedbackType === "positive" || feedbackType === "+") {
147622
+ await client.feedback({
147623
+ query: state.lastQuery,
147624
+ query_id: state.lastQueryId,
147625
+ namespace: state.namespace,
147626
+ positive_feedback: {}
147627
+ });
147628
+ return ["Positive feedback submitted. Thank you!"];
147629
+ }
147630
+ if (feedbackType === "negative" || feedbackType === "-") {
147631
+ const negType = parts[2]?.toLowerCase();
147632
+ const mappedType = negType ? FEEDBACK_TYPE_MAP[negType] : void 0;
147633
+ const comment = parts.slice(3).join(" ") || void 0;
147634
+ await client.feedback({
147635
+ query: state.lastQuery,
147636
+ query_id: state.lastQueryId,
147637
+ namespace: state.namespace,
147638
+ negative_feedback: {
147639
+ remarks: mappedType ? [mappedType] : ["OTHER"]
147640
+ },
147641
+ comment
147642
+ });
147643
+ return [
147644
+ "Negative feedback submitted. Thank you for helping improve the AI."
147645
+ ];
147646
+ }
147647
+ return [
147648
+ `Unknown feedback type: ${feedbackType}`,
147649
+ "Use 'positive' or 'negative'."
147650
+ ];
147651
+ } catch (error) {
147652
+ const message = error instanceof Error ? error.message : String(error);
147653
+ return [`Feedback failed: ${message}`];
147654
+ }
147655
+ }
147656
+ async function runChatLoop(session, namespace) {
147657
+ const apiClient = session.getAPIClient();
147658
+ if (!apiClient) {
147659
+ return ["Not connected to API. Please configure connection first."];
147660
+ }
147661
+ if (!session.isTokenValidated()) {
147662
+ return ["Not authenticated. Please check your API token."];
147663
+ }
147664
+ const client = getGenAIClient(apiClient);
147665
+ const output = [];
147666
+ output.push("");
147667
+ output.push("=== F5 XC AI Assistant Chat ===");
147668
+ output.push(`Namespace: ${namespace}`);
147669
+ output.push("Type /help for commands, /exit to quit.");
147670
+ output.push("");
147671
+ const rl = readline.createInterface({
147672
+ input: process.stdin,
147673
+ output: process.stdout,
147674
+ terminal: process.stdin.isTTY ?? false
147675
+ });
147676
+ let interrupted = false;
147677
+ rl.on("SIGINT", () => {
147678
+ interrupted = true;
147679
+ console.log("\n(Use /exit to leave chat mode)");
147680
+ rl.prompt();
147681
+ });
147682
+ const askQuestion = (prompt) => {
147683
+ return new Promise((resolve) => {
147684
+ rl.question(prompt, (answer) => {
147685
+ resolve(answer);
147686
+ });
147687
+ });
147688
+ };
147689
+ for (const line of output) {
147690
+ console.log(line);
147691
+ }
147692
+ let running = true;
147693
+ while (running && !interrupted) {
147694
+ const input = await askQuestion("ai> ");
147695
+ if (interrupted) {
147696
+ break;
147697
+ }
147698
+ const trimmed = input.trim();
147699
+ if (!trimmed) {
147700
+ continue;
147701
+ }
147702
+ if (trimmed === "/exit" || trimmed === "/quit" || trimmed === "/q") {
147703
+ console.log("Exiting chat mode.");
147704
+ running = false;
147705
+ break;
147706
+ }
147707
+ if (trimmed === "/help" || trimmed === "/h") {
147708
+ for (const line of showChatHelp()) {
147709
+ console.log(line);
147710
+ }
147711
+ continue;
147712
+ }
147713
+ if (trimmed === "/clear" || trimmed === "/c") {
147714
+ clearLastQueryState();
147715
+ console.log("Conversation context cleared.");
147716
+ continue;
147717
+ }
147718
+ if (trimmed.startsWith("/feedback")) {
147719
+ const feedbackLines = await handleFeedback(trimmed, session);
147720
+ for (const line of feedbackLines) {
147721
+ console.log(line);
147722
+ }
147723
+ continue;
147724
+ }
147725
+ if (/^\d+$/.test(trimmed)) {
147726
+ const num = parseInt(trimmed, 10);
147727
+ const state = getLastQueryState();
147728
+ if (state.followUpQueries.length > 0 && num >= 1 && num <= state.followUpQueries.length) {
147729
+ const followUp = state.followUpQueries[num - 1];
147730
+ if (followUp) {
147731
+ console.log(`
147732
+ Following up: ${followUp}
147733
+ `);
147734
+ try {
147735
+ const response = await client.query(
147736
+ namespace,
147737
+ followUp
147738
+ );
147739
+ updateLastQueryState({
147740
+ namespace,
147741
+ lastQueryId: response.query_id,
147742
+ lastQuery: followUp,
147743
+ followUpQueries: response.follow_up_queries ?? []
147744
+ });
147745
+ const lines = renderResponse(response);
147746
+ for (const line of lines) {
147747
+ console.log(line);
147748
+ }
147749
+ console.log("");
147750
+ } catch (error) {
147751
+ const message = error instanceof Error ? error.message : String(error);
147752
+ console.log(`Query failed: ${message}`);
147753
+ }
147754
+ continue;
147755
+ }
147756
+ }
147757
+ console.log(
147758
+ `Invalid selection. Choose 1-${state.followUpQueries.length} from suggested follow-ups.`
147759
+ );
147760
+ continue;
147761
+ }
147762
+ if (trimmed.startsWith("/")) {
147763
+ console.log(
147764
+ `Unknown command: ${trimmed}. Type /help for commands.`
147765
+ );
147766
+ continue;
147767
+ }
147768
+ try {
147769
+ const response = await client.query(namespace, trimmed);
147770
+ updateLastQueryState({
147771
+ namespace,
147772
+ lastQueryId: response.query_id,
147773
+ lastQuery: trimmed,
147774
+ followUpQueries: response.follow_up_queries ?? []
147775
+ });
147776
+ console.log("");
147777
+ const lines = renderResponse(response);
147778
+ for (const line of lines) {
147779
+ console.log(line);
147780
+ }
147781
+ console.log("");
147782
+ } catch (error) {
147783
+ const message = error instanceof Error ? error.message : String(error);
147784
+ console.log(`Query failed: ${message}`);
147785
+ }
147786
+ }
147787
+ rl.close();
147788
+ return ["Chat session ended."];
147789
+ }
147790
+ var chatCommand = {
147791
+ name: "chat",
147792
+ description: "Start an interactive conversation with the F5 XC AI assistant. Enter a multi-turn dialog where you can ask questions, receive responses with follow-up suggestions, and navigate through topics naturally. Use numbered responses to quickly select suggested follow-up questions. Supports in-chat feedback submission. Type /exit to return to the main CLI.",
147793
+ descriptionShort: "Interactive AI chat mode",
147794
+ descriptionMedium: "Start an interactive multi-turn conversation with the AI assistant. Supports follow-up suggestions and in-chat commands.",
147795
+ usage: "[--namespace <ns>]",
147796
+ aliases: ["interactive", "i"],
147797
+ async execute(args, session) {
147798
+ const { spec, namespace } = parseChatArgs(args, session);
147799
+ if (spec) {
147800
+ const cmdSpec = getCommandSpec("generative_ai chat");
147801
+ if (cmdSpec) {
147802
+ return successResult([formatSpec(cmdSpec)]);
147803
+ }
147804
+ }
147805
+ if (!process.stdin.isTTY) {
147806
+ return errorResult(
147807
+ "Chat mode requires an interactive terminal. Use 'ai query' for non-interactive queries."
147808
+ );
147809
+ }
147810
+ try {
147811
+ const result = await runChatLoop(session, namespace);
147812
+ return successResult(result);
147813
+ } catch (error) {
147814
+ const message = error instanceof Error ? error.message : String(error);
147815
+ return errorResult(`Chat session failed: ${message}`);
147816
+ }
147817
+ }
147818
+ };
147819
+
147820
+ // src/domains/generative_ai/feedback.ts
147821
+ function parseFeedbackArgs(args, session) {
147822
+ const { remainingArgs } = parseDomainOutputFlags(
147823
+ args,
147824
+ session.getOutputFormat()
147825
+ );
147826
+ let spec = false;
147827
+ let namespace = session.getNamespace();
147828
+ let positive = false;
147829
+ let negativeType = null;
147830
+ let comment = null;
147831
+ let queryId = null;
147832
+ let i = 0;
147833
+ while (i < remainingArgs.length) {
147834
+ const arg = remainingArgs[i];
147835
+ if (arg === "--spec") {
147836
+ spec = true;
147837
+ } else if (arg === "--namespace" || arg === "-ns") {
147838
+ if (i + 1 < remainingArgs.length) {
147839
+ namespace = remainingArgs[i + 1] ?? namespace;
147840
+ i++;
147841
+ }
147842
+ } else if (arg === "--positive" || arg === "-p") {
147843
+ positive = true;
147844
+ } else if (arg === "--negative" || arg === "-n") {
147845
+ if (i + 1 < remainingArgs.length) {
147846
+ const typeArg = remainingArgs[i + 1]?.toLowerCase();
147847
+ if (typeArg && FEEDBACK_TYPE_MAP[typeArg]) {
147848
+ negativeType = FEEDBACK_TYPE_MAP[typeArg] ?? null;
147849
+ i++;
147850
+ } else {
147851
+ negativeType = "OTHER";
147852
+ }
147853
+ } else {
147854
+ negativeType = "OTHER";
147855
+ }
147856
+ } else if (arg === "--comment" || arg === "-c") {
147857
+ if (i + 1 < remainingArgs.length) {
147858
+ comment = remainingArgs[i + 1] ?? null;
147859
+ i++;
147860
+ }
147861
+ } else if (arg === "--query-id" || arg === "-q") {
147862
+ if (i + 1 < remainingArgs.length) {
147863
+ queryId = remainingArgs[i + 1] ?? null;
147864
+ i++;
147865
+ }
147866
+ }
147867
+ i++;
147868
+ }
147869
+ return {
147870
+ spec,
147871
+ namespace,
147872
+ positive,
147873
+ negativeType,
147874
+ comment,
147875
+ queryId
147876
+ };
147877
+ }
147878
+ var feedbackCommand = {
147879
+ name: "feedback",
147880
+ description: "Submit feedback on AI assistant responses to help improve future answers. Provide positive feedback when responses are helpful, or negative feedback with a reason type when improvements are needed. Feedback is associated with the most recent query unless a specific query ID is provided. Negative feedback types include: inaccurate (wrong information), irrelevant (off-topic), poor_format (hard to read), slow (response time), or other.",
147881
+ descriptionShort: "Submit feedback for AI responses",
147882
+ descriptionMedium: "Provide positive or negative feedback for AI assistant responses. Use --negative with a type: inaccurate, irrelevant, poor_format, slow, or other.",
147883
+ usage: "--positive | --negative <type> [--comment <text>] [--query-id <id>]",
147884
+ aliases: ["fb", "rate"],
147885
+ async execute(args, session) {
147886
+ const { spec, namespace, positive, negativeType, comment, queryId } = parseFeedbackArgs(args, session);
147887
+ if (spec) {
147888
+ const cmdSpec = getCommandSpec("generative_ai feedback");
147889
+ if (cmdSpec) {
147890
+ return successResult([formatSpec(cmdSpec)]);
147891
+ }
147892
+ }
147893
+ if (!positive && !negativeType) {
147894
+ const validTypes = getValidFeedbackTypes().join(", ");
147895
+ return errorResult(
147896
+ `Please specify feedback type:
147897
+ --positive (-p) Rate the response positively
147898
+ --negative (-n) <type> Rate negatively with reason
147899
+
147900
+ Negative types: ${validTypes}`
147901
+ );
147902
+ }
147903
+ const state = getLastQueryState();
147904
+ const targetQueryId = queryId ?? state.lastQueryId;
147905
+ const targetQuery = state.lastQuery;
147906
+ if (!targetQueryId) {
147907
+ return errorResult(
147908
+ "No query to provide feedback for. Make a query first, or use --query-id to specify one."
147909
+ );
147910
+ }
147911
+ if (!targetQuery && !queryId) {
147912
+ return errorResult(
147913
+ "Could not find the original query. Use --query-id to specify the query ID."
147914
+ );
147915
+ }
147916
+ const apiClient = session.getAPIClient();
147917
+ if (!apiClient) {
147918
+ return errorResult(
147919
+ "Not connected to API. Please configure connection first."
147920
+ );
147921
+ }
147922
+ if (!session.isTokenValidated()) {
147923
+ return errorResult(
147924
+ "Not authenticated. Please check your API token."
147925
+ );
147926
+ }
147927
+ try {
147928
+ const client = getGenAIClient(apiClient);
147929
+ if (positive) {
147930
+ await client.feedback({
147931
+ query: targetQuery ?? "",
147932
+ query_id: targetQueryId,
147933
+ namespace: state.namespace || namespace,
147934
+ positive_feedback: {},
147935
+ comment: comment ?? void 0
147936
+ });
147937
+ return successResult([
147938
+ "Positive feedback submitted successfully.",
147939
+ `Query ID: ${targetQueryId}`
147940
+ ]);
147941
+ }
147942
+ await client.feedback({
147943
+ query: targetQuery ?? "",
147944
+ query_id: targetQueryId,
147945
+ namespace: state.namespace || namespace,
147946
+ negative_feedback: {
147947
+ remarks: negativeType ? [negativeType] : ["OTHER"]
147948
+ },
147949
+ comment: comment ?? void 0
147950
+ });
147951
+ return successResult([
147952
+ "Negative feedback submitted successfully.",
147953
+ `Query ID: ${targetQueryId}`,
147954
+ `Reason: ${negativeType ?? "OTHER"}`,
147955
+ ...comment ? [`Comment: ${comment}`] : []
147956
+ ]);
147957
+ } catch (error) {
147958
+ const message = error instanceof Error ? error.message : String(error);
147959
+ return errorResult(`Feedback submission failed: ${message}`);
147960
+ }
147961
+ }
147962
+ };
147963
+
147964
+ // src/domains/generative_ai/eval.ts
147965
+ function parseEvalQueryArgs(args, session) {
147966
+ const { options, remainingArgs } = parseDomainOutputFlags(
147967
+ args,
147968
+ session.getOutputFormat()
147969
+ );
147970
+ let spec = false;
147971
+ let namespace = session.getNamespace();
147972
+ const questionParts = [];
147973
+ let i = 0;
147974
+ while (i < remainingArgs.length) {
147975
+ const arg = remainingArgs[i];
147976
+ if (arg === "--spec") {
147977
+ spec = true;
147978
+ } else if (arg === "--namespace" || arg === "-ns") {
147979
+ if (i + 1 < remainingArgs.length) {
147980
+ namespace = remainingArgs[i + 1] ?? namespace;
147981
+ i++;
147982
+ }
147983
+ } else {
147984
+ questionParts.push(arg ?? "");
147985
+ }
147986
+ i++;
147987
+ }
147988
+ return {
147989
+ format: options.format,
147990
+ noColor: options.noColor,
147991
+ spec,
147992
+ namespace,
147993
+ question: questionParts.join(" ")
147994
+ };
147995
+ }
147996
+ var evalQueryCommand = {
147997
+ name: "query",
147998
+ description: "Send a query to the AI assistant using the eval endpoint. This endpoint is used for RBAC testing and validation purposes, allowing administrators to test permission scenarios without affecting production query analytics.",
147999
+ descriptionShort: "Eval mode AI query",
148000
+ descriptionMedium: "Query the AI assistant in eval mode for RBAC testing and permission validation.",
148001
+ usage: "<question> [--namespace <ns>]",
148002
+ aliases: ["q"],
148003
+ async execute(args, session) {
148004
+ const { format, noColor, spec, namespace, question } = parseEvalQueryArgs(args, session);
148005
+ if (spec) {
148006
+ const cmdSpec = getCommandSpec("generative_ai eval query");
148007
+ if (cmdSpec) {
148008
+ return successResult([formatSpec(cmdSpec)]);
148009
+ }
148010
+ }
148011
+ if (!question.trim()) {
148012
+ return errorResult(
148013
+ "Please provide a question. Usage: ai eval query <question>"
148014
+ );
148015
+ }
148016
+ const apiClient = session.getAPIClient();
148017
+ if (!apiClient) {
148018
+ return errorResult(
148019
+ "Not connected to API. Please configure connection first."
148020
+ );
148021
+ }
148022
+ if (!session.isTokenValidated()) {
148023
+ return errorResult(
148024
+ "Not authenticated. Please check your API token."
148025
+ );
148026
+ }
148027
+ try {
148028
+ const client = getGenAIClient(apiClient);
148029
+ const response = await client.evalQuery(namespace, question);
148030
+ updateLastQueryState({
148031
+ namespace,
148032
+ lastQueryId: response.query_id,
148033
+ lastQuery: question,
148034
+ followUpQueries: response.follow_up_queries ?? [],
148035
+ isActive: true
148036
+ });
148037
+ if (format === "none") {
148038
+ return successResult([]);
148039
+ }
148040
+ if (format === "json" || format === "yaml" || format === "tsv") {
148041
+ return successResult(
148042
+ formatDomainOutput(response, { format, noColor })
148043
+ );
148044
+ }
148045
+ const lines = ["[EVAL MODE]", "", ...renderResponse(response)];
148046
+ return successResult(lines);
148047
+ } catch (error) {
148048
+ const message = error instanceof Error ? error.message : String(error);
148049
+ return errorResult(`Eval query failed: ${message}`);
148050
+ }
148051
+ }
148052
+ };
148053
+ function parseEvalFeedbackArgs(args, session) {
148054
+ const { remainingArgs } = parseDomainOutputFlags(
148055
+ args,
148056
+ session.getOutputFormat()
148057
+ );
148058
+ let spec = false;
148059
+ let namespace = session.getNamespace();
148060
+ let positive = false;
148061
+ let negativeType = null;
148062
+ let comment = null;
148063
+ let queryId = null;
148064
+ let i = 0;
148065
+ while (i < remainingArgs.length) {
148066
+ const arg = remainingArgs[i];
148067
+ if (arg === "--spec") {
148068
+ spec = true;
148069
+ } else if (arg === "--namespace" || arg === "-ns") {
148070
+ if (i + 1 < remainingArgs.length) {
148071
+ namespace = remainingArgs[i + 1] ?? namespace;
148072
+ i++;
148073
+ }
148074
+ } else if (arg === "--positive" || arg === "-p") {
148075
+ positive = true;
148076
+ } else if (arg === "--negative" || arg === "-n") {
148077
+ if (i + 1 < remainingArgs.length) {
148078
+ const typeArg = remainingArgs[i + 1]?.toLowerCase();
148079
+ if (typeArg && FEEDBACK_TYPE_MAP[typeArg]) {
148080
+ negativeType = FEEDBACK_TYPE_MAP[typeArg] ?? null;
148081
+ i++;
148082
+ } else {
148083
+ negativeType = "OTHER";
148084
+ }
148085
+ } else {
148086
+ negativeType = "OTHER";
148087
+ }
148088
+ } else if (arg === "--comment" || arg === "-c") {
148089
+ if (i + 1 < remainingArgs.length) {
148090
+ comment = remainingArgs[i + 1] ?? null;
148091
+ i++;
148092
+ }
148093
+ } else if (arg === "--query-id" || arg === "-q") {
148094
+ if (i + 1 < remainingArgs.length) {
148095
+ queryId = remainingArgs[i + 1] ?? null;
148096
+ i++;
148097
+ }
148098
+ }
148099
+ i++;
148100
+ }
148101
+ return {
148102
+ spec,
148103
+ namespace,
148104
+ positive,
148105
+ negativeType,
148106
+ comment,
148107
+ queryId
148108
+ };
148109
+ }
148110
+ var evalFeedbackCommand = {
148111
+ name: "feedback",
148112
+ description: "Submit feedback for an eval mode query. Use this endpoint when providing feedback for queries made through the eval endpoint, ensuring proper RBAC testing analytics separation.",
148113
+ descriptionShort: "Eval mode feedback submission",
148114
+ descriptionMedium: "Submit feedback for eval mode AI queries, keeping RBAC testing analytics separate.",
148115
+ usage: "--positive | --negative <type> [--comment <text>] [--query-id <id>]",
148116
+ aliases: ["fb"],
148117
+ async execute(args, session) {
148118
+ const { spec, namespace, positive, negativeType, comment, queryId } = parseEvalFeedbackArgs(args, session);
148119
+ if (spec) {
148120
+ const cmdSpec = getCommandSpec("generative_ai eval feedback");
148121
+ if (cmdSpec) {
148122
+ return successResult([formatSpec(cmdSpec)]);
148123
+ }
148124
+ }
148125
+ if (!positive && !negativeType) {
148126
+ const validTypes = getValidFeedbackTypes().join(", ");
148127
+ return errorResult(
148128
+ `Please specify feedback type:
148129
+ --positive (-p) Rate the response positively
148130
+ --negative (-n) <type> Rate negatively with reason
148131
+
148132
+ Negative types: ${validTypes}`
148133
+ );
148134
+ }
148135
+ const state = getLastQueryState();
148136
+ const targetQueryId = queryId ?? state.lastQueryId;
148137
+ const targetQuery = state.lastQuery;
148138
+ if (!targetQueryId) {
148139
+ return errorResult(
148140
+ "No query to provide feedback for. Make an eval query first, or use --query-id to specify one."
148141
+ );
148142
+ }
148143
+ const apiClient = session.getAPIClient();
148144
+ if (!apiClient) {
148145
+ return errorResult(
148146
+ "Not connected to API. Please configure connection first."
148147
+ );
148148
+ }
148149
+ if (!session.isTokenValidated()) {
148150
+ return errorResult(
148151
+ "Not authenticated. Please check your API token."
148152
+ );
148153
+ }
148154
+ try {
148155
+ const client = getGenAIClient(apiClient);
148156
+ if (positive) {
148157
+ await client.evalFeedback({
148158
+ query: targetQuery ?? "",
148159
+ query_id: targetQueryId,
148160
+ namespace: state.namespace || namespace,
148161
+ positive_feedback: {},
148162
+ comment: comment ?? void 0
148163
+ });
148164
+ return successResult([
148165
+ "[EVAL MODE] Positive feedback submitted successfully.",
148166
+ `Query ID: ${targetQueryId}`
148167
+ ]);
148168
+ }
148169
+ await client.evalFeedback({
148170
+ query: targetQuery ?? "",
148171
+ query_id: targetQueryId,
148172
+ namespace: state.namespace || namespace,
148173
+ negative_feedback: {
148174
+ remarks: negativeType ? [negativeType] : ["OTHER"]
148175
+ },
148176
+ comment: comment ?? void 0
148177
+ });
148178
+ return successResult([
148179
+ "[EVAL MODE] Negative feedback submitted successfully.",
148180
+ `Query ID: ${targetQueryId}`,
148181
+ `Reason: ${negativeType ?? "OTHER"}`,
148182
+ ...comment ? [`Comment: ${comment}`] : []
148183
+ ]);
148184
+ } catch (error) {
148185
+ const message = error instanceof Error ? error.message : String(error);
148186
+ return errorResult(`Eval feedback submission failed: ${message}`);
148187
+ }
148188
+ }
148189
+ };
148190
+ var evalSubcommands = {
148191
+ name: "eval",
148192
+ description: "Eval mode commands for RBAC testing and permission validation. Use these endpoints to test AI assistant queries and feedback without affecting production analytics. Useful for administrators validating access controls.",
148193
+ descriptionShort: "RBAC testing mode commands",
148194
+ descriptionMedium: "Query and provide feedback in eval mode for RBAC testing and permission validation.",
148195
+ commands: /* @__PURE__ */ new Map([
148196
+ ["query", evalQueryCommand],
148197
+ ["feedback", evalFeedbackCommand]
148198
+ ]),
148199
+ defaultCommand: evalQueryCommand
148200
+ };
148201
+
148202
+ // src/domains/generative_ai/index.ts
148203
+ var generativeAiDomain = {
148204
+ name: "generative_ai",
148205
+ description: "Interact with the F5 Distributed Cloud AI assistant for natural language queries about platform operations. Ask questions about load balancers, WAF configurations, site status, security events, or any platform topic. Supports single queries with follow-up suggestions, interactive multi-turn chat sessions, and feedback submission to improve AI responses.",
148206
+ descriptionShort: "AI assistant queries and feedback",
148207
+ descriptionMedium: "Query the AI assistant for help with F5 XC platform operations, configurations, security analysis, and troubleshooting.",
148208
+ defaultCommand: queryCommand,
148209
+ commands: /* @__PURE__ */ new Map([
148210
+ ["query", queryCommand],
148211
+ ["chat", chatCommand],
148212
+ ["feedback", feedbackCommand]
148213
+ ]),
148214
+ subcommands: /* @__PURE__ */ new Map([["eval", evalSubcommands]])
148215
+ };
148216
+ var generativeAiAliases = ["ai", "genai", "assistant"];
148217
+
147146
148218
  // src/domains/index.ts
147147
148219
  customDomains.register(loginDomain);
147148
148220
  customDomains.register(cloudstatusDomain);
147149
148221
  customDomains.register(completionDomain);
148222
+ customDomains.register(generativeAiDomain);
147150
148223
  for (const domain of customDomains.all()) {
147151
148224
  completionRegistry.registerDomain(fromCustomDomain(domain));
147152
148225
  }
@@ -147159,6 +148232,9 @@ var domainAliases = /* @__PURE__ */ new Map();
147159
148232
  for (const alias of cloudstatusAliases) {
147160
148233
  domainAliases.set(alias, "cloudstatus");
147161
148234
  }
148235
+ for (const alias of generativeAiAliases) {
148236
+ domainAliases.set(alias, "generative_ai");
148237
+ }
147162
148238
  function resolveDomainAlias(name) {
147163
148239
  return domainAliases.get(name) ?? name;
147164
148240
  }
@@ -149927,7 +151003,7 @@ function App2({ initialSession } = {}) {
149927
151003
  }
149928
151004
 
149929
151005
  // src/headless/controller.ts
149930
- import * as readline from "readline";
151006
+ import * as readline2 from "readline";
149931
151007
 
149932
151008
  // src/headless/protocol.ts
149933
151009
  function parseInput2(line) {
@@ -150202,7 +151278,7 @@ var HeadlessController = class {
150202
151278
  */
150203
151279
  async run() {
150204
151280
  await this.initialize();
150205
- this.rl = readline.createInterface({
151281
+ this.rl = readline2.createInterface({
150206
151282
  input: process.stdin,
150207
151283
  output: process.stdout,
150208
151284
  terminal: false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robinmordasiewicz/f5xc-xcsh",
3
- "version": "1.0.90-2601021717",
3
+ "version": "1.0.90-2601022257",
4
4
  "description": "F5 Distributed Cloud Shell - Interactive CLI for F5 XC",
5
5
  "type": "module",
6
6
  "bin": {