@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.
- package/dist/index.js +144 -9
- 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.
|
|
45358
|
-
return "6.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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) => {
|