@robinmordasiewicz/f5xc-xcsh 1.0.90-2601021717 → 1.0.90-2601022220
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 +4 -4
- package/completions/xcsh.bash +9 -1
- package/completions/xcsh.fish +7 -14
- package/dist/index.js +1073 -4
- package/package.json +1 -1
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
|
|
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
|
;;
|
package/completions/xcsh.bash
CHANGED
|
@@ -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
|
|
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
|
package/completions/xcsh.fish
CHANGED
|
@@ -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
|
|
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-
|
|
46634
|
-
return "v1.0.90-
|
|
46633
|
+
if ("v1.0.90-2601022220") {
|
|
46634
|
+
return "v1.0.90-2601022220";
|
|
46635
46635
|
}
|
|
46636
46636
|
if (process.env.XCSH_VERSION) {
|
|
46637
46637
|
return process.env.XCSH_VERSION;
|
|
@@ -147143,10 +147143,1076 @@ var completionDomain = {
|
|
|
147143
147143
|
subcommands: /* @__PURE__ */ new Map()
|
|
147144
147144
|
};
|
|
147145
147145
|
|
|
147146
|
+
// src/domains/generative_ai/client.ts
|
|
147147
|
+
var GenAIClient = class {
|
|
147148
|
+
constructor(apiClient) {
|
|
147149
|
+
this.apiClient = apiClient;
|
|
147150
|
+
}
|
|
147151
|
+
/**
|
|
147152
|
+
* Query the AI assistant
|
|
147153
|
+
*
|
|
147154
|
+
* @param namespace - The namespace context for the query
|
|
147155
|
+
* @param query - The natural language query
|
|
147156
|
+
* @returns The AI assistant response with query_id and response data
|
|
147157
|
+
*/
|
|
147158
|
+
async query(namespace, query) {
|
|
147159
|
+
const request = {
|
|
147160
|
+
current_query: query,
|
|
147161
|
+
namespace
|
|
147162
|
+
};
|
|
147163
|
+
const response = await this.apiClient.post(
|
|
147164
|
+
`/api/gen-ai/namespaces/${namespace}/query`,
|
|
147165
|
+
request
|
|
147166
|
+
);
|
|
147167
|
+
if (!response.ok) {
|
|
147168
|
+
throw new Error(
|
|
147169
|
+
`GenAI query failed: ${response.statusCode} - ${JSON.stringify(response.data)}`
|
|
147170
|
+
);
|
|
147171
|
+
}
|
|
147172
|
+
return response.data;
|
|
147173
|
+
}
|
|
147174
|
+
/**
|
|
147175
|
+
* Submit feedback for a query
|
|
147176
|
+
*
|
|
147177
|
+
* @param request - The feedback request with query_id and feedback type
|
|
147178
|
+
*/
|
|
147179
|
+
async feedback(request) {
|
|
147180
|
+
const response = await this.apiClient.post(
|
|
147181
|
+
`/api/gen-ai/namespaces/${request.namespace}/query_feedback`,
|
|
147182
|
+
request
|
|
147183
|
+
);
|
|
147184
|
+
if (!response.ok) {
|
|
147185
|
+
throw new Error(
|
|
147186
|
+
`GenAI feedback failed: ${response.statusCode} - ${JSON.stringify(response.data)}`
|
|
147187
|
+
);
|
|
147188
|
+
}
|
|
147189
|
+
}
|
|
147190
|
+
/**
|
|
147191
|
+
* Query the AI assistant in eval mode (for RBAC testing)
|
|
147192
|
+
*
|
|
147193
|
+
* @param namespace - The namespace context for the query
|
|
147194
|
+
* @param query - The natural language query
|
|
147195
|
+
* @returns The AI assistant response
|
|
147196
|
+
*/
|
|
147197
|
+
async evalQuery(namespace, query) {
|
|
147198
|
+
const request = {
|
|
147199
|
+
current_query: query,
|
|
147200
|
+
namespace
|
|
147201
|
+
};
|
|
147202
|
+
const response = await this.apiClient.post(
|
|
147203
|
+
`/api/gen-ai/namespaces/${namespace}/eval_query`,
|
|
147204
|
+
request
|
|
147205
|
+
);
|
|
147206
|
+
if (!response.ok) {
|
|
147207
|
+
throw new Error(
|
|
147208
|
+
`GenAI eval query failed: ${response.statusCode} - ${JSON.stringify(response.data)}`
|
|
147209
|
+
);
|
|
147210
|
+
}
|
|
147211
|
+
return response.data;
|
|
147212
|
+
}
|
|
147213
|
+
/**
|
|
147214
|
+
* Submit feedback for an eval query
|
|
147215
|
+
*
|
|
147216
|
+
* @param request - The feedback request with query_id and feedback type
|
|
147217
|
+
*/
|
|
147218
|
+
async evalFeedback(request) {
|
|
147219
|
+
const response = await this.apiClient.post(
|
|
147220
|
+
`/api/gen-ai/namespaces/${request.namespace}/eval_query_feedback`,
|
|
147221
|
+
request
|
|
147222
|
+
);
|
|
147223
|
+
if (!response.ok) {
|
|
147224
|
+
throw new Error(
|
|
147225
|
+
`GenAI eval feedback failed: ${response.statusCode} - ${JSON.stringify(response.data)}`
|
|
147226
|
+
);
|
|
147227
|
+
}
|
|
147228
|
+
}
|
|
147229
|
+
};
|
|
147230
|
+
var cachedClient = null;
|
|
147231
|
+
function getGenAIClient(apiClient) {
|
|
147232
|
+
if (!cachedClient) {
|
|
147233
|
+
cachedClient = new GenAIClient(apiClient);
|
|
147234
|
+
}
|
|
147235
|
+
return cachedClient;
|
|
147236
|
+
}
|
|
147237
|
+
|
|
147238
|
+
// src/domains/generative_ai/types.ts
|
|
147239
|
+
function getResponseType(response) {
|
|
147240
|
+
if (response.generic_response) return "generic_response";
|
|
147241
|
+
if (response.explain_log) return "explain_log";
|
|
147242
|
+
if (response.gen_dashboard_filter) return "gen_dashboard_filter";
|
|
147243
|
+
if (response.list_response) return "list_response";
|
|
147244
|
+
if (response.site_analysis_response) return "site_analysis_response";
|
|
147245
|
+
if (response.widget_response) return "widget_response";
|
|
147246
|
+
return null;
|
|
147247
|
+
}
|
|
147248
|
+
var FEEDBACK_TYPE_MAP = {
|
|
147249
|
+
other: "OTHER",
|
|
147250
|
+
inaccurate: "INACCURATE_DATA",
|
|
147251
|
+
irrelevant: "IRRELEVANT_CONTENT",
|
|
147252
|
+
poor_format: "POOR_FORMAT",
|
|
147253
|
+
slow: "SLOW_RESPONSE"
|
|
147254
|
+
};
|
|
147255
|
+
function getValidFeedbackTypes() {
|
|
147256
|
+
return Object.keys(FEEDBACK_TYPE_MAP);
|
|
147257
|
+
}
|
|
147258
|
+
|
|
147259
|
+
// src/domains/generative_ai/response-renderer.ts
|
|
147260
|
+
function renderResponse(response) {
|
|
147261
|
+
const lines = [];
|
|
147262
|
+
const responseType = getResponseType(response);
|
|
147263
|
+
switch (responseType) {
|
|
147264
|
+
case "generic_response":
|
|
147265
|
+
lines.push(...renderGenericResponse(response.generic_response));
|
|
147266
|
+
break;
|
|
147267
|
+
case "explain_log":
|
|
147268
|
+
lines.push(...renderExplainLog(response.explain_log));
|
|
147269
|
+
break;
|
|
147270
|
+
case "gen_dashboard_filter":
|
|
147271
|
+
lines.push(
|
|
147272
|
+
...renderDashboardFilter(response.gen_dashboard_filter)
|
|
147273
|
+
);
|
|
147274
|
+
break;
|
|
147275
|
+
case "list_response":
|
|
147276
|
+
lines.push(...renderListResponse(response.list_response));
|
|
147277
|
+
break;
|
|
147278
|
+
case "site_analysis_response":
|
|
147279
|
+
lines.push(...renderSiteAnalysis(response.site_analysis_response));
|
|
147280
|
+
break;
|
|
147281
|
+
case "widget_response":
|
|
147282
|
+
lines.push(...renderWidgetResponse(response.widget_response));
|
|
147283
|
+
break;
|
|
147284
|
+
default:
|
|
147285
|
+
lines.push("No response content.");
|
|
147286
|
+
}
|
|
147287
|
+
if (response.follow_up_queries && response.follow_up_queries.length > 0) {
|
|
147288
|
+
lines.push("");
|
|
147289
|
+
lines.push("Suggested follow-up questions:");
|
|
147290
|
+
response.follow_up_queries.forEach((q, i) => {
|
|
147291
|
+
lines.push(` ${i + 1}. ${q}`);
|
|
147292
|
+
});
|
|
147293
|
+
}
|
|
147294
|
+
return lines;
|
|
147295
|
+
}
|
|
147296
|
+
function renderGenericResponse(response) {
|
|
147297
|
+
const lines = [];
|
|
147298
|
+
if (response.error) {
|
|
147299
|
+
lines.push(`Error: ${response.error}`);
|
|
147300
|
+
return lines;
|
|
147301
|
+
}
|
|
147302
|
+
if (response.text) {
|
|
147303
|
+
lines.push(...response.text.split("\n"));
|
|
147304
|
+
}
|
|
147305
|
+
if (response.links && response.links.length > 0) {
|
|
147306
|
+
lines.push("");
|
|
147307
|
+
lines.push("Related links:");
|
|
147308
|
+
for (const link3 of response.links) {
|
|
147309
|
+
lines.push(` - ${link3.title}: ${link3.url}`);
|
|
147310
|
+
}
|
|
147311
|
+
}
|
|
147312
|
+
return lines;
|
|
147313
|
+
}
|
|
147314
|
+
function renderExplainLog(response) {
|
|
147315
|
+
const lines = [];
|
|
147316
|
+
lines.push("=== Security Event Analysis ===");
|
|
147317
|
+
lines.push("");
|
|
147318
|
+
if (response.summary) {
|
|
147319
|
+
lines.push("Summary:");
|
|
147320
|
+
lines.push(` ${response.summary}`);
|
|
147321
|
+
lines.push("");
|
|
147322
|
+
}
|
|
147323
|
+
if (response.action) {
|
|
147324
|
+
lines.push(`Action Taken: ${response.action}`);
|
|
147325
|
+
}
|
|
147326
|
+
if (response.accuracy) {
|
|
147327
|
+
lines.push(`Accuracy: ${response.accuracy}`);
|
|
147328
|
+
}
|
|
147329
|
+
if (response.violations && response.violations.length > 0) {
|
|
147330
|
+
lines.push("");
|
|
147331
|
+
lines.push("Violations Detected:");
|
|
147332
|
+
for (const v of response.violations) {
|
|
147333
|
+
lines.push(` - ${v.name}: ${v.description}`);
|
|
147334
|
+
}
|
|
147335
|
+
}
|
|
147336
|
+
if (response.threat_campaigns && response.threat_campaigns.length > 0) {
|
|
147337
|
+
lines.push("");
|
|
147338
|
+
lines.push("Threat Campaigns:");
|
|
147339
|
+
for (const campaign of response.threat_campaigns) {
|
|
147340
|
+
lines.push(` - ${campaign}`);
|
|
147341
|
+
}
|
|
147342
|
+
}
|
|
147343
|
+
if (response.request_details) {
|
|
147344
|
+
lines.push("");
|
|
147345
|
+
lines.push("Request Details:");
|
|
147346
|
+
lines.push(` ${JSON.stringify(response.request_details, null, 2)}`);
|
|
147347
|
+
}
|
|
147348
|
+
return lines;
|
|
147349
|
+
}
|
|
147350
|
+
function renderDashboardFilter(response) {
|
|
147351
|
+
const lines = [];
|
|
147352
|
+
lines.push("=== Dashboard Filter ===");
|
|
147353
|
+
lines.push("");
|
|
147354
|
+
if (response.filter_expression) {
|
|
147355
|
+
lines.push("Filter Expression:");
|
|
147356
|
+
lines.push(` ${response.filter_expression}`);
|
|
147357
|
+
}
|
|
147358
|
+
if (response.dashboard_context) {
|
|
147359
|
+
lines.push("");
|
|
147360
|
+
lines.push("Dashboard Context:");
|
|
147361
|
+
lines.push(` ${response.dashboard_context}`);
|
|
147362
|
+
}
|
|
147363
|
+
return lines;
|
|
147364
|
+
}
|
|
147365
|
+
function renderListResponse(response) {
|
|
147366
|
+
const lines = [];
|
|
147367
|
+
if (response.formatted_list) {
|
|
147368
|
+
lines.push(response.formatted_list);
|
|
147369
|
+
return lines;
|
|
147370
|
+
}
|
|
147371
|
+
if (response.items && response.items.length > 0) {
|
|
147372
|
+
if (response.total_count !== void 0) {
|
|
147373
|
+
lines.push(`Total: ${response.total_count} items`);
|
|
147374
|
+
lines.push("");
|
|
147375
|
+
}
|
|
147376
|
+
for (const item of response.items) {
|
|
147377
|
+
lines.push(JSON.stringify(item, null, 2));
|
|
147378
|
+
lines.push("");
|
|
147379
|
+
}
|
|
147380
|
+
} else {
|
|
147381
|
+
lines.push("No items found.");
|
|
147382
|
+
}
|
|
147383
|
+
return lines;
|
|
147384
|
+
}
|
|
147385
|
+
function renderSiteAnalysis(response) {
|
|
147386
|
+
const lines = [];
|
|
147387
|
+
lines.push("=== Site Analysis ===");
|
|
147388
|
+
lines.push("");
|
|
147389
|
+
if (response.site_name) {
|
|
147390
|
+
lines.push(`Site: ${response.site_name}`);
|
|
147391
|
+
}
|
|
147392
|
+
if (response.health_status) {
|
|
147393
|
+
lines.push(`Health: ${response.health_status}`);
|
|
147394
|
+
}
|
|
147395
|
+
if (response.metrics) {
|
|
147396
|
+
lines.push("");
|
|
147397
|
+
lines.push("Metrics:");
|
|
147398
|
+
for (const [key, value] of Object.entries(response.metrics)) {
|
|
147399
|
+
lines.push(` ${key}: ${JSON.stringify(value)}`);
|
|
147400
|
+
}
|
|
147401
|
+
}
|
|
147402
|
+
if (response.recommendations && response.recommendations.length > 0) {
|
|
147403
|
+
lines.push("");
|
|
147404
|
+
lines.push("Recommendations:");
|
|
147405
|
+
for (const rec of response.recommendations) {
|
|
147406
|
+
lines.push(` - ${rec}`);
|
|
147407
|
+
}
|
|
147408
|
+
}
|
|
147409
|
+
return lines;
|
|
147410
|
+
}
|
|
147411
|
+
function renderWidgetResponse(response) {
|
|
147412
|
+
const lines = [];
|
|
147413
|
+
if (response.display_type) {
|
|
147414
|
+
lines.push(`Widget Type: ${response.display_type}`);
|
|
147415
|
+
lines.push("");
|
|
147416
|
+
}
|
|
147417
|
+
if (response.table) {
|
|
147418
|
+
for (const row of response.table.rows) {
|
|
147419
|
+
const cells = row.cells.map((cell) => {
|
|
147420
|
+
let value = cell.value;
|
|
147421
|
+
if (cell.properties?.status_style) {
|
|
147422
|
+
value = `[${cell.properties.status_style}] ${value}`;
|
|
147423
|
+
}
|
|
147424
|
+
return value;
|
|
147425
|
+
});
|
|
147426
|
+
lines.push(cells.join(" | "));
|
|
147427
|
+
}
|
|
147428
|
+
}
|
|
147429
|
+
if (response.links && response.links.length > 0) {
|
|
147430
|
+
lines.push("");
|
|
147431
|
+
lines.push("Links:");
|
|
147432
|
+
for (const link3 of response.links) {
|
|
147433
|
+
lines.push(` - ${link3.title}: ${link3.url}`);
|
|
147434
|
+
}
|
|
147435
|
+
}
|
|
147436
|
+
return lines;
|
|
147437
|
+
}
|
|
147438
|
+
|
|
147439
|
+
// src/domains/generative_ai/query.ts
|
|
147440
|
+
var lastQueryState = {
|
|
147441
|
+
namespace: "",
|
|
147442
|
+
lastQueryId: null,
|
|
147443
|
+
lastQuery: null,
|
|
147444
|
+
followUpQueries: [],
|
|
147445
|
+
isActive: false
|
|
147446
|
+
};
|
|
147447
|
+
function getLastQueryState() {
|
|
147448
|
+
return lastQueryState;
|
|
147449
|
+
}
|
|
147450
|
+
function updateLastQueryState(partial) {
|
|
147451
|
+
lastQueryState = { ...lastQueryState, ...partial };
|
|
147452
|
+
}
|
|
147453
|
+
function clearLastQueryState() {
|
|
147454
|
+
lastQueryState = {
|
|
147455
|
+
namespace: "",
|
|
147456
|
+
lastQueryId: null,
|
|
147457
|
+
lastQuery: null,
|
|
147458
|
+
followUpQueries: [],
|
|
147459
|
+
isActive: false
|
|
147460
|
+
};
|
|
147461
|
+
}
|
|
147462
|
+
function parseQueryArgs(args, session) {
|
|
147463
|
+
const { options, remainingArgs } = parseDomainOutputFlags(
|
|
147464
|
+
args,
|
|
147465
|
+
session.getOutputFormat()
|
|
147466
|
+
);
|
|
147467
|
+
let spec = false;
|
|
147468
|
+
let namespace = session.getNamespace();
|
|
147469
|
+
const questionParts = [];
|
|
147470
|
+
let i = 0;
|
|
147471
|
+
while (i < remainingArgs.length) {
|
|
147472
|
+
const arg = remainingArgs[i];
|
|
147473
|
+
if (arg === "--spec") {
|
|
147474
|
+
spec = true;
|
|
147475
|
+
} else if (arg === "--namespace" || arg === "-ns") {
|
|
147476
|
+
if (i + 1 < remainingArgs.length) {
|
|
147477
|
+
namespace = remainingArgs[i + 1] ?? namespace;
|
|
147478
|
+
i++;
|
|
147479
|
+
}
|
|
147480
|
+
} else {
|
|
147481
|
+
questionParts.push(arg ?? "");
|
|
147482
|
+
}
|
|
147483
|
+
i++;
|
|
147484
|
+
}
|
|
147485
|
+
return {
|
|
147486
|
+
format: options.format,
|
|
147487
|
+
noColor: options.noColor,
|
|
147488
|
+
spec,
|
|
147489
|
+
namespace,
|
|
147490
|
+
question: questionParts.join(" ")
|
|
147491
|
+
};
|
|
147492
|
+
}
|
|
147493
|
+
var queryCommand = {
|
|
147494
|
+
name: "query",
|
|
147495
|
+
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.",
|
|
147496
|
+
descriptionShort: "Query the AI assistant",
|
|
147497
|
+
descriptionMedium: "Send natural language queries to the AI assistant for help with F5 XC platform operations, configurations, and troubleshooting.",
|
|
147498
|
+
usage: "<question> [--namespace <ns>]",
|
|
147499
|
+
aliases: ["ask", "q"],
|
|
147500
|
+
async execute(args, session) {
|
|
147501
|
+
const { format, noColor, spec, namespace, question } = parseQueryArgs(
|
|
147502
|
+
args,
|
|
147503
|
+
session
|
|
147504
|
+
);
|
|
147505
|
+
if (spec) {
|
|
147506
|
+
const cmdSpec = getCommandSpec("generative_ai query");
|
|
147507
|
+
if (cmdSpec) {
|
|
147508
|
+
return successResult([formatSpec(cmdSpec)]);
|
|
147509
|
+
}
|
|
147510
|
+
}
|
|
147511
|
+
if (!question.trim()) {
|
|
147512
|
+
return errorResult(
|
|
147513
|
+
"Please provide a question. Usage: ai query <question>"
|
|
147514
|
+
);
|
|
147515
|
+
}
|
|
147516
|
+
const apiClient = session.getAPIClient();
|
|
147517
|
+
if (!apiClient) {
|
|
147518
|
+
return errorResult(
|
|
147519
|
+
"Not connected to API. Please configure connection first."
|
|
147520
|
+
);
|
|
147521
|
+
}
|
|
147522
|
+
if (!session.isTokenValidated()) {
|
|
147523
|
+
return errorResult(
|
|
147524
|
+
"Not authenticated. Please check your API token."
|
|
147525
|
+
);
|
|
147526
|
+
}
|
|
147527
|
+
try {
|
|
147528
|
+
const client = getGenAIClient(apiClient);
|
|
147529
|
+
const response = await client.query(namespace, question);
|
|
147530
|
+
updateLastQueryState({
|
|
147531
|
+
namespace,
|
|
147532
|
+
lastQueryId: response.query_id,
|
|
147533
|
+
lastQuery: question,
|
|
147534
|
+
followUpQueries: response.follow_up_queries ?? [],
|
|
147535
|
+
isActive: true
|
|
147536
|
+
});
|
|
147537
|
+
if (format === "none") {
|
|
147538
|
+
return successResult([]);
|
|
147539
|
+
}
|
|
147540
|
+
if (format === "json" || format === "yaml" || format === "tsv") {
|
|
147541
|
+
return successResult(
|
|
147542
|
+
formatDomainOutput(response, { format, noColor })
|
|
147543
|
+
);
|
|
147544
|
+
}
|
|
147545
|
+
const lines = renderResponse(response);
|
|
147546
|
+
return successResult(lines);
|
|
147547
|
+
} catch (error) {
|
|
147548
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147549
|
+
return errorResult(`Query failed: ${message}`);
|
|
147550
|
+
}
|
|
147551
|
+
}
|
|
147552
|
+
};
|
|
147553
|
+
|
|
147554
|
+
// src/domains/generative_ai/chat.ts
|
|
147555
|
+
import * as readline from "readline";
|
|
147556
|
+
function parseChatArgs(args, session) {
|
|
147557
|
+
const { remainingArgs } = parseDomainOutputFlags(
|
|
147558
|
+
args,
|
|
147559
|
+
session.getOutputFormat()
|
|
147560
|
+
);
|
|
147561
|
+
let spec = false;
|
|
147562
|
+
let namespace = session.getNamespace();
|
|
147563
|
+
let i = 0;
|
|
147564
|
+
while (i < remainingArgs.length) {
|
|
147565
|
+
const arg = remainingArgs[i];
|
|
147566
|
+
if (arg === "--spec") {
|
|
147567
|
+
spec = true;
|
|
147568
|
+
} else if (arg === "--namespace" || arg === "-ns") {
|
|
147569
|
+
if (i + 1 < remainingArgs.length) {
|
|
147570
|
+
namespace = remainingArgs[i + 1] ?? namespace;
|
|
147571
|
+
i++;
|
|
147572
|
+
}
|
|
147573
|
+
}
|
|
147574
|
+
i++;
|
|
147575
|
+
}
|
|
147576
|
+
return { spec, namespace };
|
|
147577
|
+
}
|
|
147578
|
+
function showChatHelp() {
|
|
147579
|
+
return [
|
|
147580
|
+
"",
|
|
147581
|
+
"=== AI Chat Commands ===",
|
|
147582
|
+
"",
|
|
147583
|
+
" /exit, /quit, /q - Exit chat mode",
|
|
147584
|
+
" /help, /h - Show this help",
|
|
147585
|
+
" /clear, /c - Clear conversation context",
|
|
147586
|
+
" /feedback <type> - Submit feedback for last response",
|
|
147587
|
+
" Types: positive, negative",
|
|
147588
|
+
" 1, 2, 3... - Select a follow-up question by number",
|
|
147589
|
+
"",
|
|
147590
|
+
"Just type your question to query the AI assistant.",
|
|
147591
|
+
""
|
|
147592
|
+
];
|
|
147593
|
+
}
|
|
147594
|
+
async function handleFeedback(input, session) {
|
|
147595
|
+
const state = getLastQueryState();
|
|
147596
|
+
if (!state.lastQueryId || !state.lastQuery) {
|
|
147597
|
+
return ["No previous query to provide feedback for."];
|
|
147598
|
+
}
|
|
147599
|
+
const parts = input.split(/\s+/);
|
|
147600
|
+
const feedbackType = parts[1]?.toLowerCase();
|
|
147601
|
+
if (!feedbackType) {
|
|
147602
|
+
return [
|
|
147603
|
+
"Usage: /feedback <positive|negative>",
|
|
147604
|
+
" Optional: /feedback negative <type> [comment]",
|
|
147605
|
+
" Types: other, inaccurate, irrelevant, poor_format, slow"
|
|
147606
|
+
];
|
|
147607
|
+
}
|
|
147608
|
+
const apiClient = session.getAPIClient();
|
|
147609
|
+
if (!apiClient) {
|
|
147610
|
+
return ["Not connected to API."];
|
|
147611
|
+
}
|
|
147612
|
+
try {
|
|
147613
|
+
const client = getGenAIClient(apiClient);
|
|
147614
|
+
if (feedbackType === "positive" || feedbackType === "+") {
|
|
147615
|
+
await client.feedback({
|
|
147616
|
+
query: state.lastQuery,
|
|
147617
|
+
query_id: state.lastQueryId,
|
|
147618
|
+
namespace: state.namespace,
|
|
147619
|
+
positive_feedback: {}
|
|
147620
|
+
});
|
|
147621
|
+
return ["Positive feedback submitted. Thank you!"];
|
|
147622
|
+
}
|
|
147623
|
+
if (feedbackType === "negative" || feedbackType === "-") {
|
|
147624
|
+
const negType = parts[2]?.toLowerCase();
|
|
147625
|
+
const mappedType = negType ? FEEDBACK_TYPE_MAP[negType] : void 0;
|
|
147626
|
+
const comment = parts.slice(3).join(" ") || void 0;
|
|
147627
|
+
await client.feedback({
|
|
147628
|
+
query: state.lastQuery,
|
|
147629
|
+
query_id: state.lastQueryId,
|
|
147630
|
+
namespace: state.namespace,
|
|
147631
|
+
negative_feedback: {
|
|
147632
|
+
remarks: mappedType ? [mappedType] : ["OTHER"]
|
|
147633
|
+
},
|
|
147634
|
+
comment
|
|
147635
|
+
});
|
|
147636
|
+
return [
|
|
147637
|
+
"Negative feedback submitted. Thank you for helping improve the AI."
|
|
147638
|
+
];
|
|
147639
|
+
}
|
|
147640
|
+
return [
|
|
147641
|
+
`Unknown feedback type: ${feedbackType}`,
|
|
147642
|
+
"Use 'positive' or 'negative'."
|
|
147643
|
+
];
|
|
147644
|
+
} catch (error) {
|
|
147645
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147646
|
+
return [`Feedback failed: ${message}`];
|
|
147647
|
+
}
|
|
147648
|
+
}
|
|
147649
|
+
async function runChatLoop(session, namespace) {
|
|
147650
|
+
const apiClient = session.getAPIClient();
|
|
147651
|
+
if (!apiClient) {
|
|
147652
|
+
return ["Not connected to API. Please configure connection first."];
|
|
147653
|
+
}
|
|
147654
|
+
if (!session.isTokenValidated()) {
|
|
147655
|
+
return ["Not authenticated. Please check your API token."];
|
|
147656
|
+
}
|
|
147657
|
+
const client = getGenAIClient(apiClient);
|
|
147658
|
+
const output = [];
|
|
147659
|
+
output.push("");
|
|
147660
|
+
output.push("=== F5 XC AI Assistant Chat ===");
|
|
147661
|
+
output.push(`Namespace: ${namespace}`);
|
|
147662
|
+
output.push("Type /help for commands, /exit to quit.");
|
|
147663
|
+
output.push("");
|
|
147664
|
+
const rl = readline.createInterface({
|
|
147665
|
+
input: process.stdin,
|
|
147666
|
+
output: process.stdout,
|
|
147667
|
+
terminal: process.stdin.isTTY ?? false
|
|
147668
|
+
});
|
|
147669
|
+
let interrupted = false;
|
|
147670
|
+
rl.on("SIGINT", () => {
|
|
147671
|
+
interrupted = true;
|
|
147672
|
+
console.log("\n(Use /exit to leave chat mode)");
|
|
147673
|
+
rl.prompt();
|
|
147674
|
+
});
|
|
147675
|
+
const askQuestion = (prompt) => {
|
|
147676
|
+
return new Promise((resolve) => {
|
|
147677
|
+
rl.question(prompt, (answer) => {
|
|
147678
|
+
resolve(answer);
|
|
147679
|
+
});
|
|
147680
|
+
});
|
|
147681
|
+
};
|
|
147682
|
+
for (const line of output) {
|
|
147683
|
+
console.log(line);
|
|
147684
|
+
}
|
|
147685
|
+
let running = true;
|
|
147686
|
+
while (running && !interrupted) {
|
|
147687
|
+
const input = await askQuestion("ai> ");
|
|
147688
|
+
if (interrupted) {
|
|
147689
|
+
break;
|
|
147690
|
+
}
|
|
147691
|
+
const trimmed = input.trim();
|
|
147692
|
+
if (!trimmed) {
|
|
147693
|
+
continue;
|
|
147694
|
+
}
|
|
147695
|
+
if (trimmed === "/exit" || trimmed === "/quit" || trimmed === "/q") {
|
|
147696
|
+
console.log("Exiting chat mode.");
|
|
147697
|
+
running = false;
|
|
147698
|
+
break;
|
|
147699
|
+
}
|
|
147700
|
+
if (trimmed === "/help" || trimmed === "/h") {
|
|
147701
|
+
for (const line of showChatHelp()) {
|
|
147702
|
+
console.log(line);
|
|
147703
|
+
}
|
|
147704
|
+
continue;
|
|
147705
|
+
}
|
|
147706
|
+
if (trimmed === "/clear" || trimmed === "/c") {
|
|
147707
|
+
clearLastQueryState();
|
|
147708
|
+
console.log("Conversation context cleared.");
|
|
147709
|
+
continue;
|
|
147710
|
+
}
|
|
147711
|
+
if (trimmed.startsWith("/feedback")) {
|
|
147712
|
+
const feedbackLines = await handleFeedback(trimmed, session);
|
|
147713
|
+
for (const line of feedbackLines) {
|
|
147714
|
+
console.log(line);
|
|
147715
|
+
}
|
|
147716
|
+
continue;
|
|
147717
|
+
}
|
|
147718
|
+
if (/^\d+$/.test(trimmed)) {
|
|
147719
|
+
const num = parseInt(trimmed, 10);
|
|
147720
|
+
const state = getLastQueryState();
|
|
147721
|
+
if (state.followUpQueries.length > 0 && num >= 1 && num <= state.followUpQueries.length) {
|
|
147722
|
+
const followUp = state.followUpQueries[num - 1];
|
|
147723
|
+
if (followUp) {
|
|
147724
|
+
console.log(`
|
|
147725
|
+
Following up: ${followUp}
|
|
147726
|
+
`);
|
|
147727
|
+
try {
|
|
147728
|
+
const response = await client.query(
|
|
147729
|
+
namespace,
|
|
147730
|
+
followUp
|
|
147731
|
+
);
|
|
147732
|
+
updateLastQueryState({
|
|
147733
|
+
namespace,
|
|
147734
|
+
lastQueryId: response.query_id,
|
|
147735
|
+
lastQuery: followUp,
|
|
147736
|
+
followUpQueries: response.follow_up_queries ?? []
|
|
147737
|
+
});
|
|
147738
|
+
const lines = renderResponse(response);
|
|
147739
|
+
for (const line of lines) {
|
|
147740
|
+
console.log(line);
|
|
147741
|
+
}
|
|
147742
|
+
console.log("");
|
|
147743
|
+
} catch (error) {
|
|
147744
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147745
|
+
console.log(`Query failed: ${message}`);
|
|
147746
|
+
}
|
|
147747
|
+
continue;
|
|
147748
|
+
}
|
|
147749
|
+
}
|
|
147750
|
+
console.log(
|
|
147751
|
+
`Invalid selection. Choose 1-${state.followUpQueries.length} from suggested follow-ups.`
|
|
147752
|
+
);
|
|
147753
|
+
continue;
|
|
147754
|
+
}
|
|
147755
|
+
if (trimmed.startsWith("/")) {
|
|
147756
|
+
console.log(
|
|
147757
|
+
`Unknown command: ${trimmed}. Type /help for commands.`
|
|
147758
|
+
);
|
|
147759
|
+
continue;
|
|
147760
|
+
}
|
|
147761
|
+
try {
|
|
147762
|
+
const response = await client.query(namespace, trimmed);
|
|
147763
|
+
updateLastQueryState({
|
|
147764
|
+
namespace,
|
|
147765
|
+
lastQueryId: response.query_id,
|
|
147766
|
+
lastQuery: trimmed,
|
|
147767
|
+
followUpQueries: response.follow_up_queries ?? []
|
|
147768
|
+
});
|
|
147769
|
+
console.log("");
|
|
147770
|
+
const lines = renderResponse(response);
|
|
147771
|
+
for (const line of lines) {
|
|
147772
|
+
console.log(line);
|
|
147773
|
+
}
|
|
147774
|
+
console.log("");
|
|
147775
|
+
} catch (error) {
|
|
147776
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147777
|
+
console.log(`Query failed: ${message}`);
|
|
147778
|
+
}
|
|
147779
|
+
}
|
|
147780
|
+
rl.close();
|
|
147781
|
+
return ["Chat session ended."];
|
|
147782
|
+
}
|
|
147783
|
+
var chatCommand = {
|
|
147784
|
+
name: "chat",
|
|
147785
|
+
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.",
|
|
147786
|
+
descriptionShort: "Interactive AI chat mode",
|
|
147787
|
+
descriptionMedium: "Start an interactive multi-turn conversation with the AI assistant. Supports follow-up suggestions and in-chat commands.",
|
|
147788
|
+
usage: "[--namespace <ns>]",
|
|
147789
|
+
aliases: ["interactive", "i"],
|
|
147790
|
+
async execute(args, session) {
|
|
147791
|
+
const { spec, namespace } = parseChatArgs(args, session);
|
|
147792
|
+
if (spec) {
|
|
147793
|
+
const cmdSpec = getCommandSpec("generative_ai chat");
|
|
147794
|
+
if (cmdSpec) {
|
|
147795
|
+
return successResult([formatSpec(cmdSpec)]);
|
|
147796
|
+
}
|
|
147797
|
+
}
|
|
147798
|
+
if (!process.stdin.isTTY) {
|
|
147799
|
+
return errorResult(
|
|
147800
|
+
"Chat mode requires an interactive terminal. Use 'ai query' for non-interactive queries."
|
|
147801
|
+
);
|
|
147802
|
+
}
|
|
147803
|
+
try {
|
|
147804
|
+
const result = await runChatLoop(session, namespace);
|
|
147805
|
+
return successResult(result);
|
|
147806
|
+
} catch (error) {
|
|
147807
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147808
|
+
return errorResult(`Chat session failed: ${message}`);
|
|
147809
|
+
}
|
|
147810
|
+
}
|
|
147811
|
+
};
|
|
147812
|
+
|
|
147813
|
+
// src/domains/generative_ai/feedback.ts
|
|
147814
|
+
function parseFeedbackArgs(args, session) {
|
|
147815
|
+
const { remainingArgs } = parseDomainOutputFlags(
|
|
147816
|
+
args,
|
|
147817
|
+
session.getOutputFormat()
|
|
147818
|
+
);
|
|
147819
|
+
let spec = false;
|
|
147820
|
+
let namespace = session.getNamespace();
|
|
147821
|
+
let positive = false;
|
|
147822
|
+
let negativeType = null;
|
|
147823
|
+
let comment = null;
|
|
147824
|
+
let queryId = null;
|
|
147825
|
+
let i = 0;
|
|
147826
|
+
while (i < remainingArgs.length) {
|
|
147827
|
+
const arg = remainingArgs[i];
|
|
147828
|
+
if (arg === "--spec") {
|
|
147829
|
+
spec = true;
|
|
147830
|
+
} else if (arg === "--namespace" || arg === "-ns") {
|
|
147831
|
+
if (i + 1 < remainingArgs.length) {
|
|
147832
|
+
namespace = remainingArgs[i + 1] ?? namespace;
|
|
147833
|
+
i++;
|
|
147834
|
+
}
|
|
147835
|
+
} else if (arg === "--positive" || arg === "-p") {
|
|
147836
|
+
positive = true;
|
|
147837
|
+
} else if (arg === "--negative" || arg === "-n") {
|
|
147838
|
+
if (i + 1 < remainingArgs.length) {
|
|
147839
|
+
const typeArg = remainingArgs[i + 1]?.toLowerCase();
|
|
147840
|
+
if (typeArg && FEEDBACK_TYPE_MAP[typeArg]) {
|
|
147841
|
+
negativeType = FEEDBACK_TYPE_MAP[typeArg] ?? null;
|
|
147842
|
+
i++;
|
|
147843
|
+
} else {
|
|
147844
|
+
negativeType = "OTHER";
|
|
147845
|
+
}
|
|
147846
|
+
} else {
|
|
147847
|
+
negativeType = "OTHER";
|
|
147848
|
+
}
|
|
147849
|
+
} else if (arg === "--comment" || arg === "-c") {
|
|
147850
|
+
if (i + 1 < remainingArgs.length) {
|
|
147851
|
+
comment = remainingArgs[i + 1] ?? null;
|
|
147852
|
+
i++;
|
|
147853
|
+
}
|
|
147854
|
+
} else if (arg === "--query-id" || arg === "-q") {
|
|
147855
|
+
if (i + 1 < remainingArgs.length) {
|
|
147856
|
+
queryId = remainingArgs[i + 1] ?? null;
|
|
147857
|
+
i++;
|
|
147858
|
+
}
|
|
147859
|
+
}
|
|
147860
|
+
i++;
|
|
147861
|
+
}
|
|
147862
|
+
return {
|
|
147863
|
+
spec,
|
|
147864
|
+
namespace,
|
|
147865
|
+
positive,
|
|
147866
|
+
negativeType,
|
|
147867
|
+
comment,
|
|
147868
|
+
queryId
|
|
147869
|
+
};
|
|
147870
|
+
}
|
|
147871
|
+
var feedbackCommand = {
|
|
147872
|
+
name: "feedback",
|
|
147873
|
+
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.",
|
|
147874
|
+
descriptionShort: "Submit feedback for AI responses",
|
|
147875
|
+
descriptionMedium: "Provide positive or negative feedback for AI assistant responses. Use --negative with a type: inaccurate, irrelevant, poor_format, slow, or other.",
|
|
147876
|
+
usage: "--positive | --negative <type> [--comment <text>] [--query-id <id>]",
|
|
147877
|
+
aliases: ["fb", "rate"],
|
|
147878
|
+
async execute(args, session) {
|
|
147879
|
+
const { spec, namespace, positive, negativeType, comment, queryId } = parseFeedbackArgs(args, session);
|
|
147880
|
+
if (spec) {
|
|
147881
|
+
const cmdSpec = getCommandSpec("generative_ai feedback");
|
|
147882
|
+
if (cmdSpec) {
|
|
147883
|
+
return successResult([formatSpec(cmdSpec)]);
|
|
147884
|
+
}
|
|
147885
|
+
}
|
|
147886
|
+
if (!positive && !negativeType) {
|
|
147887
|
+
const validTypes = getValidFeedbackTypes().join(", ");
|
|
147888
|
+
return errorResult(
|
|
147889
|
+
`Please specify feedback type:
|
|
147890
|
+
--positive (-p) Rate the response positively
|
|
147891
|
+
--negative (-n) <type> Rate negatively with reason
|
|
147892
|
+
|
|
147893
|
+
Negative types: ${validTypes}`
|
|
147894
|
+
);
|
|
147895
|
+
}
|
|
147896
|
+
const state = getLastQueryState();
|
|
147897
|
+
const targetQueryId = queryId ?? state.lastQueryId;
|
|
147898
|
+
const targetQuery = state.lastQuery;
|
|
147899
|
+
if (!targetQueryId) {
|
|
147900
|
+
return errorResult(
|
|
147901
|
+
"No query to provide feedback for. Make a query first, or use --query-id to specify one."
|
|
147902
|
+
);
|
|
147903
|
+
}
|
|
147904
|
+
if (!targetQuery && !queryId) {
|
|
147905
|
+
return errorResult(
|
|
147906
|
+
"Could not find the original query. Use --query-id to specify the query ID."
|
|
147907
|
+
);
|
|
147908
|
+
}
|
|
147909
|
+
const apiClient = session.getAPIClient();
|
|
147910
|
+
if (!apiClient) {
|
|
147911
|
+
return errorResult(
|
|
147912
|
+
"Not connected to API. Please configure connection first."
|
|
147913
|
+
);
|
|
147914
|
+
}
|
|
147915
|
+
if (!session.isTokenValidated()) {
|
|
147916
|
+
return errorResult(
|
|
147917
|
+
"Not authenticated. Please check your API token."
|
|
147918
|
+
);
|
|
147919
|
+
}
|
|
147920
|
+
try {
|
|
147921
|
+
const client = getGenAIClient(apiClient);
|
|
147922
|
+
if (positive) {
|
|
147923
|
+
await client.feedback({
|
|
147924
|
+
query: targetQuery ?? "",
|
|
147925
|
+
query_id: targetQueryId,
|
|
147926
|
+
namespace: state.namespace || namespace,
|
|
147927
|
+
positive_feedback: {},
|
|
147928
|
+
comment: comment ?? void 0
|
|
147929
|
+
});
|
|
147930
|
+
return successResult([
|
|
147931
|
+
"Positive feedback submitted successfully.",
|
|
147932
|
+
`Query ID: ${targetQueryId}`
|
|
147933
|
+
]);
|
|
147934
|
+
}
|
|
147935
|
+
await client.feedback({
|
|
147936
|
+
query: targetQuery ?? "",
|
|
147937
|
+
query_id: targetQueryId,
|
|
147938
|
+
namespace: state.namespace || namespace,
|
|
147939
|
+
negative_feedback: {
|
|
147940
|
+
remarks: negativeType ? [negativeType] : ["OTHER"]
|
|
147941
|
+
},
|
|
147942
|
+
comment: comment ?? void 0
|
|
147943
|
+
});
|
|
147944
|
+
return successResult([
|
|
147945
|
+
"Negative feedback submitted successfully.",
|
|
147946
|
+
`Query ID: ${targetQueryId}`,
|
|
147947
|
+
`Reason: ${negativeType ?? "OTHER"}`,
|
|
147948
|
+
...comment ? [`Comment: ${comment}`] : []
|
|
147949
|
+
]);
|
|
147950
|
+
} catch (error) {
|
|
147951
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147952
|
+
return errorResult(`Feedback submission failed: ${message}`);
|
|
147953
|
+
}
|
|
147954
|
+
}
|
|
147955
|
+
};
|
|
147956
|
+
|
|
147957
|
+
// src/domains/generative_ai/eval.ts
|
|
147958
|
+
function parseEvalQueryArgs(args, session) {
|
|
147959
|
+
const { options, remainingArgs } = parseDomainOutputFlags(
|
|
147960
|
+
args,
|
|
147961
|
+
session.getOutputFormat()
|
|
147962
|
+
);
|
|
147963
|
+
let spec = false;
|
|
147964
|
+
let namespace = session.getNamespace();
|
|
147965
|
+
const questionParts = [];
|
|
147966
|
+
let i = 0;
|
|
147967
|
+
while (i < remainingArgs.length) {
|
|
147968
|
+
const arg = remainingArgs[i];
|
|
147969
|
+
if (arg === "--spec") {
|
|
147970
|
+
spec = true;
|
|
147971
|
+
} else if (arg === "--namespace" || arg === "-ns") {
|
|
147972
|
+
if (i + 1 < remainingArgs.length) {
|
|
147973
|
+
namespace = remainingArgs[i + 1] ?? namespace;
|
|
147974
|
+
i++;
|
|
147975
|
+
}
|
|
147976
|
+
} else {
|
|
147977
|
+
questionParts.push(arg ?? "");
|
|
147978
|
+
}
|
|
147979
|
+
i++;
|
|
147980
|
+
}
|
|
147981
|
+
return {
|
|
147982
|
+
format: options.format,
|
|
147983
|
+
noColor: options.noColor,
|
|
147984
|
+
spec,
|
|
147985
|
+
namespace,
|
|
147986
|
+
question: questionParts.join(" ")
|
|
147987
|
+
};
|
|
147988
|
+
}
|
|
147989
|
+
var evalQueryCommand = {
|
|
147990
|
+
name: "query",
|
|
147991
|
+
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.",
|
|
147992
|
+
descriptionShort: "Eval mode AI query",
|
|
147993
|
+
descriptionMedium: "Query the AI assistant in eval mode for RBAC testing and permission validation.",
|
|
147994
|
+
usage: "<question> [--namespace <ns>]",
|
|
147995
|
+
aliases: ["q"],
|
|
147996
|
+
async execute(args, session) {
|
|
147997
|
+
const { format, noColor, spec, namespace, question } = parseEvalQueryArgs(args, session);
|
|
147998
|
+
if (spec) {
|
|
147999
|
+
const cmdSpec = getCommandSpec("generative_ai eval query");
|
|
148000
|
+
if (cmdSpec) {
|
|
148001
|
+
return successResult([formatSpec(cmdSpec)]);
|
|
148002
|
+
}
|
|
148003
|
+
}
|
|
148004
|
+
if (!question.trim()) {
|
|
148005
|
+
return errorResult(
|
|
148006
|
+
"Please provide a question. Usage: ai eval query <question>"
|
|
148007
|
+
);
|
|
148008
|
+
}
|
|
148009
|
+
const apiClient = session.getAPIClient();
|
|
148010
|
+
if (!apiClient) {
|
|
148011
|
+
return errorResult(
|
|
148012
|
+
"Not connected to API. Please configure connection first."
|
|
148013
|
+
);
|
|
148014
|
+
}
|
|
148015
|
+
if (!session.isTokenValidated()) {
|
|
148016
|
+
return errorResult(
|
|
148017
|
+
"Not authenticated. Please check your API token."
|
|
148018
|
+
);
|
|
148019
|
+
}
|
|
148020
|
+
try {
|
|
148021
|
+
const client = getGenAIClient(apiClient);
|
|
148022
|
+
const response = await client.evalQuery(namespace, question);
|
|
148023
|
+
updateLastQueryState({
|
|
148024
|
+
namespace,
|
|
148025
|
+
lastQueryId: response.query_id,
|
|
148026
|
+
lastQuery: question,
|
|
148027
|
+
followUpQueries: response.follow_up_queries ?? [],
|
|
148028
|
+
isActive: true
|
|
148029
|
+
});
|
|
148030
|
+
if (format === "none") {
|
|
148031
|
+
return successResult([]);
|
|
148032
|
+
}
|
|
148033
|
+
if (format === "json" || format === "yaml" || format === "tsv") {
|
|
148034
|
+
return successResult(
|
|
148035
|
+
formatDomainOutput(response, { format, noColor })
|
|
148036
|
+
);
|
|
148037
|
+
}
|
|
148038
|
+
const lines = ["[EVAL MODE]", "", ...renderResponse(response)];
|
|
148039
|
+
return successResult(lines);
|
|
148040
|
+
} catch (error) {
|
|
148041
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
148042
|
+
return errorResult(`Eval query failed: ${message}`);
|
|
148043
|
+
}
|
|
148044
|
+
}
|
|
148045
|
+
};
|
|
148046
|
+
function parseEvalFeedbackArgs(args, session) {
|
|
148047
|
+
const { remainingArgs } = parseDomainOutputFlags(
|
|
148048
|
+
args,
|
|
148049
|
+
session.getOutputFormat()
|
|
148050
|
+
);
|
|
148051
|
+
let spec = false;
|
|
148052
|
+
let namespace = session.getNamespace();
|
|
148053
|
+
let positive = false;
|
|
148054
|
+
let negativeType = null;
|
|
148055
|
+
let comment = null;
|
|
148056
|
+
let queryId = null;
|
|
148057
|
+
let i = 0;
|
|
148058
|
+
while (i < remainingArgs.length) {
|
|
148059
|
+
const arg = remainingArgs[i];
|
|
148060
|
+
if (arg === "--spec") {
|
|
148061
|
+
spec = true;
|
|
148062
|
+
} else if (arg === "--namespace" || arg === "-ns") {
|
|
148063
|
+
if (i + 1 < remainingArgs.length) {
|
|
148064
|
+
namespace = remainingArgs[i + 1] ?? namespace;
|
|
148065
|
+
i++;
|
|
148066
|
+
}
|
|
148067
|
+
} else if (arg === "--positive" || arg === "-p") {
|
|
148068
|
+
positive = true;
|
|
148069
|
+
} else if (arg === "--negative" || arg === "-n") {
|
|
148070
|
+
if (i + 1 < remainingArgs.length) {
|
|
148071
|
+
const typeArg = remainingArgs[i + 1]?.toLowerCase();
|
|
148072
|
+
if (typeArg && FEEDBACK_TYPE_MAP[typeArg]) {
|
|
148073
|
+
negativeType = FEEDBACK_TYPE_MAP[typeArg] ?? null;
|
|
148074
|
+
i++;
|
|
148075
|
+
} else {
|
|
148076
|
+
negativeType = "OTHER";
|
|
148077
|
+
}
|
|
148078
|
+
} else {
|
|
148079
|
+
negativeType = "OTHER";
|
|
148080
|
+
}
|
|
148081
|
+
} else if (arg === "--comment" || arg === "-c") {
|
|
148082
|
+
if (i + 1 < remainingArgs.length) {
|
|
148083
|
+
comment = remainingArgs[i + 1] ?? null;
|
|
148084
|
+
i++;
|
|
148085
|
+
}
|
|
148086
|
+
} else if (arg === "--query-id" || arg === "-q") {
|
|
148087
|
+
if (i + 1 < remainingArgs.length) {
|
|
148088
|
+
queryId = remainingArgs[i + 1] ?? null;
|
|
148089
|
+
i++;
|
|
148090
|
+
}
|
|
148091
|
+
}
|
|
148092
|
+
i++;
|
|
148093
|
+
}
|
|
148094
|
+
return {
|
|
148095
|
+
spec,
|
|
148096
|
+
namespace,
|
|
148097
|
+
positive,
|
|
148098
|
+
negativeType,
|
|
148099
|
+
comment,
|
|
148100
|
+
queryId
|
|
148101
|
+
};
|
|
148102
|
+
}
|
|
148103
|
+
var evalFeedbackCommand = {
|
|
148104
|
+
name: "feedback",
|
|
148105
|
+
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.",
|
|
148106
|
+
descriptionShort: "Eval mode feedback submission",
|
|
148107
|
+
descriptionMedium: "Submit feedback for eval mode AI queries, keeping RBAC testing analytics separate.",
|
|
148108
|
+
usage: "--positive | --negative <type> [--comment <text>] [--query-id <id>]",
|
|
148109
|
+
aliases: ["fb"],
|
|
148110
|
+
async execute(args, session) {
|
|
148111
|
+
const { spec, namespace, positive, negativeType, comment, queryId } = parseEvalFeedbackArgs(args, session);
|
|
148112
|
+
if (spec) {
|
|
148113
|
+
const cmdSpec = getCommandSpec("generative_ai eval feedback");
|
|
148114
|
+
if (cmdSpec) {
|
|
148115
|
+
return successResult([formatSpec(cmdSpec)]);
|
|
148116
|
+
}
|
|
148117
|
+
}
|
|
148118
|
+
if (!positive && !negativeType) {
|
|
148119
|
+
const validTypes = getValidFeedbackTypes().join(", ");
|
|
148120
|
+
return errorResult(
|
|
148121
|
+
`Please specify feedback type:
|
|
148122
|
+
--positive (-p) Rate the response positively
|
|
148123
|
+
--negative (-n) <type> Rate negatively with reason
|
|
148124
|
+
|
|
148125
|
+
Negative types: ${validTypes}`
|
|
148126
|
+
);
|
|
148127
|
+
}
|
|
148128
|
+
const state = getLastQueryState();
|
|
148129
|
+
const targetQueryId = queryId ?? state.lastQueryId;
|
|
148130
|
+
const targetQuery = state.lastQuery;
|
|
148131
|
+
if (!targetQueryId) {
|
|
148132
|
+
return errorResult(
|
|
148133
|
+
"No query to provide feedback for. Make an eval query first, or use --query-id to specify one."
|
|
148134
|
+
);
|
|
148135
|
+
}
|
|
148136
|
+
const apiClient = session.getAPIClient();
|
|
148137
|
+
if (!apiClient) {
|
|
148138
|
+
return errorResult(
|
|
148139
|
+
"Not connected to API. Please configure connection first."
|
|
148140
|
+
);
|
|
148141
|
+
}
|
|
148142
|
+
if (!session.isTokenValidated()) {
|
|
148143
|
+
return errorResult(
|
|
148144
|
+
"Not authenticated. Please check your API token."
|
|
148145
|
+
);
|
|
148146
|
+
}
|
|
148147
|
+
try {
|
|
148148
|
+
const client = getGenAIClient(apiClient);
|
|
148149
|
+
if (positive) {
|
|
148150
|
+
await client.evalFeedback({
|
|
148151
|
+
query: targetQuery ?? "",
|
|
148152
|
+
query_id: targetQueryId,
|
|
148153
|
+
namespace: state.namespace || namespace,
|
|
148154
|
+
positive_feedback: {},
|
|
148155
|
+
comment: comment ?? void 0
|
|
148156
|
+
});
|
|
148157
|
+
return successResult([
|
|
148158
|
+
"[EVAL MODE] Positive feedback submitted successfully.",
|
|
148159
|
+
`Query ID: ${targetQueryId}`
|
|
148160
|
+
]);
|
|
148161
|
+
}
|
|
148162
|
+
await client.evalFeedback({
|
|
148163
|
+
query: targetQuery ?? "",
|
|
148164
|
+
query_id: targetQueryId,
|
|
148165
|
+
namespace: state.namespace || namespace,
|
|
148166
|
+
negative_feedback: {
|
|
148167
|
+
remarks: negativeType ? [negativeType] : ["OTHER"]
|
|
148168
|
+
},
|
|
148169
|
+
comment: comment ?? void 0
|
|
148170
|
+
});
|
|
148171
|
+
return successResult([
|
|
148172
|
+
"[EVAL MODE] Negative feedback submitted successfully.",
|
|
148173
|
+
`Query ID: ${targetQueryId}`,
|
|
148174
|
+
`Reason: ${negativeType ?? "OTHER"}`,
|
|
148175
|
+
...comment ? [`Comment: ${comment}`] : []
|
|
148176
|
+
]);
|
|
148177
|
+
} catch (error) {
|
|
148178
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
148179
|
+
return errorResult(`Eval feedback submission failed: ${message}`);
|
|
148180
|
+
}
|
|
148181
|
+
}
|
|
148182
|
+
};
|
|
148183
|
+
var evalSubcommands = {
|
|
148184
|
+
name: "eval",
|
|
148185
|
+
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.",
|
|
148186
|
+
descriptionShort: "RBAC testing mode commands",
|
|
148187
|
+
descriptionMedium: "Query and provide feedback in eval mode for RBAC testing and permission validation.",
|
|
148188
|
+
commands: /* @__PURE__ */ new Map([
|
|
148189
|
+
["query", evalQueryCommand],
|
|
148190
|
+
["feedback", evalFeedbackCommand]
|
|
148191
|
+
]),
|
|
148192
|
+
defaultCommand: evalQueryCommand
|
|
148193
|
+
};
|
|
148194
|
+
|
|
148195
|
+
// src/domains/generative_ai/index.ts
|
|
148196
|
+
var generativeAiDomain = {
|
|
148197
|
+
name: "generative_ai",
|
|
148198
|
+
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.",
|
|
148199
|
+
descriptionShort: "AI assistant queries and feedback",
|
|
148200
|
+
descriptionMedium: "Query the AI assistant for help with F5 XC platform operations, configurations, security analysis, and troubleshooting.",
|
|
148201
|
+
defaultCommand: queryCommand,
|
|
148202
|
+
commands: /* @__PURE__ */ new Map([
|
|
148203
|
+
["query", queryCommand],
|
|
148204
|
+
["chat", chatCommand],
|
|
148205
|
+
["feedback", feedbackCommand]
|
|
148206
|
+
]),
|
|
148207
|
+
subcommands: /* @__PURE__ */ new Map([["eval", evalSubcommands]])
|
|
148208
|
+
};
|
|
148209
|
+
var generativeAiAliases = ["ai", "genai", "assistant"];
|
|
148210
|
+
|
|
147146
148211
|
// src/domains/index.ts
|
|
147147
148212
|
customDomains.register(loginDomain);
|
|
147148
148213
|
customDomains.register(cloudstatusDomain);
|
|
147149
148214
|
customDomains.register(completionDomain);
|
|
148215
|
+
customDomains.register(generativeAiDomain);
|
|
147150
148216
|
for (const domain of customDomains.all()) {
|
|
147151
148217
|
completionRegistry.registerDomain(fromCustomDomain(domain));
|
|
147152
148218
|
}
|
|
@@ -147159,6 +148225,9 @@ var domainAliases = /* @__PURE__ */ new Map();
|
|
|
147159
148225
|
for (const alias of cloudstatusAliases) {
|
|
147160
148226
|
domainAliases.set(alias, "cloudstatus");
|
|
147161
148227
|
}
|
|
148228
|
+
for (const alias of generativeAiAliases) {
|
|
148229
|
+
domainAliases.set(alias, "generative_ai");
|
|
148230
|
+
}
|
|
147162
148231
|
function resolveDomainAlias(name) {
|
|
147163
148232
|
return domainAliases.get(name) ?? name;
|
|
147164
148233
|
}
|
|
@@ -149927,7 +150996,7 @@ function App2({ initialSession } = {}) {
|
|
|
149927
150996
|
}
|
|
149928
150997
|
|
|
149929
150998
|
// src/headless/controller.ts
|
|
149930
|
-
import * as
|
|
150999
|
+
import * as readline2 from "readline";
|
|
149931
151000
|
|
|
149932
151001
|
// src/headless/protocol.ts
|
|
149933
151002
|
function parseInput2(line) {
|
|
@@ -150202,7 +151271,7 @@ var HeadlessController = class {
|
|
|
150202
151271
|
*/
|
|
150203
151272
|
async run() {
|
|
150204
151273
|
await this.initialize();
|
|
150205
|
-
this.rl =
|
|
151274
|
+
this.rl = readline2.createInterface({
|
|
150206
151275
|
input: process.stdin,
|
|
150207
151276
|
output: process.stdout,
|
|
150208
151277
|
terminal: false
|