mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-29 18:42:17 +03:00
Compare commits
5 Commits
dependabot
...
fix/releas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f3cb0392a | ||
|
|
767d846a44 | ||
|
|
2af28c4e2c | ||
|
|
6f1f1668dd | ||
|
|
8aa3f1e5ab |
120
.env.example
120
.env.example
@@ -403,17 +403,8 @@ ALLOW_API_KEY_REVEAL=false
|
||||
# OMNIROUTE_CHAT_LARGE_BODY_BYTES=262144
|
||||
# Actual-byte hard cap enforced during bounded ingestion. Default 52428800 (50 MB).
|
||||
# OMNIROUTE_CHAT_HARD_MAX_BODY_BYTES=52428800
|
||||
# Legacy request-COUNT cap (#503-fanout). Now binds only when explicitly set here —
|
||||
# left unset, heavyweight admission is gated by OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES below
|
||||
# instead (an auto-derived byte budget), fixing coding-agent fan-out (multiple
|
||||
# subagents/CLIs) collapsing to an effective concurrency of ~1 and 503ing.
|
||||
# Maximum heavyweight requests simultaneously admitted in one process. Default 1.
|
||||
# OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT=1
|
||||
# Override for the auto-derived ingest byte budget (#503-fanout). Default: 25% of the
|
||||
# process's effective memory ceiling (V8 heap limit, or the tighter cgroup/container
|
||||
# limit) divided by an 8x transient-amplification factor, clamped between 8 MiB and
|
||||
# 2 GiB; explicit overrides are clamped to the same safe range. Read
|
||||
# chatAdmission.maxInflightBytes/budgetSource at /api/monitoring/health before overriding.
|
||||
# OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES=134217728
|
||||
# Heap-pressure shed ratio (heapUsed/heap_size_limit) for the structural admission gate
|
||||
# (#10183, #10268): a second concurrent heavyweight request past OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT
|
||||
# is only shed with a retryable 503 when the heap is ALSO under this much pressure — on a
|
||||
@@ -612,9 +603,11 @@ CLOUD_URL=
|
||||
# Default: http://localhost:20128
|
||||
NEXT_PUBLIC_BASE_URL=http://localhost:20128
|
||||
|
||||
# Highest-priority OmniRoute public origin override, also used by non-dashboard
|
||||
# public-origin validation. Set it when external clients reach OmniRoute through
|
||||
# a stable LAN, tunnel, or public origin that differs from its internal URL.
|
||||
# Browser-facing OmniRoute origin for generated assets in API responses.
|
||||
# Highest-priority public origin override; also used by non-dashboard public-origin validation.
|
||||
# Used by: chatgpt-web image generation cache URLs (/v1/chatgpt-web/image/<id>).
|
||||
# Set this when OpenWebUI or another relay reaches OmniRoute by an internal URL
|
||||
# but the user's browser must fetch images from a LAN, tunnel, or public origin.
|
||||
# Do not include /v1; if included accidentally it will be normalized away.
|
||||
# OMNIROUTE_PUBLIC_BASE_URL=http://192.168.0.15:20128
|
||||
|
||||
@@ -627,6 +620,28 @@ NEXT_PUBLIC_BASE_URL=http://localhost:20128
|
||||
# Used by: open-sse/config/providerPluginManifestUrl.ts. Defaults to http.
|
||||
# OMNIROUTE_PUBLIC_PROTOCOL=http
|
||||
|
||||
# Max wait time for an async chatgpt-web image to land via the celsius
|
||||
# WebSocket, in milliseconds. Default 180000 (3 minutes). Increase during
|
||||
# upstream queue-deep windows ("Lots of people are creating images right now").
|
||||
# OMNIROUTE_CGPT_WEB_IMAGE_TIMEOUT_MS=180000
|
||||
|
||||
# Total in-memory byte budget for the chatgpt-web image cache (used to serve
|
||||
# /v1/chatgpt-web/image/<id>), in megabytes. Default 256. Lower this if you
|
||||
# run OmniRoute on a memory-constrained host; raise it if image generation
|
||||
# is heavy and clients are racing the 30-minute TTL.
|
||||
# OMNIROUTE_CGPT_WEB_IMAGE_CACHE_MAX_MB=256
|
||||
|
||||
# Overall wait budget for a chatgpt-web GPT-5.5 Pro background-poll handoff,
|
||||
# in milliseconds. Default 1200000 (20 minutes). Pro reasoning runs are slow
|
||||
# and complete out-of-band, so OmniRoute polls until the answer lands or this
|
||||
# budget elapses. Raise it if Pro requests time out before finishing.
|
||||
# OMNIROUTE_CGPT_WEB_PRO_TIMEOUT_MS=1200000
|
||||
|
||||
# Interval between chatgpt-web GPT-5.5 Pro background-poll attempts, in
|
||||
# milliseconds. Default 4000 (4 seconds). Lower for snappier completion at the
|
||||
# cost of more upstream polling; raise to reduce request volume.
|
||||
# OMNIROUTE_CGPT_WEB_PRO_POLL_INTERVAL_MS=4000
|
||||
|
||||
# Public cloud URL — client-side mirror of CLOUD_URL.
|
||||
NEXT_PUBLIC_CLOUD_URL=
|
||||
|
||||
@@ -901,11 +916,6 @@ NEXT_PUBLIC_ENABLE_SOCKS5_PROXY=true
|
||||
# web_fetch). Default: 60000. Used by: open-sse/mcp-server/fetchTimeout.ts
|
||||
# OMNIROUTE_MCP_UPSTREAM_TIMEOUT_MS=60000
|
||||
|
||||
# Maximum number of local-corpus index instances cached in memory.
|
||||
# Used by: src/lib/localCorpus/configured.ts — bounds the LRU cache of
|
||||
# LocalCorpusIndex objects (one per indexed root directory). Default: 5.
|
||||
# OMNIROUTE_CORPUS_CACHE_SIZE=5
|
||||
|
||||
# Model catalog sync interval in hours.
|
||||
# Used by: src/shared/services/modelSyncScheduler.ts — periodic model refresh.
|
||||
# Default: 24
|
||||
@@ -1239,6 +1249,17 @@ GITHUB_OAUTH_CLIENT_ID=Iv1.b507a08c87ecfe98
|
||||
# VISION_BRIDGE_BASE_URL=
|
||||
# VISION_BRIDGE_API_KEY=
|
||||
|
||||
# ── Raycast Pro (local auto-import) ──
|
||||
# Raycast Pro AI is a reverse-engineered, unofficial API — local/personal use
|
||||
# only (no OAuth client_id/secret; token is captured via macOS Auto-Import
|
||||
# from the Keychain + local Raycast SQLite DB, or pasted manually). These
|
||||
# vars are optional manual overrides used by open-sse/services/raycast.ts
|
||||
# and the direct-probe benchmark script scripts/raycast/usage-benchmark.mjs.
|
||||
# RAYCAST_BEARER_TOKEN=
|
||||
# RAYCAST_DEVICE_ID=
|
||||
# RAYCAST_AID=
|
||||
# RAYCAST_SIG_SECRET=
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# ⚠️ GOOGLE OAUTH (Antigravity) & OTHER PROVIDERS — REMOTE SERVERS
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -1455,6 +1476,17 @@ CURSOR_USER_AGENT="Cursor/3.4"
|
||||
# FIRECRAWL_BASE_URL=https://api.firecrawl.dev
|
||||
# FIRECRAWL_TIMEOUT_MS=30000 # Per-request timeout (default: 30000 = 30s)
|
||||
|
||||
# ── ChatGPT TLS sidecar (Firefox-fingerprinted client) ──
|
||||
# Used by: open-sse/services/chatgptTlsClient.ts — wire-level timeout for
|
||||
# the bogdanfinn/tls-client koffi binding and the JS-side grace window
|
||||
# layered on top of it when the native library is wedged.
|
||||
# OMNIROUTE_CHATGPT_TLS_TIMEOUT_MS=60000
|
||||
# OMNIROUTE_CHATGPT_TLS_GRACE_MS=10000
|
||||
# Max wait for the FIRST streamed byte from the ChatGPT TLS sidecar before the
|
||||
# request is aborted as a dead stream, in milliseconds. Default 30000 (30s).
|
||||
# Raise it if upstream cold-starts routinely exceed the window.
|
||||
# OMNIROUTE_CHATGPT_STREAM_FIRST_BYTE_TIMEOUT_MS=30000
|
||||
|
||||
# ── Claude TLS sidecar (Chromium-fingerprinted client) ──
|
||||
# Used by: open-sse/services/claudeTlsClient.ts — wire-level timeout for
|
||||
# the bogdanfinn/tls-client koffi binding and the JS-side grace window
|
||||
@@ -1763,13 +1795,6 @@ APP_LOG_TO_FILE=true
|
||||
# SEMANTIC_CACHE_MAX_BYTES=4194304 # Max total cache size in bytes (default: 4 MB)
|
||||
# SEMANTIC_CACHE_TTL_MS=1800000 # Cache entry TTL (default: 30 minutes)
|
||||
|
||||
# ── Local corpus index cache ──
|
||||
# How many local-corpus roots keep a live in-memory index at once. The cache is
|
||||
# LRU: reaching the limit evicts the least-recently-used root's index, which is
|
||||
# then rebuilt on its next query. Clamped to a minimum of 1; a non-numeric value
|
||||
# falls back to the default. Used by: src/lib/localCorpus/configured.ts
|
||||
# OMNIROUTE_CORPUS_CACHE_SIZE=5
|
||||
|
||||
# ── In-memory log buffers ──
|
||||
# Maximum recent stream events kept in memory for the Dashboard live view.
|
||||
# STREAM_HISTORY_MAX=50
|
||||
@@ -1902,6 +1927,12 @@ APP_LOG_TO_FILE=true
|
||||
# Base backoff after a transient 408 response (ms); five attempts maximum.
|
||||
# ADOBE_FIREFLY_SUBMIT_BASE_DELAY_MS=8000
|
||||
|
||||
# ── Microsoft Designer Web (Image Generation) ──
|
||||
# Polling config for the microsoft-designer-web submit-then-poll image job.
|
||||
# Used by: open-sse/handlers/imageGeneration/providers/designerWeb.ts
|
||||
# DESIGNER_WEB_POLL_TIMEOUT_MS=60000 # Max wait for job completion (default: 60s)
|
||||
# DESIGNER_WEB_POLL_INTERVAL_MS=2000 # Poll frequency (default: 2s)
|
||||
|
||||
# ── Adobe Firefly (Image Upscale) ──
|
||||
# Base delay (ms) for the submit-retry exponential backoff when Adobe Firefly's
|
||||
# upscale job submission is rate-limited. Used by:
|
||||
@@ -1959,26 +1990,6 @@ APP_LOG_TO_FILE=true
|
||||
# Custom path to cloudflared binary for tunnel management.
|
||||
# Used by: src/lib/cloudflaredTunnel.ts
|
||||
# CLOUDFLARED_BIN=/usr/local/bin/cloudflared
|
||||
#
|
||||
# Transport protocol for the tunnel. One of: http2 (default), quic, auto.
|
||||
# CLOUDFLARED_PROTOCOL=http2
|
||||
#
|
||||
# ── Named / persistent tunnel (stable hostname) ──
|
||||
# By default OmniRoute runs an ephemeral quick tunnel (random *.trycloudflare.com
|
||||
# URL that changes on every restart). To bind a stable, named hostname instead,
|
||||
# create a locally-managed tunnel with the cloudflared CLI:
|
||||
# cloudflared tunnel login
|
||||
# cloudflared tunnel create <name>
|
||||
# cloudflared tunnel route dns <name> ai.example.com
|
||||
# then write a ~/.cloudflared/config.yml with `tunnel:`, `credentials-file:`, and
|
||||
# `ingress:` rules routing your hostname to http://localhost:<PORT> (default 20128).
|
||||
# Point OmniRoute at that config to switch into named-tunnel mode — it runs
|
||||
# `cloudflared tunnel --config <path> run`.
|
||||
# CLOUDFLARED_CONFIG=/home/you/.cloudflared/config.yml
|
||||
# CLOUDFLARED_HOSTNAME is optional — when unset, OmniRoute reads the public hostname
|
||||
# from the config's first ingress rule. Set it to override what is reported as
|
||||
# publicUrl/apiUrl.
|
||||
# CLOUDFLARED_HOSTNAME=ai.example.com
|
||||
|
||||
# ── Search cache ──
|
||||
# TTL for search API response caching (Perplexity, Brave, etc.).
|
||||
@@ -2521,6 +2532,11 @@ APP_LOG_TO_FILE=true
|
||||
# Used by: src/lib/jobs/backupScheduleJob.ts
|
||||
# OMNIROUTE_BACKUP_SCHEDULE_JOB_INTERVAL_MS=30000
|
||||
|
||||
# ── TLS sidecar override ──
|
||||
# Used by: open-sse/services/chatgptTlsClient.ts tests. Production deployments
|
||||
# should leave this unset; the sidecar is auto-managed.
|
||||
# OMNIROUTE_TLS_PROXY_URL=
|
||||
|
||||
# ── Skills sandbox (experimental) ──
|
||||
# Used by: src/lib/skills/builtins.ts. All values support comma lists where
|
||||
# noted in the source.
|
||||
@@ -3017,17 +3033,3 @@ QUOTA_STORE_DRIVER=sqlite
|
||||
# without a configured budget are always considered affordable. Requires the
|
||||
# provider_quota_state table (migration 148).
|
||||
# OMNIROUTE_QUOTA_AWARE_ROUTING=0
|
||||
|
||||
# ─── LOCAL CORPUS (opt-in document index) ───
|
||||
# Size of the in-memory LRU index cache for the local document corpus used by
|
||||
# corpus-aware retrieval. Higher values keep more index entries hot.
|
||||
# Used by: src/lib/localCorpus/configured.ts
|
||||
# OMNIROUTE_CORPUS_CACHE_SIZE=5
|
||||
|
||||
# Service-worker cache-busting id for the PWA shell (#11779). NEXT_PUBLIC_SW_BUILD_ID is
|
||||
# derived at build time from OMNIROUTE_SW_BUILD_ID, then SOURCE_VERSION (set by some PaaS
|
||||
# builders), then the git SHA — override only when the build cannot see git. Used by:
|
||||
# next.config.mjs, scripts/build/assembleStandalone.mjs, src/shared/components/PwaRegister.tsx.
|
||||
#OMNIROUTE_SW_BUILD_ID=2026-08-28T12-00-00
|
||||
#SOURCE_VERSION=abcdef0123456789
|
||||
#NEXT_PUBLIC_SW_BUILD_ID=abcdef0123456789
|
||||
|
||||
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@@ -1,16 +1,9 @@
|
||||
name: Build App
|
||||
|
||||
# Manual-only since #11946. The hosted 7 GB runner can no longer build this tree — 19 of
|
||||
# the last 30 runs died with "The runner has received a shutdown signal" (VM out of
|
||||
# memory) ~8 min into `next build`, release/v3.8.51 itself included, even with the 10 GB
|
||||
# swapfile below. Triggered on `push: branches: ["**"]` it painted every branch and every
|
||||
# PR red while producing an artefact nothing downloads. The bundle is validated where a
|
||||
# build actually fits:
|
||||
# - main: ci.yml `Build` (self-hosted omni-build pool) on every merge
|
||||
# - release/**: nightly-release-green.yml (same pool, continuous)
|
||||
# Dispatch this workflow by hand when a hosted build artefact is genuinely needed.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: ["**"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -93,7 +93,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
@@ -127,8 +126,6 @@ jobs:
|
||||
- run: npm run check:route-validation:t06
|
||||
- run: npm run check:any-budget:t11
|
||||
- run: npm run check:provider-consistency
|
||||
- run: npm run check:model-lifecycle
|
||||
- run: npm run check:provider-asset-provenance
|
||||
- run: npm run check:fetch-targets
|
||||
- run: npm run check:deps
|
||||
- run: npm run check:file-size
|
||||
|
||||
10
.github/workflows/dast-smoke.yml
vendored
10
.github/workflows/dast-smoke.yml
vendored
@@ -1,15 +1,7 @@
|
||||
name: DAST smoke (PR)
|
||||
# PRs into main only since #11946. The job's "Build CLI bundle" step is a backend-only
|
||||
# `next build`; on the hosted 7 GB runner it fits main's tree (~5.5 min) but dies on
|
||||
# release/v3.8.51 (VM shutdown ~7 min in, before the server even starts), and because the
|
||||
# job is continue-on-error the result was a permanently red advisory check on every
|
||||
# release PR — noise, not signal. DAST coverage for release/** lives on the nightly rail
|
||||
# (nightly-schemathesis.yml, nightly-llm-security.yml); dispatch this workflow by hand
|
||||
# to smoke a release branch on demand.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
branches: ["main", "release/**"]
|
||||
# Runner-cost guard (#8084): the CLI-bundle build alone is 6-11min; a docs-only PR
|
||||
# cannot change DAST behavior, so skip the whole workflow for pure docs/markdown
|
||||
# changes. Any code path in the diff still runs the full smoke.
|
||||
|
||||
20
.github/workflows/docker-publish.yml
vendored
20
.github/workflows/docker-publish.yml
vendored
@@ -68,16 +68,6 @@ jobs:
|
||||
"$EVENT_NAME" "$REF_TYPE" "$REF_NAME" "$INPUT_VERSION" "$DEFAULT_BRANCH")
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Frozen release branches keep receiving coordination commits after the
|
||||
# next cycle becomes the default branch. They must not overwrite :next,
|
||||
# but that expected no-op is not a workflow failure.
|
||||
if [ "$VERSION" = "skip" ]; then
|
||||
echo "promote_latest=false" >> "$GITHUB_OUTPUT"
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Skipping Docker publish from non-default release branch: $REF_NAME"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 2) Decide whether to promote :latest. Floating channels are never
|
||||
# eligible, and the helper independently fails closed for non-semver.
|
||||
PROMOTE="false"
|
||||
@@ -171,7 +161,7 @@ jobs:
|
||||
${{ env.IMAGE_NAME }}
|
||||
${{ env.GHCR_IMAGE_NAME }}
|
||||
cache-from: type=gha,scope=docker-${{ matrix.arch }}
|
||||
cache-to: type=gha,scope=docker-${{ matrix.arch }},mode=max,ignore-error=true
|
||||
cache-to: type=gha,scope=docker-${{ matrix.arch }},mode=max
|
||||
no-cache: false
|
||||
env:
|
||||
DOCKER_BUILDKIT_INLINE_CACHE: 1
|
||||
@@ -188,7 +178,7 @@ jobs:
|
||||
${{ env.IMAGE_NAME }}
|
||||
${{ env.GHCR_IMAGE_NAME }}
|
||||
cache-from: type=gha,scope=docker-web-${{ matrix.arch }}
|
||||
cache-to: type=gha,scope=docker-web-${{ matrix.arch }},mode=max,ignore-error=true
|
||||
cache-to: type=gha,scope=docker-web-${{ matrix.arch }},mode=max
|
||||
no-cache: false
|
||||
env:
|
||||
DOCKER_BUILDKIT_INLINE_CACHE: 1
|
||||
@@ -213,7 +203,7 @@ jobs:
|
||||
${{ env.IMAGE_NAME }}
|
||||
${{ env.GHCR_IMAGE_NAME }}
|
||||
cache-from: type=gha,scope=docker-bun-base-${{ matrix.arch }}
|
||||
cache-to: type=gha,scope=docker-bun-base-${{ matrix.arch }},mode=max,ignore-error=true
|
||||
cache-to: type=gha,scope=docker-bun-base-${{ matrix.arch }},mode=max
|
||||
no-cache: false
|
||||
env:
|
||||
DOCKER_BUILDKIT_INLINE_CACHE: 1
|
||||
@@ -238,7 +228,7 @@ jobs:
|
||||
${{ env.IMAGE_NAME }}
|
||||
${{ env.GHCR_IMAGE_NAME }}
|
||||
cache-from: type=gha,scope=docker-bun-web-${{ matrix.arch }}
|
||||
cache-to: type=gha,scope=docker-bun-web-${{ matrix.arch }},mode=max,ignore-error=true
|
||||
cache-to: type=gha,scope=docker-bun-web-${{ matrix.arch }},mode=max
|
||||
no-cache: false
|
||||
env:
|
||||
DOCKER_BUILDKIT_INLINE_CACHE: 1
|
||||
@@ -499,7 +489,7 @@ jobs:
|
||||
- name: Upload Trivy SARIF to Security tab
|
||||
if: needs.prepare.outputs.version != 'main'
|
||||
continue-on-error: true
|
||||
uses: github/codeql-action/upload-sarif@v4.37.8
|
||||
uses: github/codeql-action/upload-sarif@v4.37.7
|
||||
with:
|
||||
sarif_file: trivy-results.sarif
|
||||
category: trivy-image
|
||||
|
||||
17
.github/workflows/electron-release.yml
vendored
17
.github/workflows/electron-release.yml
vendored
@@ -4,6 +4,10 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
# A dispatch builds the ref it is dispatched ON (`gh workflow run … --ref v3.8.50` rebuilds
|
||||
# that tag; `--ref main` builds the repaired line). The ref is deliberately NOT an input:
|
||||
# CodeQL flags an input-controlled checkout next to the npm cache on the default branch as
|
||||
# cache poisoning (actions/cache-poisoning/poisonable-step), and `github.ref` is trusted.
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
@@ -81,9 +85,6 @@ jobs:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
# workflow_dispatch: build the tag being (re)built, not the dispatching branch. On a
|
||||
# tag push this resolves to the same commit.
|
||||
ref: ${{ needs.validate.outputs.version }}
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
@@ -169,9 +170,6 @@ jobs:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
# workflow_dispatch: build the tag being (re)built, not the dispatching branch. On a
|
||||
# tag push this resolves to the same commit.
|
||||
ref: ${{ needs.validate.outputs.version }}
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
@@ -358,8 +356,6 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
# Source archives + SBOM come from the tag being released, not the dispatching branch.
|
||||
ref: ${{ needs.validate.outputs.version }}
|
||||
|
||||
# `merge-multiple` is deliberately OFF. It resolves same-name collisions by ARRIVAL
|
||||
# ORDER, and the two macOS jobs each emit their own `latest-mac.yml` listing only their
|
||||
@@ -417,7 +413,10 @@ jobs:
|
||||
tag_name: ${{ needs.validate.outputs.version }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
generate_release_notes: true
|
||||
# Only on the tag push. A re-attach dispatch runs against a release whose curated
|
||||
# notes already exist, and `true` APPENDS GitHub's auto-generated "What's Changed"
|
||||
# block to them (v3.8.50, run 33238093090: +1,416 chars on a 121 KB body).
|
||||
generate_release_notes: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
fail_on_unmatched_files: false
|
||||
files: |
|
||||
release-assets/*.dmg
|
||||
|
||||
10
.github/workflows/nightly-llm-security.yml
vendored
10
.github/workflows/nightly-llm-security.yml
vendored
@@ -10,10 +10,7 @@ permissions:
|
||||
jobs:
|
||||
promptfoo-guard:
|
||||
name: promptfoo — injection guard (block mode, no secret)
|
||||
# #11965: this job runs a backend-only `next build`; the hosted 7 GB runner cannot build
|
||||
# release/v3.8.51 (VM shutdown ~7 min in), so it targets the box's light pool (`omni-light`:
|
||||
# two listeners, jobs ≤ ~6 GB). Falls back to hosted when USE_VPS_RUNNER is off.
|
||||
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
@@ -49,10 +46,7 @@ jobs:
|
||||
|
||||
garak:
|
||||
name: garak probes (skip without provider secret)
|
||||
# #11965: this job runs a backend-only `next build`; the hosted 7 GB runner cannot build
|
||||
# release/v3.8.51 (VM shutdown ~7 min in), so it targets the box's light pool (`omni-light`:
|
||||
# two listeners, jobs ≤ ~6 GB). Falls back to hosted when USE_VPS_RUNNER is off.
|
||||
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
|
||||
runs-on: ubuntu-latest
|
||||
# NOTE: the `secrets` context is NOT available in a job-level `if:` — referencing
|
||||
# it there makes GitHub reject the file on push (startup_failure on every push).
|
||||
# Map the secret into a job-level env and gate each step on a presence check, so
|
||||
|
||||
5
.github/workflows/nightly-resilience.yml
vendored
5
.github/workflows/nightly-resilience.yml
vendored
@@ -78,10 +78,7 @@ jobs:
|
||||
|
||||
a11y:
|
||||
name: A11y axe (nightly, freeze-and-alert)
|
||||
# #11965: this job runs a backend-only `next build`; the hosted 7 GB runner cannot build
|
||||
# release/v3.8.51 (VM shutdown ~7 min in), so it targets the box's light pool (`omni-light`:
|
||||
# two listeners, jobs ≤ ~6 GB). Falls back to hosted when USE_VPS_RUNNER is off.
|
||||
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
|
||||
runs-on: ubuntu-latest
|
||||
# The Playwright webServer (`start` mode) builds Next via build-next-isolated.mjs and
|
||||
# boots the standalone server itself (waits on /api/monitoring/health, 15min webServer
|
||||
# timeout). Unlike the per-PR test-e2e job, this nightly job has no pre-built artifact,
|
||||
|
||||
5
.github/workflows/nightly-schemathesis.yml
vendored
5
.github/workflows/nightly-schemathesis.yml
vendored
@@ -10,10 +10,7 @@ permissions:
|
||||
jobs:
|
||||
schemathesis:
|
||||
name: Schemathesis — OpenAPI contract fuzz (advisory)
|
||||
# #11965: this job runs a backend-only `next build`; the hosted 7 GB runner cannot build
|
||||
# release/v3.8.51 (VM shutdown ~7 min in), so it targets the box's light pool (`omni-light`:
|
||||
# two listeners, jobs ≤ ~6 GB). Falls back to hosted when USE_VPS_RUNNER is off.
|
||||
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
27
.github/workflows/quality.yml
vendored
27
.github/workflows/quality.yml
vendored
@@ -61,9 +61,6 @@ jobs:
|
||||
name: Build (advisory)
|
||||
needs: changes
|
||||
# FORK PRs ONLY. build.yml's `Fast Production Build` triggers on `push: branches: ["**"]`
|
||||
# (#11946, 2026-08-29: build.yml is now workflow_dispatch-only — the hosted runner cannot
|
||||
# build this tree in any profile, 8/8 recent fork PRs included — so own-origin PRs rely on
|
||||
# ci.yml `Build` after merge to main and on nightly-release-green for release/**.)
|
||||
# and runs `build:release` — a superset of this job — so for an own-origin branch this job
|
||||
# was building the same tree twice. A fork contributor pushes to THEIR repo, so that push
|
||||
# never fires here, and this is the only pre-merge build signal they get. Measured
|
||||
@@ -192,11 +189,8 @@ jobs:
|
||||
.eslintcache
|
||||
.eslintcache-complexity
|
||||
key: eslint-${{ runner.os }}-${{ hashFiles('eslint.config.mjs', 'eslint.complexity-ratchets.config.mjs', 'config/quality/eslint-suppressions.json', 'package-lock.json') }}
|
||||
# No restore-keys fallback on purpose (#11600, P-II.1 of the v3.8.50 postmortem): a
|
||||
# cache built under a different suppressions file / lint config / lockfile reports
|
||||
# stale per-file verdicts, which is exactly how 215 pre-existing errors stayed
|
||||
# invisible for a whole cycle. Exact key or a cold full lint (~13 min) — never a
|
||||
# partial cache from another configuration.
|
||||
restore-keys: |
|
||||
eslint-${{ runner.os }}-
|
||||
# Security scanners — same hardened install as ci.yml quality-extended
|
||||
# (gh release download = authenticated, 5000 req/hr; curl to api.github.com
|
||||
# is rate-limited to 60/hr and silently no-ops when throttled). The blocking
|
||||
@@ -275,11 +269,11 @@ jobs:
|
||||
run: |
|
||||
set -uo pipefail
|
||||
gates=(
|
||||
provider-consistency provider-asset-provenance fetch-targets deps file-size error-helper
|
||||
provider-consistency fetch-targets deps file-size error-helper
|
||||
migration-numbering public-creds db-rules known-symbols
|
||||
route-guard-membership test-discovery test-runner-api
|
||||
mutation-test-coverage any-budget:t11 build-scope pack-policy
|
||||
complexity-ratchets model-lifecycle
|
||||
complexity-ratchets
|
||||
cycles lockfile duplication dead-code type-coverage compression-budget
|
||||
# #8781: open-sse workspace typecheck gate — the workspace imports @/ which
|
||||
# escapes to src/ via undeclared path aliases. See check-open-sse-typecheck.mjs.
|
||||
@@ -466,12 +460,6 @@ jobs:
|
||||
# cache restore saturating the link), while the tests themselves tied, 2m54 vs 2m31. So
|
||||
# self-hosted is strictly worse here and there is nothing to configure.
|
||||
runs-on: ubuntu-latest
|
||||
# A shard finishes in ~10 min. Without a ceiling a hung test process holds the PR for
|
||||
# GitHub's 6 h default: on 2026-08-28 shard 1/4 sat 64 min without a line of output
|
||||
# (twice, same spot — a timing race, gone on the third run) while the other three
|
||||
# shards were long green. 30 min = 3x the normal wall-clock; a shard that needs more
|
||||
# is a hang, not a slow run, and a fast red with a re-run beats a silent 6 h hold.
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -536,11 +524,8 @@ jobs:
|
||||
.eslintcache
|
||||
.eslintcache-complexity
|
||||
key: eslint-${{ runner.os }}-${{ hashFiles('eslint.config.mjs', 'eslint.complexity-ratchets.config.mjs', 'config/quality/eslint-suppressions.json', 'package-lock.json') }}
|
||||
# No restore-keys fallback on purpose (#11600, P-II.1 of the v3.8.50 postmortem): a
|
||||
# cache built under a different suppressions file / lint config / lockfile reports
|
||||
# stale per-file verdicts, which is exactly how 215 pre-existing errors stayed
|
||||
# invisible for a whole cycle. Exact key or a cold full lint (~13 min) — never a
|
||||
# partial cache from another configuration.
|
||||
restore-keys: |
|
||||
eslint-${{ runner.os }}-
|
||||
- name: ESLint (baseline congelado — warning novo = vermelho)
|
||||
# lint:json writes the report; --max-warnings 0 keeps no-new-warnings policy.
|
||||
run: npm run lint:json -- --max-warnings 0
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"clean": "rm -rf dist",
|
||||
"test": "node --import tsx/esm --test tests/scaffold.test.ts tests/auth.test.ts tests/options-schema.test.ts tests/multi-instance.test.ts tests/fetch-interceptor.test.ts tests/provider.test.ts tests/gemini-sanitize.test.ts tests/combos.test.ts tests/config-shim.test.ts tests/features.test.ts tests/feature-defaults.test.ts tests/usable-combo.test.ts tests/disk-snapshot-perms.test.ts tests/fork-features.test.ts tests/auto-combo-context.test.ts tests/provider-id-routing.test.ts tests/management-read-token.test.ts tests/auto-sync.test.ts tests/model-allowlist.test.ts tests/log-level.test.ts tests/effort-tier-variants.test.ts tests/naming.test.ts tests/free-budget-magnitude.test.ts",
|
||||
"test": "node --import tsx/esm --test tests/scaffold.test.ts tests/auth.test.ts tests/options-schema.test.ts tests/multi-instance.test.ts tests/fetch-interceptor.test.ts tests/provider.test.ts tests/gemini-sanitize.test.ts tests/combos.test.ts tests/config-shim.test.ts tests/features.test.ts tests/feature-defaults.test.ts tests/usable-combo.test.ts tests/disk-snapshot-perms.test.ts tests/fork-features.test.ts tests/auto-combo-context.test.ts tests/provider-id-routing.test.ts tests/management-read-token.test.ts tests/auto-sync.test.ts tests/model-allowlist.test.ts tests/log-level.test.ts tests/effort-tier-variants.test.ts",
|
||||
"prepublishOnly": "npm run clean && npm run build && npm test"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
@@ -23,11 +23,27 @@ const ALIAS_UPPER_MAX_CHARS = 5;
|
||||
|
||||
// ── Auto Combo Types ─────────────────────────────────────────────────────
|
||||
|
||||
export type AutoVariant = "coding" | "fast" | "cheap" | "offline" | "smart" | "lkgp";
|
||||
export type AutoVariant =
|
||||
| "coding"
|
||||
| "fast"
|
||||
| "cheap"
|
||||
| "offline"
|
||||
| "smart"
|
||||
| "lkgp";
|
||||
|
||||
export const AUTO_VARIANTS: AutoVariant[] = ["coding", "fast", "cheap", "offline", "smart", "lkgp"];
|
||||
export const AUTO_VARIANTS: AutoVariant[] = [
|
||||
"coding",
|
||||
"fast",
|
||||
"cheap",
|
||||
"offline",
|
||||
"smart",
|
||||
"lkgp",
|
||||
];
|
||||
|
||||
export const AUTO_VARIANT_DESCRIPTIONS: Record<AutoVariant | "default", string> = {
|
||||
export const AUTO_VARIANT_DESCRIPTIONS: Record<
|
||||
AutoVariant | "default",
|
||||
string
|
||||
> = {
|
||||
default: "Best provider via scoring",
|
||||
coding: "Quality-first for code tasks",
|
||||
fast: "Latency-optimized routing",
|
||||
@@ -67,15 +83,24 @@ function titleCaseAlias(alias: string): string {
|
||||
* 3. Neither → undefined.
|
||||
*/
|
||||
export function shortProviderLabel(
|
||||
enrichment: { providerDisplayName?: string; providerAlias?: string } | undefined
|
||||
enrichment:
|
||||
| { providerDisplayName?: string; providerAlias?: string }
|
||||
| undefined,
|
||||
): string | undefined {
|
||||
if (!enrichment) return undefined;
|
||||
const raw =
|
||||
typeof enrichment.providerDisplayName === "string" ? enrichment.providerDisplayName.trim() : "";
|
||||
typeof enrichment.providerDisplayName === "string"
|
||||
? enrichment.providerDisplayName.trim()
|
||||
: "";
|
||||
if (raw.length > 0 && raw.length <= PROVIDER_LABEL_MAX_CHARS) return raw;
|
||||
const alias = typeof enrichment.providerAlias === "string" ? enrichment.providerAlias.trim() : "";
|
||||
const alias =
|
||||
typeof enrichment.providerAlias === "string"
|
||||
? enrichment.providerAlias.trim()
|
||||
: "";
|
||||
if (alias.length > 0) {
|
||||
return alias.length <= ALIAS_UPPER_MAX_CHARS ? alias.toUpperCase() : titleCaseAlias(alias);
|
||||
return alias.length <= ALIAS_UPPER_MAX_CHARS
|
||||
? alias.toUpperCase()
|
||||
: titleCaseAlias(alias);
|
||||
}
|
||||
// Long displayName with no alias to fall back on: keep the long label
|
||||
// rather than dropping the provider prefix entirely.
|
||||
@@ -106,33 +131,10 @@ export function normaliseFreeLabel(name: string): string {
|
||||
|
||||
// ── Free Budget Formatting ────────────────────────────────────────────────
|
||||
|
||||
/** Scales, largest first, so the unit is chosen by descending magnitude. */
|
||||
const TOKEN_UNITS = [
|
||||
[1e9, "B"],
|
||||
[1e6, "M"],
|
||||
[1e3, "K"],
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Format a token count as a short magnitude string: `25M`, `1.5K`, `999`.
|
||||
*
|
||||
* The unit has to be picked from the value that will actually be *printed*,
|
||||
* not from the raw input. `toFixed(1)` rounds to the nearest tenth, so at the
|
||||
* K scale 999_950 and above render as `1000.0` — and by then the M branch has
|
||||
* already been skipped, producing `1000K` for a number that is `1M`. The same
|
||||
* carry turns just under a billion into `1000M`. When the rounded value reaches
|
||||
* the next scale, re-render at that scale instead.
|
||||
*/
|
||||
function fmtTokens(n: number): string {
|
||||
for (let i = 0; i < TOKEN_UNITS.length; i++) {
|
||||
const [scale, suffix] = TOKEN_UNITS[i]!;
|
||||
if (n < scale) continue;
|
||||
const value = Number((n / scale).toFixed(1));
|
||||
// `Number()` also drops a trailing `.0`, which the previous regex did.
|
||||
if (value < 1000 || i === 0) return `${value}${suffix}`;
|
||||
const [nextScale, nextSuffix] = TOKEN_UNITS[i - 1]!;
|
||||
return `${Number((n / nextScale).toFixed(1))}${nextSuffix}`;
|
||||
}
|
||||
if (n >= 1e9) return (n / 1e9).toFixed(1).replace(/\.0$/, "") + "B";
|
||||
if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, "") + "M";
|
||||
if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, "") + "K";
|
||||
return String(n);
|
||||
}
|
||||
|
||||
@@ -182,11 +184,15 @@ export function formatFreeBudget(params: {
|
||||
*/
|
||||
export function formatAutoComboName(
|
||||
variant: AutoVariant | undefined,
|
||||
candidateCount?: number
|
||||
candidateCount?: number,
|
||||
): string {
|
||||
const label = variant ? variant.charAt(0).toUpperCase() + variant.slice(1) : "Default";
|
||||
const label = variant
|
||||
? variant.charAt(0).toUpperCase() + variant.slice(1)
|
||||
: "Default";
|
||||
const count =
|
||||
typeof candidateCount === "number" && candidateCount > 0 ? ` (${candidateCount}p)` : "";
|
||||
typeof candidateCount === "number" && candidateCount > 0
|
||||
? ` (${candidateCount}p)`
|
||||
: "";
|
||||
return `Auto: ${label}${count}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Magnitude-crossover regression for the free-budget suffix
|
||||
* (`formatFreeBudget` -> `fmtTokens` in @omniroute/opencode-plugin/src/naming.ts).
|
||||
*
|
||||
* `fmtTokens` picked its unit from the raw input and then rounded with
|
||||
* `toFixed(1)`. Rounding can carry a value into the next magnitude *after* that
|
||||
* branch has been skipped, so 999_950..999_999 rendered as "1000K" rather than
|
||||
* "1M", and just under a billion rendered as "1000M" rather than "1B".
|
||||
*
|
||||
* These budgets are not always round numbers: `monthlyTokens` is derived from the
|
||||
* remote Radar feed (`tokensPerMonth`) and can be replaced wholesale by a
|
||||
* user-local override, so the crossover band is reachable with real data.
|
||||
*
|
||||
* Kept in its own file rather than added to naming.test.ts so this does not
|
||||
* collide with the coverage being added for `formatFreeBudget` in #11660.
|
||||
*/
|
||||
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { formatFreeBudget } from "../src/naming.js";
|
||||
|
||||
/** `recurring-daily` is the shortest path from a token count to a rendered suffix. */
|
||||
const daily = (monthlyTokens: number) =>
|
||||
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens }).replace(" tokens/day", "");
|
||||
|
||||
test("fmtTokens: a rounded K value that reaches 1000 is promoted to M", () => {
|
||||
// 999_950 is the true boundary, not 999_999: toFixed(1) rounds to the nearest
|
||||
// tenth, so 999.95K is the first value that carries to "1000.0".
|
||||
assert.equal(daily(999_950), "1M");
|
||||
assert.equal(daily(999_999), "1M");
|
||||
});
|
||||
|
||||
test("fmtTokens: a rounded M value that reaches 1000 is promoted to B", () => {
|
||||
assert.equal(daily(999_950_000), "1B");
|
||||
assert.equal(daily(999_999_999), "1B");
|
||||
});
|
||||
|
||||
test("fmtTokens: values just below the rounding boundary keep their own unit", () => {
|
||||
// The promotion must not fire early — 999.9K still rounds to 999.9, not 1000.
|
||||
assert.equal(daily(999_949), "999.9K");
|
||||
assert.equal(daily(999_499), "999.5K");
|
||||
assert.equal(daily(999_499_999), "999.5M");
|
||||
});
|
||||
|
||||
test("fmtTokens: ordinary magnitudes are unchanged", () => {
|
||||
assert.equal(daily(0), "0");
|
||||
assert.equal(daily(999), "999");
|
||||
assert.equal(daily(1_000), "1K");
|
||||
assert.equal(daily(1_500), "1.5K");
|
||||
assert.equal(daily(1_000_000), "1M");
|
||||
assert.equal(daily(1_500_000), "1.5M");
|
||||
assert.equal(daily(25_000_000), "25M");
|
||||
assert.equal(daily(1_234_567), "1.2M");
|
||||
assert.equal(daily(1_000_000_000), "1B");
|
||||
assert.equal(daily(2_500_000_000), "2.5B");
|
||||
});
|
||||
|
||||
test("fmtTokens: B is the top unit, so a carry there has nowhere to go", () => {
|
||||
// Deliberately pinned: promoting past B would need a unit that does not exist,
|
||||
// so "1000B" is the intended output rather than an oversight.
|
||||
assert.equal(daily(999_999_999_999), "1000B");
|
||||
});
|
||||
|
||||
test("formatFreeBudget: the promotion applies to every token-bearing branch", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-monthly", monthlyTokens: 999_999 }),
|
||||
"1M tokens/month"
|
||||
);
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-credit", creditTokens: 999_999 }),
|
||||
"1M credits"
|
||||
);
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "one-time-initial", creditTokens: 999_999 }),
|
||||
"1M credits (one-time)"
|
||||
);
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Tests for `formatFreeBudget` (@omniroute/opencode-plugin/src/naming.ts):
|
||||
* formats a free-tier model's budget info into a short human-readable
|
||||
* suffix, branching on `freeType`.
|
||||
*/
|
||||
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { formatFreeBudget, type FreeModelFreeType } from "../src/naming.js";
|
||||
|
||||
test("formatFreeBudget: recurring-daily formats tokens/day", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens: 25_000_000 }),
|
||||
"25M tokens/day"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: recurring-monthly formats tokens/month", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-monthly", monthlyTokens: 1_000_000 }),
|
||||
"1M tokens/month"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: recurring-credit formats credits", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-credit", creditTokens: 10_000_000 }),
|
||||
"10M credits"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: one-time-initial formats credits with (one-time) suffix", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "one-time-initial", creditTokens: 1_000_000 }),
|
||||
"1M credits (one-time)"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: keyless has no token/credit args", () => {
|
||||
assert.equal(formatFreeBudget({ freeType: "keyless" }), "(keyless)");
|
||||
});
|
||||
|
||||
test("formatFreeBudget: discontinued has no token/credit args", () => {
|
||||
assert.equal(formatFreeBudget({ freeType: "discontinued" }), "(discontinued)");
|
||||
});
|
||||
|
||||
test("formatFreeBudget: missing token/credit counts default to 0", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-daily" }),
|
||||
"0 tokens/day"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: unrecognised freeType falls through to the default branch", () => {
|
||||
// `freeType` is populated from catalog data at runtime, so a value the
|
||||
// build doesn't know about is reachable even though TypeScript treats the
|
||||
// `default:` arm as dead code for a well-typed caller.
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "some-future-type" as FreeModelFreeType }),
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: sub-1K token count is not abbreviated", () => {
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens: 500 }),
|
||||
"500 tokens/day"
|
||||
);
|
||||
});
|
||||
|
||||
test("formatFreeBudget: the 999_999 rounding wart is fixed — promotes to 1M", () => {
|
||||
// `toFixed(1)` rounds 999999/1e3 up to "1000.0" before the `>= 1e6` threshold
|
||||
// check has a chance to apply. fmtTokens now promotes a rounded-up "1000" in
|
||||
// any unit to the next unit up, so this correctly reads "1M" instead of the
|
||||
// old "1000K" wart.
|
||||
assert.equal(
|
||||
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens: 999_999 }),
|
||||
"1M tokens/day"
|
||||
);
|
||||
});
|
||||
22
AGENTS.md
22
AGENTS.md
@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
|
||||
|
||||
## Project at a Glance
|
||||
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 351 LLM providers, auto-fallback.
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 352 LLM providers, auto-fallback.
|
||||
|
||||
| Layer | Location | Purpose |
|
||||
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -56,7 +56,7 @@ Repository map and Reference Documentation sections below.
|
||||
| Translators | `open-sse/translator/` | Format conversion (OpenAI↔Claude↔Gemini) |
|
||||
| Transformer | `open-sse/transformer/` | Responses API ↔ Chat Completions |
|
||||
| Services | `open-sse/services/` | Combo routing, rate limits, caching, etc |
|
||||
| Database | `src/lib/db/` | SQLite domain modules (166 migrations) |
|
||||
| Database | `src/lib/db/` | SQLite domain modules (160 migrations) |
|
||||
| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback logic |
|
||||
| MCP Server | `open-sse/mcp-server/` | 110 tools (44 canonical + memory/skill/GitHub/pool/gamification/plugin/Notion/Obsidian/local-corpus/RTK modules), 3 transports (stdio / SSE / Streamable HTTP), 33 scopes |
|
||||
| A2A Server | `src/lib/a2a/` | JSON-RPC 2.0 agent protocol |
|
||||
@@ -83,7 +83,7 @@ Client → /v1/chat/completions (Next.js route)
|
||||
|
||||
API routes follow a consistent pattern: `Route → CORS preflight → Zod body validation → Optional auth (extractApiKey/isValidApiKey) → API key policy enforcement → Handler delegation (open-sse)`. No global Next.js middleware — interception is route-specific.
|
||||
|
||||
**Combo routing** (`open-sse/services/combo.ts`): 19 public strategies (priority, weighted, fill-first, round-robin, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline). Each target calls `handleSingleModel()` which wraps `handleChatCore()` with per-target error handling and circuit breaker checks. The `fusion` strategy is the exception: it fans out to a panel of models in parallel, then a judge model synthesizes one final answer (`open-sse/services/fusion.ts`). See `docs/routing/AUTO-COMBO.md` for the 15-factor Auto-Combo scoring + the full strategy table and `docs/architecture/RESILIENCE_GUIDE.md` for the 3 resilience layers.
|
||||
**Combo routing** (`open-sse/services/combo.ts`): 19 public strategies (priority, weighted, fill-first, round-robin, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline). Each target calls `handleSingleModel()` which wraps `handleChatCore()` with per-target error handling and circuit breaker checks. The `fusion` strategy is the exception: it fans out to a panel of models in parallel, then a judge model synthesizes one final answer (`open-sse/services/fusion.ts`). See `docs/routing/AUTO-COMBO.md` for the 14-factor Auto-Combo scoring + the full strategy table and `docs/architecture/RESILIENCE_GUIDE.md` for the 3 resilience layers.
|
||||
|
||||
---
|
||||
|
||||
@@ -197,7 +197,7 @@ baseCooldownMs * 2 ** failureIndex;
|
||||
The anti-thundering-herd guard prevents concurrent failures on the same connection from
|
||||
repeatedly extending the cooldown or double-incrementing `backoffLevel`.
|
||||
|
||||
Terminal states are not cooldowns. `banned`, `expired` (which becomes terminal only after N bounded retries via `EXPIRED_RETRY_MAX`), and `credits_exhausted` are
|
||||
Terminal states are not cooldowns. `banned`, `expired`, and `credits_exhausted` are
|
||||
intended to stay unavailable until credentials/settings change or an operator resets
|
||||
them. Do not overwrite terminal states with transient cooldown state.
|
||||
|
||||
@@ -411,7 +411,7 @@ For any non-trivial change, read the matching deep-dive first:
|
||||
| Repo navigation | `docs/architecture/REPOSITORY_MAP.md` |
|
||||
| Architecture | `docs/architecture/ARCHITECTURE.md` |
|
||||
| Engineering reference | `docs/architecture/CODEBASE_DOCUMENTATION.md` |
|
||||
| Auto-Combo (15-factor scoring, 19 strategies) | `docs/routing/AUTO-COMBO.md` |
|
||||
| Auto-Combo (14-factor scoring, 19 strategies) | `docs/routing/AUTO-COMBO.md` |
|
||||
| Resilience (3 mechanisms) | `docs/architecture/RESILIENCE_GUIDE.md` |
|
||||
| Reasoning replay | `docs/routing/REASONING_REPLAY.md` |
|
||||
| Skills framework | `docs/frameworks/SKILLS.md` |
|
||||
@@ -594,18 +594,6 @@ inside your feature branch (a base-red fix is its own freeze-gated `fix/release-
|
||||
PR); and if you must open a PR anyway, add `⚠️ base-red inherited: #<issue>` to the PR body so
|
||||
reviewers and CI babysitters do not chase ghosts.
|
||||
|
||||
### Sync-back landings are fast-forward, never squash
|
||||
|
||||
A `main → release/vX+1` sync-back (Phase 5 of `/generate-release`, or any later "bring main's
|
||||
post-release commits over" PR) must reach the release branch as the merge commit it already is:
|
||||
`git merge-base --is-ancestor origin/release/vX+1 <head>` then
|
||||
`git push origin <head>:refs/heads/release/vX+1` (GitHub marks the PR merged). Squash-merging it
|
||||
drops `main` from the release branch's ancestry and the next sync-back re-conflicts on every file
|
||||
main touched (551 conflicts on the v3.8.50 → v3.8.51 sync before the two-step merge). After
|
||||
landing, `git merge-base --is-ancestor origin/main origin/release/vX+1` must be true — and check
|
||||
that `config/quality/eslint-suppressions.json` / `quality-baseline.json` carried main's freezes
|
||||
(they merge as "ours" silently). Details: `.agents/skills/generate-release/phases/phase-5-next-cycle.md`.
|
||||
|
||||
---
|
||||
|
||||
## Upstream contributions
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -89,18 +89,6 @@
|
||||
- **feat(cli):** run `omniroute serve --tray` as a detached desktop process after server and tray readiness, with graphical login auto-start support.
|
||||
- **feat(routing):** add client-, provider-, and model-neutral exclusive managed session connection leases with API-key-bound generation fencing, durable SQLite ownership, explicit allowlist policy, and bounded 429 capacity retry semantics.
|
||||
|
||||
## [3.8.51] — TBD
|
||||
|
||||
_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
---
|
||||
|
||||
## [3.8.50] — 2026-08-25
|
||||
|
||||
_Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._
|
||||
@@ -3116,6 +3104,10 @@ _Living section — regenerated 2026-07-19 from all 306 cycle commits (bump 2c62
|
||||
- chore(tests): fix all 70 failing `test:vitest:ui` tests across 30 files (was advisory/parked) — root causes were 15 node:test-authored `.tsx` files never collected by vitest, a missing `window.matchMedia` jsdom polyfill, stale assertions against a redesigned BuildTab wizard / CompressionHub Phase-2 UI, and one obsolete test for a retired Plans screen; suite is now 158/158 files, 870/870 tests green (promotion to blocking is a follow-up)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 🙌 Contributors
|
||||
|
||||
Thanks to everyone whose work landed in v3.8.49:
|
||||
|
||||
38
Dockerfile
38
Dockerfile
@@ -104,7 +104,7 @@ RUN test -f package-lock.json \
|
||||
# instead of `npx --yes`, which would install an arbitrary registry version
|
||||
# on-demand and run its lifecycle scripts (Sonar docker:S6505).
|
||||
#
|
||||
# tls-client-node (claude-web/grok-web/lmarena/perplexity-web TLS
|
||||
# tls-client-node (chatgpt-web/claude-web/grok-web/lmarena/perplexity-web TLS
|
||||
# impersonation) hits the same --ignore-scripts wall: its own postinstall.js
|
||||
# fetches a platform .so/.dylib/.dll from the bogdanfinn/tls-client GitHub
|
||||
# Releases API and is never invoked when npm ci skips lifecycle scripts. Unlike
|
||||
@@ -184,29 +184,19 @@ ENV NODE_OPTIONS="--max-old-space-size=${OMNIROUTE_BUILD_MEMORY_MB}"
|
||||
# silently leaving no standalone bundle. Next derives the worker count from
|
||||
# CIRCLE_NODE_TOTAL (workers = N-1). (#10060)
|
||||
#
|
||||
# Lowered 8 → 3 (7 workers → 2) in #11419, then 3 → 2 (2 workers → 1) in #7518.
|
||||
# Every page-data worker inherits NODE_OPTIONS above, so the ceiling is per
|
||||
# PROCESS, not per build: 7 workers on a 16 GB GitHub runner (ubuntu-24.04 /
|
||||
# ubuntu-24.04-arm, 4 vCPU) exhausted the host and buildkit failed the whole
|
||||
# step with `ResourceExhausted: ... cannot allocate memory`. The compile phase
|
||||
# always finished ("✓ Compiled successfully in 4.2min"); the kernel killed the
|
||||
# build right after "Collecting page data using N workers".
|
||||
#
|
||||
# #11419's first fix (8 → 3) modeled the per-worker peak as an INFERENCE
|
||||
# (2560 MB, guessed from "7 workers didn't fit") and assumed the parent
|
||||
# process's RSS tracked the V8 heap ceiling. Both assumptions were wrong: a
|
||||
# live VPS reproduction (issue #7518, dmesg OOM-killer report) measured the
|
||||
# real per-process RSS directly at ~4.5 GB, independent of the NODE_OPTIONS
|
||||
# heap flag (Turbopack itself is native/Rust, outside the V8 heap) — and it
|
||||
# applies to the parent process too, not just workers. 2 workers (3 processes
|
||||
# × 4.5 GB = 13.5 GB) still didn't fit the 12.288 GB (75%) budget on a 16 GB
|
||||
# runner, matching the still-live publish failures after #11419 merged. 1
|
||||
# worker (2 processes × 4.5 GB = 9 GB) fits with headroom to spare.
|
||||
# tests/unit/docker-build-memory-budget.test.ts does the arithmetic against
|
||||
# the measured figure and fails if either knob is raised past what a 16 GB
|
||||
# runner holds. Override for a big builder: `--build-arg
|
||||
# OMNIROUTE_BUILD_WORKERS=8`.
|
||||
ARG OMNIROUTE_BUILD_WORKERS=2
|
||||
# Lowered 8 → 3 (7 workers → 2). Every page-data worker inherits NODE_OPTIONS
|
||||
# above, so the ceiling is per PROCESS, not per build: 7 workers on a 16 GB
|
||||
# GitHub runner (ubuntu-24.04 / ubuntu-24.04-arm, 4 vCPU) exhausted the host and
|
||||
# buildkit failed the whole step with `ResourceExhausted: ... cannot allocate
|
||||
# memory`. The compile phase always finished ("✓ Compiled successfully in
|
||||
# 4.2min"); the kernel killed the build right after "Collecting page data using
|
||||
# 7 workers". It was intermittent for a while and went 100% on 2026-08-22, which
|
||||
# is what a threshold being crossed by ordinary codebase growth looks like.
|
||||
# tests/unit/docker-build-memory-budget.test.ts does the arithmetic and fails if
|
||||
# either knob is raised past what a 16 GB runner holds. 2 workers also stops
|
||||
# oversubscribing the runner's 4 vCPU, which 7 did. Override for a big builder:
|
||||
# `--build-arg OMNIROUTE_BUILD_WORKERS=8`.
|
||||
ARG OMNIROUTE_BUILD_WORKERS=3
|
||||
ENV CIRCLE_NODE_TOTAL=${OMNIROUTE_BUILD_WORKERS}
|
||||
|
||||
COPY . ./
|
||||
|
||||
@@ -19,31 +19,23 @@ RUN apt-get update \
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
|
||||
# Cache dependency layer
|
||||
COPY package.json bun.lock* pnpm-workspace.yaml* ./
|
||||
COPY open-sse/package.json ./open-sse/package.json
|
||||
COPY packages/ ./packages/
|
||||
|
||||
# Root postinstall helpers needed during bun install lifecycle
|
||||
COPY scripts/build/ ./scripts/build/
|
||||
COPY scripts/dev/sync-env.mjs ./scripts/dev/sync-env.mjs
|
||||
COPY . .
|
||||
|
||||
# Fast Bun native package install
|
||||
RUN bun install --include=optional --quiet
|
||||
|
||||
# Compile native better-sqlite3 Node-API addon under Bun
|
||||
RUN if [ -d "node_modules/better-sqlite3" ]; then \
|
||||
(cd node_modules/better-sqlite3 && bunx node-gyp rebuild); \
|
||||
fi
|
||||
|
||||
# Fetch tls-client-node native binary if script exists
|
||||
RUN if [ -f "node_modules/tls-client-node/scripts/postinstall.js" ] && [ ! -d "node_modules/tls-client-node/bin" ]; then \
|
||||
RUN if [ -f "node_modules/tls-client-node/scripts/postinstall.js" ]; then \
|
||||
bun node_modules/tls-client-node/scripts/postinstall.js || true; \
|
||||
fi
|
||||
|
||||
# Smoke check native database driver used by Bun (bun:sqlite)
|
||||
RUN bun -e "import { Database } from 'bun:sqlite'; const db = new Database(':memory:'); db.query('SELECT 1 AS ok').get(); db.close(); console.log('bun:sqlite smoke: OK');"
|
||||
|
||||
COPY . .
|
||||
|
||||
# Turbopack is supported on Bun 1.4 + Next 16.3; override via --build-arg OMNIROUTE_USE_TURBOPACK=0 if needed
|
||||
ARG OMNIROUTE_USE_TURBOPACK=1
|
||||
ENV OMNIROUTE_USE_TURBOPACK=${OMNIROUTE_USE_TURBOPACK}
|
||||
# Disable Turbopack for Bun builder stage (Turbopack V8 internal worker bindings require Node)
|
||||
ENV OMNIROUTE_USE_TURBOPACK=0
|
||||
|
||||
ARG OMNIROUTE_BASE_PATH=""
|
||||
ENV OMNIROUTE_BASE_PATH=$OMNIROUTE_BASE_PATH
|
||||
@@ -73,7 +65,6 @@ RUN apt-get update \
|
||||
libsecret-1-0 \
|
||||
ca-certificates \
|
||||
curl \
|
||||
sqlite3 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV NODE_ENV=production
|
||||
@@ -85,23 +76,11 @@ ENV DATA_DIR=/app/data
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
COPY --from=builder /app/.build/next/standalone ./
|
||||
|
||||
COPY --from=builder /app/node_modules/better-sqlite3 ./node_modules/better-sqlite3
|
||||
ENV OMNIROUTE_MIGRATIONS_DIR=/app/migrations
|
||||
|
||||
COPY --from=builder /app/scripts/dev/healthcheck.mjs ./healthcheck.mjs
|
||||
|
||||
# Bun uses bun:sqlite. Remove every standalone/vendor copy of the Node-only
|
||||
# addon so no traced chunk can dlopen it and abort the process before fallback.
|
||||
RUN find /app \
|
||||
-path '*/node_modules/better-sqlite3' \
|
||||
-prune \
|
||||
-exec rm -rf '{}' + \
|
||||
&& test -z "$(find /app -type f -name 'better_sqlite3.node' -print -quit)"
|
||||
|
||||
RUN chown -R bun:bun /app /app/data
|
||||
|
||||
USER bun
|
||||
|
||||
EXPOSE 20128
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||
@@ -162,7 +141,6 @@ RUN apt-get update \
|
||||
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
|
||||
ENV PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium
|
||||
|
||||
# Drop back to default non-root user
|
||||
# Return to the base image non-root user after the apt install (mirrors the
|
||||
# Node Dockerfile runner-web stage, which re-asserts USER node).
|
||||
USER bun
|
||||
|
||||
ENTRYPOINT ["bun", "dev/run-standalone.mjs"]
|
||||
|
||||
447
PROVIDER_REFERENCE.md
Normal file
447
PROVIDER_REFERENCE.md
Normal file
@@ -0,0 +1,447 @@
|
||||
---
|
||||
title: "Provider Reference"
|
||||
version: 3.8.50
|
||||
lastUpdated: 2026-08-21
|
||||
---
|
||||
|
||||
# Provider Reference
|
||||
|
||||
> **Auto-generated** from `src/shared/constants/providers.ts` — do not edit by hand.
|
||||
> Regenerate with: `npm run gen:provider-reference`
|
||||
> **Last generated:** 2026-08-21
|
||||
|
||||
Total providers: **349**. See category breakdown below.
|
||||
|
||||
## Categories
|
||||
|
||||
- **Free** — free tier with API key (configured via dashboard)
|
||||
- **No-auth** — public endpoints that require no key or sign-in at all
|
||||
- **OAuth** — sign-in flow handled by OmniRoute, no API key needed
|
||||
- **Web cookie** — wraps the provider's web app via cookie auth
|
||||
- **API key** — paid provider configured via API key (free credits may apply)
|
||||
- **Local** — runs on the user's machine (Ollama, LM Studio, vLLM, etc.)
|
||||
- **Search** — web search providers
|
||||
- **Audio** — audio-only providers (TTS/STT)
|
||||
- **Upstream proxy** — providers that proxy to other providers
|
||||
- **Cloud agent** — long-running coding agents (Codex Cloud, Devin, Jules)
|
||||
- **System** — OmniRoute-internal providers (loopback, etc.)
|
||||
|
||||
Additional tags: `image`, `video`, `aggregator`, `enterprise`, `embed/rerank`, `self-hosted`.
|
||||
|
||||
`Tool calling` (where shown): `native` — real function-calling API; `emulated` — the `tools` array is prompt-emulated via `webTools.ts` (regex-parsed `<tool>{...}</tool>` blocks); `none` — `tools` is currently silently dropped. See #7286.
|
||||
|
||||
Use the dashboard at `/dashboard/providers` to enable, configure, and test each provider.
|
||||
|
||||
---
|
||||
|
||||
## No-auth Providers (no key required) (11)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes | Tool calling |
|
||||
|----|-------|------|------|---------|-------|--------------|
|
||||
| `aihorde` | `horde` | AI Horde | No-auth | [link](https://aihorde.net) | No API key required — uses AI Horde's documented anonymous key. Adding a free aihorde.net key is optional and only buys higher queue priority (kudos). | — |
|
||||
| `auggie` | `aug` | Augment (Auggie CLI) | No-auth | [link](https://augmentcode.com) | No API key stored by OmniRoute. Install the Auggie CLI and run `auggie login` on this machine, then OmniRoute spawns it locally for each request. | — |
|
||||
| `chipotle` | `pepper` | Chipotle Pepper AI (Free) | No-auth | [link](https://amelia.chipotle.com) | No credentials required. Uses Chipotle's public support chatbot via reverse-engineered SockJS/STOMP protocol. | — |
|
||||
| `cloudflare-playground` | `cfp` | Cloudflare AI Playground | No-auth | [link](https://playground.ai.cloudflare.com) | No credentials required — anonymous browser sessions over a reverse-engineered cf_agent WebSocket protocol (Playwright transport). | — |
|
||||
| `devin-cli-agentic` | `dva` | Devin CLI Agentic Bridge | No-auth | [link](https://docs.devin.ai/work-with-devin/devin-cli) | Authentication is owned by the official Devin CLI in its isolated bridge volume. | emulated |
|
||||
| `duckduckgo-web` | `ddgw` | DuckDuckGo AI Chat | No-auth | [link](https://duckduckgo.com/duckchat) | No credentials required — DuckDuckGo AI Chat is anonymous and free. | emulated |
|
||||
| `felo-web` | `felo` | Felo | No-auth | [link](https://felo.ai) | No credentials required — Felo is a free, no-signup chat/search aggregator. | — |
|
||||
| `opencode` | `oc` | OpenCode Free | No-auth | [link](https://opencode.ai) | No API key required — uses OpenCode's public free endpoint. | — |
|
||||
| `theoldllm` | `tllm` | The Old LLM (Free) | No-auth | [link](https://theoldllm.vercel.app) | No credentials required. The executor auto-generates access tokens via an embedded Playwright browser instance. | — |
|
||||
| `veoaifree-web` | `veo-free` | Veo AI Free | No-auth, video | [link](https://veoaifree.com) | No auth required. Rate limited to 6 requests/hour per IP. | — |
|
||||
| `zcode` | `zc` | ZCode (GLM Coding Plan) | No-auth | [link](https://zcode.z.ai) | No API key stored by OmniRoute. The local ZCode app-server uses the existing builtin:zai-coding-plan login. | — |
|
||||
|
||||
## OAuth Providers (25)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `agy` | `agy` | Antigravity CLI | OAuth | [link](https://antigravity.google) | Import your Antigravity CLI (`agy`) login (paste/upload its token file), auto-detect a local CLI login, or sign in with Google. Shares the Antigravity backend (incl. Claude models). |
|
||||
| `amazon-q` | `aq` | Amazon Q | OAuth | [link](https://aws.amazon.com/q/developer/) | Uses the same AWS Builder ID or imported refresh-token flow as Kiro, but keeps Amazon Q connections separate. |
|
||||
| `antigravity` | — | Antigravity | OAuth | — | — |
|
||||
| `claude` | `cc` | Claude Code | OAuth | — | — |
|
||||
| `cline` | `cl` | Cline | OAuth | — | — |
|
||||
| `clinepass` | `cp` | ClinePass | OAuth | [link](https://cline.bot/cline-pass) | ClinePass is Cline's $9.99/mo subscription bundling 10 open coding models. Sign in with your Cline account (same login as the Cline CLI/IDE), or paste a direct ClinePass API key (app.cline.bot → Settings → API Keys). A ClinePass subscription unlocks the cline-pass/* models. Reuses the Cline WorkOS OAuth flow. |
|
||||
| `codebuddy-cn` | `cbcn` | CodeBuddy CN | OAuth | [link](https://copilot.tencent.com) | Tencent CodeBuddy CN (copilot.tencent.com). Sign in via the official CLI device-code flow, or paste a direct API key (sent as Authorization: Bearer). Catalog: GLM / Kimi / MiniMax / DeepSeek / Hunyuan. |
|
||||
| `codex` | `cx` | OpenAI Codex | OAuth | — | — |
|
||||
| `cursor` | `cu` | Cursor IDE | OAuth | — | — |
|
||||
| `devin-cli` | `dv` | Devin CLI | OAuth | [link](https://cli.devin.ai) | Requires the Devin CLI binary. Run `devin auth login` to authenticate, or provide your WINDSURF_API_KEY. Install: https://cli.devin.ai |
|
||||
| `devin-desktop` | — | Devin Desktop | OAuth | [link](https://devin.ai) | Paste an existing Devin API key from an authenticated Devin session. Key export availability and steps vary by Devin version and account. |
|
||||
| `ghe-copilot` | `ghe-copilot` | GitHub Enterprise Copilot | OAuth | — | Enter your GHE instance URL (e.g., https://ghe.company.com) in provider settings, then authenticate via device flow. |
|
||||
| `github` | `gh` | GitHub Copilot | OAuth | — | — |
|
||||
| `gitlab-duo` | `gitlab-duo` | GitLab Duo | OAuth | [link](https://docs.gitlab.com/user/duo_agent_platform/code_suggestions/) | GitLab Duo OAuth is not configured. Register an OAuth application at https://gitlab.com/-/profile/applications with redirect URI http://localhost:20128/callback and scopes "ai_features read_user", then set GITLAB_DUO_OAUTH_CLIENT_ID (and optionally GITLAB_DUO_OAUTH_CLIENT_SECRET) and restart. |
|
||||
| `grok-cli` | `gc` | Grok Build | OAuth | — | Sign in with your browser, or paste your ~/.grok/auth.json (or the JWT access token) from the Grok Build CLI; refresh_token is rotated automatically either way. |
|
||||
| `kilocode` | `kc` | Kilo Code | OAuth | — | — |
|
||||
| `kimi-coding` | `kmc` | Kimi Code CLI | OAuth | [link](https://www.kimi.com/code?aff=omniroute) | Sign in with the same Kimi account used by Kimi Code CLI. OmniRoute uses the CLI OAuth flow and Kimi Coding Plan endpoints. |
|
||||
| `kiro` | `kr` | Kiro AI | OAuth | — | Free tier: 50 credits/month (~25K–100K tokens). ⚠️ Kiro ToS prohibits third-party proxy/harness use. |
|
||||
| `openference` | `of` | Openference | OAuth | [link](https://openference.com) | Sign in with your Openference account to route requests through api.openference.com. An active plan is required for inference — OAuth may authenticate but return 402 without one. |
|
||||
| `qoder` | `if` | Qoder | OAuth | — | — |
|
||||
| `raycast` | `rc` | Raycast Pro AI | OAuth | [link](https://raycast.com/ai) | Unofficial integration — uses your Raycast Pro subscription via credentials from the macOS app (Auto-Import or manual capture). May break on Raycast updates. Not for redistribution; personal use only. |
|
||||
| `trae` | `tr` | Trae | OAuth | [link](https://trae.ai) | Trae is an AI-native IDE by ByteDance (SOLO remote agent). Authorize via trae.ai in the popup, or sign in at solo.trae.ai and paste the Cloud-IDE-JWT (sent as 'Authorization: Cloud-IDE-JWT <token>', ~14-day lifetime) as the access token; web_id/biz_user_id/user_unique_id/scope/tenant/region propagate via providerSpecificData. No headless refresh for pasted tokens — re-paste on expiry. |
|
||||
| `xai-oauth` | `xao` | xAI OAuth (Grok) | OAuth | [link](https://x.ai) | Sign in with xAI to use api.x.ai models such as Grok 4.5. This is separate from Grok Build JWT sessions, which use cli-chat-proxy.grok.com and grok-build model aliases. |
|
||||
| `zed` | `zd` | Zed IDE | OAuth | [link](https://zed.dev) | Zed stores LLM provider credentials (OpenAI, Anthropic, Google, Mistral, xAI) in the OS keychain. Use the Import button below to discover and import them automatically. |
|
||||
| `zed-hosted` | — | Zed Hosted Models | OAuth | [link](https://zed.dev) | Sign in with your Zed account (native-app sign-in). OmniRoute generates a one-time RSA keypair and opens zed.dev to authorize it — on a remote/headless install, copy the resulting 127.0.0.1 callback URL from your browser's address bar and paste it back here. Distinct from the 'Zed IDE' credential-import entry above: this proxies chat completions through Zed's own hosted model aggregator (cloud.zed.dev), fronting Anthropic/OpenAI/Google/xAI models under your Zed plan. |
|
||||
|
||||
## Web Cookie Providers (35)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes | Tool calling |
|
||||
|----|-------|------|------|---------|-------|--------------|
|
||||
| `adapta-web` | `adp-web` | Adapta.org (Adapta One Web) | Web cookie | [link](https://agent.adapta.one) | Paste your __client cookie value from .clerk.agent.adapta.one (DevTools → Application → Cookies) | emulated |
|
||||
| `adobe-firefly` | `firefly` | Adobe Firefly (Image/Video) | Web cookie | [link](https://firefly.adobe.com) | RECOMMENDED: firefly.adobe.com signed-in → F12 → Network → click firefly-3p.ff.adobe.io (generate-async or models/discovery) → Request Headers → Authorization → copy the token AFTER 'Bearer ' (starts with eyJ…). Cookie-only from firefly.adobe.com mints a GUEST token → 401/403; only multi-domain IMS cookies (adobelogin.com) or that Bearer JWT work. Unofficial/experimental media + Limits. | — |
|
||||
| `blackbox-web` | `bb-web` | Blackbox Web (Subscription) | Web cookie | [link](https://app.blackbox.ai) | Paste your __Secure-authjs.session-token value or full cookie header from app.blackbox.ai | emulated |
|
||||
| `chatgpt-web` | `cgpt-web` | ChatGPT Web (Plus/Pro) | Web cookie | [link](https://chatgpt.com) | Paste your __Secure-next-auth.session-token cookie value from chatgpt.com | emulated |
|
||||
| `chatgpt-web-codex` | `cgpt-codex` | ChatGPT Web (Codex) | Web cookie | [link](https://chatgpt.com) | Paste the full ChatGPT Cookie header. OmniRoute verifies it in an isolated headless browser profile. | native |
|
||||
| `claude-web` | `cw` | Claude Web | Web cookie | [link](https://claude.ai) | Paste your session cookie from claude.ai | none |
|
||||
| `conol-web` | `cnl` | Conol (Unofficial/Experimental) | Web cookie | [link](https://conol.ai) | Use browser sign-in, or paste the full Cookie header from conol.ai. The __Secure-better-auth.session_token cookie is required. | — |
|
||||
| `copilot-m365-web` | `m365copilot` | Microsoft 365 Copilot (BizChat) | Web cookie | [link](https://m365.cloud.microsoft/chat) | Sign in at m365.cloud.microsoft/chat, then open DevTools → Network → filter 'WS' → click the Chathub WebSocket connection. Copy both the access_token query parameter AND the account-specific Chathub path segment from its request URL (wss://…/Chathub/<path>?…&access_token=…). It is NOT an Authorization: Bearer header on an XHR/Fetch request. The token is short-lived; this is an unofficial integration. Optional: store a refresh_token in providerSpecificData.refreshToken (any Microsoft device-code/refresh flow for the substrate.office.com/sydney scopes) and OmniRoute pre-flight-refreshes the access token itself — otherwise re-capture after every ~75 min expiry. | — |
|
||||
| `copilot-web` | `copilot` | Microsoft Copilot Web | Web cookie | [link](https://copilot.microsoft.com) | Paste the access_token from an authenticated copilot.microsoft.com request (DevTools → Network → Authorization), or export a HAR while logged in | — |
|
||||
| `deepseek-web` | `ds-web` | DeepSeek Web | Web cookie | [link](https://chat.deepseek.com) | Paste your userToken from chat.deepseek.com — DevTools → Application → Local Storage → userToken | emulated |
|
||||
| `doubao-web` | `db` | Dola Web (ByteDance) | Web cookie | [link](https://www.dola.com) | Paste the full Cookie header from www.dola.com. It should include sessionid, ttwid, and s_v_web_id. If s_v_web_id is unavailable, fp=verify_... from a chat/completion request URL can be used as a fallback. | — |
|
||||
| `gemini-business` | `gembiz` | Gemini Business (Enterprise) | Web cookie | [link](https://business.gemini.google) | From your enterprise account: open business.gemini.google/home/cid/{your-cid}, then copy __Secure-1PSID and __Secure-1PSIDTS cookies from DevTools → Application → Cookies. Paste as a cookie header below. | — |
|
||||
| `gemini-web` | `gweb` | Gemini Web (Free) | Web cookie | [link](https://gemini.google.com) | Paste your __Secure-1PSID cookie value from gemini.google.com. Optionally add __Secure-1PSIDTS separated by semicolon. | emulated |
|
||||
| `grok-web` | `gw` | Grok Web (Subscription) | Web cookie | [link](https://grok.com) | Paste the full grok.com cookie line from DevTools → Application → Cookies. Include both `sso` and `sso-rw` (e.g. `sso=...; sso-rw=...`) — Grok's anti-bot rejects `sso` on its own. | — |
|
||||
| `hailuo-web` | `hailuo-web` | Hailuo Web (MiniMax) | Web cookie | [link](https://hailuo.ai) | Open hailuo.ai, log in, then open DevTools → Application → Local Storage → copy the "_token" value. device_id/uuid fingerprint fields are derived automatically; if requests fail, re-capture _token (sessions can expire). | — |
|
||||
| `huggingchat` | `huggingchat` | HuggingChat (Free) | Web cookie | [link](https://huggingface.co/chat) | Paste the full Cookie header from huggingface.co/chat (DevTools → Network → /chat/conversation → Request Headers → Cookie). It should include hf-chat and may also include token / aws-waf-token. | — |
|
||||
| `hyperagent` | `ha` | HyperAgent (Unofficial/Experimental) | Web cookie | [link](https://hyperagent.com) | Paste the full Cookie header from hyperagent.com (DevTools → Network → any request → Request Headers → Cookie). Session cookies power chat + billing usage. | — |
|
||||
| `inner-ai` | `in-ai` | Inner.ai (Subscription) | Web cookie | [link](https://app.innerai.com) | Paste your token cookie and email separated by a space: open DevTools → Application → Cookies → .innerai.com, copy the token value, then append a space and your Inner.ai login email. Example: eyJhbG... user@example.com | emulated |
|
||||
| `kimi-web` | `kimi-web` | Kimi Web | Web cookie | [link](https://www.kimi.com/code?aff=omniroute) | Paste access_token from www.kimi.com DevTools → Application → Local Storage. A legacy kimi-auth cookie is also accepted. | — |
|
||||
| `lmarena` | `lma` | Arena (Free) | Web cookie | [link](https://arena.ai) | Paste the full Cookie header from arena.ai (DevTools → Network → request → Cookie). Include arena-auth-prod-v1.0/.1… and cf_clearance/__cf_bm when present. OmniRoute uses Chrome TLS impersonation; if Arena still 403s, set providerSpecificData.recaptchaV3Token from a live browser session. | — |
|
||||
| `microsoft-designer-web` | `msdesigner` | Microsoft Designer (Image Generation) | Web cookie | [link](https://designer.microsoft.com) | Sign in at designer.microsoft.com, then open DevTools → Network, generate an image, and find the request to DallE.ashx?action=GetDallEImagesCogSci. Copy the value of its Authorization: Bearer header (the access_token — no 'Bearer ' prefix). The token is short-lived; this is an unofficial, reverse-engineered integration. | — |
|
||||
| `muse-spark-web` | `ms-web` | Muse Spark Web (Meta AI) | Web cookie | [link](https://www.meta.ai) | Paste your ecto_1_sess cookie AND the ecto1:... WS auth token from meta.ai. Capture the ecto1: token in DevTools → Network → WS → the clippy request's Authorization query param. Example: ecto_1_sess=4240a308...NVDg0; ecto1:ABCD... | emulated |
|
||||
| `notion-web` | `nw` | Notion AI Web (Unofficial/Experimental) | Web cookie | [link](https://www.notion.so) | Paste only the token_v2 cookie VALUE from app.notion.com (DevTools → Application → Cookies → token_v2). Do not paste token_v2= or the full Cookie header. Workspace is auto-detected; space_id / notion_user_id are optional. | — |
|
||||
| `perplexity-web` | `pplx-web` | Perplexity Web (Pro/Max) | Web cookie | [link](https://www.perplexity.ai) | Paste your __Secure-next-auth.session-token cookie value from perplexity.ai | emulated |
|
||||
| `poe-web` | `poe` | Poe Web (Subscription) | Web cookie | [link](https://poe.com) | Paste your p-b cookie value from poe.com (DevTools → Application → Cookies → p-b) | — |
|
||||
| `promptql` | `pql` | PromptQL (Unofficial/Experimental) | Web cookie | [link](https://prompt.ql.app) | Paste the Bearer JWT from prompt.ql.app DevTools → Network → graphql → Authorization (token only). Optional projectId + session Cookie for refresh. | — |
|
||||
| `qwen-web` | `qwen-web` | Qwen Web (Free) | Web cookie | [link](https://chat.qwen.ai) | Open chat.qwen.ai, log in, then open DevTools → Application → Local Storage → copy the "token" value (or use tongyi_sso_ticket cookie as Bearer token). | emulated |
|
||||
| `t3-web` | `t3chat` | t3.chat (Pro/Free) | Web cookie | [link](https://t3.chat) | Open t3.chat in your browser, log in, then open DevTools → Application → Local Storage → https://t3.chat. Copy the value of 'convex-session-id'. Also open DevTools → Network, copy the Cookie header from any request. Paste both values here. See provider setup docs for a step-by-step guide. | emulated |
|
||||
| `tencent-aistudio-web` | `tasw` | Tencent AI Studio (Free) | Web cookie | [link](https://aistudio.tencent.ai) | Log in to aistudio.tencent.ai, open DevTools -> Network, copy any request Cookie header containing session tokens. | — |
|
||||
| `tinycms-web` | `tcw` | TinyCMS Web (Free/Sub) | Web cookie | [link](https://site.tinycms.xyz) | Go to site.tinycms.xyz, open DevTools → Application → Local Storage, copy the value of 'app-config-uuid' (starts with 'R'), and paste it here. | — |
|
||||
| `v0-vercel-web` | `v0-vercel-web` | v0 Vercel Web (Code Gen) | Web cookie | [link](https://v0.dev) | Paste your session cookie from v0.dev (DevTools → Application → Cookies) | — |
|
||||
| `venice-web` | `ven` | Venice Web (Privacy) | Web cookie | [link](https://venice.ai) | Paste your session cookie from venice.ai (DevTools → Application → Cookies) | — |
|
||||
| `yuanbao-web` | `ybw` | Tencent Yuanbao (Free) | Web cookie | [link](https://yuanbao.tencent.com) | Log in to yuanbao.tencent.com, then paste the full Cookie header (DevTools → Network → any /api request → Request Headers → Cookie). It must contain hy_user and hy_token. | — |
|
||||
| `zai-web` | `zw` | Z.ai Web | Web cookie | [link](https://chat.z.ai) | Copy the "token" value from chat.z.ai → DevTools → Application → Local Storage. Do not copy cookies; OmniRoute handles the per-request CAPTCHA through its browser transport. | — |
|
||||
| `zenmux-free` | `zmf` | ZenMux Free (Web) | Web cookie | [link](https://zenmux.ai) | Login at zenmux.ai, then export all cookies using EditThisCookie or Cookie-Editor and paste the full Cookie header string here. Refresh every ~30 days. | — |
|
||||
|
||||
## API Key Providers (paid / paid-with-free-credits) (233)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `360ai` | `360ai` | 360 AI | API key | [link](https://ai.360.cn) | Get API key at ai.360.cn |
|
||||
| `agentrouter` | `agentrouter` | AgentRouter | API key, aggregator | [link](https://agentrouter.org) | $200 free credits on signup - multi-model routing gateway |
|
||||
| `agnes` | `agnes` | Agnes AI | API key, video | [link](https://agnes-ai.com) | Get API key at agnes-ai.com |
|
||||
| `ai21` | `ai21` | AI21 Labs | API key | [link](https://www.ai21.com) | $10 trial credits on signup (valid 3 months), no credit card required |
|
||||
| `aimlapi` | `aiml` | AI/ML API | API key, aggregator | [link](https://aimlapi.com) | Free tier paused (2026) — AI/ML API is now pay-as-you-go only (min $20 top-up); no recurring free credits. |
|
||||
| `ainative` | `ainative` | AINative Studio | API key | [link](https://ainative.studio) | Create a free API key at ainative.studio (no card), then paste it here as a Bearer token. |
|
||||
| `aion` | `aion` | Aion Labs | API key | [link](https://www.aionlabs.ai) | Create a free API key at aionlabs.ai (no card), then paste it here as a Bearer token. |
|
||||
| `alibaba` | `ali` | Alibaba Cloud Model Studio | API key | [link](https://bailian.console.alibabacloud.com/) | — |
|
||||
| `alibaba-cn` | `ali-cn` | Alibaba (China) | API key | [link](https://dashscope.console.aliyun.com/) | — |
|
||||
| `ant-ling` | `ling` | Ant Ling / Ring (inclusionAI) | API key | [link](https://developer.ant-ling.com/en/docs/) | Register and create an API key at the Ant Ling API console (https://chat.ant-ling.com/open), then paste it here. OmniRoute routes chat traffic to https://api.ant-ling.com/v1/chat/completions; the provider is OpenAI-compatible and also exposes an Anthropic-compatible surface. |
|
||||
| `anthropic` | `anthropic` | Anthropic | API key | [link](https://platform.claude.com) | — |
|
||||
| `anyapi` | `anyapi` | AnyAPI AI | API key, aggregator | [link](https://anyapi.ai) | Free plan: 100,000 ANY Tokens/day and 100 RPM for eligible Free/Basic models; no credit card required. |
|
||||
| `api-airforce` | `af` | Api.airforce | API key | [link](https://api.airforce) | 55 free tier models including Grok-3, Claude 3.7, Qwen3, Kimi-K2, Gemini 2.5 Flash, DeepSeek-V3 |
|
||||
| `arcee-ai` | `arcee` | Arcee AI | API key | [link](https://arcee.ai) | Get API key at arcee.ai |
|
||||
| `auriko` | `auriko` | Auriko | API key, aggregator | [link](https://www.auriko.ai) | Free plan publishes 1,000 Platform RPM and 10,000 BYOK RPM. Platform inference still passes through provider cost; this is not a free-token pool or unlimited free inference. |
|
||||
| `azure-ai` | `azure-ai` | Azure AI Foundry | API key, enterprise | [link](https://learn.microsoft.com/azure/ai-foundry) | Use your Azure AI Foundry key. Base URL can be https://<resource>.services.ai.azure.com/openai/v1/ or https://<resource>.openai.azure.com/openai/v1/. |
|
||||
| `azure-openai` | `azure` | Azure OpenAI | API key, enterprise | [link](https://azure.microsoft.com/products/ai-services/openai-service) | Use your Azure OpenAI API key. Base URL should be your resource endpoint, for example https://my-resource.openai.azure.com. |
|
||||
| `bai` | `bai` | b.ai | API key | [link](https://b.ai) | Bearer API key for the b.ai OpenAI-compatible LLM gateway (distinct from TheB.AI). Create a key at https://docs.b.ai, then use https://api.b.ai/v1 as the OpenAI-compatible base URL. |
|
||||
| `baichuan` | `baichuan` | Baichuan | API key | [link](https://www.baichuan-ai.com/) | Get API key at platform.baichuan-ai.com |
|
||||
| `baidu` | `baidu` | Baidu (ERNIE) | API key | [link](https://ernie.baidu.com/) | Get API key at console.bce.baidu.com |
|
||||
| `bailian-coding-plan` | `bcp` | Alibaba Token Plan | API key | [link](https://www.alibabacloud.com/help/en/model-studio/token-plan-overview) | — |
|
||||
| `baseten` | `baseten` | Baseten | API key | [link](https://baseten.co) | $30 free trial credits for GPU inference |
|
||||
| `bazaarlink` | `bzl` | BazaarLink | API key | [link](https://bazaarlink.ai) | Use your BazaarLink API key (starts with sk-bl-) in Authorization: Bearer <key>. OpenAI SDK works with base URL https://bazaarlink.ai/api/v1. Models use provider/model-name format. |
|
||||
| `bedrock` | `bedrock` | Amazon Bedrock | API key, enterprise | [link](https://aws.amazon.com/bedrock) | Use your Amazon Bedrock API key and configure the AWS region where your models are enabled (for example eu-west-2). OmniRoute calls Bedrock's native Converse API directly. |
|
||||
| `black-forest-labs` | `bfl` | Black Forest Labs | API key, image | [link](https://blackforestlabs.ai) | — |
|
||||
| `blackbox` | `bb` | Blackbox AI | API key | [link](https://blackbox.ai) | Limited free access is available through Blackbox; model availability and account limits apply |
|
||||
| `bluesminds` | `bm` | BluesMinds | API key | [link](https://www.bluesminds.com) | Free daily pi credits — supports 200+ models including GPT-4o, GPT-4.1, Claude Sonnet 4.5, Gemini 2.0 Flash, DeepSeek V4, Qwen, Kimi K2 |
|
||||
| `byteplus` | `bpm` | BytePlus ModelArk | API key | [link](https://console.byteplus.com/ark) | — |
|
||||
| `bytez` | `bytez` | Bytez | API key | [link](https://bytez.com) | $1 free credits, refreshes every 4 weeks |
|
||||
| `cerebras` | `cerebras` | Cerebras | API key | [link](https://inference.cerebras.ai) | Free Trial: 1M tokens/day, 30K TPM, 5 RPM — no credit card. |
|
||||
| `charm-hyper` | `charm-hyper` | Charm Hyper | API key | [link](https://hyper.charm.land) | 100 free monthly Hypercredits on signup |
|
||||
| `chat-oripe` | `chat-oripe` | Chat Oripe | API key, aggregator | [link](https://api.oriper.com) | Official metadata advertises 2M tokens/month, but the public site and documentation were blocked during audit; treat the quota and brand mapping as unconfirmed. |
|
||||
| `chatanywhere` | `chatanywhere` | ChatAnywhere | API key, aggregator | [link](https://chatanywhere.tech) | Personal, educational or research use only: public documentation cites 10,000 points/day and 200 requests/day per IP/key; do not use for commercial traffic. |
|
||||
| `cheaperinference` | `cinf` | Cheaper Inference | API key | [link](https://cheaperinference.com/?utm_source=omniroute) | — |
|
||||
| `chenzk` | `chenzk` | Chenzk API | API key | [link](https://chenzk.top) | — |
|
||||
| `chutes` | `chutes` | Chutes.ai | API key, aggregator | [link](https://chutes.ai) | Bearer API key for the Chutes OpenAI-compatible gateway. |
|
||||
| `clarifai` | `clarifai` | Clarifai | API key, enterprise | [link](https://docs.clarifai.com) | Use your Clarifai PAT or app-specific API key. OmniRoute targets the OpenAI-compatible endpoint at https://api.clarifai.com/v2/ext/openai/v1 and authenticates with Authorization: Key <token>. |
|
||||
| `cloudcode-one` | `cloudcode-one` | CloudCode.ONE | API key, aggregator | [link](https://cloudcode.one) | Published free models include glm-4.7-flash and glm-4.6v-flash; no numeric quota is published, and key creation may require credit or a coupon. |
|
||||
| `cloudflare-ai` | `cf` | Cloudflare Workers AI | API key | [link](https://developers.cloudflare.com/workers-ai) | Requires API Token AND Account ID (found at dash.cloudflare.com) |
|
||||
| `clova-studio` | `clova` | Naver CLOVA Studio | API key | [link](https://api.ncloud-docs.com/docs/en/ai-naver-clovastudio-summary) | — |
|
||||
| `codestral` | `codestral` | Codestral | API key | [link](https://mistral.ai) | — |
|
||||
| `cohere` | `cohere` | Cohere | API key | [link](https://cohere.com) | Free Trial: 1,000 API calls/month for testing, no credit card required |
|
||||
| `command-code` | `cmd` | Command Code | API key | [link](https://commandcode.ai/) | Use a Command Code API key. Requests are sent to Command Code's /alpha/generate endpoint. |
|
||||
| `coze` | `coze` | Coze | API key | [link](https://coze.com) | Get API key at coze.com/open/api |
|
||||
| `crof` | `crof` | CrofAI | API key | [link](https://crof.ai) | — |
|
||||
| `cursor-api` | `cua` | Cursor API | API key | [link](https://cursor.com/dashboard/api) | Paste a Cursor user API key (crsr_...) from cursor.com/dashboard/api. OmniRoute exchanges it for a session token on demand; no IDE or cursor-agent install is needed. Usage bills to the Cursor plan that owns the key. |
|
||||
| `dahl` | `dahl` | Dahl | API key | [link](https://inference.dahl.global) | Click 'Add Account' to auto-generate a token, or add a manual API key. |
|
||||
| `databricks` | `databricks` | Databricks | API key, enterprise | [link](https://www.databricks.com) | — |
|
||||
| `datarobot` | `datarobot` | DataRobot | API key, enterprise | [link](https://docs.datarobot.com) | Use your DataRobot API token. Optional Base URL can be the account root (for LLM Gateway) or a deployment URL under /api/v2/deployments/<id>. |
|
||||
| `deepai` | `deepai` | DeepAI | API key, image | [link](https://deepai.org) | Use your DeepAI API key. Get one at deepai.org — requires a Pro subscription ($9.99/mo). |
|
||||
| `deepinfra` | `deepinfra` | DeepInfra | API key | [link](https://deepinfra.com) | Free signup credits for API testing and model exploration |
|
||||
| `deepseek` | `ds` | DeepSeek | API key | [link](https://platform.deepseek.com) | 5M free tokens on signup - no credit card required |
|
||||
| `dgrid` | `dgrid` | DGrid | API key | [link](https://dgrid.ai) | DGrid Free Models Router: 10 requests/minute and 100 requests/day. A $5 lifetime top-up unlocks up to 20 requests/minute and 1,000 requests/day. |
|
||||
| `dify` | `dify` | Dify | API key | [link](https://dify.ai) | Get API key from your Dify instance. |
|
||||
| `digitalocean` | `digitalocean` | DigitalOcean | API key | [link](https://docs.digitalocean.com/products/ai-platform/) | — |
|
||||
| `dit` | `dai` | DIT.ai | API key | [link](https://dit.ai) | Use your dit.ai API key in Authorization: Bearer <key>. Fully OpenAI-compatible — a drop-in replacement, just change the base URL to https://api.dit.ai/v1. |
|
||||
| `doubao` | `doubao` | Doubao | API key | [link](https://doubao.com) | Get API key at console.volcengine.com |
|
||||
| `dxnt` | `dxnt` | DXNT / DX Token | API key, aggregator | [link](https://www.dxnt.com) | Free accounts are documented at 100 calls/day; the quota may increase through invitations and can vary by account. |
|
||||
| `electronhub` | `electronhub` | Electron Hub | API key, aggregator | [link](https://www.electronhub.ai) | Free plan: 5 RPM, $0.25 weekly credits and 10 Neutrinos/day for :free models; family budgets also apply. |
|
||||
| `empower` | `empower` | Empower | API key, aggregator | [link](https://docs.empower.dev) | Bearer API key for the Empower OpenAI-compatible endpoint. |
|
||||
| `factory` | `factory` | Factory | API key | [link](https://factory.ai) | Bearer API key for the Factory OpenAI-compatible gateway. |
|
||||
| `fal-ai` | `fal` | Fal.ai | API key, image | [link](https://fal.ai) | — |
|
||||
| `fastrouter` | `fastrouter` | FastRouter | API key, aggregator | [link](https://fastrouter.ai) | Models with the :free suffix allow 10 requests/day per organization and model; availability may change. |
|
||||
| `featherless-ai` | `featherless` | Featherless AI | API key | [link](https://featherless.ai) | Free tier available — no credit card required |
|
||||
| `fenayai` | `fenayai` | FenayAI | API key, aggregator | [link](https://fenayai.com) | Bearer API key for the FenayAI OpenAI-compatible gateway. |
|
||||
| `fireworks` | `fireworks` | Fireworks AI | API key | [link](https://fireworks.ai) | $1 free starter credits on signup for API testing |
|
||||
| `free-ai` | `free-ai` | Free.ai | API key, aggregator | [link](https://free.ai) | 30,000 tokens/day cover self-hosted models after email verification. Usage beyond the pool can bill at raw cost, and premium external models are paid. |
|
||||
| `freeaiapikey` | `faik` | FreeAIAPIKey | API key | [link](https://freeaiapikey.com) | — |
|
||||
| `freebuff` | `freebuff` | Freebuff | API key | [link](https://freebuff.com) | Enter Freebuff / Codebuff Auth Token (obtained via CLI login or automated harvester). |
|
||||
| `freeinference` | `freeinference` | FreeInference | API key, aggregator | [link](https://freeinference.org) | Free research access without a card; non-Harvard applicants require manual approval and no numeric quota is publicly guaranteed. |
|
||||
| `freemodel-dev` | `fmd` | FreeModel.dev | API key | [link](https://freemodel.dev) | $300 free credits on signup — no credit card required. Access GPT-5.4 and GPT-5.5 (OpenAI's latest flagship models) through an OpenAI-compatible API. |
|
||||
| `freetheai` | `fta` | FreeTheAi | API key, aggregator | [link](https://freetheai.xyz) | Join the FreeTheAi Discord to get your free API key. |
|
||||
| `friendliai` | `friendli` | FriendliAI | API key | [link](https://friendli.ai) | Free tier for serverless inference — no credit card required |
|
||||
| `g4f-gemini` | `g4fgem` | g4f.space — Gemini | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. |
|
||||
| `g4f-groq` | `g4fgroq` | g4f.space — Groq | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. |
|
||||
| `g4f-nvidia` | `g4fnv` | g4f.space — NVIDIA | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. |
|
||||
| `g4f-ollama` | `g4foll` | g4f.space — Ollama | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. |
|
||||
| `g4f-pollinations` | `g4fpol` | g4f.space — Pollinations | API key, aggregator | [link](https://g4f.space) | No auth required. Free tier is limited to 5 requests/minute — sign up at g4f.dev/members.html for higher limits. |
|
||||
| `galadriel` | `galadriel` | Galadriel | API key | [link](https://galadriel.com) | ⚠️ **DEPRECATED.** api.galadriel.ai no longer resolves (sweep 2026-06-19); the inference API appears discontinued. |
|
||||
| `gemini` | `gemini` | Gemini (Google AI Studio) | API key | [link](https://aistudio.google.com) | Free tier available through Google AI Studio; current per-model quotas and regional limits apply |
|
||||
| `getgoapi` | `ggo` | GoAPI | API key, aggregator | [link](https://api.getgoapi.com) | — |
|
||||
| `gigachat` | `gigachat` | GigaChat (Sber) | API key | [link](https://developers.sber.ru) | — |
|
||||
| `gitlab` | `gitlab` | GitLab Duo PAT | API key | [link](https://docs.gitlab.com/user/duo_agent_platform/code_suggestions/) | GitLab personal access token for the public Code Suggestions API. Configure a self-hosted base URL when not using gitlab.com. |
|
||||
| `gitlawb` | `glb` | Gitlawb Opengateway (MiMo) | API key | [link](https://opengateway.gitlawb.com) | Free MiMo (xiaomi/mimo-v2.5) revoked 2026-05 — Opengateway is now a pay-as-you-go credit gateway; no recurring free model. |
|
||||
| `gitlawb-gmi` | `glb-gmi` | Gitlawb Opengateway (GMI Cloud) | API key | [link](https://opengateway.gitlawb.com) | Free Nemotron promo ended 2026-06 — the GMI Cloud route is now pay-as-you-go credit only. |
|
||||
| `glm` | `glm` | GLM Coding | API key | [link](https://z.ai/subscribe) | — |
|
||||
| `glm-cn` | `glmcn` | GLM Coding (China) | API key | [link](https://open.bigmodel.cn) | — |
|
||||
| `glmt` | `glmt` | GLM Thinking | API key | [link](https://open.bigmodel.cn) | — |
|
||||
| `groq` | `groq` | Groq | API key | [link](https://groq.com) | Free tier: 30 RPM / 14.4K RPD — no credit card |
|
||||
| `hackclub` | `hc` | Hackclub AI | API key, aggregator | [link](https://ai.hackclub.com) | Sign in with your Hack Club account at ai.hackclub.com. |
|
||||
| `haiper` | `hp` | Haiper | API key, video | [link](https://haiper.ai) | Get API key at haiper.ai/haiper-api |
|
||||
| `hcnsec` | `hcnsec` | Huancheng Public API | API key | [link](https://api.hcnsec.cn) | Get API key at api.hcnsec.cn |
|
||||
| `helixmind` | `helixmind` | HelixMind | API key, aggregator | [link](https://helixmind.online) | Previously circulated 3 RPM/50 RPD and no-card claims were not confirmed during the 2026-08-02 audit; current quota and billing require account verification. |
|
||||
| `helyxai` | `helyxai` | Helyx AI | API key, aggregator | [link](https://helyxai.space) | Operational Free plan documents 100,000 tokens/day; the site's separate 2M+ marketing claim conflicts and is not treated as a quota guarantee. |
|
||||
| `heroku` | `heroku` | Heroku AI | API key, enterprise | [link](https://www.heroku.com) | — |
|
||||
| `huggingface` | `hf` | HuggingFace | API key | [link](https://huggingface.co) | Free Inference API for thousands of models (Whisper, VITS, SDXL…) |
|
||||
| `hyperbolic` | `hyp` | Hyperbolic | API key | [link](https://hyperbolic.xyz) | $1-5 trial credits on signup for serverless inference |
|
||||
| `ideogram` | `ideo` | Ideogram | API key | [link](https://ideogram.ai) | Get API key at ideogram.ai/docs/api |
|
||||
| `iflytek` | `iflytek` | iFlytek Spark | API key | [link](https://xinghuo.xfyun.cn) | Get API key at console.xfyun.cn |
|
||||
| `inception` | `inception` | Inception | API key | [link](https://docs.inceptionlabs.ai) | 10M free tokens on signup, no credit card required. |
|
||||
| `inference-net` | `inet` | Inference.net | API key | [link](https://inference.net) | $25 free credits on signup plus research grants available |
|
||||
| `internlm` | `internlm` | InternLM (Intern-S1) | API key | [link](https://internlm.intern-ai.org.cn/) | Free monthly quota ~1M input / 3M output tokens (~10 RPM) |
|
||||
| `jina-ai` | `jina` | Jina AI (Foundation API) | API key, embed/rerank | [link](https://jina.ai) | Bearer API key for api.jina.ai — embeddings, rerank, classify, segment, and search. Dashboard keys take precedence over JINA_AI_API_KEY. This is not the Reader / r.jina.ai card and does not fetch URLs. |
|
||||
| `jina-reader` | `jr` | Jina Reader (r.jina.ai) | API key | [link](https://jina.ai/reader) | Bearer API key for r.jina.ai URL-to-markdown (/v1/web/fetch only). Does not serve /v1/embeddings or /v1/rerank. The same Jina token as Foundation API works; OmniRoute reuses a jina-ai dashboard key or JINA_AI_API_KEY when this card is empty. |
|
||||
| `kenari` | `kenari` | Kenari | API key | [link](https://kenari.id) | Use your Kenari API key (kn-...) in Authorization: Bearer <key>. Fully OpenAI-compatible. API base URL: https://kenari.id/v1. |
|
||||
| `kie` | `kie` | KIE.AI | API key | [link](https://kie.ai) | — |
|
||||
| `kilo-gateway` | `kg` | Kilo Gateway | API key, aggregator | [link](https://kilo.ai) | — |
|
||||
| `kimi` | `kimi` | Kimi (Legacy Moonshot API) | API key | [link](https://platform.kimi.ai?aff=omniroute) | — |
|
||||
| `kimi-coding-apikey` | `kmca` | Kimi Code API Key | API key | [link](https://www.kimi.com/code?aff=omniroute) | — |
|
||||
| `lambda-ai` | `lambda` | Lambda AI | API key | [link](https://lambda.ai) | — |
|
||||
| `laozhang` | `lz` | LaoZhang AI | API key, aggregator | [link](https://api.laozhang.ai) | — |
|
||||
| `leonardo` | `leo` | Leonardo AI | API key, video | [link](https://leonardo.ai) | Get API key at leonardo.ai/developer |
|
||||
| `liquid` | `liquid` | Liquid AI | API key | [link](https://liquid.ai) | Get API key at liquid.ai |
|
||||
| `literouter` | `literouter` | LiteRouter | API key, aggregator | [link](https://literouter.com) | Free model variants use the :free suffix; daily credit limits vary by model and free input is capped at 5,000 tokens. |
|
||||
| `llamagate` | `llamagate` | LlamaGate | API key | [link](https://llamagate.ai) | — |
|
||||
| `llm-kiwi` | `llmkiwi` | LLM.Kiwi | API key, aggregator | [link](https://llm.kiwi) | Free plan exposes auto and hrLLM; the published 40 requests/hour limit applies to hrLLM. |
|
||||
| `llm7` | `llm7` | LLM7.io | API key | [link](https://llm7.io) | Use any non-empty key (for example 'unused'). If older built-in models return model_unavailable, use Available Models → Import from /models or Auto-Sync; verified live model: gemini-3.1-flash-lite. |
|
||||
| `llmgateway` | `llmgateway` | LLM Gateway | API key, aggregator | [link](https://llmgateway.io) | Hosted Free plan: free-priced models are limited to 5 requests per 10 minutes when the account has no credits. |
|
||||
| `logfare` | `logfare` | Logfare | API key, aggregator | [link](https://logfare.ai) | Create a free account at https://logfare.ai/register (username/password, no email verification) to get an instant API key, then paste it here as a Bearer token. |
|
||||
| `longcat` | `lc` | LongCat AI | API key | [link](https://longcat.chat/platform/docs) | Free: one-time 10M-token grant after account signup + KYC verification (LongCat-2.0). One-time only — not a recurring daily/monthly allowance. |
|
||||
| `magnific` | `freepik` | Magnific | API key, image | [link](https://www.magnific.com) | Get an API key at magnific.com/user/api-keys (header x-magnific-api-key). Legacy Freepik developer keys still work. |
|
||||
| `maritalk` | `maritalk` | Maritalk | API key | [link](https://www.maritaca.ai) | — |
|
||||
| `meganova-ai` | `meganova-ai` | MegaNova AI | API key, aggregator | [link](https://meganova.ai) | Free signup without a card. Published Tier 1 per-model quotas total 550 requests/day; they are not a shared global pool, and paid overage can apply if enabled. |
|
||||
| `meta-llama` | `meta` | Meta Llama API | API key | [link](https://llama.developer.meta.com) | — |
|
||||
| `minimax` | `minimax` | Minimax Coding | API key, video | [link](https://www.minimax.io) | — |
|
||||
| `minimax-cn` | `minimax-cn` | Minimax (China) | API key | [link](https://www.minimaxi.com) | — |
|
||||
| `mistral` | `mistral` | Mistral | API key | [link](https://mistral.ai) | Free Experiment tier: rate-limited access to all models, no credit card required |
|
||||
| `mixedbread` | `mxbai` | Mixedbread AI | API key | [link](https://www.mixedbread.com) | Bearer API key for the Mixedbread embeddings API. |
|
||||
| `mixlayer` | `mixlayer` | Mixlayer | API key, aggregator | [link](https://www.mixlayer.com) | The qwen/qwen3.5-4b-free model is free for prototyping and rate-limited; no fixed public RPM or daily quota is confirmed. |
|
||||
| `mnn-ai` | `mnn-ai` | MNN AI | API key, aggregator | [link](https://mnnai.ru) | Free plan: $1 monthly credits, 10 RPM and access only to models marked Free. |
|
||||
| `modal` | `mdl` | Modal | API key, enterprise | [link](https://modal.com/docs) | Use the bearer token that protects your Modal deployment, if enabled. Base URL should point to your OpenAI-compatible Modal app, for example https://<workspace>--<app>.modal.run/v1. |
|
||||
| `modelscope` | `ms` | ModelScope | API key | [link](https://modelscope.cn) | Free tier via ModelScope API-Inference — Alibaba account required. |
|
||||
| `monsterapi` | `monster` | MonsterAPI | API key | [link](https://monsterapi.ai) | ⚠️ **DEPRECATED.** Monster API shuttered operations on 2026-06-30. Use alternative OpenAI-compatible providers. |
|
||||
| `moonshot` | `moonshot` | Kimi | API key | [link](https://platform.kimi.ai?aff=omniroute) | — |
|
||||
| `morph` | `morph` | Morph | API key | [link](https://morphllm.com) | Free tier: 250K credits/month, $0 |
|
||||
| `muse-code` | `mc` | Muse Code (Meta) | API key | [link](https://github.com/meta-llama/llama-stack) | Use your META_API_KEY env var as a Bearer token. Muse Code CLI uses the OpenAI Responses API wire format (POST /responses). |
|
||||
| `naga-ac` | `naga` | Naga.ac | API key, aggregator | [link](https://naga.ac) | Get API key at naga.ac — Google/GitHub/Discord signup available. |
|
||||
| `naga-ai` | `naga-ai` | Naga AI | API key, aggregator | [link](https://naga.ac) | Models marked :free are publicly listed, but no numeric quota is confirmed. Naga's policy warns that free-tier prompts and outputs may be collected or used for training. |
|
||||
| `nanogpt` | `nanogpt` | NanoGPT | API key | [link](https://nano-gpt.com) | — |
|
||||
| `nara` | `nara` | NaraRouter | API key | [link](https://bynara.id) | Get a free API key via NaraRouter's Telegram channel, then paste it here as a Bearer token. |
|
||||
| `navy` | `navy` | NavyAI | API key | [link](https://api.navy) | Create a free API key from the NavyAI dashboard, then paste it here as a Bearer token. |
|
||||
| `nebius` | `nebius` | Nebius AI | API key | [link](https://nebius.com) | ~$1 trial credits on signup for API testing |
|
||||
| `nlpcloud` | `nlpc` | NLP Cloud | API key | [link](https://docs.nlpcloud.com) | Use your NLP Cloud API key in Authorization: Token <key>. OmniRoute targets the chatbot endpoint on https://api.nlpcloud.io/v1/gpu/<model>/chatbot by default. |
|
||||
| `nomic` | `nomic` | Nomic | API key | [link](https://nomic.ai) | Get API key at atlas.nomic.ai |
|
||||
| `nous-research` | `nous` | Nous Research | API key | [link](https://portal.nousresearch.com/help) | Use your Nous Portal API key. OmniRoute targets the official OpenAI-compatible inference endpoint at https://inference-api.nousresearch.com/v1. |
|
||||
| `novita` | `novita` | Novita AI | API key, video, aggregator | [link](https://novita.ai) | $0.50 trial credits on signup (valid about 1 year) |
|
||||
| `nscale` | `nscale` | nScale | API key | [link](https://nscale.com) | $5 free credits on signup for inference testing |
|
||||
| `nube` | `nube` | Nube.sh | API key | [link](https://nube.sh) | — |
|
||||
| `nvidia` | `nvidia` | NVIDIA NIM | API key | [link](https://build.nvidia.com) | Free dev access: ~40 RPM, 70+ models (Kimi K2.5, GLM 4.7, DeepSeek V3.2...) |
|
||||
| `oci` | `oci` | OCI Generative AI | API key, enterprise | [link](https://www.oracle.com/artificial-intelligence/generative-ai) | Use your OCI Generative AI API key or IAM bearer token. Base URL can be https://inference.generativeai.<region>.oci.oraclecloud.com/openai/v1/. |
|
||||
| `ofoxai` | `ofoxai` | OfoxAI | API key, aggregator | [link](https://ofox.ai) | The current catalog advertises 10+ free models without a public numeric quota; review upstream provenance, retention and training terms before production use. |
|
||||
| `ollama-cloud` | `ollamacloud` | Ollama Cloud | API key | [link](https://ollama.com/settings/keys) | — |
|
||||
| `openadapter` | `oad` | OpenAdapter | API key | [link](https://openadapter.dev) | Use your OpenAdapter API key in Authorization: Bearer sk-cv-<key>. Fully OpenAI-compatible. API base URL: https://api.openadapter.in/v1. |
|
||||
| `openai` | `openai` | OpenAI | API key | [link](https://platform.openai.com) | — |
|
||||
| `opencode-go` | `opencode-go` | OpenCode Go | API key | [link](https://opencode.ai/go) | — |
|
||||
| `opencode-zen` | `opencode-zen` | OpenCode Zen | API key | [link](https://opencode.ai/zen) | — |
|
||||
| `openference-api` | `ofa` | Openference API | API key | [link](https://openference.com) | Free plan: 3-day trial with open-source models — no credit card required |
|
||||
| `openrouter` | `openrouter` | OpenRouter | API key, aggregator | [link](https://openrouter.ai) | Free models at $0/token with :free suffix - 20 RPM / 200 RPD |
|
||||
| `openvecta` | `openvecta` | OpenVecta | API key | [link](https://openvecta.com) | Free credits on signup for OpenAI-compatible inference across LLMs, embeddings, and reasoning models |
|
||||
| `orcarouter` | `orcarouter` | OrcaRouter | API key | [link](https://www.orcarouter.ai) | — |
|
||||
| `ovhcloud` | `ovh` | OVHcloud AI | API key | [link](https://www.ovhcloud.com) | — |
|
||||
| `perplexity` | `pplx` | Perplexity | API key | [link](https://www.perplexity.ai) | — |
|
||||
| `piapi` | `pi` | PiAPI | API key, aggregator | [link](https://piapi.ai) | — |
|
||||
| `pioneer` | `pn` | Pioneer AI | API key | [link](https://pioneer.ai) | $75 free usage credits — no credit card required |
|
||||
| `plamo` | `plamo` | PLaMo | API key | [link](https://plamo.preferredai.jp/api) | — |
|
||||
| `poe` | `poe` | Poe | API key, aggregator | [link](https://creator.poe.com/api-reference) | Bearer API key for the Poe OpenAI-compatible API. |
|
||||
| `poixe-ai` | `poixe-ai` | Poixe AI | API key, aggregator | [link](https://poixe.com) | Current public free limits are small and model-group specific: 2 RPM/5 RPD for large-cup models and 20 RPM/50 RPD for small-cup models. |
|
||||
| `pollinations` | `pol` | Pollinations AI | API key, video | [link](https://pollinations.ai) | Anonymous/keyless access to the documented free models is best-effort. Local v3.8.50 verification (2026-07-31) returned 401 via OmniRoute and Cloudflare 1010 on direct upstream probes from the same network. Premium models still require a Pollinations API key from enter.pollinations.ai. |
|
||||
| `poolside` | `poolside` | Poolside | API key | [link](https://poolside.ai) | Laguna S 2.1 and XS 2.1 are free during Preview; no public numeric quota is published. |
|
||||
| `predibase` | `predibase` | Predibase | API key | [link](https://predibase.com) | ⚠️ **DEPRECATED.** serving.app.predibase.com no longer resolves (sweep 2026-06-19); the managed serving API appears discontinued. |
|
||||
| `publicai` | `publicai` | PublicAI | API key | [link](https://publicai.co) | Requires an API key — one-time signup credit, then paid |
|
||||
| `qianfan` | `qianfan` | Baidu Qianfan | API key | [link](https://cloud.baidu.com/product-s/qianfan_home) | — |
|
||||
| `qiniu` | `qiniu` | Qiniu | API key | [link](https://www.qiniu.com) | — |
|
||||
| `qwen-cloud` | `qwc` | Qwen Cloud | API key | [link](https://www.qwencloud.com/) | — |
|
||||
| `qwen-cloud-token-plan` | `qct` | Qwen Cloud Token Plan | API key | [link](https://www.qwencloud.com/pricing/token-plan) | — |
|
||||
| `recraft` | `recraft` | Recraft | API key, image | [link](https://recraft.ai) | — |
|
||||
| `regolo` | `regolo` | Regolo AI | API key | [link](https://regolo.ai) | Get your Regolo API key from regolo.ai, then paste it here as a Bearer token. |
|
||||
| `reka` | `reka` | Reka | API key | [link](https://docs.reka.ai/chat/overview) | Use your Reka API key. OmniRoute supports the OpenAI-compatible base URL https://api.reka.ai/v1 and sends both Authorization and X-Api-Key headers for compatibility. |
|
||||
| `requesty` | `requesty` | Requesty | API key | [link](https://requesty.ai) | Free tier ~200 requests/day - multi-model routing gateway (300+ models) |
|
||||
| `routeway` | `routeway` | Routeway | API key | [link](https://routeway.ai) | Create a free API key at routeway.ai, then paste it here as a Bearer token. |
|
||||
| `runwayml` | `runway` | Runway | API key, video | [link](https://docs.dev.runwayml.com) | Use your Runway API key in Authorization: Bearer <key>. OmniRoute targets the current Runway API at https://api.dev.runwayml.com/v1 and sends the required X-Runway-Version header automatically. |
|
||||
| `sambanova` | `samba` | SambaNova | API key | [link](https://sambanova.ai) | $5 free credits on signup (30-day validity), no credit card required |
|
||||
| `sap` | `sap` | SAP Generative AI Hub | API key, enterprise | [link](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/generative-ai-hub-in-sap-ai-core) | Use your SAP AI Core bearer token. Base URL can be your AI_API_URL root or a deploymentUrl from Generative AI Hub. |
|
||||
| `sarvam` | `sarvam` | Sarvam AI | API key | [link](https://docs.sarvam.ai) | ₹1,000 in free signup credits — never expire |
|
||||
| `scaleway` | `scw` | Scaleway AI | API key | [link](https://www.scaleway.com/en/docs/ai-data/generative-apis/) | 1M free tokens for new accounts — EU/GDPR compliant (Paris), Qwen3 235B & Llama 70B |
|
||||
| `sealion` | `sealion` | SEA-LION | API key | [link](https://sea-lion.ai) | Sign in at sea-lion.ai with Google (no card, no region wall), create an API key, then paste it here. |
|
||||
| `segmind` | `segmind` | Segmind | API key, image, video | [link](https://segmind.com) | Use your Segmind API key in the x-api-key header. OmniRoute targets https://api.segmind.com/v1/<model> and returns the generated image/video bytes directly. |
|
||||
| `sensenova` | `sensenova` | SenseNova | API key | [link](https://platform.sensenova.cn) | Get API key at platform.sensenova.cn |
|
||||
| `siliconflow` | `siliconflow` | SiliconFlow | API key | [link](https://cloud.siliconflow.com) | $1 free credits plus currently listed $0 models after identity verification; availability and limits may change |
|
||||
| `snowflake` | `snowflake` | Snowflake Cortex | API key, enterprise | [link](https://www.snowflake.com) | — |
|
||||
| `sparkdesk` | `sparkdesk` | SparkDesk | API key | [link](https://xinghuo.xfyun.cn) | Get API key at console.xfyun.cn |
|
||||
| `speka` | `speka` | Speka AI | API key, aggregator | [link](https://speka.me) | Free plan: $1 monthly usage, 10 RPM, one API key and access to open models and the playground; no card required. |
|
||||
| `stability-ai` | `stability` | Stability AI | API key, image | [link](https://stability.ai) | — |
|
||||
| `stepfun` | `stepfun` | StepFun | API key | [link](https://stepfun.com) | Get API key at platform.stepfun.com |
|
||||
| `sumopod` | `sumopod` | SumoPod | API key | [link](https://ai.sumopod.com) | Use your SumoPod API key (sk-...) in Authorization: Bearer <key>. Fully OpenAI-compatible. API base URL: https://ai.sumopod.com/v1. |
|
||||
| `suno` | `suno` | Suno | API key | [link](https://suno.ai) | Paste session cookie from suno.ai (Clerk auth) |
|
||||
| `synthetic` | `synthetic` | Synthetic | API key, aggregator | [link](https://synthetic.new) | — |
|
||||
| `tabitoken` | `tabitoken` | TabiToken | API key, aggregator | [link](https://tabitoken.com) | — |
|
||||
| `tencent` | `tencent` | Tencent Hunyuan | API key | [link](https://hunyuan.tencent.com) | Get API key at console.cloud.tencent.com |
|
||||
| `thebai` | `thebai` | TheB.AI | API key, aggregator | [link](https://theb.ai) | Bearer API key for the TheB.AI OpenAI-compatible gateway. |
|
||||
| `tinyfish` | `tf` | TinyFish Fetch | API key | [link](https://docs.tinyfish.ai/fetch-api) | X-API-Key from agent.tinyfish.ai/api-keys |
|
||||
| `together` | `together` | Together AI | API key, video | [link](https://www.together.ai) | — |
|
||||
| `token-kiosk` | `tk` | Token Kiosk | API key | [link](https://agent-router.gaib.ai) | Use your Token Kiosk API key in Authorization: Bearer <key>. Fully OpenAI-compatible gateway. API base URL: https://agent-router.gaib.ai/v1. |
|
||||
| `tokenreply` | `tokenreply` | TokenReply | API key, aggregator | [link](https://www.tokenreply.com) | Free-tagged models have model- and campaign-specific daily limits; no fixed global free quota is published. |
|
||||
| `tokenrouter` | `trk` | TokenRouter | API key | [link](https://tokenrouter.com) | Use your TokenRouter API key in Authorization: Bearer <key>. Fully OpenAI-compatible. API base URL: https://api.tokenrouter.com/v1. |
|
||||
| `topaz` | `topaz` | Topaz | API key, image | [link](https://topazlabs.com) | — |
|
||||
| `typhoon` | `typhoon` | Typhoon | API key | [link](https://docs.opentyphoon.ai) | Free API key with a 5 req/s and 200 req/m rate limit. |
|
||||
| `udio` | `udio` | Udio | API key | [link](https://udio.com) | Paste session cookie from udio.com (Supabase auth) |
|
||||
| `uncloseai` | `unc` | UncloseAI | API key | [link](https://uncloseai.com) | No auth required. API accepts any non-empty string as key for identification. If older built-in models return 404, use Available Models → Import from /models or Auto-Sync; verified live model: solidrust/Hermes-3-Llama-3.1-8B-AWQ. |
|
||||
| `unorouter` | `unorouter` | UnoRouter | API key, aggregator | [link](https://unorouter.ai) | Models with the :free suffix do not debit balance; limit is 1 request/minute per free model per user. |
|
||||
| `upstage` | `upstage` | Upstage | API key | [link](https://www.upstage.ai) | — |
|
||||
| `v0-vercel` | `v0` | v0 (Vercel) | API key | [link](https://v0.dev) | — |
|
||||
| `venice` | `venice` | Venice.ai | API key | [link](https://venice.ai) | — |
|
||||
| `vercel-ai-gateway` | `vag` | Vercel AI Gateway | API key, aggregator | [link](https://vercel.com/docs/ai-gateway) | — |
|
||||
| `vertex` | `vertex` | Vertex AI | API key, enterprise | [link](https://cloud.google.com/vertex-ai) | Provide Service Account JSON or OAuth access_token |
|
||||
| `vertex-partner` | `vp` | Vertex AI Partners | API key, enterprise | [link](https://cloud.google.com/vertex-ai) | Provide the same Service Account JSON used for Vertex AI partner models. |
|
||||
| `void-ai` | `void-ai` | Void AI | API key, aggregator | [link](https://voidai.app) | The public model catalog marks some models with a free plan requirement, but access is conditional and no numeric quota is confirmed. |
|
||||
| `volcengine` | `volcengine` | Volcengine | API key | [link](https://www.volcengine.com) | — |
|
||||
| `voyage-ai` | `voyage` | Voyage AI | API key, embed/rerank | [link](https://www.voyageai.com) | Bearer API key for Voyage AI embeddings and rerank APIs. |
|
||||
| `wafer` | `wafer` | Wafer AI | API key | [link](https://wafer.ai) | — |
|
||||
| `wandb` | `wandb` | Weights & Biases Inference | API key | [link](https://wandb.ai) | — |
|
||||
| `watsonx` | `watsonx` | IBM watsonx.ai Gateway | API key, enterprise | [link](https://www.ibm.com/products/watsonx-ai) | Use your watsonx bearer token. Base URL can be https://<region>.ml.cloud.ibm.com/ml/gateway/v1/ or a self-managed /ml/gateway/v1 endpoint. |
|
||||
| `writer` | `writer` | Writer | API key | [link](https://dev.writer.com) | — |
|
||||
| `x5lab` | `x5lab` | X5Lab | API key | [link](https://x5lab.dev) | Use your X5Lab API key (x5-...) in Authorization: Bearer <key>. Fully OpenAI-compatible. API base URL: https://api.x5lab.dev/v1. |
|
||||
| `xai` | `xai` | xAI (Grok) | API key | [link](https://x.ai) | Use an official xAI API key, or sign in with xAI OAuth. Grok Build JWT sessions remain a separate provider. |
|
||||
| `xiaomi-mimo` | `mimo` | Xiaomi MiMo | API key | [link](https://mimo.mi.com) | — |
|
||||
| `xiaomi-mimo-token-plan` | `mimotp` | Xiaomi MiMo Token Plan | API key | [link](https://mimo.mi.com) | — |
|
||||
| `yi` | `yi` | Yi (01.AI) | API key | [link](https://01.ai) | Get API key at platform.lingyiwanwu.com |
|
||||
| `yolo-auto` | `yolo-auto` | Yolo-Auto | API key, aggregator | [link](https://yolo-auto.com) | Free API access is request-limited and intended for testing; no numeric daily quota is published and free access is not promised indefinitely. |
|
||||
| `zai` | `zai` | Z.AI | API key | [link](https://open.bigmodel.cn) | — |
|
||||
| `zenmux` | `zm` | ZenMux | API key | [link](https://zenmux.ai) | Use your ZenMux API key in Authorization: Bearer <key>. ZenMux is fully OpenAI-compatible. Base URL: https://zenmux.ai/api/v1. |
|
||||
| `zerolimitai` | `zerolimitai` | ZeroLimitAI | API key, aggregator | [link](https://www.zerolimitai.com) | Temporary free trial is advertised, but official pages conflict between 3 and 7 days; a 100-calls/day claim is not treated as permanent. |
|
||||
| `zylo-api` | `zylo` | Zylo API | API key, aggregator | [link](https://zyloai.net) | Basic plan: 10 RPM, 7,200 requests/day and 200,000 tokens/day; limited to Basic text models. |
|
||||
|
||||
## Local Providers (14)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `comfyui` | `comfyui` | ComfyUI | Local | [link](https://github.com/comfyanonymous/ComfyUI) | No API key required. Configure the local ComfyUI base URL (default: http://localhost:8188). |
|
||||
| `docker-model-runner` | `dmr` | Docker Model Runner | Local, self-hosted | [link](https://docs.docker.com/ai/model-runner/) | API key optional. Configure the local Docker Model Runner OpenAI-compatible base URL (default: http://localhost:12434/v1). |
|
||||
| `lemonade` | `lemonade` | Lemonade Server | Local, self-hosted | [link](https://lemonade-server.ai) | API key optional. Configure the local Lemonade OpenAI-compatible base URL (default: http://localhost:13305/api/v1). |
|
||||
| `llama-cpp` | `llamacpp` | llama.cpp | Local, self-hosted | [link](https://github.com/ggml-org/llama.cpp) | API key optional (use any value, e.g. sk-no-key-required). Configure the llama-server OpenAI-compatible base URL (default: http://127.0.0.1:8080/v1). Note: if Llamafile is also installed, both default to port 8080 — run only one at a time or override the port. |
|
||||
| `llamafile` | `llamafile` | Llamafile | Local, self-hosted | [link](https://github.com/Mozilla-Ocho/llamafile) | API key optional. Configure the local Llamafile OpenAI-compatible base URL (default: http://127.0.0.1:8080/v1). |
|
||||
| `lm-studio` | `lmstudio` | LM Studio | Local, self-hosted | [link](https://lmstudio.ai) | API key optional. Configure the local LM Studio OpenAI-compatible base URL (default: http://localhost:1234/v1). |
|
||||
| `mlx-gemma` | `mlx-gemma` | MLX Gemma 26B | Local, self-hosted | [link](https://github.com/ml-explore/mlx) | No API key required. Runs mlx-lm server locally on port 11435. Requires uv and mlx-lm installed. Model: mlx-community/gemma-4-26B-A4B-it-qat-q4_0-mlx-aligned (~15.9GB peak memory). |
|
||||
| `mlx-qwen` | `mlx-qwen` | MLX Qwen 3.8 27B | Local, self-hosted | [link](https://github.com/ml-explore/mlx) | No API key required. Runs mlx-lm server locally on port 11436. Requires uv and mlx-lm installed. Model: maglun/Qwen3.8-27B-MLX-Mixed-3.80bpw (~13.1GB peak memory). |
|
||||
| `ollama-local` | `ollama` | Ollama | Local, self-hosted | [link](https://ollama.com) | No API key required. Ollama runs locally — configure its OpenAI-compatible base URL (default: http://localhost:11434/v1) and make sure Ollama is running before connecting. |
|
||||
| `oobabooga` | `ooba` | oobabooga | Local, self-hosted | [link](https://github.com/oobabooga/text-generation-webui) | API key optional. Configure the local oobabooga OpenAI-compatible base URL (default: http://localhost:5000/v1). |
|
||||
| `sdwebui` | `sdwebui` | SD WebUI | Local | [link](https://github.com/AUTOMATIC1111/stable-diffusion-webui) | No API key required. Configure the local WebUI base URL (default: http://localhost:7860). |
|
||||
| `triton` | `triton` | NVIDIA Triton | Local, self-hosted | [link](https://developer.nvidia.com/triton-inference-server) | API key optional. Configure the Triton OpenAI-compatible base URL (default: http://localhost:8000/v1). |
|
||||
| `vllm` | `vllm` | vLLM | Local, self-hosted | [link](https://github.com/vllm-project/vllm) | API key optional. Configure the local vLLM OpenAI-compatible base URL (default: http://localhost:8000/v1). |
|
||||
| `xinference` | `xinference` | XInference | Local, self-hosted | [link](https://inference.readthedocs.io) | API key optional. Configure the local XInference OpenAI-compatible base URL (default: http://localhost:9997/v1). |
|
||||
|
||||
## Search Providers (13)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `brave-search` | `brave-search` | Brave Search | Search | [link](https://brave.com/search/api) | Subscription token from Brave Search API dashboard |
|
||||
| `exa-search` | `exa-search` | Exa Search | Search | [link](https://exa.ai) | API key from dashboard.exa.ai |
|
||||
| `firecrawl` | `fc` | Firecrawl | Search | [link](https://firecrawl.dev) | API key from firecrawl.dev/app/api-keys (or set your self-hosted Firecrawl base URL) |
|
||||
| `google-pse-search` | `google-pse` | Google Programmable Search | Search | [link](https://developers.google.com/custom-search/v1/overview) | Requires a Google API key and your Programmable Search Engine ID (cx) |
|
||||
| `linkup-search` | `linkup` | Linkup Search | Search | [link](https://docs.linkup.so) | Bearer API key from the Linkup dashboard |
|
||||
| `ollama-search` | `ollama-search` | Ollama Search | Search | [link](https://ollama.com/settings/keys) | Same API key as Ollama Cloud (from ollama.com/settings/keys) |
|
||||
| `perplexity-search` | `pplx-search` | Perplexity Search | Search | [link](https://docs.perplexity.ai/guides/search-quickstart) | Same API key as Perplexity (pplx-...) |
|
||||
| `searchapi-search` | `searchapi` | SearchAPI | Search | [link](https://www.searchapi.io/docs/google) | API key from SearchAPI (query param or Bearer auth) |
|
||||
| `searxng-search` | `searxng` | SearXNG Search | Search | [link](https://docs.searxng.org) | API key is optional. Set your SearXNG base URL. Some instances may require a bearer token for access. |
|
||||
| `serper-search` | `serper-search` | Serper Search | Search | [link](https://serper.dev) | API key from serper.dev dashboard |
|
||||
| `tavily-search` | `tavily-search` | Tavily Search | Search | [link](https://tavily.com) | API key from app.tavily.com (format: tvly-...) |
|
||||
| `x-search` | `x_search` | X Search (Grok) | Search | [link](https://docs.x.ai/developers/tools/x-search) | SuperGrok OAuth (xai-oauth) or xAI API key. This is Grok X Search, not the X Developer MCP. |
|
||||
| `youcom-search` | `youcom-search` | You.com Search | Search | [link](https://you.com/business/api/) | X-API-Key from the You.com platform dashboard |
|
||||
|
||||
## Audio-only Providers (12)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `assemblyai` | `aai` | AssemblyAI | Audio | [link](https://assemblyai.com) | — |
|
||||
| `aws-polly` | `polly` | AWS Polly | Audio | [link](https://aws.amazon.com/polly/) | Use AWS Secret Access Key as API key; set providerSpecificData.accessKeyId and optional region. |
|
||||
| `cartesia` | `cartesia` | Cartesia | Audio | [link](https://cartesia.ai) | — |
|
||||
| `deepgram` | `dg` | Deepgram | Audio | [link](https://deepgram.com) | — |
|
||||
| `elevenlabs` | `el` | ElevenLabs | Audio | [link](https://elevenlabs.io) | — |
|
||||
| `fishaudio` | `fishaudio` | Fish Audio | Audio | [link](https://fish.audio) | — |
|
||||
| `gladia` | `gladia` | Gladia | Audio | [link](https://gladia.io) | — |
|
||||
| `inworld` | `inworld` | Inworld | Audio | [link](https://inworld.ai) | — |
|
||||
| `playht` | `playht` | PlayHT | Audio | [link](https://play.ht) | — |
|
||||
| `rev-ai` | `revai` | Rev AI | Audio | [link](https://www.rev.ai) | — |
|
||||
| `soniox` | `sx` | Soniox | Audio | [link](https://soniox.com) | — |
|
||||
| `speechmatics` | `sm` | Speechmatics | Audio | [link](https://www.speechmatics.com) | Free tier — 8 hours/month, no credit card required. Batch (async) mode only. |
|
||||
|
||||
## Upstream Proxy Providers (2)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `9router` | `nr` | 9router | Upstream proxy | [link](https://www.npmjs.com/package/9router) | — |
|
||||
| `cliproxyapi` | `cpa` | CLIProxyAPI | Upstream proxy | [link](https://github.com/router-for-me/CLIProxyAPI) | — |
|
||||
|
||||
## Cloud Agent Providers (3)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `codex-cloud` | `codex-cloud` | Codex Cloud | Cloud agent | [link](https://openai.com/codex) | OpenAI API key with Codex Cloud task access. |
|
||||
| `devin` | `devin` | Devin | Cloud agent | [link](https://devin.ai) | Devin API key for cloud agent sessions. |
|
||||
| `jules` | `jules` | Google Jules | Cloud agent | [link](https://jules.google) | Jules API key for creating and managing cloud coding tasks. |
|
||||
|
||||
## System Providers (1)
|
||||
|
||||
| ID | Alias | Name | Tags | Website | Notes |
|
||||
|----|-------|------|------|---------|-------|
|
||||
| `auto` | `auto` | Auto (Zero-Config) | System | — | — |
|
||||
|
||||
## Sources of truth
|
||||
|
||||
- Catalog: [`src/shared/constants/providers.ts`](../../src/shared/constants/providers.ts)
|
||||
- Registry (per-model details): [`open-sse/config/providerRegistry.ts`](../../open-sse/config/providerRegistry.ts)
|
||||
- Executors: [`open-sse/executors/`](../../open-sse/executors/) (106 implementations)
|
||||
- Translators: [`open-sse/translator/`](../../open-sse/translator/)
|
||||
|
||||
## See Also
|
||||
|
||||
- [FREE_TIERS.md](./FREE_TIERS.md) — curated free-tier guide
|
||||
- [USER_GUIDE.md](../guides/USER_GUIDE.md) — provider setup walkthrough
|
||||
- [ARCHITECTURE.md](../architecture/ARCHITECTURE.md) — overall architecture
|
||||
50
README.md
50
README.md
@@ -7,7 +7,7 @@
|
||||
|
||||
# 🚀 OmniRoute — The Free AI Gateway
|
||||
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 351 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 15–95% tokens (~89% avg) — never hit limits. 351 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 352 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 15–95% tokens (~89% avg) — never hit limits. 352 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
</div>
|
||||
|
||||
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute catalogs **445 free-tier entries across 39 recurring pool keys** and computes the token headline from the **20 pools with a published positive monthly budget**, deduplicated by shared pool. The result stays visible on the dashboard (`/dashboard/free-tiers`).
|
||||
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute catalogs **455 free-tier entries across 40 recurring pool keys** and computes the token headline from the **20 pools with a published positive monthly budget**, deduplicated by shared pool. The result stays visible on the dashboard (`/dashboard/free-tiers`).
|
||||
|
||||
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.51B free tokens per month steady, up to ~2.13B in the first month with signup credits, from 39 documented recurring pool keys covering 445 cataloged free-tier entries behind one endpoint. Honest pool-deduped math — each shared pool counted once, including 20 recurring pools with a published positive monthly token budget; 13 providers are marked avoid in the terms-risk catalog so you decide. Budget bar includes Mistral 1B, LLM7 150M, Nara 150M, Gemini 60M and smaller pools, plus first-month signup credits and permanently-free no-token-cap providers surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
|
||||
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.51B free tokens per month steady, up to ~2.13B in the first month with signup credits, from 40 documented recurring pool keys covering 455 cataloged free-tier entries behind one endpoint. Honest pool-deduped math — each shared pool counted once, including 20 recurring pools with a published positive monthly token budget; 15 providers are marked avoid in the terms-risk catalog so you decide. Budget bar includes Mistral 1B, LLM7 150M, Nara 150M, Gemini 60M and smaller pools, plus first-month signup credits and permanently-free no-token-cap providers surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
|
||||
|
||||
> Animated summary of the live `/dashboard/free-tiers` page. Full methodology (pool dedupe, credit tiers, provider terms): **[docs/reference/FREE_TIERS.md](docs/reference/FREE_TIERS.md)**.
|
||||
>
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
| | v3.8.49 | **v3.8.50** | `v3.8.51+` |
|
||||
| ------------------------- | :-----: | :-----------------------: | :---------: |
|
||||
| 🌐 Providers | 290 | **350** | more queued |
|
||||
| 🌐 Providers | 290 | **352** | more queued |
|
||||
| 🧠 Unique chat model IDs | 1185 | **1312** | — |
|
||||
| 🖼️ Modality Bridge | — | 🆕 vision + audio + video | — |
|
||||
| 📡 Radar free catalog | — | 🆕 opt-in | — |
|
||||
@@ -101,7 +101,7 @@
|
||||
<tr>
|
||||
<td align="right"><b>⚙️ Features</b></td>
|
||||
<td align="center"><a href="#-combos--the-flagship">🎯 Combos</a></td>
|
||||
<td align="center"><a href="#-351-ai-providers--154-catalog-marked-free">🌐 Providers</a></td>
|
||||
<td align="center"><a href="#-352-ai-providers--154-catalog-marked-free">🌐 Providers</a></td>
|
||||
<td align="center"><a href="#-full-cli--a2a--mcp">🔌 CLI & MCP</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -189,7 +189,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/works-zero-config.svg" width="100%" alt="Works the second you install it — zero config. Three steps: 1. Install — npm i -g omniroute, server boots on localhost:20128. 2. Point your tool at http://localhost:20128/v1 — any OpenAI-compatible tool (Claude Code, Cursor, Cline). 3. It answers — call model auto for an instant reply, with no API key, no signup, no configuration. Keyless provider OpenCode Free is pre-wired into the auto combo, so a fresh install responds out of the box."/>
|
||||
<img src="./docs/diagrams/works-zero-config.svg" width="100%" alt="Works the second you install it — zero config. Three steps: 1. Install — npm i -g omniroute, server boots on localhost:20128. 2. Point your tool at http://localhost:20128/v1 — any OpenAI-compatible tool (Claude Code, Cursor, Cline). 3. It answers — call model auto for an instant reply, with no API key, no signup, no configuration. Keyless free providers OpenCode Free and Felo are pre-wired into the auto combo, so a fresh install responds out of the box."/>
|
||||
|
||||
```bash
|
||||
# Fresh install, zero credentials — `auto` already works:
|
||||
@@ -198,7 +198,7 @@ curl http://localhost:20128/v1/chat/completions \
|
||||
-d '{"model":"auto","messages":[{"role":"user","content":"Hello!"}]}'
|
||||
```
|
||||
|
||||
<sub>Prefer a specific free backend? Call `oc/…` (OpenCode Free) directly. Then graduate to `auto` and let OmniRoute pick.</sub>
|
||||
<sub>Prefer a specific free backend? Call it directly, e.g. `oc/…` (OpenCode Free) or `felo/…` (Felo). Then graduate to `auto` and let OmniRoute pick.</sub>
|
||||
|
||||
<sub>📦 Copy-paste quickstart scripts for **Python, Node.js, PHP, and cURL** → [`examples/quickstart/`](examples/quickstart/)</sub>
|
||||
|
||||
@@ -210,7 +210,7 @@ curl http://localhost:20128/v1/chat/completions \
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 351 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 351 providers · up to 95% token savings on eligible workloads · $0 to start with 90+ free tiers and 56 recurring/keyless free-forever providers · 35 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 352 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 352 providers · up to 95% token savings on eligible workloads · $0 to start with 90+ free tiers and 56 recurring/keyless free-forever providers · 35 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
@@ -266,7 +266,7 @@ curl http://localhost:20128/v1/chat/completions \
|
||||
<tr>
|
||||
<td align="center" width="150">
|
||||
<a href="https://cheaperinference.com/?utm_source=omniroute">
|
||||
<img src="./public/providers/cli-generic.svg" width="64" alt="Cheaper Inference"/>
|
||||
<img src="public/providers/cheaperinference.svg" width="64" alt="Cheaper Inference"/>
|
||||
</a>
|
||||
<br/><b>Cheaper Inference</b><br/><sub>cheaperinference.com</sub><br/><br/>
|
||||
<img src="https://img.shields.io/badge/Open_Source_Friend-31f889?style=flat-square&labelColor=04170d" alt="Open Source Friend"/>
|
||||
@@ -292,7 +292,7 @@ curl http://localhost:20128/v1/chat/completions \
|
||||
<tr>
|
||||
<td align="center" width="120">
|
||||
<a href="https://agentrouter.org/register?aff=70LM">
|
||||
<img src="./public/providers/cli-generic.svg" width="32" alt="AgentRouter"/>
|
||||
<img src="public/providers/agentrouter.png" width="32" alt="AgentRouter"/>
|
||||
</a>
|
||||
<br/><sub><b>AgentRouter</b></sub><br/><sub>agentrouter.org</sub>
|
||||
</td>
|
||||
@@ -424,7 +424,7 @@ All **19** strategies — mix & match per combo step:
|
||||
<tr>
|
||||
<td align="center">16</td>
|
||||
<td nowrap><code>lkgp</code></td>
|
||||
<td>Last-Known-Good Path — pins to the last successful provider, then falls back to rules</td>
|
||||
<td>Last-Known-Good Path — sticky to the last successful target</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">17</td>
|
||||
@@ -461,7 +461,7 @@ All **19** strategies — mix & match per combo step:
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 351 providers, 90+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 352 providers, 90+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
|
||||
|
||||
<sub>📊 Full methodology & per-feature detail vs 9router, OpenRouter, CLIProxyAPI & LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)</sub>
|
||||
|
||||
@@ -557,9 +557,9 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
|
||||
- **🧠 Memory you control** — off by default, opt-in int8 vector quantization + typed decay, per-request `x-omniroute-no-memory`. → [Memory](docs/frameworks/MEMORY.md)
|
||||
- **🛡️ Security** — prompt-injection guard on every LLM route (red-team suite), opt-in credential-masking guardrail (redacts leaked API keys/secrets in both directions), free DuckDuckGo last-resort web search, and an optional OIDC login gate for the dashboard (password login always stays available). → [Guardrails](docs/security/GUARDRAILS.md)
|
||||
- **🖼️ New endpoints** — `/v1/ocr` (Mistral OCR) and `/v1/audio/translations` (Whisper-style) round out the media surface. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Freepik, Adobe Firefly, Segmind, and speech providers such as ElevenLabs. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Freepik, Adobe Firefly, Microsoft Designer, Segmind, EdgeTTS. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **🌍 Deployment & ops** — reverse-proxy `basePath`, browser-language auto-detect, per-key device tracking, root-less MITM trust, zh-TW localization. → [Environment](docs/reference/ENVIRONMENT.md)
|
||||
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **351-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
|
||||
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **352-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
|
||||
- **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
|
||||
- **⚡ Local performance & infra** — one-click local Redis, Cloudflare Workers / Deno Deploy relay deployers, Bifrost & Mux as supervised embedded services. → [Embedded Services](docs/frameworks/EMBEDDED-SERVICES.md)
|
||||
|
||||
@@ -577,8 +577,8 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
|
||||
<td align="center" width="76"><a href="https://github.com/anthropics/claude-code"><img src="./public/providers/claude.svg" width="40" alt="Claude Code"/><br/><sub><b>Claude Code</b></sub><br/><sub> </sub></a></td>
|
||||
<td align="center" width="76"><a href="https://github.com/openai/codex"><img src="./public/providers/codex.svg" width="40" alt="Codex CLI"/><br/><sub><b>Codex CLI</b></sub><br/><sub> </sub></a></td>
|
||||
<td align="center" width="76"><picture><source media="(prefers-color-scheme:dark)" srcset="https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.91.0/dark/cline.png"/><img src="https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@1.91.0/icons/cline.svg" width="40" alt="Cline"/></picture><br/><sub><b>Cline</b></sub><br/><sub> </sub></td>
|
||||
<td align="center" width="76"><a href="https://github.com/Kilo-Org/kilocode"><img src="./public/providers/cli-generic.svg" width="40" alt="Kilo Code"/><br/><sub><b>Kilo Code</b></sub><br/><sub> </sub></a></td>
|
||||
<td align="center" width="76"><a href="https://github.com/Zoo-Code-Org/Zoo-Code"><img src="./public/providers/cli-generic.svg" width="40" alt="Zoo Code"/><br/><sub><b>Zoo Code</b></sub><br/><sub> </sub></a></td>
|
||||
<td align="center" width="76"><a href="https://github.com/Kilo-Org/kilocode"><img src="./public/providers/kilocode.svg" width="40" alt="Kilo Code"/><br/><sub><b>Kilo Code</b></sub><br/><sub> </sub></a></td>
|
||||
<td align="center" width="76"><a href="https://github.com/Zoo-Code-Org/Zoo-Code"><img src="./public/providers/zoocode.png" width="40" alt="Zoo Code"/><br/><sub><b>Zoo Code</b></sub><br/><sub> </sub></a></td>
|
||||
<td align="center" width="76"><img src="./public/providers/continue.svg" width="40" alt="Continue"/><br/><sub><b>Continue</b></sub><br/><sub> </sub></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -587,10 +587,10 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
|
||||
<td align="center" width="76"><img src="./public/providers/cli-generic.svg" width="40" alt="jcode"/><br/><sub><b>jcode</b></sub><br/><sub> </sub></td>
|
||||
<td align="center" width="76"><img src="./public/providers/deepseek.svg" width="40" alt="DeepSeek TUI"/><br/><sub><b>DeepSeek TUI</b></sub><br/><sub> </sub></td>
|
||||
<td align="center" width="76"><img src="./public/providers/cli-generic.svg" width="40" alt="CodeWhale"/><br/><sub><b>CodeWhale</b></sub><br/><sub> </sub></td>
|
||||
<td align="center" width="76"><a href="https://github.com/anomalyco/opencode"><img src="./public/providers/cli-generic.svg" width="40" alt="OpenCode"/><br/><sub><b>OpenCode</b></sub><br/><sub> </sub></a></td>
|
||||
<td align="center" width="76"><a href="https://github.com/anomalyco/opencode"><picture><source media="(prefers-color-scheme:dark)" srcset="https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.91.0/dark/opencode.png"/><img src="https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@1.91.0/icons/opencode.svg" width="40" alt="OpenCode"/></picture><br/><sub><b>OpenCode</b></sub><br/><sub> </sub></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="76"><img src="./public/providers/cli-generic.svg" width="40" alt="Factory Droid"/><br/><sub><b>Factory Droid</b></sub><br/><sub> </sub></td>
|
||||
<td align="center" width="76"><img src="./public/providers/droid.svg" width="40" alt="Factory Droid"/><br/><sub><b>Factory Droid</b></sub><br/><sub> </sub></td>
|
||||
<td align="center" width="76"><img src="./public/providers/copilot.svg" width="40" alt="GitHub Copilot CLI"/><br/><sub><b>Copilot CLI</b></sub><br/><sub> </sub></td>
|
||||
<td align="center" width="76"><img src="./public/providers/cursor.svg" width="40" alt="Cursor CLI"/><br/><sub><b>Cursor CLI</b></sub><br/><sub> </sub></td>
|
||||
<td align="center" width="76"><img src="./public/providers/cli-generic.svg" width="40" alt="Smelt"/><br/><sub><b>Smelt</b></sub><br/><sub> </sub></td>
|
||||
@@ -642,11 +642,11 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
|
||||
<div align="center">
|
||||
|
||||
## 🌐 351 AI Providers — 154 Catalog-Marked Free
|
||||
## 🌐 352 AI Providers — 154 Catalog-Marked Free
|
||||
|
||||
</div>
|
||||
|
||||
> **351 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **154 carrying `hasFree: true` discovery metadata**. The chat model registry covers **268 providers / 2,566 distinct provider-model pairs / 1,312 raw model IDs**; the separate free-budget catalog has **455 per-model rows**, **40 recurring pools** and **56 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
|
||||
> **352 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **154 carrying `hasFree: true` discovery metadata**. The chat model registry covers **268 providers / 2,566 distinct provider-model pairs / 1,312 raw model IDs**; the separate free-budget catalog has **455 per-model rows**, **40 recurring pools** and **56 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
|
||||
|
||||
<div align="center">
|
||||
|
||||
@@ -687,8 +687,8 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" width="150"><img src="./public/providers/cli-generic.svg" width="42" alt="OpenCode Zen"/><br/><b>OpenCode Zen</b><br/><sub>DeepSeek V4, Nemotron 3<br/>No token cap</sub></td>
|
||||
<td align="center" width="150"><img src="./public/providers/cli-generic.svg" width="42" alt="Kilo Code"/><br/><b>Kilo Code</b><br/><sub>Auto-router, Tencent Hy3<br/>Free forever</sub></td>
|
||||
<td align="center" width="150"><img src="./public/providers/opencode.svg" width="42" alt="OpenCode Zen"/><br/><b>OpenCode Zen</b><br/><sub>DeepSeek V4, Nemotron 3<br/>No token cap</sub></td>
|
||||
<td align="center" width="150"><img src="./public/providers/kilocode.svg" width="42" alt="Kilo Code"/><br/><b>Kilo Code</b><br/><sub>Auto-router, Tencent Hy3<br/>Free forever</sub></td>
|
||||
<td align="center" width="150"><img src="./public/providers/requesty.svg" width="42" alt="Requesty"/><br/><b>Requesty</b><br/><sub>GPT-OSS 120B, Nemotron<br/>Free forever</sub></td>
|
||||
<td align="center" width="150"><img src="https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@1.91.0/icons/siliconcloud-color.svg" width="42" alt="SiliconFlow"/><br/><b>SiliconFlow</b><br/><sub>DeepSeek V3.2 / R1<br/>Free tier</sub></td>
|
||||
<td align="center" width="150"><img src="./public/providers/zhipu.svg" width="42" alt="Z.AI GLM"/><br/><b>Z.AI GLM</b><br/><sub>GLM-4.7 / 4.5-Flash<br/>Free forever</sub></td>
|
||||
@@ -1202,7 +1202,7 @@ Métricas canônicas em 2026-08-24: **1.029 vídeos únicos** · **11.132.922 vi
|
||||
<tr><td nowrap><b>Runtime</b></td><td>Node.js 22.x / 24.x LTS — <code>>=22.22.2 <23 || >=24.0.0 <27</code></td></tr>
|
||||
<tr><td nowrap><b>Language</b></td><td>TypeScript 6.0 — <b>100% TypeScript</b> across <code>src/</code> and <code>open-sse/</code> (zero <code>any</code> in core since v2.0)</td></tr>
|
||||
<tr><td nowrap><b>Framework</b></td><td>Next.js 16 + React 19 + Tailwind CSS 4</td></tr>
|
||||
<tr><td nowrap><b>Database</b></td><td>better-sqlite3 (SQLite, WAL journaling) + LowDB (JSON legacy) — 120 domain modules, 166 migrations</td></tr>
|
||||
<tr><td nowrap><b>Database</b></td><td>better-sqlite3 (SQLite, WAL journaling) + LowDB (JSON legacy) — 120 domain modules, 160 migrations</td></tr>
|
||||
<tr><td nowrap><b>Memory</b></td><td>SQLite FTS5 full-text + int8-quantized vector embeddings, typed decay</td></tr>
|
||||
<tr><td nowrap><b>Schemas</b></td><td>Zod 4 — MCP tool I/O validation + API contracts</td></tr>
|
||||
<tr><td nowrap><b>Protocols</b></td><td>MCP (stdio / HTTP / SSE) + A2A v0.3 (JSON-RPC 2.0 + SSE)</td></tr>
|
||||
@@ -1265,7 +1265,7 @@ Métricas canônicas em 2026-08-24: **1.029 vídeos únicos** · **11.132.922 vi
|
||||
<tr><td nowrap><b><a href="docs/architecture/RESILIENCE_GUIDE.md">Resilience Guide</a></b></td><td>Circuit breakers, cooldowns, queue, anti-thundering herd, TLS spoofing</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/routing/AUTO-COMBO.md">Auto-Combo Engine</a></b></td><td>15-factor scoring, mode packs, self-healing</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/ops/PROXY_GUIDE.md">Proxy Guide</a></b></td><td>3-level proxy system, 1proxy marketplace, registry CRUD</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>Consolidated directory: 39 documented recurring pools / 445 cataloged free-tier entries</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>Consolidated directory: 40 documented recurring pools / 455 cataloged free-tier entries</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/guides/FEATURES.md">Features Gallery</a></b></td><td>Visual dashboard tour with screenshots</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/architecture/CODEBASE_DOCUMENTATION.md">Codebase Documentation</a></b></td><td>Beginner-friendly codebase walkthrough</td></tr>
|
||||
</table>
|
||||
@@ -1621,7 +1621,7 @@ OmniRoute stands on the shoulders of giants. It started as a fork of **[9router]
|
||||
|
||||
<table>
|
||||
<tr><th align="left">Project</th><th align="center">⭐</th><th align="left">How it inspired OmniRoute</th></tr>
|
||||
<tr><td nowrap><b><a href="https://github.com/chouzz/llm-interceptor">llm-interceptor</a></b></td><td align="center">66</td><td>MITM interception/analysis of coding-assistant ↔ LLM traffic informed early Traffic Inspector requirements. Four previously derived modules — SSE merging, conversation normalization, secret masking and header sanitization — have been replaced by independent clean-room implementations based on public protocol standards. The two host-passthrough surfaces (<code>passthrough.ts</code> and <code>_internal/bypass.cjs</code>) remain OmniRoute-internal implementations classified independently; they were not rewritten as part of that replacement.</td></tr>
|
||||
<tr><td nowrap><b><a href="https://github.com/chouzz/llm-interceptor">llm-interceptor</a></b></td><td align="center">66</td><td>MITM interception/analysis of coding-assistant ↔ LLM traffic — our Traffic Inspector ports its SSE merge, conversation normalization, host passthrough and secret masking. The upstream's complete license text is still under provenance review.</td></tr>
|
||||
<tr><td nowrap><b><a href="https://github.com/InterceptSuite/ProxyBridge">ProxyBridge</a></b></td><td align="center">5,995</td><td>Transparent per-process proxy routing — inspired our crash-safe MITM teardown, socket idle-timeouts, <code>/proc</code> process attribution and TPROXY capture.</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -24,280 +24,3 @@ NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPO
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
||||
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
## blackwell-systems/gcf-typescript
|
||||
|
||||
The generic-profile codec in
|
||||
`open-sse/services/compression/engines/headroom/gcf/{decode_generic,generic,index,scalar}.ts`
|
||||
is adapted from
|
||||
[`blackwell-systems/gcf-typescript`](https://github.com/blackwell-systems/gcf-typescript/tree/00972f2dc781477eb6d369e62edfe03ad4112a07),
|
||||
commit `00972f2dc781477eb6d369e62edfe03ad4112a07`. The license below is reproduced
|
||||
from that commit's
|
||||
[`LICENSE`](https://github.com/blackwell-systems/gcf-typescript/blob/00972f2dc781477eb6d369e62edfe03ad4112a07/LICENSE).
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Dayna Blackwell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
||||
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
## lipis/flag-icons
|
||||
|
||||
The country flag SVGs in `docs/assets/flags/` are copied from the `flags/4x3/` directory of
|
||||
[`lipis/flag-icons`](https://github.com/lipis/flag-icons/tree/086f7e97d657358203916dbe84f61c2bccaa81eb),
|
||||
commit `086f7e97d657358203916dbe84f61c2bccaa81eb`. The license below is reproduced
|
||||
from that commit's
|
||||
[`LICENSE`](https://github.com/lipis/flag-icons/blob/086f7e97d657358203916dbe84f61c2bccaa81eb/LICENSE).
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Panayiotis Lipiridis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
||||
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
## LobeHub provider asset derivatives
|
||||
|
||||
Six local provider SVGs contain geometry derived from fixed components in
|
||||
`@lobehub/icons@5.10.0`. The source package is pinned as follows:
|
||||
|
||||
- Tarball:
|
||||
<https://registry.npmjs.org/@lobehub/icons/-/icons-5.10.0.tgz>
|
||||
- npm shasum: `add1baced073a60157d39c7820b8d5c1928a1054`
|
||||
- npm integrity:
|
||||
`sha512-CIpjkISCLRK7haDtSugGFd0o3odaJts8ewJOkUiEFtns3xvsqbl8i24eowBnjw+yMDQVQyNONlhqTD58YC6Ljg==`
|
||||
- License file in the fixed tarball: `package/LICENSE`
|
||||
|
||||
| Local derivative | Fixed tarball source |
|
||||
| ------------------------------- | ----------------------------------------- |
|
||||
| `public/providers/360ai.svg` | `package/es/Ai360/components/Color.js` |
|
||||
| `public/providers/baichuan.svg` | `package/es/Baichuan/components/Color.js` |
|
||||
| `public/providers/codex.svg` | `package/es/Codex/components/Color.js` |
|
||||
| `public/providers/copilot.svg` | `package/es/Copilot/components/Color.js` |
|
||||
| `public/providers/openclaw.svg` | `package/es/OpenClaw/components/Color.js` |
|
||||
| `public/providers/stepfun.svg` | `package/es/Stepfun/components/Color.js` |
|
||||
|
||||
The fixed tarball contains this license notice:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 LobeHub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
This package notice applies to the derived SVG geometry identified above. It does not grant rights
|
||||
in any underlying brand name, logo, or trademark.
|
||||
|
||||
## theSVG provider assets
|
||||
|
||||
At release snapshot `091589089cd134a94df9f6cdab9ba562b2cefd18`, 65 local provider SVGs were
|
||||
byte-exact matches for `public/icons/<slug>/default.svg` in the theSVG repository at immutable
|
||||
commit [`7870bc1c5f657d9accbb7f96cc457b8dd3363ee8`](https://github.com/GLINCKER/thesvg/tree/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8).
|
||||
The fixed upstream evidence includes its
|
||||
[`LICENSE`](https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/LICENSE),
|
||||
[`LEGAL.md`](https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/LEGAL.md),
|
||||
[`TRADEMARK.md`](https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/TRADEMARK.md),
|
||||
[`LICENSING.md`](https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/LICENSING.md),
|
||||
and
|
||||
[`src/data/icons.json`](https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json).
|
||||
|
||||
The byte match proves source provenance for the listed files. It does not prove that a registry
|
||||
claim was authorized by each brand owner, and it does not relicense the logos or their underlying
|
||||
brand marks. The theSVG source applies its MIT license to its codebase, tooling, and catalog; its
|
||||
own legal documents separately reserve trademark rights to the respective owners.
|
||||
|
||||
The fixed theSVG source contains this license notice:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 thesvg.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### Byte-exact file scope (65/65)
|
||||
|
||||
- `public/providers/alibaba.svg`
|
||||
- `public/providers/anthropic.svg`
|
||||
- `public/providers/arcee.svg`
|
||||
- `public/providers/assemblyai.svg`
|
||||
- `public/providers/aws.svg`
|
||||
- `public/providers/azure.svg`
|
||||
- `public/providers/bailian.svg`
|
||||
- `public/providers/baseten.svg`
|
||||
- `public/providers/cerebras.svg`
|
||||
- `public/providers/cline.svg`
|
||||
- `public/providers/comfyui.svg`
|
||||
- `public/providers/continue.svg`
|
||||
- `public/providers/cursor.svg`
|
||||
- `public/providers/deepgram.svg`
|
||||
- `public/providers/deepinfra.svg`
|
||||
- `public/providers/elevenlabs.svg`
|
||||
- `public/providers/exa.svg`
|
||||
- `public/providers/fal.svg`
|
||||
- `public/providers/fireworks.svg`
|
||||
- `public/providers/friendli.svg`
|
||||
- `public/providers/gemini.svg`
|
||||
- `public/providers/grok.svg`
|
||||
- `public/providers/groq.svg`
|
||||
- `public/providers/heroku.svg`
|
||||
- `public/providers/huggingface.svg`
|
||||
- `public/providers/hyperbolic.svg`
|
||||
- `public/providers/ibm.svg`
|
||||
- `public/providers/inference.svg`
|
||||
- `public/providers/lambda.svg`
|
||||
- `public/providers/longcat.svg`
|
||||
- `public/providers/minimax.svg`
|
||||
- `public/providers/mistral.svg`
|
||||
- `public/providers/moonshot.svg`
|
||||
- `public/providers/morph.svg`
|
||||
- `public/providers/nebius.svg`
|
||||
- `public/providers/novita.svg`
|
||||
- `public/providers/nvidia.svg`
|
||||
- `public/providers/ollama.svg`
|
||||
- `public/providers/openai.svg`
|
||||
- `public/providers/openrouter.svg`
|
||||
- `public/providers/ovhcloud.svg`
|
||||
- `public/providers/picoclaw.svg`
|
||||
- `public/providers/poe.svg`
|
||||
- `public/providers/pollinations.svg`
|
||||
- `public/providers/qwen.svg`
|
||||
- `public/providers/recraft.svg`
|
||||
- `public/providers/replicate.svg`
|
||||
- `public/providers/roocode.svg`
|
||||
- `public/providers/runway.svg`
|
||||
- `public/providers/sambanova.svg`
|
||||
- `public/providers/searchapi.svg`
|
||||
- `public/providers/suno.svg`
|
||||
- `public/providers/tavily.svg`
|
||||
- `public/providers/topazlabs.svg`
|
||||
- `public/providers/trae.svg`
|
||||
- `public/providers/udio.svg`
|
||||
- `public/providers/upstage.svg`
|
||||
- `public/providers/v0.svg`
|
||||
- `public/providers/vercel.svg`
|
||||
- `public/providers/vllm.svg`
|
||||
- `public/providers/volcengine.svg`
|
||||
- `public/providers/voyage.svg`
|
||||
- `public/providers/windsurf.svg`
|
||||
- `public/providers/xai.svg`
|
||||
- `public/providers/zhipu.svg`
|
||||
|
||||
### Upstream registry claims
|
||||
|
||||
These are claims recorded by the fixed upstream registry. They have not been independently
|
||||
verified against an authoritative license or brand-owner notice for every asset, so they are not
|
||||
independent copyright or trademark clearance.
|
||||
|
||||
| Upstream registry claim | Count | Clearance status |
|
||||
| ----------------------- | ----: | --------------------------------------------------------------------- |
|
||||
| MIT | 46 | Upstream claim only; original per-asset copyright notices remain HOLD |
|
||||
| CC0-1.0 | 14 | Upstream claim only; not independently verified with each owner |
|
||||
| Apache-2.0 | 1 | Upstream claim only; upstream NOTICE remains HOLD |
|
||||
| brand-use | 2 | Brand terms, not open-source licenses; owner guidelines remain HOLD |
|
||||
| Custom | 1 | Custom MiniMax claim; terms remain HOLD |
|
||||
| MISSING | 1 | No matching registry claim for HuggingFace; license remains HOLD |
|
||||
|
||||
#### MIT (46)
|
||||
|
||||
`alibaba`, `arcee`, `assemblyai`, `aws`, `bailian`, `baseten`, `cerebras`, `comfyui`,
|
||||
`deepinfra`, `exa`, `fal`, `fireworks`, `friendli`, `gemini`, `grok`, `groq`, `heroku`,
|
||||
`hyperbolic`, `ibm`, `inference`, `lambda`, `longcat`, `mistral`, `moonshot`, `morph`, `nebius`,
|
||||
`novita`, `openai`, `picoclaw`, `pollinations`, `qwen`, `recraft`, `roocode`, `runway`,
|
||||
`sambanova`, `searchapi`, `tavily`, `topazlabs`, `trae`, `udio`, `upstage`, `vllm`, `volcengine`,
|
||||
`voyage`, `xai`, `zhipu`
|
||||
<!-- end:MIT -->
|
||||
|
||||
#### CC0-1.0 (14)
|
||||
|
||||
`anthropic`, `cline`, `cursor`, `deepgram`, `elevenlabs`, `nvidia`, `ollama`, `openrouter`, `poe`,
|
||||
`replicate`, `suno`, `v0`, `vercel`, `windsurf`
|
||||
<!-- end:CC0-1.0 -->
|
||||
|
||||
#### Apache-2.0 (1)
|
||||
|
||||
`continue`
|
||||
<!-- end:Apache-2.0 -->
|
||||
|
||||
#### brand-use (2)
|
||||
|
||||
`azure`, `ovhcloud`
|
||||
<!-- end:brand-use -->
|
||||
|
||||
#### Custom (1)
|
||||
|
||||
`minimax`
|
||||
<!-- end:Custom -->
|
||||
|
||||
#### MISSING (1)
|
||||
|
||||
`huggingface`
|
||||
<!-- end:MISSING -->
|
||||
|
||||
The `continue` Apache-2.0 claim remains HOLD until its authoritative upstream NOTICE obligations
|
||||
are verified. The `azure` and `ovhcloud` brand-use claims are not open-source licenses and remain
|
||||
subject to owner guidelines. `minimax` remains HOLD under custom terms. `huggingface` remains HOLD
|
||||
because its matching file has no entry or license claim in the fixed registry.
|
||||
|
||||
### Trademark and affiliation disclaimer
|
||||
|
||||
All brand names, logos, and trademarks are the property of their respective owners. OmniRoute uses
|
||||
these assets nominatively to identify provider integrations. There is no affiliation, sponsorship,
|
||||
or endorsement by the respective owners. Copyright provenance and source license claims do not
|
||||
provide trademark clearance; users should follow each owner's official brand guidelines.
|
||||
|
||||
@@ -70,7 +70,7 @@ export function registerNodes(program) {
|
||||
nodes
|
||||
.command("add")
|
||||
.requiredOption("--provider <p>", t("nodes.add.provider"))
|
||||
.requiredOption("--endpoint <url>", t("nodes.add.baseUrl"))
|
||||
.requiredOption("--base-url <url>", t("nodes.add.baseUrl"))
|
||||
.option("--name <n>", t("nodes.add.name"))
|
||||
.option("--weight <w>", t("nodes.add.weight"), parseInt, 100)
|
||||
.option("--region <r>", t("nodes.add.region"))
|
||||
@@ -83,18 +83,14 @@ export function registerNodes(program) {
|
||||
.action(async (opts, cmd) => {
|
||||
const body = {
|
||||
provider: opts.provider,
|
||||
baseUrl: opts.endpoint,
|
||||
baseUrl: opts.baseUrl,
|
||||
name: opts.name,
|
||||
weight: opts.weight,
|
||||
region: opts.region,
|
||||
enabled: true,
|
||||
headers: opts.authHeader?.length ? opts.authHeader : undefined,
|
||||
};
|
||||
const res = await apiFetch("/api/provider-nodes", {
|
||||
...cmd.optsWithGlobals(),
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
const res = await apiFetch("/api/provider-nodes", { method: "POST", body });
|
||||
if (!res.ok) {
|
||||
process.stderr.write(`Error: ${res.status}\n`);
|
||||
process.exit(1);
|
||||
@@ -104,22 +100,17 @@ export function registerNodes(program) {
|
||||
|
||||
nodes
|
||||
.command("update <nodeId>")
|
||||
.option("--endpoint <url>", t("nodes.update.baseUrl"))
|
||||
.option("--base-url <url>", t("nodes.update.baseUrl"))
|
||||
.option("--name <n>", t("nodes.update.name"))
|
||||
.option("--weight <w>", t("nodes.update.weight"), parseInt)
|
||||
.option("--region <r>", t("nodes.update.region"))
|
||||
.option("--enabled <b>", t("nodes.update.enabled"), (v) => v === "true")
|
||||
.action(async (id, opts, cmd) => {
|
||||
const body = {};
|
||||
if (opts.endpoint !== undefined) body.baseUrl = opts.endpoint;
|
||||
for (const k of ["name", "weight", "region", "enabled"]) {
|
||||
for (const k of ["baseUrl", "name", "weight", "region", "enabled"]) {
|
||||
if (opts[k] !== undefined) body[k] = opts[k];
|
||||
}
|
||||
const res = await apiFetch(`/api/provider-nodes/${id}`, {
|
||||
...cmd.optsWithGlobals(),
|
||||
method: "PUT",
|
||||
body,
|
||||
});
|
||||
const res = await apiFetch(`/api/provider-nodes/${id}`, { method: "PUT", body });
|
||||
if (!res.ok) {
|
||||
process.stderr.write(`Error: ${res.status}\n`);
|
||||
process.exit(1);
|
||||
@@ -145,13 +136,12 @@ export function registerNodes(program) {
|
||||
|
||||
nodes
|
||||
.command("validate")
|
||||
.requiredOption("--endpoint <url>", t("nodes.validate.baseUrl"))
|
||||
.requiredOption("--base-url <url>", t("nodes.validate.baseUrl"))
|
||||
.requiredOption("--provider <p>", t("nodes.validate.provider"))
|
||||
.action(async (opts, cmd) => {
|
||||
const res = await apiFetch("/api/provider-nodes/validate", {
|
||||
...cmd.optsWithGlobals(),
|
||||
method: "POST",
|
||||
body: { baseUrl: opts.endpoint, provider: opts.provider },
|
||||
body: { baseUrl: opts.baseUrl, provider: opts.provider },
|
||||
});
|
||||
if (!res.ok) {
|
||||
process.stderr.write(`Error: ${res.status}\n`);
|
||||
|
||||
@@ -24,9 +24,9 @@ function wantsProviderSetup(opts) {
|
||||
return opts.addProvider || Boolean(opts.provider) || Boolean(opts.apiKey);
|
||||
}
|
||||
|
||||
async function resolvePassword(opts, prompt, nonInteractive, settings) {
|
||||
if (opts.password !== undefined) return opts.password;
|
||||
if (!settings.password && process.env.INITIAL_PASSWORD) return process.env.INITIAL_PASSWORD;
|
||||
async function resolvePassword(opts, prompt, nonInteractive) {
|
||||
if (opts.password) return opts.password;
|
||||
if (process.env.INITIAL_PASSWORD) return process.env.INITIAL_PASSWORD;
|
||||
if (nonInteractive) return "";
|
||||
|
||||
const answer = await prompt.ask("Set an admin password now? [y/N]", "N");
|
||||
@@ -41,9 +41,9 @@ async function resolvePassword(opts, prompt, nonInteractive, settings) {
|
||||
}
|
||||
|
||||
async function setupPassword(db, opts, prompt, nonInteractive) {
|
||||
const settings = getSettings(db);
|
||||
const password = await resolvePassword(opts, prompt, nonInteractive, settings);
|
||||
const password = await resolvePassword(opts, prompt, nonInteractive);
|
||||
if (!password) {
|
||||
const settings = getSettings(db);
|
||||
if (!settings.password) {
|
||||
updateSettings(db, { requireLogin: false });
|
||||
}
|
||||
|
||||
@@ -26,17 +26,6 @@ let resolvedCached = null;
|
||||
export async function loadSqliteRuntime() {
|
||||
if (resolvedCached) return resolvedCached;
|
||||
|
||||
if (process.versions.bun) {
|
||||
try {
|
||||
const bunSqlite = await import("bun:sqlite");
|
||||
resolvedCached = {
|
||||
driver: { kind: "bun-sqlite", Database: bunSqlite.Database },
|
||||
source: "bun-sqlite",
|
||||
};
|
||||
return resolvedCached;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const bundled = await tryLoadBundled();
|
||||
if (bundled) {
|
||||
resolvedCached = { driver: bundled, source: "bundled" };
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { sep } from "node:path";
|
||||
|
||||
/**
|
||||
* A `.env` inside the installed package directory does not survive an update:
|
||||
* `npm i -g` replaces that directory wholesale, and postinstall recreates the
|
||||
* file from `.env.example`. The CLI announces every env file it loads without
|
||||
* distinguishing the ones that last from the one that doesn't.
|
||||
*
|
||||
* Returns the warning to print, or null when there is nothing worth saying.
|
||||
*
|
||||
* Two conditions, both required, so a development checkout never sees this:
|
||||
* - the file sits inside the package root, and that root is inside a
|
||||
* `node_modules` directory — i.e. an installed package, not a checkout,
|
||||
* where the same path is stable and documented in SETUP_GUIDE.md;
|
||||
* - the file actually supplied at least one value. First writer wins, so a
|
||||
* file entirely shadowed by a durable one supplied nothing, and losing it
|
||||
* costs nothing.
|
||||
*
|
||||
* @param {{ envPath: string, packageRoot: string, durableEnvPath: string, suppliedKeys: boolean }} args
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export function describeVolatileEnvWarning({ envPath, packageRoot, durableEnvPath, suppliedKeys }) {
|
||||
if (!suppliedKeys) return null;
|
||||
if (envPath === durableEnvPath) return null;
|
||||
if (!isInsideInstalledPackage(packageRoot)) return null;
|
||||
if (!envPath.startsWith(packageRoot + sep)) return null;
|
||||
|
||||
return (
|
||||
`${envPath} lives inside the installed package: updating OmniRoute replaces it. ` +
|
||||
`Move the values you set to ${durableEnvPath}, which updates leave alone.`
|
||||
);
|
||||
}
|
||||
|
||||
/** True when the path sits under a `node_modules` directory. */
|
||||
function isInsideInstalledPackage(dir) {
|
||||
return typeof dir === "string" && dir.split(sep).includes("node_modules");
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import { getDefaultDataDir } from "./cli/data-dir.mjs";
|
||||
import { shouldProvisionStorageKey } from "./cli/utils/storageKeyProvision.mjs";
|
||||
import { isVersionFastPath } from "./cli/utils/versionFastPath.mjs";
|
||||
import { parseEnvValue } from "./cli/utils/parseEnvValue.mjs";
|
||||
import { describeVolatileEnvWarning } from "./cli/utils/volatileEnvPath.mjs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -92,7 +91,9 @@ function migrateElectronServerEnv(dataDir) {
|
||||
const serverEnvPath = join(dataDir, "server.env");
|
||||
if (existsSync(envPath) || !existsSync(serverEnvPath)) return;
|
||||
writeFileSync(envPath, readFileSync(serverEnvPath, "utf-8"), "utf-8");
|
||||
console.log(` \x1b[2m♻ Migrated Electron secrets from ${serverEnvPath} to ${envPath}\x1b[0m`);
|
||||
console.log(
|
||||
` \x1b[2m♻ Migrated Electron secrets from ${serverEnvPath} to ${envPath}\x1b[0m`
|
||||
);
|
||||
} catch {
|
||||
// Ignore errors migrating server.env — fall back to normal env loading below.
|
||||
}
|
||||
@@ -163,21 +164,6 @@ function loadEnvFile() {
|
||||
const setter = winner ? winner : "the environment";
|
||||
console.warn(` \x1b[33m⚠ ${key} in ${loser} is ignored, ${setter} set it first\x1b[0m`);
|
||||
}
|
||||
|
||||
// The package directory is replaced by the next `npm i -g`, so a .env kept
|
||||
// there is silently lost. Say so once, and only when that file actually
|
||||
// supplied something.
|
||||
const durableEnvPath = join(process.env.DATA_DIR || getDefaultDataDir(), ".env");
|
||||
const suppliedKeys = [...keyOrigin.values()].some((origin) => origin === join(ROOT, ".env"));
|
||||
const volatileWarning = describeVolatileEnvWarning({
|
||||
envPath: join(ROOT, ".env"),
|
||||
packageRoot: ROOT,
|
||||
durableEnvPath,
|
||||
suppliedKeys,
|
||||
});
|
||||
if (volatileWarning && loadedEnvPaths.includes(join(ROOT, ".env"))) {
|
||||
console.warn(` \x1b[33m⚠ ${volatileWarning}\x1b[0m`);
|
||||
}
|
||||
}
|
||||
|
||||
loadEnvFile();
|
||||
@@ -261,16 +247,16 @@ if (shouldProvisionStorageKey(process.argv)) {
|
||||
const langEnv = process.env.OMNIROUTE_LANG;
|
||||
const chosen = langArg || langEnv;
|
||||
if (chosen) {
|
||||
const { setLocale } = await import(pathToFileURL(join(ROOT, "bin", "cli", "i18n.mjs")).href);
|
||||
const { setLocale } = await import(
|
||||
pathToFileURL(join(ROOT, "bin", "cli", "i18n.mjs")).href
|
||||
);
|
||||
setLocale(chosen);
|
||||
}
|
||||
}
|
||||
|
||||
// Register update notifier — checks npm once per 24h, notifies on exit via stderr.
|
||||
const _pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8"));
|
||||
const _notifier = updateNotifier
|
||||
? updateNotifier({ pkg: _pkg, updateCheckInterval: 1000 * 60 * 60 * 24 })
|
||||
: null;
|
||||
const _notifier = updateNotifier ? updateNotifier({ pkg: _pkg, updateCheckInterval: 1000 * 60 * 60 * 24 }) : null;
|
||||
process.on("exit", () => {
|
||||
if (!_notifier || !_notifier.update) return;
|
||||
if (process.env.OMNIROUTE_NO_UPDATE_NOTIFIER) return;
|
||||
@@ -279,15 +265,7 @@ process.on("exit", () => {
|
||||
const outputIdx = process.argv.indexOf("--output");
|
||||
const outputVal = outputIdx >= 0 ? process.argv[outputIdx + 1] : null;
|
||||
if (outputVal === "json" || outputVal === "jsonl" || outputVal === "csv") return;
|
||||
if (
|
||||
process.argv.some(
|
||||
(a) =>
|
||||
a.startsWith("--output=json") ||
|
||||
a.startsWith("--output=jsonl") ||
|
||||
a.startsWith("--output=csv")
|
||||
)
|
||||
)
|
||||
return;
|
||||
if (process.argv.some((a) => a.startsWith("--output=json") || a.startsWith("--output=jsonl") || a.startsWith("--output=csv"))) return;
|
||||
if (_notifier.update) {
|
||||
_notifier.notify({
|
||||
defer: false,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(audio):** proxy native ElevenLabs voices, text-to-speech, and speech-to-text HTTP routes through stored OmniRoute credentials, preserving query strings, multipart uploads, binary responses, and upstream errors (#10556).
|
||||
@@ -1 +0,0 @@
|
||||
- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`.
|
||||
@@ -1,3 +0,0 @@
|
||||
- Run synchronous RTK and Caveman request compression in a bounded worker-thread pool, keeping
|
||||
large `/v1/responses` compression heaps outside the HTTP isolate while preserving strict
|
||||
fail-open behavior and per-engine telemetry.
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146))
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(video bridge):** harden the optional drill-down cache substrate with exact-path broker policy, canonical principal/session/media isolation, independent retained-byte quotas, cancellation-safe commits, rejection of excess or non-canonical Base64 padding and non-JPEG/truncated media, warning-sensitive full JPEG canonicalization that strips trailing polyglot bytes, server-derived dimensions, and auditable derivation metadata; production tenant binding and multi-resolution selection remain follow-up work ([#11369](https://github.com/diegosouzapw/OmniRoute/pull/11369))
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(video):** add an opt-in focused analysis mode that safely uses a normalized, 500-code-point latest-user hint for task-aware frame captions while preserving full-mode prompts, temporal-window isolation, and cache identity without storing raw task text ([#11383](https://github.com/diegosouzapw/OmniRoute/pull/11383)).
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse
|
||||
@@ -1 +0,0 @@
|
||||
- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546))
|
||||
@@ -1 +0,0 @@
|
||||
- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW)
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843))
|
||||
@@ -1 +0,0 @@
|
||||
- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342)
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces.
|
||||
@@ -1 +0,0 @@
|
||||
- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed)
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(github):** proactive credential health now verifies GitHub access tokens through the existing Copilot token exchange, marks only a confirmed `401 Unauthorized` as expired, and leaves rate limits, permission failures, upstream failures, and network errors routable ([#10352](https://github.com/diegosouzapw/OmniRoute/issues/10352)) — thanks @RaviTharuma
|
||||
@@ -1 +0,0 @@
|
||||
- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol.
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(providers):** Antigravity OAuth marks connects with no Cloud Code projectId as degraded instead of a false "Connected"; BYOP detection at connect time, auto-disable of confirmed-missing accounts, and selection-side rotation ([#11284](https://github.com/diegosouzapw/OmniRoute/issues/11284))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification.
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(db):** group model patterns escape regex metacharacters, so `gpt-4.1*` no longer matches `gpt-4o1-preview` and a pattern like `gpt-4(*` no longer throws `SyntaxError` out of the completion and `/v1/models` paths ([#11311](https://github.com/diegosouzapw/OmniRoute/pull/11311))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(db):** the upstream proxy URL check judges the host by address instead of by spelling, so `http://[::ffff:169.254.169.254]`, `[::ffff:10.0.0.5]`, ULA/link-local and CGNAT targets are refused like their dotted equivalents ([#11319](https://github.com/diegosouzapw/OmniRoute/pull/11319))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324)
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(i18n):** three `pt` strings had dropped their placeholders — the cache tile's subtitle repeated its own label instead of showing `{total}` — and a unit test now enforces placeholder parity with `en` across all locales ([#11325](https://github.com/diegosouzapw/OmniRoute/pull/11325))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(kie):** map the remaining `google-imagen/*` KIE Market catalog ids (`nano-banana`, `nano-banana-pro`, `nano-banana-edit`) to their real, KIE-documented upstream `model` values — `#11225`'s fix only covered `nano-banana-2` ([#11326](https://github.com/diegosouzapw/OmniRoute/pull/11326)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(security):** `proxy-authorization` and `proxy-authenticate` are refused as upstream/custom headers, so a proxy credential is no longer forwarded to the model provider — the canonical denylist now matches the RFC 7230 §6.1 set the rest of the codebase already strips ([#11328](https://github.com/diegosouzapw/OmniRoute/pull/11328))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(video-bridge):** fall back to the deterministic active-window midpoint when a one-frame scene-aware budget cannot preserve both timeline ends; a real FFmpeg fixture matrix now covers rapid cuts, gradual changes, static and short clips, and detector failure ([#11344](https://github.com/diegosouzapw/OmniRoute/pull/11344)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(translator):** Codex Responses tool calls translated for Claude clients no longer emit a duplicate `tool_use` block with the same ID and an empty name, preventing Claude Code from terminating with `No such tool available` ([#11347](https://github.com/diegosouzapw/OmniRoute/pull/11347))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(video-bridge):** burn high-contrast timestamps into every bounded contact-sheet cell and add a real-model A/B harness whose promotion verdict stays `HOLD` until token, latency, and quality evidence is actually executed ([#11350](https://github.com/diegosouzapw/OmniRoute/pull/11350))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(video):** fingerprint protected Video Bridge bytes, coalesce concurrent work, and fail open when the bounded TTL/LRU result cache is unavailable or corrupt ([#11362](https://github.com/diegosouzapw/OmniRoute/pull/11362))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(catalog):** keep large `/v1/models` builds responsive by reusing the build-local capability snapshot throughout enrichment and Auto-Combo preparation, yielding cooperatively while constructing virtual candidate pools, and avoiding unrelated synchronous database diagnostics on the cache-TTL read path ([#11367](https://github.com/diegosouzapw/OmniRoute/pull/11367))
|
||||
@@ -1 +0,0 @@
|
||||
- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(video):** apply the caption-frame cap after bounded visual deduplication, preserve first/final candidates plus small high-contrast motion and text changes, and version the dedup policy in result-cache identity ([#11382](https://github.com/diegosouzapw/OmniRoute/pull/11382)).
|
||||
@@ -1 +0,0 @@
|
||||
- **Live dashboard:** honour the WebSocket port reported by `/api/v1/ws?handshake=1` instead of the port compiled into the bundle, so a `LIVE_WS_PORT` override reaches prebuilt Docker/npm images and Combo Studio Live connects behind a reverse proxy ([#11331](https://github.com/diegosouzapw/OmniRoute/issues/11331)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(dashboard):** Model Database sync interval slider ticks now match the thumb position — checkpoint-space slider with magnetic snap on release ([#11394](https://github.com/diegosouzapw/OmniRoute/pull/11394)) — thanks @An0nym0us92
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142.
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path.
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`.
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441))
|
||||
@@ -1 +0,0 @@
|
||||
- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449)
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)).
|
||||
@@ -1 +0,0 @@
|
||||
- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel.
|
||||
@@ -1 +0,0 @@
|
||||
- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available.
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526)
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535)
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411
|
||||
@@ -1 +0,0 @@
|
||||
- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance.
|
||||
@@ -1 +0,0 @@
|
||||
- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)).
|
||||
@@ -1 +0,0 @@
|
||||
- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `<connId>/<connId>/<model>` no longer reach upstream verbatim (#11557)
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588))
|
||||
@@ -1 +0,0 @@
|
||||
- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589))
|
||||
@@ -1 +0,0 @@
|
||||
- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597))
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones
|
||||
@@ -1 +0,0 @@
|
||||
- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist.
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones
|
||||
@@ -1 +0,0 @@
|
||||
- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)).
|
||||
@@ -1 +0,0 @@
|
||||
- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user