@f5xc-salesdemos/xcsh 18.65.0 → 18.66.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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "18.
|
|
4
|
+
"version": "18.66.0",
|
|
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.
|
|
52
|
-
"@f5xc-salesdemos/pi-agent-core": "18.
|
|
53
|
-
"@f5xc-salesdemos/pi-ai": "18.
|
|
54
|
-
"@f5xc-salesdemos/pi-natives": "18.
|
|
55
|
-
"@f5xc-salesdemos/pi-tui": "18.
|
|
56
|
-
"@f5xc-salesdemos/pi-utils": "18.
|
|
51
|
+
"@f5xc-salesdemos/xcsh-stats": "18.66.0",
|
|
52
|
+
"@f5xc-salesdemos/pi-agent-core": "18.66.0",
|
|
53
|
+
"@f5xc-salesdemos/pi-ai": "18.66.0",
|
|
54
|
+
"@f5xc-salesdemos/pi-natives": "18.66.0",
|
|
55
|
+
"@f5xc-salesdemos/pi-tui": "18.66.0",
|
|
56
|
+
"@f5xc-salesdemos/pi-utils": "18.66.0",
|
|
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.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "18.66.0",
|
|
21
|
+
"commit": "e018080e23d781f41f31f5fddb031abf40358670",
|
|
22
|
+
"shortCommit": "e018080",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v18.
|
|
25
|
-
"commitDate": "2026-05-
|
|
26
|
-
"buildDate": "2026-05-
|
|
27
|
-
"dirty":
|
|
24
|
+
"tag": "v18.66.0",
|
|
25
|
+
"commitDate": "2026-05-17T18:46:03Z",
|
|
26
|
+
"buildDate": "2026-05-17T19:10:23.269Z",
|
|
27
|
+
"dirty": false,
|
|
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.
|
|
31
|
+
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/e018080e23d781f41f31f5fddb031abf40358670",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.66.0"
|
|
33
33
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Model } from "@f5xc-salesdemos/pi-ai";
|
|
2
2
|
import { validateApiKeyAgainstModelsEndpoint } from "@f5xc-salesdemos/pi-ai/utils/oauth/api-key-validation";
|
|
3
|
-
import { $which, logger } from "@f5xc-salesdemos/pi-utils";
|
|
3
|
+
import { $which, getProjectDir, logger } from "@f5xc-salesdemos/pi-utils";
|
|
4
4
|
import { $ } from "bun";
|
|
5
5
|
import { loadProfile } from "../../internal-urls/user-profile";
|
|
6
6
|
import { type AuthStatus, ContextService } from "../../services/f5xc-context";
|
|
@@ -461,6 +461,7 @@ export function classifyAwsError(stderr: string): AwsCheckState {
|
|
|
461
461
|
if (
|
|
462
462
|
s.includes("sso session") ||
|
|
463
463
|
s.includes("sso token") ||
|
|
464
|
+
(s.includes("token from sso") && (s.includes("expired") || s.includes("refresh failed"))) ||
|
|
464
465
|
s.includes("expiredtokenexception") ||
|
|
465
466
|
s.includes("expiredtoken")
|
|
466
467
|
) {
|
|
@@ -594,3 +595,72 @@ export async function checkProfileStatus(): Promise<WelcomeProfileStatus | undef
|
|
|
594
595
|
return { state: "missing" };
|
|
595
596
|
}
|
|
596
597
|
}
|
|
598
|
+
|
|
599
|
+
export interface FixableService {
|
|
600
|
+
name: string;
|
|
601
|
+
prompt: string;
|
|
602
|
+
command: string[];
|
|
603
|
+
recheck: () => Promise<ServiceStatus>;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export function getFixableServices(statuses: {
|
|
607
|
+
aws: WelcomeAwsStatus | undefined;
|
|
608
|
+
azure: WelcomeAzureStatus | undefined;
|
|
609
|
+
gcloud: WelcomeGcloudStatus | undefined;
|
|
610
|
+
github: WelcomeGitHubStatus | undefined;
|
|
611
|
+
gitlab: WelcomeGitLabStatus | undefined;
|
|
612
|
+
salesforce: WelcomeSalesforceStatus | undefined;
|
|
613
|
+
}): FixableService[] {
|
|
614
|
+
const fixable: FixableService[] = [];
|
|
615
|
+
|
|
616
|
+
if (statuses.gitlab?.state === "auth_error") {
|
|
617
|
+
fixable.push({
|
|
618
|
+
name: "GitLab",
|
|
619
|
+
prompt: "GitLab not authenticated",
|
|
620
|
+
command: ["glab", "auth", "login"],
|
|
621
|
+
recheck: async () => mapGitLabStatus(await checkGitLabStatus(getProjectDir())),
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
if (statuses.github?.state === "auth_error") {
|
|
625
|
+
fixable.push({
|
|
626
|
+
name: "GitHub",
|
|
627
|
+
prompt: "GitHub not authenticated",
|
|
628
|
+
command: ["gh", "auth", "login"],
|
|
629
|
+
recheck: async () => mapGitHubStatus(await checkGitHubStatus()),
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
if (statuses.salesforce?.state === "session_expired") {
|
|
633
|
+
fixable.push({
|
|
634
|
+
name: "Salesforce",
|
|
635
|
+
prompt: "Salesforce session expired",
|
|
636
|
+
command: ["sf", "org", "login", "web"],
|
|
637
|
+
recheck: async () => mapSalesforceStatus(await checkSalesforceStatus(getProjectDir())),
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
if (statuses.azure?.state === "auth_error") {
|
|
641
|
+
fixable.push({
|
|
642
|
+
name: "Azure",
|
|
643
|
+
prompt: "Azure session expired",
|
|
644
|
+
command: ["az", "login", "--use-device-code"],
|
|
645
|
+
recheck: async () => mapAzureStatus(await checkAzureStatus()),
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
if (statuses.aws?.state === "sso_expired") {
|
|
649
|
+
fixable.push({
|
|
650
|
+
name: "AWS",
|
|
651
|
+
prompt: "AWS SSO session expired",
|
|
652
|
+
command: ["aws", "sso", "login"],
|
|
653
|
+
recheck: async () => mapAwsStatus(await checkAwsStatus()),
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
if (statuses.gcloud?.state === "token_expired") {
|
|
657
|
+
fixable.push({
|
|
658
|
+
name: "Google Cloud",
|
|
659
|
+
prompt: "Google Cloud token expired",
|
|
660
|
+
command: ["gcloud", "auth", "login"],
|
|
661
|
+
recheck: async () => mapGcloudStatus(await checkGcloudStatus()),
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return fixable;
|
|
666
|
+
}
|
|
@@ -60,6 +60,8 @@ import {
|
|
|
60
60
|
checkGitHubStatus,
|
|
61
61
|
checkGitLabStatus,
|
|
62
62
|
checkSalesforceStatus,
|
|
63
|
+
type FixableService,
|
|
64
|
+
getFixableServices,
|
|
63
65
|
mapAwsStatus,
|
|
64
66
|
mapAzureStatus,
|
|
65
67
|
mapContextStatus,
|
|
@@ -68,6 +70,7 @@ import {
|
|
|
68
70
|
mapGitLabStatus,
|
|
69
71
|
mapSalesforceStatus,
|
|
70
72
|
runWelcomeChecks,
|
|
73
|
+
type ServiceStatus,
|
|
71
74
|
} from "./components/welcome-checks";
|
|
72
75
|
import { BtwController } from "./controllers/btw-controller";
|
|
73
76
|
import { CommandController } from "./controllers/command-controller";
|
|
@@ -358,20 +361,32 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
358
361
|
this.ui.addChild(new Spacer(1));
|
|
359
362
|
}
|
|
360
363
|
|
|
364
|
+
const services =
|
|
365
|
+
!startupQuiet && welcomeResult.model.state === "connected"
|
|
366
|
+
? [
|
|
367
|
+
mapContextStatus(welcomeResult.context ?? { state: "no_context" }),
|
|
368
|
+
mapGitLabStatus(gitlabStatus),
|
|
369
|
+
mapGitHubStatus(githubStatus),
|
|
370
|
+
mapSalesforceStatus(salesforceStatus),
|
|
371
|
+
mapAzureStatus(azureStatus),
|
|
372
|
+
mapAwsStatus(awsStatus),
|
|
373
|
+
mapGcloudStatus(gcloudStatus),
|
|
374
|
+
]
|
|
375
|
+
: [];
|
|
376
|
+
|
|
377
|
+
const fixableServices =
|
|
378
|
+
!startupQuiet && welcomeResult.model.state === "connected"
|
|
379
|
+
? getFixableServices({
|
|
380
|
+
aws: awsStatus,
|
|
381
|
+
azure: azureStatus,
|
|
382
|
+
gcloud: gcloudStatus,
|
|
383
|
+
github: githubStatus,
|
|
384
|
+
gitlab: gitlabStatus,
|
|
385
|
+
salesforce: salesforceStatus,
|
|
386
|
+
})
|
|
387
|
+
: [];
|
|
388
|
+
|
|
361
389
|
if (!startupQuiet) {
|
|
362
|
-
// Welcome box owns all startup notifications (model, services, update)
|
|
363
|
-
const services =
|
|
364
|
-
welcomeResult.model.state === "connected"
|
|
365
|
-
? [
|
|
366
|
-
mapContextStatus(welcomeResult.context ?? { state: "no_context" }),
|
|
367
|
-
mapGitLabStatus(gitlabStatus),
|
|
368
|
-
mapGitHubStatus(githubStatus),
|
|
369
|
-
mapSalesforceStatus(salesforceStatus),
|
|
370
|
-
mapAzureStatus(azureStatus),
|
|
371
|
-
mapAwsStatus(awsStatus),
|
|
372
|
-
mapGcloudStatus(gcloudStatus),
|
|
373
|
-
]
|
|
374
|
-
: [];
|
|
375
390
|
this.#welcomeComponent = new WelcomeComponent(
|
|
376
391
|
this.#version,
|
|
377
392
|
welcomeResult.model,
|
|
@@ -423,6 +438,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
423
438
|
// Initialize hooks with TUI-based UI context
|
|
424
439
|
await this.initHooksAndCustomTools();
|
|
425
440
|
|
|
441
|
+
// Offer to fix expired cloud credentials before entering the main loop
|
|
442
|
+
if (fixableServices.length > 0) {
|
|
443
|
+
await this.#offerCredentialFixes(fixableServices, services);
|
|
444
|
+
}
|
|
445
|
+
|
|
426
446
|
// Restore mode from session (e.g. plan mode on resume)
|
|
427
447
|
await this.#restoreModeFromSession();
|
|
428
448
|
|
|
@@ -913,6 +933,40 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
913
933
|
}
|
|
914
934
|
}
|
|
915
935
|
|
|
936
|
+
async #offerCredentialFixes(fixable: FixableService[], currentServices: ServiceStatus[]): Promise<void> {
|
|
937
|
+
for (const service of fixable) {
|
|
938
|
+
const confirmed = await this.#extensionUiController.showHookConfirm(
|
|
939
|
+
`${service.name} login`,
|
|
940
|
+
`${service.prompt}. Fix now?`,
|
|
941
|
+
);
|
|
942
|
+
if (!confirmed) continue;
|
|
943
|
+
|
|
944
|
+
this.ui.stop();
|
|
945
|
+
try {
|
|
946
|
+
const proc = Bun.spawn(service.command, {
|
|
947
|
+
stdin: "inherit",
|
|
948
|
+
stdout: "inherit",
|
|
949
|
+
stderr: "inherit",
|
|
950
|
+
});
|
|
951
|
+
await proc.exited;
|
|
952
|
+
} catch (error) {
|
|
953
|
+
logger.warn(`Auto-fix for ${service.name} failed`, { error: String(error) });
|
|
954
|
+
} finally {
|
|
955
|
+
const clearScreen = settings.get("startup.clearScreen");
|
|
956
|
+
this.ui.start(clearScreen);
|
|
957
|
+
this.ui.requestRender(true);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
const updated = await service.recheck();
|
|
961
|
+
const idx = currentServices.findIndex(s => s.name === service.name);
|
|
962
|
+
if (idx !== -1) {
|
|
963
|
+
currentServices[idx] = updated;
|
|
964
|
+
this.#welcomeComponent?.setServices([...currentServices]);
|
|
965
|
+
this.ui.requestRender();
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
916
970
|
async #approvePlan(
|
|
917
971
|
planContent: string,
|
|
918
972
|
options: { planFilePath: string; finalPlanFilePath: string },
|