@f5xc-salesdemos/xcsh 19.46.1 → 19.48.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 +8 -8
- package/src/browser/extension-page-actions.ts +8 -0
- package/src/browser/extension-provider.ts +26 -0
- package/src/internal-urls/build-info-runtime.ts +1 -0
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/secrets/index.ts +2 -4
- package/src/services/xcsh-context-command.ts +7 -12
- package/src/services/xcsh-env.ts +28 -16
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "19.
|
|
4
|
+
"version": "19.48.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",
|
|
@@ -54,13 +54,13 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
56
56
|
"@mozilla/readability": "^0.6",
|
|
57
|
-
"@f5xc-salesdemos/xcsh-stats": "19.
|
|
58
|
-
"@f5xc-salesdemos/pi-agent-core": "19.
|
|
59
|
-
"@f5xc-salesdemos/pi-ai": "19.
|
|
60
|
-
"@f5xc-salesdemos/pi-natives": "19.
|
|
61
|
-
"@f5xc-salesdemos/pi-resource-management": "19.
|
|
62
|
-
"@f5xc-salesdemos/pi-tui": "19.
|
|
63
|
-
"@f5xc-salesdemos/pi-utils": "19.
|
|
57
|
+
"@f5xc-salesdemos/xcsh-stats": "19.48.0",
|
|
58
|
+
"@f5xc-salesdemos/pi-agent-core": "19.48.0",
|
|
59
|
+
"@f5xc-salesdemos/pi-ai": "19.48.0",
|
|
60
|
+
"@f5xc-salesdemos/pi-natives": "19.48.0",
|
|
61
|
+
"@f5xc-salesdemos/pi-resource-management": "19.48.0",
|
|
62
|
+
"@f5xc-salesdemos/pi-tui": "19.48.0",
|
|
63
|
+
"@f5xc-salesdemos/pi-utils": "19.48.0",
|
|
64
64
|
"@sinclair/typebox": "^0.34",
|
|
65
65
|
"@xterm/headless": "^6.0",
|
|
66
66
|
"ajv": "^8.20",
|
|
@@ -307,6 +307,14 @@ export class ExtensionPageActions implements PageActions {
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
async selectLabel(selector: string, value: string, _context?: string): Promise<void> {
|
|
311
|
+
// Atomic CDK-portal typeahead interaction: the extension's label_select tool
|
|
312
|
+
// handles type → poll → click in ONE handler without losing focus (the root
|
|
313
|
+
// cause of all previous label-selector failures). Uses plain Runtime.evaluate
|
|
314
|
+
// (not evaluateWithRecovery which detaches the debugger and closes the portal).
|
|
315
|
+
await this.#ext.labelSelect(selector, value);
|
|
316
|
+
}
|
|
317
|
+
|
|
310
318
|
async scrollIntoView(selector: string, _context?: string): Promise<void> {
|
|
311
319
|
// resolveCoords already does scrollIntoView.
|
|
312
320
|
await resolveCoords(this.#ext, selector);
|
|
@@ -53,6 +53,19 @@ export interface ExtensionPage {
|
|
|
53
53
|
clickXy(x: number, y: number): Promise<void>;
|
|
54
54
|
/** CDP Input.insertText into the focused element (genuine trusted input events). */
|
|
55
55
|
typeText(text: string): Promise<void>;
|
|
56
|
+
/** Atomically type into a CDK-portal typeahead + click the matching option.
|
|
57
|
+
* Keeps the input focused throughout (uses plain Runtime.evaluate, not the
|
|
58
|
+
* evaluateWithRecovery path that defocuses by detaching the debugger). */
|
|
59
|
+
labelSelect(
|
|
60
|
+
selector: string,
|
|
61
|
+
value: string,
|
|
62
|
+
waitMs?: number,
|
|
63
|
+
): Promise<{
|
|
64
|
+
selected: string;
|
|
65
|
+
matchedKind: string;
|
|
66
|
+
value: string;
|
|
67
|
+
optionCount: number;
|
|
68
|
+
}>;
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
/** Unwrap a {@link ToolResult}, throwing on the error flag. */
|
|
@@ -204,6 +217,19 @@ class BridgeExtensionPage implements ExtensionPage {
|
|
|
204
217
|
async typeText(text: string): Promise<void> {
|
|
205
218
|
unwrap(await this.#server.request("type_text", { text }), "type_text");
|
|
206
219
|
}
|
|
220
|
+
|
|
221
|
+
async labelSelect(
|
|
222
|
+
selector: string,
|
|
223
|
+
value: string,
|
|
224
|
+
waitMs?: number,
|
|
225
|
+
): Promise<{ selected: string; matchedKind: string; value: string; optionCount: number }> {
|
|
226
|
+
// Request timeout must exceed the handler's internal poll budget.
|
|
227
|
+
const timeout = Math.max(30_000, (waitMs ?? 8_000) + 5_000);
|
|
228
|
+
return unwrap(
|
|
229
|
+
await this.#server.request("label_select", { selector, value, wait_ms: waitMs }, timeout),
|
|
230
|
+
"label_select",
|
|
231
|
+
) as { selected: string; matchedKind: string; value: string; optionCount: number };
|
|
232
|
+
}
|
|
207
233
|
}
|
|
208
234
|
|
|
209
235
|
const CONNECT_TIMEOUT_MS = 30_000;
|
|
@@ -210,6 +210,7 @@ export function renderAboutDoc(info: RuntimeBuildInfo, context: ContextStatus |
|
|
|
210
210
|
"",
|
|
211
211
|
"SE specialization: F5 XC API integration (xcsh_api, api-catalog, api-spec),",
|
|
212
212
|
"F5 XC federated product docs (llms.txt hierarchy),",
|
|
213
|
+
"F5 XC console browser automation (catalog_workflow_runner, xcsh://console/ workflow catalog),",
|
|
213
214
|
"user/computer profiling (xcsh://user, xcsh://computer),",
|
|
214
215
|
"SE-specific subagents (deal-analyst, status-operator, cli-operator, github-ops).",
|
|
215
216
|
"",
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "19.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "19.48.0",
|
|
21
|
+
"commit": "d0c5ae1622948405d6d1efdf5d664ec72ffbb7dd",
|
|
22
|
+
"shortCommit": "d0c5ae1",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v19.
|
|
25
|
-
"commitDate": "2026-06-
|
|
26
|
-
"buildDate": "2026-06-
|
|
24
|
+
"tag": "v19.48.0",
|
|
25
|
+
"commitDate": "2026-06-25T19:38:18Z",
|
|
26
|
+
"buildDate": "2026-06-25T20:04:08.080Z",
|
|
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/v19.
|
|
31
|
+
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/d0c5ae1622948405d6d1efdf5d664ec72ffbb7dd",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v19.48.0"
|
|
33
33
|
};
|
|
@@ -21,7 +21,8 @@ presentations, technical discovery questions, POC/proof-of-concept validation pl
|
|
|
21
21
|
account planning, and competitive positioning.
|
|
22
22
|
|
|
23
23
|
Technical depth: network protocols across all OSI layers, API design, security analysis
|
|
24
|
-
(DDoS, SSL/TLS, MITM, traffic forensics), infrastructure as code,
|
|
24
|
+
(DDoS, SSL/TLS, MITM, traffic forensics), infrastructure as code, network automation,
|
|
25
|
+
and F5 XC console browser automation.
|
|
25
26
|
These are not separate roles — the SE work requires the technical depth, and the
|
|
26
27
|
technical depth exists to serve the SE work.
|
|
27
28
|
Judgment: earned from production network incidents, security investigations, live
|
package/src/secrets/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
-
import { isEnoent, logger } from "@f5xc-salesdemos/pi-utils";
|
|
2
|
+
import { isEnoent, logger, SECRET_ENV_PATTERNS } from "@f5xc-salesdemos/pi-utils";
|
|
3
3
|
import { YAML } from "bun";
|
|
4
4
|
import type { SecretEntry } from "./obfuscator";
|
|
5
5
|
import { compileSecretRegex } from "./regex";
|
|
6
6
|
|
|
7
7
|
export { deobfuscateSessionContext, obfuscateMessages, type SecretEntry, SecretObfuscator } from "./obfuscator";
|
|
8
|
+
export { SECRET_ENV_PATTERNS };
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Load secrets from project-local and global secrets.yml files.
|
|
@@ -29,9 +30,6 @@ export async function loadSecrets(cwd: string, agentDir: string): Promise<Secret
|
|
|
29
30
|
/** Minimum env var value length to consider as a secret. */
|
|
30
31
|
const MIN_ENV_VALUE_LENGTH = 8;
|
|
31
32
|
|
|
32
|
-
/** Env var name patterns that indicate secret values. */
|
|
33
|
-
export const SECRET_ENV_PATTERNS = /(?:KEY|SECRET|TOKEN|PASSWORD|PASS|AUTH|CREDENTIAL|PRIVATE|OAUTH)(?:_|$)/i;
|
|
34
|
-
|
|
35
33
|
/**
|
|
36
34
|
* Collect environment variable values that look like secrets.
|
|
37
35
|
* @param options.additionalEnv Extra env records to scan (e.g. bash.environment settings from profile).
|
|
@@ -8,19 +8,18 @@ import {
|
|
|
8
8
|
isSafeContextName,
|
|
9
9
|
t,
|
|
10
10
|
} from "@f5xc-salesdemos/pi-utils";
|
|
11
|
-
import { SECRET_ENV_PATTERNS } from "../secrets/index";
|
|
12
11
|
import { expandTilde } from "../tools/path-utils";
|
|
13
12
|
import { ContextError, ContextService, CURRENT_SCHEMA_VERSION } from "./xcsh-context";
|
|
14
13
|
import { formatStatusIcon } from "./xcsh-context-indicators";
|
|
15
14
|
import {
|
|
15
|
+
AUTH_ENV_KEYS,
|
|
16
16
|
deriveTenantFromUrl,
|
|
17
|
+
isSensitiveEnvKey,
|
|
17
18
|
RESERVED_ENV_KEYS,
|
|
18
19
|
XCSH_API_TOKEN,
|
|
19
20
|
XCSH_API_URL,
|
|
20
|
-
XCSH_CONSOLE_PASSWORD,
|
|
21
21
|
XCSH_NAMESPACE,
|
|
22
22
|
XCSH_TENANT,
|
|
23
|
-
XCSH_USERNAME,
|
|
24
23
|
} from "./xcsh-env";
|
|
25
24
|
import {
|
|
26
25
|
formatAuthIndicator,
|
|
@@ -327,10 +326,6 @@ async function handleActivatePrevious(ctx: CommandContext, service: ContextServi
|
|
|
327
326
|
}
|
|
328
327
|
}
|
|
329
328
|
|
|
330
|
-
function isSensitiveKey(key: string): boolean {
|
|
331
|
-
return SECRET_ENV_PATTERNS.test(key);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
329
|
async function handleShow(ctx: CommandContext, service: ContextService, name?: string): Promise<void> {
|
|
335
330
|
const targetName = name || service.getStatus().activeContextName;
|
|
336
331
|
if (!targetName) {
|
|
@@ -358,11 +353,11 @@ async function handleShow(ctx: CommandContext, service: ContextService, name?: s
|
|
|
358
353
|
];
|
|
359
354
|
|
|
360
355
|
// Auth-related env vars
|
|
361
|
-
const authKeys: string[] =
|
|
356
|
+
const authKeys: readonly string[] = AUTH_ENV_KEYS;
|
|
362
357
|
for (const key of authKeys) {
|
|
363
358
|
const value = context.env?.[key];
|
|
364
359
|
if (value) {
|
|
365
|
-
rows.push({ key: sanitize(key), value:
|
|
360
|
+
rows.push({ key: sanitize(key), value: isSensitiveEnvKey(key) ? service.maskToken(value) : sanitize(value) });
|
|
366
361
|
}
|
|
367
362
|
}
|
|
368
363
|
|
|
@@ -377,7 +372,7 @@ async function handleShow(ctx: CommandContext, service: ContextService, name?: s
|
|
|
377
372
|
if (context.env) {
|
|
378
373
|
for (const [key, value] of Object.entries(context.env)) {
|
|
379
374
|
if (authKeys.includes(key) || RESERVED_ENV_KEYS.has(key)) continue;
|
|
380
|
-
rows.push({ key: sanitize(key), value:
|
|
375
|
+
rows.push({ key: sanitize(key), value: isSensitiveEnvKey(key) ? service.maskToken(value) : sanitize(value) });
|
|
381
376
|
}
|
|
382
377
|
}
|
|
383
378
|
|
|
@@ -741,7 +736,7 @@ async function handleEnvList(ctx: CommandContext, service: ContextService): Prom
|
|
|
741
736
|
}
|
|
742
737
|
const rows: TableRow[] = [];
|
|
743
738
|
for (const [key, value] of Object.entries(context.env)) {
|
|
744
|
-
const sensitive =
|
|
739
|
+
const sensitive = isSensitiveEnvKey(key) || (context.sensitiveKeys ?? []).includes(key);
|
|
745
740
|
rows.push({ key: sanitize(key), value: sensitive ? service.maskToken(value) : sanitize(value) });
|
|
746
741
|
}
|
|
747
742
|
ctx.showStatus(renderXCSHTable(`${contextName} env`, rows), { dim: false });
|
|
@@ -766,7 +761,7 @@ async function handleEnvSet(ctx: CommandContext, service: ContextService, args:
|
|
|
766
761
|
bodyLines.push(`Set ${keys.length} variable${keys.length > 1 ? "s" : ""} on '${contextName}':`);
|
|
767
762
|
for (const key of keys) {
|
|
768
763
|
const lock = result.sensitive.includes(key) ? " (auto-sensitive)" : "";
|
|
769
|
-
const displayValue =
|
|
764
|
+
const displayValue = isSensitiveEnvKey(key) ? "***" : vars[key];
|
|
770
765
|
bodyLines.push(` ${key}=${displayValue}${lock}`);
|
|
771
766
|
}
|
|
772
767
|
ctx.showStatus(renderContextMessage(contextName, bodyLines.join("\n")), { dim: false });
|
package/src/services/xcsh-env.ts
CHANGED
|
@@ -1,24 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* F5 XC environment variable names
|
|
3
|
-
* `
|
|
4
|
-
*
|
|
2
|
+
* F5 XC environment variable names + the reserved-key set. The single source of
|
|
3
|
+
* truth lives in `@f5xc-salesdemos/pi-utils` (`xcsh-env-names`) so the shell and
|
|
4
|
+
* the VS Code extension agree on the exact same names; re-exported here for the
|
|
5
|
+
* shell's existing import paths.
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export const XCSH_CONSOLE_PASSWORD = "XCSH_CONSOLE_PASSWORD" as const;
|
|
12
|
-
/** Active context profile name. Read-only metadata injected by ContextService. */
|
|
13
|
-
export const XCSH_CONTEXT_NAME = "XCSH_CONTEXT_NAME" as const;
|
|
14
|
-
|
|
15
|
-
export const RESERVED_ENV_KEYS: ReadonlySet<string> = new Set([
|
|
16
|
-
XCSH_NAMESPACE,
|
|
17
|
-
XCSH_API_URL,
|
|
7
|
+
import {
|
|
8
|
+
AUTH_ENV_KEYS,
|
|
9
|
+
isSensitiveEnvKey,
|
|
10
|
+
RESERVED_ENV_KEYS,
|
|
11
|
+
SECRET_ENV_PATTERNS,
|
|
18
12
|
XCSH_API_TOKEN,
|
|
13
|
+
XCSH_API_URL,
|
|
14
|
+
XCSH_CONSOLE_PASSWORD,
|
|
15
|
+
XCSH_CONTEXT_NAME,
|
|
16
|
+
XCSH_NAMESPACE,
|
|
19
17
|
XCSH_TENANT,
|
|
18
|
+
XCSH_USERNAME,
|
|
19
|
+
} from "@f5xc-salesdemos/pi-utils";
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
AUTH_ENV_KEYS,
|
|
23
|
+
isSensitiveEnvKey,
|
|
24
|
+
RESERVED_ENV_KEYS,
|
|
25
|
+
SECRET_ENV_PATTERNS,
|
|
26
|
+
XCSH_API_TOKEN,
|
|
27
|
+
XCSH_API_URL,
|
|
28
|
+
XCSH_CONSOLE_PASSWORD,
|
|
20
29
|
XCSH_CONTEXT_NAME,
|
|
21
|
-
|
|
30
|
+
XCSH_NAMESPACE,
|
|
31
|
+
XCSH_TENANT,
|
|
32
|
+
XCSH_USERNAME,
|
|
33
|
+
};
|
|
22
34
|
|
|
23
35
|
export const RESERVED_ENV_MESSAGES: Readonly<Record<string, string>> = {
|
|
24
36
|
[XCSH_NAMESPACE]: `${XCSH_NAMESPACE} is managed by defaultNamespace. Use /context namespace <value> to change it.`,
|