diff --git a/docs/diagrams/exported/authz-pipeline.svg b/docs/diagrams/exported/authz-pipeline.svg new file mode 100644 index 0000000000..cf29408a46 --- /dev/null +++ b/docs/diagrams/exported/authz-pipeline.svg @@ -0,0 +1 @@ +PUBLICCLIENT_APIMANAGEMENTallowextract Bearervalidinvalid (REQUIRE_API_KEY=true)absent (REQUIRE_API_KEY=false)session okBearer w/ manage scopeBearer invalidno authIncoming requestStrip trusted internal headers(x-omniroute-auth-*)classifyRoute()publicPolicy(login, health, status)clientApiPolicy(/api/v1/*, /v1/*)managementPolicy(dashboard, settings, admin)Stamp x-omniroute-auth-*(kind / id / label / scopes)validateApiKey()401 AUTH_002anonymous fallthrough403 AUTH_001401 / 302 /loginHandler(uses assertAuth) \ No newline at end of file diff --git a/docs/diagrams/exported/auto-combo-9factor.svg b/docs/diagrams/exported/auto-combo-9factor.svg new file mode 100644 index 0000000000..54e8ebb9c0 --- /dev/null +++ b/docs/diagrams/exported/auto-combo-9factor.svg @@ -0,0 +1 @@ +9-factor scoring weights (sum = 1.0)health (0.22)quota (0.17)costInv (0.17)latencyInv (0.13)taskFit (0.08)specificityMatch (0.08)stability (0.05)tierPriority (0.05)tierAffinity (0.05)Incoming requestEligible candidates(provider × model × account)Compute composite scoreper candidateSort by score(desc)Pick top-N targetsDispatch sequentially(short-circuit on success) \ No newline at end of file diff --git a/docs/diagrams/exported/cloud-agent-flow.svg b/docs/diagrams/exported/cloud-agent-flow.svg new file mode 100644 index 0000000000..8196fc2036 --- /dev/null +++ b/docs/diagrams/exported/cloud-agent-flow.svg @@ -0,0 +1 @@ +SQLite domain DBCloud Agent(codex-cloud / devin / jules)Cloud Agent Registry/v1/agents/tasksUserSQLite domain DBCloud Agent(codex-cloud / devin / jules)Cloud Agent Registry/v1/agents/tasksUserAsync execution upstreamPOST createTask (description, sources)Zod validation + AuthZ (management)getAgent(providerId)instantiate with credentialscreateTask() returns planningtaskId + status: planninginsertCloudAgentTasktaskIdGET /tasks/:id (poll)getStatus(externalId)status, plan, messagesupdateCloudAgentTasksnapshotPOST approvePlanapprovePlan(externalId)status: runningstream events / final result \ No newline at end of file diff --git a/docs/diagrams/exported/db-schema-overview.svg b/docs/diagrams/exported/db-schema-overview.svg new file mode 100644 index 0000000000..1aabb59678 --- /dev/null +++ b/docs/diagrams/exported/db-schema-overview.svg @@ -0,0 +1 @@ +tracksenforcesholdsprotected_bycontainslogsflushed_tosplit_intoproducedstreamsbybyapi_keysapi_key_usagerate_limitsprovidersprovider_connectionsdomain_circuit_breakerscomboscombo_targetscombo_executionsspend_bufferusage_aggregationsmemory_documentsmemory_chunksskills_runsskills_logsagent_tasksagent_task_eventsaudit_logmcp_audit \ No newline at end of file diff --git a/docs/diagrams/exported/i18n-flow.svg b/docs/diagrams/exported/i18n-flow.svg new file mode 100644 index 0000000000..dc0d44561e --- /dev/null +++ b/docs/diagrams/exported/i18n-flow.svg @@ -0,0 +1 @@ +diffin syncdriftSource MDs(CLAUDE.md, docs/**/*.md)sha256 hash.i18n-state.jsonMark source dirtyFor each locale(39 langs)OmniRoute/chat/completions(cx/gpt-5.4-mini)Writedocs/i18n/<locale>/<rel-path>.mdCI drift check(npm run i18n:check)passfail(exit 1) \ No newline at end of file diff --git a/docs/diagrams/exported/mcp-tools-37.svg b/docs/diagrams/exported/mcp-tools-37.svg new file mode 100644 index 0000000000..2877bf0caf --- /dev/null +++ b/docs/diagrams/exported/mcp-tools-37.svg @@ -0,0 +1 @@ +MCP Server37 tools totalCore (30)Memory (3)Skills (4)Essential (8)get_health, list_combos,switch_combo, check_quota,route_request, cost_report,list_models_catalog,get_combo_metricsSearch (1)web_searchAdvanced (11)simulate_route, set_budget_guard,set_routing_strategy,set_resilience_profile,test_combo, get_provider_metrics,best_combo_for_task, explain_route,get_session_snapshot,db_health_check, sync_pricingCache (2)Compression (5)1Proxy / Tunnels (3)memory_searchmemory_savememory_deleteskill_invokeskill_listskill_diagnoseskill_uninstall \ No newline at end of file diff --git a/docs/diagrams/exported/request-pipeline.svg b/docs/diagrams/exported/request-pipeline.svg new file mode 100644 index 0000000000..f64c7903fe --- /dev/null +++ b/docs/diagrams/exported/request-pipeline.svg @@ -0,0 +1 @@ +yesnocombosingleClient(IDE/CLI/SDK)Next.js Route/v1/chat/completionsCORS preflightZod validation(request body)AuthZ pipeline(extractApiKey +isValidApiKey)API key policy(allowlist + scopes)Prompt-injectionguardrailhandleChatCoreCache hit?Return cachedRate limit(per-key, per-IP)Combotarget?resolveComboTargets(14 strategies)handleSingleModel(per target)translateRequest(OpenAI↔Claude↔Gemini)getExecutor(31 executors)Upstream Provider(177 providers)SSE / JSONresponsesTransformer(Responses↔Chat) \ No newline at end of file diff --git a/docs/diagrams/exported/resilience-3layers.svg b/docs/diagrams/exported/resilience-3layers.svg new file mode 100644 index 0000000000..b1804b1da2 --- /dev/null +++ b/docs/diagrams/exported/resilience-3layers.svg @@ -0,0 +1 @@ +CLOSEDOPENHALF_OPENavailablecooling downallowedlockedsuccess408 / 500 / 502 / 503 / 504401 / 403 (account)429 quota (model)RequestProvider Circuit Breaker(scope: whole provider)Connection Cooldown(scope: one account/key)Skip provider(or 503 retry-after)Allow probeModel Lockout(scope: provider × conn × model)Skip this connectionfall back to nextExecute upstreamFall back to next modelClear cooldowns/lockoutsTrip provider breakerStart connection cooldownStart model lockout \ No newline at end of file diff --git a/package.json b/package.json index 03cee3da5f..ccc0f0f644 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "check:docs-counts": "node scripts/check-docs-counts-sync.mjs", "check:deprecated-versions": "node scripts/check-deprecated-versions.mjs", "check:docs-all": "npm run check:docs-sync && npm run check:docs-counts && npm run check:env-doc-sync && npm run check:deprecated-versions", + "docs:render-diagrams": "node scripts/docs/render-diagrams.mjs", "check:node-runtime": "node --import tsx/esm scripts/check-supported-node-runtime.ts", "check:pack-artifact": "node --import tsx/esm scripts/validate-pack-artifact.ts", "audit:deps": "npm audit --audit-level=moderate && npm run audit:electron", @@ -128,7 +129,7 @@ "express": "^5.2.1", "fetch-socks": "^1.3.3", "fuse.js": "^7.3.0", -"gray-matter": "^4.0.3", + "gray-matter": "^4.0.3", "http-proxy-middleware": "^4.0.0", "https-proxy-agent": "^9.0.0", "ioredis": "^5.10.1", diff --git a/scripts/docs/render-diagrams.mjs b/scripts/docs/render-diagrams.mjs new file mode 100644 index 0000000000..01cd7c7377 --- /dev/null +++ b/scripts/docs/render-diagrams.mjs @@ -0,0 +1,85 @@ +#!/usr/bin/env node +/** + * Render every Mermaid source in docs/diagrams/*.mmd into docs/diagrams/exported/*.svg + * + * Usage: + * npm run docs:render-diagrams + * + * Requirements: + * - @mermaid-js/mermaid-cli (`mmdc`) on PATH or installed globally. + * `npm install -g @mermaid-js/mermaid-cli` if missing. + * + * Notes: + * - Puppeteer needs `--no-sandbox` on Ubuntu 23.10+ / WSL. A temp config file + * is written automatically. + * - Each diagram is rendered with `--backgroundColor white` so the SVG works + * against both light and dark themes. + * - The script exits non-zero on first failure so CI / pre-commit hooks can + * gate on it. + */ +import { spawnSync } from "node:child_process"; +import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { tmpdir } from "node:os"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(__dirname, "..", ".."); +const srcDir = resolve(repoRoot, "docs", "diagrams"); +const outDir = resolve(srcDir, "exported"); + +if (!existsSync(srcDir)) { + console.error(`[render-diagrams] missing source dir: ${srcDir}`); + process.exit(1); +} +if (!existsSync(outDir)) { + mkdirSync(outDir, { recursive: true }); +} + +// Puppeteer needs --no-sandbox on many Linux distros (Ubuntu 23.10+, WSL). +const puppeteerConfigPath = join(tmpdir(), "omniroute-mmdc-puppeteer.json"); +writeFileSync( + puppeteerConfigPath, + JSON.stringify({ args: ["--no-sandbox", "--disable-setuid-sandbox"] }, null, 2) +); + +const sources = readdirSync(srcDir) + .filter((f) => f.endsWith(".mmd")) + .sort(); + +if (sources.length === 0) { + console.error(`[render-diagrams] no .mmd files in ${srcDir}`); + process.exit(1); +} + +console.log(`[render-diagrams] rendering ${sources.length} diagram(s)`); +let failures = 0; +for (const src of sources) { + const input = join(srcDir, src); + const output = join(outDir, src.replace(/\.mmd$/, ".svg")); + console.log(` - ${src} -> ${output.replace(repoRoot + "/", "")}`); + const result = spawnSync( + "mmdc", + [ + "-i", + input, + "-o", + output, + "--backgroundColor", + "white", + "--puppeteerConfigFile", + puppeteerConfigPath, + ], + { stdio: "inherit" } + ); + if (result.status !== 0) { + console.error(` [FAIL] ${src} (exit ${result.status})`); + failures += 1; + } +} + +if (failures > 0) { + console.error(`[render-diagrams] ${failures} failure(s)`); + process.exit(1); +} +console.log(`[render-diagrams] all ${sources.length} diagram(s) rendered.`);
PUBLIC
CLIENT_API
MANAGEMENT
allow
extract Bearer
valid
invalid (REQUIRE_API_KEY=true)
absent (REQUIRE_API_KEY=false)
session ok
Bearer w/ manage scope
Bearer invalid
no auth
Incoming request
Strip trusted internal headers(x-omniroute-auth-*)
classifyRoute()
publicPolicy(login, health, status)
clientApiPolicy(/api/v1/*, /v1/*)
managementPolicy(dashboard, settings, admin)
Stamp x-omniroute-auth-*(kind / id / label / scopes)
validateApiKey()
401 AUTH_002
anonymous fallthrough
403 AUTH_001
401 / 302 /login
Handler(uses assertAuth)
9-factor scoring weights (sum = 1.0)
health (0.22)
quota (0.17)
costInv (0.17)
latencyInv (0.13)
taskFit (0.08)
specificityMatch (0.08)
stability (0.05)
tierPriority (0.05)
tierAffinity (0.05)
Eligible candidates(provider × model × account)
Compute composite scoreper candidate
Sort by score(desc)
Pick top-N targets
Dispatch sequentially(short-circuit on success)
tracks
enforces
holds
protected_by
contains
logs
flushed_to
split_into
produced
streams
by
api_keys
api_key_usage
rate_limits
providers
provider_connections
domain_circuit_breakers
combos
combo_targets
combo_executions
spend_buffer
usage_aggregations
memory_documents
memory_chunks
skills_runs
skills_logs
agent_tasks
agent_task_events
audit_log
mcp_audit
diff
in sync
drift
Source MDs(CLAUDE.md, docs/**/*.md)
sha256 hash
.i18n-state.json
Mark source dirty
For each locale(39 langs)
OmniRoute/chat/completions(cx/gpt-5.4-mini)
Writedocs/i18n/<locale>/<rel-path>.md
CI drift check(npm run i18n:check)
pass
fail(exit 1)
MCP Server37 tools total
Core (30)
Memory (3)
Skills (4)
Essential (8)get_health, list_combos,switch_combo, check_quota,route_request, cost_report,list_models_catalog,get_combo_metrics
Search (1)web_search
Advanced (11)simulate_route, set_budget_guard,set_routing_strategy,set_resilience_profile,test_combo, get_provider_metrics,best_combo_for_task, explain_route,get_session_snapshot,db_health_check, sync_pricing
Cache (2)
Compression (5)
1Proxy / Tunnels (3)
memory_search
memory_save
memory_delete
skill_invoke
skill_list
skill_diagnose
skill_uninstall
yes
no
combo
single
Client(IDE/CLI/SDK)
Next.js Route/v1/chat/completions
CORS preflight
Zod validation(request body)
AuthZ pipeline(extractApiKey +isValidApiKey)
API key policy(allowlist + scopes)
Prompt-injectionguardrail
handleChatCore
Cache hit?
Return cached
Rate limit(per-key, per-IP)
Combotarget?
resolveComboTargets(14 strategies)
handleSingleModel(per target)
translateRequest(OpenAI↔Claude↔Gemini)
getExecutor(31 executors)
Upstream Provider(177 providers)
SSE / JSON
responsesTransformer(Responses↔Chat)
CLOSED
OPEN
HALF_OPEN
available
cooling down
allowed
locked
success
408 / 500 / 502 / 503 / 504
401 / 403 (account)
429 quota (model)
Request
Provider Circuit Breaker(scope: whole provider)
Connection Cooldown(scope: one account/key)
Skip provider(or 503 retry-after)
Allow probe
Model Lockout(scope: provider × conn × model)
Skip this connectionfall back to next
Execute upstream
Fall back to next model
Clear cooldowns/lockouts
Trip provider breaker
Start connection cooldown
Start model lockout