Compare commits
35 Commits
fix/releas
...
radar-expo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f13fe4221 | ||
|
|
5089c17b44 | ||
|
|
bc6129bcb2 | ||
|
|
3810a6c52c | ||
|
|
85aa84ebcb | ||
|
|
c00086e616 | ||
|
|
aa32d2ed77 | ||
|
|
71f858fc48 | ||
|
|
6307504584 | ||
|
|
404d3a4211 | ||
|
|
d82e3cf8e9 | ||
|
|
121023e418 | ||
|
|
4fc0b412fe | ||
|
|
66144d86f2 | ||
|
|
f7cba50cb7 | ||
|
|
6ff83077fd | ||
|
|
49a47cbe6f | ||
|
|
ee230fa93a | ||
|
|
7d2efdf1a4 | ||
|
|
5100642ebb | ||
|
|
2eb6d59ebc | ||
|
|
48f3428307 | ||
|
|
bc9a685b70 | ||
|
|
3e0afc8444 | ||
|
|
83c77fb0bf | ||
|
|
08f23d0d0d | ||
|
|
5185571f18 | ||
|
|
f330b21afd | ||
|
|
b11b000048 | ||
|
|
82e5afed6b | ||
|
|
1accabeb4e | ||
|
|
81b0ff46a3 | ||
|
|
41ffb08e4a | ||
|
|
3d7ed7aa87 | ||
|
|
3c9cb21cca |
@@ -1033,6 +1033,10 @@ PROVIDER_LIMITS_SYNC_SPACING_MS=1500
|
||||
# Used by: src/lib/db/core.ts::getDbHealthCheckIntervalMs().
|
||||
#OMNIROUTE_DB_HEALTHCHECK_INTERVAL_MS=21600000
|
||||
|
||||
# WAL truncate cadence override (ms). Set to 0 to disable. Default: 21600000 (6h).
|
||||
# Used by: src/lib/db/core.ts::getWalTruncateIntervalMs().
|
||||
#OMNIROUTE_WAL_TRUNCATE_INTERVAL_MS=21600000
|
||||
|
||||
# Skip the Redis-backed auth cache used by API key lookups (forces DB reads).
|
||||
# Used by: src/lib/db/apiKeys.ts. Set to 1 to disable. Default: enabled.
|
||||
#OMNIROUTE_DISABLE_REDIS_AUTH_CACHE=0
|
||||
|
||||
64
.github/workflows/radar-export.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
# Publica o export estável do catálogo consumido pelo OmniRoute Radar numa URL
|
||||
# fixa (asset de release `radar-export-latest`), para o servidor privado do Radar
|
||||
# (1 GB RAM, nunca clona/builda o OmniRoute) baixá-lo via `RADAR_EXPORT_URL` em
|
||||
# vez de depender do snapshot gravado no deploy. Fonte: scripts/release/radar-export.mjs.
|
||||
#
|
||||
# A URL estável resultante (definir em RADAR_EXPORT_URL no .env do radar-server):
|
||||
# https://github.com/diegosouzapw/OmniRoute/releases/download/radar-export-latest/export-omniroute.json
|
||||
name: Radar Export
|
||||
|
||||
on:
|
||||
workflow_dispatch: # o operador pode publicar sob demanda (de qualquer ref)
|
||||
push:
|
||||
branches: [main] # produção: só o catálogo do main clobra o asset estável
|
||||
paths:
|
||||
- open-sse/config/freeModelCatalog.data.ts
|
||||
- open-sse/config/freeModelCatalog.ts
|
||||
- open-sse/config/providerRegistry.ts
|
||||
- open-sse/config/providers/**
|
||||
- scripts/release/radar-export.mjs
|
||||
- .github/workflows/radar-export.yml
|
||||
schedule:
|
||||
- cron: "17 6 * * 1" # semanal (segunda 06:17 UTC): mantém geradoEm/proveniência frescos
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: radar-export-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CI_NODE_VERSION: "24"
|
||||
|
||||
jobs:
|
||||
publish-export:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # gh release upload — clobra o asset estável do export
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false # publish usa GH_TOKEN via gh release, não a credencial do checkout
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: ${{ env.CI_NODE_VERSION }}
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Generate catalog export with provenance
|
||||
run: node --import tsx/esm scripts/release/radar-export.mjs "$RUNNER_TEMP/export-omniroute.json"
|
||||
- name: Publish to the stable release asset
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="radar-export-latest"
|
||||
# Cria o release estável na primeira vez; nas seguintes só re-anexa o asset.
|
||||
if ! gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
||||
gh release create "$TAG" \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--title "Radar catalog export (rolling)" \
|
||||
--notes "Export estável do catálogo OmniRoute para o Radar. Atualizado automaticamente; NÃO é um release de versão do produto." \
|
||||
--latest=false
|
||||
fi
|
||||
gh release upload "$TAG" "$RUNNER_TEMP/export-omniroute.json" --repo "$GITHUB_REPOSITORY" --clobber
|
||||
@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
|
||||
|
||||
## Project at a Glance
|
||||
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 341 LLM providers, auto-fallback.
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 342 LLM providers, auto-fallback.
|
||||
|
||||
| Layer | Location | Purpose |
|
||||
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
16
README.md
@@ -7,7 +7,7 @@
|
||||
|
||||
# 🚀 OmniRoute — The Free AI Gateway
|
||||
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 341 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 15–95% tokens (~89% avg) — never hit limits. 341 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 342 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 15–95% tokens (~89% avg) — never hit limits. 342 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
| | v3.8.49 | **v3.8.50** | `v3.8.51+` |
|
||||
| ------------------------- | :-----: | :---------: | :---------: |
|
||||
| 🌐 Providers | 290 | **341** | more queued |
|
||||
| 🌐 Providers | 290 | **342** | more queued |
|
||||
| 🧠 Documented models | 1185 | **1202** | — |
|
||||
| 🖼️ Modality Bridge | — | 🆕 vision | video |
|
||||
| 📡 Radar free catalog | — | 🆕 opt-in | — |
|
||||
@@ -101,7 +101,7 @@
|
||||
<tr>
|
||||
<td align="right"><b>⚙️ Features</b></td>
|
||||
<td align="center"><a href="#-combos--the-flagship">🎯 Combos</a></td>
|
||||
<td align="center"><a href="#-341-ai-providers--90-free">🌐 Providers</a></td>
|
||||
<td align="center"><a href="#-342-ai-providers--90-free">🌐 Providers</a></td>
|
||||
<td align="center"><a href="#-full-cli--a2a--mcp">🔌 CLI & MCP</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -210,7 +210,7 @@ curl http://localhost:20128/v1/chat/completions \
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint. 341 providers. Never stop building — OmniRoute picks the cheapest one that works. Six pillars: Never hit limits (auto-fallback across 341 providers in milliseconds, zero downtime) · Save up to 95% tokens (RTK + Caveman stacked compression cuts 15–95%, ~89% avg on tool-heavy sessions) · $0 to start (90+ free tiers, 56 free forever — no card needed) · Every tool works (33 coding agents through one config) · One endpoint (OpenAI ↔ Claude ↔ Gemini ↔ Responses API at /v1) · Production-grade (circuit breakers, TLS stealth, MCP 109 tools, A2A, memory, guardrails, evals — 25,000+ tests)."/>
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint. 342 providers. Never stop building — OmniRoute picks the cheapest one that works. Six pillars: Never hit limits (auto-fallback across 342 providers in milliseconds, zero downtime) · Save up to 95% tokens (RTK + Caveman stacked compression cuts 15–95%, ~89% avg on tool-heavy sessions) · $0 to start (90+ free tiers, 56 free forever — no card needed) · Every tool works (33 coding agents through one config) · One endpoint (OpenAI ↔ Claude ↔ Gemini ↔ Responses API at /v1) · Production-grade (circuit breakers, TLS stealth, MCP 109 tools, A2A, memory, guardrails, evals — 25,000+ tests)."/>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
@@ -461,7 +461,7 @@ All **19** strategies — mix & match per combo step:
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — comparison table vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 341 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, built-in MCP server with 109 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA, 43 i18n UI locales, 100% MIT self-hosted. OmniRoute is the only one with the full set; competitors show a mix of checks, partials and crosses. Verified from each project's docs."/>
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — comparison table vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 342 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, built-in MCP server with 109 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA, 43 i18n UI locales, 100% MIT self-hosted. OmniRoute is the only one with the full set; competitors show a mix of checks, partials and crosses. Verified from each project's docs."/>
|
||||
|
||||
<sub>📊 Full methodology & per-feature detail vs 9router, OpenRouter, CLIProxyAPI & LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)</sub>
|
||||
|
||||
@@ -559,7 +559,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
|
||||
- **🖼️ New endpoints** — `/v1/ocr` (Mistral OCR) and `/v1/audio/translations` (Whisper-style) round out the media surface. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Freepik, Adobe Firefly, Microsoft Designer, Segmind, EdgeTTS. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **🌍 Deployment & ops** — reverse-proxy `basePath`, browser-language auto-detect, per-key device tracking, root-less MITM trust, zh-TW localization. → [Environment](docs/reference/ENVIRONMENT.md)
|
||||
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **341-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
|
||||
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **342-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
|
||||
- **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
|
||||
- **⚡ Local performance & infra** — one-click local Redis, Cloudflare Workers / Deno Deploy relay deployers, Bifrost & Mux as supervised embedded services. → [Embedded Services](docs/frameworks/EMBEDDED-SERVICES.md)
|
||||
|
||||
@@ -642,11 +642,11 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
|
||||
<div align="center">
|
||||
|
||||
## 🌐 341 AI Providers — 90+ Free
|
||||
## 🌐 342 AI Providers — 90+ Free
|
||||
|
||||
</div>
|
||||
|
||||
> The most complete catalog of any open-source router: **341 providers**, **90+ with a free tier**, **56 free forever**.
|
||||
> The most complete catalog of any open-source router: **342 providers**, **90+ with a free tier**, **56 free forever**.
|
||||
|
||||
<div align="center">
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
- **feat(providers):** new `cursor-api` provider (card "Cursor API", alias `cua`): connect a Cursor user API key (`crsr_…`) and route `cursor-api/<model>` through the existing Cursor agent executor (the key is exchanged for a 1h session token and cached), plus a `/api/cursor-cli/*` passthrough so the Cursor CLI itself runs through OmniRoute (`CURSOR_API_ENDPOINT=http://<omniroute>/api/cursor-cli`, `CURSOR_API_KEY=<OmniRoute key>`) with every RPC attributed and logged. The IDE `cursor` provider is unchanged. (#10729)
|
||||
1
changelog.d/features/10771-health-root-endpoint.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(api):** `GET /api/health` now answers `{ status, timestamp }` without a key. Until now the path had no route, so the management-auth boundary answered first with a 401 — indistinguishable from a wrong key or an unknown route, which left Docker HEALTHCHECKs and Kubernetes probes unable to tell "down" from "misconfigured". Kept deliberately minimal: version, uptime and memory stay behind the authenticated `/api/monitoring/health` ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/10771)).
|
||||
@@ -0,0 +1 @@
|
||||
- feat(routing): make Task-Aware Smart Routing's detection patterns operator-configurable via `settings.taskRouting.patternOverrides` (`PUT /api/settings/task-routing`) — the built-in patterns are English-only, so a non-English dashboard had no recourse short of turning detection off entirely; an override now replaces the pattern list for one task type without touching the rest (#10783)
|
||||
1
changelog.d/features/kimi-coding-extra-usage.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(usage):** show Kimi Coding's fixed-order Code 5-hour/7-day quota windows plus Extra Usage status, balance, monthly spend/limit, and the official Additional Credits link on Dashboard → Quota cards.
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(providers):** copilot-m365-web chat turns no longer surface as `(empty response)` — the type:4 invocation is aligned with the 2026-08 wire shape and now carries its type:1 Metrics follow-up in the same socket write, and the access token pre-flight-refreshes from a stored refresh_token instead of requiring a DevTools re-capture every ~75 minutes ([#10732](https://github.com/diegosouzapw/OmniRoute/pull/10732) — thanks @acc0mplish)
|
||||
1
changelog.d/fixes/10734-combo-context-generic-default.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(catalog):** stop counting `getTokenLimit()`'s generic 128k catch-all as a known combo window, so `/v1/models` advertises the min of sourced member contexts instead of collapsing a 500k combo to 128k ([#10734](https://github.com/diegosouzapw/OmniRoute/issues/10734))
|
||||
1
changelog.d/fixes/10769-cache-stats-real-cache.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(api):** `/api/cache/stats` reported the prompt-cache LRU, which no request path ever writes to — it answered `0 hit / 0 miss, size 0` while the semantic cache served real traffic, and the Health and Usage dashboards rendered that as fact. It now reports the semantic cache's in-memory entries, with the same response shape ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/10769)) — thanks @Poid-ZA, who first fixed this in #9446.
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(logging):** the app log is filterable and readable again. Entries from the tagged logger (`[LEVEL] [TAG] message`) were filed under the level instead of the component, and printf format strings were never applied, so `%s`/`%d` stayed literal with the values trailing behind them unlabelled — including every LiveWS connection line, where the format is deliberate hardening against injected format specifiers ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/10770)).
|
||||
1
changelog.d/fixes/10774-claude-code-flat-rate.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(analytics):** Claude Code (`claude`/`cc`) is a flat-rate subscription, so cost analytics reports `$0` for it instead of estimating Anthropic list prices — the metered `anthropic` API keeps its real cost, and budget/quota/routing still estimate as before ([#10774](https://github.com/diegosouzapw/OmniRoute/pull/10774)) — thanks @electrumguy
|
||||
1
changelog.d/fixes/10781-wal-truncate-scheduler.md
Normal file
@@ -0,0 +1 @@
|
||||
- fix(db): periodically run `wal_checkpoint(TRUNCATE)` so the SQLite WAL file shrinks on long-running servers (default 6h, override with `OMNIROUTE_WAL_TRUNCATE_INTERVAL_MS`, `0` disables) (#10781)
|
||||
1
changelog.d/fixes/10782-ws-heartbeat-ping-pong.md
Normal file
@@ -0,0 +1 @@
|
||||
- fix(sse): replace LiveWS's application-only liveness check with a protocol-level `ws.ping()`/`pong` heartbeat (RFC 6455 §5.5.2) alongside the existing one, so a read-only dashboard subscriber that never sends anything survives the connection timeout — a socket that stops reading frames entirely is still reaped exactly as before (#10782)
|
||||
1
changelog.d/fixes/10792-double-transport-retry-scope.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(resilience):** scope the same-account transport retry (#9708) out of emergency-fallback and combo hops — it was retrying the free fallback model and combo targets too, doubling upstream calls and corrupting the terminal error status on those paths.
|
||||
1
changelog.d/fixes/9692-openai-to-claude-tool-images.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(translator):** convert OpenAI `image_url` blocks nested in `role: "tool"` / `tool_result` content to Claude `image` source blocks so OpenAI-compatible clients (Kimi Code CLI `ReadMediaFile`, and any other tool that returns media) no longer 400 the next Claude-format upstream turn ([#9692](https://github.com/diegosouzapw/OmniRoute/issues/9692))
|
||||
1
changelog.d/fixes/9708-codex-same-account-retry.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(resilience):** retry a retryable Codex pre-output 502/503/504/507 once on the same account (2–3s jitter) before cooling the connection, and stop translating that mixed pool into an all-accounts quota `429` ([#9708](https://github.com/diegosouzapw/OmniRoute/issues/9708))
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(electron):** desktop window stays hidden on Windows because the embedded Next.js server binds to the machine hostname instead of loopback ([#PENDING](https://github.com/diegosouzapw/OmniRoute/pull/PENDING))
|
||||
1
changelog.d/fixes/assemble-standalone-cpsync-race.md
Normal file
@@ -0,0 +1 @@
|
||||
- fix(build): tolerate a same-realpath symlink or stale-typed dest in the standalone bundle assembler, fixing non-deterministic `ERR_FS_CP_EINVAL`/`ERR_FS_CP_DIR_TO_NON_DIR` crashes under heavy concurrent build I/O
|
||||
@@ -0,0 +1 @@
|
||||
- chore(security): remove the unused `enforceSecrets()` duplicate of the boot secret check and pin the live `enforceWebRuntimeEnv()` wiring with a regression test (#10775)
|
||||
1
changelog.d/maintenance/10779-combo-invocation-docs.md
Normal file
@@ -0,0 +1 @@
|
||||
- **docs:** Custom combos are only invoked by their exact name in the `model` field — `auto` remains a separate zero-config router, and `openrouter/auto` is a paid OpenRouter product, not an alias ([#10779](https://github.com/diegosouzapw/OmniRoute/pull/10779)) — thanks @maxmad64bis
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(tests):** realign the two `stream-utils` passthrough cases that still asserted the pre-#10017 SSE framing — the event-boundary case declares the OpenAI Responses client format it actually exercises, and the metadata case now pins that surviving lines stay inside one event instead of expecting the `:`/`id:` control lines that #10473 stopped forwarding to every client format.
|
||||
@@ -442,12 +442,14 @@
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 1062,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 1051,
|
||||
"src/shared/components/ModelSelectModal.tsx": 1138,
|
||||
"src/shared/constants/providers/apikey/gateways.ts": 1255,
|
||||
"src/shared/constants/providers/apikey/gateways.ts": 1268,
|
||||
"open-sse/vendor/codex-chatgpt-web/bridge.ts": 1387,
|
||||
"_rebaseline_2026_08_11_v3850_merge_storm_provider_registry": "DRIFT do merge-storm 2026-08-11 (99 PRs mergeados no release/v3.8.50). AddApiKeyModal.tsx (PR #8949 ChatGPT Web provider) e useProviderConnections.ts/ModelSelectModal.tsx (PRs #9011 combo test-all, #9499 image combos) = UI nova legitima acima do cap; gateways.ts = god-file de catalogo de providers que cresceu com PRs #9009/#9421/#9468/#9594 (qualquer split arriscaria corromper o merge de novo — o proprio PR #9421 quebrou o arquivo); bridge.ts (PR #8949) = ponte Chromium vendored; proxyFetch.ts 1207->1220 = drift herdado de merges. Owner autorizou rebaseline com anotacao (2026-08-11).",
|
||||
"src/lib/modelCapabilities.ts": 1006,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts": 1014,
|
||||
"open-sse/config/imageRegistry.ts": 1019
|
||||
"open-sse/config/imageRegistry.ts": 1019,
|
||||
"src/sse/handlers/chatHelpers.ts": 1017,
|
||||
"src/shared/middleware/chatBodyAdmission.ts": 1005
|
||||
},
|
||||
"_rebaseline_base_2026_08_10_proxyfetch": "Base-red fix (green-prs sweep, issue #9985): open-sse/utils/proxyFetch.ts 1207 > cap 1000 — new proxied-TLS fetch helper introduced by the Fal reference-image work. Owner-authorized quick rebaseline to green; structural slim tracked for v3.9.0.",
|
||||
"_rebaseline_2026_07_27_v3849_train2": "Merge-train 2 (7 PRs) — owner-approved 2026-07-27. Single entry: chatCore.ts 4955->5006 (#8595, Responses multi-turn image compaction before the context hard-reject). Genuine irreducible growth at the existing compaction chokepoint in handleChatCore — the PR adds a last-resort retry against the concrete budget plus the estimateFinalInputTokens helper, both wired at the pre-existing call site rather than a new branch. Covered by tests/unit/8560-responses-image-compaction.test.ts (4 tests).",
|
||||
@@ -612,5 +614,6 @@
|
||||
"_rebaseline_2026_08_12_proxyfetch_redaction": "Base-reds round 3 (#9985): proxyFetch.ts 1220->1239 (+19) = redactProxyDetailsInMessage() helper closing the credential leak #10032 reintroduced (raw proxy URL with user:password appended to the propagated error, Hard Rule #12); irreducible security fix at the existing error-surface chokepoint. Covered by tests/unit/tls-proxy-context.test.ts (strengthened leak guards).",
|
||||
"_rebaseline_2026_08_12_modelcapabilities_snapshot_routing": "Base-reds round 3 (#9985): modelCapabilities.ts crossed the new-file cap at 1006 (+~10) when the context/max-input-token override lookups were routed through the #9199 bulk snapshot (fixing 323 per-model SQLite reads per catalog prepare — auto-combo-context-advertising guard); cohesive change at the existing resolution chokepoints, not extractable. Covered by tests/unit/auto-combo-context-advertising.test.ts + model-capability-resolution-snapshot-9199.test.ts.",
|
||||
"_rebaseline_2026_08_14_imagetotext_servicekinds": "Image-to-Text category (#10275/#10291): gateways.ts grew 1250→1255 by data lines only — the serviceKinds: [\"llm\", \"imageToText\"] declarations on the openrouter and chutes catalog entries, plus the 3-line comment recording why chutes needs no static dots.ocr entry (passthroughModels discovery). No new logic or branching; the file is a provider catalog of declarative metadata. Splitting a catalog for five lines would be worse than the growth (semantic-families rule).",
|
||||
"_rebaseline_2026_08_18_imageregistry_merge_train": "merge-train 2026-08-18 (owner-authorized, /merge-prs batch of 84): open-sse/config/imageRegistry.ts crossed the 1000-line new-file cap for the first time purely from combining three independent, already-legitimate provider registrations boarded in the same local merge-train — #10542 (aihorde optional-key image catalog), #10494 (gemini-web image generation), #10594 (freepik/magnific provider rename + validation). 996 on release tip -> 1019 on the train tip. Each PR individually adds a small, additive IMAGE_PROVIDERS registry entry at the existing chokepoint; none crosses the cap alone. Not modularized as part of this train's gate fix (out of scope for a merge reconciliation, not a feature change). Covered by each PR's own focused tests (aihorde-image-catalog/generation, gemini-web image tests, freepik/magnific provider tests)."
|
||||
}
|
||||
"_rebaseline_2026_08_18_imageregistry_merge_train": "merge-train 2026-08-18 (owner-authorized, /merge-prs batch of 84): open-sse/config/imageRegistry.ts crossed the 1000-line new-file cap for the first time purely from combining three independent, already-legitimate provider registrations boarded in the same local merge-train — #10542 (aihorde optional-key image catalog), #10494 (gemini-web image generation), #10594 (freepik/magnific provider rename + validation). 996 on release tip -> 1019 on the train tip. Each PR individually adds a small, additive IMAGE_PROVIDERS registry entry at the existing chokepoint; none crosses the cap alone. Not modularized as part of this train's gate fix (out of scope for a merge reconciliation, not a feature change). Covered by each PR's own focused tests (aihorde-image-catalog/generation, gemini-web image tests, freepik/magnific provider tests).",
|
||||
"_rebaseline_2026_08_20_v3850_merge_train_batch1": "Merge-train batch1 (2026-08-19/20, 30 PRs boarded onto release/v3.8.50): gateways.ts 1255->1268 = PR #10722 (Token Kiosk OpenAI-compatible provider gateway catalog entry, +13 declarative lines, same god-file no-split rationale as prior gateways.ts rebaselines); chatHelpers.ts (uncapped, not previously frozen) new 1017 = PR #10797 (relay/bifrost error normalization, +23/-2, own-PR growth, existing file already near cap from accumulated chokepoint wiring per its own rebaseline history above); chatBodyAdmission.ts (uncapped) new 1005 = pre-existing base-red on the pure release tip (1004>1000 before this train boarded anything, no PR in this batch touches this file) — frozen here at its current size, not authorizing further growth. Owner-authorized rebaseline (2026-08-19 merge-prs session)."
|
||||
}
|
||||
@@ -166,7 +166,8 @@
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"zizmorFindings": {
|
||||
"value": 190,
|
||||
"value": 192,
|
||||
"_rebaseline_2026_08_20_radar_export_workflow": "190 -> 192 (+2). Workflow novo `.github/workflows/radar-export.yml` (passo 10 do go-live do Radar: publica o export estável do catálogo como asset de release para o servidor privado baixar via RADAR_EXPORT_URL). Os +2 são unpinned-uses @vN: actions/checkout@v7 + actions/setup-node@v7 — a MESMA convenção deliberada de todos os workflows (ver _scanner_harden_workflows_2026_06_16); fixar por SHA só este violaria a convenção. O findings artipacked do checkout foi CORRIGIDO com `persist-credentials: false` (o job publica via GH_TOKEN em `gh release`, não usa a credencial do checkout). Nenhuma classe nova de template-injection / cache-poisoning / dangerous-triggers. Medido local com zizmor 1.25.2 via `node scripts/check/check-workflows.mjs --ratchet` = 191; +1 do delta conhecido do runner (ver _rebaseline_2026_07_28_ci_runner_delta: o runner enxerga 1 unpinned-uses @vN a mais que o devbox no mesmo commit; a baseline segue o runner) => 192.",
|
||||
"_rebaseline_2026_07_20_aliasresolver_hook_split_7808": "175 -> 176 (+1). Companion to PR #7808 (CodeQL js/incomplete-url-substring-sanitization fix in bin/aliasResolver.mjs). The +1 is NOT caused by this PR's code changes (bin/* is not a workflow file) — it is a pre-existing drift that surfaced because the ratchet gate runs on this PR's CI: the zizmor scanner version on the GitHub runner gained a new rule (or extended an existing one) since the v3.8.49 baseline was seeded on 2026-07-17. Breakdown: the new finding is an unpinned-uses @vN class item on one of the existing workflows (same deliberate convention as _scanner_harden_workflows_2026_06_16 — @vN is intentional, SHA-pinning only this one would violate the convention). No new template-injection/artipacked/cache-poisoning/dangerous-triggers classes introduced. Measured by the Quality Gates (Extended) job on run 29713001401 = 176, baseline was 175. Note: by the time this landed on release/v3.8.49, the baseline was already at 176 via _rebaseline_2026_07_17_combo_recovery_hints — this entry is kept as historical record; no further bump applied.",
|
||||
"_rebaseline_2026_07_17_v3849_release": "169 -> 175 (+6). Cycle workflow drift (v3.8.48/v3.8.49): npm-publish.yml (new, WS1.3 #7092), electron-release.yml, nightly-compat.yml, nightly-release-green.yml, CI restructures (#7501 full-history base fetch, #7355 main-green, #7202 merge-queue gates, Trunk/Codecov). Breakdown vs v3.8.47: +3 unpinned-uses (@vN convention, deliberate per _scanner_harden_workflows_2026_06_16), +2 cache-poisoning (artifact upload/cache in the OWN electron-release/npm-publish RELEASE workflows -- operator-controlled, not fork-PR exploitable), +1 excessive-permissions (nightly-compat.yml permissions:issues). No new template-injection/artipacked/dangerous-triggers. Measured with zizmor 1.25.2 via `node scripts/check/check-workflows.mjs --ratchet` = 175 on da3a0be69.",
|
||||
"direction": "down",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (341 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over the 80+ command surface: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (342 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over the 80+ command surface: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<desc>Compact animated terminal cycling three real OmniRoute CLI commands with a typewriter effect and a scrolling subcommand ticker; the first frame shows the completed providers-list screen.</desc>
|
||||
<defs><clipPath id="tickerClip"><rect x="12" y="304" width="1176" height="40"/></clipPath><clipPath id="tw0"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;31;61;92;122;153;184;214;245;245" keyTimes="0;0.012;0.018;0.024;0.030;0.036;0.042;0.048;0.054;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw1"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;26;51;76;102;128;153;178;204;204" keyTimes="0;0.345;0.351;0.357;0.363;0.369;0.375;0.381;0.387;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw2"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;20;41;61;82;102;122;143;163;163" keyTimes="0;0.678;0.684;0.690;0.696;0.702;0.708;0.714;0.720;1" dur="18s" repeatCount="indefinite"/></rect></clipPath></defs>
|
||||
<rect width="1200" height="350" fill="#0d1117"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 341 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 109 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 342 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 109 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<desc>Static-header comparison table where each capability row fades in top to bottom; the OmniRoute column is highlighted and shows a check or a leading value in every row, while competitors show a mix of checks, partials and crosses.</desc>
|
||||
<defs>
|
||||
<pattern id="gC" width="32" height="32" patternUnits="userSpaceOnUse"><path d="M 32 0 L 0 0 0 32" fill="none" stroke="#ffffff" stroke-opacity="0.05" stroke-width="1"/></pattern>
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint, 341 providers — never stop building, OmniRoute picks the cheapest one that works. Six pillars. Never hit limits: auto-fallback across 341 providers in milliseconds, quota out means the next provider takes over with zero downtime. Save up to 95 percent of tokens: RTK plus Caveman stacked compression cuts 15 to 95 percent of eligible tokens, about 89 percent average on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier, 56 free forever — Qoder, Pollinations, Cloudflare, SiliconFlow — no card needed. Every tool works: 33 coding agents including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation — point any tool at /v1 and it just works. Production-grade: circuit breakers, TLS stealth, MCP with 109 tools, A2A, memory, guardrails, evals — 25,000+ tests.">
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint, 342 providers — never stop building, OmniRoute picks the cheapest one that works. Six pillars. Never hit limits: auto-fallback across 342 providers in milliseconds, quota out means the next provider takes over with zero downtime. Save up to 95 percent of tokens: RTK plus Caveman stacked compression cuts 15 to 95 percent of eligible tokens, about 89 percent average on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier, 56 free forever — Qoder, Pollinations, Cloudflare, SiliconFlow — no card needed. Every tool works: 33 coding agents including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation — point any tool at /v1 and it just works. Production-grade: circuit breakers, TLS stealth, MCP with 109 tools, A2A, memory, guardrails, evals — 25,000+ tests.">
|
||||
<desc>Animated promise card: six pillar tiles fade in in reading order, then a soft colored border highlight sweeps from tile to tile in a continuous cycle.</desc>
|
||||
<defs>
|
||||
<pattern id="gridPaperP" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
@@ -21,7 +21,7 @@
|
||||
<line x1="150" y1="53" x2="1160" y2="53" stroke="#232b38" stroke-width="1.5"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">341 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
|
||||
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">342 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
|
||||
</g>
|
||||
|
||||
<g font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif">
|
||||
@@ -38,7 +38,7 @@
|
||||
<line x1="3.9" y1="3.9" x2="18.1" y2="18.1"/>
|
||||
</g>
|
||||
<text x="102" y="170" font-size="18" font-weight="800" fill="#74b9ff">Never hit limits</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 341 providers in</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 342 providers in</text>
|
||||
<text x="66" y="226" font-size="13.5" fill="#a1a1aa">milliseconds. Quota out? The next provider</text>
|
||||
<text x="66" y="248" font-size="13.5" fill="#a1a1aa">takes over — zero downtime.</text>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 341 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 341 AI providers, 90+ free tiers, about 1.51B free tokens per month, 15 to 95 percent token savings, 19 routing strategies, zero dollars to start.">
|
||||
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 342 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 342 AI providers, 90+ free tiers, about 1.51B free tokens per month, 15 to 95 percent token savings, 19 routing strategies, zero dollars to start.">
|
||||
<desc>Animated hero card: a pulse travels the divider line and a compression bar demo repeatedly shrinks a prompt by up to 95 percent; all headline content is static and readable on the first frame.</desc>
|
||||
<defs>
|
||||
<pattern id="gridPaperH" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
@@ -28,7 +28,7 @@
|
||||
<text x="48" y="138" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="60" font-weight="800" fill="#e9edf3">Never stop coding<tspan fill="#a855f7">.</tspan></text>
|
||||
|
||||
<!-- subheadline -->
|
||||
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">341 providers</tspan> — <tspan fill="#7ee787" font-weight="800">90+ free</tspan> — through one endpoint.</text>
|
||||
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">342 providers</tspan> — <tspan fill="#7ee787" font-weight="800">90+ free</tspan> — through one endpoint.</text>
|
||||
|
||||
<!-- plug line -->
|
||||
<text x="48" y="222" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="16.5" fill="#a1a1aa">Claude Code · Codex · Cursor · Cline · Copilot · Antigravity  →  <tspan fill="#7ee787" font-weight="700">FREE</tspan> Claude / GPT / Gemini · auto-fallback</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
@@ -189,7 +189,7 @@ Use `auto/smart` when you want the best quality and are okay with occasional exp
|
||||
|
||||
### "Can I force a specific provider?"
|
||||
|
||||
Yes! Use a combo with `priority` strategy instead of `auto`. See the [Technical Reference](../routing/AUTO-COMBO.md) for details.
|
||||
Yes! Use a combo with `priority` strategy instead of `auto`, then send the combo's **exact name** as the `model` field (e.g. `model: "my-combo"` — not `auto`). See the [Technical Reference](../routing/AUTO-COMBO.md) for details.
|
||||
|
||||
### "How is this different from round-robin?"
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
123
docs/providers/CURSOR-API-KEY-AND-CLI.md
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
title: "Cursor API provider and the Cursor CLI passthrough"
|
||||
version: 3.8.50
|
||||
lastUpdated: 2026-08-19
|
||||
---
|
||||
|
||||
# Cursor API provider and the Cursor CLI passthrough
|
||||
|
||||
Two ways to put Cursor behind OmniRoute without an IDE session:
|
||||
|
||||
1. **`cursor-api` provider** (card "Cursor API", alias `cua`): an API-key
|
||||
provider that holds a Cursor user API key (`crsr_…`, generated at
|
||||
`https://cursor.com/dashboard/api`). Any OmniRoute client then reaches
|
||||
Cursor models through `/v1/chat/completions` as `cursor-api/<model>` or
|
||||
`cua/<model>`, with the usual quota, fallback and logging layers. The IDE
|
||||
provider (`cursor`, OAuth/IDE session) is unchanged.
|
||||
2. **Cursor CLI passthrough**: point the Cursor CLI (`agent`) at OmniRoute so
|
||||
every RPC the CLI makes is authenticated with an OmniRoute API key, forwarded
|
||||
to Cursor with a `cursor-api` connection's credential, and recorded in the
|
||||
Logs page.
|
||||
|
||||
## Why the key is exchanged
|
||||
|
||||
`api2.cursor.sh` rejects a raw `crsr_…` key as a Bearer token (401). The Cursor
|
||||
CLI first POSTs the key to `/auth/exchange_user_api_key` and receives a session
|
||||
JWT that expires after one hour; the returned `refreshToken` carries the same
|
||||
`exp`, so refreshing means re-exchanging the key.
|
||||
`open-sse/services/cursorApiKeyAuth.ts` does that exchange, caches one session
|
||||
token per key, re-exchanges five minutes before expiry and drops the cached
|
||||
token when Cursor answers 401. `CursorExecutor` calls it right before opening
|
||||
the upstream stream for `cursor-api` connections.
|
||||
|
||||
## The `cursor-api` provider
|
||||
|
||||
Registry: `open-sse/config/providers/registry/cursor/index.ts`
|
||||
(`cursor_apiProvider`, `authType: "apikey"`, same `format`, `baseUrl` and
|
||||
`models` as `cursor`). Catalog card:
|
||||
`src/shared/constants/providers/apikey/specialty-media.ts`. Executor map:
|
||||
`open-sse/executors/index.ts` (`"cursor-api"` / `cua` →
|
||||
`new CursorExecutor("cursor-api")`).
|
||||
|
||||
Dashboard: Providers → Cursor API → Add API key.
|
||||
|
||||
REST:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST http://localhost:20128/api/providers \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"provider":"cursor-api","name":"cursor-api-key","apiKey":"crsr_…","priority":1}'
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
curl -sS http://localhost:20128/v1/chat/completions \
|
||||
-H "Authorization: Bearer <omniroute-api-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model":"cursor-api/auto","messages":[{"role":"user","content":"say PONG"}]}'
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Model listing for `cursor-api` comes from the static Cursor registry (the
|
||||
same list the IDE provider falls back to); no `cursor-agent` install is
|
||||
needed on the OmniRoute host.
|
||||
- `POST /api/providers/{id}/refresh-cursor` is for the `cursor` IDE provider
|
||||
only; `cursor-api` connections have no IDE session to renew.
|
||||
|
||||
## Cursor CLI passthrough
|
||||
|
||||
Route: `src/app/api/cursor-cli/[...path]/route.ts` →
|
||||
`open-sse/handlers/cursorCliProxy.ts`. The prefix `/api/cursor-cli/` is
|
||||
registered in `src/shared/constants/publicApiRoutes.ts` because the handler
|
||||
enforces its own authentication:
|
||||
|
||||
| Path | Auth expected from the CLI | What OmniRoute does |
|
||||
| ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `POST /auth/exchange_user_api_key` | `Bearer <OmniRoute API key>` | Validates the key, mints a 1h HS256 JWT (signed with `JWT_SECRET`) and returns it |
|
||||
| every other path (`/aiserver.v1.*`, `/agent.v1.AgentService/RunSSE`, `/aiserver.v1.BidiService/BidiAppend`, `/v1/traces`, …) | `Bearer <that JWT>` | Verifies issuer/audience/expiry, picks an active `cursor-api` connection, swaps the Authorization header for the exchanged Cursor token and streams the reply back |
|
||||
|
||||
The CLI decodes `exp` from whatever token it receives, so handing it an opaque
|
||||
token makes it re-exchange before almost every request; the minted JWT avoids
|
||||
that. A 401 from OmniRoute makes the CLI exchange again.
|
||||
|
||||
### Setup
|
||||
|
||||
1. Create an OmniRoute API key (Dashboard → API keys) and a `cursor-api`
|
||||
connection.
|
||||
2. Tell the CLI to use HTTP/1.1 for the agent stream. In
|
||||
`~/.cursor/cli-config.json`:
|
||||
|
||||
```json
|
||||
{ "network": { "useHttp1ForAgent": true } }
|
||||
```
|
||||
|
||||
Without this the CLI opens the agent turn over HTTP/2 to a separately
|
||||
configured agent host and only the control-plane RPCs go through the
|
||||
endpoint.
|
||||
|
||||
3. Run the CLI against OmniRoute:
|
||||
|
||||
```bash
|
||||
export CURSOR_API_ENDPOINT=http://localhost:20128/api/cursor-cli
|
||||
export CURSOR_API_KEY=<omniroute-api-key>
|
||||
agent -p --trust "Reply with exactly OK"
|
||||
```
|
||||
|
||||
Every hop lands in Logs as provider `cursor-api`, request type `cursor-cli`,
|
||||
path `/api/cursor-cli/<rpc>`, attributed to the OmniRoute API key and the
|
||||
connection that served it.
|
||||
|
||||
### Failure modes
|
||||
|
||||
| Situation | Response to the CLI |
|
||||
| ------------------------------------------------ | --------------------------------------------- |
|
||||
| Unknown OmniRoute key and `REQUIRE_API_KEY=true` | 401 `unauthenticated` on exchange |
|
||||
| `REQUIRE_API_KEY=false` | anonymous session (mirrors `/v1/*` behaviour) |
|
||||
| Expired / foreign / tampered session JWT | 401, the CLI re-exchanges |
|
||||
| OmniRoute API key revoked after exchange | 401 on the next RPC |
|
||||
| No active `cursor-api` connection | 503 `unavailable` |
|
||||
| Cursor rejects the connection's key | 401 `unauthenticated`, cached session dropped |
|
||||
| Upstream unreachable | 502 `unavailable` (sanitized message) |
|
||||
| `JWT_SECRET` unset | 503 on exchange |
|
||||
@@ -7,6 +7,7 @@
|
||||
"CHATGPT_WEB",
|
||||
"AGENTROUTER",
|
||||
"ZED-DOCKER",
|
||||
"CURSOR-DOCKER"
|
||||
"CURSOR-DOCKER",
|
||||
"CURSOR-API-KEY-AND-CLI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ OmniRoute uses **SQLite** (via `better-sqlite3`) for all persistence. These vari
|
||||
| `OMNIROUTE_CRYPT_KEY` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** for `STORAGE_ENCRYPTION_KEY`. Accepted as a fallback when the primary variable is absent. |
|
||||
| `OMNIROUTE_API_KEY_BASE64` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** (Base64-encoded form) accepted as a fallback. Decoded automatically before use. |
|
||||
| `OMNIROUTE_DB_HEALTHCHECK_INTERVAL_MS` | _(unset)_ | `src/lib/db/core.ts` | Override the periodic SQLite healthcheck interval (ms). When unset, defaults are derived from `NODE_ENV`. |
|
||||
| `OMNIROUTE_WAL_TRUNCATE_INTERVAL_MS` | `21600000` (6h) | `src/lib/db/core.ts` | Override the periodic `wal_checkpoint(TRUNCATE)` interval (ms). Auto-checkpoint never shrinks the WAL file itself, and a long-running server never closes its DB. `0` disables. |
|
||||
| `OMNIROUTE_SKIP_DB_HEALTHCHECK` | `0` | `src/lib/db/core.ts`, `src/lib/db/healthCheck.ts` | Set to `1` to skip the DB healthcheck entirely on startup. Useful for short-lived tasks and integration tests. |
|
||||
| `OMNIROUTE_FORCE_DB_HEALTHCHECK` | `0` | `src/lib/db/core.ts` | Set to `1` to force the DB healthcheck loop on, even when it would normally be skipped (e.g., short-lived tasks). |
|
||||
| `OMNIROUTE_SKIP_POSTINSTALL` | `0` | `scripts/postinstall.mjs` | Set to `1` to skip the native-runtime warm-up during `npm install`. Useful in CI/headless installs where sqlite is already built. |
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
---
|
||||
title: "Provider Reference"
|
||||
version: 3.8.50
|
||||
lastUpdated: 2026-08-18
|
||||
lastUpdated: 2026-08-19
|
||||
---
|
||||
|
||||
# Provider Reference
|
||||
|
||||
> **Auto-generated** from `src/shared/constants/providers.ts` — do not edit by hand.
|
||||
> Regenerate with: `npm run gen:provider-reference`
|
||||
> **Last generated:** 2026-08-18
|
||||
> **Last generated:** 2026-08-19
|
||||
|
||||
Total providers: **341**. See category breakdown below.
|
||||
Total providers: **342**. See category breakdown below.
|
||||
|
||||
## Categories
|
||||
|
||||
@@ -91,7 +91,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
| `chatgpt-web-codex` | `cgpt-codex` | ChatGPT Web (Codex) | Web cookie | [link](https://chatgpt.com) | Paste the full ChatGPT Cookie header. OmniRoute verifies it in an isolated headless browser profile. | native |
|
||||
| `claude-web` | `cw` | Claude Web | Web cookie | [link](https://claude.ai) | Paste your session cookie from claude.ai | none |
|
||||
| `conol-web` | `cnl` | Conol (Unofficial/Experimental) | Web cookie | [link](https://conol.ai) | Use browser sign-in, or paste the full Cookie header from conol.ai. The __Secure-better-auth.session_token cookie is required. | — |
|
||||
| `copilot-m365-web` | `m365copilot` | Microsoft 365 Copilot (BizChat) | Web cookie | [link](https://m365.cloud.microsoft/chat) | Sign in at m365.cloud.microsoft/chat, then open DevTools → Network → filter 'WS' → click the Chathub WebSocket connection. Copy both the access_token query parameter AND the account-specific Chathub path segment from its request URL (wss://…/Chathub/<path>?…&access_token=…). It is NOT an Authorization: Bearer header on an XHR/Fetch request. The token is short-lived; this is an unofficial integration. | — |
|
||||
| `copilot-m365-web` | `m365copilot` | Microsoft 365 Copilot (BizChat) | Web cookie | [link](https://m365.cloud.microsoft/chat) | Sign in at m365.cloud.microsoft/chat, then open DevTools → Network → filter 'WS' → click the Chathub WebSocket connection. Copy both the access_token query parameter AND the account-specific Chathub path segment from its request URL (wss://…/Chathub/<path>?…&access_token=…). It is NOT an Authorization: Bearer header on an XHR/Fetch request. The token is short-lived; this is an unofficial integration. Optional: store a refresh_token in providerSpecificData.refreshToken (any Microsoft device-code/refresh flow for the substrate.office.com/sydney scopes) and OmniRoute pre-flight-refreshes the access token itself — otherwise re-capture after every ~75 min expiry. | — |
|
||||
| `copilot-web` | `copilot` | Microsoft Copilot Web | Web cookie | [link](https://copilot.microsoft.com) | Paste the access_token from an authenticated copilot.microsoft.com request (DevTools → Network → Authorization), or export a HAR while logged in | — |
|
||||
| `deepseek-web` | `ds-web` | DeepSeek Web | Web cookie | [link](https://chat.deepseek.com) | Paste your userToken from chat.deepseek.com — DevTools → Application → Local Storage → userToken | emulated |
|
||||
| `doubao-web` | `db` | Dola Web (ByteDance) | Web cookie | [link](https://www.dola.com) | Paste the full Cookie header from www.dola.com. It should include sessionid, ttwid, and s_v_web_id. If s_v_web_id is unavailable, fp=verify_... from a chat/completion request URL can be used as a fallback. | — |
|
||||
@@ -120,7 +120,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
| `zai-web` | `zw` | Z.ai Web | Web cookie | [link](https://chat.z.ai) | Copy the "token" value from chat.z.ai → DevTools → Application → Local Storage. Do not copy cookies; OmniRoute handles the per-request CAPTCHA through its browser transport. | — |
|
||||
| `zenmux-free` | `zmf` | ZenMux Free (Web) | Web cookie | [link](https://zenmux.ai) | Login at zenmux.ai, then export all cookies using EditThisCookie or Cookie-Editor and paste the full Cookie header string here. Refresh every ~30 days. | — |
|
||||
|
||||
## API Key Providers (paid / paid-with-free-credits) (228)
|
||||
## API Key Providers (paid / paid-with-free-credits) (229)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
@@ -169,6 +169,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
| `command-code` | `cmd` | Command Code | API key | [link](https://commandcode.ai/) | Use a Command Code API key. Requests are sent to Command Code's /alpha/generate endpoint. |
|
||||
| `coze` | `coze` | Coze | API key | [link](https://coze.com) | Get API key at coze.com/open/api |
|
||||
| `crof` | `crof` | CrofAI | API key | [link](https://crof.ai) | — |
|
||||
| `cursor-api` | `cua` | Cursor API | API key | [link](https://cursor.com/dashboard/api) | Paste a Cursor user API key (crsr_...) from cursor.com/dashboard/api. OmniRoute exchanges it for a session token on demand; no IDE or cursor-agent install is needed. Usage bills to the Cursor plan that owns the key. |
|
||||
| `dahl` | `dahl` | Dahl | API key | [link](https://inference.dahl.global) | Click 'Add Account' to auto-generate a token, or add a manual API key. |
|
||||
| `databricks` | `databricks` | Databricks | API key, enterprise | [link](https://www.databricks.com) | — |
|
||||
| `datarobot` | `datarobot` | DataRobot | API key, enterprise | [link](https://docs.datarobot.com) | Use your DataRobot API token. Optional Base URL can be the account root (for LLM Gateway) or a deployment URL under /api/v2/deployments/<id>. |
|
||||
@@ -323,7 +324,8 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
|
||||
| `tinyfish` | `tf` | TinyFish Fetch | API key | [link](https://docs.tinyfish.ai/fetch-api) | X-API-Key from agent.tinyfish.ai/api-keys |
|
||||
| `together` | `together` | Together AI | API key, video | [link](https://www.together.ai) | — |
|
||||
| `tokenreply` | `tokenreply` | TokenReply | API key, aggregator | [link](https://www.tokenreply.com) | Free-tagged models have model- and campaign-specific daily limits; no fixed global free quota is published. |
|
||||
| `tokenrouter` | `trk` | TokenRouter | API key | [link](https://tokenrouter.com) | Use your TokenRouter API key in Authorization: Bearer <key>. Fully OpenAI-compatible. API base URL: https://api.tokenrouter.com/v1. |
|
||||
| `tokenrouter` | `trk` | TokenRouter | API key | [link](https://tokenrouter.com) | Use your TokenRouter API key in Authorization: Bearer *** Fully OpenAI-compatible. API base URL: https://api.tokenrouter.com/v1. |
|
||||
| `token-kiosk` | `tk` | Token Kiosk | API key | [link](https://agent-router.gaib.ai) | Use your Token Kiosk API key in Authorization: Bearer *** Fully OpenAI-compatible. API base URL: https://agent-router.gaib.ai/v1. |
|
||||
| `topaz` | `topaz` | Topaz | API key, image | [link](https://topazlabs.com) | — |
|
||||
| `typhoon` | `typhoon` | Typhoon | API key | [link](https://docs.opentyphoon.ai) | Free API key with a 5 req/s and 200 req/m rate limit. |
|
||||
| `udio` | `udio` | Udio | API key | [link](https://udio.com) | Paste session cookie from udio.com (Supabase auth) |
|
||||
|
||||
@@ -159,6 +159,28 @@ enumerating every existing combo that shadows a model id, so operators who
|
||||
hit this by accident (rather than intentionally, per #6940) have a signal.
|
||||
The detection helper lives in `src/lib/combos/modelNameCollision.ts`.
|
||||
|
||||
## Calling a Custom Combo From a Client
|
||||
|
||||
Persisted combos (Settings → Combos) are only used when the client sends the combo's **exact name** in the `model` field — there is no fuzzy or partial matching of the combo name, and no `auto/` prefix involved. Resolution order (`getComboForModel()` in `src/sse/services/model.ts`):
|
||||
|
||||
1. exact combo-name match (`model: "my-combo"`),
|
||||
2. `combo/<name>` prefix (`model: "combo/my-combo"`),
|
||||
3. model→combo glob mappings (`/api/model-combo-mappings`).
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/v1/chat/completions \
|
||||
-H "Authorization: Bearer <key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model":"my-combo","messages":[{"role":"user","content":"Hello"}]}'
|
||||
```
|
||||
|
||||
Two common pitfalls:
|
||||
|
||||
- **`auto` does not use your combos.** `auto`/`auto/*` builds its own zero-config candidate pool and only consults persisted combos if a combo is literally named `auto` (not recommended). To route through a combo, send its exact name — not `auto`.
|
||||
- **`openrouter/auto` is a real paid OpenRouter product** ("Auto Best Available"), not an OmniRoute alias. It is the single static model entry of the OpenRouter registry (`open-sse/config/providers/registry/openrouter/index.ts`) and is billed separately. Use Settings → Routing → Hide paid models to exclude it from `auto` pools.
|
||||
|
||||
See [#7992](https://github.com/diegosouzapw/OmniRoute/issues/7992) and [#7111](https://github.com/diegosouzapw/OmniRoute/issues/7111) for the original confusion this documents.
|
||||
|
||||
## How It Works (Persisted Auto-Combos)
|
||||
|
||||
The Auto-Combo Engine dynamically selects the best provider/model for each request using a **14-factor scoring function** (defined in `open-sse/services/autoCombo/scoring.ts` → `DEFAULT_WEIGHTS`). Weights form a normalized distribution (custom weights are renormalized by `normalizeScoringWeights()`).
|
||||
|
||||
@@ -781,6 +781,14 @@ function startNextServer() {
|
||||
...serverEnv,
|
||||
DATA_DIR: dataDir,
|
||||
PORT: String(serverPort),
|
||||
// Pin the embedded server to loopback. Next.js standalone binds to
|
||||
// `process.env.HOSTNAME || '0.0.0.0'`, and Windows always exports
|
||||
// HOSTNAME as the machine name — which resolves to the LAN address, so
|
||||
// the server listens only there and 127.0.0.1 stays closed. The renderer
|
||||
// then fails to load `http://localhost:<port>`, "ready-to-show" never
|
||||
// fires, and the window (created with `show: false`) is never shown.
|
||||
// Mirrors scripts/dev/run-next-playwright.mjs, which already pins this.
|
||||
HOSTNAME: "127.0.0.1",
|
||||
NODE_ENV: "production",
|
||||
ELECTRON_RUN_AS_NODE: "1",
|
||||
NODE_PATH: resolveServerNodePath(serverEnv, resolvePackNodePaths(dataDir)),
|
||||
|
||||
@@ -26,3 +26,8 @@ data = {
|
||||
response = requests.post(API_URL, headers=headers, json=data)
|
||||
response.raise_for_status()
|
||||
print(response.json()["choices"][0]["message"]["content"])
|
||||
|
||||
# Fresh install, zero credentials — `auto` already works:
|
||||
# curl http://localhost:20128/v1/chat/completions \
|
||||
# -H "Content-Type: application/json" \
|
||||
# -d '{"model":"auto","messages":[{"role":"user","content":"Hello!"}]}'
|
||||
|
||||
8
llm.txt
@@ -1,6 +1,6 @@
|
||||
# OmniRoute
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 341 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 342 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (109 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -165,7 +165,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── manager.ts # MITM proxy manager
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions (341), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── constants/ # Provider definitions (342), model lists, pricing, routing strategies, MCP scopes
|
||||
│ │ ├── contracts/ # Shared API contracts
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── middleware/ # Shared middleware utilities
|
||||
@@ -277,7 +277,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **341 AI providers** with automatic format translation
|
||||
- **342 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -475,7 +475,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
## v3.8.x Highlights
|
||||
|
||||
- **341-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **342-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
|
||||
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
|
||||
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
|
||||
- **MCP server expanded to 109 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import {
|
||||
CODEX_CLI_RS_ORIGINATOR,
|
||||
DEFAULT_CODEX_CLIENT_VERSION,
|
||||
getCodexCliRsHeaders as buildCodexCliRsHeaders,
|
||||
} from "@/shared/constants/codexClient";
|
||||
|
||||
export { DEFAULT_CODEX_CLIENT_VERSION } from "@/shared/constants/codexClient";
|
||||
export {
|
||||
DEFAULT_CODEX_CLIENT_VERSION,
|
||||
CODEX_CLI_RS_ORIGINATOR,
|
||||
} from "@/shared/constants/codexClient";
|
||||
const DEFAULT_CODEX_USER_AGENT_PLATFORM = "Windows 10.0.26200";
|
||||
const DEFAULT_CODEX_USER_AGENT_ARCH = "x64";
|
||||
const CODEX_VERSION_OVERRIDE_ENV = "CODEX_CLIENT_VERSION";
|
||||
@@ -51,6 +55,35 @@ export function getCodexCliRsHeaders(): Record<string, string> {
|
||||
return buildCodexCliRsHeaders(getCodexClientVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity for the credential face (auth.openai.com: token exchange / refresh).
|
||||
* The real Codex client sends only `originator` + `User-Agent` on that face
|
||||
* (codex-rs login/default_client.rs default_headers()); the `Version` header
|
||||
* gate exists only on the chatgpt.com/backend-api inference face, so it is
|
||||
* deliberately omitted here. Mirrors sub2api v0.1.178
|
||||
* ApplyCodexCanonicalAuthIdentity.
|
||||
*/
|
||||
export function getCodexAuthIdentityHeaders(): Record<string, string> {
|
||||
return {
|
||||
"User-Agent": getCodexUserAgent(),
|
||||
originator: CODEX_CLI_RS_ORIGINATOR,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical Codex CLI identity for server-initiated calls against the
|
||||
* chatgpt.com/backend-api face that are not tied to one end-client request
|
||||
* (usage / quota / models manifest / reset-credits). Same UA/version chain as
|
||||
* inference so these calls do not show up upstream as anonymous half-identities.
|
||||
*/
|
||||
export function getCodexBackendIdentityHeaders(): Record<string, string> {
|
||||
return {
|
||||
"User-Agent": getCodexUserAgent(),
|
||||
originator: CODEX_CLI_RS_ORIGINATOR,
|
||||
Version: getCodexClientVersion(),
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeCodexSessionId(value: unknown): string | null {
|
||||
if (typeof value !== "string") return null;
|
||||
const normalized = value.trim();
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
|
||||
import { normalizeCodexSessionId } from "./codexClient.ts";
|
||||
import { isCrossAccountCodexTurnState, readCodexTurnStateHeader } from "./codexTurnState.ts";
|
||||
|
||||
const CODEX_INSTALLATION_SALT = "omniroute-codex-installation";
|
||||
const CODEX_SESSION_SEED_PREFIX = "omniroute:codex-session-id:v1:";
|
||||
const CODEX_THREAD_SEED_PREFIX = "omniroute:codex-thread-id:v1:";
|
||||
// v2 derivations are keyed by the persisted per-connection random seed
|
||||
// (codexFingerprintSeed) instead of the connection-id chain, mirroring
|
||||
// sub2api v0.1.178 (#5696): deterministic derivation stays stable, but the
|
||||
// seed is generated per connection so identities never collide across
|
||||
// deployments and survive connection export/import.
|
||||
const CODEX_INSTALLATION_SEED_PREFIX_V2 = "omniroute:codex-installation:v2:";
|
||||
const CODEX_SESSION_SEED_PREFIX_V2 = "omniroute:codex-session-id:v2:";
|
||||
const CODEX_THREAD_SEED_PREFIX_V2 = "omniroute:codex-thread-id:v2:";
|
||||
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
|
||||
export const CODEX_FINGERPRINT_MODES = ["off", "device", "session", "full"] as const;
|
||||
export type CodexFingerprintMode = (typeof CODEX_FINGERPRINT_MODES)[number];
|
||||
export const CODEX_FINGERPRINT_MODE_KEY = "codexFingerprintMode";
|
||||
/**
|
||||
* System-managed per-connection random seed used as the fingerprint
|
||||
* derivation source. Never sent upstream, stripped from API responses, and
|
||||
* preserved across connection updates (sub2api `codex_fingerprint_seed`).
|
||||
*/
|
||||
export const CODEX_FINGERPRINT_SEED_KEY = "codexFingerprintSeed";
|
||||
|
||||
export type CodexClientIdentity = {
|
||||
mode: CodexFingerprintMode;
|
||||
@@ -72,6 +87,61 @@ function accountSeed(
|
||||
);
|
||||
}
|
||||
|
||||
/** The persisted system-managed random seed, when present and a valid UUID. */
|
||||
export function getCodexFingerprintSeed(
|
||||
providerSpecificData?: Record<string, unknown> | null
|
||||
): string | null {
|
||||
return normalizeUuid(providerSpecificData?.[CODEX_FINGERPRINT_SEED_KEY]);
|
||||
}
|
||||
|
||||
/** Modes that rewrite account-scoped identifiers and therefore need a stable seed. */
|
||||
export function codexFingerprintModeRequiresSeed(mode: CodexFingerprintMode): boolean {
|
||||
return mode === "device" || mode === "session" || mode === "full";
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a Codex OAuth connection carries a persisted fingerprint seed when its
|
||||
* convergence mode derives account-scoped identifiers. Called at connection
|
||||
* create/update time (the persistence layer owns the write); the request path
|
||||
* only ever READS the seed, so an identity never rotates mid-flight.
|
||||
*
|
||||
* Semantics mirror sub2api v0.1.178 `prepareCodexFingerprintExtraFor{Create,Update}`:
|
||||
* - the key is system-managed: any client-supplied value is stripped first;
|
||||
* - an existing valid seed is ALWAYS carried forward (even when the new mode
|
||||
* is `off` — it stays dormant, ready if convergence is re-enabled later);
|
||||
* - otherwise a fresh seed is created only when the mode requires one
|
||||
* (device/session/full; the OmniRoute default is session).
|
||||
*
|
||||
* Returns the (possibly new) providerSpecificData, or undefined when there is
|
||||
* nothing to store. Pre-seed connections keep their legacy connection-id
|
||||
* derived identity until the next save — one deliberate rotation, same as
|
||||
* sub2api's migration-225 backfill.
|
||||
*/
|
||||
export function ensureCodexFingerprintSeed(
|
||||
providerSpecificData?: Record<string, unknown> | null,
|
||||
credentials?: { accessToken?: unknown; refreshToken?: unknown } | null,
|
||||
existingProviderSpecificData?: Record<string, unknown> | null
|
||||
): Record<string, unknown> | undefined {
|
||||
const psd: Record<string, unknown> = { ...(providerSpecificData || {}) };
|
||||
// System-managed key: never trust an inbound value, regardless of auth type.
|
||||
delete psd[CODEX_FINGERPRINT_SEED_KEY];
|
||||
if (!isCodexOAuthCredentials(credentials)) {
|
||||
return Object.keys(psd).length > 0 ? psd : undefined;
|
||||
}
|
||||
|
||||
const existingSeed = getCodexFingerprintSeed(existingProviderSpecificData);
|
||||
if (existingSeed) {
|
||||
psd[CODEX_FINGERPRINT_SEED_KEY] = existingSeed;
|
||||
return psd;
|
||||
}
|
||||
const mode = getCodexFingerprintMode(psd, true);
|
||||
if (codexFingerprintModeRequiresSeed(mode)) {
|
||||
psd[CODEX_FINGERPRINT_SEED_KEY] = randomUUID();
|
||||
return psd;
|
||||
}
|
||||
return Object.keys(psd).length > 0 ? psd : undefined;
|
||||
}
|
||||
|
||||
function readNamedHeader(
|
||||
headers: Headers | Record<string, unknown> | null | undefined,
|
||||
name: string
|
||||
@@ -120,6 +190,11 @@ export function getCodexInstallationId(
|
||||
const explicit = normalizeUuid(providerSpecificData?.codexInstallationId);
|
||||
if (explicit) return explicit;
|
||||
|
||||
const persistedSeed = getCodexFingerprintSeed(providerSpecificData);
|
||||
if (persistedSeed) {
|
||||
return deriveStableUUIDv4(`${CODEX_INSTALLATION_SEED_PREFIX_V2}${persistedSeed}`);
|
||||
}
|
||||
|
||||
const legacyStableSource =
|
||||
nonEmptyString(providerSpecificData?.workspaceId) ||
|
||||
nonEmptyString(providerSpecificData?.accountId) ||
|
||||
@@ -137,6 +212,10 @@ export function getCodexConvergedSessionId(
|
||||
providerSpecificData?: Record<string, unknown> | null,
|
||||
accountKey?: string | null
|
||||
): string {
|
||||
const persistedSeed = getCodexFingerprintSeed(providerSpecificData);
|
||||
if (persistedSeed) {
|
||||
return deriveStableUUIDv4(`${CODEX_SESSION_SEED_PREFIX_V2}${persistedSeed}`);
|
||||
}
|
||||
return deriveStableUUIDv4(
|
||||
`${CODEX_SESSION_SEED_PREFIX}${accountSeed(providerSpecificData, accountKey)}`
|
||||
);
|
||||
@@ -148,6 +227,10 @@ export function getCodexConvergedThreadId(
|
||||
accountKey?: string | null
|
||||
): string {
|
||||
if (!nonEmptyString(clientSessionId)) return "";
|
||||
const persistedSeed = getCodexFingerprintSeed(providerSpecificData);
|
||||
if (persistedSeed) {
|
||||
return deriveStableUUIDv4(`${CODEX_THREAD_SEED_PREFIX_V2}${persistedSeed}:${clientSessionId}`);
|
||||
}
|
||||
return deriveStableUUIDv4(
|
||||
`${CODEX_THREAD_SEED_PREFIX}${accountSeed(providerSpecificData, accountKey)}:${clientSessionId}`
|
||||
);
|
||||
@@ -163,6 +246,26 @@ export function getCodexClientSessionId(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide what to do with the client's `x-codex-turn-state` echo for the
|
||||
* account about to serve this request. The blob is minted per account by the
|
||||
* upstream; replaying another account's blob after failover is a proxy-only
|
||||
* contradiction, so a known cross-account echo is stripped. Same-account or
|
||||
* unknown provenance passes through unchanged (strip only, never inject).
|
||||
* Independent of the fingerprint-convergence mode — account consistency also
|
||||
* applies to explicit `off` / passthrough.
|
||||
*/
|
||||
export function resolveCodexTurnStateEcho(
|
||||
clientHeaders?: Headers | Record<string, unknown> | null,
|
||||
accountKey?: string | null
|
||||
): string | null {
|
||||
const value = readCodexTurnStateHeader(clientHeaders);
|
||||
if (!value) return null;
|
||||
const sessionId = getCodexClientSessionId(clientHeaders);
|
||||
if (sessionId && isCrossAccountCodexTurnState(sessionId, accountKey)) return null;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* One identity object for every carrier in one upstream turn.
|
||||
* accountKey may be the OmniRoute connection id; it is never sent upstream.
|
||||
@@ -284,13 +387,19 @@ export function withCodexFingerprintCredentials<T extends CodexCredentialIdentit
|
||||
): T {
|
||||
const identity = resolveCodexFingerprintIdentity({ credentials, clientHeaders, body });
|
||||
const original = resolveCodexOriginalIdentityHeaders({ credentials, clientHeaders });
|
||||
if (!identity && !original) return credentials;
|
||||
// The turn-state echo guard runs for every Codex request (including compact
|
||||
// and explicit-off), unlike the convergence identity above.
|
||||
const turnStateEcho = credentials
|
||||
? resolveCodexTurnStateEcho(clientHeaders, credentials.connectionId ?? null)
|
||||
: null;
|
||||
if (!identity && !original && !turnStateEcho) return credentials;
|
||||
return {
|
||||
...credentials,
|
||||
providerSpecificData: {
|
||||
...(credentials.providerSpecificData || {}),
|
||||
...(identity ? { codexClientIdentity: identity } : {}),
|
||||
...(original ? { codexOriginalIdentityHeaders: original } : {}),
|
||||
...(turnStateEcho ? { codexTurnStateEcho: turnStateEcho } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
144
open-sse/config/codexTurnState.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* codexTurnState.ts — `x-codex-turn-state` relay bookkeeping and the
|
||||
* cross-account echo guard.
|
||||
*
|
||||
* The upstream mints the opaque turn-state blob under the outbound identity
|
||||
* (including the fingerprint-converged installation/session/thread ids), and
|
||||
* the real Codex client echoes it back on later requests of the same turn —
|
||||
* codex-rs captures it from the /responses SSE, the /responses/compact JSON,
|
||||
* and the WS handshake (codex-api/src/sse/responses.rs, endpoint/compact.rs).
|
||||
*
|
||||
* Replaying a blob to the SAME account is self-consistent. Replaying it to a
|
||||
* DIFFERENT account (failover rotated the connection while the client still
|
||||
* echoes the old account's blob) is a contradiction only a proxy chain can
|
||||
* produce — a real Codex client never emits it. The provenance table records
|
||||
* which connection minted the blob a downstream session last received, and
|
||||
* the outbound guard strips echoes known to come from another account.
|
||||
*
|
||||
* Mirrors sub2api v0.1.177 `openai_codex_turn_state.go` (commit 8219dcfc8).
|
||||
* OmniRoute keys the table by the client's original session id only — the
|
||||
* executor pipeline does not carry the API key id, and a real Codex session
|
||||
* id is a random UUID, so accidental cross-key collisions are not a
|
||||
* practical concern.
|
||||
*/
|
||||
|
||||
const CODEX_TURN_STATE_HEADER = "x-codex-turn-state";
|
||||
|
||||
/**
|
||||
* How long a provenance record lives. The blob is echoed within one turn,
|
||||
* but clients may hold it across a whole session; 2h covers the standard
|
||||
* 5-hour quota window's early turns without letting the map grow stale
|
||||
* entries for days.
|
||||
*/
|
||||
const CODEX_TURN_STATE_TTL_MS = 2 * 60 * 60 * 1000;
|
||||
|
||||
/** Opportunistic full sweep every N writes (the read side also lazily expires). */
|
||||
const CODEX_TURN_STATE_SWEEP_EVERY_WRITES = 256;
|
||||
|
||||
type CodexTurnStateOrigin = {
|
||||
accountKey: string;
|
||||
expiresAt: number;
|
||||
};
|
||||
|
||||
const turnStateOrigins = new Map<string, CodexTurnStateOrigin>();
|
||||
let turnStateWrites = 0;
|
||||
|
||||
function normalizeAccountKey(accountKey: unknown): string | null {
|
||||
if (typeof accountKey !== "string") return null;
|
||||
const trimmed = accountKey.trim();
|
||||
return trimmed || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the turn-state blob from a headers bag (Headers instance or a plain
|
||||
* record with arbitrary casing). Returns null when absent/blank.
|
||||
*/
|
||||
export function readCodexTurnStateHeader(
|
||||
headers: Headers | Record<string, unknown> | null | undefined
|
||||
): string | null {
|
||||
if (!headers) return null;
|
||||
if (headers instanceof Headers) {
|
||||
const value = headers.get(CODEX_TURN_STATE_HEADER);
|
||||
return typeof value === "string" && value.trim() ? value.trim() : null;
|
||||
}
|
||||
if (typeof headers === "object") {
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
if (
|
||||
key.toLowerCase() === CODEX_TURN_STATE_HEADER &&
|
||||
typeof value === "string" &&
|
||||
value.trim()
|
||||
) {
|
||||
return value.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function sweepExpiredTurnStateOrigins(now: number): void {
|
||||
for (const [key, origin] of turnStateOrigins) {
|
||||
if (origin.expiresAt <= now) {
|
||||
turnStateOrigins.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that `accountKey` minted the turn-state blob this downstream session
|
||||
* just received. Must only be called at the response commit point — when the
|
||||
* header is actually written to the client. Recording earlier (e.g. for an
|
||||
* attempt later discarded by failover) would poison the table and make the
|
||||
* guard strip the NEXT account's legitimate echo.
|
||||
*/
|
||||
export function noteCodexTurnStateProvenance(
|
||||
clientSessionId: string | null | undefined,
|
||||
accountKey: unknown,
|
||||
nowMs?: number
|
||||
): void {
|
||||
const sessionId = typeof clientSessionId === "string" ? clientSessionId.trim() : "";
|
||||
const account = normalizeAccountKey(accountKey);
|
||||
if (!sessionId || !account) return;
|
||||
|
||||
const now = typeof nowMs === "number" ? nowMs : Date.now();
|
||||
turnStateOrigins.set(sessionId, {
|
||||
accountKey: account,
|
||||
expiresAt: now + CODEX_TURN_STATE_TTL_MS,
|
||||
});
|
||||
|
||||
turnStateWrites += 1;
|
||||
if (turnStateWrites % CODEX_TURN_STATE_SWEEP_EVERY_WRITES === 0) {
|
||||
sweepExpiredTurnStateOrigins(now);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outbound guard: true when the echoed blob is KNOWN to have been minted by a
|
||||
* different account and must be stripped before going upstream. Same-account
|
||||
* or unknown provenance passes through unchanged — stripping only, never
|
||||
* injection (clients that cannot echo are the Claude bridge's concern, not
|
||||
* this module's).
|
||||
*/
|
||||
export function isCrossAccountCodexTurnState(
|
||||
clientSessionId: string | null | undefined,
|
||||
accountKey: unknown,
|
||||
nowMs?: number
|
||||
): boolean {
|
||||
const sessionId = typeof clientSessionId === "string" ? clientSessionId.trim() : "";
|
||||
const account = normalizeAccountKey(accountKey);
|
||||
if (!sessionId || !account) return false;
|
||||
|
||||
const origin = turnStateOrigins.get(sessionId);
|
||||
if (!origin) return false;
|
||||
const now = typeof nowMs === "number" ? nowMs : Date.now();
|
||||
if (origin.expiresAt <= now) {
|
||||
turnStateOrigins.delete(sessionId);
|
||||
return false;
|
||||
}
|
||||
return origin.accountKey !== account;
|
||||
}
|
||||
|
||||
/** Test hook: forget all provenance records and reset the sweep counter. */
|
||||
export function __resetCodexTurnStateOriginsForTesting(): void {
|
||||
turnStateOrigins.clear();
|
||||
turnStateWrites = 0;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ import { api_airforceProvider } from "./registry/api-airforce/index.ts";
|
||||
import { mistralProvider } from "./registry/mistral/index.ts";
|
||||
import { togetherProvider } from "./registry/together/index.ts";
|
||||
import { cohereProvider } from "./registry/cohere/index.ts";
|
||||
import { cursorProvider } from "./registry/cursor/index.ts";
|
||||
import { cursorProvider, cursor_apiProvider } from "./registry/cursor/index.ts";
|
||||
import { volcengineProvider } from "./registry/volcengine/index.ts";
|
||||
import { hackclubProvider } from "./registry/hackclub/index.ts";
|
||||
import { freetheaiProvider } from "./registry/freetheai/index.ts";
|
||||
@@ -213,6 +213,7 @@ import { kiroProvider } from "./registry/kiro/index.ts";
|
||||
import { openadapterProvider } from "./registry/openadapter/index.ts";
|
||||
import { ditProvider } from "./registry/dit/index.ts";
|
||||
import { tokenrouterProvider } from "./registry/tokenrouter/index.ts";
|
||||
import { token_kioskProvider } from "./registry/token-kiosk/index.ts";
|
||||
import { grok_cliProvider } from "./registry/grok-cli/index.ts";
|
||||
import { codebuddy_cnProvider } from "./registry/codebuddy-cn/index.ts";
|
||||
import { pioneerProvider } from "./registry/pioneer/index.ts";
|
||||
@@ -326,6 +327,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
together: togetherProvider,
|
||||
cohere: cohereProvider,
|
||||
cursor: cursorProvider,
|
||||
"cursor-api": cursor_apiProvider,
|
||||
volcengine: volcengineProvider,
|
||||
hackclub: hackclubProvider,
|
||||
freetheai: freetheaiProvider,
|
||||
@@ -475,6 +477,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
openadapter: openadapterProvider,
|
||||
dit: ditProvider,
|
||||
tokenrouter: tokenrouterProvider,
|
||||
"token-kiosk": token_kioskProvider,
|
||||
"grok-cli": grok_cliProvider,
|
||||
"codebuddy-cn": codebuddy_cnProvider,
|
||||
pioneer: pioneerProvider,
|
||||
|
||||
@@ -156,3 +156,29 @@ export const cursorProvider: RegistryEntry = {
|
||||
{ id: "kimi-k2.7-code", name: "Kimi K2.7 Code" },
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* API-key variant of the Cursor provider.
|
||||
*
|
||||
* Same wire protocol, executor and catalog as `cursor`, but the connection
|
||||
* holds a Cursor user API key (`crsr_…`, cursor.com/dashboard/api) instead of
|
||||
* an IDE/OAuth session. The executor exchanges that key for a session token
|
||||
* on demand (open-sse/services/cursorApiKeyAuth.ts), so no cursor-agent or
|
||||
* IDE install is needed on the OmniRoute host. Kept as a distinct backend ID
|
||||
* so API-key and IDE-session connections never share renewal, quota or
|
||||
* dashboard semantics.
|
||||
*/
|
||||
export const cursor_apiProvider: RegistryEntry = {
|
||||
id: "cursor-api",
|
||||
alias: "cua",
|
||||
format: cursorProvider.format,
|
||||
executor: "cursor-api",
|
||||
baseUrl: cursorProvider.baseUrl,
|
||||
chatPath: cursorProvider.chatPath,
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: cursorProvider.defaultContextLength,
|
||||
headers: getCursorRegistryHeaders(),
|
||||
clientVersion: CURSOR_REGISTRY_VERSION,
|
||||
models: cursorProvider.models,
|
||||
};
|
||||
|
||||
@@ -27,6 +27,8 @@ export const sensenovaProvider: RegistryEntry = {
|
||||
contextLength: 1048576,
|
||||
maxOutputTokens: 65536,
|
||||
supportsReasoning: true,
|
||||
supportedThinkingEfforts: ["none", "low", "medium", "high", "xhigh"],
|
||||
supportsXHighEffort: true,
|
||||
interleavedField: "reasoning_content",
|
||||
},
|
||||
{
|
||||
|
||||
20
open-sse/config/providers/registry/token-kiosk/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
|
||||
export const token_kioskProvider: RegistryEntry = {
|
||||
id: "token-kiosk",
|
||||
alias: "tk",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://agent-router.gaib.ai/v1/chat/completions",
|
||||
modelsUrl: "https://agent-router.gaib.ai/v1/models",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 128000,
|
||||
models: [
|
||||
{ id: "claude-3-5-sonnet", name: "Claude 3.5 Sonnet (Token Kiosk)", contextLength: 200000, toolCalling: true, supportsVision: true },
|
||||
{ id: "deepseek-v3", name: "DeepSeek V3 (Token Kiosk)", contextLength: 64000, toolCalling: true },
|
||||
{ id: "deepseek-r1", name: "DeepSeek R1 (Token Kiosk)", contextLength: 64000, toolCalling: true, supportsReasoning: true },
|
||||
{ id: "kimi-k1.5", name: "Kimi K1.5 (Token Kiosk)", contextLength: 128000, toolCalling: true },
|
||||
{ id: "minimax-m6", name: "MiniMax M6 (Token Kiosk)", contextLength: 128000, toolCalling: true },
|
||||
],
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
supportsClaudeMaxEffort,
|
||||
supportsXHighEffort,
|
||||
getProviderModel,
|
||||
getProviderModels,
|
||||
} from "../../config/providerModels.ts";
|
||||
|
||||
/**
|
||||
@@ -351,6 +352,31 @@ export function sanitizeReasoningEffortForProvider(
|
||||
// new models from being unusable for weeks until they're whitelisted (#8057).
|
||||
if (effortStr === "max") {
|
||||
if (supportsMax) return body; // explicitly known to accept max
|
||||
|
||||
// A model that explicitly advertises its accepted tiers is safe to normalize.
|
||||
// Keep the default pass-through for absent metadata: an unlisted model might
|
||||
// support literal `max`, and #8057 deliberately avoids blocking such models.
|
||||
const providerModelId = modelStr.startsWith(`${provider}/`)
|
||||
? modelStr.slice(provider.length + 1)
|
||||
: modelStr;
|
||||
// Do not fall back to a globally registered model here. Identical ids can
|
||||
// have different upstream contracts across providers (for example, OpenCode
|
||||
// and SenseNova both expose deepseek-v4-flash with different max support).
|
||||
const explicitEfforts = getProviderModels(provider).find(
|
||||
(entry) => entry.id === providerModelId || entry.aliases?.includes(providerModelId)
|
||||
)?.supportedThinkingEfforts;
|
||||
const maxFallback =
|
||||
Array.isArray(explicitEfforts) && !explicitEfforts.includes("max")
|
||||
? ["xhigh", "high", "medium", "low"].find((tier) => explicitEfforts.includes(tier))
|
||||
: undefined;
|
||||
if (maxFallback) {
|
||||
log?.info?.(
|
||||
"REASONING_SANITIZE",
|
||||
`${provider}/${modelStr}: downgraded reasoning_effort max → ${maxFallback} (explicit model capability)`
|
||||
);
|
||||
return writeEffortValue(b, maxFallback, c);
|
||||
}
|
||||
|
||||
if (!supportsXHigh) {
|
||||
// Model is explicitly flagged as rejecting xhigh (and not in supportsMax) —
|
||||
// it likely only accepts standard tiers. Degrade to its highest: high.
|
||||
@@ -360,7 +386,6 @@ export function sanitizeReasoningEffortForProvider(
|
||||
);
|
||||
return writeEffortValue(b, "high", c);
|
||||
}
|
||||
// Default: pass max through unchanged — trust the upstream
|
||||
return body;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
import { FETCH_BODY_TIMEOUT_MS, HTTP_STATUS, PROVIDERS } from "../config/constants.ts";
|
||||
import { readCodexPeekChunk, buildCodexTimeoutSafePassthroughBody } from "./codex/bodyTimeout.ts";
|
||||
import {
|
||||
CODEX_CLI_RS_ORIGINATOR,
|
||||
getCodexClientVersion,
|
||||
getCodexUserAgent,
|
||||
normalizeCodexSessionId,
|
||||
@@ -225,7 +226,6 @@ function convertSystemToDeveloperRole(body: Record<string, unknown>): void {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function stripOrphanedCodexFunctionCallOutputs(body: Record<string, unknown>): void {
|
||||
if (!Array.isArray(body.input)) return;
|
||||
const input = body.input;
|
||||
@@ -1045,10 +1045,11 @@ export class CodexExecutor extends BaseExecutor {
|
||||
CodexClientIdentity | null | undefined;
|
||||
const originalIdentityHeaders = credentials?.providerSpecificData
|
||||
?.codexOriginalIdentityHeaders as Record<string, string> | null | undefined;
|
||||
const turnStateEcho = credentials?.providerSpecificData?.codexTurnStateEcho;
|
||||
|
||||
// Originator header — identifies the client type to the Codex backend.
|
||||
// Ref: openai/codex login/src/auth/default_client.rs DEFAULT_ORIGINATOR = "codex_cli_rs"
|
||||
headers["originator"] = "codex_cli_rs";
|
||||
headers["originator"] = CODEX_CLI_RS_ORIGINATOR;
|
||||
|
||||
// session_id header — enables prompt cache affinity on the Codex backend.
|
||||
// The official Codex client sets this to conversation_id (a stable UUID per session).
|
||||
@@ -1060,6 +1061,13 @@ export class CodexExecutor extends BaseExecutor {
|
||||
applyCodexOriginalIdentityHeaders(headers, originalIdentityHeaders);
|
||||
applyCodexClientIdentityHeaders(headers, clientIdentity);
|
||||
|
||||
// x-codex-turn-state: forward the client's echo when the provenance guard
|
||||
// (in withCodexFingerprintCredentials) cleared it as same-account. The
|
||||
// blob is account-bound; a stripped (absent) value must stay absent.
|
||||
if (typeof turnStateEcho === "string" && turnStateEcho) {
|
||||
headers["x-codex-turn-state"] = turnStateEcho;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,16 @@ export function resolveConnectionParams(
|
||||
const psd = (credentials?.providerSpecificData ?? {}) as JsonRecord;
|
||||
const parsedApiKey =
|
||||
typeof credentials?.apiKey === "string" ? parsePastedCredential(credentials.apiKey) : {};
|
||||
// A JWT in credentials.accessToken (3 dot-separated parts — the individual-tier
|
||||
// token is an opaque JWE with 5) is the freshest copy: the executor refreshes it
|
||||
// in place before resolving params, and the framework mutates it after a refresh.
|
||||
const credentialsJwt =
|
||||
typeof credentials?.accessToken === "string" &&
|
||||
credentials.accessToken.split(".").length === 3
|
||||
? credentials.accessToken
|
||||
: "";
|
||||
const accessToken =
|
||||
credentialsJwt ||
|
||||
parsedApiKey.accessToken ||
|
||||
(typeof credentials?.apiKey === "string" &&
|
||||
credentials.apiKey &&
|
||||
@@ -254,6 +263,135 @@ export function redactWsUrl(wsUrl: string): string {
|
||||
return wsUrl.replace(/access_token=[^&]*/i, "access_token=REDACTED");
|
||||
}
|
||||
|
||||
// ── OAuth refresh support (#10718 — client ids observed in the browser token
|
||||
// and M365-Copilot2API) ────────────────────────────────────────────────────
|
||||
//
|
||||
// The browser-issued access_token lives ~75 minutes. These helpers redeem a
|
||||
// stored refresh_token at the Microsoft identity platform (same public client
|
||||
// the m365.cloud.microsoft web app uses) so the connection self-heals instead
|
||||
// of requiring a fresh DevTools capture after every expiry.
|
||||
|
||||
/** Public client id observed in both the browser token and M365-Copilot2API. */
|
||||
export const M365_OAUTH_CLIENT_ID = "c0ab8ce9-e9a0-42e7-b064-33d422df41f1";
|
||||
|
||||
export const M365_OAUTH_SCOPE =
|
||||
"openid profile offline_access https://substrate.office.com/sydney/M365Chat.Read " +
|
||||
"https://substrate.office.com/sydney/sydney.readwrite";
|
||||
|
||||
/** Refresh lead time — refresh when the current token has less than this left. */
|
||||
export const M365_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
||||
|
||||
type MinimalLog = {
|
||||
info?: (tag: string, message: string) => void;
|
||||
warn?: (tag: string, message: string) => void;
|
||||
};
|
||||
|
||||
/** Decode a JWT payload WITHOUT verification — exp/tid are routing hints, never authz. */
|
||||
export function decodeJwtClaims(
|
||||
token: string
|
||||
): { exp?: number; tid?: string; oid?: string } | null {
|
||||
try {
|
||||
const parts = token.split(".");
|
||||
if (parts.length !== 3) return null;
|
||||
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
||||
return payload && typeof payload === "object" ? payload : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** True when the token is unreadable, already expired, or inside the refresh lead window. */
|
||||
export function tokenNeedsRefresh(token: string, leadMs = M365_REFRESH_LEAD_MS): boolean {
|
||||
const claims = decodeJwtClaims(token);
|
||||
if (!claims?.exp) return true;
|
||||
return claims.exp * 1000 <= Date.now() + leadMs;
|
||||
}
|
||||
|
||||
/** The freshest readable access token for a connection (JWT column → apiKey → psd). */
|
||||
export function currentM365AccessToken(
|
||||
credentials: ProviderCredentials | undefined
|
||||
): string {
|
||||
if (
|
||||
typeof credentials?.accessToken === "string" &&
|
||||
credentials.accessToken.split(".").length === 3
|
||||
) {
|
||||
return credentials.accessToken;
|
||||
}
|
||||
if (typeof credentials?.apiKey === "string") {
|
||||
const parsed = parsePastedCredential(credentials.apiKey);
|
||||
if (parsed.accessToken && parsed.accessToken.split(".").length === 3) return parsed.accessToken;
|
||||
// Opaque (JWE) individual-tier token — still a usable credential, just not refreshable.
|
||||
return parsed.accessToken || "";
|
||||
}
|
||||
const psd = (credentials?.providerSpecificData ?? {}) as JsonRecord;
|
||||
if (typeof psd.accessToken === "string") return psd.accessToken;
|
||||
if (typeof psd.access_token === "string") return psd.access_token;
|
||||
return "";
|
||||
}
|
||||
|
||||
/** The chathub path (`<user-oid>@<tenant-id>`) from wherever it is stored. */
|
||||
export function currentM365ChathubPath(credentials: ProviderCredentials | undefined): string {
|
||||
const psd = (credentials?.providerSpecificData ?? {}) as JsonRecord;
|
||||
return (
|
||||
(typeof credentials?.apiKey === "string"
|
||||
? parsePastedCredential(credentials.apiKey).chathubPath
|
||||
: "") ||
|
||||
(typeof psd.chathubPath === "string" && psd.chathubPath) ||
|
||||
(typeof psd.userTenant === "string" && psd.userTenant) ||
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
export interface M365RefreshResult {
|
||||
accessToken: string;
|
||||
refreshToken?: string;
|
||||
expiresIn?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redeem the refresh_token (public client — no secret). MS may rotate the
|
||||
* refresh_token; callers MUST persist the returned one when present or the
|
||||
* token family dies after the first refresh.
|
||||
*/
|
||||
export async function refreshM365AccessToken(
|
||||
refreshToken: string,
|
||||
tid: string,
|
||||
log?: MinimalLog
|
||||
): Promise<M365RefreshResult | { error: string }> {
|
||||
const endpoint = `https://login.microsoftonline.com/${tid || "common"}/oauth2/v2.0/token`;
|
||||
try {
|
||||
const res = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
client_id: M365_OAUTH_CLIENT_ID,
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: refreshToken,
|
||||
scope: M365_OAUTH_SCOPE,
|
||||
}),
|
||||
});
|
||||
const data = (await res.json().catch(() => ({}))) as Record<string, unknown>;
|
||||
if (!res.ok || typeof data.access_token !== "string") {
|
||||
const error = typeof data.error === "string" ? data.error : `HTTP ${res.status}`;
|
||||
log?.warn?.("M365_TOKEN", `refresh_token grant failed: ${error}`);
|
||||
return { error };
|
||||
}
|
||||
log?.info?.("M365_TOKEN", "access token refreshed via refresh_token grant");
|
||||
return {
|
||||
accessToken: data.access_token,
|
||||
refreshToken: typeof data.refresh_token === "string" ? data.refresh_token : undefined,
|
||||
expiresIn: typeof data.expires_in === "number" ? data.expires_in : undefined,
|
||||
};
|
||||
} catch (e) {
|
||||
const error = e instanceof Error ? e.message : String(e);
|
||||
log?.warn?.("M365_TOKEN", `refresh request failed: ${error}`);
|
||||
return { error };
|
||||
}
|
||||
}
|
||||
|
||||
/** Flatten OpenAI messages into a single prompt (system instructions prepended). */
|
||||
export function buildPrompt(body: JsonRecord | undefined): string {
|
||||
const messages = (body?.messages as Array<JsonRecord>) || [];
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
* Protocol (from @skyzea1's #4042 capture):
|
||||
* - JSON messages terminated with the SignalR record separator `\x1e`.
|
||||
* - Handshake: → {"protocol":"json","version":1} ← {} → {"type":6}
|
||||
* - Send: type:4 invocation to target "chat" with arguments[0] = { message, ... }
|
||||
* - Send: type:4 invocation to target "chat" with arguments[0] = { message, ... },
|
||||
* immediately followed by a type:1 target:"Metrics" frame in the SAME socket
|
||||
* write (#10718 — an invocation without its Metrics pair is silently dropped).
|
||||
* - Stream: type:1 target:"update" deltas (bot text at arguments[0].messages[].text,
|
||||
* accumulated — NOT incremental) → isLastUpdate:true → type:2 final → type:3 completion.
|
||||
*/
|
||||
@@ -25,19 +27,18 @@ export const HANDSHAKE_REQUEST = { protocol: "json", version: 1 } as const;
|
||||
/** SignalR keepalive ping frame. */
|
||||
export const KEEPALIVE_PING = { type: 6 } as const;
|
||||
|
||||
/** Allowed message types observed in the individual M365 send frame. */
|
||||
/**
|
||||
* Allowed message types observed in the 2026-08 recapture of the working
|
||||
* `m365.cloud.microsoft/chat` client (#10718). The old 11-entry list is no longer
|
||||
* seen on the wire — the stale shape gets closed immediately after the type:4.
|
||||
*/
|
||||
export const ALLOWED_MESSAGE_TYPES = [
|
||||
"Chat",
|
||||
"Suggestion",
|
||||
"InternalSearchQuery",
|
||||
"Disengaged",
|
||||
"InternalLoaderMessage",
|
||||
"Progress",
|
||||
"GeneratedCode",
|
||||
"RenderCardRequest",
|
||||
"AdsQuery",
|
||||
"SemanticSerp",
|
||||
"GenerateContentQuery",
|
||||
"EndOfRequest",
|
||||
"InternalLoaderMessage",
|
||||
] as const;
|
||||
|
||||
/**
|
||||
@@ -74,22 +75,20 @@ export const M365_ENTERPRISE_EXTRA_MESSAGE_TYPES = [
|
||||
"SwitchRespondingEndpoint",
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Individual / EDU option sets from the 2026-08 recapture (#10718) — 14 entries.
|
||||
* The previous 25-entry consumer/MSA set (enable_msa_user, pdnascan, cwc_code_*,
|
||||
* …) is no longer observed on the wire and belongs to the shape the substrate
|
||||
* now drops silently.
|
||||
*/
|
||||
export const M365_DEFAULT_OPTION_SETS = [
|
||||
"search_result_progress_messages_with_search_queries",
|
||||
"update_textdoc_response_after_streaming",
|
||||
"deepleo_networking_timeout_10minutes_canmore",
|
||||
"cwc_flux_image",
|
||||
"cwc_code_interpreter",
|
||||
"cwc_code_interpreter_amsfix",
|
||||
"enable_msa_user",
|
||||
"cwcgptv",
|
||||
"cwcfluxgptv",
|
||||
"flux_v3_gptv_enable_upload_multi_image_in_turn_wo_ch",
|
||||
"gptvnorm2048",
|
||||
"pdnascan",
|
||||
"cwc_code_interpreter_citation_fix",
|
||||
"code_interpreter_interactive_charts",
|
||||
"cwc_code_interpreter_interactive_charts_inline_image",
|
||||
"code_interpreter_matplotlib_patching",
|
||||
"cwc_fileupload_odb",
|
||||
"update_memory_plugin",
|
||||
"add_custom_instructions",
|
||||
@@ -97,9 +96,6 @@ export const M365_DEFAULT_OPTION_SETS = [
|
||||
"flux_v3_progress_messages",
|
||||
"enable_batch_token_processing",
|
||||
"enable_gg_gpt",
|
||||
"flux_v3_image_gen_enable_non_watermarked_storage",
|
||||
"flux_v3_image_gen_enable_story",
|
||||
"rich_responses",
|
||||
] as const;
|
||||
|
||||
/** Append the record separator to a JSON-serializable frame. */
|
||||
@@ -117,6 +113,32 @@ export function keepaliveFrame(): string {
|
||||
return encodeFrame(KEEPALIVE_PING);
|
||||
}
|
||||
|
||||
/**
|
||||
* #10718 — the browser follows the type:4 chat invocation with this type:1
|
||||
* target:"Metrics" frame in the SAME socket write. Sending the invocation alone
|
||||
* gets it silently ignored (no update frames at all), so the executor must
|
||||
* concatenate `metricsFrame()` onto the invocation payload.
|
||||
*/
|
||||
export const CHAT_METRICS_FRAME = {
|
||||
arguments: [
|
||||
{
|
||||
Timestamps: {
|
||||
ConnectionEstablished: "",
|
||||
ConnectionStart: "",
|
||||
UserInputStart: "",
|
||||
UserInputSubmit: "",
|
||||
},
|
||||
},
|
||||
],
|
||||
target: "Metrics",
|
||||
type: 1,
|
||||
} as const;
|
||||
|
||||
/** Serialized Metrics follow-up frame (see {@link CHAT_METRICS_FRAME}). */
|
||||
export function metricsFrame(): string {
|
||||
return encodeFrame(CHAT_METRICS_FRAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a raw socket buffer into complete `\x1e`-terminated frames, returning any
|
||||
* trailing partial frame as `rest` so it can be prepended to the next chunk.
|
||||
@@ -155,22 +177,37 @@ export function handshakeError(frame: Record<string, unknown> | null): string |
|
||||
|
||||
export interface ChatInvocationOptions {
|
||||
text: string;
|
||||
/** Per-connection trace id (hex), reused as clientCorrelationId/traceId. */
|
||||
/** Per-invocation trace id (GUID). */
|
||||
traceId: string;
|
||||
/** Per-session id (GUID). */
|
||||
/** Client correlation id; defaults to {@link ChatInvocationOptions.traceId}. */
|
||||
clientCorrelationId?: string;
|
||||
/** Per-session id (GUID, == the WS URL X-SessionId query). */
|
||||
sessionId: string;
|
||||
/** Per-request id (== the WS URL chatsessionid/clientrequestid query). */
|
||||
requestId: string;
|
||||
/**
|
||||
* Conversation id — MUST match the ConversationId query of the WS URL the
|
||||
* invocation rides on (#10718: the server cross-checks the two).
|
||||
*/
|
||||
conversationId: string;
|
||||
/** BCP-47 locale echoed in message.locale; defaults to "en-us". */
|
||||
locale?: string;
|
||||
/** IANA time zone for message.locationInfo; defaults to "UTC". */
|
||||
timeZone?: string;
|
||||
/** Hour offset for message.locationInfo; defaults to 0. */
|
||||
timeZoneOffset?: number;
|
||||
/** Whether this is the first turn of the conversation. */
|
||||
isStartOfSession?: boolean;
|
||||
/** Tier-specific option flags; left empty by default (tuned during live validation). */
|
||||
/** Tier-specific option flags; defaults to {@link M365_DEFAULT_OPTION_SETS}. */
|
||||
optionsSets?: string[];
|
||||
tone?: string;
|
||||
/** Tier-specific allowed message types; defaults to {@link ALLOWED_MESSAGE_TYPES}. */
|
||||
allowedMessageTypes?: readonly string[];
|
||||
/**
|
||||
* Tier-specific disconnect behavior sent in every type:4 chat invocation. The work
|
||||
* Surface rejects any value other than exactly "continue" (#8971). Defaults to ""
|
||||
* for individual/consumer/EDU tiers; {@link resolveChatInvocationOverrides} returns
|
||||
* "continue" for the enterprise tier.
|
||||
* Tier-specific disconnect behavior sent in the type:4 chat invocation. The work
|
||||
* surface rejects any value other than exactly "continue" (#8971), so the
|
||||
* enterprise tier sends it; the 2026-08 recapture shows the individual/EDU
|
||||
* surface omits the key entirely, so it is left out unless set (#10718).
|
||||
*/
|
||||
disconnectBehavior?: string;
|
||||
}
|
||||
@@ -185,7 +222,7 @@ export function resolveChatInvocationOverrides(tier: string | undefined): {
|
||||
optionsSets: string[];
|
||||
tone: string;
|
||||
allowedMessageTypes: readonly string[];
|
||||
disconnectBehavior: string;
|
||||
disconnectBehavior: string | undefined;
|
||||
} {
|
||||
if (tier === "enterprise") {
|
||||
return {
|
||||
@@ -197,9 +234,12 @@ export function resolveChatInvocationOverrides(tier: string | undefined): {
|
||||
}
|
||||
return {
|
||||
optionsSets: [...M365_DEFAULT_OPTION_SETS],
|
||||
tone: "",
|
||||
// #10718 — the 2026-08 recapture sends tone:"magic" (lowercase) on the
|
||||
// individual/EDU surface; the old "" default is part of the dropped shape.
|
||||
tone: "magic",
|
||||
allowedMessageTypes: ALLOWED_MESSAGE_TYPES,
|
||||
disconnectBehavior: "",
|
||||
// Omitted entirely on the individual/EDU wire (see ChatInvocationOptions).
|
||||
disconnectBehavior: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -207,7 +247,7 @@ export function resolveChatInvocationOverrides(tier: string | undefined): {
|
||||
* BizChat exposes several models selected by the `tone` field of the `type:4` chat
|
||||
* invocation (#7872, values confirmed against a real enterprise tenant in #7850). Each
|
||||
* tone-selected variant is registered as its own model id; the bare `copilot-m365` id is
|
||||
* intentionally absent here so it keeps the tier default tone (`Magic` on enterprise, `""`
|
||||
* intentionally absent here so it keeps the tier default tone (`Magic` on enterprise, `magic`
|
||||
* otherwise) resolved by {@link resolveChatInvocationOverrides}.
|
||||
*/
|
||||
export const M365_MODEL_TONE_MAP: Readonly<Record<string, string>> = {
|
||||
@@ -228,7 +268,14 @@ export function resolveToneForModel(model: string | undefined): string | undefin
|
||||
|
||||
/**
|
||||
* Build the `type:4` chat invocation frame body (not yet `\x1e`-terminated).
|
||||
* Mirrors the argument shape captured on the individual M365 path in #4042.
|
||||
* Mirrors the argument shape recaptured from a working `m365.cloud.microsoft/chat`
|
||||
* client in 2026-08 (#10718). Notable differences from the pre-#10718 shape: a
|
||||
* populated `clientInfo` + `productThreadType:"Office"`, a `conversationId`
|
||||
* matching the WS URL query, a rich `message` object, and no
|
||||
* `spokenTextMode` / `extraExtensionParameters` / `isSbsSupported` /
|
||||
* `renderReferencesBehindEOS` / `disconnectBehavior` — none of those are still
|
||||
* observed on the wire, and the stale shape gets closed immediately after the
|
||||
* invocation.
|
||||
*/
|
||||
export function buildChatInvocation(opts: ChatInvocationOptions): Record<string, unknown> {
|
||||
return {
|
||||
@@ -237,33 +284,48 @@ export function buildChatInvocation(opts: ChatInvocationOptions): Record<string,
|
||||
invocationId: "0",
|
||||
arguments: [
|
||||
{
|
||||
source: "officeweb",
|
||||
clientCorrelationId: opts.traceId,
|
||||
sessionId: opts.sessionId,
|
||||
optionsSets: opts.optionsSets ?? [...M365_DEFAULT_OPTION_SETS],
|
||||
streamingMode: "ConciseWithPadding",
|
||||
spokenTextMode: "None",
|
||||
options: {},
|
||||
extraExtensionParameters: {},
|
||||
allowedMessageTypes: opts.allowedMessageTypes
|
||||
? [...opts.allowedMessageTypes]
|
||||
: [...ALLOWED_MESSAGE_TYPES],
|
||||
sliceIds: [],
|
||||
threadLevelGptId: {},
|
||||
traceId: opts.traceId,
|
||||
isStartOfSession: opts.isStartOfSession ?? true,
|
||||
clientInfo: {},
|
||||
message: {
|
||||
author: "user",
|
||||
inputMethod: "Keyboard",
|
||||
text: opts.text,
|
||||
messageType: "Chat",
|
||||
clientCorrelationId: opts.clientCorrelationId ?? opts.traceId,
|
||||
clientInfo: {
|
||||
clientAppName: "Office",
|
||||
clientPlatform: "mcmcopilot-web",
|
||||
},
|
||||
conversationId: opts.conversationId,
|
||||
isStartOfSession: opts.isStartOfSession ?? true,
|
||||
message: {
|
||||
adaptiveCards: [],
|
||||
attachments: null,
|
||||
author: "user",
|
||||
clientPreferences: {},
|
||||
entityAnnotationTypes: ["People", "File", "Event", "Email", "TeamsMessage"],
|
||||
experienceType: "Default",
|
||||
inputMethod: "Keyboard",
|
||||
locale: opts.locale ?? "en-us",
|
||||
locationInfo: {
|
||||
timeZone: opts.timeZone ?? "UTC",
|
||||
timeZoneOffset: opts.timeZoneOffset ?? 0,
|
||||
},
|
||||
messageType: "Chat",
|
||||
requestId: opts.requestId,
|
||||
text: opts.text,
|
||||
},
|
||||
options: {},
|
||||
optionsSets: opts.optionsSets ?? [...M365_DEFAULT_OPTION_SETS],
|
||||
plugins: [],
|
||||
isSbsSupported: false,
|
||||
tone: opts.tone ?? "",
|
||||
renderReferencesBehindEOS: true,
|
||||
disconnectBehavior: opts.disconnectBehavior ?? "",
|
||||
productThreadType: "Office",
|
||||
sessionId: opts.sessionId,
|
||||
sliceIds: [],
|
||||
source: "officeweb",
|
||||
streamingMode: "ConciseWithPadding",
|
||||
threadLevelGptId: {},
|
||||
tone: opts.tone ?? "magic",
|
||||
toolChoice: null,
|
||||
traceId: opts.traceId,
|
||||
// #8971 keeps "continue" for the enterprise tier; the individual/EDU wire
|
||||
// omits the key, so only include it when actually set (#10718).
|
||||
...(opts.disconnectBehavior ? { disconnectBehavior: opts.disconnectBehavior } : {}),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -5,8 +5,13 @@ import { BaseExecutor, type ExecuteInput, type ExecutorLog } from "./base.ts";
|
||||
import {
|
||||
buildPrompt,
|
||||
buildWsUrl,
|
||||
currentM365AccessToken,
|
||||
currentM365ChathubPath,
|
||||
decodeJwtClaims,
|
||||
redactWsUrl,
|
||||
refreshM365AccessToken,
|
||||
resolveConnectionParams,
|
||||
tokenNeedsRefresh,
|
||||
} from "./copilot-m365-connection.ts";
|
||||
import {
|
||||
accumulateBotContent,
|
||||
@@ -17,7 +22,7 @@ import {
|
||||
handshakeFrame,
|
||||
isCompletionFrame,
|
||||
isUpdateFrame,
|
||||
keepaliveFrame,
|
||||
metricsFrame,
|
||||
parseFrame,
|
||||
resolveChatInvocationOverrides,
|
||||
resolveToneForModel,
|
||||
@@ -152,8 +157,17 @@ export class CopilotM365WebExecutor extends BaseExecutor {
|
||||
|
||||
try {
|
||||
const wsUrlParts = new URL(input.wsUrl);
|
||||
const traceId = wsUrlParts.searchParams.get("clientrequestid") ?? crypto.randomUUID().replace(/-/g, "");
|
||||
// #10718 — the invocation must echo the ids riding in the WS URL query
|
||||
// (conversationId is cross-checked server-side). traceId is a fresh GUID
|
||||
// per turn, as in the browser capture.
|
||||
const requestId =
|
||||
wsUrlParts.searchParams.get("chatsessionid") ??
|
||||
wsUrlParts.searchParams.get("clientrequestid") ??
|
||||
crypto.randomUUID();
|
||||
const sessionId = wsUrlParts.searchParams.get("X-SessionId") ?? crypto.randomUUID();
|
||||
const conversationId =
|
||||
wsUrlParts.searchParams.get("ConversationId") ?? crypto.randomUUID();
|
||||
const traceId = crypto.randomUUID();
|
||||
|
||||
log?.debug?.("M365_WS", `connecting → ${redactWsUrl(input.wsUrl)}`);
|
||||
|
||||
@@ -166,23 +180,26 @@ export class CopilotM365WebExecutor extends BaseExecutor {
|
||||
});
|
||||
|
||||
const sendChat = () => {
|
||||
ws?.send(keepaliveFrame());
|
||||
const overrides = resolveChatInvocationOverrides(input.tier);
|
||||
// Model-driven tone (#7872) wins over the tier default; a bare/unknown id
|
||||
// keeps the tier tone resolved above.
|
||||
const tone = resolveToneForModel(input.model) ?? overrides.tone;
|
||||
ws?.send(
|
||||
encodeFrame(
|
||||
buildChatInvocation({
|
||||
text: input.prompt,
|
||||
traceId,
|
||||
sessionId,
|
||||
isStartOfSession: true,
|
||||
...overrides,
|
||||
tone,
|
||||
})
|
||||
)
|
||||
const invocationFrame = encodeFrame(
|
||||
buildChatInvocation({
|
||||
text: input.prompt,
|
||||
traceId,
|
||||
sessionId,
|
||||
requestId,
|
||||
conversationId,
|
||||
isStartOfSession: true,
|
||||
...overrides,
|
||||
tone,
|
||||
})
|
||||
);
|
||||
// #10718 — the invocation and its type:1 Metrics follow-up must land
|
||||
// in ONE socket write, exactly as the browser sends them; a bare
|
||||
// invocation (or one preceded by a type:6 ping) is silently dropped.
|
||||
ws?.send(invocationFrame + metricsFrame());
|
||||
};
|
||||
|
||||
ws.on("open", () => {
|
||||
@@ -273,6 +290,61 @@ export class CopilotM365WebExecutor extends BaseExecutor {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* #10718 — proactively refresh the M365 access token before opening the WS.
|
||||
* A WS-handshake 401 surfaces as an error event INSIDE the SSE stream (the HTTP
|
||||
* response is already 200 by then), so chatCore's generic 401→refresh→retry
|
||||
* orchestration never triggers — the refresh has to happen here, pre-flight.
|
||||
* No-ops for legacy connections without a stored refresh_token.
|
||||
*/
|
||||
private async ensureFreshCredentials(
|
||||
credentials: ExecuteInput["credentials"],
|
||||
onCredentialsRefreshed: ExecuteInput["onCredentialsRefreshed"],
|
||||
log: ExecutorLog | null
|
||||
): Promise<void> {
|
||||
const psd = (credentials?.providerSpecificData ?? {}) as JsonRecord;
|
||||
const refreshToken =
|
||||
credentials.refreshToken || (typeof psd.refreshToken === "string" ? psd.refreshToken : "");
|
||||
if (!refreshToken) return;
|
||||
|
||||
const current = currentM365AccessToken(credentials);
|
||||
if (current && !tokenNeedsRefresh(current)) return;
|
||||
|
||||
const tid =
|
||||
decodeJwtClaims(current)?.tid || (typeof psd.tid === "string" ? psd.tid : "") || "";
|
||||
const result = await refreshM365AccessToken(refreshToken, tid, log ?? undefined);
|
||||
if ("error" in result) {
|
||||
// Fall through with the existing token — the WS layer will surface the failure.
|
||||
return;
|
||||
}
|
||||
|
||||
const rotated = result.refreshToken || refreshToken;
|
||||
const chathubPath = currentM365ChathubPath(credentials);
|
||||
const next = {
|
||||
...credentials,
|
||||
accessToken: result.accessToken,
|
||||
refreshToken: rotated,
|
||||
// Keep the pasted-format apiKey self-consistent so every resolution path
|
||||
// (fresh column, stale column, dashboard re-read) sees the same token.
|
||||
...(chathubPath
|
||||
? { apiKey: `access_token=${result.accessToken}; chathubPath=${chathubPath}` }
|
||||
: {}),
|
||||
...(result.expiresIn
|
||||
? { expiresAt: new Date(Date.now() + result.expiresIn * 1000).toISOString() }
|
||||
: {}),
|
||||
};
|
||||
Object.assign(credentials, next);
|
||||
try {
|
||||
await onCredentialsRefreshed?.(next);
|
||||
} catch (err) {
|
||||
// #7676 pattern: a persistence failure must never fail the user-facing response.
|
||||
log?.warn?.(
|
||||
"M365_TOKEN",
|
||||
`persisting refreshed token failed (${err instanceof Error ? err.message : String(err)}) — will re-refresh next request`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async execute(input: ExecuteInput): Promise<{
|
||||
response: Response;
|
||||
url: string;
|
||||
@@ -293,6 +365,12 @@ export class CopilotM365WebExecutor extends BaseExecutor {
|
||||
};
|
||||
}
|
||||
|
||||
await this.ensureFreshCredentials(
|
||||
input.credentials,
|
||||
input.onCredentialsRefreshed,
|
||||
input.log ?? null
|
||||
);
|
||||
|
||||
const connectionParams = resolveConnectionParams(input.credentials);
|
||||
if ("error" in connectionParams) {
|
||||
return {
|
||||
|
||||
@@ -57,6 +57,13 @@ import {
|
||||
type StreamingState as ComposerStreamingState,
|
||||
} from "../utils/composerToolCalls.ts";
|
||||
import { cursorSessionManager, type CursorSession } from "../services/cursorSessionManager.ts";
|
||||
import {
|
||||
CursorApiKeyExchangeError,
|
||||
invalidateCursorSessionToken,
|
||||
isCursorApiKey,
|
||||
resolveCursorBearerToken,
|
||||
stripCursorOAuthTokenPrefix,
|
||||
} from "../services/cursorApiKeyAuth.ts";
|
||||
import crypto from "crypto";
|
||||
import * as fs from "node:fs";
|
||||
import * as zlib from "node:zlib";
|
||||
@@ -706,18 +713,44 @@ export function processFrame(
|
||||
}
|
||||
|
||||
export class CursorExecutor extends BaseExecutor {
|
||||
constructor() {
|
||||
super("cursor", PROVIDERS.cursor);
|
||||
constructor(provider: "cursor" | "cursor-api" = "cursor") {
|
||||
super(provider, PROVIDERS[provider]);
|
||||
}
|
||||
|
||||
buildUrl() {
|
||||
return CURSOR_AGENT_URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* API-key connections carry a `crsr_…` key that api2.cursor.sh does not
|
||||
* accept as a Bearer; swap it for the exchanged session token before the
|
||||
* h2 stream is opened. OAuth/IDE-session connections pass through untouched.
|
||||
*/
|
||||
async resolveExecutionCredentials(credentials) {
|
||||
if (!isCursorApiKey(credentials?.apiKey)) return credentials;
|
||||
try {
|
||||
const accessToken = await resolveCursorBearerToken(credentials);
|
||||
return { ...credentials, accessToken };
|
||||
} catch (err) {
|
||||
const status =
|
||||
err instanceof CursorApiKeyExchangeError ? err.status : HTTP_STATUS.SERVER_ERROR;
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: {
|
||||
message: sanitizeErrorMessage(message),
|
||||
type: status === HTTP_STATUS.UNAUTHORIZED ? "authentication_error" : "connection_error",
|
||||
code: "",
|
||||
},
|
||||
}),
|
||||
{ status, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
buildHeaders(credentials) {
|
||||
const accessToken = credentials.accessToken;
|
||||
const ghostMode = credentials.providerSpecificData?.ghostMode !== false;
|
||||
const cleanToken = accessToken.includes("::") ? accessToken.split("::")[1] : accessToken;
|
||||
const cleanToken = stripCursorOAuthTokenPrefix(credentials.accessToken ?? "");
|
||||
const requestId = crypto.randomUUID();
|
||||
const traceParent = `00-${crypto.randomBytes(16).toString("hex")}-${crypto.randomBytes(8).toString("hex")}-01`;
|
||||
|
||||
@@ -825,7 +858,7 @@ export class CursorExecutor extends BaseExecutor {
|
||||
*/
|
||||
private async loadLiveCatalogIds(): Promise<ReadonlySet<string> | undefined> {
|
||||
try {
|
||||
const catalog = await getActiveSyncedCatalog("cursor");
|
||||
const catalog = await getActiveSyncedCatalog(this.provider);
|
||||
if (!catalog.models.length) return undefined;
|
||||
return new Set(catalog.models.map((model) => model.id));
|
||||
} catch {
|
||||
@@ -1179,7 +1212,11 @@ export class CursorExecutor extends BaseExecutor {
|
||||
|
||||
async execute({ model, body, stream, credentials, signal, log, upstreamExtraHeaders }) {
|
||||
const url = this.buildUrl();
|
||||
const headers = this.buildHeaders(credentials);
|
||||
const executionCredentials = await this.resolveExecutionCredentials(credentials);
|
||||
if (executionCredentials instanceof Response) {
|
||||
return { response: executionCredentials, url, headers: {}, transformedBody: body };
|
||||
}
|
||||
const headers = this.buildHeaders(executionCredentials);
|
||||
mergeUpstreamExtraHeaders(headers, upstreamExtraHeaders);
|
||||
|
||||
const messages: ChatMessage[] = body.messages || [];
|
||||
@@ -1252,8 +1289,10 @@ export class CursorExecutor extends BaseExecutor {
|
||||
if (isToolFollowUp) {
|
||||
session = cursorSessionManager.acquire(conversationId);
|
||||
// #9029: content-based session match when client lacks conversation_id.
|
||||
if (!session && !body.conversation_id) session = cursorSessionManager.findByToolCallIds(
|
||||
messages.filter(m => m.role === "tool" && m.tool_call_id).map(m => m.tool_call_id!));
|
||||
if (!session && !body.conversation_id)
|
||||
session = cursorSessionManager.findByToolCallIds(
|
||||
messages.filter((m) => m.role === "tool" && m.tool_call_id).map((m) => m.tool_call_id!)
|
||||
);
|
||||
}
|
||||
|
||||
if (session) {
|
||||
@@ -1334,6 +1373,9 @@ export class CursorExecutor extends BaseExecutor {
|
||||
if (opened.status !== 200) {
|
||||
const errBuf = await opened.consumeError();
|
||||
const errText = errBuf.toString("utf8") || "Unknown error";
|
||||
if (opened.status === HTTP_STATUS.UNAUTHORIZED && isCursorApiKey(credentials.apiKey)) {
|
||||
invalidateCursorSessionToken(credentials.apiKey);
|
||||
}
|
||||
return {
|
||||
response: buildErrorResponse(opened.status, `[${opened.status}]: ${errText}`),
|
||||
url,
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { SEARCH_PROVIDERS } from "../config/searchRegistry.ts";
|
||||
import {
|
||||
registerExecutor,
|
||||
getRegisteredExecutor,
|
||||
hasRegisteredExecutor,
|
||||
} from "./registry.ts";
|
||||
import { registerExecutor, getRegisteredExecutor, hasRegisteredExecutor } from "./registry.ts";
|
||||
import { AntigravityExecutor } from "./antigravity.ts";
|
||||
import { GithubExecutor } from "./github.ts";
|
||||
import { GheCopilotExecutor } from "./ghe-copilot.ts";
|
||||
@@ -107,6 +103,8 @@ const executors = {
|
||||
"glm-cn": new GlmExecutor("glm-cn"),
|
||||
glmt: new GlmExecutor("glmt"),
|
||||
cu: new CursorExecutor(), // Alias for cursor
|
||||
"cursor-api": new CursorExecutor("cursor-api"),
|
||||
cua: new CursorExecutor("cursor-api"),
|
||||
"azure-openai": new AzureOpenAIExecutor(),
|
||||
"azure-ai": new AzureAiExecutor(),
|
||||
"command-code": new CommandCodeExecutor(),
|
||||
|
||||
@@ -41,7 +41,11 @@ import {
|
||||
isStripReasoningRequested,
|
||||
} from "./chatCore/headers.ts";
|
||||
import { markCodexScopeRateLimited } from "./chatCore/codexFailover.ts";
|
||||
import { isCodexOriginatedHeaders } from "../config/codexIdentity.ts";
|
||||
import { getCodexClientSessionId, isCodexOriginatedHeaders } from "../config/codexIdentity.ts";
|
||||
import {
|
||||
noteCodexTurnStateProvenance,
|
||||
readCodexTurnStateHeader,
|
||||
} from "../config/codexTurnState.ts";
|
||||
import { trackDevice, extractIpFromHeaders } from "../services/deviceTracker.ts";
|
||||
import { getCombosCached } from "./chatCore/comboContextCache.ts";
|
||||
export { clearCombosCache, clearUpstreamProxyConfigCache } from "./chatCore/comboContextCache.ts";
|
||||
@@ -722,12 +726,13 @@ export async function handleChatCore({
|
||||
copilotCompatibleReasoning,
|
||||
clientResponseFormat,
|
||||
} = resolveChatCoreRequestFormat({ clientRawRequest, body, provider, userAgent });
|
||||
const nativeOpenAICompatibleResponsesPassthrough = shouldUseNativeOpenAICompatibleResponsesPassthrough({
|
||||
provider,
|
||||
sourceFormat,
|
||||
endpointPath,
|
||||
providerSpecificData: credentials?.providerSpecificData,
|
||||
});
|
||||
const nativeOpenAICompatibleResponsesPassthrough =
|
||||
shouldUseNativeOpenAICompatibleResponsesPassthrough({
|
||||
provider,
|
||||
sourceFormat,
|
||||
endpointPath,
|
||||
providerSpecificData: credentials?.providerSpecificData,
|
||||
});
|
||||
const responsesInputItems = Array.isArray(body?.input) ? body.input : [];
|
||||
const customToolNames = collectCustomToolNamesForSourceFormat(
|
||||
sourceFormat,
|
||||
@@ -3387,6 +3392,15 @@ export async function handleChatCore({
|
||||
const responseHeaders = new Headers(headersObj);
|
||||
stripStaleForwardingHeaders(responseHeaders);
|
||||
stripNextMiddlewareControlHeaders(responseHeaders);
|
||||
// The upstream headers (turn-state included) are about to be committed
|
||||
// to the client — record which connection minted the blob so a later
|
||||
// cross-account echo can be stripped (Codex failover guard).
|
||||
if (provider === "codex" && readCodexTurnStateHeader(responseHeaders)) {
|
||||
noteCodexTurnStateProvenance(
|
||||
getCodexClientSessionId(clientRawRequest?.headers),
|
||||
rawResult._executionCredentials?.connectionId ?? credentials?.connectionId
|
||||
);
|
||||
}
|
||||
const contentType = (responseHeaders.get("content-type") || "").toLowerCase();
|
||||
const payload = await readNonStreamingResponseBody(
|
||||
rawResult.response,
|
||||
@@ -5106,6 +5120,17 @@ export async function handleChatCore({
|
||||
comboStrategy,
|
||||
});
|
||||
|
||||
// The streaming headers (turn-state included, when present) are committed to
|
||||
// the client from here on — record which connection minted the blob so a
|
||||
// later cross-account echo can be stripped (Codex failover guard). The
|
||||
// in-place failover update means `credentials` is the winning account.
|
||||
if (provider === "codex" && readCodexTurnStateHeader(providerResponse.headers)) {
|
||||
noteCodexTurnStateProvenance(
|
||||
getCodexClientSessionId(clientRawRequest?.headers),
|
||||
credentials?.connectionId
|
||||
);
|
||||
}
|
||||
|
||||
// Create transform stream with logger for streaming response
|
||||
let transformStream;
|
||||
const responseToolNameMap = mergeResponseToolNameMap(
|
||||
|
||||
@@ -28,11 +28,18 @@ const STREAMING_RESPONSE_HEADER_DENYLIST = new Set([
|
||||
"x-amz-security-token",
|
||||
"x-auth-token",
|
||||
"x-accel-buffering",
|
||||
// 314-byte Codex session blob. It is not a client rate-limit signal and
|
||||
// alone ate ~40% of the old 768-byte budget, evicting x-codex-*-used-percent.
|
||||
"x-codex-turn-state",
|
||||
]);
|
||||
|
||||
/**
|
||||
* `x-codex-turn-state` is forwarded verbatim and EXEMPT from the forwarding
|
||||
* budget. The real Codex client captures this ~314-byte blob from /responses
|
||||
* (and echoes it back within the same turn), so dropping it breaks the
|
||||
* protocol chain — but naively counting it against the budget used to evict
|
||||
* the x-codex-*-used-percent quota headers (the reason it was denylisted
|
||||
* under #10315-era budgeting). Carving it out keeps both.
|
||||
*/
|
||||
const CODEX_TURN_STATE_RESPONSE_HEADER = "x-codex-turn-state";
|
||||
|
||||
const DEFAULT_FORWARDED_HEADER_BUDGET_BYTES = 768;
|
||||
|
||||
/**
|
||||
@@ -206,7 +213,9 @@ export function buildStreamingResponseHeaders(
|
||||
STREAMING_RESPONSE_HEADER_DENYLIST.has(normalized) ||
|
||||
connectionScopedHeaders.has(normalized) ||
|
||||
isNextMiddlewareControlHeader(normalized) ||
|
||||
isOmniRouteInternalHeader(normalized)
|
||||
isOmniRouteInternalHeader(normalized) ||
|
||||
// Forwarded separately below, outside the byte budget.
|
||||
normalized === CODEX_TURN_STATE_RESPONSE_HEADER
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -269,6 +278,10 @@ export function buildStreamingResponseHeaders(
|
||||
"X-Accel-Buffering": "no",
|
||||
[OMNIROUTE_RESPONSE_HEADERS.cache]: "MISS",
|
||||
};
|
||||
const codexTurnState = providerHeaders.get(CODEX_TURN_STATE_RESPONSE_HEADER)?.trim();
|
||||
if (codexTurnState) {
|
||||
responseHeaders[CODEX_TURN_STATE_RESPONSE_HEADER] = codexTurnState;
|
||||
}
|
||||
attachOmniRouteMetaHeaders(responseHeaders, meta);
|
||||
return responseHeaders;
|
||||
}
|
||||
|
||||
524
open-sse/handlers/cursorCliProxy.ts
Normal file
@@ -0,0 +1,524 @@
|
||||
/**
|
||||
* Cursor CLI passthrough.
|
||||
*
|
||||
* cursor-agent honours `--endpoint` / CURSOR_API_ENDPOINT and, with
|
||||
* `network.useHttp1ForAgent: true`, talks to that endpoint exclusively over
|
||||
* HTTP/1.1: unary Connect-RPC POSTs (`/aiserver.v1.*`, `/agent.v1.*`,
|
||||
* `/aiserver.v1.BidiService/BidiAppend`), the agent turn as
|
||||
* `/agent.v1.AgentService/RunSSE` (text/event-stream), OTLP traces on
|
||||
* `/v1/traces`, and the API-key bootstrap `POST /auth/exchange_user_api_key`.
|
||||
*
|
||||
* Pointing the CLI at OmniRoute therefore only needs a thin forwarder:
|
||||
* 1. `/auth/exchange_user_api_key` authenticates the CLI with an OmniRoute
|
||||
* API key and hands back an OmniRoute-minted session JWT. The CLI reads
|
||||
* `exp` from whatever JWT it receives and re-exchanges when the token is
|
||||
* opaque or expired, so the minted token must be a real JWT with `exp`.
|
||||
* 2. Every other path verifies that JWT, resolves an active `cursor-api`
|
||||
* connection (the crsr_ key is exchanged for a session token), swaps the
|
||||
* Authorization header and streams the upstream reply back unchanged.
|
||||
* Each hop is recorded in call_logs.
|
||||
*/
|
||||
|
||||
import { SignJWT, jwtVerify, type JWTPayload } from "jose";
|
||||
import { z } from "zod";
|
||||
import { getApiKeyById, getApiKeyMetadata, validateApiKey } from "@/lib/db/apiKeys";
|
||||
import { getProviderConnections } from "@/lib/db/providers";
|
||||
import { saveCallLog } from "@/lib/usage/callLogs";
|
||||
import { isRequireApiKeyEnabled } from "@/shared/utils/featureFlags";
|
||||
import { HTTP_STATUS } from "../config/constants.ts";
|
||||
import {
|
||||
CURSOR_API_BASE_URL,
|
||||
CURSOR_API_KEY_EXCHANGE_PATH,
|
||||
CursorApiKeyExchangeError,
|
||||
invalidateCursorSessionToken,
|
||||
isCursorApiKey,
|
||||
resolveCursorBearerToken,
|
||||
} from "../services/cursorApiKeyAuth.ts";
|
||||
import { sanitizeErrorMessage } from "../utils/error.ts";
|
||||
|
||||
export const CURSOR_CLI_PROXY_PREFIX = "/api/cursor-cli";
|
||||
export const CURSOR_CLI_SESSION_ISSUER = "omniroute";
|
||||
export const CURSOR_CLI_SESSION_AUDIENCE = "cursor-cli";
|
||||
export const CURSOR_CLI_SESSION_TTL_SECONDS = 60 * 60;
|
||||
export const CURSOR_CLI_REQUEST_TYPE = "cursor-cli";
|
||||
const ANONYMOUS_SUBJECT = "anonymous";
|
||||
const PROVIDER_ID = "cursor-api";
|
||||
|
||||
const REQUEST_HEADER_DENYLIST = new Set([
|
||||
"authorization",
|
||||
"host",
|
||||
"connection",
|
||||
"content-length",
|
||||
"accept-encoding",
|
||||
"keep-alive",
|
||||
"proxy-authorization",
|
||||
"proxy-connection",
|
||||
"te",
|
||||
"trailer",
|
||||
"transfer-encoding",
|
||||
"upgrade",
|
||||
"x-forwarded-for",
|
||||
"x-forwarded-host",
|
||||
"x-forwarded-proto",
|
||||
"x-real-ip",
|
||||
"cookie",
|
||||
]);
|
||||
|
||||
const RESPONSE_HEADER_DENYLIST = new Set([
|
||||
"connection",
|
||||
"content-encoding",
|
||||
"content-length",
|
||||
"keep-alive",
|
||||
"transfer-encoding",
|
||||
"set-cookie",
|
||||
]);
|
||||
|
||||
const exchangeBodySchema = z.object({}).passthrough();
|
||||
|
||||
const sessionClaimsSchema = z.object({
|
||||
sub: z.string().min(1),
|
||||
iss: z.literal(CURSOR_CLI_SESSION_ISSUER),
|
||||
aud: z.union([
|
||||
z.literal(CURSOR_CLI_SESSION_AUDIENCE),
|
||||
z.array(z.string()).refine((list) => list.includes(CURSOR_CLI_SESSION_AUDIENCE)),
|
||||
]),
|
||||
exp: z.number(),
|
||||
name: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export type CursorCliPrincipal = {
|
||||
apiKeyId: string | null;
|
||||
apiKeyName: string | null;
|
||||
};
|
||||
|
||||
export type CursorCliConnectionLike = {
|
||||
id?: unknown;
|
||||
apiKey?: unknown;
|
||||
accessToken?: unknown;
|
||||
priority?: unknown;
|
||||
rateLimitedUntil?: unknown;
|
||||
};
|
||||
|
||||
export type CursorCliProxyDeps = {
|
||||
fetchImpl: typeof fetch;
|
||||
now: () => number;
|
||||
getSecret: () => string | undefined;
|
||||
validateApiKey: (key: string) => Promise<boolean>;
|
||||
getApiKeyMetadata: (key: string) => Promise<{ id: string; name: string } | null>;
|
||||
getApiKeyById: (id: string) => Promise<{ isActive?: unknown; revokedAt?: unknown } | null>;
|
||||
requireApiKey: () => boolean;
|
||||
listCursorConnections: () => Promise<CursorCliConnectionLike[]>;
|
||||
resolveBearer: (credentials: {
|
||||
apiKey?: string | null;
|
||||
accessToken?: string | null;
|
||||
}) => Promise<string>;
|
||||
invalidateBearer: (apiKey: string) => void;
|
||||
saveCallLog: (entry: Record<string, unknown>) => Promise<void>;
|
||||
upstreamBaseUrl: string;
|
||||
};
|
||||
|
||||
const defaultDeps: CursorCliProxyDeps = {
|
||||
fetchImpl: (input, init) => fetch(input, init),
|
||||
now: () => Date.now(),
|
||||
getSecret: () => process.env.JWT_SECRET,
|
||||
validateApiKey: (key) => validateApiKey(key),
|
||||
getApiKeyMetadata: async (key) => {
|
||||
const meta = await getApiKeyMetadata(key);
|
||||
return meta ? { id: meta.id, name: meta.name } : null;
|
||||
},
|
||||
getApiKeyById: (id) => getApiKeyById(id),
|
||||
requireApiKey: () => isRequireApiKeyEnabled(),
|
||||
listCursorConnections: async () =>
|
||||
(await getProviderConnections({
|
||||
provider: PROVIDER_ID,
|
||||
isActive: true,
|
||||
})) as CursorCliConnectionLike[],
|
||||
resolveBearer: (credentials) => resolveCursorBearerToken(credentials),
|
||||
invalidateBearer: (apiKey) => invalidateCursorSessionToken(apiKey),
|
||||
saveCallLog: (entry) => saveCallLog(entry),
|
||||
upstreamBaseUrl: CURSOR_API_BASE_URL,
|
||||
};
|
||||
|
||||
function jsonResponse(status: number, body: Record<string, unknown>): Response {
|
||||
return new Response(JSON.stringify(body), {
|
||||
status,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
function connectError(status: number, code: string, message: string): Response {
|
||||
return jsonResponse(status, { code, message: sanitizeErrorMessage(message) });
|
||||
}
|
||||
|
||||
function extractBearer(request: Request): string | null {
|
||||
const header = request.headers.get("authorization") ?? "";
|
||||
const match = /^Bearer\s+(.+)$/i.exec(header.trim());
|
||||
return match ? match[1].trim() : null;
|
||||
}
|
||||
|
||||
function secretKey(secret: string): Uint8Array {
|
||||
return new TextEncoder().encode(secret);
|
||||
}
|
||||
|
||||
export function normalizeCursorCliPath(segments: readonly string[]): string {
|
||||
return "/" + segments.map((segment) => encodeURIComponent(decodeURIComponent(segment))).join("/");
|
||||
}
|
||||
|
||||
async function authenticateExchange(
|
||||
request: Request,
|
||||
deps: CursorCliProxyDeps
|
||||
): Promise<CursorCliPrincipal | Response> {
|
||||
const bearer = extractBearer(request);
|
||||
if (bearer && (await deps.validateApiKey(bearer))) {
|
||||
const meta = await deps.getApiKeyMetadata(bearer);
|
||||
return { apiKeyId: meta?.id ?? null, apiKeyName: meta?.name ?? null };
|
||||
}
|
||||
if (!deps.requireApiKey()) {
|
||||
return { apiKeyId: null, apiKeyName: null };
|
||||
}
|
||||
return connectError(
|
||||
HTTP_STATUS.UNAUTHORIZED,
|
||||
"unauthenticated",
|
||||
"CURSOR_API_KEY must be an OmniRoute API key when OmniRoute requires API keys"
|
||||
);
|
||||
}
|
||||
|
||||
export async function mintCursorCliSessionToken(
|
||||
principal: CursorCliPrincipal,
|
||||
secret: string,
|
||||
nowMs: number
|
||||
): Promise<string> {
|
||||
const nowSeconds = Math.floor(nowMs / 1000);
|
||||
return new SignJWT({ name: principal.apiKeyName })
|
||||
.setProtectedHeader({ alg: "HS256", typ: "JWT" })
|
||||
.setIssuer(CURSOR_CLI_SESSION_ISSUER)
|
||||
.setAudience(CURSOR_CLI_SESSION_AUDIENCE)
|
||||
.setSubject(principal.apiKeyId ?? ANONYMOUS_SUBJECT)
|
||||
.setIssuedAt(nowSeconds)
|
||||
.setExpirationTime(nowSeconds + CURSOR_CLI_SESSION_TTL_SECONDS)
|
||||
.sign(secretKey(secret));
|
||||
}
|
||||
|
||||
async function verifyCursorCliSessionToken(
|
||||
token: string,
|
||||
secret: string,
|
||||
nowMs: number
|
||||
): Promise<CursorCliPrincipal | null> {
|
||||
let payload: JWTPayload;
|
||||
try {
|
||||
({ payload } = await jwtVerify(token, secretKey(secret), {
|
||||
issuer: CURSOR_CLI_SESSION_ISSUER,
|
||||
audience: CURSOR_CLI_SESSION_AUDIENCE,
|
||||
currentDate: new Date(nowMs),
|
||||
}));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const claims = sessionClaimsSchema.safeParse(payload);
|
||||
if (!claims.success) return null;
|
||||
return {
|
||||
apiKeyId: claims.data.sub === ANONYMOUS_SUBJECT ? null : claims.data.sub,
|
||||
apiKeyName: claims.data.name ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
async function isPrincipalStillValid(
|
||||
principal: CursorCliPrincipal,
|
||||
deps: CursorCliProxyDeps
|
||||
): Promise<boolean> {
|
||||
if (!principal.apiKeyId) return !deps.requireApiKey();
|
||||
const row = await deps.getApiKeyById(principal.apiKeyId);
|
||||
if (!row) return false;
|
||||
if (row.isActive === false) return false;
|
||||
return !(typeof row.revokedAt === "string" && row.revokedAt.trim() !== "");
|
||||
}
|
||||
|
||||
type ResolvedConnection = {
|
||||
connectionId: string | null;
|
||||
bearer: string;
|
||||
apiKey: string | null;
|
||||
};
|
||||
|
||||
function connectionPriority(connection: CursorCliConnectionLike): number {
|
||||
return typeof connection.priority === "number" ? connection.priority : Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
function isCoolingDown(connection: CursorCliConnectionLike, nowMs: number): boolean {
|
||||
if (typeof connection.rateLimitedUntil !== "string") return false;
|
||||
const until = Date.parse(connection.rateLimitedUntil);
|
||||
return Number.isFinite(until) && until > nowMs;
|
||||
}
|
||||
|
||||
async function resolveUpstreamConnection(
|
||||
deps: CursorCliProxyDeps
|
||||
): Promise<ResolvedConnection | Response> {
|
||||
const connections = (await deps.listCursorConnections())
|
||||
.filter((connection) => !isCoolingDown(connection, deps.now()))
|
||||
.sort((a, b) => connectionPriority(a) - connectionPriority(b));
|
||||
if (connections.length === 0) {
|
||||
return connectError(
|
||||
HTTP_STATUS.SERVICE_UNAVAILABLE,
|
||||
"unavailable",
|
||||
"No active Cursor API connection configured in OmniRoute"
|
||||
);
|
||||
}
|
||||
let lastError: unknown = null;
|
||||
for (const connection of connections) {
|
||||
const apiKey = isCursorApiKey(connection.apiKey) ? connection.apiKey : null;
|
||||
const accessToken = typeof connection.accessToken === "string" ? connection.accessToken : null;
|
||||
try {
|
||||
const bearer = await deps.resolveBearer({ apiKey, accessToken });
|
||||
return {
|
||||
connectionId: typeof connection.id === "string" ? connection.id : null,
|
||||
bearer,
|
||||
apiKey,
|
||||
};
|
||||
} catch (err) {
|
||||
lastError = err;
|
||||
}
|
||||
}
|
||||
const status =
|
||||
lastError instanceof CursorApiKeyExchangeError ? lastError.status : HTTP_STATUS.BAD_GATEWAY;
|
||||
const message = lastError instanceof Error ? lastError.message : "Cursor credential unavailable";
|
||||
return connectError(
|
||||
status,
|
||||
status === HTTP_STATUS.UNAUTHORIZED ? "unauthenticated" : "unavailable",
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
function buildUpstreamHeaders(request: Request, bearer: string): Headers {
|
||||
const headers = new Headers();
|
||||
request.headers.forEach((value, name) => {
|
||||
if (!REQUEST_HEADER_DENYLIST.has(name.toLowerCase())) headers.set(name, value);
|
||||
});
|
||||
headers.set("authorization", `Bearer ${bearer}`);
|
||||
return headers;
|
||||
}
|
||||
|
||||
function buildDownstreamHeaders(upstream: Response): Headers {
|
||||
const headers = new Headers();
|
||||
upstream.headers.forEach((value, name) => {
|
||||
if (!RESPONSE_HEADER_DENYLIST.has(name.toLowerCase())) headers.set(name, value);
|
||||
});
|
||||
return headers;
|
||||
}
|
||||
|
||||
type CallLogInput = {
|
||||
method: string;
|
||||
path: string;
|
||||
status: number;
|
||||
startedAt: number;
|
||||
principal: CursorCliPrincipal | null;
|
||||
connectionId: string | null;
|
||||
error?: string | null;
|
||||
};
|
||||
|
||||
function recordCall(deps: CursorCliProxyDeps, input: CallLogInput): void {
|
||||
void deps
|
||||
.saveCallLog({
|
||||
method: input.method,
|
||||
path: `${CURSOR_CLI_PROXY_PREFIX}${input.path}`,
|
||||
status: input.status,
|
||||
model: "-",
|
||||
provider: PROVIDER_ID,
|
||||
connectionId: input.connectionId,
|
||||
duration: Math.max(0, deps.now() - input.startedAt),
|
||||
apiKeyId: input.principal?.apiKeyId ?? null,
|
||||
apiKeyName: input.principal?.apiKeyName ?? null,
|
||||
requestType: CURSOR_CLI_REQUEST_TYPE,
|
||||
sourceFormat: CURSOR_CLI_REQUEST_TYPE,
|
||||
targetFormat: CURSOR_CLI_REQUEST_TYPE,
|
||||
error: input.error ? { message: sanitizeErrorMessage(input.error) } : null,
|
||||
})
|
||||
.catch(() => undefined);
|
||||
}
|
||||
|
||||
function streamWithCompletionLog(
|
||||
body: ReadableStream<Uint8Array>,
|
||||
onDone: (error?: string) => void
|
||||
): ReadableStream<Uint8Array> {
|
||||
const reader = body.getReader();
|
||||
let settled = false;
|
||||
const settle = (error?: string) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
onDone(error);
|
||||
};
|
||||
return new ReadableStream<Uint8Array>({
|
||||
async pull(controller) {
|
||||
try {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
settle();
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
controller.enqueue(value);
|
||||
} catch (err) {
|
||||
settle(err instanceof Error ? err.message : "upstream stream failed");
|
||||
controller.error(err);
|
||||
}
|
||||
},
|
||||
cancel(reason) {
|
||||
settle(reason instanceof Error ? reason.message : "stream cancelled");
|
||||
return reader.cancel(reason);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function handleExchange(
|
||||
request: Request,
|
||||
startedAt: number,
|
||||
deps: CursorCliProxyDeps
|
||||
): Promise<Response> {
|
||||
if (request.method !== "POST") {
|
||||
return connectError(405, "unimplemented", "Use POST");
|
||||
}
|
||||
const rawBody = await request.text();
|
||||
if (rawBody.trim().length > 0) {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(rawBody);
|
||||
} catch {
|
||||
return connectError(HTTP_STATUS.BAD_REQUEST, "invalid_argument", "Body must be JSON");
|
||||
}
|
||||
if (!exchangeBodySchema.safeParse(parsed).success) {
|
||||
return connectError(
|
||||
HTTP_STATUS.BAD_REQUEST,
|
||||
"invalid_argument",
|
||||
"Body must be a JSON object"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const principal = await authenticateExchange(request, deps);
|
||||
if (principal instanceof Response) {
|
||||
recordCall(deps, {
|
||||
method: request.method,
|
||||
path: CURSOR_API_KEY_EXCHANGE_PATH,
|
||||
status: principal.status,
|
||||
startedAt,
|
||||
principal: null,
|
||||
connectionId: null,
|
||||
error: "OmniRoute API key rejected",
|
||||
});
|
||||
return principal;
|
||||
}
|
||||
|
||||
const secret = deps.getSecret();
|
||||
if (!secret || secret.trim().length === 0) {
|
||||
return connectError(
|
||||
HTTP_STATUS.SERVICE_UNAVAILABLE,
|
||||
"unavailable",
|
||||
"JWT_SECRET is not configured; the Cursor CLI passthrough cannot mint session tokens"
|
||||
);
|
||||
}
|
||||
|
||||
const token = await mintCursorCliSessionToken(principal, secret, deps.now());
|
||||
recordCall(deps, {
|
||||
method: request.method,
|
||||
path: CURSOR_API_KEY_EXCHANGE_PATH,
|
||||
status: 200,
|
||||
startedAt,
|
||||
principal,
|
||||
connectionId: null,
|
||||
});
|
||||
return jsonResponse(200, { accessToken: token, refreshToken: token });
|
||||
}
|
||||
|
||||
async function handleForward(
|
||||
request: Request,
|
||||
path: string,
|
||||
startedAt: number,
|
||||
deps: CursorCliProxyDeps
|
||||
): Promise<Response> {
|
||||
const secret = deps.getSecret();
|
||||
const bearer = extractBearer(request);
|
||||
const principal =
|
||||
bearer && secret ? await verifyCursorCliSessionToken(bearer, secret, deps.now()) : null;
|
||||
if (!principal || !(await isPrincipalStillValid(principal, deps))) {
|
||||
return connectError(
|
||||
HTTP_STATUS.UNAUTHORIZED,
|
||||
"unauthenticated",
|
||||
"Missing or expired OmniRoute Cursor CLI session token"
|
||||
);
|
||||
}
|
||||
|
||||
const resolved = await resolveUpstreamConnection(deps);
|
||||
if (resolved instanceof Response) {
|
||||
recordCall(deps, {
|
||||
method: request.method,
|
||||
path,
|
||||
status: resolved.status,
|
||||
startedAt,
|
||||
principal,
|
||||
connectionId: null,
|
||||
error: "No usable Cursor connection",
|
||||
});
|
||||
return resolved;
|
||||
}
|
||||
|
||||
const search = new URL(request.url).search;
|
||||
const upstreamUrl = `${deps.upstreamBaseUrl}${path}${search}`;
|
||||
const hasBody = request.method !== "GET" && request.method !== "HEAD";
|
||||
let upstream: Response;
|
||||
try {
|
||||
upstream = await deps.fetchImpl(upstreamUrl, {
|
||||
method: request.method,
|
||||
headers: buildUpstreamHeaders(request, resolved.bearer),
|
||||
body: hasBody ? request.body : undefined,
|
||||
signal: request.signal,
|
||||
redirect: "manual",
|
||||
...(hasBody ? { duplex: "half" } : {}),
|
||||
} as RequestInit);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "upstream request failed";
|
||||
recordCall(deps, {
|
||||
method: request.method,
|
||||
path,
|
||||
status: HTTP_STATUS.BAD_GATEWAY,
|
||||
startedAt,
|
||||
principal,
|
||||
connectionId: resolved.connectionId,
|
||||
error: message,
|
||||
});
|
||||
return connectError(HTTP_STATUS.BAD_GATEWAY, "unavailable", message);
|
||||
}
|
||||
|
||||
if (upstream.status === HTTP_STATUS.UNAUTHORIZED && resolved.apiKey) {
|
||||
deps.invalidateBearer(resolved.apiKey);
|
||||
}
|
||||
|
||||
const logInput: CallLogInput = {
|
||||
method: request.method,
|
||||
path,
|
||||
status: upstream.status,
|
||||
startedAt,
|
||||
principal,
|
||||
connectionId: resolved.connectionId,
|
||||
};
|
||||
const headers = buildDownstreamHeaders(upstream);
|
||||
if (!upstream.body) {
|
||||
recordCall(deps, logInput);
|
||||
return new Response(null, { status: upstream.status, headers });
|
||||
}
|
||||
const body = streamWithCompletionLog(upstream.body, (error) =>
|
||||
recordCall(deps, { ...logInput, error: error ?? null })
|
||||
);
|
||||
return new Response(body, { status: upstream.status, headers });
|
||||
}
|
||||
|
||||
export async function handleCursorCliProxy(
|
||||
request: Request,
|
||||
segments: readonly string[],
|
||||
overrides: Partial<CursorCliProxyDeps> = {}
|
||||
): Promise<Response> {
|
||||
const deps: CursorCliProxyDeps = { ...defaultDeps, ...overrides };
|
||||
const startedAt = deps.now();
|
||||
const path = normalizeCursorCliPath(segments);
|
||||
if (path === CURSOR_API_KEY_EXCHANGE_PATH) {
|
||||
return handleExchange(request, startedAt, deps);
|
||||
}
|
||||
return handleForward(request, path, startedAt, deps);
|
||||
}
|
||||
@@ -284,12 +284,30 @@ export async function handleMcpStreamableHTTP(request: Request): Promise<Respons
|
||||
return protectMcpSseResponse(request, await handleStreamableRequest(request));
|
||||
}
|
||||
|
||||
interface RpcRequest {
|
||||
method?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle SSE requests.
|
||||
* SSE transport is implemented via Streamable HTTP transport with GET for SSE stream
|
||||
* and POST for messages (the Streamable HTTP transport supports both patterns).
|
||||
*/
|
||||
export async function handleMcpSSE(request: Request): Promise<Response> {
|
||||
if (request.method === "POST") {
|
||||
try {
|
||||
const body = await request.clone().json();
|
||||
const isInitialize = Array.isArray(body)
|
||||
? body.some((req: RpcRequest) => req?.method === "initialize")
|
||||
: (body as RpcRequest)?.method === "initialize";
|
||||
|
||||
if (isInitialize) {
|
||||
console.log("[MCP] New client initialize detected, resetting SSE singleton...");
|
||||
closeSseTransport();
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
const { transport } = ensureSseServer();
|
||||
|
||||
try {
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import { registerQuotaFetcher, registerQuotaWindows, type QuotaInfo } from "./quotaPreflight.ts";
|
||||
import { registerMonitorFetcher } from "./quotaMonitor.ts";
|
||||
import { throttleQuotaFetch } from "./quotaFetchThrottle.ts";
|
||||
import { getCodexBackendIdentityHeaders } from "../config/codexClient.ts";
|
||||
|
||||
/**
|
||||
* Stable identifiers for Codex's quota windows. These match the quota keys
|
||||
@@ -222,6 +223,9 @@ export async function fetchCodexQuota(
|
||||
Authorization: `Bearer ${meta.accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
// Canonical Codex backend identity (UA + originator + version), same
|
||||
// chain as inference — see getCodexUsage.
|
||||
...getCodexBackendIdentityHeaders(),
|
||||
};
|
||||
|
||||
if (meta.workspaceId) {
|
||||
|
||||