@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5xc-salesdemos/xcsh",
4
- "version": "18.88.0",
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.88.0",
54
- "@f5xc-salesdemos/pi-agent-core": "18.88.0",
55
- "@f5xc-salesdemos/pi-ai": "18.88.0",
56
- "@f5xc-salesdemos/pi-natives": "18.88.0",
57
- "@f5xc-salesdemos/pi-tui": "18.88.0",
58
- "@f5xc-salesdemos/pi-utils": "18.88.0",
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.88.0",
21
- "commit": "f9efe12cf22df675e6202334728d5fcb797e1d6a",
22
- "shortCommit": "f9efe12",
20
+ "version": "18.89.0",
21
+ "commit": "e4462e8d079ea1a120c1f9253c79f78eb4d01e65",
22
+ "shortCommit": "e4462e8",
23
23
  "branch": "main",
24
- "tag": "v18.88.0",
25
- "commitDate": "2026-05-31T14:58:13Z",
26
- "buildDate": "2026-05-31T15:17:36.756Z",
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/f9efe12cf22df675e6202334728d5fcb797e1d6a",
32
- "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.88.0"
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
- const fixableServices =
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,