@f5xc-salesdemos/xcsh 18.64.0 → 18.64.2
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.64.2] - 2026-05-16
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Welcome banner cloud provider hints: AWS SSO token expiry now correctly suggests `aws sso login` instead of `aws configure`; Google Cloud check replaced `gcloud auth list` (false positives on expired tokens) with `gcloud auth print-access-token`; F5 XC Context surfaces `errorClass` (network/URL) in hints; GitLab `project_inaccessible` gets its own hint; Salesforce differentiates `session_expired` and `not_configured` hints ([#825](https://github.com/f5xc-salesdemos/xcsh/issues/825))
|
|
10
|
+
|
|
5
11
|
## [18.64.0] - 2026-05-13
|
|
6
12
|
|
|
7
13
|
### Added
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "18.64.
|
|
4
|
+
"version": "18.64.2",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/f5xc-salesdemos/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
50
50
|
"@mozilla/readability": "^0.6",
|
|
51
|
-
"@f5xc-salesdemos/xcsh-stats": "18.64.
|
|
52
|
-
"@f5xc-salesdemos/pi-agent-core": "18.64.
|
|
53
|
-
"@f5xc-salesdemos/pi-ai": "18.64.
|
|
54
|
-
"@f5xc-salesdemos/pi-natives": "18.64.
|
|
55
|
-
"@f5xc-salesdemos/pi-tui": "18.64.
|
|
56
|
-
"@f5xc-salesdemos/pi-utils": "18.64.
|
|
51
|
+
"@f5xc-salesdemos/xcsh-stats": "18.64.2",
|
|
52
|
+
"@f5xc-salesdemos/pi-agent-core": "18.64.2",
|
|
53
|
+
"@f5xc-salesdemos/pi-ai": "18.64.2",
|
|
54
|
+
"@f5xc-salesdemos/pi-natives": "18.64.2",
|
|
55
|
+
"@f5xc-salesdemos/pi-tui": "18.64.2",
|
|
56
|
+
"@f5xc-salesdemos/pi-utils": "18.64.2",
|
|
57
57
|
"@sinclair/typebox": "^0.34",
|
|
58
58
|
"@xterm/headless": "^6.0",
|
|
59
59
|
"ajv": "^8.18",
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "18.64.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "18.64.2",
|
|
21
|
+
"commit": "192cc2f2ed25a353be4c27164699e162d96fd540",
|
|
22
|
+
"shortCommit": "192cc2f",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v18.64.
|
|
25
|
-
"commitDate": "2026-05-
|
|
26
|
-
"buildDate": "2026-05-
|
|
24
|
+
"tag": "v18.64.2",
|
|
25
|
+
"commitDate": "2026-05-16T23:00:39Z",
|
|
26
|
+
"buildDate": "2026-05-16T23:20:31.262Z",
|
|
27
27
|
"dirty": true,
|
|
28
28
|
"prNumber": "",
|
|
29
29
|
"repoUrl": "https://github.com/f5xc-salesdemos/xcsh",
|
|
30
30
|
"repoSlug": "f5xc-salesdemos/xcsh",
|
|
31
|
-
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/
|
|
32
|
-
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.64.
|
|
31
|
+
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/192cc2f2ed25a353be4c27164699e162d96fd540",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.64.2"
|
|
33
33
|
};
|
|
@@ -364,8 +364,16 @@ export function mapContextStatus(status: WelcomeContextStatus): ServiceStatus {
|
|
|
364
364
|
case "no_context":
|
|
365
365
|
return { name: "F5 XC Context", state: "unauthenticated", hint: "run: /context create" };
|
|
366
366
|
case "auth_error":
|
|
367
|
-
case "offline":
|
|
368
367
|
return { name: "F5 XC Context", state: "unauthenticated", hint: "run: /context" };
|
|
368
|
+
case "offline":
|
|
369
|
+
switch (status.errorClass) {
|
|
370
|
+
case "network":
|
|
371
|
+
return { name: "F5 XC Context", state: "unauthenticated", hint: "network unreachable" };
|
|
372
|
+
case "url_not_found":
|
|
373
|
+
return { name: "F5 XC Context", state: "unauthenticated", hint: "tenant URL not found" };
|
|
374
|
+
default:
|
|
375
|
+
return { name: "F5 XC Context", state: "unauthenticated", hint: "run: /context" };
|
|
376
|
+
}
|
|
369
377
|
}
|
|
370
378
|
}
|
|
371
379
|
|
|
@@ -376,6 +384,8 @@ export function mapGitLabStatus(status: WelcomeGitLabStatus | undefined): Servic
|
|
|
376
384
|
return { name: "GitLab", state: "connected" };
|
|
377
385
|
case "not_installed":
|
|
378
386
|
return { name: "GitLab", state: "unavailable", hint: "not installed" };
|
|
387
|
+
case "project_inaccessible":
|
|
388
|
+
return { name: "GitLab", state: "unauthenticated", hint: "check project access" };
|
|
379
389
|
default:
|
|
380
390
|
return { name: "GitLab", state: "unauthenticated", hint: "run: glab auth login" };
|
|
381
391
|
}
|
|
@@ -386,6 +396,10 @@ export function mapSalesforceStatus(status: WelcomeSalesforceStatus | undefined)
|
|
|
386
396
|
switch (status.state) {
|
|
387
397
|
case "connected":
|
|
388
398
|
return { name: "Salesforce", state: "connected" };
|
|
399
|
+
case "session_expired":
|
|
400
|
+
return { name: "Salesforce", state: "unauthenticated", hint: "session expired, run: sf org login web" };
|
|
401
|
+
case "not_configured":
|
|
402
|
+
return { name: "Salesforce", state: "unauthenticated", hint: "run: sf org login web --set-default" };
|
|
389
403
|
default:
|
|
390
404
|
return { name: "Salesforce", state: "unauthenticated", hint: "run: sf org login web" };
|
|
391
405
|
}
|
|
@@ -428,17 +442,52 @@ export function mapAzureStatus(status: WelcomeAzureStatus | undefined): ServiceS
|
|
|
428
442
|
}
|
|
429
443
|
}
|
|
430
444
|
|
|
431
|
-
export type AwsCheckState =
|
|
445
|
+
export type AwsCheckState =
|
|
446
|
+
| "connected"
|
|
447
|
+
| "sso_expired"
|
|
448
|
+
| "not_configured"
|
|
449
|
+
| "profile_not_found"
|
|
450
|
+
| "network_error"
|
|
451
|
+
| "auth_error";
|
|
432
452
|
|
|
433
453
|
export interface WelcomeAwsStatus {
|
|
434
454
|
state: AwsCheckState;
|
|
435
455
|
}
|
|
436
456
|
|
|
457
|
+
/** Classify AWS CLI stderr into a specific failure state. Exported for testing. */
|
|
458
|
+
export function classifyAwsError(stderr: string): AwsCheckState {
|
|
459
|
+
const s = stderr.toLowerCase();
|
|
460
|
+
// SSO session/token expiry
|
|
461
|
+
if (
|
|
462
|
+
s.includes("sso session") ||
|
|
463
|
+
s.includes("sso token") ||
|
|
464
|
+
s.includes("expiredtokenexception") ||
|
|
465
|
+
s.includes("expiredtoken")
|
|
466
|
+
) {
|
|
467
|
+
return "sso_expired";
|
|
468
|
+
}
|
|
469
|
+
// No credentials configured at all
|
|
470
|
+
if (s.includes("unable to locate credentials")) {
|
|
471
|
+
return "not_configured";
|
|
472
|
+
}
|
|
473
|
+
// AWS_PROFILE points to a non-existent profile
|
|
474
|
+
if (s.includes("config profile") && s.includes("could not be found")) {
|
|
475
|
+
return "profile_not_found";
|
|
476
|
+
}
|
|
477
|
+
// Network/connectivity errors
|
|
478
|
+
if (s.includes("could not connect") || s.includes("connection error") || s.includes("endpoint url")) {
|
|
479
|
+
return "network_error";
|
|
480
|
+
}
|
|
481
|
+
return "auth_error";
|
|
482
|
+
}
|
|
483
|
+
|
|
437
484
|
export async function checkAwsStatus(): Promise<WelcomeAwsStatus | undefined> {
|
|
438
485
|
try {
|
|
439
486
|
if (!$which("aws")) return undefined;
|
|
440
487
|
const result = await $`aws sts get-caller-identity --output json`.quiet().nothrow();
|
|
441
|
-
|
|
488
|
+
if (result.exitCode === 0) return { state: "connected" };
|
|
489
|
+
const stderr = result.stderr.toString();
|
|
490
|
+
return { state: classifyAwsError(stderr) };
|
|
442
491
|
} catch (err) {
|
|
443
492
|
logger.warn("AWS startup check failed", { error: String(err) });
|
|
444
493
|
return { state: "auth_error" };
|
|
@@ -450,23 +499,43 @@ export function mapAwsStatus(status: WelcomeAwsStatus | undefined): ServiceStatu
|
|
|
450
499
|
switch (status.state) {
|
|
451
500
|
case "connected":
|
|
452
501
|
return { name: "AWS", state: "connected" };
|
|
502
|
+
case "sso_expired":
|
|
503
|
+
return { name: "AWS", state: "unauthenticated", hint: "SSO session expired, run: aws sso login" };
|
|
504
|
+
case "not_configured":
|
|
505
|
+
return { name: "AWS", state: "unauthenticated", hint: "run: aws configure" };
|
|
506
|
+
case "profile_not_found":
|
|
507
|
+
return { name: "AWS", state: "unauthenticated", hint: "check AWS_PROFILE env var" };
|
|
508
|
+
case "network_error":
|
|
509
|
+
return { name: "AWS", state: "unauthenticated", hint: "network unreachable" };
|
|
453
510
|
case "auth_error":
|
|
454
511
|
return { name: "AWS", state: "unauthenticated", hint: "run: aws configure" };
|
|
455
512
|
}
|
|
456
513
|
}
|
|
457
514
|
|
|
458
|
-
export type GcloudCheckState = "connected" | "auth_error";
|
|
515
|
+
export type GcloudCheckState = "connected" | "token_expired" | "auth_error";
|
|
459
516
|
|
|
460
517
|
export interface WelcomeGcloudStatus {
|
|
461
518
|
state: GcloudCheckState;
|
|
462
519
|
}
|
|
463
520
|
|
|
521
|
+
/** Classify gcloud stderr into a specific failure state. Exported for testing. */
|
|
522
|
+
export function classifyGcloudError(stderr: string): GcloudCheckState {
|
|
523
|
+
const s = stderr.toLowerCase();
|
|
524
|
+
if (s.includes("valid credentials") || s.includes("refreshing your current auth tokens")) {
|
|
525
|
+
return "token_expired";
|
|
526
|
+
}
|
|
527
|
+
return "auth_error";
|
|
528
|
+
}
|
|
529
|
+
|
|
464
530
|
export async function checkGcloudStatus(): Promise<WelcomeGcloudStatus | undefined> {
|
|
465
531
|
try {
|
|
466
532
|
if (!$which("gcloud")) return undefined;
|
|
467
|
-
const result = await $`gcloud auth
|
|
468
|
-
|
|
469
|
-
|
|
533
|
+
const result = await $`gcloud auth print-access-token --quiet`.quiet().nothrow();
|
|
534
|
+
if (result.exitCode === 0 && result.text().trim().length > 0) {
|
|
535
|
+
return { state: "connected" };
|
|
536
|
+
}
|
|
537
|
+
const stderr = result.stderr.toString();
|
|
538
|
+
return { state: classifyGcloudError(stderr) };
|
|
470
539
|
} catch (err) {
|
|
471
540
|
logger.warn("Google Cloud startup check failed", { error: String(err) });
|
|
472
541
|
return { state: "auth_error" };
|
|
@@ -478,6 +547,8 @@ export function mapGcloudStatus(status: WelcomeGcloudStatus | undefined): Servic
|
|
|
478
547
|
switch (status.state) {
|
|
479
548
|
case "connected":
|
|
480
549
|
return { name: "Google Cloud", state: "connected" };
|
|
550
|
+
case "token_expired":
|
|
551
|
+
return { name: "Google Cloud", state: "unauthenticated", hint: "token expired, run: gcloud auth login" };
|
|
481
552
|
case "auth_error":
|
|
482
553
|
return { name: "Google Cloud", state: "unauthenticated", hint: "run: gcloud auth login" };
|
|
483
554
|
}
|