Centralizes a handful of hardcoded URLs, fetch timeouts, and circuit-breaker
constants behind opt-in environment variables. Behavior is unchanged when
the variables are unset because every call site keeps its current default.
- open-sse/services/usage.ts: extracts CROF_USAGE_URL,
GEMINI_CLI_USAGE_URL, CODEWHISPERER_BASE_URL constants backed by
OMNIROUTE_CROF_USAGE_URL, OMNIROUTE_GEMINI_CLI_USAGE_URL, and
OMNIROUTE_CODEWHISPERER_BASE_URL. Lets operators redirect quota probes
through corporate mirrors or a test fixture.
- open-sse/config/constants.ts: PROVIDER_PROFILES circuit-breaker
thresholds and reset timeouts now honor OMNIROUTE_CIRCUIT_BREAKER_*
env vars (oauth/api-key/local) with the same defaults as before.
- src/shared/utils/fetchTimeout.ts: DEFAULT_TIMEOUT_MS reads
OMNIROUTE_DEFAULT_FETCH_TIMEOUT_MS (fallback 120000) so deployments can
raise the global fallback without changing FETCH_TIMEOUT_MS semantics.
- open-sse/services/chatgptTlsClient.ts: DEFAULT_TIMEOUT_MS and
HARD_TIMEOUT_GRACE_MS now honor OMNIROUTE_CHATGPT_TLS_TIMEOUT_MS and
OMNIROUTE_CHATGPT_TLS_GRACE_MS (defaults 60000 / 10000).
- .env.example: documents the 11 new variables in the URLs and TIMEOUT
sections.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update Pollinations request transformation to send the selected model
and stream flag so requests match the active endpoint behavior.
Align the ChatGPT TLS client with shared proxy resolution so dashboard
proxy context is honored before falling back to environment settings.
Also refresh provider display names across dashboard pages, correct the
Claude extra-usage toggle messaging and visual state, and mark
Pollinations as offering a free public endpoint.
Round of fixes addressing the gemini-code-assist and chatgpt-codex review
comments on the initial PR.
## High priority
- **PoW solver no longer blocks the event loop** (gemini #1, #2). The 100k
prekey solver and 500k proof-of-work solver were synchronous SHA3-512
loops that pinned a CPU core for tens to hundreds of milliseconds per
request. Both are now async and `await`-yield to the event loop every
1000 iterations via setImmediate, so concurrent requests and I/O still
get scheduled. Wall time is approximately the same; what changes is
fairness, not throughput.
- **Real upstream streaming for stream=true requests** (codex #6). The
conv call now passes `stream: true` through to the TLS client when the
caller asked for streaming. The TLS client uses tls-client-node's
streamOutputPath primitive to write the response body to a temp file
as it arrives, and we tail that file as a ReadableStream so clients
see chunks in real time instead of getting one buffered burst at the
end. Also peeks the first 256 bytes — if the response starts with
`{` it's almost certainly a JSON error envelope, so we wait for the
full body and surface as a non-streaming error response.
## Medium priority
- **Per-cookie device id** (gemini #3). Replaced the single
process-wide DEVICE_ID with a per-cookie SHA-256-derived UUID that's
stable across requests for one connection but unique per cookie. This
matches how the browser's persistent oai-did cookie behaves and
avoids cross-account fingerprint sharing. Cache is bounded to 200
entries with FIFO eviction.
- **Removed dead conv-cache code** (gemini #4). The convCache /
convLookup / convStore trio (~70 LOC) was unused — conversationId is
hard-pinned to null because Temporary Chat conversation_ids 404 on
reuse. Deleted entirely; the comment explains why we don't persist.
- **No more console.log in the conv 4xx path** (gemini #5). Replaced
with log?.warn so it respects the application's logging
configuration.
- **Bound the warmup cache** (codex #7). The (cookie, accessToken) ->
timestamp map was unbounded; long-running multi-user deployments
with rotating tokens would grow it forever. Now capped at 200
entries with FIFO eviction (Map iteration order = insertion order).
- **Honor abort signals in TLS fetch** (codex #8). tlsFetchChatGpt now
checks options.signal before issuing the upstream call, after the
call returns, and the streaming body listens for abort to stop
tailing the temp file. tls-client-node's koffi binding can't cancel
an in-flight request mid-call, but we no longer process / re-emit a
response that the caller has already given up on.
## Tests
All 27 chatgpt-web tests still pass; updated several to find calls by
URL via findIndex rather than hardcoded indices, since the warmup
sequence (/me, /conversations, /models) and two-stage Sentinel
(prepare + chat-requirements) shifted positional offsets.
Manually verified end-to-end:
- Non-streaming completions
- Streaming completions (real-time chunks; SSE [DONE] terminator)
- Multi-turn with full history each turn (memory preserved correctly)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a new chatgpt-web provider that routes through chatgpt.com's internal
backend-api using a Plus/Pro subscription session cookie, enabling access
to GPT-5.x models without an OpenAI API key.
Heavier than perplexity-web/grok-web because chatgpt.com layers more bot
protection — this PR builds out the full pipeline needed to look like a
real browser session.
## New executor: open-sse/executors/chatgpt-web.ts
Auth/request pipeline (per chat completion):
1. exchangeSession() GET /api/auth/session cookie -> JWT (cached ~5min)
2. fetchDpl() GET / scrape data-build + script src
3. runSessionWarmup() GET /backend-api/me, /conversations, /models
4. POST /sentinel/chat-requirements/prepare -> prepare_token
5. POST /sentinel/chat-requirements -> chat-requirements-token + PoW seed/diff
6. solveProofOfWork() SHA3-512 loop -> "gAAAAAB..." sentinel proof token
7. POST /backend-api/f/conversation with all sentinel headers
8. parse SSE stream -> OpenAI chat.completion[.chunk] format
Notable details:
- 18-element prekey config matching chat2api/openai-sentinel (browser fingerprint
values, U+2212 MINUS SIGN in `webdriver−false`). Thin shapes get escalated to
mandatory Turnstile.
- Two-stage Sentinel handshake (/prepare + /chat-requirements) — sending only
the prepare result returns a 403 "Unusual activity" response.
- `turnstile.required: true` from Sentinel is treated as advisory; the conv
endpoint accepts requests without a Turnstile token as long as PoW + chat-
requirements-token are valid. Optional bring-your-own Turnstile via
`providerSpecificData.turnstileToken` for accounts that hard-require it.
- SSE parser tracks message_id and resets the accumulator on a new turn —
chatgpt.com echoes prior assistant messages (with status finished_successfully)
before sending the new turn.
- entity["...","value", ...] internal markup stripped from output (browser
renders these client-side).
- Conversation-continuity cache disabled by default: we send
history_and_training_disabled: true (Temporary Chat mode) and those
conversation_ids expire too fast to reuse — re-using returned 404. Each
request now sends conversation_id: null and replays full history, matching
what Open WebUI and OpenAI-API-style clients send anyway.
## TLS impersonation: open-sse/services/chatgptTlsClient.ts
ChatGPT's Cloudflare config pins cf_clearance to JA3/JA4 TLS fingerprint +
HTTP/2 SETTINGS frame. Plain Node Undici fetch always returns
cf-mitigated: challenge regardless of cookies. The wrapper module loads
`tls-client-node` (Firefox 148 fingerprint) in native runtime mode (.so via
koffi) — managed mode spawns a sidecar that conflicts with OmniRoute's
global fetch proxy patch.
- Lazy singleton TLSClient with process exit hooks
- Streaming-capable (file tail) and non-streaming modes
- Test injection point: __setTlsFetchOverrideForTesting() lets unit tests mock
the client without touching globalThis.fetch
## Provider wiring
- open-sse/executors/index.ts — register ChatGptWebExecutor with cgpt-web alias
- open-sse/config/providerRegistry.ts — registry entry, format=openai,
authHeader=cookie, model gpt-5.3-instant
- src/shared/constants/providers.ts — WEB_COOKIE_PROVIDERS UI metadata
(icon, color, authHint)
- src/lib/providers/validation.ts — validateChatGptWebProvider hits
/api/auth/session via the TLS client, detects cf-mitigated/HTML responses
and returns a clear "paste full Cookie line" hint instead of a generic
"Invalid"
- next.config.mjs — mark tls-client-node, koffi, tough-cookie as external
packages (Turbopack can't bundle the native .so)
## Cookie format
Validator and executor accept any of:
- bare value: "eyJhbGc..."
- unchunked cookie line: "__Secure-next-auth.session-token=eyJ..."
- chunked cookie line: "__Secure-next-auth.session-token.0=...; __Secure-next-auth.session-token.1=..."
- full DevTools Cookie header line: "Cookie: __Secure-next-auth.session-token.0=...; cf_clearance=...; ..."
NextAuth chunks the JWE when it exceeds 4KB; chunked cookies pass through
verbatim (NextAuth reassembles server-side). Recommend pasting the full
DevTools Cookie line so cf_clearance, __cf_bm, _cfuvid, _puid travel along —
without cf_clearance, Cloudflare blocks the request before NextAuth sees it.
## Tests
tests/unit/chatgpt-web.test.ts — 27 tests, all passing:
- Registration + alias resolution
- Token exchange (cookie -> Bearer flow)
- Token cache TTL
- Refreshed cookie surfaced via onCredentialsRefreshed callback
- Sentinel call ordering (session -> prepare -> chat-requirements -> conv)
- Sentinel chat-requirements-token forwarded on conv request
- PoW token has gAAAAAB prefix
- Turnstile.required: true does NOT block conv (passes through)
- Non-streaming chat.completion JSON
- Streaming SSE chunks ending with [DONE]
- Cumulative-parts diffing yields non-overlapping deltas
- Errors: 401 session, 403 sentinel, 429 conv rate-limit
- Empty messages -> 400 without any fetch
- Missing apiKey -> 401 without any fetch
- Cookie format: bare value, unchunked, chunked, "Cookie: ..." DevTools line
- Conversation continuity: each call starts a fresh conversation
- Browser-like headers on conv POST (UA, Origin, Sec-Fetch-Site, Accept)
- Payload shape (action, model=gpt-5-3, history_and_training_disabled)
- Provider registry contains chatgpt-web with gpt-5.3-instant model
Verification: typecheck:core clean, lint clean (no new warnings),
end-to-end manually verified across single-turn, multi-turn (memory
preserved), streaming, and Open WebUI-style sequential growing-history
flows.
## References
- bogdanfinn/tls-client (Go) — TLS impersonation upstream
- fatihkabakk/tls-client-node — Node bindings
- lanqian528/chat2api — Sentinel/PoW/prekey reference impl (Python)
- leetanshaj/openai-sentinel — Prekey config + SHA3-512 solver
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>