@f5-sales-demo/xcsh 20.20.0 → 20.20.2

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
@@ -16,6 +16,12 @@
16
16
  contract so TUI, Browser, Office, RPC, and ACP transports no longer identify media by tool name
17
17
  ([#3202](https://github.com/f5-sales-demo/xcsh/issues/3202)).
18
18
 
19
+ ### Fixed
20
+
21
+ - Restored bare `xcsh update` as a backward-compatible executable updater while preserving
22
+ manifest-based resource updates and the explicit `xcsh self-update` command
23
+ ([#3249](https://github.com/f5-sales-demo/xcsh/issues/3249)).
24
+
19
25
  ## [20.6.3] - 2026-08-07
20
26
 
21
27
  ### Improved
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "20.20.0",
4
+ "version": "20.20.2",
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",
@@ -61,13 +61,13 @@
61
61
  },
62
62
  "dependencies": {
63
63
  "@agentclientprotocol/sdk": "1.3.0",
64
- "@f5-sales-demo/pi-agent-core": "20.20.0",
65
- "@f5-sales-demo/pi-ai": "20.20.0",
66
- "@f5-sales-demo/pi-natives": "20.20.0",
67
- "@f5-sales-demo/pi-resource-management": "20.20.0",
68
- "@f5-sales-demo/pi-tui": "20.20.0",
69
- "@f5-sales-demo/pi-utils": "20.20.0",
70
- "@f5-sales-demo/xcsh-stats": "20.20.0",
64
+ "@f5-sales-demo/pi-agent-core": "20.20.2",
65
+ "@f5-sales-demo/pi-ai": "20.20.2",
66
+ "@f5-sales-demo/pi-natives": "20.20.2",
67
+ "@f5-sales-demo/pi-resource-management": "20.20.2",
68
+ "@f5-sales-demo/pi-tui": "20.20.2",
69
+ "@f5-sales-demo/pi-utils": "20.20.2",
70
+ "@f5-sales-demo/xcsh-stats": "20.20.2",
71
71
  "@mozilla/readability": "^0.6",
72
72
  "@sinclair/typebox": "^0.34",
73
73
  "@xterm/headless": "^6.0",
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Update CLI command handler.
3
3
  *
4
- * Handles `xcsh self-update` to check for and install executable updates.
4
+ * Handles executable updates for explicit `xcsh self-update` and compatible `xcsh update` forms.
5
5
  * Auto-detects the installation method (npm, brew, bun, or standalone binary)
6
6
  * and updates through the appropriate channel.
7
7
  */
@@ -439,14 +439,21 @@ export async function runUpdateCommand(opts: { force: boolean; check: boolean })
439
439
  * Print update command help.
440
440
  */
441
441
  export function printUpdateHelp(): void {
442
- console.log(`${chalk.bold(`${APP_NAME} update`)} - Check for and install updates
442
+ console.log(`${chalk.bold(`${APP_NAME} update`)} - Update the executable or existing resources from manifests
443
443
 
444
444
  ${chalk.bold("Usage:")}
445
- ${APP_NAME} update [options]
445
+ ${APP_NAME} update [--check | --force]
446
+ ${APP_NAME} update -f <manifest> [resource options]
446
447
 
447
- ${chalk.bold("Options:")}
448
- -c, --check Check for updates without installing
449
- -f, --force Force reinstall even if up to date
448
+ ${chalk.bold("Executable options:")}
449
+ -c, --check Check for executable updates without installing
450
+ --force Force reinstall even if up to date
451
+
452
+ ${chalk.bold("Resource input:")}
453
+ -f, --filename <manifest> Update resources from a manifest
454
+
455
+ Short -f means --filename for xcsh update. Use xcsh self-update -f for the
456
+ short executable force form. Executable and resource flags cannot be mixed.
450
457
 
451
458
  ${chalk.bold("Install methods (auto-detected):")}
452
459
  npm Installed via npm install -g
@@ -458,5 +465,6 @@ ${chalk.bold("Examples:")}
458
465
  ${APP_NAME} update Update to latest version
459
466
  ${APP_NAME} update --check Check if updates are available
460
467
  ${APP_NAME} update --force Force reinstall
468
+ ${APP_NAME} update -f manifest.yaml Update resources
461
469
  `);
462
470
  }
@@ -1,21 +1,111 @@
1
- import { Command } from "@f5-sales-demo/pi-utils/cli";
1
+ import { CliUsageError, Command, Flags, parseCommandArgv } from "@f5-sales-demo/pi-utils/cli";
2
2
  import { runResourceCli } from "../cli/resource-cli";
3
+ import { runUpdateCommand } from "../cli/update-cli";
4
+ import { initTheme } from "../modes/theme/theme";
3
5
  import { manifestResourceFlags } from "./resource-flags";
4
6
 
7
+ const updateFlags = {
8
+ ...manifestResourceFlags,
9
+ force: Flags.boolean({
10
+ description: "Force executable update (short -f is reserved for resource manifests)",
11
+ default: false,
12
+ }),
13
+ check: Flags.boolean({ char: "c", description: "Check for executable updates without installing", default: false }),
14
+ };
15
+
16
+ type ResourceOutputFormat = "json" | "yaml" | "table" | "wide";
17
+
18
+ export type UpdateInvocation =
19
+ | { mode: "executable"; force: boolean; check: boolean }
20
+ | {
21
+ mode: "resource";
22
+ filenames: string[] | undefined;
23
+ namespace: string | undefined;
24
+ outputFormat: ResourceOutputFormat;
25
+ recursive: boolean;
26
+ dryRun: "client" | undefined;
27
+ resultFile: string | undefined;
28
+ };
29
+
30
+ function explicitlyRequestsDefaultOutput(argv: readonly string[]): boolean {
31
+ return argv.some(arg => arg === "--output" || arg.startsWith("--output=") || arg === "-o");
32
+ }
33
+
34
+ /** Resolve the backward-compatible `update` command before either updater can perform I/O. */
35
+ export function parseUpdateInvocation(argv: readonly string[]): UpdateInvocation {
36
+ if (argv.length === 1 && argv[0] === "-f") {
37
+ throw new CliUsageError(
38
+ "Ambiguous -f: use 'xcsh update --force' or 'xcsh self-update -f' for executable updates; otherwise provide a resource manifest",
39
+ );
40
+ }
41
+ const parsed = parseCommandArgv(argv, { flags: updateFlags });
42
+ if (parsed.argv.length > 0) {
43
+ throw new CliUsageError(`Unexpected argument${parsed.argv.length === 1 ? "" : "s"}: ${parsed.argv.join(" ")}`);
44
+ }
45
+
46
+ const flags = parsed.flags as {
47
+ filename?: string[];
48
+ namespace?: string;
49
+ output: ResourceOutputFormat;
50
+ recursive: boolean;
51
+ "dry-run"?: "client";
52
+ "result-file"?: string;
53
+ force: boolean;
54
+ check: boolean;
55
+ };
56
+ const executableRequested = flags.force || flags.check;
57
+ const resourceRequested =
58
+ flags.filename !== undefined ||
59
+ flags.namespace !== undefined ||
60
+ flags.recursive ||
61
+ flags["dry-run"] !== undefined ||
62
+ flags["result-file"] !== undefined ||
63
+ flags.output !== "table" ||
64
+ explicitlyRequestsDefaultOutput(argv);
65
+
66
+ if (executableRequested && resourceRequested) {
67
+ throw new CliUsageError("update cannot combine executable-update and resource-update flags");
68
+ }
69
+ if (!resourceRequested) {
70
+ return { mode: "executable", force: flags.force, check: flags.check };
71
+ }
72
+ return {
73
+ mode: "resource",
74
+ filenames: flags.filename,
75
+ namespace: flags.namespace,
76
+ outputFormat: flags.output,
77
+ recursive: flags.recursive,
78
+ dryRun: flags["dry-run"],
79
+ resultFile: flags["result-file"],
80
+ };
81
+ }
82
+
5
83
  export default class Update extends Command {
6
- static description = "Update existing resources from manifests";
7
- static flags = manifestResourceFlags;
84
+ static description = "Update the xcsh executable or existing resources from manifests";
85
+ static flags = updateFlags;
86
+ static examples = [
87
+ "xcsh update # update the executable",
88
+ "xcsh update --check # check the executable version",
89
+ "xcsh update --force # force an executable reinstall",
90
+ "xcsh self-update -f # short force form for the executable",
91
+ "xcsh update -f manifest.yaml # update resources from a manifest",
92
+ ];
8
93
 
9
94
  async run(): Promise<void> {
10
- const { flags } = await this.parse(Update);
95
+ const invocation = parseUpdateInvocation(this.argv);
96
+ if (invocation.mode === "executable") {
97
+ await initTheme();
98
+ await runUpdateCommand({ force: invocation.force, check: invocation.check });
99
+ return;
100
+ }
11
101
  await runResourceCli({
12
102
  operation: "update",
13
- filenames: flags.filename,
14
- namespace: flags.namespace,
15
- outputFormat: flags.output as "json" | "yaml" | "table" | "wide",
16
- recursive: flags.recursive,
17
- dryRun: flags["dry-run"] as "client" | undefined,
18
- resultFile: flags["result-file"],
103
+ filenames: invocation.filenames,
104
+ namespace: invocation.namespace,
105
+ outputFormat: invocation.outputFormat,
106
+ recursive: invocation.recursive,
107
+ dryRun: invocation.dryRun,
108
+ resultFile: invocation.resultFile,
19
109
  });
20
110
  }
21
111
  }
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "20.20.0",
21
- "commit": "2017729c40c9c2253301fd8860bcac960b5837d2",
22
- "shortCommit": "2017729",
20
+ "version": "20.20.2",
21
+ "commit": "04c71999d5ced53fa9200d3ae79a4ad2ba86debb",
22
+ "shortCommit": "04c7199",
23
23
  "branch": "main",
24
- "tag": "v20.20.0",
25
- "commitDate": "2026-08-16T22:46:12Z",
26
- "buildDate": "2026-08-16T23:14:16.083Z",
24
+ "tag": "v20.20.2",
25
+ "commitDate": "2026-08-17T12:59:16Z",
26
+ "buildDate": "2026-08-17T13:25:27.576Z",
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/2017729c40c9c2253301fd8860bcac960b5837d2",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.20.0"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/04c71999d5ced53fa9200d3ae79a4ad2ba86debb",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.20.2"
33
33
  };
@@ -2,10 +2,10 @@
2
2
 
3
3
  import type { ConsoleCatalogData } from "./console-catalog-types";
4
4
 
5
- export const CONSOLE_CATALOG_VERSION = "35f6e40dcaccc906474e2c28999f04d4bee4f519";
5
+ export const CONSOLE_CATALOG_VERSION = "1999b4cee787136ee0bc7cd02deba57ac6381885";
6
6
 
7
7
  export const CONSOLE_CATALOG_DATA: ConsoleCatalogData = {
8
- version: "35f6e40dcaccc906474e2c28999f04d4bee4f519",
8
+ version: "1999b4cee787136ee0bc7cd02deba57ac6381885",
9
9
  workflows: {
10
10
  "address-allocator/create":
11
11
  '---\nschema: urn:xcsh:console:workflow:v1\nid: address-allocator-create\nlabel: Create IP Address Allocators\nresource: address-allocator\noperation: create\npreconditions:\n - user_logged_in\n - "role_minimum: admin"\nparams:\n name:\n required: true\n description: IP Address Allocators name (lowercase alphanumeric and hyphens)\n example: example-address-allocator\n address_allocator_mode:\n required: true\n description: Address Allocator Mode\n allocation_unit:\n required: false\n description: Allocation Unit\n default: 0\n address_pool:\n required: false\n description: Address Pool\n default: value\n address_allocation_scheme:\n required: false\n description: "Server-required: Field should be not nil"\n default: value\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-network-connect/manage/networking/legacy_network_configuration/address_allocators\n wait_for: text(\'IP Address Allocators\')\n description: Navigate to IP Address Allocators list page\n - id: click-add-tab\n action: click\n selector: text(\'Add IP Address Allocator\')\n wait_for: textbox[name=\'Name\']\n description: Click Add IP Address Allocator to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name=\'Name\']\n value: "{name}"\n description: Enter Name\n - id: select-address_allocator_mode\n action: select\n selector: listbox\n context: Address Allocator Mode section\n value: "{address_allocator_mode}"\n description: Select Address Allocator Mode\n - id: fill-allocation_unit\n action: fill\n selector: spinbutton[name=\'Allocation Unit\']\n value: "{allocation_unit}"\n description: Set Allocation Unit\n - id: fill-address_pool\n action: fill\n selector: ngx-datatable input.form-control\n context: Address Pool table\n value: "{address_pool}"\n description: Enter Address Pool in the existing table row (no Add Item needed — the table ships one empty row)\n - id: select-address_allocation_scheme\n action: select\n selector: listbox\n context: Address Allocation Scheme section\n value: "{address_allocation_scheme}"\n description: Select Address Allocation Scheme\n - id: save\n action: click\n selector: "[class*=\'save-bt\'],[class*=\'submit-button\']"\n context: footer\n wait_for: text(\'{name}\')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - "resource_name_in_list: {name}"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: "2025.06"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n',
@@ -288,7 +288,7 @@
288
288
  "welcome.modelProvider": "Model Provider",
289
289
  "welcome.plugins": "Plugins",
290
290
  "welcome.pluginSetupHint": "run: /plugin setup",
291
- "welcome.updateHint": "run: xcsh self-update",
291
+ "welcome.updateHint": "run: xcsh update",
292
292
  "welcome.newVersion": "new version",
293
293
  "welcome.connectionFailed": "— connection failed",
294
294
  "welcome.runLoginReconnect": "Run /login to reconnect",
package/src/main.ts CHANGED
@@ -180,7 +180,7 @@ async function runInteractiveMode(
180
180
  if (settings.get("startup.checkUpdate")) {
181
181
  void versionCheckPromise
182
182
  .then(latest => {
183
- if (latest) mode.showStatus(`Update available: v${latest} — run: xcsh self-update`, { dim: true });
183
+ if (latest) mode.showStatus(`Update available: v${latest} — run: xcsh update`, { dim: true });
184
184
  })
185
185
  .catch(() => {});
186
186
  }