@f5xc-salesdemos/xcsh 18.80.0 → 18.82.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 +10 -9
- package/scripts/generate-branding-index.ts +78 -0
- package/src/internal-urls/branding-index.generated.ts +88 -0
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/internal-urls/xcsh-protocol.ts +212 -1
- package/src/prompts/system/system-prompt.md +26 -0
- package/src/tools/xcsh-api.ts +16 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "18.
|
|
4
|
+
"version": "18.82.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",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"xcsh": "src/cli.ts"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
"build": "bun run generate-build-info && bun run generate-api-spec-index && test -f src/internal-urls/api-spec-index.generated.ts && bun --cwd=../stats scripts/generate-client-bundle.ts --generate && bun --cwd=../natives run embed:native && bun build --compile --define PI_COMPILED=true --external mupdf --root ../.. ./src/cli.ts --outfile dist/xcsh && bun --cwd=../natives run embed:native --reset && bun --cwd=../stats scripts/generate-client-bundle.ts --reset",
|
|
34
|
+
"build": "bun run generate-build-info && bun run generate-api-spec-index && bun run generate-branding-index && test -f src/internal-urls/api-spec-index.generated.ts && bun --cwd=../stats scripts/generate-client-bundle.ts --generate && bun --cwd=../natives run embed:native && bun build --compile --define PI_COMPILED=true --external mupdf --root ../.. ./src/cli.ts --outfile dist/xcsh && bun --cwd=../natives run embed:native --reset && bun --cwd=../stats scripts/generate-client-bundle.ts --reset",
|
|
35
35
|
"check": "biome check . && bun run format-prompts -- --check && bun run check:types",
|
|
36
|
-
"check:types": "bun run generate-build-info && bun run generate-api-spec-index && tsgo -p tsconfig.json --noEmit",
|
|
36
|
+
"check:types": "bun run generate-build-info && bun run generate-api-spec-index && bun run generate-branding-index && tsgo -p tsconfig.json --noEmit",
|
|
37
37
|
"lint": "biome lint .",
|
|
38
38
|
"test": "bun run generate-build-info && bun run generate-api-spec-index && bun test --max-concurrency 4",
|
|
39
39
|
"fix": "biome check --write --unsafe . && bun run format-prompts && bun run generate-docs-index && bun run generate-api-spec-index && bun run generate-build-info",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"format-prompts": "bun scripts/format-prompts.ts",
|
|
42
42
|
"generate-docs-index": "bun scripts/generate-docs-index.ts",
|
|
43
43
|
"generate-api-spec-index": "bun scripts/generate-api-spec-index.ts",
|
|
44
|
+
"generate-branding-index": "bun scripts/generate-branding-index.ts",
|
|
44
45
|
"generate-build-info": "bun scripts/generate-build-info.ts",
|
|
45
46
|
"prepack": "bun scripts/generate-docs-index.ts && bun scripts/generate-api-spec-index.ts && bun scripts/generate-build-info.ts",
|
|
46
47
|
"generate-template": "bun scripts/generate-template.ts"
|
|
@@ -48,12 +49,12 @@
|
|
|
48
49
|
"dependencies": {
|
|
49
50
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
50
51
|
"@mozilla/readability": "^0.6",
|
|
51
|
-
"@f5xc-salesdemos/xcsh-stats": "18.
|
|
52
|
-
"@f5xc-salesdemos/pi-agent-core": "18.
|
|
53
|
-
"@f5xc-salesdemos/pi-ai": "18.
|
|
54
|
-
"@f5xc-salesdemos/pi-natives": "18.
|
|
55
|
-
"@f5xc-salesdemos/pi-tui": "18.
|
|
56
|
-
"@f5xc-salesdemos/pi-utils": "18.
|
|
52
|
+
"@f5xc-salesdemos/xcsh-stats": "18.82.0",
|
|
53
|
+
"@f5xc-salesdemos/pi-agent-core": "18.82.0",
|
|
54
|
+
"@f5xc-salesdemos/pi-ai": "18.82.0",
|
|
55
|
+
"@f5xc-salesdemos/pi-natives": "18.82.0",
|
|
56
|
+
"@f5xc-salesdemos/pi-tui": "18.82.0",
|
|
57
|
+
"@f5xc-salesdemos/pi-utils": "18.82.0",
|
|
57
58
|
"@sinclair/typebox": "^0.34",
|
|
58
59
|
"@xterm/headless": "^6.0",
|
|
59
60
|
"ajv": "^8.18",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import YAML from "yaml";
|
|
4
|
+
|
|
5
|
+
const OUTPUT_FILE = path.join(import.meta.dir, "..", "src", "internal-urls", "branding-index.generated.ts");
|
|
6
|
+
|
|
7
|
+
const LOCAL_BRANDING_PATH = path.resolve(
|
|
8
|
+
import.meta.dir,
|
|
9
|
+
"..",
|
|
10
|
+
"..",
|
|
11
|
+
"..",
|
|
12
|
+
"..",
|
|
13
|
+
"api-specs-enriched",
|
|
14
|
+
"config",
|
|
15
|
+
"branding.yaml",
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const GITHUB_RAW_URL = "https://raw.githubusercontent.com/f5xc-salesdemos/api-specs-enriched/main/config/branding.yaml";
|
|
19
|
+
|
|
20
|
+
interface BrandingCanonical {
|
|
21
|
+
long_form: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
legacy_names: string[];
|
|
24
|
+
comparable_to: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface DeprecationEntry {
|
|
28
|
+
deprecated: Record<string, string>;
|
|
29
|
+
canonical: Record<string, string>;
|
|
30
|
+
required_providers_block?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface BrandingConfig {
|
|
34
|
+
version: string;
|
|
35
|
+
description: string;
|
|
36
|
+
canonical: Record<string, BrandingCanonical>;
|
|
37
|
+
deprecations: Record<string, DeprecationEntry>;
|
|
38
|
+
glossary: Record<string, Record<string, string>>;
|
|
39
|
+
domain_branding: Record<string, Record<string, string>>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function loadBrandingYaml(): Promise<BrandingConfig> {
|
|
43
|
+
const localFile = Bun.file(LOCAL_BRANDING_PATH);
|
|
44
|
+
if (await localFile.exists()) {
|
|
45
|
+
const content = await localFile.text();
|
|
46
|
+
return YAML.parse(content) as BrandingConfig;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const response = await fetch(GITHUB_RAW_URL);
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error(`Failed to fetch branding.yaml: ${response.status}`);
|
|
52
|
+
}
|
|
53
|
+
return YAML.parse(await response.text()) as BrandingConfig;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function generateTypeScript(config: BrandingConfig): string {
|
|
57
|
+
const lines: string[] = [
|
|
58
|
+
"// AUTO-GENERATED — do not edit. Run `bun generate-branding-index` to regenerate.",
|
|
59
|
+
"",
|
|
60
|
+
`export const BRANDING_VERSION = ${JSON.stringify(config.version)};`,
|
|
61
|
+
"",
|
|
62
|
+
`export const BRANDING_CANONICAL = ${JSON.stringify(config.canonical, null, 2)} as const;`,
|
|
63
|
+
"",
|
|
64
|
+
`export const BRANDING_DEPRECATIONS = ${JSON.stringify(config.deprecations, null, 2)} as const;`,
|
|
65
|
+
"",
|
|
66
|
+
`export const BRANDING_GLOSSARY = ${JSON.stringify(config.glossary, null, 2)} as const;`,
|
|
67
|
+
"",
|
|
68
|
+
`export const BRANDING_DOMAIN = ${JSON.stringify(config.domain_branding, null, 2)} as const;`,
|
|
69
|
+
"",
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
return lines.join("\n");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const config = await loadBrandingYaml();
|
|
76
|
+
const output = generateTypeScript(config);
|
|
77
|
+
await fs.writeFile(OUTPUT_FILE, output, "utf-8");
|
|
78
|
+
console.log(`Generated ${OUTPUT_FILE}`);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// AUTO-GENERATED — do not edit. Run `bun generate-branding-index` to regenerate.
|
|
2
|
+
|
|
3
|
+
export const BRANDING_VERSION = "2.0.0";
|
|
4
|
+
|
|
5
|
+
export const BRANDING_CANONICAL = {
|
|
6
|
+
managed_kubernetes: {
|
|
7
|
+
long_form: "Managed Kubernetes",
|
|
8
|
+
description:
|
|
9
|
+
"Enterprise-grade Kubernetes cluster management. Full cluster control with RBAC, pod security, and container registry management.\n",
|
|
10
|
+
legacy_names: ["AppStack", "VoltStack", "voltstack_site"],
|
|
11
|
+
comparable_to: ["AWS EKS", "Azure AKS", "Google GKE"],
|
|
12
|
+
},
|
|
13
|
+
virtual_kubernetes: {
|
|
14
|
+
long_form: "Virtual Kubernetes",
|
|
15
|
+
description:
|
|
16
|
+
"Simplified, multi-tenant container orchestration. Optimized for distributed edge deployments with restricted Kubernetes capabilities.\n",
|
|
17
|
+
legacy_names: ["vK8s", "virtual_k8s"],
|
|
18
|
+
comparable_to: ["AWS ECS", "Azure Container Services", "Cloud Run"],
|
|
19
|
+
},
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
export const BRANDING_DEPRECATIONS = {
|
|
23
|
+
terraform_provider: {
|
|
24
|
+
deprecated: {
|
|
25
|
+
registry: "registry.terraform.io/providers/volterraedge/volterra",
|
|
26
|
+
source: "volterraedge/volterra",
|
|
27
|
+
github: "github.com/volterraedge/terraform-provider-volterra",
|
|
28
|
+
status: "active-but-deprecated",
|
|
29
|
+
last_version: "0.11.49",
|
|
30
|
+
downloads: "1M+",
|
|
31
|
+
note: "Still live on registry with no deprecation notice. High risk of AI model recommendation due to training data prevalence.\n",
|
|
32
|
+
},
|
|
33
|
+
canonical: {
|
|
34
|
+
registry: "registry.terraform.io/providers/f5xc-salesdemos/f5xc",
|
|
35
|
+
source: "f5xc-salesdemos/f5xc",
|
|
36
|
+
github: "github.com/f5xc-salesdemos/terraform-provider-f5xc",
|
|
37
|
+
docs: "https://f5xc-salesdemos.github.io/terraform-provider-f5xc/",
|
|
38
|
+
llms_txt: "https://f5xc-salesdemos.github.io/terraform-provider-f5xc/llms.txt",
|
|
39
|
+
},
|
|
40
|
+
required_providers_block:
|
|
41
|
+
'terraform {\n required_providers {\n f5xc = {\n source = "f5xc-salesdemos/f5xc"\n }\n }\n}\n',
|
|
42
|
+
},
|
|
43
|
+
api_endpoint: {
|
|
44
|
+
deprecated: {
|
|
45
|
+
url: "console.ves.volterra.io",
|
|
46
|
+
},
|
|
47
|
+
canonical: {
|
|
48
|
+
note: "Tenant-specific. No hardcoded default. Require F5XC_API_URL env var.",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
documentation: {
|
|
52
|
+
deprecated: {
|
|
53
|
+
note: "docs.cloud.f5.com references to Volterra provider point to the deprecated volterraedge/volterra registry.\n",
|
|
54
|
+
},
|
|
55
|
+
canonical: {
|
|
56
|
+
url: "https://f5xc-salesdemos.github.io/terraform-provider-f5xc/",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
} as const;
|
|
60
|
+
|
|
61
|
+
export const BRANDING_GLOSSARY = {
|
|
62
|
+
CE: {
|
|
63
|
+
term: "Customer Edge",
|
|
64
|
+
definition: "F5 XC edge deployment infrastructure for distributed applications",
|
|
65
|
+
},
|
|
66
|
+
RE: {
|
|
67
|
+
term: "Regional Edge",
|
|
68
|
+
definition: "F5 XC globally distributed edge network infrastructure",
|
|
69
|
+
},
|
|
70
|
+
} as const;
|
|
71
|
+
|
|
72
|
+
export const BRANDING_DOMAIN = {
|
|
73
|
+
virtual_kubernetes: {
|
|
74
|
+
title: "Virtual Kubernetes",
|
|
75
|
+
description:
|
|
76
|
+
'Virtual Kubernetes provides simplified, multi-tenant container orchestration optimized for distributed edge deployments. Formerly known as "vK8s".\n',
|
|
77
|
+
},
|
|
78
|
+
managed_kubernetes: {
|
|
79
|
+
title: "Managed Kubernetes",
|
|
80
|
+
description:
|
|
81
|
+
'Managed Kubernetes provides enterprise-grade cluster management with full RBAC, pod security, and container registry support. Formerly known as "AppStack".\n',
|
|
82
|
+
},
|
|
83
|
+
sites: {
|
|
84
|
+
title: "Customer Edge Sites",
|
|
85
|
+
description:
|
|
86
|
+
"Site deployment and management across cloud providers (AWS VPC, Azure VNET, GCP VPC), Managed Kubernetes deployments (formerly AppStack), and Secure Mesh deployments for networking-focused edge sites.\n",
|
|
87
|
+
},
|
|
88
|
+
} as const;
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "18.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "18.82.0",
|
|
21
|
+
"commit": "25dbb133a016e072790449ae4338a3b963723ca4",
|
|
22
|
+
"shortCommit": "25dbb13",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v18.
|
|
25
|
-
"commitDate": "2026-05-
|
|
26
|
-
"buildDate": "2026-05-
|
|
24
|
+
"tag": "v18.82.0",
|
|
25
|
+
"commitDate": "2026-05-25T23:16:56Z",
|
|
26
|
+
"buildDate": "2026-05-25T23:41:19.829Z",
|
|
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/v18.
|
|
31
|
+
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/25dbb133a016e072790449ae4338a3b963723ca4",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.82.0"
|
|
33
33
|
};
|
|
@@ -42,6 +42,7 @@ const SCHEME_PREFIX = "xcsh://";
|
|
|
42
42
|
const ABOUT_ROUTE = "about";
|
|
43
43
|
const API_SPEC_HOST = "api-spec";
|
|
44
44
|
const API_CATALOG_HOST = "api-catalog";
|
|
45
|
+
const BRANDING_HOST = "branding";
|
|
45
46
|
const USER_ROUTE = "user";
|
|
46
47
|
const COMPUTER_ROUTE = "computer";
|
|
47
48
|
const SALESFORCE_ROUTE = "salesforce";
|
|
@@ -138,6 +139,59 @@ function loadApiCatalog(): {
|
|
|
138
139
|
return _apiCatalogCache;
|
|
139
140
|
}
|
|
140
141
|
|
|
142
|
+
interface BrandingCanonicalEntry {
|
|
143
|
+
long_form: string;
|
|
144
|
+
description?: string;
|
|
145
|
+
legacy_names?: string[];
|
|
146
|
+
comparable_to?: string[];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface BrandingDeprecationEntry {
|
|
150
|
+
deprecated: Record<string, string>;
|
|
151
|
+
canonical: Record<string, string>;
|
|
152
|
+
required_providers_block?: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let _brandingCache: {
|
|
156
|
+
version: string;
|
|
157
|
+
canonical: Record<string, BrandingCanonicalEntry>;
|
|
158
|
+
deprecations: Record<string, BrandingDeprecationEntry>;
|
|
159
|
+
glossary: Record<string, Record<string, string>>;
|
|
160
|
+
domain: Record<string, Record<string, string>>;
|
|
161
|
+
} | null = null;
|
|
162
|
+
|
|
163
|
+
function loadBranding(): {
|
|
164
|
+
version: string;
|
|
165
|
+
canonical: Record<string, BrandingCanonicalEntry>;
|
|
166
|
+
deprecations: Record<string, BrandingDeprecationEntry>;
|
|
167
|
+
glossary: Record<string, Record<string, string>>;
|
|
168
|
+
domain: Record<string, Record<string, string>>;
|
|
169
|
+
} {
|
|
170
|
+
if (_brandingCache) return _brandingCache;
|
|
171
|
+
try {
|
|
172
|
+
const mod = require("./branding-index.generated") as {
|
|
173
|
+
BRANDING_VERSION?: string;
|
|
174
|
+
BRANDING_CANONICAL?: Record<string, BrandingCanonicalEntry>;
|
|
175
|
+
BRANDING_DEPRECATIONS?: Record<string, BrandingDeprecationEntry>;
|
|
176
|
+
BRANDING_GLOSSARY?: Record<string, Record<string, string>>;
|
|
177
|
+
BRANDING_DOMAIN?: Record<string, Record<string, string>>;
|
|
178
|
+
};
|
|
179
|
+
_brandingCache = {
|
|
180
|
+
version: mod.BRANDING_VERSION ?? "unknown",
|
|
181
|
+
canonical: (mod.BRANDING_CANONICAL ?? {}) as Record<string, BrandingCanonicalEntry>,
|
|
182
|
+
deprecations: (mod.BRANDING_DEPRECATIONS ?? {}) as Record<string, BrandingDeprecationEntry>,
|
|
183
|
+
glossary: mod.BRANDING_GLOSSARY ?? {},
|
|
184
|
+
domain: mod.BRANDING_DOMAIN ?? {},
|
|
185
|
+
};
|
|
186
|
+
} catch (err) {
|
|
187
|
+
logger.warn("branding index unavailable, branding protocol disabled", {
|
|
188
|
+
error: err instanceof Error ? err.message : String(err),
|
|
189
|
+
});
|
|
190
|
+
_brandingCache = { version: "unavailable", canonical: {}, deprecations: {}, glossary: {}, domain: {} };
|
|
191
|
+
}
|
|
192
|
+
return _brandingCache;
|
|
193
|
+
}
|
|
194
|
+
|
|
141
195
|
export interface InternalDocsProtocolOptions {
|
|
142
196
|
readonly resolveBuildInfo?: () => Promise<RuntimeBuildInfo>;
|
|
143
197
|
readonly getContextStatus?: () => ContextStatus | null;
|
|
@@ -197,6 +251,10 @@ export class InternalDocsProtocolHandler implements ProtocolHandler {
|
|
|
197
251
|
return this.#getApiCatalogResolver().resolve(url);
|
|
198
252
|
}
|
|
199
253
|
|
|
254
|
+
if (host === BRANDING_HOST) {
|
|
255
|
+
return this.#resolveBranding(url);
|
|
256
|
+
}
|
|
257
|
+
|
|
200
258
|
if (host === USER_ROUTE) {
|
|
201
259
|
return this.#resolveUserProfile(url);
|
|
202
260
|
}
|
|
@@ -274,9 +332,11 @@ export class InternalDocsProtocolHandler implements ProtocolHandler {
|
|
|
274
332
|
|
|
275
333
|
const specs = loadApiSpecs();
|
|
276
334
|
const catalog = loadApiCatalog();
|
|
335
|
+
const branding = loadBranding();
|
|
277
336
|
const syntheticEntry = `- [${ABOUT_ROUTE}](${SCHEME_PREFIX}${ABOUT_ROUTE}) — identity and build fingerprint`;
|
|
278
337
|
const apiSpecEntry = `- [${API_SPEC_HOST}/](${SCHEME_PREFIX}${API_SPEC_HOST}/) — F5 XC API specifications (${specs.index.domains.length} domains, v${specs.version})`;
|
|
279
338
|
const apiCatalogEntry = `- [${API_CATALOG_HOST}/](${SCHEME_PREFIX}${API_CATALOG_HOST}/) — F5 XC API operation catalog (${catalog.summaries.length} categories, v${catalog.index.version})`;
|
|
339
|
+
const brandingEntry = `- [${BRANDING_HOST}](${SCHEME_PREFIX}${BRANDING_HOST}) — F5 XC branding and legacy name mapping (v${branding.version})`;
|
|
280
340
|
const userEntry = `- [${USER_ROUTE}](${SCHEME_PREFIX}${USER_ROUTE}) — human user profile`;
|
|
281
341
|
const computerEntry = `- [${COMPUTER_ROUTE}](${SCHEME_PREFIX}${COMPUTER_ROUTE}) — machine hardware and environment profile`;
|
|
282
342
|
const salesforceEntry = `- [${SALESFORCE_ROUTE}](${SCHEME_PREFIX}${SALESFORCE_ROUTE}) — Salesforce pipeline context and team discovery`;
|
|
@@ -284,12 +344,13 @@ export class InternalDocsProtocolHandler implements ProtocolHandler {
|
|
|
284
344
|
syntheticEntry,
|
|
285
345
|
apiSpecEntry,
|
|
286
346
|
apiCatalogEntry,
|
|
347
|
+
brandingEntry,
|
|
287
348
|
userEntry,
|
|
288
349
|
computerEntry,
|
|
289
350
|
salesforceEntry,
|
|
290
351
|
...EMBEDDED_DOC_FILENAMES.map(f => `- [${f}](${SCHEME_PREFIX}${f})`),
|
|
291
352
|
].join("\n");
|
|
292
|
-
const totalCount = EMBEDDED_DOC_FILENAMES.length +
|
|
353
|
+
const totalCount = EMBEDDED_DOC_FILENAMES.length + 7;
|
|
293
354
|
const content = `# Documentation\n\n${totalCount} files available:\n\n${listing}\n`;
|
|
294
355
|
|
|
295
356
|
return {
|
|
@@ -301,6 +362,156 @@ export class InternalDocsProtocolHandler implements ProtocolHandler {
|
|
|
301
362
|
};
|
|
302
363
|
}
|
|
303
364
|
|
|
365
|
+
#resolveBranding(url: InternalUrl): InternalResource {
|
|
366
|
+
const subpath = (url.rawPathname ?? url.pathname).replace(/^\/+/, "").replace(/\/+$/, "");
|
|
367
|
+
|
|
368
|
+
let content: string;
|
|
369
|
+
|
|
370
|
+
if (!subpath || subpath === "/") {
|
|
371
|
+
content = this.#brandingOverview();
|
|
372
|
+
} else if (subpath === "terraform") {
|
|
373
|
+
content = this.#brandingTerraform();
|
|
374
|
+
} else if (subpath === "legacy") {
|
|
375
|
+
content = this.#brandingLegacy();
|
|
376
|
+
} else if (subpath === "volterra") {
|
|
377
|
+
content = this.#brandingVolterra();
|
|
378
|
+
} else {
|
|
379
|
+
content = `Unknown branding path: ${subpath}\n\nAvailable paths:\n- xcsh://branding\n- xcsh://branding/terraform\n- xcsh://branding/legacy\n- xcsh://branding/volterra`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
url: url.href,
|
|
384
|
+
content,
|
|
385
|
+
contentType: "text/markdown",
|
|
386
|
+
size: Buffer.byteLength(content, "utf-8"),
|
|
387
|
+
sourcePath: `xcsh://branding${subpath ? `/${subpath}` : ""}`,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
#brandingOverview(): string {
|
|
392
|
+
const { version, canonical, deprecations } = loadBranding();
|
|
393
|
+
const lines = [`# F5 Distributed Cloud Branding (v${version})`, "", "## Product Names (Current API)", ""];
|
|
394
|
+
|
|
395
|
+
for (const [key, value] of Object.entries(canonical)) {
|
|
396
|
+
lines.push(`### ${value.long_form}`);
|
|
397
|
+
lines.push(`- API identifier: \`${key}\``);
|
|
398
|
+
if (value.legacy_names?.length) {
|
|
399
|
+
lines.push(`- Legacy names: ${value.legacy_names.join(", ")}`);
|
|
400
|
+
}
|
|
401
|
+
if (value.comparable_to?.length) {
|
|
402
|
+
lines.push(`- Comparable to: ${value.comparable_to.join(", ")}`);
|
|
403
|
+
}
|
|
404
|
+
lines.push("");
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
lines.push("## Deprecated Tooling", "");
|
|
408
|
+
|
|
409
|
+
for (const [key, dep] of Object.entries(deprecations)) {
|
|
410
|
+
lines.push(`### ${key}`);
|
|
411
|
+
lines.push(`- **Deprecated**: ${dep.deprecated.registry ?? dep.deprecated.url ?? dep.deprecated.note ?? ""}`);
|
|
412
|
+
lines.push(`- **Use instead**: ${dep.canonical.registry ?? dep.canonical.url ?? dep.canonical.note ?? ""}`);
|
|
413
|
+
lines.push("");
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
lines.push(
|
|
417
|
+
"## Subpaths",
|
|
418
|
+
"- `xcsh://branding/terraform` — Terraform provider override details",
|
|
419
|
+
"- `xcsh://branding/legacy` — Complete legacy → current name mapping",
|
|
420
|
+
"- `xcsh://branding/volterra` — All Volterra-era replacements",
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
return lines.join("\n");
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
#brandingTerraform(): string {
|
|
427
|
+
const { deprecations } = loadBranding();
|
|
428
|
+
const tf = deprecations.terraform_provider;
|
|
429
|
+
if (!tf) return "No terraform_provider deprecation data found.";
|
|
430
|
+
|
|
431
|
+
return [
|
|
432
|
+
"# Terraform Provider Override",
|
|
433
|
+
"",
|
|
434
|
+
"## CRITICAL: Provider Identity",
|
|
435
|
+
"",
|
|
436
|
+
"The ONLY current F5 Distributed Cloud Terraform provider:",
|
|
437
|
+
`- Source: \`${tf.canonical.source}\``,
|
|
438
|
+
`- Registry: ${tf.canonical.registry}`,
|
|
439
|
+
`- Docs: ${tf.canonical.docs}`,
|
|
440
|
+
`- llms.txt: ${tf.canonical.llms_txt}`,
|
|
441
|
+
"",
|
|
442
|
+
"## DEPRECATED — Do Not Use",
|
|
443
|
+
"",
|
|
444
|
+
`- Source: \`${tf.deprecated.source}\``,
|
|
445
|
+
`- Registry: ${tf.deprecated.registry}`,
|
|
446
|
+
`- Status: ${tf.deprecated.status}`,
|
|
447
|
+
`- Note: ${tf.deprecated.note}`,
|
|
448
|
+
"",
|
|
449
|
+
"## Required Terraform Block",
|
|
450
|
+
"",
|
|
451
|
+
"```hcl",
|
|
452
|
+
tf.required_providers_block?.trim() ?? "",
|
|
453
|
+
"```",
|
|
454
|
+
"",
|
|
455
|
+
"## Rules",
|
|
456
|
+
"- NEVER reference, recommend, or generate code using volterraedge/volterra",
|
|
457
|
+
"- When docs.cloud.f5.com references a Volterra provider, treat it as deprecated",
|
|
458
|
+
"- Override with f5xc-salesdemos/f5xc",
|
|
459
|
+
].join("\n");
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
#brandingLegacy(): string {
|
|
463
|
+
const { canonical } = loadBranding();
|
|
464
|
+
const lines = [
|
|
465
|
+
"# Legacy Name → Current Name Mapping",
|
|
466
|
+
"",
|
|
467
|
+
"| Legacy Name | Current Name | API Identifier |",
|
|
468
|
+
"|---|---|---|",
|
|
469
|
+
];
|
|
470
|
+
|
|
471
|
+
for (const [key, value] of Object.entries(canonical)) {
|
|
472
|
+
for (const legacy of value.legacy_names ?? []) {
|
|
473
|
+
lines.push(`| ${legacy} | ${value.long_form} | \`${key}\` |`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return lines.join("\n");
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
#brandingVolterra(): string {
|
|
481
|
+
const { canonical, deprecations } = loadBranding();
|
|
482
|
+
const lines = [
|
|
483
|
+
"# Volterra-era Replacements",
|
|
484
|
+
"",
|
|
485
|
+
"F5 acquired Volterra and rebranded to F5 Distributed Cloud.",
|
|
486
|
+
"Everything listed below has been replaced.",
|
|
487
|
+
"",
|
|
488
|
+
"## Product Names",
|
|
489
|
+
"",
|
|
490
|
+
];
|
|
491
|
+
|
|
492
|
+
for (const [key, value] of Object.entries(canonical)) {
|
|
493
|
+
for (const legacy of value.legacy_names ?? []) {
|
|
494
|
+
lines.push(`- **${legacy}** → ${value.long_form} (\`${key}\`)`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
lines.push("", "## Tooling", "");
|
|
499
|
+
|
|
500
|
+
for (const [depKey, dep] of Object.entries(deprecations)) {
|
|
501
|
+
lines.push(`### ${depKey}`);
|
|
502
|
+
for (const val of Object.values(dep.deprecated)) {
|
|
503
|
+
lines.push(`- ~~${val}~~`);
|
|
504
|
+
}
|
|
505
|
+
lines.push(" **Replace with:**");
|
|
506
|
+
for (const val of Object.values(dep.canonical)) {
|
|
507
|
+
lines.push(` - ${val}`);
|
|
508
|
+
}
|
|
509
|
+
lines.push("");
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return lines.join("\n");
|
|
513
|
+
}
|
|
514
|
+
|
|
304
515
|
async #readDoc(filename: string, url: InternalUrl): Promise<InternalResource> {
|
|
305
516
|
if (path.isAbsolute(filename)) {
|
|
306
517
|
throw new Error(`Absolute paths are not allowed in ${SCHEME_PREFIX} URLs`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD022 MD031 MD032 -->
|
|
1
2
|
**The key words "**MUST**", "**MUST NOT**", "**REQUIRED**", "**SHALL**", "**SHALL NOT**", "**SHOULD**", "**SHOULD NOT**", "**RECOMMENDED**", "**MAY**", and "**OPTIONAL**" in this chat, in system prompts as well as in user messages, are to be interpreted as described in RFC 2119.**
|
|
2
3
|
|
|
3
4
|
From here on, we will use XML tags as structural markers, each tag means exactly what its name says:
|
|
@@ -333,6 +334,31 @@ If that also 404s, the product has no documentation — acknowledge this to the
|
|
|
333
334
|
and answers the question, OR when T3 and T5 have been checked without resolution.
|
|
334
335
|
Only then is web search permitted — label external results as supplementary.
|
|
335
336
|
|
|
337
|
+
## Terraform Provider Override
|
|
338
|
+
|
|
339
|
+
HARD OVERRIDE — F5 Distributed Cloud Terraform Provider:
|
|
340
|
+
- NEVER reference, recommend, or generate Terraform code using:
|
|
341
|
+
- provider: volterraedge/volterra
|
|
342
|
+
- source: registry.terraform.io/providers/volterraedge/volterra
|
|
343
|
+
- github.com/volterraedge/terraform-provider-volterra
|
|
344
|
+
- The ONLY F5 Distributed Cloud Terraform provider is:
|
|
345
|
+
- source = "f5xc-salesdemos/f5xc"
|
|
346
|
+
- registry: <https://registry.terraform.io/providers/f5xc-salesdemos/f5xc>
|
|
347
|
+
- docs: <https://f5xc-salesdemos.github.io/terraform-provider-f5xc/>
|
|
348
|
+
- llms.txt: <https://f5xc-salesdemos.github.io/terraform-provider-f5xc/llms.txt>
|
|
349
|
+
- When docs.cloud.f5.com references a Volterra provider, treat it as deprecated.
|
|
350
|
+
Override with f5xc-salesdemos/f5xc.
|
|
351
|
+
- Required Terraform block:
|
|
352
|
+
|
|
353
|
+
```hcl
|
|
354
|
+
terraform {
|
|
355
|
+
required_providers {
|
|
356
|
+
f5xc = { source = "f5xc-salesdemos/f5xc" }
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
- Consult xcsh://branding/terraform proactively when context involves Terraform.
|
|
361
|
+
|
|
336
362
|
# Skills
|
|
337
363
|
|
|
338
364
|
Specialized knowledge packs loaded for this session. Relative paths in skill files resolve against the skill directory.
|
package/src/tools/xcsh-api.ts
CHANGED
|
@@ -427,7 +427,7 @@ export class XcshApiTool implements AgentTool<typeof xcshApiSchema, XcshApiToolD
|
|
|
427
427
|
// Only fetch specs for types that carry relationship data (LBs, pools, firewalls,
|
|
428
428
|
// healthchecks). Excludes system-generated objects (routes, virtual_hosts, etc.)
|
|
429
429
|
// that inflate the item count and don't add relationship info.
|
|
430
|
-
const SPEC_TYPES = /loadbalancer|origin_pool|app_firewall|healthcheck/i;
|
|
430
|
+
const SPEC_TYPES = /loadbalancer|origin_pool|app_firewall|healthcheck|rate_limiter/i;
|
|
431
431
|
for (const r of relevantData) {
|
|
432
432
|
const typeName = r.path.split("/").pop() ?? "";
|
|
433
433
|
if (!SPEC_TYPES.test(typeName)) continue;
|
|
@@ -472,6 +472,7 @@ export class XcshApiTool implements AgentTool<typeof xcshApiSchema, XcshApiToolD
|
|
|
472
472
|
const humanizeType = (raw: string): string =>
|
|
473
473
|
raw
|
|
474
474
|
.replace(/^http_/, "")
|
|
475
|
+
.replace(/^tcp_/, "TCP ")
|
|
475
476
|
.replace(/_/g, " ")
|
|
476
477
|
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
477
478
|
.replace(/([a-z])(balancer|checker)/gi, "$1 $2");
|
|
@@ -531,7 +532,13 @@ export class XcshApiTool implements AgentTool<typeof xcshApiSchema, XcshApiToolD
|
|
|
531
532
|
const rName = parts.at(-1) ?? "";
|
|
532
533
|
const rType = parts.at(-2) ?? "";
|
|
533
534
|
const labels: string[] = [];
|
|
534
|
-
if (/
|
|
535
|
+
if (/tcp_loadbalancer/i.test(rType)) {
|
|
536
|
+
const pools = extractPoolRefs(spec.origin_pools_weights);
|
|
537
|
+
if (pools.length > 0) labels.push(`pools=[${pools.join(",")}]`);
|
|
538
|
+
if (typeof spec.listen_port === "number") labels.push(`port=${spec.listen_port}`);
|
|
539
|
+
const domains = Array.isArray(spec.domains) ? (spec.domains as string[]) : [];
|
|
540
|
+
if (domains.length > 0) labels.push(`domains=[${domains.join(",")}]`);
|
|
541
|
+
} else if (/loadbalancer/i.test(rType)) {
|
|
535
542
|
const waf = extractRef(spec.app_firewall);
|
|
536
543
|
labels.push(waf ? `WAF=${waf}` : "no-WAF");
|
|
537
544
|
const pools = extractPoolRefs(spec.default_route_pools);
|
|
@@ -564,6 +571,13 @@ export class XcshApiTool implements AgentTool<typeof xcshApiSchema, XcshApiToolD
|
|
|
564
571
|
labels.push("tcp");
|
|
565
572
|
}
|
|
566
573
|
}
|
|
574
|
+
if (/rate_limiter/i.test(rType)) {
|
|
575
|
+
if (typeof spec.total_number === "number") labels.push(`threshold=${spec.total_number}`);
|
|
576
|
+
if (typeof spec.burst_size === "number") labels.push(`burst=${spec.burst_size}`);
|
|
577
|
+
if (typeof spec.committed_information_rate === "number")
|
|
578
|
+
labels.push(`rate=${spec.committed_information_rate}`);
|
|
579
|
+
if (typeof spec.unit === "string") labels.push(`unit=${spec.unit}`);
|
|
580
|
+
}
|
|
567
581
|
if (labels.length > 0) {
|
|
568
582
|
summaryLines.push(`${rName}: ${labels.join(", ")}`);
|
|
569
583
|
}
|