@robinmordasiewicz/f5xc-xcsh 2.0.46-2601220508 → 2.0.46-2601220805

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.
Files changed (2) hide show
  1. package/dist/index.js +357 -114
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1372,7 +1372,7 @@ var require_react_development = __commonJS({
1372
1372
  }
1373
1373
  return dispatcher.useContext(Context);
1374
1374
  }
1375
- function useState14(initialState) {
1375
+ function useState15(initialState) {
1376
1376
  var dispatcher = resolveDispatcher();
1377
1377
  return dispatcher.useState(initialState);
1378
1378
  }
@@ -1380,11 +1380,11 @@ var require_react_development = __commonJS({
1380
1380
  var dispatcher = resolveDispatcher();
1381
1381
  return dispatcher.useReducer(reducer, initialArg, init);
1382
1382
  }
1383
- function useRef5(initialValue) {
1383
+ function useRef6(initialValue) {
1384
1384
  var dispatcher = resolveDispatcher();
1385
1385
  return dispatcher.useRef(initialValue);
1386
1386
  }
1387
- function useEffect10(create2, deps) {
1387
+ function useEffect11(create2, deps) {
1388
1388
  var dispatcher = resolveDispatcher();
1389
1389
  return dispatcher.useEffect(create2, deps);
1390
1390
  }
@@ -1396,7 +1396,7 @@ var require_react_development = __commonJS({
1396
1396
  var dispatcher = resolveDispatcher();
1397
1397
  return dispatcher.useLayoutEffect(create2, deps);
1398
1398
  }
1399
- function useCallback11(callback, deps) {
1399
+ function useCallback12(callback, deps) {
1400
1400
  var dispatcher = resolveDispatcher();
1401
1401
  return dispatcher.useCallback(callback, deps);
1402
1402
  }
@@ -2163,19 +2163,19 @@ var require_react_development = __commonJS({
2163
2163
  exports.memo = memo;
2164
2164
  exports.startTransition = startTransition;
2165
2165
  exports.unstable_act = act;
2166
- exports.useCallback = useCallback11;
2166
+ exports.useCallback = useCallback12;
2167
2167
  exports.useContext = useContext7;
2168
2168
  exports.useDebugValue = useDebugValue;
2169
2169
  exports.useDeferredValue = useDeferredValue;
2170
- exports.useEffect = useEffect10;
2170
+ exports.useEffect = useEffect11;
2171
2171
  exports.useId = useId;
2172
2172
  exports.useImperativeHandle = useImperativeHandle;
2173
2173
  exports.useInsertionEffect = useInsertionEffect;
2174
2174
  exports.useLayoutEffect = useLayoutEffect2;
2175
2175
  exports.useMemo = useMemo3;
2176
2176
  exports.useReducer = useReducer;
2177
- exports.useRef = useRef5;
2178
- exports.useState = useState14;
2177
+ exports.useRef = useRef6;
2178
+ exports.useState = useState15;
2179
2179
  exports.useSyncExternalStore = useSyncExternalStore;
2180
2180
  exports.useTransition = useTransition;
2181
2181
  exports.version = ReactVersion;
@@ -41209,8 +41209,8 @@ var init_logo_renderer = __esm({
41209
41209
 
41210
41210
  // src/branding/index.ts
41211
41211
  function getVersion() {
41212
- if ("v2.0.46-2601220508") {
41213
- return "v2.0.46-2601220508";
41212
+ if ("v2.0.46-2601220805") {
41213
+ return "v2.0.46-2601220805";
41214
41214
  }
41215
41215
  if (process.env.XCSH_VERSION) {
41216
41216
  return process.env.XCSH_VERSION;
@@ -49209,7 +49209,7 @@ function calculateColumnWidths(columns, rows, maxTableWidth) {
49209
49209
  if (totalWidth > maxTableWidth) {
49210
49210
  const ratio = (maxTableWidth - columns.length * 3 - 1) / totalWidth;
49211
49211
  for (let i = 0; i < widths.length; i++) {
49212
- widths[i] = Math.max(5, Math.floor(widths[i] * ratio));
49212
+ widths[i] = Math.max(5, Math.floor((widths[i] ?? 0) * ratio));
49213
49213
  }
49214
49214
  }
49215
49215
  }
@@ -49255,7 +49255,7 @@ function formatBeautifulTable(data, config, noColor = false) {
49255
49255
  );
49256
49256
  }
49257
49257
  const headerCells = config.columns.map((col, i) => {
49258
- const padding = widths[i] - col.header.length;
49258
+ const padding = (widths[i] ?? 0) - col.header.length;
49259
49259
  const leftPad = Math.floor(padding / 2);
49260
49260
  const rightPad = padding - leftPad;
49261
49261
  const content = " ".repeat(leftPad) + col.header + " ".repeat(rightPad);
@@ -49268,16 +49268,16 @@ function formatBeautifulTable(data, config, noColor = false) {
49268
49268
  buildHorizontalLine(box.leftT, box.cross, box.rightT, box.horizontal)
49269
49269
  );
49270
49270
  for (let rowIndex = 0; rowIndex < data.length; rowIndex++) {
49271
- const row = data[rowIndex];
49271
+ const row = data[rowIndex] ?? {};
49272
49272
  const cellValues = config.columns.map((col, i) => {
49273
49273
  const value = getValue(row, col.accessor) || "<None>";
49274
- return config.wrapText !== false ? wrapText2(value, widths[i]) : [value.slice(0, widths[i])];
49274
+ return config.wrapText !== false ? wrapText2(value, widths[i] ?? 0) : [value.slice(0, widths[i] ?? 0)];
49275
49275
  });
49276
49276
  const maxLines = Math.max(...cellValues.map((c) => c.length));
49277
49277
  for (let lineIndex = 0; lineIndex < maxLines; lineIndex++) {
49278
49278
  const cells = cellValues.map((cellLines, i) => {
49279
49279
  const text = cellLines[lineIndex] ?? "";
49280
- const padding = widths[i] - text.length;
49280
+ const padding = (widths[i] ?? 0) - text.length;
49281
49281
  const align = config.columns[i]?.align ?? "left";
49282
49282
  let content;
49283
49283
  if (align === "center") {
@@ -144699,13 +144699,25 @@ var init_help = __esm({
144699
144699
  });
144700
144700
 
144701
144701
  // src/domains/registry.ts
144702
- function successResult(output, contextChanged = false) {
144703
- return {
144702
+ function successResult(output, options = false) {
144703
+ if (typeof options === "boolean") {
144704
+ return {
144705
+ output,
144706
+ shouldExit: false,
144707
+ shouldClear: false,
144708
+ contextChanged: options
144709
+ };
144710
+ }
144711
+ const result = {
144704
144712
  output,
144705
144713
  shouldExit: false,
144706
144714
  shouldClear: false,
144707
- contextChanged
144715
+ contextChanged: options.contextChanged ?? false
144708
144716
  };
144717
+ if (options.refreshHealth) {
144718
+ result.refreshHealth = true;
144719
+ }
144720
+ return result;
144709
144721
  }
144710
144722
  function errorResult(message) {
144711
144723
  return {
@@ -1138743,7 +1138755,9 @@ function extractFieldSpecs(schemaName, openApiSpec) {
1138743
1138755
  return [];
1138744
1138756
  }
1138745
1138757
  const fields = [];
1138746
- const required = new Set(schema.required || []);
1138758
+ const required = new Set(
1138759
+ Array.isArray(schema.required) ? schema.required : []
1138760
+ );
1138747
1138761
  for (const [fieldName, property] of Object.entries(
1138748
1138762
  schema.properties
1138749
1138763
  )) {
@@ -1138777,7 +1138791,7 @@ function extractOneOfGroups(schemaName, openApiSpec) {
1138777
1138791
  groups.push({
1138778
1138792
  groupName,
1138779
1138793
  variants,
1138780
- recommendedVariant,
1138794
+ ...recommendedVariant && { recommendedVariant },
1138781
1138795
  description: `Choose exactly one of: ${variants.join(", ")}`
1138782
1138796
  });
1138783
1138797
  }
@@ -1138788,19 +1138802,24 @@ function extractConstraints(property) {
1138788
1138802
  const constraints = {
1138789
1138803
  type: property.type || "unknown"
1138790
1138804
  };
1138791
- if (property.minLength !== void 0)
1138805
+ if (property.minLength !== void 0 && property.minLength !== null)
1138792
1138806
  constraints.minLength = property.minLength;
1138793
- if (property.maxLength !== void 0)
1138807
+ if (property.maxLength !== void 0 && property.maxLength !== null)
1138794
1138808
  constraints.maxLength = property.maxLength;
1138795
- if (property.pattern !== void 0) constraints.pattern = property.pattern;
1138796
- if (property.format !== void 0) constraints.format = property.format;
1138797
- if (property.minimum !== void 0) constraints.minimum = property.minimum;
1138798
- if (property.maximum !== void 0) constraints.maximum = property.maximum;
1138799
- if (property.maxItems !== void 0)
1138809
+ if (property.pattern !== void 0 && property.pattern !== null)
1138810
+ constraints.pattern = property.pattern;
1138811
+ if (property.format !== void 0 && property.format !== null)
1138812
+ constraints.format = property.format;
1138813
+ if (property.minimum !== void 0 && property.minimum !== null)
1138814
+ constraints.minimum = property.minimum;
1138815
+ if (property.maximum !== void 0 && property.maximum !== null)
1138816
+ constraints.maximum = property.maximum;
1138817
+ if (property.maxItems !== void 0 && property.maxItems !== null)
1138800
1138818
  constraints.maxItems = property.maxItems;
1138801
- if (property.uniqueItems !== void 0)
1138819
+ if (property.uniqueItems !== void 0 && property.uniqueItems !== null)
1138802
1138820
  constraints.uniqueItems = property.uniqueItems;
1138803
- if (property.enum !== void 0) constraints.enum = property.enum;
1138821
+ if (property.enum !== void 0 && property.enum !== null)
1138822
+ constraints.enum = property.enum;
1138804
1138823
  return constraints;
1138805
1138824
  }
1138806
1138825
  function extractF5XCExtensions(property) {
@@ -1138821,7 +1138840,10 @@ function extractF5XCExtensions(property) {
1138821
1138840
  extensions.descriptionMedium = property["x-f5xc-description-medium"];
1138822
1138841
  }
1138823
1138842
  if (property["x-f5xc-required-for"] !== void 0) {
1138824
- extensions.requiredFor = property["x-f5xc-required-for"];
1138843
+ const requiredFor = property["x-f5xc-required-for"];
1138844
+ if (requiredFor) {
1138845
+ extensions.requiredFor = requiredFor;
1138846
+ }
1138825
1138847
  }
1138826
1138848
  if (property["x-f5xc-example"] !== void 0 || property["x-ves-example"] !== void 0) {
1138827
1138849
  extensions.example = property["x-f5xc-example"] || property["x-ves-example"];
@@ -1139317,7 +1139339,7 @@ var init_ai_guide_builder = __esm({
1139317
1139339
  // src/output/resource-spec-builder.ts
1139318
1139340
  function buildResourceSpec(resourceType) {
1139319
1139341
  if (specCache.has(resourceType)) {
1139320
- return specCache.get(resourceType);
1139342
+ return specCache.get(resourceType) ?? null;
1139321
1139343
  }
1139322
1139344
  if (!cachedOpenApiSpec) {
1139323
1139345
  cachedOpenApiSpec = loadOpenApiSpec();
@@ -1142859,24 +1142881,28 @@ function renderResponse(response) {
1142859
1142881
  const responseType = getResponseType(response);
1142860
1142882
  switch (responseType) {
1142861
1142883
  case "generic_response":
1142862
- lines.push(...renderGenericResponse(response.generic_response));
1142884
+ lines.push(
1142885
+ ...renderGenericResponse(response.generic_response ?? {})
1142886
+ );
1142863
1142887
  break;
1142864
1142888
  case "explain_log":
1142865
- lines.push(...renderExplainLog(response.explain_log));
1142889
+ lines.push(...renderExplainLog(response.explain_log ?? {}));
1142866
1142890
  break;
1142867
1142891
  case "gen_dashboard_filter":
1142868
1142892
  lines.push(
1142869
- ...renderDashboardFilter(response.gen_dashboard_filter)
1142893
+ ...renderDashboardFilter(response.gen_dashboard_filter ?? {})
1142870
1142894
  );
1142871
1142895
  break;
1142872
1142896
  case "list_response":
1142873
- lines.push(...renderListResponse(response.list_response));
1142897
+ lines.push(...renderListResponse(response.list_response ?? {}));
1142874
1142898
  break;
1142875
1142899
  case "site_analysis_response":
1142876
- lines.push(...renderSiteAnalysis(response.site_analysis_response));
1142900
+ lines.push(
1142901
+ ...renderSiteAnalysis(response.site_analysis_response ?? {})
1142902
+ );
1142877
1142903
  break;
1142878
1142904
  case "widget_response":
1142879
- lines.push(...renderWidgetResponse(response.widget_response));
1142905
+ lines.push(...renderWidgetResponse(response.widget_response ?? {}));
1142880
1142906
  break;
1142881
1142907
  default:
1142882
1142908
  lines.push("No response content.");
@@ -1144241,8 +1144267,7 @@ var init_create = __esm({
1144241
1144267
  generateUsageError(
1144242
1144268
  "login create profile",
1144243
1144269
  // FIXED: Correct registry path (verb-first)
1144244
- createCommand2.usage,
1144245
- // Single source of truth
1144270
+ createCommand2.usage ?? "",
1144246
1144271
  {
1144247
1144272
  options: [
1144248
1144273
  {
@@ -1144384,8 +1144409,12 @@ var init_use = __esm({
1144384
1144409
  const tableLines = formatConnectionTable(connectionInfo);
1144385
1144410
  return successResult(
1144386
1144411
  [`Switched to profile '${name}'.`, ``, ...tableLines],
1144387
- true
1144388
- // contextChanged - prompt should update
1144412
+ {
1144413
+ contextChanged: true,
1144414
+ // prompt should update
1144415
+ refreshHealth: true
1144416
+ // refresh health indicator
1144417
+ }
1144389
1144418
  );
1144390
1144419
  } catch (error) {
1144391
1144420
  return errorResult(
@@ -1146618,7 +1146647,9 @@ function generateZshCompletion() {
1146618
1146647
  const escaped = escapeForZsh(d.description);
1146619
1146648
  return `'${d.name}:${escaped}'`;
1146620
1146649
  }).join("\n ");
1146621
- const aliasDescriptions = domains.filter((d) => d.aliases && d.aliases.length > 0).flatMap((d) => d.aliases.map((a) => `'${a}:Alias for ${d.name}'`)).join("\n ");
1146650
+ const aliasDescriptions = domains.filter((d) => d.aliases && d.aliases.length > 0).flatMap(
1146651
+ (d) => d.aliases?.map((a) => `'${a}:Alias for ${d.name}'`) ?? []
1146652
+ ).join("\n ");
1146622
1146653
  const actionDescriptions2 = getActionDescriptions();
1146623
1146654
  const actionDescArray = Object.entries(actionDescriptions2).map(([action, desc]) => {
1146624
1146655
  const escaped = escapeForZsh(desc);
@@ -1146731,9 +1146762,9 @@ function generateFishCompletion() {
1146731
1146762
  return `complete -c xcsh -n "__fish_use_subcommand" -a "${d.name}" -d '${escaped}'`;
1146732
1146763
  }).join("\n");
1146733
1146764
  const aliasCompletions = domains.filter((d) => d.aliases && d.aliases.length > 0).flatMap(
1146734
- (d) => d.aliases.map(
1146765
+ (d) => d.aliases?.map(
1146735
1146766
  (a) => `complete -c xcsh -n "__fish_use_subcommand" -a "${a}" -d 'Alias for ${d.name}'`
1146736
- )
1146767
+ ) ?? []
1146737
1146768
  ).join("\n");
1146738
1146769
  const customDomainCompletions = [];
1146739
1146770
  for (const domain of domains) {
@@ -1152739,7 +1152770,7 @@ function groupFlagsForHelp(resourceType, typeFilter) {
1152739
1152770
  flags: []
1152740
1152771
  });
1152741
1152772
  }
1152742
- groups.get(groupId).flags.push(flag);
1152773
+ groups.get(groupId)?.flags.push(flag);
1152743
1152774
  }
1152744
1152775
  return Array.from(groups.values()).sort((a, b) => a.priority - b.priority).map((group) => ({
1152745
1152776
  ...group,
@@ -1159755,6 +1159786,7 @@ var DEFAULT_RETRY_CONFIG = {
1159755
1159786
  var STARTUP_TIMEOUT = 3e3;
1159756
1159787
  var STARTUP_MAX_RETRIES = 0;
1159757
1159788
  var CONNECTIVITY_TIMEOUT = 2e3;
1159789
+ var HEALTH_CHECK_TIMEOUT = 3e3;
1159758
1159790
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([
1159759
1159791
  408,
1159760
1159792
  // Request Timeout
@@ -1159940,6 +1159972,60 @@ var APIClient = class {
1159940
1159972
  return { reachable: false };
1159941
1159973
  }
1159942
1159974
  }
1159975
+ /**
1159976
+ * Lightweight health check for background polling.
1159977
+ * Two-phase check:
1159978
+ * 1. Connectivity check with HEAD request (fast-fail)
1159979
+ * 2. Auth validation with HEAD to /api/web/namespaces
1159980
+ *
1159981
+ * Returns status:
1159982
+ * - 'connected': API reachable and authenticated
1159983
+ * - 'offline': API unreachable (network/timeout)
1159984
+ * - 'auth_error': API reachable but authentication failed (401/403)
1159985
+ */
1159986
+ async healthCheck() {
1159987
+ const start = Date.now();
1159988
+ const connectivity = await this.checkConnectivity();
1159989
+ if (!connectivity.reachable) {
1159990
+ return { status: "offline" };
1159991
+ }
1159992
+ if (!this.apiToken) {
1159993
+ return { status: "auth_error", latencyMs: Date.now() - start };
1159994
+ }
1159995
+ const controller = new AbortController();
1159996
+ const timeoutId = setTimeout(
1159997
+ () => controller.abort(),
1159998
+ HEALTH_CHECK_TIMEOUT
1159999
+ );
1160000
+ try {
1160001
+ const response = await fetch(
1160002
+ `${this.serverUrl}/api/web/namespaces`,
1160003
+ {
1160004
+ method: "HEAD",
1160005
+ headers: {
1160006
+ Authorization: `APIToken ${this.apiToken}`,
1160007
+ Accept: "application/json"
1160008
+ },
1160009
+ signal: controller.signal
1160010
+ }
1160011
+ );
1160012
+ clearTimeout(timeoutId);
1160013
+ const latencyMs = Date.now() - start;
1160014
+ if (response.ok || response.status === 200) {
1160015
+ return { status: "connected", latencyMs };
1160016
+ }
1160017
+ if (response.status === 401 || response.status === 403) {
1160018
+ return { status: "auth_error", latencyMs };
1160019
+ }
1160020
+ return { status: "connected", latencyMs };
1160021
+ } catch (error) {
1160022
+ clearTimeout(timeoutId);
1160023
+ if (error instanceof Error && error.name === "AbortError") {
1160024
+ return { status: "offline" };
1160025
+ }
1160026
+ return { status: "offline" };
1160027
+ }
1160028
+ }
1159943
1160029
  /**
1159944
1160030
  * Build full URL from path and query parameters
1159945
1160031
  */
@@ -1160930,7 +1161016,7 @@ function buildPlainPrompt(session) {
1160930
1161016
  }
1160931
1161017
 
1160932
1161018
  // src/repl/App.tsx
1160933
- var import_react34 = __toESM(require_react(), 1);
1161019
+ var import_react35 = __toESM(require_react(), 1);
1160934
1161020
 
1160935
1161021
  // src/repl/components/InputBox.tsx
1160936
1161022
  var import_react23 = __toESM(require_react(), 1);
@@ -1161098,16 +1161184,46 @@ function getFormattedCwd() {
1161098
1161184
  }
1161099
1161185
  return cwd2;
1161100
1161186
  }
1161187
+ var CONNECTION_COLORS = {
1161188
+ connected: "#00c853",
1161189
+ // Green - connected and authenticated
1161190
+ auth_error: "#ffc107",
1161191
+ // Yellow - connected but auth error
1161192
+ offline: "#ca260a",
1161193
+ // Red - disconnected/unreachable
1161194
+ unknown: "#666666"
1161195
+ // Gray - unknown/checking
1161196
+ };
1161197
+ function getConnectionIndicator(status, isChecking) {
1161198
+ if (isChecking) {
1161199
+ return { symbol: "\u25CB", color: CONNECTION_COLORS.unknown };
1161200
+ }
1161201
+ return {
1161202
+ symbol: "\u25CF",
1161203
+ color: CONNECTION_COLORS[status]
1161204
+ };
1161205
+ }
1161101
1161206
  function StatusBar({
1161102
1161207
  gitInfo,
1161103
1161208
  width = 80,
1161104
- hint = "Ctrl+C: quit"
1161209
+ hint = "Ctrl+C: quit",
1161210
+ connectionStatus = "unknown",
1161211
+ isCheckingHealth = false
1161105
1161212
  }) {
1161213
+ const renderConnectionIndicator = () => {
1161214
+ const { symbol, color } = getConnectionIndicator(
1161215
+ connectionStatus,
1161216
+ isCheckingHealth
1161217
+ );
1161218
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color, children: symbol });
1161219
+ };
1161106
1161220
  const renderLeft = () => {
1161107
1161221
  if (gitInfo?.inRepo) {
1161108
1161222
  const icon = getStatusIcon(gitInfo);
1161109
1161223
  const color = getStatusColor(gitInfo);
1161110
1161224
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
1161225
+ renderConnectionIndicator(),
1161226
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
1161111
1161227
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#ffffff", children: gitInfo.repoName }),
1161112
1161228
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#666666", children: "/" }),
1161113
1161229
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color, children: gitInfo.branch }),
@@ -1161115,7 +1161231,11 @@ function StatusBar({
1161115
1161231
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color, children: icon })
1161116
1161232
  ] });
1161117
1161233
  }
1161118
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#666666", children: getFormattedCwd() });
1161234
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
1161235
+ renderConnectionIndicator(),
1161236
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
1161237
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#666666", children: getFormattedCwd() })
1161238
+ ] });
1161119
1161239
  };
1161120
1161240
  const renderRight = () => {
1161121
1161241
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#666666", children: hint });
@@ -1162320,6 +1162440,104 @@ function useGitStatus(options = {}) {
1162320
1162440
  return { gitInfo, refresh, lastRefresh };
1162321
1162441
  }
1162322
1162442
 
1162443
+ // src/repl/hooks/useHealthCheck.ts
1162444
+ var import_react34 = __toESM(require_react(), 1);
1162445
+ var DEFAULT_POLL_INTERVAL_MS2 = 3e4;
1162446
+ var MIN_POLL_INTERVAL_MS2 = 1e4;
1162447
+ function getPollInterval2() {
1162448
+ const envValue = process.env.XCSH_HEALTH_POLL_INTERVAL;
1162449
+ if (envValue === void 0) return DEFAULT_POLL_INTERVAL_MS2;
1162450
+ if (envValue === "0") return 0;
1162451
+ const seconds = parseInt(envValue, 10);
1162452
+ if (isNaN(seconds) || seconds <= 0) return DEFAULT_POLL_INTERVAL_MS2;
1162453
+ return Math.max(seconds * 1e3, MIN_POLL_INTERVAL_MS2);
1162454
+ }
1162455
+ function useHealthCheck(options) {
1162456
+ const { enabled = true, session } = options;
1162457
+ const pollIntervalMs = options.pollIntervalMs ?? getPollInterval2();
1162458
+ const [health, setHealth] = (0, import_react34.useState)({
1162459
+ status: "unknown",
1162460
+ latencyMs: void 0,
1162461
+ isChecking: false
1162462
+ });
1162463
+ const [lastRefresh, setLastRefresh] = (0, import_react34.useState)(0);
1162464
+ const intervalRef = (0, import_react34.useRef)(null);
1162465
+ const isCheckingRef = (0, import_react34.useRef)(false);
1162466
+ const performHealthCheck = (0, import_react34.useCallback)(async () => {
1162467
+ if (isCheckingRef.current) {
1162468
+ return;
1162469
+ }
1162470
+ const client = session?.getAPIClient();
1162471
+ if (!client) {
1162472
+ setHealth({
1162473
+ status: "unknown",
1162474
+ latencyMs: void 0,
1162475
+ isChecking: false
1162476
+ });
1162477
+ return;
1162478
+ }
1162479
+ isCheckingRef.current = true;
1162480
+ setHealth((prev) => ({ ...prev, isChecking: true }));
1162481
+ try {
1162482
+ const result = await client.healthCheck();
1162483
+ setHealth({
1162484
+ status: result.status,
1162485
+ latencyMs: result.latencyMs,
1162486
+ isChecking: false
1162487
+ });
1162488
+ setLastRefresh(Date.now());
1162489
+ } catch {
1162490
+ setHealth({
1162491
+ status: "offline",
1162492
+ latencyMs: void 0,
1162493
+ isChecking: false
1162494
+ });
1162495
+ setLastRefresh(Date.now());
1162496
+ } finally {
1162497
+ isCheckingRef.current = false;
1162498
+ }
1162499
+ }, [session]);
1162500
+ const refresh = (0, import_react34.useCallback)(() => {
1162501
+ performHealthCheck();
1162502
+ }, [performHealthCheck]);
1162503
+ (0, import_react34.useEffect)(() => {
1162504
+ if (enabled && session) {
1162505
+ const connectionStatus = session.getConnectionStatus();
1162506
+ let initialStatus = "unknown";
1162507
+ if (connectionStatus === "connected") {
1162508
+ initialStatus = "connected";
1162509
+ } else if (connectionStatus === "offline") {
1162510
+ initialStatus = "offline";
1162511
+ } else if (connectionStatus === "error") {
1162512
+ initialStatus = "auth_error";
1162513
+ }
1162514
+ setHealth({
1162515
+ status: initialStatus,
1162516
+ latencyMs: void 0,
1162517
+ isChecking: false
1162518
+ });
1162519
+ const initialCheckTimeout = setTimeout(() => {
1162520
+ performHealthCheck();
1162521
+ }, 1e3);
1162522
+ return () => clearTimeout(initialCheckTimeout);
1162523
+ }
1162524
+ return void 0;
1162525
+ }, [enabled, session, performHealthCheck]);
1162526
+ (0, import_react34.useEffect)(() => {
1162527
+ if (!enabled || pollIntervalMs === 0 || !session) {
1162528
+ return;
1162529
+ }
1162530
+ intervalRef.current = setInterval(performHealthCheck, pollIntervalMs);
1162531
+ return () => {
1162532
+ if (intervalRef.current) {
1162533
+ clearInterval(intervalRef.current);
1162534
+ intervalRef.current = null;
1162535
+ }
1162536
+ };
1162537
+ }, [enabled, pollIntervalMs, session, performHealthCheck]);
1162538
+ return { health, refresh, lastRefresh };
1162539
+ }
1162540
+
1162323
1162541
  // src/repl/executor.ts
1162324
1162542
  init_domains();
1162325
1162543
  init_domains2();
@@ -1162812,7 +1163030,7 @@ async function detectDependencies(resourceType, resourceName, namespace, client)
1162812
1163030
  searchTimeMs
1162813
1163031
  };
1162814
1163032
  } catch (error) {
1162815
- console.debug(`Dependency detection error: ${error}`);
1163033
+ console.warn(`Dependency detection error: ${error}`);
1162816
1163034
  const searchTimeMs = Date.now() - startTime;
1162817
1163035
  return {
1162818
1163036
  found: false,
@@ -1165690,11 +1165908,11 @@ function toUISuggestions(suggestions) {
1165690
1165908
  function App2({ initialSession } = {}) {
1165691
1165909
  const { exit } = use_app_default();
1165692
1165910
  const { stdout } = use_stdout_default();
1165693
- const [session] = (0, import_react34.useState)(() => initialSession ?? new REPLSession());
1165694
- const [isInitialized, setIsInitialized] = (0, import_react34.useState)(!!initialSession);
1165695
- const [input, setInputState] = (0, import_react34.useState)("");
1165696
- const inputRef = (0, import_react34.useRef)("");
1165697
- const setInput = (0, import_react34.useCallback)(
1165911
+ const [session] = (0, import_react35.useState)(() => initialSession ?? new REPLSession());
1165912
+ const [isInitialized, setIsInitialized] = (0, import_react35.useState)(!!initialSession);
1165913
+ const [input, setInputState] = (0, import_react35.useState)("");
1165914
+ const inputRef = (0, import_react35.useRef)("");
1165915
+ const setInput = (0, import_react35.useCallback)(
1165698
1165916
  (value) => {
1165699
1165917
  setInputState((prev) => {
1165700
1165918
  const newValue = typeof value === "function" ? value(prev) : value;
@@ -1165704,24 +1165922,24 @@ function App2({ initialSession } = {}) {
1165704
1165922
  },
1165705
1165923
  []
1165706
1165924
  );
1165707
- const [outputItems, setOutputItems] = (0, import_react34.useState)([]);
1165708
- const outputIdRef = (0, import_react34.useRef)(0);
1165709
- const commandIdRef = (0, import_react34.useRef)(0);
1165710
- const [prompt, setPrompt] = (0, import_react34.useState)("> ");
1165711
- const [width, setWidth] = (0, import_react34.useState)(stdout?.columns ?? 80);
1165712
- const [statusHint, setStatusHint] = (0, import_react34.useState)("Ctrl+C twice to exit");
1165713
- const [historyArray, setHistoryArray] = (0, import_react34.useState)([]);
1165714
- const [inputKey, setInputKey] = (0, import_react34.useState)(0);
1165715
- const [hideStatusBar, setHideStatusBar] = (0, import_react34.useState)(false);
1165716
- const [pendingRawStdout, setPendingRawStdout] = (0, import_react34.useState)(
1165925
+ const [outputItems, setOutputItems] = (0, import_react35.useState)([]);
1165926
+ const outputIdRef = (0, import_react35.useRef)(0);
1165927
+ const commandIdRef = (0, import_react35.useRef)(0);
1165928
+ const [prompt, setPrompt] = (0, import_react35.useState)("> ");
1165929
+ const [width, setWidth] = (0, import_react35.useState)(stdout?.columns ?? 80);
1165930
+ const [statusHint, setStatusHint] = (0, import_react35.useState)("Ctrl+C twice to exit");
1165931
+ const [historyArray, setHistoryArray] = (0, import_react35.useState)([]);
1165932
+ const [inputKey, setInputKey] = (0, import_react35.useState)(0);
1165933
+ const [hideStatusBar, setHideStatusBar] = (0, import_react35.useState)(false);
1165934
+ const [pendingRawStdout, setPendingRawStdout] = (0, import_react35.useState)(
1165717
1165935
  null
1165718
1165936
  );
1165719
- const [mode, setMode] = (0, import_react34.useState)(
1165937
+ const [mode, setMode] = (0, import_react35.useState)(
1165720
1165938
  "repl"
1165721
1165939
  );
1165722
- const [chatConfig, setChatConfig] = (0, import_react34.useState)(null);
1165723
- const [profileDeleteConfig, setProfileDeleteConfig] = (0, import_react34.useState)(null);
1165724
- (0, import_react34.useEffect)(() => {
1165940
+ const [chatConfig, setChatConfig] = (0, import_react35.useState)(null);
1165941
+ const [profileDeleteConfig, setProfileDeleteConfig] = (0, import_react35.useState)(null);
1165942
+ (0, import_react35.useEffect)(() => {
1165725
1165943
  if (hideStatusBar && pendingRawStdout) {
1165726
1165944
  process.stdout.write(pendingRawStdout);
1165727
1165945
  process.stdout.write("\n\n\n");
@@ -1165730,6 +1165948,10 @@ function App2({ initialSession } = {}) {
1165730
1165948
  }
1165731
1165949
  }, [hideStatusBar, pendingRawStdout]);
1165732
1165950
  const gitStatus = useGitStatus({ enabled: isInitialized });
1165951
+ const healthCheck = useHealthCheck({
1165952
+ enabled: isInitialized,
1165953
+ session: isInitialized ? session : null
1165954
+ });
1165733
1165955
  const completion = useCompletion({
1165734
1165956
  session: isInitialized ? session : null
1165735
1165957
  });
@@ -1165750,7 +1165972,7 @@ function App2({ initialSession } = {}) {
1165750
1165972
  session.saveHistory().finally(() => exit());
1165751
1165973
  }
1165752
1165974
  });
1165753
- (0, import_react34.useEffect)(() => {
1165975
+ (0, import_react35.useEffect)(() => {
1165754
1165976
  const init = async () => {
1165755
1165977
  if (!isInitialized) {
1165756
1165978
  await session.initialize();
@@ -1165764,7 +1165986,7 @@ function App2({ initialSession } = {}) {
1165764
1165986
  };
1165765
1165987
  init();
1165766
1165988
  }, [session]);
1165767
- (0, import_react34.useEffect)(() => {
1165989
+ (0, import_react35.useEffect)(() => {
1165768
1165990
  const handleResize = () => {
1165769
1165991
  if (stdout) {
1165770
1165992
  const newWidth = stdout.columns ?? 80;
@@ -1165792,7 +1166014,7 @@ function App2({ initialSession } = {}) {
1165792
1166014
  stdout?.off("resize", handleResize);
1165793
1166015
  };
1165794
1166016
  }, [stdout, width]);
1165795
- const addOutput = (0, import_react34.useCallback)((line) => {
1166017
+ const addOutput = (0, import_react35.useCallback)((line) => {
1165796
1166018
  const lines = line.split("\n");
1165797
1166019
  const currentCommandId = commandIdRef.current;
1165798
1166020
  const newItems = lines.map((content) => ({
@@ -1165842,7 +1166064,7 @@ function App2({ initialSession } = {}) {
1165842
1166064
  return combined;
1165843
1166065
  });
1165844
1166066
  }, []);
1165845
- const applyCompletion = (0, import_react34.useCallback)(
1166067
+ const applyCompletion = (0, import_react35.useCallback)(
1165846
1166068
  (suggestion) => {
1165847
1166069
  const currentInput = inputRef.current;
1165848
1166070
  let newValue;
@@ -1165867,16 +1166089,17 @@ function App2({ initialSession } = {}) {
1165867
1166089
  setInput(newValue);
1165868
1166090
  setInputKey((k) => k + 1);
1165869
1166091
  },
1166092
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1165870
1166093
  []
1165871
1166094
  // No dependencies needed since we use inputRef
1165872
1166095
  );
1165873
- const refreshHistory = (0, import_react34.useCallback)(() => {
1166096
+ const refreshHistory = (0, import_react35.useCallback)(() => {
1165874
1166097
  const histMgr = session.getHistory();
1165875
1166098
  if (histMgr) {
1165876
1166099
  setHistoryArray(histMgr.getHistory());
1165877
1166100
  }
1165878
1166101
  }, [session]);
1165879
- const runCommand = (0, import_react34.useCallback)(
1166102
+ const runCommand = (0, import_react35.useCallback)(
1165880
1166103
  async (cmd) => {
1165881
1166104
  const trimmed = cmd.trim();
1165882
1166105
  if (!trimmed) return;
@@ -1165939,11 +1166162,23 @@ function App2({ initialSession } = {}) {
1165939
1166162
  } else {
1165940
1166163
  gitStatus.refresh();
1165941
1166164
  }
1166165
+ if (result.refreshHealth) {
1166166
+ healthCheck.refresh();
1166167
+ }
1165942
1166168
  refreshHistory();
1165943
1166169
  },
1165944
- [session, prompt, addOutput, exit, refreshHistory, gitStatus]
1166170
+ [
1166171
+ session,
1166172
+ prompt,
1166173
+ addOutput,
1166174
+ exit,
1166175
+ refreshHistory,
1166176
+ gitStatus,
1166177
+ healthCheck,
1166178
+ outputItems
1166179
+ ]
1165945
1166180
  );
1165946
- const handleInputChange = (0, import_react34.useCallback)(
1166181
+ const handleInputChange = (0, import_react35.useCallback)(
1165947
1166182
  (newValue) => {
1165948
1166183
  const oldValue = input;
1165949
1166184
  setInput(newValue);
@@ -1165969,9 +1166204,9 @@ function App2({ initialSession } = {}) {
1165969
1166204
  }
1165970
1166205
  }
1165971
1166206
  },
1165972
- [input, completion]
1166207
+ [input, completion, setInput]
1165973
1166208
  );
1165974
- const handleSubmit = (0, import_react34.useCallback)(
1166209
+ const handleSubmit = (0, import_react35.useCallback)(
1165975
1166210
  async (value) => {
1165976
1166211
  if (completion.isShowing) {
1165977
1166212
  completion.hide();
@@ -1165980,7 +1166215,7 @@ function App2({ initialSession } = {}) {
1165980
1166215
  history.reset();
1165981
1166216
  await runCommand(value);
1165982
1166217
  },
1165983
- [completion, applyCompletion, runCommand, history]
1166218
+ [completion, runCommand, history, setInput]
1165984
1166219
  );
1165985
1166220
  use_input_default((char, key) => {
1165986
1166221
  if (key.ctrl && char === "c") {
@@ -1165997,6 +1166232,12 @@ function App2({ initialSession } = {}) {
1165997
1166232
  setTimeout(() => setStatusHint("Ctrl+C twice to exit"), 2e3);
1165998
1166233
  return;
1165999
1166234
  }
1166235
+ if (key.ctrl && char === "h") {
1166236
+ healthCheck.refresh();
1166237
+ setStatusHint("Health check refreshed");
1166238
+ setTimeout(() => setStatusHint("Ctrl+C twice to exit"), 2e3);
1166239
+ return;
1166240
+ }
1166000
1166241
  if (key.tab) {
1166001
1166242
  const currentInput = inputRef.current;
1166002
1166243
  if (completion.isShowing) {
@@ -1166060,7 +1166301,7 @@ function App2({ initialSession } = {}) {
1166060
1166301
  return;
1166061
1166302
  }
1166062
1166303
  });
1166063
- const handleSuggestionNavigate = (0, import_react34.useCallback)(
1166304
+ const handleSuggestionNavigate = (0, import_react35.useCallback)(
1166064
1166305
  (direction) => {
1166065
1166306
  if (direction === "up") {
1166066
1166307
  completion.navigateUp();
@@ -1166070,14 +1166311,14 @@ function App2({ initialSession } = {}) {
1166070
1166311
  },
1166071
1166312
  [completion]
1166072
1166313
  );
1166073
- const handleSuggestionSelect = (0, import_react34.useCallback)(
1166314
+ const handleSuggestionSelect = (0, import_react35.useCallback)(
1166074
1166315
  (suggestion) => {
1166075
1166316
  applyCompletion(suggestion);
1166076
1166317
  completion.hide();
1166077
1166318
  },
1166078
1166319
  [applyCompletion, completion]
1166079
1166320
  );
1166080
- const handleChatExit = (0, import_react34.useCallback)(
1166321
+ const handleChatExit = (0, import_react35.useCallback)(
1166081
1166322
  (chatMessages) => {
1166082
1166323
  chatMessages.forEach((msg) => addOutput(msg));
1166083
1166324
  setMode("repl");
@@ -1166085,7 +1166326,7 @@ function App2({ initialSession } = {}) {
1166085
1166326
  },
1166086
1166327
  [addOutput]
1166087
1166328
  );
1166088
- const handleProfileDeleteExit = (0, import_react34.useCallback)(
1166329
+ const handleProfileDeleteExit = (0, import_react35.useCallback)(
1166089
1166330
  (success, messages) => {
1166090
1166331
  messages.forEach((msg) => addOutput(msg));
1166091
1166332
  if (success && profileDeleteConfig?.isActive) {
@@ -1166098,7 +1166339,7 @@ function App2({ initialSession } = {}) {
1166098
1166339
  },
1166099
1166340
  [addOutput, session, profileDeleteConfig, setInput]
1166100
1166341
  );
1166101
- (0, import_react34.useEffect)(() => {
1166342
+ (0, import_react35.useEffect)(() => {
1166102
1166343
  const uniqueCommands = new Set(
1166103
1166344
  outputItems.map((item) => item.commandId)
1166104
1166345
  );
@@ -1166170,7 +1166411,9 @@ function App2({ initialSession } = {}) {
1166170
1166411
  {
1166171
1166412
  gitInfo: gitStatus.gitInfo,
1166172
1166413
  width,
1166173
- hint: statusHint
1166414
+ hint: statusHint,
1166415
+ connectionStatus: healthCheck.health.status,
1166416
+ isCheckingHealth: healthCheck.health.isChecking
1166174
1166417
  }
1166175
1166418
  )
1166176
1166419
  ] }) : !isInitialized ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: "Initializing..." }) : null
@@ -1166344,7 +1166587,7 @@ var HeadlessController = class {
1166344
1166587
  * Write output to stdout
1166345
1166588
  */
1166346
1166589
  write(output) {
1166347
- console.log(formatOutput2(output));
1166590
+ console.warn(formatOutput2(output));
1166348
1166591
  }
1166349
1166592
  /**
1166350
1166593
  * Emit an event
@@ -1166533,11 +1166776,11 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
1166533
1166776
  ).option("-o, --output <format>", `Output format (${OUTPUT_FORMAT_HELP})`).option("--spec", "Output command specification as JSON (for AI)").option("--headless", "Run in headless JSON protocol mode (for AI agents)").option("-h, --help", "Show help").argument("[command...]", "Command to execute non-interactively").allowUnknownOption(true).helpOption(false).action(
1166534
1166777
  async (commandArgs, options) => {
1166535
1166778
  if (options.help && commandArgs.length === 0) {
1166536
- formatRootHelp().forEach((line) => console.log(line));
1166779
+ formatRootHelp().forEach((line) => console.warn(line));
1166537
1166780
  process.exit(0);
1166538
1166781
  }
1166539
1166782
  if (options.spec && commandArgs.length === 0) {
1166540
- console.log(formatFullCLISpec());
1166783
+ console.warn(formatFullCLISpec());
1166541
1166784
  process.exit(0);
1166542
1166785
  }
1166543
1166786
  if (options.help && commandArgs.length > 0) {
@@ -1166591,7 +1166834,7 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
1166591
1166834
  const activeProfile = session.getActiveProfile();
1166592
1166835
  const envConfigured = process.env[`${ENV_PREFIX}_API_URL`] && process.env[`${ENV_PREFIX}_API_TOKEN`];
1166593
1166836
  if (activeProfile) {
1166594
- console.log("");
1166837
+ console.warn("");
1166595
1166838
  const connectionInfo = buildConnectionInfo(
1166596
1166839
  session.getActiveProfileName() || activeProfile.name || "default",
1166597
1166840
  activeProfile.apiUrl || session.getServerUrl() || "",
@@ -1166604,24 +1166847,24 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
1166604
1166847
  session.getEnvVarsPresent()
1166605
1166848
  );
1166606
1166849
  const tableLines = formatConnectionTable(connectionInfo);
1166607
- tableLines.forEach((line) => console.log(line));
1166850
+ tableLines.forEach((line) => console.warn(line));
1166608
1166851
  if (session.isOfflineMode()) {
1166609
- console.log("");
1166610
- console.log(
1166852
+ console.warn("");
1166853
+ console.warn(
1166611
1166854
  `${colors.yellow}\u26A0\uFE0F Offline Mode: API endpoint unreachable${colors.reset}`
1166612
1166855
  );
1166613
- console.log(
1166856
+ console.warn(
1166614
1166857
  `${colors.dim} Commands requiring API access will fail.${colors.reset}`
1166615
1166858
  );
1166616
1166859
  }
1166617
1166860
  if (session.getAuthSource() === "profile-fallback") {
1166618
- console.log("");
1166619
- console.log(
1166861
+ console.warn("");
1166862
+ console.warn(
1166620
1166863
  `${colors.blue}Info: Using credentials from profile '${session.getActiveProfileName()}' (environment variables were invalid)${colors.reset}`
1166621
1166864
  );
1166622
1166865
  }
1166623
1166866
  } else if (envConfigured) {
1166624
- console.log("");
1166867
+ console.warn("");
1166625
1166868
  const connectionInfo = buildConnectionInfo(
1166626
1166869
  "(environment)",
1166627
1166870
  process.env[`${ENV_PREFIX}_API_URL`] || "",
@@ -1166636,42 +1166879,42 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
1166636
1166879
  // envVarsPresent - by definition, this flow means env vars exist
1166637
1166880
  );
1166638
1166881
  const tableLines = formatConnectionTable(connectionInfo);
1166639
- tableLines.forEach((line) => console.log(line));
1166882
+ tableLines.forEach((line) => console.warn(line));
1166640
1166883
  if (session.isOfflineMode()) {
1166641
- console.log("");
1166642
- console.log(
1166884
+ console.warn("");
1166885
+ console.warn(
1166643
1166886
  `${colors.yellow}\u26A0\uFE0F Offline Mode: API endpoint unreachable${colors.reset}`
1166644
1166887
  );
1166645
1166888
  }
1166646
1166889
  if (!session.isTokenValidated() && session.getValidationError()) {
1166647
- console.log("");
1166648
- console.log(
1166890
+ console.warn("");
1166891
+ console.warn(
1166649
1166892
  `${colors.yellow}Warning: ${session.getValidationError()}${colors.reset}`
1166650
1166893
  );
1166651
1166894
  }
1166652
1166895
  } else {
1166653
- console.log("");
1166654
- console.log(
1166896
+ console.warn("");
1166897
+ console.warn(
1166655
1166898
  `${colors.yellow}No connection profiles found.${colors.reset}`
1166656
1166899
  );
1166657
- console.log("");
1166658
- console.log(
1166900
+ console.warn("");
1166901
+ console.warn(
1166659
1166902
  "Create a profile to connect to F5 Distributed Cloud:"
1166660
1166903
  );
1166661
- console.log("");
1166662
- console.log(
1166904
+ console.warn("");
1166905
+ console.warn(
1166663
1166906
  ` ${colors.blue}login profile create${colors.reset} <name> --url <api-url> --token <api-token>`
1166664
1166907
  );
1166665
- console.log("");
1166666
- console.log("Or set environment variables:");
1166667
- console.log("");
1166668
- console.log(
1166908
+ console.warn("");
1166909
+ console.warn("Or set environment variables:");
1166910
+ console.warn("");
1166911
+ console.warn(
1166669
1166912
  ` ${colors.blue}export ${ENV_PREFIX}_API_URL${colors.reset}=https://tenant.console.ves.volterra.io`
1166670
1166913
  );
1166671
- console.log(
1166914
+ console.warn(
1166672
1166915
  ` ${colors.blue}export ${ENV_PREFIX}_API_TOKEN${colors.reset}=<your-api-token>`
1166673
1166916
  );
1166674
- console.log("");
1166917
+ console.warn("");
1166675
1166918
  }
1166676
1166919
  process.stdin.resume();
1166677
1166920
  const appProps = { initialSession: session };
@@ -1166733,7 +1166976,7 @@ async function executeNonInteractive(args) {
1166733
1166976
  const command = args.join(" ");
1166734
1166977
  const result = await executeCommand(command, session);
1166735
1166978
  result.output.forEach((line) => {
1166736
- console.log(line);
1166979
+ console.warn(line);
1166737
1166980
  });
1166738
1166981
  if (result.error) {
1166739
1166982
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robinmordasiewicz/f5xc-xcsh",
3
- "version": "2.0.46-2601220508",
3
+ "version": "2.0.46-2601220805",
4
4
  "description": "F5 Distributed Cloud Shell - Interactive CLI for F5 XC",
5
5
  "type": "module",
6
6
  "bin": {