@robinmordasiewicz/f5xc-xcsh 6.39.0 → 6.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +144 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -45354,8 +45354,8 @@ function getLogoModeFromEnv(envPrefix) {
45354
45354
  var CLI_NAME = "xcsh";
45355
45355
  var CLI_FULL_NAME = "F5 Distributed Cloud Shell";
45356
45356
  function getVersion() {
45357
- if ("6.39.0") {
45358
- return "6.39.0";
45357
+ if ("6.40.0") {
45358
+ return "6.40.0";
45359
45359
  }
45360
45360
  if (process.env.XCSH_VERSION) {
45361
45361
  return process.env.XCSH_VERSION;
@@ -45791,6 +45791,9 @@ var APIClient = class {
45791
45791
  timeout;
45792
45792
  debug;
45793
45793
  retryConfig;
45794
+ // Token validation state
45795
+ _isValidated = false;
45796
+ _validationError = null;
45794
45797
  constructor(config) {
45795
45798
  this.serverUrl = config.serverUrl.replace(/\/+$/, "");
45796
45799
  this.apiToken = config.apiToken ?? "";
@@ -45802,11 +45805,64 @@ var APIClient = class {
45802
45805
  };
45803
45806
  }
45804
45807
  /**
45805
- * Check if client has authentication configured
45808
+ * Check if client has authentication configured (token exists).
45809
+ * Note: This does NOT verify the token is valid. Use isValidated() for that.
45806
45810
  */
45807
45811
  isAuthenticated() {
45808
45812
  return this.apiToken !== "";
45809
45813
  }
45814
+ /**
45815
+ * Validate the API token by making a lightweight API call.
45816
+ * Returns true if token is valid, false otherwise.
45817
+ */
45818
+ async validateToken() {
45819
+ if (!this.apiToken) {
45820
+ this._isValidated = false;
45821
+ this._validationError = "No API token configured";
45822
+ return { valid: false, error: this._validationError };
45823
+ }
45824
+ try {
45825
+ await this.get("/api/web/namespaces");
45826
+ this._isValidated = true;
45827
+ this._validationError = null;
45828
+ return { valid: true };
45829
+ } catch (error) {
45830
+ this._isValidated = false;
45831
+ if (error instanceof APIError) {
45832
+ if (error.statusCode === 401) {
45833
+ this._validationError = "Invalid or expired API token";
45834
+ } else if (error.statusCode === 403) {
45835
+ this._validationError = "Token lacks required permissions";
45836
+ } else if (error.statusCode === 0) {
45837
+ this._validationError = "Network error - could not reach server";
45838
+ } else {
45839
+ this._validationError = `Validation failed: HTTP ${error.statusCode}`;
45840
+ }
45841
+ } else {
45842
+ this._validationError = "Validation failed: Unknown error";
45843
+ }
45844
+ return { valid: false, error: this._validationError };
45845
+ }
45846
+ }
45847
+ /**
45848
+ * Check if client has a validated (verified working) token
45849
+ */
45850
+ isValidated() {
45851
+ return this._isValidated;
45852
+ }
45853
+ /**
45854
+ * Get the validation error message, if any
45855
+ */
45856
+ getValidationError() {
45857
+ return this._validationError;
45858
+ }
45859
+ /**
45860
+ * Clear validation state (called on profile switch)
45861
+ */
45862
+ clearValidationCache() {
45863
+ this._isValidated = false;
45864
+ this._validationError = null;
45865
+ }
45810
45866
  /**
45811
45867
  * Get the server URL
45812
45868
  */
@@ -46766,6 +46822,9 @@ var REPLSession = class {
46766
46822
  _activeProfileName = null;
46767
46823
  _namespaceCache = [];
46768
46824
  _namespaceCacheTime = 0;
46825
+ // Token validation state
46826
+ _tokenValidated = false;
46827
+ _validationError = null;
46769
46828
  constructor(config = {}) {
46770
46829
  this._namespace = config.namespace ?? this.getDefaultNamespace();
46771
46830
  this._contextPath = new ContextPath();
@@ -46801,7 +46860,12 @@ var REPLSession = class {
46801
46860
  }
46802
46861
  await this.loadActiveProfile();
46803
46862
  if (this._apiClient?.isAuthenticated()) {
46804
- await this.fetchUserInfo();
46863
+ const result = await this._apiClient.validateToken();
46864
+ this._tokenValidated = result.valid;
46865
+ this._validationError = result.error ?? null;
46866
+ if (result.valid) {
46867
+ await this.fetchUserInfo();
46868
+ }
46805
46869
  }
46806
46870
  }
46807
46871
  /**
@@ -46963,6 +47027,18 @@ var REPLSession = class {
46963
47027
  isAuthenticated() {
46964
47028
  return this._apiClient?.isAuthenticated() ?? false;
46965
47029
  }
47030
+ /**
47031
+ * Check if the token has been validated (verified working)
47032
+ */
47033
+ isTokenValidated() {
47034
+ return this._tokenValidated;
47035
+ }
47036
+ /**
47037
+ * Get the token validation error, if any
47038
+ */
47039
+ getValidationError() {
47040
+ return this._validationError;
47041
+ }
46966
47042
  /**
46967
47043
  * Get the API client
46968
47044
  */
@@ -47018,6 +47094,8 @@ var REPLSession = class {
47018
47094
  return false;
47019
47095
  }
47020
47096
  this.clearNamespaceCache();
47097
+ this._tokenValidated = false;
47098
+ this._validationError = null;
47021
47099
  this._activeProfileName = profileName;
47022
47100
  this._activeProfile = profile;
47023
47101
  if (profile.apiUrl) {
@@ -47036,6 +47114,11 @@ var REPLSession = class {
47036
47114
  apiToken: this._apiToken,
47037
47115
  debug: this._debug
47038
47116
  });
47117
+ if (this._apiClient.isAuthenticated()) {
47118
+ const validationResult = await this._apiClient.validateToken();
47119
+ this._tokenValidated = validationResult.valid;
47120
+ this._validationError = validationResult.error ?? null;
47121
+ }
47039
47122
  } else {
47040
47123
  this._apiClient = null;
47041
47124
  }
@@ -48390,6 +48473,18 @@ function extractTenantFromUrl(url) {
48390
48473
  return "unknown";
48391
48474
  }
48392
48475
  }
48476
+ function getAuthStatusValue(info, colorStatus) {
48477
+ if (!info.hasToken) {
48478
+ return colorStatus("\u2717 No token", false);
48479
+ }
48480
+ if (info.isValidated) {
48481
+ return colorStatus("\u2713 Authenticated", true);
48482
+ }
48483
+ if (info.validationError) {
48484
+ return colorStatus(`\u2717 ${info.validationError}`, false);
48485
+ }
48486
+ return colorStatus("\u26A0 Token not verified", false);
48487
+ }
48393
48488
  function formatConnectionTable(info, noColor = false) {
48394
48489
  const useColors = shouldUseColors(void 0, noColor);
48395
48490
  const box = useColors ? UNICODE_BOX2 : ASCII_BOX2;
@@ -48404,7 +48499,7 @@ function formatConnectionTable(info, noColor = false) {
48404
48499
  { label: "API URL", value: info.apiUrl },
48405
48500
  {
48406
48501
  label: "Auth",
48407
- value: info.hasToken ? colorStatus("\u2713 Token configured", true) : colorStatus("\u2717 No token", false)
48502
+ value: getAuthStatusValue(info, colorStatus)
48408
48503
  },
48409
48504
  { label: "Namespace", value: info.namespace || "default" },
48410
48505
  {
@@ -48445,8 +48540,8 @@ function formatConnectionTable(info, noColor = false) {
48445
48540
  function stripAnsi2(str) {
48446
48541
  return str.replace(/\x1b\[[0-9;]*m/g, "");
48447
48542
  }
48448
- function buildConnectionInfo(profileName, apiUrl, hasToken, namespace, isConnected) {
48449
- return {
48543
+ function buildConnectionInfo(profileName, apiUrl, hasToken, namespace, isConnected, isValidated, validationError) {
48544
+ const info = {
48450
48545
  profileName,
48451
48546
  tenant: extractTenantFromUrl(apiUrl),
48452
48547
  apiUrl,
@@ -48454,6 +48549,13 @@ function buildConnectionInfo(profileName, apiUrl, hasToken, namespace, isConnect
48454
48549
  namespace,
48455
48550
  isConnected
48456
48551
  };
48552
+ if (isValidated !== void 0) {
48553
+ info.isValidated = isValidated;
48554
+ }
48555
+ if (validationError) {
48556
+ info.validationError = validationError;
48557
+ }
48558
+ return info;
48457
48559
  }
48458
48560
 
48459
48561
  // src/domains/login/profile/create.ts
@@ -48568,7 +48670,9 @@ var useCommand = {
48568
48670
  profile?.apiUrl || "",
48569
48671
  !!profile?.apiToken,
48570
48672
  profile?.defaultNamespace || session.getNamespace(),
48571
- session.isAuthenticated()
48673
+ session.isAuthenticated(),
48674
+ session.isTokenValidated(),
48675
+ session.getValidationError() ?? void 0
48572
48676
  );
48573
48677
  const tableLines = formatConnectionTable(connectionInfo);
48574
48678
  return successResult(
@@ -49260,6 +49364,14 @@ async function getWhoamiInfo(session, _options = {}) {
49260
49364
  namespace: session.getNamespace(),
49261
49365
  isAuthenticated: session.isAuthenticated()
49262
49366
  };
49367
+ const isValidated = session.isTokenValidated();
49368
+ const validationError = session.getValidationError();
49369
+ if (isValidated !== void 0) {
49370
+ info.isValidated = isValidated;
49371
+ }
49372
+ if (validationError) {
49373
+ info.validationError = validationError;
49374
+ }
49263
49375
  if (!info.isAuthenticated) {
49264
49376
  return info;
49265
49377
  }
@@ -49298,6 +49410,18 @@ function formatWhoamiJson(info) {
49298
49410
  output.isAuthenticated = info.isAuthenticated;
49299
49411
  return [JSON.stringify(output, null, 2)];
49300
49412
  }
49413
+ function getAuthStatusDisplay(info) {
49414
+ if (!info.isAuthenticated) {
49415
+ return "Not authenticated";
49416
+ }
49417
+ if (info.isValidated) {
49418
+ return "\u2713 Authenticated";
49419
+ }
49420
+ if (info.validationError) {
49421
+ return `\u2717 ${info.validationError}`;
49422
+ }
49423
+ return "\u26A0 Token not verified";
49424
+ }
49301
49425
  function formatWhoamiBox(info) {
49302
49426
  const lines = [];
49303
49427
  const red = colors.red;
@@ -49323,7 +49447,7 @@ function formatWhoamiBox(info) {
49323
49447
  contentLines.push({ label: "Server", value: info.serverUrl });
49324
49448
  contentLines.push({
49325
49449
  label: "Auth",
49326
- value: info.isAuthenticated ? "\u2713 Authenticated" : "Not authenticated"
49450
+ value: getAuthStatusDisplay(info)
49327
49451
  });
49328
49452
  const maxLabelWidth = Math.max(...contentLines.map((c) => c.label.length));
49329
49453
  const formattedContent = contentLines.map((c) => {
@@ -53055,6 +53179,12 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
53055
53179
  await session.initialize();
53056
53180
  process.stdout.write("\r\x1B[K");
53057
53181
  renderBanner(cliLogoMode, "startup");
53182
+ if (session.isAuthenticated() && !session.isTokenValidated() && session.getValidationError()) {
53183
+ console.log("");
53184
+ console.log(
53185
+ `${colors.yellow}Warning: ${session.getValidationError()}${colors.reset}`
53186
+ );
53187
+ }
53058
53188
  const profiles = await session.getProfileManager().list();
53059
53189
  const envConfigured = process.env[`${ENV_PREFIX}_API_URL`] && process.env[`${ENV_PREFIX}_API_TOKEN`];
53060
53190
  if (profiles.length === 0 && !envConfigured) {
@@ -53092,6 +53222,11 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
53092
53222
  async function executeNonInteractive(args) {
53093
53223
  const session = new REPLSession();
53094
53224
  await session.initialize();
53225
+ if (session.isAuthenticated() && !session.isTokenValidated() && session.getValidationError()) {
53226
+ console.error(
53227
+ `${colors.yellow}Warning: ${session.getValidationError()}${colors.reset}`
53228
+ );
53229
+ }
53095
53230
  const command = args.join(" ");
53096
53231
  const result = await executeCommand(command, session);
53097
53232
  result.output.forEach((line) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robinmordasiewicz/f5xc-xcsh",
3
- "version": "6.39.0",
3
+ "version": "6.40.0",
4
4
  "description": "F5 Distributed Cloud Shell - Interactive CLI for F5 XC",
5
5
  "type": "module",
6
6
  "bin": {