Compare commits

..

102 Commits

Author SHA1 Message Date
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
959 changed files with 11651 additions and 223363 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
-55
View File
@@ -1,55 +0,0 @@
---
description: Deploy the latest OmniRoute code to the Akamai VPS (69.164.221.35) via npm
---
# Deploy to VPS Workflow
Deploy OmniRoute to the production VPS using Node.js + PM2 (no Docker).
**VPS:** `69.164.221.35` (Akamai, Ubuntu 24.04, 1GB RAM + 2.5GB swap)
**App path:** `/opt/omniroute-app`
**Process manager:** PM2 (`omniroute`)
**Port:** `20128`
## Steps
### 1. Push to GitHub
Ensure all changes are committed and pushed:
```bash
git push origin main
```
### 2. SSH into VPS, pull latest code, rebuild, and restart
// turbo-all
```bash
ssh root@69.164.221.35 "
cd /opt/omniroute-app &&
git fetch origin &&
git reset --hard origin/main &&
export NODE_OPTIONS='--max-old-space-size=1536' &&
npm install --no-audit --no-fund &&
npm run build &&
pm2 restart omniroute &&
pm2 save &&
echo '✅ Deploy complete!'
"
```
### 3. Verify the deployment
```bash
ssh root@69.164.221.35 "pm2 list && curl -s -o /dev/null -w 'HTTP %{http_code}' http://localhost:20128/"
```
Expected: PM2 shows `online`, HTTP returns `307` (redirect to login).
## Notes
- The VPS has only 1GB RAM. `NODE_OPTIONS='--max-old-space-size=1536'` uses swap for the build.
- PM2 is configured with `pm2 startup` to auto-restart on reboot.
- The `.env` file is at `/opt/omniroute-app/.env` (copied from the old Docker setup at `/opt/omniroute/.env`).
- Nginx proxies `omniroute.online``localhost:20128`.
-78
View File
@@ -1,78 +0,0 @@
---
description: Create a new release, bump version up to 1.x.10 threshold, update changelog, and manage Pull Requests
---
# Generate Release Workflow
Bump version, finalize CHANGELOG, commit, tag, push, publish to npm, and create GitHub release.
## Steps
### 1. Determine new version
Check current version in `package.json` and increment the patch number:
```bash
grep '"version"' package.json
```
Version format: `1.x.y` — increment `y` for patch, `x` for minor (threshold: y=10 triggers x+1).
### 2. Finalize CHANGELOG.md
Replace `[Unreleased]` header with the new version and date:
```markdown
## [1.x.y] — YYYY-MM-DD
```
### 3. Bump version in package.json
```bash
sed -i 's/"version": "OLD"/"version": "NEW"/' package.json
```
### 4. Stage, commit, and tag
// turbo-all
```bash
git add -A
git commit -m "feat(release): vX.Y.Z — summary of changes"
git tag -a vX.Y.Z -m "Release vX.Y.Z — summary"
```
### 5. Push to GitHub
```bash
git push origin main
git push origin vX.Y.Z
```
### 6. Publish to npm
```bash
npm publish
```
Wait for completion (prepublishOnly runs `npm run build:cli` automatically).
### 7. Create GitHub release
```bash
gh release create vX.Y.Z --title "Release vX.Y.Z" --notes-file /tmp/release_notes.md
```
### 8. Deploy to VPS (if requested)
See `/deploy-vps` workflow for Akamai VPS or use npm for local VPS:
```bash
ssh root@<VPS_IP> "npm install -g omniroute@X.Y.Z && pm2 restart omniroute"
```
## Notes
- Always run `/update-docs` BEFORE this workflow (ensures CHANGELOG and README are current)
- The `prepublishOnly` script runs `npm run build:cli` automatically during `npm publish`
- After npm publish, verify with `npm info omniroute version`
-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 |
```
-50
View File
@@ -1,50 +0,0 @@
---
description: How to respond to GitHub issues with insufficient information
---
# Issue Triage Workflow
Respond to GitHub issues that need more information before they can be investigated.
## Steps
### 1. Identify issues needing triage
```bash
gh issue list --state open --limit 20
```
### 2. Evaluate each issue
Check if the issue has:
- Clear reproduction steps
- Environment details (OS, Node.js version, OmniRoute version)
- Error logs/screenshots
- Expected vs actual behavior
### 3. Respond with triage template
For issues missing information:
```markdown
Thank you for reporting this issue! To help us investigate, please provide:
1. **OmniRoute version**: (`omniroute --version`)
2. **Node.js version**: (`node --version`)
3. **Operating system**: (e.g., Ubuntu 24.04, macOS 15, Windows 11)
4. **Installation method**: (npm, Docker, source)
5. **Steps to reproduce**: (exact commands/actions that trigger the issue)
6. **Error logs**: (paste relevant logs from the console)
7. **Expected behavior**: (what should happen)
This will help us debug and resolve your issue faster. 🙏
```
### 4. Label the issue
Add appropriate labels: `needs-info`, `bug`, `enhancement`, `question`, etc.
```bash
gh issue edit <NUMBER> --add-label "needs-info"
```
-120
View File
@@ -1,120 +0,0 @@
---
description: Fetch all open GitHub issues, analyze bugs, resolve what's possible, triage the rest, wait for user validation, 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, and triages issues with insufficient information. **It does NOT merge or release automatically** — it creates a PR and waits for user validation before merging.
## 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. **Create a fix branch**`git checkout -b fix/issue-<NUMBER>-<short-description>`
2. **Research** — Search the codebase for files related to the issue
3. **Root Cause** — Identify the root cause by reading the relevant source files
4. **Implement Fix** — Apply the fix following existing code patterns and conventions
5. **Test** — Build the project and run tests to verify the fix
6. **Commit** — Commit with message format: `fix: <description> (#<issue_number>)`
### 5. Generate Report & Wait for Validation
Present a summary report to the user via `notify_user` with `BlockedOnUser: true`:
| Issue | Title | Status | Action |
| ----- | ----- | ------------- | ----------------------------- |
| #N | Title | ✅ Ready | Files changed (not committed) |
| #N | Title | ❓ Needs Info | Triage comment posted |
| #N | Title | ⏭️ Skipped | Feature request / not a bug |
> **⚠️ IMPORTANT**: Do NOT commit, close issues, or generate releases at this step.
> Wait for the user to review the changes and respond with **OK** before proceeding.
- If the user says **OK** or approves → Proceed to step 6
- If the user requests changes → Apply the requested adjustments first, then present the report again
- If the user rejects → Revert the changes and stop
### 6. Commit & Push Fix Branch (only after user approval)
After the user validates:
- Commit each fix individually with message format: `fix: <description> (#<issue_number>)`
- Push the fix branch: `git push origin fix/issue-<NUMBER>-<short-description>`
- Create a PR: `gh pr create --title "fix: <description> (#<issue_number>)" --body "<details>" --base main`
### 7. 🛑 WAIT — Notify User & Await PR Verification
**This is a mandatory stop point.** Use `notify_user` with `BlockedOnUser: true`:
- Inform the user that the PR was created and is **awaiting their verification**
- Include the PR number, URL, and a summary of what was changed
- **DO NOT merge, close issues, generate releases, or deploy until the user confirms**
Wait for the user to respond:
- **User confirms** → Proceed to step 8
- **User requests changes** → Apply changes, push to the same branch, notify again
- **User rejects** → Close the PR and stop
### 8. Merge, Close Issues & Release (only after user confirms PR)
After the user confirms the PR:
1. **Merge** the PR: `gh pr merge <NUMBER> --merge --repo <owner>/<repo>` or via local merge
2. **Close** resolved issues with a comment: `gh issue close <NUMBER> --repo <owner>/<repo> --comment "Fixed in <commit_hash>. The fix will be included in the next release."`
3. **Switch to main**: `git checkout main && git pull`
4. Run the `/update-docs` workflow (at `~/.gemini/antigravity/global_workflows/update-docs.md`) to update CHANGELOG and README
5. Run the `/generate-release` workflow (at `.agents/workflows/generate-release.md`) to bump version, tag, and publish
6. Deploy to local VPS: `ssh root@192.168.0.15 "npm install -g omniroute@<VERSION> && pm2 restart omniroute"`
If NO fixes were committed, skip this step and just present the report.
-145
View File
@@ -1,145 +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. **All improvements are committed on top of the PR branch** and the user must verify before merge.
## 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?
#### 3f. Cross-Layer (Global) Analysis
Perform a **global impact assessment** to verify whether the PR changes are complete across all layers of the application:
- **Backend → Frontend check**: If the PR adds or modifies backend-only resources (new endpoints, services, data models), evaluate whether corresponding frontend changes are missing:
- Does a new endpoint require a new screen/page in the dashboard?
- Should there be a new action button, menu item, or navigation link?
- Are there new data fields that should be displayed or editable in the UI?
- Does a new feature need a toggle, configuration panel, or status indicator?
- **Frontend → Backend check**: If the PR adds frontend elements, verify the backend support exists:
- Are the required API endpoints implemented?
- Is the data model sufficient for the new UI components?
- **Cross-cutting concerns**: Check shared layers (types, DTOs, validation schemas, routes, middleware) for completeness
- **Document gaps** — If missing layers are detected, list them as **IMPORTANT** issues in the report with concrete suggestions for what should be added
### 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: `gh pr checkout <NUMBER>`
- Implement any required fixes identified in the analysis
- If the Cross-Layer Analysis (3f) identified missing frontend/backend counterparts, implement them
- **Commit improvements on top of the PR branch** with descriptive commit messages
- 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
- Push the updated branch: `git push origin <branch-name>`
### 7. 🛑 WAIT — Notify User & Await PR Verification
**This is a mandatory stop point.** Use `notify_user` with `BlockedOnUser: true`:
- Inform the user that the PR has been **improved and pushed**, and is **awaiting their verification**
- Include:
- PR number and URL
- Summary of improvements/fixes applied
- Build/test status
- List of files changed
- **DO NOT merge, generate releases, or deploy until the user confirms**
Wait for the user to respond:
- **User confirms** → Proceed to step 8
- **User requests more changes** → Apply changes, push to the same branch, notify again
- **User rejects** → Leave a review comment and stop
### 8. Thank the Contributor
- Post a **thank-you comment** on the PR via the GitHub API
- The message should:
- Thank the author by name/username for their contribution
- Briefly mention what the PR accomplishes and any improvements applied
- Be friendly, professional, and encouraging
- Example: _"Thanks @author for this great contribution! 🎉 The [feature/fix] is now merged and will be part of the next release. We appreciate your effort!"_
### 9. Merge & Release (only after user confirms PR)
After the user confirms the PR:
1. **Merge** the PR into main (local merge with `--no-ff` or via `gh pr merge`)
2. **Push** to main: `git push origin main`
3. **Clean up** the feature branch: `git branch -d <branch-name>`
4. **Update CHANGELOG.md** with the new feature/fix
5. Run the `/generate-release` workflow (at `.agents/workflows/generate-release.md`) to bump version, tag, and publish
6. Deploy to local VPS: `ssh root@192.168.0.15 "npm install -g omniroute@<VERSION> && pm2 restart omniroute"`
-105
View File
@@ -1,105 +0,0 @@
---
description: How to automatically summarize recent changes and update README and CHANGELOG
---
# Update Documentation Workflow
Update CHANGELOG.md, README.md, docs/ files, and all multi-language translations whenever features are added or changed.
## Steps
### 1. Summarize recent changes
Review git log and identify new features, fixes, or changes since the last release tag:
```bash
git log $(git describe --tags --abbrev=0)..HEAD --oneline
```
### 2. Update English CHANGELOG.md
Add an `[Unreleased]` section (or version header if releasing) with:
- `### ✨ New Features` — each feature as a bullet point
- `### 🐛 Bug Fixes` — if applicable
- `### 🧪 Tests` — test count changes
- `### 📁 New Files` — table of new files with purpose
### 3. Update English README.md
Update the feature tables in these sections:
- **🧠 Routing & Intelligence** — for routing/model features
- **🛡️ Resilience & Security** — for security/resilience features
- **📊 Observability & Analytics** — for monitoring features
- **☁️ Deploy & Sync** — for deployment features
### 4. Update docs/ files
- `docs/FEATURES.md` — update the Settings section description
- `docs/API_REFERENCE.md` — add new API routes if any
- `docs/ARCHITECTURE.md` — update architecture if structural changes
### 5. 🌐 Sync Multi-Language Documentation (CRITICAL)
// turbo-all
**This step MUST be run after every README or docs update.**
The project has **30 language versions** of documentation:
**README files (root directory):**
```
README.md (English - source of truth)
README.pt-BR.md README.pt.md README.es.md README.fr.md README.it.md
README.de.md README.nl.md README.sv.md README.no.md README.da.md README.fi.md
README.ru.md README.uk-UA.md README.bg.md README.sk.md README.pl.md README.ro.md README.hu.md
README.ar.md README.he.md README.th.md README.in.md README.id.md README.ms.md README.vi.md
README.ja.md README.ko.md README.zh-CN.md README.phi.md
```
**docs/i18n/ directories (29 languages):**
```
docs/i18n/{ar,bg,da,de,es,fi,fr,he,hu,id,in,it,ja,ko,ms,nl,no,phi,pl,pt,pt-BR,ro,ru,sk,sv,th,uk-UA,vi,zh-CN}/
Each contains: API_REFERENCE.md, ARCHITECTURE.md, CODEBASE_DOCUMENTATION.md, FEATURES.md, TROUBLESHOOTING.md, USER_GUIDE.md
```
**Sync approach for feature table updates:**
a. Identify which feature table rows were added to English README.md
b. For each translated README, find the corresponding anchor lines:
- **Routing section:** Find the `💬` (System Prompt) table row — the line before it is always the last routing feature. Insert new routing features before System Prompt.
- **Resilience section:** Find the `📊` Rate Limits table row (the one in lines 590-600, NOT the quota tracking one in lines 560-570). Insert new resilience features after it.
c. The new feature entries can stay in English for technical features, matching the pattern used in the existing translations.
d. Use `sed` or similar tool to batch-insert across all 29 translated READMEs.
**Verification:**
```bash
# Verify all READMEs have the new features
grep -l "NEW_FEATURE_NAME" README.*.md | wc -l
# Should return 30 (all language versions)
```
**FEATURES.md sync:**
```bash
# Update Settings description in all docs/i18n/*/FEATURES.md
for dir in docs/i18n/*/; do
# Update the Settings section description to mention new features
# Check FEATURES.md in each directory
done
```
### 6. Verify documentation changes
```bash
# Check all modified files
git status --short
# Verify no broken markdown
# Optional: run markdownlint if available
```
+6 -91
View File
@@ -23,15 +23,7 @@ SQLITE_MAX_SIZE_MB=2048
SQLITE_CLEAN_LEGACY_FILES=true
# Recommended runtime variables
# Canonical/base port (keeps backward compatibility)
PORT=20128
# Optional split ports:
# API_PORT=20129
# API_HOST=0.0.0.0
# DASHBOARD_PORT=20128
# Optional Docker production host publish ports:
# PROD_DASHBOARD_PORT=20130
# PROD_API_PORT=20131
NODE_ENV=production
INSTANCE_NAME=omniroute
@@ -52,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=
@@ -68,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
@@ -90,61 +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
# ─────────────────────────────────────────────────────────────────────────────
# Provider User-Agent Overrides (optional — customize per-provider UA headers)
# ─────────────────────────────────────────────────────────────────────────────
# Format: {PROVIDER_ID}_USER_AGENT=custom-value
# When set, overrides the default User-Agent header sent to that provider.
# Useful when providers update versions or block old user-agents.
CLAUDE_USER_AGENT=claude-cli/1.0.83 (external, cli)
CODEX_USER_AGENT=codex-cli/0.92.0 (Windows 10.0.26100; x64)
GITHUB_USER_AGENT=GitHubCopilotChat/0.26.7
ANTIGRAVITY_USER_AGENT=antigravity/1.104.0 darwin/arm64
KIRO_USER_AGENT=AWS-SDK-JS/3.0.0 kiro-ide/1.0.0
IFLOW_USER_AGENT=iFlow-Cli
QWEN_USER_AGENT=google-api-nodejs-client/9.15.1
CURSOR_USER_AGENT=connect-es/1.6.1
GEMINI_CLI_USER_AGENT=google-api-nodejs-client/9.15.1
# 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
@@ -173,28 +113,3 @@ GEMINI_CLI_USER_AGENT=google-api-nodejs-client/9.15.1
# 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
# ─────────────────────────────────────────────────────────────────────────────
# Memory Optimization (Low-RAM configurations)
# ─────────────────────────────────────────────────────────────────────────────
# Node.js heap limit in MB (default: 256 for Docker, system default for npm)
# OMNIROUTE_MEMORY_MB=256
# Prompt cache settings
# PROMPT_CACHE_MAX_SIZE=50
# PROMPT_CACHE_MAX_BYTES=2097152
# PROMPT_CACHE_TTL_MS=300000
# Semantic cache settings (temperature=0 responses)
# SEMANTIC_CACHE_MAX_SIZE=100
# SEMANTIC_CACHE_MAX_BYTES=4194304
# SEMANTIC_CACHE_TTL_MS=1800000
# In-memory log buffers
# PROXY_LOG_MAX_ENTRIES=200
# CALL_LOGS_MAX=200
# STREAM_HISTORY_MAX=50
-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
+22
View File
@@ -0,0 +1,22 @@
name: Codex PR Review
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'
});
-38
View File
@@ -1,38 +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')
&& vars.DEPLOY_ENABLED == 'true'
name: Deploy OmniRoute to VPS
runs-on: ubuntu-latest
steps:
- 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 ==="
-117
View File
@@ -1,117 +0,0 @@
name: Publish to Docker Hub
on:
release:
types: [published]
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@v7
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
runs-on: ubuntu-latest
needs: build
env:
IMAGE_NAME: diegosouzapw/omniroute
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Extract version from release tag
id: version
run: |
VERSION="${GITHUB_REF_NAME}"
VERSION="${VERSION#v}"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Publishing Docker image version: $VERSION"
- name: Download digests
uses: actions/download-artifact@v8
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
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: Update Docker Hub description
uses: peter-evans/dockerhub-description@v5
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: diegosouzapw/omniroute
short-description: "OmniRoute — Unified AI proxy. Route any LLM through one endpoint."
readme-filepath: ./README.md
-173
View File
@@ -1,173 +0,0 @@
name: Build Electron Desktop App
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
version:
description: "Release version (e.g., v1.6.8)"
required: true
type: string
permissions:
contents: write
jobs:
validate:
name: Validate version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.validate.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Validate version format
id: validate
run: |
if [[ "${{ github.event_name }}" == "push" ]]; then
VERSION="${GITHUB_REF#refs/tags/}"
else
VERSION="${{ inputs.version }}"
fi
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Invalid version format. Expected: v1.6.8"
exit 1
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "✓ Valid version: $VERSION"
build:
name: Build Electron (${{ matrix.platform }})
needs: validate
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- platform: windows
runner: windows-latest
target: win
ext: .exe
- platform: macos-intel
runner: macos-latest
target: mac
ext: .dmg
- platform: macos-arm64
runner: macos-latest
target: mac
ext: -arm64.dmg
- platform: linux
runner: ubuntu-latest
target: linux
ext: .AppImage
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: npm
- name: Cache node_modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci
- name: Build Next.js standalone
env:
JWT_SECRET: ci-build-secret-with-sufficient-length-for-validation
run: npm run build
- name: Install Electron dependencies
working-directory: electron
run: npm install --no-audit --no-fund
- name: Build Electron for ${{ matrix.platform }}
working-directory: electron
run: npm run build:${{ matrix.target }}
- name: Collect installers
shell: bash
run: |
mkdir -p release-assets
cd electron/dist-electron
# Copy only installer files for this platform
for file in *${{ matrix.ext }}; do
[ -f "$file" ] && cp "$file" ../../release-assets/
done
# Windows: also copy portable standalone exe
if [ "${{ matrix.platform }}" = "windows" ]; then
[ -f "OmniRoute.exe" ] && cp OmniRoute.exe ../../release-assets/
fi
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: electron-${{ matrix.platform }}
path: release-assets/
release:
name: Create Release
needs: [validate, build]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: release-assets
merge-multiple: true
- name: Create source archives
run: |
# Create source code archives (excluding dev dependencies and build artifacts)
export TARBALL="OmniRoute-${{ needs.validate.outputs.version }}.source.tar.gz"
export ZIPBALL="OmniRoute-${{ needs.validate.outputs.version }}.source.zip"
# Use git archive for clean source export
git archive --format=tar.gz --prefix=OmniRoute-${{ needs.validate.outputs.version }}/ HEAD -o "release-assets/$TARBALL"
git archive --format=zip --prefix=OmniRoute-${{ needs.validate.outputs.version }}/ HEAD -o "release-assets/$ZIPBALL"
echo "✓ Created source archives:"
ls -lh "release-assets/$TARBALL" "release-assets/$ZIPBALL"
- name: List release files
run: ls -la release-assets/
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.validate.outputs.version }}
draft: false
prerelease: false
generate_release_notes: true
files: |
release-assets/*.dmg
release-assets/*-arm64.dmg
release-assets/*.exe
release-assets/*.AppImage
release-assets/*.blockmap
release-assets/*.source.tar.gz
release-assets/*.source.zip
release-assets/OmniRoute.exe
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_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
+3 -36
View File
@@ -1,7 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
node_modules/
/node_modules
/.pnp
.pnp.*
.yarn/*
@@ -11,10 +11,10 @@ node_modules/
!.yarn/versions
# testing
coverage/
/coverage
# next.js
.next/
/.next/
/out/
# production
@@ -56,9 +56,6 @@ docs/*
!docs/ARCHITECTURE.md
!docs/CODEBASE_DOCUMENTATION.md
!docs/CONTRIBUTING.md
!docs/USER_GUIDE.md
!docs/API_REFERENCE.md
!docs/TROUBLESHOOTING.md
!docs/EXECUTION_CONTEXT_PROVIDER_SYNC.md
!docs/TASK_NEBIUS_BACKEND_ENABLEMENT.md
!docs/frontend-backend-provider-gap-report.md
@@ -71,11 +68,6 @@ docs/*
!docs/planning/
!docs/improvement-plans/
!docs/api/
!docs/VM_DEPLOYMENT_GUIDE.md
!docs/FEATURES.md
!docs/screenshots/
!docs/i18n/
!docs/i18n/**
# open-sse tests
open-sse/test/*
@@ -88,28 +80,3 @@ test-results/
playwright-report/
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
# Backup directories
app.__qa_backup/
# Production standalone build (created by scripts/prepublish.mjs)
# Conflicts with Next.js App Router detection in dev (root app/ shadows src/app/)
# npm publish still includes it via package.json "files" field
app/
# Electron (subproject dependency lock and build artifacts)
electron/package-lock.json
electron/dist-electron/
electron/node_modules/
icon.iconset/
+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
+334 -1013
View File
File diff suppressed because it is too large Load Diff
+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)
+4 -10
View File
@@ -2,7 +2,6 @@ FROM node:22-bookworm-slim AS builder
WORKDIR /app
COPY package*.json ./
COPY scripts/postinstall.mjs ./scripts/postinstall.mjs
RUN if [ -f package-lock.json ]; then npm ci --no-audit --no-fund; else npm install --no-audit --no-fund; fi
COPY . ./
@@ -13,32 +12,26 @@ WORKDIR /app
LABEL org.opencontainers.image.title="omniroute" \
org.opencontainers.image.description="Unified AI proxy — route any LLM through one endpoint" \
org.opencontainers.image.url="https://omniroute.online" \
org.opencontainers.image.source="https://github.com/diegosouzapw/OmniRoute" \
org.opencontainers.image.licenses="MIT"
ENV NODE_ENV=production
ENV PORT=20128
ENV HOSTNAME=0.0.0.0
ENV NODE_OPTIONS="--max-old-space-size=256"
# 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
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/scripts/run-standalone.mjs ./run-standalone.mjs
COPY --from=builder /app/scripts/runtime-env.mjs ./runtime-env.mjs
COPY --from=builder /app/scripts/healthcheck.mjs ./healthcheck.mjs
EXPOSE 20128
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD ["node", "healthcheck.mjs"]
CMD node -e "fetch('http://127.0.0.1:20128/api/settings').then(r=>{if(!r.ok)throw r.status}).catch(()=>process.exit(1))"
CMD ["node", "run-standalone.mjs"]
CMD ["node", "server.js"]
FROM runner-base AS runner-cli
@@ -50,3 +43,4 @@ RUN apt-get update \
# Install CLI tools globally. Separate layer from apt for better cache reuse.
RUN npm install -g --no-audit --no-fund @openai/codex @anthropic-ai/claude-code droid openclaw@latest
-1367
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1217
View File
File diff suppressed because it is too large Load Diff
-1220
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1218
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1148
View File
File diff suppressed because it is too large Load Diff
-1217
View File
File diff suppressed because it is too large Load Diff
-1367
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
+104 -1273
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1294
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1216
View File
File diff suppressed because it is too large Load Diff
-1373
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1363
View File
File diff suppressed because it is too large Load Diff
-1374
View File
File diff suppressed because it is too large Load Diff
-1372
View File
File diff suppressed because it is too large Load Diff
-1216
View File
File diff suppressed because it is too large Load Diff
+24 -127
View File
@@ -5,7 +5,7 @@
If you discover a security vulnerability in OmniRoute, please report it responsibly:
1. **DO NOT** open a public GitHub issue
2. Use [GitHub Security Advisories](https://github.com/diegosouzapw/OmniRoute/security/advisories/new)
2. Email: **security@omniroute.dev** (or use GitHub Security Advisories)
3. Include: description, reproduction steps, and potential impact
## Response Timeline
@@ -20,150 +20,47 @@ 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.4.x | ✅ Active |
| 0.3.x | ✅ Active |
| < 0.3.0 | ❌ Unsupported |
---
## Security Best Practices
## Security Architecture
OmniRoute implements a multi-layered security model:
```
Request → CORS → API Key Auth → Prompt Injection Guard → Input Sanitizer → Rate Limiter → Circuit Breaker → Provider
```
### 🔐 Authentication & Authorization
| Feature | Implementation |
| -------------------- | ---------------------------------------------------------- |
| **Dashboard Login** | Password-based auth with JWT tokens (HttpOnly cookies) |
| **API Key Auth** | HMAC-signed keys with CRC validation |
| **OAuth 2.0 + PKCE** | Secure provider auth (Claude, Codex, Gemini, Cursor, etc.) |
| **Token Refresh** | Automatic OAuth token refresh before expiry |
| **Secure Cookies** | `AUTH_COOKIE_SECURE=true` for HTTPS environments |
### 🛡️ Encryption at Rest
All sensitive data stored in SQLite is encrypted using **AES-256-GCM** with scrypt key derivation:
- API keys, access tokens, refresh tokens, and ID tokens
- Versioned format: `enc:v1:<iv>:<ciphertext>:<authTag>`
- Passthrough mode (plaintext) when `STORAGE_ENCRYPTION_KEY` is not set
```bash
# Generate encryption key:
STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 32)
```
### 🧠 Prompt Injection Guard
Middleware that detects and blocks prompt injection attacks in LLM requests:
| Pattern Type | Severity | Example |
| ------------------- | -------- | ---------------------------------------------- |
| System Override | High | "ignore all previous instructions" |
| Role Hijack | High | "you are now DAN, you can do anything" |
| Delimiter Injection | Medium | Encoded separators to break context boundaries |
| DAN/Jailbreak | High | Known jailbreak prompt patterns |
| Instruction Leak | Medium | "show me your system prompt" |
Configure via dashboard (Settings → Security) or `.env`:
```env
INPUT_SANITIZER_ENABLED=true
INPUT_SANITIZER_MODE=block # warn | block | redact
```
### 🔒 PII Redaction
Automatic detection and optional redaction of personally identifiable information:
| PII Type | Pattern | Replacement |
| ------------- | --------------------- | ------------------ |
| Email | `user@domain.com` | `[EMAIL_REDACTED]` |
| CPF (Brazil) | `123.456.789-00` | `[CPF_REDACTED]` |
| CNPJ (Brazil) | `12.345.678/0001-00` | `[CNPJ_REDACTED]` |
| Credit Card | `4111-1111-1111-1111` | `[CC_REDACTED]` |
| Phone | `+55 11 99999-9999` | `[PHONE_REDACTED]` |
| SSN (US) | `123-45-6789` | `[SSN_REDACTED]` |
```env
PII_REDACTION_ENABLED=true
```
### 🌐 Network Security
| Feature | Description |
| ------------------------ | ---------------------------------------------------------------- |
| **CORS** | Configurable origin control (`CORS_ORIGIN` env var, default `*`) |
| **IP Filtering** | Whitelist/blacklist IP ranges in dashboard |
| **Rate Limiting** | Per-provider rate limits with automatic backoff |
| **Anti-Thundering Herd** | Mutex + per-connection locking prevents cascading 502s |
### 🔌 Resilience & Availability
| Feature | Description |
| ----------------------- | ------------------------------------------------------------------ |
| **Circuit Breaker** | 3-state (Closed → Open → Half-Open) per provider, SQLite-persisted |
| **Request Idempotency** | 5-second dedup window for duplicate requests |
| **Exponential Backoff** | Automatic retry with increasing delays |
| **Health Dashboard** | Real-time provider health monitoring |
### 📋 Compliance
| Feature | Description |
| ------------------ | --------------------------------------------------- |
| **Log Retention** | Automatic cleanup after `LOG_RETENTION_DAYS` |
| **No-Log Opt-out** | Per API key `noLog` flag disables request logging |
| **Audit Log** | Administrative actions tracked in `audit_log` table |
---
## Required Environment Variables
### Required Environment Variables
All secrets must be set before starting the server. The server will **fail fast** if they are missing or weak.
```bash
# REQUIRED — server will not start without these:
JWT_SECRET=$(openssl rand -base64 48) # min 32 chars
API_KEY_SECRET=$(openssl rand -hex 32) # min 16 chars
# RECOMMENDED — enables encryption at rest:
# Generate strong secrets:
JWT_SECRET=$(openssl rand -base64 48)
API_KEY_SECRET=$(openssl rand -hex 32)
STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 32)
```
The server actively rejects known-weak values like `changeme`, `secret`, or `password`.
### Input Protection
---
OmniRoute includes built-in protection against:
## Docker Security
- **Prompt injection** — Detects system override, role hijack, delimiter injection, and DAN/jailbreak patterns
- **PII leakage** — Optional detection and redaction of emails, CPF/CNPJ, credit cards, and phone numbers
Configure in `.env`:
```env
INPUT_SANITIZER_ENABLED=true
INPUT_SANITIZER_MODE=block # warn | block | redact
PII_REDACTION_ENABLED=true
```
### Docker Security
- Use non-root user in production
- Mount secrets as read-only volumes
- Never copy `.env` files into Docker images
- Use `.dockerignore` to exclude sensitive files
- Set `AUTH_COOKIE_SECURE=true` when behind HTTPS
```bash
docker run -d \
--name omniroute \
--restart unless-stopped \
--read-only \
-p 20128:20128 \
-v omniroute-data:/app/data \
-e JWT_SECRET="$(openssl rand -base64 48)" \
-e API_KEY_SECRET="$(openssl rand -hex 32)" \
-e STORAGE_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
diegosouzapw/omniroute:latest
```
---
## Dependencies
### Dependencies
- Run `npm audit` regularly
- Keep dependencies updated
- The project uses `husky` + `lint-staged` for pre-commit checks
- CI pipeline runs ESLint security rules on every push
+29 -118
View File
@@ -12,69 +12,15 @@
*/
import { spawn } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { existsSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { homedir, platform } from "node:os";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ROOT = join(__dirname, "..");
const APP_DIR = join(ROOT, "app");
// ── Load .env file (for global npm install) ─────────────────
function loadEnvFile() {
const envPaths = [];
// 1. DATA_DIR/.env if set
if (process.env.DATA_DIR) {
envPaths.push(join(process.env.DATA_DIR, ".env"));
}
// 2. ~/.omniroute/.env (default data dir)
const home = homedir();
if (home) {
if (platform() === "win32") {
const appData = process.env.APPDATA || join(home, "AppData", "Roaming");
envPaths.push(join(appData, "omniroute", ".env"));
} else {
envPaths.push(join(home, ".omniroute", ".env"));
}
}
// 3. ./.env (current working directory)
envPaths.push(join(process.cwd(), ".env"));
for (const envPath of envPaths) {
try {
if (existsSync(envPath)) {
const content = readFileSync(envPath, "utf-8");
for (const line of content.split("\n")) {
const trimmed = line.trim();
// Skip empty lines and comments
if (!trimmed || trimmed.startsWith("#")) continue;
const eqIdx = trimmed.indexOf("=");
if (eqIdx > 0) {
const key = trimmed.slice(0, eqIdx).trim();
const value = trimmed.slice(eqIdx + 1).trim();
// Don't override existing env vars
if (process.env[key] === undefined) {
// Remove surrounding quotes
process.env[key] = value.replace(/^["']|["']$/g, "");
}
}
}
console.log(` \x1b[2m📋 Loaded env from ${envPath}\x1b[0m`);
return;
}
} catch {
// Ignore errors reading env files
}
}
}
loadEnvFile();
// ── Parse args ─────────────────────────────────────────────
const args = process.argv.slice(2);
@@ -84,22 +30,18 @@ if (args.includes("--help") || args.includes("-h")) {
\x1b[1mUsage:\x1b[0m
omniroute Start the server
omniroute --port <port> Use custom API port (default: 20128)
omniroute --port <port> Use custom port (default: 20128)
omniroute --no-open Don't open browser automatically
omniroute --help Show this help
omniroute --version Show version
\x1b[1mConfig:\x1b[0m
Loads .env from: ~/.omniroute/.env or ./.env
Memory limit: OMNIROUTE_MEMORY_MB (default: 512)
\x1b[1mAfter starting:\x1b[0m
Dashboard: http://localhost:<dashboard-port>
API: http://localhost:<api-port>/v1
Dashboard: http://localhost:<port>
API: http://localhost:<port>/v1
\x1b[1mConnect your tools:\x1b[0m
Set your CLI tool (Cursor, Cline, Codex, etc.) to use:
\x1b[33mhttp://localhost:<api-port>/v1\x1b[0m
\x1b[33mhttp://localhost:20128/v1\x1b[0m
`);
process.exit(0);
}
@@ -116,58 +58,40 @@ if (args.includes("--version") || args.includes("-v")) {
process.exit(0);
}
function parsePort(value, fallback) {
const parsed = parseInt(String(value), 10);
return Number.isFinite(parsed) && parsed > 0 && parsed <= 65535 ? parsed : fallback;
}
// Parse --port (canonical/base port)
let port = parsePort(process.env.PORT || "20128", 20128);
// Parse --port
let port = 20128;
const portIdx = args.indexOf("--port");
if (portIdx !== -1 && args[portIdx + 1]) {
const cliPort = parsePort(args[portIdx + 1], null);
if (cliPort === null) {
port = parseInt(args[portIdx + 1], 10);
if (isNaN(port)) {
console.error("\x1b[31m✖ Invalid port number\x1b[0m");
process.exit(1);
}
port = cliPort;
}
const apiPort = parsePort(process.env.API_PORT || String(port), port);
const dashboardPort = parsePort(process.env.DASHBOARD_PORT || String(port), port);
const noOpen = args.includes("--no-open");
// ── Banner ─────────────────────────────────────────────────
console.log(`
\x1b[36m ____ _ ____ _
/ __ \\ (_) __ \\ | |
| | | |_ __ ___ _ __ _| |__) |___ _ _| |_ ___
| | | | '_ \` _ \\| '_ \\ | _ // _ \\| | | | __/ _ \\
| |__| | | | | | | | | | | | \\ \\ (_) | |_| | || __/
\\____/|_| |_| |_|_| |_|_|_| \\_\\___/ \\__,_|\\__\\___|
/ __ \\ (_) __ \\ | |
| | | |_ __ ___ _ __ _| |__) |___ _ _| |_ ___
| | | | '_ \` _ \\| '_ \\ | _ // _ \\| | | | __/ _ \\
| |__| | | | | | | | | | | | \\ \\ (_) | |_| | || __/
\\____/|_| |_| |_|_| |_|_|_| \\_\\___/ \\__,_|\\__\\___|
\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);
}
@@ -175,23 +99,14 @@ if (!existsSync(serverJs)) {
// ── Start server ───────────────────────────────────────────
console.log(` \x1b[2m⏳ Starting server...\x1b[0m\n`);
// Sanitize memory limit — parseInt to prevent command injection (#150)
const rawMemory = parseInt(process.env.OMNIROUTE_MEMORY_MB || "512", 10);
const memoryLimit =
Number.isFinite(rawMemory) && rawMemory >= 64 && rawMemory <= 16384 ? rawMemory : 512;
const env = {
...process.env,
OMNIROUTE_PORT: String(port),
PORT: String(dashboardPort),
DASHBOARD_PORT: String(dashboardPort),
API_PORT: String(apiPort),
PORT: String(port),
HOSTNAME: "0.0.0.0",
NODE_ENV: "production",
NODE_OPTIONS: `--max-old-space-size=${memoryLimit}`,
};
const server = spawn("node", [`--max-old-space-size=${memoryLimit}`, serverJs], {
const server = spawn("node", [serverJs], {
cwd: APP_DIR,
env,
stdio: "pipe",
@@ -204,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();
}
@@ -244,17 +156,16 @@ process.on("SIGTERM", shutdown);
// ── On ready ───────────────────────────────────────────────
async function onReady() {
const dashboardUrl = `http://localhost:${dashboardPort}`;
const apiUrl = `http://localhost:${apiPort}`;
const url = `http://localhost:${port}`;
console.log(`
\x1b[32m OmniRoute is running!\x1b[0m
\x1b[1m Dashboard:\x1b[0m ${dashboardUrl}
\x1b[1m API Base:\x1b[0m ${apiUrl}/v1
\x1b[1m Dashboard:\x1b[0m ${url}
\x1b[1m API Base:\x1b[0m ${url}/v1
\x1b[2m Point your CLI tool (Cursor, Cline, Codex) to:\x1b[0m
\x1b[33m ${apiUrl}/v1\x1b[0m
\x1b[33m ${url}/v1\x1b[0m
\x1b[2m Press Ctrl+C to stop\x1b[0m
`);
@@ -262,7 +173,7 @@ async function onReady() {
if (!noOpen) {
try {
const open = await import("open");
await open.default(dashboardUrl);
await open.default(url);
} catch {
// open is optional — if not available, just skip
}
+9 -7
View File
@@ -22,19 +22,21 @@ services:
env_file: .env
environment:
- NODE_ENV=production
- PORT=${PORT:-20128}
- DASHBOARD_PORT=${DASHBOARD_PORT:-${PORT:-20128}}
- API_PORT=${API_PORT:-20129}
- API_HOST=${API_HOST:-0.0.0.0}
- PORT=20128
- HOSTNAME=0.0.0.0
- DATA_DIR=/app/data
ports:
- "${PROD_DASHBOARD_PORT:-20130}:${DASHBOARD_PORT:-${PORT:-20128}}"
- "${PROD_API_PORT:-20131}:${API_PORT:-20129}"
- "20130:20128"
volumes:
- omniroute-prod-data:/app/data
healthcheck:
test: ["CMD", "node", "healthcheck.mjs"]
test:
[
"CMD",
"node",
"-e",
"fetch('http://127.0.0.1:20128/api/settings').then(r=>{if(!r.ok)throw r.status}).catch(()=>process.exit(1))",
]
interval: 30s
timeout: 5s
retries: 3
+10 -18
View File
@@ -18,16 +18,16 @@
x-common: &common
restart: unless-stopped
env_file: .env
environment:
- DATA_DIR=/app/data # Must match the volume mount below
- PORT=${PORT:-20128}
- DASHBOARD_PORT=${DASHBOARD_PORT:-${PORT:-20128}}
- API_PORT=${API_PORT:-20129}
- API_HOST=${API_HOST:-0.0.0.0}
volumes:
- omniroute-data:/app/data
healthcheck:
test: ["CMD", "node", "healthcheck.mjs"]
test:
[
"CMD",
"node",
"-e",
"fetch('http://127.0.0.1:20128/api/settings').then(r=>{if(!r.ok)throw r.status}).catch(()=>process.exit(1))",
]
interval: 30s
timeout: 5s
retries: 3
@@ -43,8 +43,7 @@ services:
target: runner-base
image: omniroute:base
ports:
- "${DASHBOARD_PORT:-${PORT:-20128}}:${DASHBOARD_PORT:-${PORT:-20128}}"
- "${API_PORT:-20129}:${API_PORT:-20129}"
- "${PORT:-20128}:20128"
profiles:
- base
@@ -57,8 +56,7 @@ services:
target: runner-cli
image: omniroute:cli
ports:
- "${DASHBOARD_PORT:-${PORT:-20128}}:${DASHBOARD_PORT:-${PORT:-20128}}"
- "${API_PORT:-20129}:${API_PORT:-20129}"
- "${PORT:-20128}:20128"
profiles:
- cli
@@ -71,14 +69,8 @@ services:
target: runner-base
image: omniroute:base
ports:
- "${DASHBOARD_PORT:-${PORT:-20128}}:${DASHBOARD_PORT:-${PORT:-20128}}"
- "${API_PORT:-20129}:${API_PORT:-20129}"
- "${PORT:-20128}:20128"
environment:
- DATA_DIR=/app/data
- PORT=${PORT:-20128}
- DASHBOARD_PORT=${DASHBOARD_PORT:-${PORT:-20128}}
- API_PORT=${API_PORT:-20129}
- API_HOST=${API_HOST:-0.0.0.0}
- CLI_MODE=host
- CLI_EXTRA_PATHS=/host-local/bin:/host-node/bin
- CLI_CONFIG_HOME=/host-home
-441
View File
@@ -1,441 +0,0 @@
# API Reference
🌐 **Languages:** 🇺🇸 [English](API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](i18n/es/API_REFERENCE.md) | 🇫🇷 [Français](i18n/fr/API_REFERENCE.md) | 🇮🇹 [Italiano](i18n/it/API_REFERENCE.md) | 🇷🇺 [Русский](i18n/ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](i18n/de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](i18n/in/API_REFERENCE.md) | 🇹🇭 [ไทย](i18n/th/API_REFERENCE.md) | 🇺🇦 [Українська](i18n/uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](i18n/ar/API_REFERENCE.md) | 🇯🇵 [日本語](i18n/ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/API_REFERENCE.md) | 🇧🇬 [Български](i18n/bg/API_REFERENCE.md) | 🇩🇰 [Dansk](i18n/da/API_REFERENCE.md) | 🇫🇮 [Suomi](i18n/fi/API_REFERENCE.md) | 🇮🇱 [עברית](i18n/he/API_REFERENCE.md) | 🇭🇺 [Magyar](i18n/hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/API_REFERENCE.md) | 🇰🇷 [한국어](i18n/ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](i18n/nl/API_REFERENCE.md) | 🇳🇴 [Norsk](i18n/no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/API_REFERENCE.md) | 🇷🇴 [Română](i18n/ro/API_REFERENCE.md) | 🇵🇱 [Polski](i18n/pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](i18n/sk/API_REFERENCE.md) | 🇸🇪 [Svenska](i18n/sv/API_REFERENCE.md) | 🇵🇭 [Filipino](i18n/phi/API_REFERENCE.md)
Complete reference for all OmniRoute API endpoints.
---
## Table of Contents
- [Chat Completions](#chat-completions)
- [Embeddings](#embeddings)
- [Image Generation](#image-generation)
- [List Models](#list-models)
- [Compatibility Endpoints](#compatibility-endpoints)
- [Semantic Cache](#semantic-cache)
- [Dashboard & Management](#dashboard--management)
- [Request Processing](#request-processing)
- [Authentication](#authentication)
---
## Chat Completions
```bash
POST /v1/chat/completions
Authorization: Bearer your-api-key
Content-Type: application/json
{
"model": "cc/claude-opus-4-6",
"messages": [
{"role": "user", "content": "Write a function to..."}
],
"stream": true
}
```
### Custom Headers
| Header | Direction | Description |
| ------------------------ | --------- | --------------------------------- |
| `X-OmniRoute-No-Cache` | Request | Set to `true` to bypass cache |
| `X-OmniRoute-Progress` | Request | Set to `true` for progress events |
| `Idempotency-Key` | Request | Dedup key (5s window) |
| `X-Request-Id` | Request | Alternative dedup key |
| `X-OmniRoute-Cache` | Response | `HIT` or `MISS` (non-streaming) |
| `X-OmniRoute-Idempotent` | Response | `true` if deduplicated |
| `X-OmniRoute-Progress` | Response | `enabled` if progress tracking on |
---
## Embeddings
```bash
POST /v1/embeddings
Authorization: Bearer your-api-key
Content-Type: application/json
{
"model": "nebius/Qwen/Qwen3-Embedding-8B",
"input": "The food was delicious"
}
```
Available providers: Nebius, OpenAI, Mistral, Together AI, Fireworks, NVIDIA.
```bash
# List all embedding models
GET /v1/embeddings
```
---
## Image Generation
```bash
POST /v1/images/generations
Authorization: Bearer your-api-key
Content-Type: application/json
{
"model": "openai/dall-e-3",
"prompt": "A beautiful sunset over mountains",
"size": "1024x1024"
}
```
Available providers: OpenAI (DALL-E), xAI (Grok Image), Together AI (FLUX), Fireworks AI.
```bash
# List all image models
GET /v1/images/generations
```
---
## List Models
```bash
GET /v1/models
Authorization: Bearer your-api-key
→ Returns all chat, embedding, and image models + combos in OpenAI format
```
---
## Compatibility Endpoints
| Method | Path | Format |
| ------ | --------------------------- | ---------------------- |
| POST | `/v1/chat/completions` | OpenAI |
| POST | `/v1/messages` | Anthropic |
| POST | `/v1/responses` | OpenAI Responses |
| POST | `/v1/embeddings` | OpenAI |
| POST | `/v1/images/generations` | OpenAI |
| GET | `/v1/models` | OpenAI |
| POST | `/v1/messages/count_tokens` | Anthropic |
| GET | `/v1beta/models` | Gemini |
| POST | `/v1beta/models/{...path}` | Gemini generateContent |
| POST | `/v1/api/chat` | Ollama |
### Dedicated Provider Routes
```bash
POST /v1/providers/{provider}/chat/completions
POST /v1/providers/{provider}/embeddings
POST /v1/providers/{provider}/images/generations
```
The provider prefix is auto-added if missing. Mismatched models return `400`.
---
## Semantic Cache
```bash
# Get cache stats
GET /api/cache
# Clear all caches
DELETE /api/cache
```
Response example:
```json
{
"semanticCache": {
"memorySize": 42,
"memoryMaxSize": 500,
"dbSize": 128,
"hitRate": 0.65
},
"idempotency": {
"activeKeys": 3,
"windowMs": 5000
}
}
```
---
## Dashboard & Management
### Authentication
| Endpoint | Method | Description |
| ----------------------------- | ------- | --------------------- |
| `/api/auth/login` | POST | Login |
| `/api/auth/logout` | POST | Logout |
| `/api/settings/require-login` | GET/PUT | Toggle login required |
### Provider Management
| Endpoint | Method | Description |
| ---------------------------- | --------------- | ------------------------ |
| `/api/providers` | GET/POST | List / create providers |
| `/api/providers/[id]` | GET/PUT/DELETE | Manage a provider |
| `/api/providers/[id]/test` | POST | Test provider connection |
| `/api/providers/[id]/models` | GET | List provider models |
| `/api/providers/validate` | POST | Validate provider config |
| `/api/provider-nodes*` | Various | Provider node management |
| `/api/provider-models` | GET/POST/DELETE | Custom models |
### OAuth Flows
| Endpoint | Method | Description |
| -------------------------------- | ------- | ----------------------- |
| `/api/oauth/[provider]/[action]` | Various | Provider-specific OAuth |
### Routing & Config
| Endpoint | Method | Description |
| --------------------- | -------- | ----------------------------- |
| `/api/models/alias` | GET/POST | Model aliases |
| `/api/models/catalog` | GET | All models by provider + type |
| `/api/combos*` | Various | Combo management |
| `/api/keys*` | Various | API key management |
| `/api/pricing` | GET | Model pricing |
### Usage & Analytics
| Endpoint | Method | Description |
| --------------------------- | ------ | -------------------- |
| `/api/usage/history` | GET | Usage history |
| `/api/usage/logs` | GET | Usage logs |
| `/api/usage/request-logs` | GET | Request-level logs |
| `/api/usage/[connectionId]` | GET | Per-connection usage |
### Settings
| Endpoint | Method | Description |
| ------------------------------- | ------- | ---------------------- |
| `/api/settings` | GET/PUT | General settings |
| `/api/settings/proxy` | GET/PUT | Network proxy config |
| `/api/settings/proxy/test` | POST | Test proxy connection |
| `/api/settings/ip-filter` | GET/PUT | IP allowlist/blocklist |
| `/api/settings/thinking-budget` | GET/PUT | Reasoning token budget |
| `/api/settings/system-prompt` | GET/PUT | Global system prompt |
### Monitoring
| Endpoint | Method | Description |
| ------------------------ | ---------- | ----------------------- |
| `/api/sessions` | GET | Active session tracking |
| `/api/rate-limits` | GET | Per-account rate limits |
| `/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 |
| ---------------------- | ------- | --------------------- |
| `/api/sync/cloud` | Various | Cloud sync operations |
| `/api/sync/initialize` | POST | Initialize sync |
| `/api/cloud/*` | Various | Cloud management |
### CLI Tools
| Endpoint | Method | Description |
| ---------------------------------- | ------ | ------------------- |
| `/api/cli-tools/claude-settings` | GET | Claude CLI status |
| `/api/cli-tools/codex-settings` | GET | Codex CLI status |
| `/api/cli-tools/droid-settings` | GET | Droid CLI status |
| `/api/cli-tools/openclaw-settings` | GET | OpenClaw CLI status |
| `/api/cli-tools/runtime/[toolId]` | GET | Generic CLI runtime |
CLI responses include: `installed`, `runnable`, `command`, `commandPath`, `runtimeMode`, `reason`.
### Resilience & Rate Limits
| Endpoint | Method | Description |
| ----------------------- | ------- | ------------------------------- |
| `/api/resilience` | GET/PUT | Get/update resilience profiles |
| `/api/resilience/reset` | POST | Reset circuit breakers |
| `/api/rate-limits` | GET | Per-account rate limit status |
| `/api/rate-limit` | GET | Global rate limit configuration |
### Evals
| Endpoint | Method | Description |
| ------------ | -------- | --------------------------------- |
| `/api/evals` | GET/POST | List eval suites / run evaluation |
### Policies
| Endpoint | Method | Description |
| --------------- | --------------- | ----------------------- |
| `/api/policies` | GET/POST/DELETE | Manage routing policies |
### Compliance
| Endpoint | Method | Description |
| --------------------------- | ------ | ----------------------------- |
| `/api/compliance/audit-log` | GET | Compliance audit log (last N) |
### v1beta (Gemini-Compatible)
| Endpoint | Method | Description |
| -------------------------- | ------ | --------------------------------- |
| `/v1beta/models` | GET | List models in Gemini format |
| `/v1beta/models/{...path}` | POST | Gemini `generateContent` endpoint |
These endpoints mirror Gemini's API format for clients that expect native Gemini SDK compatibility.
### Internal / System APIs
| Endpoint | Method | Description |
| --------------- | ------ | ---------------------------------------------------- |
| `/api/init` | GET | Application initialization check (used on first run) |
| `/api/tags` | GET | Ollama-compatible model tags (for Ollama clients) |
| `/api/restart` | POST | Trigger graceful server restart |
| `/api/shutdown` | POST | Trigger graceful server shutdown |
> **Note:** These endpoints are used internally by the system or for Ollama client compatibility. They are not typically called by end users.
---
## Audio Transcription
```bash
POST /v1/audio/transcriptions
Authorization: Bearer your-api-key
Content-Type: multipart/form-data
```
Transcribe audio files using Deepgram or AssemblyAI.
**Request:**
```bash
curl -X POST http://localhost:20128/v1/audio/transcriptions \
-H "Authorization: Bearer your-api-key" \
-F "file=@recording.mp3" \
-F "model=deepgram/nova-3"
```
**Response:**
```json
{
"text": "Hello, this is the transcribed audio content.",
"task": "transcribe",
"language": "en",
"duration": 12.5
}
```
**Supported providers:** `deepgram/nova-3`, `assemblyai/best`.
**Supported formats:** `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`.
---
## Ollama Compatibility
For clients that use Ollama's API format:
```bash
# Chat endpoint (Ollama format)
POST /v1/api/chat
# Model listing (Ollama format)
GET /api/tags
```
Requests are automatically translated between Ollama and internal formats.
---
## Telemetry
```bash
# Get latency telemetry summary (p50/p95/p99 per provider)
GET /api/telemetry/summary
```
**Response:**
```json
{
"providers": {
"claudeCode": { "p50": 245, "p95": 890, "p99": 1200, "count": 150 },
"github": { "p50": 180, "p95": 620, "p99": 950, "count": 320 }
}
}
```
---
## Budget
```bash
# Get budget status for all API keys
GET /api/usage/budget
# Set or update a budget
POST /api/usage/budget
Content-Type: application/json
{
"keyId": "key-123",
"limit": 50.00,
"period": "monthly"
}
```
---
## Model Availability
```bash
# Get real-time model availability across all providers
GET /api/models/availability
# Check availability for a specific model
POST /api/models/availability
Content-Type: application/json
{
"model": "claude-sonnet-4-5-20250929"
}
```
---
## Request Processing
1. Client sends request to `/v1/*`
2. Route handler calls `handleChat`, `handleEmbedding`, `handleAudioTranscription`, or `handleImageGeneration`
3. Model is resolved (direct provider/model or alias/combo)
4. Credentials selected from local DB with account availability filtering
5. For chat: `handleChatCore` — format detection, translation, cache check, idempotency check
6. Provider executor sends upstream request
7. Response translated back to client format (chat) or returned as-is (embeddings/images/audio)
8. Usage/logging recorded
9. Fallback applies on errors according to combo rules
Full architecture reference: [`ARCHITECTURE.md`](ARCHITECTURE.md)
---
## Authentication
- Dashboard routes (`/dashboard/*`) use `auth_token` cookie
- Login uses saved password hash; fallback to `INITIAL_PASSWORD`
- `requireLogin` toggleable via `/api/settings/require-login`
- `/v1/*` routes optionally require Bearer API key when `REQUIRE_API_KEY=true`
+84 -102
View File
@@ -1,8 +1,6 @@
# OmniRoute Architecture
🌐 **Languages:** 🇺🇸 [English](ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](i18n/es/ARCHITECTURE.md) | 🇫🇷 [Français](i18n/fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](i18n/it/ARCHITECTURE.md) | 🇷🇺 [Русский](i18n/ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](i18n/de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](i18n/in/ARCHITECTURE.md) | 🇹🇭 [ไทย](i18n/th/ARCHITECTURE.md) | 🇺🇦 [Українська](i18n/uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](i18n/ar/ARCHITECTURE.md) | 🇯🇵 [日本語](i18n/ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/ARCHITECTURE.md) | 🇧🇬 [Български](i18n/bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](i18n/da/ARCHITECTURE.md) | 🇫🇮 [Suomi](i18n/fi/ARCHITECTURE.md) | 🇮🇱 [עברית](i18n/he/ARCHITECTURE.md) | 🇭🇺 [Magyar](i18n/hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/ARCHITECTURE.md) | 🇰🇷 [한국어](i18n/ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](i18n/nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](i18n/no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/ARCHITECTURE.md) | 🇷🇴 [Română](i18n/ro/ARCHITECTURE.md) | 🇵🇱 [Polski](i18n/pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](i18n/sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](i18n/sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](i18n/phi/ARCHITECTURE.md)
_Last updated: 2026-02-18_
_Last updated: 2026-02-15_
## Executive Summary
@@ -19,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
@@ -125,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:
@@ -171,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`)
@@ -334,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
@@ -366,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)
@@ -415,9 +408,6 @@ erDiagram
number stickyRoundRobinLimit
boolean requireLogin
string password_hash
string fallbackStrategy
json rateLimitDefaults
json providerProfiles
}
PROVIDER_CONNECTION {
@@ -569,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 |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
@@ -654,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 |
@@ -685,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
@@ -731,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`
@@ -763,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 -48
View File
@@ -1,7 +1,5 @@
# omniroute — Codebase Documentation
🌐 **Languages:** 🇺🇸 [English](CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](i18n/es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](i18n/fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](i18n/it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](i18n/ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](i18n/de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](i18n/in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](i18n/th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](i18n/uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](i18n/ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](i18n/ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](i18n/vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](i18n/bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](i18n/da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](i18n/fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](i18n/he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](i18n/hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](i18n/ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](i18n/nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](i18n/no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](i18n/pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](i18n/ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](i18n/pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](i18n/sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](i18n/sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](i18n/phi/CODEBASE_DOCUMENTATION.md)
> A comprehensive, beginner-friendly guide to the **omniroute** multi-provider AI proxy router.
---
@@ -112,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"]
@@ -196,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 |
---
@@ -214,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
@@ -264,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
@@ -379,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
@@ -399,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
@@ -452,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.) |
@@ -486,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
-93
View File
@@ -1,93 +0,0 @@
# OmniRoute — Dashboard Features Gallery
🌐 **Languages:** 🇺🇸 [English](FEATURES.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/FEATURES.md) | 🇪🇸 [Español](i18n/es/FEATURES.md) | 🇫🇷 [Français](i18n/fr/FEATURES.md) | 🇮🇹 [Italiano](i18n/it/FEATURES.md) | 🇷🇺 [Русский](i18n/ru/FEATURES.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](i18n/de/FEATURES.md) | 🇮🇳 [हिन्दी](i18n/in/FEATURES.md) | 🇹🇭 [ไทย](i18n/th/FEATURES.md) | 🇺🇦 [Українська](i18n/uk-UA/FEATURES.md) | 🇸🇦 [العربية](i18n/ar/FEATURES.md) | 🇯🇵 [日本語](i18n/ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](i18n/vi/FEATURES.md) | 🇧🇬 [Български](i18n/bg/FEATURES.md) | 🇩🇰 [Dansk](i18n/da/FEATURES.md) | 🇫🇮 [Suomi](i18n/fi/FEATURES.md) | 🇮🇱 [עברית](i18n/he/FEATURES.md) | 🇭🇺 [Magyar](i18n/hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/FEATURES.md) | 🇰🇷 [한국어](i18n/ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/FEATURES.md) | 🇳🇱 [Nederlands](i18n/nl/FEATURES.md) | 🇳🇴 [Norsk](i18n/no/FEATURES.md) | 🇵🇹 [Português (Portugal)](i18n/pt/FEATURES.md) | 🇷🇴 [Română](i18n/ro/FEATURES.md) | 🇵🇱 [Polski](i18n/pl/FEATURES.md) | 🇸🇰 [Slovenčina](i18n/sk/FEATURES.md) | 🇸🇪 [Svenska](i18n/sv/FEATURES.md) | 🇵🇭 [Filipino](i18n/phi/FEATURES.md)
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 (model aliases, background task degradation), resilience (rate limit persistence), 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, Antigravity, and **GitHub Copilot** (config generator for `chatLanguageModels.json`).
![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)
---
## 🖥️ Desktop Application
Native Electron desktop app for Windows, macOS, and Linux. Run OmniRoute as a standalone application with system tray integration, offline support, and one-click install.
Key features:
- Server readiness polling (no blank screen on cold start)
- System tray with port management
- Content Security Policy
- Single-instance lock
- Platform-conditional UI (macOS traffic lights, Windows/Linux default titlebar)
📖 See [`electron/README.md`](../electron/README.md) for full documentation.
+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
-254
View File
@@ -1,254 +0,0 @@
# Troubleshooting
🌐 **Languages:** 🇺🇸 [English](TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](i18n/es/TROUBLESHOOTING.md) | 🇫🇷 [Français](i18n/fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](i18n/it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](i18n/ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](i18n/de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](i18n/in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](i18n/th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](i18n/uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](i18n/ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](i18n/ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](i18n/vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](i18n/bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](i18n/da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](i18n/fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](i18n/he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](i18n/hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](i18n/ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](i18n/nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](i18n/no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](i18n/pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](i18n/ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](i18n/pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](i18n/sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](i18n/sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](i18n/phi/TROUBLESHOOTING.md)
Common problems and solutions for OmniRoute.
---
## Quick Fixes
| Problem | Solution |
| ----------------------------- | ------------------------------------------------------------------ |
| First login not working | Check `INITIAL_PASSWORD` in `.env` (default: `123456`) |
| Dashboard opens on wrong port | Set `PORT=20128` and `NEXT_PUBLIC_BASE_URL=http://localhost:20128` |
| No request logs under `logs/` | Set `ENABLE_REQUEST_LOGS=true` |
| EACCES: permission denied | Set `DATA_DIR=/path/to/writable/dir` to override `~/.omniroute` |
| Routing strategy not saving | Update to v1.4.11+ (Zod schema fix for settings persistence) |
---
## Provider Issues
### "Language model did not provide messages"
**Cause:** Provider quota exhausted.
**Fix:**
1. Check dashboard quota tracker
2. Use a combo with fallback tiers
3. Switch to cheaper/free tier
### Rate Limiting
**Cause:** Subscription quota exhausted.
**Fix:**
- Add fallback: `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
- Use GLM/MiniMax as cheap backup
### OAuth Token Expired
OmniRoute auto-refreshes tokens. If issues persist:
1. Dashboard → Provider → Reconnect
2. Delete and re-add the provider connection
---
## Cloud Issues
### Cloud Sync Errors
1. Verify `BASE_URL` points to your running instance (e.g., `http://localhost:20128`)
2. Verify `CLOUD_URL` points to your cloud endpoint (e.g., `https://omniroute.dev`)
3. Keep `NEXT_PUBLIC_*` values aligned with server-side values
### Cloud `stream=false` Returns 500
**Symptom:** `Unexpected token 'd'...` on cloud endpoint for non-streaming calls.
**Cause:** Upstream returns SSE payload while client expects JSON.
**Workaround:** Use `stream=true` for cloud direct calls. Local runtime includes SSE→JSON fallback.
### Cloud Says Connected but "Invalid API key"
1. Create a fresh key from local dashboard (`/api/keys`)
2. Run cloud sync: Enable Cloud → Sync Now
3. Old/non-synced keys can still return `401` on cloud
---
## Docker Issues
### CLI Tool Shows Not Installed
1. Check runtime fields: `curl http://localhost:20128/api/cli-tools/runtime/codex | jq`
2. For portable mode: use image target `runner-cli` (bundled CLIs)
3. For host mount mode: set `CLI_EXTRA_PATHS` and mount host bin directory as read-only
4. If `installed=true` and `runnable=false`: binary was found but failed healthcheck
### Quick Runtime Validation
```bash
curl -s http://localhost:20128/api/cli-tools/codex-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
curl -s http://localhost:20128/api/cli-tools/claude-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
curl -s http://localhost:20128/api/cli-tools/openclaw-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
```
---
## Cost Issues
### High Costs
1. Check usage stats in Dashboard → Usage
2. Switch primary model to GLM/MiniMax
3. Use free tier (Gemini CLI, iFlow) for non-critical tasks
4. Set cost budgets per API key: Dashboard → API Keys → Budget
---
## Debugging
### Enable Request Logs
Set `ENABLE_REQUEST_LOGS=true` in your `.env` file. Logs appear under `logs/` directory.
### Check Provider Health
```bash
# Health dashboard
http://localhost:20128/dashboard/health
# API health check
curl http://localhost:20128/api/monitoring/health
```
### Runtime Storage
- Main state: `${DATA_DIR}/db.json` (providers, combos, aliases, keys, settings)
- Usage: `${DATA_DIR}/usage.json`, `${DATA_DIR}/log.txt`, `${DATA_DIR}/call_logs/`
- Request logs: `<repo>/logs/...` (when `ENABLE_REQUEST_LOGS=true`)
---
## Circuit Breaker Issues
### Provider stuck in OPEN state
When a provider's circuit breaker is OPEN, requests are blocked until the cooldown expires.
**Fix:**
1. Go to **Dashboard → Settings → Resilience**
2. Check the circuit breaker card for the affected provider
3. Click **Reset All** to clear all breakers, or wait for the cooldown to expire
4. Verify the provider is actually available before resetting
### Provider keeps tripping the circuit breaker
If a provider repeatedly enters OPEN state:
1. Check **Dashboard → Health → Provider Health** for the failure pattern
2. Go to **Settings → Resilience → Provider Profiles** and increase the failure threshold
3. Check if the provider has changed API limits or requires re-authentication
4. Review latency telemetry — high latency may cause timeout-based failures
---
## Audio Transcription Issues
### "Unsupported model" error
- Ensure you're using the correct prefix: `deepgram/nova-3` or `assemblyai/best`
- Verify the provider is connected in **Dashboard → Providers**
### Transcription returns empty or fails
- Check supported audio formats: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`
- Verify file size is within provider limits (typically < 25MB)
- Check provider API key validity in the provider card
---
## Translator Debugging
Use **Dashboard → Translator** to debug format translation issues:
| Mode | When to Use |
| ---------------- | -------------------------------------------------------------------------------------------- |
| **Playground** | Compare input/output formats side by side — paste a failing request to see how it translates |
| **Chat Tester** | Send live messages and inspect the full request/response payload including headers |
| **Test Bench** | Run batch tests across format combinations to find which translations are broken |
| **Live Monitor** | Watch real-time request flow to catch intermittent translation issues |
### Common format 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`
---
## Resilience Settings
### Auto rate-limit not triggering
- Auto rate-limit only applies to API key providers (not OAuth/subscription)
- Verify **Settings → Resilience → Provider Profiles** has auto-rate-limit enabled
- Check if the provider returns `429` status codes or `Retry-After` headers
### Tuning exponential backoff
Provider profiles support these settings:
- **Base delay** — Initial wait time after first failure (default: 1s)
- **Max delay** — Maximum wait time cap (default: 30s)
- **Multiplier** — How much to increase delay per consecutive failure (default: 2x)
### Anti-thundering herd
When many concurrent requests hit a rate-limited provider, OmniRoute uses mutex + auto rate-limiting to serialize requests and prevent cascading failures. This is automatic for API key providers.
---
## Optional RAG / LLM failure taxonomy (16 problems)
Some OmniRoute users place the gateway in front of RAG or agent stacks. In those setups it is common to see a strange pattern: OmniRoute looks healthy (providers up, routing profiles ok, no rate limit alerts) but the final answer is still wrong.
In practice these incidents usually come from the downstream RAG pipeline, not from the gateway itself.
If you want a shared vocabulary to describe those failures you can use the WFGY ProblemMap, an external MIT license text resource that defines sixteen recurring RAG / LLM failure patterns. At a high level it covers:
- retrieval drift and broken context boundaries
- empty or stale indexes and vector stores
- embedding versus semantic mismatch
- prompt assembly and context window issues
- logic collapse and overconfident answers
- long chain and agent coordination failures
- multi agent memory and role drift
- deployment and bootstrap ordering problems
The idea is simple:
1. When you investigate a bad response, capture:
- user task and request
- route or provider combo in OmniRoute
- any RAG context used downstream (retrieved documents, tool calls, etc)
2. Map the incident to one or two WFGY ProblemMap numbers (`No.1``No.16`).
3. Store the number in your own dashboard, runbook, or incident tracker next to the OmniRoute logs.
4. Use the corresponding WFGY page to decide whether you need to change your RAG stack, retriever, or routing strategy.
Full text and concrete recipes live here (MIT license, text only):
[WFGY ProblemMap README](https://github.com/onestardao/WFGY/blob/main/ProblemMap/README.md)
You can ignore this section if you do not run RAG or agent pipelines behind OmniRoute.
---
## Still Stuck?
- **GitHub Issues**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
- **Architecture**: See [`docs/ARCHITECTURE.md`](ARCHITECTURE.md) for internal details
- **API Reference**: See [`docs/API_REFERENCE.md`](API_REFERENCE.md) for all endpoints
- **Health Dashboard**: Check **Dashboard → Health** for real-time system status
- **Translator**: Use **Dashboard → Translator** to debug format issues
-809
View File
@@ -1,809 +0,0 @@
# User Guide
🌐 **Languages:** 🇺🇸 [English](USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](i18n/es/USER_GUIDE.md) | 🇫🇷 [Français](i18n/fr/USER_GUIDE.md) | 🇮🇹 [Italiano](i18n/it/USER_GUIDE.md) | 🇷🇺 [Русский](i18n/ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](i18n/de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](i18n/in/USER_GUIDE.md) | 🇹🇭 [ไทย](i18n/th/USER_GUIDE.md) | 🇺🇦 [Українська](i18n/uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](i18n/ar/USER_GUIDE.md) | 🇯🇵 [日本語](i18n/ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/USER_GUIDE.md) | 🇧🇬 [Български](i18n/bg/USER_GUIDE.md) | 🇩🇰 [Dansk](i18n/da/USER_GUIDE.md) | 🇫🇮 [Suomi](i18n/fi/USER_GUIDE.md) | 🇮🇱 [עברית](i18n/he/USER_GUIDE.md) | 🇭🇺 [Magyar](i18n/hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/USER_GUIDE.md) | 🇰🇷 [한국어](i18n/ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](i18n/nl/USER_GUIDE.md) | 🇳🇴 [Norsk](i18n/no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/USER_GUIDE.md) | 🇷🇴 [Română](i18n/ro/USER_GUIDE.md) | 🇵🇱 [Polski](i18n/pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](i18n/sk/USER_GUIDE.md) | 🇸🇪 [Svenska](i18n/sv/USER_GUIDE.md) | 🇵🇭 [Filipino](i18n/phi/USER_GUIDE.md)
Complete guide for configuring providers, creating combos, integrating CLI tools, and deploying OmniRoute.
---
## Table of Contents
- [Pricing at a Glance](#-pricing-at-a-glance)
- [Use Cases](#-use-cases)
- [Provider Setup](#-provider-setup)
- [CLI Integration](#-cli-integration)
- [Deployment](#-deployment)
- [Available Models](#-available-models)
- [Advanced Features](#-advanced-features)
---
## 💰 Pricing at a Glance
| Tier | Provider | Cost | Quota Reset | Best For |
| ------------------- | ----------------- | ----------- | ---------------- | -------------------- |
| **💳 SUBSCRIPTION** | Claude Code (Pro) | $20/mo | 5h + weekly | Already subscribed |
| | Codex (Plus/Pro) | $20-200/mo | 5h + weekly | OpenAI users |
| | Gemini CLI | **FREE** | 180K/mo + 1K/day | Everyone! |
| | GitHub Copilot | $10-19/mo | Monthly | GitHub users |
| **🔑 API KEY** | DeepSeek | Pay per use | None | Cheap reasoning |
| | Groq | Pay per use | None | Ultra-fast inference |
| | xAI (Grok) | Pay per use | None | Grok 4 reasoning |
| | Mistral | Pay per use | None | EU-hosted models |
| | Perplexity | Pay per use | None | Search-augmented |
| | Together AI | Pay per use | None | Open-source models |
| | Fireworks AI | Pay per use | None | Fast FLUX images |
| | Cerebras | Pay per use | None | Wafer-scale speed |
| | Cohere | Pay per use | None | Command R+ RAG |
| | NVIDIA NIM | Pay per use | None | Enterprise models |
| **💰 CHEAP** | GLM-4.7 | $0.6/1M | Daily 10AM | Budget backup |
| | MiniMax M2.1 | $0.2/1M | 5-hour rolling | Cheapest option |
| | Kimi K2 | $9/mo flat | 10M tokens/mo | Predictable cost |
| **🆓 FREE** | iFlow | $0 | Unlimited | 8 models free |
| | Qwen | $0 | Unlimited | 3 models free |
| | Kiro | $0 | Unlimited | Claude free |
**💡 Pro Tip:** Start with Gemini CLI (180K free/month) + iFlow (unlimited free) combo = $0 cost!
---
## 🎯 Use Cases
### Case 1: "I have Claude Pro subscription"
**Problem:** Quota expires unused, rate limits during heavy coding
```
Combo: "maximize-claude"
1. cc/claude-opus-4-6 (use subscription fully)
2. glm/glm-4.7 (cheap backup when quota out)
3. if/kimi-k2-thinking (free emergency fallback)
Monthly cost: $20 (subscription) + ~$5 (backup) = $25 total
vs. $20 + hitting limits = frustration
```
### Case 2: "I want zero cost"
**Problem:** Can't afford subscriptions, need reliable AI coding
```
Combo: "free-forever"
1. gc/gemini-3-flash (180K free/month)
2. if/kimi-k2-thinking (unlimited free)
3. qw/qwen3-coder-plus (unlimited free)
Monthly cost: $0
Quality: Production-ready models
```
### Case 3: "I need 24/7 coding, no interruptions"
**Problem:** Deadlines, can't afford downtime
```
Combo: "always-on"
1. cc/claude-opus-4-6 (best quality)
2. cx/gpt-5.2-codex (second subscription)
3. glm/glm-4.7 (cheap, resets daily)
4. minimax/MiniMax-M2.1 (cheapest, 5h reset)
5. if/kimi-k2-thinking (free unlimited)
Result: 5 layers of fallback = zero downtime
Monthly cost: $20-200 (subscriptions) + $10-20 (backup)
```
### Case 4: "I want FREE AI in OpenClaw"
**Problem:** Need AI assistant in messaging apps, completely free
```
Combo: "openclaw-free"
1. if/glm-4.7 (unlimited free)
2. if/minimax-m2.1 (unlimited free)
3. if/kimi-k2-thinking (unlimited free)
Monthly cost: $0
Access via: WhatsApp, Telegram, Slack, Discord, iMessage, Signal...
```
---
## 📖 Provider Setup
### 🔐 Subscription Providers
#### Claude Code (Pro/Max)
```bash
Dashboard → Providers → Connect Claude Code
→ OAuth login → Auto token refresh
→ 5-hour + weekly quota tracking
Models:
cc/claude-opus-4-6
cc/claude-sonnet-4-5-20250929
cc/claude-haiku-4-5-20251001
```
**Pro Tip:** Use Opus for complex tasks, Sonnet for speed. OmniRoute tracks quota per model!
#### OpenAI Codex (Plus/Pro)
```bash
Dashboard → Providers → Connect Codex
→ OAuth login (port 1455)
→ 5-hour + weekly reset
Models:
cx/gpt-5.2-codex
cx/gpt-5.1-codex-max
```
#### Gemini CLI (FREE 180K/month!)
```bash
Dashboard → Providers → Connect Gemini CLI
→ Google OAuth
→ 180K completions/month + 1K/day
Models:
gc/gemini-3-flash-preview
gc/gemini-2.5-pro
```
**Best Value:** Huge free tier! Use this before paid tiers.
#### GitHub Copilot
```bash
Dashboard → Providers → Connect GitHub
→ OAuth via GitHub
→ Monthly reset (1st of month)
Models:
gh/gpt-5
gh/claude-4.5-sonnet
gh/gemini-3-pro
```
### 💰 Cheap Providers
#### GLM-4.7 (Daily reset, $0.6/1M)
1. Sign up: [Zhipu AI](https://open.bigmodel.cn/)
2. Get API key from Coding Plan
3. Dashboard → Add API Key: Provider: `glm`, API Key: `your-key`
**Use:** `glm/glm-4.7`**Pro Tip:** Coding Plan offers 3× quota at 1/7 cost! Reset daily 10:00 AM.
#### MiniMax M2.1 (5h reset, $0.20/1M)
1. Sign up: [MiniMax](https://www.minimax.io/)
2. Get API key → Dashboard → Add API Key
**Use:** `minimax/MiniMax-M2.1`**Pro Tip:** Cheapest option for long context (1M tokens)!
#### Kimi K2 ($9/month flat)
1. Subscribe: [Moonshot AI](https://platform.moonshot.ai/)
2. Get API key → Dashboard → Add API Key
**Use:** `kimi/kimi-latest`**Pro Tip:** Fixed $9/month for 10M tokens = $0.90/1M effective cost!
### 🆓 FREE Providers
#### iFlow (8 FREE models)
```bash
Dashboard → Connect iFlow → OAuth login → Unlimited usage
Models: if/kimi-k2-thinking, if/qwen3-coder-plus, if/glm-4.7, if/minimax-m2, if/deepseek-r1
```
#### Qwen (3 FREE models)
```bash
Dashboard → Connect Qwen → Device code auth → Unlimited usage
Models: qw/qwen3-coder-plus, qw/qwen3-coder-flash
```
#### Kiro (Claude FREE)
```bash
Dashboard → Connect Kiro → AWS Builder ID or Google/GitHub → Unlimited
Models: kr/claude-sonnet-4.5, kr/claude-haiku-4.5
```
---
## 🎨 Combos
### Example 1: Maximize Subscription → Cheap Backup
```
Dashboard → Combos → Create New
Name: premium-coding
Models:
1. cc/claude-opus-4-6 (Subscription primary)
2. glm/glm-4.7 (Cheap backup, $0.6/1M)
3. minimax/MiniMax-M2.1 (Cheapest fallback, $0.20/1M)
Use in CLI: premium-coding
```
### Example 2: Free-Only (Zero Cost)
```
Name: free-combo
Models:
1. gc/gemini-3-flash-preview (180K free/month)
2. if/kimi-k2-thinking (unlimited)
3. qw/qwen3-coder-plus (unlimited)
Cost: $0 forever!
```
---
## 🔧 CLI Integration
### Cursor IDE
```
Settings → Models → Advanced:
OpenAI API Base URL: http://localhost:20128/v1
OpenAI API Key: [from omniroute dashboard]
Model: cc/claude-opus-4-6
```
### Claude Code
Edit `~/.claude/config.json`:
```json
{
"anthropic_api_base": "http://localhost:20128/v1",
"anthropic_api_key": "your-omniroute-api-key"
}
```
### Codex CLI
```bash
export OPENAI_BASE_URL="http://localhost:20128"
export OPENAI_API_KEY="your-omniroute-api-key"
codex "your prompt"
```
### OpenClaw
Edit `~/.openclaw/openclaw.json`:
```json
{
"agents": {
"defaults": {
"model": { "primary": "omniroute/if/glm-4.7" }
}
},
"models": {
"providers": {
"omniroute": {
"baseUrl": "http://localhost:20128/v1",
"apiKey": "your-omniroute-api-key",
"api": "openai-completions",
"models": [{ "id": "if/glm-4.7", "name": "glm-4.7" }]
}
}
}
}
```
**Or use Dashboard:** CLI Tools → OpenClaw → Auto-config
### Cline / Continue / RooCode
```
Provider: OpenAI Compatible
Base URL: http://localhost:20128/v1
API Key: [from dashboard]
Model: cc/claude-opus-4-6
```
---
## 🚀 Deployment
### Global npm install (Recommended)
```bash
npm install -g omniroute
# Create config directory
mkdir -p ~/.omniroute
# Create .env file (see .env.example)
cp .env.example ~/.omniroute/.env
# Start server
omniroute
# Or with custom port:
omniroute --port 3000
```
The CLI automatically loads `.env` from `~/.omniroute/.env` or `./.env`.
### VPS Deployment
```bash
git clone https://github.com/diegosouzapw/OmniRoute.git
cd OmniRoute && npm install && npm run build
export JWT_SECRET="your-secure-secret-change-this"
export INITIAL_PASSWORD="your-password"
export DATA_DIR="/var/lib/omniroute"
export PORT="20128"
export HOSTNAME="0.0.0.0"
export NODE_ENV="production"
export NEXT_PUBLIC_BASE_URL="http://localhost:20128"
export API_KEY_SECRET="endpoint-proxy-api-key-secret"
npm run start
# Or: pm2 start npm --name omniroute -- start
```
### PM2 Deployment (Low Memory)
For servers with limited RAM, use the memory limit option:
```bash
# With 512MB limit (default)
pm2 start npm --name omniroute -- start
# Or with custom memory limit
OMNIROUTE_MEMORY_MB=512 pm2 start npm --name omniroute -- start
# Or using ecosystem.config.js
pm2 start ecosystem.config.js
```
Create `ecosystem.config.js`:
```javascript
module.exports = {
apps: [
{
name: "omniroute",
script: "npm",
args: "start",
env: {
NODE_ENV: "production",
OMNIROUTE_MEMORY_MB: "512",
JWT_SECRET: "your-secret",
INITIAL_PASSWORD: "your-password",
},
node_args: "--max-old-space-size=512",
max_memory_restart: "300M",
},
],
};
```
### Docker
```bash
# Build image (default = runner-cli with codex/claude/droid preinstalled)
docker build -t omniroute:cli .
# Portable mode (recommended)
docker run -d --name omniroute -p 20128:20128 --env-file ./.env -v omniroute-data:/app/data omniroute:cli
```
For host-integrated mode with CLI binaries, see the Docker section in the main docs.
### Environment Variables
| Variable | Default | Description |
| ------------------------- | ------------------------------------ | ------------------------------------------------------- |
| `JWT_SECRET` | `omniroute-default-secret-change-me` | JWT signing secret (**change in production**) |
| `INITIAL_PASSWORD` | `123456` | First login password |
| `DATA_DIR` | `~/.omniroute` | Data directory (db, usage, logs) |
| `PORT` | framework default | Service port (`20128` in examples) |
| `HOSTNAME` | framework default | Bind host (Docker defaults to `0.0.0.0`) |
| `NODE_ENV` | runtime default | Set `production` for deploy |
| `BASE_URL` | `http://localhost:20128` | Server-side internal base URL |
| `CLOUD_URL` | `https://omniroute.dev` | Cloud sync endpoint base URL |
| `API_KEY_SECRET` | `endpoint-proxy-api-key-secret` | HMAC secret for generated API keys |
| `REQUIRE_API_KEY` | `false` | Enforce Bearer API key on `/v1/*` |
| `ENABLE_REQUEST_LOGS` | `false` | Enables request/response logs |
| `AUTH_COOKIE_SECURE` | `false` | Force `Secure` auth cookie (behind HTTPS reverse proxy) |
| `OMNIROUTE_MEMORY_MB` | `512` | Node.js heap limit in MB |
| `PROMPT_CACHE_MAX_SIZE` | `50` | Max prompt cache entries |
| `SEMANTIC_CACHE_MAX_SIZE` | `100` | Max semantic cache entries |
For the full environment variable reference, see the [README](../README.md).
---
## 📊 Available Models
<details>
<summary><b>View all available models</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/`)** — FREE: `gc/gemini-3-flash-preview`, `gc/gemini-2.5-pro`
**GitHub Copilot (`gh/`)**: `gh/gpt-5`, `gh/claude-4.5-sonnet`
**GLM (`glm/`)** — $0.6/1M: `glm/glm-4.7`
**MiniMax (`minimax/`)** — $0.2/1M: `minimax/MiniMax-M2.1`
**iFlow (`if/`)** — FREE: `if/kimi-k2-thinking`, `if/qwen3-coder-plus`, `if/deepseek-r1`
**Qwen (`qw/`)** — FREE: `qw/qwen3-coder-plus`, `qw/qwen3-coder-flash`
**Kiro (`kr/`)** — FREE: `kr/claude-sonnet-4.5`, `kr/claude-haiku-4.5`
**DeepSeek (`ds/`)**: `ds/deepseek-chat`, `ds/deepseek-reasoner`
**Groq (`groq/`)**: `groq/llama-3.3-70b-versatile`, `groq/llama-4-maverick-17b-128e-instruct`
**xAI (`xai/`)**: `xai/grok-4`, `xai/grok-4-0709-fast-reasoning`, `xai/grok-code-mini`
**Mistral (`mistral/`)**: `mistral/mistral-large-2501`, `mistral/codestral-2501`
**Perplexity (`pplx/`)**: `pplx/sonar-pro`, `pplx/sonar`
**Together AI (`together/`)**: `together/meta-llama/Llama-3.3-70B-Instruct-Turbo`
**Fireworks AI (`fireworks/`)**: `fireworks/accounts/fireworks/models/deepseek-v3p1`
**Cerebras (`cerebras/`)**: `cerebras/llama-3.3-70b`
**Cohere (`cohere/`)**: `cohere/command-r-plus-08-2024`
**NVIDIA NIM (`nvidia/`)**: `nvidia/nvidia/llama-3.3-70b-instruct`
</details>
---
## 🧩 Advanced Features
### Custom Models
Add any model ID to any provider without waiting for an app update:
```bash
# Via API
curl -X POST http://localhost:20128/api/provider-models \
-H "Content-Type: application/json" \
-d '{"provider": "openai", "modelId": "gpt-4.5-preview", "modelName": "GPT-4.5 Preview"}'
# List: curl http://localhost:20128/api/provider-models?provider=openai
# Remove: curl -X DELETE "http://localhost:20128/api/provider-models?provider=openai&model=gpt-4.5-preview"
```
Or use Dashboard: **Providers → [Provider] → Custom Models**.
### Dedicated Provider Routes
Route requests directly to a specific provider with model validation:
```bash
POST http://localhost:20128/v1/providers/openai/chat/completions
POST http://localhost:20128/v1/providers/openai/embeddings
POST http://localhost:20128/v1/providers/fireworks/images/generations
```
The provider prefix is auto-added if missing. Mismatched models return `400`.
### Network Proxy Configuration
```bash
# Set global proxy
curl -X PUT http://localhost:20128/api/settings/proxy \
-d '{"global": {"type":"http","host":"proxy.example.com","port":"8080"}}'
# Per-provider proxy
curl -X PUT http://localhost:20128/api/settings/proxy \
-d '{"providers": {"openai": {"type":"socks5","host":"proxy.example.com","port":"1080"}}}'
# Test proxy
curl -X POST http://localhost:20128/api/settings/proxy/test \
-d '{"proxy":{"type":"socks5","host":"proxy.example.com","port":"1080"}}'
```
**Precedence:** Key-specific → Combo-specific → Provider-specific → Global → Environment.
### Model Catalog API
```bash
curl http://localhost:20128/api/models/catalog
```
Returns models grouped by provider with types (`chat`, `embedding`, `image`).
### Cloud Sync
- Sync providers, combos, and settings across devices
- Automatic background sync with timeout + fail-fast
- Prefer server-side `BASE_URL`/`CLOUD_URL` in production
### LLM Gateway Intelligence (Phase 9)
- **Semantic Cache** — Auto-caches non-streaming, temperature=0 responses (bypass with `X-OmniRoute-No-Cache: true`)
- **Request Idempotency** — Deduplicates requests within 5s via `Idempotency-Key` or `X-Request-Id` header
- **Progress Tracking** — Opt-in SSE `event: progress` events via `X-OmniRoute-Progress: true` header
---
### Translator Playground
Access via **Dashboard → Translator**. Debug and visualize how OmniRoute translates API requests between providers.
| Mode | Purpose |
| ---------------- | -------------------------------------------------------------------------------------- |
| **Playground** | Select source/target formats, paste a request, and see the translated output instantly |
| **Chat Tester** | Send live chat messages through the proxy and inspect the full request/response cycle |
| **Test Bench** | Run batch tests across multiple format combinations to verify translation correctness |
| **Live Monitor** | Watch real-time translations as requests flow through the proxy |
**Use cases:**
- Debug why a specific client/provider combination fails
- Verify that thinking tags, tool calls, and system prompts translate correctly
- Compare format differences between OpenAI, Claude, Gemini, and Responses API formats
---
### Routing Strategies
Configure via **Dashboard → Settings → Routing**.
| Strategy | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------ |
| **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
Create wildcard patterns to remap model names:
```
Pattern: claude-sonnet-* → Target: cc/claude-sonnet-4-5-20250929
Pattern: gpt-* → Target: gh/gpt-5.1-codex
```
Wildcards support `*` (any characters) and `?` (single character).
#### Fallback Chains
Define global fallback chains that apply across all requests:
```
Chain: production-fallback
1. cc/claude-opus-4-6
2. gh/gpt-5.1-codex
3. glm/glm-4.7
```
---
### Resilience & Circuit Breakers
Configure via **Dashboard → Settings → Resilience**.
OmniRoute implements provider-level resilience with four components:
1. **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.
**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**.
| Tab | Purpose |
| ----------- | ---------------------------------------------------------------------------------------- |
| **Budget** | Set spending limits per API key with daily/weekly/monthly budgets and real-time tracking |
| **Pricing** | View and edit model pricing entries — cost per 1K input/output tokens per provider |
```bash
# API: Set a budget
curl -X POST http://localhost:20128/api/usage/budget \
-H "Content-Type: application/json" \
-d '{"keyId": "key-123", "limit": 50.00, "period": "monthly"}'
# API: Get current budget status
curl http://localhost:20128/api/usage/budget
```
**Cost Tracking:** Every request logs token usage and calculates cost using the pricing table. View breakdowns in **Dashboard → Usage** by provider, model, and API key.
---
### Audio Transcription
OmniRoute supports audio transcription via the OpenAI-compatible endpoint:
```bash
POST /v1/audio/transcriptions
Authorization: Bearer your-api-key
Content-Type: multipart/form-data
# Example with curl
curl -X POST http://localhost:20128/v1/audio/transcriptions \
-H "Authorization: Bearer your-api-key" \
-F "file=@audio.mp3" \
-F "model=deepgram/nova-3"
```
Available providers: **Deepgram** (`deepgram/`), **AssemblyAI** (`assemblyai/`).
Supported audio formats: `mp3`, `wav`, `m4a`, `flac`, `ogg`, `webm`.
---
### Combo Balancing Strategies
Configure per-combo balancing in **Dashboard → Combos → Create/Edit → Strategy**.
| Strategy | Description |
| ------------------ | ------------------------------------------------------------------------ |
| **Round-Robin** | Rotates through models sequentially |
| **Priority** | Always tries the first model; falls back only on error |
| **Random** | Picks a random model from the combo for each request |
| **Weighted** | Routes proportionally based on assigned weights per model |
| **Least-Used** | Routes to the model with the fewest recent requests (uses combo metrics) |
| **Cost-Optimized** | Routes to the cheapest available model (uses pricing table) |
Global combo defaults can be set in **Dashboard → Settings → Routing → Combo Defaults**.
---
### Health Dashboard
Access via **Dashboard → Health**. Real-time system health overview with 6 cards:
| Card | What It Shows |
| --------------------- | ----------------------------------------------------------- |
| **System Status** | Uptime, version, memory usage, data directory |
| **Provider Health** | Per-provider circuit breaker state (Closed/Open/Half-Open) |
| **Rate Limits** | Active rate limit cooldowns per account with remaining time |
| **Active Lockouts** | Providers temporarily blocked by the lockout policy |
| **Signature Cache** | Deduplication cache stats (active keys, hit rate) |
| **Latency Telemetry** | p50/p95/p99 latency aggregation per provider |
**Pro Tip:** The Health page auto-refreshes every 10 seconds. Use the circuit breaker card to identify which providers are experiencing issues.
---
## 🖥️ Desktop Application (Electron)
OmniRoute is available as a native desktop application for Windows, macOS, and Linux.
### Installation
```bash
# From the electron directory:
cd electron
npm install
# Development mode (connect to running Next.js dev server):
npm run dev
# Production mode (uses standalone build):
npm start
```
### Building Installers
```bash
cd electron
npm run build # Current platform
npm run build:win # Windows (.exe NSIS)
npm run build:mac # macOS (.dmg universal)
npm run build:linux # Linux (.AppImage)
```
Output → `electron/dist-electron/`
### Key Features
| Feature | Description |
| --------------------------- | ---------------------------------------------------- |
| **Server Readiness** | Polls server before showing window (no blank screen) |
| **System Tray** | Minimize to tray, change port, quit from tray menu |
| **Port Management** | Change server port from tray (auto-restarts server) |
| **Content Security Policy** | Restrictive CSP via session headers |
| **Single Instance** | Only one app instance can run at a time |
| **Offline Mode** | Bundled Next.js server works without internet |
### Environment Variables
| Variable | Default | Description |
| --------------------- | ------- | -------------------------------- |
| `OMNIROUTE_PORT` | `20128` | Server port |
| `OMNIROUTE_MEMORY_MB` | `512` | Node.js heap limit (6416384 MB) |
📖 Full documentation: [`electron/README.md`](../electron/README.md)
-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) |
+31
View File
@@ -0,0 +1,31 @@
# Architecture Decision Record Template
## ADR-XXX: [Title]
**Date:** YYYY-MM-DD
**Status:** Accepted | Superseded | Deprecated
**Deciders:** @team
## Context
What is the issue we're seeing that motivates this decision?
## Decision
What is the change that we're proposing and/or doing?
## Consequences
What becomes easier or more difficult because of this change?
### Positive
- ...
### Negative
- ...
### Neutral
- ...
+44
View File
@@ -0,0 +1,44 @@
# ADR-001: SQLite as Primary Data Store
**Date:** 2025-10-15
**Status:** Accepted
**Deciders:** @diegosouzapw
## Context
OmniRoute needs to persist usage data, call logs, API keys, and configuration. Options considered:
- PostgreSQL/MySQL — full RDBMS
- SQLite — embedded, zero-config
- JSON files (LowDB) — simple but fragile
- Redis — in-memory, ephemeral
The project targets self-hosted, single-tenant deployments where operational simplicity is paramount.
## Decision
Use **SQLite** via `better-sqlite3` as the primary data store.
- All usage tracking, call logs, API keys, and settings stored in a single `.db` file
- Synchronous reads (no async overhead for simple queries)
- WAL mode for concurrent read/write performance
- Automatic migration from legacy JSON format (`usageDb.json`) on first boot
## Consequences
### Positive
- Zero infrastructure — no database server needed
- Single-file backup (`cp data/omniroute.db backup/`)
- Fast queries for dashboard stats (< 5ms typical)
- Easy migration path from JSON format
### Negative
- Single-writer limitation (acceptable for single-tenant)
- No built-in replication
- Would need migration to PostgreSQL for multi-tenant cloud deployment
### Neutral
- File-based storage works well in Docker volumes
+36
View File
@@ -0,0 +1,36 @@
# ADR-002: Multi-Provider Fallback Strategy
**Date:** 2025-11-20
**Status:** Accepted
**Deciders:** @diegosouzapw
## Context
OmniRoute routes requests to multiple LLM providers (OpenAI, Anthropic, Google, etc.). Providers may become unavailable due to rate limiting, outages, or credential expiry. The system needs a strategy to handle these failures gracefully.
## Decision
Implement a **declarative fallback chain** with three layers:
1. **Credential Retry Loop** — Rotate through available credentials for the same provider before failing
2. **Model Fallback Policy** — Configurable fallback chain per model (e.g., `gpt-4o → azure-gpt-4o → anthropic-claude`)
3. **Circuit Breaker** — Trip open after consecutive failures to prevent cascading requests to broken providers
The fallback policy is defined in `src/domain/fallbackPolicy.js` and integrates with the circuit breaker in `src/shared/utils/circuitBreaker.js`.
## Consequences
### Positive
- Automatic failover with zero user intervention
- Per-model granularity — different models can have different fallback strategies
- Circuit breaker prevents wasting quota on broken providers
### Negative
- Fallback chain requires manual configuration per model
- Response latency increases when primary fails (retry + fallback time)
### Neutral
- Lockout policy (n consecutive failures → temporary block) complements but is separate from fallback
+47
View File
@@ -0,0 +1,47 @@
# ADR-003: OAuth Strategy — Multi-Flow Support
**Date:** 2025-11-01
**Status:** Accepted
**Deciders:** @diegosouzapw
## Context
OmniRoute supports 12+ providers, each with different OAuth implementations:
- Authorization Code + PKCE (Claude, Codex, Gemini, Antigravity, iFlow)
- Device Code Flow (Qwen, GitHub, Kiro, Kilocode, Kimi-Coding, Cline)
- Token Import (Cursor — extracted from local SQLite)
A unified approach is needed to manage authentication across all providers.
## Decision
Use a **base class + strategy pattern**:
1. `OAuthService` base class (`src/lib/oauth/services/oauth.js`) — handles common authorization code flow with PKCE
2. Provider-specific subclasses (e.g., `GitHubService`, `ClaudeService`) — override authentication methods
3. Provider registry (`src/lib/oauth/providers.js`) — declarative config per provider with `flowType`, `buildAuthUrl`, `exchangeToken`, `mapTokens`
4. Constants centralized in `src/lib/oauth/constants/oauth.js`
Each provider defines:
- `flowType`: `authorization_code_pkce` | `authorization_code` | `device_code` | `import_token`
- Required hooks: `buildAuthUrl()`, `exchangeToken()`, `mapTokens()`
- Optional hooks: `postExchange()` for provider-specific post-auth logic
## Consequences
### Positive
- Adding new providers requires only a config entry + optional subclass
- PKCE, state validation, and token exchange are shared (DRY)
- Device code flow providers share polling logic
### Negative
- Some providers have unique quirks (Kiro uses AWS SSO OIDC with client registration)
- Testing requires mocking external OAuth endpoints
### Neutral
- ~1050 lines in `providers.js` — could be further split per provider if needed
+43
View File
@@ -0,0 +1,43 @@
# ADR-004: JavaScript + JSDoc over TypeScript
**Date:** 2025-10-01
**Status:** Accepted
**Deciders:** @diegosouzapw
## Context
The project needs type safety and developer experience improvements. Options:
1. **Full TypeScript migration**`.ts` files, `tsconfig.json`, build step
2. **JavaScript + JSDoc + @ts-check** — type checking without compilation
3. **No type checking** — status quo
## Decision
Adopt **JavaScript with JSDoc annotations and `@ts-check`** instead of migrating to TypeScript.
- Add `// @ts-check` to critical module files
- Use JSDoc `@param`, `@returns`, `@typedef` for type documentation
- TypeScript compiler used only for checking (via IDE), not for building
- Zod schemas for runtime validation at API boundaries
## Consequences
### Positive
- No build step — `node src/proxy.js` runs directly
- Faster development iteration (no compile wait)
- Gradual adoption — files can be annotated one at a time
- IDE still provides autocomplete and type errors via JSDoc
- Lower barrier for contributors
### Negative
- JSDoc type syntax is more verbose than TypeScript
- Some advanced TypeScript features (generics, conditional types) are harder in JSDoc
- No `.d.ts` generation for consumers
### Neutral
- Existing Zod schemas provide runtime validation regardless of type system choice
- `@ts-check` can be added to any file without affecting others
+39
View File
@@ -0,0 +1,39 @@
# ADR-005: Single-Tenant Architecture
**Date:** 2025-10-01
**Status:** Accepted
**Deciders:** @diegosouzapw
## Context
OmniRoute needs to decide between single-tenant and multi-tenant architecture. The primary use case is individuals and small teams running their own proxy instance.
## Decision
Adopt a **single-tenant architecture** where each deployment serves one user/team.
- One SQLite database per instance
- One set of API keys and credentials per instance
- Password-based login (single admin user)
- No user management, roles, or permissions beyond admin
- Settings stored in a single `settings` table
## Consequences
### Positive
- Dramatically simpler codebase (no tenant isolation, RBAC, or data partitioning)
- SQLite is perfectly suited (no concurrent multi-tenant writes)
- Easy deployment: one Docker container = one instance
- Complete data isolation between users (separate deployments)
### Negative
- Not suitable for SaaS or shared hosting without running multiple instances
- No built-in multi-user collaboration features
- Scaling requires deploying separate instances
### Neutral
- Cloud worker mode exists as a separate deployment target with different constraints
- Future multi-tenant support would require a PostgreSQL migration (see ADR-001)
+48
View File
@@ -0,0 +1,48 @@
# ADR-006: Translator Registry Pattern
**Date:** 2025-12-01
**Status:** Accepted
**Deciders:** @diegosouzapw
## Context
OmniRoute translates requests between different LLM API formats (OpenAI ↔ Anthropic ↔ Google ↔ etc.). Each provider has a unique request/response schema. The translator must:
- Convert incoming requests to the target provider's format
- Convert streaming responses back to the client's expected format
- Handle provider-specific features (tool calls, vision, system prompts)
## Decision
Use a **registry pattern** for translators:
1. Each provider pair has a translator module in `src/sse/translators/`
2. Translators are registered by `(sourceFormat, targetFormat)` key
3. The `translateRequest()` function auto-detects source format and applies the appropriate translator
4. Translators handle both request translation and response stream mapping
Key translators:
- `openai → anthropic` (and reverse)
- `openai → google` (and reverse)
- `anthropic → google` (and reverse)
- Identity translators for same-format routing
## Consequences
### Positive
- Adding a new provider requires only a new translator module
- Each translator is independently testable
- Auto-detection reduces configuration burden on users
- Supports chained translation (A → B → C) if needed
### Negative
- O(n²) translator combinations as providers grow (mitigated by identity translators)
- Some edge cases in format conversion (e.g., tool call schemas differ significantly)
### Neutral
- The Translator Playground UI provides visual testing of translation chains
- Performance overhead is minimal (JSON transformation, no network calls)
-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**
-12
View File
@@ -1,12 +0,0 @@
# Multilingual Documentation
This directory contains machine-assisted translations based on the English docs.
- **API_REFERENCE.md**: 🇺🇸 [English](../API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](./es/API_REFERENCE.md) | 🇫🇷 [Français](./fr/API_REFERENCE.md) | 🇮🇹 [Italiano](./it/API_REFERENCE.md) | 🇷🇺 [Русский](./ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](./zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](./de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](./in/API_REFERENCE.md) | 🇹🇭 [ไทย](./th/API_REFERENCE.md) | 🇺🇦 [Українська](./uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](./ar/API_REFERENCE.md) | 🇯🇵 [日本語](./ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](./vi/API_REFERENCE.md) | 🇧🇬 [Български](./bg/API_REFERENCE.md) | 🇩🇰 [Dansk](./da/API_REFERENCE.md) | 🇫🇮 [Suomi](./fi/API_REFERENCE.md) | 🇮🇱 [עברית](./he/API_REFERENCE.md) | 🇭🇺 [Magyar](./hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](./id/API_REFERENCE.md) | 🇰🇷 [한국어](./ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](./ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](./nl/API_REFERENCE.md) | 🇳🇴 [Norsk](./no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](./pt/API_REFERENCE.md) | 🇷🇴 [Română](./ro/API_REFERENCE.md) | 🇵🇱 [Polski](./pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](./sk/API_REFERENCE.md) | 🇸🇪 [Svenska](./sv/API_REFERENCE.md) | 🇵🇭 [Filipino](./phi/API_REFERENCE.md)
- **ARCHITECTURE.md**: 🇺🇸 [English](../ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](./es/ARCHITECTURE.md) | 🇫🇷 [Français](./fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](./it/ARCHITECTURE.md) | 🇷🇺 [Русский](./ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](./zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](./de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](./in/ARCHITECTURE.md) | 🇹🇭 [ไทย](./th/ARCHITECTURE.md) | 🇺🇦 [Українська](./uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](./ar/ARCHITECTURE.md) | 🇯🇵 [日本語](./ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](./vi/ARCHITECTURE.md) | 🇧🇬 [Български](./bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](./da/ARCHITECTURE.md) | 🇫🇮 [Suomi](./fi/ARCHITECTURE.md) | 🇮🇱 [עברית](./he/ARCHITECTURE.md) | 🇭🇺 [Magyar](./hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](./id/ARCHITECTURE.md) | 🇰🇷 [한국어](./ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](./ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](./nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](./no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](./pt/ARCHITECTURE.md) | 🇷🇴 [Română](./ro/ARCHITECTURE.md) | 🇵🇱 [Polski](./pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](./sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](./sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](./phi/ARCHITECTURE.md)
- **CODEBASE_DOCUMENTATION.md**: 🇺🇸 [English](../CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](./pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](./es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](./fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](./it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](./ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](./zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](./de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](./in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](./th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](./uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](./ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](./ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](./vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](./bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](./da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](./fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](./he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](./hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](./id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](./ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](./ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](./nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](./no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](./pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](./ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](./pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](./sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](./sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](./phi/CODEBASE_DOCUMENTATION.md)
- **FEATURES.md**: 🇺🇸 [English](../FEATURES.md) | 🇧🇷 [Português (Brasil)](./pt-BR/FEATURES.md) | 🇪🇸 [Español](./es/FEATURES.md) | 🇫🇷 [Français](./fr/FEATURES.md) | 🇮🇹 [Italiano](./it/FEATURES.md) | 🇷🇺 [Русский](./ru/FEATURES.md) | 🇨🇳 [中文 (简体)](./zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](./de/FEATURES.md) | 🇮🇳 [हिन्दी](./in/FEATURES.md) | 🇹🇭 [ไทย](./th/FEATURES.md) | 🇺🇦 [Українська](./uk-UA/FEATURES.md) | 🇸🇦 [العربية](./ar/FEATURES.md) | 🇯🇵 [日本語](./ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](./vi/FEATURES.md) | 🇧🇬 [Български](./bg/FEATURES.md) | 🇩🇰 [Dansk](./da/FEATURES.md) | 🇫🇮 [Suomi](./fi/FEATURES.md) | 🇮🇱 [עברית](./he/FEATURES.md) | 🇭🇺 [Magyar](./hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](./id/FEATURES.md) | 🇰🇷 [한국어](./ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](./ms/FEATURES.md) | 🇳🇱 [Nederlands](./nl/FEATURES.md) | 🇳🇴 [Norsk](./no/FEATURES.md) | 🇵🇹 [Português (Portugal)](./pt/FEATURES.md) | 🇷🇴 [Română](./ro/FEATURES.md) | 🇵🇱 [Polski](./pl/FEATURES.md) | 🇸🇰 [Slovenčina](./sk/FEATURES.md) | 🇸🇪 [Svenska](./sv/FEATURES.md) | 🇵🇭 [Filipino](./phi/FEATURES.md)
- **TROUBLESHOOTING.md**: 🇺🇸 [English](../TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](./pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](./es/TROUBLESHOOTING.md) | 🇫🇷 [Français](./fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](./it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](./ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](./zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](./de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](./in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](./th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](./uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](./ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](./ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](./vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](./bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](./da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](./fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](./he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](./hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](./id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](./ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](./ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](./nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](./no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](./pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](./ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](./pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](./sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](./sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](./phi/TROUBLESHOOTING.md)
- **USER_GUIDE.md**: 🇺🇸 [English](../USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](./es/USER_GUIDE.md) | 🇫🇷 [Français](./fr/USER_GUIDE.md) | 🇮🇹 [Italiano](./it/USER_GUIDE.md) | 🇷🇺 [Русский](./ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](./zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](./de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](./in/USER_GUIDE.md) | 🇹🇭 [ไทย](./th/USER_GUIDE.md) | 🇺🇦 [Українська](./uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](./ar/USER_GUIDE.md) | 🇯🇵 [日本語](./ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](./vi/USER_GUIDE.md) | 🇧🇬 [Български](./bg/USER_GUIDE.md) | 🇩🇰 [Dansk](./da/USER_GUIDE.md) | 🇫🇮 [Suomi](./fi/USER_GUIDE.md) | 🇮🇱 [עברית](./he/USER_GUIDE.md) | 🇭🇺 [Magyar](./hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](./id/USER_GUIDE.md) | 🇰🇷 [한국어](./ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](./ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](./nl/USER_GUIDE.md) | 🇳🇴 [Norsk](./no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](./pt/USER_GUIDE.md) | 🇷🇴 [Română](./ro/USER_GUIDE.md) | 🇵🇱 [Polski](./pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](./sk/USER_GUIDE.md) | 🇸🇪 [Svenska](./sv/USER_GUIDE.md) | 🇵🇭 [Filipino](./phi/USER_GUIDE.md)
Generated on 2026-02-26.
-441
View File
@@ -1,441 +0,0 @@
# مرجع واجهة برمجة التطبيقات
🌐 **Languages:** 🇺🇸 [English](../../API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](../es/API_REFERENCE.md) | 🇫🇷 [Français](../fr/API_REFERENCE.md) | 🇮🇹 [Italiano](../it/API_REFERENCE.md) | 🇷🇺 [Русский](../ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](../zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](../de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](../in/API_REFERENCE.md) | 🇹🇭 [ไทย](../th/API_REFERENCE.md) | 🇺🇦 [Українська](../uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](../ar/API_REFERENCE.md) | 🇯🇵 [日本語](../ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](../vi/API_REFERENCE.md) | 🇧🇬 [Български](../bg/API_REFERENCE.md) | 🇩🇰 [Dansk](../da/API_REFERENCE.md) | 🇫🇮 [Suomi](../fi/API_REFERENCE.md) | 🇮🇱 [עברית](../he/API_REFERENCE.md) | 🇭🇺 [Magyar](../hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](../id/API_REFERENCE.md) | 🇰🇷 [한국어](../ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](../ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](../nl/API_REFERENCE.md) | 🇳🇴 [Norsk](../no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](../pt/API_REFERENCE.md) | 🇷🇴 [Română](../ro/API_REFERENCE.md) | 🇵🇱 [Polski](../pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](../sk/API_REFERENCE.md) | 🇸🇪 [Svenska](../sv/API_REFERENCE.md) | 🇵🇭 [Filipino](../phi/API_REFERENCE.md)
مرجع كامل لجميع نقاط نهاية OmniRoute API.
---
## جدول المحتويات
- [Chat Completions](#chat-completions)
- [Embeddings](#embeddings)
- [Image Generation](#image-generation)
- [List Models](#list-models)
- [Compatibility Endpoints](#compatibility-endpoints)
- [Semantic Cache](#semantic-cache)
- [Dashboard & Management](#dashboard--management)
- [Request Processing](#request-processing)
- [Authentication](#authentication)
---
## إكمالات الدردشة
```bash
POST /v1/chat/completions
Authorization: Bearer your-api-key
Content-Type: application/json
{
"model": "cc/claude-opus-4-6",
"messages": [
{"role": "user", "content": "Write a function to..."}
],
"stream": true
}
```
### رؤوس مخصصة
| رأس | الاتجاه | الوصف |
| ------------------------ | ------- | ------------------------------------------- |
| `X-OmniRoute-No-Cache` | طلب | اضبط على `true` لتجاوز ذاكرة التخزين المؤقت |
| `X-OmniRoute-Progress` | طلب | اضبط على `true` لأحداث التقدم |
| `Idempotency-Key` | طلب | مفتاح Dedup (نافذة 5 ثواني) |
| `X-Request-Id` | طلب | مفتاح إلغاء الحذف البديل |
| `X-OmniRoute-Cache` | الرد | `HIT` أو `MISS` (غير متدفق) |
| `X-OmniRoute-Idempotent` | الرد | `true` إذا تم إلغاء التكرار |
| `X-OmniRoute-Progress` | الرد | `enabled` إذا تم تتبع التقدم على |
---
## التضمينات
```bash
POST /v1/embeddings
Authorization: Bearer your-api-key
Content-Type: application/json
{
"model": "nebius/Qwen/Qwen3-Embedding-8B",
"input": "The food was delicious"
}
```
مقدمو الخدمة المتاحون: Nebius، وOpenAI، وMistral، وTogether AI، وFireworks، وNVIDIA.
```bash
# List all embedding models
GET /v1/embeddings
```
---
## توليد الصور
```bash
POST /v1/images/generations
Authorization: Bearer your-api-key
Content-Type: application/json
{
"model": "openai/dall-e-3",
"prompt": "A beautiful sunset over mountains",
"size": "1024x1024"
}
```
الموفرون المتاحون: OpenAI (DALL-E)، xAI (Grok Image)، Together AI (FLUX)، Fireworks AI.
```bash
# List all image models
GET /v1/images/generations
```
---
## قائمة النماذج
```bash
GET /v1/models
Authorization: Bearer your-api-key
→ Returns all chat, embedding, and image models + combos in OpenAI format
```
---
## نقاط نهاية التوافق
| الطريقة | المسار | تنسيق |
| -------- | --------------------------- | --------------------- |
| مشاركة | `/v1/chat/completions` | أوبن آي |
| مشاركة | `/v1/messages` | انثروبي |
| مشاركة | `/v1/responses` | ردود OpenAI |
| مشاركة | `/v1/embeddings` | أوبن آي |
| مشاركة | `/v1/images/generations` | أوبن آي |
| احصل على | `/v1/models` | أوبن آي |
| مشاركة | `/v1/messages/count_tokens` | انثروبي |
| احصل على | `/v1beta/models` | الجوزاء |
| مشاركة | `/v1beta/models/{...path}` | الجوزاء توليد المحتوى |
| مشاركة | `/v1/api/chat` | أولاما |
### مسارات موفر مخصصة
```bash
POST /v1/providers/{provider}/chat/completions
POST /v1/providers/{provider}/embeddings
POST /v1/providers/{provider}/images/generations
```
تتم إضافة بادئة الموفر تلقائيًا في حالة فقدانها. تُرجع النماذج غير المتطابقة `400`.
---
## ذاكرة التخزين المؤقت الدلالية
```bash
# Get cache stats
GET /api/cache
# Clear all caches
DELETE /api/cache
```
مثال الاستجابة:
```json
{
"semanticCache": {
"memorySize": 42,
"memoryMaxSize": 500,
"dbSize": 128,
"hitRate": 0.65
},
"idempotency": {
"activeKeys": 3,
"windowMs": 5000
}
}
```
---
## لوحة القيادة والإدارة
### المصادقة
| نقطة النهاية | الطريقة | الوصف |
| ----------------------------- | -------------- | ------------------------ |
| `/api/auth/login` | مشاركة | تسجيل الدخول |
| `/api/auth/logout` | مشاركة | تسجيل الخروج |
| `/api/settings/require-login` | الحصول على/وضع | تبديل تسجيل الدخول مطلوب |
### إدارة مقدمي الخدمة
| نقطة النهاية | الطريقة | الوصف |
| ---------------------------- | ------------------ | --------------------------- |
| `/api/providers` | الحصول على/النشر | قائمة / إنشاء مقدمي الخدمات |
| `/api/providers/[id]` | الحصول على/وضع/حذف | إدارة مزود |
| `/api/providers/[id]/test` | مشاركة | اختبار اتصال الموفر |
| `/api/providers/[id]/models` | احصل على | قائمة نماذج المزود |
| `/api/providers/validate` | مشاركة | التحقق من صحة تكوين الموفر |
| `/api/provider-nodes*` | متنوع | إدارة عقدة الموفر |
| `/api/provider-models` | الحصول على/نشر/حذف | نماذج مخصصة |
### تدفقات OAuth
| نقطة النهاية | الطريقة | الوصف |
| -------------------------------- | ------- | ------------------------ |
| `/api/oauth/[provider]/[action]` | متنوع | OAuth الخاص بموفر الخدمة |
### التوجيه والتكوين
| نقطة النهاية | الطريقة | الوصف |
| --------------------- | ---------------- | --------------------------------- |
| `/api/models/alias` | الحصول على/النشر | الأسماء المستعارة للنموذج |
| `/api/models/catalog` | احصل على | جميع الموديلات حسب المزود + النوع |
| `/api/combos*` | متنوع | إدارة التحرير والسرد |
| `/api/keys*` | متنوع | إدارة مفاتيح API |
| `/api/pricing` | احصل على | التسعير النموذجي |
### الاستخدام والتحليلات
| نقطة النهاية | الطريقة | الوصف |
| --------------------------- | -------- | --------------------- |
| `/api/usage/history` | احصل على | تاريخ الاستخدام |
| `/api/usage/logs` | احصل على | سجلات الاستخدام |
| `/api/usage/request-logs` | احصل على | سجلات على مستوى الطلب |
| `/api/usage/[connectionId]` | احصل على | الاستخدام لكل اتصال |
### الإعدادات
| نقطة النهاية | الطريقة | الوصف |
| ------------------------------- | -------------- | ----------------------------------------------- |
| `/api/settings` | الحصول على/وضع | الإعدادات العامة |
| `/api/settings/proxy` | الحصول على/وضع | تكوين وكيل الشبكة |
| `/api/settings/proxy/test` | مشاركة | اختبار اتصال الوكيل |
| `/api/settings/ip-filter` | الحصول على/وضع | القائمة المسموح بها/القائمة المحظورة لعناوين IP |
| `/api/settings/thinking-budget` | الحصول على/وضع | الميزانية الرمزية المنطقية |
| `/api/settings/system-prompt` | الحصول على/وضع | موجه النظام العالمي |
### المراقبة
| نقطة النهاية | الطريقة | الوصف |
| ------------------------ | -------------- | ----------------------------------- |
| `/api/sessions` | احصل على | تتبع الجلسة النشطة |
| `/api/rate-limits` | احصل على | حدود المعدل لكل حساب |
| `/api/monitoring/health` | احصل على | فحص الصحة |
| `/api/cache` | الحصول على/حذف | إحصائيات ذاكرة التخزين المؤقت / مسح |
### النسخ الاحتياطي والتصدير/الاستيراد
| نقطة النهاية | الطريقة | الوصف |
| --------------------------- | -------- | -------------------------------------------------- |
| `/api/db-backups` | احصل على | قائمة النسخ الاحتياطية المتاحة |
| `/api/db-backups` | ضع | إنشاء نسخة احتياطية يدوية |
| `/api/db-backups` | مشاركة | استعادة من نسخة احتياطية محددة |
| `/api/db-backups/export` | احصل على | تنزيل قاعدة البيانات كملف .sqlite |
| `/api/db-backups/import` | مشاركة | قم بتحميل ملف .sqlite لاستبدال قاعدة البيانات |
| `/api/db-backups/exportAll` | احصل على | قم بتنزيل النسخة الاحتياطية الكاملة كأرشيف .tar.gz |
### المزامنة السحابية
| نقطة النهاية | الطريقة | الوصف |
| ---------------------- | ------- | ------------------------ |
| `/api/sync/cloud` | متنوع | عمليات المزامنة السحابية |
| `/api/sync/initialize` | مشاركة | تهيئة المزامنة |
| `/api/cloud/*` | متنوع | إدارة السحابة |
### أدوات سطر الأوامر
| نقطة النهاية | الطريقة | الوصف |
| ---------------------------------- | -------- | ------------------- |
| `/api/cli-tools/claude-settings` | احصل على | حالة كلود CLI |
| `/api/cli-tools/codex-settings` | احصل على | حالة Codex CLI |
| `/api/cli-tools/droid-settings` | احصل على | حالة Droid CLI |
| `/api/cli-tools/openclaw-settings` | احصل على | حالة OpenClaw CLI |
| `/api/cli-tools/runtime/[toolId]` | احصل على | وقت تشغيل CLI العام |
تتضمن استجابات واجهة سطر الأوامر: `installed`، `runnable`، `command`، `commandPath`، `runtimeMode`، `reason`.
### المرونة وحدود الأسعار
| نقطة النهاية | الطريقة | الوصف |
| ----------------------- | -------------- | ------------------------------------ |
| `/api/resilience` | الحصول على/وضع | الحصول على/تحديث ملفات تعريف المرونة |
| `/api/resilience/reset` | مشاركة | إعادة ضبط قواطع الدائرة |
| `/api/rate-limits` | احصل على | حالة حد المعدل لكل حساب |
| `/api/rate-limit` | احصل على | تكوين حد المعدل العالمي |
### التقييم
| نقطة النهاية | الطريقة | الوصف |
| ------------ | ---------------- | ------------------------------------- |
| `/api/evals` | الحصول على/النشر | قائمة مجموعات التقييم / تشغيل التقييم |
### السياسات
| نقطة النهاية | الطريقة | الوصف |
| --------------- | ------------------ | -------------------- |
| `/api/policies` | الحصول على/نشر/حذف | إدارة سياسات التوجيه |
###الامتثال
| نقطة النهاية | الطريقة | الوصف |
| --------------------------- | -------- | ---------------------------- |
| `/api/compliance/audit-log` | احصل على | سجل تدقيق الامتثال (آخر رقم) |
### v1beta (متوافق مع الجوزاء)
| نقطة النهاية | الطريقة | الوصف |
| -------------------------- | -------- | -------------------------------------- |
| `/v1beta/models` | احصل على | قائمة النماذج بصيغة الجوزاء |
| `/v1beta/models/{...path}` | مشاركة | الجوزاء `generateContent` نقطة النهاية |
تعكس نقاط النهاية هذه تنسيق Gemini API للعملاء الذين يتوقعون توافق Gemini SDK الأصلي.
### واجهات برمجة التطبيقات الداخلية / النظام
| نقطة النهاية | الطريقة | الوصف |
| --------------- | -------- | -------------------------------------------------- |
| `/api/init` | احصل على | فحص تهيئة التطبيق (يستخدم عند التشغيل لأول مرة) |
| `/api/tags` | احصل على | علامات النماذج المتوافقة مع Ollama (لعملاء Ollama) |
| `/api/restart` | مشاركة | تشغيل إعادة تشغيل الخادم الرشيقة |
| `/api/shutdown` | مشاركة | تشغيل إيقاف تشغيل الخادم بشكل رشيق |
> **ملاحظة:** يتم استخدام نقاط النهاية هذه داخليًا بواسطة النظام أو للتوافق مع عميل Ollama. لا يتم استدعاؤها عادة من قبل المستخدمين النهائيين.
---
## النسخ الصوتي
```bash
POST /v1/audio/transcriptions
Authorization: Bearer your-api-key
Content-Type: multipart/form-data
```
قم بنسخ الملفات الصوتية باستخدام Deepgram أو AssemblyAI.
**الطلب:**
```bash
curl -X POST http://localhost:20128/v1/audio/transcriptions \
-H "Authorization: Bearer your-api-key" \
-F "file=@recording.mp3" \
-F "model=deepgram/nova-3"
```
**الرد:**
```json
{
"text": "Hello, this is the transcribed audio content.",
"task": "transcribe",
"language": "en",
"duration": 12.5
}
```
**الموفرون المدعمون:** `deepgram/nova-3`، `assemblyai/best`.
**التنسيقات المدعومة:** `mp3`، `wav`، `m4a`، `flac`، `ogg`، `webm`.
---
## التوافق مع أولاما
بالنسبة للعملاء الذين يستخدمون تنسيق واجهة برمجة تطبيقات Olma:
```bash
# Chat endpoint (Ollama format)
POST /v1/api/chat
# Model listing (Ollama format)
GET /api/tags
```
تتم ترجمة الطلبات تلقائيًا بين تنسيقات Ollama والتنسيقات الداخلية.
---
## القياس عن بعد
```bash
# Get latency telemetry summary (p50/p95/p99 per provider)
GET /api/telemetry/summary
```
**الرد:**
```json
{
"providers": {
"claudeCode": { "p50": 245, "p95": 890, "p99": 1200, "count": 150 },
"github": { "p50": 180, "p95": 620, "p99": 950, "count": 320 }
}
}
```
---
## الميزانية
```bash
# Get budget status for all API keys
GET /api/usage/budget
# Set or update a budget
POST /api/usage/budget
Content-Type: application/json
{
"keyId": "key-123",
"limit": 50.00,
"period": "monthly"
}
```
---
## توفر النموذج
```bash
# Get real-time model availability across all providers
GET /api/models/availability
# Check availability for a specific model
POST /api/models/availability
Content-Type: application/json
{
"model": "claude-sonnet-4-5-20250929"
}
```
---
## معالجة الطلب
1. يرسل العميل طلبًا إلى `/v1/*`
2. يستدعي معالج المسار `handleChat`، `handleEmbedding`، `handleAudioTranscription`، أو `handleImageGeneration`
3. تم حل النموذج (المزود/النموذج المباشر أو الاسم المستعار/السرد)
4. تم تحديد بيانات الاعتماد من قاعدة البيانات المحلية مع تصفية توفر الحساب
5. للدردشة: `handleChatCore` — اكتشاف التنسيق، والترجمة، والتحقق من ذاكرة التخزين المؤقت، والتحقق من عدم الكفاءة
6. يقوم منفذ الموفر بإرسال طلب المنبع
7. تتم ترجمة الاستجابة مرة أخرى إلى تنسيق العميل (الدردشة) أو إعادتها كما هي (التضمينات/الصور/الصوت)
8. تم تسجيل الاستخدام/التسجيل
9. يتم تطبيق الإجراء الاحتياطي على الأخطاء وفقًا لقواعد التحرير والسرد
مرجع البنية الكاملة: [link](ARCHITECTURE.md)
---
## المصادقة
- تستخدم مسارات لوحة المعلومات (`/dashboard/*`) ملف تعريف الارتباط `auth_token`
- يستخدم تسجيل الدخول تجزئة كلمة المرور المحفوظة؛ الرجوع إلى `INITIAL_PASSWORD`
- `requireLogin` قابل للتبديل عبر `/api/settings/require-login`
- تتطلب مسارات `/v1/*` بشكل اختياري مفتاح Bearer API عندما `REQUIRE_API_KEY=true`
-781
View File
@@ -1,781 +0,0 @@
# العمارة OmniRoute
🌐 **Languages:** 🇺🇸 [English](../../ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](../es/ARCHITECTURE.md) | 🇫🇷 [Français](../fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](../it/ARCHITECTURE.md) | 🇷🇺 [Русский](../ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](../zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](../de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](../in/ARCHITECTURE.md) | 🇹🇭 [ไทย](../th/ARCHITECTURE.md) | 🇺🇦 [Українська](../uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](../ar/ARCHITECTURE.md) | 🇯🇵 [日本語](../ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](../vi/ARCHITECTURE.md) | 🇧🇬 [Български](../bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](../da/ARCHITECTURE.md) | 🇫🇮 [Suomi](../fi/ARCHITECTURE.md) | 🇮🇱 [עברית](../he/ARCHITECTURE.md) | 🇭🇺 [Magyar](../hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](../id/ARCHITECTURE.md) | 🇰🇷 [한국어](../ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](../ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](../nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](../no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](../pt/ARCHITECTURE.md) | 🇷🇴 [Română](../ro/ARCHITECTURE.md) | 🇵🇱 [Polski](../pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](../sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](../sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](../phi/ARCHITECTURE.md)
_آخر تحديث: 2026-02-18_
## ملخص تنفيذي
OmniRoute عبارة عن بوابة توجيه محلية تعمل بالذكاء الاصطناعي ولوحة معلومات مبنية على Next.js.
فهو يوفر نقطة نهاية واحدة متوافقة مع OpenAI (`/v1/*`) ويوجه حركة المرور عبر العديد من موفري الخدمات الأولية مع الترجمة والاحتياط وتحديث الرمز المميز وتتبع الاستخدام.
القدرات الأساسية:
- سطح API متوافق مع OpenAI لـ CLI/الأدوات (28 مزودًا)
- ترجمة الطلب/الاستجابة عبر تنسيقات الموفر
- نموذج احتياطي التحرير والسرد (تسلسل متعدد النماذج)
- احتياطي على مستوى الحساب (حسابات متعددة لكل مزود)
- إدارة اتصال موفر OAuth + API-key
- إنشاء التضمين عبر `/v1/embeddings` (6 موفري خدمات، 9 نماذج)
- إنشاء الصور عبر `/v1/images/generations` (4 مقدمي خدمات، 9 نماذج)
- فكر في تحليل العلامات (`<think>...</think>`) لنماذج الاستدلال
- تعقيم الاستجابة للتوافق الصارم مع OpenAI SDK
- تطبيع الدور (المطور → النظام، النظام → المستخدم) للتوافق بين الموفرين
- تحويل الإخراج المنظم (json_schema → Gemini ResponseSchema)
- الثبات المحلي لمقدمي الخدمات والمفاتيح والأسماء المستعارة والمجموعات والإعدادات والتسعير
- تتبع الاستخدام/التكلفة وتسجيل الطلب
- مزامنة سحابية اختيارية لمزامنة الأجهزة/الحالة المتعددة
- القائمة المسموح بها/القائمة المحظورة لـ IP للتحكم في الوصول إلى واجهة برمجة التطبيقات
- التفكير في إدارة الميزانية (العبور / التلقائي / المخصص / التكيفي)
- الحقن الفوري للنظام العالمي
- تتبع الجلسة وأخذ البصمات
- تحديد المعدل المحسن لكل حساب مع الملفات الشخصية الخاصة بالمزود
- نمط قاطع الدائرة لمرونة المزود
- حماية القطيع ضد الرعد مع قفل Mutex
- ذاكرة التخزين المؤقت لإلغاء البيانات المكررة للطلب المستندة إلى التوقيع
- طبقة المجال: توفر النموذج، وقواعد التكلفة، والسياسة الاحتياطية، وسياسة الإغلاق
- استمرارية حالة المجال (ذاكرة التخزين المؤقت للكتابة في SQLite للاحتياطيات والميزانيات وعمليات الإغلاق وقواطع الدائرة)
- محرك السياسة لتقييم الطلب المركزي (التأمين → الميزانية → الاحتياطي)
- طلب القياس عن بعد مع تجميع الكمون p50/p95/p99
- معرف الارتباط (X-Request-Id) للتتبع الشامل
- تسجيل تدقيق الامتثال مع إلغاء الاشتراك لكل مفتاح API
- إطار تقييمي لضمان جودة LLM
- لوحة تحكم واجهة المستخدم المرنة مع حالة قاطع الدائرة في الوقت الفعلي
- موفرو OAuth المعياريون (12 وحدة فردية ضمن `src/lib/oauth/providers/`)
نموذج وقت التشغيل الأساسي:
- تقوم مسارات تطبيق Next.js ضمن `src/app/api/*` بتنفيذ كل من واجهات برمجة تطبيقات لوحة المعلومات وواجهات برمجة تطبيقات التوافق
- نواة توجيه/SSE مشتركة في `src/sse/*` + `open-sse/*` تتعامل مع تنفيذ الموفر والترجمة والتدفق والرجوع والاستخدام
## النطاق والحدود
### في النطاق
- وقت تشغيل البوابة المحلية
- واجهات برمجة التطبيقات لإدارة لوحة المعلومات
- مصادقة الموفر وتحديث الرمز المميز
- طلب الترجمة وتدفق SSE
- الحالة المحلية + استمرارية الاستخدام
- تنسيق مزامنة سحابية اختيارية
### خارج النطاق
- تنفيذ الخدمة السحابية خلف `NEXT_PUBLIC_CLOUD_URL`
- مزود مستوى جيش تحرير السودان/مستوى التحكم خارج العملية المحلية
- ثنائيات CLI الخارجية نفسها (Claude CLI، Codex CLI، وما إلى ذلك)
## سياق النظام عالي المستوى
```mermaid
flowchart LR
subgraph Clients[Developer Clients]
C1[Claude Code]
C2[Codex CLI]
C3[OpenClaw / Droid / Cline / Continue / Roo]
C4[Custom OpenAI-compatible clients]
BROWSER[Browser Dashboard]
end
subgraph Router[OmniRoute Local Process]
API[V1 Compatibility API\n/v1/*]
DASH[Dashboard + Management API\n/api/*]
CORE[SSE + Translation Core\nopen-sse + src/sse]
DB[(db.json)]
UDB[(usage.json + log.txt)]
end
subgraph Upstreams[Upstream Providers]
P1[OAuth Providers\nClaude/Codex/Gemini/Qwen/iFlow/GitHub/Kiro/Cursor/Antigravity]
P2[API Key Providers\nOpenAI/Anthropic/OpenRouter/GLM/Kimi/MiniMax\nDeepSeek/Groq/xAI/Mistral/Perplexity\nTogether/Fireworks/Cerebras/Cohere/NVIDIA]
P3[Compatible Nodes\nOpenAI-compatible / Anthropic-compatible]
end
subgraph Cloud[Optional Cloud Sync]
CLOUD[Cloud Sync Endpoint\nNEXT_PUBLIC_CLOUD_URL]
end
C1 --> API
C2 --> API
C3 --> API
C4 --> API
BROWSER --> DASH
API --> CORE
DASH --> DB
CORE --> DB
CORE --> UDB
CORE --> P1
CORE --> P2
CORE --> P3
DASH --> CLOUD
```
## مكونات وقت التشغيل الأساسية
## 1) واجهة برمجة التطبيقات وطبقة التوجيه (مسارات تطبيق Next.js)
الدلائل الرئيسية:
- `src/app/api/v1/*` و`src/app/api/v1beta/*` لواجهات برمجة تطبيقات التوافق
- `src/app/api/*` لواجهات برمجة التطبيقات للإدارة/التكوين
- تتم إعادة الكتابة التالية في الخريطة `next.config.mjs` من `/v1/*` إلى `/api/v1/*`
طرق التوافق الهامة:
- `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` — يتضمن نماذج مخصصة مع `custom: true`
- `src/app/api/v1/embeddings/route.ts` — إنشاء التضمين (6 موفري)
- `src/app/api/v1/images/generations/route.ts` — إنشاء الصور (أكثر من 4 موفري خدمة، بما في ذلك Antigravity/Nebius)
- `src/app/api/v1/messages/count_tokens/route.ts`
- `src/app/api/v1/providers/[provider]/chat/completions/route.ts` — دردشة مخصصة لكل مزود
- `src/app/api/v1/providers/[provider]/embeddings/route.ts` — عمليات التضمين المخصصة لكل مزود
- `src/app/api/v1/providers/[provider]/images/generations/route.ts` — صور مخصصة لكل مزود
- `src/app/api/v1beta/models/route.ts`
- `src/app/api/v1beta/models/[...path]/route.ts`
مجالات الإدارة:
- المصادقة/الإعدادات: `src/app/api/auth/*`، `src/app/api/settings/*`
- مقدمو الخدمة/الاتصالات: `src/app/api/providers*`
- عقد الموفر: `src/app/api/provider-nodes*`
- النماذج المخصصة: `src/app/api/provider-models` (GET/POST/DELETE)
- كتالوج النماذج: `src/app/api/models/catalog` (GET)
- تكوين الوكيل: `src/app/api/settings/proxy` (GET/PUT/DELETE) + `src/app/api/settings/proxy/test` (POST)
- OAuth: `src/app/api/oauth/*`
- المفاتيح/الأسماء المستعارة/المجموعات/التسعير: `src/app/api/keys*`، `src/app/api/models/alias`، `src/app/api/combos*`، `src/app/api/pricing`
- الاستخدام: `src/app/api/usage/*`
- المزامنة/السحابة: `src/app/api/sync/*`، `src/app/api/cloud/*`
- مساعدي أدوات CLI: `src/app/api/cli-tools/*`
- مرشح IP: `src/app/api/settings/ip-filter` (GET/PUT)
- ميزانية التفكير: `src/app/api/settings/thinking-budget` (GET/PUT)
- موجه النظام: `src/app/api/settings/system-prompt` (GET/PUT)
- الجلسات: `src/app/api/sessions` (GET)
- حدود الأسعار: `src/app/api/rate-limits` (GET)
- المرونة: `src/app/api/resilience` (GET/PATCH) - ملفات تعريف الموفر، قاطع الدائرة، حالة حد المعدل
- إعادة ضبط المرونة: `src/app/api/resilience/reset` (POST) — إعادة ضبط الفواصل + فترات التهدئة
- إحصائيات ذاكرة التخزين المؤقت: `src/app/api/cache/stats` (الحصول على/الحذف)
- توفر النموذج: `src/app/api/models/availability` (GET/POST)
- القياس عن بعد: `src/app/api/telemetry/summary` (GET)
- الميزانية: `src/app/api/usage/budget` (GET/POST)
- السلاسل الاحتياطية: `src/app/api/fallback/chains` (GET/POST/DELETE)
- تدقيق الامتثال: `src/app/api/compliance/audit-log` (GET)
- التقييمات: `src/app/api/evals` (GET/POST)، `src/app/api/evals/[suiteId]` (GET)
- السياسات: `src/app/api/policies` (GET/POST)
## 2) SSE + جوهر الترجمة
وحدات التدفق الرئيسية:
- الإدخال: `src/sse/handlers/chat.ts`
- التنسيق الأساسي: `open-sse/handlers/chatCore.ts`
- محولات تنفيذ الموفر: `open-sse/executors/*`
- اكتشاف التنسيق/تكوين الموفر: `open-sse/services/provider.ts`
- تحليل/حل النموذج: `src/sse/services/model.ts`، `open-sse/services/model.ts`
- المنطق الاحتياطي للحساب: `open-sse/services/accountFallback.ts`
- سجل الترجمة: `open-sse/translator/index.ts`
- تحويلات الدفق: `open-sse/utils/stream.ts`، `open-sse/utils/streamHandler.ts`
- استخراج/تسوية الاستخدام: `open-sse/utils/usageTracking.ts`
- محلل العلامات: `open-sse/utils/thinkTagParser.ts`
- معالج التضمين: `open-sse/handlers/embeddings.ts`
- تسجيل موفر التضمين: `open-sse/config/embeddingRegistry.ts`
- معالج إنشاء الصور: `open-sse/handlers/imageGeneration.ts`
- سجل موفر الصور: `open-sse/config/imageRegistry.ts`
- تعقيم الاستجابة: `open-sse/handlers/responseSanitizer.ts`
- تطبيع الدور: `open-sse/services/roleNormalizer.ts`
الخدمات (منطق الأعمال):
- اختيار الحساب/تسجيل النقاط: `open-sse/services/accountSelector.ts`
- إدارة دورة حياة السياق: `open-sse/services/contextManager.ts`
- فرض عامل تصفية IP: `open-sse/services/ipFilter.ts`
- تتبع الجلسة: `open-sse/services/sessionManager.ts`
- طلب إلغاء البيانات المكررة: `open-sse/services/signatureCache.ts`
- الحقن الفوري للنظام: `open-sse/services/systemPrompt.ts`
- إدارة ميزانية التفكير: `open-sse/services/thinkingBudget.ts`
- توجيه نموذج حرف البدل: `open-sse/services/wildcardRouter.ts`
- إدارة حدود السعر: `open-sse/services/rateLimitManager.ts`
- قاطع الدائرة: `open-sse/services/circuitBreaker.ts`
وحدات طبقة المجال:
- توفر النموذج: `src/lib/domain/modelAvailability.ts`
- قواعد/ميزانيات التكلفة: `src/lib/domain/costRules.ts`
- السياسة الاحتياطية: `src/lib/domain/fallbackPolicy.ts`
- محلل التحرير والسرد: `src/lib/domain/comboResolver.ts`
- سياسة التأمين: `src/lib/domain/lockoutPolicy.ts`
- محرك السياسة: `src/domain/policyEngine.ts` — الإغلاق المركزي ← الميزانية ← التقييم الاحتياطي
- كتالوج رموز الأخطاء: `src/lib/domain/errorCodes.ts`
- معرف الطلب: `src/lib/domain/requestId.ts`
- مهلة الجلب: `src/lib/domain/fetchTimeout.ts`
- طلب القياس عن بعد: `src/lib/domain/requestTelemetry.ts`
- الامتثال/التدقيق: `src/lib/domain/compliance/index.ts`
- عداء التقييم: `src/lib/domain/evalRunner.ts`
- استمرارية حالة المجال: `src/lib/db/domainState.ts` — SQLite CRUD للسلاسل الاحتياطية، والميزانيات، وتاريخ التكلفة، وحالة الإغلاق، وقواطع الدائرة
وحدات موفر OAuth (12 ملفًا فرديًا ضمن `src/lib/oauth/providers/`):
- فهرس التسجيل: `src/lib/oauth/providers/index.ts`
- مقدمو الخدمات الأفراد: `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`
- الغلاف الرقيق: `src/lib/oauth/providers.ts` — إعادة التصدير من الوحدات الفردية
## 3) طبقة الثبات
قاعدة بيانات الحالة الأساسية:
- `src/lib/localDb.ts`
- الملف: `${DATA_DIR}/db.json` (أو `$XDG_CONFIG_HOME/omniroute/db.json` عند التعيين، وإلا `~/.omniroute/db.json`)
- الكيانات:providerConnections، وproviderNodes، وmodelAliases، والمجموعات، وapiKeys، والإعدادات، والتسعير، **customModels**، **proxyConfig**، **ipFilter**، **thinkingBudget**، **systemPrompt**
قاعدة بيانات الاستخدام:
- `src/lib/usageDb.ts`
- الملفات: `${DATA_DIR}/usage.json`، `${DATA_DIR}/log.txt`، `${DATA_DIR}/call_logs/`
- يتبع نفس سياسة الدليل الأساسي مثل `localDb` (`DATA_DIR`، ثم `XDG_CONFIG_HOME/omniroute` عند التعيين)
- مقسمة إلى وحدات فرعية مركزة: `migrations.ts`، `usageHistory.ts`، `costCalculator.ts`، `usageStats.ts`، `callLogs.ts`
قاعدة بيانات حالة المجال (SQLite):
- `src/lib/db/domainState.ts` — عمليات CRUD لحالة المجال
- الجداول (التي تم إنشاؤها في `src/lib/db/core.ts`): `domain_fallback_chains`، `domain_budgets`، `domain_cost_history`، `domain_lockout_state`، `domain_circuit_breakers`
- نمط ذاكرة التخزين المؤقت للكتابة: تعد الخرائط الموجودة في الذاكرة موثوقة في وقت التشغيل؛ تتم كتابة الطفرات بشكل متزامن إلى SQLite؛ تتم استعادة الحالة من قاعدة البيانات عند البداية الباردة
## 4) المصادقة + الأسطح الأمنية
- مصادقة ملف تعريف الارتباط للوحة المعلومات: `src/proxy.ts`، `src/app/api/auth/login/route.ts`
- إنشاء/التحقق من مفتاح واجهة برمجة التطبيقات: `src/shared/utils/apiKey.ts`
- استمرت أسرار الموفر في إدخالات `providerConnections`
- دعم الوكيل الصادر عبر `open-sse/utils/proxyFetch.ts` (env vars) و`open-sse/utils/networkProxy.ts` (قابل للتكوين لكل موفر أو عالمي)
## 5) المزامنة السحابية
- الحرف الأول للمجدول: `src/lib/initCloudSync.ts`، `src/shared/services/initializeCloudSync.ts`
- المهمة الدورية: `src/shared/services/cloudSyncScheduler.ts`
- مسار التحكم: `src/app/api/sync/cloud/route.ts`
## دورة حياة الطلب (`/v1/chat/completions`)
```mermaid
sequenceDiagram
autonumber
participant Client as CLI/SDK Client
participant Route as /api/v1/chat/completions
participant Chat as src/sse/handlers/chat
participant Core as open-sse/handlers/chatCore
participant Model as Model Resolver
participant Auth as Credential Selector
participant Exec as Provider Executor
participant Prov as Upstream Provider
participant Stream as Stream Translator
participant Usage as usageDb
Client->>Route: POST /v1/chat/completions
Route->>Chat: handleChat(request)
Chat->>Model: parse/resolve model or combo
alt Combo model
Chat->>Chat: iterate combo models (handleComboChat)
end
Chat->>Auth: getProviderCredentials(provider)
Auth-->>Chat: active account + tokens/api key
Chat->>Core: handleChatCore(body, modelInfo, credentials)
Core->>Core: detect source format
Core->>Core: translate request to target format
Core->>Exec: execute(provider, transformedBody)
Exec->>Prov: upstream API call
Prov-->>Exec: SSE/JSON response
Exec-->>Core: response + metadata
alt 401/403
Core->>Exec: refreshCredentials()
Exec-->>Core: updated tokens
Core->>Exec: retry request
end
Core->>Stream: translate/normalize stream to client format
Stream-->>Client: SSE chunks / JSON response
Stream->>Usage: extract usage + persist history/log
```
## التحرير والسرد + التدفق الاحتياطي للحساب
```mermaid
flowchart TD
A[Incoming model string] --> B{Is combo name?}
B -- Yes --> C[Load combo models sequence]
B -- No --> D[Single model path]
C --> E[Try model N]
E --> F[Resolve provider/model]
D --> F
F --> G[Select account credentials]
G --> H{Credentials available?}
H -- No --> I[Return provider unavailable]
H -- Yes --> J[Execute request]
J --> K{Success?}
K -- Yes --> L[Return response]
K -- No --> M{Fallback-eligible error?}
M -- No --> N[Return error]
M -- Yes --> O[Mark account unavailable cooldown]
O --> P{Another account for provider?}
P -- Yes --> G
P -- No --> Q{In combo with next model?}
Q -- Yes --> E
Q -- No --> R[Return all unavailable]
```
يتم اتخاذ القرارات الاحتياطية بواسطة `open-sse/services/accountFallback.ts` باستخدام رموز الحالة والاستدلال على رسائل الخطأ.
## دورة حياة OAuth Onboarding وتحديث الرمز المميز
```mermaid
sequenceDiagram
autonumber
participant UI as Dashboard UI
participant OAuth as /api/oauth/[provider]/[action]
participant ProvAuth as Provider Auth Server
participant DB as localDb
participant Test as /api/providers/[id]/test
participant Exec as Provider Executor
UI->>OAuth: GET authorize or device-code
OAuth->>ProvAuth: create auth/device flow
ProvAuth-->>OAuth: auth URL or device code payload
OAuth-->>UI: flow data
UI->>OAuth: POST exchange or poll
OAuth->>ProvAuth: token exchange/poll
ProvAuth-->>OAuth: access/refresh tokens
OAuth->>DB: createProviderConnection(oauth data)
OAuth-->>UI: success + connection id
UI->>Test: POST /api/providers/[id]/test
Test->>Exec: validate credentials / optional refresh
Exec-->>Test: valid or refreshed token info
Test->>DB: update status/tokens/errors
Test-->>UI: validation result
```
يتم تنفيذ التحديث أثناء حركة المرور المباشرة داخل `open-sse/handlers/chatCore.ts` عبر المنفذ `refreshCredentials()`.
## دورة حياة المزامنة السحابية (تمكين / مزامنة / تعطيل)
```mermaid
sequenceDiagram
autonumber
participant UI as Endpoint Page UI
participant Sync as /api/sync/cloud
participant DB as localDb
participant Cloud as External Cloud Sync
participant Claude as ~/.claude/settings.json
UI->>Sync: POST action=enable
Sync->>DB: set cloudEnabled=true
Sync->>DB: ensure API key exists
Sync->>Cloud: POST /sync/{machineId} (providers/aliases/combos/keys)
Cloud-->>Sync: sync result
Sync->>Cloud: GET /{machineId}/v1/verify
Sync-->>UI: enabled + verification status
UI->>Sync: POST action=sync
Sync->>Cloud: POST /sync/{machineId}
Cloud-->>Sync: remote data
Sync->>DB: update newer local tokens/status
Sync-->>UI: synced
UI->>Sync: POST action=disable
Sync->>DB: set cloudEnabled=false
Sync->>Cloud: DELETE /sync/{machineId}
Sync->>Claude: switch ANTHROPIC_BASE_URL back to local (if needed)
Sync-->>UI: disabled
```
يتم تشغيل المزامنة الدورية بواسطة `CloudSyncScheduler` عند تمكين السحابة.
## نموذج البيانات وخريطة التخزين
```mermaid
erDiagram
SETTINGS ||--o{ PROVIDER_CONNECTION : controls
PROVIDER_NODE ||--o{ PROVIDER_CONNECTION : backs_compatible_provider
PROVIDER_CONNECTION ||--o{ USAGE_ENTRY : emits_usage
SETTINGS {
boolean cloudEnabled
number stickyRoundRobinLimit
boolean requireLogin
string password_hash
string fallbackStrategy
json rateLimitDefaults
json providerProfiles
}
PROVIDER_CONNECTION {
string id
string provider
string authType
string name
number priority
boolean isActive
string apiKey
string accessToken
string refreshToken
string expiresAt
string testStatus
string lastError
string rateLimitedUntil
json providerSpecificData
}
PROVIDER_NODE {
string id
string type
string name
string prefix
string apiType
string baseUrl
}
MODEL_ALIAS {
string alias
string targetModel
}
COMBO {
string id
string name
string[] models
}
API_KEY {
string id
string name
string key
string machineId
}
USAGE_ENTRY {
string provider
string model
number prompt_tokens
number completion_tokens
string connectionId
string timestamp
}
CUSTOM_MODEL {
string id
string name
string providerId
}
PROXY_CONFIG {
string global
json providers
}
IP_FILTER {
string mode
string[] allowlist
string[] blocklist
}
THINKING_BUDGET {
string mode
number customBudget
string effortLevel
}
SYSTEM_PROMPT {
boolean enabled
string prompt
string position
}
```
ملفات التخزين المادية:
- الحالة الرئيسية: `${DATA_DIR}/db.json` (أو `$XDG_CONFIG_HOME/omniroute/db.json` عند التعيين، وإلا `~/.omniroute/db.json`)
- إحصائيات الاستخدام: `${DATA_DIR}/usage.json`
- خطوط سجل الطلب: `${DATA_DIR}/log.txt`
- جلسات تصحيح أخطاء المترجم/الطلب الاختيارية: `<repo>/logs/...`
## طبولوجيا النشر
```mermaid
flowchart LR
subgraph LocalHost[Developer Host]
CLI[CLI Tools]
Browser[Dashboard Browser]
end
subgraph ContainerOrProcess[OmniRoute Runtime]
Next[Next.js Server\nPORT=20128]
Core[SSE Core + Executors]
MainDB[(db.json)]
UsageDB[(usage.json/log.txt)]
end
subgraph External[External Services]
Providers[AI Providers]
SyncCloud[Cloud Sync Service]
end
CLI --> Next
Browser --> Next
Next --> Core
Next --> MainDB
Core --> MainDB
Core --> UsageDB
Core --> Providers
Next --> SyncCloud
```
## تعيين الوحدة (القرار الحاسم)
### وحدات المسار وواجهة برمجة التطبيقات
- `src/app/api/v1/*`، `src/app/api/v1beta/*`: واجهات برمجة تطبيقات التوافق
- `src/app/api/v1/providers/[provider]/*`: مسارات مخصصة لكل مزود (الدردشة والتضمين والصور)
- `src/app/api/providers*`: موفر CRUD والتحقق من الصحة والاختبار
- `src/app/api/provider-nodes*`: إدارة العقدة المتوافقة المخصصة
- `src/app/api/provider-models`: إدارة النماذج المخصصة (CRUD)
- `src/app/api/models/catalog`: واجهة برمجة تطبيقات كتالوج النموذج الكامل (جميع الأنواع مجمعة حسب الموفر)
- `src/app/api/oauth/*`: تدفقات OAuth/رمز الجهاز
- `src/app/api/keys*`: دورة حياة مفتاح واجهة برمجة التطبيقات المحلية
- `src/app/api/models/alias`: إدارة الاسم المستعار
- `src/app/api/combos*`: إدارة التحرير والسرد الاحتياطية
- `src/app/api/pricing`: تجاوزات التسعير لحساب التكلفة
- `src/app/api/settings/proxy`: تكوين الوكيل (GET/PUT/DELETE)
- `src/app/api/settings/proxy/test`: اختبار اتصال الوكيل الصادر (POST)
- `src/app/api/usage/*`: واجهات برمجة تطبيقات الاستخدام والسجلات
- `src/app/api/sync/*` + `src/app/api/cloud/*`: المزامنة السحابية والمساعدون الذين يواجهون السحابة
- `src/app/api/cli-tools/*`: كاتب/أداة فحص تكوين CLI المحلية
- `src/app/api/settings/ip-filter`: قائمة IP المسموح بها/القائمة المحظورة (GET/PUT)
- `src/app/api/settings/thinking-budget`: تكوين ميزانية الرمز المميز (GET/PUT)
- `src/app/api/settings/system-prompt`: موجه النظام العالمي (GET/PUT)
- `src/app/api/sessions`: قائمة الجلسة النشطة (GET)
- `src/app/api/rate-limits`: حالة حد السعر لكل حساب (GET)
### التوجيه والتنفيذ الأساسي
- `src/sse/handlers/chat.ts`: تحليل الطلب، ومعالجة التحرير والسرد، وحلقة اختيار الحساب
- `open-sse/handlers/chatCore.ts`: الترجمة، إرسال المنفذ، معالجة إعادة المحاولة/التحديث، إعداد الدفق
- `open-sse/executors/*`: سلوك الشبكة والتنسيق الخاص بالموفر
### سجل الترجمة ومحولات التنسيق
- `open-sse/translator/index.ts`: تسجيل المترجم وتنسيقه
- طلب المترجمين: `open-sse/translator/request/*`
- مترجمو الرد: `open-sse/translator/response/*`
- ثوابت التنسيق: `open-sse/translator/formats.ts`
### المثابرة
- `src/lib/localDb.ts`: التكوين/الحالة المستمرة
- `src/lib/usageDb.ts`: سجل الاستخدام وسجلات الطلبات المتجددة
## تغطية منفذي الخدمة (نمط الإستراتيجية)
كل مزود لديه منفذ متخصص يمتد `BaseExecutor` (في `open-sse/executors/base.ts`)، والذي يوفر بناء عنوان URL، وإنشاء الرأس، وإعادة المحاولة مع التراجع الأسي، وخطافات تحديث بيانات الاعتماد، وطريقة التنسيق `execute()`.
| المنفذ | المزود (المقدمون) | التعامل الخاص |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| `DefaultExecutor` | OpenAI، Claude، Gemini، Qwen، iFlow، OpenRouter، GLM، Kimi، MiniMax، DeepSeek، Groq، xAI، Mistral، Perplexity، Together، Fireworks، Cerebras، Cohere، NVIDIA | تكوين عنوان URL/الرأس الديناميكي لكل مزود |
| `AntigravityExecutor` | جوجل مكافحة الجاذبية | معرفات المشروع/الجلسة المخصصة، إعادة المحاولة بعد التحليل |
| `CodexExecutor` | OpenAI Codex | يحقن تعليمات النظام، ويفرض جهدًا منطقيًا |
| `CursorExecutor` | بيئة تطوير متكاملة للمؤشر | بروتوكول ConnectRPC، تشفير Protobuf، طلب التوقيع عبر المجموع الاختباري |
| `GithubExecutor` | جيثب مساعد الطيار | تحديث الرمز المميز لـ Copilot، ورؤوس محاكاة VSCode |
| `KiroExecutor` | AWS CodeWhisperer/كيرو | تنسيق AWS EventStream الثنائي → تحويل SSE |
| `GeminiCLIExecutor` | الجوزاء CLI | دورة تحديث رمز OAuth المميز لـ Google |
يستخدم جميع الموفرين الآخرين (بما في ذلك العقد المتوافقة المخصصة) `DefaultExecutor`.
## مصفوفة توافق الموفر
| مقدم | تنسيق | مصادقة | تيار | غير دفق | تحديث الرمز المميز | واجهة برمجة تطبيقات الاستخدام |
| ----------------------------------- | ---------------- | --------------------------- | --------------- | ------- | ------------------ | ----------------------------- |
| كلود | كلود | مفتاح API / OAuth | ✅ | ✅ | ✅ | ⚠️ المشرف فقط |
| الجوزاء | الجوزاء | مفتاح API / OAuth | ✅ | ✅ | ✅ | ⚠️ وحدة التحكم السحابية |
| الجوزاء CLI | الجوزاء-cli | أووث | ✅ | ✅ | ✅ | ⚠️ وحدة التحكم السحابية |
| مكافحة الجاذبية | ضد الجاذبية | أووث | ✅ | ✅ | ✅ | ✅ الحصة الكاملة API |
| أوبن آي | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| الدستور الغذائي | openai-responses | أووث | ✅ مجبور | ❌ | ✅ | ✅ حدود المعدل |
| جيثب مساعد الطيار | أوبيناي | OAuth + رمز مساعد الطيار | ✅ | ✅ | ✅ | ✅ لقطات الحصص |
| المؤشر | المؤشر | المجموع الاختباري المخصص | ✅ | ✅ | ❌ | ❌ |
| كيرو | كيرو | AWS SSO OIDC | ✅(ايفنت ستريم) | ❌ | ✅ | ✅ حدود الاستخدام |
| كوين | أوبيناي | أووث | ✅ | ✅ | ✅ | ⚠️ حسب الطلب |
| اي فلو | أوبيناي | OAuth (أساسي) | ✅ | ✅ | ✅ | ⚠️ حسب الطلب |
| اوبن راوتر | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| جي إل إم/كيمي/ميني ماكس | كلود | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| ديب سيك | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| جروك | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| xAI (جروك) | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| ميسترال | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| الحيرة | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| معا منظمة العفو الدولية | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| الألعاب النارية منظمة العفو الدولية | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| المخيخ | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| كوهير | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
| نفيديا نيم | أوبيناي | مفتاح واجهة برمجة التطبيقات | ✅ | ✅ | ❌ | ❌ |
## تنسيق تغطية الترجمة
تتضمن تنسيقات المصدر المكتشفة ما يلي:
- `openai`
- `openai-responses`
- `claude`
- `gemini`
تتضمن التنسيقات المستهدفة ما يلي:
- دردشة/ردود OpenAI
- كلود
- الجوزاء/الجوزاء-CLI/الظرف المضاد للجاذبية
- كيرو
- المؤشر
تستخدم الترجمات **OpenAI كتنسيق مركزي** — تمر جميع التحويلات عبر OpenAI كتنسيق وسيط:
```
Source Format → OpenAI (hub) → Target Format
```
يتم تحديد الترجمات ديناميكيًا استنادًا إلى شكل حمولة المصدر والتنسيق المستهدف للموفر.
طبقات معالجة إضافية في مسار الترجمة:
- **تطهير الاستجابة** — يزيل الحقول غير القياسية من استجابات تنسيق OpenAI (سواء المتدفقة أو غير المتدفقة) لضمان الامتثال الصارم لـ SDK
- **تطبيع الدور** — تحويل `developer``system` للأهداف غير التابعة لـ OpenAI؛ يدمج `system``user` للنماذج التي ترفض دور النظام (GLM، ERNIE)
- **فكر في استخراج العلامات** — يوزع كتل `<think>...</think>` من المحتوى إلى حقل `reasoning_content`
- **الإخراج المنظم** — يحول OpenAI `response_format.json_schema` إلى `responseMimeType` + `responseSchema` الخاص بـ Gemini
## نقاط نهاية واجهة برمجة التطبيقات المدعومة
| نقطة النهاية | تنسيق | معالج |
| -------------------------------------------------- | ------------------ | ------------------------------------------------------------------------- |
| `POST /v1/chat/completions` | دردشة OpenAI | `src/sse/handlers/chat.ts` |
| `POST /v1/messages` | رسائل كلود | نفس المعالج (تم اكتشافه تلقائيًا) |
| `POST /v1/responses` | ردود OpenAI | `open-sse/handlers/responsesHandler.ts` |
| `POST /v1/embeddings` | تضمينات OpenAI | `open-sse/handlers/embeddings.ts` |
| `GET /v1/embeddings` | قائمة النماذج | طريق API |
| `POST /v1/images/generations` | صور OpenAI | `open-sse/handlers/imageGeneration.ts` |
| `GET /v1/images/generations` | قائمة النماذج | طريق API |
| `POST /v1/providers/{provider}/chat/completions` | دردشة OpenAI | مخصص لكل مزود مع التحقق من صحة النموذج |
| `POST /v1/providers/{provider}/embeddings` | تضمينات OpenAI | مخصص لكل مزود مع التحقق من صحة النموذج |
| `POST /v1/providers/{provider}/images/generations` | صور OpenAI | مخصص لكل مزود مع التحقق من صحة النموذج |
| `POST /v1/messages/count_tokens` | عدد كلود توكن | طريق API |
| `GET /v1/models` | قائمة نماذج OpenAI | مسار واجهة برمجة التطبيقات (الدردشة + التضمين + الصورة + النماذج المخصصة) |
| `GET /api/models/catalog` | كتالوج | جميع النماذج مجمعة حسب الموفر + النوع |
| `POST /v1beta/models/*:streamGenerateContent` | مولود برج الجوزاء | طريق API |
| `GET/PUT/DELETE /api/settings/proxy` | تكوين الوكيل | تكوين وكيل الشبكة |
| `POST /api/settings/proxy/test` | اتصال الوكيل | نقطة نهاية اختبار صحة الوكيل/الاتصال |
| `GET/POST/DELETE /api/provider-models` | نماذج مخصصة | إدارة النماذج المخصصة لكل مزود |
## تجاوز المعالج
يعترض معالج التجاوز (`open-sse/utils/bypassHandler.ts`) طلبات "الرمي" المعروفة من Claude CLI - أصوات التمهيد، واستخراج العناوين، وعدد الرموز المميزة - ويعيد **استجابة زائفة** دون استهلاك الرموز المميزة للموفر الرئيسي. يتم تشغيل هذا فقط عندما يحتوي `User-Agent` على `claude-cli`.
## طلب خط أنابيب المسجل
يوفر مسجل الطلب (`open-sse/utils/requestLogger.ts`) مسارًا لتسجيل تصحيح الأخطاء مكون من 7 مراحل، معطل افتراضيًا، وممكن عبر `ENABLE_REQUEST_LOGS=true`:
```
1_req_client.json → 2_req_source.json → 3_req_openai.json → 4_req_target.json
→ 5_res_provider.txt → 6_res_openai.txt → 7_res_client.txt
```
تتم كتابة الملفات إلى `<repo>/logs/<session>/` لكل جلسة طلب.
## أوضاع الفشل والمرونة
## 1) توفر الحساب/المزود
- فترة تباطؤ حساب الموفر عند حدوث أخطاء عابرة/معدل/مصادقة
- احتياطي الحساب قبل فشل الطلب
- نموذج التحرير والسرد الاحتياطي عند استنفاد مسار النموذج/المزود الحالي
## 2) انتهاء صلاحية الرمز المميز
- الفحص المسبق والتحديث مع إعادة المحاولة لموفري الخدمة القابلين للتحديث
- 401/403 إعادة المحاولة بعد محاولة التحديث في المسار الأساسي
## 3) سلامة الدفق
- وحدة تحكم تيار قطع الاتصال
- دفق الترجمة مع تدفق نهاية الدفق ومعالجة `[DONE]`
- احتياطي تقدير الاستخدام عندما تكون البيانات الوصفية لاستخدام الموفر مفقودة
## 4) تدهور المزامنة السحابية
- ظهرت أخطاء المزامنة ولكن يستمر وقت التشغيل المحلي
- يحتوي المجدول على منطق قادر على إعادة المحاولة، ولكن التنفيذ الدوري يستدعي حاليًا مزامنة المحاولة الواحدة بشكل افتراضي
## 5) سلامة البيانات
- ترحيل/إصلاح شكل قاعدة البيانات للمفاتيح المفقودة
- ضمانات إعادة تعيين JSON الفاسدة لـ localDb وuseDb
## إمكانية الملاحظة والإشارات التشغيلية
مصادر رؤية وقت التشغيل:
- سجلات وحدة التحكم من `src/sse/utils/logger.ts`
- مجاميع الاستخدام لكل طلب في `usage.json`
- سجل حالة الطلب النصي في `log.txt`
- سجلات الطلب/الترجمة العميقة الاختيارية ضمن `logs/` عندما `ENABLE_REQUEST_LOGS=true`
- نقاط نهاية استخدام لوحة المعلومات (`/api/usage/*`) لاستهلاك واجهة المستخدم
## الحدود الحساسة للأمن
- سر JWT (`JWT_SECRET`) يؤمن التحقق/التوقيع على ملف تعريف الارتباط لجلسة لوحة المعلومات
- يجب تجاوز الاحتياطي الأولي لكلمة المرور (`INITIAL_PASSWORD`، الافتراضي `123456`) في عمليات النشر الحقيقية
- سر HMAC لمفتاح API (`API_KEY_SECRET`) يؤمن تنسيق مفتاح API المحلي الذي تم إنشاؤه
- تظل أسرار الموفر (مفاتيح/رموز واجهة برمجة التطبيقات) موجودة في قاعدة البيانات المحلية ويجب حمايتها على مستوى نظام الملفات
- تعتمد نقاط نهاية المزامنة السحابية على مصادقة مفتاح API + دلالات معرف الجهاز
## مصفوفة البيئة ووقت التشغيل
متغيرات البيئة المستخدمة بشكل نشط بواسطة التعليمات البرمجية:
- التطبيق/المصادقة: `JWT_SECRET`، `INITIAL_PASSWORD`
- التخزين: `DATA_DIR`
- سلوك العقدة المتوافقة: `ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE`
- تجاوز قاعدة التخزين الاختيارية (Linux/macOS عند إلغاء تعيين `DATA_DIR`): `XDG_CONFIG_HOME`
- التجزئة الأمنية: `API_KEY_SECRET`، `MACHINE_ID_SALT`
- التسجيل: `ENABLE_REQUEST_LOGS`
- عنوان URL للمزامنة/السحابة: `NEXT_PUBLIC_BASE_URL`، `NEXT_PUBLIC_CLOUD_URL`
- الوكيل الصادر: `HTTP_PROXY`، `HTTPS_PROXY`، `ALL_PROXY`، `NO_PROXY` ومتغيرات الأحرف الصغيرة
- علامات ميزات SOCKS5: `ENABLE_SOCKS5_PROXY`، `NEXT_PUBLIC_ENABLE_SOCKS5_PROXY`
- مساعدو النظام الأساسي/وقت التشغيل (ليس التكوين الخاص بالتطبيق): `APPDATA`، `NODE_ENV`، `PORT`، `HOSTNAME`
## الملاحظات المعمارية المعروفة
1. يتشارك `usageDb` و`localDb` الآن نفس سياسة الدليل الأساسي (`DATA_DIR` -> `XDG_CONFIG_HOME/omniroute` -> `~/.omniroute`) مع ترحيل الملفات القديمة.
2. يقوم `/api/v1/route.ts` بإرجاع قائمة نماذج ثابتة وهو ليس مصدر النماذج الرئيسي الذي يستخدمه `/v1/models`.
3. يقوم مسجل الطلب بكتابة الرؤوس/النص الكامل عند تمكينه؛ التعامل مع دليل السجل على أنه حساس.
4. يعتمد سلوك السحابة على `NEXT_PUBLIC_BASE_URL` الصحيح وإمكانية الوصول إلى نقطة نهاية السحابة.
5. تم نشر الدليل `open-sse/` باسم `@omniroute/open-sse` **حزمة مساحة عمل npm**. يقوم كود المصدر باستيراده عبر `@omniroute/open-sse/...` (تم حله بواسطة Next.js `transpilePackages`). لا تزال مسارات الملفات في هذا المستند تستخدم اسم الدليل `open-sse/` لتحقيق الاتساق.
6. تستخدم المخططات الموجودة في لوحة المعلومات **Recharts** (المستندة إلى SVG) لتصورات التحليلات التفاعلية التي يمكن الوصول إليها (المخططات الشريطية لاستخدام النموذج، والجداول التفصيلية للموفرين مع معدلات النجاح).
7. تستخدم اختبارات E2E **Playwright** (`tests/e2e/`)، ويتم تشغيلها عبر `npm run test:e2e`. تستخدم اختبارات الوحدة **مشغل اختبار Node.js** (`tests/unit/`)، ويتم تشغيله عبر `npm run test:plan3`. كود المصدر ضمن `src/` هو **TypeScript** (`.ts`/`.tsx`)؛ تظل مساحة العمل `open-sse/` JavaScript (`.js`).
8. تم تنظيم صفحة الإعدادات في 5 علامات تبويب: الأمان، التوجيه (6 إستراتيجيات عالمية: التعبئة أولاً، جولة روبن، p2c، عشوائي، الأقل استخدامًا، تحسين التكلفة)، المرونة (حدود المعدل القابلة للتحرير، قاطع الدائرة، السياسات)، الذكاء الاصطناعي (ميزانية التفكير، موجه النظام، ذاكرة التخزين المؤقت السريعة)، المتقدم (الوكيل).
## قائمة التحقق التشغيلية
- البناء من المصدر: `npm run build`
- إنشاء صورة Docker: `docker build -t omniroute .`
- ابدأ الخدمة وتحقق:
- `GET /api/settings`
- `GET /api/v1/models`
- يجب أن يكون عنوان URL الأساسي لهدف واجهة سطر الأوامر هو `http://<host>:20128/v1` عندما يكون `PORT=20128`

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