Merge remote-tracking branch 'origin/release/v3.8.51' into fix/release-v3.8.51-basereds-agent-skills
@@ -3043,6 +3043,13 @@ QUOTA_STORE_DRIVER=sqlite
|
||||
# CHATGPT_WEB_CODEX_CHROME_PATH=/usr/bin/chromium
|
||||
# CHROME_PATH=/usr/bin/chromium
|
||||
# CHATGPT_WEB_CODEX_CDP_URL=http://chatgpt-web-codex-browser:9223
|
||||
# CDP_PROXY_TOKEN required by docker/chatgpt-web-codex-browser/cdp-proxy.mjs (#13679):
|
||||
# when set, every request to the CDP proxy sidecar must present it as an
|
||||
# `X-Omni-Cdp-Token` header. Left unset, the proxy keeps forwarding requests
|
||||
# unauthenticated (network isolation via docker-compose.yml's dedicated
|
||||
# `chatgpt-web-codex-net` is the default mitigation). Generate with:
|
||||
# `openssl rand -hex 32`
|
||||
# CDP_PROXY_TOKEN=
|
||||
# CHATGPT_WEB_CODEX_TUNNEL_ID=tunnel_0123456789abcdef0123456789abcdef
|
||||
# CHATGPT_WEB_CODEX_RUNTIME_KEY=
|
||||
# CHATGPT_WEB_CODEX_CONNECTOR_NAME=OmniRoute Codex v2
|
||||
|
||||
9
.github/workflows/ci.yml
vendored
@@ -533,6 +533,15 @@ jobs:
|
||||
env:
|
||||
BASE_REF: ${{ github.base_ref && format('origin/{0}', github.base_ref) || '' }}
|
||||
run: node scripts/i18n/check-new-key-coverage.mjs
|
||||
# Absolute complement of the two gates above: every locale must carry exactly the key
|
||||
# set of en.json, whatever the age of the key. A locale batch is generated from the
|
||||
# en.json of the day the branch is cut and translates for days while the base keeps
|
||||
# adding keys — the batch PR adds no key itself, so the new-key gate stays silent and
|
||||
# 43 absent keys out of ~13,000 still read 99.7 % coverage. Incident 2026-09-15:
|
||||
# batch 1 (#13044) landed 43 keys short in nine locales, batch 2 (#13660) 10 keys short
|
||||
# in eight. Fix is `sync-ui-keys --locale=<codes> --translate-markers`.
|
||||
- name: i18n key completeness (every locale carries every en.json key)
|
||||
run: node scripts/i18n/check-key-completeness.mjs
|
||||
|
||||
# #8038: cheap glossary/protected-terms consistency gate —
|
||||
# complements i18n-ui-coverage (key parity) and the ICU `i18n` job below
|
||||
|
||||
42
.github/workflows/release-acceptance.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Release acceptance
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["release/v*"]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: release-acceptance-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
acceptance:
|
||||
name: Release acceptance
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Emit shadow acceptance report
|
||||
run: |
|
||||
node scripts/quality/validate-release-acceptance.mjs \
|
||||
--plan tests/fixtures/release-acceptance/plan-lint.json \
|
||||
--manifests tests/fixtures/release-acceptance/shadow-manifests \
|
||||
--out release-acceptance-report.json
|
||||
continue-on-error: true
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: release-acceptance-report
|
||||
path: release-acceptance-report.json
|
||||
if-no-files-found: ignore
|
||||
retention-days: 30
|
||||
@@ -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/telemetry.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 tests/models-fetcher.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/telemetry.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 tests/models-fetcher.test.ts tests/issue-13000-cold-start-combo-limit.test.ts",
|
||||
"prepublishOnly": "npm run clean && npm run build && npm test"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
@@ -4631,9 +4631,21 @@ export function buildStaticProviderEntry(
|
||||
.map((m) => m.max_output_tokens)
|
||||
.filter((v): v is number => typeof v === "number" && v > 0);
|
||||
|
||||
if (contextValues.length > 0 && outputValues.length > 0) {
|
||||
// Prefer the server-computed aggregate (accounts for explicit
|
||||
// context_length overrides and members outside memberEntries, e.g.
|
||||
// not yet resolved in /v1/models) over the raw Math.min(member)
|
||||
// lower bound. Mirrors mapComboToModelV2's limit.context logic
|
||||
// (#13000) so the static catalog and the dynamic hook agree.
|
||||
const preferredContext =
|
||||
typeof combo.computed_context_length === "number" && combo.computed_context_length > 0
|
||||
? combo.computed_context_length
|
||||
: contextValues.length > 0
|
||||
? Math.min(...contextValues)
|
||||
: undefined;
|
||||
|
||||
if (preferredContext !== undefined && outputValues.length > 0) {
|
||||
entry.limit = {
|
||||
context: Math.min(...contextValues),
|
||||
context: preferredContext,
|
||||
output: Math.min(...outputValues),
|
||||
};
|
||||
}
|
||||
@@ -5511,6 +5523,32 @@ export function createOmniRouteConfigHook(
|
||||
|
||||
const modelsFetchOk = !modelsFetchThrew && localRawModels.length > 0;
|
||||
|
||||
// Snapshot backfill for computed_context_length: a live /api/combos
|
||||
// response can come back without this field (server hasn't finished
|
||||
// recomputing it yet, e.g. just after a restart) even though the
|
||||
// combo's members and identity are otherwise unchanged. When that
|
||||
// happens, prefer the last-known-good value from the warm disk
|
||||
// snapshot over the Math.min(member) fallback in
|
||||
// mapComboToModelV2() — never overwrite any other combo field
|
||||
// (models/name/etc.) with stale data, only this one derived number.
|
||||
if (warmSnapshot) {
|
||||
const snapshotComboById = new Map(warmSnapshot.rawCombos.map((c) => [c.id, c]));
|
||||
for (const combo of localRawCombos) {
|
||||
const hasLive =
|
||||
typeof combo.computed_context_length === "number" &&
|
||||
combo.computed_context_length > 0;
|
||||
if (hasLive) continue;
|
||||
const stale = snapshotComboById.get(combo.id);
|
||||
if (
|
||||
stale &&
|
||||
typeof stale.computed_context_length === "number" &&
|
||||
stale.computed_context_length > 0
|
||||
) {
|
||||
combo.computed_context_length = stale.computed_context_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disk-cache fallback (cold first run, no warm snapshot): when the
|
||||
// live fetch returned no models AND features.diskCache !== false,
|
||||
// hydrate from the last-known-good snapshot so OC still surfaces a
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* Repro for #13000: combo context limits fall back to Math.min(member)
|
||||
* instead of using computed_context_length after cold start — no disk
|
||||
* snapshot fallback.
|
||||
*
|
||||
* Scenario (mirrors the report): a warm disk snapshot holds the combo with
|
||||
* its correct server-computed `computed_context_length` (245000, from all 6
|
||||
* members). After a restart, the live refresh's combos fetch returns the
|
||||
* SAME combo but without `computed_context_length` (e.g. the value hasn't
|
||||
* propagated yet), and the live models fetch only resolves 2 of the 6
|
||||
* members (the rest not yet in /v1/models). The background refresh then
|
||||
* republishes the provider block built from this degraded live data,
|
||||
* downgrading a previously-known-good 245000 limit to Math.min(163840,
|
||||
* 1_000_000) = 163840 — exactly the member-minimum described in the issue.
|
||||
*/
|
||||
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import type { Config } from "@opencode-ai/plugin";
|
||||
|
||||
import {
|
||||
createOmniRouteConfigHook,
|
||||
_resetInflightRefresh,
|
||||
type OmniRouteAutoCombosFetcher,
|
||||
type OmniRouteCombosFetcher,
|
||||
type OmniRouteCompressionMetaFetcher,
|
||||
type OmniRouteEnrichmentFetcher,
|
||||
type OmniRouteFetchCache,
|
||||
type OmniRouteModelsFetcher,
|
||||
type OmniRouteProvidersFetcher,
|
||||
type OmniRouteRawCombo,
|
||||
type OmniRouteRawModelEntry,
|
||||
type OmniRouteReadAuthJson,
|
||||
type OmniRouteStaticProviderEntry,
|
||||
type OmniRouteDiskSnapshotReader,
|
||||
type OmniRouteDiskSnapshotWriter,
|
||||
} from "../src/index.js";
|
||||
|
||||
test.beforeEach(() => {
|
||||
_resetInflightRefresh();
|
||||
});
|
||||
|
||||
function stubReadAuthJson(value: Record<string, unknown>): OmniRouteReadAuthJson {
|
||||
return async () => value as never;
|
||||
}
|
||||
|
||||
function authStub() {
|
||||
return stubReadAuthJson({
|
||||
"opencode-omniroute": {
|
||||
type: "api",
|
||||
key: "sk-test",
|
||||
baseURL: "https://or.example.com/v1",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function makeInput(): Config {
|
||||
return { provider: {} } as unknown as Config;
|
||||
}
|
||||
|
||||
// The two members resolvable in the degraded live /v1/models response.
|
||||
const MEMBER_DEEPSEEK: OmniRouteRawModelEntry = {
|
||||
id: "deepseek-v4-pro",
|
||||
capabilities: { tool_calling: true, reasoning: true, vision: false, thinking: false },
|
||||
context_length: 163_840,
|
||||
max_output_tokens: 64_000,
|
||||
input_modalities: ["text"],
|
||||
output_modalities: ["text"],
|
||||
};
|
||||
|
||||
const MEMBER_GLM: OmniRouteRawModelEntry = {
|
||||
id: "glm-5.2",
|
||||
capabilities: { tool_calling: true, reasoning: true, vision: false, thinking: false },
|
||||
context_length: 1_000_000,
|
||||
max_output_tokens: 16_384,
|
||||
input_modalities: ["text"],
|
||||
output_modalities: ["text"],
|
||||
};
|
||||
|
||||
// The other member that IS present once the server is fully warm.
|
||||
const MEMBER_GLM_53_HIGH: OmniRouteRawModelEntry = {
|
||||
id: "GLM-5.3-high",
|
||||
capabilities: { tool_calling: true, reasoning: true, vision: false, thinking: false },
|
||||
context_length: 245_000,
|
||||
max_output_tokens: 128_000,
|
||||
input_modalities: ["text"],
|
||||
output_modalities: ["text"],
|
||||
};
|
||||
|
||||
const COMBO_MODELS: OmniRouteRawCombo["models"] = [
|
||||
{ kind: "model", model: "deepseek-v4-pro", weight: 25 },
|
||||
{ kind: "model", model: "glm-5.2", weight: 25 },
|
||||
{ kind: "model", model: "GLM-5.3-high", weight: 50 },
|
||||
];
|
||||
|
||||
test("issue #13000: warm combo limit (245000) survives a degraded post-restart refresh instead of downgrading to Math.min(member)", async () => {
|
||||
const warmSnapshot: Omit<import("../src/index.js").OmniRouteFetchCacheEntry, "expiresAt"> = {
|
||||
rawModels: [MEMBER_DEEPSEEK, MEMBER_GLM, MEMBER_GLM_53_HIGH],
|
||||
rawCombos: [
|
||||
{
|
||||
id: "orchestrator",
|
||||
name: "orchestrator",
|
||||
models: COMBO_MODELS,
|
||||
computed_context_length: 245_000,
|
||||
},
|
||||
],
|
||||
rawAutoCombos: [],
|
||||
rawEnrichment: new Map(),
|
||||
rawCompressionCombos: [],
|
||||
rawConnections: [],
|
||||
};
|
||||
|
||||
const fetcher: OmniRouteModelsFetcher = async () => [MEMBER_DEEPSEEK, MEMBER_GLM];
|
||||
const combosFetcher: OmniRouteCombosFetcher = async () => [
|
||||
{
|
||||
id: "orchestrator",
|
||||
name: "orchestrator",
|
||||
models: COMBO_MODELS,
|
||||
// computed_context_length intentionally omitted.
|
||||
},
|
||||
];
|
||||
const autoCombosFetcher: OmniRouteAutoCombosFetcher = async () => [];
|
||||
const enrichmentFetcher: OmniRouteEnrichmentFetcher = async () => new Map();
|
||||
const compressionMetaFetcher: OmniRouteCompressionMetaFetcher = async () => [];
|
||||
const providersFetcher: OmniRouteProvidersFetcher = async () => [];
|
||||
|
||||
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => warmSnapshot;
|
||||
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
|
||||
|
||||
const sharedCache: OmniRouteFetchCache = new Map();
|
||||
|
||||
const hook = createOmniRouteConfigHook(
|
||||
{ providerId: "omniroute", modelCacheTtl: 60_000 },
|
||||
{
|
||||
readAuthJson: authStub(),
|
||||
fetcher,
|
||||
combosFetcher,
|
||||
autoCombosFetcher,
|
||||
enrichmentFetcher,
|
||||
compressionMetaFetcher,
|
||||
providersFetcher,
|
||||
diskSnapshotReader,
|
||||
diskSnapshotWriter,
|
||||
cache: sharedCache,
|
||||
}
|
||||
);
|
||||
|
||||
const input = makeInput();
|
||||
await hook(input);
|
||||
|
||||
// Let the detached background refresh (degraded live data) complete and
|
||||
// republish the block.
|
||||
await new Promise((r) => setTimeout(r, 100));
|
||||
|
||||
const entryAfter = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
|
||||
"opencode-omniroute"
|
||||
];
|
||||
const comboModelAfter = entryAfter.models["orchestrator"];
|
||||
assert.ok(comboModelAfter, "combo model still published after refresh");
|
||||
|
||||
assert.equal(
|
||||
comboModelAfter.limit.context,
|
||||
245_000,
|
||||
`expected the combo limit to stay at the known-good 245000, but got ${comboModelAfter.limit.context} ` +
|
||||
`(Math.min(member) fallback — the exact bug described in #13000)`
|
||||
);
|
||||
});
|
||||
|
||||
test("issue #13000 (control): no warm snapshot exists — Math.min(member) fallback is still used (expected, documented behavior)", async () => {
|
||||
const fetcher: OmniRouteModelsFetcher = async () => [MEMBER_DEEPSEEK, MEMBER_GLM];
|
||||
const combosFetcher: OmniRouteCombosFetcher = async () => [
|
||||
{
|
||||
id: "orchestrator",
|
||||
name: "orchestrator",
|
||||
models: COMBO_MODELS,
|
||||
// computed_context_length intentionally omitted.
|
||||
},
|
||||
];
|
||||
const autoCombosFetcher: OmniRouteAutoCombosFetcher = async () => [];
|
||||
const enrichmentFetcher: OmniRouteEnrichmentFetcher = async () => new Map();
|
||||
const compressionMetaFetcher: OmniRouteCompressionMetaFetcher = async () => [];
|
||||
const providersFetcher: OmniRouteProvidersFetcher = async () => [];
|
||||
|
||||
// No prior snapshot on disk.
|
||||
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
|
||||
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
|
||||
|
||||
const sharedCache: OmniRouteFetchCache = new Map();
|
||||
|
||||
const hook = createOmniRouteConfigHook(
|
||||
{ providerId: "omniroute", modelCacheTtl: 60_000 },
|
||||
{
|
||||
readAuthJson: authStub(),
|
||||
fetcher,
|
||||
combosFetcher,
|
||||
autoCombosFetcher,
|
||||
enrichmentFetcher,
|
||||
compressionMetaFetcher,
|
||||
providersFetcher,
|
||||
diskSnapshotReader,
|
||||
diskSnapshotWriter,
|
||||
cache: sharedCache,
|
||||
}
|
||||
);
|
||||
|
||||
const input = makeInput();
|
||||
await hook(input);
|
||||
|
||||
const entryAfter = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
|
||||
"opencode-omniroute"
|
||||
];
|
||||
const comboModelAfter = entryAfter.models["orchestrator"];
|
||||
assert.ok(comboModelAfter, "combo model published on cold first run");
|
||||
|
||||
// No snapshot to backfill from — Math.min(163840, 1_000_000) = 163840.
|
||||
assert.equal(
|
||||
comboModelAfter.limit.context,
|
||||
163_840,
|
||||
"pure cold start with no snapshot must keep using the Math.min(member) fallback"
|
||||
);
|
||||
});
|
||||
@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
|
||||
|
||||
## Project at a Glance
|
||||
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 358 LLM providers, auto-fallback.
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 359 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 (176 migrations) |
|
||||
| Database | `src/lib/db/` | SQLite domain modules (177 migrations) |
|
||||
| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback logic |
|
||||
| MCP Server | `open-sse/mcp-server/` | 110 tools (45 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 |
|
||||
|
||||
16
README.md
@@ -7,7 +7,7 @@
|
||||
|
||||
# 🚀 OmniRoute — The Free AI Gateway
|
||||
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 358 providers — 150+ 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. 358 AI providers · 150+ free tiers · ~1.47B 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 → 359 providers — 150+ 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. 359 AI providers · 150+ free tiers · ~1.47B 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 **443 free-tier entries across 34 recurring pool keys** and computes the token headline from the **16 pools with a published positive monthly budget plus five per-model Groq caps**, deduplicated by shared pool. Quotas that only open after a regional identity check (today: ModelScope) are shown apart, +~6M behind regional identity verification, and never summed into the headline. 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 **446 free-tier entries across 34 recurring pool keys** and computes the token headline from the **16 pools with a published positive monthly budget plus five per-model Groq caps**, deduplicated by shared pool. Quotas that only open after a regional identity check (today: ModelScope) are shown apart, +~6M behind regional identity verification, and never summed into the headline. 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.47B free tokens per month steady, up to ~2.07B in the first month with signup credits, from 34 documented recurring pool keys covering 443 cataloged free-tier entries behind one endpoint. Honest pool-deduped math — each shared pool counted once, including 16 recurring pools with a published positive monthly token budget plus five per-model Groq caps; 13 providers are marked avoid in the terms-risk catalog so you decide. Budget bar includes Mistral 1B, Nara 210M, LLM7 150M, Groq 30M (five per-model caps) 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.47B free tokens per month steady, up to ~2.07B in the first month with signup credits, from 34 documented recurring pool keys covering 446 cataloged free-tier entries behind one endpoint. Honest pool-deduped math — each shared pool counted once, including 16 recurring pools with a published positive monthly token budget plus five per-model Groq caps; 13 providers are marked avoid in the terms-risk catalog so you decide. Budget bar includes Mistral 1B, Nara 210M, LLM7 150M, Groq 30M (five per-model caps) 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)**.
|
||||
>
|
||||
@@ -233,7 +233,7 @@ curl http://localhost:20128/v1/chat/completions \
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 358 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 358 providers · up to 95% token savings on eligible workloads · $0 to start with 150+ free tiers and 52 recurring/keyless free-forever providers · 36 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 359 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 359 providers · up to 95% token savings on eligible workloads · $0 to start with 150+ free tiers and 53 recurring/keyless free-forever providers · 36 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/>
|
||||
@@ -486,7 +486,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: 358 providers, 150+ 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 42 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: 359 providers, 150+ 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 42 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>
|
||||
|
||||
@@ -672,7 +672,7 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
|
||||
</div>
|
||||
|
||||
> **352 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **152 carrying `hasFree: true` discovery metadata**. The chat model registry covers **229 providers / 2,554 distinct provider-model pairs / 1,283 raw model IDs**; the separate free-budget catalog has **443 per-model rows**, **34 recurring pools** and **52 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 **152 carrying `hasFree: true` discovery metadata**. The chat model registry covers **229 providers / 2,554 distinct provider-model pairs / 1,283 raw model IDs**; the separate free-budget catalog has **443 per-model rows**, **34 recurring pools** and **53 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">
|
||||
|
||||
@@ -1268,7 +1268,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) — 122 domain modules, 176 migrations</td></tr>
|
||||
<tr><td nowrap><b>Database</b></td><td>better-sqlite3 (SQLite, WAL journaling) + LowDB (JSON legacy) — 122 domain modules, 177 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>
|
||||
@@ -1331,7 +1331,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>16-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: 34 documented recurring pools / 443 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: 34 documented recurring pools / 446 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>
|
||||
|
||||
@@ -9,6 +9,8 @@ function truncate(v, len = 60) {
|
||||
return s.length > len ? s.slice(0, len - 1) + "…" : s;
|
||||
}
|
||||
|
||||
const VALID_MCP_TRANSPORTS = ["stdio", "sse", "streamable-http"];
|
||||
|
||||
const mcpToolSchema = [
|
||||
{ key: "name", header: "Tool", width: 36 },
|
||||
{
|
||||
@@ -43,6 +45,25 @@ export function registerMcp(program) {
|
||||
if (exitCode !== 0) process.exit(exitCode);
|
||||
});
|
||||
|
||||
mcp
|
||||
.command("enable")
|
||||
.description(t("mcp.enable.description"))
|
||||
.option("--transport <transport>", t("mcp.enable.transport"))
|
||||
.action(async (opts, cmd) => {
|
||||
const globalOpts = cmd.parent.optsWithGlobals();
|
||||
const exitCode = await runMcpEnableCommand({ ...opts, output: globalOpts.output });
|
||||
if (exitCode !== 0) process.exit(exitCode);
|
||||
});
|
||||
|
||||
mcp
|
||||
.command("disable")
|
||||
.description(t("mcp.disable.description"))
|
||||
.action(async (opts, cmd) => {
|
||||
const globalOpts = cmd.parent.optsWithGlobals();
|
||||
const exitCode = await runMcpDisableCommand({ ...opts, output: globalOpts.output });
|
||||
if (exitCode !== 0) process.exit(exitCode);
|
||||
});
|
||||
|
||||
// 5.1 — mcp call + mcp scopes
|
||||
mcp
|
||||
.command("call <tool> [argsJson]")
|
||||
@@ -61,10 +82,15 @@ export function registerMcp(program) {
|
||||
? JSON.parse(argsPositional)
|
||||
: {};
|
||||
|
||||
const exitCode = await runMcpCallCommand(tool, args, {
|
||||
...opts,
|
||||
stream: opts.stream,
|
||||
}, globalOpts);
|
||||
const exitCode = await runMcpCallCommand(
|
||||
tool,
|
||||
args,
|
||||
{
|
||||
...opts,
|
||||
stream: opts.stream,
|
||||
},
|
||||
globalOpts
|
||||
);
|
||||
|
||||
if (exitCode !== 0) process.exit(exitCode);
|
||||
});
|
||||
@@ -127,7 +153,9 @@ async function mcpJsonRpcCall(tool, args, { stream = false, globalOpts = {} } =
|
||||
|
||||
if (!initRes.ok) {
|
||||
const text = await initRes.text().catch(() => "");
|
||||
process.stderr.write(`MCP initialize failed: HTTP ${initRes.status}${text ? ` — ${text}` : ""}\n`);
|
||||
process.stderr.write(
|
||||
`MCP initialize failed: HTTP ${initRes.status}${text ? ` — ${text}` : ""}\n`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -227,6 +255,7 @@ export async function runMcpStatusCommand(opts = {}) {
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.log(t("mcp.stopped"));
|
||||
console.log(t("mcp.stoppedHint"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -240,6 +269,9 @@ export async function runMcpStatusCommand(opts = {}) {
|
||||
const transport = status.transport || "stdio";
|
||||
const online = status.online ?? status.running;
|
||||
console.log(online ? t("mcp.running", { transport }) : t("mcp.stopped"));
|
||||
if (!online && status.enabled === false) {
|
||||
console.log(t("mcp.stoppedHint"));
|
||||
}
|
||||
if (status.toolsCount !== undefined) console.log(` Tools: ${status.toolsCount}`);
|
||||
if (status.scopes?.length) {
|
||||
console.log(" Scopes:");
|
||||
@@ -270,10 +302,76 @@ export async function runMcpRestartCommand(opts = {}) {
|
||||
console.log(t("mcp.restarted"));
|
||||
return 0;
|
||||
}
|
||||
console.error(t("common.error", { message: `HTTP ${res.status}` }));
|
||||
const body = await res.json().catch(() => null);
|
||||
const message = body?.error || `HTTP ${res.status}`;
|
||||
console.error(t("common.error", { message }));
|
||||
return 1;
|
||||
} catch (err) {
|
||||
console.error(t("common.error", { message: err instanceof Error ? err.message : String(err) }));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
export async function runMcpEnableCommand(opts = {}) {
|
||||
const serverUp = await isServerUp();
|
||||
if (!serverUp) {
|
||||
console.error(t("common.serverOffline"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (opts.transport && !VALID_MCP_TRANSPORTS.includes(opts.transport)) {
|
||||
console.error(
|
||||
t("common.error", {
|
||||
message: `Invalid transport '${opts.transport}'. Valid: ${VALID_MCP_TRANSPORTS.join(", ")}`,
|
||||
})
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
const body = { mcpEnabled: true };
|
||||
if (opts.transport) body.mcpTransport = opts.transport;
|
||||
|
||||
const res = await apiFetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
body,
|
||||
retry: false,
|
||||
acceptNotOk: true,
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.error(t("common.error", { message: `HTTP ${res.status}` }));
|
||||
return 1;
|
||||
}
|
||||
console.log(t("mcp.enabled"));
|
||||
return 0;
|
||||
} catch (err) {
|
||||
console.error(t("common.error", { message: err instanceof Error ? err.message : String(err) }));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
export async function runMcpDisableCommand(opts = {}) {
|
||||
const serverUp = await isServerUp();
|
||||
if (!serverUp) {
|
||||
console.error(t("common.serverOffline"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await apiFetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
body: { mcpEnabled: false },
|
||||
retry: false,
|
||||
acceptNotOk: true,
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.error(t("common.error", { message: `HTTP ${res.status}` }));
|
||||
return 1;
|
||||
}
|
||||
console.log(t("mcp.disabled"));
|
||||
return 0;
|
||||
} catch (err) {
|
||||
console.error(t("common.error", { message: err instanceof Error ? err.message : String(err) }));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,6 +348,16 @@
|
||||
"running": "MCP server running ({transport})",
|
||||
"stopped": "MCP server stopped.",
|
||||
"restarted": "MCP server restarted.",
|
||||
"stoppedHint": "Run `omniroute mcp enable` to turn it on.",
|
||||
"enabled": "MCP server enabled.",
|
||||
"disabled": "MCP server disabled.",
|
||||
"enable": {
|
||||
"description": "Enable the MCP server",
|
||||
"transport": "Transport to use: stdio|sse|streamable-http"
|
||||
},
|
||||
"disable": {
|
||||
"description": "Disable the MCP server"
|
||||
},
|
||||
"call": {
|
||||
"description": "Invoke an MCP tool directly",
|
||||
"args": "JSON arguments object (inline)",
|
||||
|
||||
@@ -346,6 +346,16 @@
|
||||
"running": "MCP 服务器正在运行({transport})",
|
||||
"stopped": "MCP 服务器已停止。",
|
||||
"restarted": "MCP 服务器已重启。",
|
||||
"stoppedHint": "运行 `omniroute mcp enable` 以启用它。",
|
||||
"enabled": "MCP 服务器已启用。",
|
||||
"disabled": "MCP 服务器已禁用。",
|
||||
"enable": {
|
||||
"description": "启用 MCP 服务器",
|
||||
"transport": "要使用的传输方式:stdio|sse|streamable-http"
|
||||
},
|
||||
"disable": {
|
||||
"description": "禁用 MCP 服务器"
|
||||
},
|
||||
"call": {
|
||||
"description": "直接调用 MCP 工具",
|
||||
"args": "JSON 参数对象(内联)",
|
||||
|
||||
@@ -346,6 +346,16 @@
|
||||
"running": "MCP 伺服器正在執行({transport})",
|
||||
"stopped": "MCP 伺服器已停止。",
|
||||
"restarted": "MCP 伺服器已重啟。",
|
||||
"stoppedHint": "執行 `omniroute mcp enable` 以啟用它。",
|
||||
"enabled": "MCP 伺服器已啟用。",
|
||||
"disabled": "MCP 伺服器已停用。",
|
||||
"enable": {
|
||||
"description": "啟用 MCP 伺服器",
|
||||
"transport": "要使用的傳輸方式:stdio|sse|streamable-http"
|
||||
},
|
||||
"disable": {
|
||||
"description": "停用 MCP 伺服器"
|
||||
},
|
||||
"call": {
|
||||
"description": "直接呼叫 MCP 工具",
|
||||
"args": "JSON 引數物件(內聯)",
|
||||
|
||||
@@ -14,6 +14,7 @@ import { stopProcessGracefully } from "../../../src/shared/platform/windowsProce
|
||||
import {
|
||||
isFatalInstrumentationHookFailure,
|
||||
formatAndroidInstrumentationFailureHint,
|
||||
isFatalStartupDiagnostic,
|
||||
} from "../utils/ensureAndroidCacheDir.mjs";
|
||||
|
||||
const CRASH_LOG_LINES = 50;
|
||||
@@ -55,12 +56,14 @@ export class ServerSupervisor {
|
||||
this.child = null;
|
||||
this.isShuttingDown = false;
|
||||
this.instrumentationFailureHintPrinted = false;
|
||||
this.fatalStartupDiagnosticPrinted = false;
|
||||
}
|
||||
|
||||
start() {
|
||||
this.startedAt = Date.now();
|
||||
this.crashLog = [];
|
||||
this.instrumentationFailureHintPrinted = false;
|
||||
this.fatalStartupDiagnosticPrinted = false;
|
||||
|
||||
const showLog = process.env.OMNIROUTE_SHOW_LOG === "1";
|
||||
// #6321: stdout used to be discarded (`"ignore"`) whenever `--log`/OMNIROUTE_SHOW_LOG
|
||||
@@ -99,6 +102,15 @@ export class ServerSupervisor {
|
||||
)
|
||||
);
|
||||
}
|
||||
// #13314: surface any `[STARTUP] Fatal:`-guarded boot diagnostic
|
||||
// immediately, even without --log — otherwise it is only buffered and
|
||||
// reaches the operator on exit/crash, which never happens when the
|
||||
// HTTP listener still comes up after the fatal failure (every route
|
||||
// then 500s with zero visible diagnostic anywhere).
|
||||
if (!this.fatalStartupDiagnosticPrinted && isFatalStartupDiagnostic(text)) {
|
||||
this.fatalStartupDiagnosticPrinted = true;
|
||||
process.stderr.write(text.endsWith("\n") ? text : `${text}\n`);
|
||||
}
|
||||
};
|
||||
|
||||
if (this.child.stdout) {
|
||||
|
||||
BIN
bin/cli/tray/icon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 713 B After Width: | Height: | Size: 286 B |
@@ -105,6 +105,27 @@ export function isFatalInstrumentationHookFailure(text) {
|
||||
return /Unsupported platform:\s*android/i.test(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect any fatal boot-time diagnostic guarded by the `[STARTUP] Fatal:`
|
||||
* prefix (`src/instrumentation-node.ts::ensureDbReadyForBoot()`,
|
||||
* `src/instrumentation.ts::register()`, and any future guard using the same
|
||||
* marker). #13314: in the default `omniroute serve` mode (no `--log`),
|
||||
* `ServerSupervisor` only buffers stdout/stderr and flushes it to the real
|
||||
* console on exit/crash/readiness-timeout — so if the HTTP listener still
|
||||
* comes up after a fatal boot diagnostic was already printed (e.g. the
|
||||
* better-sqlite3 / node:sqlite driver cascade failing hard), the operator
|
||||
* sees "OmniRoute is running!" with zero visible diagnostic anywhere, and
|
||||
* every route 500s. This generalizes the #10028 Android/Termux carve-out to
|
||||
* every `[STARTUP] Fatal:` guard, not just that one platform-specific string.
|
||||
*
|
||||
* @param {string} text
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isFatalStartupDiagnostic(text) {
|
||||
if (!text) return false;
|
||||
return /^\[STARTUP\] Fatal:/m.test(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator-facing hint when that instrumentation failure shows up in child
|
||||
* output — defense in depth if prep was skipped or a future Next.js probe
|
||||
|
||||
1
changelog.d/features/13827-i18n-key-completeness-gate.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(i18n):** new blocking gate `i18n:check-keys` (`scripts/i18n/check-key-completeness.mjs`) — every locale catalog must carry exactly the key set of `en.json`, whatever the age of the key; the percentage and new-key gates let batch 1 (#13044) ship 43 keys short and batch 2 (#13660) 10 keys short. The i18n guide now documents the post-merge re-sync and the retranslation flow. (#13827)
|
||||
1
changelog.d/features/agnes-cn-provider.md
Normal file
@@ -0,0 +1 @@
|
||||
- feat(providers): **Added Agnes AI (China) as `agnes-cn` pointed at `https://api.agnes-ai.cn/v1`. Keys issued for `apihub.agnes-ai.com` stay on the existing `agnes` card. Live `/v1/models` on that host lists `agnes-3.0-flash` (same id as intl); the CN seed matches 2.0/2.5/3.0 and not retired 1.5.**
|
||||
1
changelog.d/features/xai-oauth-live-models.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(providers):** share the existing `api.x.ai/v1/models` discovery config with `xai-oauth` so SuperGrok OAuth connections pick up new Grok ids without a registry seed edit.
|
||||
1
changelog.d/fixes/12370-responses-function-call-name.md
Normal file
@@ -0,0 +1 @@
|
||||
- fix(api): restore the `name` field on non-streaming `/v1/responses` `function_call` output items — a plain (non-namespace) tool call's identity restore was blindly applying the `_toolNameMap` alias-table fallback as a `{namespace, name}` object, silently blanking `name` to `undefined` (dropped entirely by JSON.stringify) and leaving Codex unable to dispatch the call, so it re-narrated its intent in a loop instead (#12370)
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(db):** give `conversation_turn_nodes` its own independent retention knob (`retention.conversationTurnNodes`, default 30 days — matching `callLogs` so upgrading changes nothing until an operator overrides it) instead of sharing `callLogs`, and sweep orphaned `agentic_conversations` after the nodes expire (#12453).
|
||||
1
changelog.d/fixes/12491-codex-wreq-standalone-runtime.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(sse):** Codex WebSocket transport (including the app-server) no longer fails to load in the Next.js standalone Docker runtime — the wreq-js loader now resolves its module name dynamically instead of a literal Turbopack could rewrite to an unreachable build-time symlink (#12491) — thanks @marshalfevzi
|
||||
1
changelog.d/fixes/12692-xai-legacy-function-call.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(providers):** xAI requests no longer silently drop an assistant tool call sent in the legacy OpenAI `function_call` shape (instead of `tool_calls[]`) — the call is now translated into the xAI request the same way modern tool calls are (#12692) — thanks @soroush5
|
||||
1
changelog.d/fixes/12700-xai-usage-total.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(providers):** xAI responses no longer report `total_tokens`/`totalTokenCount` as `0` when upstream usage uses the legacy `prompt_tokens`/`completion_tokens` names instead of `input_tokens`/`output_tokens` (#12700) — thanks @soroush5
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(resilience):** a recoverable direct-fetch response-start timeout (`DIRECT_RESPONSE_START_TIMEOUT`) could, in a narrow timer/promise-settlement race, escape as an `unhandledRejection` → `uncaughtException` and kill the server process — even though `proxyFetch` already retries this exact condition on a fresh socket. Guarded the timer callback so it can no longer fire against an already-settled attempt, and extended the process-level crash guard (already used by the WS/API-bridge servers) to recognize and swallow this code if it ever escapes anyway. Also installs that same guard in the production server entrypoint (`dist/server-ws.mjs`), which never had it even though the dev server already did ([#12861](https://github.com/diegosouzapw/OmniRoute/issues/12861)) — thanks @insoln
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(providers):** correct Magnific API key validation, which reported every valid key as invalid due to a GET probe against a POST-only endpoint (#12927) — thanks @hubo1989
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(providers):** GitLab Duo Retest and chat requests now fall back to the public Code Suggestions endpoint for ANY `direct_access` 403 (not only the "direct connections are disabled" tenant-config message), and surface the real upstream error body instead of a generic "Access denied" when both endpoints reject the token (#12958) — thanks @Rahulsharma0810
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(sse):** stop misclassifying a truncated Anthropic-compatible `max_tokens` probe response (`content:[{type:"text",text:""}]`) as an empty upstream response (#12968) — thanks @pranay-gpt
|
||||
@@ -0,0 +1 @@
|
||||
- fix(providers): stop `@omniroute/opencode-plugin` combo context limits from downgrading to the raw `Math.min(member)` lower bound after a restart — the static catalog now honors the server-computed `computed_context_length` (mirroring the dynamic hook), and a background refresh with a degraded `/api/combos` response backfills the field from the last-known-good disk snapshot instead of overwriting it (#13000) — thanks @morpheus9393
|
||||
1
changelog.d/fixes/13012-cli-mcp-restart-and-enable.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(cli):** `omniroute mcp restart` no longer 404s — the missing `POST /api/mcp/restart` route now exists — and new `omniroute mcp enable`/`mcp disable [--transport]` subcommands give the CLI a way to turn the MCP server on without the dashboard ([#13012](https://github.com/diegosouzapw/OmniRoute/issues/13012)) — thanks @ricardusx
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(skills):** repair nested malformed skill-tool schemas (bare property maps, boolean `required: true`) for OpenAI-compatible providers, not just the schema root (#13022) — thanks @ftevxk
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(routing):** round-robin combos now show up in Combo Studio's Live dashboard — they were completing successfully but never publishing the attempt/success/failure events the dashboard listens for (#13089) — thanks @adityadwi21
|
||||
1
changelog.d/fixes/13122-responses-custom-tool-choice.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(sse):** stop rejecting a Responses API `tool_choice.type: "custom"` (e.g. Codex CLI forcing `functions__exec`) with a 400 `unsupported_feature` error (#13122) — thanks @phamtienduceng-eng
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(sse):** classify a missing Playwright Chromium install on the Z.ai web transport as an actionable 503 host/config cooldown instead of a generic 502 that trips the provider circuit breaker (#13232) — thanks @oleksandr1811
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(db):** defer `process.exit(0)` on graceful shutdown by one macrotask, avoiding a Windows-only libuv abort when the sql.js fallback driver has a statement in flight (#13306) — thanks @anhtahaylove
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(cli):** `omniroute serve` now surfaces a fatal `[STARTUP] Fatal: ...` boot diagnostic (e.g. a DB driver init failure) to the console immediately, even without `--log`, instead of only when the process later crashes or restarts (#13314) — thanks @Orion1943
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(memory):** stop FTS5 rewrite on access-count updates; rebuild the index on cleanup so leftover tombstones shrink (#13326).
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(providers):** stop zed-hosted `claude-haiku-4-5` extended-thinking requests from inflating `max_tokens` past the model's real 64000 output cap (#13364) — thanks @ThiagoMafra-Integrare
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(providers):** gemini-web no longer drops the system instruction on single-turn requests or the tool contract when a client system message is present, and switches to an atomic composer insert so embedded newlines can't submit the message early (#13380) — thanks @formilw
|
||||
1
changelog.d/fixes/13389-catalog-cache-backoff-reset.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(db):** stop routine connection-backoff auto-recovery from busting the entire `/v1/models` response cache, which was causing intermittent 75-120s/502 responses on deployments routing many providers (#13389) — thanks @RaviTharuma
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(compression):** stop lite compression from dropping a `role:"tool"` message when it is byte-identical to the previous message, which orphaned a `tool_call_id` and triggered upstream 400 errors on parallel tool calls (#13429) — thanks @tolgaaksoy
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(sse):** frame post-keepalive `/v1/responses` stream errors with a top-level `type` field so Responses clients (Codex) surface the real upstream error instead of reporting "stream disconnected before completion" (#13431) — thanks @andrea-kingautomation
|
||||
1
changelog.d/fixes/13432-incremental-auto-vacuum-drift.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(db):** reconcile `auto_vacuum` drift between the configured INCREMENTAL mode and the live SQLite pragma — detected at startup and reconciled out-of-request by the vacuum scheduler, which now also runs a bounded `PRAGMA incremental_vacuum` reclaim instead of an unconditional full `VACUUM` once INCREMENTAL is actually in effect (#13432) — thanks @tolgaaksoy
|
||||
1
changelog.d/fixes/13452-provider-node-baseurl-ignored.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(sse):** stop an unhydrated `openai-compatible-*`/`anthropic-compatible-*` connection from silently routing chat requests (and its stored credential) to the real OpenAI/Anthropic API instead of the operator's configured provider-node endpoint (#13452) — thanks @DenXio101
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(resilience):** background OAuth token refresh (proactive health-check sweep and the shared refresh helper behind `refreshAccessToken`/`refreshClaudeOAuthToken`/etc.) now fails closed like the interactive chat path when a connection's assigned proxy pool is entirely dead, instead of silently sending the refresh-token exchange out direct or via a stray `HTTPS_PROXY` (#13470) — thanks @elielsousa-pathbit
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(sse):** forward Anthropic prompt-cache-creation tokens through the `/v1/responses` usage hop so cache-write counts stop logging as zero (#13472) — thanks @fidelix
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(sse):** stop the streaming PII sanitizer from splicing OpenRouter metadata (`provider`, `native_finish_reason`, `reasoning_details[].format`) into the answer text buffer (#13488) — thanks @Xore
|
||||
1
changelog.d/fixes/13535-windows-tray-icon-contrast.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(cli):** redraw the CLI/Electron system tray icon with a dark outline and ship a native multi-res `icon.ico` so it is no longer a pure-white, nearly invisible glyph on the Windows light-theme taskbar and hidden-icons flyout (#13535) — thanks @ProphetOfDoom-PoD
|
||||
1
changelog.d/fixes/13544-audio-transcription-call-log.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(api):** `/v1/audio/transcriptions`, `/v1/audio/translations` and `/v1/audio/speech` requests now show up in Dashboard → Request Logs — the three routes never called the shared call-log pipeline, so every successful (and failed) transcription/translation/speech request was silently dropped from `call_logs` ([#13544](https://github.com/diegosouzapw/OmniRoute/issues/13544)) — thanks @delafu
|
||||
1
changelog.d/fixes/13558-minimax-m3-reasoning-leak.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(providers):** MiniMax-M3's inline `<think>...</think>` reasoning no longer leaks into `message.content`/`delta.content` on the `minimax`/`minimax-cn` routes — it is now stripped and surfaced as `reasoning_content`, in both streaming and non-streaming responses (#13558) — thanks @pan17
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(api):** `PATCH /api/settings` now persists `hideAutoCombos` and `hideNoThinkVariants` instead of silently dropping them (#13562) — thanks @texastoland
|
||||
@@ -0,0 +1,3 @@
|
||||
- **fix(providers):** Antigravity error responses and logs now surface the real upstream
|
||||
message (e.g. Gemini field-path rejections) instead of the generic "Antigravity upstream
|
||||
error (400)" placeholder (#13591) — thanks @afonsoft
|
||||
1
changelog.d/fixes/13597-calllogs-worker-error-detail.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(usage):** the call-logs artifact worker's failure warning now includes the underlying error's message/code instead of the generic "detail omitted" — a crashed or non-zero-exit worker was previously undiagnosable in the logs (#13597) — thanks @afonsoft
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(providers):** echo back `reasoning_content` on `bai` DeepSeek thinking-mode follow-up turns, fixing the upstream 400 "reasoning_content must be passed back" (#13599) — thanks @afonsoft
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(sse):** stream reasoning deltas from combo targets incrementally instead of buffering them into a single burst, and stop rejecting reasoning-only streams as an empty completion (#13620) — thanks @NaNomicon
|
||||
2
changelog.d/fixes/13628-grok46-default-effort.md
Normal file
@@ -0,0 +1,2 @@
|
||||
- fix(providers): restore grok-4.6/4.5 default reasoning effort so requests without an explicit effort keep reasoning enabled (#13628)
|
||||
- fix(registry): declare supportedThinkingEfforts on claude-opus-5 and claude-fable-5 across the anthropic/claude/claude-web/ghe-copilot/github registries (#13628)
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(sse):** Kiro translator no longer re-prepends the full relocated tool-documentation block onto every subsequent turn of a multi-turn conversation; it now stays anchored to the turn that originally carried it. (#13652) — thanks @KelvinKSPS
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(docker):** isolate the ChatGPT Web (Codex) CDP proxy sidecar onto its own Compose network, add an opt-in `CDP_PROXY_TOKEN` auth gate to `cdp-proxy.mjs`, and stop the VNC browser-login CDP bridge from starting when no token is configured (#13679)
|
||||
1
changelog.d/fixes/13679-cloudsync-hmac-fail-open.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(auth):** `verifyCloudSignature()` no longer accepts an unverifiable `X-Cloud-Sig` when `OMNIROUTE_CLOUD_SYNC_SECRET` is unset — a forged/garbage signature is rejected outright, and the new opt-in `OMNIROUTE_CLOUD_SYNC_ENFORCE_SIGNATURE=true` flag rejects unsigned Cloud-sync payloads too (default stays legacy pass-through for v3.8.x; the default flips in v3.9) ([#13679](https://github.com/diegosouzapw/OmniRoute/issues/13679))
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(security):** removed the copy-pasteable placeholder `JWT_SECRET`/`API_KEY_SECRET`/`INITIAL_PASSWORD` values from the Podman Quadlet deploy manifest, and blocked remote dashboard logins with the well-known default `INITIAL_PASSWORD=CHANGEME` (#13679)
|
||||
1
changelog.d/fixes/13679-selfloop-bearer-random.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(security):** the internal self-loop admission-bypass bearer is now a random per-process secret instead of the checked-in literal `"sk_omniroute"` when no `OMNIROUTE_API_KEY`/`ROUTER_API_KEY` is configured (#13679)
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(db):** `DELETE /v1/batches/delete-completed` now caps the work it does per request and reports `hasMore` so a caller can resume, and the sweep no longer deletes a file that another batch still references (#13680, #13681)
|
||||
1
changelog.d/fixes/agnes-cn-thinking-effort-tiers.md
Normal file
@@ -0,0 +1 @@
|
||||
- fix(providers): **declare Agnes CN chat models' live `reasoning_effort` vocabulary** so the catalog and sanitizer stop inventing tiers the CN API rejects. Probes on api.agnes-ai.cn (2026-09-14) match the international endpoint: 2.0/2.5 accept `none/low/medium/high/max`, 3.0 also accepts `minimal` and `xhigh`; `off`/`ultra` clamp off the wire and Hermes' default `xhigh` clamps to `max` on 2.x.
|
||||
1
changelog.d/fixes/gemini-38-think-level.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(gemini):** send Gemini 3.8 `thinkingLevel` instead of a numeric `thinkingBudget`, and omit `includeThoughts` unless the client asked, so hidden thoughts stop eating `maxOutputTokens`
|
||||
1
changelog.d/fixes/release-acceptance-shadow.md
Normal file
@@ -0,0 +1 @@
|
||||
- Add a shadow release-acceptance report next to release-green.json. It does not close #12732 and is not a Mergify required check.
|
||||
@@ -0,0 +1 @@
|
||||
- **chore(skills):** regenerate the `omni-settings` agent skill after the pool egress-observation route landed (#13581), clearing the `check:agent-skills-sync` base-red (#12732)
|
||||
@@ -0,0 +1 @@
|
||||
- **chore(build):** ship `httpClientAbortGuard.mjs` in the published tarball — the #13636 crash guard was a new `server-ws.mjs` import missing from both pack-artifact allowlists (#12732)
|
||||
@@ -0,0 +1 @@
|
||||
- **chore(ci):** register the #13609 ambiguous-401 regression test in the mutation-coverage config, clearing the second `check:agent-skills-sync`/`mutation-test-coverage` base-red (#12732)
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
"open-sse/config/providers/registry/claude/index.ts": {
|
||||
"@typescript-eslint/no-unused-vars": {
|
||||
"count": 7
|
||||
"count": 6
|
||||
}
|
||||
},
|
||||
"open-sse/config/providers/registry/vertex/index.ts": {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"_rebaseline_2026_09_15_13572_combined_growth": "Combined growth of the 2026-09-15 maxmad64bis uplift batch (each PR rebaselined its own growth; the merged sum is larger): open-sse/executors/base.ts->1754. Every hunk is flag-gated or a verified fix covered by that PR's tests; see the batch report.",
|
||||
"_rebaseline_2026_09_15_13445_combined_growth": "Combined growth of the 2026-09-15 maxmad64bis uplift batch (each PR rebaselined its own growth; the merged sum is larger): open-sse/utils/proxyFetch.ts->1296. Every hunk is flag-gated or a verified fix covered by that PR's tests; see the batch report.",
|
||||
"_rebaseline_2026_09_15_13643_combined_growth": "Combined growth of the 2026-09-15 maxmad64bis uplift batch (each PR rebaselined its own growth; the merged sum is larger): open-sse/executors/codex.ts->1528. Every hunk is flag-gated or a verified fix covered by that PR's tests; see the batch report.",
|
||||
"_rebaseline_2026_09_15_13344_conversation_turn_nodes_retention_field": "PR #13344 rework: src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx 1596->1597 (+1 checker count on the pure tip, matching the existing frozen 1597; this rework's own edit only changes the literal default shown in the retentionFields row from 1 to 30, no line added/removed). The one added line is the PR's own retentionFields row (conversationTurnNodes) exposing the new independent retention.conversationTurnNodes knob (src/types/databaseSettings.ts) added by the same PR. Irreducible: one row per existing retention setting in this table. Covered by tests/unit/db-cleanup-conversation-nodes-12453.test.ts.",
|
||||
"_rebaseline_2026_09_15_13609_combined_growth": "Combined growth of the 2026-09-15 maxmad64bis uplift batch (each PR rebaselined its own growth; the merged sum is larger): open-sse/services/accountFallback.ts->2507. Every hunk is flag-gated or a verified fix covered by that PR's tests; see the batch report.",
|
||||
"_rebaseline_2026_09_15_13602_combined_growth": "Combined growth of the 2026-09-15 maxmad64bis uplift batch (each PR rebaselined its own growth; the merged sum is larger): src/sse/handlers/chatHelpers.ts->1214. Every hunk is flag-gated or a verified fix covered by that PR's tests; see the batch report.",
|
||||
"_rebaseline_2026_09_15_13580_combined_growth": "Combined growth of the 2026-09-15 maxmad64bis uplift batch (each PR rebaselined its own growth; the merged sum is larger): src/sse/handlers/chatHelpers.ts->1202. Every hunk is flag-gated or a verified fix covered by that PR's tests; see the batch report.",
|
||||
@@ -482,7 +483,7 @@
|
||||
"src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx": 1477,
|
||||
"src/app/(dashboard)/dashboard/settings/components/ResilienceTab.tsx": 1271,
|
||||
"src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx": 1607,
|
||||
"src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx": 1597,
|
||||
"src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx": 1598,
|
||||
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": 2152,
|
||||
"src/app/api/providers/[id]/models/route.ts": 2432,
|
||||
"src/app/api/providers/[id]/test/route.ts": 1252,
|
||||
@@ -503,6 +504,7 @@
|
||||
"open-sse/services/autoCombo/virtualFactory.ts": 1230,
|
||||
"open-sse/services/combo/roundRobinCombo.ts": 1213
|
||||
},
|
||||
"_rebaseline_2026_09_15_roundrobin_dashboard_events": "Fix #13089 (Combo Studio Live dashboard shows an empty backlog for round-robin combos): open-sse/services/combo/roundRobinCombo.ts 1205->1213. Round-robin is the only combo strategy that bypasses handleComboChat/executeTargetAttempt.ts, the path that publishes the combo.target.attempt/succeeded/failed EventBus events the Live dashboard listens for — so round-robin completions never showed up. The new call-site wiring (createRRDashboardEvents(...) instantiated once per target, one-line .attempt()/.succeeded()/.failed() calls at the 6 existing dispatch/outcome points) is the emitter logic actually extracted into a new module, open-sse/services/combo/rrDashboardEvents.ts — this is the minimum irreducible footprint for wiring 6 required call sites into 6 fixed control-flow points of the frozen file. Covered by tests/unit/issue-13089-roundrobin-live-ws-events.test.ts (2 tests: success + failure paths).",
|
||||
"_rebaseline_base_2026_08_10_proxyfetch": "Base-red fix (green-prs sweep, issue #9985): open-sse/utils/proxyFetch.ts 1207 > cap 1000 — new proxied-TLS fetch helper introduced by the Fal reference-image work. Owner-authorized quick rebaseline to green; structural slim tracked for v3.9.0.",
|
||||
"_rebaseline_2026_07_27_v3849_train2": "Merge-train 2 (7 PRs) — owner-approved 2026-07-27. Single entry: chatCore.ts 4955->5006 (#8595, Responses multi-turn image compaction before the context hard-reject). Genuine irreducible growth at the existing compaction chokepoint in handleChatCore — the PR adds a last-resort retry against the concrete budget plus the estimateFinalInputTokens helper, both wired at the pre-existing call site rather than a new branch. Covered by tests/unit/8560-responses-image-compaction.test.ts (4 tests).",
|
||||
"_rebaseline_2026_07_27_v3849_train3": "Merge-train 3 (13 PRs) — owner-approved 2026-07-27. Both entries are genuine irreducible growth at existing chokepoints, not new branches: src/lib/db/apiKeys.ts 1518->1529 (#8805 cx/* ≡ codex/* API-key model permissions); open-sse/handlers/chatCore.ts 5006->5020 (#8806 real response payload into plugin onResponse hooks). Covered by tests/unit/db-apiKeys-crud.test.ts (4 new cases) and the two plugin-hook test files updated in #8806 respectively.",
|
||||
|
||||
183
config/quality/release-acceptance.schema.json
Normal file
@@ -0,0 +1,183 @@
|
||||
{
|
||||
"$id": "https://omniroute.local/quality/release-acceptance.schema.json",
|
||||
"title": "Release acceptance report",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"identity",
|
||||
"required_gates",
|
||||
"gates",
|
||||
"evidence_errors",
|
||||
"verdict",
|
||||
"artifact"
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": { "type": "integer", "const": 1 },
|
||||
"identity": { "$ref": "#/$defs/identity" },
|
||||
"required_gates": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": { "$ref": "#/$defs/gateInstanceKey" }
|
||||
},
|
||||
"gates": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/gateResult" }
|
||||
},
|
||||
"evidence_errors": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/evidenceError" }
|
||||
},
|
||||
"verdict": { "enum": ["VERIFIED", "FAILED", "UNVERIFIED"] },
|
||||
"artifact": {
|
||||
"anyOf": [
|
||||
{ "type": "null" },
|
||||
{ "$ref": "#/$defs/artifact" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": { "properties": { "verdict": { "const": "VERIFIED" } }, "required": ["verdict"] },
|
||||
"then": { "properties": { "required_gates": { "minItems": 1 } } }
|
||||
}
|
||||
],
|
||||
"$defs": {
|
||||
"sha": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{40}$"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{64}$"
|
||||
},
|
||||
"gateInstanceKey": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["gate_id", "suite_id", "shard_index", "shard_total"],
|
||||
"properties": {
|
||||
"gate_id": { "type": "string", "minLength": 1 },
|
||||
"suite_id": { "type": ["string", "null"] },
|
||||
"shard_index": { "type": ["integer", "null"], "minimum": 0 },
|
||||
"shard_total": { "type": ["integer", "null"], "minimum": 1 }
|
||||
}
|
||||
},
|
||||
"identity": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"repository",
|
||||
"run_id",
|
||||
"run_attempt",
|
||||
"workflow",
|
||||
"trigger",
|
||||
"scope",
|
||||
"requested_ref",
|
||||
"base_sha",
|
||||
"candidate_sha",
|
||||
"tested_sha"
|
||||
],
|
||||
"properties": {
|
||||
"repository": { "type": "string", "minLength": 1 },
|
||||
"run_id": { "type": "string", "minLength": 1 },
|
||||
"run_attempt": { "type": "integer", "minimum": 1 },
|
||||
"workflow": { "type": "string", "minLength": 1 },
|
||||
"trigger": { "type": "string", "minLength": 1 },
|
||||
"scope": { "enum": ["pr", "release", "scheduled"] },
|
||||
"requested_ref": { "type": "string", "minLength": 1 },
|
||||
"base_sha": { "$ref": "#/$defs/sha" },
|
||||
"candidate_sha": { "$ref": "#/$defs/sha" },
|
||||
"tested_sha": { "$ref": "#/$defs/sha" }
|
||||
}
|
||||
},
|
||||
"evidenceRef": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["artifact_id", "member", "algorithm", "digest"],
|
||||
"properties": {
|
||||
"artifact_id": { "type": "string", "minLength": 1 },
|
||||
"member": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"pattern": "^(?!/)(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$))[^\\s]+$"
|
||||
},
|
||||
"algorithm": { "const": "sha256" },
|
||||
"digest": { "$ref": "#/$defs/digest" }
|
||||
}
|
||||
},
|
||||
"artifact": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["algorithm", "digest", "identity"],
|
||||
"properties": {
|
||||
"algorithm": { "const": "sha256" },
|
||||
"digest": { "$ref": "#/$defs/digest" },
|
||||
"identity": { "type": "string", "minLength": 1 }
|
||||
}
|
||||
},
|
||||
"evidenceError": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["code", "gate", "detail"],
|
||||
"properties": {
|
||||
"code": { "type": "string", "minLength": 1 },
|
||||
"gate": { "$ref": "#/$defs/gateInstanceKey" },
|
||||
"detail": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"gateResult": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"gate_id",
|
||||
"suite_id",
|
||||
"shard_index",
|
||||
"shard_total",
|
||||
"tested_sha",
|
||||
"run_id",
|
||||
"run_attempt",
|
||||
"command_id",
|
||||
"gate_type",
|
||||
"status",
|
||||
"cause",
|
||||
"exit_code",
|
||||
"duration_ms",
|
||||
"evidence"
|
||||
],
|
||||
"properties": {
|
||||
"gate_id": { "type": "string", "minLength": 1 },
|
||||
"suite_id": { "type": ["string", "null"] },
|
||||
"shard_index": { "type": ["integer", "null"], "minimum": 0 },
|
||||
"shard_total": { "type": ["integer", "null"], "minimum": 1 },
|
||||
"tested_sha": { "$ref": "#/$defs/sha" },
|
||||
"run_id": { "type": "string", "minLength": 1 },
|
||||
"run_attempt": { "type": "integer", "minimum": 1 },
|
||||
"command_id": { "type": "string", "minLength": 1 },
|
||||
"gate_type": { "enum": ["static", "test", "artifact"] },
|
||||
"status": { "enum": ["PASS", "FAIL", "INFRA_ERROR", "SKIPPED"] },
|
||||
"reason": { "type": "string", "minLength": 1 },
|
||||
"cause": {
|
||||
"anyOf": [
|
||||
{ "type": "null" },
|
||||
{ "$ref": "#/$defs/gateInstanceKey" }
|
||||
]
|
||||
},
|
||||
"exit_code": { "type": ["integer", "null"] },
|
||||
"duration_ms": { "type": "integer", "minimum": 0 },
|
||||
"evidence": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/evidenceRef" }
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": { "status": { "const": "SKIPPED" } },
|
||||
"required": ["status"]
|
||||
},
|
||||
"then": { "required": ["reason"] }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,28 @@ cp contrib/podman/*.network ~/.config/containers/systemd/omniroute/
|
||||
cp contrib/podman/*.volume ~/.config/containers/systemd/omniroute/
|
||||
```
|
||||
|
||||
### 3. Mount the project .env for secrets
|
||||
### 3. Generate secrets before first start
|
||||
|
||||
`omniroute.container` no longer ships `JWT_SECRET` / `API_KEY_SECRET` /
|
||||
`INITIAL_PASSWORD` values — earlier versions shipped copy-pasteable
|
||||
placeholders (`change-me-to-a-random-base64-string`,
|
||||
`change-me-to-a-random-hex-string`) that an operator could forget to
|
||||
rotate, leaving the deployment with a public, guessable secret/password
|
||||
(#13679). Generate real ones and put them in your project `.env`:
|
||||
|
||||
```bash
|
||||
echo "JWT_SECRET=$(openssl rand -base64 48)" >> .env
|
||||
echo "API_KEY_SECRET=$(openssl rand -hex 32)" >> .env
|
||||
echo "INITIAL_PASSWORD=$(openssl rand -hex 24)" >> .env
|
||||
```
|
||||
|
||||
If you skip this: `JWT_SECRET`/`API_KEY_SECRET` are auto-generated and
|
||||
persisted on first boot, and the dashboard requires setup from `localhost`
|
||||
before it accepts any password — safer than a literal default, but a real
|
||||
`INITIAL_PASSWORD` is still recommended so a non-interactive first boot has
|
||||
a known credential to log in with.
|
||||
|
||||
### 4. Mount the project .env for secrets
|
||||
|
||||
Edit `~/.config/containers/systemd/omniroute/omniroute.container` and
|
||||
uncomment/replace the `EnvironmentFile` line with the absolute path to
|
||||
@@ -54,7 +75,7 @@ Make sure `CONTAINER_HOST=podman` is set in that `.env`.
|
||||
|
||||
Alternatively, edit the env vars directly in the `.container` file.
|
||||
|
||||
### 4. Reload systemd and start
|
||||
### 5. Reload systemd and start
|
||||
|
||||
```bash
|
||||
systemctl --user daemon-reload
|
||||
@@ -62,7 +83,7 @@ systemctl --user start omniroute-redis
|
||||
systemctl --user start omniroute
|
||||
```
|
||||
|
||||
### 5. Verify
|
||||
### 6. Verify
|
||||
|
||||
```bash
|
||||
systemctl --user status omniroute
|
||||
|
||||
@@ -28,14 +28,23 @@ Environment=DASHBOARD_PORT=20128
|
||||
Environment=API_PORT=20129
|
||||
Environment=API_HOST=0.0.0.0
|
||||
Environment=REDIS_URL=redis://redis:6379
|
||||
Environment=JWT_SECRET=change-me-to-a-random-base64-string
|
||||
Environment=API_KEY_SECRET=change-me-to-a-random-base64-string
|
||||
Environment=INITIAL_PASSWORD=change-me-to-a-random-hex-string
|
||||
Environment=NODE_ENV=production
|
||||
Environment=REQUIRE_API_KEY=true
|
||||
|
||||
# Load additional secrets (API keys, OAuth creds) from the project .env:
|
||||
# JWT_SECRET, API_KEY_SECRET and INITIAL_PASSWORD are deliberately NOT set here.
|
||||
# This unit used to ship copy-pasteable "replace-me" placeholder literals — an
|
||||
# operator who forgot to replace them ran production with a public, guessable
|
||||
# secret and dashboard password (#13679). Generate real values and load them from
|
||||
# your project .env before the FIRST start — see "Generate secrets before first
|
||||
# start" in contrib/podman/README.md — by uncommenting and pointing this at your
|
||||
# project .env:
|
||||
# EnvironmentFile=%h/code/docker/OmniRoute/.env
|
||||
#
|
||||
# If left unset: JWT_SECRET and API_KEY_SECRET are auto-generated and persisted
|
||||
# on first boot, and the dashboard requires setup from localhost before it
|
||||
# accepts any password (see managementPassword.ts / apiAuth.ts) — safer than a
|
||||
# known-literal default either way, but a real INITIAL_PASSWORD is still
|
||||
# recommended for non-interactive first boots.
|
||||
|
||||
HealthCmd=node /app/healthcheck.mjs
|
||||
HealthInterval=30s
|
||||
|
||||
@@ -142,11 +142,24 @@ services:
|
||||
- "${APP_BIND_HOST:-127.0.0.1}:${DASHBOARD_PORT:-20128}:${DASHBOARD_PORT:-20128}"
|
||||
- "${APP_BIND_HOST:-127.0.0.1}:${API_PORT:-20129}:${API_PORT:-20129}"
|
||||
- "${APP_BIND_HOST:-127.0.0.1}:${LIVE_WS_PORT:-20132}:${LIVE_WS_PORT:-20132}"
|
||||
# SECURITY (#13679): joins BOTH `default` (to keep reaching redis and the
|
||||
# other sidecars) AND the dedicated `chatgpt-web-codex-net` (the one
|
||||
# legitimate consumer of the CDP proxy below).
|
||||
networks:
|
||||
- default
|
||||
- chatgpt-web-codex-net
|
||||
profiles:
|
||||
- web
|
||||
|
||||
# Internal-only Chromium runtime for ChatGPT Web (Codex). No CDP or browser
|
||||
# UI port is published to the host.
|
||||
#
|
||||
# SECURITY (#13679): isolated onto its own `chatgpt-web-codex-net` network
|
||||
# instead of the shared implicit default bridge — its cdp-proxy.mjs
|
||||
# sidecar republishes Chromium's CDP on 0.0.0.0:9223, and CDP grants full
|
||||
# control over a live browser session. Without this isolation, any
|
||||
# compromised sibling container (redis, qdrant, bifrost, cliproxyapi,
|
||||
# codex-app-server, ...) on the default network could reach it.
|
||||
chatgpt-web-codex-browser:
|
||||
build:
|
||||
context: .
|
||||
@@ -154,8 +167,12 @@ services:
|
||||
image: omniroute:chatgpt-web-codex-browser
|
||||
restart: unless-stopped
|
||||
shm_size: "2gb"
|
||||
environment:
|
||||
- CDP_PROXY_TOKEN=${CDP_PROXY_TOKEN:-}
|
||||
volumes:
|
||||
- chatgpt-web-codex-browser-data:/browser-profile
|
||||
networks:
|
||||
- chatgpt-web-codex-net
|
||||
profiles:
|
||||
- web
|
||||
|
||||
@@ -370,7 +387,12 @@ services:
|
||||
# compose network by the omniroute app.
|
||||
healthcheck:
|
||||
test:
|
||||
["CMD", "node", "-e", "require('http').get('http://127.0.0.1:1456/readyz',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))"]
|
||||
[
|
||||
"CMD",
|
||||
"node",
|
||||
"-e",
|
||||
"require('http').get('http://127.0.0.1:1456/readyz',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))",
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
@@ -378,6 +400,13 @@ services:
|
||||
profiles:
|
||||
- codex-app-server
|
||||
|
||||
networks:
|
||||
# SECURITY (#13679): dedicated network for the unauthenticated-by-default
|
||||
# CDP proxy sidecar (docker/chatgpt-web-codex-browser/cdp-proxy.mjs) —
|
||||
# shared only with omniroute-web, not with redis/qdrant/bifrost/cliproxyapi/
|
||||
# codex-app-server or any other sibling on the implicit default network.
|
||||
chatgpt-web-codex-net: {}
|
||||
|
||||
volumes:
|
||||
chatgpt-web-codex-browser-data:
|
||||
name: omniroute-chatgpt-web-codex-browser-data
|
||||
|
||||
@@ -5,6 +5,31 @@ const listenPort = 9223;
|
||||
const upstreamHost = "127.0.0.1";
|
||||
const upstreamPort = 9222;
|
||||
|
||||
// SECURITY (#13679): this proxy republishes Chromium's loopback CDP onto
|
||||
// 0.0.0.0:9223 with no auth of its own — CDP grants full control over a
|
||||
// live browser session (Runtime.evaluate, cookie theft, etc). When the
|
||||
// operator sets CDP_PROXY_TOKEN, every request/WS-upgrade MUST present it as
|
||||
// an `X-Omni-Cdp-Token: <token>` header before a single byte is forwarded
|
||||
// upstream, mirroring the gate docker/vnc-browser/chromium/cdp-bridge.py
|
||||
// already has (#12571). Left unset, the proxy keeps its historical
|
||||
// zero-config behavior — the primary mitigation for the shared-bridge risk
|
||||
// is docker-compose.yml isolating this service onto its own network so no
|
||||
// unrelated sibling container can reach it at all.
|
||||
const TOKEN = process.env.CDP_PROXY_TOKEN || "";
|
||||
const TOKEN_HEADER = "x-omni-cdp-token";
|
||||
|
||||
if (!TOKEN) {
|
||||
console.error(
|
||||
"[cdp-proxy] WARNING: running without CDP_PROXY_TOKEN — every request is forwarded " +
|
||||
"unauthenticated. Set CDP_PROXY_TOKEN to require an X-Omni-Cdp-Token header (#13679)."
|
||||
);
|
||||
}
|
||||
|
||||
function hasValidToken(headers) {
|
||||
if (!TOKEN) return true;
|
||||
return headers[TOKEN_HEADER] === TOKEN;
|
||||
}
|
||||
|
||||
function proxyHeaders(headers) {
|
||||
const next = { ...headers, host: `${upstreamHost}:${upstreamPort}` };
|
||||
delete next.connection;
|
||||
@@ -13,6 +38,11 @@ function proxyHeaders(headers) {
|
||||
}
|
||||
|
||||
const server = http.createServer((request, response) => {
|
||||
if (!hasValidToken(request.headers)) {
|
||||
response.writeHead(403, { "content-type": "application/json" });
|
||||
response.end(JSON.stringify({ error: "missing or invalid X-Omni-Cdp-Token" }));
|
||||
return;
|
||||
}
|
||||
const upstream = http.request(
|
||||
{
|
||||
host: upstreamHost,
|
||||
@@ -48,6 +78,10 @@ const server = http.createServer((request, response) => {
|
||||
});
|
||||
|
||||
server.on("upgrade", (request, socket, head) => {
|
||||
if (!hasValidToken(request.headers)) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
const upstream = net.connect(upstreamPort, upstreamHost, () => {
|
||||
const upgradeHeaders = {
|
||||
...request.headers,
|
||||
@@ -69,4 +103,6 @@ server.on("upgrade", (request, socket, head) => {
|
||||
upstream.on("error", () => socket.destroy());
|
||||
});
|
||||
|
||||
server.listen(listenPort, "0.0.0.0");
|
||||
server.listen(listenPort, "0.0.0.0", () => {
|
||||
console.error(`[cdp-proxy] listening on 0.0.0.0:${listenPort}`);
|
||||
});
|
||||
|
||||
@@ -10,7 +10,13 @@ if [[ "${PIXELFLUX_WAYLAND,,}" == "true" ]]; then
|
||||
echo "[svc-de] ${SOCKET_PATH} found launching de"
|
||||
cd $HOME
|
||||
# OmniRoute: bridge Chromium DevTools (127.0.0.1:9222) to 0.0.0.0:9223.
|
||||
( sleep 8; python3 /usr/local/bin/cdp-bridge.py >/proc/1/fd/2 2>&1 ) &
|
||||
# SECURITY (#13679): only start the bridge when CDP_BRIDGE_TOKEN is
|
||||
# configured — cdp-bridge.py already fails closed for every caller when
|
||||
# it is unset (#12571), so an unconfigured container gains nothing by
|
||||
# running an always-listening 0.0.0.0:9223 process anyway.
|
||||
if [ -n "${CDP_BRIDGE_TOKEN:-}" ]; then
|
||||
( sleep 8; python3 /usr/local/bin/cdp-bridge.py >/proc/1/fd/2 2>&1 ) &
|
||||
fi
|
||||
exec s6-setuidgid abc \
|
||||
/bin/bash /defaults/startwm_wayland.sh &
|
||||
PID=$!
|
||||
@@ -57,7 +63,13 @@ chmod 777 /tmp/selkies*
|
||||
# run
|
||||
cd $HOME
|
||||
# OmniRoute: bridge Chromium DevTools (127.0.0.1:9222) to 0.0.0.0:9223.
|
||||
( sleep 8; python3 /usr/local/bin/cdp-bridge.py >/proc/1/fd/2 2>&1 ) &
|
||||
# SECURITY (#13679): only start the bridge when CDP_BRIDGE_TOKEN is
|
||||
# configured — cdp-bridge.py already fails closed for every caller when it
|
||||
# is unset (#12571), so an unconfigured container gains nothing by running
|
||||
# an always-listening 0.0.0.0:9223 process anyway.
|
||||
if [ -n "${CDP_BRIDGE_TOKEN:-}" ]; then
|
||||
( sleep 8; python3 /usr/local/bin/cdp-bridge.py >/proc/1/fd/2 2>&1 ) &
|
||||
fi
|
||||
exec s6-setuidgid abc \
|
||||
/bin/bash /defaults/startwm.sh &
|
||||
PID=$!
|
||||
|
||||
@@ -145,6 +145,7 @@ Runs on every PR to `main`. Blocks merge on failure.
|
||||
| `check-ui-keys-coverage` (inline) | UI i18n key coverage is ≥ 65% | Yes |
|
||||
| `check-ui-value-drift` (inline) | A rewritten English **value** leaves no stale translation behind | Yes |
|
||||
| `check-new-key-coverage` (inline) | A **new** English key reaches every locale | Yes |
|
||||
| `check-key-completeness` (inline) | Every locale carries exactly the key set of `en.json` (absent key = defect, whatever its age; `__MISSING__` counts as present) | Yes |
|
||||
| `check-translation-ratio` | Real-translation ratio per locale (identical-to-English / placeholder / missing leaves outside the allowlist) must not exceed `config/quality/i18n-translation-baseline.json` + slack | **Advisory** |
|
||||
|
||||
Needs `fetch-depth: 0` — the value-drift gate diffs `en.json` against the merge base.
|
||||
@@ -536,6 +537,21 @@ several "obvious" merges turned out to hide debt and are **not** clean drop-ins.
|
||||
|
||||
- Supply-chain (provenance, SBOM, Trivy, Scorecard): [`docs/security/SUPPLY_CHAIN.md`](../security/SUPPLY_CHAIN.md)
|
||||
|
||||
#### `check-key-completeness` — key-set parity gate
|
||||
|
||||
`scripts/i18n/check-key-completeness.mjs` (`npm run i18n:check-keys`, job `i18n-ui-coverage`).
|
||||
Compares the leaf key set of every `src/i18n/messages/<locale>.json` with `en.json` and fails
|
||||
on any absent or extra leaf, regardless of when the key was added. `__MISSING__:` placeholders
|
||||
count as present (their content is the ratio gate's business). It is the absolute complement
|
||||
of the two diff-based/percentage gates: `check-ui-keys-coverage` enforces an 80 % floor per
|
||||
locale (43 absent keys out of ~13,000 still read 99.7 %) and `check-new-key-coverage` judges
|
||||
only the keys a PR adds to `en.json`. A locale batch is generated from the `en.json` of the day
|
||||
its branch is cut and translates for days while the base keeps adding keys; the batch PR adds no
|
||||
key itself, so both siblings stayed silent when batch 1 (#13044) landed 43 keys short in nine
|
||||
locales and batch 2 (#13660) 10 keys short in eight (2026-09-15). Fix a red with
|
||||
`node scripts/i18n/sync-ui-keys.mjs --locale=<codes> --translate-markers`; an `extra` leaf
|
||||
means the source dropped it — delete it from the locale. `--warn` reports without failing.
|
||||
|
||||
#### `check-new-key-coverage` — new-key i18n gate
|
||||
|
||||
Sibling of `check-ui-value-drift`. That one catches an English value that was **rewritten**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (358 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 86 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (359 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 86 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<desc>Compact animated terminal cycling three real OmniRoute CLI commands with a typewriter effect and a scrolling subcommand ticker; the first frame shows the completed providers-list screen.</desc>
|
||||
<defs><clipPath id="tickerClip"><rect x="12" y="304" width="1176" height="40"/></clipPath><clipPath id="tw0"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;31;61;92;122;153;184;214;245;245" keyTimes="0;0.012;0.018;0.024;0.030;0.036;0.042;0.048;0.054;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw1"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;26;51;76;102;128;153;178;204;204" keyTimes="0;0.348;0.351;0.357;0.363;0.369;0.375;0.381;0.387;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw2"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;20;41;61;82;102;122;143;163;163" keyTimes="0;0.678;0.684;0.690;0.696;0.702;0.708;0.714;0.720;1" dur="18s" repeatCount="indefinite"/></rect></clipPath></defs>
|
||||
<rect width="1200" height="350" fill="#0d1117"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 358 providers, 150+ free providers built-in, 20 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 42 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 359 providers, 150+ free providers built-in, 20 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 42 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<desc>Static-header comparison table where each capability row fades in top to bottom; the OmniRoute column is highlighted and shows a check or a leading value in every row, while competitors show a mix of checks, partials and crosses.</desc>
|
||||
<defs>
|
||||
<pattern id="gC" width="32" height="32" patternUnits="userSpaceOnUse"><path d="M 32 0 L 0 0 0 32" fill="none" stroke="#ffffff" stroke-opacity="0.05" stroke-width="1"/></pattern>
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -64,7 +64,7 @@
|
||||
<text x="60" y="228" font-family="Consolas, 'Courier New', monospace" font-size="104" font-weight="800" fill="url(#gradBrandF)">~1.47B</text>
|
||||
<text x="62" y="266" font-family="Consolas, 'Courier New', monospace" font-size="15" letter-spacing="3" font-weight="700" fill="#a1a1aa">FREE TOKENS / MONTH · <tspan fill="#22c55e">STEADY</tspan></text>
|
||||
<text x="62" y="298" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="16" fill="#F7F6FC">up to <tspan font-weight="800" fill="#22c55e">~2.07B</tspan> in your first month — signup credits</text>
|
||||
<text x="62" y="326" font-family="Consolas, 'Courier New', monospace" font-size="12" fill="#71717a">documented free tiers · <tspan fill="#8b5cf6">34 recurring pools</tspan> · <tspan fill="#8b5cf6">443 catalog entries</tspan> · one endpoint</text>
|
||||
<text x="62" y="326" font-family="Consolas, 'Courier New', monospace" font-size="12" fill="#71717a">documented free tiers · <tspan fill="#8b5cf6">34 recurring pools</tspan> · <tspan fill="#8b5cf6">446 catalog entries</tspan> · one endpoint</text>
|
||||
|
||||
<!-- ═══ Panel · The honest math ═══ -->
|
||||
<rect x="680" y="84" width="460" height="216" rx="14" fill="#161b22" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint and 358 providers. Six pillars. Resilient fallback: automatic routing continues while another healthy target is available. Save up to 95 percent of eligible tokens: RTK plus Caveman stacked compression averages about 89 percent on tool-heavy sessions. Zero dollars to start: 150+ providers with a free tier and 52 recurring or keyless free-forever providers. Every tool works: 36 CLI and agent integration records, including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity, through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation at /v1. Production controls: circuit breakers, TLS stealth, MCP with 110 tools, A2A, memory, guardrails, evals, and 39,000+ static test declarations across 5,100+ tracked test files.">
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint and 359 providers. Six pillars. Resilient fallback: automatic routing continues while another healthy target is available. Save up to 95 percent of eligible tokens: RTK plus Caveman stacked compression averages about 89 percent on tool-heavy sessions. Zero dollars to start: 150+ providers with a free tier and 53 recurring or keyless free-forever providers. Every tool works: 36 CLI and agent integration records, including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity, through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation at /v1. Production controls: circuit breakers, TLS stealth, MCP with 110 tools, A2A, memory, guardrails, evals, and 39,000+ static test declarations across 5,100+ tracked test files.">
|
||||
<desc>Animated promise card: six pillar tiles fade in in reading order, then a soft colored border highlight sweeps from tile to tile in a continuous cycle.</desc>
|
||||
<defs>
|
||||
<pattern id="gridPaperP" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
@@ -21,7 +21,7 @@
|
||||
<line x1="150" y1="53" x2="1160" y2="53" stroke="#232b38" stroke-width="1.5"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">358 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
|
||||
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">359 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
|
||||
</g>
|
||||
|
||||
<g font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif">
|
||||
@@ -38,7 +38,7 @@
|
||||
<line x1="3.9" y1="3.9" x2="18.1" y2="18.1"/>
|
||||
</g>
|
||||
<text x="102" y="170" font-size="18" font-weight="800" fill="#74b9ff">Never hit limits</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 358 providers in</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 359 providers in</text>
|
||||
<text x="66" y="226" font-size="13.5" fill="#a1a1aa">milliseconds. Quota out? The next provider</text>
|
||||
<text x="66" y="248" font-size="13.5" fill="#a1a1aa">takes over while a healthy target remains.</text>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 358 providers — 150+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 358 AI providers, 150+ free tiers, about 1.47B free tokens per month, 15 to 95 percent token savings, 20 routing strategies, zero dollars to start.">
|
||||
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 359 providers — 150+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 359 AI providers, 150+ free tiers, about 1.47B free tokens per month, 15 to 95 percent token savings, 20 routing strategies, zero dollars to start.">
|
||||
<desc>Animated hero card: a pulse travels the divider line and a compression bar demo repeatedly shrinks a prompt by up to 95 percent; all headline content is static and readable on the first frame.</desc>
|
||||
<defs>
|
||||
<pattern id="gridPaperH" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
@@ -28,7 +28,7 @@
|
||||
<text x="48" y="138" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="60" font-weight="800" fill="#e9edf3">Never stop coding<tspan fill="#a855f7">.</tspan></text>
|
||||
|
||||
<!-- subheadline -->
|
||||
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">358 providers</tspan> — <tspan fill="#7ee787" font-weight="800">150+ free</tspan> — through one endpoint.</text>
|
||||
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">359 providers</tspan> — <tspan fill="#7ee787" font-weight="800">150+ free</tspan> — through one endpoint.</text>
|
||||
|
||||
<!-- plug line -->
|
||||
<text x="48" y="222" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="16.5" fill="#a1a1aa">Claude Code · Codex · Cursor · Cline · Copilot · Antigravity  →  <tspan fill="#7ee787" font-weight="700">FREE</tspan> Claude / GPT / Gemini · auto-fallback</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
@@ -25,6 +25,23 @@ Or via the open-sse transport:
|
||||
omniroute --dev # MCP auto-starts on /mcp endpoint
|
||||
```
|
||||
|
||||
The HTTP transports (`sse` / `streamable-http`, served in-process by the dashboard server) are
|
||||
off by default and were previously toggleable only from the `/dashboard/mcp` page. As of v3.8.51
|
||||
the CLI has parity:
|
||||
|
||||
```bash
|
||||
omniroute mcp status # enabled/online, transport, tool count
|
||||
omniroute mcp enable [--transport stdio|sse|streamable-http]
|
||||
omniroute mcp disable
|
||||
omniroute mcp restart # resets active sse/streamable-http sessions
|
||||
```
|
||||
|
||||
`mcp enable`/`mcp disable` PATCH the same `mcpEnabled` (and optionally `mcpTransport`) setting
|
||||
the dashboard toggles via `/api/settings`. `mcp restart` calls `POST /api/mcp/restart`: it tears
|
||||
down active `sse`/`streamable-http` sessions so the next request re-initializes cleanly, returns
|
||||
`409` if MCP is disabled, and `501` for the `stdio` transport (stdio clients own their own
|
||||
subprocess — there is no in-process handle to restart).
|
||||
|
||||
## Transports
|
||||
|
||||
The MCP server exposes three transports, all backed by the same `createMcpServer()` factory:
|
||||
|
||||
@@ -211,6 +211,38 @@ npm run i18n:check-ui-coverage && npm run i18n:check-ratio && npm run check:docs
|
||||
adapter and must not be edited by hand. The Google-Translate generator
|
||||
(`generate-multilang.mjs`) is deprecated and is not part of this flow.
|
||||
|
||||
## Keeping catalogs complete and retranslating English copies
|
||||
|
||||
Three gates guard the catalogs, and they see different things:
|
||||
|
||||
| Gate | Sees |
|
||||
| -------------------------------- | ----------------------------------------------------------------------- |
|
||||
| `npm run i18n:check-ui-coverage` | ≥ 80 % of leaves translated per locale |
|
||||
| `npm run i18n:check-new-keys` | a key the PR adds to `en.json` reached every locale |
|
||||
| `npm run i18n:check-keys` | every locale carries exactly the key set of `en.json`, whatever the age |
|
||||
| `npm run i18n:check-ratio` | share of leaves still identical to English may only fall (ratchet) |
|
||||
|
||||
**After every merge of the base into a locale branch**, re-sync the locales the branch owns —
|
||||
the base keeps adding keys while a batch translates:
|
||||
|
||||
```bash
|
||||
node scripts/i18n/sync-ui-keys.mjs --locale=km,kn,ml --translate-markers --batch-size=40
|
||||
npm run i18n:check-keys
|
||||
```
|
||||
|
||||
**Retranslating verbatim-English leaves** (`--retranslate-identical`) turns every leaf that is
|
||||
still byte-identical to `en.json` — outside `scripts/i18n/untranslatable-keys.json` — into a
|
||||
`__MISSING__:` placeholder and translates it in the same run. Before a bulk run, put every key
|
||||
that a test pins to its English value (product, engine and flag names — e.g. the Vietnamese
|
||||
sidebar engines in `dashboard-localization-contract.test.ts`, the pt-BR label in
|
||||
`server-owned-tool-loop-flag.test.ts`) into the allowlist first, then:
|
||||
|
||||
```bash
|
||||
node scripts/i18n/sync-ui-keys.mjs --locale=es --retranslate-identical --translate-markers --batch-size=40
|
||||
npm run i18n:check-ratio:update # tighten the baseline once the locale improved
|
||||
npm run i18n:check-glossary # zh-CN / zh-TW / ko protected terms
|
||||
```
|
||||
|
||||
## Auto-Translation Pipeline
|
||||
|
||||
### generate-multilang.mjs (Google Translate)
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
---
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -19,7 +21,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -129,7 +131,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -282,7 +284,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -394,7 +396,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -438,7 +440,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
---
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 358 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 359 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -18,7 +20,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
- **Runtime:** Node.js `>=22.22.2 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 6
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 176 migrations)
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config, 177 migrations)
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
@@ -128,7 +130,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── secrets.ts # Secrets management
|
||||
│ │ │ ├── stateReset.ts # State reset utilities
|
||||
│ │ │ ├── migrationRunner.ts # Schema migration runner
|
||||
│ │ │ └── migrations/ # 176 versioned SQL migration files
|
||||
│ │ │ └── migrations/ # 177 versioned SQL migration files
|
||||
│ │ ├── evals/ # Eval runner and scheduler
|
||||
│ │ ├── memory/ # Persistent conversational memory
|
||||
│ │ │ ├── extraction.ts # Memory extraction from conversations
|
||||
@@ -281,7 +283,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
## Key Features (v3.8.50)
|
||||
|
||||
### Core Proxy
|
||||
- **358 AI providers** with automatic format translation
|
||||
- **359 AI providers** with automatic format translation
|
||||
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
|
||||
- **19 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
@@ -393,7 +395,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 176 SQL migrations.
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in 122 `src/lib/db/` modules with 177 SQL migrations.
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
@@ -437,7 +439,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 176 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules (122 domain-specific files, 177 migrations). `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests** use Node.js built-in test runner + Vitest. Run `npm test`. Vitest for MCP/autoCombo (`npm run test:vitest`). Playwright for E2E (`npm run test:e2e`). Coverage gate: ratchet vs `quality-baseline.json`, absolute floor 60% statements/lines/functions/branches.
|
||||
|
||||
|
||||