feat(providers): Cursor API provider (crsr_ keys) and Cursor CLI passthrough (#10729)
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
@@ -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,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 |
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>. |
|
||||
|
||||
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)
|
||||
|
||||
@@ -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";
|
||||
@@ -327,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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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(),
|
||||
|
||||
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);
|
||||
}
|
||||
198
open-sse/services/cursorApiKeyAuth.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Cursor user API keys (`crsr_…`, minted at cursor.com/dashboard/api) are not
|
||||
* accepted as a Bearer credential by api2.cursor.sh (401). cursor-agent first
|
||||
* POSTs the key to `/auth/exchange_user_api_key` and receives a 1-hour session
|
||||
* JWT (`type: "api_key_token"`); the accompanying refreshToken carries the same
|
||||
* `exp`, so "refresh" simply means re-exchanging the key. This module owns that
|
||||
* exchange plus a per-key cache so the executor and the Cursor CLI passthrough
|
||||
* share one live session token per key.
|
||||
*/
|
||||
|
||||
import crypto from "node:crypto";
|
||||
|
||||
export const CURSOR_API_BASE_URL = "https://api2.cursor.sh";
|
||||
export const CURSOR_API_KEY_PREFIX = "crsr_";
|
||||
export const CURSOR_API_KEY_EXCHANGE_PATH = "/auth/exchange_user_api_key";
|
||||
export const CURSOR_API_KEY_EXCHANGE_URL = `${CURSOR_API_BASE_URL}${CURSOR_API_KEY_EXCHANGE_PATH}`;
|
||||
|
||||
const REFRESH_SKEW_MS = 5 * 60 * 1000;
|
||||
const FALLBACK_TTL_MS = 55 * 60 * 1000;
|
||||
const EXCHANGE_TIMEOUT_MS = 15_000;
|
||||
|
||||
export type CursorSessionToken = {
|
||||
accessToken: string;
|
||||
refreshToken: string | null;
|
||||
expiresAt: number;
|
||||
};
|
||||
|
||||
export class CursorApiKeyExchangeError extends Error {
|
||||
readonly status: number;
|
||||
|
||||
constructor(message: string, status: number) {
|
||||
super(message);
|
||||
this.name = "CursorApiKeyExchangeError";
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
||||
|
||||
export type CursorApiKeyAuthOptions = {
|
||||
fetchImpl?: FetchLike;
|
||||
signal?: AbortSignal;
|
||||
now?: () => number;
|
||||
};
|
||||
|
||||
const sessionCache = new Map<string, CursorSessionToken>();
|
||||
const inflightExchanges = new Map<string, Promise<CursorSessionToken>>();
|
||||
|
||||
export function isCursorApiKey(value: unknown): value is string {
|
||||
return typeof value === "string" && value.startsWith(CURSOR_API_KEY_PREFIX);
|
||||
}
|
||||
|
||||
function cacheKeyFor(apiKey: string): string {
|
||||
return crypto.createHash("sha256").update(apiKey).digest("hex");
|
||||
}
|
||||
|
||||
export function readJwtExpiryMs(token: string): number | null {
|
||||
const parts = token.split(".");
|
||||
if (parts.length !== 3) return null;
|
||||
try {
|
||||
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8")) as {
|
||||
exp?: unknown;
|
||||
};
|
||||
return typeof payload.exp === "number" && Number.isFinite(payload.exp)
|
||||
? payload.exp * 1000
|
||||
: null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function parseExchangeBody(raw: string): { accessToken: string; refreshToken: string | null } {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
} catch {
|
||||
throw new CursorApiKeyExchangeError("Cursor API key exchange returned a non-JSON body", 502);
|
||||
}
|
||||
if (!parsed || typeof parsed !== "object") {
|
||||
throw new CursorApiKeyExchangeError("Cursor API key exchange returned an empty body", 502);
|
||||
}
|
||||
const { accessToken, refreshToken } = parsed as { accessToken?: unknown; refreshToken?: unknown };
|
||||
if (typeof accessToken !== "string" || accessToken.length === 0) {
|
||||
throw new CursorApiKeyExchangeError("Cursor API key exchange returned no accessToken", 502);
|
||||
}
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken: typeof refreshToken === "string" && refreshToken.length > 0 ? refreshToken : null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function exchangeCursorApiKey(
|
||||
apiKey: string,
|
||||
options: CursorApiKeyAuthOptions = {}
|
||||
): Promise<CursorSessionToken> {
|
||||
if (!isCursorApiKey(apiKey)) {
|
||||
throw new CursorApiKeyExchangeError(
|
||||
`Cursor API keys start with "${CURSOR_API_KEY_PREFIX}"`,
|
||||
400
|
||||
);
|
||||
}
|
||||
const fetchImpl = options.fetchImpl ?? fetch;
|
||||
const now = options.now ?? Date.now;
|
||||
const signal = options.signal ?? AbortSignal.timeout(EXCHANGE_TIMEOUT_MS);
|
||||
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetchImpl(CURSOR_API_KEY_EXCHANGE_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
authorization: `Bearer ${apiKey}`,
|
||||
"content-type": "application/json",
|
||||
accept: "application/json",
|
||||
},
|
||||
body: "{}",
|
||||
signal,
|
||||
});
|
||||
} catch {
|
||||
throw new CursorApiKeyExchangeError("Cursor API key exchange request failed", 502);
|
||||
}
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
throw new CursorApiKeyExchangeError("Cursor rejected the API key", 401);
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new CursorApiKeyExchangeError(
|
||||
`Cursor API key exchange failed with HTTP ${response.status}`,
|
||||
response.status >= 500 ? 502 : response.status
|
||||
);
|
||||
}
|
||||
|
||||
const { accessToken, refreshToken } = parseExchangeBody(await response.text());
|
||||
const expiresAt = readJwtExpiryMs(accessToken) ?? now() + FALLBACK_TTL_MS;
|
||||
return { accessToken, refreshToken, expiresAt };
|
||||
}
|
||||
|
||||
function isFresh(token: CursorSessionToken, nowMs: number): boolean {
|
||||
return token.expiresAt - REFRESH_SKEW_MS > nowMs;
|
||||
}
|
||||
|
||||
export async function resolveCursorSessionToken(
|
||||
apiKey: string,
|
||||
options: CursorApiKeyAuthOptions = {}
|
||||
): Promise<CursorSessionToken> {
|
||||
const now = options.now ?? Date.now;
|
||||
const key = cacheKeyFor(apiKey);
|
||||
const cached = sessionCache.get(key);
|
||||
if (cached && isFresh(cached, now())) return cached;
|
||||
|
||||
const pending = inflightExchanges.get(key);
|
||||
if (pending) return pending;
|
||||
|
||||
const exchange = exchangeCursorApiKey(apiKey, options)
|
||||
.then((token) => {
|
||||
sessionCache.set(key, token);
|
||||
return token;
|
||||
})
|
||||
.finally(() => {
|
||||
inflightExchanges.delete(key);
|
||||
});
|
||||
inflightExchanges.set(key, exchange);
|
||||
return exchange;
|
||||
}
|
||||
|
||||
export function invalidateCursorSessionToken(apiKey: string): void {
|
||||
sessionCache.delete(cacheKeyFor(apiKey));
|
||||
}
|
||||
|
||||
export function stripCursorOAuthTokenPrefix(accessToken: string): string {
|
||||
return accessToken.includes("::") ? accessToken.split("::")[1] : accessToken;
|
||||
}
|
||||
|
||||
export type CursorBearerCredentials = {
|
||||
apiKey?: string | null;
|
||||
accessToken?: string | null;
|
||||
};
|
||||
|
||||
export async function resolveCursorBearerToken(
|
||||
credentials: CursorBearerCredentials,
|
||||
options: CursorApiKeyAuthOptions = {}
|
||||
): Promise<string> {
|
||||
if (isCursorApiKey(credentials.apiKey)) {
|
||||
const session = await resolveCursorSessionToken(credentials.apiKey, options);
|
||||
return session.accessToken;
|
||||
}
|
||||
if (typeof credentials.accessToken === "string" && credentials.accessToken.length > 0) {
|
||||
return stripCursorOAuthTokenPrefix(credentials.accessToken);
|
||||
}
|
||||
throw new CursorApiKeyExchangeError(
|
||||
"Cursor connection has neither an API key nor a session token",
|
||||
401
|
||||
);
|
||||
}
|
||||
|
||||
export function __resetCursorApiKeyAuthForTest(): void {
|
||||
sessionCache.clear();
|
||||
inflightExchanges.clear();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "3.8.50",
|
||||
"description": "Unified AI router with 341 providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.",
|
||||
"description": "Unified AI router with 342 providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"omniroute": "bin/omniroute.mjs",
|
||||
|
||||
14
src/app/api/cursor-cli/[...path]/route.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { handleCursorCliProxy } from "@omniroute/open-sse/handlers/cursorCliProxy.ts";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
type RouteContext = { params: Promise<{ path: string[] }> };
|
||||
|
||||
async function proxy(request: Request, context: RouteContext): Promise<Response> {
|
||||
const { path } = await context.params;
|
||||
return handleCursorCliProxy(request, path ?? []);
|
||||
}
|
||||
|
||||
export const GET = proxy;
|
||||
export const POST = proxy;
|
||||
@@ -6220,6 +6220,7 @@
|
||||
"claude": "Connect Claude Code with the existing OAuth flow.",
|
||||
"cline": "Connect Cline with the existing OAuth flow.",
|
||||
"cursor": "Connect Cursor IDE with the existing OAuth flow.",
|
||||
"cursor-api": "Connect Cursor with a user API key (crsr_...) from cursor.com/dashboard/api; no IDE session needed.",
|
||||
"github": "Connect GitHub Copilot with the existing OAuth flow.",
|
||||
"gitlab-duo": "OAuth application with ai_features + read_user scopes. Configure GITLAB_DUO_OAUTH_CLIENT_ID and optionally GITLAB_DUO_OAUTH_CLIENT_SECRET on this OmniRoute instance.",
|
||||
"kilocode": "Connect Kilo Code with the existing OAuth flow.",
|
||||
|
||||
@@ -112,6 +112,7 @@ import { validateAdobeFireflyProvider } from "./validation/adobeFirefly";
|
||||
import {
|
||||
validateV0VercelProvider,
|
||||
validateAuggieProvider,
|
||||
validateCursorApiProvider,
|
||||
validateQoderProvider,
|
||||
validateKiroProvider,
|
||||
validateGitlabProvider,
|
||||
@@ -185,6 +186,7 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
|
||||
// for parity with the "jules" cloud-agent entry above — see #6142.
|
||||
devin: validateDevinCloudAgentProvider,
|
||||
auggie: validateAuggieProvider,
|
||||
"cursor-api": validateCursorApiProvider,
|
||||
aihorde: validateAiHordeProvider,
|
||||
// #10522: registered under both the canonical id and the short alias — Firefly
|
||||
// connections are commonly stored as "firefly" (same prefix as firefly/<model>
|
||||
|
||||
@@ -61,6 +61,28 @@ export async function validateAuggieProvider() {
|
||||
return { valid: true, error: null, unsupported: false, method: result.version };
|
||||
}
|
||||
|
||||
export async function validateCursorApiProvider({ apiKey }: { apiKey?: string }) {
|
||||
const { exchangeCursorApiKey, CursorApiKeyExchangeError, isCursorApiKey } =
|
||||
await import("@omniroute/open-sse/services/cursorApiKeyAuth.ts");
|
||||
const key = (apiKey || "").trim();
|
||||
if (!isCursorApiKey(key)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: "Cursor user API keys start with crsr_ (cursor.com/dashboard/api)",
|
||||
unsupported: false,
|
||||
statusCode: 400,
|
||||
};
|
||||
}
|
||||
try {
|
||||
await exchangeCursorApiKey(key);
|
||||
return { valid: true, error: null, unsupported: false, method: "exchange_user_api_key" };
|
||||
} catch (error) {
|
||||
const statusCode = error instanceof CursorApiKeyExchangeError ? error.status : 502;
|
||||
const message = error instanceof Error ? error.message : "Cursor API key exchange failed";
|
||||
return { valid: false, error: message, unsupported: false, statusCode };
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateQoderProvider({ apiKey, providerSpecificData }: any) {
|
||||
// Bifurcate validation: PAT tokens use Cosy auth against api1.qoder.sh;
|
||||
// regular API keys validate against dashscope (OpenAI-compatible endpoint).
|
||||
|
||||
@@ -240,6 +240,7 @@ const KNOWN_SVGS = new Set([
|
||||
]);
|
||||
|
||||
const LOCAL_SVG_ALIASES: Record<string, string> = {
|
||||
"cursor-api": "cursor",
|
||||
"qwen-cloud": "qwencloud",
|
||||
"qwen-cloud-token-plan": "qwencloud",
|
||||
};
|
||||
|
||||
@@ -146,8 +146,7 @@ export const AGGREGATOR_PROVIDER_IDS = new Set([
|
||||
"free-ai",
|
||||
"void-ai",
|
||||
"helixmind",
|
||||
|
||||
]);;
|
||||
]);
|
||||
|
||||
export const ENTERPRISE_CLOUD_PROVIDER_IDS = new Set([
|
||||
"azure-openai",
|
||||
|
||||
@@ -305,4 +305,19 @@ export const APIKEY_PROVIDERS_SPECIALTY = {
|
||||
apiHint:
|
||||
"DeepAI uses per-endpoint REST calls (e.g. /api/text2img) instead of OpenAI chat/completions. OmniRoute adapts OpenAI image generation requests to DeepAI's /api/{slug} endpoints.",
|
||||
},
|
||||
"cursor-api": {
|
||||
id: "cursor-api",
|
||||
alias: "cua",
|
||||
name: "Cursor API",
|
||||
icon: "edit_note",
|
||||
color: "#00D4AA",
|
||||
textIcon: "CA",
|
||||
website: "https://cursor.com/dashboard/api",
|
||||
subscriptionRisk: true,
|
||||
riskNoticeVariant: "oauth",
|
||||
authHint:
|
||||
"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.",
|
||||
apiHint:
|
||||
"Same agent protocol and model catalog as the Cursor IDE provider. The Cursor CLI can also be pointed at /api/cursor-cli on this instance and authenticated with an OmniRoute API key.",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -30,6 +30,12 @@ const PUBLIC_API_ROUTE_PREFIXES = [
|
||||
// auth (503 when TELEGRAM_BOT_TOKEN is unset; 401 on invalid initData
|
||||
// HMAC). See src/app/api/telegram/update/route.ts. Do not widen.
|
||||
"/api/telegram/",
|
||||
// Cursor CLI passthrough (CURSOR_API_ENDPOINT -> OmniRoute -> api2.cursor.sh).
|
||||
// The handler enforces its own auth: /auth/exchange_user_api_key requires an
|
||||
// OmniRoute API key (validateApiKey); every other path requires the
|
||||
// OmniRoute-minted session JWT that exchange returns. See
|
||||
// open-sse/handlers/cursorCliProxy.ts. Do not widen.
|
||||
"/api/cursor-cli/",
|
||||
];
|
||||
|
||||
const PUBLIC_READONLY_API_ROUTE_PREFIXES = [
|
||||
|
||||
@@ -190,11 +190,21 @@
|
||||
"configSource": "cursor",
|
||||
"provider": "cursor"
|
||||
},
|
||||
"cua": {
|
||||
"className": "CursorExecutor",
|
||||
"configSource": "cursor-api",
|
||||
"provider": "cursor-api"
|
||||
},
|
||||
"cursor": {
|
||||
"className": "CursorExecutor",
|
||||
"configSource": "cursor",
|
||||
"provider": "cursor"
|
||||
},
|
||||
"cursor-api": {
|
||||
"className": "CursorExecutor",
|
||||
"configSource": "cursor-api",
|
||||
"provider": "cursor-api"
|
||||
},
|
||||
"cw-web": {
|
||||
"className": "ClaudeWebExecutor",
|
||||
"configSource": "<custom-config>",
|
||||
@@ -686,6 +696,6 @@
|
||||
"provider": "zai-web"
|
||||
}
|
||||
},
|
||||
"keyCount": 137,
|
||||
"keyCount": 139,
|
||||
"sharedInstances": []
|
||||
}
|
||||
|
||||
227
tests/unit/cursor-api-key-auth.test.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* Cursor API-key exchange (open-sse/services/cursorApiKeyAuth.ts).
|
||||
*
|
||||
* api2.cursor.sh rejects a raw `crsr_…` key as Bearer; cursor-agent exchanges
|
||||
* it at /auth/exchange_user_api_key for a 1h session JWT. These tests pin the
|
||||
* exchange request shape, the 401/5xx/malformed-body mapping, the per-key
|
||||
* cache (refresh 5 min before exp, in-flight dedupe, invalidation) and the
|
||||
* shared bearer resolver used by the executor and the CLI passthrough.
|
||||
*/
|
||||
import { describe, it, beforeEach } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
CURSOR_API_KEY_EXCHANGE_URL,
|
||||
CursorApiKeyExchangeError,
|
||||
__resetCursorApiKeyAuthForTest,
|
||||
exchangeCursorApiKey,
|
||||
invalidateCursorSessionToken,
|
||||
isCursorApiKey,
|
||||
readJwtExpiryMs,
|
||||
resolveCursorBearerToken,
|
||||
resolveCursorSessionToken,
|
||||
stripCursorOAuthTokenPrefix,
|
||||
} from "@omniroute/open-sse/services/cursorApiKeyAuth.ts";
|
||||
|
||||
const API_KEY = "crsr_test_key_0123456789";
|
||||
|
||||
function jwtWithExp(expSeconds: number): string {
|
||||
const b64 = (value: object) => Buffer.from(JSON.stringify(value)).toString("base64url");
|
||||
return `${b64({ alg: "HS256", typ: "JWT" })}.${b64({ exp: expSeconds, type: "api_key_token" })}.sig`;
|
||||
}
|
||||
|
||||
type RecordedCall = { url: string; init: RequestInit | undefined };
|
||||
|
||||
function fakeFetch(
|
||||
responder: (call: RecordedCall) => Response,
|
||||
calls: RecordedCall[] = []
|
||||
): { fetchImpl: (url: string, init?: RequestInit) => Promise<Response>; calls: RecordedCall[] } {
|
||||
return {
|
||||
calls,
|
||||
fetchImpl: async (url, init) => {
|
||||
const call = { url, init };
|
||||
calls.push(call);
|
||||
return responder(call);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function okExchange(expSeconds: number): Response {
|
||||
return new Response(
|
||||
JSON.stringify({ accessToken: jwtWithExp(expSeconds), refreshToken: jwtWithExp(expSeconds) }),
|
||||
{ status: 200, headers: { "content-type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
describe("cursorApiKeyAuth", () => {
|
||||
beforeEach(() => {
|
||||
__resetCursorApiKeyAuthForTest();
|
||||
});
|
||||
|
||||
it("recognises crsr_ keys only", () => {
|
||||
assert.equal(isCursorApiKey(API_KEY), true);
|
||||
assert.equal(isCursorApiKey("sk-other"), false);
|
||||
assert.equal(isCursorApiKey(undefined), false);
|
||||
});
|
||||
|
||||
it("reads exp from a JWT and returns null for opaque tokens", () => {
|
||||
assert.equal(readJwtExpiryMs(jwtWithExp(1_800_000_000)), 1_800_000_000_000);
|
||||
assert.equal(readJwtExpiryMs("opaque"), null);
|
||||
});
|
||||
|
||||
it("strips the WorkOS composite prefix from OAuth session tokens only", () => {
|
||||
assert.equal(stripCursorOAuthTokenPrefix("user_123::jwt.part.sig"), "jwt.part.sig");
|
||||
assert.equal(stripCursorOAuthTokenPrefix("jwt.part.sig"), "jwt.part.sig");
|
||||
});
|
||||
|
||||
it("POSTs the key as Bearer to the exchange endpoint and parses the session", async () => {
|
||||
const exp = Math.floor(Date.now() / 1000) + 3600;
|
||||
const { fetchImpl, calls } = fakeFetch(() => okExchange(exp));
|
||||
|
||||
const session = await exchangeCursorApiKey(API_KEY, { fetchImpl });
|
||||
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(calls[0].url, CURSOR_API_KEY_EXCHANGE_URL);
|
||||
assert.equal(calls[0].init?.method, "POST");
|
||||
const headers = calls[0].init?.headers as Record<string, string>;
|
||||
assert.equal(headers.authorization, `Bearer ${API_KEY}`);
|
||||
assert.equal(headers["content-type"], "application/json");
|
||||
assert.equal(calls[0].init?.body, "{}");
|
||||
assert.equal(session.accessToken, jwtWithExp(exp));
|
||||
assert.equal(session.expiresAt, exp * 1000);
|
||||
});
|
||||
|
||||
it("rejects non-crsr keys without calling upstream", async () => {
|
||||
const { fetchImpl, calls } = fakeFetch(() => okExchange(1));
|
||||
await assert.rejects(
|
||||
exchangeCursorApiKey("sk-not-cursor", { fetchImpl }),
|
||||
(err: unknown) => err instanceof CursorApiKeyExchangeError && err.status === 400
|
||||
);
|
||||
assert.equal(calls.length, 0);
|
||||
});
|
||||
|
||||
it("maps upstream 401/403 to a 401 exchange error", async () => {
|
||||
for (const status of [401, 403]) {
|
||||
const { fetchImpl } = fakeFetch(() => new Response("nope", { status }));
|
||||
await assert.rejects(
|
||||
exchangeCursorApiKey(API_KEY, { fetchImpl }),
|
||||
(err: unknown) =>
|
||||
err instanceof CursorApiKeyExchangeError &&
|
||||
err.status === 401 &&
|
||||
!err.message.includes(API_KEY)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("maps upstream 5xx and malformed bodies to 502", async () => {
|
||||
const cases: Array<() => Response> = [
|
||||
() => new Response("boom", { status: 503 }),
|
||||
() => new Response("not json", { status: 200 }),
|
||||
() => new Response(JSON.stringify({ refreshToken: "x" }), { status: 200 }),
|
||||
];
|
||||
for (const responder of cases) {
|
||||
const { fetchImpl } = fakeFetch(responder);
|
||||
await assert.rejects(
|
||||
exchangeCursorApiKey(API_KEY, { fetchImpl }),
|
||||
(err: unknown) => err instanceof CursorApiKeyExchangeError && err.status === 502
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("maps network failures to 502", async () => {
|
||||
const fetchImpl = async () => {
|
||||
throw new Error("ECONNRESET");
|
||||
};
|
||||
await assert.rejects(
|
||||
exchangeCursorApiKey(API_KEY, { fetchImpl }),
|
||||
(err: unknown) => err instanceof CursorApiKeyExchangeError && err.status === 502
|
||||
);
|
||||
});
|
||||
|
||||
it("caches the session per key and re-exchanges 5 minutes before expiry", async () => {
|
||||
let nowMs = 1_000_000_000_000;
|
||||
const now = () => nowMs;
|
||||
const { fetchImpl, calls } = fakeFetch(() => okExchange(Math.floor(now() / 1000) + 3600));
|
||||
|
||||
const first = await resolveCursorSessionToken(API_KEY, { fetchImpl, now });
|
||||
const second = await resolveCursorSessionToken(API_KEY, { fetchImpl, now });
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(second.accessToken, first.accessToken);
|
||||
|
||||
nowMs += 54 * 60 * 1000;
|
||||
await resolveCursorSessionToken(API_KEY, { fetchImpl, now });
|
||||
assert.equal(calls.length, 1, "still fresh at 54 min");
|
||||
|
||||
nowMs += 2 * 60 * 1000;
|
||||
const refreshed = await resolveCursorSessionToken(API_KEY, { fetchImpl, now });
|
||||
assert.equal(calls.length, 2, "re-exchanged inside the 5 min skew window");
|
||||
assert.notEqual(refreshed.accessToken, first.accessToken);
|
||||
});
|
||||
|
||||
it("dedupes concurrent exchanges for the same key", async () => {
|
||||
let release: (() => void) | null = null;
|
||||
const gate = new Promise<void>((resolve) => {
|
||||
release = resolve;
|
||||
});
|
||||
const { fetchImpl, calls } = fakeFetch(() => okExchange(Math.floor(Date.now() / 1000) + 3600));
|
||||
const gatedFetch = async (url: string, init?: RequestInit) => {
|
||||
await gate;
|
||||
return fetchImpl(url, init);
|
||||
};
|
||||
|
||||
const pending = Promise.all([
|
||||
resolveCursorSessionToken(API_KEY, { fetchImpl: gatedFetch }),
|
||||
resolveCursorSessionToken(API_KEY, { fetchImpl: gatedFetch }),
|
||||
resolveCursorSessionToken(API_KEY, { fetchImpl: gatedFetch }),
|
||||
]);
|
||||
release?.();
|
||||
const tokens = await pending;
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(new Set(tokens.map((t) => t.accessToken)).size, 1);
|
||||
});
|
||||
|
||||
it("invalidation forces a fresh exchange on the next call", async () => {
|
||||
const { fetchImpl, calls } = fakeFetch(() => okExchange(Math.floor(Date.now() / 1000) + 3600));
|
||||
await resolveCursorSessionToken(API_KEY, { fetchImpl });
|
||||
invalidateCursorSessionToken(API_KEY);
|
||||
await resolveCursorSessionToken(API_KEY, { fetchImpl });
|
||||
assert.equal(calls.length, 2);
|
||||
});
|
||||
|
||||
it("does not cache a failed exchange", async () => {
|
||||
let attempt = 0;
|
||||
const { fetchImpl } = fakeFetch(() => {
|
||||
attempt += 1;
|
||||
return attempt === 1
|
||||
? new Response("down", { status: 503 })
|
||||
: okExchange(Math.floor(Date.now() / 1000) + 3600);
|
||||
});
|
||||
await assert.rejects(resolveCursorSessionToken(API_KEY, { fetchImpl }));
|
||||
const session = await resolveCursorSessionToken(API_KEY, { fetchImpl });
|
||||
assert.ok(session.accessToken.length > 0);
|
||||
assert.equal(attempt, 2);
|
||||
});
|
||||
|
||||
it("resolveCursorBearerToken prefers the exchanged token for API-key connections", async () => {
|
||||
const exp = Math.floor(Date.now() / 1000) + 3600;
|
||||
const { fetchImpl } = fakeFetch(() => okExchange(exp));
|
||||
const bearer = await resolveCursorBearerToken(
|
||||
{ apiKey: API_KEY, accessToken: "user_1::stale" },
|
||||
{ fetchImpl }
|
||||
);
|
||||
assert.equal(bearer, jwtWithExp(exp));
|
||||
});
|
||||
|
||||
it("resolveCursorBearerToken keeps the OAuth path untouched and rejects empty creds", async () => {
|
||||
const { fetchImpl, calls } = fakeFetch(() => okExchange(1));
|
||||
assert.equal(
|
||||
await resolveCursorBearerToken({ accessToken: "user_1::session.jwt.sig" }, { fetchImpl }),
|
||||
"session.jwt.sig"
|
||||
);
|
||||
assert.equal(calls.length, 0);
|
||||
await assert.rejects(
|
||||
resolveCursorBearerToken({}, { fetchImpl }),
|
||||
(err: unknown) => err instanceof CursorApiKeyExchangeError && err.status === 401
|
||||
);
|
||||
});
|
||||
});
|
||||
176
tests/unit/cursor-apikey-provider.test.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* `cursor-api` is the API-key sibling of the `cursor` (IDE session) provider:
|
||||
* same executor, format and model catalog, its own registry/catalog entry so
|
||||
* API-key and IDE-session connections never share renewal or dashboard
|
||||
* semantics. The executor swaps the crsr_ key for the exchanged session token
|
||||
* before dialing.
|
||||
*/
|
||||
import { describe, it, afterEach } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { APIKEY_PROVIDERS, OAUTH_PROVIDERS } =
|
||||
await import("../../src/shared/constants/providers.ts");
|
||||
const { isManagedProviderConnectionId } = await import("../../src/lib/providers/catalog.ts");
|
||||
const { cursorProvider, cursor_apiProvider } =
|
||||
await import("../../open-sse/config/providers/registry/cursor/index.ts");
|
||||
const { REGISTRY, generateAliasMap, getProviderCategory } =
|
||||
await import("../../open-sse/config/providerRegistry.ts");
|
||||
const { CursorExecutor, getExecutor, hasSpecializedExecutor } =
|
||||
await import("../../open-sse/executors/index.ts");
|
||||
const { __resetCursorApiKeyAuthForTest } =
|
||||
await import("../../open-sse/services/cursorApiKeyAuth.ts");
|
||||
const { validateProviderApiKey } = await import("../../src/lib/providers/validation.ts");
|
||||
|
||||
const API_KEY = "crsr_provider_test_key";
|
||||
|
||||
function jwt(exp: number): string {
|
||||
const b64 = (value: object) => Buffer.from(JSON.stringify(value)).toString("base64url");
|
||||
return `${b64({ alg: "HS256" })}.${b64({ exp })}.sig`;
|
||||
}
|
||||
|
||||
describe("cursor-api provider wiring", () => {
|
||||
it("is a distinct API-key registry entry sharing the cursor executor, format and models", () => {
|
||||
assert.equal(cursor_apiProvider.id, "cursor-api");
|
||||
assert.equal(cursor_apiProvider.authType, "apikey");
|
||||
assert.equal(cursor_apiProvider.format, cursorProvider.format);
|
||||
assert.equal(cursor_apiProvider.baseUrl, cursorProvider.baseUrl);
|
||||
assert.equal(cursor_apiProvider.models, cursorProvider.models);
|
||||
assert.equal(REGISTRY["cursor-api"], cursor_apiProvider);
|
||||
assert.equal(generateAliasMap()["cursor-api"], "cua");
|
||||
assert.equal(getProviderCategory("cursor-api"), "apikey");
|
||||
});
|
||||
|
||||
it("leaves the IDE cursor provider OAuth-only", () => {
|
||||
assert.equal(cursorProvider.authType, "oauth");
|
||||
assert.equal(getProviderCategory("cursor"), "oauth");
|
||||
assert.ok(OAUTH_PROVIDERS.cursor);
|
||||
assert.ok(!APIKEY_PROVIDERS.cursor);
|
||||
});
|
||||
|
||||
it("has its own API-key catalog card admitted by the managed-connection gate", () => {
|
||||
assert.ok(APIKEY_PROVIDERS["cursor-api"]);
|
||||
assert.equal(APIKEY_PROVIDERS["cursor-api"].alias, "cua");
|
||||
assert.ok(!OAUTH_PROVIDERS["cursor-api"]);
|
||||
assert.equal(isManagedProviderConnectionId("cursor-api"), true);
|
||||
});
|
||||
|
||||
it("routes cursor-api and its alias to a CursorExecutor bound to the cursor-api id", () => {
|
||||
for (const key of ["cursor-api", "cua"]) {
|
||||
assert.equal(hasSpecializedExecutor(key), true, key);
|
||||
const executor = getExecutor(key);
|
||||
assert.ok(executor instanceof CursorExecutor, key);
|
||||
assert.equal(executor.getProvider(), "cursor-api");
|
||||
}
|
||||
assert.equal(getExecutor("cursor").getProvider(), "cursor");
|
||||
});
|
||||
});
|
||||
|
||||
describe("CursorExecutor credential resolution", () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
__resetCursorApiKeyAuthForTest();
|
||||
});
|
||||
|
||||
it("sends the stripped IDE session token for OAuth connections", () => {
|
||||
const executor = new CursorExecutor();
|
||||
const headers = executor.buildHeaders({
|
||||
accessToken: "user_01::ide.session.jwt",
|
||||
providerSpecificData: {},
|
||||
});
|
||||
assert.equal(headers.authorization, "Bearer ide.session.jwt");
|
||||
assert.equal(headers["x-cursor-client-type"], "cli");
|
||||
});
|
||||
|
||||
it("exchanges a crsr_ key and sends the session JWT, never the raw key", async () => {
|
||||
const calls: string[] = [];
|
||||
const exp = Math.floor(Date.now() / 1000) + 3600;
|
||||
globalThis.fetch = (async (input: string | URL | Request) => {
|
||||
calls.push(String(input));
|
||||
return new Response(JSON.stringify({ accessToken: jwt(exp), refreshToken: jwt(exp) }), {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}) as typeof fetch;
|
||||
|
||||
const executor = new CursorExecutor("cursor-api");
|
||||
const resolved = await executor.resolveExecutionCredentials({
|
||||
apiKey: API_KEY,
|
||||
providerSpecificData: {},
|
||||
});
|
||||
assert.ok(!(resolved instanceof Response));
|
||||
const headers = executor.buildHeaders(resolved);
|
||||
assert.equal(headers.authorization, `Bearer ${jwt(exp)}`);
|
||||
assert.ok(!headers.authorization.includes(API_KEY));
|
||||
assert.equal(calls.length, 1);
|
||||
assert.match(calls[0], /\/auth\/exchange_user_api_key$/);
|
||||
});
|
||||
|
||||
it("returns a sanitized 401 response when Cursor rejects the key", async () => {
|
||||
globalThis.fetch = (async () => new Response("bad key", { status: 401 })) as typeof fetch;
|
||||
const executor = new CursorExecutor("cursor-api");
|
||||
const resolved = await executor.resolveExecutionCredentials({ apiKey: API_KEY });
|
||||
assert.ok(resolved instanceof Response);
|
||||
assert.equal(resolved.status, 401);
|
||||
const body = (await resolved.json()) as { error: { message: string; type: string } };
|
||||
assert.equal(body.error.type, "authentication_error");
|
||||
assert.ok(!body.error.message.includes(API_KEY));
|
||||
assert.ok(!body.error.message.includes("at /"));
|
||||
});
|
||||
|
||||
it("leaves OAuth credentials untouched without calling the exchange endpoint", async () => {
|
||||
globalThis.fetch = (async () => {
|
||||
throw new Error("exchange must not be called");
|
||||
}) as typeof fetch;
|
||||
const executor = new CursorExecutor();
|
||||
const credentials = { accessToken: "user_01::ide.session.jwt" };
|
||||
const resolved = await executor.resolveExecutionCredentials(credentials);
|
||||
assert.equal(resolved, credentials);
|
||||
});
|
||||
});
|
||||
|
||||
describe("cursor-api connection test", () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
__resetCursorApiKeyAuthForTest();
|
||||
});
|
||||
|
||||
it("validates by exchanging the key and reports the exchange as the method", async () => {
|
||||
let exchangeCalls = 0;
|
||||
globalThis.fetch = (async () => {
|
||||
exchangeCalls += 1;
|
||||
return new Response(JSON.stringify({ accessToken: jwt(1_900_000_000), refreshToken: "r" }), {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}) as typeof fetch;
|
||||
const result = await validateProviderApiKey({ provider: "cursor-api", apiKey: API_KEY });
|
||||
assert.equal(result.valid, true);
|
||||
assert.equal(result.unsupported, false);
|
||||
assert.equal(exchangeCalls, 1);
|
||||
});
|
||||
|
||||
it("reports a rejected key as invalid (401), not as unsupported", async () => {
|
||||
globalThis.fetch = (async () => new Response("nope", { status: 401 })) as typeof fetch;
|
||||
const result = await validateProviderApiKey({ provider: "cursor-api", apiKey: API_KEY });
|
||||
assert.equal(result.valid, false);
|
||||
assert.equal(result.unsupported, false);
|
||||
assert.equal(result.statusCode, 401);
|
||||
assert.ok(!String(result.error).includes(API_KEY));
|
||||
});
|
||||
|
||||
it("rejects keys without the crsr_ prefix locally", async () => {
|
||||
globalThis.fetch = (async () => {
|
||||
throw new Error("must not be called");
|
||||
}) as typeof fetch;
|
||||
const result = await validateProviderApiKey({
|
||||
provider: "cursor-api",
|
||||
apiKey: "sk-not-cursor",
|
||||
});
|
||||
assert.equal(result.valid, false);
|
||||
assert.equal(result.statusCode, 400);
|
||||
});
|
||||
});
|
||||
417
tests/unit/cursor-cli-proxy.test.ts
Normal file
@@ -0,0 +1,417 @@
|
||||
/**
|
||||
* Cursor CLI passthrough handler (open-sse/handlers/cursorCliProxy.ts).
|
||||
*
|
||||
* The handler is exercised through its dependency seam so no SQLite, no
|
||||
* network and no JWT_SECRET env are needed: every collaborator (API-key
|
||||
* validation, connection listing, bearer resolution, upstream fetch, call
|
||||
* logging) is injected per test.
|
||||
*/
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { SignJWT, decodeJwt } from "jose";
|
||||
|
||||
import {
|
||||
CURSOR_CLI_PROXY_PREFIX,
|
||||
CURSOR_CLI_REQUEST_TYPE,
|
||||
CURSOR_CLI_SESSION_AUDIENCE,
|
||||
CURSOR_CLI_SESSION_ISSUER,
|
||||
CURSOR_CLI_SESSION_TTL_SECONDS,
|
||||
handleCursorCliProxy,
|
||||
mintCursorCliSessionToken,
|
||||
normalizeCursorCliPath,
|
||||
type CursorCliProxyDeps,
|
||||
} from "@omniroute/open-sse/handlers/cursorCliProxy.ts";
|
||||
import { CursorApiKeyExchangeError } from "@omniroute/open-sse/services/cursorApiKeyAuth.ts";
|
||||
|
||||
const SECRET = "unit-test-jwt-secret-with-enough-entropy-0123456789";
|
||||
const OMNI_KEY = "sk-omniroute-unit-key";
|
||||
const CURSOR_KEY = "crsr_unit_cursor_key";
|
||||
const UPSTREAM = "https://upstream.example";
|
||||
const NOW = 1_800_000_000_000;
|
||||
|
||||
type UpstreamCall = { url: string; init: RequestInit };
|
||||
|
||||
function makeDeps(overrides: Partial<CursorCliProxyDeps> = {}): {
|
||||
deps: Partial<CursorCliProxyDeps>;
|
||||
upstreamCalls: UpstreamCall[];
|
||||
logs: Record<string, unknown>[];
|
||||
} {
|
||||
const upstreamCalls: UpstreamCall[] = [];
|
||||
const logs: Record<string, unknown>[] = [];
|
||||
const deps: Partial<CursorCliProxyDeps> = {
|
||||
fetchImpl: (async (url: string, init: RequestInit) => {
|
||||
upstreamCalls.push({ url, init });
|
||||
return new Response("upstream-ok", {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/proto", "content-encoding": "identity" },
|
||||
});
|
||||
}) as unknown as typeof fetch,
|
||||
now: () => NOW,
|
||||
getSecret: () => SECRET,
|
||||
validateApiKey: async (key) => key === OMNI_KEY,
|
||||
getApiKeyMetadata: async (key) => (key === OMNI_KEY ? { id: "key-1", name: "unit key" } : null),
|
||||
getApiKeyById: async (id) => (id === "key-1" ? { isActive: true, revokedAt: null } : null),
|
||||
requireApiKey: () => true,
|
||||
listCursorConnections: async () => [{ id: "conn-1", apiKey: CURSOR_KEY, priority: 1 }],
|
||||
resolveBearer: async ({ apiKey }) =>
|
||||
apiKey === CURSOR_KEY ? "cursor-session-jwt" : "oauth-jwt",
|
||||
invalidateBearer: () => undefined,
|
||||
saveCallLog: async (entry) => {
|
||||
logs.push(entry);
|
||||
},
|
||||
upstreamBaseUrl: UPSTREAM,
|
||||
...overrides,
|
||||
};
|
||||
return { deps, upstreamCalls, logs };
|
||||
}
|
||||
|
||||
function exchangeRequest(bearer: string | null, body = "{}"): Request {
|
||||
return new Request("http://omniroute.local/api/cursor-cli/auth/exchange_user_api_key", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
...(bearer ? { authorization: `Bearer ${bearer}` } : {}),
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
function rpcRequest(
|
||||
bearer: string | null,
|
||||
path = "/aiserver.v1.DashboardService/GetMe",
|
||||
body: BodyInit | null = new Uint8Array([0, 0, 0, 0, 0])
|
||||
): Request {
|
||||
return new Request(`http://omniroute.local/api/cursor-cli${path}?x=1`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
...(bearer ? { authorization: `Bearer ${bearer}` } : {}),
|
||||
"content-type": "application/proto",
|
||||
"connect-protocol-version": "1",
|
||||
host: "omniroute.local",
|
||||
"accept-encoding": "gzip,br",
|
||||
cookie: "auth_token=dashboard",
|
||||
},
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
async function mintedToken(): Promise<string> {
|
||||
return mintCursorCliSessionToken({ apiKeyId: "key-1", apiKeyName: "unit key" }, SECRET, NOW);
|
||||
}
|
||||
|
||||
async function flushLogs(): Promise<void> {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
}
|
||||
|
||||
describe("cursorCliProxy: path normalisation", () => {
|
||||
it("joins catch-all segments into the upstream RPC path", () => {
|
||||
assert.equal(
|
||||
normalizeCursorCliPath(["aiserver.v1.DashboardService", "GetMe"]),
|
||||
"/aiserver.v1.DashboardService/GetMe"
|
||||
);
|
||||
assert.equal(
|
||||
normalizeCursorCliPath(["auth", "exchange_user_api_key"]),
|
||||
"/auth/exchange_user_api_key"
|
||||
);
|
||||
assert.equal(normalizeCursorCliPath(["v1", "traces"]), "/v1/traces");
|
||||
});
|
||||
|
||||
it("re-encodes segments so encoded slashes and spaces cannot smuggle extra path", () => {
|
||||
assert.equal(normalizeCursorCliPath(["%2Fetc", "x y"]), "/%2Fetc/x%20y");
|
||||
assert.equal(normalizeCursorCliPath(["a%2Fb"]), "/a%2Fb");
|
||||
});
|
||||
});
|
||||
|
||||
describe("cursorCliProxy: /auth/exchange_user_api_key", () => {
|
||||
it("mints a 1h OmniRoute session JWT for a valid OmniRoute API key", async () => {
|
||||
const { deps, logs } = makeDeps();
|
||||
const res = await handleCursorCliProxy(
|
||||
exchangeRequest(OMNI_KEY),
|
||||
["auth", "exchange_user_api_key"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 200);
|
||||
const body = (await res.json()) as { accessToken: string; refreshToken: string };
|
||||
assert.equal(body.refreshToken, body.accessToken);
|
||||
const claims = decodeJwt(body.accessToken);
|
||||
assert.equal(claims.iss, CURSOR_CLI_SESSION_ISSUER);
|
||||
assert.equal(claims.aud, CURSOR_CLI_SESSION_AUDIENCE);
|
||||
assert.equal(claims.sub, "key-1");
|
||||
assert.equal(claims.exp, Math.floor(NOW / 1000) + CURSOR_CLI_SESSION_TTL_SECONDS);
|
||||
assert.ok(!body.accessToken.includes(OMNI_KEY));
|
||||
await flushLogs();
|
||||
assert.equal(logs.length, 1);
|
||||
assert.equal(logs[0].path, `${CURSOR_CLI_PROXY_PREFIX}/auth/exchange_user_api_key`);
|
||||
assert.equal(logs[0].requestType, CURSOR_CLI_REQUEST_TYPE);
|
||||
assert.equal(logs[0].apiKeyId, "key-1");
|
||||
});
|
||||
|
||||
it("rejects an unknown key with 401 when OmniRoute requires API keys", async () => {
|
||||
const { deps } = makeDeps();
|
||||
const res = await handleCursorCliProxy(
|
||||
exchangeRequest("sk-wrong"),
|
||||
["auth", "exchange_user_api_key"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 401);
|
||||
const body = (await res.json()) as { code: string; message: string };
|
||||
assert.equal(body.code, "unauthenticated");
|
||||
assert.ok(!body.message.includes("at /"));
|
||||
});
|
||||
|
||||
it("falls back to an anonymous session when REQUIRE_API_KEY is off", async () => {
|
||||
const { deps } = makeDeps({ requireApiKey: () => false });
|
||||
const res = await handleCursorCliProxy(
|
||||
exchangeRequest("anything"),
|
||||
["auth", "exchange_user_api_key"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 200);
|
||||
const body = (await res.json()) as { accessToken: string };
|
||||
assert.equal(decodeJwt(body.accessToken).sub, "anonymous");
|
||||
});
|
||||
|
||||
it("returns 503 without minting when JWT_SECRET is missing", async () => {
|
||||
const { deps } = makeDeps({ getSecret: () => undefined });
|
||||
const res = await handleCursorCliProxy(
|
||||
exchangeRequest(OMNI_KEY),
|
||||
["auth", "exchange_user_api_key"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 503);
|
||||
});
|
||||
|
||||
it("rejects non-JSON and non-POST exchange calls", async () => {
|
||||
const { deps } = makeDeps();
|
||||
const bad = await handleCursorCliProxy(
|
||||
exchangeRequest(OMNI_KEY, "not-json"),
|
||||
["auth", "exchange_user_api_key"],
|
||||
deps
|
||||
);
|
||||
assert.equal(bad.status, 400);
|
||||
const get = await handleCursorCliProxy(
|
||||
new Request("http://omniroute.local/api/cursor-cli/auth/exchange_user_api_key", {
|
||||
method: "GET",
|
||||
}),
|
||||
["auth", "exchange_user_api_key"],
|
||||
deps
|
||||
);
|
||||
assert.equal(get.status, 405);
|
||||
});
|
||||
});
|
||||
|
||||
describe("cursorCliProxy: forwarded RPCs", () => {
|
||||
it("swaps the OmniRoute session token for the Cursor bearer and strips hop headers", async () => {
|
||||
const { deps, upstreamCalls, logs } = makeDeps();
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(await mintedToken()),
|
||||
["aiserver.v1.DashboardService", "GetMe"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 200);
|
||||
assert.equal(await res.text(), "upstream-ok");
|
||||
assert.equal(res.headers.get("content-type"), "application/proto");
|
||||
assert.equal(res.headers.get("content-encoding"), null);
|
||||
|
||||
assert.equal(upstreamCalls.length, 1);
|
||||
assert.equal(upstreamCalls[0].url, `${UPSTREAM}/aiserver.v1.DashboardService/GetMe?x=1`);
|
||||
const headers = upstreamCalls[0].init.headers as Headers;
|
||||
assert.equal(headers.get("authorization"), "Bearer cursor-session-jwt");
|
||||
assert.equal(headers.get("connect-protocol-version"), "1");
|
||||
assert.equal(headers.get("host"), null);
|
||||
assert.equal(headers.get("cookie"), null);
|
||||
assert.equal(headers.get("accept-encoding"), null);
|
||||
assert.equal(upstreamCalls[0].init.method, "POST");
|
||||
|
||||
await flushLogs();
|
||||
assert.equal(logs.length, 1);
|
||||
assert.equal(logs[0].path, `${CURSOR_CLI_PROXY_PREFIX}/aiserver.v1.DashboardService/GetMe`);
|
||||
assert.equal(logs[0].provider, "cursor-api");
|
||||
assert.equal(logs[0].connectionId, "conn-1");
|
||||
assert.equal(logs[0].status, 200);
|
||||
assert.equal(logs[0].apiKeyId, "key-1");
|
||||
});
|
||||
|
||||
it("rejects a raw OmniRoute API key on RPC paths so the CLI exchanges first", async () => {
|
||||
const { deps, upstreamCalls } = makeDeps();
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(OMNI_KEY),
|
||||
["aiserver.v1.DashboardService", "GetMe"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 401);
|
||||
assert.equal(upstreamCalls.length, 0);
|
||||
});
|
||||
|
||||
it("rejects missing, expired, foreign-audience and tampered tokens with 401", async () => {
|
||||
const { deps, upstreamCalls } = makeDeps();
|
||||
const expired = await mintCursorCliSessionToken(
|
||||
{ apiKeyId: "key-1", apiKeyName: null },
|
||||
SECRET,
|
||||
NOW - (CURSOR_CLI_SESSION_TTL_SECONDS + 60) * 1000
|
||||
);
|
||||
const foreign = await new SignJWT({})
|
||||
.setProtectedHeader({ alg: "HS256" })
|
||||
.setIssuer(CURSOR_CLI_SESSION_ISSUER)
|
||||
.setAudience("dashboard")
|
||||
.setSubject("key-1")
|
||||
.setExpirationTime(Math.floor(NOW / 1000) + 600)
|
||||
.sign(new TextEncoder().encode(SECRET));
|
||||
const tampered = (await mintedToken()).slice(0, -4) + "AAAA";
|
||||
for (const token of [null, expired, foreign, tampered]) {
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(token),
|
||||
["aiserver.v1.DashboardService", "GetMe"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 401, `token=${token === null ? "none" : token.slice(0, 12)}`);
|
||||
}
|
||||
assert.equal(upstreamCalls.length, 0);
|
||||
});
|
||||
|
||||
it("rejects a session whose OmniRoute API key was revoked or deactivated", async () => {
|
||||
const revoked = makeDeps({
|
||||
getApiKeyById: async () => ({ isActive: true, revokedAt: "2026-01-01T00:00:00Z" }),
|
||||
});
|
||||
const deactivated = makeDeps({
|
||||
getApiKeyById: async () => ({ isActive: false, revokedAt: null }),
|
||||
});
|
||||
const gone = makeDeps({ getApiKeyById: async () => null });
|
||||
for (const { deps } of [revoked, deactivated, gone]) {
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(await mintedToken()),
|
||||
["aiserver.v1.DashboardService", "GetMe"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 401);
|
||||
}
|
||||
});
|
||||
|
||||
it("returns 503 when no active Cursor connection exists", async () => {
|
||||
const { deps, logs } = makeDeps({ listCursorConnections: async () => [] });
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(await mintedToken()),
|
||||
["aiserver.v1.DashboardService", "GetMe"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 503);
|
||||
const body = (await res.json()) as { code: string; message: string };
|
||||
assert.equal(body.code, "unavailable");
|
||||
assert.ok(!body.message.includes("at /"));
|
||||
await flushLogs();
|
||||
assert.equal(logs[0].status, 503);
|
||||
});
|
||||
|
||||
it("skips cooling-down connections and falls through when a key cannot be exchanged", async () => {
|
||||
const { deps, upstreamCalls } = makeDeps({
|
||||
listCursorConnections: async () => [
|
||||
{
|
||||
id: "cooling",
|
||||
apiKey: "crsr_cooling",
|
||||
priority: 0,
|
||||
rateLimitedUntil: new Date(NOW + 60_000).toISOString(),
|
||||
},
|
||||
{ id: "broken", apiKey: "crsr_broken", priority: 1 },
|
||||
{ id: "oauth", accessToken: "user::session", priority: 2 },
|
||||
],
|
||||
resolveBearer: async ({ apiKey, accessToken }) => {
|
||||
if (apiKey === "crsr_broken")
|
||||
throw new CursorApiKeyExchangeError("Cursor rejected the API key", 401);
|
||||
if (apiKey === "crsr_cooling") throw new Error("must not be used");
|
||||
return accessToken === "user::session" ? "oauth-jwt" : "unexpected";
|
||||
},
|
||||
});
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(await mintedToken()),
|
||||
["aiserver.v1.DashboardService", "GetMe"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 200);
|
||||
assert.equal(
|
||||
(upstreamCalls[0].init.headers as Headers).get("authorization"),
|
||||
"Bearer oauth-jwt"
|
||||
);
|
||||
});
|
||||
|
||||
it("surfaces an exchange failure as 401 when no connection resolves", async () => {
|
||||
const { deps } = makeDeps({
|
||||
resolveBearer: async () => {
|
||||
throw new CursorApiKeyExchangeError("Cursor rejected the API key", 401);
|
||||
},
|
||||
});
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(await mintedToken()),
|
||||
["aiserver.v1.DashboardService", "GetMe"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 401);
|
||||
const body = (await res.json()) as { code: string };
|
||||
assert.equal(body.code, "unauthenticated");
|
||||
});
|
||||
|
||||
it("invalidates the cached Cursor session when upstream answers 401", async () => {
|
||||
const invalidated: string[] = [];
|
||||
const { deps } = makeDeps({
|
||||
fetchImpl: (async () => new Response("expired", { status: 401 })) as unknown as typeof fetch,
|
||||
invalidateBearer: (key) => {
|
||||
invalidated.push(key);
|
||||
},
|
||||
});
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(await mintedToken()),
|
||||
["aiserver.v1.DashboardService", "GetMe"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 401);
|
||||
assert.deepEqual(invalidated, [CURSOR_KEY]);
|
||||
});
|
||||
|
||||
it("streams SSE bodies through and logs once when the stream completes", async () => {
|
||||
const encoder = new TextEncoder();
|
||||
const upstreamBody = new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(encoder.encode("event: a\ndata: 1\n\n"));
|
||||
controller.enqueue(encoder.encode("event: b\ndata: 2\n\n"));
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
const { deps, logs } = makeDeps({
|
||||
fetchImpl: (async () =>
|
||||
new Response(upstreamBody, {
|
||||
status: 200,
|
||||
headers: { "content-type": "text/event-stream", "transfer-encoding": "chunked" },
|
||||
})) as unknown as typeof fetch,
|
||||
});
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(await mintedToken(), "/agent.v1.AgentService/RunSSE"),
|
||||
["agent.v1.AgentService", "RunSSE"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 200);
|
||||
assert.equal(res.headers.get("content-type"), "text/event-stream");
|
||||
assert.equal(res.headers.get("transfer-encoding"), null);
|
||||
assert.equal(await res.text(), "event: a\ndata: 1\n\nevent: b\ndata: 2\n\n");
|
||||
await flushLogs();
|
||||
assert.equal(logs.length, 1);
|
||||
assert.equal(logs[0].path, `${CURSOR_CLI_PROXY_PREFIX}/agent.v1.AgentService/RunSSE`);
|
||||
assert.equal(logs[0].status, 200);
|
||||
});
|
||||
|
||||
it("maps upstream network failures to a sanitized 502", async () => {
|
||||
const { deps, logs } = makeDeps({
|
||||
fetchImpl: (async () => {
|
||||
throw new Error("connect ECONNREFUSED at /home/user/OmniRoute/open-sse/x.ts:1:1");
|
||||
}) as unknown as typeof fetch,
|
||||
});
|
||||
const res = await handleCursorCliProxy(
|
||||
rpcRequest(await mintedToken()),
|
||||
["aiserver.v1.DashboardService", "GetMe"],
|
||||
deps
|
||||
);
|
||||
assert.equal(res.status, 502);
|
||||
const body = (await res.json()) as { message: string };
|
||||
assert.ok(!body.message.includes("at /"), body.message);
|
||||
await flushLogs();
|
||||
assert.equal(logs[0].status, 502);
|
||||
});
|
||||
});
|
||||
@@ -21,7 +21,8 @@
|
||||
// Raycast Pro #8895), then later additions to 199; retiring GitHub Models brings it to 198.
|
||||
// The v3.8.50 free-tier gateway waves (#9631 registry cycle, waves 2-5, #9210 phase 3) grew the
|
||||
// gateways family to 228 measured on the tip; Puter retired (#10210) and chatanywhere restored
|
||||
// (base-reds round 3, #9985) are both included in that measurement.
|
||||
// (base-reds round 3, #9985) are both included in that measurement; Cursor API (specialty-media,
|
||||
// #10729) brings it to 229.
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
@@ -50,12 +51,12 @@ test("barrel still exports every catalog + key helpers", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("APIKEY_PROVIDERS merges the 6 family files into 228 entries (no loss / no dup)", async () => {
|
||||
test("APIKEY_PROVIDERS merges the 6 family files into 229 entries (no loss / no dup)", async () => {
|
||||
const keys = Object.keys((P as Record<string, object>).APIKEY_PROVIDERS);
|
||||
assert.equal(keys.length, 228);
|
||||
assert.equal(new Set(keys).size, 228, "duplicate keys after spread-merge");
|
||||
assert.equal(keys.length, 229);
|
||||
assert.equal(new Set(keys).size, 229, "duplicate keys after spread-merge");
|
||||
// the merged object's entry-count equals the sum of the 6 semantic family files; families are a
|
||||
// strict partition (every provider in exactly one), so the sum must be exactly 228.
|
||||
// strict partition (every provider in exactly one), so the sum must be exactly 229.
|
||||
const families: [string, string][] = [
|
||||
["gateways", "APIKEY_PROVIDERS_GATEWAYS"],
|
||||
["frontier-labs", "APIKEY_PROVIDERS_FRONTIER"],
|
||||
@@ -75,7 +76,7 @@ test("APIKEY_PROVIDERS merges the 6 family files into 228 entries (no loss / no
|
||||
seen.add(k);
|
||||
}
|
||||
}
|
||||
assert.equal(famTotal, 228, "families must partition all 228 providers");
|
||||
assert.equal(famTotal, 229, "families must partition all 229 providers");
|
||||
});
|
||||
|
||||
test("AI_PROVIDERS Proxy aggregates all sections; lookups resolve", () => {
|
||||
|
||||