fix(providers): migrate web cookie TLS transport to wreq-js (#12429)

Migrates the Claude, Grok, LMArena, Notion and Perplexity web-cookie transports from the tls-client-node/Koffi sidecar to the exactly pinned wreq-js 3.2.0 runtime, keeping the per-provider browser/OS profiles, making request cookies ephemeral, bounding and generation-protecting the shared native transport pool, removing the legacy downloader and repair path, and carrying the native binding and license evidence through the npm, standalone, Electron, Docker and Bun packaging surfaces.

This is the consolidation of the two competing migrations, and the consolidation was decided by evidence rather than by preference. #11753's six suites were installed over this implementation and run as an independent specification: 31 of 36 passed. All five failures are artefacts of #11753 being the older design, not coverage gaps —

- two hardcode the 3.0.0 pin in their assertions (this branch pins 3.2.0, which is what the release tip already resolves; #11753's 3.0.0 would have conflicted);
- one reads open-sse/services/chatgptTlsClient.ts, deleted when #11754 retired ChatGPT Web, so the test is stale against the current tip;
- two import WREQ_JS_NATIVE_BINARY_NAMES / resolveWreqJsNativeBinaryName, which this branch redesigned into WREQ_JS_NATIVE_BINDINGS / resolveWreqJsNativeBinding plus WREQ_JS_VERSION — a rename from modelling natives as file names to modelling them as package bindings, verified as an API difference rather than a lost capability (the linux-x64-gnu .node is present and serviceable).

This branch is also the strict superset by scope: 7 files exclusive to it, including the wreq-js Rust license inventory and notices, .trivyignore, open-sse/utils/tlsClient.ts and assembleStandalone.mjs. #11753 had one exclusive file, its changelog fragment. Nothing needed porting, so #11753 is superseded rather than merged, and the changelog entry credits both.

Reconciled on merge: clean against the tip. The new migration suite (tests/unit/tls-client-wreq-migration.test.ts, 1374 lines, 31 cases) is frozen at its exact LOC with the rationale — it shares one native-transport harness, so splitting it mid-merge would duplicate that harness for no coverage gain. Verified that no existing cap moves.

Verified: 182/182 across the eight TLS, native-manifest, postinstall, standalone-bundle, pack-artifact and provider-validation suites, typecheck:core clean, check:cycles OK, check-changelog-integrity OK, check-file-size OK, and every changed TypeScript file parses.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-09-02 10:41:00 -03:00
committed by GitHub
parent 7ed8ada432
commit 500568a1cd
60 changed files with 16506 additions and 1566 deletions

View File

@@ -75,7 +75,9 @@ When you run `npm install -g omniroute`, you may see a wall of warnings like `np
The warnings come from stale peer-dependency ranges in third-party packages OmniRoute doesn't control:
1. **`marked-terminal` wants `marked >=1 <16`, found `marked@18`** — works fine in practice; the upstream peer range is just stale.
2. **`deprecated prebuild-install@7.1.3`** — the native-binary fetch helper. Only relevant later if a web-cookie provider reports a missing `tls-client-node` native binary (a separate issue, not caused by this warning).
2. **`deprecated prebuild-install@7.1.3`** — a transitive native-binary fetch helper. It is not
used to install the pinned `wreq-js` transport binding and does not indicate that web-cookie
provider transport setup failed.
**No action needed** — the warnings cannot be fully silenced without forking upstream packages.
@@ -148,9 +150,9 @@ desktop app, for example:
- `resources/app/.build/next/node_modules/playwright-<hash>/lib/…/agentParser.js` and
`workerProcessEntry.js` — [Playwright](https://playwright.dev), the browser-automation
library used for in-app provider login and browser-backed chat.
- `resources/app/.build/next/node_modules/tls-client-node-<hash>/bin/tls-client-windows-64-<ver>.dll`
— the native binary from `tls-client-node`, used for Cloudflare-tolerant HTTP on some web
providers.
- `resources/app/.build/next/node_modules/@wreq-js/binding-win32-<arch>-msvc-<hash>/wreq-js.win32-<arch>-msvc.node`
— the pinned `wreq-js` native binding used for browser-fingerprinted HTTP on web-cookie
providers (`<arch>` is `x64` or `arm64`).
**Why it fires:** the Windows installer is **not yet code-signed**, so an unsigned NSIS
installer has zero reputation and behavioral heuristics run at maximum aggression. Combined

View File

@@ -764,15 +764,15 @@ REQUEST_TIMEOUT_MS (global override)
| `OMNIROUTE_PROVIDER_PROBE_TIMEOUT_MS` | `8000` | Timeout (ms) for the `validationRead` and `modelsProbe` presets in `src/shared/network/safeOutboundFetch.ts`. Raise for slow endpoints (Cerebras, Cloudflare AI, Groq) to prevent flapping between active/error in the dashboard. Falls back to 8000ms for invalid (<1000) or non-numeric values. |
| `OMNIROUTE_RELAY_FETCH_TIMEOUT_MS` | `25000` | Relay-specific fetch timeout in `open-sse/utils/proxyFetch.ts` (#9158). A hung relay must fail before the client/agent timeout (~30s) so callers see a relay-specific failure instead of a generic upstream timeout. Capped at `29000` so it always fires first. |
| `OMNIROUTE_RETRY_BACKOFF_MS` | `10` | Shared retry backoff for the direct/relay/proxy retry-once paths in `open-sse/utils/proxyFetch.ts` (#9158). `0` = retry immediately. |
| `OMNIROUTE_CLAUDE_TLS_TIMEOUT_MS` | `60000` | Wire-level timeout for the bogdanfinn/tls-client koffi binding (`claudeTlsClient.ts`). |
| `OMNIROUTE_CLAUDE_TLS_GRACE_MS` | `10000` | JS-side grace added on top of the wire timeout when the native binding is wedged. |
| `OMNIROUTE_PPLX_TLS_TIMEOUT_MS` | `30000` | Wire-level timeout for the bogdanfinn/tls-client koffi binding (`perplexityTlsClient.ts`). |
| `OMNIROUTE_PPLX_TLS_GRACE_MS` | `10000` | JS-side grace added on top of the wire timeout when the native binding is wedged. |
| `OMNIROUTE_CLAUDE_TLS_TIMEOUT_MS` | `60000` | Native wreq-js request timeout (`claudeTlsClient.ts`). |
| `OMNIROUTE_CLAUDE_TLS_GRACE_MS` | `10000` | Absolute JS hard-deadline grace added on top of the native timeout. |
| `OMNIROUTE_PPLX_TLS_TIMEOUT_MS` | `30000` | Native wreq-js request timeout (`perplexityTlsClient.ts`). |
| `OMNIROUTE_PPLX_TLS_GRACE_MS` | `10000` | Absolute JS hard-deadline grace added on top of the native timeout. |
| `OMNIROUTE_PPLX_SEARCH_HINT` | `0` (off) | Appends "You have built-in web search. Answer questions directly using search results." to the caller's system message (`perplexity-web/protocol.ts`). Off by default — Perplexity searches anyway, and the sentence leaks into replies as meta-commentary for coding clients. Set `1`/`true`/`yes`/`on` to restore. |
| `OMNIROUTE_GROK_TLS_TIMEOUT_MS` | `60000` | Wire-level timeout for the bogdanfinn/tls-client koffi binding (`grokTlsClient.ts`). |
| `OMNIROUTE_GROK_TLS_GRACE_MS` | `10000` | JS-side grace added on top of the wire timeout when the native binding is wedged. |
| `OMNIROUTE_NOTION_TLS_TIMEOUT_MS` | `30000` | Wire-level timeout for the bogdanfinn/tls-client koffi binding (`notionTlsClient.ts`); the `notion-web` executor raises it per-request to `180000` for long generations. |
| `OMNIROUTE_NOTION_TLS_GRACE_MS` | `10000` | JS-side grace added on top of the wire timeout when the native binding is wedged. |
| `OMNIROUTE_GROK_TLS_TIMEOUT_MS` | `60000` | Native wreq-js request timeout (`grokTlsClient.ts`). |
| `OMNIROUTE_GROK_TLS_GRACE_MS` | `10000` | Absolute JS hard-deadline grace added on top of the native timeout. |
| `OMNIROUTE_NOTION_TLS_TIMEOUT_MS` | `30000` | Native wreq-js request timeout (`notionTlsClient.ts`); `notion-web` raises it per request to `180000` for long generations. |
| `OMNIROUTE_NOTION_TLS_GRACE_MS` | `10000` | Absolute JS hard-deadline grace added on top of the native timeout. |
| `OMNIROUTE_BROWSER_POOL` | `on` | Shared Playwright browser pool for browser-backed web-cookie chat (`browserPool.ts`); set `off` to disable. |
| `WEB_COOKIE_USE_BROWSER` | `0` | Opt a web-cookie chat request into the browser-backed path (`browserBackedChat.ts`); `1` to enable. |
| `KIMI_WEB_BASE_URL` | `https://www.kimi.ai` | Base URL for the Kimi Web (international kimi.ai Connect-RPC) executor (`kimi-web.ts`); override only for mirror/proxy endpoints. |

View File

@@ -1,13 +1,13 @@
---
title: "Stealth Guide"
version: 3.8.40
lastUpdated: 2026-06-28
version: 3.8.51
lastUpdated: 2026-09-02
---
# Stealth Guide
> **Source of truth:** `open-sse/utils/tlsClient.ts`, `open-sse/services/{claudeCodeCCH,claudeCodeFingerprint,claudeCodeObfuscation,claudeCodeCompatible}.ts`, `open-sse/config/cliFingerprints.ts`, `src/mitm/`
> **Last updated:** 2026-06-28 — v3.8.40
> **Source of truth:** `open-sse/utils/tlsClient.ts`, `open-sse/services/{tlsClientBase,claudeTlsClient,perplexityTlsClient,grokTlsClient,notionTlsClient,lmarenaTlsClient,claudeCodeCCH,claudeCodeFingerprint,claudeCodeObfuscation,claudeCodeCompatible}.ts`, `open-sse/config/cliFingerprints.ts`, `src/mitm/`
> **Last updated:** 2026-09-02 — v3.8.51
> **Audience:** Engineers maintaining provider-specific stealth integrations.
OmniRoute integrates with providers whose edges actively fingerprint non-official clients (TLS JA3/JA4, header ordering, JSON body shape, integrity tokens). This page documents the stealth surfaces OmniRoute exposes and where they are implemented.
@@ -22,13 +22,56 @@ Stealth features exist so OmniRoute can act as a compatibility layer between use
### `open-sse/utils/tlsClient.ts` — wreq-js (Chrome 124)
Lazy-loaded `wreq-js` session that impersonates **Chrome 124 on macOS**. Used as a generic JA3/JA4 wrapper for upstreams behind Cloudflare. Falls back to native fetch when `wreq-js` is not installed (`available = false`).
Persistent `wreq-js` sessions are created lazily per account scope and resolved proxy. The
process-wide `TlsClient` pools at most 128 sessions that impersonate **Chrome 124 on macOS** for
upstreams behind Cloudflare. `TlsClient.fetch()` fails closed when the native runtime is
unavailable; a caller may explicitly select a fallback outside this wrapper.
- Singleton session: `browser: "chrome_124", os: "macos"`
- Session profile: `browser: "chrome_124", os: "macos"`
- Proxy resolution (priority): `HTTPS_PROXY``HTTP_PROXY``ALL_PROXY` (also lower-case)
- Timeout: `TLS_CLIENT_TIMEOUT_MS` (inherits from `FETCH_TIMEOUT_MS`, default 600000)
- `wreq-js` Response is fetch-compatible (`headers`, `text()`, `json()`, `clone()`, `body`).
### Web-cookie provider transport — wreq-js 3.2.0
`open-sse/services/tlsClientBase.ts` is the shared adapter for the five specialized
web-cookie transports below. Each thin provider wrapper selects a browser/OS profile. The adapter
uses the single wreq runtime loader and transport pool in `open-sse/utils/tlsClient.ts`, keyed by
profile + OS + resolved proxy, while every request uses `cookieMode: "ephemeral"`. Accounts and
requests therefore share transport-level connections, but never a wreq session or cookie jar.
| Provider | Profile | Emulated OS | Stream EOF policy |
| ---------- | ------------- | ----------- | -------------------------------- |
| Claude | `chrome_146` | Linux | include `[DONE]` |
| Perplexity | `firefox_148` | macOS | include `event: end_of_stream` |
| Grok | `chrome_146` | Linux | exclude `[DONE]` |
| Notion | `chrome_146` | Windows | include `[DONE]` |
| LMArena | `chrome_146` | Windows | no sentinel; close on native EOF |
- Streaming consumes the native response `ReadableStream` directly; no temp file or sidecar is
created.
- Up to 256 initial bytes are inspected before exposing a stream. SSE providers buffer non-SSE
errors; Grok/LMArena map Cloudflare challenges to `403` and HTML interstitials to `502`.
- The native request timeout remains wrapped by an absolute JS hard deadline. A hang invalidates
and closes only the affected profile/OS/proxy transport before the next request recreates it.
- Proxy resolution priority is per-call `proxyUrl` → request-scoped account/dashboard context →
`HTTPS_PROXY`/`HTTP_PROXY`/`ALL_PROXY` (including lowercase variants). Resolution errors fail
closed instead of leaking a direct connection. LMArena deliberately resolves against `arena.ai`.
- `byteResponse` returns a content-typed `data:` URL without UTF-8 corruption.
- Errors are `TlsClientUnavailableError` (package/addon unavailable), `TlsClientHangError`
(deadline exceeded), and `WreqTransportCapacityError` (the shared session-capacity error code)
when all 128 bounded profile/OS/proxy slots are active or closing.
The generic `TlsClient` session above remains specialized for persistent browser-backed cookie
state. Both paths reuse one cached wreq module loader and process lifecycle hook; their pools remain
separate because their cookie lifetimes are intentionally different.
The profiles are supported by the pinned package, but real WAF acceptance can change independently
of local contract tests. Validate fingerprint changes against an explicitly authorized live account
before claiming parity with an upstream browser.
---
## Claude Code Stealth Bundle
When `cliCompatMode` is on, OmniRoute reshapes outgoing Claude requests so they are indistinguishable from `claude-cli` traffic. Three modules collaborate: