mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
docs/ux(release): tier marketing, onboarding tour, comparison, and v3.8.0 changelog
Task 8 — Tier 1/2/3 marketing & onboarding UX:
- README "Why OmniRoute?" enhanced with ASCII 3-tier fallback diagram
and comparison table vs 9router/LiteLLM/OpenRouter/Portkey
- docs/marketing/TIERS.md: user-facing tier guide with provider
classification, strategy notes, and common patterns
- images/tier-flow-{light,dark}.svg: SVG tier flow diagrams
- TierFlowDiagram.tsx: responsive SVG diagram (light/dark via next-themes)
- TierTour.tsx: onboarding step showing tier flow + 3 tier cards
- onboarding/page.tsx: inserts "How It Works" tier step after Welcome
- TierCoverageWidget.tsx: home dashboard card showing active provider
counts per tier with "Add" CTA for empty tiers
- HomePageClient.tsx: renders TierCoverageWidget before providers section
- en.json: onboarding.tier namespace (tier1/2/3 labels, subtitle, CTAs)
- docs/routing/AUTO-COMBO.md: tier weight table and override example
Task 9 — Docs, CHANGELOG, comparison page:
- CHANGELOG.md: [3.8.0] section documenting all Tasks 1-8
- docs/releases/v3.8.0.md: detailed release notes with migration guide
- docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md: 9router/LiteLLM/
OpenRouter/Portkey comparison matrix
- docs/architecture/REPOSITORY_MAP.md: new bin/cli/tray/, bin/cli/runtime/,
skills/ directories
- docs/ops/RELEASE_CHECKLIST.md: v3.8.0+ checks (tray, SQLite, MCP filter,
route guard)
This commit is contained in:
78
CHANGELOG.md
78
CHANGELOG.md
@@ -6,46 +6,66 @@
|
||||
|
||||
- **refactor(@omniroute/opencode-provider):** complete rewrite of the npm helper. The `1.0.0` artifact was non-functional — `index.js` re-exported from `.ts` (unrunnable at install time) and the emitted shape didn't match the OpenCode `https://opencode.ai/config.json` schema. The new release ships a real `tsup` build (CJS + ESM + `.d.ts`), schema-correct output (`npm: "@ai-sdk/openai-compatible"`, with `models` catalog), `baseURL` deduplication (no more `/v1/v1`), input validation, 13 unit tests, and full documentation in [`docs/frameworks/OPENCODE.md`](docs/frameworks/OPENCODE.md). Versioned as `0.1.0` to signal the pre-1.0 reset.
|
||||
- **chore(npm):** [`@omniroute/opencode-provider@0.1.0`](https://www.npmjs.com/package/@omniroute/opencode-provider) published to npmjs.com under the new `@omniroute` org. Install with `npm install --save-dev @omniroute/opencode-provider`.
|
||||
- **BREAKING**: dropped Node 20.x support. Minimum Node version is now 22.22.2 (or 24.0.0+). Required because http-proxy-middleware 4.x requires `node >=22.15.0`. Users on Node 20 must upgrade — see [`package.json` engines field](package.json) and the README Node badge.
|
||||
|
||||
### Security
|
||||
|
||||
- **fix(oauth/windsurf):** Windsurf Firebase token refresh now reads `WINDSURF_CONFIG.firebaseApiKey` instead of `process.env.WINDSURF_FIREBASE_API_KEY` directly. The literal was removed from `.env.example` in this release, so the previous direct read would have silently skipped refresh for browser-flow Windsurf/Devin sessions (forcing re-auth instead of renewing). Operators with a legacy `WINDSURF_FIREBASE_API_KEY` value in their `.env` keep working — the env override path is preserved by `resolvePublicCred()`. See [`docs/security/PUBLIC_CREDS.md`](docs/security/PUBLIC_CREDS.md).
|
||||
- **fix(kiro/translator):** assistant-first conversations no longer collide on a single `conversationId`. The synthetic "(empty)" user turn injected to satisfy Kiro's "first message must be user" rule is now marked non-enumerable `__synthetic` and excluded from the `uuidv5` conversationId derivation, so unrelated chats no longer share the same upstream AWS Builder ID context. Prevents leaking prior session state across unrelated chats.
|
||||
- **fix(utils/publicCreds):** `decodePublicCred()` no longer silently mangles raw credential overrides that don't match `RAW_VALUE_PATTERN`. The previous path always base64-decoded + XOR-unmasked (and `Buffer.from(v, "base64")` is lenient, accepting many non-base64 inputs without throwing). Now: strict-base64 alphabet check + printable-plain check on the decoded result; failing either, the original value is returned untouched.
|
||||
- **fix(auth/extractApiKey):** `x-api-key` fallback now only triggers when the request also carries an `anthropic-version` header. Without this scoping, non-Anthropic clients in local mode (placeholder `x-api-key`) would get `401 Invalid API key` from per-route gates even with `REQUIRE_API_KEY` off.
|
||||
- **fix(providers/qoder):** the OAuth+PAT disambiguation message now actually surfaces. The `getProviderRuntimeStatus()` early-return on `qoder + !apikey` was masking the new branch added in #2247.
|
||||
- **fix(oauth/windsurf):** Windsurf Firebase token refresh now reads `WINDSURF_CONFIG.firebaseApiKey` instead of `process.env.WINDSURF_FIREBASE_API_KEY` directly.
|
||||
- **fix(kiro/translator):** assistant-first conversations no longer collide on a single `conversationId`.
|
||||
- **fix(utils/publicCreds):** `decodePublicCred()` no longer silently mangles raw credential overrides that don't match `RAW_VALUE_PATTERN`.
|
||||
- **fix(auth/extractApiKey):** `x-api-key` fallback now only triggers when the request also carries an `anthropic-version` header.
|
||||
- **fix(providers/qoder):** the OAuth+PAT disambiguation message now actually surfaces.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **fix(executor/claude-code):** store tool-name round-trip metadata in non-enumerable `_toolNameMap` so it survives in-memory but is stripped by `JSON.stringify()` — prevents internal OmniRoute metadata from leaking to upstream providers. ([#2254](https://github.com/diegosouzapw/OmniRoute/pull/2254) — thanks @Rikonorus)
|
||||
- **fix(streaming):** strip upstream `Content-Encoding`, `Content-Length`, and `Transfer-Encoding` headers from SSE responses — prevents client-side decompression corruption when the proxy serves plain-text event streams through nginx/caddy. ([#2253](https://github.com/diegosouzapw/OmniRoute/pull/2253) — thanks @Rikonorus)
|
||||
- **fix(kiro):** harden OpenAI-to-Kiro translator for API compliance: recursively strip `additionalProperties` and empty `required: []` from tool schemas; merge consecutive assistant messages; prepend synthetic user for assistant-first conversations; convert orphaned tool results to inline text; enforce `origin: "AI_EDITOR"` on all history user messages; deterministic `uuidv5` session caching. Closes #2213. ([#2251](https://github.com/diegosouzapw/OmniRoute/pull/2251) — thanks @8mbe)
|
||||
- **fix(models):** sync managed model aliases with provider model visibility — remove aliases when models are hidden/deleted, skip alias creation for hidden models during sync, restore aliases when unhidden, cross-connection safety guard prevents pruning aliases still valid from another connection. ([#2250](https://github.com/diegosouzapw/OmniRoute/pull/2250) — thanks @InkshadeWoods)
|
||||
- **fix(models/cleanup):** align managed model cleanup for imported models — provider-level "Delete All" now also removes synced available model storage; delete-alias button only shown for alias-source rows; compatible models section uses proper 3-way source-aware delete logic. ([#2261](https://github.com/diegosouzapw/OmniRoute/pull/2261) — thanks @InkshadeWoods)
|
||||
- **fix(auth):** accept `x-api-key` header in `extractApiKey` so Anthropic-native clients (Claude Code, `@anthropic-ai/sdk`) hit the same per-key policy enforcement as Bearer clients. Previously these requests were treated as anonymous, bypassing model/budget/rate-limit policies and showing up as `NULL` in `usage_history.api_key_id` (~50% of traffic invisible in Costs/Analytics). `Authorization: Bearer` still wins when both are present (back-compat). (#2225)
|
||||
- **fix(translator/claude-to-openai):** stop including `cache_creation_input_tokens` in `prompt_tokens`. Anthropic pads short prompts up to a 1024-token minimum on cache creation, so a 2-token `"hi"` could be reported as ~2008 `prompt_tokens` and inflate downstream billing (Sub2API/NewAPI/OneAPI) ~250x. `prompt_tokens` now matches the dashboard "Total In" (`input + cache_read`); `cache_creation_tokens` is exposed separately in `prompt_tokens_details.cache_creation_tokens` for auditing. (#2215)
|
||||
- **fix(ui/claude-extra-usage):** clarify the toggle-success notification text to spell out the toggle→effect relationship ("Claude extra-usage blocking enabled/disabled" instead of the ambiguous "blocked/allowed"). (#2157)
|
||||
- **fix(providers/qoder):** disambiguate the "Local CLI runtime is not installed" error when a user pastes a Personal Access Token but the connection is in OAuth/CLI-flavored mode. The test route now surfaces a single actionable message ("switch this connection to API Key auth") instead of cascading CLI + 401 errors. (#2247)
|
||||
- **fix(dashboard/api-manager):** route custom OpenAI-/Anthropic-compatible provider IDs through `getProviderDisplayName` so the model grouping label shows `Compatible (openai)` instead of leaking the raw synthetic `openai-compatible-chat-<uuid>` value. (#2021)
|
||||
- **fix(providers/blackbox-web):** add `BLACKBOX_WEB_VALIDATED_TOKEN` env override and 403 token-error disambiguation. Blackbox `/api/chat` started rejecting requests whose `validated` field didn't match the frontend `tk` token, even with a valid cookie + active subscription. Operators with the real token can now set the env var; otherwise the previous random-UUID fallback still ships, and a 403 with a token-specific body now surfaces a one-line "set BLACKBOX_WEB_VALIDATED_TOKEN" hint instead of the generic "cookie expired" message. (#2252)
|
||||
- **docs(security):** document the ToS-violation hot spot of `ANTIGRAVITY_CREDITS=always` in `STEALTH_GUIDE.md`, including why it draws Google abuse detection more aggressively than free-tier-only usage and the recommended posture (`=retry`, Auto-Combo spread, per-connection RPM caps). (#2246)
|
||||
- **fix(executor/claude-code):** store tool-name round-trip metadata in non-enumerable `_toolNameMap`. ([#2254](https://github.com/diegosouzapw/OmniRoute/pull/2254))
|
||||
- **fix(streaming):** strip upstream `Content-Encoding`, `Content-Length`, and `Transfer-Encoding` headers from SSE responses. ([#2253](https://github.com/diegosouzapw/OmniRoute/pull/2253))
|
||||
- **fix(kiro):** harden OpenAI-to-Kiro translator for API compliance. ([#2251](https://github.com/diegosouzapw/OmniRoute/pull/2251))
|
||||
- **fix(models):** sync managed model aliases with provider model visibility. ([#2250](https://github.com/diegosouzapw/OmniRoute/pull/2250))
|
||||
- **fix(auth):** accept `x-api-key` header in `extractApiKey`. (#2225)
|
||||
- **fix(translator/claude-to-openai):** stop including `cache_creation_input_tokens` in `prompt_tokens`. (#2215)
|
||||
- **Docs:** 270 broken internal markdown links repaired.
|
||||
|
||||
---
|
||||
|
||||
## [3.8.0] - 2026-05-15
|
||||
|
||||
### Added
|
||||
|
||||
- `feat(mcp): MCP accessibility-tree smart filter engine` — collapses ≥30 repeated sibling lines, preserves `[ref=eXX]` anchors, 60-80% savings on browser snapshot outputs (Task 1)
|
||||
- `docs(skills): publish 10 SKILL.md manifests for external AI agents` — zero-friction onboarding for Claude Desktop, ChatGPT, Cursor, Cline (Task 2)
|
||||
- `feat(cli): standalone system tray with PowerShell fallback on Windows` — no Electron required; `omniroute --tray`; autostart via LaunchAgent/.desktop/registry (Task 3)
|
||||
- `feat(auth): CLI machine-ID HMAC-SHA256 token` — zero-friction local auth without JWT/password; loopback-only; constant-time compare (Task 4)
|
||||
- `feat(security): route protection tiers` — 5 tiers: public/read-only/protected/always/local-only; spawn-capable routes enforce loopback even with valid JWT (Task 5)
|
||||
- `feat(compression): Caveman SHARED_BOUNDARIES` — all 6 languages × 3 intensities embed boundary clause; `alreadyApplied` check order fixed (Task 6)
|
||||
- `feat(runtime): dynamic SQLite 5-step fallback chain` — bundled → runtime-installed → lazy-install → node:sqlite → sql.js; magic-byte validation (ELF/Mach-O/PE) (Task 7)
|
||||
- `docs/ux: tier 1/2/3 marketing, onboarding tour, dashboard widget` — README tier diagram, `docs/marketing/TIERS.md`, TierTour onboarding step, Tier Coverage widget (Task 8)
|
||||
- `docs(comparison): OMNIROUTE_VS_ALTERNATIVES.md` — objective comparison vs 9router, LiteLLM, OpenRouter, Portkey
|
||||
|
||||
### Changed
|
||||
|
||||
- **BREAKING**: dropped Node 20.x support. Minimum Node version is now 22.22.2 (or 24.0.0+). Required because http-proxy-middleware 4.x requires `node >=22.15.0`. Users on Node 20 must upgrade — see [`package.json` engines field](package.json) and the README Node badge.
|
||||
- **Platform overhaul (FASES 1-9):** scripts cleanup, `.env` audit, `/docs` restructure, diagrams folder, i18n pipelines (docs + UI), `/src/app/docs` sync, CI gates.
|
||||
- **Scripts:** `scripts/` reorganized into 6 subfolders (`build/`, `dev/`, `check/`, `docs/`, `i18n/`, `ad-hoc/`); 23 one-shot scripts archived to the `archive/scripts-scratch-pre-3.8` branch.
|
||||
- **Environment:** `.env.example` cleaned (-11 orphan vars, +63 missing vars, 11 hardcoded URLs/timeouts promoted to env); new strict `scripts/check/check-env-doc-sync.mjs` validates code ↔ `.env.example` ↔ `docs/reference/ENVIRONMENT.md` parity.
|
||||
- **Docs:** `/docs` restructured into 8 functional subfolders (`architecture/`, `guides/`, `reference/`, `frameworks/`, `routing/`, `security/`, `compression/`, `ops/`); ~899 cross-references rewritten.
|
||||
- **Diagrams:** new `docs/diagrams/` with 8 canonical Mermaid sources + SVG exports, linked into 9 docs.
|
||||
- **i18n (docs):** hash-based incremental pipeline (`config/i18n.json`, `scripts/i18n/run-translation.mjs`, `.i18n-state.json`); old `i18n_autotranslate.py` and `generate-multilang.mjs` deprecated.
|
||||
- **i18n (UI):** `scripts/i18n/sync-ui-keys.mjs` propagates `en.json` keys to all 40 locales (no missing keys; coverage ≥ 85.8%); cosmetic `DocsI18n.tsx` removed (locale handling unified via `next-intl`).
|
||||
- **`/src/app/docs`:** drift fixes (179 → 177 providers, 13 → 14 strategies, 36 → 37 MCP tools); YAML frontmatter added to all docs; `ApiExplorer` consumes `docs/reference/openapi.yaml` (19 endpoints); `content.ts` updated (37 MCP tool groups, 7 internal deployment guide hrefs).
|
||||
- **CI gates:** strict env-doc-sync in pre-commit; `check:doc-links` validates internal markdown refs; new `docs-sync-strict` and `i18n-ui-coverage` jobs in `.github/workflows/ci.yml`.
|
||||
- `getDbInstance()` requires prior `ensureDbInitialized()` call — server startup awaits it automatically (see release notes for migration)
|
||||
- Caveman prompts embed `SHARED_BOUNDARIES` verbatim (LITE/FULL/ULTRA × 6 languages)
|
||||
- README "Why OmniRoute?" enhanced with 3-tier ASCII diagram and comparison table
|
||||
- Onboarding wizard gains "How It Works" tier tour step (after Welcome, before Security)
|
||||
- Home dashboard shows "Tier coverage" widget (configured + active counts per tier)
|
||||
|
||||
### Fixed
|
||||
### Security
|
||||
|
||||
- **Docs:** 270 broken internal markdown links repaired (consequence of `/docs` subfolder restructure not relativizing all paths). Categories: 241 `i18n-relative`, 14 `parent-relative`, 9 `screenshots`, 2 deleted-RFC, 4 misc. Now `npm run check:doc-links` PASS with 0 broken links.
|
||||
- Hard Rule #15: spawn-capable routes must call `assertRouteAllowed(req)` (CLAUDE.md)
|
||||
- CLI token rejected on non-loopback hosts even when the HMAC is correct
|
||||
- `always`-protected routes (shutdown, db export) reject CLI tokens unconditionally
|
||||
|
||||
### Documentation
|
||||
|
||||
- `docs/security/CLI_TOKEN.md`
|
||||
- `docs/security/ROUTE_GUARD_TIERS.md`
|
||||
- `docs/ops/SQLITE_RUNTIME.md`
|
||||
- `docs/marketing/TIERS.md`
|
||||
- `docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`
|
||||
- `docs/releases/v3.8.0.md`
|
||||
|
||||
---
|
||||
|
||||
## [3.8.0] — 2026-05-06
|
||||
|
||||
|
||||
76
README.md
76
README.md
@@ -211,26 +211,80 @@ _Connect any AI-powered IDE or CLI tool through OmniRoute — free API gateway f
|
||||
|
||||
## 🤔 Why OmniRoute?
|
||||
|
||||
**Stop wasting money, tokens and hitting limits:**
|
||||
**One endpoint. 207+ providers. Never stop building.**
|
||||
|
||||
❌ Subscription quota expires unused every month
|
||||
❌ Rate limits stop you mid-coding
|
||||
❌ Tool outputs (`git diff`, `grep`, `ls`...) burn tokens fast
|
||||
❌ Expensive APIs ($20-50/month per provider)
|
||||
❌ Manual switching between providers
|
||||
❌ Each provider has a different API format
|
||||
❌ AI providers blocked in your country
|
||||
Stop juggling 10 dashboards, dead API keys, and surprise bills. OmniRoute
|
||||
routes every request through the cheapest viable provider — automatically.
|
||||
|
||||
**OmniRoute solves all of this:**
|
||||
### The 3-tier fallback (zero-downtime AI)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ Your IDE / CLI / App │
|
||||
│ (Claude Code, Cursor, Cline, Copilot, …) │
|
||||
└─────────────────────┬────────────────────────────────────┘
|
||||
│ http://localhost:20128/v1
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ OmniRoute (Smart Router) │
|
||||
│ • RTK token saver (47 specialized filters) │
|
||||
│ • Caveman terse-mode (3 levels + SHARED_BOUNDARIES) │
|
||||
│ • Auto-fallback combos (14 strategies) │
|
||||
│ • Circuit breaker · TLS fingerprint stealth (JA3/JA4) │
|
||||
│ • Memory · MCP server · A2A · Guardrails · Evals │
|
||||
└─────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────┼─────────────────┐
|
||||
▼ Tier 1 ▼ Tier 2 ▼ Tier 3
|
||||
SUBSCRIPTION CHEAP FREE
|
||||
(Claude Code, (DeepSeek $0.27, (Kiro, OpenCode,
|
||||
Codex, Copilot, GLM $0.60, Gemini CLI,
|
||||
Cursor, Antigravity) MiniMax $0.20) Vertex $300cr)
|
||||
|
||||
quota exhausted? budget hit? always available
|
||||
→ falls to Tier 2 → falls to Tier 3
|
||||
```
|
||||
|
||||
### Why this matters
|
||||
|
||||
- ❌ **Subscription quota wasted** every month? OmniRoute uses every token before expiry.
|
||||
- ❌ **Rate limits stop you mid-flow?** Auto-fallback to the next provider in milliseconds.
|
||||
- ❌ **Tool outputs burn tokens?** RTK compresses `git diff`, logs, and grep results 30-50%.
|
||||
- ❌ **Paying $50/mo across 5 providers?** Route to the cheapest viable model automatically.
|
||||
- ❌ **Each AI tool wants its own setup?** One endpoint, every tool, one dashboard.
|
||||
|
||||
### What sets OmniRoute apart
|
||||
|
||||
| Feature | OmniRoute | Other routers |
|
||||
| ----------------------------------- | ------------------------------------------------------------- | ------------- |
|
||||
| Providers | **207+** | 20-100 |
|
||||
| Combo strategies | **14** (priority, weighted, cost-optimized, context-relay, …) | 1-3 |
|
||||
| Token compression (RTK) | **47 specialized filters** | None |
|
||||
| Built-in MCP server | **37 tools, 3 transports, 13 scopes** | Rare |
|
||||
| A2A agent protocol | **5 skills, JSON-RPC 2.0** | None |
|
||||
| Memory (FTS5 + vector) | **Yes** | Rare |
|
||||
| Guardrails (PII, injection, vision) | **Yes** | Rare |
|
||||
| Cloud agent integrations | Codex, Devin, Jules | None |
|
||||
| Circuit breaker per provider | **3-state, lazy recovery** | Rare |
|
||||
| TLS fingerprint stealth | **JA3/JA4 via wreq-js** | None |
|
||||
| Eval framework | **Built-in** | Rare |
|
||||
| CLI (no Electron required) | **Yes** + system tray | Varies |
|
||||
| i18n | **40+ locales** | 0-4 |
|
||||
|
||||
See [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md) for a detailed comparison vs 9router, LiteLLM, OpenRouter, and Portkey.
|
||||
|
||||
---
|
||||
|
||||
**Also solves:**
|
||||
|
||||
✅ **Prompt Compression** — auto-compress prompts & tool outputs, save 15-95% eligible tokens per request with RTK+Caveman stacked mode
|
||||
✅ **Maximize subscriptions** — track quota, use every bit before reset
|
||||
✅ **Auto fallback** — Subscription → API Key → Cheap → Free, zero downtime
|
||||
✅ **Auto fallback** — Subscription → Cheap → Free, zero downtime
|
||||
✅ **Multi-account** — round-robin between accounts per provider
|
||||
✅ **Format translation** — OpenAI ↔ Claude ↔ Gemini ↔ Responses API, any tool works
|
||||
✅ **3-level proxy** — bypass geo-blocks with global, per-provider, and per-key proxies
|
||||
✅ **10 multi-modal APIs** — chat, images, video, music, audio, search in one endpoint
|
||||
✅ **MCP + A2A** — 29 MCP tools + agent-to-agent protocol, production-ready
|
||||
✅ **MCP + A2A** — 37 MCP tools + agent-to-agent protocol, production-ready
|
||||
✅ **Universal** — works with Claude Code, Codex, Gemini CLI, Cursor, Cline, OpenClaw, any CLI tool
|
||||
|
||||
---
|
||||
|
||||
@@ -278,10 +278,24 @@ open-sse/
|
||||
| `cli/commands/doctor.mjs` | System health diagnostics (8+ checks) |
|
||||
| `cli/commands/providers.mjs` | Provider list/test/validate |
|
||||
| `cli/{args,data-dir,encryption,io,provider-catalog,provider-store,provider-test,settings-store,sqlite}.mjs` | CLI helper modules |
|
||||
| `cli/tray/tray.ts` | System tray integration (cross-platform: NotifyIcon on Windows, systray2 on macOS/Linux) |
|
||||
| `cli/tray/tray.ps1` | PowerShell NotifyIcon backend (Windows, zero new binaries) |
|
||||
| `cli/tray/autostart.ts` | Cross-platform autostart (LaunchAgent / .desktop / registry) |
|
||||
| `cli/runtime/sqliteRuntime.mjs` | 5-step SQLite driver resolution chain (bundled → runtime → lazy-install → node:sqlite → sql.js) |
|
||||
| `cli/runtime/magicBytes.mjs` | Binary magic-byte validation (ELF / Mach-O / Mach-O fat / PE) |
|
||||
| `cli/runtime/index.mjs` | `warmUpRuntimes()` — pre-resolves drivers at postinstall / first startup |
|
||||
| `nodeRuntimeSupport.mjs` | Validate supported Node.js version on install |
|
||||
|
||||
---
|
||||
|
||||
## `skills/` — Public Agent Skills
|
||||
|
||||
| File | Purpose |
|
||||
| ---------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| `skills/omniroute*/SKILL.md` | 10 skill manifests for external AI agents (Claude Desktop, ChatGPT, Cursor, Cline) |
|
||||
|
||||
---
|
||||
|
||||
## `scripts/` — Build & Check Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|
||||
73
docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md
Normal file
73
docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: "OmniRoute vs Alternatives"
|
||||
version: 3.8.0
|
||||
lastUpdated: 2026-05-15
|
||||
---
|
||||
|
||||
# OmniRoute vs Alternatives
|
||||
|
||||
Objective feature comparison vs popular open-source AI routers.
|
||||
|
||||
> **Methodology**: Public repos audited 2026-Q2. Versions as listed.
|
||||
> Submit corrections via PR — we want this to be accurate.
|
||||
|
||||
| Feature | OmniRoute 3.8 | 9router 0.4 | LiteLLM 1.x | OpenRouter (SaaS) | Portkey |
|
||||
| -------------------------------------------------- | :------------------------: | :------------------------: | :------------: | :---------------: | :---------: |
|
||||
| **Providers** | **207+** | ~40 | ~100 | ~50 | ~30 |
|
||||
| **Self-hostable** | ✅ | ✅ | ✅ | ❌ | ⚠ paid |
|
||||
| **OAuth providers (Claude, Codex, Copilot, etc.)** | **15+** | 8 | partial | ❌ | ❌ |
|
||||
| **Auto-fallback combos** | **14 strategies** | round-robin only | priority-based | tier-based | weighted |
|
||||
| **Tier 1/2/3 fallback (subscription→cheap→free)** | ✅ + UI | ✅ + UI | manual | n/a | manual |
|
||||
| **Token compression** | RTK (47 filters) + Caveman | RTK (10 filters) + Caveman | none | none | none |
|
||||
| **Built-in MCP server** | ✅ 37 tools, 13 scopes | stdio bridge only | ❌ | ❌ | ❌ |
|
||||
| **A2A protocol** | ✅ 5 skills | ❌ | ❌ | ❌ | ❌ |
|
||||
| **Memory (FTS5 + vector)** | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| **Guardrails (PII, injection, vision)** | ✅ | ❌ | partial | ❌ | ✅ paid |
|
||||
| **Cloud agent integrations** | Codex, Devin, Jules | ❌ | ❌ | ❌ | ❌ |
|
||||
| **Circuit breaker per provider** | ✅ 3-state, lazy recovery | ❌ | basic | ❌ | ✅ |
|
||||
| **TLS fingerprint stealth (JA3/JA4)** | ✅ wreq-js | ❌ | ❌ | ❌ | ❌ |
|
||||
| **Eval framework** | ✅ built-in | ❌ | ❌ | ❌ | ⚠ paid |
|
||||
| **MITM proxy (intercepts Cursor/Antigravity)** | ✅ cross-platform | ✅ cross-platform | ❌ | ❌ | ❌ |
|
||||
| **CLI with system tray (no Electron)** | ✅ | ✅ | ❌ | n/a | n/a |
|
||||
| **CLI machine-ID auto-auth** | ✅ | ✅ | ❌ | n/a | n/a |
|
||||
| **Dashboard** | Next.js 16 | Next.js | basic | proprietary | proprietary |
|
||||
| **i18n** | **40+ locales** | 4 locales | ❌ | ❌ | ⚠ |
|
||||
| **Public agent skills (SKILL.md)** | ✅ 10 | ✅ 8 | ❌ | ❌ | ❌ |
|
||||
| **Tunnel support (Cloudflared, Tailscale, Ngrok)** | ✅ | partial (Tailscale) | ❌ | n/a | n/a |
|
||||
| **License** | MIT | MIT | MIT | proprietary | proprietary |
|
||||
|
||||
## When to choose OmniRoute
|
||||
|
||||
- You self-host and want **maximum provider coverage** (207+)
|
||||
- You need a **built-in MCP server** (LLM tools, memory, skills exposed as tools)
|
||||
- You need **A2A protocol** for agent-to-agent workflows
|
||||
- You want **fingerprint stealth** (JA3/JA4) to avoid detection by upstream CAPTCHAs
|
||||
- You need **enterprise features** (guardrails, evals, audit trail) without a SaaS bill
|
||||
|
||||
## When to choose 9router
|
||||
|
||||
- You prefer a **lightweight** router (~5× smaller codebase)
|
||||
- You want the same tier strategy but with less surface area to maintain
|
||||
- You don't need MCP / A2A / Memory / Guardrails / Cloud agents
|
||||
|
||||
## When to choose LiteLLM
|
||||
|
||||
- You're **Python-first** and need tight integration with `litellm.completion()`
|
||||
- You need **mature production deployment recipes** (k8s, Helm charts)
|
||||
- Your team already runs Python microservices
|
||||
|
||||
## When to choose OpenRouter (SaaS)
|
||||
|
||||
- You don't want to self-host
|
||||
- You're fine paying per-token at SaaS markup
|
||||
- You need a **single payment method** across all providers
|
||||
|
||||
## When to choose Portkey
|
||||
|
||||
- You need a **commercial SLA** with uptime guarantees
|
||||
- You prefer a **managed dashboard** without ops overhead
|
||||
- You need **enterprise compliance** features out of the box
|
||||
|
||||
---
|
||||
|
||||
_Last updated: 2026-05-15. Submit corrections via PR to keep this table accurate._
|
||||
107
docs/marketing/TIERS.md
Normal file
107
docs/marketing/TIERS.md
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: "OmniRoute Tiers — User Guide"
|
||||
version: 3.8.0
|
||||
lastUpdated: 2026-05-15
|
||||
---
|
||||
|
||||
# OmniRoute Tiers — User Guide
|
||||
|
||||
OmniRoute organizes the 207+ supported providers into 3 economic tiers. Each
|
||||
request travels through them in order until one returns successfully — you
|
||||
get the cheapest viable response without ever writing fallback code.
|
||||
|
||||
## Tier 1 — Subscription
|
||||
|
||||
**Providers you already pay for.** OmniRoute uses every drop of quota before
|
||||
it expires.
|
||||
|
||||
| Provider | Why Tier 1 |
|
||||
| ----------------------------------- | -------------------------------------------- |
|
||||
| Claude Code OAuth | Anthropic Pro/Team — flat-rate, often unused |
|
||||
| OpenAI Codex (ChatGPT subscription) | Plus/Team includes Codex quota |
|
||||
| GitHub Copilot | Per-seat — quota resets monthly |
|
||||
| Cursor IDE | Pro plan quota |
|
||||
| Antigravity / Windsurf | Built-in quotas |
|
||||
|
||||
**Strategy**: route here first for every request that fits the model's
|
||||
strengths. Quota tracker monitors approaching reset; combo strategies
|
||||
`reset-aware` and `subscription` prioritize accordingly.
|
||||
|
||||
## Tier 2 — Cheap
|
||||
|
||||
**Pay-per-token providers under $1/1M tokens.** Reserved for high-volume work
|
||||
or after Tier 1 quotas hit limits.
|
||||
|
||||
| Provider | Price (input/output) | Strengths |
|
||||
| ---------------------------- | -------------------- | -------------------- |
|
||||
| DeepSeek V4 Pro | $0.27 / $1.10 per 1M | Code, reasoning |
|
||||
| GLM-4.5 | $0.60 / $2.20 per 1M | Long context |
|
||||
| MiniMax M1 | $0.20 / $1.10 per 1M | Speed |
|
||||
| Qwen Coder | $0.30 / $1.20 per 1M | Code |
|
||||
| OpenRouter (price-optimized) | varies | 100+ models, dynamic |
|
||||
|
||||
**Strategy**: combo `cost-optimized` picks lowest $/token model that meets
|
||||
the task's capability filter (vision, JSON mode, tools, max-context).
|
||||
|
||||
## Tier 3 — Free
|
||||
|
||||
**Zero-cost providers** — free tiers, credit programs, OAuth daily quotas.
|
||||
|
||||
| Provider | Free quota / credits |
|
||||
| ---------------- | ------------------------------------ |
|
||||
| Kiro AI | Free Claude tier (generous fair-use) |
|
||||
| OpenCode Free | No auth, generous rate limits |
|
||||
| Qoder | Free OAuth |
|
||||
| Gemini CLI OAuth | Generous daily quota |
|
||||
| Google Vertex AI | $300 new-account credits |
|
||||
| Amazon Q | Free tier for AWS users |
|
||||
| Pollinations | Open public API |
|
||||
| Cloudflare AI | Workers AI free tier |
|
||||
|
||||
**Strategy**: combo `auto` with budget cap routes here when Tier 1+2 fail
|
||||
or when `useFreeOnly=true` is set. Free providers often have weaker
|
||||
rate limits — circuit breaker recovers them on backoff.
|
||||
|
||||
## Configuring tiers
|
||||
|
||||
Dashboard → **Tiers** → assign your providers. Defaults (from `tierDefaults.json`) are
|
||||
sensible; edit when you have specific subscriptions to prioritize or providers to exclude.
|
||||
|
||||
Auto-Combo's 9-factor scoring also considers tier. See
|
||||
[`docs/routing/AUTO-COMBO.md`](../routing/AUTO-COMBO.md).
|
||||
|
||||
## Telemetry
|
||||
|
||||
Dashboard → **Usage** shows tokens spent per tier per day. Use this to:
|
||||
|
||||
- Confirm Tier 1 is utilized fully (otherwise you're wasting subscription value)
|
||||
- Identify which Tier 2 models are picked most (consolidate to 1-2)
|
||||
- Verify Tier 3 saves money on test/exploration workloads
|
||||
|
||||
## Common patterns
|
||||
|
||||
### Pure-free workload
|
||||
|
||||
```json
|
||||
{
|
||||
"strategy": "auto",
|
||||
"config": { "auto": { "weights": { "costInv": 0.5, "tierPriority": 0.3 } } }
|
||||
}
|
||||
```
|
||||
|
||||
Forces strongly towards Tier 3; only uses Tier 2 if Tier 3 is unavailable.
|
||||
|
||||
### Subscription-first with cheap fallback
|
||||
|
||||
```json
|
||||
{
|
||||
"strategy": "priority",
|
||||
"targets": [
|
||||
{ "provider": "claude-code-oauth", "weight": 1 },
|
||||
{ "provider": "deepseek", "weight": 1 },
|
||||
{ "provider": "kiro", "weight": 1 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Explicit ordered list matching Tier 1 → Tier 2 → Tier 3.
|
||||
@@ -188,6 +188,27 @@ If `electron/` changed:
|
||||
- [ ] Open milestone for next version
|
||||
- [ ] If critical: pin discussion or post in `news.json` for in-app banner
|
||||
|
||||
## v3.8.0+ checks
|
||||
|
||||
Before shipping any v3.8.x release, verify these additional items:
|
||||
|
||||
- [ ] `omniroute --tray` boots on macOS (systray2 installed into `~/.omniroute/runtime/`)
|
||||
- [ ] `omniroute --tray` boots on Linux (requires DISPLAY; graceful error if not set)
|
||||
- [ ] `omniroute --tray` boots on Windows (PowerShell NotifyIcon, no extra binaries)
|
||||
- [ ] `omniroute config tray enable` creates autostart entry; disable removes it
|
||||
- [ ] `npm install -g omniroute@<this-version>` runs postinstall without fatal exit
|
||||
- [ ] `omniroute status` works with no `.env` (CLI token path, loopback only)
|
||||
- [ ] `curl http://localhost:20128/api/shutdown` returns 401 (always-protected route)
|
||||
- [ ] `curl -H "host: evil.com" http://localhost:20128/api/mcp/sse` returns 401 (loopback guard)
|
||||
- [ ] SQLite runtime resolves to `bundled` on first run (bundled binary valid for platform)
|
||||
- [ ] SQLite runtime falls back to `runtime` when `node_modules/better-sqlite3` is deleted
|
||||
- [ ] Smart MCP filter compresses real `playwright-mcp browser_snapshot` output (≥50% reduction)
|
||||
- [ ] All 10 `skills/omniroute*/SKILL.md` files are publicly fetchable via raw GitHub URL
|
||||
- [ ] Onboarding wizard shows "How It Works" tier tour step on fresh setup
|
||||
- [ ] Home dashboard tier coverage widget shows configured/active counts
|
||||
|
||||
---
|
||||
|
||||
## Rollback
|
||||
|
||||
If release has critical issue:
|
||||
|
||||
146
docs/releases/v3.8.0.md
Normal file
146
docs/releases/v3.8.0.md
Normal file
@@ -0,0 +1,146 @@
|
||||
---
|
||||
title: "OmniRoute v3.8.0 Release Notes"
|
||||
version: 3.8.0
|
||||
releaseDate: 2026-05-15
|
||||
---
|
||||
|
||||
# OmniRoute v3.8.0 — Release Notes
|
||||
|
||||
**Release date**: 2026-05-15
|
||||
**Highlights**: 7 features inspired by 9router analysis + tier UX overhaul + 10 public AI agent skills.
|
||||
|
||||
## New features
|
||||
|
||||
### MCP smart text filter (Task 1)
|
||||
|
||||
Compression engine specifically for verbose MCP outputs (browser snapshots,
|
||||
accessibility trees). Collapses ≥30 repeated sibling lines into head + summary
|
||||
|
||||
- tail, preserves `[ref=eXX]` anchors required by Playwright/computer-use,
|
||||
hard-truncates oversized text with a navigation hint.
|
||||
|
||||
* New engine: `open-sse/services/compression/engines/mcpAccessibility/`
|
||||
* Settings: `compression.mcpAccessibility` (migration 056)
|
||||
* Expected savings: 60-80% on browser snapshot tool results
|
||||
|
||||
### Public AI agent skills (Task 2)
|
||||
|
||||
Ten `SKILL.md` manifests under `skills/omniroute*/` published for external AI
|
||||
agents (Claude Desktop, ChatGPT, Cursor, Cline) to consume via raw GitHub URL.
|
||||
Zero-friction onboarding: tell your agent to fetch one URL and start routing.
|
||||
|
||||
### Standalone system tray (Task 3)
|
||||
|
||||
CLI users now get a system tray icon without needing Electron. Windows uses
|
||||
PowerShell `NotifyIcon` (zero new binaries, AV-friendly). macOS/Linux use the
|
||||
`systray2` fork installed lazily into `~/.omniroute/runtime/`. Includes
|
||||
cross-platform autostart (LaunchAgent / `.desktop` / registry).
|
||||
|
||||
- New flag: `omniroute --tray`
|
||||
- New command: `omniroute config tray <enable|disable>`
|
||||
- New modules: `bin/cli/tray/`
|
||||
|
||||
### CLI machine-ID token (Task 4)
|
||||
|
||||
`HMAC-SHA256(machine-id, salt)` → constant-time-compared token sent via
|
||||
`x-omniroute-cli-token` header. Lets CLI commands authenticate locally
|
||||
without JWT/password.
|
||||
|
||||
- Accepted only on loopback host + whitelisted read-only routes
|
||||
- Salt rotation via `OMNIROUTE_CLI_SALT` env var
|
||||
- Falls back to `~/.omniroute/machine-id` (0600) if `node-machine-id` fails
|
||||
- Docs: `docs/security/CLI_TOKEN.md`
|
||||
|
||||
### Route protection tiers (Task 5)
|
||||
|
||||
All API routes classified into 5 tiers (`public`, `read-only`, `protected`,
|
||||
`always`, `local-only`). Spawn-capable routes (MCP, CLI tools, OAuth
|
||||
callbacks, MITM, skills) enforce loopback host+origin even with valid JWT —
|
||||
leaked JWT via tunnel can no longer trigger child process spawn.
|
||||
|
||||
- New module: `src/lib/auth/routeGuard.ts`
|
||||
- New Hard Rule #15 in `CLAUDE.md`
|
||||
- Docs: `docs/security/ROUTE_GUARD_TIERS.md`
|
||||
|
||||
### Caveman SHARED_BOUNDARIES (Task 6)
|
||||
|
||||
Caveman prompts (LITE/FULL/ULTRA) now embed a shared boundary clause
|
||||
instructing the LLM to keep code blocks, file paths, commands, errors, and
|
||||
URLs exact, and to write security warnings and irreversible-action
|
||||
confirmations in normal prose.
|
||||
|
||||
- New constant: `SHARED_BOUNDARIES` in `open-sse/services/compression/outputMode.ts`
|
||||
- All 6 languages × 3 intensities updated
|
||||
- `alreadyApplied` check order fixed (must precede `shouldBypassCavemanOutputMode`)
|
||||
|
||||
### Dynamic SQLite runtime installer (Task 7)
|
||||
|
||||
Five-step driver resolution chain:
|
||||
`bundled better-sqlite3` → `runtime-installed better-sqlite3` → `lazy install`
|
||||
→ `node:sqlite (Node ≥22.5)` → `bundled sql.js (WASM)`. Each native binary is
|
||||
validated against expected platform magic bytes (ELF / Mach-O / PE) before
|
||||
load. Solves Windows EBUSY error on global updates while OmniRoute is running.
|
||||
|
||||
- New scripts: `scripts/postinstall.mjs`, `bin/cli/runtime/`
|
||||
- Runtime dir: `~/.omniroute/runtime/`
|
||||
- New helper: `src/lib/db/core.ts::ensureDbInitialized()`
|
||||
- Docs: `docs/ops/SQLITE_RUNTIME.md`
|
||||
|
||||
### Tier 1/2/3 marketing & onboarding (Task 8)
|
||||
|
||||
README "Why OmniRoute?" section enhanced with ASCII tier diagram and comparison
|
||||
table. New `/dashboard/onboarding/steps/TierTour.tsx` step shows tier flow on
|
||||
first run with light/dark SVG assets. Home dashboard gains "Tier coverage"
|
||||
widget with empty-state CTA.
|
||||
|
||||
- New assets: `images/tier-flow-light.svg`, `images/tier-flow-dark.svg`
|
||||
- New docs: `docs/marketing/TIERS.md`
|
||||
|
||||
## Improvements
|
||||
|
||||
- New i18n strings for tier UI (en first; translations in v3.8.1)
|
||||
- Compression analytics dashboard now reports per-engine savings
|
||||
- 47 RTK filters documented in provider reference
|
||||
|
||||
## Security
|
||||
|
||||
- Route protection tiers (Task 5) — spawn-capable routes blocked off loopback
|
||||
- Hard Rule #15 in `CLAUDE.md` — `assertRouteAllowed(req)` mandatory for spawn routes
|
||||
- CLI machine-ID token does NOT bypass `always`-protected routes (shutdown, db export)
|
||||
|
||||
## Documentation
|
||||
|
||||
- `docs/security/CLI_TOKEN.md`
|
||||
- `docs/security/ROUTE_GUARD_TIERS.md`
|
||||
- `docs/ops/SQLITE_RUNTIME.md`
|
||||
- `docs/marketing/TIERS.md`
|
||||
- `docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`
|
||||
|
||||
## Breaking changes
|
||||
|
||||
`getDbInstance()` now requires `ensureDbInitialized()` to have run first.
|
||||
The server startup orchestrator awaits it automatically. In tests or custom
|
||||
embeddings, call `await ensureDbInitialized()` before any `src/lib/db/*`
|
||||
import that triggers instance access.
|
||||
|
||||
## Migration guide
|
||||
|
||||
For existing installations:
|
||||
|
||||
```bash
|
||||
npm install -g omniroute@3.8.0
|
||||
omniroute setup # optional — re-runs onboarding to show new tier tour
|
||||
```
|
||||
|
||||
CLI tokens are derived automatically; no migration needed.
|
||||
To enable tray: `omniroute --tray` or `omniroute config tray enable`.
|
||||
|
||||
For developers embedding OmniRoute:
|
||||
|
||||
- Replace `getDbInstance()` direct calls with `await ensureDbInitialized(); getDbInstance()`
|
||||
|
||||
## Contributors
|
||||
|
||||
This release was informed by deep analysis of the `9router` project
|
||||
(decolua/9router). Their tray, SHARED_BOUNDARIES, and tier marketing
|
||||
patterns were adapted to OmniRoute's TypeScript infrastructure.
|
||||
@@ -205,6 +205,37 @@ Including the bare `auto` (default) plus the 6 `AutoVariant` values declared in
|
||||
|
||||
(`AutoVariant` itself enumerates 6 values; the 7th option is "no variant" — bare `auto` — handled by `parseAutoPrefix()` as `variant: undefined`.)
|
||||
|
||||
## How tiers fit Auto-Combo
|
||||
|
||||
The 9-factor scoring function (`open-sse/services/autoCombo/scoring.ts`) treats tier
|
||||
membership as one signal via the `tierPriority` weight. Default weights (from `DEFAULT_WEIGHTS`):
|
||||
|
||||
| Factor | Default weight | Notes |
|
||||
| ------------------------ | -------------- | --------------------------------- |
|
||||
| Tier priority | 0.05 | Tier 1 premium → higher score |
|
||||
| Latency (p50 inverse) | 0.35 | Fastest wins |
|
||||
| Cost ($/1M inverse) | 0.20 | Cheapest wins |
|
||||
| Recent health/error rate | 0.15 | Unhealthy deprioritized |
|
||||
| Quota remaining | 0.10 | Near-exhausted deprioritized |
|
||||
| Context window match | 0.08 | Penalizes short windows |
|
||||
| Task fitness | 0.10 | Coding → coding-specialist models |
|
||||
| Stability | 0.00 | Disabled by default |
|
||||
|
||||
Tier alone does **not** force Tier 1 first — if Tier 1 latency is bad or
|
||||
cost-vs-quality is suboptimal, Tier 2 wins. To force tier ordering, use combo
|
||||
strategy `priority` and arrange providers by tier.
|
||||
|
||||
To strongly favor Tier 1 (subscription), increase `tierPriority` weight:
|
||||
|
||||
```json
|
||||
{
|
||||
"strategy": "auto",
|
||||
"config": { "auto": { "weights": { "tierPriority": 0.3, "costInv": 0.05 } } }
|
||||
}
|
||||
```
|
||||
|
||||
See `docs/marketing/TIERS.md` for tier definitions and provider classification.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|
||||
66
images/tier-flow-dark.svg
Normal file
66
images/tier-flow-dark.svg
Normal file
@@ -0,0 +1,66 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 420" font-family="system-ui, -apple-system, sans-serif">
|
||||
<defs>
|
||||
<marker id="arrow-dark" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#9ca3af"/>
|
||||
</marker>
|
||||
<marker id="arrow-fallback-dark" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#f87171"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="800" height="420" fill="#111827" rx="12"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="400" y="32" text-anchor="middle" font-size="18" font-weight="700" fill="#f9fafb">OmniRoute 3-tier fallback</text>
|
||||
<text x="400" y="52" text-anchor="middle" font-size="12" fill="#6b7280">Never stop building — automatic zero-config failover across 207+ providers</text>
|
||||
|
||||
<!-- Client box -->
|
||||
<rect x="275" y="70" width="250" height="52" rx="8" fill="#1f2937" stroke="#374151" stroke-width="1.5"/>
|
||||
<text x="400" y="97" text-anchor="middle" font-size="13" font-weight="600" fill="#f3f4f6">Your IDE / CLI / App</text>
|
||||
<text x="400" y="114" text-anchor="middle" font-size="10" fill="#6b7280">Claude Code · Cursor · Cline · Copilot · JetBrains AI</text>
|
||||
|
||||
<!-- Arrow down to router -->
|
||||
<line x1="400" y1="122" x2="400" y2="150" stroke="#9ca3af" stroke-width="1.5" marker-end="url(#arrow-dark)"/>
|
||||
<text x="420" y="140" font-size="9" fill="#6b7280">localhost:20128/v1</text>
|
||||
|
||||
<!-- Router box -->
|
||||
<rect x="200" y="152" width="400" height="72" rx="8" fill="#1e3a5f" stroke="#3b82f6" stroke-width="1.5"/>
|
||||
<text x="400" y="177" text-anchor="middle" font-size="14" font-weight="700" fill="#93c5fd">OmniRoute Smart Router</text>
|
||||
<text x="400" y="196" text-anchor="middle" font-size="10" fill="#60a5fa">RTK compression · Caveman terse-mode · Auto-Combo (14 strategies)</text>
|
||||
<text x="400" y="213" text-anchor="middle" font-size="10" fill="#60a5fa">Circuit breaker · MCP server (37 tools) · A2A · Memory · Guardrails</text>
|
||||
|
||||
<!-- Arrows from router to tiers -->
|
||||
<line x1="310" y1="224" x2="150" y2="285" stroke="#9ca3af" stroke-width="1.5" marker-end="url(#arrow-dark)"/>
|
||||
<line x1="400" y1="224" x2="400" y2="285" stroke="#9ca3af" stroke-width="1.5" marker-end="url(#arrow-dark)"/>
|
||||
<line x1="490" y1="224" x2="650" y2="285" stroke="#9ca3af" stroke-width="1.5" marker-end="url(#arrow-dark)"/>
|
||||
|
||||
<!-- Tier 1: Subscription -->
|
||||
<rect x="30" y="287" width="220" height="110" rx="8" fill="#1c1a00" stroke="#d97706" stroke-width="1.5"/>
|
||||
<text x="140" y="310" text-anchor="middle" font-size="11" font-weight="700" fill="#fbbf24">Tier 1 — SUBSCRIPTION</text>
|
||||
<text x="140" y="328" text-anchor="middle" font-size="10" fill="#d97706">Pay flat-rate, use every drop</text>
|
||||
<text x="140" y="346" text-anchor="middle" font-size="9.5" fill="#fbbf24">Claude Code · Codex · Copilot</text>
|
||||
<text x="140" y="362" text-anchor="middle" font-size="9.5" fill="#fbbf24">Cursor · Antigravity · Windsurf</text>
|
||||
<text x="140" y="387" text-anchor="middle" font-size="9" fill="#f59e0b">quota exhausted → Tier 2</text>
|
||||
<line x1="140" y1="378" x2="300" y2="378" stroke="#f87171" stroke-width="1" stroke-dasharray="4,2" marker-end="url(#arrow-fallback-dark)"/>
|
||||
|
||||
<!-- Tier 2: Cheap -->
|
||||
<rect x="290" y="287" width="220" height="110" rx="8" fill="#052e16" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<text x="400" y="310" text-anchor="middle" font-size="11" font-weight="700" fill="#4ade80">Tier 2 — CHEAP</text>
|
||||
<text x="400" y="328" text-anchor="middle" font-size="10" fill="#22c55e">Pay-per-token, under $1/1M</text>
|
||||
<text x="400" y="346" text-anchor="middle" font-size="9.5" fill="#4ade80">DeepSeek $0.27 · GLM $0.60</text>
|
||||
<text x="400" y="362" text-anchor="middle" font-size="9.5" fill="#4ade80">MiniMax $0.20 · Qwen $0.30</text>
|
||||
<text x="400" y="387" text-anchor="middle" font-size="9" fill="#16a34a">budget hit → Tier 3</text>
|
||||
<line x1="400" y1="378" x2="560" y2="378" stroke="#f87171" stroke-width="1" stroke-dasharray="4,2" marker-end="url(#arrow-fallback-dark)"/>
|
||||
|
||||
<!-- Tier 3: Free -->
|
||||
<rect x="550" y="287" width="220" height="110" rx="8" fill="#1e1b4b" stroke="#6366f1" stroke-width="1.5"/>
|
||||
<text x="660" y="310" text-anchor="middle" font-size="11" font-weight="700" fill="#a5b4fc">Tier 3 — FREE</text>
|
||||
<text x="660" y="328" text-anchor="middle" font-size="10" fill="#818cf8">Free tiers & credit programs</text>
|
||||
<text x="660" y="346" text-anchor="middle" font-size="9.5" fill="#a5b4fc">Kiro · OpenCode · Qoder</text>
|
||||
<text x="660" y="362" text-anchor="middle" font-size="9.5" fill="#a5b4fc">Gemini CLI · Vertex $300cr</text>
|
||||
<text x="660" y="387" text-anchor="middle" font-size="9" fill="#6366f1">always available</text>
|
||||
|
||||
<!-- Footer note -->
|
||||
<text x="400" y="413" text-anchor="middle" font-size="9" fill="#4b5563">Fallback happens in milliseconds — transparent to the calling tool</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
66
images/tier-flow-light.svg
Normal file
66
images/tier-flow-light.svg
Normal file
@@ -0,0 +1,66 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 420" font-family="system-ui, -apple-system, sans-serif">
|
||||
<defs>
|
||||
<marker id="arrow-light" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-fallback-light" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#ef4444"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="800" height="420" fill="#f9fafb" rx="12"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="400" y="32" text-anchor="middle" font-size="18" font-weight="700" fill="#111827">OmniRoute 3-tier fallback</text>
|
||||
<text x="400" y="52" text-anchor="middle" font-size="12" fill="#6b7280">Never stop building — automatic zero-config failover across 207+ providers</text>
|
||||
|
||||
<!-- Client box -->
|
||||
<rect x="275" y="70" width="250" height="52" rx="8" fill="#ffffff" stroke="#d1d5db" stroke-width="1.5"/>
|
||||
<text x="400" y="97" text-anchor="middle" font-size="13" font-weight="600" fill="#374151">Your IDE / CLI / App</text>
|
||||
<text x="400" y="114" text-anchor="middle" font-size="10" fill="#9ca3af">Claude Code · Cursor · Cline · Copilot · JetBrains AI</text>
|
||||
|
||||
<!-- Arrow down to router -->
|
||||
<line x1="400" y1="122" x2="400" y2="150" stroke="#6b7280" stroke-width="1.5" marker-end="url(#arrow-light)"/>
|
||||
<text x="420" y="140" font-size="9" fill="#9ca3af">localhost:20128/v1</text>
|
||||
|
||||
<!-- Router box -->
|
||||
<rect x="200" y="152" width="400" height="72" rx="8" fill="#dbeafe" stroke="#3b82f6" stroke-width="1.5"/>
|
||||
<text x="400" y="177" text-anchor="middle" font-size="14" font-weight="700" fill="#1d4ed8">OmniRoute Smart Router</text>
|
||||
<text x="400" y="196" text-anchor="middle" font-size="10" fill="#3b82f6">RTK compression · Caveman terse-mode · Auto-Combo (14 strategies)</text>
|
||||
<text x="400" y="213" text-anchor="middle" font-size="10" fill="#3b82f6">Circuit breaker · MCP server (37 tools) · A2A · Memory · Guardrails</text>
|
||||
|
||||
<!-- Arrows from router to tiers -->
|
||||
<line x1="310" y1="224" x2="150" y2="285" stroke="#6b7280" stroke-width="1.5" marker-end="url(#arrow-light)"/>
|
||||
<line x1="400" y1="224" x2="400" y2="285" stroke="#6b7280" stroke-width="1.5" marker-end="url(#arrow-light)"/>
|
||||
<line x1="490" y1="224" x2="650" y2="285" stroke="#6b7280" stroke-width="1.5" marker-end="url(#arrow-light)"/>
|
||||
|
||||
<!-- Tier 1: Subscription -->
|
||||
<rect x="30" y="287" width="220" height="110" rx="8" fill="#fffbeb" stroke="#f59e0b" stroke-width="1.5"/>
|
||||
<text x="140" y="310" text-anchor="middle" font-size="11" font-weight="700" fill="#92400e">Tier 1 — SUBSCRIPTION</text>
|
||||
<text x="140" y="328" text-anchor="middle" font-size="10" fill="#78350f">Pay flat-rate, use every drop</text>
|
||||
<text x="140" y="346" text-anchor="middle" font-size="9.5" fill="#92400e">Claude Code · Codex · Copilot</text>
|
||||
<text x="140" y="362" text-anchor="middle" font-size="9.5" fill="#92400e">Cursor · Antigravity · Windsurf</text>
|
||||
<text x="140" y="387" text-anchor="middle" font-size="9" fill="#b45309">quota exhausted → Tier 2</text>
|
||||
<line x1="140" y1="378" x2="300" y2="378" stroke="#ef4444" stroke-width="1" stroke-dasharray="4,2" marker-end="url(#arrow-fallback-light)"/>
|
||||
|
||||
<!-- Tier 2: Cheap -->
|
||||
<rect x="290" y="287" width="220" height="110" rx="8" fill="#f0fdf4" stroke="#22c55e" stroke-width="1.5"/>
|
||||
<text x="400" y="310" text-anchor="middle" font-size="11" font-weight="700" fill="#14532d">Tier 2 — CHEAP</text>
|
||||
<text x="400" y="328" text-anchor="middle" font-size="10" fill="#166534">Pay-per-token, under $1/1M</text>
|
||||
<text x="400" y="346" text-anchor="middle" font-size="9.5" fill="#14532d">DeepSeek $0.27 · GLM $0.60</text>
|
||||
<text x="400" y="362" text-anchor="middle" font-size="9.5" fill="#14532d">MiniMax $0.20 · Qwen $0.30</text>
|
||||
<text x="400" y="387" text-anchor="middle" font-size="9" fill="#15803d">budget hit → Tier 3</text>
|
||||
<line x1="400" y1="378" x2="560" y2="378" stroke="#ef4444" stroke-width="1" stroke-dasharray="4,2" marker-end="url(#arrow-fallback-light)"/>
|
||||
|
||||
<!-- Tier 3: Free -->
|
||||
<rect x="550" y="287" width="220" height="110" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
|
||||
<text x="660" y="310" text-anchor="middle" font-size="11" font-weight="700" fill="#312e81">Tier 3 — FREE</text>
|
||||
<text x="660" y="328" text-anchor="middle" font-size="10" fill="#3730a3">Free tiers & credit programs</text>
|
||||
<text x="660" y="346" text-anchor="middle" font-size="9.5" fill="#312e81">Kiro · OpenCode · Qoder</text>
|
||||
<text x="660" y="362" text-anchor="middle" font-size="9.5" fill="#312e81">Gemini CLI · Vertex $300cr</text>
|
||||
<text x="660" y="387" text-anchor="middle" font-size="9" fill="#4338ca">always available</text>
|
||||
|
||||
<!-- Footer note -->
|
||||
<text x="400" y="413" text-anchor="middle" font-size="9" fill="#9ca3af">Fallback happens in milliseconds — transparent to the calling tool</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
@@ -12,6 +12,7 @@ import { AI_PROVIDERS, FREE_PROVIDERS, OAUTH_PROVIDERS } from "@/shared/constant
|
||||
import { useNotificationStore } from "@/store/notificationStore";
|
||||
import { copyToClipboard } from "@/shared/utils/clipboard";
|
||||
import type { NewsAnnouncement } from "@/shared/utils/releaseNotes";
|
||||
import { TierCoverageWidget } from "./TierCoverageWidget";
|
||||
|
||||
type UpdateStep = {
|
||||
step: string;
|
||||
@@ -748,6 +749,9 @@ export default function HomePageClient({ machineId }: HomePageClientProps) {
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Tier Coverage */}
|
||||
<TierCoverageWidget />
|
||||
|
||||
{/* Providers Overview */}
|
||||
<Card>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
|
||||
100
src/app/(dashboard)/dashboard/TierCoverageWidget.tsx
Normal file
100
src/app/(dashboard)/dashboard/TierCoverageWidget.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { FREE_PROVIDERS, OAUTH_PROVIDERS } from "@/shared/constants/providers";
|
||||
|
||||
type TierCount = { configured: number; active: number };
|
||||
type Coverage = { tier1: TierCount; tier2: TierCount; tier3: TierCount };
|
||||
|
||||
const FREE_IDS = new Set(Object.keys(FREE_PROVIDERS));
|
||||
const OAUTH_IDS = new Set(Object.keys(OAUTH_PROVIDERS));
|
||||
|
||||
function classifyConnection(providerId: string): "tier1" | "tier2" | "tier3" {
|
||||
if (FREE_IDS.has(providerId)) return "tier3";
|
||||
if (OAUTH_IDS.has(providerId)) return "tier1";
|
||||
return "tier2";
|
||||
}
|
||||
|
||||
const TIER_LABELS: Record<string, string> = {
|
||||
tier1: "Subscription",
|
||||
tier2: "Cheap",
|
||||
tier3: "Free",
|
||||
};
|
||||
const TIER_COLORS: Record<string, string> = {
|
||||
tier1: "text-amber-500",
|
||||
tier2: "text-green-500",
|
||||
tier3: "text-indigo-400",
|
||||
};
|
||||
|
||||
export function TierCoverageWidget() {
|
||||
const t = useTranslations("common");
|
||||
const [coverage, setCoverage] = useState<Coverage | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/providers")
|
||||
.then((r) => r.json())
|
||||
.then((data) => {
|
||||
const connections: { provider: string; isActive: boolean }[] = data.connections ?? [];
|
||||
const counts: Coverage = {
|
||||
tier1: { configured: 0, active: 0 },
|
||||
tier2: { configured: 0, active: 0 },
|
||||
tier3: { configured: 0, active: 0 },
|
||||
};
|
||||
for (const conn of connections) {
|
||||
const tier = classifyConnection(conn.provider);
|
||||
counts[tier].configured++;
|
||||
if (conn.isActive) counts[tier].active++;
|
||||
}
|
||||
setCoverage(counts);
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
if (!coverage) return null;
|
||||
|
||||
const tiers = (["tier1", "tier2", "tier3"] as const).map((k) => ({
|
||||
key: k,
|
||||
label: TIER_LABELS[k],
|
||||
colorClass: TIER_COLORS[k],
|
||||
...coverage[k],
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-white/[0.06] bg-surface p-5">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h3 className="font-semibold text-sm">Tier coverage</h3>
|
||||
<p className="text-xs text-text-muted mt-0.5">Providers configured per fallback tier</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/dashboard/providers"
|
||||
className="text-xs text-text-muted hover:text-text-main transition-colors"
|
||||
>
|
||||
Manage →
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{tiers.map(({ key, label, colorClass, configured, active }) => (
|
||||
<div key={key} className="text-center">
|
||||
<div className={`text-2xl font-bold ${colorClass}`}>{active}</div>
|
||||
<div className="text-xs text-text-muted mt-0.5">{label}</div>
|
||||
{configured > 0 && active < configured && (
|
||||
<div className="text-xs text-amber-500 mt-0.5">{configured - active} inactive</div>
|
||||
)}
|
||||
{configured === 0 && (
|
||||
<Link
|
||||
href="/dashboard/providers/new"
|
||||
className="text-xs text-blue-400 underline mt-0.5 block"
|
||||
>
|
||||
{t("add")}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
"use client";
|
||||
|
||||
import { useTheme } from "next-themes";
|
||||
import Image from "next/image";
|
||||
|
||||
export function TierFlowDiagram() {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const src =
|
||||
resolvedTheme === "dark" ? "/images/tier-flow-dark.svg" : "/images/tier-flow-light.svg";
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-3 my-4">
|
||||
<Image
|
||||
src={src}
|
||||
alt="OmniRoute 3-tier fallback diagram"
|
||||
width={800}
|
||||
height={420}
|
||||
priority
|
||||
className="w-full max-w-2xl rounded-lg border border-white/[0.06]"
|
||||
/>
|
||||
<p className="text-xs text-text-muted max-w-xl text-center">
|
||||
Requests flow through your subscription quotas first, then pay-per-token cheap providers,
|
||||
then free-tier providers — automatic, zero-config.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,9 +4,10 @@ import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useDisplayBaseUrl } from "@/shared/hooks";
|
||||
import { TierTour } from "./steps/TierTour";
|
||||
|
||||
const STEP_IDS = ["welcome", "security", "provider", "test", "done"];
|
||||
const STEP_ICONS = ["waving_hand", "lock", "dns", "play_circle", "check_circle"];
|
||||
const STEP_IDS = ["welcome", "tiers", "security", "provider", "test", "done"];
|
||||
const STEP_ICONS = ["waving_hand", "layers", "lock", "dns", "play_circle", "check_circle"];
|
||||
|
||||
const COMMON_PROVIDERS = [
|
||||
{ id: "openai", name: "OpenAI", color: "#10A37F" },
|
||||
@@ -301,6 +302,9 @@ export default function OnboardingWizard() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tiers */}
|
||||
{currentStep.id === "tiers" && <TierTour />}
|
||||
|
||||
{/* Security */}
|
||||
{currentStep.id === "security" && (
|
||||
<div className="space-y-4">
|
||||
@@ -465,6 +469,14 @@ export default function OnboardingWizard() {
|
||||
{t("getStarted")}
|
||||
</button>
|
||||
)}
|
||||
{currentStep.id === "tiers" && (
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className="px-6 py-2.5 bg-primary rounded-lg text-white font-medium text-sm hover:bg-primary/90 transition-colors cursor-pointer"
|
||||
>
|
||||
{t("continue")}
|
||||
</button>
|
||||
)}
|
||||
{currentStep.id === "security" && (
|
||||
<button
|
||||
onClick={handleSetPassword}
|
||||
|
||||
75
src/app/(dashboard)/dashboard/onboarding/steps/TierTour.tsx
Normal file
75
src/app/(dashboard)/dashboard/onboarding/steps/TierTour.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { TierFlowDiagram } from "../components/TierFlowDiagram";
|
||||
|
||||
type TierCardProps = {
|
||||
number: number;
|
||||
colorClass: string;
|
||||
label: string;
|
||||
description: string;
|
||||
examples: string[];
|
||||
};
|
||||
|
||||
function TierCard({ number, colorClass, label, description, examples }: TierCardProps) {
|
||||
return (
|
||||
<div className={`p-4 rounded-xl border-2 ${colorClass}`}>
|
||||
<div className="flex items-baseline gap-2 mb-2">
|
||||
<span className="text-2xl font-bold">{number}</span>
|
||||
<span className="font-semibold text-sm">{label}</span>
|
||||
</div>
|
||||
<p className="text-xs text-text-muted mb-3">{description}</p>
|
||||
<ul className="text-xs space-y-0.5 text-text-muted">
|
||||
{examples.map((e) => (
|
||||
<li key={e}>· {e}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TierTour() {
|
||||
const t = useTranslations("onboarding.tier");
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-text-muted">{t("subtitle")}</p>
|
||||
</div>
|
||||
|
||||
<TierFlowDiagram />
|
||||
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
||||
<TierCard
|
||||
number={1}
|
||||
colorClass="border-amber-500/60 bg-amber-500/[0.06]"
|
||||
label={t("tier1.label")}
|
||||
description={t("tier1.description")}
|
||||
examples={["Claude Code", "Codex", "Copilot", "Cursor"]}
|
||||
/>
|
||||
<TierCard
|
||||
number={2}
|
||||
colorClass="border-green-500/60 bg-green-500/[0.06]"
|
||||
label={t("tier2.label")}
|
||||
description={t("tier2.description")}
|
||||
examples={["DeepSeek", "GLM", "MiniMax", "Qwen"]}
|
||||
/>
|
||||
<TierCard
|
||||
number={3}
|
||||
colorClass="border-indigo-500/60 bg-indigo-500/[0.06]"
|
||||
label={t("tier3.label")}
|
||||
description={t("tier3.description")}
|
||||
examples={["Kiro", "OpenCode", "Gemini CLI", "Vertex"]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-text-muted/60 text-center">
|
||||
<Link href="/dashboard/providers/new" className="underline hover:text-text-muted">
|
||||
{t("configure")}
|
||||
</Link>{" "}
|
||||
after setup.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2585,8 +2585,27 @@
|
||||
"onboarding": {
|
||||
"welcome": "Welcome",
|
||||
"security": "Security",
|
||||
"tiers": "How It Works",
|
||||
"test": "Test",
|
||||
"ready": "Ready!",
|
||||
"tier": {
|
||||
"title": "How OmniRoute saves you money",
|
||||
"subtitle": "Three tiers, automatic fallback — never stop building.",
|
||||
"tier1": {
|
||||
"label": "Subscription",
|
||||
"description": "Use what you already pay for — every drop of quota before it resets."
|
||||
},
|
||||
"tier2": {
|
||||
"label": "Cheap",
|
||||
"description": "Pay-per-token under $1/1M — kicks in when subscription quotas hit."
|
||||
},
|
||||
"tier3": {
|
||||
"label": "Free",
|
||||
"description": "Generous free tiers and credit programs — final safety net."
|
||||
},
|
||||
"continue": "Continue",
|
||||
"configure": "Configure tiers now"
|
||||
},
|
||||
"setPassword": "Set Password",
|
||||
"addProvider": "Add your first provider",
|
||||
"getStarted": "Get Started",
|
||||
|
||||
Reference in New Issue
Block a user