@f5-sales-demo/xcsh 22.4.5 → 22.4.6
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 +2 -0
- package/package.json +8 -8
- package/src/cli/plugin-cli.ts +21 -14
- package/src/extensibility/extensions/loader.ts +9 -1
- package/src/extensibility/extensions/types.ts +2 -0
- package/src/extensibility/plugins/marketplace/builtin-marketplace-snapshot.ts +1 -1
- package/src/extensibility/plugins/marketplace/builtin-marketplace.provenance.json +3 -3
- package/src/extensibility/plugins/marketplace/fetcher.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +3 -0
- package/src/extensibility/plugins/marketplace/runtime.ts +10 -0
- package/src/extensibility/plugins/marketplace/types.ts +1 -0
- package/src/host/archive.ts +182 -0
- package/src/host/host.ts +78 -0
- package/src/host/management.ts +63 -0
- package/src/host/software.ts +370 -0
- package/src/index.ts +3 -0
- package/src/integrations/registry.ts +37 -5
- package/src/integrations/setup.ts +19 -3
- package/src/integrations/types.ts +17 -1
- package/src/internal-urls/api-catalog-qmd-index.generated.ts +2 -2
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/modes/controllers/selector-controller.ts +10 -2
- package/src/person-profile/machine-collectors.ts +4 -86
- package/src/slash-commands/builtin-registry.ts +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
+
- Added shared host-aware software setup with live management probes, reviewed package-manager routes, checksum-verified per-user archives, and asynchronous CLI/TUI setup preparation ([#4598](https://github.com/f5-sales-demo/xcsh/issues/4598)).
|
|
10
|
+
|
|
9
11
|
- Refreshed generated internal LiteLLM configuration with seven current GPT-6/Claude models,
|
|
10
12
|
route-accurate Responses and native Messages transports, hidden legacy session resolution, and an
|
|
11
13
|
independent maximum-context setting ([#4279](https://github.com/f5-sales-demo/xcsh/issues/4279)).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "22.4.
|
|
4
|
+
"version": "22.4.6",
|
|
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",
|
|
@@ -67,13 +67,13 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@agentclientprotocol/sdk": "1.4.0",
|
|
70
|
-
"@f5-sales-demo/pi-agent-core": "22.4.
|
|
71
|
-
"@f5-sales-demo/pi-ai": "22.4.
|
|
72
|
-
"@f5-sales-demo/pi-natives": "22.4.
|
|
73
|
-
"@f5-sales-demo/pi-resource-management": "22.4.
|
|
74
|
-
"@f5-sales-demo/pi-tui": "22.4.
|
|
75
|
-
"@f5-sales-demo/pi-utils": "22.4.
|
|
76
|
-
"@f5-sales-demo/xcsh-stats": "22.4.
|
|
70
|
+
"@f5-sales-demo/pi-agent-core": "22.4.6",
|
|
71
|
+
"@f5-sales-demo/pi-ai": "22.4.6",
|
|
72
|
+
"@f5-sales-demo/pi-natives": "22.4.6",
|
|
73
|
+
"@f5-sales-demo/pi-resource-management": "22.4.6",
|
|
74
|
+
"@f5-sales-demo/pi-tui": "22.4.6",
|
|
75
|
+
"@f5-sales-demo/pi-utils": "22.4.6",
|
|
76
|
+
"@f5-sales-demo/xcsh-stats": "22.4.6",
|
|
77
77
|
"@mozilla/readability": "^0.6",
|
|
78
78
|
"@resvg/resvg-wasm": "2.6.2",
|
|
79
79
|
"@sinclair/typebox": "0.34.52",
|
package/src/cli/plugin-cli.ts
CHANGED
|
@@ -344,10 +344,10 @@ export async function reviewAndExecuteIntegrationSetup(
|
|
|
344
344
|
handle: IntegrationHandle<unknown>,
|
|
345
345
|
options: { force?: boolean } = {},
|
|
346
346
|
) {
|
|
347
|
-
|
|
348
|
-
if (!plan) throw new Error(`Integration ${handle.id} does not declare setup`);
|
|
347
|
+
if (!handle.setupPlan) throw new Error(`Integration ${handle.id} does not declare setup`);
|
|
349
348
|
const current = await handle.get();
|
|
350
349
|
if (shouldSkipIntegrationSetup(current.state, options.force)) return current;
|
|
350
|
+
const plan = handle.prepareSetup ? await handle.prepareSetup() : handle.setupPlan!;
|
|
351
351
|
if (plan.guidedAction?.kind === "context_wizard")
|
|
352
352
|
throw new Error(
|
|
353
353
|
`Plugin ${handle.plugin ?? handle.id} setup requires interactive xcsh; run /plugin setup ${handle.plugin ?? handle.id} in the xcsh TUI.`,
|
|
@@ -373,18 +373,22 @@ async function handleIntegrationSetup(
|
|
|
373
373
|
if (args.length !== 1) throw new Error(`Usage: ${APP_NAME} plugin setup <plugin> [--context <name>] [--force]`);
|
|
374
374
|
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Plugin setup requires an interactive terminal");
|
|
375
375
|
const handles = await loadIntegrationHandles();
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
376
|
+
try {
|
|
377
|
+
await withPluginContext(flags.context, async () => {
|
|
378
|
+
const handle = selectSetupIntegration(handles, args[0]);
|
|
379
|
+
const current = await handle.get();
|
|
380
|
+
if (shouldSkipIntegrationSetup(current.state, flags.force)) {
|
|
381
|
+
process.stdout.write(`${handle.plugin ?? handle.id}: ready (setup is not required)\n`);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const result = await reviewAndExecuteIntegrationSetup(handle, { force: flags.force });
|
|
385
|
+
process.stdout.write(
|
|
386
|
+
`${handle.plugin ?? handle.id}: ${result.state}${result.reason ? ` (${result.reason})` : ""}\n`,
|
|
387
|
+
);
|
|
388
|
+
});
|
|
389
|
+
} catch (error) {
|
|
390
|
+
throw new CliUsageError(error instanceof Error ? error.message : "Plugin setup failed");
|
|
391
|
+
}
|
|
388
392
|
}
|
|
389
393
|
|
|
390
394
|
async function reportMarketplaceInstallation(
|
|
@@ -411,6 +415,9 @@ async function reportMarketplaceInstallation(
|
|
|
411
415
|
lifecycle: entry,
|
|
412
416
|
trigger: "direct-install",
|
|
413
417
|
handles,
|
|
418
|
+
review: async handle => {
|
|
419
|
+
process.stdout.write(`${describeSetupPlan(handle)}\n`);
|
|
420
|
+
},
|
|
414
421
|
});
|
|
415
422
|
if (installSetup) await personProfileService.reconcileFromCollectors(undefined, 0);
|
|
416
423
|
const statuses = await Promise.all(
|
|
@@ -21,12 +21,15 @@ import {
|
|
|
21
21
|
} from "../../discovery/helpers";
|
|
22
22
|
import type { ExecOptions } from "../../exec/exec";
|
|
23
23
|
import { execCommand } from "../../exec/exec";
|
|
24
|
+
import { host } from "../../host/host";
|
|
25
|
+
import { software } from "../../host/software";
|
|
24
26
|
import { integrationRegistry } from "../../integrations/registry";
|
|
25
27
|
import type { IntegrationDefinition, IntegrationHandle } from "../../integrations/types";
|
|
26
28
|
import { personProfileService } from "../../person-profile/service";
|
|
27
29
|
import type { CustomMessage } from "../../session/messages";
|
|
28
30
|
import { EventBus } from "../../utils/event-bus";
|
|
29
31
|
import { getAllPluginExtensionPaths } from "../plugins/loader";
|
|
32
|
+
import { assertRuntimeRequirement } from "../plugins/marketplace/runtime";
|
|
30
33
|
import { resolvePath } from "../utils";
|
|
31
34
|
import herdrReporter from "./bundled/herdr-reporter";
|
|
32
35
|
import herdrTerminal from "./bundled/herdr-terminal";
|
|
@@ -130,6 +133,8 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
130
133
|
config: import("./types").ProviderConfig;
|
|
131
134
|
sourceId: string;
|
|
132
135
|
}> = [];
|
|
136
|
+
readonly host = host;
|
|
137
|
+
readonly software = software;
|
|
133
138
|
readonly personProfile: ExtensionAPI["personProfile"];
|
|
134
139
|
readonly integrations: ExtensionAPI["integrations"];
|
|
135
140
|
readonly advisories: ExtensionAPI["advisories"];
|
|
@@ -301,7 +306,7 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
301
306
|
}
|
|
302
307
|
|
|
303
308
|
exec(command: string, args: string[], options?: ExecOptions) {
|
|
304
|
-
return execCommand(command, args, options?.cwd ?? this.cwd, options);
|
|
309
|
+
return execCommand(host.findExecutable(command) ?? command, args, options?.cwd ?? this.cwd, options);
|
|
305
310
|
}
|
|
306
311
|
|
|
307
312
|
getActiveTools(): string[] {
|
|
@@ -692,6 +697,9 @@ export async function discoverAndLoadExtensions(
|
|
|
692
697
|
// A marketplace plugin may use package metadata only.
|
|
693
698
|
}
|
|
694
699
|
|
|
700
|
+
const minimumRuntimeVersion = (canonical as { minimumRuntimeVersion?: string } | undefined)
|
|
701
|
+
?.minimumRuntimeVersion;
|
|
702
|
+
if (minimumRuntimeVersion) assertRuntimeRequirement({ name: root.plugin, minimumRuntimeVersion });
|
|
695
703
|
// The canonical manifest owns an explicitly declared extension list. Older marketplace
|
|
696
704
|
// plugins keep lifecycle metadata in that manifest and their extension declaration in
|
|
697
705
|
// package.json, so use the established package contract only when it is omitted.
|
|
@@ -1040,6 +1040,8 @@ export interface ExtensionAPI {
|
|
|
1040
1040
|
pi: typeof piCodingAgent;
|
|
1041
1041
|
|
|
1042
1042
|
/** Canonical, provenance-aware person profile integration. */
|
|
1043
|
+
readonly host: typeof import("../../host/host").host;
|
|
1044
|
+
readonly software: typeof import("../../host/software").software;
|
|
1043
1045
|
readonly personProfile: {
|
|
1044
1046
|
get(): Promise<import("../../person-profile/schema").PersonProfile>;
|
|
1045
1047
|
registerCollector(collector: import("../../person-profile/service").ExtensionProfileCollector): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
const builtinMarketplaceSnapshot =
|
|
2
|
-
'{\n "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",\n "name": "f5-sales-demo-marketplace",\n "version": "1.0.0",\n "metadata": {\n "description": "Claude Code plugins for f5-sales-demo documentation repositories"\n },\n "owner": {\n "name": "f5-sales-demo",\n "url": "https://github.com/f5-sales-demo"\n },\n "plugins": [\n {\n "name": "docs-tools",\n "description": "MDX content validation and review tools for f5-sales-demo docs repos",\n "version": "1.1.6",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/docs-tools",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/docs-tools",\n "category": "productivity",\n "keywords": ["mdx", "documentation", "validation", "starlight", "astro"],\n "tags": ["docs", "linting", "content-review", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["Documentation content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "sales-engineer",\n "description": "Sales Engineer persona framework for F5 XC demo repositories — demo execution, environment operations, presentation, Q&A, post-session debrief",\n "version": "1.0.12",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/sales-engineer",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/sales-engineer",\n "category": "productivity",\n "keywords": ["sales-engineer", "demo", "persona", "presentation", "f5xc"],\n "tags": ["demo", "sales", "persona", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["Sales engineering content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "docs-pipeline",\n "description": "Documentation pipeline architecture — config ownership, release dispatch chain, content authoring, managed files, local preview",\n "version": "1.0.6",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/docs-pipeline",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/docs-pipeline",\n "category": "productivity",\n "keywords": ["docs", "pipeline", "astro", "starlight", "content", "authoring"],\n "tags": ["docs", "pipeline", "content", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["Documentation pipeline content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "brand",\n "description": "F5 corporate branding enforcement — colors, typography, logos, icons, accessibility, and visual identity standards across all content types",\n "version": "1.0.6",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/brand",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/brand",\n "category": "productivity",\n "keywords": ["brand", "design-system", "colors", "typography", "accessibility"],\n "tags": ["brand", "design", "visual-identity", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["Brand guidance content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "meddpicc",\n "description": "MEDDPICC sales qualification and deal-execution framework — evidence-based deal management, stakeholder mapping, mutual action plans, and forecast integrity for complex enterprise B2B sales",\n "version": "7.5.9",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/meddpicc",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/meddpicc",\n "category": "productivity",\n "keywords": [\n "meddpicc",\n "sales-qualification",\n "deal-management",\n "enterprise-sales",\n "forecast",\n "champion",\n "economic-buyer",\n "mutual-action-plan",\n "f5xc"\n ],\n "tags": ["sales", "qualification", "deal-management", "forecast", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["MEDDPICC content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "devcontainer",\n "description": "Container awareness — tool catalog, self-identity, self-diagnosis, and maintenance for the f5-sales-demo devcontainer",\n "version": "1.2.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/devcontainer",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/devcontainer",\n "category": "productivity",\n "keywords": ["devcontainer", "tools", "catalog", "cli", "inventory", "container"],\n "tags": ["devcontainer", "tools", "catalog", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["devcontainer"],\n "requirements": ["A supported development container runtime"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "platform",\n "description": "Provider-neutral F5 Distributed Cloud Secure Mesh Site v2 capabilities, configuration, bootstrap, and status tools",\n "version": "6.0.1",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/platform",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/platform",\n "category": "productivity",\n "keywords": [\n "console",\n "browser",\n "automation",\n "azure-sso",\n "mcp",\n "chrome-devtools",\n "f5xc",\n "api",\n "rest",\n "platform",\n "openapi",\n "crud",\n "spec-aware"\n ],\n "tags": ["console", "browser", "automation", "f5xc", "api", "platform", "openapi"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["platform"],\n "requirements": ["F5 Distributed Cloud API URL and token environment variables"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "osint-framework",\n "description": "OSINT Framework tool catalog and investigation skills — 1,064 free intelligence-gathering tools across 34 categories, mapped from osintframework.com. Category-based skills, executable investigation pipelines, CLI tool execution, and OPSEC-aware workflows.",\n "version": "1.0.8",\n "author": {\n "name": "f5-sales-demo"\n },\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/osint-framework",\n "keywords": ["osint", "reconnaissance", "intelligence", "investigation", "security"],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/osint-framework",\n "category": "security",\n "tags": ["osint", "recon", "security", "intelligence"],\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["OSINT framework content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "firecrawl",\n "description": "Local self-hosted firecrawl web scraping — scrape, crawl, and map websites via the local firecrawl API on port 3002. No API keys, no subscriptions, fully open-source.",\n "version": "1.2.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/firecrawl",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/firecrawl",\n "category": "productivity",\n "keywords": ["firecrawl", "scrape", "crawl", "web-scraping", "markdown", "self-hosted", "local", "open-source"],\n "tags": ["firecrawl", "scraping", "web", "local"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["firecrawl"],\n "requirements": ["A reachable local Firecrawl service"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "salesforce",\n "description": "Salesforce pipeline intelligence with shared lifecycle setup, account discovery, context, and reporting tools",\n "version": "1.5.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/salesforce",\n "keywords": [\n "salesforce",\n "sf",\n "apex",\n "lwc",\n "soql",\n "metadata",\n "deploy",\n "testing",\n "sfdx",\n "agentforce",\n "pipeline",\n "xcsh"\n ],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/salesforce",\n "category": "development",\n "tags": ["salesforce", "development", "cli", "crm", "xcsh-extension"],\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["salesforce"],\n "requirements": ["Salesforce CLI", "An authenticated default Salesforce org"],\n "setupRequired": true,\n "collectedData": ["accounts", "identifiers", "business_profile"],\n "pluginDependencies": []\n }\n },\n {\n "name": "cloudstatus",\n "description": "Live cloud status and Internet network intelligence for xcsh — current incidents, routing, registration, RPKI, peering, facilities, F5 Regional Edge evidence, and bounded path diagnostics",\n "version": "1.7.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/cloudstatus",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/cloudstatus",\n "category": "productivity",\n "keywords": [\n "status",\n "monitoring",\n "incidents",\n "maintenance",\n "statuspage",\n "operational-intelligence",\n "network-intelligence",\n "bgp",\n "rdap",\n "rpki",\n "routing",\n "location",\n "peeringdb",\n "facilities",\n "f5xc",\n "atlassian"\n ],\n "tags": ["status", "network-intelligence", "routing", "peering", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "on_demand",\n "integrations": ["cloudstatus"],\n "requirements": ["Network access when a cloud-status command is invoked"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "azure",\n "description": "Azure CLI integration and deterministic F5 Customer Edge discovery, planning, lifecycle, and diagnostics",\n "version": "4.4.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/azure",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/azure",\n "category": "development",\n "keywords": ["azure", "az", "cloud", "f5xc", "customer-edge", "secure-mesh-site"],\n "tags": ["azure", "cloud", "f5xc", "customer-edge", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["azure"],\n "requirements": ["Azure CLI", "An authenticated Azure account"],\n "setupRequired": true,\n "collectedData": ["accounts"],\n "pluginDependencies": ["platform"]\n }\n },\n {\n "name": "aws",\n "description": "AWS CLI integration and deterministic F5 Secure Mesh Site v2 Customer Edge discovery, planning, lifecycle, and diagnostics",\n "version": "2.1.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/aws",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/aws",\n "category": "development",\n "keywords": ["aws", "amazon", "cloud", "f5xc", "customer-edge", "secure-mesh-site", "tgw", "nlb"],\n "tags": ["aws", "cloud", "f5xc", "customer-edge", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["aws"],\n "requirements": ["AWS CLI", "AWS credentials or an authenticated SSO profile"],\n "setupRequired": true,\n "collectedData": ["accounts"],\n "pluginDependencies": ["platform"]\n }\n },\n {\n "name": "kvm",\n "description": "Dual-NIC KVM Secure Mesh Site v2 with isolated SLO, home-LAN SLI, inside VIP, and rollback-protected host preparation",\n "version": "3.0.4",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/kvm",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/kvm",\n "category": "development",\n "keywords": ["kvm", "libvirt", "qemu", "f5xc", "customer-edge", "secure-mesh-site", "home-lan", "terraform"],\n "tags": ["kvm", "libvirt", "f5xc", "customer-edge", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["kvm"],\n "requirements": [\n "Terraform CLI",\n "libvirt and qemu system access",\n "curl",\n "F5 Distributed Cloud Platform integration"\n ],\n "setupRequired": true,\n "setupAuthorization": "install",\n "collectedData": [],\n "pluginDependencies": ["platform"]\n }\n },\n {\n "name": "gcloud",\n "description": "Google Cloud CLI integration — container-adapted auth, context injection, CLI operator agent, token expiry detection, and welcome screen status",\n "version": "1.3.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/gcloud",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/gcloud",\n "category": "development",\n "keywords": ["gcloud", "google", "cloud"],\n "tags": ["gcloud", "google", "cloud", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["gcloud"],\n "requirements": ["Google Cloud CLI", "An authenticated Google Cloud account"],\n "setupRequired": true,\n "collectedData": ["accounts"],\n "pluginDependencies": []\n }\n },\n {\n "name": "gitlab",\n "description": "GitLab CLI integration with shared lifecycle readiness, profile discovery, and issue management tools",\n "version": "1.3.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "keywords": ["gitlab", "glab", "issues", "git"],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/gitlab",\n "category": "development",\n "tags": ["gitlab", "development", "cli", "xcsh-extension"],\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/gitlab",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["gitlab"],\n "requirements": ["GitLab CLI", "An authenticated GitLab account"],\n "setupRequired": true,\n "collectedData": ["accounts"],\n "pluginDependencies": []\n }\n },\n {\n "name": "github",\n "description": "GitHub and Git operations with typed lifecycle stages, normalized evidence, and advisory governance",\n "version": "3.1.1",\n "author": {\n "name": "f5-sales-demo"\n },\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/github",\n "keywords": ["github", "gh", "git", "pr", "issues", "ci", "workflow", "git-sops", "github-ops"],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/github",\n "category": "development",\n "tags": ["github", "development", "cli", "workflow", "xcsh-extension"],\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["github", "github_email"],\n "requirements": ["GitHub CLI", "An authenticated GitHub account"],\n "setupRequired": true,\n "setupAuthorization": "install",\n "collectedData": ["accounts", "email", "identifiers", "sameAs"],\n "pluginDependencies": []\n }\n },\n {\n "name": "herdr",\n "description": "Install and operate the stable F5 Herdr fork with verified native xcsh health, explicit pairing, and complete pane-control guidance.",\n "version": "1.1.5",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/herdr",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "extensions": ["extensions/integration.ts"],\n "xcsh": {\n "name": "Herdr",\n "version": "1.1.5"\n },\n "source": "./plugins/herdr",\n "category": "development",\n "keywords": ["herdr", "terminal", "multiplexer", "panes", "agents"],\n "tags": ["terminal", "orchestration", "agents", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["herdr"],\n "requirements": [\n "Supported 64-bit Linux, macOS, or Windows host",\n "HTTPS access to the f5-sales-demo/herdr stable manifest and release"\n ],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "asm-migration",\n "description": "Deterministic BIG-IP ASM conversion and guarded F5 Distributed Cloud deployment",\n "version": "2.1.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/asm-migration",\n "keywords": ["asm", "waf", "migration", "f5xc", "xcsh-extension"],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/asm-migration",\n "category": "security",\n "tags": ["asm", "waf", "security", "migration", "f5xc"],\n "recommended": false,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["asm_migration"],\n "requirements": ["ASM migration runtime dependencies", "F5 Distributed Cloud Platform integration"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": ["platform"]\n }\n },\n {\n "name": "terraform",\n "description": "Senior DevOps Infrastructure-as-Code plugin providing HashiCorp Registry proactive version checks, HCL authoring, testing frameworks, and troubleshooting playbooks.",\n "version": "2.1.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/terraform",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/terraform",\n "category": "development",\n "keywords": ["terraform", "hcl", "iac", "devops", "hashicorp", "testing", "troubleshooting"],\n "tags": ["terraform", "iac", "devops", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["terraform"],\n "requirements": ["Terraform CLI"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "xorg",\n "version": "1.1.9",\n "description": "Ubuntu 24.04 Xorg desktop automation substrate",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "extensions": ["extensions/integration.ts"],\n "xcsh": {\n "name": "Xorg",\n "version": "1.1.9"\n },\n "source": "./plugins/xorg",\n "category": "development",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/xorg",\n "keywords": ["xorg", "at-spi", "xtest", "pipewire"],\n "tags": ["linux", "desktop", "automation"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["xorg"],\n "requirements": ["Ubuntu 24.04 with passwordless sudo"],\n "setupRequired": true,\n "setupAuthorization": "install",\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "zoom",\n "version": "1.1.1",\n "description": "Verified Zoom Workplace control through public xorgctl JSON",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "extensions": ["extensions/integration.ts"],\n "xcsh": {\n "name": "Zoom",\n "version": "1.1.1"\n },\n "source": "./plugins/zoom",\n "category": "productivity",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/zoom",\n "keywords": ["zoom", "meeting", "accessibility", "virtual-camera"],\n "tags": ["zoom", "meeting", "automation"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["zoom"],\n "requirements": ["Ubuntu 24.04 amd64", "Zoom Workplace 7.2.1.5760", "Xorg, Herdr, and Ghostty plugins"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": ["xorg", "herdr", "ghostty"]\n }\n },\n {\n "name": "ghostty",\n "version": "1.0.4",\n "description": "Curated Linux Ghostty setup for Xorg and Herdr terminal sessions",\n "license": "MIT",\n "author": {\n "name": "f5-sales-demo"\n },\n "extensions": ["extensions/integration.ts"],\n "xcsh": {\n "name": "Ghostty",\n "version": "1.0.4"\n },\n "source": "./plugins/ghostty",\n "category": "development",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/ghostty",\n "keywords": ["ghostty", "terminal", "xorg", "herdr"],\n "tags": ["linux", "terminal", "xorg"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["ghostty"],\n "requirements": ["Ubuntu Linux", "Xorg and Herdr marketplace plugins"],\n "setupRequired": true,\n "setupAuthorization": "install",\n "collectedData": [],\n "pluginDependencies": ["xorg", "herdr"]\n }\n }\n ]\n}\n';
|
|
2
|
+
'{\n "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",\n "name": "f5-sales-demo-marketplace",\n "version": "1.0.0",\n "metadata": {\n "description": "Claude Code plugins for f5-sales-demo documentation repositories"\n },\n "owner": {\n "name": "f5-sales-demo",\n "url": "https://github.com/f5-sales-demo"\n },\n "plugins": [\n {\n "name": "docs-tools",\n "description": "MDX content validation and review tools for f5-sales-demo docs repos",\n "version": "1.1.6",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/docs-tools",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/docs-tools",\n "category": "productivity",\n "keywords": ["mdx", "documentation", "validation", "starlight", "astro"],\n "tags": ["docs", "linting", "content-review", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["Documentation content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "sales-engineer",\n "description": "Sales Engineer persona framework for F5 XC demo repositories — demo execution, environment operations, presentation, Q&A, post-session debrief",\n "version": "1.0.12",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/sales-engineer",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/sales-engineer",\n "category": "productivity",\n "keywords": ["sales-engineer", "demo", "persona", "presentation", "f5xc"],\n "tags": ["demo", "sales", "persona", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["Sales engineering content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "docs-pipeline",\n "description": "Documentation pipeline architecture — config ownership, release dispatch chain, content authoring, managed files, local preview",\n "version": "1.0.6",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/docs-pipeline",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/docs-pipeline",\n "category": "productivity",\n "keywords": ["docs", "pipeline", "astro", "starlight", "content", "authoring"],\n "tags": ["docs", "pipeline", "content", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["Documentation pipeline content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "brand",\n "description": "F5 corporate branding enforcement — colors, typography, logos, icons, accessibility, and visual identity standards across all content types",\n "version": "1.0.6",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/brand",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/brand",\n "category": "productivity",\n "keywords": ["brand", "design-system", "colors", "typography", "accessibility"],\n "tags": ["brand", "design", "visual-identity", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["Brand guidance content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "meddpicc",\n "description": "MEDDPICC sales qualification and deal-execution framework — evidence-based deal management, stakeholder mapping, mutual action plans, and forecast integrity for complex enterprise B2B sales",\n "version": "7.6.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/meddpicc",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/meddpicc",\n "category": "productivity",\n "keywords": [\n "meddpicc",\n "sales-qualification",\n "deal-management",\n "enterprise-sales",\n "forecast",\n "champion",\n "economic-buyer",\n "mutual-action-plan",\n "f5xc"\n ],\n "tags": ["sales", "qualification", "deal-management", "forecast", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["MEDDPICC content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "devcontainer",\n "description": "Container awareness — tool catalog, self-identity, self-diagnosis, and maintenance for the f5-sales-demo devcontainer",\n "version": "1.2.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/devcontainer",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/devcontainer",\n "category": "productivity",\n "keywords": ["devcontainer", "tools", "catalog", "cli", "inventory", "container"],\n "tags": ["devcontainer", "tools", "catalog", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["devcontainer"],\n "requirements": ["A supported development container runtime"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "platform",\n "description": "Provider-neutral F5 Distributed Cloud Secure Mesh Site v2 capabilities, configuration, bootstrap, and status tools",\n "version": "6.0.1",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/platform",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/platform",\n "category": "productivity",\n "keywords": [\n "console",\n "browser",\n "automation",\n "azure-sso",\n "mcp",\n "chrome-devtools",\n "f5xc",\n "api",\n "rest",\n "platform",\n "openapi",\n "crud",\n "spec-aware"\n ],\n "tags": ["console", "browser", "automation", "f5xc", "api", "platform", "openapi"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["platform"],\n "requirements": ["F5 Distributed Cloud API URL and token environment variables"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "osint-framework",\n "description": "OSINT Framework tool catalog and investigation skills — 1,064 free intelligence-gathering tools across 34 categories, mapped from osintframework.com. Category-based skills, executable investigation pipelines, CLI tool execution, and OPSEC-aware workflows.",\n "version": "1.0.8",\n "author": {\n "name": "f5-sales-demo"\n },\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/osint-framework",\n "keywords": ["osint", "reconnaissance", "intelligence", "investigation", "security"],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/osint-framework",\n "category": "security",\n "tags": ["osint", "recon", "security", "intelligence"],\n "lifecycle": {\n "mode": "content",\n "integrations": [],\n "requirements": ["OSINT framework content only"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "firecrawl",\n "description": "Local self-hosted firecrawl web scraping — scrape, crawl, and map websites via the local firecrawl API on port 3002. No API keys, no subscriptions, fully open-source.",\n "version": "1.2.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/firecrawl",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/firecrawl",\n "category": "productivity",\n "keywords": ["firecrawl", "scrape", "crawl", "web-scraping", "markdown", "self-hosted", "local", "open-source"],\n "tags": ["firecrawl", "scraping", "web", "local"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["firecrawl"],\n "requirements": ["A reachable local Firecrawl service"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "salesforce",\n "description": "Salesforce pipeline intelligence with shared lifecycle setup, account discovery, context, and reporting tools",\n "version": "1.5.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/salesforce",\n "keywords": [\n "salesforce",\n "sf",\n "apex",\n "lwc",\n "soql",\n "metadata",\n "deploy",\n "testing",\n "sfdx",\n "agentforce",\n "pipeline",\n "xcsh"\n ],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/salesforce",\n "category": "development",\n "tags": ["salesforce", "development", "cli", "crm", "xcsh-extension"],\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["salesforce"],\n "requirements": ["Salesforce CLI", "An authenticated default Salesforce org"],\n "setupRequired": true,\n "collectedData": ["accounts", "identifiers", "business_profile"],\n "pluginDependencies": []\n }\n },\n {\n "name": "cloudstatus",\n "description": "Live cloud status and Internet network intelligence for xcsh — current incidents, routing, registration, RPKI, peering, facilities, F5 Regional Edge evidence, and bounded path diagnostics",\n "version": "1.7.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/cloudstatus",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/cloudstatus",\n "category": "productivity",\n "keywords": [\n "status",\n "monitoring",\n "incidents",\n "maintenance",\n "statuspage",\n "operational-intelligence",\n "network-intelligence",\n "bgp",\n "rdap",\n "rpki",\n "routing",\n "location",\n "peeringdb",\n "facilities",\n "f5xc",\n "atlassian"\n ],\n "tags": ["status", "network-intelligence", "routing", "peering", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "on_demand",\n "integrations": ["cloudstatus"],\n "requirements": ["Network access when a cloud-status command is invoked"],\n "setupRequired": false,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "azure",\n "description": "Azure CLI integration and deterministic F5 Customer Edge discovery, planning, lifecycle, and diagnostics",\n "version": "4.4.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/azure",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/azure",\n "category": "development",\n "keywords": ["azure", "az", "cloud", "f5xc", "customer-edge", "secure-mesh-site"],\n "tags": ["azure", "cloud", "f5xc", "customer-edge", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["azure"],\n "requirements": ["Azure CLI", "An authenticated Azure account"],\n "setupRequired": true,\n "collectedData": ["accounts"],\n "pluginDependencies": ["platform"]\n }\n },\n {\n "name": "aws",\n "description": "AWS CLI integration and deterministic F5 Secure Mesh Site v2 Customer Edge discovery, planning, lifecycle, and diagnostics",\n "version": "2.1.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/aws",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/aws",\n "category": "development",\n "keywords": ["aws", "amazon", "cloud", "f5xc", "customer-edge", "secure-mesh-site", "tgw", "nlb"],\n "tags": ["aws", "cloud", "f5xc", "customer-edge", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["aws"],\n "requirements": ["AWS CLI", "AWS credentials or an authenticated SSO profile"],\n "setupRequired": true,\n "collectedData": ["accounts"],\n "pluginDependencies": ["platform"]\n }\n },\n {\n "name": "kvm",\n "description": "Dual-NIC KVM Secure Mesh Site v2 with isolated SLO, home-LAN SLI, inside VIP, and rollback-protected host preparation",\n "version": "3.0.4",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/kvm",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/kvm",\n "category": "development",\n "keywords": ["kvm", "libvirt", "qemu", "f5xc", "customer-edge", "secure-mesh-site", "home-lan", "terraform"],\n "tags": ["kvm", "libvirt", "f5xc", "customer-edge", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["kvm"],\n "requirements": [\n "Terraform CLI",\n "libvirt and qemu system access",\n "curl",\n "F5 Distributed Cloud Platform integration"\n ],\n "setupRequired": true,\n "setupAuthorization": "install",\n "collectedData": [],\n "pluginDependencies": ["platform"]\n }\n },\n {\n "name": "gcloud",\n "description": "Google Cloud CLI integration — container-adapted auth, context injection, CLI operator agent, token expiry detection, and welcome screen status",\n "version": "1.3.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/gcloud",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/gcloud",\n "category": "development",\n "keywords": ["gcloud", "google", "cloud"],\n "tags": ["gcloud", "google", "cloud", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["gcloud"],\n "requirements": ["Google Cloud CLI", "An authenticated Google Cloud account"],\n "setupRequired": true,\n "collectedData": ["accounts"],\n "pluginDependencies": []\n }\n },\n {\n "name": "gitlab",\n "description": "GitLab CLI integration with shared lifecycle readiness, profile discovery, and issue management tools",\n "version": "1.3.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "keywords": ["gitlab", "glab", "issues", "git"],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/gitlab",\n "category": "development",\n "tags": ["gitlab", "development", "cli", "xcsh-extension"],\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/gitlab",\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["gitlab"],\n "requirements": ["GitLab CLI", "An authenticated GitLab account"],\n "setupRequired": true,\n "collectedData": ["accounts"],\n "pluginDependencies": []\n }\n },\n {\n "name": "github",\n "description": "GitHub and Git operations with typed lifecycle stages, normalized evidence, and advisory governance",\n "version": "3.1.1",\n "author": {\n "name": "f5-sales-demo"\n },\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/github",\n "keywords": ["github", "gh", "git", "pr", "issues", "ci", "workflow", "git-sops", "github-ops"],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/github",\n "category": "development",\n "tags": ["github", "development", "cli", "workflow", "xcsh-extension"],\n "recommended": true,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["github", "github_email"],\n "requirements": ["GitHub CLI", "An authenticated GitHub account"],\n "setupRequired": true,\n "setupAuthorization": "install",\n "collectedData": ["accounts", "email", "identifiers", "sameAs"],\n "pluginDependencies": []\n }\n },\n {\n "name": "herdr",\n "description": "Install and operate the stable F5 Herdr fork with verified native xcsh health, explicit pairing, and complete pane-control guidance.",\n "version": "1.1.5",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/herdr",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "extensions": ["extensions/integration.ts"],\n "xcsh": {\n "name": "Herdr",\n "version": "1.1.5"\n },\n "source": "./plugins/herdr",\n "category": "development",\n "keywords": ["herdr", "terminal", "multiplexer", "panes", "agents"],\n "tags": ["terminal", "orchestration", "agents", "xcsh-extension"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["herdr"],\n "requirements": [\n "Supported 64-bit Linux, macOS, or Windows host",\n "HTTPS access to the f5-sales-demo/herdr stable manifest and release"\n ],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "asm-migration",\n "description": "Deterministic BIG-IP ASM conversion and guarded F5 Distributed Cloud deployment",\n "version": "2.1.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/asm-migration",\n "keywords": ["asm", "waf", "migration", "f5xc", "xcsh-extension"],\n "license": "Apache-2.0",\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "source": "./plugins/asm-migration",\n "category": "security",\n "tags": ["asm", "waf", "security", "migration", "f5xc"],\n "recommended": false,\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["asm_migration"],\n "requirements": ["ASM migration runtime dependencies", "F5 Distributed Cloud Platform integration"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": ["platform"]\n }\n },\n {\n "name": "terraform",\n "description": "Senior DevOps Infrastructure-as-Code plugin providing HashiCorp Registry proactive version checks, HCL authoring, testing frameworks, and troubleshooting playbooks.",\n "version": "2.1.0",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/terraform",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "source": "./plugins/terraform",\n "category": "development",\n "keywords": ["terraform", "hcl", "iac", "devops", "hashicorp", "testing", "troubleshooting"],\n "tags": ["terraform", "iac", "devops", "f5xc"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["terraform"],\n "requirements": ["Terraform CLI"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "xorg",\n "version": "1.1.9",\n "description": "Ubuntu 24.04 Xorg desktop automation substrate",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "extensions": ["extensions/integration.ts"],\n "xcsh": {\n "name": "Xorg",\n "version": "1.1.9"\n },\n "source": "./plugins/xorg",\n "category": "development",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/xorg",\n "keywords": ["xorg", "at-spi", "xtest", "pipewire"],\n "tags": ["linux", "desktop", "automation"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["xorg"],\n "requirements": ["Ubuntu 24.04 with passwordless sudo"],\n "setupRequired": true,\n "setupAuthorization": "install",\n "collectedData": [],\n "pluginDependencies": []\n }\n },\n {\n "name": "zoom",\n "version": "1.1.6",\n "description": "Verified Zoom Workplace control through public xorgctl JSON",\n "license": "Apache-2.0",\n "author": {\n "name": "f5-sales-demo"\n },\n "extensions": ["extensions/integration.ts"],\n "xcsh": {\n "name": "Zoom",\n "version": "1.1.6"\n },\n "source": "./plugins/zoom",\n "category": "productivity",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/zoom",\n "keywords": ["zoom", "meeting", "accessibility", "virtual-camera"],\n "tags": ["zoom", "meeting", "automation"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["zoom"],\n "requirements": ["Ubuntu 24.04 amd64", "Zoom Workplace 7.2.1.5760", "Xorg, Herdr, and Ghostty plugins"],\n "setupRequired": true,\n "collectedData": [],\n "pluginDependencies": ["xorg", "herdr", "ghostty"]\n }\n },\n {\n "name": "ghostty",\n "version": "1.0.4",\n "description": "Curated Linux Ghostty setup for Xorg and Herdr terminal sessions",\n "license": "MIT",\n "author": {\n "name": "f5-sales-demo"\n },\n "extensions": ["extensions/integration.ts"],\n "xcsh": {\n "name": "Ghostty",\n "version": "1.0.4"\n },\n "source": "./plugins/ghostty",\n "category": "development",\n "homepage": "https://github.com/f5-sales-demo/marketplace/tree/main/plugins/ghostty",\n "keywords": ["ghostty", "terminal", "xorg", "herdr"],\n "tags": ["linux", "terminal", "xorg"],\n "repository": "https://github.com/f5-sales-demo/marketplace",\n "lifecycle": {\n "mode": "integrated",\n "integrations": ["ghostty"],\n "requirements": ["Ubuntu Linux", "Xorg and Herdr marketplace plugins"],\n "setupRequired": true,\n "setupAuthorization": "install",\n "collectedData": [],\n "pluginDependencies": ["xorg", "herdr"]\n }\n }\n ]\n}\n';
|
|
3
3
|
|
|
4
4
|
export default builtinMarketplaceSnapshot;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repository": "https://github.com/f5-sales-demo/marketplace",
|
|
3
3
|
"sourcePath": ".xcsh-plugin/marketplace.json",
|
|
4
|
-
"commit": "
|
|
5
|
-
"sha256": "
|
|
6
|
-
"capturedAt": "2026-09-
|
|
4
|
+
"commit": "e5460adca2d6ebab2e585c8a8924b0c1ce502c8f",
|
|
5
|
+
"sha256": "9de315c4ef171142a1e2235f99be47c9bdb51963ba456728d153c570cb6e8724",
|
|
6
|
+
"capturedAt": "2026-09-30T03:10:08.894Z"
|
|
7
7
|
}
|
|
@@ -132,6 +132,12 @@ export function parseMarketplaceCatalog(content: string, filePath: string): Mark
|
|
|
132
132
|
const entry = plugins[i];
|
|
133
133
|
assertField(typeof entry === "object" && entry !== null && !Array.isArray(entry), `plugins[${i}]`, filePath);
|
|
134
134
|
const p = entry as Record<string, unknown>;
|
|
135
|
+
assertField(
|
|
136
|
+
p.minimumRuntimeVersion === undefined ||
|
|
137
|
+
(typeof p.minimumRuntimeVersion === "string" && /^\d+\.\d+\.\d+$/.test(p.minimumRuntimeVersion)),
|
|
138
|
+
`plugins[${i}].minimumRuntimeVersion`,
|
|
139
|
+
filePath,
|
|
140
|
+
);
|
|
135
141
|
assertField(typeof p.name === "string" && isValidNameSegment(p.name), `plugins[${i}].name`, filePath);
|
|
136
142
|
// source can be a string path or a typed object (github/url/git-subdir/npm)
|
|
137
143
|
// all typed objects carry a "source" discriminant string field
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertRuntimeRequirement } from "./runtime";
|
|
1
2
|
/**
|
|
2
3
|
* MarketplaceManager — orchestrates registry, fetcher, resolver, and cache.
|
|
3
4
|
*
|
|
@@ -491,6 +492,8 @@ export class MarketplaceManager {
|
|
|
491
492
|
if (!entry) throw new Error(`Marketplace "${marketplace}" not found`);
|
|
492
493
|
const catalog = await this.#readCatalog(entry);
|
|
493
494
|
const plan = resolvePluginDependencyNames(catalog, name);
|
|
495
|
+
for (const pluginName of plan)
|
|
496
|
+
assertRuntimeRequirement(catalog.plugins.find(plugin => plugin.name === pluginName)!);
|
|
494
497
|
const retainReplacedCache = entry.sourceType === "local";
|
|
495
498
|
const registryPath = this.#registryPath(scope);
|
|
496
499
|
const before = await readInstalledPluginsRegistry(registryPath);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { VERSION } from "@f5-sales-demo/pi-utils";
|
|
2
|
+
export function assertRuntimeRequirement(
|
|
3
|
+
plugin: { name: string; minimumRuntimeVersion?: string },
|
|
4
|
+
current = VERSION,
|
|
5
|
+
): void {
|
|
6
|
+
if (plugin.minimumRuntimeVersion === undefined) return;
|
|
7
|
+
if (!/^\d+\.\d+\.\d+$/.test(plugin.minimumRuntimeVersion)) throw new Error("Invalid minimum xcsh runtime version");
|
|
8
|
+
if (Bun.semver.order(current, plugin.minimumRuntimeVersion) < 0)
|
|
9
|
+
throw new Error(`${plugin.name} requires xcsh ${plugin.minimumRuntimeVersion} or later; upgrade xcsh first`);
|
|
10
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
3
|
+
import { chmod, link as hardlink, lstat, mkdir, mkdtemp, rename, rm, symlink } from "node:fs/promises";
|
|
4
|
+
import { dirname, isAbsolute, join, posix } from "node:path";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
6
|
+
import { pipeline } from "node:stream/promises";
|
|
7
|
+
import { createGunzip } from "node:zlib";
|
|
8
|
+
import { extract } from "tar-stream";
|
|
9
|
+
import type { ArchiveInstall } from "../integrations/types";
|
|
10
|
+
import { exposeExecutable } from "./host";
|
|
11
|
+
|
|
12
|
+
export function safeArchivePath(name: string): string {
|
|
13
|
+
if (
|
|
14
|
+
!name ||
|
|
15
|
+
name.includes("\\") ||
|
|
16
|
+
name.includes("\0") ||
|
|
17
|
+
name.startsWith("/") ||
|
|
18
|
+
/^[a-z]:/i.test(name) ||
|
|
19
|
+
name.split("/").includes("..")
|
|
20
|
+
)
|
|
21
|
+
throw new Error("Unsafe archive extraction path");
|
|
22
|
+
const normalized = posix.normalize(name);
|
|
23
|
+
if (normalized === "." || normalized.startsWith("../")) throw new Error("Unsafe archive extraction path");
|
|
24
|
+
return normalized.replace(/\/$/, "");
|
|
25
|
+
}
|
|
26
|
+
async function unpack(file: string, stage: string, baseDir: string, signal: AbortSignal) {
|
|
27
|
+
const archive = extract();
|
|
28
|
+
const pendingLinks: { path: string; target: string; hard: boolean }[] = [];
|
|
29
|
+
const seen = new Set<string>();
|
|
30
|
+
let total = 0;
|
|
31
|
+
let entries = 0;
|
|
32
|
+
archive.on("entry", (header, stream, next) => {
|
|
33
|
+
stream.on("error", error => archive.destroy(error));
|
|
34
|
+
void (async () => {
|
|
35
|
+
signal.throwIfAborted();
|
|
36
|
+
const name = safeArchivePath(header.name);
|
|
37
|
+
if ((name !== baseDir && !name.startsWith(`${baseDir}/`)) || seen.has(name) || ++entries > 100_000)
|
|
38
|
+
throw new Error("Unsafe or duplicate archive extraction path");
|
|
39
|
+
seen.add(name);
|
|
40
|
+
total += header.size ?? 0;
|
|
41
|
+
if (total > 2 * 1024 ** 3) throw new Error("Archive extraction size limit exceeded");
|
|
42
|
+
const path = join(stage, name);
|
|
43
|
+
if (header.type === "directory") {
|
|
44
|
+
await mkdir(path, { recursive: true, mode: 0o700 });
|
|
45
|
+
stream.resume();
|
|
46
|
+
} else if (header.type === "file" || header.type === "contiguous-file") {
|
|
47
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
48
|
+
await pipeline(stream, createWriteStream(path, { flags: "wx", mode: (header.mode ?? 0o600) & 0o777 }), {
|
|
49
|
+
signal,
|
|
50
|
+
});
|
|
51
|
+
} else if (header.type === "symlink" || header.type === "link") {
|
|
52
|
+
const target = header.linkname ?? "";
|
|
53
|
+
if (!target || isAbsolute(target) || target.includes("\\") || target.includes("\0"))
|
|
54
|
+
throw new Error("Unsafe archive link");
|
|
55
|
+
const resolved =
|
|
56
|
+
header.type === "link"
|
|
57
|
+
? safeArchivePath(target)
|
|
58
|
+
: posix.normalize(posix.join(posix.dirname(name), target));
|
|
59
|
+
if (!resolved.startsWith(`${baseDir}/`) && resolved !== baseDir)
|
|
60
|
+
throw new Error("Unsafe archive link target");
|
|
61
|
+
pendingLinks.push({
|
|
62
|
+
path,
|
|
63
|
+
target: header.type === "link" ? join(stage, resolved) : target,
|
|
64
|
+
hard: header.type === "link",
|
|
65
|
+
});
|
|
66
|
+
stream.resume();
|
|
67
|
+
} else throw new Error("Unsupported archive entry type");
|
|
68
|
+
})().then(
|
|
69
|
+
() => next(),
|
|
70
|
+
error => {
|
|
71
|
+
stream.destroy(error);
|
|
72
|
+
archive.destroy(error);
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
await pipeline(createReadStream(file), createGunzip(), archive, { signal });
|
|
77
|
+
// No deferred link may be an ancestor of any other entry, including another link.
|
|
78
|
+
const relativeLinks = pendingLinks.map(item => item.path.slice(stage.length + 1));
|
|
79
|
+
for (const linkPath of relativeLinks)
|
|
80
|
+
if ([...seen].some(name => name.startsWith(`${linkPath}/`))) throw new Error("Unsafe archive link ancestor");
|
|
81
|
+
// Create links only after extraction; no extracted file can traverse a symlink.
|
|
82
|
+
for (const item of pendingLinks) {
|
|
83
|
+
signal.throwIfAborted();
|
|
84
|
+
await mkdir(dirname(item.path), { recursive: true, mode: 0o700 });
|
|
85
|
+
if (item.hard) {
|
|
86
|
+
if (!(await lstat(item.target)).isFile()) throw new Error("Unsafe archive hard link");
|
|
87
|
+
await hardlink(item.target, item.path);
|
|
88
|
+
} else await symlink(item.target, item.path);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export async function installArchive(
|
|
92
|
+
archive: ArchiveInstall,
|
|
93
|
+
signal?: AbortSignal,
|
|
94
|
+
timeoutMs = 600_000,
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
97
|
+
const combined = signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
98
|
+
if (
|
|
99
|
+
!isAbsolute(archive.destination) ||
|
|
100
|
+
!isAbsolute(archive.link) ||
|
|
101
|
+
!/^[a-f0-9]{64}$/.test(archive.sha256) ||
|
|
102
|
+
new URL(archive.url).protocol !== "https:"
|
|
103
|
+
)
|
|
104
|
+
throw new Error("Invalid archive installation plan");
|
|
105
|
+
const baseDir = safeArchivePath(archive.baseDir);
|
|
106
|
+
const relativeExecutable = safeArchivePath(archive.executable);
|
|
107
|
+
if (baseDir.includes("/")) throw new Error("Invalid archive base directory");
|
|
108
|
+
combined.throwIfAborted();
|
|
109
|
+
await mkdir(dirname(archive.destination), { recursive: true, mode: 0o700 });
|
|
110
|
+
const stage = await mkdtemp(join(dirname(archive.destination), ".xcsh-stage-"));
|
|
111
|
+
let ownedDestination = false;
|
|
112
|
+
let ownedLink = false;
|
|
113
|
+
try {
|
|
114
|
+
const response = await fetch(archive.url, {
|
|
115
|
+
signal: combined,
|
|
116
|
+
redirect: "error",
|
|
117
|
+
headers: { "User-Agent": "curl/8.5.0 xcsh-software" },
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok || !response.body) throw new Error("Archive download failed");
|
|
120
|
+
const file = join(stage, "download.tar.gz");
|
|
121
|
+
const hash = createHash("sha256");
|
|
122
|
+
let size = 0;
|
|
123
|
+
const reader = response.body.getReader();
|
|
124
|
+
const output = createWriteStream(file, { flags: "wx", mode: 0o600 });
|
|
125
|
+
try {
|
|
126
|
+
await pipeline(
|
|
127
|
+
Readable.from(
|
|
128
|
+
(async function* () {
|
|
129
|
+
while (true) {
|
|
130
|
+
const { done, value } = await reader.read();
|
|
131
|
+
if (done) break;
|
|
132
|
+
size += value.length;
|
|
133
|
+
if (size > 1024 ** 3) throw new Error("Archive download size limit exceeded");
|
|
134
|
+
hash.update(value);
|
|
135
|
+
yield value;
|
|
136
|
+
}
|
|
137
|
+
})(),
|
|
138
|
+
),
|
|
139
|
+
output,
|
|
140
|
+
{ signal: combined },
|
|
141
|
+
);
|
|
142
|
+
} finally {
|
|
143
|
+
await reader.cancel().catch(() => {});
|
|
144
|
+
reader.releaseLock();
|
|
145
|
+
}
|
|
146
|
+
if (hash.digest("hex") !== archive.sha256) throw new Error("Archive SHA-256 checksum mismatch");
|
|
147
|
+
await unpack(file, stage, baseDir, combined);
|
|
148
|
+
const executable = join(stage, baseDir, relativeExecutable);
|
|
149
|
+
if (!(await lstat(executable)).isFile())
|
|
150
|
+
throw new Error("Archive executable is missing or is not a regular file");
|
|
151
|
+
await chmod(executable, 0o755);
|
|
152
|
+
const child = Bun.spawn([executable, ...archive.versionArgs], {
|
|
153
|
+
stdin: "ignore",
|
|
154
|
+
stdout: "pipe",
|
|
155
|
+
stderr: "ignore",
|
|
156
|
+
signal: AbortSignal.any([combined, AbortSignal.timeout(15_000)]),
|
|
157
|
+
killSignal: "SIGKILL",
|
|
158
|
+
});
|
|
159
|
+
const version = await new Response(child.stdout).text();
|
|
160
|
+
if ((await child.exited) !== 0 || !version.includes(archive.version))
|
|
161
|
+
throw new Error("Archive executable version verification failed");
|
|
162
|
+
combined.throwIfAborted();
|
|
163
|
+
// Exclusive reservation prevents a competing or unrelated destination from being replaced.
|
|
164
|
+
await mkdir(archive.destination, { mode: 0o700 });
|
|
165
|
+
ownedDestination = true;
|
|
166
|
+
await rename(join(stage, baseDir), join(archive.destination, baseDir));
|
|
167
|
+
await mkdir(dirname(archive.link), { recursive: true, mode: 0o700 });
|
|
168
|
+
combined.throwIfAborted();
|
|
169
|
+
const installed = join(archive.destination, baseDir, relativeExecutable);
|
|
170
|
+
await symlink(installed, archive.link);
|
|
171
|
+
ownedLink = true;
|
|
172
|
+
exposeExecutable(installed);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (ownedLink) await rm(archive.link, { force: true });
|
|
175
|
+
if (ownedDestination) await rm(archive.destination, { recursive: true, force: true });
|
|
176
|
+
if (combined.aborted)
|
|
177
|
+
throw new Error(signal?.aborted ? "Integration setup cancelled" : "Archive installation timed out");
|
|
178
|
+
throw error;
|
|
179
|
+
} finally {
|
|
180
|
+
await rm(stage, { recursive: true, force: true });
|
|
181
|
+
}
|
|
182
|
+
}
|