@f5xc-salesdemos/xcsh 18.90.0 → 18.91.1
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 +6 -0
- package/package.json +7 -7
- package/src/extensibility/extensions/types.ts +1 -0
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/modes/components/welcome-checks.ts +2 -46
- package/src/modes/components/welcome.ts +38 -3
- package/src/modes/interactive-mode.ts +15 -5
- package/src/modes/rpc/rpc-mode.ts +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.91.0] - 2026-06-01
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Welcome screen fully plugin-driven: removed all hardcoded cloud service stubs (`getFixableServices` empty stub, `checkProfileStatus` dead code). Only F5 XC Context remains as a built-in service. All cloud connector status checks now come exclusively from the Extension API `registerServiceStatus()`. ([#1076](https://github.com/f5xc-salesdemos/xcsh/issues/1076))
|
|
10
|
+
|
|
5
11
|
## [18.90.0] - 2026-06-01
|
|
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.
|
|
4
|
+
"version": "18.91.1",
|
|
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",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
52
52
|
"@mozilla/readability": "^0.6",
|
|
53
|
-
"@f5xc-salesdemos/xcsh-stats": "18.
|
|
54
|
-
"@f5xc-salesdemos/pi-agent-core": "18.
|
|
55
|
-
"@f5xc-salesdemos/pi-ai": "18.
|
|
56
|
-
"@f5xc-salesdemos/pi-natives": "18.
|
|
57
|
-
"@f5xc-salesdemos/pi-tui": "18.
|
|
58
|
-
"@f5xc-salesdemos/pi-utils": "18.
|
|
53
|
+
"@f5xc-salesdemos/xcsh-stats": "18.91.1",
|
|
54
|
+
"@f5xc-salesdemos/pi-agent-core": "18.91.1",
|
|
55
|
+
"@f5xc-salesdemos/pi-ai": "18.91.1",
|
|
56
|
+
"@f5xc-salesdemos/pi-natives": "18.91.1",
|
|
57
|
+
"@f5xc-salesdemos/pi-tui": "18.91.1",
|
|
58
|
+
"@f5xc-salesdemos/pi-utils": "18.91.1",
|
|
59
59
|
"@sinclair/typebox": "^0.34",
|
|
60
60
|
"@xterm/headless": "^6.0",
|
|
61
61
|
"ajv": "^8.18",
|
|
@@ -939,6 +939,7 @@ export interface RegisteredCommand {
|
|
|
939
939
|
|
|
940
940
|
export interface ServiceStatusContribution {
|
|
941
941
|
name: string;
|
|
942
|
+
group?: string;
|
|
942
943
|
check: () => Promise<{ state: "connected" | "unauthenticated" | "unavailable"; hint?: string }>;
|
|
943
944
|
fix?: {
|
|
944
945
|
prompt: string;
|
|
@@ -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.91.1",
|
|
21
|
+
"commit": "722eac01533203da9bd0bd3ddafb10c38f851d5c",
|
|
22
|
+
"shortCommit": "722eac0",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v18.
|
|
25
|
-
"commitDate": "2026-06-
|
|
26
|
-
"buildDate": "2026-06-
|
|
24
|
+
"tag": "v18.91.1",
|
|
25
|
+
"commitDate": "2026-06-01T21:58:38Z",
|
|
26
|
+
"buildDate": "2026-06-01T22:19:50.728Z",
|
|
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.
|
|
31
|
+
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/722eac01533203da9bd0bd3ddafb10c38f851d5c",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.91.1"
|
|
33
33
|
};
|
|
@@ -1,7 +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
3
|
import { logger } from "@f5xc-salesdemos/pi-utils";
|
|
4
|
-
import { loadProfile } from "../../internal-urls/user-profile";
|
|
5
4
|
import { type AuthStatus, ContextService } from "../../services/f5xc-context";
|
|
6
5
|
import { deriveTenantFromUrl } from "../../services/f5xc-env";
|
|
7
6
|
import type { AuthStorage } from "../../session/auth-storage";
|
|
@@ -185,6 +184,8 @@ export interface ServiceStatus {
|
|
|
185
184
|
name: string;
|
|
186
185
|
state: ServiceState;
|
|
187
186
|
hint?: string;
|
|
187
|
+
_isPlugin?: boolean;
|
|
188
|
+
_group?: string;
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
export function mapContextStatus(status: WelcomeContextStatus): ServiceStatus {
|
|
@@ -207,54 +208,9 @@ export function mapContextStatus(status: WelcomeContextStatus): ServiceStatus {
|
|
|
207
208
|
}
|
|
208
209
|
}
|
|
209
210
|
|
|
210
|
-
export type ProfileCheckState = "current" | "stale" | "missing";
|
|
211
|
-
|
|
212
|
-
export interface WelcomeProfileStatus {
|
|
213
|
-
state: ProfileCheckState;
|
|
214
|
-
name?: string;
|
|
215
|
-
updatedAt?: string;
|
|
216
|
-
staleDays?: number;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const PROFILE_STALE_HOURS = 24;
|
|
220
|
-
|
|
221
|
-
/** Check user profile freshness. Returns undefined only on unexpected error. */
|
|
222
|
-
export async function checkProfileStatus(): Promise<WelcomeProfileStatus | undefined> {
|
|
223
|
-
try {
|
|
224
|
-
const profile = await loadProfile();
|
|
225
|
-
if (!profile.givenName && !profile.familyName) {
|
|
226
|
-
return { state: "missing" };
|
|
227
|
-
}
|
|
228
|
-
const name = [profile.givenName, profile.familyName].filter(Boolean).join(" ");
|
|
229
|
-
const updatedAt = profile.updatedAt;
|
|
230
|
-
|
|
231
|
-
if (!updatedAt) {
|
|
232
|
-
return { state: "stale", name };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const ageHours = (Date.now() - new Date(updatedAt).getTime()) / (1000 * 60 * 60);
|
|
236
|
-
if (ageHours > PROFILE_STALE_HOURS) {
|
|
237
|
-
return {
|
|
238
|
-
state: "stale",
|
|
239
|
-
name,
|
|
240
|
-
updatedAt,
|
|
241
|
-
staleDays: Math.floor(ageHours / 24),
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return { state: "current", name, updatedAt };
|
|
246
|
-
} catch {
|
|
247
|
-
return { state: "missing" };
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
211
|
export interface FixableService {
|
|
252
212
|
name: string;
|
|
253
213
|
prompt: string;
|
|
254
214
|
command: string[];
|
|
255
215
|
recheck: () => Promise<ServiceStatus>;
|
|
256
216
|
}
|
|
257
|
-
|
|
258
|
-
export function getFixableServices(): FixableService[] {
|
|
259
|
-
return [];
|
|
260
|
-
}
|
|
@@ -122,15 +122,37 @@ export class WelcomeComponent implements Component {
|
|
|
122
122
|
|
|
123
123
|
#measureStatusWidth(): number {
|
|
124
124
|
const lines: string[] = [" Model Provider", ...this.#renderModelStatus()];
|
|
125
|
-
|
|
125
|
+
const coreServices = this.services.filter(s => !s._isPlugin);
|
|
126
|
+
const pluginServices = this.services.filter(s => s._isPlugin);
|
|
127
|
+
for (const svc of coreServices) {
|
|
126
128
|
lines.push(this.#renderServiceLine(svc));
|
|
127
129
|
}
|
|
130
|
+
if (pluginServices.length > 0) {
|
|
131
|
+
const groups = this.#groupPluginServices(pluginServices);
|
|
132
|
+
for (const [groupName] of groups) {
|
|
133
|
+
lines.push(` ${groupName}`);
|
|
134
|
+
}
|
|
135
|
+
for (const svc of pluginServices) {
|
|
136
|
+
lines.push(this.#renderServiceLine(svc));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
128
139
|
if (this.#showUpdateSection()) {
|
|
129
140
|
lines.push(this.#renderUpdateLine());
|
|
130
141
|
}
|
|
131
142
|
return Math.max(...lines.map(l => visibleWidth(l)));
|
|
132
143
|
}
|
|
133
144
|
|
|
145
|
+
#groupPluginServices(plugins: ServiceStatus[]): Map<string, ServiceStatus[]> {
|
|
146
|
+
const groups = new Map<string, ServiceStatus[]>();
|
|
147
|
+
for (const svc of plugins) {
|
|
148
|
+
const groupName = svc._group ?? "Plugins";
|
|
149
|
+
const list = groups.get(groupName) ?? [];
|
|
150
|
+
list.push(svc);
|
|
151
|
+
groups.set(groupName, list);
|
|
152
|
+
}
|
|
153
|
+
return groups;
|
|
154
|
+
}
|
|
155
|
+
|
|
134
156
|
#buildStatusLines(rightCol: number): string[] {
|
|
135
157
|
const lines: string[] = [];
|
|
136
158
|
const separatorWidth = Math.max(0, rightCol - 2);
|
|
@@ -139,11 +161,24 @@ export class WelcomeComponent implements Component {
|
|
|
139
161
|
lines.push(` ${theme.bold(theme.fg("contentAccent", "Model Provider"))}`);
|
|
140
162
|
lines.push(...this.#renderModelStatus());
|
|
141
163
|
lines.push("");
|
|
142
|
-
|
|
164
|
+
const coreServices = this.services.filter(s => !s._isPlugin);
|
|
165
|
+
const pluginServices = this.services.filter(s => s._isPlugin);
|
|
166
|
+
const hasContent = coreServices.length > 0 || pluginServices.length > 0 || this.#showUpdateSection();
|
|
167
|
+
if (hasContent) {
|
|
143
168
|
lines.push(separator);
|
|
144
|
-
for (const svc of
|
|
169
|
+
for (const svc of coreServices) {
|
|
145
170
|
lines.push(this.#renderServiceLine(svc));
|
|
146
171
|
}
|
|
172
|
+
if (pluginServices.length > 0) {
|
|
173
|
+
const groups = this.#groupPluginServices(pluginServices);
|
|
174
|
+
for (const [groupName, groupServices] of groups) {
|
|
175
|
+
lines.push("");
|
|
176
|
+
lines.push(` ${theme.fg("dim", groupName)}`);
|
|
177
|
+
for (const svc of groupServices) {
|
|
178
|
+
lines.push(this.#renderServiceLine(svc));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
147
182
|
if (this.#showUpdateSection()) {
|
|
148
183
|
lines.push(this.#renderUpdateLine());
|
|
149
184
|
}
|
|
@@ -54,7 +54,6 @@ import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
|
54
54
|
import { type UpdateStatus, WelcomeComponent } from "./components/welcome";
|
|
55
55
|
import {
|
|
56
56
|
type FixableService,
|
|
57
|
-
getFixableServices,
|
|
58
57
|
mapContextStatus,
|
|
59
58
|
runWelcomeChecks,
|
|
60
59
|
type ServiceStatus,
|
|
@@ -346,15 +345,20 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
346
345
|
for (const contribution of pluginContributions) {
|
|
347
346
|
try {
|
|
348
347
|
const status = await contribution.check();
|
|
349
|
-
services.push({ name: contribution.name, ...status });
|
|
348
|
+
services.push({ name: contribution.name, ...status, _isPlugin: true, _group: contribution.group });
|
|
350
349
|
} catch {
|
|
351
|
-
services.push({
|
|
350
|
+
services.push({
|
|
351
|
+
name: contribution.name,
|
|
352
|
+
state: "unavailable",
|
|
353
|
+
hint: "check failed",
|
|
354
|
+
_isPlugin: true,
|
|
355
|
+
_group: contribution.group,
|
|
356
|
+
});
|
|
352
357
|
}
|
|
353
358
|
}
|
|
354
359
|
}
|
|
355
360
|
|
|
356
|
-
const fixableServices: FixableService[] =
|
|
357
|
-
!startupQuiet && welcomeResult.model.state === "connected" ? getFixableServices() : [];
|
|
361
|
+
const fixableServices: FixableService[] = [];
|
|
358
362
|
|
|
359
363
|
// Add fixable services from plugins
|
|
360
364
|
if (this.session.extensionRunner) {
|
|
@@ -559,6 +563,12 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
559
563
|
finishPendingSubmission(input: SubmittedUserInput): void {
|
|
560
564
|
if (this.#pendingSubmittedInput === input) {
|
|
561
565
|
this.#pendingSubmittedInput = undefined;
|
|
566
|
+
this.#pendingWorkingMessage = undefined;
|
|
567
|
+
if (this.loadingAnimation) {
|
|
568
|
+
this.loadingAnimation.stop();
|
|
569
|
+
this.loadingAnimation = undefined;
|
|
570
|
+
this.statusContainer.clear();
|
|
571
|
+
}
|
|
562
572
|
}
|
|
563
573
|
}
|
|
564
574
|
|
|
@@ -625,9 +625,15 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
625
625
|
for (const contribution of pluginContributions) {
|
|
626
626
|
try {
|
|
627
627
|
const status = await contribution.check();
|
|
628
|
-
services.push({ name: contribution.name, ...status });
|
|
628
|
+
services.push({ name: contribution.name, ...status, _isPlugin: true, _group: contribution.group });
|
|
629
629
|
} catch {
|
|
630
|
-
services.push({
|
|
630
|
+
services.push({
|
|
631
|
+
name: contribution.name,
|
|
632
|
+
state: "unavailable",
|
|
633
|
+
hint: "check failed",
|
|
634
|
+
_isPlugin: true,
|
|
635
|
+
_group: contribution.group,
|
|
636
|
+
});
|
|
631
637
|
}
|
|
632
638
|
}
|
|
633
639
|
}
|