Compare commits

..

116 Commits

Author SHA1 Message Date
Diego Rodrigues de Sa e Souza a1137c1fa1 Merge pull request #48 from diegosouzapw/style/add-website-url
v0.8.0 — Website launch, SECURITY.md audit, docs improvements
2026-02-16 14:33:46 -03:00
diegosouzapw e962e4f558 chore: bump version to 0.8.0
- Website launch (omniroute.online)
- Comprehensive SECURITY.md
- Documentation improvements
- Updated provider count to 36+
2026-02-16 14:33:13 -03:00
diegosouzapw 6d3b4166df docs: remove all Architecture Decision Record (ADR) documents. 2026-02-16 14:31:35 -03:00
diegosouzapw 45eced0b3e docs: comprehensive SECURITY.md based on full codebase audit
Documented all security features actually implemented:
- AES-256-GCM encryption at rest (API keys, tokens)
- Prompt injection guard (5 pattern types)
- PII redaction (email, CPF, CNPJ, CC, phone, SSN)
- Secrets validator (fail-fast on weak values)
- Circuit breaker (3-state, SQLite-persisted)
- OAuth 2.0 + PKCE, CORS, IP filtering
- Rate limiting, anti-thundering herd
- Compliance (log retention, audit log, no-log)
2026-02-16 14:29:50 -03:00
diegosouzapw 1b00a6b70b docs: update SECURITY.md and add to README
- Bump supported versions: 0.7.x (active), 0.6.x (security)
- Replace fake email with GitHub Security Advisories link
- Add Security Policy to README documentation table
2026-02-16 14:27:17 -03:00
diegosouzapw 03d05e0490 docs: track USER_GUIDE, API_REFERENCE, and TROUBLESHOOTING in git
These files were referenced in README but ignored by .gitignore.
Added them to the exception list so they appear in the repository.
2026-02-16 14:25:52 -03:00
diegosouzapw 4cac582cb0 docs: add omniroute.online website URL to README, npm, and Docker
- Add Website badge to README header
- Add website link to nav, tech stack, support, and footer
- Update provider count from 28 to 36+
- Set package.json homepage to https://omniroute.online
- Add OCI image URL label to Dockerfile
2026-02-16 14:22:29 -03:00
Diego Rodrigues de Sa e Souza eb0fbccd05 Merge pull request #47 from diegosouzapw/fix/vm-onboarding-auth
fix: auto-skip onboarding and enforce login on VM deployments
2026-02-16 11:25:06 -03:00
diegosouzapw 32c0f40021 fix: auto-skip onboarding and enforce login on VM deployments
- getSettings() auto-sets setupComplete=true when INITIAL_PASSWORD env is set
- proxy.js middleware enforces login when INITIAL_PASSWORD exists (even without DB hash)
- Settings API reports hasPassword=true when INITIAL_PASSWORD is configured
- Login page hides '123456' default hint when a custom password is set

Fixes fresh Docker/VM deployments getting stuck on onboarding wizard
and being accessible without authentication.
2026-02-16 11:24:15 -03:00
Diego Rodrigues de Sa e Souza baae44f825 Merge pull request #46 from diegosouzapw/feature/release-0.7.0
chore: release v0.7.0 — Docker Hub, changelog overhaul
2026-02-16 09:49:07 -03:00
diegosouzapw 5fe53168b2 chore: bump version to 0.7.0, reorganize changelog, add Docker Hub CI/CD and README
- Bump version from 0.6.0 to 0.7.0 in package.json
- Rewrite CHANGELOG.md with comprehensive entries from v0.1.0 to v0.7.0
- Add docker-publish.yml GitHub Actions workflow for auto Docker Hub push
- Add Docker Hub badge, Docker section, and Docker Hub link to README
- Update release command example to v0.7.0
2026-02-16 09:48:35 -03:00
diegosouzapw ebdc1c214d refactor: extract CliStatusBadge component and refactor CLI tool cards
- Create shared CliStatusBadge component with support for configured,
  not_configured, not_installed, other, and unknown statuses
- Replace inline status badge markup in ClaudeToolCard and ClineToolCard
  with the new reusable component
- Add colored status dot indicator alongside badge text
- Support batch status fallback so badges render even when cards are collapsed
- Refactor model/provider selection logic in tool card configuration
2026-02-16 01:35:57 -03:00
Diego Rodrigues de Sa e Souza 467e998650 Merge pull request #45 from diegosouzapw/fix/cloud-connection-ux
fix(cloud): improve cloud connection UX
2026-02-16 01:03:23 -03:00
diegosouzapw 9a9f592f54 fix(cloud): improve cloud connection UX with GET status, toast feedback, and sidebar indicator
- Add GET handler to /api/sync/cloud for real-time status polling
- Rewrite CloudSyncStatus: clickable, event-driven updates, clearer labels
- Add inline status toast with auto-dismiss after enable/disable
- Show success animation in modal before auto-closing
- Dispatch cloud-status-changed event for instant sidebar updates
2026-02-16 01:02:52 -03:00
Diego Rodrigues de Sa e Souza c3fe96e221 Merge pull request #44 from diegosouzapw/feature/release-0.6.0
chore: release v0.6.0
2026-02-16 00:23:19 -03:00
diegosouzapw 1c6541f25d chore: bump version to 0.6.0 and update changelog 2026-02-16 00:22:55 -03:00
diegosouzapw 9f8dfa1398 feat: add costs page and enhance health provider status display
Add dedicated Costs page combining Budget and Pricing tabs with a
sidebar navigation layout. Improve Health page provider section with
status legend (Healthy/Recovering/Down) and import AI_PROVIDERS
constants for richer provider display.
2026-02-15 23:01:27 -03:00
diegosouzapw e4db9bff0e feat: add provider metrics display and model import for passthrough providers
- Fetch and display request metrics (success rate, latency, total requests) on provider overview cards
- Create /api/provider-metrics endpoint to aggregate stats from request logs
- Add /api/providers/[id]/import-models endpoint to import models from provider's /models API
- Add "Import from /models" button on passthrough provider detail pages
- Include proper PropTypes for new metrics and alias fields
2026-02-15 21:53:27 -03:00
diegosouzapw 54aba4c087 feat: redesign app icons with network node graph and new color scheme
Replace text-based "9" favicon with a network/router node graph design
across all icon sizes. Add apple-touch-icon and icon-192 SVG assets.
Update brand gradient from orange (#f97815) to red (#E54D5E).
2026-02-15 20:56:48 -03:00
Diego Rodrigues de Sa e Souza c2542b75f6 Merge pull request #43 from diegosouzapw/feature/release-0.5.0
chore: release v0.5.0
2026-02-15 19:03:37 -03:00
diegosouzapw 6572f2a1b6 chore: bump version to 0.5.0 and update changelog 2026-02-15 19:03:18 -03:00
Diego Rodrigues de Sa e Souza 2ad23f3b3c Merge pull request #42 from diegosouzapw/feature/ajustes-cosmeticos
feat: v0.5.0 - Dashboard refinements, evals framework, and combo strategies
2026-02-15 18:57:29 -03:00
diegosouzapw fa9687ae0d feat: Implement API key usage for EvalsTab LLM calls, enhance model filtering with provider aliases, and remove the random routing strategy option. 2026-02-15 18:56:33 -03:00
diegosouzapw 0ed7738c7c feat: add LLM evaluation framework with golden set testing
Introduce built-in eval system for testing LLM response quality against
golden sets. Includes eval runner with 4 match strategies (exact,
contains, regex, custom), a pre-loaded 10-case golden set, REST API
endpoints for managing and running suites, and a dashboard UI under
Analytics → Evals with real-time progress tracking and pass rate
summaries. Also adds OpenAPI tags for Audio, Moderations, and Rerank.
2026-02-15 18:37:18 -03:00
diegosouzapw a43b1c9218 feat: add random, least-used, and cost-optimized combo strategies
Extend combo routing with 3 new model selection strategies beyond the
existing priority, weighted, and round-robin options:
- random: Fisher-Yates shuffle for uniform distribution
- least-used: sorts models by request count via comboMetrics
- cost-optimized: sorts models by pricing (cheapest first)

Also enhance the dashboard provider cards with model counts and
clickable detail views for selected providers.
2026-02-15 16:57:00 -03:00
diegosouzapw 413a9f2a69 feat: add ModelAvailabilityBadge component and retheme landing page
- Add compact ModelAvailabilityBadge with popover for model status
  monitoring, cooldown clearing, and auto-refresh polling
- Retheme landing page from warm orange tones to cool blue-grey palette
  across FlowAnimation, Footer, and related components
- Update accent color from orange (#f97815) to rose (#E54D5E)
2026-02-15 16:26:01 -03:00
diegosouzapw 14714aa9f5 feat: refactor dashboard with shared UI component library
Introduce reusable shared components (Button, Card, Modal, Table,
StatusBadge, EmptyState) and refactor dashboard pages to use them.
Extract client components from server pages, centralize provider
constants, and update CI Node.js matrix from 18 to 20.
2026-02-15 16:11:56 -03:00
Diego Rodrigues de Sa e Souza ac4df197c3 Merge pull request #41 from diegosouzapw/fix/chat-completions-then-error
fix(api): resolve TypeError in chat/completions route
2026-02-15 13:25:28 -03:00
diegosouzapw 06826d4c86 fix(api): resolve TypeError in chat/completions ensureInitialized
initTranslators() is a no-op stub (translators self-register via static
imports). Wrapping the call with Promise.resolve() ensures .then() works
even when the function returns undefined instead of a Promise.
2026-02-15 13:24:54 -03:00
Diego Rodrigues de Sa e Souza 78b0782129 Merge pull request #40 from diegosouzapw/fix/ci-lint-build-errors
fix(ci): resolve build and lint failures
2026-02-15 12:56:44 -03:00
diegosouzapw 5c92f05079 fix(ci): resolve build and lint failures
- Remove non-existent DarkTooltip/CostTooltip exports from analytics/index.js
- Fix setState-in-effect in ProxyTab.js (inline fetch in useEffect)
- Fix anonymous default export warning in prettier.config.mjs
- Remove @ts-check from promptInjectionGuard.js (SanitizeResult type mismatch)
2026-02-15 12:56:09 -03:00
Diego Rodrigues de Sa e Souza 16cdb345a0 Merge pull request #39 from diegosouzapw/feature/v0.4.0-action-items
feat: implement 26 action items + bump v0.4.0
2026-02-15 12:34:45 -03:00
diegosouzapw 0e238a61fb feat(core): implement 26 action items from critical analysis + bump v0.4.0
- Security: AES-256-GCM encryption for API keys/tokens, CI security audit
- Accessibility: ARIA labels, aria-live regions, skip-to-content, contrast utility
- Components: Tooltip, CloudSyncStatus, SystemMonitor, StreamTracker
- Utils: costEstimator, promptInjectionGuard middleware, Zod validation schemas
- Docs: openapi.yaml +9 routes, API_REFERENCE internal APIs, version bumps
- Quality: coverage thresholds 60/50/50, error handling improvements
2026-02-15 12:33:56 -03:00
Diego Rodrigues de Sa e Souza 6964904bac Merge pull request #38 from diegosouzapw/feature/phase10-docs-ux-cleanup
feat(phase10): docs restructuring, component decomposition, and cleanup
2026-02-15 11:54:27 -03:00
diegosouzapw 597c590a1d feat(phase10): docs restructuring, component decomposition, and cleanup
- Split README (1326→211 lines): USER_GUIDE, API_REFERENCE, TROUBLESHOOTING
- Expand CONTRIBUTING.md (112→273 lines)
- Extract RequestLoggerDetail from RequestLoggerV2 (910→665 lines)
- Extract ProxyLogDetail from ProxyLogger (677→519 lines)
- Add accessibility attrs (aria-label, role=dialog) to extracted modals
- Migrate not-found.js inline styles to TailwindCSS gradient classes
- Remove continue-on-error from E2E CI step
- Add PolicyEngine class with glob-based policy matching
- Create a11yAudit.js (WCAG rule checker)
- Fix policyEngine test import path
2026-02-15 11:53:40 -03:00
Diego Rodrigues de Sa e Souza a94b6a8d8c Merge pull request #37 from diegosouzapw/feat/phase-9-llm-intelligence
feat(gateway): Phase 9 — LLM Gateway Intelligence
2026-02-15 11:05:37 -03:00
diegosouzapw b08fb31a28 feat(gateway): Phase 9 — LLM Gateway Intelligence
9.1 — Semantic Cache
  - New: src/lib/semanticCache.js — Two-tier cache (in-memory LRU + SQLite)
  - Signature = SHA-256(model + normalized messages + temperature + top_p)
  - Only caches non-streaming, temperature=0 requests
  - X-OmniRoute-No-Cache: true header bypass
  - Response headers: X-OmniRoute-Cache: HIT/MISS
  - DB table: semantic_cache with indexes on signature and model
  - New: src/app/api/cache/route.js — GET stats, DELETE clear

9.2 — Request Idempotency
  - New: src/lib/idempotencyLayer.js — In-memory 5s dedup window
  - Reads Idempotency-Key or X-Request-Id headers
  - Returns cached response with X-OmniRoute-Idempotent: true header
  - Ephemeral by design (no SQLite)

9.3 — Progress Tracking in Streaming
  - New: open-sse/utils/progressTracker.js
  - Emits SSE 'event: progress' with tokens_generated + elapsed_ms
  - Opt-in via X-OmniRoute-Progress: true header
  - Supports AbortSignal cancellation
  - Final event includes done: true

Integration:
  - chatCore.js: idempotency check → cache check → provider call → cache store → idempotency save
  - Streaming path: optional progress transform chain
  - DB: semantic_cache table added to db/core.js schema

Tests: 320 pass (+25 new) | Build: success
2026-02-15 11:04:51 -03:00
Diego Rodrigues de Sa e Souza 5bfd621438 Merge pull request #36 from diegosouzapw/feat/phase-8-missing-flows
feat(pages): Phase 8 — Missing Flows & Pages
2026-02-15 10:48:54 -03:00
diegosouzapw 034f1a7e1d feat(pages): Phase 8 — Missing Flows & Pages
8.1 — 403 Forbidden Page
  - New: src/app/forbidden/page.js
  - Gradient code + Access Denied message + Dashboard link
  - Consistent design with not-found.js

8.2 — Password Recovery Flow
  - New: src/app/forgot-password/page.js
  - Two methods: CLI reset + manual database reset
  - Added 'Forgot password?' link to login page

8.3 — Health/Status Dashboard
  - New: src/app/(dashboard)/dashboard/health/page.js
  - New: src/app/api/monitoring/health/route.js
  - Cards: Uptime, Version, Memory, Provider count
  - Provider health (circuit breaker states with color coding)
  - Rate limit status table
  - Active lockouts list
  - Auto-refresh every 15s
  - Added Health nav link to Sidebar

8.4 — Maintenance Banner
  - New: src/shared/components/MaintenanceBanner.js
  - Auto-detects server health issues every 10s
  - Shows/hides automatically, dismissible
  - Wired into DashboardLayout

8.5 — Empty States
  - Verified EmptyState component applied in 6+ pages
  - Providers page has its own empty handling

Bonus:
  - Fixed stale GitHub link in Sidebar (decolua → diegosouzapw)

Tests: 295 pass | Build: success
2026-02-15 10:46:11 -03:00
Diego Rodrigues de Sa e Souza bc95095f82 Merge pull request #35 from diegosouzapw/refactor/phase-7-api-code-quality
feat(api): Phase 7 — API & Code Quality
2026-02-15 10:16:34 -03:00
diegosouzapw 7cf8ae8db6 feat(api): Phase 7 — API & Code Quality
7.1 — Consolidated rate-limit routes
  - Merged rate-limit/ and rate-limits/ into rate-limits/route.js
  - GET returns connections + overview + lockouts + cacheStats (unified)
  - POST handles toggle protection
  - Old rate-limit/ now redirects 308 → rate-limits/
  - Updated frontend fetch URL in providers/[id]/page.js

7.2 — Zod schema for provider constants
  - New: src/shared/validation/providerSchema.js
  - Validates FREE_PROVIDERS, OAUTH_PROVIDERS, APIKEY_PROVIDERS at module load
  - Catches config drift (invalid colors, missing fields) at startup

7.3 — TailwindCSS error pages
  - Converted not-found.js inline styles → Tailwind classes
  - Converted global-error.js inline styles → Tailwind classes
  - Replaced JS hover handlers with Tailwind hover: utilities

7.4 — Fixed GitHub link in privacy page
  - decolua/omniroute → diegosouzapw/OmniRoute

7.5 — Already completed in Phase 5

Tests: 295 pass | Build: success
2026-02-15 10:15:44 -03:00
Diego Rodrigues de Sa e Souza 05e93fa9b0 Merge pull request #34 from diegosouzapw/fix/phase-6-6-sse-dedup-analysis
fix(sse): resolve ghost import in chatHelpers.js (Phase 6.6)
2026-02-15 08:17:00 -03:00
diegosouzapw 1acaf66dd0 fix(sse): resolve ghost import in chatHelpers.js
chatHelpers.js imported detectFormat/getTargetFormat/getModelTargetFormat
from '../services/translator.js' which does not exist.

Fixed to import from canonical sources:
- detectFormat, getTargetFormat → @omniroute/open-sse/services/provider.js
- getModelTargetFormat, PROVIDER_ID_TO_ALIAS → @omniroute/open-sse/config/providerModels.js

Phase 6.6 analysis: src/sse/ is an intentional adapter layer over
open-sse/ (not duplication). No further deduplication needed.
2026-02-15 08:16:26 -03:00
Diego Rodrigues de Sa e Souza 1cee3ec8ff Merge pull request #33 from diegosouzapw/refactor/phase-5-6-architecture
refactor(phase5-6): domain persistence, policy engine, OAuth extraction
2026-02-15 08:12:56 -03:00
diegosouzapw 33c28f73db refactor(phase5-6): domain persistence, policy engine, OAuth extraction, proxy decoupling
Phase 5 — Foundation & Security:
- SQLite domain state persistence (5 tables, 4 modules: fallback, budget, lockout, circuit breaker)
- Write-through cache pattern for state survival across restarts
- Race condition fix in route.js (Promise-based singleton)
- Default password hardening (.env.example)
- Server init error handling improvement

Phase 6 — Architecture Refactoring:
- OAuth providers extracted into 12 individual modules (providers.js 1051→144 lines)
- Policy Engine (lockout→budget→fallback) with evaluateRequest/evaluateFirstAllowed
- Deterministic round-robin via persistent counter Map
- Telemetry window fix with proper recordedAt timestamps
- Proxy decoupled from API settings (direct import vs HTTP self-fetch)

Tests: 295 pass (22 new: domain-persistence 16, policy-engine 6)
Docs: CHANGELOG, README, ARCHITECTURE.md updated
2026-02-15 08:12:12 -03:00
diegosouzapw 25f4f5987c Merge feature/release-0.3.0: docs and changelog for v0.3.0 release 2026-02-15 02:06:36 -03:00
diegosouzapw 4269c25a9f docs: update CHANGELOG, README, and ARCHITECTURE for v0.3.0 release
- CHANGELOG.md: comprehensive v0.3.0 entry with security hardening, domain layer,
  pipeline wiring, 9 API routes, frontend 100% coverage, rate limit overhaul
  (4 phases), ADRs, compliance, eval framework, 273+ tests
- README.md: add 10 new feature rows (circuit breaker, anti-thundering herd,
  resilience profiles/UI, cost budgets, telemetry, correlation IDs, compliance,
  model availability, eval framework), update tech stack and release example
- ARCHITECTURE.md: add domain layer modules, resilience modules, 11 new API
  routes, usageDb decomposition note, update date
2026-02-15 02:06:14 -03:00
diegosouzapw 87b36dc197 Merge feature/rate-limit-overhaul: rate limit overhaul (4 phases) 2026-02-15 01:47:14 -03:00
diegosouzapw 6e29bd1197 feat(resilience): rate limit overhaul — exponential backoff, circuit breaker, anti-thundering herd, Resilience UI
Phase 1: Error classification + provider profiles (constants, providerRegistry, accountFallback)
Phase 2: Circuit breaker integration in combo pipeline (combo.js)
Phase 3: Anti-thundering herd mutex + auto rate limits for API key providers (auth.js, rateLimitManager)
Phase 4: Frontend Resilience tab with Circuit Breaker, Provider Profiles, Rate Limit cards

Tests: 63/63 passing (error-classification, combo-circuit-breaker, thundering-herd, rate-limit-enhanced)
2026-02-15 01:42:50 -03:00
diegosouzapw 5db6676319 feat: detect and handle unrecoverable refresh token errors by marking connections as expired and requiring re-authentication. 2026-02-14 22:04:00 -03:00
Diego Rodrigues de Sa e Souza d08b3889d3 Merge pull request #32 from diegosouzapw/feature/frontend-100-coverage
feat(frontend): 100% backend API coverage — 7 batches
2026-02-14 21:05:32 -03:00
diegosouzapw 443e5b7b62 feat(frontend): 100% backend API coverage — 7 batches
Batch A: Fix Breadcrumbs (usePathname), rewrite ComplianceTab (DataTable+FilterBar+ColumnToggle), delete dead code (a11yAudit.js, policyEngine.js), wire toast notifications (Combos, Providers)
Batch B: ModelAvailabilityPanel — cooldown badges, clear action, auto-refresh
Batch C: BudgetTab — spend cards, progress bars, budget limits form
Batch D: FallbackChainsEditor — color-coded provider chains, create/delete
Batch E: PoliciesPanel — circuit breaker states, locked identifiers, force unlock
Batch F: EvalsTab — expandable suites, run eval, DataTable results
Batch G: TokenHealthBadge + /api/token-health — OAuth health in header

8 new files, 9 modified, 2 deleted. Build passes (exit 0).
2026-02-14 21:04:51 -03:00
Diego Rodrigues de Sa e Souza b87e04db22 Merge pull request #31 from diegosouzapw/feature/cleanup-and-tests
chore: integration wiring tests + branch cleanup
2026-02-14 20:24:01 -03:00
diegosouzapw 4341254b5d chore: add integration wiring tests + branch cleanup
Batch 5 — Cleanup + Tests:
- Delete 4 stale local branches
- Prune 18 stale remote refs
- Add integration-wiring.test.mjs: 44 tests across 12 suites
  verifying all 5 batches of pipeline/API/UI integration

All 44/44 tests pass.
2026-02-14 20:23:46 -03:00
Diego Rodrigues de Sa e Souza 6718b23d57 Merge pull request #30 from diegosouzapw/feature/page-integration
feat(ui): wire budget/telemetry/compliance into dashboard pages
2026-02-14 20:21:28 -03:00
diegosouzapw e3fc9387be feat(ui): wire budget/telemetry/compliance into dashboard pages
Batch 4 — Page Integration:
- Usage page: add BudgetTelemetryCards (latency p50/p95/p99, cache, system health)
- Settings page: add ComplianceTab (audit log), CacheStatsCard (prompt cache + flush)
- Combos page: replace inline empty state with EmptyState component

3 new components, 3 page modifications. Build verified: exit code 0
2026-02-14 20:21:15 -03:00
Diego Rodrigues de Sa e Souza c094c9c678 Merge pull request #29 from diegosouzapw/feature/barrel-exports-layout
feat(ui): export 6 shared components + notificationStore, wire into layout
2026-02-14 20:15:48 -03:00
diegosouzapw 388f06c74f feat(ui): export 6 shared components + notificationStore, wire into layout
Batch 3 — Barrel Exports + Layout:
- shared/components/index.js: export Breadcrumbs, EmptyState, NotificationToast,
  FilterBar, ColumnToggle, DataTable
- store/index.js: export useNotificationStore
- DashboardLayout.js: render <Breadcrumbs/> between Header and content,
  render <NotificationToast/> as global fixed overlay

Build verified: exit code 0
2026-02-14 20:15:33 -03:00
Diego Rodrigues de Sa e Souza 91f6750d59 Merge pull request #28 from diegosouzapw/feature/api-routes
feat(api): create 9 API routes for backend module access
2026-02-14 20:11:42 -03:00
diegosouzapw 7e066df6cd feat(api): create 9 API routes for backend module access
Batch 2 — API Routes:
- /api/cache/stats — GET cache stats, DELETE flush
- /api/models/availability — GET availability report, POST clear cooldown
- /api/telemetry/summary — GET p50/p95/p99 latency metrics
- /api/usage/budget — GET cost summary, POST set budget per key
- /api/fallback/chains — GET/POST/DELETE fallback chain management
- /api/compliance/audit-log — GET filterable audit log
- /api/evals — GET list suites, POST run suite
- /api/evals/[suiteId] — GET suite details
- /api/policies — GET circuit breaker + lockout status, POST force-unlock

Build verified: exit code 0
2026-02-14 20:11:27 -03:00
Diego Rodrigues de Sa e Souza 33f8123835 Merge pull request #27 from diegosouzapw/feature/pipeline-wiring
feat(pipeline): wire 7 backend modules into request pipeline
2026-02-14 20:07:02 -03:00
diegosouzapw e87067f2fb feat(pipeline): wire 7 backend modules into request pipeline
Batch 1 — Pipeline Wiring:
- server-init.js: initialize compliance audit_log, run expired log cleanup, log server.start
- chat.js: wire circuitBreaker (provider resilience), modelAvailability (TTL cooldowns),
  requestTelemetry (7-phase lifecycle), requestId, costRules (budget check/record),
  compliance audit logging. All wiring is non-breaking with try/catch guards.
- proxy.js: replace bare fetch() with fetchWithTimeout (5s timeout on /api/settings),
  add X-Request-Id header for end-to-end tracing
- 307/307 tests pass, build succeeds
2026-02-14 20:06:44 -03:00
diegosouzapw a9a85fdc1b fix: add Record type annotation to getAllFallbackChains result 2026-02-14 19:43:19 -03:00
Diego Rodrigues de Sa e Souza 31c09f08e1 Merge pull request #26 from diegosouzapw/fix/eslint-v9-compatibility
fix: downgrade ESLint v10→v9 for eslint-config-next compatibility
2026-02-14 19:36:19 -03:00
diegosouzapw 3e89560a33 fix: downgrade ESLint v10→v9 for eslint-config-next compatibility
- ESLint 10 broke with scopeManager.addGlobals error (eslint-config-next plugins only support ≤v9)
- Rewrote eslint.config.mjs: removed defineConfig/globalIgnores (ESLint 10-only APIs)
- Now using ESLint 9 flat config format with plain array export
- Fixed TS lint warnings in compliance/index.js and a11yAudit.js
- Added omniroute-reset-password bin entry to package.json
- Lint passes cleanly (1 pre-existing React useState-in-effect warning)
- All 144 tests pass
2026-02-14 19:36:01 -03:00
Diego Rodrigues de Sa e Souza 2dbb717377 Merge pull request #25 from diegosouzapw/feature/batch-b-final-tasks
feat: complete all 46 tasks — Batch B final (T-30, T-33, T-35, T-38, T-39, T-42, T-43)
2026-02-14 19:18:27 -03:00
diegosouzapw f44ec7e1f2 feat: complete all 46 tasks — ADRs, eval framework, compliance, a11y, CLI, Playwright specs (Batch B)
T-30 — ADRs:
- 6 ADRs: SQLite, Fallback Strategy, OAuth, JS+JSDoc, Single-Tenant, Translator Registry

T-33 — JSDoc Coverage:
- Full JSDoc on all new modules (100% exported functions documented)

T-35 — Accessibility:
- a11yAudit.js: lightweight WCAG AA checker (aria-label, dialog role, alt text, labels)

T-38 — Password Reset CLI:
- bin/reset-password.mjs: interactive CLI tool for admin password reset

T-39 — Playwright Specs:
- tests/e2e/responsiveSpecs.mjs: viewports (375/768/1280), 4 pages, test matrix

T-42 — Eval Framework:
- evalRunner.js: 4 strategies (exact, contains, regex, custom) + golden set (10 cases)

T-43 — Compliance:
- audit_log table, noLog opt-out per API key, LOG_RETENTION_DAYS cleanup

TASKS.md: 46/46 Concluído 
Tests: 144/144 pass (119 existing + 25 new)
2026-02-14 19:18:02 -03:00
Diego Rodrigues de Sa e Souza ad003b6226 Merge pull request #24 from diegosouzapw/feature/batch-a-domain-infra
feat: domain layer, error codes, request ID, fetch timeout (Batch A)
2026-02-14 19:04:15 -03:00
diegosouzapw c09978454b feat: domain layer, error codes, request ID, fetch timeout, JSDoc (T-19, T-22, T-23, T-25, T-27)
T-19 — Domain Layer:
- modelAvailability.js: Model availability tracking with TTL cooldowns
- costRules.js: Per-API-key budget management with daily/monthly limits
- fallbackPolicy.js: Declarative fallback chain routing

T-22 — Error Codes Catalog:
- errorCodes.js: 24 codes in 6 categories + createErrorResponse helper

T-23 — Correlation ID:
- requestId.js: AsyncLocalStorage-based x-request-id propagation

T-25 — Fetch Timeout:
- fetchTimeout.js: AbortController wrapper with FETCH_TIMEOUT_MS env var

T-27 — JSDoc + @ts-check:
- Added @ts-check to 8 critical files

TASKS.md updated: 37/46 tasks Concluído, 9 remaining

Tests: 119/119 pass (88 existing + 31 new)
2026-02-14 19:03:55 -03:00
Diego Rodrigues de Sa e Souza daffd6c9ca Merge pull request #23 from diegosouzapw/feature/decomposition-t15-t28-t29
refactor: decompose usageDb, chat handler, UI (T-15, T-28, T-29)
2026-02-14 18:53:34 -03:00
diegosouzapw 492afc4ff1 refactor: decompose usageDb, handleSingleModelChat, UI components (T-15, T-28, T-29)
T-15 — Decompose usageDb.js (969→40 lines):
- Extract src/lib/usage/migrations.js (legacy + JSON→SQLite migration)
- Extract src/lib/usage/usageHistory.js (tracking, pending, log.txt)
- Extract src/lib/usage/costCalculator.js (pure cost calculation)
- Extract src/lib/usage/usageStats.js (dashboard aggregation)
- Extract src/lib/usage/callLogs.js (structured logs, CRUD, rotation)
- usageDb.js is now a thin facade re-exporting all functions

T-28 — Decompose handleSingleModelChat (183→80 lines):
- Extract handleNoCredentials() — credential error responses
- Extract safeResolveProxy() — proxy resolution with error handling
- Extract safeLogEvents() — fire-and-forget proxy + translation logging
- Also created chatHelpers.js with standalone helper exports

T-29 — Extract shared UI primitives (3230 total lines):
- FilterBar.js — search input + filter chips dropdown
- ColumnToggle.js — table column visibility toggle
- DataTable.js — generic data table with sticky header, loading/empty

Tests: 88/88 pass (no regressions)
2026-02-14 18:53:14 -03:00
Diego Rodrigues de Sa e Souza 967689d0a1 Merge pull request #22 from diegosouzapw/feature/remaining-tasks
feat(remaining): deferred tasks — error pages, UX, telemetry, domain
2026-02-14 18:43:30 -03:00
diegosouzapw f77dd89d53 feat(remaining): deferred tasks — error pages, UX components, telemetry, domain extraction
T-20 — .gitignore cleanup:
- Add .analysis/ and antigravity-manager-analysis/ to gitignore
- Whitelist FASE docs, PLANO-IMPLANTACAO.md, TASKS.md

T-21 — Error pages:
- Create not-found.js (404 page with gradient design)
- Create global-error.js (root error boundary with dev details)

T-36 — Breadcrumbs:
- Create Breadcrumbs.js with path-to-label mapping and ARIA semantics

T-37 — Empty states:
- Create EmptyState.js with bounce animation and optional CTA

T-45 — Request telemetry:
- Create requestTelemetry.js (7-phase lifecycle, p50/p95/p99 aggregation)

T-46 — Domain extraction:
- Create comboResolver.js (priority/round-robin/random/least-used strategies)
- Create lockoutPolicy.js (sliding window lockout with force-unlock)

Tests: 13/13 new tests pass (88/88 total)
2026-02-14 18:43:07 -03:00
Diego Rodrigues de Sa e Souza 0a5434cae5 Merge pull request #21 from diegosouzapw/feature/security-hardening
feat(security): FASE-01 to FASE-09 — Security Hardening & Advanced Features
2026-02-14 18:31:31 -03:00
diegosouzapw 2178e99da7 feat(advanced): FASE-07 to FASE-09 advanced features
FASE-07 — UX & Microinteractions:
- Create notificationStore.js (Zustand global toast store)
- Create NotificationToast.js (glassmorphism toast UI with ARIA)

FASE-08 — LLM Proxy Advanced:
- Create policyEngine.js (declarative routing/budget/access policies)
- Create cacheLayer.js (LRU cache with content hashing and TTL)

FASE-09 — E2E Flow Hardening:
- Create streamState.js (SSE stream state machine with TTFB tracking)

Tests: 23/23 advanced tests pass (75/75 total across all suites)
2026-02-14 18:28:55 -03:00
diegosouzapw 1cbbc33f20 feat(security): FASE-01 to FASE-06 security hardening
FASE-01 — Security Hardening:
- Remove hardcoded JWT_SECRET and API_KEY_SECRET fallbacks (fail-fast)
- Create secretsValidator.js with enforceSecrets() at startup
- Create inputSanitizer.js (prompt injection + PII detection)
- Integrate sanitizer in chat.js handler pipeline
- Add structured logging to silent catch blocks in proxy.js
- Remove .passthrough() from Zod updateSettingsSchema
- Remove insecure npm fs dependency
- Update .env.example with generation commands

FASE-02 — CI/CD & Tests:
- Create ci.yml workflow (lint, build, test, coverage, e2e)
- Fix test scripts (test now runs actual tests)
- Add test:unit, test:security, test:coverage (c8), test:all
- Add security rules to ESLint (no-eval, no-implied-eval, no-new-func)

FASE-03 — Architecture:
- Create settingsCache.js (eliminate self-fetch anti-pattern)
- Create domain/types.js and domain/responses.js

FASE-04 — Observability:
- Create correlationId.js (AsyncLocalStorage tracing)
- Create circuitBreaker.js (full state machine + registry)
- Create requestTimeout.js (per-provider timeouts)

FASE-05 — Code Quality:
- Create structuredLogger.js (JSON/human-readable logging)

FASE-06 — Documentation:
- Update SECURITY.md with hardening practices
- Create CONTRIBUTING.md with dev setup and PR checklist

Tests: 52/52 pass (23 security + 15 observability + 14 integration)
2026-02-14 18:21:47 -03:00
Diego Rodrigues de Sa e Souza d408be489c Merge pull request #20 from diegosouzapw/feature/v0.2.0-release
feat: v0.2.0 — advanced routing services, cost analytics, pricing overhaul
2026-02-14 13:51:18 -03:00
diegosouzapw 81a4f2986c feat: v0.2.0 — advanced routing services, cost analytics dashboard, pricing overhaul
Added:
- 8 new open-sse services (account selector, IP filter, session manager, etc.)
- 6 new dashboard settings tabs (IP filter, system prompt, thinking budget, pricing)
- Usage cost dashboard with provider cost donut, cost trend line, model cost column
- Pricing API merging registry + custom + pricing-only models
- 9 unit tests for all new services

Changed:
- Usage analytics layout redesigned with prominent cost display
- DailyTrendChart upgraded to ComposedChart with dual Y-axes

Fixed:
- Pricing page now shows custom/imported models
- Icon rendering (material-symbols-rounded → outlined)
2026-02-14 13:50:45 -03:00
Diego Rodrigues de Sa e Souza c1f1069a06 docs: add npm badge, CLI options table, and automated release section to README (#19)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-02-14 04:47:08 -03:00
Diego Rodrigues de Sa e Souza c4cdb52fe6 fix(ci): reference NPM_TOKEN environment for environment-scoped secrets (#18)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-02-14 04:31:26 -03:00
Diego Rodrigues de Sa e Souza 15ec100dcb fix(ci): write .npmrc explicitly for npm auth — support both secrets and vars (#17)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-02-14 04:26:05 -03:00
Diego Rodrigues de Sa e Souza d401ef40f1 fix(ci): support NPM_TOKEN from both secrets and vars, remove --provenance (#16)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-02-14 04:22:00 -03:00
Diego Rodrigues de Sa e Souza 92b4c59fec feat(npm): add npm package publishing with CLI entry point (#15)
- Add bin/omniroute.mjs CLI with banner, auto-open browser, graceful shutdown
- Add scripts/prepublish.mjs to build Next.js standalone into app/
- Add .github/workflows/npm-publish.yml for automated publish on release
- Update package.json: name=omniroute, bin, files, engines, keywords, prepublishOnly
- Add output: 'standalone' to next.config.mjs
- Add MIT LICENSE
- Update .npmignore and .gitignore for app/ build artifact

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-02-14 04:14:14 -03:00
Diego Rodrigues de Sa e Souza 4f0155eb4a Create SECURITY.md for security policy
Added a security policy document outlining supported versions and vulnerability reporting.
2026-02-13 23:21:18 -03:00
Diego Rodrigues de Sa e Souza e544d81624 Merge pull request #13 from diegosouzapw/dependabot/npm_and_yarn/qs-6.14.2
deps: bump qs from 6.14.1 to 6.14.2
2026-02-13 23:19:59 -03:00
Diego Rodrigues de Sa e Souza b8c50cc62d Merge pull request #14 from diegosouzapw/feature/new-endpoints-and-providers
feat: add new endpoints (rerank, audio, moderations) and providers (Hyperbolic, Deepgram, AssemblyAI, NanoBanana)
2026-02-13 22:58:15 -03:00
diegosouzapw c6000ecc8e feat(providers): add new endpoints (rerank, audio, moderations) and providers (Hyperbolic, Deepgram, AssemblyAI, NanoBanana)
- Add /v1/rerank endpoint with Cohere, Together, NVIDIA, Fireworks
- Add /v1/audio/transcriptions with OpenAI, Groq, Deepgram, AssemblyAI
- Add /v1/audio/speech with OpenAI, Hyperbolic, Deepgram
- Add /v1/moderations with OpenAI
- Add Hyperbolic as chat provider (8 models, OpenAI-compatible)
- Add Hyperbolic image generation (SDXL, SD2, FLUX)
- Add NanoBanana image generation (Flash + Pro via nanobananaapi.ai)
- Add Deepgram STT (Nova 3, Nova 2) with Token auth and binary format
- Add AssemblyAI STT (Universal 3 Pro) with async upload-poll workflow
- Add Deepgram TTS (Aura voices) and Hyperbolic TTS (Melo)
- Update EndpointPageClient to show 7 endpoint sections
- Update /v1/models to return type/subtype for all model categories
- Fix build: remove output:standalone from next.config.mjs
2026-02-13 22:57:32 -03:00
diegosouzapw 4e6ba7d9cc docs: Restructure changelog to Keep a Changelog format and add entries for model selector autocomplete and OpenAPI specification. 2026-02-13 21:35:06 -03:00
diegosouzapw c0c2816671 feat: Implement model selection and dynamic model fetching in ChatTesterMode and TestBenchMode. 2026-02-13 21:11:19 -03:00
diegosouzapw 80cc76d531 fix: Ensure server port is free before startup by killing existing processes. 2026-02-13 20:59:11 -03:00
dependabot[bot] af1ffab63d deps: bump qs from 6.14.1 to 6.14.2
Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 23:58:54 +00:00
diegosouzapw 7ff1ba978a feat: add OpenAPI specification, generate package-lock, and remove various SVG assets from the public directory. 2026-02-13 20:57:44 -03:00
diegosouzapw d43bbf77fe feat: Enhance restart.sh to include clean build, robust server startup with health check, graceful shutdown, and real-time log tailing. 2026-02-13 20:47:12 -03:00
Diego Rodrigues de Sa e Souza 4e5dc6a80b Merge pull request #12 from diegosouzapw/fix/select-dark-theme
fix(ui): fix Select dropdown dark theme inconsistency
2026-02-13 19:22:36 -03:00
diegosouzapw 0be2852af2 fix(ui): fix Select dropdown dark theme inconsistency
Use bg-surface (theme-aware) instead of bg-white for select and
option elements. Prevents white dropdown panels in dark mode.
2026-02-13 19:22:09 -03:00
Diego Rodrigues de Sa e Souza b3c36ed2ca Merge pull request #11 from diegosouzapw/chore/rebrand-ui-omniroute
chore(ui): rebrand to OmniRoute
2026-02-13 19:14:19 -03:00
diegosouzapw 2b1b8e4539 chore(ui): rebrand to OmniRoute
- Sidebar: 'Endpoint Proxy' → 'OmniRoute'
- Page title: 'OmniRoute — AI Gateway for Multi-Provider LLMs'
- Description updated across layout and config
2026-02-13 19:13:57 -03:00
Diego Rodrigues de Sa e Souza 8b7b50f6fb Merge pull request #10 from diegosouzapw/fix/cloud-sync-404-spam
fix(sync): disable cloud sync and truncate error logs
2026-02-13 19:01:53 -03:00
diegosouzapw 9c0ba39ed6 fix(sync): disable cloud sync and truncate error logs
CLOUD_URL was pointing to omniroute.com which doesn't have a /sync/
endpoint, causing repeated 404 HTML dumps in console logs.

- Clear CLOUD_URL to disable cloud sync until server is ready
- Truncate sync error text to 200 chars to prevent HTML spam in logs
2026-02-13 19:01:31 -03:00
Diego Rodrigues de Sa e Souza 801a050995 Merge pull request #9 from diegosouzapw/feat/enable-socks5-proxy
feat(proxy): enable SOCKS5 proxy support by default
2026-02-13 18:30:08 -03:00
diegosouzapw e7532d7189 feat(proxy): enable SOCKS5 proxy support by default
SOCKS5 was already fully implemented in both backend
(proxyDispatcher.js) and frontend (ProxyConfigModal.js).
Enable the feature flags to show SOCKS5 option in the UI.
2026-02-13 18:29:49 -03:00
Diego Rodrigues de Sa e Souza 7349052b33 Merge pull request #8 from diegosouzapw/fix/test-token-corruption
fix(oauth): prevent connection test from corrupting valid tokens
2026-02-13 18:09:52 -03:00
diegosouzapw 0af22a558f fix(oauth): prevent connection test from corrupting valid tokens
Only attempt token refresh on 401/403 during connection tests when
the token is actually expired (isTokenExpired). Previously, any 401/403
triggered an aggressive refresh that could overwrite valid tokens when
the upstream returned transient errors (rate-limiting, etc.).

Fixes Cline, Qwen, and iFlow losing authentication after tests.
2026-02-13 18:09:28 -03:00
Diego Rodrigues de Sa e Souza 3206583299 Merge pull request #7 from diegosouzapw/fix/iflow-secret-env-sync
chore(env): sync .env.example with current .env structure
2026-02-13 17:47:58 -03:00
diegosouzapw 575bda7375 chore(env): sync .env.example with current .env structure
Add missing Storage (SQLite) section, INSTANCE_NAME, and
align all sections with the actual .env file.
2026-02-13 17:47:38 -03:00
Diego Rodrigues de Sa e Souza b3cde151c3 Merge pull request #6 from diegosouzapw/fix/oauth-upsert-broadened
fix(oauth): broaden upsert to match any existing connection
2026-02-13 17:30:16 -03:00
diegosouzapw b7e757f3ba fix(oauth): broaden upsert to match any existing connection
Remove test_status restriction from the no-email upsert fallback.
Now matches any existing OAuth connection for the same provider
(not just failed ones), preventing duplicates regardless of status.
2026-02-13 17:29:51 -03:00
Diego Rodrigues de Sa e Souza 81a78e8733 Merge pull request #5 from diegosouzapw/fix/oauth-reauth-upsert
fix(oauth): prevent duplicate connections on re-authentication
2026-02-13 17:23:19 -03:00
diegosouzapw fbac38b2b5 fix(oauth): prevent duplicate connections on re-authentication
For providers that don't return email (e.g. Codex), re-authenticating
always created a new 'Account N' entry because the upsert check in
createProviderConnection only matched by email.

Added fallback: when no email is available, find the most recent
auth_failed/refresh_failed connection for the same provider and update
it instead of creating a duplicate.
2026-02-13 17:22:50 -03:00
Diego Rodrigues de Sa e Souza f5898d3898 Merge pull request #3 from diegosouzapw/dependabot/npm_and_yarn/development-55a9f41e4a
deps: bump eslint from 9.39.2 to 10.0.0 in the development group
2026-02-13 16:51:58 -03:00
Diego Rodrigues de Sa e Souza 75e9648d06 Merge pull request #2 from diegosouzapw/dependabot/github_actions/actions/github-script-8
build(deps): bump actions/github-script from 7 to 8
2026-02-13 16:51:25 -03:00
Diego Rodrigues de Sa e Souza 878277edc9 Merge pull request #4 from diegosouzapw/feature/versioning-and-model-import
feat(versioning): set initial version to 0.0.1 and restore dynamic model import
2026-02-13 16:50:59 -03:00
diegosouzapw 50fc931086 feat(versioning): set initial version to 0.0.1 and restore dynamic model import
- Set package.json version to 0.0.1 (initial OmniRoute release)
- Set open-sse/package.json version to 0.0.1
- Restore 'Import from /models' button for standard providers (openai, gemini, deepseek, etc.)
- Add handleImportModels function to ProviderDetailPage
2026-02-13 16:49:06 -03:00
dependabot[bot] 172461863e deps: bump eslint from 9.39.2 to 10.0.0 in the development group
Bumps the development group with 1 update: [eslint](https://github.com/eslint/eslint).


Updates `eslint` from 9.39.2 to 10.0.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.2...v10.0.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 19:31:36 +00:00
dependabot[bot] 79e10c9a41 build(deps): bump actions/github-script from 7 to 8
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 19:30:25 +00:00
diegosouzapw 699329944e feat: initial OmniRoute release (rebranded from 9router)
This project is inspired by and originally forked from 9router by decolua
(https://github.com/decolua/9router).

Full rebrand: 9router → OmniRoute across all source code, configuration,
Docker, documentation, and assets.
2026-02-13 16:29:27 -03:00
663 changed files with 6788 additions and 33138 deletions
+54
View File
@@ -0,0 +1,54 @@
---
description: Git workflow — NEVER commit directly to main. Always use feature branches.
---
# Git Workflow
## ⚠️ CRITICAL RULE: NEVER commit directly to `main`
## Steps
1. **Before starting any work**, create a feature branch from `main`:
```bash
git checkout main && git pull origin main
git checkout -b feature/<feature-name>
```
2. **During development**, commit to the feature branch:
```bash
git add -A && git commit -m "<type>(<scope>): <description>"
```
3. **Before pushing**, verify the build passes:
```bash
npm run build
```
4. **When the feature is complete and verified**, push the branch and STOP:
```bash
git push origin feature/<feature-name>
```
5. **DO NOT** create a PR, merge, or push to `main`. Let the user handle that.
## Branch naming convention
- `feature/<name>` — new features
- `fix/<name>` — bugfixes
- `refactor/<name>` — refactoring
- `docker/<name>` — Docker / infrastructure changes
- `style/<name>` — UI / CSS changes
## Commit types
- `feat` — new feature
- `fix` — bugfix
- `refactor` — code refactoring
- `style` — UI / CSS changes
- `docker` — Docker / infrastructure
- `docs` — documentation
- `chore` — maintenance
-131
View File
@@ -1,131 +0,0 @@
---
description: Analyze open feature request issues, implement viable ones on dedicated branches, and respond to authors
---
# /implement-features — Feature Request Implementation Workflow
## Overview
Fetches open feature request issues, analyzes each against the current codebase, implements viable ones on dedicated branches, and responds to authors with results. Does NOT merge to main — leaves branches for author validation.
## Steps
### 1. Identify the Repository
// turbo
- Run: `git -C <project_root> remote get-url origin` to extract owner/repo
### 2. Fetch Open Feature Request Issues
// turbo
- Run: `gh issue list --repo <owner>/<repo> --state open --limit 50 --json number,title,labels,body,comments,createdAt,author`
- Filter for issues that are feature requests (label `enhancement`/`feature`, or body describes new functionality, or previously classified as feature request)
- Sort by oldest first
### 3. Analyze Each Feature Request
For each feature request issue, perform a **two-level analysis**:
#### Level 1 — Viability Assessment
Ask yourself:
- Does this feature align with the project's goals and architecture?
- Is the request technically feasible with the current codebase?
- Does it duplicate existing functionality?
- Would it introduce breaking changes or security risks?
- Is there enough detail to implement it?
**Verdict options:**
1.**VIABLE** — Makes sense, enough detail to implement → Go to Level 2
2.**NEEDS MORE INFO** — Good idea but insufficient detail → Post comment asking for specifics
3.**NOT VIABLE** — Doesn't fit the project or is fundamentally flawed → Post comment explaining why, close issue
#### Level 2 — Implementation (only for VIABLE features)
1. **Research** — Read all related source files to understand the current architecture
2. **Design** — Plan the implementation, filling gaps in the original request
3. **Create branch** — Name format: `feat/issue-<NUMBER>-<short-slug>`
```bash
git checkout main
git pull origin main
git checkout -b feat/issue-<NUMBER>-<short-slug>
```
4. **Implement** — Build the complete solution following project patterns
5. **Build** — Run `npm run build` to verify compilation
6. **Commit** — Commit with: `feat: <description> (#<NUMBER>)`
7. **Push** — Push the branch: `git push -u origin feat/issue-<NUMBER>-<short-slug>`
8. **Return to main** — `git checkout main`
### 4. Respond to Authors
#### For VIABLE (implemented) features:
// turbo
Post a comment on the issue:
````markdown
## ✅ Feature Implemented!
Hi @<author>! We've analyzed your request and implemented it on a dedicated branch.
**Branch:** `feat/issue-<NUMBER>-<short-slug>`
### What was implemented:
- <bullet list of what was done>
### How to try it:
```bash
git fetch origin
git checkout feat/issue-<NUMBER>-<short-slug>
npm install && npm run dev
```
````
### Next steps:
1. **Test it** — Please verify it works as you expected
2. **Want to improve it?** — You're welcome to contribute! Just:
```bash
git checkout feat/issue-<NUMBER>-<short-slug>
# Make your improvements
git add -A && git commit -m "improve: <your changes>"
git push origin feat/issue-<NUMBER>-<short-slug>
```
Then open a Pull Request from your branch to `main` 🎉
3. **Not quite right?** — Let us know in this issue what needs to change
Looking forward to your feedback! 🚀
```
#### For NEEDS MORE INFO:
// turbo
Post a comment asking for specific missing details needed to implement, e.g.:
- "Could you describe the exact behavior when X happens?"
- "Which API endpoints should be affected?"
- "Should this apply to all providers or only specific ones?"
Add the context of WHY you need each piece of information.
#### For NOT VIABLE:
// turbo
Post a polite comment explaining why the feature doesn't fit at this time:
- If the idea is decent but timing is wrong: "This is an interesting idea, but it doesn't align with our current priorities. Feel free to open a new issue with more details if you'd like us to reconsider."
- If fundamentally flawed: Explain the technical or architectural reasons why it won't work, suggest alternatives if possible.
- Close the issue after posting the comment.
### 5. Summary Report
Present a summary report to the user via `notify_user`:
| Issue | Title | Verdict | Branch / Action |
|---|---|---|---|
| #N | Title | ✅ Implemented | `feat/issue-N-slug` |
| #N | Title | ❓ Needs Info | Comment posted |
| #N | Title | ❌ Not Viable | Closed with explanation |
```
-100
View File
@@ -1,100 +0,0 @@
---
description: Fetch all open GitHub issues, analyze bugs, resolve what's possible, triage the rest, then commit and release
---
# /resolve-issues — Automated Issue Resolution Workflow
## Overview
This workflow fetches all open issues from the project's GitHub repository, classifies them, analyzes bugs, resolves what can be fixed, triages issues with insufficient information, and generates a release with all fixes.
## Steps
### 1. Identify the GitHub Repository
// turbo
- Run: `git -C <project_root> remote get-url origin` to extract the owner/repo
- Parse the owner and repo name from the URL
### 2. Fetch All Open Issues
// turbo
- Run: `gh issue list --repo <owner>/<repo> --state open --limit 100 --json number,title,labels,body,comments,createdAt,author`
- Parse the JSON output to get a list of all open issues
- Sort by oldest first (FIFO)
### 3. Classify Each Issue
For each issue, determine its type:
- **Bug** — Has `bug` label, or body contains error messages, stack traces, "doesn't work", "broken", "crash", "error"
- **Feature Request** — Has `enhancement`/`feature` label, or body describes new functionality
- **Question** — Has `question` label, or is asking "how to" something
- **Other** — Anything else
Focus ONLY on **Bugs** for resolution. Feature requests and questions should be skipped with a note in the final report.
### 4. Analyze Each Bug — For each bug issue:
#### 4a. Check Information Sufficiency
Verify the issue contains enough information to reproduce and fix:
- [ ] Clear description of the problem
- [ ] Steps to reproduce
- [ ] Error messages or logs
- [ ] Expected vs actual behavior
#### 4b. If Information Is INSUFFICIENT
Call the `/issue-triage` workflow (located at `~/.gemini/antigravity/global_workflows/issue-triage.md`):
// turbo
- Post a comment asking for more details using `gh issue comment`
- Add `needs-info` label using `gh issue edit`
- Mark this issue as **DEFERRED** and move to the next one
#### 4c. If Information Is SUFFICIENT
Proceed with resolution:
1. **Research** — Search the codebase for files related to the issue
2. **Root Cause** — Identify the root cause by reading the relevant source files
3. **Implement Fix** — Apply the fix following existing code patterns and conventions
4. **Test** — Build the project and run tests to verify the fix
5. **Commit** — Commit with message format: `fix: <description> (#<issue_number>)`
### 5. Commit All Fixes
After processing all issues:
- Ensure all fixes are committed with proper issue references
- Each fix should be its own commit for clean git history
### 6. Close Resolved Issues
For each successfully fixed issue:
// turbo
- Close with a comment: `gh issue close <NUMBER> --repo <owner>/<repo> --comment "Fixed in <commit_hash>. The fix will be included in the next release."`
### 7. Generate Report
Present a summary report to the user via `notify_user`:
| Issue | Title | Status | Action |
| ----- | ----- | ------------- | --------------------------- |
| #N | Title | ✅ Fixed | Commit hash |
| #N | Title | ❓ Needs Info | Triage comment posted |
| #N | Title | ⏭️ Skipped | Feature request / not a bug |
### 8. Update Docs & Release
If any fixes were committed:
1. Run the `/update-docs` workflow (at `~/.gemini/antigravity/global_workflows/update-docs.md`) to update CHANGELOG and README
2. Run the `/generate-release` workflow (at `.agents/workflows/generate-release.md`) to bump version, tag, and publish
If NO fixes were committed, skip this step and just present the report.
-96
View File
@@ -1,96 +0,0 @@
---
description: Analyze open Pull Requests from the project's GitHub repository, generate a critical report, and optionally implement approved changes
---
# /review-prs — PR Review & Analysis Workflow
## Overview
This workflow fetches all open PRs from the project's GitHub repository, performs a critical analysis of each one, generates a detailed report, and waits for user approval before proceeding with implementation.
## Steps
### 1. Identify the GitHub Repository
- Read `package.json` to get the repository URL, or use the git remote origin URL
// turbo
- Run: `git -C <project_root> remote get-url origin` to extract the owner/repo
### 2. Fetch Open Pull Requests
- Navigate to `https://github.com/<owner>/<repo>/pulls` and scrape all open PRs
- For each open PR, collect:
- PR number, title, author, branch, number of commits, date
- PR description/body
- Files changed (diff)
- Existing review comments (from bots or humans)
### 3. Analyze Each PR — For each open PR, perform the following analysis:
#### 3a. Feature Assessment
- **Does it make sense?** Evaluate if the feature fills a real gap or solves a valid problem
- **Alignment** — Check if it aligns with the project's architecture and roadmap
- **Complexity** — Assess if the scope is reasonable or if it should be split
#### 3b. Code Quality Review
- Check for code duplication
- Evaluate error handling patterns (consistent with existing codebase?)
- Check naming conventions and code style
- Verify TypeScript types (any `any` usage, missing types?)
#### 3c. Security Review
- Check for missing authentication/authorization on new endpoints
- Check for injection vulnerabilities (URL params, SQL, XSS)
- Verify input validation on all user-controlled data
- Check for hardcoded secrets or credentials
#### 3d. Architecture Review
- Does the change follow existing patterns?
- Are there any breaking changes to public APIs?
- Is the database schema affected? Migration needed?
- Impact on performance (N+1 queries, missing indexes?)
#### 3e. Test Coverage
- Does the PR include tests?
- Are edge cases covered?
- Would existing tests break?
### 4. Generate Report — Create a markdown report for each PR including:
- **PR Summary** — What it does, files affected, commit count
- **Improvements/Benefits** — Numbered list with impact level (HIGH/MEDIUM/LOW)
- **Risks & Issues** — Categorized as CRITICAL / IMPORTANT / MINOR
- **Scoring Table** — Rate across: Feature Relevance, Code Quality, Security, Robustness, Tests
- **Verdict** — Ready to merge? With mandatory vs optional fixes
- **Next Steps** — What will happen if approved
### 5. Present to User
- Show the report via `notify_user` with `BlockedOnUser: true`
- Wait for user decision:
- **Approved** → Proceed to step 6
- **Approved with changes** → Implement the fixes and corrections before merging
- **Rejected** → Close the PR or leave a review comment
### 6. Implementation (if approved)
- Checkout the PR branch or apply changes locally
- Implement any required fixes identified in the analysis
- Run the project's test suite to verify nothing breaks
// turbo
- Run: `npm test` or equivalent test command
- Build the project to verify compilation
// turbo
- Run: `npm run build` or equivalent build command
- If all checks pass, prepare the merge
### 7. Post-Merge (if applicable)
- Update CHANGELOG.md with the new feature
- Consider version bump if warranted
- Follow the `/generate-release` workflow if a release is needed
+6 -46
View File
@@ -44,9 +44,6 @@ REQUIRE_API_KEY=false
BASE_URL=http://localhost:20128
CLOUD_URL=
# Backward-compatible/public variables:
# NEXT_PUBLIC_BASE_URL is also used as the OAuth redirect_uri origin when running behind a
# reverse proxy (e.g., nginx). Set this to your public-facing URL so OAuth callbacks work.
# Example: NEXT_PUBLIC_BASE_URL=https://omniroute.example.com
NEXT_PUBLIC_BASE_URL=http://localhost:20128
NEXT_PUBLIC_CLOUD_URL=
@@ -60,11 +57,6 @@ NEXT_PUBLIC_ENABLE_SOCKS5_PROXY=true
# ALL_PROXY=socks5://127.0.0.1:7890
# NO_PROXY=localhost,127.0.0.1
# TLS fingerprint spoofing (opt-in) — mimics Chrome 124 TLS handshake via wreq-js
# Reduces risk of JA3/JA4 fingerprint-based blocking by providers (e.g., Google)
# Requires wreq-js to be installed (included in dependencies)
# ENABLE_TLS_FINGERPRINT=true
# Optional CLI runtime overrides (Docker/host integration)
# CLI_MODE=auto
# CLI_EXTRA_PATHS=/host-cli/bin
@@ -82,45 +74,17 @@ NEXT_PUBLIC_ENABLE_SOCKS5_PROXY=true
# Provider OAuth Credentials (optional — override hardcoded defaults)
# These can also be set via data/provider-credentials.json
# CLAUDE_OAUTH_CLIENT_ID=
# ─────────────────────────────────────────────────────────────────────────────
# ⚠️ GOOGLE OAUTH (Antigravity, Gemini CLI) — IMPORTANT FOR REMOTE SERVERS
# ─────────────────────────────────────────────────────────────────────────────
# The built-in Google OAuth credentials ONLY work when OmniRoute runs on
# localhost (127.0.0.1 / local network). They are registered with
# redirect_uri = http://localhost:PORT/callback and Google will reject any
# other redirect URI with: redirect_uri_mismatch.
#
# If you are hosting OmniRoute on a remote server (VPS, Docker, cloud), you
# MUST register your own Google Cloud OAuth 2.0 credentials:
#
# 1. Go to https://console.cloud.google.com/apis/credentials
# 2. Create an OAuth 2.0 Client ID (type: "Web application")
# 3. Add your server URL as Authorized redirect URI:
# https://your-server.com/callback
# 4. Copy the Client ID and Client Secret below.
#
# See the full tutorial in README.md → "OAuth em Servidor Remoto" section.
#
# Antigravity (Google Gemini Code Assist):
# ANTIGRAVITY_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com
# ANTIGRAVITY_OAUTH_CLIENT_SECRET=GOCSPX-your-secret
ANTIGRAVITY_OAUTH_CLIENT_SECRET=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
# Gemini CLI (Google AI):
# GEMINI_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com
# GEMINI_OAUTH_CLIENT_SECRET=GOCSPX-your-secret
# GEMINI_OAUTH_CLIENT_ID=
# GEMINI_OAUTH_CLIENT_SECRET=
# GEMINI_CLI_OAUTH_CLIENT_ID=
GEMINI_OAUTH_CLIENT_SECRET=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
GEMINI_CLI_OAUTH_CLIENT_SECRET=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
# ─────────────────────────────────────────────────────────────────────────────
# CLAUDE_OAUTH_CLIENT_ID=
# GEMINI_CLI_OAUTH_CLIENT_SECRET=
# CODEX_OAUTH_CLIENT_ID=
# CODEX_OAUTH_CLIENT_SECRET=
# QWEN_OAUTH_CLIENT_ID=
# IFLOW_OAUTH_CLIENT_ID=
IFLOW_OAUTH_CLIENT_SECRET=4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW
# IFLOW_OAUTH_CLIENT_SECRET=
# ANTIGRAVITY_OAUTH_CLIENT_ID=
# ANTIGRAVITY_OAUTH_CLIENT_SECRET=
# API Key Providers (Phase 1 + Phase 4)
# Add via Dashboard → Providers → Add API Key, or set here
@@ -149,7 +113,3 @@ IFLOW_OAUTH_CLIENT_SECRET=4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW
# Logging
# LOG_LEVEL=info
# LOG_FORMAT=text
LOG_TO_FILE=true
# LOG_FILE_PATH=logs/application/app.log
# LOG_MAX_FILE_SIZE=50M
# LOG_RETENTION_DAYS=7
-4
View File
@@ -20,10 +20,6 @@ updates:
update-types: ["version-update:semver-major"]
- dependency-name: "next"
update-types: ["version-update:semver-major"]
- dependency-name: "eslint"
update-types: ["version-update:semver-major"]
- dependency-name: "eslint-config-next"
update-types: ["version-update:semver-major"]
- package-ecosystem: "github-actions"
directory: "/"
+13 -50
View File
@@ -15,8 +15,8 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
@@ -27,8 +27,8 @@ jobs:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
@@ -36,8 +36,7 @@ jobs:
- name: Dependency audit
run: npm audit --audit-level=high --omit=dev
- name: Check for known vulnerabilities
run: npx is-my-node-vulnerable
continue-on-error: true
run: npx is-my-node-vulnerable || true
build:
name: Build
@@ -46,8 +45,8 @@ jobs:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
@@ -65,8 +64,8 @@ jobs:
JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation
API_KEY_SECRET: ci-test-api-key-secret-long
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
@@ -81,8 +80,8 @@ jobs:
JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation
API_KEY_SECRET: ci-test-api-key-secret-long
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
@@ -100,8 +99,8 @@ jobs:
JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation
API_KEY_SECRET: ci-test-api-key-secret-long
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
@@ -109,39 +108,3 @@ jobs:
- run: npx playwright install --with-deps chromium
- run: npm run build
- run: npm run test:e2e
test-integration:
name: Integration Tests
runs-on: ubuntu-latest
needs: build
env:
JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation
API_KEY_SECRET: ci-test-api-key-secret-long
INITIAL_PASSWORD: ci-test-password-for-integration
DATA_DIR: /tmp/omniroute-ci
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run test:integration
continue-on-error: true
test-security:
name: Security Tests
runs-on: ubuntu-latest
needs: build
env:
JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation
API_KEY_SECRET: ci-test-api-key-secret-long
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run test:security
continue-on-error: true
+20 -20
View File
@@ -1,22 +1,22 @@
# name: Codex PR Review
name: Codex PR Review
# on:
# pull_request:
# types: [opened, synchronize]
on:
pull_request:
types: [opened, synchronize]
# jobs:
# request-codex-review:
# runs-on: ubuntu-latest
# permissions:
# pull-requests: write
# steps:
# - name: Request Codex Review
# uses: actions/github-script@v8
# with:
# script: |
# await github.rest.issues.createComment({
# owner: context.repo.owner,
# repo: context.repo.repo,
# issue_number: context.payload.pull_request.number,
# body: '@codex review'
# });
jobs:
request-codex-review:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Request Codex Review
uses: actions/github-script@v8
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: '@codex review'
});
-44
View File
@@ -1,44 +0,0 @@
name: Deploy to VPS
on:
workflow_run:
workflows: ["Publish to Docker Hub"]
types: [completed]
workflow_dispatch:
jobs:
deploy:
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
name: Deploy OmniRoute to VPS
runs-on: ubuntu-latest
steps:
- name: Install Tailscale
uses: tailscale/github-action@v3
with:
oauth-client-id: ""
oauth-secret: ""
tags: tag:ci-deploy
continue-on-error: true
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: 22
timeout: 30s
script: |
echo "=== Updating OmniRoute ==="
npm install -g omniroute@latest 2>&1
INSTALLED_VERSION=$(omniroute --version 2>/dev/null || echo "unknown")
echo "Installed version: $INSTALLED_VERSION"
echo "=== Restarting PM2 ==="
pm2 restart omniroute || pm2 start omniroute --name omniroute -- --port 20128
pm2 save
echo "=== Health Check ==="
sleep 3
curl -sf http://localhost:20128/api/settings > /dev/null && echo "✅ OmniRoute is healthy" || echo "❌ Health check failed"
echo "=== Deploy complete ==="
+18 -80
View File
@@ -8,68 +8,12 @@ permissions:
contents: read
jobs:
build:
name: Build (${{ matrix.platform }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
platform_pair: linux-amd64
runner: ubuntu-latest
- platform: linux/arm64
platform_pair: linux-arm64
runner: ubuntu-24.04-arm
env:
IMAGE_NAME: diegosouzapw/omniroute
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
target: runner-base
platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=omniroute-runner-base-${{ matrix.platform_pair }}
cache-to: type=gha,mode=max,scope=omniroute-runner-base-${{ matrix.platform_pair }}
- name: Export digest
run: |
mkdir -p "${{ runner.temp }}/digests"
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.platform_pair }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
merge:
name: Merge manifest and publish tags
docker:
name: Build & Push Docker Image
runs-on: ubuntu-latest
needs: build
env:
IMAGE_NAME: diegosouzapw/omniroute
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Extract version from release tag
id: version
@@ -79,12 +23,8 @@ jobs:
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Publishing Docker image version: $VERSION"
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
@@ -92,23 +32,21 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create \
-t "${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}" \
-t "${{ env.IMAGE_NAME }}:latest" \
$(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect "${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}"
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
target: runner-base
push: true
tags: |
diegosouzapw/omniroute:${{ steps.version.outputs.version }}
diegosouzapw/omniroute:latest
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
- name: Update Docker Hub description
uses: peter-evans/dockerhub-description@v5
uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+2 -2
View File
@@ -14,10 +14,10 @@ jobs:
environment: NPM_TOKEN
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: 22
registry-url: https://registry.npmjs.org
-12
View File
@@ -71,9 +71,6 @@ docs/*
!docs/planning/
!docs/improvement-plans/
!docs/api/
!docs/VM_DEPLOYMENT_GUIDE.md
!docs/FEATURES.md
!docs/screenshots/
# open-sse tests
open-sse/test/*
@@ -88,12 +85,3 @@ blob-report/
cloud/
omnirouteCloud/
omnirouteSite/
# Security Analysis (standalone project with own git)
security-analysis/
# Deploy workflow (contains sensitive VPS credentials)
.agent/workflows/deploy.md
clipr/
app.log
*.tgz
+17 -18
View File
@@ -8,7 +8,6 @@ Unified AI proxy/router — route any LLM through one endpoint. Multi-provider s
## Stack
- **Runtime**: Next.js 16 (App Router), Node.js, ES Modules
- **Language**: TypeScript 5.9 (`src/`) + JavaScript (`open-sse/`)
- **Database**: better-sqlite3 (SQLite) — `DATA_DIR` configurable, default `~/.omniroute/`
- **Streaming**: SSE via `open-sse` internal package
- **Styling**: Tailwind CSS v4
@@ -22,15 +21,15 @@ All persistence uses SQLite through domain-specific modules:
| Module | Responsibility |
| -------------- | ------------------------------------------ |
| `core.ts` | SQLite engine, migrations, WAL, encryption |
| `providers.ts` | Provider connections & nodes |
| `models.ts` | Model aliases, MITM aliases, custom models |
| `combos.ts` | Combo configurations |
| `apiKeys.ts` | API key management & validation |
| `settings.ts` | Settings, pricing, proxy config |
| `backup.ts` | Backup / restore operations |
| `core.js` | SQLite engine, migrations, WAL, encryption |
| `providers.js` | Provider connections & nodes |
| `models.js` | Model aliases, MITM aliases, custom models |
| `combos.js` | Combo configurations |
| `apiKeys.js` | API key management & validation |
| `settings.js` | Settings, pricing, proxy config |
| `backup.js` | Backup / restore operations |
`src/lib/localDb.ts` is a **re-export layer only** — all 27+ consumers import from it,
`src/lib/localDb.js` is a **re-export layer only** — all 27+ consumers import from it,
but the real logic lives in `src/lib/db/`.
### Request Pipeline (`open-sse/`)
@@ -50,25 +49,25 @@ Translation between provider formats: `open-sse/translator/`
### OAuth & Tokens (`src/lib/oauth/`)
18 modules handling OAuth flows, token refresh, and provider credentials.
Default credentials are hardcoded in `src/lib/oauth/constants/oauth.ts`,
Default credentials are hardcoded in `src/lib/oauth/constants/oauth.js`,
overridable via env vars or `data/provider-credentials.json`.
### Supporting Systems
| System | Location |
| -------------------------- | ------------------------------------------------- |
| Usage tracking & analytics | `src/lib/usageDb.ts`, `src/lib/usageAnalytics.ts` |
| Token health checks | `src/lib/tokenHealthCheck.ts` |
| Cloud sync | `src/lib/cloudSync.ts` |
| Proxy logging | `src/lib/proxyLogger.ts` |
| Data paths resolution | `src/lib/dataPaths.ts` |
| Usage tracking & analytics | `src/lib/usageDb.js`, `src/lib/usageAnalytics.js` |
| Token health checks | `src/lib/tokenHealthCheck.js` |
| Cloud sync | `src/lib/cloudSync.js` |
| Proxy logging | `src/lib/proxyLogger.js` |
| Data paths resolution | `src/lib/dataPaths.js` |
### Adding a New Provider
1. Register in `src/shared/constants/providers.ts`
1. Register in `src/shared/constants/providers.js`
2. Add executor in `open-sse/executors/`
3. Add translator rules in `open-sse/translator/` (if non-OpenAI format)
4. Add OAuth config in `src/lib/oauth/constants/oauth.ts` (if OAuth-based)
4. Add OAuth config in `src/lib/oauth/constants/oauth.js` (if OAuth-based)
## Review Focus
@@ -84,7 +83,7 @@ overridable via env vars or `data/provider-credentials.json`.
- DB operations go through `src/lib/db/` modules, never raw SQL in routes
- Provider requests flow through `open-sse/handlers/`
- Translations use `open-sse/translator/` modules
- `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module
- `localDb.js` is re-exports only — add new functions to the proper `db/*.js` module
### Code Quality
+96 -655
View File
@@ -7,706 +7,147 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
---
## [1.4.11] — 2026-02-25
## [0.8.0] — 2026-02-16
> ### 🐛 Settings Persistence Fix
>
> Fixes routing strategy and wildcard aliases not saving after page refresh.
### Added
### 🐛 Bug Fixes
- 🌐 **Official website** — [omniroute.online](https://omniroute.online) live with static site on Akamai VM + Cloudflare proxy
- 🛡️ **Comprehensive SECURITY.md** — Full codebase audit documenting 10+ security features (AES-256-GCM, prompt injection guard, PII redaction, circuit breaker, etc.)
- 📖 **Documentation tracking**`USER_GUIDE.md`, `API_REFERENCE.md`, `TROUBLESHOOTING.md` now tracked in git
- 🏷️ **Website badge** — Official website badge and links in README, npm, and Docker Hub
- 🔗 **36+ providers** — Updated provider count across documentation
- **Routing Strategy Not Saved After Refresh (#134)** — Added `fallbackStrategy`, `wildcardAliases`, and `stickyRoundRobinLimit` to the Zod validation schema. These fields were silently stripped during validation, preventing them from being persisted to the database
### Changed
### 📝 Notes
- **#135 Closed** — Feature request for proxy configuration (global + per-provider) was already implemented in v1.4.10
- 📦 **npm homepage** — Points to `omniroute.online` instead of GitHub
- 🐳 **Docker OCI labels** — Added `org.opencontainers.image.url` for Docker Hub
- 🔒 **Security policy**Updated supported versions, replaced email with GitHub Security Advisories
---
## [1.4.10] — 2026-02-25
## [0.7.0] — 2026-02-16
> ### 🔒 Proxy Visibility + Bug Fixes
>
> Color-coded proxy badges, provider-level proxy configuration, CLI tools page fix, and EACCES fix for restricted environments.
### Added
### ✨ New Features
- **Color-Coded Proxy Badges** — Each provider connection now shows its proxy status with color-coded badges: 🟢 green (global proxy), 🟡 amber (provider-level proxy), 🔵 blue (per-connection proxy). Badge always displays the proxy IP/host
- **Provider-Level Proxy Button** — New "Provider Proxy" button in the Connections header of each provider detail page. Allows configuring a proxy that applies to all connections of that provider
- **Proxy IP Display** — The proxy badge now always shows the proxy host/IP address for quick identification
### 🐛 Bug Fixes
- **CLI Tools Page Stuck in Loading** — Fixed the `/api/cli-tools/status` endpoint hanging indefinitely when binary checks stall on VPS. Added 5s server-side timeout per tool and 8s client-side AbortController timeout (#cli-tools-hang)
- **EACCES on Restricted Home Directories** — Fixed crash when `~/.omniroute` directory cannot be created due to permission issues. Now gracefully warns and suggests using the `DATA_DIR` environment variable (#133)
- 🐳 **Docker Hub public image**`diegosouzapw/omniroute` available on [Docker Hub](https://hub.docker.com/r/diegosouzapw/omniroute) with `latest` and versioned tags
- 🔄 **Docker CI/CD** — GitHub Actions workflow (`docker-publish.yml`) auto-builds and pushes Docker image on every release
- ☁️ **Akamai VM deployment**Nanode 1GB instance created for remote hosting
- 🎯 **Provider model filtering**Filter model suggestions by selected provider in Translator and Chat Tester
- 🔌 **CLI status badges** — Extract `CliStatusBadge` component; status visible on collapsed tool cards
- ☁️ **Cloud connection UX** — GET status endpoint, toast feedback, and sidebar indicator for cloud sync
- 🔐 **OAuth provider secrets** — Default cloud URL and OAuth provider secrets set via environment variables
-**Edge compatibility** — Replace `uuid` package with native `crypto.randomUUID()` for Cloudflare Workers compatibility
---
> ### 🌐 Full Internationalization (i18n) + Multi-Account Fix
>
> Complete dashboard i18n migration with next-intl, supporting English and Portuguese (Brazil). Fixes production build issues and enables multiple Codex accounts from the same workspace.
## [0.6.0] — 2026-02-16
### ✨ New Features
### Added
- **Full Dashboard Internationalization** — Complete i18n migration of 21+ pages and components using `next-intl`. Every dashboard string is now translatable with full EN and PT-BR support. Includes language selector (globe icon) in the header for real-time language switching
- **Portuguese (Brazil) Translation** — Complete `pt-BR.json` translation file with 500+ keys covering all pages: Home, Providers, Settings, Combos, Analytics, Costs, Logs, Health, CLI Tools, Endpoint, API Manager, and Onboarding
- **Language Selector Component** — New `LanguageSelector` component in the header with flag icons and dropdown for switching between 🇺🇸 English and 🇧🇷 Português
### 🐛 Bug Fixes
- **Multiple Codex Accounts from Same Workspace** — Fixed deduplication logic in `createProviderConnection` that prevented adding multiple OpenAI Pro Business accounts from the same Team workspace. Now uses compound check (workspaceId + email) instead of workspaceId-only, allowing separate connections per user
- **Production Build — Crypto Import** — Fixed `instrumentation.ts` using `eval('require')('crypto')` to bypass webpack's static analysis that blocked the Node.js crypto module in the bundled instrumentation file
- **Production Build — Translation Scope** — Fixed sub-components `ProviderOverviewCard` and `ProviderModelsModal` in `HomePageClient.tsx` that referenced parent-scope translation hooks. Each sub-component now has its own `useTranslations()` call
- **Production Build — app/ Directory Conflict** — Resolved Next.js 16 confusing the production `app/` directory (server build output) with the `src/app/` app router directory, which caused "missing root layout" build failures
### 📄 i18n Pages Migrated
Home, Endpoint, API Manager, Providers (list + detail + new), Combos, Logs, Costs, Analytics, Health, CLI Tools, Settings (General, Security, Routing, Session, IP Filter, Compliance, Fallback Chains, Thinking Budget, Policies, Pricing, Resilience, Advanced), Onboarding Wizard, Audit Log, Usage
- 💰 **Costs & Budget page** — Dedicated dashboard page for cost tracking and budget management
- 📊 **Provider metrics display**Show per-provider usage metrics and statistics
- 📥 **Model import for passthrough providers** — Import models from API-compatible providers (Deepgram, AssemblyAI, NanoBanana)
- 🎨 **App icon redesign** — New network node graph icon with updated color scheme
---
## [1.4.7] — 2026-02-25
## [0.5.0] — 2026-02-15
> ### 🐛 Bugfix — Antigravity Model Prefix & Version Sync
>
> Fixes model name sent to Antigravity upstream API containing `antigravity/` prefix, causing 400 errors for non-opus models. Also syncs package-lock.json version.
### Added
### 🐛 Bug Fixes
- 🧪 **LLM Evaluations (Evals)** — Golden set testing framework with 4 match strategies (`exact`, `contains`, `regex`, `custom`)
- 🎲 **Advanced combo strategies**`random`, `least-used`, and `cost-optimized` balancing strategies for combos
- 📊 **API key usage in Evals** — Evals tab uses API key auth for real LLM calls through the proxy
- 🏷️ **Model availability badge** — Visual indicator for model availability per provider
- 🎨 **Landing page retheme** — Updated landing page design with new aesthetic
- 🧩 **Shared UI component library** — Refactored dashboard with reusable component library
- **Antigravity Model Prefix Stripping** — Model names sent to the Antigravity upstream API (Google Cloud Code) now have any `provider/` prefix defensively stripped. Previously, models like `antigravity/gemini-3-flash` were sent with the prefix intact, causing 400 errors from the upstream API. Only `claude-opus-4-6-thinking` worked because its routing path differed. Fix applied in 3 locations: `antigravity.ts` executor, and both `wrapInCloudCodeEnvelope` and `wrapInCloudCodeEnvelopeForClaude` in the translator
- **Package-lock.json Version Sync** — Fixed `package-lock.json` being stuck at `1.4.3` while `package.json` was at `1.4.6`, which prevented npm from publishing the correct version and caused the VPS deploy to stay on the old version
### Fixed
- 🐛 Fix `TypeError` in `chat/completions` `ensureInitialized` call
---
## [1.4.6] — 2026-02-25
## [0.4.0] — 2026-02-15
> ### ✨ Community Release — Security Fix, Multi-Platform Docker, Model Updates & Plus Tier
>
> Enforces API key model restrictions across all endpoints, adds ARM64 Docker support, updates model registry for latest AI models, and introduces Plus tier in ProviderLimits.
### Added
### 🔒 Security
- 🧠 **LLM Gateway Intelligence** (Phase 9) — Smart routing, semantic caching, request idempotency, progress tracking
- 📄 **Missing flows & pages** (Phase 8) — Error pages, UX components, telemetry dashboards
- 🔧 **API & code quality** (Phase 7) — API restructuring, JSDoc documentation, code quality improvements
- 📚 **Documentation restructuring** (Phase 10) — Component decomposition, docs cleanup
-**26 action items** from critical analysis resolved
- **API Key Model Restrictions Enforced** — `isModelAllowedForKey()` was never called, allowing API keys with `allowedModels` restrictions to access any model. Created centralized `enforceApiKeyPolicy()` middleware and wired it into all `/v1/*` endpoints (chat, embeddings, images, audio, moderations, rerank). Supports exact match, prefix match (`openai/*`), and wildcard patterns ([#130](https://github.com/diegosouzapw/OmniRoute/issues/130), [PR #131](https://github.com/diegosouzapw/OmniRoute/pull/131) by [@ersintarhan](https://github.com/ersintarhan))
- **ApiKeyMetadata Type Safety** — Replaced `any` types with proper `ApiKeyMetadata` interface in the policy middleware. Added error logging in catch blocks for metadata fetch and budget check failures
### Changed
### ✨ New Features
- ♻️ **Architecture refactor** (Phase 5-6) — Domain persistence, policy engine, OAuth extraction, proxy decoupling
- **Docker Multi-Platform Builds** — Restructured Docker CI workflow to support both `linux/amd64` and `linux/arm64` using native runners and digest-based manifest merging. ARM64 users (Apple Silicon, AWS Graviton, Raspberry Pi) can now run OmniRoute natively ([PR #127](https://github.com/diegosouzapw/OmniRoute/pull/127) by [@npmSteven](https://github.com/npmSteven))
- **Plus Tier in ProviderLimits** — Added "Plus" as a separate category in the ProviderLimits dashboard, distinguishing Plus/Paid plans from Pro plans with proper ranking and filtering ([PR #126](https://github.com/diegosouzapw/OmniRoute/pull/126) by [@nyatoru](https://github.com/nyatoru))
### Fixed
### 🔧 Improvements
- **Model Registry Updates** — Updated provider registry, usage tracking, CLI tools config, and pricing for latest AI models: added Claude Sonnet 4.6, Gemini 3.1 Pro (High/Low), GPT OSS 120B Medium; removed deprecated Claude 4.5 variants and Gemini 2.5 Flash ([PR #128](https://github.com/diegosouzapw/OmniRoute/pull/128) by [@nyatoru](https://github.com/nyatoru))
- **Model ID Consistency** — Fixed `claude-sonnet-4-6-thinking``claude-sonnet-4-6` mismatch in `importantModels` to match the provider registry
- 🐛 Fix CI build and lint failures
- 🐛 Fix ghost import in `chatHelpers.js` SSE handling
---
## [1.4.5] — 2026-02-24
## [0.3.0] — 2026-02-15
> ### 🐛 Bugfix Release — Claude Code OAuth & OAuth Proxy Routing
>
> Fixes Claude Code OAuth failures on remote deployments and routes all OAuth token exchanges through configured proxy.
### Added
### 🐛 Bug Fixes
-**Resilience system** — Exponential backoff, circuit breaker, anti-thundering herd mutex, Resilience UI settings page
- 🖥️ **100% frontend API coverage** — 7 implementation batches covering all backend routes
- 📊 **9 new API routes** — Budget, telemetry, compliance, tags, storage health, and more
- 🧪 **Eval framework & compliance** — ADRs, accessibility, CLI specs, Playwright test specs (46 tasks)
- 🏗️ **Pipeline integration** — 7 backend modules wired into request processing pipeline
- 🔐 **Security hardening** — Phases 0106 (input validation, CSRF, rate limiting, auth hardening)
- 🤖 **Advanced features** — Phases 0709 (domain extraction, error codes, request ID, fetch timeout)
- 🔄 **Unrecoverable token handling** — Detect and mark connections as expired on fatal refresh errors
- **Claude Code OAuth** — Fixed `400 Bad Request` on remote deployments by using Anthropic's registered `redirect_uri` (`https://platform.claude.com/oauth/code/callback`) instead of the dynamic server URL. Added missing OAuth scopes (`user:sessions:claude_code`, `user:mcp_servers`) to match the official Claude CLI. Configurable via `CLAUDE_CODE_REDIRECT_URI` env var ([#124](https://github.com/diegosouzapw/OmniRoute/issues/124))
- **OAuth Token Exchange Through Proxy** — OAuth token exchange during new connection setup now routes through the configured proxy (provider-level → global → direct), fixing `unsupported_country_region_territory` errors for region-restricted providers like OpenAI Codex ([#119](https://github.com/diegosouzapw/OmniRoute/issues/119))
### Changed
- ♻️ Decompose `usageDb`, `handleSingleModelChat`, and UI components for maintainability
- ⬇️ Downgrade ESLint v10 → v9 for `eslint-config-next` compatibility
---
## [1.4.4] — 2026-02-24
## [0.2.0] — 2026-02-14
> ### ✨ Feature Release — Custom Provider Models in /v1/models
>
> Compatible provider models are now saved to the customModels database, making them visible via `/v1/models` for all OpenAI-compatible clients.
### Added
### ✨ New Features
- **Custom Provider Model Persistence** — Compatible provider models (manual or imported) are now saved to the `customModels` database so they appear in `/v1/models` listing for clients like Cursor, Cline, Antigravity, and Claude Code ([PR #122](https://github.com/diegosouzapw/OmniRoute/pull/122) by [@nyatoru](https://github.com/nyatoru))
- **Provider Models API** — New `/api/provider-models` endpoint (GET/POST/DELETE) for managing custom model entries with full authentication via `isAuthenticated`
- **Unified Model Deletion** — New `handleDeleteModel` removes models from both alias configuration and `customModels` database, preventing orphaned entries
- **Provider Node Prefix Resolution** — `getModelInfo` refactored to use provider node prefixes for accurate custom provider model resolution
### 🔒 Security
- **Authentication on Provider Models API** — All `/api/provider-models` endpoints require API key or JWT session authentication via shared `isAuthenticated` utility
- **URL Parameter Injection Fix** — Applied `encodeURIComponent` to all user-controlled URL parameters (`providerStorageAlias`, `providerId`) to prevent query string injection attacks
- **Shared Auth Utility** — Authentication logic extracted to `@/shared/utils/apiAuth.ts`, eliminating code duplication across `/api/models/alias` and `/api/provider-models`
### 🔧 Improvements
- **Toast Notifications** — Replaced blocking `alert()` calls with non-blocking `notify.error`/`notify.success` toast notifications matching the project's notification system
- **Transactional Save** — Model persistence is now transactional: database save must succeed before alias creation, preventing inconsistent state
- **Consistent Error Handling** — All model operations (add, import, delete) now provide user-facing error/success feedback via toast notifications
- **ComboFormModal Matching** — Improved provider node matching by ID or prefix for combo model selection
- 🛣️ **Advanced routing services** — Priority-based routing, global strategy configuration
- 💰 **Cost analytics dashboard** — Token cost tracking and analytics visualization
- 💎 **Pricing overhaul** — Comprehensive pricing data for all supported providers and models
- 📦 **npm badge & CLI options**npm version badge in README, CLI options table, automated release docs
---
## [1.4.3] — 2026-02-23
## [0.1.0] — 2026-02-14
### 🐛 Bug Fix
### Added
- **OAuth LAN Access** — Fixed OAuth flow for remote/LAN IP access (`192.168.x.x`). Previously, LAN IPs incorrectly used popup mode, leading to a broken redirect loop. Now defaults to manual callback URL input mode for non-localhost access
- 🎉 **Initial OmniRoute release** — Rebranded from 9router with full feature set
- 🔄 **28 AI providers** — OpenAI, Claude, Gemini, Copilot, DeepSeek, Groq, xAI, Mistral, Qwen, iFlow, and more
- 🎯 **Smart fallback** — 3-tier auto-routing (Subscription → Cheap → Free)
- 🔀 **Format translation** — Seamless OpenAI ↔ Claude ↔ Gemini format conversion
- 👥 **Multi-account support** — Multiple accounts per provider with round-robin
- 🔐 **OAuth 2.0 (PKCE)** — Automatic token management and refresh
- 📊 **Usage tracking** — Real-time quota monitoring with reset countdown
- 🎨 **Custom combos** — Create model combinations with fallback chains
- ☁️ **Cloud sync** — Sync configuration across devices via Cloudflare Worker
- 📖 **OpenAPI specification** — Full API documentation
- 🛡️ **SOCKS5 proxy support** — Outbound proxy for upstream provider calls
- 🔌 **New endpoints**`/v1/rerank`, `/v1/audio/*`, `/v1/moderations`
- 📦 **npm CLI package**`npm install -g omniroute` with auto-launch
- 🐳 **Docker support** — Multi-stage Dockerfile with `base` and `cli` profiles
- 🔒 **Security policy**`SECURITY.md` with vulnerability reporting guidelines
- 🧪 **CI/CD pipeline** — GitHub Actions for lint, build, test, and npm publish
---
## [1.4.2] — 2026-02-23
### 🐛 Bug Fix
- **OAuth Token Refresh** — Fixed `client_secret is missing` error for Google-based OAuth providers (Antigravity, Gemini, Gemini CLI, iFlow). Desktop/CLI OAuth secrets are now hardcoded as defaults since Next.js inlined empty strings at build time.
---
## [1.4.1] — 2026-02-23
### 🔧 Improvements
- **Endpoint Page Cleanup** — Removed redundant API Key Management section from Endpoint page (now fully managed in the dedicated API Manager page)
- **CI/CD** — Added `deploy-vps.yml` workflow for automatic VPS deployment on new releases
---
## [1.4.0] — 2026-02-23
> ### ✨ Feature Release — Dedicated API Key Manager with Model Permissions
>
> Community-contributed API Key Manager page with model-level access control, enhanced with usage statistics, key status indicators, and improved UX.
### ✨ New Features
- **Dedicated API Key Manager** — New `/dashboard/api-manager` page for managing API keys, extracted from the Endpoint page. Includes create, delete, and permissions management with a clean table UI ([PR #118](https://github.com/diegosouzapw/OmniRoute/pull/118) by [@nyatoru](https://github.com/nyatoru))
- **Model-Level API Key Permissions** — Restrict API keys to specific models using `allowed_models` with wildcard pattern support (e.g., `openai/*`). Toggle between "Allow All" and "Restrict" modes with an intuitive provider-grouped model selector
- **API Key Validation Cache** — 3-tier caching layer (validation, metadata, permission) reduces database hits on every request, with automatic cache invalidation on key changes
- **Usage Statistics Per Key** — Each API key shows total request count and last used timestamp, with a stats summary dashboard (total keys, restricted keys, total requests, models available)
- **Key Status Indicators** — Color-coded lock/unlock icons and copy buttons on each key row for quick identification of restricted vs unrestricted keys
### 🔧 Improvements
- **Endpoint Page Simplified** — API key management removed from Endpoint page and replaced with a prominent link to the API Manager
- **Sidebar Navigation** — New "API Manager" entry with `vpn_key` icon in the sidebar
- **Prepared Statements** — API key database operations now use cached prepared statements for better performance
- **Input Validation** — XSS-safe sanitization and regex validation for key names; ID format validation for API calls
---
## [1.3.1] — 2026-02-23
> ### 🐛 Bugfix Release — Proxy Connection Tests & Compatible Provider Display
>
> Fixes provider connection tests bypassing configured proxy and improves compatible provider display in the request logger.
### 🐛 Bug Fixes
- **Connection Tests Now Use Proxy** — Provider connection tests (`Test Connection` button) now route through the configured proxy (key → combo → provider → global → direct), matching the behavior of real API calls. Previously, `fetch()` was called directly, bypassing the proxy entirely ([#119](https://github.com/diegosouzapw/OmniRoute/issues/119))
- **Compatible Provider Display in Logs** — OpenAI/Anthropic compatible providers now show friendly labels (`OAI-COMPAT`, `ANT-COMPAT`) instead of raw UUID-based IDs in the request logger's provider column, dropdown, and quick filters ([#113](https://github.com/diegosouzapw/OmniRoute/issues/113))
### 🧪 Tests
- **Connection Test Unit Tests** — 26 new test cases covering error classification logic, token expiry detection, and provider display label resolution
---
## [1.3.0] — 2026-02-23
> ### ✨ Feature Release — iFlow Fix, Health Check Logs Toggle, Kilocode Models & Model Deduplication
>
> Community-driven release with iFlow HMAC-SHA256 signature support, health check log management, expanded Kilocode model list, and model deduplication on the dashboard.
### ✨ New Features
- **Hide Health Check Logs** — New toggle in Settings → Appearance to suppress verbose `[HealthCheck]` messages from the server console. Uses a 30-second cache to minimize database reads with request coalescing for concurrent calls ([PR #111](https://github.com/diegosouzapw/OmniRoute/pull/111) by [@nyatoru](https://github.com/nyatoru))
- **Kilocode Custom Models Endpoint** — Added `modelsUrl` support in `RegistryEntry` for providers with non-standard model endpoints. Expanded Kilocode model list from 8 to 26 models including Qwen3, GPT-5, Claude 3 Haiku, Gemini 2.5, DeepSeek V3, Llama 4, and more ([PR #115](https://github.com/diegosouzapw/OmniRoute/pull/115) by [@benzntech](https://github.com/benzntech))
### 🐛 Bug Fixes
- **iFlow 406 Error** — Created dedicated `IFlowExecutor` with HMAC-SHA256 signature support (`session-id`, `x-iflow-timestamp`, `x-iflow-signature` headers). The iFlow provider was previously using the default executor which lacked the required signature headers, causing 406 errors ([#114](https://github.com/diegosouzapw/OmniRoute/issues/114))
- **Duplicate Models in Endpoint Lists** — Filtered out parent models (`!m.parent`) from all model categorization and count logic on the Endpoint page. Provider modal lists also exclude duplicates ([PR #112](https://github.com/diegosouzapw/OmniRoute/pull/112) by [@nyatoru](https://github.com/nyatoru))
### 🧪 Tests
- **IFlowExecutor Unit Tests** — 11 new test cases covering HMAC-SHA256 signature generation, header building, URL construction, body passthrough, and executor registry integration
---
## [1.2.0] — 2026-02-22
> ### ✨ Feature Release — Dashboard Session Auth for Models Endpoint
>
> Dashboard users can now access `/v1/models` via their existing session when API key auth is required.
### ✨ New Features
- **JWT Session Auth Fallback** — When `requireAuthForModels` is enabled, the `/v1/models` endpoint now accepts both API key (Bearer token) for external clients **and** the dashboard JWT session cookie (`auth_token`), allowing logged-in dashboard users to view models without needing an explicit API key ([PR #110](https://github.com/diegosouzapw/OmniRoute/pull/110) by [@nyatoru](https://github.com/nyatoru))
### 🔧 Improvements
- **401 instead of 404** — Authentication failures on `/v1/models` now return `401 Unauthorized` with a structured JSON error body (OpenAI-compatible format) instead of a generic `404 Not Found`, improving debuggability for API clients
- **Simplified auth logic** — Refactored the JWT cookie verification to reuse the same pattern as `apiAuth.ts`, removing redundant same-origin detection (~60 lines) since the `sameSite:lax` + `httpOnly` cookie flags already provide equivalent CSRF protection
---
## [1.1.1] — 2026-02-22
> ### 🐛 Bugfix Release — API Key Creation & Codex Team Plan Quotas
>
> Fixes API key creation crash when `API_KEY_SECRET` is not set and adds Code Review rate limit window to Codex quota display.
### 🐛 Bug Fixes
- **API Key Creation** — Added deterministic fallback for `API_KEY_SECRET` to prevent `crypto.createHmac` crash when the environment variable is not configured. Keys created without the secret are insecure (warned at startup) but the application no longer crashes ([#108](https://github.com/diegosouzapw/OmniRoute/issues/108))
- **Codex Code Review Quota** — Added parsing of the third rate limit window (`code_review_rate_limit`) from the ChatGPT usage API, supporting Plus/Pro/Team plan differences. The dashboard now displays all three quota bars: Session (5h), Weekly, and Code Review ([#106](https://github.com/diegosouzapw/OmniRoute/issues/106))
---
## [1.1.0] — 2026-02-21
> ### 🐛 Bugfix Release — OAuth Client Secret and Codex Business Quotas
>
> Fixes missing remote-server OAuth configurations and adds ChatGPT Business account quota monitoring.
### 🐛 Bug Fixes
- **OAuth Client Secret** — Omitted explicitly empty `client_secret` parameters to resolve token exchange connection rejection on remote servers missing environment variables for Antigravity, Gemini and iFlow ([#103](https://github.com/diegosouzapw/OmniRoute/issues/103))
- **Codex Business Quotas** — Automatically fetches the appropriate ChatGPT workspace to unlock the 5-hour Business usage limits directly inside the Quota tab and mapped `BIZ` string variant perfectly ([#101](https://github.com/diegosouzapw/OmniRoute/issues/101))
---
## [1.0.10] — 2026-02-21
> ### 🐛 Bugfix — Multi-Account Support for Qwen
>
> Solves the issue where adding a second Qwen account would overwrite the first one.
### 🐛 Bug Fixes
- **OAuth Accounts** — Extracted user email from the `id_token` using JWT decoding for Qwen and similar providers, allowing multiple accounts of the same provider to be authenticated simultaneously instead of triggering the fallback overwrite logic ([#99](https://github.com/diegosouzapw/OmniRoute/issues/99))
---
## [1.0.9] — 2026-02-21
> ### 🐛 Hotfix — Settings Persistence
>
> Fixes blocked providers and API auth toggle not being saved after page reload.
### 🐛 Bug Fixes
- **Settings Persistence** — Added `requireAuthForModels` (boolean) and `blockedProviders` (string array) to the Zod validation schema, which was silently stripping these fields during PATCH requests, preventing them from being saved to the database
---
## [1.0.8] — 2026-02-21
> ### 🔒 API Security & Windows Support
>
> Adds API Endpoint Protection for `/models`, Windows server startup fixes, and UI improvements.
### ✨ New Features
- **API Endpoint Protection (`/models`)** — New Security Tab settings to optionally require an API key for the `/v1/models` endpoint (returns 404 when unauthorized) and to selectively block specific providers from appearing in the models list ([#100](https://github.com/diegosouzapw/OmniRoute/issues/100), [#96](https://github.com/diegosouzapw/OmniRoute/issues/96))
- **Interactive Provider UI** — Blocked Providers setting features an interactive chip selector with visual badges for all available AI providers
### 🐛 Bug Fixes
- **Windows Server Startup** — Fixed `ERR_INVALID_FILE_URL_PATH` crash on Windows by safely wrapping `import.meta.url` resolution with a fallback to `process.cwd()` for globally installed npm packages ([#98](https://github.com/diegosouzapw/OmniRoute/issues/98))
- **Combo buttons visibility** — Fixed layout overlap and tight spacing for the Quick Action buttons (Clone / Delete / Test) on the Combos page on narrower screens ([#95](https://github.com/diegosouzapw/OmniRoute/issues/95))
---
## [1.0.7] — 2026-02-20
> ### 🐛 Bugfix Release — OpenAI Compatibility, Custom Models & OAuth UX
>
> Fixes three community-reported issues: stream default now follows OpenAI spec, custom OpenAI-compatible providers appear in `/v1/models`, and Google OAuth shows a clear error + tutorial for remote deployments.
### 🐛 Bug Fixes
- **`stream` defaults to `false`** — Aligns with the OpenAI specification which explicitly states `stream` defaults to `false`. Previously OmniRoute defaulted to `true`, causing SSE data to be returned instead of a JSON object, breaking clients like Spacebot, OpenCode, and standard Python/Rust/Go OpenAI SDKs that don't explicitly set `stream: true` ([#89](https://github.com/diegosouzapw/OmniRoute/issues/89))
- **Custom AI providers now appear in `/v1/models`** — OpenAI-compatible custom providers (e.g. FriendLI) whose provider ID wasn't in the built-in alias map were silently excluded from the models list even when active. Fixed by also checking the raw provider ID from the database against active connections ([#90](https://github.com/diegosouzapw/OmniRoute/issues/90))
- **OAuth `redirect_uri_mismatch` — improved UX for remote deployments** — Google OAuth providers (Antigravity, Gemini CLI) now always use `localhost` as redirect URI matching the registered credentials. Remote-access users see a targeted amber warning with a link to the new setup guide. The token exchange error message explains the root cause and guides users to configure their own credentials ([#91](https://github.com/diegosouzapw/OmniRoute/issues/91))
### 📖 Documentation
- **OAuth em Servidor Remoto tutorial** — New README section with step-by-step guide to configure custom Google Cloud OAuth 2.0 credentials for remote/VPS/Docker deployments
- **`.env.example` Google OAuth block** — Added prominent warning block explaining remote credential requirements with direct links to Google Cloud Console
### 📁 Files Modified
| File | Change |
| -------------------------------------- | ------------------------------------------------------------------------------------------- |
| `open-sse/handlers/chatCore.ts` | `stream` defaults to `false` (was `true`) per OpenAI spec |
| `src/app/api/v1/models/route.ts` | Added raw `providerId` check for custom models active-provider filter |
| `src/shared/components/OAuthModal.tsx` | Force `localhost` redirect for Google OAuth; improved `redirect_uri_mismatch` error message |
| `.env.example` | Added ⚠️ Google OAuth remote credentials block with step-by-step instructions |
| `README.md` | New "🔐 OAuth em Servidor Remoto" tutorial section |
---
## [1.0.6] — 2026-02-20
> ### ✨ Provider & Combo Toggles — Strict Model Filtering
>
> `/v1/models` now shows only models from providers with active connections. Combos and providers can be toggled on/off directly from the dashboard.
### ✨ New Features
- **Provider toggle on Providers page** — Enable/disable all connections for a provider directly from the main Providers list. Toggle is always visible, no hover needed
- **Combo enable/disable toggle** — Each combo on the Combos page now has a toggle. Disabled combos are excluded from `/v1/models`
- **OAuth private IP support** — Expanded localhost detection to include private/LAN IPs (`192.168.x.x`, `10.x.x.x`, `172.16-31.x.x`) for correct OAuth redirect URIs
### 🐛 Bug Fixes
- **`/v1/models` strict filtering** — Models are now shown only from providers with active, enabled connections. Previously, if no connections existed or all were disabled, all 378+ models were shown as a fallback
- **Disabled provider models hidden** — Toggling off a provider immediately removes its models from `/v1/models`
---
## [1.0.5] — 2026-02-20
> ### 🐛 Hotfix — Model Filtering & Docker DATA_DIR
>
> Filters all model types in `/v1/models` by active providers and fixes Docker data directory mismatch.
### 🐛 Bug Fixes
- **`/v1/models` full filtering** — Embedding, image, rerank, audio, and moderation models are now filtered by active provider connections, matching chat model behavior. Providers like Together AI no longer appear without a configured API key (#88)
- **Docker `DATA_DIR`** — Added `ENV DATA_DIR=/app/data` to Dockerfile and `docker-compose.yml` ensuring the volume mount always matches the app data directory — prevents empty database on container recreation
---
## [1.0.4] — 2026-02-19
> ### 🔧 Provider Filtering, OAuth Proxy Fix & Documentation
>
> Dashboard model filtering by active providers, provider enable/disable visual indicators, OAuth login fix for nginx reverse proxy, and LLM onboarding documentation.
### ✨ Features
- **API Models filtering** — `GET /api/models` now returns only models from active providers; use `?all=true` for all models (#85)
- **Provider disabled indicator** — Provider cards show ⏸ "Disabled" badge with reduced opacity when all connections are inactive (#85)
- **`llm.txt`** — Comprehensive LLM onboarding file with project overview, architecture, flows, and conventions (#84)
- **WhatsApp Community** — Added WhatsApp group link to README badges and Support section
### 🐛 Bug Fixes
- **OAuth behind nginx** — Fixed OAuth login failing when behind a reverse proxy by using `window.location.origin` for redirect URI instead of hardcoded `localhost` (#86)
- **`NEXT_PUBLIC_BASE_URL` for OAuth** — Documented env var usage as redirect URI override for proxy deployments (#86)
### 📁 Files Added
| File | Purpose |
| --------- | -------------------------------------------------- |
| `llm.txt` | LLM and contributor onboarding (llms.txt standard) |
### 📁 Files Modified
| File | Change |
| -------------------------------------------------- | ---------------------------------------------------------------- |
| `src/app/api/models/route.ts` | Filter by active providers, `?all=true` param, `available` field |
| `src/app/(dashboard)/dashboard/providers/page.tsx` | `allDisabled` detection + ⏸ badge + opacity-50 on provider cards |
| `src/shared/components/OAuthModal.tsx` | Proxy-aware redirect URI using `window.location.origin` |
| `.env.example` | Documented `NEXT_PUBLIC_BASE_URL` for OAuth behind proxy |
---
## [1.0.3] — 2026-02-19
> ### 📊 Logs Dashboard & Real-Time Console Viewer
>
> Unified logs interface with real-time console log viewer, file-based logging via console interception, and server initialization improvements.
### ✨ Features
- **Logs Dashboard** — Consolidated 4-tab page at `/dashboard/logs` with Request Logs, Proxy Logs, Audit Logs, and Console tabs
- **Console Log Viewer** — Terminal-style real-time log viewer with color-coded log levels, auto-scroll, search/filtering, level filter, and 5-second polling
- **Console Interceptor** — Monkey-patches `console.log/info/warn/error/debug` at server start to capture all application output as JSON lines to `logs/application/app.log`
- **Log Rotation** — Size-based rotation and retention-based cleanup for log files
### 🔧 Improvements
- **Instrumentation consolidation** — Moved `initAuditLog()`, `cleanupExpiredLogs()`, and console interceptor initialization to Next.js `instrumentation.ts` (runs on both dev and prod server start)
- **Structured Logger file output** — `structuredLogger.ts` now also appends JSON log entries to the log file
- **Pino Logger fix** — Fixed broken mix of pino `transport` targets + manual `createWriteStream`; now uses `pino/file` transport targets exclusively with absolute paths
### 🗂️ Files Added
| File | Purpose |
| ---------------------------------------------------- | ----------------------------------------------------------------- |
| `src/app/(dashboard)/dashboard/logs/page.tsx` | Tabbed Logs Dashboard page |
| `src/app/(dashboard)/dashboard/logs/AuditLogTab.tsx` | Audit log tab component extracted from standalone page |
| `src/shared/components/ConsoleLogViewer.tsx` | Terminal-style real-time log viewer |
| `src/app/api/logs/console/route.ts` | API endpoint to read log file (filters last 1h, level, component) |
| `src/lib/consoleInterceptor.ts` | Console method monkey-patching for file capture |
| `src/lib/logRotation.ts` | Log rotation by size and cleanup by retention days |
### 🗂️ Files Modified
| File | Change |
| --------------------------------------- | ------------------------------------------------------------------------------- |
| `src/shared/components/Sidebar.tsx` | Nav: "Request Logs" → "Logs" with `description` icon |
| `src/shared/components/Breadcrumbs.tsx` | Added breadcrumb labels for `logs`, `audit-log`, `console` |
| `src/instrumentation.ts` | Added console interceptor + audit log init + expired log cleanup |
| `src/server-init.ts` | Added console interceptor import (backup init) |
| `src/shared/utils/logger.ts` | Fixed pino file transport using `pino/file` targets |
| `src/shared/utils/structuredLogger.ts` | Added `appendFileSync` file writing + log file config |
| `.env.example` | Added `LOG_TO_FILE`, `LOG_FILE_PATH`, `LOG_MAX_FILE_SIZE`, `LOG_RETENTION_DAYS` |
### ⚙️ Configuration
New environment variables:
| Variable | Default | Description |
| -------------------- | -------------------------- | ----------------------------- |
| `LOG_TO_FILE` | `true` | Enable/disable file logging |
| `LOG_FILE_PATH` | `logs/application/app.log` | Log file path |
| `LOG_MAX_FILE_SIZE` | `50M` | Max file size before rotation |
| `LOG_RETENTION_DAYS` | `7` | Days to retain old log files |
---
## [1.0.2] — 2026-02-18
> ### 🔒 Security Hardening, Architecture Improvements & UX Polish
>
> Comprehensive audit-driven improvements across security, architecture, testing, and user experience.
### 🛡️ Security (Phase 0)
- **Auth guard** — API route protection via `withAuth` middleware for all dashboard routes
- **CSRF protection** — Token-based CSRF guard for all state-changing API routes
- **Request payload validation** — Zod schemas for provider, combo, key, and settings endpoints
- **Prompt injection guard** — Input sanitization against malicious prompt patterns
- **Body size guard** — Route-specific body size limits with dedicated audio upload threshold
- **Rate limiter** — Per-IP rate limiting with configurable windows and thresholds
### 🏗️ Architecture (Phase 12)
- **DI container** — Simple dependency injection container for service registration
- **Policy engine** — Consolidated `PolicyEngine` for routing, security, and rate limiting
- **SQLite migration** — Database migration system with versioned migration runner
- **Graceful shutdown** — Clean server shutdown with connection draining
- **TypeScript fixes** — Resolved all `tsc` errors; removed redundant `@ts-check` directives
- **Pipeline decomposition** — `handleSingleModelChat` decomposed into composable pipeline stages
- **Prompt template versioning** — Version-tracked prompt templates with rollback support
- **Eval scheduling** — Automated evaluation suite scheduling with cron-based runner
- **Plugin architecture** — Extensible plugin system for custom middleware and handlers
### 🧪 Testing & CI (Phase 2)
- **Coverage thresholds** — Jest coverage thresholds enforced in CI (368 tests passing)
- **Proxy pipeline integration tests** — End-to-end tests for the proxy request pipeline
- **CI audit workflow** — npm audit and security scanning in GitHub Actions
- **k6 load tests** — Performance testing with ramping VUs and custom metrics
### ✨ UX & Polish (Phase 34)
- **Session management** — Session info card with login time, age, user agent, and logout
- **Focus indicators** — Global `:focus-visible` styles and `--focus-ring` CSS utility
- **Audit log viewer** — Security event audit log with structured data display
- **Dashboard cleanup** — Removed unused files, fixed Quick Start links to Endpoint page
- **Documentation** — Troubleshooting guide, deployment improvements
---
## [1.0.1] — 2026-02-18
> ### 🔧 API Compatibility & SDK Hardening
>
> Response sanitization, role normalization, and structured output improvements for strict OpenAI SDK compatibility and cross-provider robustness.
### 🛡️ Response Sanitization (NEW)
- **Response sanitizer module** — New `responseSanitizer.ts` strips non-standard fields (`x_groq`, `usage_breakdown`, `service_tier`, etc.) from all OpenAI-format provider responses, fixing OpenAI Python SDK v1.83+ Pydantic validation failures that returned raw strings instead of parsed `ChatCompletion` objects
- **Streaming chunk sanitization** — Passthrough streaming mode now sanitizes each SSE chunk in real-time via `sanitizeStreamingChunk()`, ensuring strict `chat.completion.chunk` schema compliance
- **ID/Object/Usage normalization** — Ensures `id`, `object`, `created`, `model`, `choices`, and `usage` fields always exist with correct types
- **Usage field cleanup** — Strips non-standard usage sub-fields, keeps only `prompt_tokens`, `completion_tokens`, `total_tokens`, and OpenAI detail fields
### 🧠 Think Tag Extraction (NEW)
- **`<think>` tag extraction** — Automatically extracts `<think>...</think>` blocks from thinking model responses (DeepSeek R1, Kimi K2 Thinking, etc.) into OpenAI's standard `reasoning_content` field
- **Streaming think-tag stripping** — Real-time `<think>` extraction in passthrough SSE stream, preventing JSON parsing errors in downstream tools
- **Preserves native reasoning** — Providers that already send `reasoning_content` natively (e.g., OpenAI o1) are not overwritten
### 🔄 Role Normalization (NEW)
- **`developer``system` conversion** — OpenAI's new `developer` role is automatically converted to `system` for all non-OpenAI providers (Claude, Gemini, Kiro, etc.)
- **`system``user` merging** — For models that reject the `system` role (GLM, ERNIE), system messages are intelligently merged into the first user message with clear delimiters
- **Model-aware normalization** — Uses model name prefix matching (`glm-*`, `ernie-*`) for compatibility decisions, avoiding hardcoded provider-level flags
### 📐 Structured Output for Gemini (NEW)
- **`response_format` → Gemini conversion** — OpenAI's `json_schema` structured output is now translated to Gemini's `responseMimeType` + `responseSchema` in the translator pipeline
- **`json_object` support** — `response_format: { type: "json_object" }` maps to Gemini's `application/json` MIME type
- **Schema cleanup** — Automatically removes unsupported JSON Schema keywords (`$schema`, `additionalProperties`) for Gemini compatibility
### 📁 Files Added
| File | Purpose |
| ---------------------------------------- | ---------------------------------------------------------------------- |
| `open-sse/handlers/responseSanitizer.ts` | Response field stripping, think-tag extraction, ID/usage normalization |
| `open-sse/services/roleNormalizer.ts` | Developer→system, system→user role conversion pipeline |
### 📁 Files Modified
| File | Change |
| ------------------------------------------------- | ------------------------------------------------------------------------------- |
| `open-sse/handlers/chatCore.ts` | Integrated response sanitizer for non-streaming OpenAI responses |
| `open-sse/utils/stream.ts` | Integrated streaming chunk sanitizer + think-tag extraction in passthrough mode |
| `open-sse/translator/index.ts` | Integrated role normalizer into the request translation pipeline |
| `open-sse/translator/request/openai-to-gemini.ts` | Added `response_format``responseMimeType`/`responseSchema` conversion |
---
## [1.0.0] — 2026-02-18
> ### 🎉 First Major Release — OmniRoute 1.0
>
> OmniRoute is an intelligent API gateway that unifies 20+ AI providers behind a single OpenAI-compatible endpoint. This release represents the culmination of the entire development effort — from initial prototype to production-ready platform.
### 🧠 Core Routing & Intelligence
- **Smart 4-tier fallback** — Auto-routing: Subscription → Cheap → Free → Emergency
- **6 routing strategies** — Fill First, Round Robin, Power-of-Two-Choices, Random, Least Used, Cost Optimized
- **Semantic caching** — Auto-cache responses for deduplication with configurable TTL
- **Request idempotency** — Prevent duplicate processing of identical requests
- **Thinking budget validation** — Control reasoning token allocation per request
- **System prompt injection** — Configurable global system prompts for all requests
### 🔌 Providers & Models
- **20+ AI providers** — OpenAI, Claude (Anthropic), Gemini, GitHub Copilot, DeepSeek, Groq, xAI, Mistral, Qwen, iFlow, Kiro, OpenRouter, GLM, MiniMax, Kimi, NVIDIA NIM, and more
- **Multi-account support** — Multiple accounts per provider with automatic rotation
- **OAuth 2.0 (PKCE)** — Automatic token management and refresh for Claude Code, Codex, Gemini CLI, Copilot, Kiro
- **Auto token refresh** — Background refresh with expiry detection and unrecoverable error handling
- **Model import** — Import models from API-compatible passthrough providers
- **OpenAI-compatible validation** — Fallback validation via chat completions for providers without `/models` endpoint
- **TLS fingerprint spoofing** — Browser-like TLS fingerprinting via `wreq-js` to bypass bot detection
### 🔄 Format Translation
- **Multi-format translation** — Seamless OpenAI ↔ Claude ↔ Gemini ↔ OpenAI Responses API conversion
- **Translator Playground** — 4 interactive modes:
- **Playground** — Test format translations between any provider formats
- **Chat Tester** — Send real requests through the proxy with visual response rendering
- **Test Bench** — Automated batch testing across multiple providers
- **Live Monitor** — Real-time stream of active proxy requests and translations
### 🎯 Combos & Fallback Chains
- **Custom combos** — Create model combinations with multi-provider fallback chains
- **6 combo balancing strategies** — Fill First, Round Robin, Random, Least Used, P2C, Cost Optimized
- **Combo circuit breaker** — Auto-disable failing providers within a combo chain
### 🛡️ Resilience & Security
- **Circuit breakers** — Auto-recovery with configurable thresholds and cooldown periods
- **Exponential backoff** — Progressive retry delays for failed requests
- **Anti-thundering herd** — Mutex-based protection against concurrent retry storms
- **Rate limit detection** — Per-provider RPM, min gap, and max concurrent request tracking
- **Editable rate limits** — Configurable defaults via Settings → Resilience with persistence
- **Prompt injection guard** — Input sanitization for malicious prompt patterns
- **PII redaction** — Automatic detection and masking of personally identifiable information
- **AES-256-GCM encryption** — Credential encryption at rest
- **IP access control** — Whitelist/blacklist IP filtering
- **SOCKS5 proxy support** — Outbound proxy for upstream provider calls
### 📊 Observability & Analytics
- **Analytics dashboard** — Recharts-based SVG charts: stat cards, model usage bar chart, provider breakdown table with success rates and latency
- **Real-time health monitoring** — Provider health, rate limits, latency telemetry
- **Request logs** — Dedicated page with SQLite-persisted proxy request/response logs
- **Limits & Quotas** — Separate dashboard for quota monitoring with reset countdowns
- **Cost analytics** — Token cost tracking and budget management per provider
- **Request telemetry** — Correlation IDs, structured logging, request timing
### 💾 Database & Backup
- **Dual database** — LowDB (JSON) for config + SQLite for domain state and proxy logs
- **Export database** — `GET /api/db-backups/export` — Download SQLite database file
- **Export all** — `GET /api/db-backups/exportAll` — Full backup as `.tar.gz` archive (DB + settings + combos + providers + masked API keys)
- **Import database** — `POST /api/db-backups/import` — Upload and restore with validation, integrity check, and pre-import backup
- **Automatic backups** — Configurable backup schedule with retention
- **Storage health** — Dashboard widget with database size, path, and backup status
### 🖥️ Dashboard & UI
- **Full dashboard** — Provider management, analytics, health monitoring, settings, CLI tools
- **9 dashboard sections** — Providers, Combos, Analytics, Health, Translator, Settings, CLI Tools, Usage, Endpoint
- **Settings restructure** — 6 tabs: Security, Routing, Resilience, AI, System/Storage, Advanced
- **Shared UI component library** — Reusable components (Avatar, Badge, Button, Card, DataTable, Modal, etc.)
- **Dark/Light/System theme** — Persistent theme selection with system preference detection
- **Agent showcase grid** — Visual grid of 10 AI coding agents in README header
- **Provider logos** — Logo assets for all supported agents and providers
- **Red shield badges** — Styled badge icons across all documentation
### ☁️ Deployment & Infrastructure
- **Docker support** — Multi-stage Dockerfile with `base` and `cli` profiles
- **Docker Hub** — `diegosouzapw/omniroute` with `latest` and versioned tags
- **Docker CI/CD** — GitHub Actions auto-build and push on release
- **npm CLI package** — `npx omniroute` with auto-launch
- **npm CI/CD** — GitHub Actions auto-publish to npm on release
- **Akamai VM deployment** — Production deployment on Nanode 1GB with nginx reverse proxy
- **Cloud sync** — Sync configuration across devices via Cloudflare Worker
- **Edge compatibility** — Native `crypto.randomUUID()` for Cloudflare Workers
### 🧪 Testing & Quality
- **100% TypeScript** — Full migration of `src/` (200+ files) and `open-sse/` (94 files) — zero `@ts-ignore`, zero TypeScript errors
- **CI/CD pipeline** — GitHub Actions for lint, build, test, npm publish, Docker publish
- **Unit tests** — 20+ test suites covering domain logic, security, caching, routing
- **E2E tests** — Playwright specs for API, navigation, and responsive behavior
- **LLM evaluations** — Golden set testing framework with 4 match strategies (`exact`, `contains`, `regex`, `custom`)
- **Security tests** — CLI runtime, Docker hardening, cloud sync, and OpenAI compatibility
### 📖 Documentation
- **8 language READMEs** — English, Portuguese (pt-BR), Spanish, Russian, Chinese (zh-CN), German, French, Italian
- **VM Deployment Guide** — Complete guide (VM + Docker + nginx + Cloudflare + security)
- **Features Gallery** — 9 dashboard screenshots with descriptions
- **API Reference** — Full endpoint documentation including backup/export/import
- **User Guide** — Step-by-step setup, configuration, and usage instructions
- **Architecture docs** — System design, component decomposition, ADRs
- **OpenAPI specification** — Machine-readable API documentation
- **Troubleshooting guide** — Common issues and solutions
- **Security policy** — `SECURITY.md` with vulnerability reporting via GitHub Security Advisories
- **Roadmap** — 150+ planned features across 6 categories
### 🔌 API Endpoints
- `/v1/chat/completions` — OpenAI-compatible chat endpoint with format translation
- `/v1/embeddings` — Embedding generation
- `/v1/images/generations` — Image generation
- `/v1/models` — Model listing with provider filtering
- `/v1/rerank` — Re-ranking endpoint
- `/v1/audio/*` — Audio transcription and translation
- `/v1/moderations` — Content moderation
- `/api/db-backups/export` — Database export
- `/api/db-backups/exportAll` — Full archive export
- `/api/db-backups/import` — Database import with validation
- 30+ dashboard API routes for providers, combos, settings, analytics, health, CLI tools
---
[1.4.7]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.4.7
[1.4.6]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.4.6
[1.4.5]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.4.5
[1.4.4]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.4.4
[1.4.3]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.4.3
[1.4.2]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.4.2
[1.4.1]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.4.1
[1.4.0]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.4.0
[1.3.1]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.3.1
[1.3.0]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.3.0
[1.2.0]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.2.0
[1.1.1]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.1.1
[1.0.7]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.7
[1.0.6]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.6
[1.0.5]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.5
[1.0.4]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.4
[1.1.0]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.1.0
[1.0.1]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.1
[1.0.3]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.3
[1.0.2]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.2
[1.0.0]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.0
[0.8.0]: https://github.com/diegosouzapw/OmniRoute/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/diegosouzapw/OmniRoute/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/diegosouzapw/OmniRoute/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/diegosouzapw/OmniRoute/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/diegosouzapw/OmniRoute/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/diegosouzapw/OmniRoute/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/diegosouzapw/OmniRoute/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v0.1.0
+25 -25
View File
@@ -123,7 +123,7 @@ npm run lint
npm run check
```
Current test status: **368+ unit tests** covering:
Current test status: **320+ unit tests** covering:
- Provider translators and format conversion
- Rate limiting, circuit breaker, and resilience
@@ -138,7 +138,7 @@ Current test status: **368+ unit tests** covering:
- **ESLint** — Run `npm run lint` before committing
- **Prettier** — Auto-formatted via `lint-staged` on commit
- **TypeScript** — All `src/` code uses `.ts`/`.tsx`; document with TSDoc (`@param`, `@returns`, `@throws`)
- **JSDoc** — Document public functions with `@param`, `@returns`, `@throws`
- **No `eval()`** — ESLint enforces `no-eval`, `no-implied-eval`, `no-new-func`
- **Zod validation** — Use Zod schemas for API input validation
@@ -147,34 +147,34 @@ Current test status: **368+ unit tests** covering:
## Project Structure
```
src/ # TypeScript (.ts / .tsx)
src/
├── app/ # Next.js App Router
│ ├── (dashboard)/ # Dashboard pages (.tsx)
│ ├── api/ # API routes (.ts)
│ └── login/ # Auth pages (.tsx)
├── domain/ # Domain types and response helpers (.ts)
├── lib/ # Core business logic (.ts)
│ ├── (dashboard)/ # Dashboard pages
│ ├── api/ # API routes
│ └── login/ # Auth pages
├── domain/ # Domain types and response helpers
├── lib/ # Core business logic
│ ├── db/ # SQLite database layer
│ ├── oauth/ # OAuth services per provider
│ ├── cacheLayer.ts # LRU cache
│ ├── semanticCache.ts # Semantic response cache
│ ├── idempotencyLayer.ts # Request deduplication
│ └── localDb.ts # Settings facade (LowDB for config, SQLite for domain data)
│ ├── cacheLayer.js # LRU cache
│ ├── semanticCache.js # Semantic response cache
│ ├── idempotencyLayer.js # Request deduplication
│ └── localDb.js # LowDB (JSON) storage
├── shared/
│ ├── components/ # React components (.tsx)
│ ├── components/ # React components
│ ├── middleware/ # Correlation IDs, etc.
│ ├── utils/ # Circuit breaker, sanitizer, etc.
│ └── validation/ # Zod schemas
└── sse/ # SSE chat handlers (.ts)
└── sse/ # SSE chat handlers
open-sse/ # @omniroute/open-sse workspace (JavaScript)
open-sse/ # @omniroute/open-sse workspace
├── handlers/ # chatCore.js — main request handler
├── services/ # Rate limit, fallback
├── translators/ # Format converters (OpenAI ↔ Claude ↔ Gemini)
└── utils/ # Progress tracker, stream helpers
tests/
├── unit/ # Node.js test runner (.test.mjs)
├── unit/ # Node.js test runner
└── e2e/ # Playwright tests
docs/ # Documentation
@@ -191,10 +191,10 @@ docs/ # Documentation
### Step 1: OAuth Service (if using OAuth)
Create `src/lib/oauth/services/your-provider.ts` extending `OAuthService`:
Create `src/lib/oauth/services/your-provider.js` extending `OAuthService`:
```typescript
import { OAuthService } from "../OAuthService";
```javascript
import { OAuthService } from "../OAuthService.js";
export class YourProviderService extends OAuthService {
constructor() {
@@ -211,16 +211,16 @@ export class YourProviderService extends OAuthService {
### Step 2: Register Provider
Add to `src/lib/oauth/providers.ts`:
Add to `src/lib/oauth/providers.js`:
```typescript
import { YourProviderService } from "./services/your-provider";
```javascript
import { YourProviderService } from "./services/your-provider.js";
// Add to the providers map
```
### Step 3: Add Constants
Add provider constants in `src/lib/providerConstants.ts`:
Add provider constants in `src/lib/providerConstants.js`:
- Provider prefix (e.g., `yp/`)
- Default models
@@ -232,7 +232,7 @@ Create translator in `open-sse/translators/` if the provider uses a custom API f
### Step 5: Add Timeout
Add request timeout configuration in `src/shared/utils/requestTimeout.ts`.
Add request timeout configuration in `src/shared/utils/requestTimeout.js`.
### Step 6: Add Tests
@@ -249,7 +249,7 @@ Write unit tests in `tests/unit/` covering at minimum:
- [ ] Tests pass (`npm test`)
- [ ] Linting passes (`npm run lint`)
- [ ] Build succeeds (`npm run build`)
- [ ] TypeScript types added for new public functions and interfaces
- [ ] JSDoc added for new public functions
- [ ] No hardcoded secrets or fallback values
- [ ] CHANGELOG updated (if user-facing change)
- [ ] Documentation updated (if applicable)
+1 -2
View File
@@ -20,8 +20,7 @@ ENV NODE_ENV=production
ENV PORT=20128
ENV HOSTNAME=0.0.0.0
# Data directory inside Docker — must match the volume mount in docker-compose.yml
ENV DATA_DIR=/app/data
# Runtime writable location for localDb when DATA_DIR is configured to /app/data
RUN mkdir -p /app/data
COPY --from=builder /app/public ./public
-1000
View File
File diff suppressed because it is too large Load Diff
-999
View File
@@ -1,999 +0,0 @@
<div align="center">
<img src="./docs/screenshots/MainOmniRoute.png" alt="OmniRoute Dashboard" width="800"/>
# 🚀 OmniRoute — El Gateway de IA Gratuito
### Nunca dejes de programar. Enrutamiento inteligente hacia **modelos de IA GRATUITOS y económicos** con fallback automático.
_Tu proxy de API universal — un endpoint, 36+ proveedores, cero tiempo de inactividad._
**Chat Completions • Embeddings • Generación de Imágenes • Audio • Reranking • 100% TypeScript**
---
### 🤖 Proveedor de IA Gratuito para tus agentes de programación favoritos
_Conecta cualquier IDE o herramienta CLI con IA a través de OmniRoute — gateway de API gratuito para programación ilimitada._
<table>
<tr>
<td align="center" width="110">
<a href="https://github.com/cline/cline">
<img src="./public/providers/openclaw.png" alt="OpenClaw" width="48"/><br/>
<b>OpenClaw</b>
</a><br/>
<sub>⭐ 205K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/HKUDS/nanobot">
<img src="./public/providers/nanobot.png" alt="NanoBot" width="48"/><br/>
<b>NanoBot</b>
</a><br/>
<sub>⭐ 20.9K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/sipeed/picoclaw">
<img src="./public/providers/picoclaw.jpg" alt="PicoClaw" width="48"/><br/>
<b>PicoClaw</b>
</a><br/>
<sub>⭐ 14.6K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/zeroclaw-labs/zeroclaw">
<img src="./public/providers/zeroclaw.png" alt="ZeroClaw" width="48"/><br/>
<b>ZeroClaw</b>
</a><br/>
<sub>⭐ 9.9K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/nearai/ironclaw">
<img src="./public/providers/ironclaw.png" alt="IronClaw" width="48"/><br/>
<b>IronClaw</b>
</a><br/>
<sub>⭐ 2.1K</sub>
</td>
</tr>
<tr>
<td align="center" width="110">
<a href="https://github.com/anomalyco/opencode">
<img src="./public/providers/opencode.svg" alt="OpenCode" width="48"/><br/>
<b>OpenCode</b>
</a><br/>
<sub>⭐ 106K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/openai/codex">
<img src="./public/providers/codex.png" alt="Codex CLI" width="48"/><br/>
<b>Codex CLI</b>
</a><br/>
<sub>⭐ 60.8K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/anthropics/claude-code">
<img src="./public/providers/claude.png" alt="Claude Code" width="48"/><br/>
<b>Claude Code</b>
</a><br/>
<sub>⭐ 67.3K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/google-gemini/gemini-cli">
<img src="./public/providers/gemini-cli.png" alt="Gemini CLI" width="48"/><br/>
<b>Gemini CLI</b>
</a><br/>
<sub>⭐ 94.7K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/Kilo-Org/kilocode">
<img src="./public/providers/kilocode.png" alt="Kilo Code" width="48"/><br/>
<b>Kilo Code</b>
</a><br/>
<sub>⭐ 15.5K</sub>
</td>
</tr>
</table>
<sub>📡 Todos los agentes se conectan vía <code>http://localhost:20128/v1</code> o <code>http://cloud.omniroute.online/v1</code> — una configuración, modelos y cuota ilimitados</sub>
---
[![npm version](https://img.shields.io/npm/v/omniroute?color=cb3837&logo=npm)](https://www.npmjs.com/package/omniroute)
[![Docker Hub](https://img.shields.io/docker/v/diegosouzapw/omniroute?label=Docker%20Hub&logo=docker&color=2496ED)](https://hub.docker.com/r/diegosouzapw/omniroute)
[![License](https://img.shields.io/github/license/diegosouzapw/OmniRoute)](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
[![Website](https://img.shields.io/badge/Website-omniroute.online-blue?logo=google-chrome&logoColor=white)](https://omniroute.online)
[![WhatsApp](https://img.shields.io/badge/WhatsApp-Community-25D366?logo=whatsapp&logoColor=white)](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
[🌐 Website](https://omniroute.online) • [🚀 Inicio Rápido](#-inicio-rápido) • [💡 Características](#-características-principales) • [📖 Docs](#-documentación) • [💰 Precios](#-precios-resumidos)
🌐 **Disponible en:** [English](README.md) | [Português](README.pt-BR.md) | [Español](README.es.md) | [Русский](README.ru.md) | [中文](README.zh-CN.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | [Italiano](README.it.md)
</div>
---
## 🤔 ¿Por qué OmniRoute?
**Deja de desperdiciar dinero y chocar con límites:**
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> La cuota de suscripción expira sin usar cada mes
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> Los límites de tasa te detienen en medio de la programación
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> APIs caras ($20-50/mes por proveedor)
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> Cambiar manualmente entre proveedores
**OmniRoute resuelve esto:**
-**Maximiza suscripciones** - Rastrea cuotas, usa cada bit antes del reset
-**Fallback automático** - Suscripción → API Key → Barato → Gratuito, cero tiempo de inactividad
-**Multi-cuenta** - Round-robin entre cuentas por proveedor
-**Universal** - Funciona con Claude Code, Codex, Gemini CLI, Cursor, Cline, OpenClaw, cualquier herramienta CLI
---
## 🔄 Cómo Funciona
```
┌─────────────┐
│ Tu CLI │ (Claude Code, Codex, Gemini CLI, OpenClaw, Cursor, Cline...)
│ Tool │
└──────┬──────┘
│ http://localhost:20128/v1
┌─────────────────────────────────────────┐
│ OmniRoute (Enrutador Inteligente) │
│ • Traducción de formato (OpenAI ↔ Claude) │
│ • Rastreo de cuota + Embeddings + Imágenes │
│ • Renovación automática de tokens │
└──────┬──────────────────────────────────┘
├─→ [Tier 1: SUSCRIPCIÓN] Claude Code, Codex, Gemini CLI
│ ↓ cuota agotada
├─→ [Tier 2: API KEY] DeepSeek, Groq, xAI, Mistral, NVIDIA NIM, etc.
│ ↓ límite de presupuesto
├─→ [Tier 3: BARATO] GLM ($0.6/1M), MiniMax ($0.2/1M)
│ ↓ límite de presupuesto
└─→ [Tier 4: GRATUITO] iFlow, Qwen, Kiro (ilimitado)
Resultado: Nunca dejes de programar, costo mínimo
```
---
## ⚡ Inicio Rápido
**1. Instala globalmente:**
```bash
npm install -g omniroute
omniroute
```
🎉 El Dashboard se abre en `http://localhost:20128`
| Comando | Descripción |
| ----------------------- | ---------------------------------------------- |
| `omniroute` | Iniciar servidor (puerto predeterminado 20128) |
| `omniroute --port 3000` | Usar puerto personalizado |
| `omniroute --no-open` | No abrir navegador automáticamente |
| `omniroute --help` | Mostrar ayuda |
**2. Conecta un proveedor GRATUITO:**
Dashboard → Proveedores → Conectar **Claude Code** o **Antigravity** → Login OAuth → ¡Listo!
**3. Usa en tu herramienta CLI:**
```
Claude Code/Codex/Gemini CLI/OpenClaw/Cursor/Cline Configuración:
Endpoint: http://localhost:20128/v1
API Key: [copiar del dashboard]
Model: if/kimi-k2-thinking
```
**¡Eso es todo!** Comienza a programar con modelos de IA GRATUITOS.
**Alternativa — ejecutar desde código fuente:**
```bash
cp .env.example .env
npm install
PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev
```
---
## 🐳 Docker
OmniRoute está disponible como imagen Docker pública en [Docker Hub](https://hub.docker.com/r/diegosouzapw/omniroute).
**Ejecución rápida:**
```bash
docker run -d \
--name omniroute \
--restart unless-stopped \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
**Con archivo de entorno:**
```bash
# Copia y edita el .env primero
cp .env.example .env
docker run -d \
--name omniroute \
--restart unless-stopped \
--env-file .env \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
**Usando Docker Compose:**
```bash
# Perfil base (sin herramientas CLI)
docker compose --profile base up -d
# Perfil CLI (Claude Code, Codex, OpenClaw integrados)
docker compose --profile cli up -d
```
| Imagen | Tag | Tamaño | Descripción |
| ------------------------ | -------- | ------ | ---------------------- |
| `diegosouzapw/omniroute` | `latest` | ~250MB | Última versión estable |
| `diegosouzapw/omniroute` | `1.0.6` | ~250MB | Versión actual |
---
## 💰 Precios Resumidos
| Tier | Proveedor | Costo | Reset de Cuota | Mejor Para |
| ------------------ | ----------------- | ---------------------------- | ----------------- | ----------------------- |
| **💳 SUSCRIPCIÓN** | Claude Code (Pro) | $20/mes | 5h + semanal | Ya suscrito |
| | Codex (Plus/Pro) | $20-200/mes | 5h + semanal | Usuarios OpenAI |
| | Gemini CLI | **GRATUITO** | 180K/mes + 1K/día | ¡Todos! |
| | GitHub Copilot | $10-19/mes | Mensual | Usuarios GitHub |
| **🔑 API KEY** | NVIDIA NIM | **GRATUITO** (1000 créditos) | Único | Pruebas gratuitas |
| | DeepSeek | Por uso | Ninguno | Mejor precio/calidad |
| | Groq | Tier gratuito + pago | Limitado | Inferencia ultra-rápida |
| | xAI (Grok) | Por uso | Ninguno | Modelos Grok |
| | Mistral | Tier gratuito + pago | Limitado | IA Europea |
| | OpenRouter | Por uso | Ninguno | 100+ modelos |
| **💰 BARATO** | GLM-4.7 | $0.6/1M | Diario 10h | Respaldo económico |
| | MiniMax M2.1 | $0.2/1M | Rotativo 5h | Opción más barata |
| | Kimi K2 | $9/mes fijo | 10M tokens/mes | Costo predecible |
| **🆓 GRATUITO** | iFlow | $0 | Ilimitado | 8 modelos gratuitos |
| | Qwen | $0 | Ilimitado | 3 modelos gratuitos |
| | Kiro | $0 | Ilimitado | Claude gratuito |
**💡 Consejo Pro:** ¡Comienza con Gemini CLI (180K gratis/mes) + iFlow (ilimitado gratis) = $0 de costo!
---
## 🎯 Casos de Uso
### Caso 1: "Tengo suscripción Claude Pro"
**Problema:** La cuota expira sin usar, límites de tasa durante programación intensa
```
Combo: "maximize-claude"
1. cc/claude-opus-4-6 (usar suscripción al máximo)
2. glm/glm-4.7 (respaldo barato cuando la cuota se agota)
3. if/kimi-k2-thinking (fallback de emergencia gratuito)
Costo mensual: $20 (suscripción) + ~$5 (respaldo) = $25 total
vs. $20 + chocar con límites = frustración
```
### Caso 2: "Quiero costo cero"
**Problema:** No puede pagar suscripciones, necesita IA confiable para programar
```
Combo: "free-forever"
1. gc/gemini-3-flash (180K gratis/mes)
2. if/kimi-k2-thinking (ilimitado gratis)
3. qw/qwen3-coder-plus (ilimitado gratis)
Costo mensual: $0
Calidad: Modelos listos para producción
```
### Caso 3: "Necesito programar 24/7, sin interrupciones"
**Problema:** Plazos ajustados, no puede permitirse tiempo de inactividad
```
Combo: "always-on"
1. cc/claude-opus-4-6 (mejor calidad)
2. cx/gpt-5.2-codex (segunda suscripción)
3. glm/glm-4.7 (barato, reset diario)
4. minimax/MiniMax-M2.1 (más barato, reset 5h)
5. if/kimi-k2-thinking (gratuito ilimitado)
Resultado: 5 capas de fallback = cero tiempo de inactividad
```
### Caso 4: "Quiero IA GRATUITA en OpenClaw"
**Problema:** Necesita asistente de IA en apps de mensajería, completamente gratuito
```
Combo: "openclaw-free"
1. if/glm-4.7 (ilimitado gratis)
2. if/minimax-m2.1 (ilimitado gratis)
3. if/kimi-k2-thinking (ilimitado gratis)
Costo mensual: $0
Acceso vía: WhatsApp, Telegram, Slack, Discord, iMessage, Signal...
```
---
## 💡 Características Principales
### 🧠 Enrutamiento e Inteligencia
| Característica | Qué Hace |
| -------------------------------------- | ------------------------------------------------------------------------------- |
| 🎯 **Fallback Inteligente 4 Tiers** | Auto-enrutamiento: Suscripción → API Key → Barato → Gratuito |
| 📊 **Rastreo de Cuota en Tiempo Real** | Conteo de tokens en vivo + countdown de reset por proveedor |
| 🔄 **Traducción de Formato** | OpenAI ↔ Claude ↔ Gemini ↔ Cursor ↔ Kiro transparente |
| 👥 **Soporte Multi-Cuenta** | Múltiples cuentas por proveedor con selección inteligente |
| 🔄 **Renovación Automática de Token** | Tokens OAuth se renuevan automáticamente con reintentos |
| 🎨 **Combos Personalizados** | 6 estrategias: fill-first, round-robin, p2c, random, least-used, cost-optimized |
| 🧩 **Modelos Personalizados** | Agrega cualquier ID de modelo a cualquier proveedor |
| 🌐 **Enrutador Wildcard** | Enruta patrones `provider/*` a cualquier proveedor dinámicamente |
| 🧠 **Presupuesto de Razonamiento** | Modos passthrough, auto, custom y adaptativo para modelos de razonamiento |
| 💬 **Inyección de System Prompt** | System prompt global aplicado en todas las solicitudes |
| 📄 **API Responses** | Soporte completo de la API Responses de OpenAI (`/v1/responses`) para Codex |
### 🎵 APIs Multi-Modal
| Característica | Qué Hace |
| ----------------------------- | ------------------------------------------------------ |
| 🖼️ **Generación de Imágenes** | `/v1/images/generations` — 4 proveedores, 9+ modelos |
| 📐 **Embeddings** | `/v1/embeddings` — 6 proveedores, 9+ modelos |
| 🎤 **Transcripción de Audio** | `/v1/audio/transcriptions` — Compatible con Whisper |
| 🔊 **Texto a Voz** | `/v1/audio/speech` — Síntesis de audio multi-proveedor |
| 🛡️ **Moderaciones** | `/v1/moderations` — Verificaciones de seguridad |
| 🔀 **Reranking** | `/v1/rerank` — Reranking de relevancia de documentos |
### 🛡️ Resiliencia y Seguridad
| Característica | Qué Hace |
| ---------------------------------- | ---------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Auto-apertura/cierre por proveedor con umbrales configurables |
| 🛡️ **Anti-Thundering Herd** | Mutex + semáforo rate-limit para proveedores con API key |
| 🧠 **Caché Semántico** | Caché de dos niveles (firma + semántico) reduce costo y latencia |
| ⚡ **Idempotencia de Solicitud** | Ventana de dedup de 5s para solicitudes duplicadas |
| 🔒 **Spoofing de Fingerprint TLS** | Bypass de detección de bot vía TLS con wreq-js |
| 🌐 **Filtrado de IP** | Allowlist/blocklist para control de acceso a la API |
| 📊 **Rate Limits Editables** | RPM, gap mínimo y concurrencia máxima configurables |
### 📊 Observabilidad y Analytics
| Característica | Qué Hace |
| ------------------------------ | --------------------------------------------------------------------- |
| 📝 **Logs de Solicitud** | Modo debug con logs completos de request/response |
| 💾 **Logs SQLite** | Logs de proxy persistentes sobreviven a reinicios |
| 📊 **Dashboard de Analytics** | Recharts: cards de estadísticas, gráfico de uso, tabla de proveedores |
| 📈 **Rastreo de Progreso** | Eventos de progreso SSE opt-in para streaming |
| 🧪 **Evaluaciones de LLM** | Pruebas con conjunto golden y 4 estrategias de match |
| 🔍 **Telemetría de Solicitud** | Agregación de latencia p50/p95/p99 + rastreo X-Request-Id |
| 📋 **Logs + Cuotas** | Páginas dedicadas para navegación de logs y rastreo de cuotas |
| 🏥 **Dashboard de Salud** | Uptime, estados de circuit breaker, lockouts, stats de caché |
| 💰 **Rastreo de Costos** | Gestión de presupuesto + configuración de precios por modelo |
### ☁️ Deploy y Sincronización
| Característica | Qué Hace |
| --------------------------------- | ------------------------------------------------------------------------------- |
| 💾 **Cloud Sync** | Sincroniza configuraciones entre dispositivos vía Cloudflare Workers |
| 🌐 **Deploy en Cualquier Lugar** | Localhost, VPS, Docker, Cloudflare Workers |
| 🔑 **Gestión de API Keys** | Genera, rota y define alcance de API keys por proveedor |
| 🧙 **Asistente de Configuración** | Setup guiado en 4 pasos para nuevos usuarios |
| 🔧 **Dashboard CLI Tools** | Configuración en un clic para Claude, Codex, Cline, OpenClaw, Kilo, Antigravity |
| 🔄 **Backups de DB** | Backup y restauración automáticos de todas las configuraciones |
<details>
<summary><b>📖 Detalles de Características</b></summary>
### 🎯 Fallback Inteligente 4 Tiers
Crea combos con fallback automático:
```
Combo: "my-coding-stack"
1. cc/claude-opus-4-6 (tu suscripción)
2. nvidia/llama-3.3-70b (API NVIDIA gratuita)
3. glm/glm-4.7 (respaldo barato, $0.6/1M)
4. if/kimi-k2-thinking (fallback gratuito)
→ Cambia automáticamente cuando la cuota se agota o ocurren errores
```
### 📊 Rastreo de Cuota en Tiempo Real
- Consumo de tokens por proveedor
- Countdown de reset (5 horas, diario, semanal)
- Estimación de costo para tiers pagos
- Reportes de gastos mensuales
### 🔄 Traducción de Formato
Traducción transparente entre formatos:
- **OpenAI** ↔ **Claude****Gemini****OpenAI Responses**
- Tu herramienta CLI envía formato OpenAI → OmniRoute traduce → El proveedor recibe formato nativo
- Funciona con cualquier herramienta que soporte endpoints OpenAI personalizados
### 👥 Soporte Multi-Cuenta
- Agrega múltiples cuentas por proveedor
- Round-robin automático o enrutamiento por prioridad
- Fallback a la siguiente cuenta cuando una alcanza la cuota
### 🔄 Renovación Automática de Token
- Los tokens OAuth se renuevan automáticamente antes de expirar
- Sin necesidad de re-autenticación manual
- Experiencia transparente en todos los proveedores
### 🎨 Combos Personalizados
- Crea combinaciones ilimitadas de modelos
- 6 estrategias: fill-first, round-robin, power-of-two-choices, random, least-used, cost-optimized
- Comparte combos entre dispositivos con Cloud Sync
### 🏥 Dashboard de Salud
- Estado del sistema (uptime, versión, uso de memoria)
- Estados de circuit breaker por proveedor (Closed/Open/Half-Open)
- Estado de rate limit y lockouts activos
- Estadísticas de caché de firma
- Telemetría de latencia (p50/p95/p99) + caché de prompt
- Reset de salud con un clic
### 🔧 Playground del Traductor
- Debug, prueba y visualiza traducciones de formato de API
- Envía solicitudes y ve cómo OmniRoute traduce entre formatos de proveedores
- Invaluable para troubleshooting de problemas de integración
### 💾 Cloud Sync
- Sincroniza proveedores, combos y configuraciones entre dispositivos
- Sincronización automática en segundo plano
- Almacenamiento cifrado seguro
</details>
---
## 📖 Guía de Configuración
<details>
<summary><b>💳 Proveedores por Suscripción</b></summary>
### Claude Code (Pro/Max)
```bash
Dashboard → Proveedores → Conectar Claude Code
→ Login OAuth → Renovación automática de token
→ Rastreo de cuota 5h + semanal
Modelos:
cc/claude-opus-4-6
cc/claude-sonnet-4-5-20250929
cc/claude-haiku-4-5-20251001
```
**Consejo Pro:** Usa Opus para tareas complejas, Sonnet para velocidad. ¡OmniRoute rastrea cuota por modelo!
### OpenAI Codex (Plus/Pro)
```bash
Dashboard → Proveedores → Conectar Codex
→ Login OAuth (puerto 1455)
→ Reset 5h + semanal
Modelos:
cx/gpt-5.2-codex
cx/gpt-5.1-codex-max
```
### Gemini CLI (¡GRATUITO 180K/mes!)
```bash
Dashboard → Proveedores → Conectar Gemini CLI
→ Google OAuth
→ 180K completions/mes + 1K/día
Modelos:
gc/gemini-3-flash-preview
gc/gemini-2.5-pro
```
**Mejor Valor:** ¡Tier gratuito enorme! Úsalo antes de los tiers pagos.
### GitHub Copilot
```bash
Dashboard → Proveedores → Conectar GitHub
→ OAuth vía GitHub
→ Reset mensual (1ro del mes)
Modelos:
gh/gpt-5
gh/claude-4.5-sonnet
gh/gemini-3-pro
```
</details>
<details>
<summary><b>🔑 Proveedores por API Key</b></summary>
### NVIDIA NIM (¡GRATUITO 1000 créditos!)
1. Regístrate: [build.nvidia.com](https://build.nvidia.com)
2. Obtén API key gratuita (1000 créditos de inferencia incluidos)
3. Dashboard → Agregar Proveedor → NVIDIA NIM:
- API Key: `nvapi-your-key`
**Modelos:** `nvidia/llama-3.3-70b-instruct`, `nvidia/mistral-7b-instruct`, y 50+ más
**Consejo Pro:** ¡API compatible con OpenAI — funciona perfectamente con la traducción de formato de OmniRoute!
### DeepSeek
1. Regístrate: [platform.deepseek.com](https://platform.deepseek.com)
2. Obtén API key
3. Dashboard → Agregar Proveedor → DeepSeek
**Modelos:** `deepseek/deepseek-chat`, `deepseek/deepseek-coder`
### Groq (¡Tier Gratuito Disponible!)
1. Regístrate: [console.groq.com](https://console.groq.com)
2. Obtén API key (tier gratuito incluido)
3. Dashboard → Agregar Proveedor → Groq
**Modelos:** `groq/llama-3.3-70b`, `groq/mixtral-8x7b`
**Consejo Pro:** ¡Inferencia ultra-rápida — mejor para programación en tiempo real!
### OpenRouter (100+ Modelos)
1. Regístrate: [openrouter.ai](https://openrouter.ai)
2. Obtén API key
3. Dashboard → Agregar Proveedor → OpenRouter
**Modelos:** Accede a 100+ modelos de todos los principales proveedores a través de una única API key.
</details>
<details>
<summary><b>💰 Proveedores Baratos (Respaldo)</b></summary>
### GLM-4.7 (Reset diario, $0.6/1M)
1. Regístrate: [Zhipu AI](https://open.bigmodel.cn/)
2. Obtén API key del Plan Coding
3. Dashboard → Agregar API Key:
- Proveedor: `glm`
- API Key: `your-key`
**Usa:** `glm/glm-4.7`
**Consejo Pro:** ¡El Plan Coding ofrece 3× cuota a 1/7 del costo! Reset diario 10:00 AM.
### MiniMax M2.1 (Reset 5h, $0.20/1M)
1. Regístrate: [MiniMax](https://www.minimax.io/)
2. Obtén API key
3. Dashboard → Agregar API Key
**Usa:** `minimax/MiniMax-M2.1`
**Consejo Pro:** ¡Opción más barata para contexto largo (1M tokens)!
### Kimi K2 ($9/mes fijo)
1. Suscríbete: [Moonshot AI](https://platform.moonshot.ai/)
2. Obtén API key
3. Dashboard → Agregar API Key
**Usa:** `kimi/kimi-latest`
**Consejo Pro:** ¡$9/mes fijo por 10M tokens = $0.90/1M de costo efectivo!
</details>
<details>
<summary><b>🆓 Proveedores GRATUITOS (Respaldo de Emergencia)</b></summary>
### iFlow (8 modelos GRATUITOS)
```bash
Dashboard → Conectar iFlow
→ Login OAuth iFlow
→ Uso ilimitado
Modelos:
if/kimi-k2-thinking
if/qwen3-coder-plus
if/glm-4.7
if/minimax-m2
if/deepseek-r1
```
### Qwen (3 modelos GRATUITOS)
```bash
Dashboard → Conectar Qwen
→ Autorización por código de dispositivo
→ Uso ilimitado
Modelos:
qw/qwen3-coder-plus
qw/qwen3-coder-flash
```
### Kiro (Claude GRATUITO)
```bash
Dashboard → Conectar Kiro
→ AWS Builder ID o Google/GitHub
→ Uso ilimitado
Modelos:
kr/claude-sonnet-4.5
kr/claude-haiku-4.5
```
</details>
<details>
<summary><b>🎨 Crear Combos</b></summary>
### Ejemplo 1: Maximizar Suscripción → Respaldo Barato
```
Dashboard → Combos → Crear Nuevo
Nombre: premium-coding
Modelos:
1. cc/claude-opus-4-6 (Suscripción primaria)
2. glm/glm-4.7 (Respaldo barato, $0.6/1M)
3. minimax/MiniMax-M2.1 (Fallback más barato, $0.20/1M)
Usa en CLI: premium-coding
```
### Ejemplo 2: Solo Gratuito (Costo Cero)
```
Nombre: free-combo
Modelos:
1. gc/gemini-3-flash-preview (180K gratis/mes)
2. if/kimi-k2-thinking (ilimitado)
3. qw/qwen3-coder-plus (ilimitado)
Costo: ¡$0 para siempre!
```
</details>
<details>
<summary><b>🔧 Integración CLI</b></summary>
### Cursor IDE
```
Configuración → Modelos → Avanzado:
OpenAI API Base URL: http://localhost:20128/v1
OpenAI API Key: [del dashboard OmniRoute]
Model: cc/claude-opus-4-6
```
### Claude Code
Usa la página **CLI Tools** en el dashboard para configuración en un clic, o edita `~/.claude/settings.json` manualmente.
### Codex CLI
```bash
export OPENAI_BASE_URL="http://localhost:20128"
export OPENAI_API_KEY="your-omniroute-api-key"
codex "your prompt"
```
### OpenClaw
**Opción 1 — Dashboard (recomendado):**
```
Dashboard → CLI Tools → OpenClaw → Seleccionar Modelo → Aplicar
```
**Opción 2 — Manual:** Edita `~/.openclaw/openclaw.json`:
```json
{
"models": {
"providers": {
"omniroute": {
"baseUrl": "http://127.0.0.1:20128/v1",
"apiKey": "sk_omniroute",
"api": "openai-completions"
}
}
}
}
```
> **Nota:** OpenClaw solo funciona con OmniRoute local. Usa `127.0.0.1` en lugar de `localhost` para evitar problemas de resolución IPv6.
### Cline / Continue / RooCode
```
Configuración → Configuración de API:
Proveedor: OpenAI Compatible
Base URL: http://localhost:20128/v1
API Key: [del dashboard OmniRoute]
Model: if/kimi-k2-thinking
```
</details>
---
## 📊 Modelos Disponibles
<details>
<summary><b>Ver todos los modelos disponibles</b></summary>
**Claude Code (`cc/`)** - Pro/Max:
- `cc/claude-opus-4-6`
- `cc/claude-sonnet-4-5-20250929`
- `cc/claude-haiku-4-5-20251001`
**Codex (`cx/`)** - Plus/Pro:
- `cx/gpt-5.2-codex`
- `cx/gpt-5.1-codex-max`
**Gemini CLI (`gc/`)** - GRATUITO:
- `gc/gemini-3-flash-preview`
- `gc/gemini-2.5-pro`
**GitHub Copilot (`gh/`)**:
- `gh/gpt-5`
- `gh/claude-4.5-sonnet`
**NVIDIA NIM (`nvidia/`)** - Créditos GRATUITOS:
- `nvidia/llama-3.3-70b-instruct`
- `nvidia/mistral-7b-instruct`
- 50+ más modelos en [build.nvidia.com](https://build.nvidia.com)
**GLM (`glm/`)** - $0.6/1M:
- `glm/glm-4.7`
**MiniMax (`minimax/`)** - $0.2/1M:
- `minimax/MiniMax-M2.1`
**iFlow (`if/`)** - GRATUITO:
- `if/kimi-k2-thinking`
- `if/qwen3-coder-plus`
- `if/deepseek-r1`
- `if/glm-4.7`
- `if/minimax-m2`
**Qwen (`qw/`)** - GRATUITO:
- `qw/qwen3-coder-plus`
- `qw/qwen3-coder-flash`
**Kiro (`kr/`)** - GRATUITO:
- `kr/claude-sonnet-4.5`
- `kr/claude-haiku-4.5`
**OpenRouter (`or/`)** - 100+ modelos:
- `or/anthropic/claude-4-sonnet`
- `or/google/gemini-2.5-pro`
- Cualquier modelo de [openrouter.ai/models](https://openrouter.ai/models)
</details>
---
## 🧪 Evaluaciones (Evals)
OmniRoute incluye un framework de evaluación integrado para probar la calidad de respuestas de LLM contra un conjunto golden. Accede vía **Analytics → Evals** en el dashboard.
### Conjunto Golden Integrado
El "OmniRoute Golden Set" precargado contiene 10 casos de prueba que cubren:
- Saludos, matemáticas, geografía, generación de código
- Conformidad de formato JSON, traducción, markdown
- Rechazo de seguridad (contenido dañino), conteo, lógica booleana
### Estrategias de Evaluación
| Estrategia | Descripción | Ejemplo |
| ---------- | ---------------------------------------------------- | -------------------------------- |
| `exact` | La salida debe coincidir exactamente | `"4"` |
| `contains` | La salida debe contener subcadena (case-insensitive) | `"Paris"` |
| `regex` | La salida debe coincidir con el patrón regex | `"1.*2.*3"` |
| `custom` | Función JS personalizada retorna true/false | `(output) => output.length > 10` |
---
## 🐛 Solución de Problemas
<details>
<summary><b>Haz clic para expandir la guía de solución de problemas</b></summary>
**"Language model did not provide messages"**
- Cuota del proveedor agotada → Verifica el rastreador de cuota en el dashboard
- Solución: Usa combo con fallback o cambia a tier más barato
**Rate limiting**
- Cuota de suscripción agotada → Fallback a GLM/MiniMax
- Agrega combo: `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
**Token OAuth expirado**
- Renovado automáticamente por OmniRoute
- Si persiste: Dashboard → Proveedor → Reconectar
**Costos altos**
- Verifica estadísticas de uso en Dashboard → Costos
- Cambia modelo primario a GLM/MiniMax
- Usa tier gratuito (Gemini CLI, iFlow) para tareas no críticas
**Dashboard se abre en el puerto equivocado**
- Establece `PORT=20128` y `NEXT_PUBLIC_BASE_URL=http://localhost:20128`
**Errores de cloud sync**
- Verifica que `BASE_URL` apunte a tu instancia en ejecución
- Verifica que `CLOUD_URL` apunte a tu endpoint cloud esperado
- Mantén los valores `NEXT_PUBLIC_*` alineados con los valores del servidor
**Primer login no funciona**
- Verifica `INITIAL_PASSWORD` en `.env`
- Si no está definido, la contraseña predeterminada es `123456`
**Sin logs de solicitud**
- Establece `ENABLE_REQUEST_LOGS=true` en `.env`
**Prueba de conexión muestra "Invalid" para proveedores compatibles con OpenAI**
- Muchos proveedores no exponen el endpoint `/models`
- OmniRoute v1.0.6+ incluye validación vía chat completions como fallback
- Asegúrate de que la URL base incluya el sufijo `/v1`
</details>
---
## 🛠️ Stack Tecnológico
- **Runtime**: Node.js 20+
- **Lenguaje**: TypeScript 5.9 — **100% TypeScript** en `src/` y `open-sse/` (v1.0.6)
- **Framework**: Next.js 16 + React 19 + Tailwind CSS 4
- **Base de Datos**: LowDB (JSON) + SQLite (estado del dominio + logs de proxy)
- **Streaming**: Server-Sent Events (SSE)
- **Auth**: OAuth 2.0 (PKCE) + JWT + API Keys
- **Testing**: Node.js test runner (368+ tests unitarios)
- **CI/CD**: GitHub Actions (publicación automática npm + Docker Hub en release)
- **Website**: [omniroute.online](https://omniroute.online)
- **Paquete**: [npmjs.com/package/omniroute](https://www.npmjs.com/package/omniroute)
- **Docker**: [hub.docker.com/r/diegosouzapw/omniroute](https://hub.docker.com/r/diegosouzapw/omniroute)
- **Resiliencia**: Circuit breaker, backoff exponencial, anti-thundering herd, spoofing TLS
---
## 📖 Documentación
| Documento | Descripción |
| ------------------------------------------------ | -------------------------------------------------- |
| [Guía del Usuario](docs/USER_GUIDE.md) | Proveedores, combos, integración CLI, deploy |
| [Referencia de API](docs/API_REFERENCE.md) | Todos los endpoints con ejemplos |
| [Solución de Problemas](docs/TROUBLESHOOTING.md) | Problemas comunes y soluciones |
| [Arquitectura](docs/ARCHITECTURE.md) | Arquitectura del sistema e internos |
| [Contribuir](CONTRIBUTING.md) | Setup de desarrollo y directrices |
| [Spec OpenAPI](docs/openapi.yaml) | Especificación OpenAPI 3.0 |
| [Política de Seguridad](SECURITY.md) | Reportar vulnerabilidades y prácticas de seguridad |
---
## 📧 Soporte
> 💬 **¡Únete a la comunidad!** [Grupo WhatsApp](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t) — Obtén ayuda, comparte consejos y mantente al día.
- **Website**: [omniroute.online](https://omniroute.online)
- **GitHub**: [github.com/diegosouzapw/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- **Issues**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
- **WhatsApp**: [Grupo de la Comunidad](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
- **Proyecto Original**: [9router por decolua](https://github.com/decolua/9router)
---
## 👥 Contribuidores
[![Contributors](https://contrib.rocks/image?repo=diegosouzapw/OmniRoute&max=100&columns=20&anon=1)](https://github.com/diegosouzapw/OmniRoute/graphs/contributors)
### Cómo Contribuir
1. Haz fork del repositorio
2. Crea tu rama de funcionalidad (`git checkout -b feature/amazing-feature`)
3. Haz commit de tus cambios (`git commit -m 'Add amazing feature'`)
4. Haz push a la rama (`git push origin feature/amazing-feature`)
5. Abre un Pull Request
Consulta [CONTRIBUTING.md](CONTRIBUTING.md) para directrices detalladas.
### Lanzar una Nueva Versión
```bash
# Crea un release — la publicación en npm ocurre automáticamente
gh release create v1.0.6 --title "v1.0.6" --generate-notes
```
---
## 📊 Historial de Stars
<a href="https://star-history.com/#diegosouzapw/OmniRoute&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date" />
</picture>
</a>
---
## 🙏 Agradecimientos
Agradecimiento especial a **[9router](https://github.com/decolua/9router)** por **[decolua](https://github.com/decolua)** — el proyecto original que inspiró este fork. OmniRoute se construye sobre esa increíble base con características adicionales, APIs multi-modal y una reescritura completa en TypeScript.
Agradecimiento especial a **[CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI)** — la implementación original en Go que inspiró esta adaptación a JavaScript.
---
## 📄 Licencia
Licencia MIT - consulta [LICENSE](LICENSE) para detalles.
---
<div align="center">
<sub>Hecho con ❤️ para desarrolladores que programan 24/7</sub>
<br/>
<sub><a href="https://omniroute.online">omniroute.online</a></sub>
</div>
-999
View File
@@ -1,999 +0,0 @@
<div align="center">
<img src="./docs/screenshots/MainOmniRoute.png" alt="OmniRoute Dashboard" width="800"/>
# 🚀 OmniRoute — La Passerelle IA Gratuite
### N'arrêtez jamais de coder. Routage intelligent vers des **modèles IA GRATUITS et économiques** avec fallback automatique.
_Votre proxy API universel — un endpoint, 36+ fournisseurs, zéro temps d'arrêt._
**Chat Completions • Embeddings • Génération d'images • Audio • Reranking • 100% TypeScript**
---
### 🤖 Fournisseur IA gratuit pour vos agents de programmation préférés
_Connectez n'importe quel IDE ou outil CLI alimenté par l'IA via OmniRoute — passerelle API gratuite pour un codage illimité._
<table>
<tr>
<td align="center" width="110">
<a href="https://github.com/cline/cline">
<img src="./public/providers/openclaw.png" alt="OpenClaw" width="48"/><br/>
<b>OpenClaw</b>
</a><br/>
<sub>⭐ 205K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/HKUDS/nanobot">
<img src="./public/providers/nanobot.png" alt="NanoBot" width="48"/><br/>
<b>NanoBot</b>
</a><br/>
<sub>⭐ 20.9K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/sipeed/picoclaw">
<img src="./public/providers/picoclaw.jpg" alt="PicoClaw" width="48"/><br/>
<b>PicoClaw</b>
</a><br/>
<sub>⭐ 14.6K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/zeroclaw-labs/zeroclaw">
<img src="./public/providers/zeroclaw.png" alt="ZeroClaw" width="48"/><br/>
<b>ZeroClaw</b>
</a><br/>
<sub>⭐ 9.9K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/nearai/ironclaw">
<img src="./public/providers/ironclaw.png" alt="IronClaw" width="48"/><br/>
<b>IronClaw</b>
</a><br/>
<sub>⭐ 2.1K</sub>
</td>
</tr>
<tr>
<td align="center" width="110">
<a href="https://github.com/anomalyco/opencode">
<img src="./public/providers/opencode.svg" alt="OpenCode" width="48"/><br/>
<b>OpenCode</b>
</a><br/>
<sub>⭐ 106K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/openai/codex">
<img src="./public/providers/codex.png" alt="Codex CLI" width="48"/><br/>
<b>Codex CLI</b>
</a><br/>
<sub>⭐ 60.8K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/anthropics/claude-code">
<img src="./public/providers/claude.png" alt="Claude Code" width="48"/><br/>
<b>Claude Code</b>
</a><br/>
<sub>⭐ 67.3K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/google-gemini/gemini-cli">
<img src="./public/providers/gemini-cli.png" alt="Gemini CLI" width="48"/><br/>
<b>Gemini CLI</b>
</a><br/>
<sub>⭐ 94.7K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/Kilo-Org/kilocode">
<img src="./public/providers/kilocode.png" alt="Kilo Code" width="48"/><br/>
<b>Kilo Code</b>
</a><br/>
<sub>⭐ 15.5K</sub>
</td>
</tr>
</table>
<sub>📡 Tous les agents se connectent via <code>http://localhost:20128/v1</code> ou <code>http://cloud.omniroute.online/v1</code> — une configuration, modèles et quota illimités</sub>
---
[![npm version](https://img.shields.io/npm/v/omniroute?color=cb3837&logo=npm)](https://www.npmjs.com/package/omniroute)
[![Docker Hub](https://img.shields.io/docker/v/diegosouzapw/omniroute?label=Docker%20Hub&logo=docker&color=2496ED)](https://hub.docker.com/r/diegosouzapw/omniroute)
[![License](https://img.shields.io/github/license/diegosouzapw/OmniRoute)](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
[![Website](https://img.shields.io/badge/Website-omniroute.online-blue?logo=google-chrome&logoColor=white)](https://omniroute.online)
[![WhatsApp](https://img.shields.io/badge/WhatsApp-Community-25D366?logo=whatsapp&logoColor=white)](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
[🌐 Site web](https://omniroute.online) • [🚀 Démarrage rapide](#-démarrage-rapide) • [💡 Fonctionnalités](#-fonctionnalités-principales) • [📖 Docs](#-documentation) • [💰 Tarifs](#-aperçu-des-tarifs)
🌐 **Disponible en :** [English](README.md) | [Português](README.pt-BR.md) | [Español](README.es.md) | [Русский](README.ru.md) | [中文](README.zh-CN.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | [Italiano](README.it.md)
</div>
---
## 🤔 Pourquoi OmniRoute ?
**Arrêtez de gaspiller de l'argent et de vous heurter aux limites :**
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> Le quota d'abonnement expire inutilisé chaque mois
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> Les limites de débit vous arrêtent en plein codage
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> APIs coûteuses (20-50 $/mois par fournisseur)
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> Changement manuel entre fournisseurs
**OmniRoute résout ces problèmes :**
-**Maximisez les abonnements** — Suivez les quotas, utilisez chaque bit avant la réinitialisation
-**Fallback automatique** — Abonnement → Clé API → Économique → Gratuit, zéro temps d'arrêt
-**Multi-comptes** — Round-robin entre les comptes par fournisseur
-**Universel** — Fonctionne avec Claude Code, Codex, Gemini CLI, Cursor, Cline, OpenClaw, tout outil CLI
---
## 🔄 Comment ça fonctionne
```
┌─────────────┐
│ Votre CLI │ (Claude Code, Codex, Gemini CLI, OpenClaw, Cursor, Cline...)
│ Tool │
└──────┬──────┘
│ http://localhost:20128/v1
┌─────────────────────────────────────────┐
│ OmniRoute (Routeur intelligent) │
│ • Traduction de format (OpenAI ↔ Claude) │
│ • Suivi des quotas + Embeddings + Images │
│ • Renouvellement automatique des tokens │
└──────┬──────────────────────────────────┘
├─→ [Tier 1: ABONNEMENT] Claude Code, Codex, Gemini CLI
│ ↓ quota épuisé
├─→ [Tier 2: CLÉ API] DeepSeek, Groq, xAI, Mistral, NVIDIA NIM, etc.
│ ↓ limite de budget
├─→ [Tier 3: ÉCONOMIQUE] GLM ($0.6/1M), MiniMax ($0.2/1M)
│ ↓ limite de budget
└─→ [Tier 4: GRATUIT] iFlow, Qwen, Kiro (illimité)
Résultat : Ne jamais arrêter de coder, coût minimal
```
---
## ⚡ Démarrage rapide
**1. Installer globalement :**
```bash
npm install -g omniroute
omniroute
```
🎉 Le tableau de bord s'ouvre sur `http://localhost:20128`
| Commande | Description |
| ----------------------- | ------------------------------------------- |
| `omniroute` | Démarrer le serveur (port par défaut 20128) |
| `omniroute --port 3000` | Utiliser un port personnalisé |
| `omniroute --no-open` | Ne pas ouvrir le navigateur automatiquement |
| `omniroute --help` | Afficher l'aide |
**2. Connecter un fournisseur GRATUIT :**
Tableau de bord → Fournisseurs → Connecter **Claude Code** ou **Antigravity** → Connexion OAuth → Terminé !
**3. Utiliser dans votre outil CLI :**
```
Claude Code/Codex/Gemini CLI/OpenClaw/Cursor/Cline Paramètres :
Endpoint : http://localhost:20128/v1
API Key : [copier depuis le tableau de bord]
Model : if/kimi-k2-thinking
```
**C'est tout !** Commencez à coder avec des modèles IA GRATUITS.
**Alternative — exécuter depuis le code source :**
```bash
cp .env.example .env
npm install
PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev
```
---
## 🐳 Docker
OmniRoute est disponible en tant qu'image Docker publique sur [Docker Hub](https://hub.docker.com/r/diegosouzapw/omniroute).
**Démarrage rapide :**
```bash
docker run -d \
--name omniroute \
--restart unless-stopped \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
**Avec fichier d'environnement :**
```bash
# Copier et modifier le .env d'abord
cp .env.example .env
docker run -d \
--name omniroute \
--restart unless-stopped \
--env-file .env \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
**Avec Docker Compose :**
```bash
# Profil de base (sans outils CLI)
docker compose --profile base up -d
# Profil CLI (Claude Code, Codex, OpenClaw intégrés)
docker compose --profile cli up -d
```
| Image | Tag | Taille | Description |
| ------------------------ | -------- | ------ | ----------------------- |
| `diegosouzapw/omniroute` | `latest` | ~250MB | Dernière version stable |
| `diegosouzapw/omniroute` | `1.0.6` | ~250MB | Version actuelle |
---
## 💰 Aperçu des tarifs
| Tier | Fournisseur | Coût | Réinitialisation | Idéal pour |
| ----------------- | ----------------- | -------------------------- | ------------------- | ----------------------------- |
| **💳 ABONNEMENT** | Claude Code (Pro) | 20 $/mois | 5h + hebdomadaire | Déjà abonné |
| | Codex (Plus/Pro) | 20-200 $/mois | 5h + hebdomadaire | Utilisateurs OpenAI |
| | Gemini CLI | **GRATUIT** | 180K/mois + 1K/jour | Tout le monde ! |
| | GitHub Copilot | 10-19 $/mois | Mensuel | Utilisateurs GitHub |
| **🔑 CLÉ API** | NVIDIA NIM | **GRATUIT** (1000 crédits) | Unique | Tests gratuits |
| | DeepSeek | À l'usage | Aucune | Meilleur rapport qualité-prix |
| | Groq | Niveau gratuit + payant | Limité | Inférence ultra-rapide |
| | xAI (Grok) | À l'usage | Aucune | Modèles Grok |
| | Mistral | Niveau gratuit + payant | Limité | IA européenne |
| | OpenRouter | À l'usage | Aucune | 100+ modèles |
| **💰 ÉCONOMIQUE** | GLM-4.7 | 0,6 $/1M | Quotidien 10h | Backup économique |
| | MiniMax M2.1 | 0,2 $/1M | Rotatif 5h | Option la moins chère |
| | Kimi K2 | 9 $/mois fixe | 10M tokens/mois | Coût prévisible |
| **🆓 GRATUIT** | iFlow | 0 $ | Illimité | 8 modèles gratuits |
| | Qwen | 0 $ | Illimité | 3 modèles gratuits |
| | Kiro | 0 $ | Illimité | Claude gratuit |
**💡 Conseil Pro :** Commencez avec Gemini CLI (180K gratuits/mois) + iFlow (illimité gratuit) = 0 $ de coût !
---
## 🎯 Cas d'utilisation
### Cas 1 : « J'ai un abonnement Claude Pro »
**Problème :** Le quota expire inutilisé, limites de débit pendant le codage intensif
```
Combo : "maximize-claude"
1. cc/claude-opus-4-6 (utiliser l'abonnement au maximum)
2. glm/glm-4.7 (backup économique quand le quota est épuisé)
3. if/kimi-k2-thinking (fallback d'urgence gratuit)
Coût mensuel : 20 $ (abonnement) + ~5 $ (backup) = 25 $ au total
vs. 20 $ + atteindre les limites = frustration
```
### Cas 2 : « Je veux zéro coût »
**Problème :** Impossible de payer des abonnements, besoin d'IA fiable pour coder
```
Combo : "free-forever"
1. gc/gemini-3-flash (180K gratuits/mois)
2. if/kimi-k2-thinking (illimité gratuit)
3. qw/qwen3-coder-plus (illimité gratuit)
Coût mensuel : 0 $
Qualité : Modèles prêts pour la production
```
### Cas 3 : « Je dois coder 24/7, sans interruption »
**Problème :** Délais serrés, ne peut pas se permettre de temps d'arrêt
```
Combo : "always-on"
1. cc/claude-opus-4-6 (meilleure qualité)
2. cx/gpt-5.2-codex (deuxième abonnement)
3. glm/glm-4.7 (économique, reset quotidien)
4. minimax/MiniMax-M2.1 (le moins cher, reset 5h)
5. if/kimi-k2-thinking (gratuit illimité)
Résultat : 5 niveaux de fallback = zéro temps d'arrêt
```
### Cas 4 : « Je veux l'IA GRATUITE dans OpenClaw »
**Problème :** Besoin d'assistant IA dans les apps de messagerie, entièrement gratuit
```
Combo : "openclaw-free"
1. if/glm-4.7 (illimité gratuit)
2. if/minimax-m2.1 (illimité gratuit)
3. if/kimi-k2-thinking (illimité gratuit)
Coût mensuel : 0 $
Accès via : WhatsApp, Telegram, Slack, Discord, iMessage, Signal...
```
---
## 💡 Fonctionnalités principales
### 🧠 Routage & Intelligence
| Fonctionnalité | Ce qu'elle fait |
| ------------------------------------- | ------------------------------------------------------------------------------- |
| 🎯 **Fallback intelligent 4 niveaux** | Auto-routage : Abonnement → Clé API → Économique → Gratuit |
| 📊 **Suivi des quotas en temps réel** | Comptage de tokens en direct + compte à rebours de réinitialisation |
| 🔄 **Traduction de format** | OpenAI ↔ Claude ↔ Gemini ↔ Cursor ↔ Kiro transparent |
| 👥 **Support multi-comptes** | Plusieurs comptes par fournisseur avec sélection intelligente |
| 🔄 **Renouvellement auto des tokens** | Les tokens OAuth se renouvellent automatiquement avec retry |
| 🎨 **Combos personnalisés** | 6 stratégies : fill-first, round-robin, p2c, random, least-used, cost-optimized |
| 🧩 **Modèles personnalisés** | Ajoutez n'importe quel ID de modèle à n'importe quel fournisseur |
| 🌐 **Routeur wildcard** | Routez les patterns `provider/*` vers n'importe quel fournisseur dynamiquement |
| 🧠 **Budget de raisonnement** | Modes passthrough, auto, custom et adaptive pour les modèles de raisonnement |
| 💬 **Injection System Prompt** | System prompt global appliqué à toutes les requêtes |
| 📄 **API Responses** | Support complet de l'API Responses d'OpenAI (`/v1/responses`) pour Codex |
### 🎵 APIs multi-modales
| Fonctionnalité | Ce qu'elle fait |
| -------------------------- | ------------------------------------------------------- |
| 🖼️ **Génération d'images** | `/v1/images/generations` — 4 fournisseurs, 9+ modèles |
| 📐 **Embeddings** | `/v1/embeddings` — 6 fournisseurs, 9+ modèles |
| 🎤 **Transcription audio** | `/v1/audio/transcriptions` — compatible Whisper |
| 🔊 **Texte vers parole** | `/v1/audio/speech` — synthèse audio multi-fournisseur |
| 🛡️ **Modérations** | `/v1/moderations` — vérifications de sécurité |
| 🔀 **Reranking** | `/v1/rerank` — reclassement de pertinence des documents |
### 🛡️ Résilience & Sécurité
| Fonctionnalité | Ce qu'elle fait |
| ------------------------------- | -------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Ouverture/fermeture auto par fournisseur avec seuils configurables |
| 🛡️ **Anti-Thundering Herd** | Mutex + sémaphore de rate-limit pour les fournisseurs avec clé API |
| 🧠 **Cache sémantique** | Cache à deux niveaux (signature + sémantique) réduit coût et latence |
| ⚡ **Idempotence des requêtes** | Fenêtre de dédup 5s pour les requêtes dupliquées |
| 🔒 **Spoofing TLS Fingerprint** | Contournement de détection de bot via wreq-js |
| 🌐 **Filtrage IP** | Allowlist/blocklist pour le contrôle d'accès API |
| 📊 **Rate limits éditables** | RPM configurable, intervalle minimum, concurrence max |
### 📊 Observabilité & Analytique
| Fonctionnalité | Ce qu'elle fait |
| --------------------------------- | ------------------------------------------------------------------------- |
| 📝 **Logs de requêtes** | Mode debug avec logs complets requête/réponse |
| 💾 **Logs SQLite** | Logs proxy persistants survivant aux redémarrages |
| 📊 **Tableau de bord analytique** | Recharts : cartes de stats, graphique d'utilisation, tableau fournisseurs |
| 📈 **Suivi de progression** | Événements SSE de progression opt-in pour le streaming |
| 🧪 **Évaluations LLM** | Tests avec golden set et 4 stratégies de correspondance |
| 🔍 **Télémétrie des requêtes** | Agrégation de latence p50/p95/p99 + traçage X-Request-Id |
| 📋 **Logs + Quotas** | Pages dédiées pour navigation des logs et suivi des quotas |
| 🏥 **Tableau de bord santé** | Uptime, états circuit breaker, lockouts, stats cache |
| 💰 **Suivi des coûts** | Gestion de budget + configuration des prix par modèle |
### ☁️ Déploiement & Synchronisation
| Fonctionnalité | Ce qu'elle fait |
| --------------------------------- | ------------------------------------------------------------------------------- |
| 💾 **Cloud Sync** | Synchroniser les paramètres entre appareils via Cloudflare Workers |
| 🌐 **Déployer partout** | Localhost, VPS, Docker, Cloudflare Workers |
| 🔑 **Gestion des clés API** | Générer, faire tourner et limiter les clés API par fournisseur |
| 🧙 **Assistant de configuration** | Setup guidé en 4 étapes pour les nouveaux utilisateurs |
| 🔧 **Tableau de bord CLI Tools** | Configuration en un clic pour Claude, Codex, Cline, OpenClaw, Kilo, Antigravity |
| 🔄 **Sauvegardes DB** | Sauvegarde et restauration automatiques de tous les paramètres |
<details>
<summary><b>📖 Détails des fonctionnalités</b></summary>
### 🎯 Fallback intelligent 4 niveaux
Créez des combos avec fallback automatique :
```
Combo : "my-coding-stack"
1. cc/claude-opus-4-6 (votre abonnement)
2. nvidia/llama-3.3-70b (API NVIDIA gratuite)
3. glm/glm-4.7 (backup économique, $0.6/1M)
4. if/kimi-k2-thinking (fallback gratuit)
→ Bascule automatiquement lorsque le quota est épuisé ou en cas d'erreurs
```
### 📊 Suivi des quotas en temps réel
- Consommation de tokens par fournisseur
- Compte à rebours de réinitialisation (5 heures, quotidien, hebdomadaire)
- Estimation des coûts pour les niveaux payants
- Rapports de dépenses mensuels
### 🔄 Traduction de format
Traduction transparente entre les formats :
- **OpenAI** ↔ **Claude****Gemini****OpenAI Responses**
- Votre CLI envoie le format OpenAI → OmniRoute traduit → Le fournisseur reçoit le format natif
- Fonctionne avec tout outil supportant les endpoints OpenAI personnalisés
### 👥 Support multi-comptes
- Ajouter plusieurs comptes par fournisseur
- Round-robin automatique ou routage par priorité
- Basculement vers le compte suivant lorsqu'un quota est atteint
### 🔄 Renouvellement automatique des tokens
- Les tokens OAuth se renouvellent automatiquement avant expiration
- Pas de réauthentification manuelle nécessaire
- Expérience transparente sur tous les fournisseurs
### 🎨 Combos personnalisés
- Créer des combinaisons de modèles illimitées
- 6 stratégies : fill-first, round-robin, power-of-two-choices, random, least-used, cost-optimized
- Partager les combos entre appareils avec Cloud Sync
### 🏥 Tableau de bord santé
- Statut du système (uptime, version, utilisation mémoire)
- États des circuit breakers par fournisseur (Closed/Open/Half-Open)
- Statut des rate limits et lockouts actifs
- Statistiques du cache de signatures
- Télémétrie de latence (p50/p95/p99) + cache de prompt
- Réinitialisation de la santé en un clic
### 🔧 Playground du traducteur
- Déboguer, tester et visualiser les traductions de format d'API
- Envoyer des requêtes et voir comment OmniRoute traduit entre les formats des fournisseurs
- Inestimable pour résoudre les problèmes d'intégration
### 💾 Cloud Sync
- Synchroniser fournisseurs, combos et paramètres entre appareils
- Synchronisation en arrière-plan automatique
- Stockage chiffré sécurisé
</details>
---
## 📖 Guide de configuration
<details>
<summary><b>💳 Fournisseurs par abonnement</b></summary>
### Claude Code (Pro/Max)
```bash
Tableau de bord → Fournisseurs → Connecter Claude Code
→ Connexion OAuth → Renouvellement auto des tokens
→ Suivi de quota 5h + hebdomadaire
Modèles :
cc/claude-opus-4-6
cc/claude-sonnet-4-5-20250929
cc/claude-haiku-4-5-20251001
```
**Conseil Pro :** Utilisez Opus pour les tâches complexes, Sonnet pour la vitesse. OmniRoute suit les quotas par modèle !
### OpenAI Codex (Plus/Pro)
```bash
Tableau de bord → Fournisseurs → Connecter Codex
→ Connexion OAuth (port 1455)
→ Reset 5h + hebdomadaire
Modèles :
cx/gpt-5.2-codex
cx/gpt-5.1-codex-max
```
### Gemini CLI (GRATUIT 180K/mois !)
```bash
Tableau de bord → Fournisseurs → Connecter Gemini CLI
→ Google OAuth
→ 180K completions/mois + 1K/jour
Modèles :
gc/gemini-3-flash-preview
gc/gemini-2.5-pro
```
**Meilleure valeur :** Niveau gratuit énorme ! Utilisez avant les niveaux payants.
### GitHub Copilot
```bash
Tableau de bord → Fournisseurs → Connecter GitHub
→ OAuth via GitHub
→ Reset mensuel (1er du mois)
Modèles :
gh/gpt-5
gh/claude-4.5-sonnet
gh/gemini-3-pro
```
</details>
<details>
<summary><b>🔑 Fournisseurs par clé API</b></summary>
### NVIDIA NIM (GRATUIT 1000 crédits !)
1. S'inscrire : [build.nvidia.com](https://build.nvidia.com)
2. Obtenir une clé API gratuite (1000 crédits d'inférence inclus)
3. Tableau de bord → Ajouter fournisseur → NVIDIA NIM :
- API Key : `nvapi-your-key`
**Modèles :** `nvidia/llama-3.3-70b-instruct`, `nvidia/mistral-7b-instruct` et 50+ autres
**Conseil Pro :** API compatible OpenAI — fonctionne parfaitement avec la traduction de format d'OmniRoute !
### DeepSeek
1. S'inscrire : [platform.deepseek.com](https://platform.deepseek.com)
2. Obtenir une clé API
3. Tableau de bord → Ajouter fournisseur → DeepSeek
**Modèles :** `deepseek/deepseek-chat`, `deepseek/deepseek-coder`
### Groq (Niveau gratuit disponible !)
1. S'inscrire : [console.groq.com](https://console.groq.com)
2. Obtenir une clé API (niveau gratuit inclus)
3. Tableau de bord → Ajouter fournisseur → Groq
**Modèles :** `groq/llama-3.3-70b`, `groq/mixtral-8x7b`
**Conseil Pro :** Inférence ultra-rapide — idéal pour le codage en temps réel !
### OpenRouter (100+ modèles)
1. S'inscrire : [openrouter.ai](https://openrouter.ai)
2. Obtenir une clé API
3. Tableau de bord → Ajouter fournisseur → OpenRouter
**Modèles :** Accès à 100+ modèles de tous les grands fournisseurs via une seule clé API.
</details>
<details>
<summary><b>💰 Fournisseurs économiques (Backup)</b></summary>
### GLM-4.7 (Reset quotidien, $0.6/1M)
1. S'inscrire : [Zhipu AI](https://open.bigmodel.cn/)
2. Obtenir une clé API du Coding Plan
3. Tableau de bord → Ajouter clé API :
- Fournisseur : `glm`
- API Key : `your-key`
**Utilisez :** `glm/glm-4.7`
**Conseil Pro :** Le Coding Plan offre 3× le quota à 1/7 du coût ! Reset quotidien à 10h.
### MiniMax M2.1 (Reset 5h, $0.20/1M)
1. S'inscrire : [MiniMax](https://www.minimax.io/)
2. Obtenir une clé API
3. Tableau de bord → Ajouter clé API
**Utilisez :** `minimax/MiniMax-M2.1`
**Conseil Pro :** L'option la moins chère pour le contexte long (1M tokens) !
### Kimi K2 (9 $/mois fixe)
1. S'abonner : [Moonshot AI](https://platform.moonshot.ai/)
2. Obtenir une clé API
3. Tableau de bord → Ajouter clé API
**Utilisez :** `kimi/kimi-latest`
**Conseil Pro :** 9 $/mois fixe pour 10M tokens = 0,90 $/1M de coût effectif !
</details>
<details>
<summary><b>🆓 Fournisseurs GRATUITS (Backup d'urgence)</b></summary>
### iFlow (8 modèles GRATUITS)
```bash
Tableau de bord → Connecter iFlow
→ Connexion OAuth iFlow
→ Utilisation illimitée
Modèles :
if/kimi-k2-thinking
if/qwen3-coder-plus
if/glm-4.7
if/minimax-m2
if/deepseek-r1
```
### Qwen (3 modèles GRATUITS)
```bash
Tableau de bord → Connecter Qwen
→ Autorisation par code d'appareil
→ Utilisation illimitée
Modèles :
qw/qwen3-coder-plus
qw/qwen3-coder-flash
```
### Kiro (Claude GRATUIT)
```bash
Tableau de bord → Connecter Kiro
→ AWS Builder ID ou Google/GitHub
→ Utilisation illimitée
Modèles :
kr/claude-sonnet-4.5
kr/claude-haiku-4.5
```
</details>
<details>
<summary><b>🎨 Créer des combos</b></summary>
### Exemple 1 : Maximiser l'abonnement → Backup économique
```
Tableau de bord → Combos → Créer nouveau
Nom : premium-coding
Modèles :
1. cc/claude-opus-4-6 (Abonnement principal)
2. glm/glm-4.7 (Backup économique, $0.6/1M)
3. minimax/MiniMax-M2.1 (Fallback le moins cher, $0.20/1M)
Utilisez en CLI : premium-coding
```
### Exemple 2 : Gratuit uniquement (Zéro coût)
```
Nom : free-combo
Modèles :
1. gc/gemini-3-flash-preview (180K gratuits/mois)
2. if/kimi-k2-thinking (illimité)
3. qw/qwen3-coder-plus (illimité)
Coût : 0 $ pour toujours !
```
</details>
<details>
<summary><b>🔧 Intégration CLI</b></summary>
### Cursor IDE
```
Paramètres → Modèles → Avancé :
OpenAI API Base URL : http://localhost:20128/v1
OpenAI API Key : [du tableau de bord OmniRoute]
Model : cc/claude-opus-4-6
```
### Claude Code
Utilisez la page **CLI Tools** dans le tableau de bord pour la configuration en un clic, ou modifiez `~/.claude/settings.json` manuellement.
### Codex CLI
```bash
export OPENAI_BASE_URL="http://localhost:20128"
export OPENAI_API_KEY="your-omniroute-api-key"
codex "your prompt"
```
### OpenClaw
**Option 1 — Tableau de bord (recommandé) :**
```
Tableau de bord → CLI Tools → OpenClaw → Sélectionner modèle → Appliquer
```
**Option 2 — Manuel :** Modifier `~/.openclaw/openclaw.json` :
```json
{
"models": {
"providers": {
"omniroute": {
"baseUrl": "http://127.0.0.1:20128/v1",
"apiKey": "sk_omniroute",
"api": "openai-completions"
}
}
}
}
```
> **Note :** OpenClaw fonctionne uniquement avec OmniRoute local. Utilisez `127.0.0.1` au lieu de `localhost` pour éviter les problèmes de résolution IPv6.
### Cline / Continue / RooCode
```
Paramètres → Configuration API :
Fournisseur : OpenAI Compatible
Base URL : http://localhost:20128/v1
API Key : [du tableau de bord OmniRoute]
Model : if/kimi-k2-thinking
```
</details>
---
## 📊 Modèles disponibles
<details>
<summary><b>Voir tous les modèles disponibles</b></summary>
**Claude Code (`cc/`)** - Pro/Max :
- `cc/claude-opus-4-6`
- `cc/claude-sonnet-4-5-20250929`
- `cc/claude-haiku-4-5-20251001`
**Codex (`cx/`)** - Plus/Pro :
- `cx/gpt-5.2-codex`
- `cx/gpt-5.1-codex-max`
**Gemini CLI (`gc/`)** - GRATUIT :
- `gc/gemini-3-flash-preview`
- `gc/gemini-2.5-pro`
**GitHub Copilot (`gh/`)** :
- `gh/gpt-5`
- `gh/claude-4.5-sonnet`
**NVIDIA NIM (`nvidia/`)** - Crédits GRATUITS :
- `nvidia/llama-3.3-70b-instruct`
- `nvidia/mistral-7b-instruct`
- 50+ modèles sur [build.nvidia.com](https://build.nvidia.com)
**GLM (`glm/`)** - $0.6/1M :
- `glm/glm-4.7`
**MiniMax (`minimax/`)** - $0.2/1M :
- `minimax/MiniMax-M2.1`
**iFlow (`if/`)** - GRATUIT :
- `if/kimi-k2-thinking`
- `if/qwen3-coder-plus`
- `if/deepseek-r1`
- `if/glm-4.7`
- `if/minimax-m2`
**Qwen (`qw/`)** - GRATUIT :
- `qw/qwen3-coder-plus`
- `qw/qwen3-coder-flash`
**Kiro (`kr/`)** - GRATUIT :
- `kr/claude-sonnet-4.5`
- `kr/claude-haiku-4.5`
**OpenRouter (`or/`)** - 100+ modèles :
- `or/anthropic/claude-4-sonnet`
- `or/google/gemini-2.5-pro`
- Tout modèle de [openrouter.ai/models](https://openrouter.ai/models)
</details>
---
## 🧪 Évaluations (Evals)
OmniRoute inclut un framework d'évaluation intégré pour tester la qualité des réponses LLM contre un golden set. Accès via **Analytics → Evals** dans le tableau de bord.
### Golden Set intégré
Le « OmniRoute Golden Set » préchargé contient 10 cas de test :
- Salutations, mathématiques, géographie, génération de code
- Conformité format JSON, traduction, markdown
- Rejet de sécurité (contenu nocif), comptage, logique booléenne
### Stratégies d'évaluation
| Stratégie | Description | Exemple |
| ---------- | -------------------------------------------------------------- | -------------------------------- |
| `exact` | La sortie doit correspondre exactement | `"4"` |
| `contains` | La sortie doit contenir la sous-chaîne (insensible à la casse) | `"Paris"` |
| `regex` | La sortie doit correspondre au motif regex | `"1.*2.*3"` |
| `custom` | Fonction JS personnalisée retourne true/false | `(output) => output.length > 10` |
---
## 🐛 Dépannage
<details>
<summary><b>Cliquez pour développer le guide de dépannage</b></summary>
**« Language model did not provide messages »**
- Quota du fournisseur épuisé → Vérifiez le suivi de quota dans le tableau de bord
- Solution : Utilisez un combo avec fallback ou passez à un niveau moins cher
**Rate limiting**
- Quota d'abonnement épuisé → Fallback vers GLM/MiniMax
- Ajoutez un combo : `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
**Token OAuth expiré**
- Renouvelé automatiquement par OmniRoute
- Si le problème persiste : Tableau de bord → Fournisseur → Reconnecter
**Coûts élevés**
- Vérifiez les statistiques d'utilisation dans Tableau de bord → Coûts
- Changez le modèle principal pour GLM/MiniMax
- Utilisez le niveau gratuit (Gemini CLI, iFlow) pour les tâches non critiques
**Le tableau de bord s'ouvre sur le mauvais port**
- Définissez `PORT=20128` et `NEXT_PUBLIC_BASE_URL=http://localhost:20128`
**Erreurs de cloud sync**
- Vérifiez que `BASE_URL` pointe vers votre instance en cours d'exécution
- Vérifiez que `CLOUD_URL` pointe vers le point de terminaison cloud attendu
- Gardez les valeurs `NEXT_PUBLIC_*` alignées avec les valeurs du serveur
**Le premier login ne fonctionne pas**
- Vérifiez `INITIAL_PASSWORD` dans `.env`
- Si non défini, le mot de passe par défaut est `123456`
**Pas de logs de requêtes**
- Définissez `ENABLE_REQUEST_LOGS=true` dans `.env`
**Le test de connexion affiche « Invalid » pour les fournisseurs compatibles OpenAI**
- Beaucoup de fournisseurs n'exposent pas le point de terminaison `/models`
- OmniRoute v1.0.6+ inclut une validation de secours via chat completions
- Assurez-vous que l'URL de base inclut le suffixe `/v1`
</details>
---
## 🛠️ Stack technologique
- **Runtime** : Node.js 20+
- **Langage** : TypeScript 5.9 — **100% TypeScript** dans `src/` et `open-sse/` (v1.0.6)
- **Framework** : Next.js 16 + React 19 + Tailwind CSS 4
- **Base de données** : LowDB (JSON) + SQLite (état du domaine + logs proxy)
- **Streaming** : Server-Sent Events (SSE)
- **Auth** : OAuth 2.0 (PKCE) + JWT + API Keys
- **Tests** : Node.js test runner (368+ tests unitaires)
- **CI/CD** : GitHub Actions (publication automatique npm + Docker Hub lors du release)
- **Site web** : [omniroute.online](https://omniroute.online)
- **Package** : [npmjs.com/package/omniroute](https://www.npmjs.com/package/omniroute)
- **Docker** : [hub.docker.com/r/diegosouzapw/omniroute](https://hub.docker.com/r/diegosouzapw/omniroute)
- **Résilience** : Circuit breaker, backoff exponentiel, anti-thundering herd, spoofing TLS
---
## 📖 Documentation
| Document | Description |
| ------------------------------------------ | --------------------------------------------------- |
| [Guide utilisateur](docs/USER_GUIDE.md) | Fournisseurs, combos, intégration CLI, déploiement |
| [Référence API](docs/API_REFERENCE.md) | Tous les endpoints avec exemples |
| [Dépannage](docs/TROUBLESHOOTING.md) | Problèmes courants et solutions |
| [Architecture](docs/ARCHITECTURE.md) | Architecture système et détails internes |
| [Contribuer](CONTRIBUTING.md) | Configuration de développement et directives |
| [Spécification OpenAPI](docs/openapi.yaml) | Spécification OpenAPI 3.0 |
| [Politique de sécurité](SECURITY.md) | Signalement de vulnérabilités et pratiques sécurité |
---
## 📧 Support
> 💬 **Rejoignez notre communauté !** [Groupe WhatsApp](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t) — Obtenez de l'aide, partagez des astuces et restez informé.
- **Site web** : [omniroute.online](https://omniroute.online)
- **GitHub** : [github.com/diegosouzapw/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- **Issues** : [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
- **WhatsApp** : [Groupe communautaire](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
- **Projet original** : [9router par decolua](https://github.com/decolua/9router)
---
## 👥 Contributeurs
[![Contributors](https://contrib.rocks/image?repo=diegosouzapw/OmniRoute&max=100&columns=20&anon=1)](https://github.com/diegosouzapw/OmniRoute/graphs/contributors)
### Comment contribuer
1. Forkez le dépôt
2. Créez votre branche de fonctionnalité (`git checkout -b feature/amazing-feature`)
3. Committez vos changements (`git commit -m 'Add amazing feature'`)
4. Poussez vers la branche (`git push origin feature/amazing-feature`)
5. Ouvrez une Pull Request
Consultez [CONTRIBUTING.md](CONTRIBUTING.md) pour les directives détaillées.
### Publier une nouvelle version
```bash
# Créer un release — la publication npm est automatique
gh release create v1.0.6 --title "v1.0.6" --generate-notes
```
---
## 📊 Historique des Stars
<a href="https://star-history.com/#diegosouzapw/OmniRoute&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date" />
</picture>
</a>
---
## 🙏 Remerciements
Remerciements spéciaux à **[9router](https://github.com/decolua/9router)** par **[decolua](https://github.com/decolua)** — le projet original qui a inspiré ce fork. OmniRoute construit sur cette base incroyable avec des fonctionnalités supplémentaires, des APIs multi-modales et une réécriture complète en TypeScript.
Remerciements spéciaux à **[CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI)** — l'implémentation originale en Go qui a inspiré ce portage en JavaScript.
---
## 📄 Licence
Licence MIT — voir [LICENSE](LICENSE) pour les détails.
---
<div align="center">
<sub>Fait avec ❤️ pour les développeurs qui codent 24/7</sub>
<br/>
<sub><a href="https://omniroute.online">omniroute.online</a></sub>
</div>
-1000
View File
File diff suppressed because it is too large Load Diff
+97 -999
View File
File diff suppressed because it is too large Load Diff
-1054
View File
File diff suppressed because it is too large Load Diff
-999
View File
@@ -1,999 +0,0 @@
<div align="center">
<img src="./docs/screenshots/MainOmniRoute.png" alt="OmniRoute Dashboard" width="800"/>
# 🚀 OmniRoute — Бесплатный AI Gateway
### Никогда не прекращайте программировать. Умная маршрутизация к **БЕСПЛАТНЫМ и дешёвым AI-моделям** с автоматическим fallback.
_Ваш универсальный API-прокси — одна точка доступа, 36+ провайдеров, нулевой простой._
**Chat Completions • Embeddings • Генерация изображений • Аудио • Reranking • 100% TypeScript**
---
### 🤖 Бесплатный AI-провайдер для ваших любимых агентов программирования
_Подключайте любую IDE или CLI-инструмент с AI через OmniRoute — бесплатный API gateway для неограниченного программирования._
<table>
<tr>
<td align="center" width="110">
<a href="https://github.com/cline/cline">
<img src="./public/providers/openclaw.png" alt="OpenClaw" width="48"/><br/>
<b>OpenClaw</b>
</a><br/>
<sub>⭐ 205K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/HKUDS/nanobot">
<img src="./public/providers/nanobot.png" alt="NanoBot" width="48"/><br/>
<b>NanoBot</b>
</a><br/>
<sub>⭐ 20.9K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/sipeed/picoclaw">
<img src="./public/providers/picoclaw.jpg" alt="PicoClaw" width="48"/><br/>
<b>PicoClaw</b>
</a><br/>
<sub>⭐ 14.6K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/zeroclaw-labs/zeroclaw">
<img src="./public/providers/zeroclaw.png" alt="ZeroClaw" width="48"/><br/>
<b>ZeroClaw</b>
</a><br/>
<sub>⭐ 9.9K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/nearai/ironclaw">
<img src="./public/providers/ironclaw.png" alt="IronClaw" width="48"/><br/>
<b>IronClaw</b>
</a><br/>
<sub>⭐ 2.1K</sub>
</td>
</tr>
<tr>
<td align="center" width="110">
<a href="https://github.com/anomalyco/opencode">
<img src="./public/providers/opencode.svg" alt="OpenCode" width="48"/><br/>
<b>OpenCode</b>
</a><br/>
<sub>⭐ 106K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/openai/codex">
<img src="./public/providers/codex.png" alt="Codex CLI" width="48"/><br/>
<b>Codex CLI</b>
</a><br/>
<sub>⭐ 60.8K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/anthropics/claude-code">
<img src="./public/providers/claude.png" alt="Claude Code" width="48"/><br/>
<b>Claude Code</b>
</a><br/>
<sub>⭐ 67.3K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/google-gemini/gemini-cli">
<img src="./public/providers/gemini-cli.png" alt="Gemini CLI" width="48"/><br/>
<b>Gemini CLI</b>
</a><br/>
<sub>⭐ 94.7K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/Kilo-Org/kilocode">
<img src="./public/providers/kilocode.png" alt="Kilo Code" width="48"/><br/>
<b>Kilo Code</b>
</a><br/>
<sub>⭐ 15.5K</sub>
</td>
</tr>
</table>
<sub>📡 Все агенты подключаются через <code>http://localhost:20128/v1</code> или <code>http://cloud.omniroute.online/v1</code> — одна конфигурация, неограниченные модели и квота</sub>
---
[![npm version](https://img.shields.io/npm/v/omniroute?color=cb3837&logo=npm)](https://www.npmjs.com/package/omniroute)
[![Docker Hub](https://img.shields.io/docker/v/diegosouzapw/omniroute?label=Docker%20Hub&logo=docker&color=2496ED)](https://hub.docker.com/r/diegosouzapw/omniroute)
[![License](https://img.shields.io/github/license/diegosouzapw/OmniRoute)](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
[![Website](https://img.shields.io/badge/Website-omniroute.online-blue?logo=google-chrome&logoColor=white)](https://omniroute.online)
[![WhatsApp](https://img.shields.io/badge/WhatsApp-Community-25D366?logo=whatsapp&logoColor=white)](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
[🌐 Сайт](https://omniroute.online) • [🚀 Быстрый старт](#-быстрый-старт) • [💡 Функции](#-основные-функции) • [📖 Документация](#-документация) • [💰 Цены](#-обзор-цен)
🌐 **Доступно на:** [English](README.md) | [Português](README.pt-BR.md) | [Español](README.es.md) | [Русский](README.ru.md) | [中文](README.zh-CN.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | [Italiano](README.it.md)
</div>
---
## 🤔 Почему OmniRoute?
**Перестаньте тратить деньги и упираться в лимиты:**
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> Квота подписки истекает неиспользованной каждый месяц
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> Лимиты скорости останавливают вас посреди программирования
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> Дорогие API ($20-50/месяц за провайдера)
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> Ручное переключение между провайдерами
**OmniRoute решает это:**
- ✅ **Максимизируйте подписки** — Отслеживайте квоты, используйте всё до сброса
- ✅ **Автоматический fallback** — Подписка → API Key → Дешёвый → Бесплатный, нулевой простой
- ✅ **Мульти-аккаунт** — Round-robin между аккаунтами каждого провайдера
- ✅ **Универсальный** — Работает с Claude Code, Codex, Gemini CLI, Cursor, Cline, OpenClaw, любым CLI-инструментом
---
## 🔄 Как это работает
```
┌─────────────┐
│ Ваш CLI │ (Claude Code, Codex, Gemini CLI, OpenClaw, Cursor, Cline...)
│ Tool │
└──────┬──────┘
│ http://localhost:20128/v1
┌─────────────────────────────────────────┐
│ OmniRoute (Умный маршрутизатор) │
│ • Трансляция формата (OpenAI ↔ Claude) │
│ • Отслеживание квот + Embeddings + Изображения │
│ • Автообновление токенов │
└──────┬──────────────────────────────────┘
├─→ [Tier 1: ПОДПИСКА] Claude Code, Codex, Gemini CLI
│ ↓ квота исчерпана
├─→ [Tier 2: API KEY] DeepSeek, Groq, xAI, Mistral, NVIDIA NIM и др.
│ ↓ лимит бюджета
├─→ [Tier 3: ДЕШЁВЫЙ] GLM ($0.6/1M), MiniMax ($0.2/1M)
│ ↓ лимит бюджета
└─→ [Tier 4: БЕСПЛАТНЫЙ] iFlow, Qwen, Kiro (неограниченно)
Результат: Никогда не прекращайте программировать, минимальные затраты
```
---
## ⚡ Быстрый старт
**1. Установите глобально:**
```bash
npm install -g omniroute
omniroute
```
🎉 Dashboard открывается на `http://localhost:20128`
| Команда | Описание |
| ----------------------- | ------------------------------------------ |
| `omniroute` | Запустить сервер (порт по умолчанию 20128) |
| `omniroute --port 3000` | Использовать другой порт |
| `omniroute --no-open` | Не открывать браузер автоматически |
| `omniroute --help` | Показать справку |
**2. Подключите БЕСПЛАТНОГО провайдера:**
Dashboard → Провайдеры → Подключить **Claude Code** или **Antigravity** → OAuth вход → Готово!
**3. Используйте в CLI-инструменте:**
```
Claude Code/Codex/Gemini CLI/OpenClaw/Cursor/Cline Настройки:
Endpoint: http://localhost:20128/v1
API Key: [скопируйте из dashboard]
Model: if/kimi-k2-thinking
```
**Готово!** Начните программировать с БЕСПЛАТНЫМИ AI-моделями.
**Альтернатива — запуск из исходного кода:**
```bash
cp .env.example .env
npm install
PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev
```
---
## 🐳 Docker
OmniRoute доступен как публичный Docker-образ на [Docker Hub](https://hub.docker.com/r/diegosouzapw/omniroute).
**Быстрый запуск:**
```bash
docker run -d \
--name omniroute \
--restart unless-stopped \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
**С файлом окружения:**
```bash
# Скопируйте и отредактируйте .env
cp .env.example .env
docker run -d \
--name omniroute \
--restart unless-stopped \
--env-file .env \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
**Используя Docker Compose:**
```bash
# Базовый профиль (без CLI-инструментов)
docker compose --profile base up -d
# CLI-профиль (Claude Code, Codex, OpenClaw встроены)
docker compose --profile cli up -d
```
| Образ | Тег | Размер | Описание |
| ------------------------ | -------- | ------ | -------------------------- |
| `diegosouzapw/omniroute` | `latest` | ~250MB | Последний стабильный релиз |
| `diegosouzapw/omniroute` | `1.0.6` | ~250MB | Текущая версия |
---
## 💰 Обзор цен
| Tier | Провайдер | Стоимость | Сброс квоты | Лучше всего для |
| ----------------- | ----------------- | ----------------------------- | ------------------ | -------------------------------- |
| **💳 ПОДПИСКА** | Claude Code (Pro) | $20/мес | 5ч + еженедельно | Уже подписан |
| | Codex (Plus/Pro) | $20-200/мес | 5ч + еженедельно | Пользователи OpenAI |
| | Gemini CLI | **БЕСПЛАТНО** | 180K/мес + 1K/день | Все! |
| | GitHub Copilot | $10-19/мес | Ежемесячно | Пользователи GitHub |
| **🔑 API KEY** | NVIDIA NIM | **БЕСПЛАТНО** (1000 кредитов) | Одноразово | Бесплатное тестирование |
| | DeepSeek | По использованию | Нет | Лучшее соотношение цена/качество |
| | Groq | Беспл. уровень + платный | Ограничено | Сверхбыстрый вывод |
| | xAI (Grok) | По использованию | Нет | Модели Grok |
| | Mistral | Беспл. уровень + платный | Ограничено | Европейский AI |
| | OpenRouter | По использованию | Нет | 100+ моделей |
| **💰 ДЕШЁВЫЙ** | GLM-4.7 | $0.6/1M | Ежедневно 10ч | Бюджетный бэкап |
| | MiniMax M2.1 | $0.2/1M | 5ч ротация | Самый дешёвый вариант |
| | Kimi K2 | $9/мес фикс | 10M токенов/мес | Предсказуемая цена |
| **🆓 БЕСПЛАТНЫЙ** | iFlow | $0 | Неограниченно | 8 бесплатных моделей |
| | Qwen | $0 | Неограниченно | 3 бесплатные модели |
| | Kiro | $0 | Неограниченно | Claude бесплатно |
**💡 Совет:** Начните с Gemini CLI (180K бесплатно/мес) + iFlow (неограниченно бесплатно) = $0!
---
## 🎯 Сценарии использования
### Сценарий 1: «У меня подписка Claude Pro»
**Проблема:** Квота истекает неиспользованной, лимиты скорости во время интенсивного программирования
```
Combo: "maximize-claude"
1. cc/claude-opus-4-6 (используйте подписку полностью)
2. glm/glm-4.7 (дешёвый бэкап при исчерпании квоты)
3. if/kimi-k2-thinking (бесплатный аварийный fallback)
Месячная стоимость: $20 (подписка) + ~$5 (бэкап) = $25 итого
vs. $20 + упирание в лимиты = разочарование
```
### Сценарий 2: «Хочу нулевую стоимость»
**Проблема:** Не может позволить подписки, нужен надёжный AI для программирования
```
Combo: "free-forever"
1. gc/gemini-3-flash (180K бесплатно/мес)
2. if/kimi-k2-thinking (неограниченно бесплатно)
3. qw/qwen3-coder-plus (неограниченно бесплатно)
Месячная стоимость: $0
Качество: Модели готовые к продакшену
```
### Сценарий 3: «Мне нужно программировать 24/7, без перерывов»
**Проблема:** Дедлайны, не может позволить простой
```
Combo: "always-on"
1. cc/claude-opus-4-6 (лучшее качество)
2. cx/gpt-5.2-codex (вторая подписка)
3. glm/glm-4.7 (дешёвый, ежедневный сброс)
4. minimax/MiniMax-M2.1 (самый дешёвый, сброс 5ч)
5. if/kimi-k2-thinking (бесплатно неограниченно)
Результат: 5 уровней fallback = нулевой простой
```
### Сценарий 4: «Хочу БЕСПЛАТНЫЙ AI в OpenClaw»
**Проблема:** Нужен AI-ассистент в мессенджерах, полностью бесплатно
```
Combo: "openclaw-free"
1. if/glm-4.7 (неограниченно бесплатно)
2. if/minimax-m2.1 (неограниченно бесплатно)
3. if/kimi-k2-thinking (неограниченно бесплатно)
Месячная стоимость: $0
Доступ через: WhatsApp, Telegram, Slack, Discord, iMessage, Signal...
```
---
## 💡 Основные функции
### 🧠 Маршрутизация и интеллект
| Функция | Что делает |
| ------------------------------------------- | ----------------------------------------------------------------------------- |
| 🎯 **Умный 4-уровневый Fallback** | Авто-маршрутизация: Подписка → API Key → Дешёвый → Бесплатный |
| 📊 **Отслеживание квот в реальном времени** | Счётчик токенов в реальном времени + обратный отсчёт до сброса |
| 🔄 **Трансляция формата** | OpenAI ↔ Claude ↔ Gemini ↔ Cursor ↔ Kiro бесшовно |
| 👥 **Мульти-аккаунт** | Несколько аккаунтов на провайдера с интеллектуальным выбором |
| 🔄 **Автообновление токенов** | OAuth-токены обновляются автоматически с повторами |
| 🎨 **Пользовательские комбо** | 6 стратегий: fill-first, round-robin, p2c, random, least-used, cost-optimized |
| 🧩 **Пользовательские модели** | Добавьте любой ID модели к любому провайдеру |
| 🌐 **Wildcard-маршрутизатор** | Маршрутизируйте паттерны `provider/*` к любому провайдеру динамически |
| 🧠 **Бюджет рассуждений** | Режимы passthrough, auto, custom и adaptive для моделей рассуждений |
| 💬 **Инъекция System Prompt** | Глобальный system prompt для всех запросов |
| 📄 **API Responses** | Полная поддержка OpenAI Responses API (`/v1/responses`) для Codex |
### 🎵 Мультимодальные API
| Функция | Что делает |
| ---------------------------- | --------------------------------------------------- |
| 🖼️ **Генерация изображений** | `/v1/images/generations` — 4 провайдера, 9+ моделей |
| 📐 **Embeddings** | `/v1/embeddings` — 6 провайдеров, 9+ моделей |
| 🎤 **Транскрипция аудио** | `/v1/audio/transcriptions` — Совместимо с Whisper |
| 🔊 **Текст в речь** | `/v1/audio/speech` — Мульти-провайдерный синтез |
| 🛡️ **Модерация** | `/v1/moderations` — Проверки безопасности контента |
| 🔀 **Reranking** | `/v1/rerank` — Переранжирование релевантности |
### 🛡️ Устойчивость и безопасность
| Функция | Что делает |
| -------------------------------- | -------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Авто-открытие/закрытие по провайдеру с настраиваемыми порогами |
| 🛡️ **Anti-Thundering Herd** | Mutex + семафор для API key провайдеров |
| 🧠 **Семантический кеш** | Двухуровневый кеш (сигнатура + семантика) снижает стоимость |
| ⚡ **Идемпотентность запросов** | 5с окно дедупликации для дублирующихся запросов |
| 🔒 **Спуфинг TLS Fingerprint** | Обход обнаружения ботов через wreq-js |
| 🌐 **Фильтрация IP** | Allowlist/blocklist для контроля доступа к API |
| 📊 **Настраиваемые Rate Limits** | Настраиваемые RPM, минимальный интервал, макс. конкуррентность |
### 📊 Наблюдаемость и аналитика
| Функция | Что делает |
| ----------------------------- | ------------------------------------------------------------------------ |
| 📝 **Логи запросов** | Режим debug с полными логами запросов/ответов |
| 💾 **Логи SQLite** | Постоянные proxy-логи переживают перезапуски |
| 📊 **Dashboard аналитики** | Recharts: карточки статистики, график использования, таблица провайдеров |
| 📈 **Отслеживание прогресса** | Opt-in SSE-события прогресса для стриминга |
| 🧪 **Оценки LLM** | Тестирование с golden set и 4 стратегиями сравнения |
| 🔍 **Телеметрия запросов** | Агрегация латентности p50/p95/p99 + трекинг X-Request-Id |
| 📋 **Логи + Квоты** | Отдельные страницы для просмотра логов и отслеживания квот |
| 🏥 **Dashboard здоровья** | Uptime, состояния circuit breaker, блокировки, статистика кеша |
| 💰 **Отслеживание стоимости** | Управление бюджетом + настройка цен по моделям |
### ☁️ Деплой и синхронизация
| Функция | Что делает |
| -------------------------- | --------------------------------------------------------------------------- |
| 💾 **Cloud Sync** | Синхронизация настроек между устройствами через Cloudflare Workers |
| 🌐 **Деплой куда угодно** | Localhost, VPS, Docker, Cloudflare Workers |
| 🔑 **Управление API Keys** | Генерация, ротация и настройка scope API keys по провайдерам |
| 🧙 **Мастер настройки** | 4-шаговая настройка для новых пользователей |
| 🔧 **Dashboard CLI Tools** | Настройка в один клик для Claude, Codex, Cline, OpenClaw, Kilo, Antigravity |
| 🔄 **Бэкапы БД** | Автоматическое резервное копирование и восстановление всех настроек |
<details>
<summary><b>📖 Подробности функций</b></summary>
### 🎯 Умный 4-уровневый Fallback
Создавайте комбо с автоматическим fallback:
```
Combo: "my-coding-stack"
1. cc/claude-opus-4-6 (ваша подписка)
2. nvidia/llama-3.3-70b (бесплатный NVIDIA API)
3. glm/glm-4.7 (дешёвый бэкап, $0.6/1M)
4. if/kimi-k2-thinking (бесплатный fallback)
→ Автоматически переключается при исчерпании квоты или ошибках
```
### 📊 Отслеживание квот в реальном времени
- Потребление токенов по провайдерам
- Обратный отсчёт до сброса (5 часов, ежедневно, еженедельно)
- Оценка стоимости для платных уровней
- Ежемесячные отчёты о расходах
### 🔄 Трансляция формата
Бесшовная трансляция между форматами:
- **OpenAI****Claude****Gemini** ↔ **OpenAI Responses**
- Ваш CLI отправляет формат OpenAI → OmniRoute транслирует → Провайдер получает нативный формат
- Работает с любым инструментом, поддерживающим пользовательские OpenAI endpoints
### 👥 Мульти-аккаунт
- Добавляйте несколько аккаунтов на провайдера
- Автоматический round-robin или маршрутизация по приоритету
- Fallback на следующий аккаунт при исчерпании квоты
### 🔄 Автообновление токенов
- OAuth-токены обновляются автоматически до истечения
- Без необходимости ручной повторной аутентификации
- Бесшовный опыт по всем провайдерам
### 🎨 Пользовательские комбо
- Создавайте неограниченные комбинации моделей
- 6 стратегий: fill-first, round-robin, power-of-two-choices, random, least-used, cost-optimized
- Делитесь комбо между устройствами с Cloud Sync
### 🏥 Dashboard здоровья
- Статус системы (uptime, версия, использование памяти)
- Состояния circuit breaker по провайдерам (Closed/Open/Half-Open)
- Статус rate limit и активные блокировки
- Статистика кеша сигнатур
- Телеметрия латентности (p50/p95/p99) + кеш промптов
- Сброс состояния здоровья одним кликом
### 🔧 Playground транслятора
- Отладка, тестирование и визуализация трансляции форматов API
- Отправляйте запросы и смотрите, как OmniRoute транслирует между форматами провайдеров
- Бесценно для устранения проблем интеграции
### 💾 Cloud Sync
- Синхронизация провайдеров, комбо и настроек между устройствами
- Автоматическая фоновая синхронизация
- Безопасное шифрованное хранилище
</details>
---
## 📖 Руководство по настройке
<details>
<summary><b>💳 Провайдеры по подписке</b></summary>
### Claude Code (Pro/Max)
```bash
Dashboard → Провайдеры → Подключить Claude Code
→ OAuth вход → Автообновление токенов
→ Отслеживание квоты 5ч + еженедельно
Модели:
cc/claude-opus-4-6
cc/claude-sonnet-4-5-20250929
cc/claude-haiku-4-5-20251001
```
**Совет:** Используйте Opus для сложных задач, Sonnet для скорости. OmniRoute отслеживает квоту по моделям!
### OpenAI Codex (Plus/Pro)
```bash
Dashboard → Провайдеры → Подключить Codex
→ OAuth вход (порт 1455)
→ Сброс 5ч + еженедельно
Модели:
cx/gpt-5.2-codex
cx/gpt-5.1-codex-max
```
### Gemini CLI (БЕСПЛАТНО 180K/мес!)
```bash
Dashboard → Провайдеры → Подключить Gemini CLI
→ Google OAuth
→ 180K completions/мес + 1K/день
Модели:
gc/gemini-3-flash-preview
gc/gemini-2.5-pro
```
**Лучшая ценность:** Огромный бесплатный уровень! Используйте перед платными.
### GitHub Copilot
```bash
Dashboard → Провайдеры → Подключить GitHub
→ OAuth через GitHub
→ Ежемесячный сброс (1-е число)
Модели:
gh/gpt-5
gh/claude-4.5-sonnet
gh/gemini-3-pro
```
</details>
<details>
<summary><b>🔑 Провайдеры по API Key</b></summary>
### NVIDIA NIM (БЕСПЛАТНО 1000 кредитов!)
1. Регистрация: [build.nvidia.com](https://build.nvidia.com)
2. Получите бесплатный API key (1000 кредитов включены)
3. Dashboard → Добавить провайдера → NVIDIA NIM:
- API Key: `nvapi-your-key`
**Модели:** `nvidia/llama-3.3-70b-instruct`, `nvidia/mistral-7b-instruct` и 50+ других
**Совет:** OpenAI-совместимый API — работает идеально с трансляцией форматов OmniRoute!
### DeepSeek
1. Регистрация: [platform.deepseek.com](https://platform.deepseek.com)
2. Получите API key
3. Dashboard → Добавить провайдера → DeepSeek
**Модели:** `deepseek/deepseek-chat`, `deepseek/deepseek-coder`
### Groq (Бесплатный уровень доступен!)
1. Регистрация: [console.groq.com](https://console.groq.com)
2. Получите API key (бесплатный уровень включён)
3. Dashboard → Добавить провайдера → Groq
**Модели:** `groq/llama-3.3-70b`, `groq/mixtral-8x7b`
**Совет:** Сверхбыстрый вывод — лучший для программирования в реальном времени!
### OpenRouter (100+ моделей)
1. Регистрация: [openrouter.ai](https://openrouter.ai)
2. Получите API key
3. Dashboard → Добавить провайдера → OpenRouter
**Модели:** Доступ к 100+ моделям от всех основных провайдеров через один API key.
</details>
<details>
<summary><b>💰 Дешёвые провайдеры (Бэкап)</b></summary>
### GLM-4.7 (Ежедневный сброс, $0.6/1M)
1. Регистрация: [Zhipu AI](https://open.bigmodel.cn/)
2. Получите API key из Coding Plan
3. Dashboard → Добавить API Key:
- Провайдер: `glm`
- API Key: `your-key`
**Используйте:** `glm/glm-4.7`
**Совет:** Coding Plan предлагает 3× квоту по цене 1/7! Ежедневный сброс в 10:00.
### MiniMax M2.1 (Сброс 5ч, $0.20/1M)
1. Регистрация: [MiniMax](https://www.minimax.io/)
2. Получите API key
3. Dashboard → Добавить API Key
**Используйте:** `minimax/MiniMax-M2.1`
**Совет:** Самый дешёвый вариант для длинного контекста (1M токенов)!
### Kimi K2 ($9/мес фикс)
1. Подпишитесь: [Moonshot AI](https://platform.moonshot.ai/)
2. Получите API key
3. Dashboard → Добавить API Key
**Используйте:** `kimi/kimi-latest`
**Совет:** Фикс $9/мес за 10M токенов = $0.90/1M эффективная стоимость!
</details>
<details>
<summary><b>🆓 БЕСПЛАТНЫЕ провайдеры (Аварийный бэкап)</b></summary>
### iFlow (8 БЕСПЛАТНЫХ моделей)
```bash
Dashboard → Подключить iFlow
→ OAuth вход iFlow
→ Неограниченное использование
Модели:
if/kimi-k2-thinking
if/qwen3-coder-plus
if/glm-4.7
if/minimax-m2
if/deepseek-r1
```
### Qwen (3 БЕСПЛАТНЫЕ модели)
```bash
Dashboard → Подключить Qwen
→ Авторизация по коду устройства
→ Неограниченное использование
Модели:
qw/qwen3-coder-plus
qw/qwen3-coder-flash
```
### Kiro (Claude БЕСПЛАТНО)
```bash
Dashboard → Подключить Kiro
→ AWS Builder ID или Google/GitHub
→ Неограниченное использование
Модели:
kr/claude-sonnet-4.5
kr/claude-haiku-4.5
```
</details>
<details>
<summary><b>🎨 Создание комбо</b></summary>
### Пример 1: Максимизация подписки → Дешёвый бэкап
```
Dashboard → Комбо → Создать новое
Название: premium-coding
Модели:
1. cc/claude-opus-4-6 (Основная подписка)
2. glm/glm-4.7 (Дешёвый бэкап, $0.6/1M)
3. minimax/MiniMax-M2.1 (Самый дешёвый fallback, $0.20/1M)
Используйте в CLI: premium-coding
```
### Пример 2: Только бесплатные (Нулевая стоимость)
```
Название: free-combo
Модели:
1. gc/gemini-3-flash-preview (180K бесплатно/мес)
2. if/kimi-k2-thinking (неограниченно)
3. qw/qwen3-coder-plus (неограниченно)
Стоимость: $0 навсегда!
```
</details>
<details>
<summary><b>🔧 Интеграция с CLI</b></summary>
### Cursor IDE
```
Настройки → Модели → Расширенные:
OpenAI API Base URL: http://localhost:20128/v1
OpenAI API Key: [из dashboard OmniRoute]
Model: cc/claude-opus-4-6
```
### Claude Code
Используйте страницу **CLI Tools** в dashboard для настройки в один клик, или редактируйте `~/.claude/settings.json` вручную.
### Codex CLI
```bash
export OPENAI_BASE_URL="http://localhost:20128"
export OPENAI_API_KEY="your-omniroute-api-key"
codex "your prompt"
```
### OpenClaw
**Вариант 1 — Dashboard (рекомендуется):**
```
Dashboard → CLI Tools → OpenClaw → Выбрать модель → Применить
```
**Вариант 2 — Вручную:** Редактируйте `~/.openclaw/openclaw.json`:
```json
{
"models": {
"providers": {
"omniroute": {
"baseUrl": "http://127.0.0.1:20128/v1",
"apiKey": "sk_omniroute",
"api": "openai-completions"
}
}
}
}
```
> **Примечание:** OpenClaw работает только с локальным OmniRoute. Используйте `127.0.0.1` вместо `localhost` для избежания проблем с IPv6.
### Cline / Continue / RooCode
```
Настройки → Конфигурация API:
Провайдер: OpenAI Compatible
Base URL: http://localhost:20128/v1
API Key: [из dashboard OmniRoute]
Model: if/kimi-k2-thinking
```
</details>
---
## 📊 Доступные модели
<details>
<summary><b>Посмотреть все доступные модели</b></summary>
**Claude Code (`cc/`)** - Pro/Max:
- `cc/claude-opus-4-6`
- `cc/claude-sonnet-4-5-20250929`
- `cc/claude-haiku-4-5-20251001`
**Codex (`cx/`)** - Plus/Pro:
- `cx/gpt-5.2-codex`
- `cx/gpt-5.1-codex-max`
**Gemini CLI (`gc/`)** - БЕСПЛАТНО:
- `gc/gemini-3-flash-preview`
- `gc/gemini-2.5-pro`
**GitHub Copilot (`gh/`)**:
- `gh/gpt-5`
- `gh/claude-4.5-sonnet`
**NVIDIA NIM (`nvidia/`)** - БЕСПЛАТНЫЕ кредиты:
- `nvidia/llama-3.3-70b-instruct`
- `nvidia/mistral-7b-instruct`
- 50+ моделей на [build.nvidia.com](https://build.nvidia.com)
**GLM (`glm/`)** - $0.6/1M:
- `glm/glm-4.7`
**MiniMax (`minimax/`)** - $0.2/1M:
- `minimax/MiniMax-M2.1`
**iFlow (`if/`)** - БЕСПЛАТНО:
- `if/kimi-k2-thinking`
- `if/qwen3-coder-plus`
- `if/deepseek-r1`
- `if/glm-4.7`
- `if/minimax-m2`
**Qwen (`qw/`)** - БЕСПЛАТНО:
- `qw/qwen3-coder-plus`
- `qw/qwen3-coder-flash`
**Kiro (`kr/`)** - БЕСПЛАТНО:
- `kr/claude-sonnet-4.5`
- `kr/claude-haiku-4.5`
**OpenRouter (`or/`)** - 100+ моделей:
- `or/anthropic/claude-4-sonnet`
- `or/google/gemini-2.5-pro`
- Любая модель с [openrouter.ai/models](https://openrouter.ai/models)
</details>
---
## 🧪 Оценки (Evals)
OmniRoute включает встроенный фреймворк оценки для тестирования качества ответов LLM по golden set. Доступ через **Analytics → Evals** в dashboard.
### Встроенный Golden Set
Предзагруженный «OmniRoute Golden Set» содержит 10 тестов:
- Приветствия, математика, география, генерация кода
- Соответствие формату JSON, перевод, markdown
- Отказ от небезопасного контента, подсчёт, булева логика
### Стратегии оценки
| Стратегия | Описание | Пример |
| ---------- | ----------------------------------------------------- | -------------------------------- |
| `exact` | Вывод должен совпадать точно | `"4"` |
| `contains` | Вывод должен содержать подстроку (без учёта регистра) | `"Paris"` |
| `regex` | Вывод должен соответствовать regex-паттерну | `"1.*2.*3"` |
| `custom` | Пользовательская JS-функция возвращает true/false | `(output) => output.length > 10` |
---
## 🐛 Устранение неполадок
<details>
<summary><b>Нажмите для раскрытия руководства</b></summary>
**«Language model did not provide messages»**
- Квота провайдера исчерпана → Проверьте трекер квот в dashboard
- Решение: Используйте комбо с fallback или переключитесь на более дешёвый уровень
**Rate limiting**
- Квота подписки исчерпана → Fallback на GLM/MiniMax
- Добавьте комбо: `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
**OAuth-токен истёк**
- Обновляется автоматически OmniRoute
- Если проблема сохраняется: Dashboard → Провайдер → Переподключить
**Высокие расходы**
- Проверьте статистику в Dashboard → Расходы
- Переключите основную модель на GLM/MiniMax
- Используйте бесплатный уровень (Gemini CLI, iFlow) для некритичных задач
**Dashboard открывается на неправильном порту**
- Установите `PORT=20128` и `NEXT_PUBLIC_BASE_URL=http://localhost:20128`
**Ошибки cloud sync**
- Проверьте что `BASE_URL` указывает на ваш запущенный экземпляр
- Проверьте что `CLOUD_URL` указывает на правильный облачный endpoint
- Держите значения `NEXT_PUBLIC_*` синхронизированными с серверными значениями
**Первый вход не работает**
- Проверьте `INITIAL_PASSWORD` в `.env`
- Если не задан, пароль по умолчанию `123456`
**Нет логов запросов**
- Установите `ENABLE_REQUEST_LOGS=true` в `.env`
**Тест подключения показывает «Invalid» для OpenAI-совместимых провайдеров**
- Многие провайдеры не предоставляют endpoint `/models`
- OmniRoute v1.0.6+ включает fallback-валидацию через chat completions
- Убедитесь что base URL содержит суффикс `/v1`
</details>
---
## 🛠️ Технологический стек
- **Runtime**: Node.js 20+
- **Язык**: TypeScript 5.9 — **100% TypeScript** в `src/` и `open-sse/` (v1.0.6)
- **Framework**: Next.js 16 + React 19 + Tailwind CSS 4
- **База данных**: LowDB (JSON) + SQLite (состояние домена + proxy-логи)
- **Стриминг**: Server-Sent Events (SSE)
- **Аутентификация**: OAuth 2.0 (PKCE) + JWT + API Keys
- **Тестирование**: Node.js test runner (368+ юнит-тестов)
- **CI/CD**: GitHub Actions (авто-публикация npm + Docker Hub при релизе)
- **Сайт**: [omniroute.online](https://omniroute.online)
- **Пакет**: [npmjs.com/package/omniroute](https://www.npmjs.com/package/omniroute)
- **Docker**: [hub.docker.com/r/diegosouzapw/omniroute](https://hub.docker.com/r/diegosouzapw/omniroute)
- **Устойчивость**: Circuit breaker, экспоненциальный backoff, anti-thundering herd, TLS-спуфинг
---
## 📖 Документация
| Документ | Описание |
| ----------------------------------------------- | ------------------------------------------------ |
| [Руководство пользователя](docs/USER_GUIDE.md) | Провайдеры, комбо, интеграция CLI, деплой |
| [Справка API](docs/API_REFERENCE.md) | Все endpoints с примерами |
| [Устранение неполадок](docs/TROUBLESHOOTING.md) | Частые проблемы и решения |
| [Архитектура](docs/ARCHITECTURE.md) | Архитектура системы и внутреннее устройство |
| [Как внести вклад](CONTRIBUTING.md) | Настройка разработки и руководящие принципы |
| [Спецификация OpenAPI](docs/openapi.yaml) | Спецификация OpenAPI 3.0 |
| [Политика безопасности](SECURITY.md) | Сообщение об уязвимостях и практики безопасности |
---
## 📧 Поддержка
> 💬 **Присоединяйтесь к сообществу!** [Группа WhatsApp](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t) — Получайте помощь, делитесь советами и оставайтесь в курсе.
- **Сайт**: [omniroute.online](https://omniroute.online)
- **GitHub**: [github.com/diegosouzapw/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- **Issues**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
- **WhatsApp**: [Группа сообщества](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
- **Оригинальный проект**: [9router от decolua](https://github.com/decolua/9router)
---
## 👥 Участники
[![Contributors](https://contrib.rocks/image?repo=diegosouzapw/OmniRoute&max=100&columns=20&anon=1)](https://github.com/diegosouzapw/OmniRoute/graphs/contributors)
### Как внести вклад
1. Сделайте fork репозитория
2. Создайте ветку функции (`git checkout -b feature/amazing-feature`)
3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`)
4. Отправьте в ветку (`git push origin feature/amazing-feature`)
5. Откройте Pull Request
См. [CONTRIBUTING.md](CONTRIBUTING.md) для подробных рекомендаций.
### Выпуск новой версии
```bash
# Создайте релиз — публикация в npm происходит автоматически
gh release create v1.0.6 --title "v1.0.6" --generate-notes
```
---
## 📊 История звёзд
<a href="https://star-history.com/#diegosouzapw/OmniRoute&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date" />
</picture>
</a>
---
## 🙏 Благодарности
Особая благодарность **[9router](https://github.com/decolua/9router)** от **[decolua](https://github.com/decolua)** — оригинальному проекту, вдохновившему этот форк. OmniRoute строится на этом невероятном фундаменте с дополнительными функциями, мультимодальными API и полной переписью на TypeScript.
Особая благодарность **[CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI)** — оригинальной реализации на Go, вдохновившей этот порт на JavaScript.
---
## 📄 Лицензия
Лицензия MIT — см. [LICENSE](LICENSE) для подробностей.
---
<div align="center">
<sub>Сделано с ❤️ для разработчиков, которые программируют 24/7</sub>
<br/>
<sub><a href="https://omniroute.online">omniroute.online</a></sub>
</div>
-999
View File
@@ -1,999 +0,0 @@
<div align="center">
<img src="./docs/screenshots/MainOmniRoute.png" alt="OmniRoute Dashboard" width="800"/>
# 🚀 OmniRoute — 免费 AI 网关
### 永不停止编程。智能路由至**免费和低成本 AI 模型**,自动故障转移。
_您的通用 API 代理 — 一个端点,36+ 提供商,零停机时间。_
**Chat Completions • Embeddings • 图像生成 • 音频 • Reranking • 100% TypeScript**
---
### 🤖 为您最爱的编程代理提供免费 AI
_通过 OmniRoute 连接任何 AI 驱动的 IDE 或 CLI 工具 — 免费 API 网关,无限编程。_
<table>
<tr>
<td align="center" width="110">
<a href="https://github.com/cline/cline">
<img src="./public/providers/openclaw.png" alt="OpenClaw" width="48"/><br/>
<b>OpenClaw</b>
</a><br/>
<sub>⭐ 205K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/HKUDS/nanobot">
<img src="./public/providers/nanobot.png" alt="NanoBot" width="48"/><br/>
<b>NanoBot</b>
</a><br/>
<sub>⭐ 20.9K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/sipeed/picoclaw">
<img src="./public/providers/picoclaw.jpg" alt="PicoClaw" width="48"/><br/>
<b>PicoClaw</b>
</a><br/>
<sub>⭐ 14.6K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/zeroclaw-labs/zeroclaw">
<img src="./public/providers/zeroclaw.png" alt="ZeroClaw" width="48"/><br/>
<b>ZeroClaw</b>
</a><br/>
<sub>⭐ 9.9K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/nearai/ironclaw">
<img src="./public/providers/ironclaw.png" alt="IronClaw" width="48"/><br/>
<b>IronClaw</b>
</a><br/>
<sub>⭐ 2.1K</sub>
</td>
</tr>
<tr>
<td align="center" width="110">
<a href="https://github.com/anomalyco/opencode">
<img src="./public/providers/opencode.svg" alt="OpenCode" width="48"/><br/>
<b>OpenCode</b>
</a><br/>
<sub>⭐ 106K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/openai/codex">
<img src="./public/providers/codex.png" alt="Codex CLI" width="48"/><br/>
<b>Codex CLI</b>
</a><br/>
<sub>⭐ 60.8K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/anthropics/claude-code">
<img src="./public/providers/claude.png" alt="Claude Code" width="48"/><br/>
<b>Claude Code</b>
</a><br/>
<sub>⭐ 67.3K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/google-gemini/gemini-cli">
<img src="./public/providers/gemini-cli.png" alt="Gemini CLI" width="48"/><br/>
<b>Gemini CLI</b>
</a><br/>
<sub>⭐ 94.7K</sub>
</td>
<td align="center" width="110">
<a href="https://github.com/Kilo-Org/kilocode">
<img src="./public/providers/kilocode.png" alt="Kilo Code" width="48"/><br/>
<b>Kilo Code</b>
</a><br/>
<sub>⭐ 15.5K</sub>
</td>
</tr>
</table>
<sub>📡 所有代理通过 <code>http://localhost:20128/v1</code> 或 <code>http://cloud.omniroute.online/v1</code> 连接 — 一个配置,无限模型和配额</sub>
---
[![npm version](https://img.shields.io/npm/v/omniroute?color=cb3837&logo=npm)](https://www.npmjs.com/package/omniroute)
[![Docker Hub](https://img.shields.io/docker/v/diegosouzapw/omniroute?label=Docker%20Hub&logo=docker&color=2496ED)](https://hub.docker.com/r/diegosouzapw/omniroute)
[![License](https://img.shields.io/github/license/diegosouzapw/OmniRoute)](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
[![Website](https://img.shields.io/badge/Website-omniroute.online-blue?logo=google-chrome&logoColor=white)](https://omniroute.online)
[![WhatsApp](https://img.shields.io/badge/WhatsApp-Community-25D366?logo=whatsapp&logoColor=white)](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
[🌐 网站](https://omniroute.online) • [🚀 快速开始](#-快速开始) • [💡 功能特性](#-核心功能) • [📖 文档](#-文档) • [💰 定价](#-定价概览)
🌐 **多语言版本:** [English](README.md) | [Português](README.pt-BR.md) | [Español](README.es.md) | [Русский](README.ru.md) | [中文](README.zh-CN.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | [Italiano](README.it.md)
</div>
---
## 🤔 为什么选择 OmniRoute
**停止浪费金钱和遭遇限制:**
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> 订阅配额每月未使用就过期
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> 速率限制在编程中途停止你
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> 昂贵的 API(每个提供商 $20-50/月)
- <img src="https://img.shields.io/badge/✗-e74c3c?style=flat-square" height="16"/> 手动在提供商间切换
**OmniRoute 解决这些问题:**
- ✅ **最大化订阅** — 追踪配额,在重置前用完每一点
- ✅ **自动故障转移** — 订阅 → API Key → 低价 → 免费,零停机
- ✅ **多账号** — 每个提供商的账号轮询
- ✅ **通用** — 适用于 Claude Code、Codex、Gemini CLI、Cursor、Cline、OpenClaw、任何 CLI 工具
---
## 🔄 工作原理
```
┌─────────────┐
│ 您的 CLI │ (Claude Code, Codex, Gemini CLI, OpenClaw, Cursor, Cline...)
│ 工具 │
└──────┬──────┘
│ http://localhost:20128/v1
┌─────────────────────────────────────────┐
│ OmniRoute(智能路由器) │
│ • 格式转换(OpenAI ↔ Claude
│ • 配额追踪 + Embeddings + 图像 │
│ • 自动令牌刷新 │
└──────┬──────────────────────────────────┘
├─→ [第1层: 订阅] Claude Code, Codex, Gemini CLI
│ ↓ 配额用完
├─→ [第2层: API KEY] DeepSeek, Groq, xAI, Mistral, NVIDIA NIM 等
│ ↓ 预算限制
├─→ [第3层: 低价] GLM ($0.6/1M), MiniMax ($0.2/1M)
│ ↓ 预算限制
└─→ [第4层: 免费] iFlow, Qwen, Kiro(无限制)
结果:永不停止编程,成本最低
```
---
## ⚡ 快速开始
**1. 全局安装:**
```bash
npm install -g omniroute
omniroute
```
🎉 仪表板在 `http://localhost:20128` 打开
| 命令 | 描述 |
| ----------------------- | ---------------------------- |
| `omniroute` | 启动服务器(默认端口 20128) |
| `omniroute --port 3000` | 使用自定义端口 |
| `omniroute --no-open` | 不自动打开浏览器 |
| `omniroute --help` | 显示帮助 |
**2. 连接免费提供商:**
仪表板 → 提供商 → 连接 **Claude Code****Antigravity** → OAuth 登录 → 完成!
**3. 在 CLI 工具中使用:**
```
Claude Code/Codex/Gemini CLI/OpenClaw/Cursor/Cline 设置:
Endpoint: http://localhost:20128/v1
API Key: [从仪表板复制]
Model: if/kimi-k2-thinking
```
**完成!** 开始使用免费 AI 模型编程。
**替代方案 — 从源代码运行:**
```bash
cp .env.example .env
npm install
PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev
```
---
## 🐳 Docker
OmniRoute 作为公共 Docker 镜像在 [Docker Hub](https://hub.docker.com/r/diegosouzapw/omniroute) 上可用。
**快速运行:**
```bash
docker run -d \
--name omniroute \
--restart unless-stopped \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
**使用环境文件:**
```bash
# 先复制并编辑 .env
cp .env.example .env
docker run -d \
--name omniroute \
--restart unless-stopped \
--env-file .env \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
**使用 Docker Compose**
```bash
# 基础配置(无 CLI 工具)
docker compose --profile base up -d
# CLI 配置(内置 Claude Code、Codex、OpenClaw
docker compose --profile cli up -d
```
| 镜像 | 标签 | 大小 | 描述 |
| ------------------------ | -------- | ------ | ---------- |
| `diegosouzapw/omniroute` | `latest` | ~250MB | 最新稳定版 |
| `diegosouzapw/omniroute` | `1.0.6` | ~250MB | 当前版本 |
---
## 💰 定价概览
| 层级 | 提供商 | 费用 | 配额重置 | 最适合 |
| -------------- | ----------------- | --------------------- | --------------- | ------------ |
| **💳 订阅** | Claude Code (Pro) | $20/月 | 5小时 + 每周 | 已订阅用户 |
| | Codex (Plus/Pro) | $20-200/月 | 5小时 + 每周 | OpenAI 用户 |
| | Gemini CLI | **免费** | 180K/月 + 1K/天 | 所有人! |
| | GitHub Copilot | $10-19/月 | 每月 | GitHub 用户 |
| **🔑 API KEY** | NVIDIA NIM | **免费**(1000 积分) | 一次性 | 免费测试 |
| | DeepSeek | 按使用量 | 无 | 最佳性价比 |
| | Groq | 免费层 + 付费 | 限速 | 超快推理 |
| | xAI (Grok) | 按使用量 | 无 | Grok 模型 |
| | Mistral | 免费层 + 付费 | 限速 | 欧洲 AI |
| | OpenRouter | 按使用量 | 无 | 100+ 模型 |
| **💰 低价** | GLM-4.7 | $0.6/1M | 每日 10时 | 经济备用 |
| | MiniMax M2.1 | $0.2/1M | 5小时滚动 | 最便宜选项 |
| | Kimi K2 | $9/月固定 | 每月 10M Token | 可预测成本 |
| **🆓 免费** | iFlow | $0 | 无限制 | 8 个免费模型 |
| | Qwen | $0 | 无限制 | 3 个免费模型 |
| | Kiro | $0 | 无限制 | 免费 Claude |
**💡 专业建议:** 从 Gemini CLI(每月 180K 免费)+ iFlow(无限免费)开始 = $0 成本!
---
## 🎯 使用场景
### 场景 1"我有 Claude Pro 订阅"
**问题:** 配额未使用就过期,编程高峰期遇到速率限制
```
Combo: "maximize-claude"
1. cc/claude-opus-4-6 (充分使用订阅)
2. glm/glm-4.7 (配额用完时的便宜备用)
3. if/kimi-k2-thinking (免费应急后备)
每月成本:$20(订阅)+ ~$5(备用)= $25 总计
对比:$20 + 遇到限制 = 受挫
```
### 场景 2"我想要零成本"
**问题:** 无法承担订阅费用,需要可靠的 AI 编程
```
Combo: "free-forever"
1. gc/gemini-3-flash (每月 180K 免费)
2. if/kimi-k2-thinking (无限免费)
3. qw/qwen3-coder-plus (无限免费)
每月成本:$0
质量:生产级模型
```
### 场景 3"我需要 24/7 编程,不中断"
**问题:** 截止日期紧迫,不能有停机时间
```
Combo: "always-on"
1. cc/claude-opus-4-6 (最佳质量)
2. cx/gpt-5.2-codex (第二个订阅)
3. glm/glm-4.7 (便宜,每日重置)
4. minimax/MiniMax-M2.1 (最便宜,5小时重置)
5. if/kimi-k2-thinking (免费无限制)
结果:5 层故障转移 = 零停机
```
### 场景 4"我想在 OpenClaw 中使用免费 AI"
**问题:** 需要在消息应用中使用 AI 助手,完全免费
```
Combo: "openclaw-free"
1. if/glm-4.7 (无限免费)
2. if/minimax-m2.1 (无限免费)
3. if/kimi-k2-thinking (无限免费)
每月成本:$0
访问方式:WhatsApp、Telegram、Slack、Discord、iMessage、Signal...
```
---
## 💡 核心功能
### 🧠 路由与智能
| 功能 | 功能描述 |
| ------------------------- | -------------------------------------------------------------------------- |
| 🎯 **智能 4 层故障转移** | 自动路由:订阅 → API Key → 低价 → 免费 |
| 📊 **实时配额追踪** | 实时 Token 计数 + 每个提供商的重置倒计时 |
| 🔄 **格式转换** | OpenAI ↔ Claude ↔ Gemini ↔ Cursor ↔ Kiro 无缝切换 |
| 👥 **多账号支持** | 每个提供商多个账号,智能选择 |
| 🔄 **自动令牌刷新** | OAuth 令牌自动刷新并重试 |
| 🎨 **自定义组合** | 6 种策略:fill-first、round-robin、p2c、random、least-used、cost-optimized |
| 🧩 **自定义模型** | 为任何提供商添加任何模型 ID |
| 🌐 **通配符路由** | 动态路由 `provider/*` 模式到任何提供商 |
| 🧠 **推理预算** | passthrough、auto、custom 和 adaptive 模式用于推理模型 |
| 💬 **System Prompt 注入** | 全局 System Prompt 应用于所有请求 |
| 📄 **Responses API** | 完整支持 OpenAI Responses API (`/v1/responses`) 用于 Codex |
### 🎵 多模态 API
| 功能 | 功能描述 |
| ----------------- | ---------------------------------------------- |
| 🖼️ **图像生成** | `/v1/images/generations` — 4 个提供商,9+ 模型 |
| 📐 **Embeddings** | `/v1/embeddings` — 6 个提供商,9+ 模型 |
| 🎤 **音频转录** | `/v1/audio/transcriptions` — Whisper 兼容 |
| 🔊 **文字转语音** | `/v1/audio/speech` — 多提供商音频合成 |
| 🛡️ **内容审核** | `/v1/moderations` — 内容安全检查 |
| 🔀 **重排序** | `/v1/rerank` — 文档相关性重排序 |
### 🛡️ 弹性与安全
| 功能 | 功能描述 |
| --------------------- | -------------------------------------- |
| 🔌 **断路器** | 每个提供商自动打开/关闭,可配置阈值 |
| 🛡️ **反惊群** | Mutex + 信号量限速用于 API Key 提供商 |
| 🧠 **语义缓存** | 两层缓存(签名 + 语义)降低成本和延迟 |
| ⚡ **请求幂等性** | 5 秒去重窗口防止重复请求 |
| 🔒 **TLS 指纹伪装** | 通过 wreq-js 绕过基于 TLS 的机器人检测 |
| 🌐 **IP 过滤** | 白名单/黑名单用于 API 访问控制 |
| 📊 **可编辑速率限制** | 可配置的 RPM、最小间隔和最大并发 |
### 📊 可观察性与分析
| 功能 | 功能描述 |
| ------------------ | ------------------------------------------ |
| 📝 **请求日志** | 调试模式,完整的请求/响应日志 |
| 💾 **SQLite 日志** | 持久化代理日志,服务器重启后仍然保留 |
| 📊 **分析仪表板** | Recharts:统计卡片、使用量图表、提供商表格 |
| 📈 **进度追踪** | 流式传输的 SSE 进度事件(可选) |
| 🧪 **LLM 评估** | 黄金集测试,4 种匹配策略 |
| 🔍 **请求遥测** | p50/p95/p99 延迟聚合 + X-Request-Id 追踪 |
| 📋 **日志 + 配额** | 专用页面用于日志浏览和配额追踪 |
| 🏥 **健康仪表板** | 运行时间、断路器状态、锁定、缓存统计 |
| 💰 **成本追踪** | 预算管理 + 每模型定价配置 |
### ☁️ 部署与同步
| 功能 | 功能描述 |
| --------------------- | ---------------------------------------------------------- |
| 💾 **Cloud Sync** | 通过 Cloudflare Workers 在设备间同步配置 |
| 🌐 **随处部署** | Localhost、VPS、Docker、Cloudflare Workers |
| 🔑 **API Key 管理** | 按提供商生成、轮换和设定 API Key 范围 |
| 🧙 **配置向导** | 4 步引导式设置,面向新用户 |
| 🔧 **CLI 工具仪表板** | 一键配置 Claude、Codex、Cline、OpenClaw、Kilo、Antigravity |
| 🔄 **数据库备份** | 自动备份和恢复所有设置 |
<details>
<summary><b>📖 功能详情</b></summary>
### 🎯 智能 4 层故障转移
创建带自动故障转移的组合:
```
Combo: "my-coding-stack"
1. cc/claude-opus-4-6 (您的订阅)
2. nvidia/llama-3.3-70b (免费 NVIDIA API
3. glm/glm-4.7 (便宜备用,$0.6/1M)
4. if/kimi-k2-thinking (免费后备)
→ 配额用完或出错时自动切换
```
### 📊 实时配额追踪
- 每个提供商的 Token 消耗
- 重置倒计时(5 小时、每日、每周)
- 付费层级的成本估算
- 月度支出报告
### 🔄 格式转换
格式间的无缝转换:
- **OpenAI****Claude****Gemini** ↔ **OpenAI Responses**
- 您的 CLI 发送 OpenAI 格式 → OmniRoute 转换 → 提供商接收原生格式
- 适用于任何支持自定义 OpenAI 端点的工具
### 👥 多账号支持
- 每个提供商添加多个账号
- 自动轮询或基于优先级的路由
- 当一个账号达到配额时自动切换到下一个
### 🔄 自动令牌刷新
- OAuth 令牌在过期前自动刷新
- 无需手动重新认证
- 所有提供商的无缝体验
### 🎨 自定义组合
- 创建无限模型组合
- 6 种策略:fill-first、round-robin、power-of-two-choices、random、least-used、cost-optimized
- 通过 Cloud Sync 在设备间共享组合
### 🏥 健康仪表板
- 系统状态(运行时间、版本、内存使用)
- 每个提供商的断路器状态(Closed/Open/Half-Open
- 速率限制状态和活动锁定
- 签名缓存统计
- 延迟遥测(p50/p95/p99+ 提示缓存
- 一键重置健康状态
### 🔧 翻译器 Playground
- 调试、测试和可视化 API 格式转换
- 发送请求并查看 OmniRoute 如何在提供商格式间转换
- 对排查集成问题非常有价值
### 💾 Cloud Sync
- 在设备间同步提供商、组合和设置
- 自动后台同步
- 安全加密存储
</details>
---
## 📖 设置指南
<details>
<summary><b>💳 订阅提供商</b></summary>
### Claude Code (Pro/Max)
```bash
仪表板 → 提供商 → 连接 Claude Code
→ OAuth 登录 → 自动令牌刷新
→ 5 小时 + 每周配额追踪
模型:
cc/claude-opus-4-6
cc/claude-sonnet-4-5-20250929
cc/claude-haiku-4-5-20251001
```
**专业建议:** 复杂任务用 Opus,追求速度用 Sonnet。OmniRoute 按模型追踪配额!
### OpenAI Codex (Plus/Pro)
```bash
仪表板 → 提供商 → 连接 Codex
→ OAuth 登录(端口 1455
→ 5 小时 + 每周重置
模型:
cx/gpt-5.2-codex
cx/gpt-5.1-codex-max
```
### Gemini CLI(免费 180K/月!)
```bash
仪表板 → 提供商 → 连接 Gemini CLI
→ Google OAuth
→ 每月 180K completions + 每天 1K
模型:
gc/gemini-3-flash-preview
gc/gemini-2.5-pro
```
**最佳价值:** 巨大的免费额度!在付费层级之前使用。
### GitHub Copilot
```bash
仪表板 → 提供商 → 连接 GitHub
→ 通过 GitHub OAuth
→ 每月重置(每月 1 日)
模型:
gh/gpt-5
gh/claude-4.5-sonnet
gh/gemini-3-pro
```
</details>
<details>
<summary><b>🔑 API Key 提供商</b></summary>
### NVIDIA NIM(免费 1000 积分!)
1. 注册:[build.nvidia.com](https://build.nvidia.com)
2. 获取免费 API key(包含 1000 推理积分)
3. 仪表板 → 添加提供商 → NVIDIA NIM
- API Key`nvapi-your-key`
**模型:** `nvidia/llama-3.3-70b-instruct``nvidia/mistral-7b-instruct` 及 50+ 更多
**专业建议:** OpenAI 兼容的 API — 与 OmniRoute 的格式转换完美配合!
### DeepSeek
1. 注册:[platform.deepseek.com](https://platform.deepseek.com)
2. 获取 API key
3. 仪表板 → 添加提供商 → DeepSeek
**模型:** `deepseek/deepseek-chat``deepseek/deepseek-coder`
### Groq(免费层可用!)
1. 注册:[console.groq.com](https://console.groq.com)
2. 获取 API key(包含免费层)
3. 仪表板 → 添加提供商 → Groq
**模型:** `groq/llama-3.3-70b``groq/mixtral-8x7b`
**专业建议:** 超快推理 — 最适合实时编程!
### OpenRouter100+ 模型)
1. 注册:[openrouter.ai](https://openrouter.ai)
2. 获取 API key
3. 仪表板 → 添加提供商 → OpenRouter
**模型:** 通过一个 API key 访问所有主要提供商的 100+ 模型。
</details>
<details>
<summary><b>💰 低价提供商(备用)</b></summary>
### GLM-4.7(每日重置,$0.6/1M
1. 注册:[Zhipu AI](https://open.bigmodel.cn/)
2. 从 Coding Plan 获取 API key
3. 仪表板 → 添加 API Key
- 提供商:`glm`
- API Key`your-key`
**使用:** `glm/glm-4.7`
**专业建议:** Coding Plan 以 1/7 的价格提供 3 倍配额!每日 10:00 AM 重置。
### MiniMax M2.15 小时重置,$0.20/1M
1. 注册:[MiniMax](https://www.minimax.io/)
2. 获取 API key
3. 仪表板 → 添加 API Key
**使用:** `minimax/MiniMax-M2.1`
**专业建议:** 长上下文(1M Token)最便宜的选项!
### Kimi K2$9/月固定)
1. 订阅:[Moonshot AI](https://platform.moonshot.ai/)
2. 获取 API key
3. 仪表板 → 添加 API Key
**使用:** `kimi/kimi-latest`
**专业建议:** 固定 $9/月 10M Token = $0.90/1M 有效成本!
</details>
<details>
<summary><b>🆓 免费提供商(应急备用)</b></summary>
### iFlow8 个免费模型)
```bash
仪表板 → 连接 iFlow
→ iFlow OAuth 登录
→ 无限使用
模型:
if/kimi-k2-thinking
if/qwen3-coder-plus
if/glm-4.7
if/minimax-m2
if/deepseek-r1
```
### Qwen3 个免费模型)
```bash
仪表板 → 连接 Qwen
→ 设备码授权
→ 无限使用
模型:
qw/qwen3-coder-plus
qw/qwen3-coder-flash
```
### Kiro(免费 Claude
```bash
仪表板 → 连接 Kiro
→ AWS Builder ID 或 Google/GitHub
→ 无限使用
模型:
kr/claude-sonnet-4.5
kr/claude-haiku-4.5
```
</details>
<details>
<summary><b>🎨 创建组合</b></summary>
### 示例 1:最大化订阅 → 便宜备用
```
仪表板 → 组合 → 创建新的
名称:premium-coding
模型:
1. cc/claude-opus-4-6(订阅主力)
2. glm/glm-4.7(便宜备用,$0.6/1M
3. minimax/MiniMax-M2.1(最便宜的后备,$0.20/1M
在 CLI 中使用:premium-coding
```
### 示例 2:仅免费(零成本)
```
名称:free-combo
模型:
1. gc/gemini-3-flash-preview(每月 180K 免费)
2. if/kimi-k2-thinking(无限制)
3. qw/qwen3-coder-plus(无限制)
成本:永远 $0
```
</details>
<details>
<summary><b>🔧 CLI 集成</b></summary>
### Cursor IDE
```
设置 → 模型 → 高级:
OpenAI API Base URL: http://localhost:20128/v1
OpenAI API Key: [从 OmniRoute 仪表板获取]
Model: cc/claude-opus-4-6
```
### Claude Code
使用仪表板中的 **CLI Tools** 页面一键配置,或手动编辑 `~/.claude/settings.json`
### Codex CLI
```bash
export OPENAI_BASE_URL="http://localhost:20128"
export OPENAI_API_KEY="your-omniroute-api-key"
codex "your prompt"
```
### OpenClaw
**选项 1 — 仪表板(推荐):**
```
仪表板 → CLI Tools → OpenClaw → 选择模型 → 应用
```
**选项 2 — 手动:** 编辑 `~/.openclaw/openclaw.json`
```json
{
"models": {
"providers": {
"omniroute": {
"baseUrl": "http://127.0.0.1:20128/v1",
"apiKey": "sk_omniroute",
"api": "openai-completions"
}
}
}
}
```
> **注意:** OpenClaw 仅支持本地 OmniRoute。使用 `127.0.0.1` 而非 `localhost` 以避免 IPv6 解析问题。
### Cline / Continue / RooCode
```
设置 → API 配置:
提供商:OpenAI Compatible
Base URL: http://localhost:20128/v1
API Key: [从 OmniRoute 仪表板获取]
Model: if/kimi-k2-thinking
```
</details>
---
## 📊 可用模型
<details>
<summary><b>查看所有可用模型</b></summary>
**Claude Code (`cc/`)** - Pro/Max:
- `cc/claude-opus-4-6`
- `cc/claude-sonnet-4-5-20250929`
- `cc/claude-haiku-4-5-20251001`
**Codex (`cx/`)** - Plus/Pro:
- `cx/gpt-5.2-codex`
- `cx/gpt-5.1-codex-max`
**Gemini CLI (`gc/`)** - 免费:
- `gc/gemini-3-flash-preview`
- `gc/gemini-2.5-pro`
**GitHub Copilot (`gh/`)**:
- `gh/gpt-5`
- `gh/claude-4.5-sonnet`
**NVIDIA NIM (`nvidia/`)** - 免费积分:
- `nvidia/llama-3.3-70b-instruct`
- `nvidia/mistral-7b-instruct`
- 50+ 更多模型在 [build.nvidia.com](https://build.nvidia.com)
**GLM (`glm/`)** - $0.6/1M:
- `glm/glm-4.7`
**MiniMax (`minimax/`)** - $0.2/1M:
- `minimax/MiniMax-M2.1`
**iFlow (`if/`)** - 免费:
- `if/kimi-k2-thinking`
- `if/qwen3-coder-plus`
- `if/deepseek-r1`
- `if/glm-4.7`
- `if/minimax-m2`
**Qwen (`qw/`)** - 免费:
- `qw/qwen3-coder-plus`
- `qw/qwen3-coder-flash`
**Kiro (`kr/`)** - 免费:
- `kr/claude-sonnet-4.5`
- `kr/claude-haiku-4.5`
**OpenRouter (`or/`)** - 100+ 模型:
- `or/anthropic/claude-4-sonnet`
- `or/google/gemini-2.5-pro`
- [openrouter.ai/models](https://openrouter.ai/models) 上的任何模型
</details>
---
## 🧪 评估 (Evals)
OmniRoute 包含内置评估框架,用于针对黄金集测试 LLM 响应质量。通过仪表板中的 **Analytics → Evals** 访问。
### 内置黄金集
预加载的「OmniRoute Golden Set」包含 10 个测试用例:
- 问候、数学、地理、代码生成
- JSON 格式合规性、翻译、markdown
- 安全拒绝(有害内容)、计数、布尔逻辑
### 评估策略
| 策略 | 描述 | 示例 |
| ---------- | -------------------------------- | -------------------------------- |
| `exact` | 输出必须完全匹配 | `"4"` |
| `contains` | 输出必须包含子串(不区分大小写) | `"Paris"` |
| `regex` | 输出必须匹配正则表达式模式 | `"1.*2.*3"` |
| `custom` | 自定义 JS 函数返回 true/false | `(output) => output.length > 10` |
---
## 🐛 故障排除
<details>
<summary><b>点击展开故障排除指南</b></summary>
**"Language model did not provide messages"**
- 提供商配额已耗尽 → 检查仪表板配额追踪器
- 解决方案:使用组合故障转移或切换到更便宜的层级
**速率限制**
- 订阅配额耗尽 → 回退到 GLM/MiniMax
- 添加组合:`cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
**OAuth 令牌过期**
- OmniRoute 自动刷新
- 如果问题持续:仪表板 → 提供商 → 重新连接
**高成本**
- 在仪表板 → 成本中检查使用统计
- 将主要模型切换为 GLM/MiniMax
- 对非关键任务使用免费层(Gemini CLI、iFlow
**仪表板在错误端口打开**
- 设置 `PORT=20128``NEXT_PUBLIC_BASE_URL=http://localhost:20128`
**Cloud sync 错误**
- 验证 `BASE_URL` 指向您正在运行的实例
- 验证 `CLOUD_URL` 指向预期的云端点
- 保持 `NEXT_PUBLIC_*` 值与服务器端值一致
**首次登录不工作**
- 检查 `.env` 中的 `INITIAL_PASSWORD`
- 如未设置,默认密码为 `123456`
**没有请求日志**
- 在 `.env` 中设置 `ENABLE_REQUEST_LOGS=true`
**兼容 OpenAI 的提供商连接测试显示 "Invalid"**
- 许多提供商不暴露 `/models` 端点
- OmniRoute v1.0.6+ 包含通过 chat completions 的回退验证
- 确保 base URL 包含 `/v1` 后缀
</details>
---
## 🛠️ 技术栈
- **运行时**: Node.js 20+
- **语言**: TypeScript 5.9 — `src/``open-sse/`**100% TypeScript**v1.0.6
- **框架**: Next.js 16 + React 19 + Tailwind CSS 4
- **数据库**: LowDB (JSON) + SQLite(领域状态 + 代理日志)
- **流式传输**: Server-Sent Events (SSE)
- **认证**: OAuth 2.0 (PKCE) + JWT + API Keys
- **测试**: Node.js test runner368+ 单元测试)
- **CI/CD**: GitHub Actions(发布时自动 npm 发布 + Docker Hub
- **网站**: [omniroute.online](https://omniroute.online)
- **包**: [npmjs.com/package/omniroute](https://www.npmjs.com/package/omniroute)
- **Docker**: [hub.docker.com/r/diegosouzapw/omniroute](https://hub.docker.com/r/diegosouzapw/omniroute)
- **弹性**: 断路器、指数退避、反惊群、TLS 伪装
---
## 📖 文档
| 文档 | 描述 |
| ----------------------------------- | ---------------------------- |
| [用户指南](docs/USER_GUIDE.md) | 提供商、组合、CLI 集成、部署 |
| [API 参考](docs/API_REFERENCE.md) | 所有端点及示例 |
| [故障排除](docs/TROUBLESHOOTING.md) | 常见问题和解决方案 |
| [架构](docs/ARCHITECTURE.md) | 系统架构和内部机制 |
| [贡献指南](CONTRIBUTING.md) | 开发设置和指南 |
| [OpenAPI 规范](docs/openapi.yaml) | OpenAPI 3.0 规范 |
| [安全策略](SECURITY.md) | 漏洞报告和安全实践 |
---
## 📧 支持
> 💬 **加入我们的社区!** [WhatsApp 群组](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t) — 获取帮助、分享技巧、了解最新动态。
- **网站**: [omniroute.online](https://omniroute.online)
- **GitHub**: [github.com/diegosouzapw/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- **Issues**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
- **WhatsApp**: [社区群组](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
- **原始项目**: [decolua 的 9router](https://github.com/decolua/9router)
---
## 👥 贡献者
[![Contributors](https://contrib.rocks/image?repo=diegosouzapw/OmniRoute&max=100&columns=20&anon=1)](https://github.com/diegosouzapw/OmniRoute/graphs/contributors)
### 如何贡献
1. Fork 仓库
2. 创建功能分支(`git checkout -b feature/amazing-feature`
3. 提交更改(`git commit -m 'Add amazing feature'`
4. 推送到分支(`git push origin feature/amazing-feature`
5. 打开 Pull Request
详细指南请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
### 发布新版本
```bash
# 创建发布 — npm 发布自动完成
gh release create v1.0.6 --title "v1.0.6" --generate-notes
```
---
## 📊 Star 历史
<a href="https://star-history.com/#diegosouzapw/OmniRoute&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=diegosouzapw/OmniRoute&type=Date" />
</picture>
</a>
---
## 🙏 致谢
特别感谢 **[decolua](https://github.com/decolua)** 的 **[9router](https://github.com/decolua/9router)** — 启发了本 fork 的原始项目。OmniRoute 在这个令人难以置信的基础上添加了额外功能、多模态 API 和完整的 TypeScript 重写。
特别感谢 **[CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI)** — 启发了本 JavaScript 移植的原始 Go 实现。
---
## 📄 许可证
MIT 许可证 — 详见 [LICENSE](LICENSE)。
---
<div align="center">
<sub>用 ❤️ 为 24/7 编程的开发者打造</sub>
<br/>
<sub><a href="https://omniroute.online">omniroute.online</a></sub>
</div>
+3 -3
View File
@@ -20,9 +20,9 @@ If you discover a security vulnerability in OmniRoute, please report it responsi
| Version | Support Status |
| ------- | -------------- |
| 1.0.x | ✅ Active |
| 0.8.x | ✅ Security |
| < 0.8.0 | ❌ Unsupported |
| 0.8.x | ✅ Active |
| 0.7.x | ✅ Security |
| < 0.7.0 | ❌ Unsupported |
---
+8 -20
View File
@@ -81,26 +81,17 @@ console.log(`
\\____/|_| |_| |_|_| |_|_|_| \\_\\___/ \\__,_|\\__\\___|
\x1b[0m`);
// ── Node.js version check ──────────────────────────────────
const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
if (nodeMajor >= 24) {
console.warn(`\x1b[33m ⚠ Warning: You are running Node.js ${process.versions.node}.
OmniRoute uses better-sqlite3, a native addon that does not yet
have compatible prebuilt binaries for Node.js 24+.
You may experience errors like "is not a valid Win32 application"
or "NODE_MODULE_VERSION mismatch".
Recommended: use Node.js 22 LTS (or 20 LTS).
Workaround: npm rebuild better-sqlite3\x1b[0m
`);
}
// ── Resolve server entry ───────────────────────────────────
const serverJs = join(APP_DIR, "server.js");
if (!existsSync(serverJs)) {
console.error("\x1b[31m✖ Server not found at:\x1b[0m", serverJs);
console.error(" This usually means the package was not built correctly.");
console.error(
"\x1b[31m✖ Server not found at:\x1b[0m",
serverJs,
);
console.error(
" This usually means the package was not built correctly.",
);
console.error(" Try reinstalling: npm install -g omniroute");
process.exit(1);
}
@@ -128,10 +119,7 @@ server.stdout.on("data", (data) => {
process.stdout.write(text);
// Detect server ready
if (
!started &&
(text.includes("Ready") || text.includes("started") || text.includes("listening"))
) {
if (!started && (text.includes("Ready") || text.includes("started") || text.includes("listening"))) {
started = true;
onReady();
}
Submodule clipr/9router deleted from bc91be7305
Submodule clipr/CLIProxyAPI deleted from 068630dbd0
-2
View File
@@ -18,8 +18,6 @@
x-common: &common
restart: unless-stopped
env_file: .env
environment:
- DATA_DIR=/app/data # Must match the volume mount below
volumes:
- omniroute-data:/app/data
healthcheck:
-11
View File
@@ -227,17 +227,6 @@ Response example:
| `/api/monitoring/health` | GET | Health check |
| `/api/cache` | GET/DELETE | Cache stats / clear |
### Backup & Export/Import
| Endpoint | Method | Description |
| --------------------------- | ------ | --------------------------------------- |
| `/api/db-backups` | GET | List available backups |
| `/api/db-backups` | PUT | Create a manual backup |
| `/api/db-backups` | POST | Restore from a specific backup |
| `/api/db-backups/export` | GET | Download database as .sqlite file |
| `/api/db-backups/import` | POST | Upload .sqlite file to replace database |
| `/api/db-backups/exportAll` | GET | Download full backup as .tar.gz archive |
### Cloud Sync
| Endpoint | Method | Description |
+84 -100
View File
@@ -1,6 +1,6 @@
# OmniRoute Architecture
_Last updated: 2026-02-18_
_Last updated: 2026-02-15_
## Executive Summary
@@ -17,9 +17,6 @@ Core capabilities:
- Embedding generation via `/v1/embeddings` (6 providers, 9 models)
- Image generation via `/v1/images/generations` (4 providers, 9 models)
- Think tag parsing (`<think>...</think>`) for reasoning models
- Response sanitization for strict OpenAI SDK compatibility
- Role normalization (developer→system, system→user) for cross-provider compatibility
- Structured output conversion (json_schema → Gemini responseSchema)
- Local persistence for providers, keys, aliases, combos, settings, pricing
- Usage/cost tracking and request logging
- Optional cloud sync for multi-device/state sync
@@ -123,18 +120,18 @@ Main directories:
Important compatibility routes:
- `src/app/api/v1/chat/completions/route.ts`
- `src/app/api/v1/messages/route.ts`
- `src/app/api/v1/responses/route.ts`
- `src/app/api/v1/models/route.ts` — includes custom models with `custom: true`
- `src/app/api/v1/embeddings/route.ts` — embedding generation (6 providers)
- `src/app/api/v1/images/generations/route.ts` — image generation (4+ providers incl. Antigravity/Nebius)
- `src/app/api/v1/messages/count_tokens/route.ts`
- `src/app/api/v1/providers/[provider]/chat/completions/route.ts` — dedicated per-provider chat
- `src/app/api/v1/providers/[provider]/embeddings/route.ts` — dedicated per-provider embeddings
- `src/app/api/v1/providers/[provider]/images/generations/route.ts` — dedicated per-provider images
- `src/app/api/v1beta/models/route.ts`
- `src/app/api/v1beta/models/[...path]/route.ts`
- `src/app/api/v1/chat/completions/route.js`
- `src/app/api/v1/messages/route.js`
- `src/app/api/v1/responses/route.js`
- `src/app/api/v1/models/route.js` — includes custom models with `custom: true`
- `src/app/api/v1/embeddings/route.js` — embedding generation (6 providers)
- `src/app/api/v1/images/generations/route.js` — image generation (4+ providers incl. Antigravity/Nebius)
- `src/app/api/v1/messages/count_tokens/route.js`
- `src/app/api/v1/providers/[provider]/chat/completions/route.js` — dedicated per-provider chat
- `src/app/api/v1/providers/[provider]/embeddings/route.js` — dedicated per-provider embeddings
- `src/app/api/v1/providers/[provider]/images/generations/route.js` — dedicated per-provider images
- `src/app/api/v1beta/models/route.js`
- `src/app/api/v1beta/models/[...path]/route.js`
Management domains:
@@ -169,91 +166,89 @@ Management domains:
Main flow modules:
- Entry: `src/sse/handlers/chat.ts`
- Core orchestration: `open-sse/handlers/chatCore.ts`
- Entry: `src/sse/handlers/chat.js`
- Core orchestration: `open-sse/handlers/chatCore.js`
- Provider execution adapters: `open-sse/executors/*`
- Format detection/provider config: `open-sse/services/provider.ts`
- Model parse/resolve: `src/sse/services/model.ts`, `open-sse/services/model.ts`
- Account fallback logic: `open-sse/services/accountFallback.ts`
- Translation registry: `open-sse/translator/index.ts`
- Stream transformations: `open-sse/utils/stream.ts`, `open-sse/utils/streamHandler.ts`
- Usage extraction/normalization: `open-sse/utils/usageTracking.ts`
- Think tag parser: `open-sse/utils/thinkTagParser.ts`
- Embedding handler: `open-sse/handlers/embeddings.ts`
- Embedding provider registry: `open-sse/config/embeddingRegistry.ts`
- Image generation handler: `open-sse/handlers/imageGeneration.ts`
- Image provider registry: `open-sse/config/imageRegistry.ts`
- Response sanitization: `open-sse/handlers/responseSanitizer.ts`
- Role normalization: `open-sse/services/roleNormalizer.ts`
- Format detection/provider config: `open-sse/services/provider.js`
- Model parse/resolve: `src/sse/services/model.js`, `open-sse/services/model.js`
- Account fallback logic: `open-sse/services/accountFallback.js`
- Translation registry: `open-sse/translator/index.js`
- Stream transformations: `open-sse/utils/stream.js`, `open-sse/utils/streamHandler.js`
- Usage extraction/normalization: `open-sse/utils/usageTracking.js`
- Think tag parser: `open-sse/utils/thinkTagParser.js`
- Embedding handler: `open-sse/handlers/embeddings.js`
- Embedding provider registry: `open-sse/config/embeddingRegistry.js`
- Image generation handler: `open-sse/handlers/imageGeneration.js`
- Image provider registry: `open-sse/config/imageRegistry.js`
Services (business logic):
- Account selection/scoring: `open-sse/services/accountSelector.ts`
- Context lifecycle management: `open-sse/services/contextManager.ts`
- IP filter enforcement: `open-sse/services/ipFilter.ts`
- Session tracking: `open-sse/services/sessionManager.ts`
- Request deduplication: `open-sse/services/signatureCache.ts`
- System prompt injection: `open-sse/services/systemPrompt.ts`
- Thinking budget management: `open-sse/services/thinkingBudget.ts`
- Wildcard model routing: `open-sse/services/wildcardRouter.ts`
- Rate limit management: `open-sse/services/rateLimitManager.ts`
- Circuit breaker: `open-sse/services/circuitBreaker.ts`
- Account selection/scoring: `open-sse/services/accountSelector.js`
- Context lifecycle management: `open-sse/services/contextManager.js`
- IP filter enforcement: `open-sse/services/ipFilter.js`
- Session tracking: `open-sse/services/sessionManager.js`
- Request deduplication: `open-sse/services/signatureCache.js`
- System prompt injection: `open-sse/services/systemPrompt.js`
- Thinking budget management: `open-sse/services/thinkingBudget.js`
- Wildcard model routing: `open-sse/services/wildcardRouter.js`
- Rate limit management: `open-sse/services/rateLimitManager.js`
- Circuit breaker: `open-sse/services/circuitBreaker.js`
Domain layer modules:
- Model availability: `src/lib/domain/modelAvailability.ts`
- Cost rules/budgets: `src/lib/domain/costRules.ts`
- Fallback policy: `src/lib/domain/fallbackPolicy.ts`
- Combo resolver: `src/lib/domain/comboResolver.ts`
- Lockout policy: `src/lib/domain/lockoutPolicy.ts`
- Policy engine: `src/domain/policyEngine.ts` — centralized lockout → budget → fallback evaluation
- Error codes catalog: `src/lib/domain/errorCodes.ts`
- Request ID: `src/lib/domain/requestId.ts`
- Fetch timeout: `src/lib/domain/fetchTimeout.ts`
- Request telemetry: `src/lib/domain/requestTelemetry.ts`
- Compliance/audit: `src/lib/domain/compliance/index.ts`
- Eval runner: `src/lib/domain/evalRunner.ts`
- Domain state persistence: `src/lib/db/domainState.ts` — SQLite CRUD for fallback chains, budgets, cost history, lockout state, circuit breakers
- Model availability: `src/lib/domain/modelAvailability.js`
- Cost rules/budgets: `src/lib/domain/costRules.js`
- Fallback policy: `src/lib/domain/fallbackPolicy.js`
- Combo resolver: `src/lib/domain/comboResolver.js`
- Lockout policy: `src/lib/domain/lockoutPolicy.js`
- Policy engine: `src/domain/policyEngine.js` — centralized lockout → budget → fallback evaluation
- Error codes catalog: `src/lib/domain/errorCodes.js`
- Request ID: `src/lib/domain/requestId.js`
- Fetch timeout: `src/lib/domain/fetchTimeout.js`
- Request telemetry: `src/lib/domain/requestTelemetry.js`
- Compliance/audit: `src/lib/domain/compliance/index.js`
- Eval runner: `src/lib/domain/evalRunner.js`
- Domain state persistence: `src/lib/db/domainState.js` — SQLite CRUD for fallback chains, budgets, cost history, lockout state, circuit breakers
OAuth provider modules (12 individual files under `src/lib/oauth/providers/`):
- Registry index: `src/lib/oauth/providers/index.ts`
- Individual providers: `claude.ts`, `codex.ts`, `gemini.ts`, `antigravity.ts`, `iflow.ts`, `qwen.ts`, `kimi-coding.ts`, `github.ts`, `kiro.ts`, `cursor.ts`, `kilocode.ts`, `cline.ts`
- Thin wrapper: `src/lib/oauth/providers.ts` — re-exports from individual modules
- Registry index: `src/lib/oauth/providers/index.js`
- Individual providers: `claude.js`, `codex.js`, `gemini.js`, `antigravity.js`, `iflow.js`, `qwen.js`, `kimi-coding.js`, `github.js`, `kiro.js`, `cursor.js`, `kilocode.js`, `cline.js`
- Thin wrapper: `src/lib/oauth/providers.js` — re-exports from individual modules
## 3) Persistence Layer
Primary state DB:
- `src/lib/localDb.ts`
- `src/lib/localDb.js`
- file: `${DATA_DIR}/db.json` (or `$XDG_CONFIG_HOME/omniroute/db.json` when set, else `~/.omniroute/db.json`)
- entities: providerConnections, providerNodes, modelAliases, combos, apiKeys, settings, pricing, **customModels**, **proxyConfig**, **ipFilter**, **thinkingBudget**, **systemPrompt**
Usage DB:
- `src/lib/usageDb.ts`
- `src/lib/usageDb.js`
- files: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
- follows same base directory policy as `localDb` (`DATA_DIR`, then `XDG_CONFIG_HOME/omniroute` when set)
- decomposed into focused sub-modules: `migrations.ts`, `usageHistory.ts`, `costCalculator.ts`, `usageStats.ts`, `callLogs.ts`
- decomposed into focused sub-modules: `migrations.js`, `usageHistory.js`, `costCalculator.js`, `usageStats.js`, `callLogs.js`
Domain State DB (SQLite):
- `src/lib/db/domainState.ts` — CRUD operations for domain state
- Tables (created in `src/lib/db/core.ts`): `domain_fallback_chains`, `domain_budgets`, `domain_cost_history`, `domain_lockout_state`, `domain_circuit_breakers`
- `src/lib/db/domainState.js` — CRUD operations for domain state
- Tables (created in `src/lib/db/core.js`): `domain_fallback_chains`, `domain_budgets`, `domain_cost_history`, `domain_lockout_state`, `domain_circuit_breakers`
- Write-through cache pattern: in-memory Maps are authoritative at runtime; mutations are written synchronously to SQLite; state is restored from DB on cold start
## 4) Auth + Security Surfaces
- Dashboard cookie auth: `src/proxy.ts`, `src/app/api/auth/login/route.ts`
- API key generation/verification: `src/shared/utils/apiKey.ts`
- Dashboard cookie auth: `src/proxy.js`, `src/app/api/auth/login/route.js`
- API key generation/verification: `src/shared/utils/apiKey.js`
- Provider secrets persisted in `providerConnections` entries
- Outbound proxy support via `open-sse/utils/proxyFetch.ts` (env vars) and `open-sse/utils/networkProxy.ts` (configurable per-provider or global)
- Outbound proxy support via `open-sse/utils/proxyFetch.js` (env vars) and `open-sse/utils/networkProxy.js` (configurable per-provider or global)
## 5) Cloud Sync
- Scheduler init: `src/lib/initCloudSync.ts`, `src/shared/services/initializeCloudSync.ts`
- Periodic task: `src/shared/services/cloudSyncScheduler.ts`
- Control route: `src/app/api/sync/cloud/route.ts`
- Scheduler init: `src/lib/initCloudSync.js`, `src/shared/services/initializeCloudSync.js`
- Periodic task: `src/shared/services/cloudSyncScheduler.js`
- Control route: `src/app/api/sync/cloud/route.js`
## Request Lifecycle (`/v1/chat/completions`)
@@ -332,7 +327,7 @@ flowchart TD
Q -- No --> R[Return all unavailable]
```
Fallback decisions are driven by `open-sse/services/accountFallback.ts` using status codes and error-message heuristics.
Fallback decisions are driven by `open-sse/services/accountFallback.js` using status codes and error-message heuristics.
## OAuth Onboarding and Token Refresh Lifecycle
@@ -364,7 +359,7 @@ sequenceDiagram
Test-->>UI: validation result
```
Refresh during live traffic is executed inside `open-sse/handlers/chatCore.ts` via executor `refreshCredentials()`.
Refresh during live traffic is executed inside `open-sse/handlers/chatCore.js` via executor `refreshCredentials()`.
## Cloud Sync Lifecycle (Enable / Sync / Disable)
@@ -413,9 +408,6 @@ erDiagram
number stickyRoundRobinLimit
boolean requireLogin
string password_hash
string fallbackStrategy
json rateLimitDefaults
json providerProfiles
}
PROVIDER_CONNECTION {
@@ -567,25 +559,25 @@ flowchart LR
### Routing and Execution Core
- `src/sse/handlers/chat.ts`: request parse, combo handling, account selection loop
- `open-sse/handlers/chatCore.ts`: translation, executor dispatch, retry/refresh handling, stream setup
- `src/sse/handlers/chat.js`: request parse, combo handling, account selection loop
- `open-sse/handlers/chatCore.js`: translation, executor dispatch, retry/refresh handling, stream setup
- `open-sse/executors/*`: provider-specific network and format behavior
### Translation Registry and Format Converters
- `open-sse/translator/index.ts`: translator registry and orchestration
- `open-sse/translator/index.js`: translator registry and orchestration
- Request translators: `open-sse/translator/request/*`
- Response translators: `open-sse/translator/response/*`
- Format constants: `open-sse/translator/formats.ts`
- Format constants: `open-sse/translator/formats.js`
### Persistence
- `src/lib/localDb.ts`: persistent config/state
- `src/lib/usageDb.ts`: usage history and rolling request logs
- `src/lib/localDb.js`: persistent config/state
- `src/lib/usageDb.js`: usage history and rolling request logs
## Provider Executor Coverage (Strategy Pattern)
Each provider has a specialized executor extending `BaseExecutor` (in `open-sse/executors/base.ts`), which provides URL building, header construction, retry with exponential backoff, credential refresh hooks, and the `execute()` orchestration method.
Each provider has a specialized executor extending `BaseExecutor` (in `open-sse/executors/base.js`), which provides URL building, header construction, retry with exponential backoff, credential refresh hooks, and the `execute()` orchestration method.
| Executor | Provider(s) | Special Handling |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
@@ -652,23 +644,16 @@ Source Format → OpenAI (hub) → Target Format
Translations are selected dynamically based on source payload shape and provider target format.
Additional processing layers in the translation pipeline:
- **Response sanitization** — Strips non-standard fields from OpenAI-format responses (both streaming and non-streaming) to ensure strict SDK compliance
- **Role normalization** — Converts `developer``system` for non-OpenAI targets; merges `system``user` for models that reject the system role (GLM, ERNIE)
- **Think tag extraction** — Parses `<think>...</think>` blocks from content into `reasoning_content` field
- **Structured output** — Converts OpenAI `response_format.json_schema` to Gemini's `responseMimeType` + `responseSchema`
## Supported API Endpoints
| Endpoint | Format | Handler |
| -------------------------------------------------- | ------------------ | ---------------------------------------------------- |
| `POST /v1/chat/completions` | OpenAI Chat | `src/sse/handlers/chat.ts` |
| `POST /v1/chat/completions` | OpenAI Chat | `src/sse/handlers/chat.js` |
| `POST /v1/messages` | Claude Messages | Same handler (auto-detected) |
| `POST /v1/responses` | OpenAI Responses | `open-sse/handlers/responsesHandler.ts` |
| `POST /v1/embeddings` | OpenAI Embeddings | `open-sse/handlers/embeddings.ts` |
| `POST /v1/responses` | OpenAI Responses | `open-sse/handlers/responsesHandler.js` |
| `POST /v1/embeddings` | OpenAI Embeddings | `open-sse/handlers/embeddings.js` |
| `GET /v1/embeddings` | Model listing | API route |
| `POST /v1/images/generations` | OpenAI Images | `open-sse/handlers/imageGeneration.ts` |
| `POST /v1/images/generations` | OpenAI Images | `open-sse/handlers/imageGeneration.js` |
| `GET /v1/images/generations` | Model listing | API route |
| `POST /v1/providers/{provider}/chat/completions` | OpenAI Chat | Dedicated per-provider with model validation |
| `POST /v1/providers/{provider}/embeddings` | OpenAI Embeddings | Dedicated per-provider with model validation |
@@ -683,11 +668,11 @@ Additional processing layers in the translation pipeline:
## Bypass Handler
The bypass handler (`open-sse/utils/bypassHandler.ts`) intercepts known "throwaway" requests from Claude CLI — warmup pings, title extractions, and token counts — and returns a **fake response** without consuming upstream provider tokens. This is triggered only when `User-Agent` contains `claude-cli`.
The bypass handler (`open-sse/utils/bypassHandler.js`) intercepts known "throwaway" requests from Claude CLI — warmup pings, title extractions, and token counts — and returns a **fake response** without consuming upstream provider tokens. This is triggered only when `User-Agent` contains `claude-cli`.
## Request Logger Pipeline
The request logger (`open-sse/utils/requestLogger.ts`) provides a 7-stage debug logging pipeline, disabled by default, enabled via `ENABLE_REQUEST_LOGS=true`:
The request logger (`open-sse/utils/requestLogger.js`) provides a 7-stage debug logging pipeline, disabled by default, enabled via `ENABLE_REQUEST_LOGS=true`:
```
1_req_client.json → 2_req_source.json → 3_req_openai.json → 4_req_target.json
@@ -729,7 +714,7 @@ Files are written to `<repo>/logs/<session>/` for each request session.
Runtime visibility sources:
- console logs from `src/sse/utils/logger.ts`
- console logs from `src/sse/utils/logger.js`
- per-request usage aggregates in `usage.json`
- textual request status log in `log.txt`
- optional deep request/translation logs under `logs/` when `ENABLE_REQUEST_LOGS=true`
@@ -761,18 +746,17 @@ Environment variables actively used by code:
## Known Architectural Notes
1. `usageDb` and `localDb` now share the same base directory policy (`DATA_DIR` -> `XDG_CONFIG_HOME/omniroute` -> `~/.omniroute`) with legacy file migration.
2. `/api/v1/route.ts` returns a static model list and is not the main models source used by `/v1/models`.
2. `/api/v1/route.js` returns a static model list and is not the main models source used by `/v1/models`.
3. Request logger writes full headers/body when enabled; treat log directory as sensitive.
4. Cloud behavior depends on correct `NEXT_PUBLIC_BASE_URL` and cloud endpoint reachability.
5. The `open-sse/` directory is published as the `@omniroute/open-sse` **npm workspace package**. Source code imports it via `@omniroute/open-sse/...` (resolved by Next.js `transpilePackages`). File paths in this document still use the directory name `open-sse/` for consistency.
6. Charts in the dashboard use **Recharts** (SVG-based) for accessible, interactive analytics visualizations (model usage bar charts, provider breakdown tables with success rates).
7. E2E tests use **Playwright** (`tests/e2e/`), run via `npm run test:e2e`. Unit tests use **Node.js test runner** (`tests/unit/`), run via `npm run test:plan3`. Source code under `src/` is **TypeScript** (`.ts`/`.tsx`); the `open-sse/` workspace remains JavaScript (`.js`).
8. Settings page is organized into 5 tabs: Security, Routing (6 global strategies: fill-first, round-robin, p2c, random, least-used, cost-optimized), Resilience (editable rate limits, circuit breaker, policies), AI (thinking budget, system prompt, prompt cache), Advanced (proxy).
6. Charts in the dashboard use **Recharts** (SVG-based) for accessible, interactive visualizations (bar charts, donut charts).
7. E2E tests use **Playwright** (`tests/e2e/`), run via `npm run test:e2e`. Unit tests use **Node.js test runner** (`tests/unit/`), run via `npm run test:plan3`.
## Operational Verification Checklist
- Build from source: `npm run build`
- Build Docker image: `docker build -t omniroute .`
- Build from source: `cd /root/dev/omniroute && npm run build`
- Build Docker image: `cd /root/dev/omniroute && docker build -t omniroute .`
- Start service and verify:
- `GET /api/settings`
- `GET /api/v1/models`
+46 -46
View File
@@ -110,18 +110,18 @@ The **single source of truth** for all provider configuration.
| File | Purpose |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `constants.ts` | `PROVIDERS` object with base URLs, OAuth credentials (defaults), headers, and default system prompts for every provider. Also defines `HTTP_STATUS`, `ERROR_TYPES`, `COOLDOWN_MS`, `BACKOFF_CONFIG`, and `SKIP_PATTERNS`. |
| `credentialLoader.ts` | Loads external credentials from `data/provider-credentials.json` and merges them over the hardcoded defaults in `PROVIDERS`. Keeps secrets out of source control while maintaining backwards compatibility. |
| `providerModels.ts` | Central model registry: maps provider aliases → model IDs. Functions like `getModels()`, `getProviderByAlias()`. |
| `codexInstructions.ts` | System instructions injected into Codex requests (editing constraints, sandbox rules, approval policies). |
| `defaultThinkingSignature.ts` | Default "thinking" signatures for Claude and Gemini models. |
| `ollamaModels.ts` | Schema definition for local Ollama models (name, size, family, quantization). |
| `constants.js` | `PROVIDERS` object with base URLs, OAuth credentials (defaults), headers, and default system prompts for every provider. Also defines `HTTP_STATUS`, `ERROR_TYPES`, `COOLDOWN_MS`, `BACKOFF_CONFIG`, and `SKIP_PATTERNS`. |
| `credentialLoader.js` | Loads external credentials from `data/provider-credentials.json` and merges them over the hardcoded defaults in `PROVIDERS`. Keeps secrets out of source control while maintaining backwards compatibility. |
| `providerModels.js` | Central model registry: maps provider aliases → model IDs. Functions like `getModels()`, `getProviderByAlias()`. |
| `codexInstructions.js` | System instructions injected into Codex requests (editing constraints, sandbox rules, approval policies). |
| `defaultThinkingSignature.js` | Default "thinking" signatures for Claude and Gemini models. |
| `ollamaModels.js` | Schema definition for local Ollama models (name, size, family, quantization). |
#### Credential Loading Flow
```mermaid
flowchart TD
A["App starts"] --> B["constants.ts defines PROVIDERS\nwith hardcoded defaults"]
A["App starts"] --> B["constants.js defines PROVIDERS\nwith hardcoded defaults"]
B --> C{"data/provider-credentials.json\nexists?"}
C -->|Yes| D["credentialLoader reads JSON"]
C -->|No| E["Use hardcoded defaults"]
@@ -194,15 +194,15 @@ classDiagram
| Executor | Provider | Key Specializations |
| ---------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `base.ts` | — | Abstract base: URL building, headers, retry logic, credential refresh |
| `default.ts` | Claude, Gemini, OpenAI, GLM, Kimi, MiniMax | Generic OAuth token refresh for standard providers |
| `antigravity.ts` | Google Cloud Code | Project/session ID generation, multi-URL fallback, custom retry parsing from error messages ("reset after 2h7m23s") |
| `cursor.ts` | Cursor IDE | **Most complex**: SHA-256 checksum auth, Protobuf request encoding, binary EventStream → SSE response parsing |
| `codex.ts` | OpenAI Codex | Injects system instructions, manages thinking levels, removes unsupported parameters |
| `gemini-cli.ts` | Google Gemini CLI | Custom URL building (`streamGenerateContent`), Google OAuth token refresh |
| `github.ts` | GitHub Copilot | Dual token system (GitHub OAuth + Copilot token), VSCode header mimicking |
| `kiro.ts` | AWS CodeWhisperer | AWS EventStream binary parsing, AMZN event frames, token estimation |
| `index.ts` | — | Factory: maps provider name → executor class, with default fallback |
| `base.js` | — | Abstract base: URL building, headers, retry logic, credential refresh |
| `default.js` | Claude, Gemini, OpenAI, GLM, Kimi, MiniMax | Generic OAuth token refresh for standard providers |
| `antigravity.js` | Google Cloud Code | Project/session ID generation, multi-URL fallback, custom retry parsing from error messages ("reset after 2h7m23s") |
| `cursor.js` | Cursor IDE | **Most complex**: SHA-256 checksum auth, Protobuf request encoding, binary EventStream → SSE response parsing |
| `codex.js` | OpenAI Codex | Injects system instructions, manages thinking levels, removes unsupported parameters |
| `gemini-cli.js` | Google Gemini CLI | Custom URL building (`streamGenerateContent`), Google OAuth token refresh |
| `github.js` | GitHub Copilot | Dual token system (GitHub OAuth + Copilot token), VSCode header mimicking |
| `kiro.js` | AWS CodeWhisperer | AWS EventStream binary parsing, AMZN event frames, token estimation |
| `index.js` | — | Factory: maps provider name → executor class, with default fallback |
---
@@ -212,12 +212,12 @@ The **orchestration layer** — coordinates translation, execution, streaming, a
| File | Purpose |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chatCore.ts` | **Central orchestrator** (~600 lines). Handles the complete request lifecycle: format detection → translation → executor dispatch → streaming/non-streaming response → token refresh → error handling → usage logging. |
| `responsesHandler.ts` | Adapter for OpenAI's Responses API: converts Responses format → Chat Completions → sends to `chatCore` → converts SSE back to Responses format. |
| `embeddings.ts` | Embedding generation handler: resolves embedding model → provider, dispatches to provider API, returns OpenAI-compatible embedding response. Supports 6+ providers. |
| `imageGeneration.ts` | Image generation handler: resolves image model → provider, supports OpenAI-compatible, Gemini-image (Antigravity), and fallback (Nebius) modes. Returns base64 or URL images. |
| `chatCore.js` | **Central orchestrator** (~600 lines). Handles the complete request lifecycle: format detection → translation → executor dispatch → streaming/non-streaming response → token refresh → error handling → usage logging. |
| `responsesHandler.js` | Adapter for OpenAI's Responses API: converts Responses format → Chat Completions → sends to `chatCore` → converts SSE back to Responses format. |
| `embeddings.js` | Embedding generation handler: resolves embedding model → provider, dispatches to provider API, returns OpenAI-compatible embedding response. Supports 6+ providers. |
| `imageGeneration.js` | Image generation handler: resolves image model → provider, supports OpenAI-compatible, Gemini-image (Antigravity), and fallback (Nebius) modes. Returns base64 or URL images. |
#### Request Lifecycle (chatCore.ts)
#### Request Lifecycle (chatCore.js)
```mermaid
sequenceDiagram
@@ -262,20 +262,20 @@ Business logic that supports the handlers and executors.
| File | Purpose |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `provider.ts` | **Format detection** (`detectFormat`): analyzes request body structure to identify Claude/OpenAI/Gemini/Antigravity/Responses formats (includes `max_tokens` heuristic for Claude). Also: URL building, header building, thinking config normalization. Supports `openai-compatible-*` and `anthropic-compatible-*` dynamic providers. |
| `model.ts` | Model string parsing (`claude/model-name``{provider: "claude", model: "model-name"}`), alias resolution with collision detection, input sanitization (rejects path traversal/control chars), and model info resolution with async alias getter support. |
| `accountFallback.ts` | Rate-limit handling: exponential backoff (1s → 2s → 4s → max 2min), account cooldown management, error classification (which errors trigger fallback vs. not). |
| `tokenRefresh.ts` | OAuth token refresh for **every provider**: Google (Gemini, Antigravity), Claude, Codex, Qwen, iFlow, GitHub (OAuth + Copilot dual-token), Kiro (AWS SSO OIDC + Social Auth). Includes in-flight promise deduplication cache and retry with exponential backoff. |
| `combo.ts` | **Combo models**: chains of fallback models. If model A fails with a fallback-eligible error, try model B, then C, etc. Returns actual upstream status codes. |
| `usage.ts` | Fetches quota/usage data from provider APIs (GitHub Copilot quotas, Antigravity model quotas, Codex rate limits, Kiro usage breakdowns, Claude settings). |
| `accountSelector.ts` | Smart account selection with scoring algorithm: considers priority, health status, round-robin position, and cooldown state to pick the optimal account for each request. |
| `contextManager.ts` | Request context lifecycle management: creates and tracks per-request context objects with metadata (request ID, timestamps, provider info) for debugging and logging. |
| `ipFilter.ts` | IP-based access control: supports allowlist and blocklist modes. Validates client IP against configured rules before processing API requests. |
| `sessionManager.ts` | Session tracking with client fingerprinting: tracks active sessions using hashed client identifiers, monitors request counts, and provides session metrics. |
| `signatureCache.ts` | Request signature-based deduplication cache: prevents duplicate requests by caching recent request signatures and returning cached responses for identical requests within a time window. |
| `systemPrompt.ts` | Global system prompt injection: prepends or appends a configurable system prompt to all requests, with per-provider compatibility handling. |
| `thinkingBudget.ts` | Reasoning token budget management: supports passthrough, auto (strip thinking config), custom (fixed budget), and adaptive (complexity-scaled) modes for controlling thinking/reasoning tokens. |
| `wildcardRouter.ts` | Wildcard model pattern routing: resolves wildcard patterns (e.g., `*/claude-*`) to concrete provider/model pairs based on availability and priority. |
| `provider.js` | **Format detection** (`detectFormat`): analyzes request body structure to identify Claude/OpenAI/Gemini/Antigravity/Responses formats (includes `max_tokens` heuristic for Claude). Also: URL building, header building, thinking config normalization. Supports `openai-compatible-*` and `anthropic-compatible-*` dynamic providers. |
| `model.js` | Model string parsing (`claude/model-name``{provider: "claude", model: "model-name"}`), alias resolution with collision detection, input sanitization (rejects path traversal/control chars), and model info resolution with async alias getter support. |
| `accountFallback.js` | Rate-limit handling: exponential backoff (1s → 2s → 4s → max 2min), account cooldown management, error classification (which errors trigger fallback vs. not). |
| `tokenRefresh.js` | OAuth token refresh for **every provider**: Google (Gemini, Antigravity), Claude, Codex, Qwen, iFlow, GitHub (OAuth + Copilot dual-token), Kiro (AWS SSO OIDC + Social Auth). Includes in-flight promise deduplication cache and retry with exponential backoff. |
| `combo.js` | **Combo models**: chains of fallback models. If model A fails with a fallback-eligible error, try model B, then C, etc. Returns actual upstream status codes. |
| `usage.js` | Fetches quota/usage data from provider APIs (GitHub Copilot quotas, Antigravity model quotas, Codex rate limits, Kiro usage breakdowns, Claude settings). |
| `accountSelector.js` | Smart account selection with scoring algorithm: considers priority, health status, round-robin position, and cooldown state to pick the optimal account for each request. |
| `contextManager.js` | Request context lifecycle management: creates and tracks per-request context objects with metadata (request ID, timestamps, provider info) for debugging and logging. |
| `ipFilter.js` | IP-based access control: supports allowlist and blocklist modes. Validates client IP against configured rules before processing API requests. |
| `sessionManager.js` | Session tracking with client fingerprinting: tracks active sessions using hashed client identifiers, monitors request counts, and provides session metrics. |
| `signatureCache.js` | Request signature-based deduplication cache: prevents duplicate requests by caching recent request signatures and returning cached responses for identical requests within a time window. |
| `systemPrompt.js` | Global system prompt injection: prepends or appends a configurable system prompt to all requests, with per-provider compatibility handling. |
| `thinkingBudget.js` | Reasoning token budget management: supports passthrough, auto (strip thinking config), custom (fixed budget), and adaptive (complexity-scaled) modes for controlling thinking/reasoning tokens. |
| `wildcardRouter.js` | Wildcard model pattern routing: resolves wildcard patterns (e.g., `*/claude-*`) to concrete provider/model pairs based on availability and priority. |
#### Token Refresh Deduplication
@@ -377,8 +377,8 @@ graph TD
| `request/` | 8 translators | Convert request bodies between formats. Each file self-registers via `register(from, to, fn)` on import. |
| `response/` | 7 translators | Convert streaming response chunks between formats. Handles SSE event types, thinking blocks, tool calls. |
| `helpers/` | 6 helpers | Shared utilities: `claudeHelper` (system prompt extraction, thinking config), `geminiHelper` (parts/contents mapping), `openaiHelper` (format filtering), `toolCallHelper` (ID generation, missing response injection), `maxTokensHelper`, `responsesApiHelper`. |
| `index.ts` | — | Translation engine: `translateRequest()`, `translateResponse()`, state management, registry. |
| `formats.ts` | — | Format constants: `OPENAI`, `CLAUDE`, `GEMINI`, `ANTIGRAVITY`, `KIRO`, `CURSOR`, `OPENAI_RESPONSES`. |
| `index.js` | — | Translation engine: `translateRequest()`, `translateResponse()`, state management, registry. |
| `formats.js` | — | Format constants: `OPENAI`, `CLAUDE`, `GEMINI`, `ANTIGRAVITY`, `KIRO`, `CURSOR`, `OPENAI_RESPONSES`. |
#### Key Design: Self-Registering Plugins
@@ -397,13 +397,13 @@ import "./request/claude-to-openai.js"; // ← self-registers
| File | Purpose |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `error.ts` | Error response building (OpenAI-compatible format), upstream error parsing, Antigravity retry-time extraction from error messages, SSE error streaming. |
| `stream.ts` | **SSE Transform Stream** — the core streaming pipeline. Two modes: `TRANSLATE` (full format translation) and `PASSTHROUGH` (normalize + extract usage). Handles chunk buffering, usage estimation, content length tracking. Per-stream encoder/decoder instances avoid shared state. |
| `streamHelpers.ts` | Low-level SSE utilities: `parseSSELine` (whitespace-tolerant), `hasValuableContent` (filters empty chunks for OpenAI/Claude/Gemini), `fixInvalidId`, `formatSSE` (format-aware SSE serialization with `perf_metrics` cleanup). |
| `usageTracking.ts` | Token usage extraction from any format (Claude/OpenAI/Gemini/Responses), estimation with separate tool/message char-per-token ratios, buffer addition (2000 tokens safety margin), format-specific field filtering, console logging with ANSI colors. |
| `requestLogger.ts` | File-based request logging (opt-in via `ENABLE_REQUEST_LOGS=true`). Creates session folders with numbered files: `1_req_client.json``7_res_client.txt`. All I/O is async (fire-and-forget). Masks sensitive headers. |
| `bypassHandler.ts` | Intercepts specific patterns from Claude CLI (title extraction, warmup, count) and returns fake responses without calling any provider. Supports both streaming and non-streaming. Intentionally limited to Claude CLI scope. |
| `networkProxy.ts` | Resolves outbound proxy URL for a given provider with precedence: provider-specific config → global config → environment variables (`HTTPS_PROXY`/`HTTP_PROXY`/`ALL_PROXY`). Supports `NO_PROXY` exclusions. Caches config for 30s. |
| `error.js` | Error response building (OpenAI-compatible format), upstream error parsing, Antigravity retry-time extraction from error messages, SSE error streaming. |
| `stream.js` | **SSE Transform Stream** — the core streaming pipeline. Two modes: `TRANSLATE` (full format translation) and `PASSTHROUGH` (normalize + extract usage). Handles chunk buffering, usage estimation, content length tracking. Per-stream encoder/decoder instances avoid shared state. |
| `streamHelpers.js` | Low-level SSE utilities: `parseSSELine` (whitespace-tolerant), `hasValuableContent` (filters empty chunks for OpenAI/Claude/Gemini), `fixInvalidId`, `formatSSE` (format-aware SSE serialization with `perf_metrics` cleanup). |
| `usageTracking.js` | Token usage extraction from any format (Claude/OpenAI/Gemini/Responses), estimation with separate tool/message char-per-token ratios, buffer addition (2000 tokens safety margin), format-specific field filtering, console logging with ANSI colors. |
| `requestLogger.js` | File-based request logging (opt-in via `ENABLE_REQUEST_LOGS=true`). Creates session folders with numbered files: `1_req_client.json``7_res_client.txt`. All I/O is async (fire-and-forget). Masks sensitive headers. |
| `bypassHandler.js` | Intercepts specific patterns from Claude CLI (title extraction, warmup, count) and returns fake responses without calling any provider. Supports both streaming and non-streaming. Intentionally limited to Claude CLI scope. |
| `networkProxy.js` | Resolves outbound proxy URL for a given provider with precedence: provider-specific config → global config → environment variables (`HTTPS_PROXY`/`HTTP_PROXY`/`ALL_PROXY`). Supports `NO_PROXY` exclusions. Caches config for 30s. |
#### SSE Streaming Pipeline
@@ -450,7 +450,7 @@ logs/
| Directory | Purpose |
| ------------- | ---------------------------------------------------------------------- |
| `src/app/` | Web UI, API routes, Express middleware, OAuth callback handlers |
| `src/lib/` | Database access (`localDb.ts`, `usageDb.ts`), authentication, shared |
| `src/lib/` | Database access (`localDb.js`, `usageDb.js`), authentication, shared |
| `src/mitm/` | Man-in-the-middle proxy utilities for intercepting provider traffic |
| `src/models/` | Database model definitions |
| `src/shared/` | Wrappers around open-sse functions (provider, stream, error, etc.) |
@@ -484,7 +484,7 @@ All formats translate through **OpenAI format as the hub**. Adding a new provide
### 5.2 Executor Strategy Pattern
Each provider has a dedicated executor class inheriting from `BaseExecutor`. The factory in `executors/index.ts` selects the right one at runtime.
Each provider has a dedicated executor class inheriting from `BaseExecutor`. The factory in `executors/index.js` selects the right one at runtime.
### 5.3 Self-Registering Plugin System
-75
View File
@@ -1,75 +0,0 @@
# OmniRoute — Dashboard Features Gallery
Visual guide to every section of the OmniRoute dashboard.
---
## 🔌 Providers
Manage AI provider connections: OAuth providers (Claude Code, Codex, Gemini CLI), API key providers (Groq, DeepSeek, OpenRouter), and free providers (iFlow, Qwen, Kiro).
![Providers Dashboard](screenshots/01-providers.png)
---
## 🎨 Combos
Create model routing combos with 6 strategies: fill-first, round-robin, power-of-two-choices, random, least-used, and cost-optimized. Each combo chains multiple models with automatic fallback.
![Combos Dashboard](screenshots/02-combos.png)
---
## 📊 Analytics
Comprehensive usage analytics with token consumption, cost estimates, activity heatmaps, weekly distribution charts, and per-provider breakdowns.
![Analytics Dashboard](screenshots/03-analytics.png)
---
## 🏥 System Health
Real-time monitoring: uptime, memory, version, latency percentiles (p50/p95/p99), cache statistics, and provider circuit breaker states.
![Health Dashboard](screenshots/04-health.png)
---
## 🔧 Translator Playground
Four modes for debugging API translations: **Playground** (format converter), **Chat Tester** (live requests), **Test Bench** (batch tests), and **Live Monitor** (real-time stream).
![Translator Playground](screenshots/05-translator.png)
---
## ⚙️ Settings
General settings, system storage, backup management (export/import database), appearance (dark/light mode), security (includes API endpoint protection and custom provider blocking), routing, resilience, and advanced configuration.
![Settings Dashboard](screenshots/06-settings.png)
---
## 🔧 CLI Tools
One-click configuration for AI coding tools: Claude Code, Codex CLI, Gemini CLI, OpenClaw, Kilo Code, and Antigravity.
![CLI Tools Dashboard](screenshots/07-cli-tools.png)
---
## 📝 Request Logs
Real-time request logging with filtering by provider, model, account, and API key. Shows status codes, token usage, latency, and response details.
![Usage Logs](screenshots/08-usage.png)
---
## 🌐 API Endpoint
Your unified API endpoint with capability breakdown: Chat Completions, Embeddings, Image Generation, Reranking, Audio Transcription, and registered API keys.
![Endpoint Dashboard](screenshots/09-endpoint.png)
+35 -56
View File
@@ -8,24 +8,24 @@
### Backend Core
- [x] `constants.ts` — Substituir `COOLDOWN_MS.transient` por `transientInitial` (5s) + `transientMax` (60s)
- [x] `constants.ts` — Adicionar `PROVIDER_PROFILES` (oauth / apikey) com cooldowns diferenciados
- [x] `constants.ts` — Adicionar `DEFAULT_API_LIMITS` (100 RPM, 200ms minTime)
- [x] `providerRegistry.ts` — Criar helper `getProviderCategory(providerId)``"oauth"` | `"apikey"`
- [x] `accountFallback.ts` — Aceitar `provider` como parâmetro em `checkFallbackError`
- [x] `accountFallback.ts` — Implementar backoff exponencial para 502/503/504 transientes
- [x] `accountFallback.ts` — Calcular cooldown baseado no perfil do provedor
- [x] `accountFallback.ts` — Adicionar helper `getProviderProfile(provider)`
- [ ] `constants.js` — Substituir `COOLDOWN_MS.transient` por `transientInitial` (5s) + `transientMax` (60s)
- [ ] `constants.js` — Adicionar `PROVIDER_PROFILES` (oauth / apikey) com cooldowns diferenciados
- [ ] `constants.js` — Adicionar `DEFAULT_API_LIMITS` (100 RPM, 200ms minTime)
- [ ] `providerRegistry.js` — Criar helper `getProviderCategory(providerId)``"oauth"` | `"apikey"`
- [ ] `accountFallback.js` — Aceitar `provider` como parâmetro em `checkFallbackError`
- [ ] `accountFallback.js` — Implementar backoff exponencial para 502/503/504 transientes
- [ ] `accountFallback.js` — Calcular cooldown baseado no perfil do provedor
- [ ] `accountFallback.js` — Adicionar helper `getProviderProfile(provider)`
### Callers (propagar `provider`)
- [x] `auth.ts``markAccountUnavailable` — Passar `provider` para `checkFallbackError`
- [x] `combo.ts``handleComboChat` / `handleRoundRobinCombo` — Passar `provider` nos erros
- [ ] `auth.js``markAccountUnavailable` — Passar `provider` para `checkFallbackError`
- [ ] `combo.js``handleComboChat` / `handleRoundRobinCombo` — Passar `provider` nos erros
### Testes
- [x] Atualizar `rate-limit-enhanced.test.mjs` — Teste "transient errors don't increase backoff" → `newBackoffLevel = 1`
- [x] Criar `error-classification.test.mjs` — Cooldown exponencial 502, perfis OAuth/API, helper `getProviderCategory`
- [ ] Atualizar `rate-limit-enhanced.test.mjs` — Teste "transient errors don't increase backoff" → `newBackoffLevel = 1`
- [ ] Criar `error-classification.test.mjs` — Cooldown exponencial 502, perfis OAuth/API, helper `getProviderCategory`
---
@@ -33,15 +33,15 @@
### Backend
- [x] `combo.ts` — Importar `getCircuitBreaker` e `CircuitBreakerOpenError`
- [x] `combo.ts``handleComboChat` — Verificar `breaker.canExecute()` antes de cada modelo
- [x] `combo.ts``handleRoundRobinCombo` — Integrar breaker per-model
- [x] `combo.ts` — Marcar `semaphore.markRateLimited` para 502/503/504 (não só 429)
- [x] `combo.ts` — Implementar early exit quando todos os modelos têm breaker OPEN
- [ ] `combo.js` — Importar `getCircuitBreaker` e `CircuitBreakerOpenError`
- [ ] `combo.js``handleComboChat` — Verificar `breaker.canExecute()` antes de cada modelo
- [ ] `combo.js``handleRoundRobinCombo` — Integrar breaker per-model
- [ ] `combo.js` — Marcar `semaphore.markRateLimited` para 502/503/504 (não só 429)
- [ ] `combo.js` — Implementar early exit quando todos os modelos têm breaker OPEN
### Testes
- [x] Criar `combo-circuit-breaker.test.mjs` — Combo skip breaker OPEN, early exit, semáforo 502
- [ ] Criar `combo-circuit-breaker.test.mjs` — Combo skip breaker OPEN, early exit, semáforo 502
---
@@ -49,13 +49,13 @@
### Backend
- [x] `rateLimitManager.ts` — Auto-enable para `apikey` providers com limites elevados
- [x] `rateLimitManager.ts` — Criar limiter com defaults (100 RPM) quando não configurado
- [x] `auth.ts` — Adicionar mutex na `markAccountUnavailable` para evitar marcação paralela
- [ ] `rateLimitManager.js` — Auto-enable para `apikey` providers com limites elevados
- [ ] `rateLimitManager.js` — Criar limiter com defaults (100 RPM) quando não configurado
- [ ] `auth.js` — Adicionar mutex na `markAccountUnavailable` para evitar marcação paralela
### Testes
- [x] Criar `thundering-herd.test.mjs` — Mutex, auto-enable, limites não restritivos
- [ ] Criar `thundering-herd.test.mjs` — Mutex, auto-enable, limites não restritivos
---
@@ -63,51 +63,30 @@
### Settings Page
- [x] `settings/page.tsx` — Adicionar tab "Resilience" (icon: `health_and_safety`) entre Routing e Pricing
- [ ] `settings/page.js` — Adicionar tab "Resilience" (icon: `health_and_safety`) entre Routing e Pricing
### Novos Componentes
- [x] Criar `ResilienceTab.tsx` — Layout com 4 cards (Provider Profiles → Rate Limiting → Circuit Breakers → Policies)
- [x] Criar `ProviderProfilesCard.tsx` — Toggle OAuth/API Key, inputs para cooldowns
- [x] Criar `CircuitBreakerCard.tsx` — Status real-time per-provider, auto-refresh 5s, botão reset
- [x] Criar `RateLimitOverviewCard.tsx` — Tabela providers × accounts × cooldown — **agora editável com RPM, Min Gap, Max Concurrent**
- [ ] Criar `ResilienceTab.js` — Layout com 3 cards
- [ ] Criar `ProviderProfilesCard.js` — Toggle OAuth/API Key, inputs para cooldowns
- [ ] Criar `CircuitBreakerCard.js` — Status real-time per-provider, auto-refresh 5s, botão reset
- [ ] Criar `RateLimitOverviewCard.js` — Tabela providers × accounts × cooldown
### API Routes
- [x] Criar `api/resilience/route.ts` — GET (estado completo + defaults mesclados) + PATCH (salvar perfis + defaults)
- [x] Criar `api/resilience/reset/route.ts` — POST (resetar breakers + cooldowns)
- [ ] Criar `api/resilience/route.js` — GET (estado completo) + PATCH (salvar perfis)
- [ ] Criar `api/resilience/reset/route.js` — POST (resetar breakers + cooldowns)
### Migração
- [x] `PoliciesPanel.tsx` movido de Security para Resilience tab
---
## Fase 5 — Settings Page Restructure (v0.9.0)
### Tab Reorganization
- [x] **Security** — Simplificado para Login/Password + IP Access Control
- [x] **Routing** — Expandido para 6 estratégias globais com descrições
- [x] **Resilience** — Reordenado: Provider Profiles → Rate Limiting (editável) → Circuit Breakers → Policies
- [x] **AI** — Thinking Budget + System Prompt + Prompt Cache (movido do Advanced)
- [x] **Advanced** — Simplificado para apenas Global Proxy
### Backend Routing Strategies
- [x] `auth.ts` — Implementar `random` (Fisher-Yates shuffle)
- [x] `auth.ts` — Implementar `least-used` (sorted by lastUsedAt)
- [x] `auth.ts` — Implementar `cost-optimized` (sorted by priority)
- [x] `auth.ts` — Corrigir `p2c` (power-of-two-choices com health scoring)
- [x] `settings.ts` — Expandir tipo `fallbackStrategy` para 6 valores
- [ ] Avaliar se `PoliciesPanel.js` pode ser removido ou simplificado após nova aba
---
## Verificação Final
- [x] Rodar todos os testes unitários: `node --test tests/unit/*.test.mjs`
- [x] Build do Next.js: `npm run build`
- [x] Verificar aba Resilience no browser
- [x] Testar persistência dos perfis (salvar → reload)
- [x] Testar Reset All Breakers
- [x] Verificar todas as 5 tabs reestruturadas
- [ ] Rodar todos os testes unitários: `node --test tests/unit/*.test.mjs`
- [ ] Build do Next.js: `npm run build`
- [ ] Verificar aba Resilience no browser
- [ ] Testar persistência dos perfis (salvar → reload)
- [ ] Testar Reset All Breakers
-4
View File
@@ -177,10 +177,6 @@ Use **Dashboard → Translator** to debug format translation issues:
- **Thinking tags not appearing** — Check if the target provider supports thinking and the thinking budget setting
- **Tool calls dropping** — Some format translations may strip unsupported fields; verify in Playground mode
- **System prompt missing** — Claude and Gemini handle system prompts differently; check translation output
- **SDK returns raw string instead of object** — Fixed in v1.1.0: response sanitizer now strips non-standard fields (`x_groq`, `usage_breakdown`, etc.) that cause OpenAI SDK Pydantic validation failures
- **GLM/ERNIE rejects `system` role** — Fixed in v1.1.0: role normalizer automatically merges system messages into user messages for incompatible models
- **`developer` role not recognized** — Fixed in v1.1.0: automatically converted to `system` for non-OpenAI providers
- **`json_schema` not working with Gemini** — Fixed in v1.1.0: `response_format` is now converted to Gemini's `responseMimeType` + `responseSchema`
---
+8 -65
View File
@@ -513,9 +513,6 @@ Configure via **Dashboard → Settings → Routing**.
| **Fill First** | Uses accounts in priority order — primary account handles all requests until unavailable |
| **Round Robin** | Cycles through all accounts with a configurable sticky limit (default: 3 calls per account) |
| **P2C (Power of Two Choices)** | Picks 2 random accounts and routes to the healthier one — balances load with awareness of health |
| **Random** | Randomly selects an account for each request using Fisher-Yates shuffle |
| **Least Used** | Routes to the account with the oldest `lastUsedAt` timestamp, distributing traffic evenly |
| **Cost Optimized** | Routes to the account with the lowest priority value, optimizing for lowest-cost providers |
#### Wildcard Model Aliases
@@ -545,79 +542,25 @@ Chain: production-fallback
Configure via **Dashboard → Settings → Resilience**.
OmniRoute implements provider-level resilience with four components:
OmniRoute implements provider-level resilience with three components:
1. **Provider Profiles** — Per-provider configuration for:
1. **Circuit Breaker** — Tracks failures per provider and automatically opens the circuit when a threshold is reached:
- **CLOSED** (Healthy) — Requests flow normally
- **OPEN** — Provider is temporarily blocked after repeated failures
- **HALF_OPEN** — Testing if provider has recovered
2. **Provider Profiles** — Per-provider configuration for:
- Failure threshold (how many failures before opening)
- Cooldown duration
- Rate limit detection sensitivity
- Exponential backoff parameters
2. **Editable Rate Limits** — System-level defaults configurable in the dashboard:
- **Requests Per Minute (RPM)** — Maximum requests per minute per account
- **Min Time Between Requests** — Minimum gap in milliseconds between requests
- **Max Concurrent Requests** — Maximum simultaneous requests per account
- Click **Edit** to modify, then **Save** or **Cancel**. Values persist via the resilience API.
3. **Circuit Breaker** — Tracks failures per provider and automatically opens the circuit when a threshold is reached:
- **CLOSED** (Healthy) — Requests flow normally
- **OPEN** — Provider is temporarily blocked after repeated failures
- **HALF_OPEN** — Testing if provider has recovered
4. **Policies & Locked Identifiers** — Shows circuit breaker status and locked identifiers with force-unlock capability.
5. **Rate Limit Auto-Detection** — Monitors `429` and `Retry-After` headers to proactively avoid hitting provider rate limits.
3. **Rate Limit Auto-Detection** — Monitors `429` and `Retry-After` headers to proactively avoid hitting provider rate limits.
**Pro Tip:** Use **Reset All** button to clear all circuit breakers and cooldowns when a provider recovers from an outage.
---
### Database Export / Import
Manage database backups in **Dashboard → Settings → System & Storage**.
| Action | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| **Export Database** | Downloads the current SQLite database as a `.sqlite` file |
| **Export All (.tar.gz)** | Downloads a full backup archive including: database, settings, combos, provider connections (no credentials), API key metadata |
| **Import Database** | Upload a `.sqlite` file to replace the current database. A pre-import backup is automatically created |
```bash
# API: Export database
curl -o backup.sqlite http://localhost:20128/api/db-backups/export
# API: Export all (full archive)
curl -o backup.tar.gz http://localhost:20128/api/db-backups/exportAll
# API: Import database
curl -X POST http://localhost:20128/api/db-backups/import \
-F "file=@backup.sqlite"
```
**Import Validation:** The imported file is validated for integrity (SQLite pragma check), required tables (`provider_connections`, `provider_nodes`, `combos`, `api_keys`), and size (max 100MB).
**Use Cases:**
- Migrate OmniRoute between machines
- Create external backups for disaster recovery
- Share configurations between team members (export all → share archive)
---
### Settings Dashboard
The settings page is organized into 5 tabs for easy navigation:
| Tab | Contents |
| -------------- | ---------------------------------------------------------------------------------------------- |
| **Security** | Login/Password settings, IP Access Control, API auth for `/models`, and Provider Blocking |
| **Routing** | Global routing strategy (6 options), wildcard model aliases, fallback chains, combo defaults |
| **Resilience** | Provider profiles, editable rate limits, circuit breaker status, policies & locked identifiers |
| **AI** | Thinking budget configuration, global system prompt injection, prompt cache stats |
| **Advanced** | Global proxy configuration (HTTP/SOCKS5) |
---
### Costs & Budget Management
Access via **Dashboard → Costs**.
-399
View File
@@ -1,399 +0,0 @@
# OmniRoute — Guia de Deploy em VM com Cloudflare
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
---
## Pré-Requisitos
| Item | Mínimo | Recomendado |
| ----------- | ------------------------ | ---------------- |
| **CPU** | 1 vCPU | 2 vCPU |
| **RAM** | 1 GB | 2 GB |
| **Disco** | 10 GB SSD | 25 GB SSD |
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
| **Domínio** | Registrado no Cloudflare | — |
| **Docker** | Docker Engine 24+ | Docker 27+ |
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
---
## 1. Configurar a VM
### 1.1 Criar a instância
No seu provider de VPS preferido:
- Escolha Ubuntu 24.04 LTS
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
- Defina uma senha forte para root ou configure SSH key
- Anote o **IP público** (ex: `203.0.113.10`)
### 1.2 Conectar via SSH
```bash
ssh root@203.0.113.10
```
### 1.3 Atualizar o sistema
```bash
apt update && apt upgrade -y
```
### 1.4 Instalar Docker
```bash
# Instalar dependências
apt install -y ca-certificates curl gnupg
# Adicionar repositório oficial do Docker
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
```
### 1.5 Instalar nginx
```bash
apt install -y nginx
```
### 1.6 Configurar Firewall (UFW)
```bash
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP (redirect)
ufw allow 443/tcp # HTTPS
ufw enable
```
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
---
## 2. Instalar o OmniRoute
### 2.1 Criar diretório de configuração
```bash
mkdir -p /opt/omniroute
```
### 2.2 Criar arquivo de variáveis de ambiente
```bash
cat > /opt/omniroute/.env << 'EOF'
# === Segurança ===
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
INITIAL_PASSWORD=SuaSenhaSegura123!
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
STORAGE_ENCRYPTION_KEY_VERSION=v1
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
# === App ===
PORT=20128
NODE_ENV=production
HOSTNAME=0.0.0.0
DATA_DIR=/app/data
STORAGE_DRIVER=sqlite
ENABLE_REQUEST_LOGS=true
AUTH_COOKIE_SECURE=false
REQUIRE_API_KEY=false
# === Domain (altere para seu domínio) ===
BASE_URL=https://llms.seudominio.com
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
# === Cloud Sync (opcional) ===
# CLOUD_URL=https://cloud.omniroute.online
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
EOF
```
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
### 2.3 Iniciar o container
```bash
docker pull diegosouzapw/omniroute:latest
docker run -d \
--name omniroute \
--restart unless-stopped \
--env-file /opt/omniroute/.env \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
### 2.4 Verificar se está rodando
```bash
docker ps | grep omniroute
docker logs omniroute --tail 20
```
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
---
## 3. Configurar nginx (Reverse Proxy)
### 3.1 Gerar certificado SSL (Cloudflare Origin)
No painel da Cloudflare:
1. Vá em **SSL/TLS → Origin Server**
2. Clique **Create Certificate**
3. Deixe os padrões (15 anos, \*.seudominio.com)
4. Copie o **Origin Certificate** e a **Private Key**
```bash
mkdir -p /etc/nginx/ssl
# Colar o certificado
nano /etc/nginx/ssl/origin.crt
# Colar a chave privada
nano /etc/nginx/ssl/origin.key
chmod 600 /etc/nginx/ssl/origin.key
```
### 3.2 Configuração do nginx
```bash
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
# Default server — bloqueia acesso direto por IP
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /etc/nginx/ssl/origin.crt;
ssl_certificate_key /etc/nginx/ssl/origin.key;
server_name _;
return 444;
}
# OmniRoute — HTTPS
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name llms.seudominio.com; # Altere para seu domínio
ssl_certificate /etc/nginx/ssl/origin.crt;
ssl_certificate_key /etc/nginx/ssl/origin.key;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:20128;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# SSE (Server-Sent Events) — streaming AI responses
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
# HTTP → HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name llms.seudominio.com;
return 301 https://$server_name$request_uri;
}
NGINX
```
### 3.3 Ativar e testar
```bash
# Remover config padrão
rm -f /etc/nginx/sites-enabled/default
# Ativar OmniRoute
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
# Testar e recarregar
nginx -t && systemctl reload nginx
```
---
## 4. Configurar Cloudflare DNS
### 4.1 Adicionar registro DNS
No painel da Cloudflare → DNS:
| Type | Name | Content | Proxy |
| ---- | ------ | ------------------------- | ---------- |
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
### 4.2 Configurar SSL
Em **SSL/TLS → Overview**:
- Modo: **Full (Strict)**
Em **SSL/TLS → Edge Certificates**:
- Always Use HTTPS: ✅ On
- Minimum TLS Version: TLS 1.2
- Automatic HTTPS Rewrites: ✅ On
### 4.3 Testar
```bash
curl -sI https://llms.seudominio.com/health
# Deve retornar HTTP/2 200
```
---
## 5. Operações e Manutenção
### Atualizar para nova versão
```bash
docker pull diegosouzapw/omniroute:latest
docker stop omniroute && docker rm omniroute
docker run -d --name omniroute --restart unless-stopped \
--env-file /opt/omniroute/.env \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
```
### Ver logs
```bash
docker logs -f omniroute # Stream em tempo real
docker logs omniroute --tail 50 # Últimas 50 linhas
```
### Backup manual do banco
```bash
# Copiar dados do volume para o host
docker cp omniroute:/app/data ./backup-$(date +%F)
# Ou comprimir todo o volume
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
```
### Restaurar de backup
```bash
docker stop omniroute
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
docker start omniroute
```
---
## 6. Segurança Avançada
### Restringir nginx para Cloudflare IPs
```bash
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
# Cloudflare IPv4 ranges — atualizar periodicamente
# https://www.cloudflare.com/ips-v4/
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
real_ip_header CF-Connecting-IP;
CF
```
Adicionar no `nginx.conf` dentro do bloco `http {}`:
```nginx
include /etc/nginx/cloudflare-ips.conf;
```
### Install fail2ban
```bash
apt install -y fail2ban
systemctl enable fail2ban
systemctl start fail2ban
# Verificar status
fail2ban-client status sshd
```
### Bloquear acesso direto na porta do Docker
```bash
# Impedir acesso externo direto à porta 20128
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
# Persistir as regras
apt install -y iptables-persistent
netfilter-persistent save
```
---
## 7. Deploy do Cloud Worker (Opcional)
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
```bash
# No repositório local
cd omnirouteCloud
npm install
npx wrangler login
npx wrangler deploy
```
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
---
## Resumo de Portas
| Porta | Serviço | Acesso |
| ----- | ----------- | ----------------------------- |
| 22 | SSH | Público (com fail2ban) |
| 80 | nginx HTTP | Redirect → HTTPS |
| 443 | nginx HTTPS | Via Cloudflare Proxy |
| 20128 | OmniRoute | Somente localhost (via nginx) |
-37
View File
@@ -1,37 +0,0 @@
# ADR-001: Next.js as the Foundation for an AI Gateway
## Status: Accepted
## Context
OmniRoute is an AI routing gateway that translates, forwards, and manages requests across 20+ LLM providers. We needed a framework that could serve both the API proxy layer and a management dashboard from a single codebase.
**Alternatives considered:**
- **Express.js only** — Simpler proxy, but requires separate frontend tooling
- **Fastify** — Fast, but no built-in SSR/dashboard support
- **Next.js** — Unified full-stack framework with API routes, SSR, and static pages
## Decision
We chose Next.js because:
1. **Single deployment** — API routes (`/api/*`) and dashboard UI in one process
2. **Middleware layer** — Native request interception for auth guards and request tracing
3. **File-based routing** — Easy to map provider endpoints to handlers
4. **Built-in TypeScript** — Type safety across the entire codebase
## Consequences
**Positive:**
- One `npm run build` produces both API and UI
- Middleware provides centralized auth and request tracing
- Dashboard gets automatic code splitting and optimization
**Negative:**
- Next.js middleware has limitations (no heavy imports, edge runtime constraints)
- Serverless deployment model doesn't align with persistent WebSocket/SSE connections
- Build times are longer than Express-only setups
- The SSE proxy layer (`open-sse/`) operates outside Next.js conventions
-37
View File
@@ -1,37 +0,0 @@
# ADR-002: Hub-and-Spoke Translation with OpenAI as Intermediate Format
## Status: Accepted
## Context
OmniRoute routes requests across 20+ providers, each with its own API format (OpenAI, Anthropic Messages, Google Gemini, AWS Bedrock, etc.). Direct provider-to-provider translation would require O(n²) translators.
**Alternatives considered:**
- **Direct translation** — Each pair needs a dedicated translator (n² complexity)
- **Common intermediate format** — Translate to/from a canonical format (2n complexity)
- **Protocol buffers** — Strong typing but heavy overhead for a proxy
## Decision
We use the **OpenAI Chat Completions format** as the canonical intermediate representation. All incoming requests are normalized to OpenAI format, processed, then translated to the target provider's format.
```
Client → [any format] → OpenAI canonical → [target format] → Provider
Provider → [response] → OpenAI canonical → [original format] → Client
```
## Consequences
**Positive:**
- Only 2 translators per provider (inbound + outbound) instead of n² pairs
- OpenAI format is the de facto standard — most clients already use it
- Adding a new provider requires only implementing one translator pair
- Streaming (SSE) works consistently through the canonical format
**Negative:**
- Some provider-specific features may be lost in translation
- The double translation adds latency (typically < 5ms)
- OpenAI format changes require updating the canonical representation
-39
View File
@@ -1,39 +0,0 @@
# ADR-003: Dual Storage — SQLite Primary with JSON Migration Path
## Status: Accepted
## Context
OmniRoute originally used LowDB (JSON file) for all persistence. As the project grew, JSON-based storage became a bottleneck for concurrent access, querying, and data integrity.
**Alternatives considered:**
- **LowDB only** — Simple but no concurrent access, no ACID, no querying
- **SQLite only** — Fast, ACID-compliant, but breaks existing deployments
- **PostgreSQL** — Production-grade but requires external dependency
- **Dual storage with migration** — SQLite primary + automatic JSON migration
## Decision
We migrated to **SQLite as the primary store** with an automatic one-time migration from `db.json`:
1. On startup, if `db.json` exists and SQLite is empty, auto-migrate all data
2. All new reads/writes go through SQLite
3. The `db.json` file is preserved but no longer written to
Settings remain in a hybrid model where LowDB handles simple key-value configuration for backward compatibility.
## Consequences
**Positive:**
- ACID transactions for provider connections, API keys, and usage data
- Proper SQL queries for analytics and log filtering
- Concurrent read/write safety via WAL mode
- Zero-downtime migration from JSON — users upgrade transparently
**Negative:**
- Two storage engines to maintain (SQLite + LowDB for settings)
- Migration code must handle edge cases and partial data
- SQLite binary dependency needed in deployment environments
-36
View File
@@ -1,36 +0,0 @@
# Task 01 — Home Page (Dashboard)
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `home`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/page.tsx` | 17 | 0 (wrapper) |
| `src/app/(dashboard)/dashboard/HomePageClient.tsx` | 500 | ~25 |
## Strings a Traduzir
### HomePageClient.tsx
| Linha | String EN | Chave i18n | String PT-BR |
|-------|-----------|------------|--------------|
| 138 | "Quick Start" | `home.quickStart` | "Início Rápido" |
| 242 | "Providers Overview" | `home.providersOverview` | "Visão Geral dos Provedores" |
| 436 | "No models available for this provider." | `home.noModelsAvailable` | "Nenhum modelo disponível para este provedor." |
| — | "Total Requests" | `home.totalRequests` | "Total de Requisições" |
| — | "Active Providers" | `home.activeProviders` | "Provedores Ativos" |
| — | "Success Rate" | `home.successRate` | "Taxa de Sucesso" |
| — | "Avg Latency" | `home.avgLatency` | "Latência Média" |
| — | "Configure Endpoint" | `home.configureEndpoint` | "Configurar Endpoint" |
| — | "Add Provider" | `home.addProvider` | "Adicionar Provedor" |
| — | "View Docs" | `home.viewDocs` | "Ver Documentação" |
| — | "Copied!" | `common.copied` | ✅ já existe |
| — | "requests" | `home.requests` | "requisições" |
| — | "models" | `home.models` | "modelos" |
| — | "accounts" | `home.accounts` | "contas" |
## Checklist
- [ ] Adicionar chaves no `en.json` (namespace `home`)
- [ ] Adicionar traduções no `pt-BR.json`
- [ ] Substituir strings por `t()` no `HomePageClient.tsx`
- [ ] Testar em EN e PT-BR
-25
View File
@@ -1,25 +0,0 @@
# Task 02 — Analytics Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `analytics`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/analytics/page.tsx` | 46 | ~8 |
## Strings a Traduzir
| Linha | String EN | Chave i18n | String PT-BR |
|-------|-----------|------------|--------------|
| 11 | "Monitor your API usage patterns..." | `analytics.overviewDescription` | "Monitore padrões de uso da API..." |
| 13 | "Run evaluation suites to test..." | `analytics.evalsDescription` | "Execute suítes de avaliação..." |
| 24 | "Analytics" | `analytics.title` | "Análises" |
| 32 | "Overview" | `analytics.overview` | "Visão Geral" |
| 33 | "Evals" | `analytics.evals` | "Avaliações" |
## Checklist
- [ ] Adicionar chaves no `en.json`
- [ ] Adicionar traduções no `pt-BR.json`
- [ ] Substituir strings por `t()` em `page.tsx`
- [ ] Testar em EN e PT-BR
-31
View File
@@ -1,31 +0,0 @@
# Task 03 — API Manager Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `apiManager`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx` | ~400 | ~20 |
## Strings a Traduzir (levantamento parcial — abrir código para completar)
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "API Keys" | `apiManager.title` | "Chaves de API" |
| "Create API Key" | `apiManager.createKey` | "Criar Chave de API" |
| "Name" | `apiManager.name` | "Nome" |
| "Key" | `apiManager.key` | "Chave" |
| "Created" | `apiManager.created` | "Criado em" |
| "Last Used" | `apiManager.lastUsed` | "Último Uso" |
| "Actions" | `apiManager.actions` | "Ações" |
| "Delete" | `common.delete` | ✅ já existe |
| "No API keys found" | `apiManager.noKeys` | "Nenhuma chave de API encontrada" |
| ~11 strings adicionais | — | Levantar no código |
## Checklist
- [ ] Levantar todas as strings do `ApiManagerPageClient.tsx`
- [ ] Adicionar chaves no `en.json`
- [ ] Adicionar traduções no `pt-BR.json`
- [ ] Substituir strings por `t()`
- [ ] Testar em EN e PT-BR
-31
View File
@@ -1,31 +0,0 @@
# Task 04 — Audit Log Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `auditLog`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/audit-log/page.tsx` | 241 | ~12 |
| `src/app/(dashboard)/dashboard/logs/AuditLogTab.tsx` | ~100 | ~3 |
## Strings a Traduzir
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "Audit Log" | `auditLog.title` | "Log de Auditoria" |
| "Search actions..." | `auditLog.searchPlaceholder` | "Buscar ações..." |
| "Action" | `auditLog.action` | "Ação" |
| "Actor" | `auditLog.actor` | "Autor" |
| "Target" | `auditLog.target` | "Alvo" |
| "Details" | `auditLog.details` | "Detalhes" |
| "IP Address" | `auditLog.ipAddress` | "Endereço IP" |
| "Timestamp" | `auditLog.timestamp` | "Data/Hora" |
| "No audit entries found" | `auditLog.noEntries` | "Nenhum registro de auditoria" |
| "Load More" | `auditLog.loadMore` | "Carregar Mais" |
## Checklist
- [ ] Adicionar chaves no `en.json`
- [ ] Adicionar traduções no `pt-BR.json`
- [ ] Substituir strings em `audit-log/page.tsx` e `AuditLogTab.tsx`
- [ ] Testar em EN e PT-BR
-37
View File
@@ -1,37 +0,0 @@
# Task 05 — CLI Tools Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `cliTools`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `cli-tools/components/AntigravityToolCard.tsx` | ~3 |
| `cli-tools/components/ClaudeToolCard.tsx` | ~3 |
| `cli-tools/components/ClineToolCard.tsx` | ~4 |
| `cli-tools/components/CodexToolCard.tsx` | ~3 |
| `cli-tools/components/DefaultToolCard.tsx` | ~3 |
| `cli-tools/components/DroidToolCard.tsx` | ~2 |
| `cli-tools/components/KiloToolCard.tsx` | ~4 |
| `cli-tools/components/OpenClawToolCard.tsx` | ~2 |
## Strings comuns entre Tool Cards
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "Status" | `cliTools.status` | "Status" |
| "Connected" | `cliTools.connected` | "Conectado" |
| "Not Connected" | `cliTools.notConnected` | "Não Conectado" |
| "Configure" | `cliTools.configure` | "Configurar" |
| "Test Connection" | `cliTools.testConnection` | "Testar Conexão" |
| "Models" | `cliTools.models` | "Modelos" |
| "Map Models" | `cliTools.mapModels` | "Mapear Modelos" |
| "Save" | `common.save` | ✅ já existe |
| "Cancel" | `common.cancel` | ✅ já existe |
## Checklist
- [ ] Levantar strings de cada ToolCard
- [ ] Adicionar chaves no `en.json`
- [ ] Adicionar traduções no `pt-BR.json`
- [ ] Substituir por `t()` em cada componente
- [ ] Testar em EN e PT-BR
-34
View File
@@ -1,34 +0,0 @@
# Task 06 — Combos Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `combos`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/combos/page.tsx` | ~1000 | ~20 |
## Strings a Traduzir
| Linha | String EN | Chave i18n | String PT-BR |
|-------|-----------|------------|--------------|
| 207 | "Combos" | `combos.title` | "Combos" |
| 372 | "No models" | `combos.noModels` | "Sem modelos" |
| 747 | "Routing Strategy" | `combos.routingStrategy` | "Estratégia de Roteamento" |
| 791 | "Models" | `combos.models` | "Modelos" |
| 807 | "No models added yet" | `combos.noModelsYet` | "Nenhum modelo adicionado" |
| 922 | "Max Retries" | `combos.maxRetries` | "Máximo de Tentativas" |
| 959 | "Timeout (ms)" | `combos.timeout` | "Timeout (ms)" |
| 977 | "Healthcheck" | `combos.healthcheck` | "Verificação de Saúde" |
| — | "Create Combo" | `combos.create` | "Criar Combo" |
| — | "Edit Combo" | `combos.edit` | "Editar Combo" |
| — | "Delete Combo" | `combos.deleteCombo` | "Excluir Combo" |
| — | "Add Model" | `combos.addModel` | "Adicionar Modelo" |
| — | "Priority" | `combos.priority` | "Prioridade" |
| — | "Fallback" | `combos.fallback` | "Fallback" |
## Checklist
- [ ] Adicionar chaves no `en.json`
- [ ] Adicionar traduções no `pt-BR.json`
- [ ] Substituir strings por `t()` em `combos/page.tsx`
- [ ] Testar em EN e PT-BR
-23
View File
@@ -1,23 +0,0 @@
# Task 07 — Costs Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `costs`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/costs/page.tsx` | ~200 | ~5 |
## Strings a Traduzir
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "Costs" | `costs.title` | "Custos" |
| "Total Cost" | `costs.totalCost` | "Custo Total" |
| "Cost Breakdown" | `costs.breakdown` | "Detalhamento de Custos" |
| "No cost data" | `costs.noData` | "Sem dados de custo" |
## Checklist
- [ ] Levantar strings completas do código
- [ ] Adicionar chaves no `en.json` / `pt-BR.json`
- [ ] Substituir por `t()` e testar
-30
View File
@@ -1,30 +0,0 @@
# Task 08 — Endpoint Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `endpoint`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx` | ~750 | ~20 |
## Strings a Traduzir
| Linha | String EN | Chave i18n | String PT-BR |
|-------|-----------|------------|--------------|
| 320 | "API Endpoint" | `endpoint.title` | "Endpoint da API" |
| 403 | "Available Endpoints" | `endpoint.available` | "Endpoints Disponíveis" |
| 561 | "Cloud Proxy" | `endpoint.cloudProxy` | "Proxy na Nuvem" |
| 632 | "Note" | `endpoint.note` | "Nota" |
| 717 | "Warning" | `endpoint.warning` | "Aviso" |
| 740 | "Are you sure you want to disable cloud proxy?" | `endpoint.disableConfirm` | "Tem certeza que deseja desativar o proxy na nuvem?" |
| — | "Copy" | `common.copy` | ✅ já existe |
| — | "Base URL" | `endpoint.baseUrl` | "URL Base" |
| — | "Connected" | `endpoint.connected` | "Conectado" |
| — | "Enable" | `endpoint.enable` | "Ativar" |
| — | "Disable" | `endpoint.disable` | "Desativar" |
## Checklist
- [ ] Levantar strings restantes
- [ ] Adicionar chaves no `en.json` / `pt-BR.json`
- [ ] Substituir por `t()` e testar
-30
View File
@@ -1,30 +0,0 @@
# Task 09 — Health Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `health`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/health/page.tsx` | ~350 | ~15 |
## Strings a Traduzir
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "System Health" | `health.title` | "Saúde do Sistema" |
| "Healthy" | `health.healthy` | "Saudável" |
| "Degraded" | `health.degraded` | "Degradado" |
| "Down" | `health.down` | "Offline" |
| "Uptime" | `health.uptime` | "Tempo Ativo" |
| "Memory" | `health.memory` | "Memória" |
| "CPU" | `health.cpu` | "CPU" |
| "Database" | `health.database` | "Banco de Dados" |
| "Last Check" | `health.lastCheck` | "Última Verificação" |
| "Refresh" | `common.refresh` | ✅ já existe |
| ~5 strings adicionais | — | Levantar |
## Checklist
- [ ] Levantar strings restantes
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` e testar
-24
View File
@@ -1,24 +0,0 @@
# Task 10 — Limits Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `limits`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/limits/page.tsx` | ~150 | ~5 |
## Strings a Traduzir
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "Limits & Quotas" | `limits.title` | "Limites e Cotas" |
| "Rate Limit" | `limits.rateLimit` | "Limite de Taxa" |
| "Provider" | `limits.provider` | "Provedor" |
| "Remaining" | `limits.remaining` | "Restante" |
| "Reset" | `limits.reset` | "Reiniciar" |
## Checklist
- [ ] Levantar strings completas
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` e testar
-24
View File
@@ -1,24 +0,0 @@
# Task 11 — Logs Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `logs`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/logs/` | ~200 | ~5 |
## Strings a Traduzir
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "Logs" | `logs.title` | "Logs" |
| "Request Logs" | `logs.requestLogs` | "Logs de Requisições" |
| "Proxy Logs" | `logs.proxyLogs` | "Logs do Proxy" |
| "Audit Log" | `logs.auditLog` | "Log de Auditoria" |
| "Console" | `logs.console` | "Console" |
## Checklist
- [ ] Levantar strings completas
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` e testar
-26
View File
@@ -1,26 +0,0 @@
# Task 12 — Onboarding Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `onboarding`
## Arquivos
| Arquivo | Linhas | Strings |
|---------|--------|---------|
| `src/app/(dashboard)/dashboard/onboarding/page.tsx` | ~300 | ~10 |
## Strings a Traduzir
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "Welcome to OmniRoute" | `onboarding.welcome` | "Bem-vindo ao OmniRoute" |
| "Set Password" | `onboarding.setPassword` | "Definir Senha" |
| "Add Provider" | `onboarding.addProvider` | "Adicionar Provedor" |
| "Get Started" | `onboarding.getStarted` | "Começar" |
| "Skip" | `onboarding.skip` | "Pular" |
| "Next" | `common.next` | ✅ já existe |
| ~4 strings adicionais | — | Levantar |
## Checklist
- [ ] Levantar strings completas
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` e testar
-35
View File
@@ -1,35 +0,0 @@
# Task 13 — Providers Pages
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `providers`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `providers/page.tsx` | ~5 |
| `providers/[id]/page.tsx` | ~12 |
| `providers/new/page.tsx` | ~3 |
| `providers/components/ModelAvailabilityPanel.tsx` | ~3 |
| `providers/components/ModelAvailabilityBadge.tsx` | ~1 |
## Strings a Traduzir
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "Providers" | `providers.title` | "Provedores" |
| "Add Provider" | `providers.add` | "Adicionar Provedor" |
| "Edit Provider" | `providers.edit` | "Editar Provedor" |
| "Test Connection" | `providers.testConnection` | "Testar Conexão" |
| "Connected" | `providers.connected` | "Conectado" |
| "Disconnected" | `providers.disconnected` | "Desconectado" |
| "Models" | `providers.models` | "Modelos" |
| "Accounts" | `providers.accounts` | "Contas" |
| "Delete Provider" | `providers.deleteProvider` | "Excluir Provedor" |
| "No providers configured" | `providers.noProviders` | "Nenhum provedor configurado" |
| "Model Availability" | `providers.modelAvailability` | "Disponibilidade de Modelos" |
| ~9 strings adicionais | — | Levantar |
## Checklist
- [ ] Levantar strings completas de todos os arquivos
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` e testar
-51
View File
@@ -1,51 +0,0 @@
# Task 14 — Settings Page (MAIOR TAREFA)
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `settings`
## Arquivos (20 componentes!)
| Arquivo | Strings |
|---------|---------|
| `settings/components/AppearanceTab.tsx` | ~4 |
| `settings/components/CacheStatsCard.tsx` | ~4 |
| `settings/components/ComboDefaultsTab.tsx` | ~8 |
| `settings/components/FallbackChainsEditor.tsx` | ~2 |
| `settings/components/IPFilterSection.tsx` | ~2 |
| `settings/components/PoliciesPanel.tsx` | ~5 |
| `settings/components/PricingTab.tsx` | ~8 |
| `settings/components/ProxyTab.tsx` | ~2 |
| `settings/components/ResilienceTab.tsx` | ~7 |
| `settings/components/RoutingTab.tsx` | ~4 |
| `settings/components/SecurityTab.tsx` | ~5 |
| `settings/components/SessionInfoCard.tsx` | ~5 |
| `settings/components/SystemPromptTab.tsx` | ~2 |
| `settings/components/SystemStorageTab.tsx` | ~8 |
| `settings/components/ThinkingBudgetTab.tsx` | ~5 |
| `settings/pricing/page.tsx` | ~17 |
## Strings a Traduzir (amostra)
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "General" | `settings.general` | "Geral" |
| "Security" | `settings.security` | "Segurança" |
| "Appearance" | `settings.appearance` | "Aparência" |
| "Routing" | `settings.routing` | "Roteamento" |
| "Cache" | `settings.cache` | "Cache" |
| "Resilience" | `settings.resilience` | "Resiliência" |
| "System Prompt" | `settings.systemPrompt` | "Prompt do Sistema" |
| "Thinking Budget" | `settings.thinkingBudget` | "Orçamento de Raciocínio" |
| "Proxy" | `settings.proxy` | "Proxy" |
| "Pricing" | `settings.pricing` | "Preços" |
| "Storage" | `settings.storage` | "Armazenamento" |
| "Policies" | `settings.policies` | "Políticas" |
| "IP Filter" | `settings.ipFilter` | "Filtro de IP" |
| "Combo Defaults" | `settings.comboDefaults` | "Padrões de Combo" |
| "Fallback Chains" | `settings.fallbackChains` | "Cadeias de Fallback" |
| ~40 strings adicionais | — | Levantar em cada tab |
## Checklist
- [ ] Levantar strings de CADA componente (16 arquivos)
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` em todos os 16 arquivos
- [ ] Testar cada aba em EN e PT-BR
-41
View File
@@ -1,41 +0,0 @@
# Task 15 — Translator Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `translator`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `translator/components/LiveMonitorMode.tsx` | ~11 |
| `translator/components/PlaygroundMode.tsx` | ~5 |
| `translator/components/TestBenchMode.tsx` | ~3 |
| `translator/components/ChatTesterMode.tsx` | ~4 |
## Strings a Traduzir
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "Real-Time Translation Activity" | `translator.realtime` | "Atividade de Tradução em Tempo Real" |
| "Chat Tester" | `translator.chatTester` | "Testador de Chat" |
| "Test Bench" | `translator.testBench` | "Bancada de Testes" |
| "Recent Translations" | `translator.recentTranslations` | "Traduções Recentes" |
| "No translations yet" | `translator.noTranslations` | "Nenhuma tradução ainda" |
| "Time" | `translator.time` | "Tempo" |
| "Source" | `translator.source` | "Origem" |
| "Target" | `translator.target` | "Destino" |
| "Model" | `translator.model` | "Modelo" |
| "Status" | `translator.status` | "Status" |
| "Latency" | `translator.latency` | "Latência" |
| "Format Converter" | `translator.formatConverter` | "Conversor de Formato" |
| "Input" | `translator.input` | "Entrada" |
| "Output" | `translator.output` | "Saída" |
| "Example Templates" | `translator.exampleTemplates` | "Modelos de Exemplo" |
| "Compatibility Tester" | `translator.compatibilityTester` | "Testador de Compatibilidade" |
| "Compatibility Report" | `translator.compatibilityReport` | "Relatório de Compatibilidade" |
| "Pipeline Debugger" | `translator.pipelineDebugger` | "Depurador de Pipeline" |
| "Translation Pipeline" | `translator.translationPipeline` | "Pipeline de Tradução" |
## Checklist
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` em cada componente
- [ ] Testar em EN e PT-BR
-57
View File
@@ -1,57 +0,0 @@
# Task 16 — Usage Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `usage`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `usage/components/BudgetTab.tsx` | ~4 |
| `usage/components/BudgetTelemetryCards.tsx` | ~9 |
| `usage/components/EvalsTab.tsx` | ~6 |
| `usage/components/RateLimitStatus.tsx` | ~2 |
| `usage/components/SessionsTab.tsx` | ~7 |
| `usage/components/ProviderLimits/index.tsx` | ~7 |
| `usage/components/ProviderLimits/ProviderLimitCard.tsx` | ~1 |
| `usage/components/ProviderLimits/QuotaTable.tsx` | ~1 |
## Strings a Traduzir
| String EN | Chave i18n | String PT-BR |
|-----------|------------|--------------|
| "Budget Management" | `usage.budgetManagement` | "Gerenciamento de Orçamento" |
| "API Key" | `usage.apiKey` | "Chave de API" |
| "This Month" | `usage.thisMonth` | "Este Mês" |
| "Set Limits" | `usage.setLimits` | "Definir Limites" |
| "Total requests" | `usage.totalRequests` | "Total de requisições" |
| "No data yet" | `usage.noData` | "Sem dados ainda" |
| "Entries" | `usage.entries` | "Entradas" |
| "Hit Rate" | `usage.hitRate` | "Taxa de Acerto" |
| "Circuit Breakers" | `usage.circuitBreakers` | "Disjuntores" |
| "Locked IPs" | `usage.lockedIPs` | "IPs Bloqueados" |
| "How It Works" | `usage.howItWorks` | "Como Funciona" |
| "Define" | `usage.define` | "Definir" |
| "Run" | `usage.run` | "Executar" |
| "Evaluate" | `usage.evaluate` | "Avaliar" |
| "Evaluation Suites" | `usage.evalSuites` | "Suítes de Avaliação" |
| "Model Evaluations" | `usage.modelEvals` | "Avaliações de Modelos" |
| "Model Lockouts" | `usage.modelLockouts` | "Bloqueios de Modelo" |
| "No models currently locked" | `usage.noLockouts` | "Nenhum modelo bloqueado" |
| "Active Sessions" | `usage.activeSessions` | "Sessões Ativas" |
| "No active sessions" | `usage.noSessions` | "Sem sessões ativas" |
| "Session" | `usage.session` | "Sessão" |
| "Age" | `usage.age` | "Idade" |
| "Requests" | `usage.requests` | "Requisições" |
| "Connection" | `usage.connection` | "Conexão" |
| "Provider Limits" | `usage.providerLimits` | "Limites do Provedor" |
| "No Providers Connected" | `usage.noProviders` | "Nenhum Provedor Conectado" |
| "Account" | `usage.account` | "Conta" |
| "Model Quotas" | `usage.modelQuotas` | "Cotas de Modelo" |
| "Last Used" | `usage.lastUsed` | "Último Uso" |
| "Actions" | `usage.actions` | "Ações" |
| "No quota data" | `usage.noQuota` | "Sem dados de cota" |
## Checklist
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` em cada componente
- [ ] Testar em EN e PT-BR
-44
View File
@@ -1,44 +0,0 @@
# Task 17 — Shared Modals
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `modals`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `shared/components/OAuthModal.tsx` | ~6 |
| `shared/components/KiroAuthModal.tsx` | ~10 |
| `shared/components/KiroSocialOAuthModal.tsx` | ~3 |
| `shared/components/CursorAuthModal.tsx` | ~2 |
| `shared/components/PricingModal.tsx` | ~10 |
| `shared/components/ModelSelectModal.tsx` | ~2 |
| `shared/components/ProxyConfigModal.tsx` | ~1 |
## Strings a Traduzir
| String EN | String PT-BR |
|-----------|--------------|
| "Waiting for Authorization" | "Aguardando Autorização" |
| "Verification URL" | "URL de Verificação" |
| "Your Code" | "Seu Código" |
| "Remote access:" | "Acesso remoto:" |
| "Connected Successfully!" | "Conectado com Sucesso!" |
| "Connection Failed" | "Falha na Conexão" |
| "Choose your authentication method:" | "Escolha seu método de autenticação:" |
| "AWS Builder ID" | "AWS Builder ID" |
| "AWS IAM Identity Center" | "AWS IAM Identity Center" |
| "Google Account" | "Conta Google" |
| "GitHub Account" | "Conta GitHub" |
| "Import Token" | "Importar Token" |
| "Auto-detecting tokens..." | "Detectando tokens automaticamente..." |
| "Pricing Configuration" | "Configuração de Preços" |
| "Loading pricing data..." | "Carregando dados de preços..." |
| "Model" / "Input" / "Output" / "Cached" | "Modelo" / "Entrada" / "Saída" / "Em Cache" |
| "Combos" | "Combos" |
| "No models found" | "Nenhum modelo encontrado" |
| "Connected" | "Conectado" |
## Checklist
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` em cada modal
- [ ] Testar cada modal em EN e PT-BR
-37
View File
@@ -1,37 +0,0 @@
# Task 18 — Shared Loggers
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `loggers`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `shared/components/RequestLoggerV2.tsx` | ~12 |
| `shared/components/RequestLoggerDetail.tsx` | ~4 |
| `shared/components/ProxyLogger.tsx` | ~7 |
| `shared/components/ProxyLogDetail.tsx` | ~6 |
| `shared/components/ConsoleLogViewer.tsx` | ~2 |
## Strings a Traduzir
| String EN | String PT-BR |
|-----------|--------------|
| "All Providers" | "Todos os Provedores" |
| "All Models" | "Todos os Modelos" |
| "All Accounts" | "Todas as Contas" |
| "All API Keys" | "Todas as Chaves de API" |
| "Newest" / "Oldest" | "Mais Recente" / "Mais Antigo" |
| "Model A-Z" / "Model Z-A" | "Modelo A-Z" / "Modelo Z-A" |
| "Columns" | "Colunas" |
| "Loading logs..." | "Carregando logs..." |
| "All Types" | "Todos os Tipos" |
| "All Levels" | "Todos os Níveis" |
| "Proxy Event" | "Evento do Proxy" |
| "Time" / "Model" / "Combo" | "Tempo" / "Modelo" / "Combo" |
| "No log entries found" | "Nenhuma entrada de log encontrada" |
| "No payload data available" | "Nenhum dado de payload disponível" |
## Checklist
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` em cada logger
- [ ] Testar em EN e PT-BR
-36
View File
@@ -1,36 +0,0 @@
# Task 19 — Shared Charts & Stats
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `stats`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `shared/components/UsageStats.tsx` | ~6 |
| `shared/components/analytics/charts.tsx` | ~15 |
| `shared/components/TokenHealthBadge.tsx` | ~6 |
| `shared/components/SystemMonitor.tsx` | ~1 |
| `shared/components/Footer.tsx` | ~3 |
## Strings a Traduzir
| String EN | String PT-BR |
|-----------|--------------|
| "Usage Overview" | "Visão Geral de Uso" |
| "Output Tokens" | "Tokens de Saída" |
| "Total Cost" | "Custo Total" |
| "Usage by Model" | "Uso por Modelo" |
| "Usage by Account" | "Uso por Conta" |
| "Failed to load usage statistics." | "Falha ao carregar estatísticas." |
| "Token Health" | "Saúde dos Tokens" |
| "Total OAuth" | "Total OAuth" |
| "Healthy" / "Errored" / "Warning" | "Saudável" / "Com Erro" / "Aviso" |
| "Last check" | "Última verificação" |
| "No data" / "Share" | "Sem dados" / "Compartilhar" |
| "Unable to load system metrics" | "Não foi possível carregar métricas" |
| "Product" / "Resources" / "Company" (Footer) | "Produto" / "Recursos" / "Empresa" |
## Checklist
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` em cada componente
- [ ] Testar em EN e PT-BR
-36
View File
@@ -1,36 +0,0 @@
# Task 20 — Login & Auth Pages
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `auth`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `src/app/login/page.tsx` | ~8 |
| `src/app/forgot-password/page.tsx` | ~3 |
| `src/app/callback/page.tsx` | ~5 |
| `src/app/forbidden/page.tsx` | ~1 |
## Strings a Traduzir
| String EN | String PT-BR |
|-----------|--------------|
| "Welcome" | "Bem-vindo" |
| "OmniRoute" | "OmniRoute" (não traduzir) |
| "Sign in" | "Entrar" |
| "Enter your password to continue" | "Digite sua senha para continuar" |
| "Password" | "Senha" |
| "Unified AI API Proxy" | "Proxy Unificado de API de IA" |
| "Loading..." | "Carregando..." |
| "Password protection is not enabled" | "Proteção por senha não está ativada" |
| "Reset Password" | "Redefinir Senha" |
| "Choose a method to recover access" | "Escolha um método para recuperar acesso" |
| "Processing..." | "Processando..." |
| "Authorization Successful!" | "Autorização bem-sucedida!" |
| "Copy This URL" | "Copiar esta URL" |
| "Access Denied" | "Acesso Negado" |
## Checklist
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` em cada página
- [ ] Testar em EN e PT-BR
-34
View File
@@ -1,34 +0,0 @@
# Task 21 — Landing Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `landing`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `landing/components/HeroSection.tsx` | ~3 |
| `landing/components/Features.tsx` | ~10 |
| `landing/components/HowItWorks.tsx` | ~5 |
| `landing/components/GetStarted.tsx` | ~5 |
| `landing/components/Navigation.tsx` | ~2 |
| `landing/components/FlowAnimation.tsx` | ~2 |
| `landing/components/Footer.tsx` | ~5 |
## Strings a Traduzir (amostra)
| String EN | String PT-BR |
|-----------|--------------|
| "All AI Providers" | "Todos os Provedores de IA" |
| "One Endpoint" | "Um Endpoint" |
| "Powerful Features" | "Recursos Poderosos" |
| "How OmniRoute Works" | "Como o OmniRoute Funciona" |
| "Install OmniRoute" | "Instalar o OmniRoute" |
| "Open Dashboard" | "Abrir Painel" |
| "Route Requests" | "Rotear Requisições" |
| "Data Location:" | "Local dos Dados:" |
| "Product" / "Resources" / "Legal" | "Produto" / "Recursos" / "Legal" |
## Checklist
- [ ] Levantar strings completas de cada componente
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` e testar
-32
View File
@@ -1,32 +0,0 @@
# Task 22 — Docs Page
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `docs`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `src/app/docs/page.tsx` | ~25 |
## Strings a Traduzir
| String EN | String PT-BR |
|-----------|--------------|
| "Quick Start" | "Início Rápido" |
| "Features" | "Recursos" |
| "Supported Providers" | "Provedores Suportados" |
| "Common Use Cases" | "Casos de Uso Comuns" |
| "Client Compatibility" | "Compatibilidade de Clientes" |
| "Cherry Studio" | "Cherry Studio" (não traduzir) |
| "Codex / GitHub Copilot Models" | "Modelos Codex / GitHub Copilot" |
| "Cursor IDE" | "Cursor IDE" (não traduzir) |
| "Claude Code / Antigravity" | "Claude Code / Antigravity" |
| "API Reference" | "Referência da API" |
| "Method" / "Path" / "Notes" | "Método" / "Caminho" / "Notas" |
| "Model Prefixes" | "Prefixos de Modelo" |
| "Prefix" / "Provider" / "Type" | "Prefixo" / "Provedor" / "Tipo" |
| "Troubleshooting" | "Solução de Problemas" |
## Checklist
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` e testar
-31
View File
@@ -1,31 +0,0 @@
# Task 23 — Legal Pages (Privacy & Terms)
**Status:** `[ ]` Não iniciado
**Namespace JSON:** `legal`
## Arquivos
| Arquivo | Strings |
|---------|---------|
| `src/app/privacy/page.tsx` | ~10 |
| `src/app/terms/page.tsx` | ~5 |
## Strings a Traduzir
| String EN | String PT-BR |
|-----------|--------------|
| "Privacy Policy" | "Política de Privacidade" |
| "Terms of Service" | "Termos de Serviço" |
| "Provider configurations" | "Configurações de provedores" |
| "API keys" | "Chaves de API" |
| "Usage logs" | "Logs de uso" |
| "Application settings" | "Configurações do aplicativo" |
| "View and export usage analytics" | "Visualizar e exportar análises de uso" |
| "Clear usage history at any time" | "Limpar histórico de uso a qualquer momento" |
| "Configure log retention policies" | "Configurar políticas de retenção de logs" |
| "Back up and restore your database" | "Fazer backup e restaurar seu banco de dados" |
## Checklist
- [ ] Adicionar chaves / traduções
- [ ] Substituir por `t()` e testar
> **Nota:** Textos legais podem requerer revisão jurídica para tradução formal.
-51
View File
@@ -1,51 +0,0 @@
# i18n Translation Tasks
Cada arquivo `.md` nesta pasta representa **uma tarefa de tradução** para uma página ou componente do OmniRoute.
## Status Legend
- `[ ]` — Não iniciado
- `[/]` — Em progresso
- `[x]` — Concluído
## Dashboard Pages (~260 strings)
| # | Tarefa | Arquivo | Strings | Status |
| --- | ---------------------------------- | ----------------------------------------------------- | ------- | ------ |
| 01 | [Home](./01-home.md) | `HomePageClient.tsx` | ~25 | `[ ]` |
| 02 | [Analytics](./02-analytics.md) | `analytics/page.tsx` | ~8 | `[ ]` |
| 03 | [API Manager](./03-api-manager.md) | `api-manager/` | ~20 | `[ ]` |
| 04 | [Audit Log](./04-audit-log.md) | `audit-log/page.tsx`, `logs/AuditLogTab.tsx` | ~15 | `[ ]` |
| 05 | [CLI Tools](./05-cli-tools.md) | `cli-tools/components/*.tsx` | ~20 | `[ ]` |
| 06 | [Combos](./06-combos.md) | `combos/page.tsx` | ~20 | `[ ]` |
| 07 | [Costs](./07-costs.md) | `costs/page.tsx` | ~5 | `[ ]` |
| 08 | [Endpoint](./08-endpoint.md) | `endpoint/EndpointPageClient.tsx` | ~20 | `[ ]` |
| 09 | [Health](./09-health.md) | `health/page.tsx` | ~15 | `[ ]` |
| 10 | [Limits](./10-limits.md) | `limits/page.tsx` | ~5 | `[ ]` |
| 11 | [Logs](./11-logs.md) | `logs/` | ~5 | `[ ]` |
| 12 | [Onboarding](./12-onboarding.md) | `onboarding/page.tsx` | ~10 | `[ ]` |
| 13 | [Providers](./13-providers.md) | `providers/page.tsx`, `[id]/page.tsx`, `new/page.tsx` | ~20 | `[ ]` |
| 14 | [Settings](./14-settings.md) | `settings/components/*.tsx` | ~55 | `[ ]` |
| 15 | [Translator](./15-translator.md) | `translator/components/*.tsx` | ~25 | `[ ]` |
| 16 | [Usage](./16-usage.md) | `usage/components/*.tsx` | ~35 | `[ ]` |
## Shared Components (~95 strings)
| # | Tarefa | Arquivo(s) | Strings | Status |
| --- | ---------------------------------------------- | ---------------------------------------------------- | ------- | ------ |
| 17 | [Shared Modals](./17-shared-modals.md) | `OAuthModal`, `KiroAuthModal`, `PricingModal`, etc. | ~40 | `[ ]` |
| 18 | [Shared Loggers](./18-shared-loggers.md) | `RequestLoggerV2`, `ProxyLogger`, `ProxyLogDetail` | ~30 | `[ ]` |
| 19 | [Shared Charts & Stats](./19-shared-charts.md) | `UsageStats`, `analytics/charts`, `TokenHealthBadge` | ~25 | `[ ]` |
## Non-Dashboard Pages (~75 strings)
| # | Tarefa | Arquivo(s) | Strings | Status |
| --- | ---------------------------------- | ------------------------------------------------------- | ------- | ------ |
| 20 | [Login & Auth](./20-login-auth.md) | `login/`, `forgot-password/`, `callback/`, `forbidden/` | ~20 | `[ ]` |
| 21 | [Landing Page](./21-landing.md) | `landing/components/*.tsx` | ~25 | `[ ]` |
| 22 | [Docs Page](./22-docs.md) | `docs/page.tsx` | ~25 | `[ ]` |
| 23 | [Legal Pages](./23-legal.md) | `privacy/`, `terms/` | ~15 | `[ ]` |
---
**Total estimado: ~460 strings em 23 tarefas**
Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

+3 -15
View File
@@ -1,10 +1,9 @@
import nextVitals from "eslint-config-next/core-web-vitals";
import tseslint from "typescript-eslint";
/** @type {import("eslint").Linter.Config[]} */
const eslintConfig = [
...nextVitals,
// FASE-02: Security rules (strict everywhere)
// FASE-02: Security rules
{
rules: {
"no-eval": "error",
@@ -12,25 +11,14 @@ const eslintConfig = [
"no-new-func": "error",
},
},
// Relaxed rules for open-sse and tests (incremental adoption)
{
files: ["open-sse/**/*.ts", "tests/**/*.mjs", "tests/**/*.ts"],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
rules: {
"@typescript-eslint/no-explicit-any": "warn",
"@next/next/no-assign-module-variable": "off",
"react-hooks/rules-of-hooks": "off",
},
},
// Global ignores (open-sse and tests REMOVED — now linted)
// Global ignores
{
ignores: [
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
"tests/**",
"scripts/**",
"bin/**",
"node_modules/**",
-134
View File
@@ -1,134 +0,0 @@
# 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 36+ AI providers — all through a single OpenAI-compatible endpoint.
## Overview
OmniRoute solves the problem of managing multiple AI provider subscriptions, quotas, and rate limits. It sits between your AI-powered tools (IDE agents, CLI tools) and AI providers, routing requests intelligently through a 4-tier fallback system: Subscription → API Key → Cheap → Free.
**Key value:** One endpoint (`http://localhost:20128/v1`), unlimited models, zero downtime, minimal cost.
## Tech Stack
- **Runtime:** Node.js >= 18
- **Framework:** Next.js 16 (App Router) with TypeScript
- **Database:** SQLite via better-sqlite3 (local, zero-config)
- **State management:** Zustand (client), lowdb (server JSON persistence)
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
- **Background jobs:** Custom token health check scheduler
- **Streaming:** Server-Sent Events (SSE) for real-time proxy responses
- **Proxy engine:** Custom pipeline with format translation, circuit breaker, rate limiting
- **Package:** Published on npm (`omniroute`) and Docker Hub (`diegosouzapw/omniroute`)
## Project Structure
```
/
├── src/ # Main application source
│ ├── app/ # Next.js App Router pages and API routes
│ │ ├── (dashboard)/ # Dashboard UI pages (providers, combos, analytics, logs, etc.)
│ │ ├── api/ # REST API endpoints
│ │ │ ├── v1/ # OpenAI-compatible API (chat, models, embeddings, images, audio)
│ │ │ ├── oauth/ # OAuth flows per provider (authorize, exchange, callback)
│ │ │ ├── providers/ # Provider CRUD and batch testing
│ │ │ ├── models/ # Dashboard model listing and aliases
│ │ │ ├── combos/ # Combo CRUD (multi-model fallback chains)
│ │ │ └── ... # Other endpoints (usage, logs, health, settings, etc.)
│ │ └── login/ # Login page
│ ├── domain/ # Domain types and business logic interfaces
│ ├── lib/ # Core libraries
│ │ ├── db/ # SQLite database layer (providers, combos, prompts, logs)
│ │ ├── oauth/ # OAuth providers, services, and utilities
│ │ │ ├── providers/ # Provider-specific OAuth configs (GitHub, Google, Claude, etc.)
│ │ │ ├── services/ # Provider-specific token exchange logic
│ │ │ └── utils/ # PKCE, callback server, token helpers
│ │ ├── cloudSync.ts # Cloud sync via Cloudflare Workers
│ │ ├── tokenHealthCheck.ts # Background OAuth token refresh scheduler
│ │ └── localDb.ts # Unified database access layer
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, etc.)
│ │ ├── constants/ # Provider definitions, model lists, pricing
│ │ └── utils/ # Helpers (auth, CORS, error codes, machine ID)
│ ├── sse/ # SSE proxy pipeline
│ │ ├── services/ # Auth resolution, format translation, response handling
│ │ └── middleware/ # Rate limiting, circuit breaker, caching, idempotency
│ ├── store/ # Zustand client-side stores
│ ├── types/ # TypeScript type definitions
│ ├── proxy.ts # Main proxy request handler
│ └── server-init.ts # Server initialization (DB, health checks)
├── open-sse/ # Standalone SSE server (npm workspace)
│ ├── config/ # Model registries (embedding, image, audio, rerank, moderation)
│ ├── handlers/ # Request handlers per API type
│ └── translators/ # Format translators (OpenAI ↔ Claude ↔ Gemini ↔ Responses)
├── tests/ # Test suites
│ ├── unit/ # Unit tests (32+ test files)
│ └── integration/ # Integration tests
├── docs/ # Documentation and screenshots
├── bin/ # CLI entry points (omniroute, reset-password)
└── .env.example # Environment variable template
```
## Key Architectural Decisions
1. **OpenAI-compatible API surface:** All incoming requests follow the OpenAI API format (`/v1/chat/completions`, `/v1/models`, etc.). This makes OmniRoute a drop-in replacement for any tool that supports custom OpenAI endpoints.
2. **Provider abstraction via format translators:** Each AI provider (Claude, Gemini, etc.) has a translator in `open-sse/translators/` that converts between the OpenAI format and the provider's native format. This happens transparently.
3. **Connection-based provider model:** Providers are stored as "connections" in SQLite. Each connection has an `id`, `provider`, `authType` (oauth/apikey/free), `isActive` flag, and credentials. Multiple connections per provider are supported for multi-account rotation.
4. **Combo system for fallback:** Users create "combos" — ordered lists of `provider/model` pairs. The proxy tries each in order until one succeeds. Supports 6 strategies: fill-first, round-robin, power-of-two-choices, random, least-used, cost-optimized.
5. **SSE proxy pipeline (`src/sse/`):** The proxy pipeline is middleware-based: request → auth resolution → rate limiting → circuit breaker → format translation → upstream call → response translation → SSE streaming back to client.
6. **SQLite for persistence:** All state (providers, combos, logs, settings) is stored in a single SQLite database file at `data/omniroute.db`. This keeps the app self-contained and zero-config.
7. **OAuth with PKCE:** OAuth flows use PKCE for security. A local callback server (`src/lib/oauth/utils/server.ts`) handles the redirect. Token refresh is handled by a background job (`tokenHealthCheck.ts`).
## Main Flows
### Proxy Request Flow
1. Client sends OpenAI-format request to `/v1/chat/completions`
2. API key validation (`src/shared/utils/apiAuth.ts`)
3. Model resolution: direct model or combo lookup
4. For combos: iterate through models in fallback order
5. Auth resolution: get credentials for the target provider
6. Format translation: OpenAI → provider native format
7. Upstream request with circuit breaker and rate limiting
8. Response translation: provider → OpenAI format
9. SSE streaming back to client
### OAuth Flow
1. Dashboard initiates `/api/oauth/[provider]/authorize`
2. User completes OAuth login in browser
3. Callback hits `/api/oauth/[provider]/exchange`
4. Tokens stored as a provider connection in SQLite
5. Background job refreshes tokens before expiry
### Model Listing
- `/api/models` — Dashboard endpoint, lists all defined models with aliases
- `/v1/models` — OpenAI-compatible endpoint, lists only models from active providers
## Important Notes for LLMs
1. **Two model endpoints exist:** `/api/models` (dashboard, all models) and `/v1/models` (OpenAI-compatible, active only). Don't confuse them.
2. **Provider IDs vs aliases:** Providers have both an ID (`claude`, `github`) and a short alias (`cc`, `gh`). Models are referenced as `alias/model-name` (e.g., `cc/claude-opus-4-6`).
3. **The `open-sse/` directory is a separate npm workspace** with its own config, handlers, and translators. It handles the actual SSE streaming and format translation.
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
5. **Database migrations:** SQLite schema is managed inline in `src/lib/db/core.ts` and `src/lib/db/providers.ts`. No migration framework — schema changes are applied on startup.
6. **Tests use Node.js built-in test runner:** Run `npm test` or `node --test tests/unit/*.test.mjs`. Playwright is used for E2E tests.
7. **The proxy pipeline is in `src/sse/`**, not in `src/app/api/v1/`. The API routes in `src/app/api/v1/` delegate to the SSE server running on a separate Express instance.
## Links
- Repository: https://github.com/diegosouzapw/OmniRoute
- Website: https://omniroute.online
- npm: https://www.npmjs.com/package/omniroute
- Docker Hub: https://hub.docker.com/r/diegosouzapw/omniroute
- Documentation: See `/docs/` directory
+1 -11
View File
@@ -1,22 +1,12 @@
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
/** @type {import('next').NextConfig} */
const nextConfig = {
turbopack: {},
output: "standalone",
serverExternalPackages: ["better-sqlite3"],
transpilePackages: ["@omniroute/open-sse"],
allowedDevOrigins: ["192.168.*"],
typescript: {
// TODO: Re-enable after fixing all sub-component useTranslations scope issues
ignoreBuildErrors: true,
},
images: {
unoptimized: true,
},
// NEXT_PUBLIC_CLOUD_URL is set in .env — do NOT hardcode here (it overrides .env)
webpack: (config, { isServer }) => {
// Ignore fs/path modules in browser bundle
@@ -67,4 +57,4 @@ const nextConfig = {
},
};
export default withNextIntl(nextConfig);
export default nextConfig;
@@ -6,22 +6,7 @@
* - /v1/audio/speech (TTS API)
*/
interface AudioModel {
id: string;
name: string;
}
interface AudioProvider {
id: string;
baseUrl: string;
authType: string;
authHeader: string;
format?: string;
async?: boolean;
models: AudioModel[];
}
export const AUDIO_TRANSCRIPTION_PROVIDERS: Record<string, AudioProvider> = {
export const AUDIO_TRANSCRIPTION_PROVIDERS = {
openai: {
id: "openai",
baseUrl: "https://api.openai.com/v1/audio/transcriptions",
@@ -72,7 +57,7 @@ export const AUDIO_TRANSCRIPTION_PROVIDERS: Record<string, AudioProvider> = {
},
};
export const AUDIO_SPEECH_PROVIDERS: Record<string, AudioProvider> = {
export const AUDIO_SPEECH_PROVIDERS = {
openai: {
id: "openai",
baseUrl: "https://api.openai.com/v1/audio/speech",
@@ -111,21 +96,21 @@ export const AUDIO_SPEECH_PROVIDERS: Record<string, AudioProvider> = {
/**
* Get transcription provider config by ID
*/
export function getTranscriptionProvider(providerId: string): AudioProvider | null {
export function getTranscriptionProvider(providerId) {
return AUDIO_TRANSCRIPTION_PROVIDERS[providerId] || null;
}
/**
* Get speech provider config by ID
*/
export function getSpeechProvider(providerId: string): AudioProvider | null {
export function getSpeechProvider(providerId) {
return AUDIO_SPEECH_PROVIDERS[providerId] || null;
}
/**
* Parse audio model string (format: "provider/model" or just "model")
*/
function parseAudioModel(modelStr: string | null, registry: Record<string, AudioProvider>): { provider: string | null; model: string | null } {
function parseAudioModel(modelStr, registry) {
if (!modelStr) return { provider: null, model: null };
for (const [providerId, config] of Object.entries(registry)) {
@@ -143,11 +128,11 @@ function parseAudioModel(modelStr: string | null, registry: Record<string, Audio
return { provider: null, model: modelStr };
}
export function parseTranscriptionModel(modelStr: string | null) {
export function parseTranscriptionModel(modelStr) {
return parseAudioModel(modelStr, AUDIO_TRANSCRIPTION_PROVIDERS);
}
export function parseSpeechModel(modelStr: string | null) {
export function parseSpeechModel(modelStr) {
return parseAudioModel(modelStr, AUDIO_SPEECH_PROVIDERS);
}
@@ -1,4 +1,4 @@
import { loadProviderCredentials } from "./credentialLoader.ts";
import { loadProviderCredentials } from "./credentialLoader.js";
// Timeout for non-streaming fetch requests (ms). Prevents stalled connections.
export const FETCH_TIMEOUT_MS = parseInt(process.env.FETCH_TIMEOUT_MS || "120000", 10);
@@ -9,7 +9,7 @@ export const STREAM_IDLE_TIMEOUT_MS = parseInt(process.env.STREAM_IDLE_TIMEOUT_M
// Provider configurations
// OAuth credentials read from env vars with hardcoded fallbacks for backward compatibility.
// Use provider-credentials.json or env vars to override in production.
import { generateLegacyProviders } from "./providerRegistry.ts";
import { generateLegacyProviders } from "./providerRegistry.js";
export const PROVIDERS = generateLegacyProviders();
@@ -1,4 +1,4 @@
import { generateModels, generateAliasMap } from "./providerRegistry.ts";
import { generateModels, generateAliasMap } from "./providerRegistry.js";
// Provider models - Generated from providerRegistry.js (single source of truth)
export const PROVIDER_MODELS = generateModels();
@@ -6,52 +6,9 @@
* is auto-generated from this registry.
*/
// ── Types ─────────────────────────────────────────────────────────────────
export interface RegistryModel {
id: string;
name: string;
targetFormat?: string;
}
export interface RegistryOAuth {
clientIdEnv?: string;
clientIdDefault?: string;
clientSecretEnv?: string;
clientSecretDefault?: string;
tokenUrl?: string;
refreshUrl?: string;
authUrl?: string;
initiateUrl?: string;
pollUrlBase?: string;
}
export interface RegistryEntry {
id: string;
alias: string;
format: string;
executor: string;
baseUrl?: string;
baseUrls?: string[];
responsesBaseUrl?: string;
urlSuffix?: string;
urlBuilder?: (base: string, model: string, stream: boolean) => string;
authType: string;
authHeader: string;
authPrefix?: string;
headers?: Record<string, string>;
extraHeaders?: Record<string, string>;
oauth?: RegistryOAuth;
models: RegistryModel[];
modelsUrl?: string;
chatPath?: string;
clientVersion?: string;
passthroughModels?: boolean;
}
// ── Registry ──────────────────────────────────────────────────────────────
export const REGISTRY: Record<string, RegistryEntry> = {
export const REGISTRY = {
// ─── OAuth Providers ───────────────────────────────────────────────────
claude: {
id: "claude",
@@ -108,7 +65,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
clientIdEnv: "GEMINI_OAUTH_CLIENT_ID",
clientIdDefault: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com",
clientSecretEnv: "GEMINI_OAUTH_CLIENT_SECRET",
clientSecretDefault: "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",
clientSecretDefault: "",
},
models: [
{ id: "gemini-3-pro-preview", name: "Gemini 3 Pro Preview" },
@@ -134,7 +91,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
clientIdEnv: "GEMINI_CLI_OAUTH_CLIENT_ID",
clientIdDefault: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com",
clientSecretEnv: "GEMINI_CLI_OAUTH_CLIENT_SECRET",
clientSecretDefault: "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",
clientSecretDefault: "",
},
models: [
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash Preview" },
@@ -212,7 +169,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
id: "iflow",
alias: "if",
format: "openai",
executor: "iflow",
executor: "default",
baseUrl: "https://apis.iflow.cn/v1/chat/completions",
authType: "oauth",
authHeader: "bearer",
@@ -223,7 +180,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
clientIdEnv: "IFLOW_OAUTH_CLIENT_ID",
clientIdDefault: "10009311001",
clientSecretEnv: "IFLOW_OAUTH_CLIENT_SECRET",
clientSecretDefault: "4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW",
clientSecretDefault: "",
tokenUrl: "https://iflow.cn/oauth/token",
authUrl: "https://iflow.cn/oauth",
},
@@ -261,15 +218,18 @@ export const REGISTRY: Record<string, RegistryEntry> = {
clientIdEnv: "ANTIGRAVITY_OAUTH_CLIENT_ID",
clientIdDefault: "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com",
clientSecretEnv: "ANTIGRAVITY_OAUTH_CLIENT_SECRET",
clientSecretDefault: "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",
clientSecretDefault: "",
},
models: [
{ id: "claude-opus-4-6-thinking", name: "Claude Opus 4.6 Thinking" },
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" },
{ id: "gemini-3.1-pro-high", name: "Gemini 3.1 Pro High" },
{ id: "gemini-3.1-pro-low", name: "Gemini 3.1 Pro Low" },
{ id: "claude-opus-4-5-thinking", name: "Claude Opus 4.5 Thinking" },
{ id: "claude-sonnet-4-5-thinking", name: "Claude Sonnet 4.5 Thinking" },
{ id: "claude-sonnet-4-5", name: "Claude Sonnet 4.5" },
{ id: "gemini-3-pro-high", name: "Gemini 3 Pro High" },
{ id: "gemini-3-pro-low", name: "Gemini 3 Pro Low" },
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash Preview" },
{ id: "gemini-3-flash", name: "Gemini 3 Flash" },
{ id: "gpt-oss-120b-medium", name: "GPT OSS 120B Medium" },
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash" },
],
},
@@ -501,7 +461,6 @@ export const REGISTRY: Record<string, RegistryEntry> = {
format: "openrouter",
executor: "openrouter",
baseUrl: "https://api.kilo.ai/api/openrouter/chat/completions",
modelsUrl: "https://api.kilo.ai/api/openrouter/models",
authType: "oauth",
authHeader: "Authorization",
authPrefix: "Bearer ",
@@ -510,32 +469,14 @@ export const REGISTRY: Record<string, RegistryEntry> = {
pollUrlBase: "https://api.kilo.ai/api/device-auth/codes",
},
models: [
{ id: "openrouter/free", name: "Free Models Router" },
{ id: "qwen/qwen3-vl-235b-a22b-thinking", name: "Qwen3 VL 235B A22B Thinking" },
{ id: "qwen/qwen3-235b-a22b-thinking-2507", name: "Qwen3 235B A22B Thinking 2507" },
{ id: "qwen/qwen3-vl-30b-a3b-thinking", name: "Qwen3 VL 30B A3B Thinking" },
{ id: "stepfun/step-3.5-flash:free", name: "StepFun Step 3.5 Flash" },
{ id: "arcee-ai/trinity-large-preview:free", name: "Arcee AI Trinity Large Preview" },
{ id: "openai/gpt-4o-mini", name: "GPT-4o Mini" },
{ id: "openai/gpt-4.1-nano", name: "GPT-4.1 Nano" },
{ id: "openai/gpt-5-nano", name: "GPT-5 Nano" },
{ id: "openai/gpt-5-mini", name: "GPT-5 Mini" },
{ id: "anthropic/claude-3-haiku", name: "Claude 3 Haiku" },
{ id: "google/gemini-2.0-flash", name: "Gemini 2.0 Flash" },
{ id: "google/gemini-2.5-flash-lite", name: "Gemini 2.5 Flash Lite" },
{ id: "deepseek/deepseek-chat-v3.1", name: "DeepSeek V3.1" },
{ id: "deepseek/deepseek-v3.2", name: "DeepSeek V3.2" },
{ id: "meta-llama/llama-3.3-70b-instruct", name: "Llama 3.3 70B" },
{ id: "meta-llama/llama-4-scout", name: "Llama 4 Scout" },
{ id: "meta-llama/llama-4-maverick", name: "Llama 4 Maverick" },
{ id: "qwen/qwen3-8b", name: "Qwen3 8B" },
{ id: "qwen/qwen3-32b", name: "Qwen3 32B" },
{ id: "qwen/qwen3-coder", name: "Qwen3 Coder 480B" },
{ id: "qwen/qwq-32b", name: "QwQ 32B" },
{ id: "mistralai/mistral-small-24b-instruct-2501", name: "Mistral Small 3" },
{ id: "mistralai/mistral-7b-instruct", name: "Mistral 7B" },
{ id: "x-ai/grok-code-fast-1", name: "Grok Code Fast 1" },
{ id: "moonshotai/kimi-k2.5", name: "Kimi K2.5" },
{ id: "anthropic/claude-sonnet-4-20250514", name: "Claude Sonnet 4" },
{ id: "anthropic/claude-opus-4-20250514", name: "Claude Opus 4" },
{ id: "google/gemini-2.5-pro", name: "Gemini 2.5 Pro" },
{ id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash" },
{ id: "openai/gpt-4.1", name: "GPT-4.1" },
{ id: "openai/o3", name: "o3" },
{ id: "deepseek/deepseek-chat", name: "DeepSeek Chat" },
{ id: "deepseek/deepseek-reasoner", name: "DeepSeek Reasoner" },
],
passthroughModels: true,
},
@@ -819,10 +760,10 @@ export const REGISTRY: Record<string, RegistryEntry> = {
// ── Generator Functions ───────────────────────────────────────────────────
/** Generate legacy PROVIDERS object shape for constants.js backward compatibility */
export function generateLegacyProviders(): Record<string, any> {
const providers: Record<string, any> = {};
export function generateLegacyProviders() {
const providers = {};
for (const [id, entry] of Object.entries(REGISTRY)) {
const p: Record<string, any> = { format: entry.format };
const p = { format: entry.format };
// URL(s)
if (entry.baseUrls) {
@@ -867,8 +808,8 @@ export function generateLegacyProviders(): Record<string, any> {
}
/** Generate PROVIDER_MODELS map (alias → model list) */
export function generateModels(): Record<string, RegistryModel[]> {
const models: Record<string, RegistryModel[]> = {};
export function generateModels() {
const models = {};
for (const entry of Object.values(REGISTRY)) {
if (entry.models && entry.models.length > 0) {
const key = entry.alias || entry.id;
@@ -882,8 +823,8 @@ export function generateModels(): Record<string, RegistryModel[]> {
}
/** Generate PROVIDER_ID_TO_ALIAS map */
export function generateAliasMap(): Record<string, string> {
const map: Record<string, string> = {};
export function generateAliasMap() {
const map = {};
for (const entry of Object.values(REGISTRY)) {
map[entry.id] = entry.alias || entry.id;
}
@@ -900,12 +841,12 @@ for (const entry of Object.values(REGISTRY)) {
}
/** Get registry entry by provider ID or alias */
export function getRegistryEntry(provider: string): RegistryEntry | null {
export function getRegistryEntry(provider) {
return REGISTRY[provider] || _byAlias.get(provider) || null;
}
/** Get all registered provider IDs */
export function getRegisteredProviders(): string[] {
export function getRegisteredProviders() {
return Object.keys(REGISTRY);
}
@@ -915,7 +856,7 @@ export function getRegisteredProviders(): string[] {
* @param {string} provider - Provider ID or alias
* @returns {"oauth"|"apikey"}
*/
export function getProviderCategory(provider: string): "oauth" | "apikey" {
export function getProviderCategory(provider) {
const entry = getRegistryEntry(provider);
if (!entry) return "apikey"; // Safe default for unknown providers
return entry.authType === "apikey" ? "apikey" : "oauth";
@@ -1,18 +1,9 @@
import crypto from "crypto";
import { BaseExecutor } from "./base.ts";
import { PROVIDERS, OAUTH_ENDPOINTS, HTTP_STATUS } from "../config/constants.ts";
import { BaseExecutor } from "./base.js";
import { PROVIDERS, OAUTH_ENDPOINTS, HTTP_STATUS } from "../config/constants.js";
const MAX_RETRY_AFTER_MS = 10000;
/**
* Strip any provider prefix (e.g. "antigravity/model" "model").
* Ensures the model name sent to the upstream API never contains a routing prefix.
*/
function cleanModelName(model: string): string {
if (!model) return model;
return model.includes("/") ? model.split("/").pop()! : model;
}
export class AntigravityExecutor extends BaseExecutor {
constructor() {
super("antigravity", PROVIDERS.antigravity);
@@ -69,12 +60,10 @@ export class AntigravityExecutor extends BaseExecutor {
: body.request?.toolConfig,
};
const upstreamModel = cleanModelName(model);
return {
...body,
project: projectId,
model: upstreamModel,
model: model,
userAgent: "antigravity",
requestType: "agent",
requestId: `agent-${crypto.randomUUID()}`,
@@ -1,4 +1,4 @@
import { HTTP_STATUS, FETCH_TIMEOUT_MS } from "../config/constants.ts";
import { HTTP_STATUS, FETCH_TIMEOUT_MS } from "../config/constants.js";
/**
* BaseExecutor - Base class for provider executors.
@@ -6,10 +6,7 @@ import { HTTP_STATUS, FETCH_TIMEOUT_MS } from "../config/constants.ts";
* (buildUrl, buildHeaders, transformRequest, etc.) for each provider.
*/
export class BaseExecutor {
provider: any;
config: any;
constructor(provider: any, config: any) {
constructor(provider, config) {
this.provider = provider;
this.config = config;
}
@@ -99,7 +96,7 @@ export class BaseExecutor {
? AbortSignal.any([signal, timeoutSignal])
: signal || timeoutSignal;
const fetchOptions: Record<string, any> = {
const fetchOptions = {
method: "POST",
headers,
body: JSON.stringify(transformedBody),
@@ -1,11 +1,10 @@
import { BaseExecutor } from "./base.ts";
import { CODEX_DEFAULT_INSTRUCTIONS } from "../config/codexInstructions.ts";
import { PROVIDERS } from "../config/constants.ts";
import { BaseExecutor } from "./base.js";
import { CODEX_DEFAULT_INSTRUCTIONS } from "../config/codexInstructions.js";
import { PROVIDERS } from "../config/constants.js";
/**
* Codex Executor - handles OpenAI Codex API (Responses API format)
* Automatically injects default instructions if missing.
* IMPORTANT: Includes chatgpt-account-id header for workspace binding.
* Automatically injects default instructions if missing
*/
export class CodexExecutor extends BaseExecutor {
constructor() {
@@ -15,18 +14,9 @@ export class CodexExecutor extends BaseExecutor {
/**
* Codex Responses endpoint is SSE-first.
* Always request event-stream from upstream, even when client requested stream=false.
* Includes chatgpt-account-id header for strict workspace binding.
*/
buildHeaders(credentials, stream = true) {
const headers = super.buildHeaders(credentials, true);
// Add workspace binding header if workspaceId is persisted
const workspaceId = credentials?.providerSpecificData?.workspaceId;
if (workspaceId) {
headers["chatgpt-account-id"] = workspaceId;
}
return headers;
return super.buildHeaders(credentials, true);
}
/**
@@ -1,4 +1,3 @@
declare var EdgeRuntime: any;
/**
* CursorExecutor Handles communication with the Cursor IDE API.
*
@@ -20,16 +19,16 @@ declare var EdgeRuntime: any;
* @see cursorProtobuf.js for Protobuf encoding/decoding utilities
*/
import { BaseExecutor } from "./base.ts";
import { PROVIDERS, HTTP_STATUS } from "../config/constants.ts";
import { BaseExecutor } from "./base.js";
import { PROVIDERS, HTTP_STATUS } from "../config/constants.js";
import {
generateCursorBody,
parseConnectRPCFrame,
extractTextFromResponse,
} from "../utils/cursorProtobuf.ts";
import { estimateUsage } from "../utils/usageTracking.ts";
import { FORMATS } from "../translator/formats.ts";
import { buildCursorRequest } from "../translator/request/openai-to-cursor.ts";
} from "../utils/cursorProtobuf.js";
import { estimateUsage } from "../utils/usageTracking.js";
import { FORMATS } from "../translator/formats.js";
import { buildCursorRequest } from "../translator/request/openai-to-cursor.js";
import crypto from "crypto";
import { v5 as uuidv5 } from "uuid";
import zlib from "zlib";
@@ -227,7 +226,7 @@ export class CursorExecutor extends BaseExecutor {
return {
status: response.status,
headers: Object.fromEntries((response.headers as any).entries()),
headers: Object.fromEntries(response.headers.entries()),
body: Buffer.from(await response.arrayBuffer()),
};
}
@@ -291,7 +290,7 @@ export class CursorExecutor extends BaseExecutor {
const transformedBody = this.transformRequest(model, body, stream, credentials);
try {
const response: any = http2
const response = http2
? await this.makeHttp2Request(url, headers, transformedBody, signal)
: await this.makeFetchRequest(url, headers, transformedBody, signal);
@@ -459,7 +458,8 @@ export class CursorExecutor extends BaseExecutor {
console.log(`[CURSOR BUFFER] Final toolCalls count: ${toolCalls.length}`);
const message: Record<string, any> = { role: "assistant",
const message = {
role: "assistant",
content: totalContent || null,
};
@@ -1,6 +1,6 @@
import { BaseExecutor } from "./base.ts";
import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.ts";
import { getAccessToken } from "../services/tokenRefresh.ts";
import { BaseExecutor } from "./base.js";
import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.js";
import { getAccessToken } from "../services/tokenRefresh.js";
export class DefaultExecutor extends BaseExecutor {
constructor(provider) {
@@ -1,5 +1,5 @@
import { BaseExecutor } from "./base.ts";
import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.ts";
import { BaseExecutor } from "./base.js";
import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.js";
export class GeminiCLIExecutor extends BaseExecutor {
constructor() {
@@ -1,6 +1,6 @@
import { BaseExecutor } from "./base.ts";
import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.ts";
import { getModelTargetFormat } from "../config/providerModels.ts";
import { BaseExecutor } from "./base.js";
import { PROVIDERS, OAUTH_ENDPOINTS } from "../config/constants.js";
import { getModelTargetFormat } from "../config/providerModels.js";
export class GithubExecutor extends BaseExecutor {
constructor() {
-97
View File
@@ -1,97 +0,0 @@
import crypto from "crypto";
import { BaseExecutor } from "./base.ts";
import { PROVIDERS } from "../config/constants.ts";
/**
* IFlowExecutor - Executor for iFlow API with HMAC-SHA256 signature.
*
* iFlow requires custom headers including a session ID, timestamp,
* and an HMAC-SHA256 signature for request authentication.
* Without these headers, the API returns a 406 error.
*
* Fixes: https://github.com/diegosouzapw/OmniRoute/issues/114
*/
export class IFlowExecutor extends BaseExecutor {
constructor() {
super("iflow", PROVIDERS.iflow);
}
/**
* Create iFlow signature using HMAC-SHA256
* @param userAgent - User agent string
* @param sessionID - Session ID
* @param timestamp - Unix timestamp in milliseconds
* @param apiKey - API key for signing
* @returns Hex-encoded signature
*/
createIFlowSignature(
userAgent: string,
sessionID: string,
timestamp: number,
apiKey: string
): string {
if (!apiKey) return "";
const payload = `${userAgent}:${sessionID}:${timestamp}`;
const hmac = crypto.createHmac("sha256", apiKey);
hmac.update(payload);
return hmac.digest("hex");
}
/**
* Build headers with iFlow-specific HMAC-SHA256 signature.
* Includes session-id, x-iflow-timestamp, and x-iflow-signature.
*/
buildHeaders(credentials: any, stream = true) {
// Generate session ID and timestamp
const sessionID = `session-${crypto.randomUUID()}`;
const timestamp = Date.now();
// Get user agent from config
const userAgent = this.config.headers?.["User-Agent"] || "iFlow-Cli";
// Get API key (prefer apiKey, fallback to accessToken)
const apiKey = credentials.apiKey || credentials.accessToken || "";
// Create HMAC-SHA256 signature
const signature = this.createIFlowSignature(userAgent, sessionID, timestamp, apiKey);
// Build headers
const headers: Record<string, string> = {
"Content-Type": "application/json",
...this.config.headers,
"session-id": sessionID,
"x-iflow-timestamp": timestamp.toString(),
"x-iflow-signature": signature,
};
// Add authorization
if (credentials.apiKey) {
headers["Authorization"] = `Bearer ${credentials.apiKey}`;
} else if (credentials.accessToken) {
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
}
// Add streaming header
if (stream) {
headers["Accept"] = "text/event-stream";
}
return headers;
}
/**
* Build URL for iFlow API uses baseUrl directly.
*/
buildUrl(model: string, stream: boolean, urlIndex = 0, credentials: any = null) {
return this.config.baseUrl;
}
/**
* Transform request body (passthrough for iFlow).
*/
transformRequest(model: string, body: any, stream: boolean, credentials: any) {
return body;
}
}
export default IFlowExecutor;
+38
View File
@@ -0,0 +1,38 @@
import { AntigravityExecutor } from "./antigravity.js";
import { GeminiCLIExecutor } from "./gemini-cli.js";
import { GithubExecutor } from "./github.js";
import { KiroExecutor } from "./kiro.js";
import { CodexExecutor } from "./codex.js";
import { CursorExecutor } from "./cursor.js";
import { DefaultExecutor } from "./default.js";
const executors = {
antigravity: new AntigravityExecutor(),
"gemini-cli": new GeminiCLIExecutor(),
github: new GithubExecutor(),
kiro: new KiroExecutor(),
codex: new CodexExecutor(),
cursor: new CursorExecutor(),
cu: new CursorExecutor(), // Alias for cursor
};
const defaultCache = new Map();
export function getExecutor(provider) {
if (executors[provider]) return executors[provider];
if (!defaultCache.has(provider)) defaultCache.set(provider, new DefaultExecutor(provider));
return defaultCache.get(provider);
}
export function hasSpecializedExecutor(provider) {
return !!executors[provider];
}
export { BaseExecutor } from "./base.js";
export { AntigravityExecutor } from "./antigravity.js";
export { GeminiCLIExecutor } from "./gemini-cli.js";
export { GithubExecutor } from "./github.js";
export { KiroExecutor } from "./kiro.js";
export { CodexExecutor } from "./codex.js";
export { CursorExecutor } from "./cursor.js";
export { DefaultExecutor } from "./default.js";
-41
View File
@@ -1,41 +0,0 @@
import { AntigravityExecutor } from "./antigravity.ts";
import { GeminiCLIExecutor } from "./gemini-cli.ts";
import { GithubExecutor } from "./github.ts";
import { IFlowExecutor } from "./iflow.ts";
import { KiroExecutor } from "./kiro.ts";
import { CodexExecutor } from "./codex.ts";
import { CursorExecutor } from "./cursor.ts";
import { DefaultExecutor } from "./default.ts";
const executors = {
antigravity: new AntigravityExecutor(),
"gemini-cli": new GeminiCLIExecutor(),
github: new GithubExecutor(),
iflow: new IFlowExecutor(),
kiro: new KiroExecutor(),
codex: new CodexExecutor(),
cursor: new CursorExecutor(),
cu: new CursorExecutor(), // Alias for cursor
};
const defaultCache = new Map();
export function getExecutor(provider) {
if (executors[provider]) return executors[provider];
if (!defaultCache.has(provider)) defaultCache.set(provider, new DefaultExecutor(provider));
return defaultCache.get(provider);
}
export function hasSpecializedExecutor(provider) {
return !!executors[provider];
}
export { BaseExecutor } from "./base.ts";
export { AntigravityExecutor } from "./antigravity.ts";
export { GeminiCLIExecutor } from "./gemini-cli.ts";
export { GithubExecutor } from "./github.ts";
export { IFlowExecutor } from "./iflow.ts";
export { KiroExecutor } from "./kiro.ts";
export { CodexExecutor } from "./codex.ts";
export { CursorExecutor } from "./cursor.ts";
export { DefaultExecutor } from "./default.ts";
@@ -1,7 +1,7 @@
import { BaseExecutor } from "./base.ts";
import { PROVIDERS } from "../config/constants.ts";
import { BaseExecutor } from "./base.js";
import { PROVIDERS } from "../config/constants.js";
import { v4 as uuidv4 } from "uuid";
import { refreshKiroToken } from "../services/tokenRefresh.ts";
import { refreshKiroToken } from "../services/tokenRefresh.js";
// ── CRC32 lookup table (IEEE polynomial, no dependency) ──
const CRC32_TABLE = new Uint32Array(256);
@@ -83,7 +83,8 @@ export class KiroExecutor extends BaseExecutor {
let chunkIndex = 0;
const responseId = `chatcmpl-${Date.now()}`;
const created = Math.floor(Date.now() / 1000);
const state: Record<string, any> = { endDetected: false,
const state = {
endDetected: false,
finishEmitted: false,
hasToolCalls: false,
toolCallIndex: 0,
@@ -125,7 +126,7 @@ export class KiroExecutor extends BaseExecutor {
const content = event.payload.content;
state.totalContentLength += content.length;
const chunk: Record<string, any> = {
const chunk = {
id: responseId,
object: "chat.completion.chunk",
created,
@@ -144,7 +145,7 @@ export class KiroExecutor extends BaseExecutor {
// Handle codeEvent
if (eventType === "codeEvent" && event.payload?.content) {
const chunk: Record<string, any> = {
const chunk = {
id: responseId,
object: "chat.completion.chunk",
created,
@@ -256,7 +257,7 @@ export class KiroExecutor extends BaseExecutor {
// Handle messageStopEvent
if (eventType === "messageStopEvent") {
const chunk: Record<string, any> = {
const chunk = {
id: responseId,
object: "chat.completion.chunk",
created,
@@ -329,7 +330,7 @@ export class KiroExecutor extends BaseExecutor {
};
}
const finishChunk: Record<string, any> = {
const finishChunk = {
id: responseId,
object: "chat.completion.chunk",
created,

Some files were not shown because too many files have changed in this diff Show More