@f5xc-salesdemos/xcsh 18.88.0 → 18.89.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 +7 -7
- package/src/extensibility/extensions/loader.ts +6 -0
- package/src/extensibility/extensions/runner.ts +12 -0
- package/src/extensibility/extensions/types.ts +17 -0
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/modes/interactive-mode.ts +41 -2
- package/src/modes/rpc/rpc-mode.ts +13 -0
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.89.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",
|
|
@@ -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.89.0",
|
|
54
|
+
"@f5xc-salesdemos/pi-agent-core": "18.89.0",
|
|
55
|
+
"@f5xc-salesdemos/pi-ai": "18.89.0",
|
|
56
|
+
"@f5xc-salesdemos/pi-natives": "18.89.0",
|
|
57
|
+
"@f5xc-salesdemos/pi-tui": "18.89.0",
|
|
58
|
+
"@f5xc-salesdemos/pi-utils": "18.89.0",
|
|
59
59
|
"@sinclair/typebox": "^0.34",
|
|
60
60
|
"@xterm/headless": "^6.0",
|
|
61
61
|
"ajv": "^8.18",
|
|
@@ -28,6 +28,7 @@ import type {
|
|
|
28
28
|
LoadExtensionsResult,
|
|
29
29
|
MessageRenderer,
|
|
30
30
|
RegisteredCommand,
|
|
31
|
+
ServiceStatusContribution,
|
|
31
32
|
ToolDefinition,
|
|
32
33
|
} from "./types";
|
|
33
34
|
|
|
@@ -176,6 +177,10 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
176
177
|
this.extension.messageRenderers.set(customType, renderer as MessageRenderer);
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
registerServiceStatus(contribution: ServiceStatusContribution): void {
|
|
181
|
+
this.extension.serviceStatuses.set(contribution.name, contribution);
|
|
182
|
+
}
|
|
183
|
+
|
|
179
184
|
getFlag(name: string): boolean | string | undefined {
|
|
180
185
|
if (!this.extension.flags.has(name)) return undefined;
|
|
181
186
|
return this.runtime.flagValues.get(name);
|
|
@@ -257,6 +262,7 @@ function createExtension(extensionPath: string, resolvedPath: string): Extension
|
|
|
257
262
|
commands: new Map(),
|
|
258
263
|
flags: new Map(),
|
|
259
264
|
shortcuts: new Map(),
|
|
265
|
+
serviceStatuses: new Map(),
|
|
260
266
|
};
|
|
261
267
|
}
|
|
262
268
|
|
|
@@ -36,6 +36,7 @@ import type {
|
|
|
36
36
|
RegisteredTool,
|
|
37
37
|
ResourcesDiscoverEvent,
|
|
38
38
|
ResourcesDiscoverResult,
|
|
39
|
+
ServiceStatusContribution,
|
|
39
40
|
SessionBeforeBranchResult,
|
|
40
41
|
SessionBeforeCompactResult,
|
|
41
42
|
SessionBeforeSwitchResult,
|
|
@@ -251,6 +252,17 @@ export class ExtensionRunner {
|
|
|
251
252
|
return tools;
|
|
252
253
|
}
|
|
253
254
|
|
|
255
|
+
/** Get all registered service status contributions from all extensions. */
|
|
256
|
+
getAllRegisteredServiceStatuses(): ServiceStatusContribution[] {
|
|
257
|
+
const statuses: ServiceStatusContribution[] = [];
|
|
258
|
+
for (const ext of this.extensions) {
|
|
259
|
+
for (const contribution of ext.serviceStatuses.values()) {
|
|
260
|
+
statuses.push(contribution);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return statuses;
|
|
264
|
+
}
|
|
265
|
+
|
|
254
266
|
getFlags(): Map<string, ExtensionFlag> {
|
|
255
267
|
const allFlags = new Map<string, ExtensionFlag>();
|
|
256
268
|
for (const ext of this.extensions) {
|
|
@@ -933,6 +933,19 @@ export interface RegisteredCommand {
|
|
|
933
933
|
handler: (args: string, ctx: ExtensionCommandContext) => Promise<void>;
|
|
934
934
|
}
|
|
935
935
|
|
|
936
|
+
// ============================================================================
|
|
937
|
+
// Service Status Contributions
|
|
938
|
+
// ============================================================================
|
|
939
|
+
|
|
940
|
+
export interface ServiceStatusContribution {
|
|
941
|
+
name: string;
|
|
942
|
+
check: () => Promise<{ state: "connected" | "unauthenticated" | "unavailable"; hint?: string }>;
|
|
943
|
+
fix?: {
|
|
944
|
+
prompt: string;
|
|
945
|
+
command: string[];
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
|
|
936
949
|
// ============================================================================
|
|
937
950
|
// Extension API
|
|
938
951
|
// ============================================================================
|
|
@@ -1064,6 +1077,9 @@ export interface ExtensionAPI {
|
|
|
1064
1077
|
/** Register a custom renderer for CustomMessageEntry. */
|
|
1065
1078
|
registerMessageRenderer<T = unknown>(customType: string, renderer: MessageRenderer<T>): void;
|
|
1066
1079
|
|
|
1080
|
+
/** Register a service status check for the welcome screen. */
|
|
1081
|
+
registerServiceStatus(contribution: ServiceStatusContribution): void;
|
|
1082
|
+
|
|
1067
1083
|
// =========================================================================
|
|
1068
1084
|
// Actions
|
|
1069
1085
|
// =========================================================================
|
|
@@ -1351,6 +1367,7 @@ export interface Extension {
|
|
|
1351
1367
|
commands: Map<string, RegisteredCommand>;
|
|
1352
1368
|
flags: Map<string, ExtensionFlag>;
|
|
1353
1369
|
shortcuts: Map<KeyId, ExtensionShortcut>;
|
|
1370
|
+
serviceStatuses: Map<string, ServiceStatusContribution>;
|
|
1354
1371
|
}
|
|
1355
1372
|
|
|
1356
1373
|
/** Result of loading extensions. */
|
|
@@ -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.89.0",
|
|
21
|
+
"commit": "e4462e8d079ea1a120c1f9253c79f78eb4d01e65",
|
|
22
|
+
"shortCommit": "e4462e8",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v18.
|
|
25
|
-
"commitDate": "2026-05-
|
|
26
|
-
"buildDate": "2026-
|
|
24
|
+
"tag": "v18.89.0",
|
|
25
|
+
"commitDate": "2026-05-31T23:55:12Z",
|
|
26
|
+
"buildDate": "2026-06-01T00:31:02.396Z",
|
|
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/e4462e8d079ea1a120c1f9253c79f78eb4d01e65",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.89.0"
|
|
33
33
|
};
|
|
@@ -352,7 +352,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
352
352
|
this.ui.addChild(new Spacer(1));
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
const services =
|
|
355
|
+
const services: ServiceStatus[] =
|
|
356
356
|
!startupQuiet && welcomeResult.model.state === "connected"
|
|
357
357
|
? [
|
|
358
358
|
mapContextStatus(welcomeResult.context ?? { state: "no_context" }),
|
|
@@ -364,7 +364,20 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
364
364
|
]
|
|
365
365
|
: [];
|
|
366
366
|
|
|
367
|
-
|
|
367
|
+
// Collect service statuses from plugins
|
|
368
|
+
if (this.session.extensionRunner) {
|
|
369
|
+
const pluginContributions = this.session.extensionRunner.getAllRegisteredServiceStatuses();
|
|
370
|
+
for (const contribution of pluginContributions) {
|
|
371
|
+
try {
|
|
372
|
+
const status = await contribution.check();
|
|
373
|
+
services.push({ name: contribution.name, ...status });
|
|
374
|
+
} catch {
|
|
375
|
+
services.push({ name: contribution.name, state: "unavailable", hint: "check failed" });
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const fixableServices: FixableService[] =
|
|
368
381
|
!startupQuiet && welcomeResult.model.state === "connected"
|
|
369
382
|
? getFixableServices({
|
|
370
383
|
aws: awsStatus,
|
|
@@ -375,6 +388,32 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
375
388
|
})
|
|
376
389
|
: [];
|
|
377
390
|
|
|
391
|
+
// Add fixable services from plugins
|
|
392
|
+
if (this.session.extensionRunner) {
|
|
393
|
+
const pluginContributions = this.session.extensionRunner.getAllRegisteredServiceStatuses();
|
|
394
|
+
for (const contribution of pluginContributions) {
|
|
395
|
+
if (contribution.fix) {
|
|
396
|
+
// Find the status we already checked above
|
|
397
|
+
const status = services.find(s => s.name === contribution.name);
|
|
398
|
+
if (status && status.state === "unauthenticated") {
|
|
399
|
+
fixableServices.push({
|
|
400
|
+
name: contribution.name,
|
|
401
|
+
prompt: contribution.fix.prompt,
|
|
402
|
+
command: contribution.fix.command,
|
|
403
|
+
recheck: async () => {
|
|
404
|
+
try {
|
|
405
|
+
const result = await contribution.check();
|
|
406
|
+
return { name: contribution.name, ...result };
|
|
407
|
+
} catch {
|
|
408
|
+
return { name: contribution.name, state: "unavailable" as const, hint: "recheck failed" };
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
378
417
|
if (!startupQuiet) {
|
|
379
418
|
this.#welcomeComponent = new WelcomeComponent(
|
|
380
419
|
this.#version,
|
|
@@ -649,6 +649,19 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
649
649
|
]
|
|
650
650
|
: [];
|
|
651
651
|
|
|
652
|
+
// Collect service statuses from plugins
|
|
653
|
+
if (extensionRunner) {
|
|
654
|
+
const pluginContributions = extensionRunner.getAllRegisteredServiceStatuses();
|
|
655
|
+
for (const contribution of pluginContributions) {
|
|
656
|
+
try {
|
|
657
|
+
const status = await contribution.check();
|
|
658
|
+
services.push({ name: contribution.name, ...status });
|
|
659
|
+
} catch {
|
|
660
|
+
services.push({ name: contribution.name, state: "unavailable", hint: "check failed" });
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
652
665
|
return success(id, "get_integrations", {
|
|
653
666
|
version: VERSION,
|
|
654
667
|
model: welcomeResult.model,
|