@f5xc-salesdemos/xcsh 19.3.0 → 19.5.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 +7 -7
- package/src/extensibility/plugins/marketplace/manager.ts +1 -1
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/internal-urls/terraform-index.generated.ts +17 -17
- package/src/main.ts +1 -1
- package/src/modes/components/plugin-selector.ts +1 -1
- package/src/modes/components/plugins/plugin-dashboard.ts +6 -0
- package/src/slash-commands/builtin-registry.ts +184 -145
- package/src/slash-commands/marketplace-install-parser.ts +1 -1
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.5.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": "19.
|
|
54
|
-
"@f5xc-salesdemos/pi-agent-core": "19.
|
|
55
|
-
"@f5xc-salesdemos/pi-ai": "19.
|
|
56
|
-
"@f5xc-salesdemos/pi-natives": "19.
|
|
57
|
-
"@f5xc-salesdemos/pi-tui": "19.
|
|
58
|
-
"@f5xc-salesdemos/pi-utils": "19.
|
|
53
|
+
"@f5xc-salesdemos/xcsh-stats": "19.5.0",
|
|
54
|
+
"@f5xc-salesdemos/pi-agent-core": "19.5.0",
|
|
55
|
+
"@f5xc-salesdemos/pi-ai": "19.5.0",
|
|
56
|
+
"@f5xc-salesdemos/pi-natives": "19.5.0",
|
|
57
|
+
"@f5xc-salesdemos/pi-tui": "19.5.0",
|
|
58
|
+
"@f5xc-salesdemos/pi-utils": "19.5.0",
|
|
59
59
|
"@sinclair/typebox": "^0.34",
|
|
60
60
|
"@xterm/headless": "^6.0",
|
|
61
61
|
"ajv": "^8.20",
|
|
@@ -719,7 +719,7 @@ export class MarketplaceManager {
|
|
|
719
719
|
} catch (err) {
|
|
720
720
|
if (isEnoent(err)) {
|
|
721
721
|
throw new Error(
|
|
722
|
-
`Marketplace catalog not found at ${entry.catalogPath}. Try: /marketplace update ${entry.name}`,
|
|
722
|
+
`Marketplace catalog not found at ${entry.catalogPath}. Try: /plugin marketplace update ${entry.name}`,
|
|
723
723
|
);
|
|
724
724
|
}
|
|
725
725
|
throw err;
|
|
@@ -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.5.0",
|
|
21
|
+
"commit": "1845641b31e29a09b04c301d1bcfc297a719c5f9",
|
|
22
|
+
"shortCommit": "1845641",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v19.
|
|
25
|
-
"commitDate": "2026-06-
|
|
26
|
-
"buildDate": "2026-06-
|
|
24
|
+
"tag": "v19.5.0",
|
|
25
|
+
"commitDate": "2026-06-04T16:42:25Z",
|
|
26
|
+
"buildDate": "2026-06-04T17:10:47.523Z",
|
|
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/1845641b31e29a09b04c301d1bcfc297a719c5f9",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v19.5.0"
|
|
33
33
|
};
|
|
@@ -146,13 +146,6 @@ export const TERRAFORM_INDEX: TerraformIndex = {
|
|
|
146
146
|
resource_count: 4,
|
|
147
147
|
resources: ["app_setting", "app_type", "discovery", "filter_set"],
|
|
148
148
|
},
|
|
149
|
-
{
|
|
150
|
-
name: "BIG-IP Integration",
|
|
151
|
-
slug: "big-ip-integration",
|
|
152
|
-
description: "BIG-IP proxy, data groups, and iRules integration",
|
|
153
|
-
resource_count: 3,
|
|
154
|
-
resources: ["bigip_http_proxy", "data_group", "irule"],
|
|
155
|
-
},
|
|
156
149
|
{
|
|
157
150
|
name: "Authentication",
|
|
158
151
|
slug: "authentication",
|
|
@@ -167,6 +160,13 @@ export const TERRAFORM_INDEX: TerraformIndex = {
|
|
|
167
160
|
resource_count: 3,
|
|
168
161
|
resources: ["container_registry", "workload", "workload_flavor"],
|
|
169
162
|
},
|
|
163
|
+
{
|
|
164
|
+
name: "BIG-IP Integration",
|
|
165
|
+
slug: "big-ip-integration",
|
|
166
|
+
description: "BIG-IP proxy, data groups, and iRules integration",
|
|
167
|
+
resource_count: 3,
|
|
168
|
+
resources: ["bigip_http_proxy", "data_group", "irule"],
|
|
169
|
+
},
|
|
170
170
|
{
|
|
171
171
|
name: "DNS",
|
|
172
172
|
slug: "dns",
|
|
@@ -175,11 +175,11 @@ export const TERRAFORM_INDEX: TerraformIndex = {
|
|
|
175
175
|
resources: ["dns_compliance_checks", "dns_domain", "dns_proxy"],
|
|
176
176
|
},
|
|
177
177
|
{
|
|
178
|
-
name: "
|
|
179
|
-
slug: "
|
|
180
|
-
description: "
|
|
178
|
+
name: "Uncategorized",
|
|
179
|
+
slug: "uncategorized",
|
|
180
|
+
description: "Resources pending categorization",
|
|
181
181
|
resource_count: 2,
|
|
182
|
-
resources: ["
|
|
182
|
+
resources: ["application_profiles", "authorization_server"],
|
|
183
183
|
},
|
|
184
184
|
{
|
|
185
185
|
name: "Organization",
|
|
@@ -189,11 +189,11 @@ export const TERRAFORM_INDEX: TerraformIndex = {
|
|
|
189
189
|
resources: ["namespace", "tenant_configuration"],
|
|
190
190
|
},
|
|
191
191
|
{
|
|
192
|
-
name: "
|
|
193
|
-
slug: "
|
|
194
|
-
description: "
|
|
192
|
+
name: "Cloud Resources",
|
|
193
|
+
slug: "cloud-resources",
|
|
194
|
+
description: "Cloud elastic IPs, address allocators, and geo-location resources",
|
|
195
195
|
resource_count: 2,
|
|
196
|
-
resources: ["
|
|
196
|
+
resources: ["address_allocator", "cloud_elastic_ip"],
|
|
197
197
|
},
|
|
198
198
|
{
|
|
199
199
|
name: "Integrations",
|
|
@@ -1411,7 +1411,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
|
|
|
1411
1411
|
required: ["name", "namespace"],
|
|
1412
1412
|
oneof_groups: [
|
|
1413
1413
|
{
|
|
1414
|
-
fields: ["allow_list", "deny_list", "rule_list"],
|
|
1414
|
+
fields: ["allow_all_requests", "allow_list", "deny_all_requests", "deny_list", "rule_list"],
|
|
1415
1415
|
},
|
|
1416
1416
|
{
|
|
1417
1417
|
fields: ["any_server", "server_name", "server_name_matcher", "server_selector"],
|
|
@@ -1419,7 +1419,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
|
|
|
1419
1419
|
],
|
|
1420
1420
|
server_defaults: ["port_matcher", "any_server"],
|
|
1421
1421
|
minimal_config:
|
|
1422
|
-
'resource "f5xc_service_policy" "example" {\n name = "example-service-policy"\n namespace = "staging"\n\n labels = {\n environment = "production"\n managed_by = "terraform"\n }\n\n annotations = {\n "owner" = "platform-team"\n }\n\n // One of the arguments from this list "allow_list deny_list rule_list" must be set\n\n rule_list {\n rules {\n metadata {\n name = "allow-api"\n }\n spec {\n action = "ALLOW"\n any_client {}\n any_ip {}\n path {\n prefix_values = ["/api/"]\n }\n }\n }\n }\n\n // One of the arguments from this list "any_server server_name server_name_matcher server_selector" must be set\n\n any_server {}\n}',
|
|
1422
|
+
'resource "f5xc_service_policy" "example" {\n name = "example-service-policy"\n namespace = "staging"\n\n labels = {\n environment = "production"\n managed_by = "terraform"\n }\n\n annotations = {\n "owner" = "platform-team"\n }\n\n // One of the arguments from this list "allow_all_requests allow_list deny_all_requests deny_list rule_list" must be set\n\n rule_list {\n rules {\n metadata {\n name = "allow-api"\n }\n spec {\n action = "ALLOW"\n any_client {}\n any_ip {}\n path {\n prefix_values = ["/api/"]\n }\n }\n }\n }\n\n // One of the arguments from this list "any_server server_name server_name_matcher server_selector" must be set\n\n any_server {}\n}',
|
|
1423
1423
|
dependencies: {
|
|
1424
1424
|
requires: ["namespace"],
|
|
1425
1425
|
},
|
package/src/main.ts
CHANGED
|
@@ -749,7 +749,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
749
749
|
await mgr.upgradeAllPlugins();
|
|
750
750
|
logger.debug(`Auto-upgraded ${updates.length} marketplace plugin(s)`);
|
|
751
751
|
} else {
|
|
752
|
-
logger.debug(`${updates.length} marketplace plugin update(s) available \u2014 /
|
|
752
|
+
logger.debug(`${updates.length} marketplace plugin update(s) available \u2014 /plugin upgrade`);
|
|
753
753
|
}
|
|
754
754
|
} catch {
|
|
755
755
|
// Silently ignore — network failure, corrupt data, offline.
|
|
@@ -54,7 +54,7 @@ export class PluginSelectorComponent extends Container {
|
|
|
54
54
|
label: "No plugins available",
|
|
55
55
|
description:
|
|
56
56
|
marketplaceCount === 0
|
|
57
|
-
? "Add a marketplace first: /marketplace add <source>"
|
|
57
|
+
? "Add a marketplace first: /plugin marketplace add <source>"
|
|
58
58
|
: "Configured marketplaces have no plugins",
|
|
59
59
|
});
|
|
60
60
|
}
|
|
@@ -113,6 +113,12 @@ export class PluginDashboard extends Container {
|
|
|
113
113
|
// Network failure on first run is fine — continue with whatever is available
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
try {
|
|
117
|
+
await this.#mgr.refreshStaleMarketplaces();
|
|
118
|
+
} catch {
|
|
119
|
+
// Network failure is fine — display whatever is cached
|
|
120
|
+
}
|
|
121
|
+
|
|
116
122
|
try {
|
|
117
123
|
this.#state = await createInitialState(this.#mgr, this.#npmMgr);
|
|
118
124
|
} catch (error) {
|
|
@@ -858,37 +858,60 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
858
858
|
handle: shutdownHandler,
|
|
859
859
|
},
|
|
860
860
|
{
|
|
861
|
-
name: "
|
|
862
|
-
|
|
861
|
+
name: "plugin",
|
|
862
|
+
aliases: ["marketplace", "plugins"],
|
|
863
|
+
description: "Manage plugins and marketplace sources",
|
|
863
864
|
subcommands: [
|
|
864
|
-
{ name: "
|
|
865
|
-
{ name: "remove", description: "Remove a marketplace source", usage: "<name>" },
|
|
866
|
-
{ name: "update", description: "Update marketplace catalog(s)", usage: "[name]" },
|
|
867
|
-
{ name: "list", description: "List configured marketplaces" },
|
|
868
|
-
{ name: "discover", description: "Browse available plugins", usage: "[marketplace]" },
|
|
865
|
+
{ name: "marketplace", description: "Manage marketplace sources (add, remove, update, list)" },
|
|
869
866
|
{
|
|
870
867
|
name: "install",
|
|
871
|
-
description: "Install a plugin
|
|
872
|
-
usage: "[--force] [name@marketplace
|
|
868
|
+
description: "Install a plugin",
|
|
869
|
+
usage: "[--force] [--scope user|project] <name@marketplace>",
|
|
873
870
|
},
|
|
874
|
-
{ name: "uninstall", description: "Uninstall a plugin
|
|
875
|
-
{ name: "
|
|
876
|
-
{ name: "
|
|
871
|
+
{ name: "uninstall", description: "Uninstall a plugin", usage: "[--scope user|project] <name@marketplace>" },
|
|
872
|
+
{ name: "enable", description: "Enable a plugin", usage: "[--scope user|project] <name@marketplace>" },
|
|
873
|
+
{ name: "disable", description: "Disable a plugin", usage: "[--scope user|project] <name@marketplace>" },
|
|
874
|
+
{ name: "upgrade", description: "Upgrade plugins", usage: "[--scope user|project] [name@marketplace]" },
|
|
875
|
+
{ name: "discover", description: "Browse available plugins", usage: "[marketplace]" },
|
|
876
|
+
{ name: "list", description: "List all installed plugins" },
|
|
877
|
+
{ name: "validate", description: "Validate marketplace or plugin manifest", usage: "[path]" },
|
|
877
878
|
{ name: "help", description: "Show usage guide" },
|
|
878
879
|
],
|
|
879
880
|
allowArgs: true,
|
|
880
881
|
handle: async (command, runtime) => {
|
|
881
882
|
runtime.ctx.editor.setText("");
|
|
882
883
|
const args = command.args.trim().split(/\s+/);
|
|
883
|
-
const sub = args[0] || "
|
|
884
|
+
const sub = args[0] || "";
|
|
884
885
|
const rest = args.slice(1).join(" ").trim();
|
|
885
886
|
|
|
886
|
-
// /
|
|
887
|
-
if (
|
|
887
|
+
// /plugin (no args) → open interactive dashboard
|
|
888
|
+
if (!sub) {
|
|
889
|
+
runtime.ctx.showPluginDashboard();
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// /plugin install (no args) → interactive browser
|
|
894
|
+
if (sub === "install" && !rest) {
|
|
888
895
|
try {
|
|
889
896
|
runtime.ctx.showPluginSelector("install");
|
|
890
897
|
} catch (err) {
|
|
891
|
-
runtime.ctx.showStatus(`
|
|
898
|
+
runtime.ctx.showStatus(`Plugin error: ${err}`);
|
|
899
|
+
}
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// /plugin list (no args) → open interactive dashboard
|
|
904
|
+
if (sub === "list" && !rest) {
|
|
905
|
+
runtime.ctx.showPluginDashboard();
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// /plugin uninstall (no args) → interactive uninstall selector
|
|
910
|
+
if (sub === "uninstall" && !rest) {
|
|
911
|
+
try {
|
|
912
|
+
runtime.ctx.showPluginSelector("uninstall");
|
|
913
|
+
} catch (err) {
|
|
914
|
+
runtime.ctx.showStatus(`Plugin error: ${err}`);
|
|
892
915
|
}
|
|
893
916
|
return;
|
|
894
917
|
}
|
|
@@ -911,9 +934,63 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
911
934
|
|
|
912
935
|
try {
|
|
913
936
|
switch (sub) {
|
|
937
|
+
// ── Marketplace management (/plugin marketplace add|remove|update|list) ──
|
|
938
|
+
case "marketplace": {
|
|
939
|
+
const mktArgs = rest.split(/\s+/);
|
|
940
|
+
const mktSub = mktArgs[0] || "";
|
|
941
|
+
const mktRest = mktArgs.slice(1).join(" ").trim();
|
|
942
|
+
switch (mktSub) {
|
|
943
|
+
case "add": {
|
|
944
|
+
if (!mktRest) {
|
|
945
|
+
runtime.ctx.showStatus("Usage: /plugin marketplace add <source>");
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
const entry = await mgr.addMarketplace(mktRest);
|
|
949
|
+
runtime.ctx.showStatus(`Added marketplace: ${entry.name}`);
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
case "remove":
|
|
953
|
+
case "rm": {
|
|
954
|
+
if (!mktRest) {
|
|
955
|
+
runtime.ctx.showStatus("Usage: /plugin marketplace remove <name>");
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
await mgr.removeMarketplace(mktRest);
|
|
959
|
+
runtime.ctx.showStatus(`Removed marketplace: ${mktRest}`);
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
case "update": {
|
|
963
|
+
if (mktRest) {
|
|
964
|
+
await mgr.updateMarketplace(mktRest);
|
|
965
|
+
runtime.ctx.showStatus(`Updated marketplace: ${mktRest}`);
|
|
966
|
+
} else {
|
|
967
|
+
const results = await mgr.updateAllMarketplaces();
|
|
968
|
+
runtime.ctx.showStatus(`Updated ${results.length} marketplace(s)`);
|
|
969
|
+
}
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
case "list":
|
|
973
|
+
default: {
|
|
974
|
+
const marketplaces = await mgr.listMarketplaces();
|
|
975
|
+
if (marketplaces.length === 0) {
|
|
976
|
+
runtime.ctx.showStatus(
|
|
977
|
+
"No marketplaces configured.\n\nGet started:\n /plugin marketplace add f5xc-salesdemos/marketplace",
|
|
978
|
+
);
|
|
979
|
+
} else {
|
|
980
|
+
const lines = marketplaces.map(m => ` ${m.name} ${m.sourceUri}`);
|
|
981
|
+
runtime.ctx.showStatus(
|
|
982
|
+
`Marketplaces:\n${lines.join("\n")}\n\nUse /plugin discover to browse plugins`,
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
break;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
break;
|
|
989
|
+
}
|
|
990
|
+
// ── Legacy shorthand: /marketplace add|remove|update → /plugin marketplace ──
|
|
914
991
|
case "add": {
|
|
915
992
|
if (!rest) {
|
|
916
|
-
runtime.ctx.showStatus("Usage: /marketplace add <source>");
|
|
993
|
+
runtime.ctx.showStatus("Usage: /plugin marketplace add <source>");
|
|
917
994
|
return;
|
|
918
995
|
}
|
|
919
996
|
const entry = await mgr.addMarketplace(rest);
|
|
@@ -923,7 +1000,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
923
1000
|
case "remove":
|
|
924
1001
|
case "rm": {
|
|
925
1002
|
if (!rest) {
|
|
926
|
-
runtime.ctx.showStatus("Usage: /marketplace remove <name>");
|
|
1003
|
+
runtime.ctx.showStatus("Usage: /plugin marketplace remove <name>");
|
|
927
1004
|
return;
|
|
928
1005
|
}
|
|
929
1006
|
await mgr.removeMarketplace(rest);
|
|
@@ -940,13 +1017,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
940
1017
|
}
|
|
941
1018
|
break;
|
|
942
1019
|
}
|
|
1020
|
+
// ── Plugin discovery ──
|
|
943
1021
|
case "discover": {
|
|
944
1022
|
const plugins = await mgr.listAvailablePlugins(rest || undefined);
|
|
945
1023
|
if (plugins.length === 0) {
|
|
946
1024
|
const marketplaces = await mgr.listMarketplaces();
|
|
947
1025
|
if (marketplaces.length === 0) {
|
|
948
1026
|
runtime.ctx.showStatus(
|
|
949
|
-
"No marketplaces configured. Try:\n /marketplace add f5xc-salesdemos/marketplace",
|
|
1027
|
+
"No marketplaces configured. Try:\n /plugin marketplace add f5xc-salesdemos/marketplace",
|
|
950
1028
|
);
|
|
951
1029
|
} else {
|
|
952
1030
|
runtime.ctx.showStatus("No plugins available in configured marketplaces");
|
|
@@ -960,8 +1038,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
960
1038
|
}
|
|
961
1039
|
break;
|
|
962
1040
|
}
|
|
1041
|
+
// ── Install ──
|
|
963
1042
|
case "install": {
|
|
964
|
-
// Parse: /marketplace install [--force] [--scope user|project] name@marketplace
|
|
965
1043
|
const parsed = parseMarketplaceInstallArgs(rest);
|
|
966
1044
|
if ("error" in parsed) {
|
|
967
1045
|
runtime.ctx.showStatus(parsed.error);
|
|
@@ -974,15 +1052,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
974
1052
|
runtime.ctx.showStatus(`Installed ${name} from ${marketplace}`);
|
|
975
1053
|
break;
|
|
976
1054
|
}
|
|
1055
|
+
// ── Uninstall ──
|
|
977
1056
|
case "uninstall": {
|
|
978
|
-
if (!rest) {
|
|
979
|
-
// No args → open interactive uninstall selector
|
|
980
|
-
runtime.ctx.showPluginSelector("uninstall");
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
1057
|
const uninstArgs = parsePluginScopeArgs(
|
|
984
1058
|
rest,
|
|
985
|
-
"Usage: /
|
|
1059
|
+
"Usage: /plugin uninstall [--scope user|project] <name@marketplace>",
|
|
986
1060
|
);
|
|
987
1061
|
if ("error" in uninstArgs) {
|
|
988
1062
|
runtime.ctx.showStatus(uninstArgs.error);
|
|
@@ -992,23 +1066,28 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
992
1066
|
runtime.ctx.showStatus(`Uninstalled ${uninstArgs.pluginId}`);
|
|
993
1067
|
break;
|
|
994
1068
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
runtime.ctx.showStatus(
|
|
1069
|
+
// ── Enable / Disable ──
|
|
1070
|
+
case "enable":
|
|
1071
|
+
case "disable": {
|
|
1072
|
+
const parsed = parsePluginScopeArgs(
|
|
1073
|
+
rest ?? "",
|
|
1074
|
+
`Usage: /plugin ${sub} [--scope user|project] <name@marketplace>`,
|
|
1075
|
+
);
|
|
1076
|
+
if ("error" in parsed) {
|
|
1077
|
+
runtime.ctx.showStatus(parsed.error);
|
|
1078
|
+
return;
|
|
1004
1079
|
}
|
|
1080
|
+
const isEnable = sub === "enable";
|
|
1081
|
+
await mgr.setPluginEnabled(parsed.pluginId, isEnable, parsed.scope);
|
|
1082
|
+
runtime.ctx.showStatus(`${isEnable ? "Enabled" : "Disabled"} ${parsed.pluginId}`);
|
|
1005
1083
|
break;
|
|
1006
1084
|
}
|
|
1085
|
+
// ── Upgrade ──
|
|
1007
1086
|
case "upgrade": {
|
|
1008
1087
|
if (rest) {
|
|
1009
1088
|
const upArgs = parsePluginScopeArgs(
|
|
1010
1089
|
rest,
|
|
1011
|
-
"Usage: /
|
|
1090
|
+
"Usage: /plugin upgrade [--scope user|project] <name@marketplace>",
|
|
1012
1091
|
);
|
|
1013
1092
|
if ("error" in upArgs) {
|
|
1014
1093
|
runtime.ctx.showStatus(upArgs.error);
|
|
@@ -1019,7 +1098,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
1019
1098
|
} else {
|
|
1020
1099
|
const results = await mgr.upgradeAllPlugins();
|
|
1021
1100
|
if (results.length === 0) {
|
|
1022
|
-
runtime.ctx.showStatus("All
|
|
1101
|
+
runtime.ctx.showStatus("All plugins are up to date");
|
|
1023
1102
|
} else {
|
|
1024
1103
|
const lines = results.map(r => ` ${r.pluginId}: ${r.from} -> ${r.to}`);
|
|
1025
1104
|
runtime.ctx.showStatus(`Upgraded ${results.length} plugin(s):\n${lines.join("\n")}`);
|
|
@@ -1027,106 +1106,9 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
1027
1106
|
}
|
|
1028
1107
|
break;
|
|
1029
1108
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
[
|
|
1033
|
-
"Marketplace commands:",
|
|
1034
|
-
" /marketplace Browse and install plugins",
|
|
1035
|
-
" /marketplace add <source> Add a marketplace (e.g. owner/repo)",
|
|
1036
|
-
" /marketplace remove <name> Remove a marketplace",
|
|
1037
|
-
" /marketplace update [name] Re-fetch catalog(s)",
|
|
1038
|
-
" /marketplace list List configured marketplaces",
|
|
1039
|
-
" /marketplace discover [marketplace] Browse available plugins",
|
|
1040
|
-
" /marketplace install <name@marketplace> Install a plugin",
|
|
1041
|
-
" /marketplace uninstall <name@marketplace> Uninstall a plugin",
|
|
1042
|
-
" /marketplace installed List installed plugins",
|
|
1043
|
-
" /marketplace upgrade [name@marketplace] Upgrade plugin(s)",
|
|
1044
|
-
"",
|
|
1045
|
-
"Quick start:",
|
|
1046
|
-
" /marketplace add f5xc-salesdemos/marketplace",
|
|
1047
|
-
" /marketplace (opens interactive browser)",
|
|
1048
|
-
].join("\n"),
|
|
1049
|
-
);
|
|
1050
|
-
break;
|
|
1051
|
-
}
|
|
1052
|
-
default: {
|
|
1053
|
-
const marketplaces = await mgr.listMarketplaces();
|
|
1054
|
-
if (marketplaces.length === 0) {
|
|
1055
|
-
runtime.ctx.showStatus(
|
|
1056
|
-
"No marketplaces configured.\n\nGet started:\n /marketplace add f5xc-salesdemos/marketplace\n\nThen browse plugins with /marketplace or /marketplace discover",
|
|
1057
|
-
);
|
|
1058
|
-
} else {
|
|
1059
|
-
const lines = marketplaces.map(m => ` ${m.name} ${m.sourceUri}`);
|
|
1060
|
-
runtime.ctx.showStatus(
|
|
1061
|
-
`Marketplaces:\n${lines.join("\n")}\n\nUse /marketplace discover to browse plugins, or /marketplace help for all commands`,
|
|
1062
|
-
);
|
|
1063
|
-
}
|
|
1064
|
-
break;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
} catch (err) {
|
|
1068
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1069
|
-
runtime.ctx.showStatus(`Marketplace error: ${msg}`);
|
|
1070
|
-
}
|
|
1071
|
-
},
|
|
1072
|
-
},
|
|
1073
|
-
{
|
|
1074
|
-
name: "plugins",
|
|
1075
|
-
description: "View and manage installed plugins",
|
|
1076
|
-
subcommands: [
|
|
1077
|
-
{ name: "list", description: "List all installed plugins (npm + marketplace)" },
|
|
1078
|
-
{ name: "enable", description: "Enable a marketplace plugin", usage: "<name@marketplace>" },
|
|
1079
|
-
{ name: "disable", description: "Disable a marketplace plugin", usage: "<name@marketplace>" },
|
|
1080
|
-
],
|
|
1081
|
-
allowArgs: true,
|
|
1082
|
-
handle: async (command, runtime) => {
|
|
1083
|
-
runtime.ctx.editor.setText("");
|
|
1084
|
-
const args = command.args.trim().split(/\s+/);
|
|
1085
|
-
const sub = args[0] || "";
|
|
1086
|
-
const rest = args.slice(1).join(" ").trim();
|
|
1087
|
-
|
|
1088
|
-
// No args or bare "list" with no further args → open interactive dashboard
|
|
1089
|
-
if (!sub || (sub === "list" && !rest)) {
|
|
1090
|
-
runtime.ctx.showPluginDashboard();
|
|
1091
|
-
return;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
try {
|
|
1095
|
-
const mgr = new MarketplaceManager({
|
|
1096
|
-
marketplacesRegistryPath: getMarketplacesRegistryPath(),
|
|
1097
|
-
installedRegistryPath: getInstalledPluginsRegistryPath(),
|
|
1098
|
-
projectInstalledRegistryPath: await resolveOrDefaultProjectRegistryPath(
|
|
1099
|
-
runtime.ctx.sessionManager.getCwd(),
|
|
1100
|
-
),
|
|
1101
|
-
marketplacesCacheDir: getMarketplacesCacheDir(),
|
|
1102
|
-
pluginsCacheDir: getPluginsCacheDir(),
|
|
1103
|
-
clearPluginRootsCache: (extraPaths?: readonly string[]) => {
|
|
1104
|
-
const home = os.homedir();
|
|
1105
|
-
invalidateFsCache(path.join(home, getConfigDirName(), "plugins", "installed_plugins.json"));
|
|
1106
|
-
for (const p of extraPaths ?? []) invalidateFsCache(p);
|
|
1107
|
-
clearXcshPluginRootsCache();
|
|
1108
|
-
},
|
|
1109
|
-
});
|
|
1110
|
-
|
|
1111
|
-
switch (sub) {
|
|
1112
|
-
case "enable":
|
|
1113
|
-
case "disable": {
|
|
1114
|
-
const parsed = parsePluginScopeArgs(
|
|
1115
|
-
rest ?? "",
|
|
1116
|
-
`Usage: /plugins ${sub} [--scope user|project] <name@marketplace>`,
|
|
1117
|
-
);
|
|
1118
|
-
if ("error" in parsed) {
|
|
1119
|
-
runtime.ctx.showStatus(parsed.error);
|
|
1120
|
-
return;
|
|
1121
|
-
}
|
|
1122
|
-
const isEnable = sub === "enable";
|
|
1123
|
-
await mgr.setPluginEnabled(parsed.pluginId, isEnable, parsed.scope);
|
|
1124
|
-
runtime.ctx.showStatus(`${isEnable ? "Enabled" : "Disabled"} ${parsed.pluginId}`);
|
|
1125
|
-
break;
|
|
1126
|
-
}
|
|
1127
|
-
case "list": {
|
|
1109
|
+
// ── Installed list ──
|
|
1110
|
+
case "installed": {
|
|
1128
1111
|
const lines: string[] = [];
|
|
1129
|
-
|
|
1130
1112
|
const npm = new PluginManager();
|
|
1131
1113
|
const npmPlugins = await npm.list();
|
|
1132
1114
|
if (npmPlugins.length > 0) {
|
|
@@ -1136,7 +1118,6 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
1136
1118
|
lines.push(` ${p.name}@${p.version}${status}`);
|
|
1137
1119
|
}
|
|
1138
1120
|
}
|
|
1139
|
-
|
|
1140
1121
|
const mktPlugins = await mgr.listInstalledPlugins();
|
|
1141
1122
|
if (mktPlugins.length > 0) {
|
|
1142
1123
|
if (lines.length > 0) lines.push("");
|
|
@@ -1148,7 +1129,6 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
1148
1129
|
lines.push(` ${p.id} v${entry?.version ?? "?"}${status} [${p.scope}]${shadowed}`);
|
|
1149
1130
|
}
|
|
1150
1131
|
}
|
|
1151
|
-
|
|
1152
1132
|
if (lines.length === 0) {
|
|
1153
1133
|
runtime.ctx.showStatus("No plugins installed");
|
|
1154
1134
|
} else {
|
|
@@ -1156,19 +1136,78 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
1156
1136
|
}
|
|
1157
1137
|
break;
|
|
1158
1138
|
}
|
|
1159
|
-
|
|
1139
|
+
// ── Validate ──
|
|
1140
|
+
case "validate": {
|
|
1141
|
+
const targetPath = rest
|
|
1142
|
+
? path.resolve(runtime.ctx.sessionManager.getCwd(), rest)
|
|
1143
|
+
: runtime.ctx.sessionManager.getCwd();
|
|
1144
|
+
const catalogPath = path.join(targetPath, ".xcsh-plugin", "marketplace.json");
|
|
1145
|
+
const pluginPath = path.join(targetPath, ".xcsh-plugin", "plugin.json");
|
|
1146
|
+
const { existsSync } = await import("node:fs");
|
|
1147
|
+
if (existsSync(catalogPath)) {
|
|
1148
|
+
const { parseMarketplaceCatalog } = await import("../extensibility/plugins/marketplace/fetcher");
|
|
1149
|
+
const content = await Bun.file(catalogPath).text();
|
|
1150
|
+
const catalog = parseMarketplaceCatalog(content, catalogPath);
|
|
1151
|
+
runtime.ctx.showStatus(
|
|
1152
|
+
`Marketplace "${catalog.name}" is valid (${catalog.plugins.length} plugin(s))`,
|
|
1153
|
+
);
|
|
1154
|
+
} else if (existsSync(pluginPath)) {
|
|
1155
|
+
const content = await Bun.file(pluginPath).text();
|
|
1156
|
+
const manifest = JSON.parse(content);
|
|
1157
|
+
runtime.ctx.showStatus(`Plugin "${manifest.name ?? path.basename(targetPath)}" manifest is valid`);
|
|
1158
|
+
} else {
|
|
1159
|
+
runtime.ctx.showStatus(
|
|
1160
|
+
`No .xcsh-plugin/marketplace.json or .xcsh-plugin/plugin.json found at ${targetPath}`,
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
break;
|
|
1164
|
+
}
|
|
1165
|
+
// ── Help ──
|
|
1166
|
+
case "help": {
|
|
1160
1167
|
runtime.ctx.showStatus(
|
|
1161
|
-
|
|
1162
|
-
"
|
|
1163
|
-
" /
|
|
1164
|
-
" /
|
|
1165
|
-
" /
|
|
1168
|
+
[
|
|
1169
|
+
"Plugin commands:",
|
|
1170
|
+
" /plugin Open plugin dashboard",
|
|
1171
|
+
" /plugin marketplace add <source> Add a marketplace (e.g. owner/repo)",
|
|
1172
|
+
" /plugin marketplace remove <name> Remove a marketplace",
|
|
1173
|
+
" /plugin marketplace update [name] Re-fetch catalog(s)",
|
|
1174
|
+
" /plugin marketplace list List configured marketplaces",
|
|
1175
|
+
" /plugin discover [marketplace] Browse available plugins",
|
|
1176
|
+
" /plugin install <name@marketplace> Install a plugin",
|
|
1177
|
+
" /plugin uninstall <name@marketplace> Uninstall a plugin",
|
|
1178
|
+
" /plugin enable <name@marketplace> Enable a plugin",
|
|
1179
|
+
" /plugin disable <name@marketplace> Disable a plugin",
|
|
1180
|
+
" /plugin upgrade [name@marketplace] Upgrade plugin(s)",
|
|
1181
|
+
" /plugin list List installed plugins",
|
|
1182
|
+
" /plugin validate [path] Validate marketplace or plugin",
|
|
1183
|
+
"",
|
|
1184
|
+
"Quick start:",
|
|
1185
|
+
" /plugin marketplace add f5xc-salesdemos/marketplace",
|
|
1186
|
+
" /plugin (opens plugin dashboard)",
|
|
1187
|
+
"",
|
|
1188
|
+
"Aliases: /marketplace, /plugins",
|
|
1189
|
+
].join("\n"),
|
|
1166
1190
|
);
|
|
1167
1191
|
break;
|
|
1168
1192
|
}
|
|
1193
|
+
default: {
|
|
1194
|
+
const marketplaces = await mgr.listMarketplaces();
|
|
1195
|
+
if (marketplaces.length === 0) {
|
|
1196
|
+
runtime.ctx.showStatus(
|
|
1197
|
+
"No marketplaces configured.\n\nGet started:\n /plugin marketplace add f5xc-salesdemos/marketplace\n\nThen browse plugins with /plugin or /plugin discover",
|
|
1198
|
+
);
|
|
1199
|
+
} else {
|
|
1200
|
+
const lines = marketplaces.map(m => ` ${m.name} ${m.sourceUri}`);
|
|
1201
|
+
runtime.ctx.showStatus(
|
|
1202
|
+
`Marketplaces:\n${lines.join("\n")}\n\nUse /plugin discover to browse plugins, or /plugin help for all commands`,
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
break;
|
|
1206
|
+
}
|
|
1169
1207
|
}
|
|
1170
1208
|
} catch (err) {
|
|
1171
|
-
|
|
1209
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1210
|
+
runtime.ctx.showStatus(`Plugin error: ${msg}`);
|
|
1172
1211
|
}
|
|
1173
1212
|
},
|
|
1174
1213
|
},
|