@f5-sales-demo/xcsh 19.61.1 → 19.61.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "19.61.1",
4
+ "version": "19.61.2",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -36,7 +36,7 @@
36
36
  "check:types": "bun run generate-build-info && bun run generate-extension-capabilities && bun run generate-api-spec-index && bun run generate-branding-index && bun run generate-terraform-index && tsgo -p tsconfig.json --noEmit",
37
37
  "lint": "biome lint .",
38
38
  "check:bundle": "bun scripts/check-bundle.ts",
39
- "test": "bun run generate-build-info && bun run generate-extension-capabilities && bun run generate-api-spec-index && bun run generate-console-catalog && bun run generate-console-field-metadata && bun test --max-concurrency 4",
39
+ "test": "bun run generate-build-info && bun run generate-extension-capabilities && bun run generate-api-spec-index && bun run generate-console-catalog && bun run generate-console-field-metadata && bun scripts/bun-test-guarded.ts --max-concurrency 4",
40
40
  "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
41
  "fmt": "biome format --write . && bun run format-prompts",
42
42
  "format-prompts": "bun scripts/format-prompts.ts",
@@ -56,13 +56,13 @@
56
56
  "dependencies": {
57
57
  "@agentclientprotocol/sdk": "0.16.1",
58
58
  "@mozilla/readability": "^0.6",
59
- "@f5-sales-demo/xcsh-stats": "19.61.1",
60
- "@f5-sales-demo/pi-agent-core": "19.61.1",
61
- "@f5-sales-demo/pi-ai": "19.61.1",
62
- "@f5-sales-demo/pi-natives": "19.61.1",
63
- "@f5-sales-demo/pi-resource-management": "19.61.1",
64
- "@f5-sales-demo/pi-tui": "19.61.1",
65
- "@f5-sales-demo/pi-utils": "19.61.1",
59
+ "@f5-sales-demo/xcsh-stats": "19.61.2",
60
+ "@f5-sales-demo/pi-agent-core": "19.61.2",
61
+ "@f5-sales-demo/pi-ai": "19.61.2",
62
+ "@f5-sales-demo/pi-natives": "19.61.2",
63
+ "@f5-sales-demo/pi-resource-management": "19.61.2",
64
+ "@f5-sales-demo/pi-tui": "19.61.2",
65
+ "@f5-sales-demo/pi-utils": "19.61.2",
66
66
  "@sinclair/typebox": "^0.34",
67
67
  "@xterm/headless": "^6.0",
68
68
  "ajv": "^8.20",
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Guarded `bun test` runner.
4
+ *
5
+ * Runs `bun test <args…>` and enforces one extra invariant: if the run exits 0
6
+ * it MUST have printed a "Ran N tests" summary. A test module that calls
7
+ * `process.exit(0)` terminates the whole runner with a success code BEFORE the
8
+ * summary prints, silently masking every failure that already ran (this made the
9
+ * required CI `test` job a no-op gate — issue #1903). Exit code alone can't catch
10
+ * that regression; the missing summary can.
11
+ *
12
+ * Behaviour:
13
+ * - child exits non-zero → propagate it (real failures already fail the job).
14
+ * - child exits 0 WITH a summary ("Ran N tests" / "No tests found") → pass.
15
+ * - child exits 0 WITHOUT a summary → fail (exit 1): a test aborted the runner.
16
+ *
17
+ * stderr (where bun writes results) is streamed through unchanged so CI logs are
18
+ * identical; we only tee it to scan for the summary line.
19
+ */
20
+ const args = process.argv.slice(2);
21
+
22
+ // Use the running bun (process.execPath), not a PATH lookup of "bun", so the
23
+ // guard works in any environment the parent runs in.
24
+ const proc = Bun.spawn([process.execPath, "test", ...args], {
25
+ stdout: "inherit",
26
+ stderr: "pipe",
27
+ env: process.env,
28
+ });
29
+
30
+ const SUMMARY = /Ran \d+ tests?\b|No tests found/;
31
+ const decoder = new TextDecoder();
32
+ // Keep a small rolling tail so a summary line split across two chunks is still
33
+ // matched, without buffering the entire (potentially large) stderr stream.
34
+ let tail = "";
35
+
36
+ for await (const chunk of proc.stderr as ReadableStream<Uint8Array>) {
37
+ process.stderr.write(chunk); // stream through unchanged
38
+ tail = (tail + decoder.decode(chunk, { stream: true })).slice(-4096);
39
+ }
40
+
41
+ const sawSummary = SUMMARY.test(tail);
42
+ const code = await proc.exited;
43
+
44
+ if (code !== 0) process.exit(code);
45
+
46
+ if (!sawSummary) {
47
+ process.stderr.write(
48
+ '\n\x1b[31mtest-guard: `bun test` exited 0 but printed no "Ran N tests" summary.\x1b[0m\n' +
49
+ "A test module likely called process.exit() and aborted the runner, masking failures (see #1903).\n",
50
+ );
51
+ process.exit(1);
52
+ }
53
+
54
+ process.exit(0);
@@ -228,6 +228,9 @@ export default class Worker extends Command {
228
228
  enableMCP: false,
229
229
  enableLsp: false,
230
230
  disableExtensionDiscovery: true,
231
+ // TTFT Phase 3: load the hermetic bench stand-in provider when the benchmark
232
+ // sets XCSH_BENCH_EXTENSION (absolute path). Inert for normal workers.
233
+ ...(process.env.XCSH_BENCH_EXTENSION ? { additionalExtensionPaths: [process.env.XCSH_BENCH_EXTENSION] } : {}),
231
234
  });
232
235
 
233
236
  const chatHandler = new ChatHandler(bridge, session);
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "19.61.1",
21
- "commit": "2060729b09fb7a4ede87e49a0ac0fb76725160ae",
22
- "shortCommit": "2060729",
20
+ "version": "19.61.2",
21
+ "commit": "b6923d7302a3f43d8b9600ed4bddd6e26f16fad6",
22
+ "shortCommit": "b6923d7",
23
23
  "branch": "main",
24
- "tag": "v19.61.1",
25
- "commitDate": "2026-07-07T00:26:41Z",
26
- "buildDate": "2026-07-07T01:18:58.960Z",
24
+ "tag": "v19.61.2",
25
+ "commitDate": "2026-07-07T05:10:31Z",
26
+ "buildDate": "2026-07-07T05:33:53.753Z",
27
27
  "dirty": true,
28
28
  "prNumber": "",
29
29
  "repoUrl": "https://github.com/f5-sales-demo/xcsh",
30
30
  "repoSlug": "f5-sales-demo/xcsh",
31
- "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/2060729b09fb7a4ede87e49a0ac0fb76725160ae",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.61.1"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/b6923d7302a3f43d8b9600ed4bddd6e26f16fad6",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.61.2"
33
33
  };
@@ -146,6 +146,21 @@ export const TERRAFORM_INDEX: TerraformIndex = {
146
146
  "workload_flavor",
147
147
  ],
148
148
  },
149
+ {
150
+ name: "DNS",
151
+ slug: "dns",
152
+ description: "DNS domains, zones, compliance checks, and DNS proxy configuration",
153
+ resource_count: 7,
154
+ resources: [
155
+ "dns_compliance_checks",
156
+ "dns_domain",
157
+ "dns_lb_health_check",
158
+ "dns_lb_pool",
159
+ "dns_load_balancer",
160
+ "dns_proxy",
161
+ "dns_zone",
162
+ ],
163
+ },
149
164
  {
150
165
  name: "API Security",
151
166
  slug: "api-security",
@@ -195,19 +210,12 @@ export const TERRAFORM_INDEX: TerraformIndex = {
195
210
  resource_count: 3,
196
211
  resources: ["bigip_http_proxy", "data_group", "irule"],
197
212
  },
198
- {
199
- name: "DNS",
200
- slug: "dns",
201
- description: "DNS domains, zones, compliance checks, and DNS proxy configuration",
202
- resource_count: 3,
203
- resources: ["dns_compliance_checks", "dns_domain", "dns_proxy"],
204
- },
205
213
  {
206
214
  name: "Cloud Resources",
207
215
  slug: "cloud-resources",
208
216
  description: "Cloud elastic IPs, address allocators, and geo-location resources",
209
- resource_count: 2,
210
- resources: ["address_allocator", "cloud_elastic_ip"],
217
+ resource_count: 3,
218
+ resources: ["address_allocator", "cloud_elastic_ip", "geo_location_set"],
211
219
  },
212
220
  {
213
221
  name: "Organization",
@@ -283,7 +291,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
283
291
  alert_receiver: {
284
292
  category: "monitoring",
285
293
  description: "New Alert Receiver object",
286
- required: ["name", "namespace"],
294
+ required: ["name", "namespace", "receiver_choice"],
287
295
  minimal_config:
288
296
  'resource "xcsh_alert_receiver" "example" {\n name = "example-alert-receiver"\n namespace = "staging"\n}',
289
297
  dependencies: {
@@ -316,7 +324,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
316
324
  api_discovery: {
317
325
  category: "api-security",
318
326
  description: "API discovery creates a new object in the storage backend for metadata.namespace",
319
- required: ["name", "namespace"],
327
+ required: ["name", "namespace", "user_defined_api_discovery_policy"],
320
328
  minimal_config:
321
329
  'resource "xcsh_api_discovery" "example" {\n name = "example-api-discovery"\n namespace = "staging"\n}',
322
330
  dependencies: {
@@ -480,7 +488,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
480
488
  bgp_routing_policy: {
481
489
  category: "security",
482
490
  description:
483
- "Bgp routing policy is a list of rules containing match criteria and action to be applied. these rules help control routes which are imported or exported to bgp peers. configuration",
491
+ "Bgp routing policy is a list of rules containing match criteria and action to be applied. these rules help contol routes which are imported or exported to bgp peers. configuration",
484
492
  required: ["name", "namespace"],
485
493
  minimal_config:
486
494
  'resource "xcsh_bgp_routing_policy" "example" {\n name = "example-bgp-routing-policy"\n namespace = "staging"\n}',
@@ -547,7 +555,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
547
555
  certificate: {
548
556
  category: "certificates",
549
557
  description: "Certificate. configuration",
550
- required: ["name", "namespace", "certificate_url"],
558
+ required: ["name", "namespace", "certificate_url", "private_key"],
551
559
  minimal_config:
552
560
  'resource "xcsh_certificate" "example" {\n name = "example-certificate"\n namespace = "staging"\n\n certificate_url = "example-value"\n}',
553
561
  dependencies: {
@@ -740,6 +748,39 @@ export const TERRAFORM_INDEX: TerraformIndex = {
740
748
  },
741
749
  import_syntax: "terraform import xcsh_dns_domain.example namespace/name",
742
750
  },
751
+ dns_lb_health_check: {
752
+ category: "dns",
753
+ description: "DNS Load Balancer Health Check in a given namespace. If one already exist it will give a error",
754
+ required: ["name", "namespace"],
755
+ minimal_config:
756
+ 'resource "xcsh_dns_lb_health_check" "example" {\n name = "example-dns-lb-health-check"\n namespace = "staging"\n}',
757
+ dependencies: {
758
+ requires: [],
759
+ },
760
+ import_syntax: "terraform import xcsh_dns_lb_health_check.example namespace/name",
761
+ },
762
+ dns_lb_pool: {
763
+ category: "dns",
764
+ description: "DNS Load Balancer Pool in a given namespace. If one already exist it will give a error",
765
+ required: ["name", "namespace", "load_balancing_mode"],
766
+ minimal_config:
767
+ 'resource "xcsh_dns_lb_pool" "example" {\n name = "example-dns-lb-pool"\n namespace = "staging"\n\n load_balancing_mode = "ROUND_ROBIN"\n}',
768
+ dependencies: {
769
+ requires: [],
770
+ },
771
+ import_syntax: "terraform import xcsh_dns_lb_pool.example namespace/name",
772
+ },
773
+ dns_load_balancer: {
774
+ category: "dns",
775
+ description: "DNS Load Balancer in a given namespace. If one already exist it will give a error",
776
+ required: ["name", "namespace"],
777
+ minimal_config:
778
+ 'resource "xcsh_dns_load_balancer" "example" {\n name = "example-dns-load-balancer"\n namespace = "staging"\n}',
779
+ dependencies: {
780
+ requires: [],
781
+ },
782
+ import_syntax: "terraform import xcsh_dns_load_balancer.example namespace/name",
783
+ },
743
784
  dns_proxy: {
744
785
  category: "dns",
745
786
  description: "DNS Proxy in a given namespace. If one already exists it will give an error",
@@ -751,6 +792,17 @@ export const TERRAFORM_INDEX: TerraformIndex = {
751
792
  },
752
793
  import_syntax: "terraform import xcsh_dns_proxy.example namespace/name",
753
794
  },
795
+ dns_zone: {
796
+ category: "dns",
797
+ description: "DNS Zone in a given namespace. If one already exist it will give a error",
798
+ required: ["name", "namespace"],
799
+ minimal_config:
800
+ 'resource "xcsh_dns_zone" "example" {\n name = "example-dns-zone"\n namespace = "staging"\n}',
801
+ dependencies: {
802
+ requires: [],
803
+ },
804
+ import_syntax: "terraform import xcsh_dns_zone.example namespace/name",
805
+ },
754
806
  endpoint: {
755
807
  category: "load-balancing",
756
808
  description: "Endpoint will create the object in the storage backend for namespace metadata.namespace",
@@ -869,10 +921,21 @@ export const TERRAFORM_INDEX: TerraformIndex = {
869
921
  },
870
922
  import_syntax: "terraform import xcsh_gcp_vpc_site.example namespace/name",
871
923
  },
924
+ geo_location_set: {
925
+ category: "cloud-resources",
926
+ description: "Geolocation Set",
927
+ required: ["name", "namespace"],
928
+ minimal_config:
929
+ 'resource "xcsh_geo_location_set" "example" {\n name = "example-geo-location-set"\n namespace = "staging"\n}',
930
+ dependencies: {
931
+ requires: [],
932
+ },
933
+ import_syntax: "terraform import xcsh_geo_location_set.example namespace/name",
934
+ },
872
935
  global_log_receiver: {
873
936
  category: "monitoring",
874
937
  description: "New Global Log Receiver object",
875
- required: ["name", "namespace"],
938
+ required: ["name", "namespace", "log_type", "receiver_choice"],
876
939
  minimal_config:
877
940
  'resource "xcsh_global_log_receiver" "example" {\n name = "example-global-log-receiver"\n namespace = "staging"\n}',
878
941
  dependencies: {
@@ -884,8 +947,8 @@ export const TERRAFORM_INDEX: TerraformIndex = {
884
947
  category: "load-balancing",
885
948
  description:
886
949
  "Healthcheck object defines method to determine if the given endpoint is healthy. single healthcheck object can be referred to by one or many cluster objects. configuration",
887
- required: ["name", "namespace", "healthy_threshold", "interval", "timeout", "unhealthy_threshold"],
888
- server_defaults: ["jitter_percent"],
950
+ required: ["name", "namespace", "interval", "timeout", "healthy_threshold", "unhealthy_threshold"],
951
+ server_defaults: ["jitter_percent", "use_http2"],
889
952
  minimal_config:
890
953
  'resource "xcsh_healthcheck" "example" {\n name = "example-healthcheck"\n namespace = "staging"\n\n healthy_threshold = 1\n interval = 1\n timeout = 1\n unhealthy_threshold = 1\n}',
891
954
  dependencies: {
@@ -897,7 +960,8 @@ export const TERRAFORM_INDEX: TerraformIndex = {
897
960
  http_loadbalancer: {
898
961
  category: "load-balancing",
899
962
  description: "Load balancing HTTP/HTTPS traffic with advanced routing and security",
900
- required: ["name", "namespace"],
963
+ required: ["name", "namespace", "domains"],
964
+ server_defaults: ["connection_timeout", "http_idle_timeout"],
901
965
  minimal_config:
902
966
  'resource "xcsh_http_loadbalancer" "example" {\n name = "example-http-loadbalancer"\n namespace = "staging"\n\n domains = ["example-value"]\n}',
903
967
  dependencies: {
@@ -1177,7 +1241,8 @@ export const TERRAFORM_INDEX: TerraformIndex = {
1177
1241
  origin_pool: {
1178
1242
  category: "load-balancing",
1179
1243
  description: "Defining backend server pools for load balancer targets",
1180
- required: ["name", "namespace"],
1244
+ required: ["name", "namespace", "origin_servers", "port"],
1245
+ server_defaults: ["connection_timeout", "http_idle_timeout"],
1181
1246
  minimal_config:
1182
1247
  'resource "xcsh_origin_pool" "example" {\n name = "example-origin-pool"\n namespace = "staging"\n}',
1183
1248
  dependencies: {
@@ -1212,7 +1277,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
1212
1277
  category: "security",
1213
1278
  description:
1214
1279
  "Protocol Inspection Specification in a given namespace. If one already exists it will give an error",
1215
- required: ["name", "namespace"],
1280
+ required: ["name", "namespace", "enable_disable_compliance_checks", "enable_disable_signatures"],
1216
1281
  minimal_config:
1217
1282
  'resource "xcsh_protocol_inspection" "example" {\n name = "example-protocol-inspection"\n namespace = "staging"\n}',
1218
1283
  dependencies: {
@@ -1257,7 +1322,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
1257
1322
  rate_limiter_policy: {
1258
1323
  category: "security",
1259
1324
  description: "Rate limiter policy create specification. configuration",
1260
- required: ["name", "namespace"],
1325
+ required: ["name", "namespace", "burst_size", "committed_information_rate"],
1261
1326
  minimal_config:
1262
1327
  'resource "xcsh_rate_limiter_policy" "example" {\n name = "example-rate-limiter-policy"\n namespace = "staging"\n}',
1263
1328
  dependencies: {
@@ -1407,7 +1472,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
1407
1472
  tcp_loadbalancer: {
1408
1473
  category: "load-balancing",
1409
1474
  description: "Load balancing TCP traffic across origin pools",
1410
- required: ["name", "namespace"],
1475
+ required: ["name", "namespace", "origin_pools"],
1411
1476
  minimal_config:
1412
1477
  'resource "xcsh_tcp_loadbalancer" "example" {\n name = "example-tcp-loadbalancer"\n namespace = "staging"\n}',
1413
1478
  dependencies: {
@@ -1473,7 +1538,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
1473
1538
  user_identification: {
1474
1539
  category: "security",
1475
1540
  description: "User_identification creates a new object in the storage backend for metadata.namespace",
1476
- required: ["name", "namespace"],
1541
+ required: ["name", "namespace", "rules"],
1477
1542
  minimal_config:
1478
1543
  'resource "xcsh_user_identification" "example" {\n name = "example-user-identification"\n namespace = "staging"\n}',
1479
1544
  dependencies: {
@@ -1527,7 +1592,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
1527
1592
  virtual_site: {
1528
1593
  category: "sites",
1529
1594
  description: "Virtual site object in given namespace",
1530
- required: ["name", "namespace", "site_type"],
1595
+ required: ["name", "namespace", "site_type", "site_selector"],
1531
1596
  minimal_config:
1532
1597
  'resource "xcsh_virtual_site" "example" {\n name = "example-virtual-site"\n namespace = "staging"\n\n site_type = "INVALID"\n}',
1533
1598
  dependencies: {
@@ -1549,7 +1614,7 @@ export const TERRAFORM_INDEX: TerraformIndex = {
1549
1614
  waf_exclusion_policy: {
1550
1615
  category: "security",
1551
1616
  description: "WAF exclusion policy",
1552
- required: ["name", "namespace"],
1617
+ required: ["name", "namespace", "waf_exclusion_rules"],
1553
1618
  minimal_config:
1554
1619
  'resource "xcsh_waf_exclusion_policy" "example" {\n name = "example-waf-exclusion-policy"\n namespace = "staging"\n}',
1555
1620
  dependencies: {
@@ -327,6 +327,11 @@ export async function resolvePromptInput(input: string | undefined, description:
327
327
  export interface LoadContextFilesOptions {
328
328
  /** Working directory to start walking up from. Default: getProjectDir() */
329
329
  cwd?: string;
330
+ /**
331
+ * Explicit disabled extension IDs to apply instead of the global settings default.
332
+ * Pass `[]` to force discovery independent of any process-wide settings state.
333
+ */
334
+ disabledExtensions?: string[];
330
335
  }
331
336
 
332
337
  function dedupeExactContextFiles(
@@ -351,7 +356,10 @@ export async function loadProjectContextFiles(
351
356
  ): Promise<Array<{ path: string; content: string; depth?: number }>> {
352
357
  const resolvedCwd = options.cwd ?? getProjectDir();
353
358
 
354
- const result = await loadCapability(contextFileCapability.id, { cwd: resolvedCwd });
359
+ const result = await loadCapability(contextFileCapability.id, {
360
+ cwd: resolvedCwd,
361
+ disabledExtensions: options.disabledExtensions,
362
+ });
355
363
 
356
364
  // Convert ContextFile items and preserve depth info
357
365
  const files = result.items.map(item => {
@@ -436,6 +444,11 @@ export interface BuildSystemPromptOptions {
436
444
  cwd?: string;
437
445
  /** Pre-loaded context files (skips discovery if provided). */
438
446
  contextFiles?: Array<{ path: string; content: string; depth?: number }>;
447
+ /**
448
+ * Explicit disabled extension IDs applied to context-file discovery instead of the
449
+ * global settings default. Pass `[]` to discover independent of process-wide settings.
450
+ */
451
+ disabledExtensions?: string[];
439
452
  /** Skills provided directly to system prompt construction. */
440
453
  skills?: Skill[];
441
454
  /** Pre-loaded rulebook rules (descriptions, excluding TTSR and always-apply). */
@@ -519,7 +532,10 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
519
532
  });
520
533
  const contextFilesPromise = providedContextFiles
521
534
  ? Promise.resolve(providedContextFiles)
522
- : logger.time("loadProjectContextFiles", loadProjectContextFiles, { cwd: resolvedCwd });
535
+ : logger.time("loadProjectContextFiles", loadProjectContextFiles, {
536
+ cwd: resolvedCwd,
537
+ disabledExtensions: options.disabledExtensions,
538
+ });
523
539
  const agentsMdSearchPromise = logger.time("buildAgentsMdSearch", buildAgentsMdSearch, resolvedCwd);
524
540
  const mergedSkillsSettings = {
525
541
  ...skillsSettings,