@f5-sales-demo/xcsh 19.58.1 → 19.58.3

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": "@f5-sales-demo/xcsh",
4
- "version": "19.58.1",
4
+ "version": "19.58.3",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -55,13 +55,13 @@
55
55
  "dependencies": {
56
56
  "@agentclientprotocol/sdk": "0.16.1",
57
57
  "@mozilla/readability": "^0.6",
58
- "@f5-sales-demo/xcsh-stats": "19.58.1",
59
- "@f5-sales-demo/pi-agent-core": "19.58.1",
60
- "@f5-sales-demo/pi-ai": "19.58.1",
61
- "@f5-sales-demo/pi-natives": "19.58.1",
62
- "@f5-sales-demo/pi-resource-management": "19.58.1",
63
- "@f5-sales-demo/pi-tui": "19.58.1",
64
- "@f5-sales-demo/pi-utils": "19.58.1",
58
+ "@f5-sales-demo/xcsh-stats": "19.58.3",
59
+ "@f5-sales-demo/pi-agent-core": "19.58.3",
60
+ "@f5-sales-demo/pi-ai": "19.58.3",
61
+ "@f5-sales-demo/pi-natives": "19.58.3",
62
+ "@f5-sales-demo/pi-resource-management": "19.58.3",
63
+ "@f5-sales-demo/pi-tui": "19.58.3",
64
+ "@f5-sales-demo/pi-utils": "19.58.3",
65
65
  "@sinclair/typebox": "^0.34",
66
66
  "@xterm/headless": "^6.0",
67
67
  "ajv": "^8.20",
@@ -23,7 +23,7 @@ import { initializeWithSettings } from "../discovery";
23
23
  import { createAgentSession } from "../sdk";
24
24
  import { activateTenantContext } from "../services/session-context-binding";
25
25
  import { ContextService } from "../services/xcsh-context";
26
- import { sessionKeyFromUrl } from "../services/xcsh-env";
26
+ import { deriveTenantEnv } from "../services/xcsh-env";
27
27
 
28
28
  /** Mutable worker identity. Seeded from env at spawn (backward compat); replaced by a
29
29
  * late IPC bind on a pre-warmed spare. `null` fields fall back to env, then the "spare"
@@ -64,17 +64,13 @@ export function sessionInfoForWorker(): {
64
64
  /* ContextService not initialized — fall through to the tenant key; contextBound stays false. */
65
65
  }
66
66
  apiUrl = apiUrl ?? process.env.XCSH_API_URL ?? null;
67
- if (apiUrl) {
68
- const key = sessionKeyFromUrl(apiUrl);
69
- return { tenant: key?.tenant ?? null, env: key?.env ?? null, apiUrl, contextBound, sessionId };
70
- }
71
- // Contextless: advertise the tenant carried by the bind (or spawn env).
72
- const raw = boundIdentity?.tenantKey ?? process.env.XCSH_SESSION_TENANT;
73
- if (raw) {
74
- const [tenant, env] = raw.split("|");
75
- return { tenant: tenant || null, env: env || null, apiUrl: null, contextBound, sessionId };
76
- }
77
- return { tenant: null, env: null, apiUrl: null, contextBound, sessionId };
67
+ // Prefer the apiUrl-derived key (active context wins), but fall back to the
68
+ // tenant this worker was assigned (IPC bind or spawn env) so an apiUrl whose
69
+ // host we can't parse never blanks a KNOWN tenant which would make the
70
+ // extension drop the bridge and show "No xcsh running for this tenant" (#1872).
71
+ const tenantKey = boundIdentity?.tenantKey ?? process.env.XCSH_SESSION_TENANT ?? null;
72
+ const { tenant, env } = deriveTenantEnv(apiUrl, tenantKey);
73
+ return { tenant, env, apiUrl, contextBound, sessionId };
78
74
  }
79
75
 
80
76
  /** Browser-automation tool set — identical scoping to `main.ts`'s extension path.
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "19.58.1",
21
- "commit": "f72a3fa845f24642e4db4d84aa427bb2e2d64538",
22
- "shortCommit": "f72a3fa",
20
+ "version": "19.58.3",
21
+ "commit": "1e0e9e203ce3f95f2e4bb1069f4371d9b7529e91",
22
+ "shortCommit": "1e0e9e2",
23
23
  "branch": "main",
24
- "tag": "v19.58.1",
25
- "commitDate": "2026-07-05T13:33:19Z",
26
- "buildDate": "2026-07-05T13:53:37.495Z",
24
+ "tag": "v19.58.3",
25
+ "commitDate": "2026-07-05T18:06:30Z",
26
+ "buildDate": "2026-07-05T18:27:55.948Z",
27
27
  "dirty": true,
28
28
  "prNumber": "",
29
29
  "repoUrl": "https://github.com/f5-sales-demo/xcsh",
30
30
  "repoSlug": "f5-sales-demo/xcsh",
31
- "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/f72a3fa845f24642e4db4d84aa427bb2e2d64538",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.58.1"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/1e0e9e203ce3f95f2e4bb1069f4371d9b7529e91",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.58.3"
33
33
  };
package/src/main.ts CHANGED
@@ -815,7 +815,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
815
815
  // tenant), so the panel can lock its session and route per tenant.
816
816
  {
817
817
  const { ContextService } = await import("./services/xcsh-context");
818
- const { sessionKeyFromUrl } = await import("./services/xcsh-env");
818
+ const { deriveTenantEnv } = await import("./services/xcsh-env");
819
819
  const readInfo = () => {
820
820
  let apiUrl: string | null = null;
821
821
  let contextBound = false;
@@ -828,15 +828,13 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
828
828
  }
829
829
  // Fall back to the env override when no context is active (env-only mode).
830
830
  apiUrl = apiUrl ?? process.env.XCSH_API_URL ?? null;
831
- const key = apiUrl ? sessionKeyFromUrl(apiUrl) : null;
831
+ // Prefer the apiUrl-derived key; fall back to the assigned tenant key so an
832
+ // unparseable apiUrl never blanks a known tenant (#1872). Same contract as
833
+ // the worker path (sessionInfoForWorker).
834
+ const tenantKey = process.env.XCSH_SESSION_TENANT ?? null;
835
+ const { tenant, env } = deriveTenantEnv(apiUrl, tenantKey);
832
836
  // Interactive path has no per-tab XCSH_SESSION_ID; workers echo one, this doesn't.
833
- return {
834
- tenant: key?.tenant ?? null,
835
- env: key?.env ?? null,
836
- apiUrl,
837
- contextBound,
838
- sessionId: null,
839
- };
837
+ return { tenant, env, apiUrl, contextBound, sessionId: null };
840
838
  };
841
839
  bridgeServer.setSessionInfo(readInfo);
842
840
  ContextService.onContextChange(() => bridgeServer?.broadcastTenantChanged());
@@ -128,6 +128,31 @@ export function sessionKeyFromUrl(url: string | undefined): SessionKey | null {
128
128
  return null;
129
129
  }
130
130
 
131
+ /**
132
+ * Resolve the `{tenant, env}` a worker advertises to the extension's `hello`
133
+ * handshake. Prefers the session key parsed from `apiUrl` (the live/active
134
+ * context is authoritative) but falls back to the worker's assigned tenant key
135
+ * (`"tenant|env"`, from `boundIdentity.tenantKey` / `XCSH_SESSION_TENANT`) when
136
+ * `apiUrl` is absent OR its host doesn't parse to a key.
137
+ *
138
+ * The fallback is load-bearing (#1872): the extension's `liveTenants` filter
139
+ * keeps a bridge only when BOTH `tenant` and `env` are set, so returning
140
+ * `{tenant:null, env:null}` for a worker that KNOWS its tenant (e.g. an
141
+ * activated context whose stored `apiUrl` isn't `<tenant>.console.ves.volterra.io`)
142
+ * silently drops the bridge and the panel shows "No xcsh running for this tenant".
143
+ */
144
+ export function deriveTenantEnv(
145
+ apiUrl: string | null,
146
+ tenantKey: string | null | undefined,
147
+ ): { tenant: string | null; env: string | null } {
148
+ const key = apiUrl ? sessionKeyFromUrl(apiUrl) : null;
149
+ const [assignedTenant, assignedEnv] = (tenantKey ?? "").split("|");
150
+ return {
151
+ tenant: key?.tenant ?? (assignedTenant || null),
152
+ env: key?.env ?? (assignedEnv || null),
153
+ };
154
+ }
155
+
131
156
  /**
132
157
  * Reduce an API URL to its origin (`https://host[:port]`) — the canonical stored
133
158
  * form for a context endpoint.