@f5-sales-demo/xcsh 19.51.4 → 19.51.5

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 CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [19.51.5] - 2026-06-26
6
+
7
+ ### Fixed
8
+ - Sign + notarize the embedded native addon (`pi_natives.*.node`) in `build-sign-macos` **before** it is embedded into the compiled binary. xcsh extracts the embedded `.node` to `~/.xcsh/natives/<ver>/` at runtime; previously that extracted copy was ad-hoc-signed, so managed/MDM Macs blocked the `dlopen` ("could not verify it is free of malware"). The addon is now Developer-ID-signed with hardened runtime and notarized, so Gatekeeper's online check passes it without manual approval.
9
+
5
10
  ## [19.51.4] - 2026-06-26
6
11
 
7
12
  ### Added
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "19.51.4",
4
+ "version": "19.51.5",
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",
@@ -54,13 +54,13 @@
54
54
  "dependencies": {
55
55
  "@agentclientprotocol/sdk": "0.16.1",
56
56
  "@mozilla/readability": "^0.6",
57
- "@f5-sales-demo/xcsh-stats": "19.51.3",
58
- "@f5-sales-demo/pi-agent-core": "19.51.3",
59
- "@f5-sales-demo/pi-ai": "19.51.3",
60
- "@f5-sales-demo/pi-natives": "19.51.3",
61
- "@f5-sales-demo/pi-resource-management": "19.51.3",
62
- "@f5-sales-demo/pi-tui": "19.51.3",
63
- "@f5-sales-demo/pi-utils": "19.51.3",
57
+ "@f5-sales-demo/xcsh-stats": "19.51.4",
58
+ "@f5-sales-demo/pi-agent-core": "19.51.4",
59
+ "@f5-sales-demo/pi-ai": "19.51.4",
60
+ "@f5-sales-demo/pi-natives": "19.51.4",
61
+ "@f5-sales-demo/pi-resource-management": "19.51.4",
62
+ "@f5-sales-demo/pi-tui": "19.51.4",
63
+ "@f5-sales-demo/pi-utils": "19.51.4",
64
64
  "@sinclair/typebox": "^0.34",
65
65
  "@xterm/headless": "^6.0",
66
66
  "ajv": "^8.20",
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "19.51.3",
21
- "commit": "8a28cb0c84322756aee28ea0aec6e27fa17b92fb",
22
- "shortCommit": "8a28cb0",
20
+ "version": "19.51.4",
21
+ "commit": "4219192c5e072c0d7d4a54148b38b27b47d520f8",
22
+ "shortCommit": "4219192",
23
23
  "branch": "main",
24
- "tag": "v19.51.4",
25
- "commitDate": "2026-06-26T23:17:20Z",
26
- "buildDate": "2026-06-26T23:52:22.272Z",
24
+ "tag": "v19.51.5",
25
+ "commitDate": "2026-06-27T00:31:04Z",
26
+ "buildDate": "2026-06-27T01:00:15.899Z",
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/8a28cb0c84322756aee28ea0aec6e27fa17b92fb",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.51.3"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/4219192c5e072c0d7d4a54148b38b27b47d520f8",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.51.4"
33
33
  };
@@ -1,6 +1,6 @@
1
1
  import { Container, Input, matchesKey, Spacer, Text, TruncatedText } from "@f5-sales-demo/pi-tui";
2
2
  import type { XCSHContext } from "../../services/xcsh-context";
3
- import { deriveTenantFromUrl } from "../../services/xcsh-env";
3
+ import { deriveTenantFromUrl, isSensitiveEnvKey, XCSH_CONSOLE_PASSWORD, XCSH_USERNAME } from "../../services/xcsh-env";
4
4
  import { theme } from "../theme/theme";
5
5
  import { matchesAppInterrupt } from "../utils/keybinding-matchers";
6
6
  import { DynamicBorder } from "./dynamic-border";
@@ -28,18 +28,57 @@ export function validateWizardName(name: string): string | null {
28
28
  return null;
29
29
  }
30
30
 
31
- type WizardStep = "url" | "token" | "name" | "validating" | "namespace" | "confirm" | "activate";
31
+ type WizardStep =
32
+ | "url"
33
+ | "token"
34
+ | "name"
35
+ | "validating"
36
+ | "namespace"
37
+ | "username"
38
+ | "password"
39
+ | "confirm"
40
+ | "activate";
32
41
 
33
42
  interface WizardState {
34
43
  url: string;
35
44
  token: string;
36
45
  name: string;
37
46
  namespace: string;
47
+ /** Web-console login username (optional) — stored as the XCSH_USERNAME env var. */
48
+ username: string;
49
+ /** Web-console login password (optional) — stored as the XCSH_CONSOLE_PASSWORD env var. */
50
+ password: string;
51
+ }
52
+
53
+ /**
54
+ * Build the XCSHContext the wizard will persist from its collected state.
55
+ *
56
+ * The optional web-console credentials become generic env vars (XCSH_USERNAME /
57
+ * XCSH_CONSOLE_PASSWORD); any secret-looking key (the password) is auto-marked
58
+ * sensitive so it is masked in `/context show` and redacted on export. Exported
59
+ * so the persistence shape is unit-testable without driving the TUI.
60
+ */
61
+ export function buildWizardContext(state: WizardState): XCSHContext {
62
+ const context: XCSHContext = {
63
+ name: state.name,
64
+ apiUrl: state.url,
65
+ apiToken: state.token,
66
+ defaultNamespace: state.namespace,
67
+ };
68
+ const env: Record<string, string> = {};
69
+ if (state.username) env[XCSH_USERNAME] = state.username;
70
+ if (state.password) env[XCSH_CONSOLE_PASSWORD] = state.password;
71
+ if (Object.keys(env).length > 0) {
72
+ context.env = env;
73
+ const sensitiveKeys = Object.keys(env).filter(isSensitiveEnvKey);
74
+ if (sensitiveKeys.length > 0) context.sensitiveKeys = sensitiveKeys;
75
+ }
76
+ return context;
38
77
  }
39
78
 
40
79
  export class ContextAddWizard extends Container {
41
80
  #currentStep: WizardStep = "url";
42
- #state: WizardState = { url: "", token: "", name: "", namespace: "default" };
81
+ #state: WizardState = { url: "", token: "", name: "", namespace: "default", username: "", password: "" };
43
82
  #contentContainer: Container;
44
83
  #inputField: Input | null = null;
45
84
  #selectedIndex = 0;
@@ -104,6 +143,12 @@ export class ContextAddWizard extends Container {
104
143
  case "namespace":
105
144
  this.#renderNamespaceStep();
106
145
  break;
146
+ case "username":
147
+ this.#renderUsernameStep();
148
+ break;
149
+ case "password":
150
+ this.#renderPasswordStep();
151
+ break;
107
152
  case "confirm":
108
153
  this.#renderConfirmStep();
109
154
  break;
@@ -139,6 +184,7 @@ export class ContextAddWizard extends Container {
139
184
  this.#contentContainer.addChild(new Spacer(1));
140
185
 
141
186
  this.#inputField = new Input();
187
+ this.#inputField.setMasked(true);
142
188
  this.#inputField.setValue(this.#state.token);
143
189
  this.#contentContainer.addChild(this.#inputField);
144
190
  this.#contentContainer.addChild(new Spacer(1));
@@ -288,8 +334,41 @@ export class ContextAddWizard extends Container {
288
334
  this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
289
335
  }
290
336
 
337
+ #renderUsernameStep(): void {
338
+ this.#contentContainer.addChild(new Text(theme.fg("contentAccent", "Step 6: Web-Console Username")));
339
+ this.#contentContainer.addChild(new Spacer(1));
340
+ this.#contentContainer.addChild(new Text("Enter the web-console login username (optional):", 0, 0));
341
+ this.#contentContainer.addChild(new Spacer(1));
342
+
343
+ this.#inputField = new Input();
344
+ this.#inputField.setValue(this.#state.username);
345
+ this.#contentContainer.addChild(this.#inputField);
346
+ this.#contentContainer.addChild(new Spacer(1));
347
+
348
+ this.#contentContainer.addChild(
349
+ new Text(theme.fg("muted", "[Enter to continue (empty to skip), Esc to go back]"), 0, 0),
350
+ );
351
+ }
352
+
353
+ #renderPasswordStep(): void {
354
+ this.#contentContainer.addChild(new Text(theme.fg("contentAccent", "Step 7: Web-Console Password")));
355
+ this.#contentContainer.addChild(new Spacer(1));
356
+ this.#contentContainer.addChild(new Text("Enter the web-console login password (optional):", 0, 0));
357
+ this.#contentContainer.addChild(new Spacer(1));
358
+
359
+ this.#inputField = new Input();
360
+ this.#inputField.setMasked(true);
361
+ this.#inputField.setValue(this.#state.password);
362
+ this.#contentContainer.addChild(this.#inputField);
363
+ this.#contentContainer.addChild(new Spacer(1));
364
+
365
+ this.#contentContainer.addChild(
366
+ new Text(theme.fg("muted", "[Enter to continue (empty to skip), Esc to go back]"), 0, 0),
367
+ );
368
+ }
369
+
291
370
  #renderConfirmStep(): void {
292
- this.#contentContainer.addChild(new Text(theme.fg("contentAccent", "Step 6: Confirm")));
371
+ this.#contentContainer.addChild(new Text(theme.fg("contentAccent", "Step 8: Confirm")));
293
372
  this.#contentContainer.addChild(new Spacer(1));
294
373
 
295
374
  // Summary table
@@ -298,6 +377,12 @@ export class ContextAddWizard extends Container {
298
377
  const masked = this.#state.token.length > 4 ? `${"*".repeat(8)}${this.#state.token.slice(-4)}` : "****";
299
378
  this.#contentContainer.addChild(new Text(`Token: ${masked}`, 0, 0));
300
379
  this.#contentContainer.addChild(new Text(`Namespace: ${this.#state.namespace}`, 0, 0));
380
+ if (this.#state.username) {
381
+ this.#contentContainer.addChild(new Text(`Username: ${this.#state.username}`, 0, 0));
382
+ }
383
+ if (this.#state.password) {
384
+ this.#contentContainer.addChild(new Text(`Console Password: ${"*".repeat(8)}`, 0, 0));
385
+ }
301
386
  this.#contentContainer.addChild(new Spacer(1));
302
387
 
303
388
  this.#contentContainer.addChild(new Text("Save this context?", 0, 0));
@@ -318,7 +403,7 @@ export class ContextAddWizard extends Container {
318
403
  }
319
404
 
320
405
  #renderActivateStep(): void {
321
- this.#contentContainer.addChild(new Text(theme.fg("contentAccent", "Step 7: Activate")));
406
+ this.#contentContainer.addChild(new Text(theme.fg("contentAccent", "Step 9: Activate")));
322
407
  this.#contentContainer.addChild(new Spacer(1));
323
408
  this.#contentContainer.addChild(new Text("Activate this context now?", 0, 0));
324
409
  this.#contentContainer.addChild(new Spacer(1));
@@ -436,6 +521,19 @@ export class ContextAddWizard extends Container {
436
521
  }
437
522
  case "namespace": {
438
523
  this.#state.namespace = value || "default";
524
+ this.#currentStep = "username";
525
+ break;
526
+ }
527
+ case "username": {
528
+ // Trimmed: usernames never carry meaningful surrounding whitespace.
529
+ this.#state.username = value;
530
+ this.#currentStep = "password";
531
+ break;
532
+ }
533
+ case "password": {
534
+ // Preserve the password exactly as typed — do NOT use the trimmed
535
+ // `value`, since a password may legitimately contain whitespace.
536
+ this.#state.password = this.#inputField.getValue();
439
537
  this.#currentStep = "confirm";
440
538
  this.#selectedIndex = 0;
441
539
  break;
@@ -477,12 +575,7 @@ export class ContextAddWizard extends Container {
477
575
  return;
478
576
  }
479
577
  case "activate": {
480
- const context: XCSHContext = {
481
- name: this.#state.name,
482
- apiUrl: this.#state.url,
483
- apiToken: this.#state.token,
484
- defaultNamespace: this.#state.namespace,
485
- };
578
+ const context = buildWizardContext(this.#state);
486
579
  this.#onCompleteCallback(context, this.#selectedIndex === 0);
487
580
  return;
488
581
  }
@@ -521,9 +614,15 @@ export class ContextAddWizard extends Container {
521
614
  case "namespace":
522
615
  this.#currentStep = "name";
523
616
  break;
524
- case "confirm":
617
+ case "username":
525
618
  this.#currentStep = "namespace";
526
619
  break;
620
+ case "password":
621
+ this.#currentStep = "username";
622
+ break;
623
+ case "confirm":
624
+ this.#currentStep = "password";
625
+ break;
527
626
  case "activate":
528
627
  this.#currentStep = "confirm";
529
628
  break;