@f5-sales-demo/xcsh 19.105.4 → 19.105.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +154 -28
  2. package/package.json +8 -8
  3. package/scripts/generate-api-spec-index.ts +7 -22
  4. package/scripts/generate-console-catalog.ts +2 -1
  5. package/scripts/sanitize-generated-content.ts +141 -0
  6. package/src/cli.ts +0 -3
  7. package/src/config/auto-config.ts +12 -1
  8. package/src/discovery/agents-md.ts +2 -2
  9. package/src/discovery/agents.ts +7 -7
  10. package/src/discovery/builtin.ts +14 -14
  11. package/src/discovery/claude-plugins.ts +6 -6
  12. package/src/discovery/claude.ts +10 -10
  13. package/src/discovery/cline.ts +2 -2
  14. package/src/discovery/codex.ts +10 -10
  15. package/src/discovery/cursor.ts +4 -4
  16. package/src/discovery/gemini.ts +7 -7
  17. package/src/discovery/github.ts +3 -3
  18. package/src/discovery/mcp-json.ts +2 -2
  19. package/src/discovery/opencode.ts +7 -7
  20. package/src/discovery/ssh.ts +2 -2
  21. package/src/discovery/vscode.ts +2 -2
  22. package/src/discovery/windsurf.ts +3 -3
  23. package/src/extensibility/extensions/loader.ts +0 -8
  24. package/src/extensibility/extensions/types.ts +0 -4
  25. package/src/internal-urls/build-info-runtime.ts +0 -1
  26. package/src/internal-urls/build-info.generated.ts +8 -8
  27. package/src/internal-urls/console-catalog.generated.ts +1 -1
  28. package/src/internal-urls/index.ts +0 -3
  29. package/src/internal-urls/terraform-resolve.ts +1 -1
  30. package/src/internal-urls/xcsh-protocol.ts +0 -73
  31. package/src/modes/interactive-mode.ts +0 -8
  32. package/src/prompts/system/system-prompt.md +63 -26
  33. package/src/prompts/tools/xcsh-api.md +114 -60
  34. package/src/sandbox/containment.ts +3 -6
  35. package/src/sdk.ts +0 -47
  36. package/src/session/compaction/compaction.ts +1 -1
  37. package/src/system-prompt.ts +0 -21
  38. package/src/discovery/language.ts +0 -46
  39. package/src/internal-urls/computer-profile.ts +0 -692
  40. package/src/internal-urls/profile-collectors.ts +0 -371
  41. package/src/internal-urls/user-profile.ts +0 -431
package/CHANGELOG.md CHANGED
@@ -2,8 +2,28 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [19.105.6] - 2026-07-31
6
+
7
+ ### Breaking Changes
8
+
9
+ - Removed the `registerProfileCollector()` extension API and the `xcsh://user` and `xcsh://computer` routes. Extensions must no longer register workstation or user identity collectors. ([#2674](https://github.com/f5-sales-demo/xcsh/issues/2674))
10
+
11
+ ### Removed
12
+
13
+ - Removed automatic collection, persistence, and prompt injection of user and workstation profile data, including names, contact details, employer and manager information, addresses, social identifiers, hostnames, and endpoint security inventory. ([#2674](https://github.com/f5-sales-demo/xcsh/issues/2674))
14
+
5
15
  ### Fixed
6
16
 
17
+ - Fixed the checksum-pinned Zig installer on macOS and Windows native release runners and added cross-platform pre-merge validation ([#61](https://github.com/f5-sales-demo/xcsh/issues/61))
18
+
19
+ ## [19.105.5] - 2026-07-31
20
+
21
+ ### Fixed
22
+
23
+ - Added post-publication Homebrew install verification to the release workflow ([#2719](https://github.com/f5-sales-demo/xcsh/issues/2719))
24
+ - Replaced the deprecated Node 20 Zig setup action with a checksum-pinned official installer ([#61](https://github.com/f5-sales-demo/xcsh/issues/61))
25
+ - Invalidated stale release PRs immediately on new `main` pushes, before the full release-creation gate ([#2732](https://github.com/f5-sales-demo/xcsh/issues/2732))
26
+ - Preserved GPT-5.6 Sol High reasoning and token-limit metadata through generated LiteLLM model discovery ([#2729](https://github.com/f5-sales-demo/xcsh/issues/2729))
7
27
  - Regenerated stale same-version release PRs when newer commits have merged into `main` ([#2727](https://github.com/f5-sales-demo/xcsh/issues/2727))
8
28
 
9
29
  ## [19.105.3] - 2026-07-31
@@ -33,16 +53,23 @@
33
53
  ## [19.51.5] - 2026-06-26
34
54
 
35
55
  ### Fixed
36
- - Sign + notarize the embedded native addon (`pi_natives.*.node`) in `build-sign-macos` **before** it is embedded into the compiled binary. xcsh extracts the embedded `.node` to `~/.xcsh/natives/<ver>/` at runtime; previously that extracted copy was ad-hoc-signed, so managed/MDM Macs blocked the `dlopen` ("could not verify it is free of malware"). The addon is now Developer-ID-signed with hardened runtime and notarized, so Gatekeeper's online check passes it without manual approval.
56
+
57
+ - Sign + notarize the embedded native addon (`pi_natives.*.node`) in `build-sign-macos` **before** it is embedded into
58
+ the compiled binary. xcsh extracts the embedded `.node` to `~/.xcsh/natives/<ver>/` at runtime; previously that
59
+ extracted copy was ad-hoc-signed, so managed/MDM Macs blocked the `dlopen` ("could not verify it is free of
60
+ malware"). The addon is now Developer-ID-signed with hardened runtime and notarized, so Gatekeeper's online check
61
+ passes it without manual approval.
37
62
 
38
63
  ## [19.51.4] - 2026-06-26
39
64
 
40
65
  ### Added
66
+
41
67
  - Deterministic NL→XC console automation: the system prompt mandates the purpose-built `catalog_workflow_runner` console skill (extension-driven) and forbids the generic Puppeteer browser tool for F5 XC console pages; visible Chrome is framed as the flagship showcase, headless as the exception.
42
68
  - Native-messaging host auto-install on console-automation init (idempotent), with a baked-in Web Store URL and fail-fast guidance when the extension is not connected.
43
69
  - Gated, security-clean relay tracing in `chrome-host` for diagnosing the bridge-connect step.
44
70
 
45
71
  ### Fixed
72
+
46
73
  - Native host now uses the compiled `xcsh` binary (not `bun + cli.ts`), which survives Chrome's stripped-env launch.
47
74
  - Re-embedded console catalog/field-metadata with the `urn:xcsh:console:workflow:v1` schema and the tcp-load-balancer / network-interface defect fixes plus label corrections.
48
75
  - Pre-existing biome lint/format issues that were blocking CI.
@@ -67,7 +94,10 @@
67
94
 
68
95
  ### Changed
69
96
 
70
- - Salesforce tools extracted to marketplace plugin: sf_setup, sf_query, sf_org_display, and sf_pipeline_report are now available as an installable plugin (`@f5-sales-demo/xcsh-salesforce`) via the Extension API instead of built-in tools. Install with `xcsh plugin install salesforce`. Context discovery, pipeline reporting, and container-adapted authentication are preserved with full feature parity. ([#1059](https://github.com/f5-sales-demo/xcsh/issues/1059))
97
+ - Salesforce tools extracted to marketplace plugin: sf_setup, sf_query, sf_org_display, and sf_pipeline_report are now
98
+ available as an installable plugin (`@f5-sales-demo/xcsh-salesforce`) via the Extension API instead of built-in
99
+ tools. Install with `xcsh plugin install salesforce`. Context discovery, pipeline reporting, and container-adapted
100
+ authentication are preserved with full feature parity. ([#1059](https://github.com/f5-sales-demo/xcsh/issues/1059))
71
101
 
72
102
  ## [18.75.0] - 2026-05-23
73
103
 
@@ -79,7 +109,11 @@
79
109
 
80
110
  ### Fixed
81
111
 
82
- - Welcome banner cloud provider hints: AWS SSO token expiry now correctly suggests `aws sso login` instead of `aws configure`; Google Cloud check replaced `gcloud auth list` (false positives on expired tokens) with `gcloud auth print-access-token`; F5 XC Context surfaces `errorClass` (network/URL) in hints; GitLab `project_inaccessible` gets its own hint; Salesforce differentiates `session_expired` and `not_configured` hints ([#825](https://github.com/f5-sales-demo/xcsh/issues/825))
112
+ - Welcome banner cloud provider hints: AWS SSO token expiry now correctly suggests `aws sso login` instead of `aws
113
+ configure`; Google Cloud check replaced `gcloud auth list` (false positives on expired tokens) with `gcloud auth
114
+ print-access-token`; F5 XC Context surfaces `errorClass` (network/URL) in hints; GitLab `project_inaccessible` gets
115
+ its own hint; Salesforce differentiates `session_expired` and `not_configured` hints
116
+ ([#825](https://github.com/f5-sales-demo/xcsh/issues/825))
83
117
 
84
118
  ## [18.64.0] - 2026-05-13
85
119
 
@@ -92,7 +126,11 @@
92
126
 
93
127
  ### Changed
94
128
 
95
- - Regenerated API spec index from catalog v2.1.82: http_loadbalancer CRUD verification corrections (6 new server defaults, corrected minimum configs, cross-field dependencies, default_pool inline pool discovery, 5 composable routing approaches) and tcp_loadbalancer minimum config corrections (listen_port, origin_pools_weights, do_not_advertise format, 9 server defaults, forced hash_policy default) ([#753](https://github.com/f5-sales-demo/xcsh/issues/753), [#757](https://github.com/f5-sales-demo/xcsh/issues/757))
129
+ - Regenerated API spec index from catalog v2.1.82: http_loadbalancer CRUD verification corrections (6 new server
130
+ defaults, corrected minimum configs, cross-field dependencies, default_pool inline pool discovery, 5 composable
131
+ routing approaches) and tcp_loadbalancer minimum config corrections (listen_port, origin_pools_weights,
132
+ do_not_advertise format, 9 server defaults, forced hash_policy default)
133
+ ([#753](https://github.com/f5-sales-demo/xcsh/issues/753), [#757](https://github.com/f5-sales-demo/xcsh/issues/757))
96
134
 
97
135
  ## [18.53.0] - 2026-05-09
98
136
 
@@ -156,25 +194,40 @@
156
194
 
157
195
  ### Fixed
158
196
 
159
- - Fixed gutter width propagation in the fallback tool renderer: `#formatToolExecution()` now receives the actual available width at render-time and uses it for line truncation instead of a hardcoded 80-column limit. On narrow terminals (<82 cols) this prevents content wider than the gutter-adjusted viewport; on wide terminals it allows longer output lines. ([#117](https://github.com/f5-sales-demo/xcsh/issues/117))
197
+ - Fixed gutter width propagation in the fallback tool renderer: `#formatToolExecution()` now receives the actual
198
+ available width at render-time and uses it for line truncation instead of a hardcoded 80-column limit. On narrow
199
+ terminals (<82 cols) this prevents content wider than the gutter-adjusted viewport; on wide terminals it allows
200
+ longer output lines. ([#117](https://github.com/f5-sales-demo/xcsh/issues/117))
160
201
  - Fixed `resolveConfigValue` returning literal env var names (e.g. `"LITELLM_API_KEY"`) as API keys when the env var is unset, causing 401 errors on first launch. The resolver now rejects unresolved `ALL_CAPS_WITH_UNDERSCORES` patterns, matching the existing guard in `resolveYamlApiKeyConfig`. ([#241](https://github.com/f5-sales-demo/xcsh/issues/241))
161
202
 
162
203
  ### Changed
163
204
 
164
- - Renamed F5 XC credential system from "profile" to "context" to align with kubectl conventions. The `/profile` command is now `/context`, all types/classes use `Context*` naming (`ContextService`, `ContextStatus`, `F5XCContext`, etc.), on-disk paths changed from `profiles/` to `contexts/` and `active_profile` to `active_context`, and the status-line segment ID is now `context_f5xc`. ([#302](https://github.com/f5-sales-demo/xcsh/issues/302))
205
+ - Renamed F5 XC credential system from "profile" to "context" to align with kubectl conventions. The `/profile` command
206
+ is now `/context`, all types/classes use `Context*` naming (`ContextService`, `ContextStatus`, `F5XCContext`, etc.),
207
+ on-disk paths changed from `profiles/` to `contexts/` and `active_profile` to `active_context`, and the status-line
208
+ segment ID is now `context_f5xc`. ([#302](https://github.com/f5-sales-demo/xcsh/issues/302))
165
209
 
166
210
  ## [18.12.0] - 2026-04-23
167
211
 
168
212
  ### Changed
169
213
 
170
- - **System prompt realigned around the SE coworker primary mission.** The `<role>` block now leads with "technical coworker for F5 Distributed Cloud sales engineers" and names demos, MEDDPICC qualification, customer meeting preparation, and F5 XC product subject-matter expertise as primary activities — technical depth is retained as an explicit enabling credential, not subordinated. `<behavior>` reframes around the **presentation reflex** (confirming a capability before verifying it against current docs) with a conditional clause preserving the **deployment reflex** guard when the task is infrastructure. `<stakes>` leads with customer-facing risk (lost deals, damaged credibility, post-sale trust erosion) and retains infra stakes (outages, security exposures) as a secondary clause.
214
+ - **System prompt realigned around the SE coworker primary mission.** The `<role>` block now leads with "technical
215
+ coworker for F5 Distributed Cloud sales engineers" and names demos, MEDDPICC qualification, customer meeting
216
+ preparation, and F5 XC product subject-matter expertise as primary activities — technical depth is retained as an
217
+ explicit enabling credential, not subordinated. `<behavior>` reframes around the **presentation reflex** (confirming
218
+ a capability before verifying it against current docs) with a conditional clause preserving the **deployment reflex**
219
+ guard when the task is infrastructure. `<stakes>` leads with customer-facing risk (lost deals, damaged credibility,
220
+ post-sale trust erosion) and retains infra stakes (outages, security exposures) as a secondary clause.
171
221
  - **Identity section moved above Workspace in the rendered prompt.** Static behavioral anchors now occupy the highest-attention position at the top of the prompt, and dynamic context-file changes no longer invalidate the cache for static behavioral content.
172
222
  - **Replaced the sea-color epistemic-integrity example** with a bot-defense-SKU vs contract-claim scenario. The three examples now cover SE domain (product claim vs contract reality), infra domain (LB health-check probe layer), and technical domain (mutex race) — diverse and domain-grounded.
173
223
  - **Compressed the `xcsh://` routing instruction** from 3 lines to 2 lines. The redundant trigger-phrase enumeration is removed; the MUST directive is sufficient now that `xcsh://about` also documents the architecture.
174
224
 
175
225
  ### Added
176
226
 
177
- - **`.xcsh/rules/config-integrity.md`** — new scoped rule carrying the dependency-first IaC guidance that previously lived in the always-on `<config-integrity>` block. Fires on `tool:edit` and `tool:write` for `.tf`, `.yaml`, `.yml`, `.json`, `.sh`, `Makefile`, `Dockerfile`, `.toml`. Uses `condition: "."` (always-true regex) so the rules engine registers it — rules without a `condition` are silently dropped.
227
+ - **`.xcsh/rules/config-integrity.md`** — new scoped rule carrying the dependency-first IaC guidance that previously
228
+ lived in the always-on `<config-integrity>` block. Fires on `tool:edit` and `tool:write` for `.tf`, `.yaml`, `.yml`,
229
+ `.json`, `.sh`, `Makefile`, `Dockerfile`, `.toml`. Uses `condition: "."` (always-true regex) so the rules engine
230
+ registers it — rules without a `condition` are silently dropped.
178
231
  - **`xcsh://about` enriched** with `## Lineage` (upstream fork + maintainer), `## Architecture` (package role map), and `## Capabilities` (feature surface summary). On-demand only; zero system-prompt token cost.
179
232
 
180
233
  ### Removed
@@ -187,12 +240,24 @@ Refs: #254, #243.
187
240
 
188
241
  ### Removed
189
242
 
190
- - **All built-in themes except `xcsh-dark` and `xcsh-light` have been removed.** The 99 community themes previously shipped under `packages/coding-agent/src/modes/theme/defaults/` (`dark-ocean`, `dark-dracula`, `titanium`, `anthracite`, etc.), as well as the base `dark` and `light` themes, are no longer included. The hardcoded fallback when a user's theme fails to load now targets `xcsh-dark`. Users on a removed theme will fall back to `xcsh-dark` on next launch. User-authored themes under `~/.xcsh/agent/themes/` are unaffected.
243
+ - **All built-in themes except `xcsh-dark` and `xcsh-light` have been removed.** The 99 community themes previously
244
+ shipped under `packages/coding-agent/src/modes/theme/defaults/` (`dark-ocean`, `dark-dracula`, `titanium`,
245
+ `anthracite`, etc.), as well as the base `dark` and `light` themes, are no longer included. The hardcoded fallback
246
+ when a user's theme fails to load now targets `xcsh-dark`. Users on a removed theme will fall back to `xcsh-dark` on
247
+ next launch. User-authored themes under `~/.xcsh/agent/themes/` are unaffected.
191
248
 
192
249
  ### Changed
193
250
 
194
- - **TUI tool-call outcomes now render as a single colored gutter ball (●).** Inline `✓/✗/⚠` status icons were removed from every tool renderer. A new "warning" outcome (orange ball) signals non-error degraded states such as grep with zero matches, exa with zero results, ast-grep with zero matches, or ask falling back to a headless/empty-questions path. `xcsh-dark` uses `warmAmber` (#ffb347); `xcsh-light` uses `#b85e00` (WCAG AA on white). User-authored themes inherit via the `gutterWarning → warning` fallback chain.
195
- - **`AgentToolResult` and `tool_execution_end` events gain an optional `isWarning?: boolean`** alongside `isError`. Tools that want an orange gutter ball on zero-result or fallback paths set this field (or use the new `ToolResultBuilder.isWarning()` fluent method). Extensions subscribing to `tool_execution_end` and `tool_result` hook events now receive the flag; existing extensions are unaffected. Backwards-compatible unset means "not a warning."
251
+ - **TUI tool-call outcomes now render as a single colored gutter ball (●).** Inline `✓/✗/⚠` status icons were
252
+ removed from every tool renderer. A new "warning" outcome (orange ball) signals non-error degraded states such as
253
+ grep with zero matches, exa with zero results, ast-grep with zero matches, or ask falling back to a
254
+ headless/empty-questions path. `xcsh-dark` uses `warmAmber` (#ffb347); `xcsh-light` uses `#b85e00` (WCAG AA on
255
+ white). User-authored themes inherit via the `gutterWarning → warning` fallback chain.
256
+ - **`AgentToolResult` and `tool_execution_end` events gain an optional `isWarning?: boolean`** alongside `isError`.
257
+ Tools that want an orange gutter ball on zero-result or fallback paths set this field (or use the new
258
+ `ToolResultBuilder.isWarning()` fluent method). Extensions subscribing to `tool_execution_end` and `tool_result` hook
259
+ events now receive the flag; existing extensions are unaffected. Backwards-compatible — unset means "not a
260
+ warning."
196
261
  - **`formatEmptyMessage` and `formatErrorMessage` helpers no longer prepend ⚠/✗ glyphs.** Centralized fix so every tool renderer inherits the glyph-free output without a per-file workaround.
197
262
 
198
263
  Refs: #173.
@@ -201,7 +266,10 @@ Refs: #173.
201
266
 
202
267
  ### Fixed
203
268
 
204
- - Widened the `defaultThinkingLevel` schema enum to include `"off"`, which was already a supported runtime value via `resolveThinkingLevelForModel` but was not declared as a valid schema value. This closes a type-safety gap where programmatic callers (tests, config.yml edits) could set `"off"` without a `SettingValue` type narrowing it. The `/settings` UI dropdown is unchanged; the widening is schema-level only.
269
+ - Widened the `defaultThinkingLevel` schema enum to include `"off"`, which was already a supported runtime value via
270
+ `resolveThinkingLevelForModel` but was not declared as a valid schema value. This closes a type-safety gap where
271
+ programmatic callers (tests, config.yml edits) could set `"off"` without a `SettingValue` type narrowing it. The
272
+ `/settings` UI dropdown is unchanged; the widening is schema-level only.
205
273
 
206
274
  ### Changed
207
275
 
@@ -3892,7 +3960,10 @@ Refs: #173.
3892
3960
 
3893
3961
  ### Fixed
3894
3962
 
3895
- - External edits to `config.yml` are now preserved when omp reloads or saves unrelated settings. Previously, editing config.yml directly (e.g., removing a package from `packages` array) would be silently reverted on next omp startup when automatic setters like `setLastChangelogVersion()` triggered a save. ([#1046](https://github.com/badlogic/pi-mono/pull/1046) by [@nicobailonMD](https://github.com/nicobailonMD))
3963
+ - External edits to `config.yml` are now preserved when omp reloads or saves unrelated settings. Previously, editing
3964
+ config.yml directly (e.g., removing a package from `packages` array) would be silently reverted on next omp startup
3965
+ when automatic setters like `setLastChangelogVersion()` triggered a save.
3966
+ ([#1046](https://github.com/badlogic/pi-mono/pull/1046) by [@nicobailonMD](https://github.com/nicobailonMD))
3896
3967
 
3897
3968
  ## [8.13.0] - 2026-01-29
3898
3969
 
@@ -5042,7 +5113,13 @@ Refs: #173.
5042
5113
  ### Added
5043
5114
 
5044
5115
  - Exported `InteractiveModeOptions` type for programmatic SDK usage
5045
- - Exported additional UI components for extensions: `ArminComponent`, `AssistantMessageComponent`, `BashExecutionComponent`, `BranchSummaryMessageComponent`, `CompactionSummaryMessageComponent`, `CustomEditor`, `CustomMessageComponent`, `FooterComponent`, `ExtensionEditorComponent`, `ExtensionInputComponent`, `ExtensionSelectorComponent`, `LoginDialogComponent`, `ModelSelectorComponent`, `OAuthSelectorComponent`, `SessionSelectorComponent`, `SettingsSelectorComponent`, `ShowImagesSelectorComponent`, `ThemeSelectorComponent`, `ThinkingSelectorComponent`, `ToolExecutionComponent`, `TreeSelectorComponent`, `UserMessageComponent`, `UserMessageSelectorComponent`
5116
+ - Exported additional UI components for extensions: `ArminComponent`, `AssistantMessageComponent`,
5117
+ `BashExecutionComponent`, `BranchSummaryMessageComponent`, `CompactionSummaryMessageComponent`, `CustomEditor`,
5118
+ `CustomMessageComponent`, `FooterComponent`, `ExtensionEditorComponent`, `ExtensionInputComponent`,
5119
+ `ExtensionSelectorComponent`, `LoginDialogComponent`, `ModelSelectorComponent`, `OAuthSelectorComponent`,
5120
+ `SessionSelectorComponent`, `SettingsSelectorComponent`, `ShowImagesSelectorComponent`, `ThemeSelectorComponent`,
5121
+ `ThinkingSelectorComponent`, `ToolExecutionComponent`, `TreeSelectorComponent`, `UserMessageComponent`,
5122
+ `UserMessageSelectorComponent`
5046
5123
  - Exported `renderDiff`, `truncateToVisualLines`, and related types for extension use
5047
5124
  - `setFooter()` and `setHeader()` methods on `ExtensionUIContext` for custom footer/header components
5048
5125
  - `setEditorComponent()` method on `ExtensionUIContext` for custom editor components
@@ -5489,7 +5566,12 @@ Refs: #173.
5489
5566
 
5490
5567
  ### Added
5491
5568
 
5492
- - Added 65 new built-in color themes including dark variants (abyss, aurora, cavern, copper, cosmos, eclipse, ember, equinox, lavender, lunar, midnight, nebula, rainforest, reef, sakura, slate, solstice, starfall, swamp, taiga, terminal, tundra, twilight, volcanic), light variants (aurora-day, canyon, cirrus, coral, dawn, dunes, eucalyptus, frost, glacier, haze, honeycomb, lagoon, lavender, meadow, mint, opal, orchard, paper, prism, sand, savanna, soleil, wetland, zenith), and material themes (alabaster, amethyst, anthracite, basalt, birch, graphite, limestone, mahogany, marble, obsidian, onyx, pearl, porcelain, quartz, sandstone, titanium)
5569
+ - Added 65 new built-in color themes including dark variants (abyss, aurora, cavern, copper, cosmos, eclipse, ember,
5570
+ equinox, lavender, lunar, midnight, nebula, rainforest, reef, sakura, slate, solstice, starfall, swamp, taiga,
5571
+ terminal, tundra, twilight, volcanic), light variants (aurora-day, canyon, cirrus, coral, dawn, dunes, eucalyptus,
5572
+ frost, glacier, haze, honeycomb, lagoon, lavender, meadow, mint, opal, orchard, paper, prism, sand, savanna, soleil,
5573
+ wetland, zenith), and material themes (alabaster, amethyst, anthracite, basalt, birch, graphite, limestone, mahogany,
5574
+ marble, obsidian, onyx, pearl, porcelain, quartz, sandstone, titanium)
5493
5575
 
5494
5576
  ### Fixed
5495
5577
 
@@ -6324,7 +6406,11 @@ Total color count increased from 46 to 50. See [docs/theme.md](docs/theme.md) fo
6324
6406
 
6325
6407
  ### Breaking
6326
6408
 
6327
- - **Session hooks API redesign**: Merged `branch` event into `session` event. `BranchEvent`, `BranchEventResult` types and `pi.on("branch", ...)` removed. Use `pi.on("session", ...)` with `reason: "before_branch" | "branch"` instead. `AgentSession.branch()` returns `{ cancelled }` instead of `{ skipped }`. `AgentSession.reset()` and `switchSession()` now return `boolean` (false if cancelled by hook). RPC commands `reset`, `switch_session`, and `branch` now include `cancelled` in response data. ([#278](https://github.com/badlogic/pi-mono/issues/278))
6409
+ - **Session hooks API redesign**: Merged `branch` event into `session` event. `BranchEvent`, `BranchEventResult` types
6410
+ and `pi.on("branch", ...)` removed. Use `pi.on("session", ...)` with `reason: "before_branch" | "branch"` instead.
6411
+ `AgentSession.branch()` returns `{ cancelled }` instead of `{ skipped }`. `AgentSession.reset()` and
6412
+ `switchSession()` now return `boolean` (false if cancelled by hook). RPC commands `reset`, `switch_session`, and
6413
+ `branch` now include `cancelled` in response data. ([#278](https://github.com/badlogic/pi-mono/issues/278))
6328
6414
 
6329
6415
  ### Added
6330
6416
 
@@ -6480,7 +6566,11 @@ Total color count increased from 46 to 50. See [docs/theme.md](docs/theme.md) fo
6480
6566
 
6481
6567
  - **Subagent example improvements**: Parallel mode now streams updates from all tasks. Chain mode shows all completed steps during streaming. Expanded view uses proper markdown rendering with syntax highlighting. Usage footer shows turn count.
6482
6568
 
6483
- - **Skills standard compliance**: Skills now adhere to the [Agent Skills standard](https://agentskills.io/specification). Validates name (must match parent directory, lowercase, max 64 chars), description (required, max 1024 chars), and frontmatter fields. Warns on violations but remains lenient. Prompt format changed to XML structure. Removed `{baseDir}` placeholder in favor of relative paths. ([#231](https://github.com/badlogic/pi-mono/issues/231))
6569
+ - **Skills standard compliance**: Skills now adhere to the [Agent Skills
6570
+ standard](https://agentskills.io/specification). Validates name (must match parent directory, lowercase, max 64
6571
+ chars), description (required, max 1024 chars), and frontmatter fields. Warns on violations but remains lenient.
6572
+ Prompt format changed to XML structure. Removed `{baseDir}` placeholder in favor of relative paths.
6573
+ ([#231](https://github.com/badlogic/pi-mono/issues/231))
6484
6574
 
6485
6575
  ### Fixed
6486
6576
 
@@ -6545,7 +6635,10 @@ Total color count increased from 46 to 50. See [docs/theme.md](docs/theme.md) fo
6545
6635
 
6546
6636
  ### Fixed
6547
6637
 
6548
- - Fixed Claude models via GitHub Copilot re-answering all previous prompts in multi-turn conversations. The issue was that assistant message content was sent as an array instead of a string, which Copilot's Claude adapter misinterpreted. Also added missing `Openai-Intent: conversation-edits` header and fixed `X-Initiator` logic to check for any assistant/tool message in history. ([#209](https://github.com/badlogic/pi-mono/issues/209))
6638
+ - Fixed Claude models via GitHub Copilot re-answering all previous prompts in multi-turn conversations. The issue was
6639
+ that assistant message content was sent as an array instead of a string, which Copilot's Claude adapter
6640
+ misinterpreted. Also added missing `Openai-Intent: conversation-edits` header and fixed `X-Initiator` logic to check
6641
+ for any assistant/tool message in history. ([#209](https://github.com/badlogic/pi-mono/issues/209))
6549
6642
 
6550
6643
  - Detect image MIME type via file magic (read tool and `@file` attachments), not filename extension.
6551
6644
 
@@ -6633,7 +6726,11 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
6633
6726
 
6634
6727
  ### Added
6635
6728
 
6636
- - **Inline image rendering**: Terminals supporting Kitty graphics protocol (Kitty, Ghostty, WezTerm) or iTerm2 inline images now render images inline in tool output. Aspect ratio is preserved by querying terminal cell dimensions on startup. Toggle with `/show-images` command or `terminal.showImages` setting. Falls back to text placeholder on unsupported terminals or when disabled. ([#177](https://github.com/badlogic/pi-mono/pull/177) by [@nicobailon](https://github.com/nicobailon))
6729
+ - **Inline image rendering**: Terminals supporting Kitty graphics protocol (Kitty, Ghostty, WezTerm) or iTerm2 inline
6730
+ images now render images inline in tool output. Aspect ratio is preserved by querying terminal cell dimensions on
6731
+ startup. Toggle with `/show-images` command or `terminal.showImages` setting. Falls back to text placeholder on
6732
+ unsupported terminals or when disabled. ([#177](https://github.com/badlogic/pi-mono/pull/177) by
6733
+ [@nicobailon](https://github.com/nicobailon))
6637
6734
 
6638
6735
  - **Gemini 3 Pro thinking levels**: Thinking level selector now works with Gemini 3 Pro models. Minimal/low map to Google's LOW, medium/high map to Google's HIGH. ([#176](https://github.com/badlogic/pi-mono/pull/176) by [@markusylisiurunen](https://github.com/markusylisiurunen))
6639
6736
 
@@ -6669,7 +6766,11 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
6669
6766
 
6670
6767
  ### Added
6671
6768
 
6672
- - **Skills system**: Auto-discover and load instruction files on-demand. Supports Claude Code (`~/.claude/skills/*/SKILL.md`), Codex CLI (`~/.codex/skills/`), and OMP-native formats (`~/.omp/agent/skills/`, `.omp/skills/`). Skills are listed in system prompt with descriptions, agent loads them via read tool when needed. Supports `{baseDir}` placeholder. Disable with `--no-skills` or `skills.enabled: false` in settings. ([#169](https://github.com/badlogic/pi-mono/issues/169))
6769
+ - **Skills system**: Auto-discover and load instruction files on-demand. Supports Claude Code
6770
+ (`~/.claude/skills/*/SKILL.md`), Codex CLI (`~/.codex/skills/`), and OMP-native formats (`~/.omp/agent/skills/`,
6771
+ `.omp/skills/`). Skills are listed in system prompt with descriptions, agent loads them via read tool when needed.
6772
+ Supports `{baseDir}` placeholder. Disable with `--no-skills` or `skills.enabled: false` in settings.
6773
+ ([#169](https://github.com/badlogic/pi-mono/issues/169))
6673
6774
 
6674
6775
  - **Version flag**: Added `--version` / `-v` flag to display the current version and exit. ([#170](https://github.com/badlogic/pi-mono/pull/170))
6675
6776
 
@@ -6677,7 +6778,11 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
6677
6778
 
6678
6779
  ### Added
6679
6780
 
6680
- - **Auto-retry on transient errors**: Automatically retries requests when providers return overloaded, rate limit, or server errors (429, 500, 502, 503, 504). Uses exponential backoff (2s, 4s, 8s). Shows retry status in TUI with option to cancel via Escape. Configurable in `settings.json` via `retry.enabled`, `retry.maxRetries`, `retry.baseDelayMs`. RPC mode emits `auto_retry_start` and `auto_retry_end` events. ([#157](https://github.com/badlogic/pi-mono/issues/157))
6781
+ - **Auto-retry on transient errors**: Automatically retries requests when providers return overloaded, rate limit, or
6782
+ server errors (429, 500, 502, 503, 504). Uses exponential backoff (2s, 4s, 8s). Shows retry status in TUI with option
6783
+ to cancel via Escape. Configurable in `settings.json` via `retry.enabled`, `retry.maxRetries`, `retry.baseDelayMs`.
6784
+ RPC mode emits `auto_retry_start` and `auto_retry_end` events.
6785
+ ([#157](https://github.com/badlogic/pi-mono/issues/157))
6681
6786
 
6682
6787
  - **HTML export line numbers**: Read tool calls in HTML exports now display line number ranges (e.g., `file.txt:10-20`) when offset/limit parameters are used, matching the TUI display format. Line numbers appear in yellow color for better visibility. ([#166](https://github.com/badlogic/pi-mono/issues/166))
6683
6788
 
@@ -6703,7 +6808,12 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
6703
6808
 
6704
6809
  ### Added
6705
6810
 
6706
- - **Hooks system**: TypeScript modules that extend agent behavior by subscribing to lifecycle events. Hooks can intercept tool calls, prompt for confirmation, modify results, and inject messages from external sources. Auto-discovered from `~/.omp/agent/hooks/*.ts` and `.omp/hooks/*.ts`. Thanks to [@nicobailon](https://github.com/nicobailon) for the collaboration on the design and implementation. ([#145](https://github.com/badlogic/pi-mono/issues/145), supersedes [#158](https://github.com/badlogic/pi-mono/pull/158))
6811
+ - **Hooks system**: TypeScript modules that extend agent behavior by subscribing to lifecycle events. Hooks can
6812
+ intercept tool calls, prompt for confirmation, modify results, and inject messages from external sources.
6813
+ Auto-discovered from `~/.omp/agent/hooks/*.ts` and `.omp/hooks/*.ts`. Thanks to
6814
+ [@nicobailon](https://github.com/nicobailon) for the collaboration on the design and implementation.
6815
+ ([#145](https://github.com/badlogic/pi-mono/issues/145), supersedes
6816
+ [#158](https://github.com/badlogic/pi-mono/pull/158))
6707
6817
 
6708
6818
  - **`pi.send()` API**: Hooks can inject messages into the agent session from external sources (file watchers, webhooks, CI systems). If streaming, messages are queued; otherwise a new agent loop starts immediately.
6709
6819
 
@@ -6741,7 +6851,10 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
6741
6851
 
6742
6852
  ### Breaking Changes
6743
6853
 
6744
- - **New RPC protocol**: The RPC mode (`--mode rpc`) has been completely redesigned with a new JSON protocol. The old protocol is no longer supported. See [`docs/rpc.md`](docs/rpc.md) for the new protocol documentation and [`test/rpc-example.ts`](test/rpc-example.ts) for a working example. Includes `RpcClient` TypeScript class for easy integration. ([#91](https://github.com/badlogic/pi-mono/issues/91))
6854
+ - **New RPC protocol**: The RPC mode (`--mode rpc`) has been completely redesigned with a new JSON protocol. The old
6855
+ protocol is no longer supported. See [`docs/rpc.md`](docs/rpc.md) for the new protocol documentation and
6856
+ [`test/rpc-example.ts`](test/rpc-example.ts) for a working example. Includes `RpcClient` TypeScript class for easy
6857
+ integration. ([#91](https://github.com/badlogic/pi-mono/issues/91))
6745
6858
 
6746
6859
  ### Changed
6747
6860
 
@@ -6751,7 +6864,10 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
6751
6864
 
6752
6865
  ### Changed
6753
6866
 
6754
- - **Major code refactoring**: Restructured codebase for better maintainability and separation of concerns. Moved files into organized directories (`core/`, `modes/`, `utils/`, `cli/`). Extracted `AgentSession` class as central session management abstraction. Split `main.ts` and `tui-renderer.ts` into focused modules. See `DEVELOPMENT.md` for the new code map. ([#153](https://github.com/badlogic/pi-mono/issues/153))
6867
+ - **Major code refactoring**: Restructured codebase for better maintainability and separation of concerns. Moved files
6868
+ into organized directories (`core/`, `modes/`, `utils/`, `cli/`). Extracted `AgentSession` class as central session
6869
+ management abstraction. Split `main.ts` and `tui-renderer.ts` into focused modules. See `DEVELOPMENT.md` for the new
6870
+ code map. ([#153](https://github.com/badlogic/pi-mono/issues/153))
6755
6871
 
6756
6872
  ## [0.14.2] - 2025-12-08
6757
6873
 
@@ -6777,13 +6893,20 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
6777
6893
 
6778
6894
  ### Added
6779
6895
 
6780
- - **OpenAI compatibility overrides in models.json**: Custom models using `openai-completions` API can now specify a `compat` object to override provider quirks (`supportsStore`, `supportsDeveloperRole`, `supportsReasoningEffort`, `maxTokensField`). Useful for LiteLLM, custom proxies, and other non-standard endpoints. ([#133](https://github.com/badlogic/pi-mono/issues/133), thanks @fink-andreas for the initial idea and PR)
6896
+ - **OpenAI compatibility overrides in models.json**: Custom models using `openai-completions` API can now specify a
6897
+ `compat` object to override provider quirks (`supportsStore`, `supportsDeveloperRole`, `supportsReasoningEffort`,
6898
+ `maxTokensField`). Useful for LiteLLM, custom proxies, and other non-standard endpoints.
6899
+ ([#133](https://github.com/badlogic/pi-mono/issues/133), thanks @fink-andreas for the initial idea and PR)
6781
6900
 
6782
6901
  - **xhigh thinking level**: Added `xhigh` thinking level for OpenAI codex-max models. Cycle through thinking levels with Shift+Tab; `xhigh` appears only when using a codex-max model. ([#143](https://github.com/badlogic/pi-mono/issues/143))
6783
6902
 
6784
6903
  - **Collapse changelog setting**: Add `"collapseChangelog": true` to `~/.omp/agent/settings.json` to show a condensed "Updated to vX.Y.Z" message instead of the full changelog after updates. Use `/changelog` to view the full changelog. ([#148](https://github.com/badlogic/pi-mono/issues/148))
6785
6904
 
6786
- - **Bash mode**: Execute shell commands directly from the editor by prefixing with `!` (e.g., `!ls -la`). Output streams in real-time, is added to the LLM context, and persists in session history. Supports multiline commands, cancellation (Escape), truncation for large outputs, and preview/expand toggle (Ctrl+O). Also available in RPC mode via `{"type":"bash","command":"..."}`. ([#112](https://github.com/badlogic/pi-mono/pull/112), original implementation by [@markusylisiurunen](https://github.com/markusylisiurunen))
6905
+ - **Bash mode**: Execute shell commands directly from the editor by prefixing with `!` (e.g., `!ls -la`). Output
6906
+ streams in real-time, is added to the LLM context, and persists in session history. Supports multiline commands,
6907
+ cancellation (Escape), truncation for large outputs, and preview/expand toggle (Ctrl+O). Also available in RPC mode
6908
+ via `{"type":"bash","command":"..."}`. ([#112](https://github.com/badlogic/pi-mono/pull/112), original implementation
6909
+ by [@markusylisiurunen](https://github.com/markusylisiurunen))
6787
6910
 
6788
6911
  ## [0.13.2] - 2025-12-07
6789
6912
 
@@ -6839,7 +6962,10 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
6839
6962
  ### Added
6840
6963
 
6841
6964
  - **Fuzzy search models and sessions**: Implemented a simple fuzzy search for models and sessions (e.g., `codexmax` now finds `gpt-5.1-codex-max`). ([#122](https://github.com/badlogic/pi-mono/pull/122) by [@markusylisiurunen](https://github.com/markusylisiurunen))
6842
- - **Prompt History Navigation**: Browse previously submitted prompts using Up/Down arrow keys when the editor is empty. Press Up to cycle through older prompts, Down to return to newer ones or clear the editor. Similar to shell history and Claude Code's prompt history feature. History is session-scoped and stores up to 100 entries. ([#121](https://github.com/badlogic/pi-mono/pull/121) by [@nicobailon](https://github.com/nicobailon))
6965
+ - **Prompt History Navigation**: Browse previously submitted prompts using Up/Down arrow keys when the editor is empty.
6966
+ Press Up to cycle through older prompts, Down to return to newer ones or clear the editor. Similar to shell history
6967
+ and Claude Code's prompt history feature. History is session-scoped and stores up to 100 entries.
6968
+ ([#121](https://github.com/badlogic/pi-mono/pull/121) by [@nicobailon](https://github.com/nicobailon))
6843
6969
  - **`/resume` Command**: Switch to a different session mid-conversation. Opens an interactive selector showing all available sessions. Equivalent to the `--resume` CLI flag but can be used without restarting the agent. ([#117](https://github.com/badlogic/pi-mono/pull/117) by [@hewliyang](https://github.com/hewliyang))
6844
6970
 
6845
6971
  ## [0.12.11] - 2025-12-05
@@ -6959,7 +7085,7 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
6959
7085
 
6960
7086
  ### Fixed
6961
7087
 
6962
- - Absolute glob patterns (e.g., `/Users/foo/**/*.ts`) are now handled correctly. Previously the leading `/` was being stripped, causing the pattern to be interpreted relative to the current directory.
7088
+ - Absolute glob patterns (e.g., `/Users/example/**/*.ts`) are now handled correctly. Previously the leading `/` was being stripped, causing the pattern to be interpreted relative to the current directory.
6963
7089
 
6964
7090
  ## [0.11.7] - 2025-12-01
6965
7091
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "19.105.4",
4
+ "version": "19.105.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",
@@ -57,13 +57,13 @@
57
57
  "dependencies": {
58
58
  "@agentclientprotocol/sdk": "1.3.0",
59
59
  "@mozilla/readability": "^0.6",
60
- "@f5-sales-demo/xcsh-stats": "19.105.4",
61
- "@f5-sales-demo/pi-agent-core": "19.105.4",
62
- "@f5-sales-demo/pi-ai": "19.105.4",
63
- "@f5-sales-demo/pi-natives": "19.105.4",
64
- "@f5-sales-demo/pi-resource-management": "19.105.4",
65
- "@f5-sales-demo/pi-tui": "19.105.4",
66
- "@f5-sales-demo/pi-utils": "19.105.4",
60
+ "@f5-sales-demo/xcsh-stats": "19.105.6",
61
+ "@f5-sales-demo/pi-agent-core": "19.105.6",
62
+ "@f5-sales-demo/pi-ai": "19.105.6",
63
+ "@f5-sales-demo/pi-natives": "19.105.6",
64
+ "@f5-sales-demo/pi-resource-management": "19.105.6",
65
+ "@f5-sales-demo/pi-tui": "19.105.6",
66
+ "@f5-sales-demo/pi-utils": "19.105.6",
67
67
  "@sinclair/typebox": "^0.34",
68
68
  "@xterm/headless": "^6.0",
69
69
  "ajv": "^8.20",
@@ -5,6 +5,7 @@ import * as os from "node:os";
5
5
  import * as path from "node:path";
6
6
  import { $ } from "bun";
7
7
  import { isLocalSpecsCurrent } from "./api-specs-version";
8
+ import { sanitizeAcmePlaceholders, sanitizePublicIpv4Examples } from "./sanitize-generated-content";
8
9
 
9
10
  interface SpecPathOperation {
10
11
  operationId?: string;
@@ -108,30 +109,11 @@ const RESERVED_EMAIL_DOMAINS = new Set(["example.com", "example.net", "example.o
108
109
  */
109
110
  function sanitizeEmails(text: string): string {
110
111
  // The lookbehind skips URL userinfo (`https://token:secret@host`), which is not a contact address.
111
- return text.replace(/(?<![:/])\b[A-Za-z0-9._%+-]+@([A-Za-z0-9.-]+\.[A-Za-z]{2,})\b/g, (whole, domain) =>
112
+ return text.replace(/(?<![:/])\b[A-Za-z0-9._%+-]+@([A-Za-z0-9.-]+\.[A-Za-z]+)\b/g, (whole, domain) =>
112
113
  RESERVED_EMAIL_DOMAINS.has(String(domain).toLowerCase()) ? whole : "dana@example.com",
113
114
  );
114
115
  }
115
116
 
116
- /**
117
- * Upstream specs use ACME as a placeholder tenant, company and hostname. STYLE_GUIDE.md bans it:
118
- * it is not trademark-cleared, and in TLS content the name already belongs to RFC 8555. Rewrite it
119
- * to the `Example` pattern here rather than in the emitted file, so a regeneration cannot bring it
120
- * back (#2650).
121
- *
122
- * `_acme-challenge` is the RFC 8555 DNS-01 record label, not the placeholder, and must survive.
123
- */
124
- function sanitizePlaceholders(text: string): string {
125
- // The sentinel must contain no form of the word being rewritten, or the rewrite eats it.
126
- const CHALLENGE = " RFC8555_DNS01 ";
127
- return text
128
- .replace(/_acme-challenge/gi, CHALLENGE)
129
- .replace(/ACME/g, "Example")
130
- .replace(/Acme/g, "Example")
131
- .replace(/acme/g, "example")
132
- .replaceAll(CHALLENGE, "_acme-challenge");
133
- }
134
-
135
117
  const MAX_RETRIES = 3;
136
118
  const INITIAL_BACKOFF_MS = 2000;
137
119
 
@@ -587,7 +569,7 @@ const output = [
587
569
  .filter(l => l !== undefined)
588
570
  .join("\n");
589
571
 
590
- await Bun.write(outputPath, sanitizeEmails(sanitizePlaceholders(output)));
572
+ await Bun.write(outputPath, sanitizePublicIpv4Examples(sanitizeEmails(sanitizeAcmePlaceholders(output))));
591
573
 
592
574
  const outputSize = (Buffer.byteLength(output) / 1024 / 1024).toFixed(1);
593
575
  console.log(
@@ -631,7 +613,10 @@ if (catalog) {
631
613
  "",
632
614
  ].join("\n");
633
615
 
634
- await Bun.write(catalogOutputPath, sanitizeEmails(sanitizePlaceholders(catalogOutput)));
616
+ await Bun.write(
617
+ catalogOutputPath,
618
+ sanitizePublicIpv4Examples(sanitizeEmails(sanitizeAcmePlaceholders(catalogOutput))),
619
+ );
635
620
  const catalogSize = (Buffer.byteLength(catalogOutput) / 1024 / 1024).toFixed(1);
636
621
  console.log(
637
622
  `Generated ${path.relative(process.cwd(), catalogOutputPath)} (${categories.length} categories, ${catalogSize} MB)`,
@@ -2,6 +2,7 @@
2
2
  // scripts/generate-console-catalog.ts
3
3
  import * as fs from "node:fs";
4
4
  import * as path from "node:path";
5
+ import { sanitizePublicIpv4Examples } from "./sanitize-generated-content";
5
6
 
6
7
  const repoRoot = path.resolve(import.meta.dir, "..");
7
8
  const outputPath = path.join(repoRoot, "src/internal-urls/console-catalog.generated.ts");
@@ -98,7 +99,7 @@ const output = [
98
99
  "",
99
100
  ].join("\n");
100
101
 
101
- await Bun.write(outputPath, sanitizeTenantHosts(output));
102
+ await Bun.write(outputPath, sanitizePublicIpv4Examples(sanitizeTenantHosts(output)));
102
103
  console.log(
103
104
  `Wrote ${outputPath} (workflows=${Object.keys(data.workflows).length}, resources=${Object.keys(data.resources).length})`,
104
105
  );
@@ -0,0 +1,141 @@
1
+ const IPV4_RE = /(?<![A-Za-z0-9.])((?:[0-9]{1,3}\.){3}[0-9]{1,3})(?:\/([0-9]{1,2}))?(?![A-Za-z0-9.])/g;
2
+ const DOTTED_VERSION_PREFIX_RE =
3
+ /(?:(?:chrome|headlesschrome|chromium|firefox|safari|edg|edge|opera)\/\s*$|[A-Za-z0-9_.-]*version[A-Za-z0-9_.-]*\s*[:=]\s*['"`]?\s*$|version[A-Za-z0-9_.-]*\)?\.to(?:be|equal)\(\s*['"`]?\s*$)/i;
4
+ const SVG_PATH_ATTRIBUTE_RE = /(?:^|\s)d\s*=\s*(['"])/gi;
5
+ const RFC_8555_TERM_RE =
6
+ /_acme-challenge|\bAutomated Certificate Management Environment\s*\(ACME\)|\bRFC\s*8555\s*\(ACME\)|\bACME\s*\(RFC\s*8555\)|\bACME\s+(?:account|authorization|certificate|challenge|client|directory|nonce|order|protocol|server|service)\b/gi;
7
+ const RFC_8555_TOKEN_RE = /\0RFC8555_([0-9]+)\0/g;
8
+
9
+ type Ipv4Address = readonly [number, number, number, number];
10
+ type Ipv4Range = readonly [Ipv4Address, number];
11
+
12
+ const NON_GLOBAL_RANGES: readonly Ipv4Range[] = [
13
+ [[0, 0, 0, 0], 8],
14
+ [[10, 0, 0, 0], 8],
15
+ [[100, 64, 0, 0], 10],
16
+ [[127, 0, 0, 0], 8],
17
+ [[169, 254, 0, 0], 16],
18
+ [[172, 16, 0, 0], 12],
19
+ [[192, 0, 0, 0], 24],
20
+ [[192, 0, 2, 0], 24],
21
+ [[192, 168, 0, 0], 16],
22
+ [[198, 18, 0, 0], 15],
23
+ [[198, 51, 100, 0], 24],
24
+ [[203, 0, 113, 0], 24],
25
+ [[224, 0, 0, 0], 4],
26
+ [[240, 0, 0, 0], 4],
27
+ ];
28
+
29
+ const GLOBAL_PROTOCOL_HOSTS = new Set([9, 10]);
30
+ const DOCUMENTATION_PREFIXES: readonly (readonly [number, number, number])[] = [
31
+ [192, 0, 2],
32
+ [198, 51, 100],
33
+ [203, 0, 113],
34
+ ];
35
+
36
+ interface ProtectedRfc8555Terms {
37
+ text: string;
38
+ terms: string[];
39
+ }
40
+
41
+ function protectRfc8555Terms(text: string): ProtectedRfc8555Terms {
42
+ const terms: string[] = [];
43
+ return {
44
+ text: text.replace(RFC_8555_TERM_RE, term => {
45
+ const token = `\0RFC8555_${terms.length}\0`;
46
+ terms.push(term);
47
+ return token;
48
+ }),
49
+ terms,
50
+ };
51
+ }
52
+
53
+ /** Count uses of the name that are not RFC 8555 terminology. */
54
+ export function countAcmePlaceholderOccurrences(text: string): number {
55
+ const protectedText = protectRfc8555Terms(text).text;
56
+ return protectedText.match(/acme/gi)?.length ?? 0;
57
+ }
58
+
59
+ /**
60
+ * Replace fictitious ACME tenants, companies, and hosts with the Example pattern while preserving
61
+ * registered RFC 8555 terminology.
62
+ */
63
+ export function sanitizeAcmePlaceholders(text: string): string {
64
+ const protectedTerms = protectRfc8555Terms(text);
65
+ const sanitized = protectedTerms.text
66
+ .replace(/ACME/g, "Example")
67
+ .replace(/Acme/g, "Example")
68
+ .replace(/acme/g, "example");
69
+ return sanitized.replace(RFC_8555_TOKEN_RE, (token, index: string) => protectedTerms.terms[Number(index)] ?? token);
70
+ }
71
+
72
+ function parseIpv4(value: string): Ipv4Address | undefined {
73
+ const octets = value.split(".").map(Number);
74
+ if (octets.length !== 4 || octets.some(octet => !Number.isInteger(octet) || octet < 0 || octet > 255)) {
75
+ return undefined;
76
+ }
77
+ return [octets[0], octets[1], octets[2], octets[3]];
78
+ }
79
+
80
+ function toInteger(address: Ipv4Address): number {
81
+ return ((address[0] * 256 + address[1]) * 256 + address[2]) * 256 + address[3];
82
+ }
83
+
84
+ function inRange(address: Ipv4Address, [base, prefix]: Ipv4Range): boolean {
85
+ const divisor = 2 ** (32 - prefix);
86
+ return Math.floor(toInteger(address) / divisor) === Math.floor(toInteger(base) / divisor);
87
+ }
88
+
89
+ function isGloballyRoutableUnicast(address: Ipv4Address): boolean {
90
+ if (address[0] === 192 && address[1] === 0 && address[2] === 0 && GLOBAL_PROTOCOL_HOSTS.has(address[3])) {
91
+ return true;
92
+ }
93
+ return !NON_GLOBAL_RANGES.some(range => inRange(address, range));
94
+ }
95
+
96
+ function isDottedVersion(text: string, offset: number): boolean {
97
+ return DOTTED_VERSION_PREFIX_RE.test(text.slice(Math.max(0, offset - 96), offset));
98
+ }
99
+
100
+ function isInsideSvgPathData(text: string, offset: number): boolean {
101
+ const lineStart = text.lastIndexOf("\n", offset - 1) + 1;
102
+ const prefix = text.slice(lineStart, offset);
103
+ for (const match of prefix.matchAll(SVG_PATH_ATTRIBUTE_RE)) {
104
+ const quote = match[1];
105
+ if (!prefix.slice((match.index ?? 0) + match[0].length).includes(quote)) return true;
106
+ }
107
+ return false;
108
+ }
109
+
110
+ function hashAddress(value: string): number {
111
+ let hash = 0x811c9dc5;
112
+ for (const character of value) {
113
+ hash ^= character.charCodeAt(0);
114
+ hash = Math.imul(hash, 0x01000193) >>> 0;
115
+ }
116
+ return hash;
117
+ }
118
+
119
+ function documentationAddress(value: string, prefixText?: string): string {
120
+ const hash = hashAddress(value);
121
+ const network = DOCUMENTATION_PREFIXES[hash % DOCUMENTATION_PREFIXES.length];
122
+ let host = (Math.floor(hash / DOCUMENTATION_PREFIXES.length) % 254) + 1;
123
+ if (prefixText === undefined) return `${network.join(".")}.${host}`;
124
+
125
+ const originalPrefix = Number(prefixText);
126
+ const prefix = Math.max(24, originalPrefix);
127
+ const hostMask = 0xff << (32 - prefix);
128
+ host &= hostMask;
129
+ return `${network.join(".")}.${host}/${prefix}`;
130
+ }
131
+
132
+ /** Replace unsafe generated IPv4 examples with deterministic RFC 5737 values. */
133
+ export function sanitizePublicIpv4Examples(text: string): string {
134
+ return text.replace(IPV4_RE, (whole: string, value: string, prefix: string | undefined, offset: number) => {
135
+ const address = parseIpv4(value);
136
+ if (!address || (prefix !== undefined && Number(prefix) > 32)) return whole;
137
+ if (!isGloballyRoutableUnicast(address)) return whole;
138
+ if (isDottedVersion(text, offset) || isInsideSvgPathData(text, offset)) return whole;
139
+ return documentationAddress(value, prefix);
140
+ });
141
+ }