feat(quality): gate the free-tier headline so it can never silently drift again (#7798)
* docs(free-tiers): correct the headline to the 1.37B the catalog actually computes
The 2026-06-17 honesty correction landed 1.54B, but v3.8.42 reclassified longcat
from a 150M/mo recurring grant to a one-time 10M signup credit and the doc was
never resynced. Verified against computeFreeModelTotals() at every release tag
from 3.8.13 to HEAD: no free provider was lost by mistake.
* feat(quality): gate the free-tier headline against the live catalog
The README headlined ~1.6B free tokens/mo for seven releases after the catalog had
already been corrected down to 1.37B. No gate watched that number, so the drift was
invisible — check:docs-counts only covered providers, locales, executors, strategies,
oauth, a2a skills and cloud agents.
Adds a STRICT check that runs computeFreeModelTotals() (the same function behind
/api/free-tier/summary) and fails the build when README.md or FREE_TIERS.md publish a
headline that no longer rounds to it. Degrades to a skip if tsx is unavailable rather
than going falsely red.
The extractor is a whitelist: the theoretical ceiling (~10B), the historical ~1.94B and
per-model rows (~1.00B) are legitimate figures that must never trip the gate.
Also adds the biweekly-audit note under the README headline, so readers know the number
moves both ways and is what the catalog computes rather than a rounded-up best case.
* feat(quality): extend the counts gate to engines, MCP tools/scopes and CLI tools
The v3.8.49 audit found four more numbers that had silently drifted, all invisible to
CI because check:docs-counts only watched providers/locales/executors/strategies/oauth/
a2a/cloud-agents: 10->11 compression engines, 94->104 MCP tools, 30->31 scopes,
26->33 CLI tools.
Adds a generic makeNumberClaimValidator that reads every fact in ONE tsx subprocess via
the same functions the app serves (ENGINE_IDS, countUniqueMcpTools, the live scope union,
CLI_TOOLS) — never a hardcoded copy — with DATA_DIR redirected to a throwaway dir so
importing the MCP tool modules can't touch the operator's SQLite. Each check declares a
skip pattern so legitimate non-aggregate figures never trip it: per-module tool counts
('Memory tool definitions (3 tools)') and the CLI catalog total sitting next to the MCP
total. Degrades to a skip when tsx is unavailable rather than a false red.
7 new unit tests (all pass) covering the exact stale values this audit found and proving
per-module counts are ignored.
* docs(diagrams): sync the animated cards and mermaid sources to the audited v3.8.49 numbers
The README text was fixed in #7795 but the SVG cards and mermaid sources kept the
old numbers baked in — exactly the drift the readers see first.
- compression-pipeline.svg: 10 -> 11 engine cells (Omniglyph added as #8, matching
the README alt text), re-spaced 51px cells, highlight cascade re-timed, the
Caveman kill-dot repositioned inside its cell, default-stack bracket recentered
- free-tier-budget.svg: bar and grid rebuilt from computeFreeModelTotals() — 21 -> 19
countable pools (LongCat-2.0 moved to one-time credit, Inclusion provider removed),
huggingchat entry is now ERNIE 4.5 VL, kiro shows Claude Sonnet 4.5, signup credits
~616M -> ~626M (+longcat 10M pill), aria said 'about 1.6 billion' -> 1.4/2.0,
lower sections shifted up 30px (viewBox 872 -> 842)
- promise-pillars.svg: 26 -> 33 coding agents
- mcp-tools-94.mmd -> mcp-tools-104.mmd: real per-collection unique contributions
(42 base + memory 3 + skill 4 + githubSkill 3 + pool 6 + gamification 8 + plugin 8
+ notion 6 + obsidian 22 + compression 2), exported SVG regenerated, zh-CN ref synced
- request-pipeline.mmd: 17 -> 18 strategies, exported SVG regenerated
- README free-tier alt + docs/diagrams/README.md synced to the same numbers
Both edited cards pass validate-svg.sh and were render-verified at 4 timestamps
(animation runs; first frame is the finished composition).
* docs(env): register the 4 env vars missing from the .env.example contract (base-red unblock)
FREE_PROXY_AUTO_SYNC_ENABLED / FREE_PROXY_AUTO_SYNC_INTERVAL_MS (scheduler.ts) and
MITM_ROOT_CA_ENABLED / MITM_CERT_MODE (mitm manager/server, #6684) landed on
release/v3.8.49 without their .env.example + ENVIRONMENT.md entries, turning the
docs-gates job red for every PR on the branch. Documented with their real defaults
and the set-by-manager caveat for MITM_CERT_MODE.
* fix(dashboard): narrow the Codex session ParseResult with an equality check (base-red unblock)
#7725 landed 'if (!result.ok)' in OAuthModal — under this repo's strict:false,
tsc 6 only narrows a discriminated union on the equality form, so the negation
raised TS2339 (Property 'error' does not exist on ParseResult) and turned the
dashboard-typecheck gate red for every PR on release/v3.8.49. Runtime semantics
are identical (ok is a strict boolean).
Also ratchets the frozen baseline down 260 -> 259: the real fix here plus 3
baselined errors that other merges already fixed (CostOverviewTab TS2304,
SidebarTab TS2322, FreePoolTab TS2304). Baseline diff is deletions-only.
* fix(dashboard): keep OAuthModal within the frozen file-size cap
The narrowing comment pushed the file to 1032 > 1030 frozen; the rationale lives
in the previous commit message and the dashboard-typecheck gate itself guards the
'=== false' form from being refactored back to '!result.ok'.
* test(providers): align the grok-web credential assertion with the #7567 hint (base-red unblock)
#7713 added hintKey/hintFallback (proactive cf_clearance/User-Agent guidance) to the
grok-web web-session metadata without touching this test's deepEqual, turning unit
shard 2/4 red for every PR on release/v3.8.49. Rewritten in the same contract-only
style the file already uses for lmarena: structural fields stay strictly asserted,
the hint asserts key + intent (cf_clearance / User-Agent) without freezing operator
copy. Net stronger than before — the old assertion never checked the hint at all.
* test(golden): regenerate translate-path snapshot for the notion-web endpoint move (base-red unblock)
#7768 switched notion-web to app.notion.com without regenerating the golden,
turning unit shard 3/4 red for every PR on release/v3.8.49. Two-line regen,
reflects the deliberate production change.
17
.env.example
@@ -1806,6 +1806,15 @@ APP_LOG_TO_FILE=true
|
||||
# Skip writing to the hosts file when adding/removing DNS entries (e.g. sandboxed
|
||||
# or read-only test environments). Used by: src/mitm/dns/dnsConfig.ts.
|
||||
# OMNIROUTE_SKIP_DNS_WRITE=1
|
||||
# Opt in to the root-CA + per-host-leaf cert model for the MITM proxy (#6684).
|
||||
# Fresh installs and installs with this set to "true" get a persisted root CA that
|
||||
# signs per-host leaves; installs with a pre-existing trusted legacy leaf keep the
|
||||
# legacy fixed-SAN cert unless opted in. Used by: src/mitm/manager.ts.
|
||||
# MITM_ROOT_CA_ENABLED=true
|
||||
# Set BY the MITM manager for the spawned proxy process ("root-ca" | "legacy") —
|
||||
# reflects the migration decision above; not meant to be set manually.
|
||||
# Read by: src/mitm/server.cjs.
|
||||
# MITM_CERT_MODE=legacy
|
||||
|
||||
# ── Test/CI-only guards (never needed in production) ──
|
||||
# Set automatically by tests/_setup/isolateDataDir.ts and the CI workflows: the
|
||||
@@ -1825,6 +1834,14 @@ APP_LOG_TO_FILE=true
|
||||
# ONEPROXY_MAX_PROXIES=500
|
||||
# ONEPROXY_MIN_QUALITY_THRESHOLD=50
|
||||
|
||||
# ── Free Proxy Pool (auto-sync scheduler) ──
|
||||
# Background refresh of the free-proxy pool. Opt-in, OFF by default (parallels
|
||||
# Hard Rule #20's default-off posture for data-mutating background features).
|
||||
# Used by: src/lib/freeProxyProviders/scheduler.ts
|
||||
# FREE_PROXY_AUTO_SYNC_ENABLED=true
|
||||
# Sync interval in ms (default: 1800000 = 30 min).
|
||||
# FREE_PROXY_AUTO_SYNC_INTERVAL_MS=1800000
|
||||
|
||||
# ── Free Proxy Pool (1proxy source) ──
|
||||
# Used by: src/lib/freeProxyProviders/oneproxy.ts
|
||||
# Set FREE_PROXY_1PROXY_ENABLED=false to disable this source.
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
|
||||
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute aggregates the **documented** free tiers of **39 provider pools / 460+ models** into one honest number and shows it live on the dashboard (`/dashboard/free-tiers`).
|
||||
|
||||
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.4B free tokens per month steady, up to ~2.0B in the first month with signup credits, from the documented free tiers of 39 provider pools / 460+ models behind one endpoint. Honest pool-deduped math — each shared pool counted once (counting every rate limit 24/7 would read ~10B; not published), 15 providers ToS-flagged so you decide. Budget bar of the 21 countable free pools with per-model grid (Mistral Large 3 1B, GPT-4o mini 150M, LongCat 150M, Gemini 2.5 Flash 60M … Auto 25K), ~616M one-time first-month signup credits (vertex 300M, agentrouter 200M, predibase 25M, together 25M, glm-cn 20M, doubao 15M, ai21 10M, deepseek 5M, hyperbolic 5M), plus permanently-free no-token-cap providers (SiliconFlow, Z.AI GLM-Flash, Kilo, OpenCode Zen, baidu …) and a $10 OpenRouter top-up unlocking +24M/mo — 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.4B free tokens per month steady, up to ~2.0B in the first month with signup credits, from the documented free tiers of 39 provider pools / 460+ models behind one endpoint. Honest pool-deduped math — each shared pool counted once (counting every rate limit 24/7 would read ~10B; not published), 15 providers ToS-flagged so you decide. Budget bar of the 19 countable free pools with per-model grid (Mistral Large 3 1B, GPT-4o mini 150M, Gemini 2.5 Flash 60M … Claude Sonnet 4.5 25K), ~626M one-time first-month signup credits (vertex 300M, agentrouter 200M, predibase 25M, together 25M, glm-cn 20M, doubao 15M, ai21 10M, longcat 10M, deepseek 5M, hyperbolic 5M, nscale 5M), plus permanently-free no-token-cap providers (SiliconFlow, Z.AI GLM-Flash, Kilo, OpenCode Zen, baidu …) and a $10 OpenRouter top-up unlocking +24M/mo — 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)**.
|
||||
>
|
||||
> <sub>These figures are re-audited every two weeks against the live catalog and **move both ways** — a provider ends a free tier and the number drops; a new one lands and it climbs. We publish what the catalog actually computes, never a rounded-up best case. A CI gate (`check:docs-counts`) fails the build if this headline drifts from the code.</sub>
|
||||
|
||||
<div align="center">
|
||||
|
||||
|
||||
@@ -63,9 +63,6 @@
|
||||
"src/app/(dashboard)/dashboard/compression/studio/EncoderComparisonTable.tsx": {
|
||||
"TS2322": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx": {
|
||||
"TS2304": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/costs/quota-share/QuotaSharePageClient.tsx": {
|
||||
"TS2551": 7,
|
||||
"TS2322": 2,
|
||||
@@ -184,12 +181,6 @@
|
||||
"src/app/(dashboard)/dashboard/settings/components/RedisLauncherPanel.tsx": {
|
||||
"TS2345": 11
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/settings/components/SidebarTab.tsx": {
|
||||
"TS2322": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/settings/components/proxy/FreePoolTab.tsx": {
|
||||
"TS2304": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/tools/traffic-inspector/components/CustomHostsManager.tsx": {
|
||||
"TS2339": 1
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ Mermaid sources (`.mmd`) and exported SVGs for OmniRoute v3.8.0 architecture flo
|
||||
| [auto-combo-12factor.mmd](./auto-combo-12factor.mmd) | [SVG](./exported/auto-combo-12factor.svg) | docs/routing/AUTO-COMBO.md |
|
||||
| [resilience-3layers.mmd](./resilience-3layers.mmd) | [SVG](./exported/resilience-3layers.svg) | docs/architecture/RESILIENCE_GUIDE.md, CLAUDE.md |
|
||||
| [i18n-flow.mmd](./i18n-flow.mmd) | [SVG](./exported/i18n-flow.svg) | docs/guides/I18N.md |
|
||||
| [mcp-tools-94.mmd](./mcp-tools-94.mmd) | [SVG](./exported/mcp-tools-94.svg) | docs/frameworks/MCP-SERVER.md |
|
||||
| [mcp-tools-104.mmd](./mcp-tools-104.mmd) | [SVG](./exported/mcp-tools-104.svg) | docs/frameworks/MCP-SERVER.md |
|
||||
| [cloud-agent-flow.mmd](./cloud-agent-flow.mmd) | [SVG](./exported/cloud-agent-flow.svg) | docs/frameworks/CLOUD_AGENT.md |
|
||||
| [authz-pipeline.mmd](./authz-pipeline.mmd) | [SVG](./exported/authz-pipeline.svg) | docs/architecture/AUTHZ_GUIDE.md |
|
||||
| [db-schema-overview.mmd](./db-schema-overview.mmd) | [SVG](./exported/db-schema-overview.svg) | docs/architecture/CODEBASE_DOCUMENTATION.md |
|
||||
@@ -34,7 +34,7 @@ inside GitHub's `<img>` sandbox:
|
||||
| [combo-always-on.svg](./combo-always-on.svg) | style reference | Animated priority-combo fallback (4 layers, 16s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [cli-terminal.svg](./cli-terminal.svg) | README.md (root) | Compact half-height animated terminal (1200×350): 3 real CLI commands cycling with typewriter + scrolling subcommand ticker; first frame = completed providers screen. Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [compression-pipeline.svg](./compression-pipeline.svg) | README.md (root) | Animated 10-engine compression funnel (8s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [free-tier-budget.svg](./free-tier-budget.svg) | README.md (root) | Animated free-tier budget card (~1.6B/mo headline, 21-pool budget bar, per-model grid, signup credits, 10s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [free-tier-budget.svg](./free-tier-budget.svg) | README.md (root) | Animated free-tier budget card (~1.4B/mo headline, 19-pool budget bar, per-model grid, signup credits, 10s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [readme-hero.svg](./readme-hero.svg) | README.md (root) | Animated hero card (tagline, 268-provider/90+ free headline, full-width compression bar demo, 6 stat chips). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [promise-pillars.svg](./promise-pillars.svg) | README.md (root) | Animated "The Promise" 6-pillar card (12s border-highlight sweep). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [why-pain-fix.svg](./why-pain-fix.svg) | README.md (root) | Animated "Why OmniRoute" 10-row pain-vs-fix ledger (15s green row sweep). Edit the SVG directly — there is no `.mmd` source. |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 460" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute compression pipeline: a client request of 10,000 tokens passes through 10 stacked engines — Session-Dedup, CCR, RTK, Headroom, Relevance, Caveman, LLMLingua-2, Lite, Aggressive, Ultra — and reaches the provider at about 1,080 tokens, up to 95% saved. The default stacked combo runs RTK then Caveman (average 89.2%, range 78.4–94.6%). Code, URLs and JSON are always preserved byte-perfect.">
|
||||
<svg viewBox="0 0 1200 460" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute compression pipeline: a client request of 10,000 tokens passes through 11 stacked engines — Session-Dedup, CCR, RTK, Headroom, Relevance, Caveman, LLMLingua-2, Omniglyph, Lite, Aggressive, Ultra — and reaches the provider at about 1,080 tokens, up to 95% saved. The default stacked combo runs RTK then Caveman (average 89.2%, range 78.4–94.6%). Code, URLs and JSON are always preserved byte-perfect.">
|
||||
<defs>
|
||||
<pattern id="gridPaperZ" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
<path d="M 32 0 L 0 0 0 32" fill="none" stroke="#ffffff" stroke-opacity="0.06" stroke-width="1"/>
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<!-- ═══ Pipeline container ═══ -->
|
||||
<rect x="285" y="112" width="630" height="230" rx="14" fill="#161b22" stroke="#8b5cf6" stroke-width="1.2" stroke-opacity="0.4"/>
|
||||
<text x="600" y="140" text-anchor="middle" font-family="Consolas, 'Courier New', monospace" font-size="10.5" letter-spacing="2.5" font-weight="700" fill="url(#gradBrandZ)">OMNIROUTE COMPRESSION — 10 ENGINES</text>
|
||||
<text x="600" y="140" text-anchor="middle" font-family="Consolas, 'Courier New', monospace" font-size="10.5" letter-spacing="2.5" font-weight="700" fill="url(#gradBrandZ)">OMNIROUTE COMPRESSION — 11 ENGINES</text>
|
||||
<linearGradient id="gradBrandZ" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#e54d5e"/>
|
||||
<stop offset="100%" stop-color="#a855f7"/>
|
||||
@@ -53,10 +53,10 @@
|
||||
</circle>
|
||||
<!-- dies at engine 6 (Caveman) -->
|
||||
<circle r="4">
|
||||
<animateMotion dur="8s" repeatCount="indefinite" keyPoints="0;0;0.55;0.55" keyTimes="0;0.09;0.355;1" calcMode="linear"><mpath href="#pTokZ"/></animateMotion>
|
||||
<animateMotion dur="8s" repeatCount="indefinite" keyPoints="0;0;0.50;0.50" keyTimes="0;0.09;0.355;1" calcMode="linear"><mpath href="#pTokZ"/></animateMotion>
|
||||
<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.09;0.10;0.35;0.365;1" dur="8s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<!-- dies at engine 9 -->
|
||||
<!-- dies at engine 10 (Aggressive) -->
|
||||
<circle r="4">
|
||||
<animateMotion dur="8s" repeatCount="indefinite" keyPoints="0;0;0.81;0.81" keyTimes="0;0.095;0.49;1" calcMode="linear"><mpath href="#pTokZ"/></animateMotion>
|
||||
<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.095;0.105;0.485;0.50;1" dur="8s" repeatCount="indefinite"/>
|
||||
@@ -77,105 +77,114 @@
|
||||
</g>
|
||||
|
||||
|
||||
<!-- 10 engine cells -->
|
||||
<!-- 11 engine cells -->
|
||||
<g font-family="Consolas, 'Courier New', monospace" text-anchor="middle">
|
||||
<!-- 1 Session-Dedup -->
|
||||
<g transform="translate(303,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<g transform="translate(296,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;0.9;0;0" keyTimes="0;0.10;0.13;0.17;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#71717a">1</text>
|
||||
<text x="27.5" y="56" font-size="8" fill="#a1a1aa">Session</text>
|
||||
<text x="27.5" y="68" font-size="8" fill="#a1a1aa">Dedup</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#71717a">1</text>
|
||||
<text x="25.5" y="56" font-size="8" fill="#a1a1aa">Session</text>
|
||||
<text x="25.5" y="68" font-size="8" fill="#a1a1aa">Dedup</text>
|
||||
</g>
|
||||
<!-- 2 CCR -->
|
||||
<g transform="translate(364,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<g transform="translate(352,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;0.9;0;0" keyTimes="0;0.145;0.175;0.215;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#71717a">2</text>
|
||||
<text x="27.5" y="62" font-size="8" fill="#a1a1aa">CCR</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#71717a">2</text>
|
||||
<text x="25.5" y="62" font-size="8" fill="#a1a1aa">CCR</text>
|
||||
</g>
|
||||
<!-- 3 RTK (default stack) -->
|
||||
<g transform="translate(425,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#e54d5e" stroke-opacity="0.55" stroke-width="1.2"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#e54d5e" stroke-width="1.6" stroke-opacity="0">
|
||||
<g transform="translate(408,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#e54d5e" stroke-opacity="0.55" stroke-width="1.2"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#e54d5e" stroke-width="1.6" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;1;0;0" keyTimes="0;0.19;0.22;0.26;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#e54d5e">3</text>
|
||||
<text x="27.5" y="62" font-size="8.5" font-weight="700" fill="#F7F6FC">RTK</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#e54d5e">3</text>
|
||||
<text x="25.5" y="62" font-size="8.5" font-weight="700" fill="#F7F6FC">RTK</text>
|
||||
</g>
|
||||
<!-- 4 Headroom -->
|
||||
<g transform="translate(486,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<g transform="translate(464,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;0.9;0;0" keyTimes="0;0.235;0.265;0.305;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#71717a">4</text>
|
||||
<text x="27.5" y="62" font-size="8" fill="#a1a1aa">Headroom</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#71717a">4</text>
|
||||
<text x="25.5" y="62" font-size="8" fill="#a1a1aa">Headroom</text>
|
||||
</g>
|
||||
<!-- 5 Relevance -->
|
||||
<g transform="translate(547,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<g transform="translate(520,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;0.9;0;0" keyTimes="0;0.275;0.305;0.345;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#71717a">5</text>
|
||||
<text x="27.5" y="62" font-size="8" fill="#a1a1aa">Relevance</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#71717a">5</text>
|
||||
<text x="25.5" y="62" font-size="8" fill="#a1a1aa">Relevance</text>
|
||||
</g>
|
||||
<!-- 6 Caveman (default stack) -->
|
||||
<g transform="translate(608,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#e54d5e" stroke-opacity="0.55" stroke-width="1.2"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#e54d5e" stroke-width="1.6" stroke-opacity="0">
|
||||
<g transform="translate(576,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#e54d5e" stroke-opacity="0.55" stroke-width="1.2"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#e54d5e" stroke-width="1.6" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;1;0;0" keyTimes="0;0.325;0.355;0.395;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#e54d5e">6</text>
|
||||
<text x="27.5" y="62" font-size="8.5" font-weight="700" fill="#F7F6FC">Caveman</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#e54d5e">6</text>
|
||||
<text x="25.5" y="62" font-size="8.5" font-weight="700" fill="#F7F6FC">Caveman</text>
|
||||
</g>
|
||||
<!-- 7 LLMLingua-2 -->
|
||||
<g transform="translate(669,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<g transform="translate(632,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;0.9;0;0" keyTimes="0;0.375;0.405;0.445;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#71717a">7</text>
|
||||
<text x="27.5" y="56" font-size="8" fill="#a1a1aa">LLM</text>
|
||||
<text x="27.5" y="68" font-size="8" fill="#a1a1aa">Lingua-2</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#71717a">7</text>
|
||||
<text x="25.5" y="56" font-size="8" fill="#a1a1aa">LLM</text>
|
||||
<text x="25.5" y="68" font-size="8" fill="#a1a1aa">Lingua-2</text>
|
||||
</g>
|
||||
<!-- 8 Lite -->
|
||||
<g transform="translate(730,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<!-- 8 Omniglyph -->
|
||||
<g transform="translate(688,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;0.9;0;0" keyTimes="0;0.415;0.445;0.485;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#71717a">8</text>
|
||||
<text x="27.5" y="62" font-size="8" fill="#a1a1aa">Lite</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#71717a">8</text>
|
||||
<text x="25.5" y="62" font-size="7.5" fill="#a1a1aa">Omniglyph</text>
|
||||
</g>
|
||||
<!-- 9 Aggressive -->
|
||||
<g transform="translate(791,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<!-- 9 Lite -->
|
||||
<g transform="translate(744,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;0.9;0;0" keyTimes="0;0.46;0.49;0.53;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#71717a">9</text>
|
||||
<text x="27.5" y="62" font-size="7.5" fill="#a1a1aa">Aggressive</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#71717a">9</text>
|
||||
<text x="25.5" y="62" font-size="8" fill="#a1a1aa">Lite</text>
|
||||
</g>
|
||||
<!-- 10 Ultra -->
|
||||
<g transform="translate(852,168)">
|
||||
<rect width="55" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="55" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<!-- 10 Aggressive -->
|
||||
<g transform="translate(800,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;0.9;0;0" keyTimes="0;0.50;0.53;0.57;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="27.5" y="30" font-size="13" font-weight="800" fill="#71717a">10</text>
|
||||
<text x="27.5" y="62" font-size="8" fill="#a1a1aa">Ultra</text>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#71717a">10</text>
|
||||
<text x="25.5" y="62" font-size="7.5" fill="#a1a1aa">Aggressive</text>
|
||||
</g>
|
||||
<!-- 11 Ultra -->
|
||||
<g transform="translate(856,168)">
|
||||
<rect width="51" height="86" rx="8" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<rect width="51" height="86" rx="8" fill="none" stroke="#8b5cf6" stroke-width="1.4" stroke-opacity="0">
|
||||
<animate attributeName="stroke-opacity" values="0;0;0.9;0;0" keyTimes="0;0.54;0.57;0.61;1" dur="8s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="25.5" y="30" font-size="13" font-weight="800" fill="#71717a">11</text>
|
||||
<text x="25.5" y="62" font-size="8" fill="#a1a1aa">Ultra</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- default stack bracket -->
|
||||
<path d="M 452,262 L 452,272 L 635,272 L 635,262" fill="none" stroke="#e54d5e" stroke-width="1" stroke-opacity="0.5"/>
|
||||
<text x="543" y="288" text-anchor="middle" font-family="Consolas, 'Courier New', monospace" font-size="9.5" fill="#e54d5e">default stack: RTK → Caveman</text>
|
||||
<path d="M 433.5,262 L 433.5,272 L 601.5,272 L 601.5,262" fill="none" stroke="#e54d5e" stroke-width="1" stroke-opacity="0.5"/>
|
||||
<text x="517.5" y="288" text-anchor="middle" font-family="Consolas, 'Courier New', monospace" font-size="9.5" fill="#e54d5e">default stack: RTK → Caveman</text>
|
||||
|
||||
<!-- preservation note -->
|
||||
<text x="600" y="326" text-anchor="middle" font-family="Consolas, 'Courier New', monospace" font-size="9.5" fill="#22c55e" opacity="0.85">{ } code · URLs · JSON — always preserved byte-perfect</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
1
docs/diagrams/exported/mcp-tools-104.svg
Normal file
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 872" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute free-tier budget: about 1.6 billion free tokens per month steady, up to about 2.1 billion in your first month with signup credits, aggregated from the documented free tiers of 39 provider pools and 460+ models behind one endpoint, live on /dashboard/free-tiers. Honest pool-deduped math: each shared free pool is counted once — counting every rate limit 24/7 would read about 10B, which we don't publish; 15 providers carry a ToS flag so you decide. Budget bar of the 21 countable free pools with per-model breakdown: Mistral Large 3 1B, GPT-4o mini 150M, LongCat-2.0-Preview 150M, Gemini 2.5 Flash 60M, GLM 4.7 30M, Llama 3.3 70B 30M, Grok-3 24M, DeepSeek V4 Pro 20M, GPT-4.1 18M, Llama 4 Scout 15M, Inclusion Model 15M, GPT-4o 7M, MiniMax-M2.7 6M, Arcee Trinity 5M, and more. First month adds one-time signup credits of about 616M (vertex 300M, agentrouter 200M, predibase 25M, together 25M, glm-cn 20M, doubao 15M, ai21 10M, deepseek 5M, hyperbolic 5M). Plus the un-countable: permanently-free no-token-cap providers (SiliconFlow, Z.AI GLM-Flash, Kilo, OpenCode Zen, baidu and more) and a $10 OpenRouter top-up unlocking +24M per month, surfaced separately so they never inflate the headline. Live used/remaining and per-model breakdown on the dashboard.">
|
||||
<svg viewBox="0 0 1200 842" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute free-tier budget: about 1.4 billion free tokens per month steady, up to about 2.0 billion in your first month with signup credits, aggregated from the documented free tiers of 39 provider pools and 460+ models behind one endpoint, live on /dashboard/free-tiers. Honest pool-deduped math: each shared free pool is counted once — counting every rate limit 24/7 would read about 10B, which we don't publish; 15 providers carry a ToS flag so you decide. Budget bar of the 19 countable free pools with per-model breakdown: Mistral Large 3 1B, GPT-4o mini 150M, Gemini 2.5 Flash 60M, GLM 4.7 30M, Llama 3.3 70B 30M, Grok-3 24M, DeepSeek V4 Pro 20M, GPT-4.1 18M, Llama 4 Scout 15M, GPT-4o 7M, MiniMax-M2.7 6M, Arcee Trinity 5M, and more. First month adds one-time signup credits of about 626M (vertex 300M, agentrouter 200M, predibase 25M, together 25M, glm-cn 20M, doubao 15M, ai21 10M, longcat 10M, deepseek 5M, hyperbolic 5M, nscale 5M). Plus the un-countable: permanently-free no-token-cap providers (SiliconFlow, Z.AI GLM-Flash, Kilo, OpenCode Zen, baidu and more) and a $10 OpenRouter top-up unlocking +24M per month, surfaced separately so they never inflate the headline. Live used/remaining and per-model breakdown on the dashboard.">
|
||||
<defs>
|
||||
<pattern id="gridPaperF" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
<path d="M 32 0 L 0 0 0 32" fill="none" stroke="#ffffff" stroke-opacity="0.06" stroke-width="1"/>
|
||||
@@ -27,9 +27,9 @@
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect width="1200" height="872" fill="#0b0e14"/>
|
||||
<rect width="1200" height="872" fill="url(#gridPaperF)"/>
|
||||
<rect width="1200" height="872" fill="url(#bgGlowF)"/>
|
||||
<rect width="1200" height="842" fill="#0b0e14"/>
|
||||
<rect width="1200" height="842" fill="url(#gridPaperF)"/>
|
||||
<rect width="1200" height="842" fill="url(#bgGlowF)"/>
|
||||
|
||||
<!-- ═══ Header ═══ -->
|
||||
<text x="60" y="52" font-family="Consolas, 'Courier New', monospace" font-size="11" letter-spacing="2.5" font-weight="700" fill="#71717a">FREE-TIER BUDGET · LIVE ON <tspan fill="#8b5cf6">/dashboard/free-tiers</tspan></text>
|
||||
@@ -79,32 +79,30 @@
|
||||
<text x="836" y="244" font-family="Consolas, 'Courier New', monospace" font-size="11.5" fill="#22c55e">counted once ✓</text>
|
||||
<text x="704" y="280" font-family="Consolas, 'Courier New', monospace" font-size="12" fill="#f59e0b"><tspan font-weight="800">15 providers</tspan> ToS-flagged <tspan fill="#71717a">— we flag it · you decide</tspan></text>
|
||||
|
||||
<!-- ═══ Budget bar · 21 countable pools ═══ -->
|
||||
<text x="60" y="356" font-family="Consolas, 'Courier New', monospace" font-size="10.5" letter-spacing="2.5" font-weight="700" fill="#a78bfa">WHERE IT COMES FROM · <tspan fill="#F7F6FC">21 COUNTABLE FREE POOLS</tspan></text>
|
||||
<!-- ═══ Budget bar · 19 countable pools ═══ -->
|
||||
<text x="60" y="356" font-family="Consolas, 'Courier New', monospace" font-size="10.5" letter-spacing="2.5" font-weight="700" fill="#a78bfa">WHERE IT COMES FROM · <tspan fill="#F7F6FC">19 COUNTABLE FREE POOLS</tspan></text>
|
||||
<g clip-path="url(#barShapeF)">
|
||||
<rect x="60" y="372" width="1080" height="18" fill="#1c2230"/>
|
||||
<g clip-path="url(#barRevF)">
|
||||
<rect x="60.0" y="372" width="579.1" height="18" fill="#6c5ce7"/>
|
||||
<rect x="640.1" y="372" width="94.3" height="18" fill="#00b894"/>
|
||||
<rect x="735.4" y="372" width="94.3" height="18" fill="#0984e3"/>
|
||||
<rect x="830.7" y="372" width="42.9" height="18" fill="#e17055"/>
|
||||
<rect x="874.6" y="372" width="25.8" height="18" fill="#fdcb6e"/>
|
||||
<rect x="901.5" y="372" width="25.8" height="18" fill="#e84393"/>
|
||||
<rect x="928.3" y="372" width="22.4" height="18" fill="#00cec9"/>
|
||||
<rect x="951.7" y="372" width="20.1" height="18" fill="#d63031"/>
|
||||
<rect x="972.8" y="372" width="19.0" height="18" fill="#a29bfe"/>
|
||||
<rect x="992.8" y="372" width="17.2" height="18" fill="#55efc4"/>
|
||||
<rect x="1011.0" y="372" width="17.2" height="18" fill="#74b9ff"/>
|
||||
<rect x="1029.2" y="372" width="12.7" height="18" fill="#ffeaa7"/>
|
||||
<rect x="1042.9" y="372" width="12.1" height="18" fill="#fab1a0"/>
|
||||
<rect x="1056.0" y="372" width="11.5" height="18" fill="#81ecec"/>
|
||||
<rect x="1068.5" y="372" width="10.7" height="18" fill="#6c5ce7"/>
|
||||
<rect x="1080.2" y="372" width="9.3" height="18" fill="#00b894"/>
|
||||
<rect x="1090.5" y="372" width="9.2" height="18" fill="#0984e3"/>
|
||||
<rect x="1100.7" y="372" width="8.9" height="18" fill="#e17055"/>
|
||||
<rect x="1110.6" y="372" width="8.9" height="18" fill="#fdcb6e"/>
|
||||
<rect x="1120.5" y="372" width="8.8" height="18" fill="#e84393"/>
|
||||
<rect x="1130.3" y="372" width="8.7" height="18" fill="#00cec9"/>
|
||||
<rect x="60.0" y="372" width="662.3" height="18" fill="#6c5ce7"/>
|
||||
<rect x="723.3" y="372" width="106.7" height="18" fill="#00b894"/>
|
||||
<rect x="831.0" y="372" width="47.9" height="18" fill="#0984e3"/>
|
||||
<rect x="879.9" y="372" width="28.3" height="18" fill="#e17055"/>
|
||||
<rect x="909.2" y="372" width="28.3" height="18" fill="#fdcb6e"/>
|
||||
<rect x="938.5" y="372" width="24.4" height="18" fill="#e84393"/>
|
||||
<rect x="963.9" y="372" width="21.8" height="18" fill="#00cec9"/>
|
||||
<rect x="986.7" y="372" width="20.5" height="18" fill="#d63031"/>
|
||||
<rect x="1008.2" y="372" width="18.5" height="18" fill="#a29bfe"/>
|
||||
<rect x="1027.7" y="372" width="13.3" height="18" fill="#55efc4"/>
|
||||
<rect x="1042.0" y="372" width="12.6" height="18" fill="#74b9ff"/>
|
||||
<rect x="1055.6" y="372" width="12.0" height="18" fill="#ffeaa7"/>
|
||||
<rect x="1068.6" y="372" width="11.3" height="18" fill="#fab1a0"/>
|
||||
<rect x="1080.9" y="372" width="9.4" height="18" fill="#81ecec"/>
|
||||
<rect x="1091.3" y="372" width="9.2" height="18" fill="#6c5ce7"/>
|
||||
<rect x="1101.5" y="372" width="9.0" height="18" fill="#00b894"/>
|
||||
<rect x="1111.5" y="372" width="9.0" height="18" fill="#0984e3"/>
|
||||
<rect x="1121.5" y="372" width="8.8" height="18" fill="#e17055"/>
|
||||
<rect x="1131.3" y="372" width="8.7" height="18" fill="#fdcb6e"/>
|
||||
</g>
|
||||
</g>
|
||||
<circle r="3.2" fill="#F7F6FC">
|
||||
@@ -113,86 +111,88 @@
|
||||
</circle>
|
||||
<text x="60" y="416" font-family="Consolas, 'Courier New', monospace" font-size="11.5" fill="#71717a">each segment = one free pool · widths floored so every provider shows · honest numbers below</text>
|
||||
|
||||
<!-- ═══ Per-model grid (21 pools) ═══ -->
|
||||
<!-- ═══ Per-model grid (19 pools) ═══ -->
|
||||
<g font-family="Consolas, 'Courier New', monospace" font-size="12.5">
|
||||
<circle cx="66" cy="452" r="5" fill="#6c5ce7"/><text x="78" y="456" fill="#c9d1d9">Mistral Large 3 <tspan fill="#71717a">1.00B</tspan></text>
|
||||
<circle cx="346" cy="452" r="5" fill="#00b894"/><text x="358" y="456" fill="#c9d1d9">GPT-4o mini <tspan fill="#71717a">150M</tspan></text>
|
||||
<circle cx="626" cy="452" r="5" fill="#0984e3"/><text x="638" y="456" fill="#c9d1d9">LongCat-2.0-Preview <tspan fill="#71717a">150M</tspan></text>
|
||||
<circle cx="906" cy="452" r="5" fill="#e17055"/><text x="918" y="456" fill="#c9d1d9">Gemini 2.5 Flash <tspan fill="#71717a">60M</tspan></text>
|
||||
<circle cx="66" cy="482" r="5" fill="#fdcb6e"/><text x="78" y="486" fill="#c9d1d9">GLM 4.7 <tspan fill="#71717a">30M</tspan></text>
|
||||
<circle cx="346" cy="482" r="5" fill="#e84393"/><text x="358" y="486" fill="#c9d1d9">Llama 3.3 70B <tspan fill="#71717a">30M</tspan></text>
|
||||
<circle cx="626" cy="482" r="5" fill="#00cec9"/><text x="638" y="486" fill="#c9d1d9">Grok-3 <tspan fill="#71717a">24M</tspan></text>
|
||||
<circle cx="906" cy="482" r="5" fill="#d63031"/><text x="918" y="486" fill="#c9d1d9">DeepSeek V4 Pro <tspan fill="#71717a">20M</tspan></text>
|
||||
<circle cx="66" cy="512" r="5" fill="#a29bfe"/><text x="78" y="516" fill="#c9d1d9">GPT-4.1 <tspan fill="#71717a">18M</tspan></text>
|
||||
<circle cx="346" cy="512" r="5" fill="#55efc4"/><text x="358" y="516" fill="#c9d1d9">Llama 4 Scout <tspan fill="#71717a">15M</tspan></text>
|
||||
<circle cx="626" cy="512" r="5" fill="#74b9ff"/><text x="638" y="516" fill="#c9d1d9">Inclusion Model <tspan fill="#71717a">15M</tspan></text>
|
||||
<circle cx="906" cy="512" r="5" fill="#ffeaa7"/><text x="918" y="516" fill="#c9d1d9">GPT-4o <tspan fill="#71717a">7M</tspan></text>
|
||||
<circle cx="66" cy="542" r="5" fill="#fab1a0"/><text x="78" y="546" fill="#c9d1d9">MiniMax-M2.7 <tspan fill="#71717a">6M</tspan></text>
|
||||
<circle cx="346" cy="542" r="5" fill="#81ecec"/><text x="358" y="546" fill="#c9d1d9">Arcee Trinity Large Prev <tspan fill="#71717a">5M</tspan></text>
|
||||
<circle cx="626" cy="542" r="5" fill="#6c5ce7"/><text x="638" y="546" fill="#c9d1d9">Auto Free <tspan fill="#71717a">4M</tspan></text>
|
||||
<circle cx="906" cy="542" r="5" fill="#00b894"/><text x="918" y="546" fill="#c9d1d9">Auto <tspan fill="#71717a">1M</tspan></text>
|
||||
<circle cx="66" cy="572" r="5" fill="#0984e3"/><text x="78" y="576" fill="#c9d1d9">Command A Reasoning <tspan fill="#71717a">800K</tspan></text>
|
||||
<circle cx="346" cy="572" r="5" fill="#e17055"/><text x="358" y="576" fill="#c9d1d9">Llama 3.3 70B <tspan fill="#71717a">500K</tspan></text>
|
||||
<circle cx="626" cy="572" r="5" fill="#fdcb6e"/><text x="638" y="576" fill="#c9d1d9">morph-v3-large <tspan fill="#71717a">400K</tspan></text>
|
||||
<circle cx="906" cy="572" r="5" fill="#e84393"/><text x="918" y="576" fill="#c9d1d9">Llama 3.1 8B <tspan fill="#71717a">200K</tspan></text>
|
||||
<circle cx="66" cy="602" r="5" fill="#00cec9"/><text x="78" y="606" fill="#c9d1d9">Auto <tspan fill="#71717a">25K</tspan></text>
|
||||
<circle cx="626" cy="452" r="5" fill="#0984e3"/><text x="638" y="456" fill="#c9d1d9">Gemini 2.5 Flash <tspan fill="#71717a">60M</tspan></text>
|
||||
<circle cx="906" cy="452" r="5" fill="#e17055"/><text x="918" y="456" fill="#c9d1d9">GLM 4.7 <tspan fill="#71717a">30M</tspan></text>
|
||||
<circle cx="66" cy="482" r="5" fill="#fdcb6e"/><text x="78" y="486" fill="#c9d1d9">Llama 3.3 70B <tspan fill="#71717a">30M</tspan></text>
|
||||
<circle cx="346" cy="482" r="5" fill="#e84393"/><text x="358" y="486" fill="#c9d1d9">Grok-3 <tspan fill="#71717a">24M</tspan></text>
|
||||
<circle cx="626" cy="482" r="5" fill="#00cec9"/><text x="638" y="486" fill="#c9d1d9">DeepSeek V4 Pro <tspan fill="#71717a">20M</tspan></text>
|
||||
<circle cx="906" cy="482" r="5" fill="#d63031"/><text x="918" y="486" fill="#c9d1d9">GPT-4.1 <tspan fill="#71717a">18M</tspan></text>
|
||||
<circle cx="66" cy="512" r="5" fill="#a29bfe"/><text x="78" y="516" fill="#c9d1d9">Llama 4 Scout <tspan fill="#71717a">15M</tspan></text>
|
||||
<circle cx="346" cy="512" r="5" fill="#55efc4"/><text x="358" y="516" fill="#c9d1d9">GPT-4o <tspan fill="#71717a">7M</tspan></text>
|
||||
<circle cx="626" cy="512" r="5" fill="#74b9ff"/><text x="638" y="516" fill="#c9d1d9">MiniMax-M2.7 <tspan fill="#71717a">6M</tspan></text>
|
||||
<circle cx="906" cy="512" r="5" fill="#ffeaa7"/><text x="918" y="516" fill="#c9d1d9">Arcee Trinity Large Prev <tspan fill="#71717a">5M</tspan></text>
|
||||
<circle cx="66" cy="542" r="5" fill="#fab1a0"/><text x="78" y="546" fill="#c9d1d9">Auto Free <tspan fill="#71717a">4M</tspan></text>
|
||||
<circle cx="346" cy="542" r="5" fill="#81ecec"/><text x="358" y="546" fill="#c9d1d9">Auto <tspan fill="#71717a">1M</tspan></text>
|
||||
<circle cx="626" cy="542" r="5" fill="#6c5ce7"/><text x="638" y="546" fill="#c9d1d9">Command A Reasoning <tspan fill="#71717a">800K</tspan></text>
|
||||
<circle cx="906" cy="542" r="5" fill="#00b894"/><text x="918" y="546" fill="#c9d1d9">ERNIE 4.5 VL 424B <tspan fill="#71717a">500K</tspan></text>
|
||||
<circle cx="66" cy="572" r="5" fill="#0984e3"/><text x="78" y="576" fill="#c9d1d9">morph-v3-large <tspan fill="#71717a">400K</tspan></text>
|
||||
<circle cx="346" cy="572" r="5" fill="#e17055"/><text x="358" y="576" fill="#c9d1d9">Llama 3.1 8B <tspan fill="#71717a">200K</tspan></text>
|
||||
<circle cx="626" cy="572" r="5" fill="#fdcb6e"/><text x="638" y="576" fill="#c9d1d9">Claude Sonnet 4.5 <tspan fill="#71717a">25K</tspan></text>
|
||||
</g>
|
||||
|
||||
<!-- ═══ First-month signup credits ═══ -->
|
||||
<text x="60" y="652" font-family="Consolas, 'Courier New', monospace" font-size="10.5" letter-spacing="2.5" font-weight="700" fill="#22c55e">+ FIRST MONTH · ONE-TIME SIGNUP CREDITS <tspan fill="#7ee787">~616M</tspan></text>
|
||||
<text x="60" y="622" font-family="Consolas, 'Courier New', monospace" font-size="10.5" letter-spacing="2.5" font-weight="700" fill="#22c55e">+ FIRST MONTH · ONE-TIME SIGNUP CREDITS <tspan fill="#7ee787">~626M</tspan></text>
|
||||
<g font-family="Consolas, 'Courier New', monospace">
|
||||
<rect x="60" y="662" width="90" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="105" y="677" font-size="11.5" fill="#7ee787" text-anchor="middle">vertex 300M</text>
|
||||
<rect x="160" y="662" width="123" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="222" y="677" font-size="11.5" fill="#7ee787" text-anchor="middle">agentrouter 200M</text>
|
||||
<rect x="293" y="662" width="103" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="344" y="677" font-size="11.5" fill="#7ee787" text-anchor="middle">predibase 25M</text>
|
||||
<rect x="406" y="662" width="96" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="454" y="677" font-size="11.5" fill="#7ee787" text-anchor="middle">together 25M</text>
|
||||
<rect x="512" y="662" width="83" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="554" y="677" font-size="11.5" fill="#7ee787" text-anchor="middle">glm-cn 20M</text>
|
||||
<rect x="605" y="662" width="83" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="646" y="677" font-size="11.5" fill="#7ee787" text-anchor="middle">doubao 15M</text>
|
||||
<rect x="698" y="662" width="70" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="733" y="677" font-size="11.5" fill="#7ee787" text-anchor="middle">ai21 10M</text>
|
||||
<rect x="778" y="662" width="90" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="823" y="677" font-size="11.5" fill="#7ee787" text-anchor="middle">deepseek 5M</text>
|
||||
<rect x="878" y="662" width="103" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="930" y="677" font-size="11.5" fill="#7ee787" text-anchor="middle">hyperbolic 5M</text>
|
||||
<rect x="60" y="632" width="90" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="105" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">vertex 300M</text>
|
||||
<rect x="160" y="632" width="123" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="222" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">agentrouter 200M</text>
|
||||
<rect x="293" y="632" width="103" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="344" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">predibase 25M</text>
|
||||
<rect x="406" y="632" width="96" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="454" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">together 25M</text>
|
||||
<rect x="512" y="632" width="83" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="554" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">glm-cn 20M</text>
|
||||
<rect x="605" y="632" width="83" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="646" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">doubao 15M</text>
|
||||
<rect x="698" y="632" width="70" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="733" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">ai21 10M</text>
|
||||
<rect x="778" y="632" width="90" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="823" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">deepseek 5M</text>
|
||||
<rect x="878" y="632" width="103" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="930" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">hyperbolic 5M</text>
|
||||
<rect x="991" y="632" width="90" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="1036" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">longcat 10M</text>
|
||||
<rect x="1091" y="632" width="34" height="22" rx="11" fill="#13311f" stroke="#238636" stroke-opacity="0.8"/>
|
||||
<text x="1108" y="647" font-size="11.5" fill="#7ee787" text-anchor="middle">…</text>
|
||||
</g>
|
||||
|
||||
<!-- ═══ Plus the un-countable ═══ -->
|
||||
<text x="60" y="720" font-family="Consolas, 'Courier New', monospace" font-size="19" font-weight="800" fill="#8b5cf6">∞<animate attributeName="opacity" values="1;0.55;1" dur="4s" repeatCount="indefinite"/></text>
|
||||
<text x="86" y="718" font-family="Consolas, 'Courier New', monospace" font-size="10.5" letter-spacing="2.5" font-weight="700" fill="#a78bfa">PLUS THE UN-COUNTABLE <tspan fill="#71717a">— PERMANENTLY FREE · NO TOKEN CAP</tspan></text>
|
||||
<text x="60" y="690" font-family="Consolas, 'Courier New', monospace" font-size="19" font-weight="800" fill="#8b5cf6">∞<animate attributeName="opacity" values="1;0.55;1" dur="4s" repeatCount="indefinite"/></text>
|
||||
<text x="86" y="688" font-family="Consolas, 'Courier New', monospace" font-size="10.5" letter-spacing="2.5" font-weight="700" fill="#a78bfa">PLUS THE UN-COUNTABLE <tspan fill="#71717a">— PERMANENTLY FREE · NO TOKEN CAP</tspan></text>
|
||||
<g font-family="Consolas, 'Courier New', monospace" font-size="10.5" fill="#a1a1aa">
|
||||
<rect x="60" y="732" width="94" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="107" y="747" text-anchor="middle">SiliconFlow</text>
|
||||
<rect x="162" y="732" width="118" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="221" y="747" text-anchor="middle">Z.AI GLM-Flash</text>
|
||||
<rect x="288" y="732" width="48" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="312" y="747" text-anchor="middle">Kilo</text>
|
||||
<rect x="344" y="732" width="106" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="397" y="747" text-anchor="middle">OpenCode Zen</text>
|
||||
<rect x="458" y="732" width="56" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="486" y="747" text-anchor="middle">baidu</text>
|
||||
<rect x="522" y="732" width="30" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="537" y="747" text-anchor="middle">…</text>
|
||||
<rect x="60" y="702" width="94" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="107" y="717" text-anchor="middle">SiliconFlow</text>
|
||||
<rect x="162" y="702" width="118" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="221" y="717" text-anchor="middle">Z.AI GLM-Flash</text>
|
||||
<rect x="288" y="702" width="48" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="312" y="717" text-anchor="middle">Kilo</text>
|
||||
<rect x="344" y="702" width="106" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="397" y="717" text-anchor="middle">OpenCode Zen</text>
|
||||
<rect x="458" y="702" width="56" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="486" y="717" text-anchor="middle">baidu</text>
|
||||
<rect x="522" y="702" width="30" height="22" rx="11" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08"/>
|
||||
<text x="537" y="717" text-anchor="middle">…</text>
|
||||
</g>
|
||||
<text x="608" y="748" font-family="Consolas, 'Courier New', monospace" font-size="13.5" fill="#F7F6FC"><tspan fill="#f59e0b" font-weight="700">$10</tspan> OpenRouter top-up → <tspan fill="#22c55e" font-weight="700">+24M/mo</tspan></text>
|
||||
<text x="60" y="776" font-family="Consolas, 'Courier New', monospace" font-size="11" fill="#71717a">surfaced separately — never inflates the headline</text>
|
||||
<text x="608" y="718" font-family="Consolas, 'Courier New', monospace" font-size="13.5" fill="#F7F6FC"><tspan fill="#f59e0b" font-weight="700">$10</tspan> OpenRouter top-up → <tspan fill="#22c55e" font-weight="700">+24M/mo</tspan></text>
|
||||
<text x="60" y="746" font-family="Consolas, 'Courier New', monospace" font-size="11" fill="#71717a">surfaced separately — never inflates the headline</text>
|
||||
|
||||
<!-- ═══ Footer strip ═══ -->
|
||||
<rect x="60" y="800" width="1080" height="56" rx="14" fill="#161b22" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
|
||||
<text x="84" y="822" font-family="Consolas, 'Courier New', monospace" font-size="9.5" letter-spacing="2" font-weight="700" fill="#71717a">CURRENT MONTH</text>
|
||||
<rect x="84" y="830" width="240" height="8" rx="4" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
|
||||
<rect x="85" y="831" width="132" height="6" rx="3" fill="#8b5cf6"/>
|
||||
<rect x="60" y="770" width="1080" height="56" rx="14" fill="#161b22" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
|
||||
<text x="84" y="792" font-family="Consolas, 'Courier New', monospace" font-size="9.5" letter-spacing="2" font-weight="700" fill="#71717a">CURRENT MONTH</text>
|
||||
<rect x="84" y="800" width="240" height="8" rx="4" fill="#1c2230" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
|
||||
<rect x="85" y="801" width="132" height="6" rx="3" fill="#8b5cf6"/>
|
||||
<circle r="2.6" fill="#F7F6FC">
|
||||
<animateMotion path="M 85,834 L 217,834" dur="3s" repeatCount="indefinite"/>
|
||||
<animateMotion path="M 85,804 L 217,804" dur="3s" repeatCount="indefinite"/>
|
||||
<animate attributeName="opacity" values="0;1;1;0" dur="3s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="344" cy="834" r="3.5" fill="#22c55e">
|
||||
<circle cx="344" cy="804" r="3.5" fill="#22c55e">
|
||||
<animate attributeName="opacity" values="1;0.3;1" dur="1.2s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<text x="356" y="838" font-family="Consolas, 'Courier New', monospace" font-size="10" letter-spacing="1.5" font-weight="700" fill="#22c55e">LIVE</text>
|
||||
<text x="424" y="838" font-family="Consolas, 'Courier New', monospace" font-size="11.5" fill="#a1a1aa">used / remaining · per-model breakdown · transparent <tspan fill="#f59e0b">terms flag</tspan> per provider</text>
|
||||
<text x="356" y="808" font-family="Consolas, 'Courier New', monospace" font-size="10" letter-spacing="1.5" font-weight="700" fill="#22c55e">LIVE</text>
|
||||
<text x="424" y="808" font-family="Consolas, 'Courier New', monospace" font-size="11.5" fill="#a1a1aa">used / remaining · per-model breakdown · transparent <tspan fill="#f59e0b">terms flag</tspan> per provider</text>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -1,16 +1,20 @@
|
||||
%% MCP Server tool inventory by category
|
||||
%% Reflects: open-sse/mcp-server/ — TOTAL_MCP_TOOL_COUNT in server.ts
|
||||
%% (MCP_TOOLS 34 + memory 3 + skill 4 + agentSkill 3 + pool 6 + gamification 8 + plugin 8 + notion 6 + obsidian 22 = 94)
|
||||
%% Reflects: open-sse/mcp-server/ — TOTAL_MCP_TOOL_COUNT in server.ts (countUniqueMcpTools)
|
||||
%% Unique contributions: MCP_TOOLS 42 + memory 3 + skill 4 + githubSkill 3 + pool 6
|
||||
%% + gamification 8 + plugin 8 + notion 6 + obsidian 22 + compression 2 = 104
|
||||
%% (agentSkillTools fully overlaps the skill registry; 11 of the 13 compression
|
||||
%% tools live in the base registry — duplicates counted once)
|
||||
%% Regenerate the SVG with: npm run docs:render-diagrams
|
||||
%% v3.8.40
|
||||
%% v3.8.49
|
||||
flowchart LR
|
||||
MCP["MCP Server<br/>94 tools total"]
|
||||
MCP --> Core["Core (34)<br/>routing, cache, compression,<br/>quota, proxy, tunnels, search, web_fetch"]
|
||||
MCP["MCP Server<br/>104 tools total"]
|
||||
MCP --> Core["Core (42)<br/>routing, cache, compression,<br/>quota, proxy, tunnels, search, web_fetch"]
|
||||
MCP --> Mem["Memory (3)<br/>memory_search, memory_save,<br/>memory_delete"]
|
||||
MCP --> Skl["Skills (4)<br/>skill_invoke, skill_list,<br/>skill_diagnose, skill_uninstall"]
|
||||
MCP --> ASkl["Agent-Skills (3)<br/>cli-registry discovery"]
|
||||
MCP --> Skl["Skills (4)<br/>skill_invoke, skill_list,<br/>skill_diagnose, skill_uninstall<br/>+ agent-skill cli-registry discovery"]
|
||||
MCP --> GSkl["GitHub-Skills (3)<br/>marketplace search,<br/>install, update"]
|
||||
MCP --> Pool["Pool (6)<br/>pool_status, pool_sessions, pool_reset,<br/>pool_warm, pool_health, browser_pool_status"]
|
||||
MCP --> Gam["Gamification (8)"]
|
||||
MCP --> Plg["Plugins (8)"]
|
||||
MCP --> Notion["Notion (6)<br/>search, get_page, query_database,<br/>get_database, list_block_children,<br/>append_blocks"]
|
||||
MCP --> Obs["Obsidian (22)<br/>vault read/write, search,<br/>frontmatter, daily notes, …"]
|
||||
MCP --> Cmp["Compression Studios (+2)<br/>beyond the 11 in Core"]
|
||||
@@ -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, 268 providers — never stop building, OmniRoute picks the cheapest one that works. Six pillars. Never hit limits: auto-fallback across 268 providers in milliseconds, quota out means the next provider takes over with zero downtime. Save up to 95 percent of tokens: RTK plus Caveman stacked compression cuts 15 to 95 percent of eligible tokens, about 89 percent average on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier, 40+ free forever — Qoder, Pollinations, Cloudflare, SiliconFlow — no card needed. Every tool works: 26 coding agents including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation — point any tool at /v1 and it just works. Production-grade: circuit breakers, TLS stealth, MCP with 104 tools, A2A, memory, guardrails, evals — 25,000+ tests.">
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint, 268 providers — never stop building, OmniRoute picks the cheapest one that works. Six pillars. Never hit limits: auto-fallback across 268 providers in milliseconds, quota out means the next provider takes over with zero downtime. Save up to 95 percent of tokens: RTK plus Caveman stacked compression cuts 15 to 95 percent of eligible tokens, about 89 percent average on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier, 40+ free forever — Qoder, Pollinations, Cloudflare, SiliconFlow — no card needed. Every tool works: 33 coding agents including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation — point any tool at /v1 and it just works. Production-grade: circuit breakers, TLS stealth, MCP with 104 tools, A2A, memory, guardrails, evals — 25,000+ tests.">
|
||||
<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">
|
||||
@@ -91,7 +91,7 @@
|
||||
<path d="M 10,18 L 10,22"/>
|
||||
</g>
|
||||
<text x="102" y="354" font-size="18" font-weight="800" fill="#a78bfa">Every tool works</text>
|
||||
<text x="66" y="388" font-size="13.5" fill="#a1a1aa">26 coding agents — Claude Code, Codex,</text>
|
||||
<text x="66" y="388" font-size="13.5" fill="#a1a1aa">33 coding agents — Claude Code, Codex,</text>
|
||||
<text x="66" y="410" font-size="13.5" fill="#a1a1aa">Cursor, Cline, Copilot, Antigravity —</text>
|
||||
<text x="66" y="432" font-size="13.5" fill="#a1a1aa">through one config.</text>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@@ -13,7 +13,7 @@ flowchart LR
|
||||
Cache -->|yes| Return["Return cached"]
|
||||
Cache -->|no| Rate["Rate limit<br/>(per-key, per-IP)"]
|
||||
Rate --> Combo{"Combo<br/>target?"}
|
||||
Combo -->|combo| Resolve["resolveComboTargets<br/>(17 strategies)"]
|
||||
Combo -->|combo| Resolve["resolveComboTargets<br/>(18 strategies)"]
|
||||
Resolve --> Single["handleSingleModel<br/>(per target)"]
|
||||
Combo -->|single| Single
|
||||
Single --> Translate["translateRequest<br/>(OpenAI↔Claude↔Gemini)"]
|
||||
|
||||
@@ -10,9 +10,9 @@ lastUpdated: 2026-06-28
|
||||
>
|
||||
> 数据来源:`open-sse/mcp-server/schemas/tools.ts`(34 个基础工具)+ `memoryTools.ts`(3)+ `skillTools.ts`(4)+ `agentSkillTools.ts`(3)+ `poolTools.ts`(6)+ `gamificationTools.ts`(8)+ `pluginTools.ts`(8)+ `notionTools.ts`(6)+ `obsidianTools.ts`(22)= **94**(`TOTAL_MCP_TOOL_COUNT`)。工具注册和权限域绑定逻辑见 `open-sse/mcp-server/server.ts`。
|
||||
|
||||

|
||||

|
||||
|
||||
> 来源:[diagrams/mcp-tools-94.mmd](../diagrams/mcp-tools-94.mmd)(通过 `npm run docs:render-diagrams` 重新生成)。
|
||||
> 来源:[diagrams/mcp-tools-104.mmd](../diagrams/mcp-tools-104.mmd)(通过 `npm run docs:render-diagrams` 重新生成)。
|
||||
|
||||
## 安装
|
||||
|
||||
|
||||
@@ -1029,6 +1029,8 @@ Provider quota endpoints, network tunnels (Tailscale, Ngrok, MITM debug proxy),
|
||||
| `MITM_DISABLE_TLS_VERIFY` | `0` | `src/mitm/server.cjs` | Set `1` to disable upstream TLS verification (development only). |
|
||||
| `MITM_IDLE_TIMEOUT_MS` | `60000` | `src/mitm/socketTimeouts.ts`, `src/mitm/server.cjs` | Idle socket timeout (ms) for proxied connections; idle sockets past this are torn down to avoid leaking half-open tunnels. |
|
||||
| `MITM_VERBOSE` | `1` | `src/mitm/server.cjs`, `src/mitm/_internal/bypass.cjs` | Routing-decision log verbosity: `0` silences, higher values log more bypass/route decisions. |
|
||||
| `MITM_ROOT_CA_ENABLED` | `false` | `src/mitm/manager.ts` | Set `true` to opt in to the root-CA + per-host-leaf cert model (#6684). Fresh installs get it automatically; installs with a pre-existing trusted legacy leaf keep the legacy fixed-SAN cert unless opted in. |
|
||||
| `MITM_CERT_MODE` | `legacy` | `src/mitm/manager.ts`, `src/mitm/server.cjs` | Set BY the MITM manager for the spawned proxy process (`root-ca` \| `legacy`) — reflects the cert-migration decision; not meant to be set manually. |
|
||||
| `OMNIROUTE_NO_SUDO` | `0` | `src/mitm/systemCommands.ts` | Set `1` (truthy) to strip the leading `sudo` from MITM cert-trust commands — for root-less / user-namespaced deployments where the operator trusts the CA manually (e.g. via Node's extra-CA-certs mechanism). |
|
||||
| `SKIP_ANTIGRAVITY_DNS` | _(unset)_ | `src/mitm/dns/provision.ts` | Set `true` to skip provisioning `/etc/hosts` DNS entries for the Antigravity proxy hostnames entirely — for containers with no sudo/root available. |
|
||||
| `OMNIROUTE_SKIP_DNS_WRITE` | _(unset)_ | `src/mitm/dns/dnsConfig.ts` | Set `1` to skip writing to the hosts file when adding/removing DNS entries — for sandboxed or read-only test environments. |
|
||||
@@ -1039,6 +1041,8 @@ Provider quota endpoints, network tunnels (Tailscale, Ngrok, MITM debug proxy),
|
||||
| `ONEPROXY_API_URL` | `https://1proxy-api.aitradepulse.com` | `src/lib/oneproxySync.ts` | 1Proxy service API URL override. |
|
||||
| `ONEPROXY_MAX_PROXIES` | `500` | `src/lib/oneproxySync.ts` | Maximum proxies imported per sync. |
|
||||
| `ONEPROXY_MIN_QUALITY_THRESHOLD` | `50` | `src/lib/oneproxySync.ts` | Minimum quality score for imported proxies. |
|
||||
| `FREE_PROXY_AUTO_SYNC_ENABLED` | `false` | `src/lib/freeProxyProviders/scheduler.ts` | Set `true` to enable the background free-proxy pool auto-sync scheduler. Opt-in, off by default. |
|
||||
| `FREE_PROXY_AUTO_SYNC_INTERVAL_MS` | `1800000` | `src/lib/freeProxyProviders/scheduler.ts` | Auto-sync interval in milliseconds (default 30 min). |
|
||||
| `FREE_PROXY_1PROXY_ENABLED` | `true` | `src/lib/freeProxyProviders/oneproxy.ts` | Enable the 1proxy free proxy source. Set to `false` to disable. |
|
||||
| `FREE_PROXY_1PROXY_API_URL` | _(see oneproxy.ts)_ | `src/lib/freeProxyProviders/oneproxy.ts` | 1proxy API URL override. |
|
||||
| `FREE_PROXY_1PROXY_MAX` | `500` | `src/lib/freeProxyProviders/oneproxy.ts` | Maximum proxies fetched per sync from 1proxy. |
|
||||
|
||||
@@ -15,15 +15,17 @@ lastUpdated: 2026-06-28
|
||||
|
||||
| Metric | Tokens / month | Meaning |
|
||||
| ------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **Documented recurring grant (steady)** | **~1.54B** | Free-tier **pools** (per-model catalog), each shared pool counted **once**. The live source behind `/api/free-tier/summary` and the dashboard's Free-Tier Budget page. **Use this number.** |
|
||||
| **+ first month with signup credits** | **~2.15B** | Steady + one-time signup credits (Together $25, Z.AI 20M, DeepSeek 5M, …), deduped per account. **First month only** — does not recur. |
|
||||
| **Documented recurring grant (steady)** | **~1.37B** | Free-tier **pools** (per-model catalog), each shared pool counted **once**. The live source behind `/api/free-tier/summary` and the dashboard's Free-Tier Budget page. **Use this number.** |
|
||||
| **+ first month with signup credits** | **~2.00B** | Steady + one-time signup credits (Together $25, Z.AI 20M, DeepSeek 5M, …), deduped per account. **First month only** — does not recur. |
|
||||
| **+ permanently free, no published cap** | _un-quantifiable_ | `siliconflow`, `glm-cn` (GLM-4-Flash), `tencent`, `baidu`, `kilo-gateway`, `opencode-zen` — real recurring access, rate/concurrency-limited, **no token cap to count**. Listed, never summed (counting them at `RPM×24/7` is the inflation we reject). |
|
||||
| **+ deposit-unlock boost** | **+~24M** | A one-time **$10** OpenRouter top-up raises its free pool from 50 → 1000 req/day. Reported separately so it never inflates the steady number. |
|
||||
| Theoretical ceiling (all rate limits, 24/7) | ~10B | Sum of every provider rate limit extrapolated to non-stop use. **Not a guarantee** — do not headline this. |
|
||||
|
||||
**Honest headline:** _OmniRoute aggregates **~1.6B documented free tokens per month** (up to ~2.1B in your first month with signup credits) across 40+ free-tier pools — plus a long tail of permanently-free, no-cap providers — and RTK + Caveman compression (15–95% token savings) stretches that further._
|
||||
**Honest headline:** _OmniRoute aggregates **~1.37B documented free tokens per month** (up to ~2.0B in your first month with signup credits) across 39 free-tier pools — plus a long tail of permanently-free, no-cap providers — and RTK + Caveman compression (15–95% token savings) stretches that further._
|
||||
|
||||
> **Why this dropped from the previous ~1.94B.** The 2026-06-17 refresh is an honesty correction, not a loss: `gemini` is now pool-deduped (was inflated by counting each Flash variant separately, 462M → 60M), `cloudflare-ai` corrected to its real 10k-Neurons/day (122M → 30M), `doubao` reclassified as a one-time signup credit (not recurring), and shut-down tiers removed (`github-models` closed to new signups, `chutes`/`phind`/`kluster` discontinued). Partly offset by `llm7` (correct 5M/day → 150M) and new free providers (Kilo, OpenCode Zen, Z.AI GLM-Flash).
|
||||
>
|
||||
> **Further corrected to ~1.37B in v3.8.42:** `longcat` was reclassified from a 150M/mo recurring grant to a one-time 10M signup credit after its free preview ended. Same honesty rule — no provider was dropped by mistake.
|
||||
|
||||
Biggest **documented** contributors: `mistral` 1.00B, `llm7` 150M, `groq` 117M, `gemini` 60M, `cerebras` 30M, `cloudflare-ai` 30M, `sambanova` 30M. (`longcat` is excluded — its 10M LongCat-2.0 grant is a one-time, KYC-gated signup credit, not a recurring monthly budget.)
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
// not catch it — regenerate with `npm run gen:provider-reference` before relying on it.
|
||||
|
||||
import fs from "node:fs";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
@@ -102,6 +104,16 @@ export function tallyDrift(checks, getContent) {
|
||||
}
|
||||
for (const f of c.files) {
|
||||
const content = getContent(f);
|
||||
if (c.validate) {
|
||||
if (content == null) continue;
|
||||
const v = c.validate(content);
|
||||
lines.push(` ${v.ok ? "✓" : c.strict ? "✗" : "⚠"} ${f} — ${v.detail}`);
|
||||
if (!v.ok) {
|
||||
if (c.strict) strict++;
|
||||
else soft++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const found = content != null && content.includes(String(c.actual));
|
||||
if (found) {
|
||||
lines.push(` ✓ ${f} mentions "${c.actual}"`);
|
||||
@@ -115,6 +127,131 @@ export function tallyDrift(checks, getContent) {
|
||||
return { strict, soft, lines };
|
||||
}
|
||||
|
||||
// Reads every code-derived fact in ONE tsx subprocess — the same functions the app
|
||||
// serves at runtime, never a hardcoded copy. DATA_DIR is redirected to a throwaway dir
|
||||
// so importing the MCP tool modules cannot touch the operator's real SQLite file.
|
||||
// Returns null when tsx is unavailable so the gate degrades to a skip, not a false red.
|
||||
function readCodeFacts() {
|
||||
const script = [
|
||||
'import {computeFreeModelTotals} from "./open-sse/config/freeModelCatalog.ts";',
|
||||
'import {ENGINE_IDS} from "./open-sse/services/compression/engineCatalog.ts";',
|
||||
'import {CLI_TOOLS} from "./src/shared/constants/cliTools.ts";',
|
||||
'import {countUniqueMcpTools} from "./open-sse/mcp-server/toolCount.ts";',
|
||||
'import {MCP_TOOLS} from "./open-sse/mcp-server/schemas/tools.ts";',
|
||||
'import {memoryTools} from "./open-sse/mcp-server/tools/memoryTools.ts";',
|
||||
'import {skillTools} from "./open-sse/mcp-server/tools/skillTools.ts";',
|
||||
'import {agentSkillTools} from "./open-sse/mcp-server/tools/agentSkillTools.ts";',
|
||||
'import {githubSkillTools} from "./open-sse/mcp-server/tools/githubSkillTools.ts";',
|
||||
'import {poolTools} from "./open-sse/mcp-server/tools/poolTools.ts";',
|
||||
'import {gamificationTools} from "./open-sse/mcp-server/tools/gamificationTools.ts";',
|
||||
'import {pluginTools} from "./open-sse/mcp-server/tools/pluginTools.ts";',
|
||||
'import {notionTools} from "./open-sse/mcp-server/tools/notionTools.ts";',
|
||||
'import {obsidianTools} from "./open-sse/mcp-server/tools/obsidianTools.ts";',
|
||||
'import {compressionTools} from "./open-sse/mcp-server/tools/compressionTools.ts";',
|
||||
"const cols={MCP_TOOLS,memoryTools,skillTools,agentSkillTools,githubSkillTools,poolTools,",
|
||||
"gamificationTools,pluginTools,notionTools,obsidianTools,compressionTools};",
|
||||
"const sc=new Set();",
|
||||
"for(const col of Object.values(cols))for(const t of Object.values(col))",
|
||||
"for(const x of (t?.scopes||[]))sc.add(x);",
|
||||
"const t=computeFreeModelTotals();const cli=Object.values(CLI_TOOLS);",
|
||||
"const by=(c)=>cli.filter(x=>x.category===c).length;",
|
||||
'console.log("@@"+JSON.stringify({freeSteady:t.steadyRecurringTokens,',
|
||||
"freeFirst:t.firstMonthRealisticTokens,freePools:t.poolCount,engines:ENGINE_IDS.length,",
|
||||
"cliTotal:cli.length,cliCode:by('code'),cliAgent:by('agent'),",
|
||||
"mcpTools:countUniqueMcpTools(cols),mcpScopes:sc.size}));",
|
||||
].join("");
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "docs-counts-"));
|
||||
try {
|
||||
const r = spawnSync(process.execPath, ["--import", "tsx/esm", "-e", script], {
|
||||
cwd: ROOT,
|
||||
encoding: "utf8",
|
||||
timeout: 180000,
|
||||
env: { ...process.env, DATA_DIR: tmp, APP_LOG_LEVEL: "silent" },
|
||||
});
|
||||
if (r.status !== 0 || !r.stdout) return null;
|
||||
const line = r.stdout.split("\n").find((l) => l.startsWith("@@"));
|
||||
return line ? JSON.parse(line.slice(2)) : null;
|
||||
} catch {
|
||||
return null;
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
// The docs publish a rounded aggregate headline ("~1.4B"). Accept a claim that rounds to
|
||||
// the live value at one decimal place; reject a stale one. Tolerance is tight on purpose:
|
||||
// this gate exists so the headline cannot drift upward unnoticed.
|
||||
//
|
||||
// Only the AGGREGATE headline is validated, via an explicit whitelist. These files also
|
||||
// carry figures that are legitimately not the headline and must never trip the gate:
|
||||
// the theoretical ceiling ("would read ~10B; not published"), the historical "previous
|
||||
// ~1.94B", and per-model rows ("mistral … ~1.00B"). A whitelist keeps those safe without
|
||||
// having to enumerate every contrastive phrasing.
|
||||
const HEADLINE_AFTER = /^\s*(?:documented\s+)?free tokens|^\s*in (?:your|the) first month/i;
|
||||
const HEADLINE_BEFORE = /(recurring grant[^|]*\|\s*\**|signup credits[^|]*\|\s*\**|up to\s*)$/i;
|
||||
|
||||
export function extractHeadlineClaims(content) {
|
||||
const claims = [];
|
||||
for (const m of content.matchAll(/~(\d+(?:\.\d+)?)B/g)) {
|
||||
const after = content.slice(m.index + m[0].length, m.index + m[0].length + 80);
|
||||
const before = content.slice(Math.max(0, m.index - 80), m.index);
|
||||
if (!HEADLINE_AFTER.test(after) && !HEADLINE_BEFORE.test(before)) continue;
|
||||
claims.push({ value: Number(m[1]), text: m[0] });
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
export function checkFreeTierHeadline(content, totals) {
|
||||
const claims = extractHeadlineClaims(content);
|
||||
if (!claims.length) return { ok: true, detail: "no aggregate free-tier headline in this file" };
|
||||
const steady = totals.s / 1e9;
|
||||
const first = totals.m / 1e9;
|
||||
const stale = claims.filter(
|
||||
(c) => Math.abs(c.value - steady) >= 0.05 && Math.abs(c.value - first) >= 0.05
|
||||
);
|
||||
if (!stale.length)
|
||||
return { ok: true, detail: `${claims.length} headline claim(s) match the live catalog` };
|
||||
return {
|
||||
ok: false,
|
||||
detail:
|
||||
`stale headline ${[...new Set(stale.map((c) => c.text))].join(", ")} — live catalog ` +
|
||||
`computes ~${steady.toFixed(2)}B steady / ~${first.toFixed(2)}B first month`,
|
||||
};
|
||||
}
|
||||
|
||||
// --- Generic numeric-claim gate ---------------------------------------------
|
||||
// Same principle as the free-tier headline: docs legitimately carry numbers that are
|
||||
// NOT the aggregate being gated (per-module tool counts like "Memory tool definitions
|
||||
// (3 tools)", the CLI catalog's "33 tools (25 CLI Code's ...)" next to the MCP total).
|
||||
// So every check declares what to skip rather than assuming any "N tools" is the claim.
|
||||
export function extractNumberClaims(content, { pattern, skipBefore, skipAfter }) {
|
||||
const claims = [];
|
||||
for (const m of content.matchAll(pattern)) {
|
||||
const before = content.slice(Math.max(0, m.index - 40), m.index);
|
||||
const after = content.slice(m.index + m[0].length, m.index + m[0].length + 40);
|
||||
if (skipBefore && skipBefore.test(before)) continue;
|
||||
if (skipAfter && skipAfter.test(after)) continue;
|
||||
claims.push({ value: Number(m[1]), text: m[0].trim() });
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
export function makeNumberClaimValidator(expected, opts) {
|
||||
return (content) => {
|
||||
const claims = extractNumberClaims(content, opts);
|
||||
if (!claims.length) return { ok: true, detail: `no ${opts.what} claim in this file` };
|
||||
const stale = claims.filter((c) => c.value !== expected);
|
||||
if (!stale.length)
|
||||
return { ok: true, detail: `${claims.length} ${opts.what} claim(s) match the code` };
|
||||
return {
|
||||
ok: false,
|
||||
detail:
|
||||
`stale ${opts.what}: ${[...new Set(stale.map((c) => `"${c.text}"`))].join(", ")} — ` +
|
||||
`code has ${expected}`,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function buildChecks() {
|
||||
return [
|
||||
{
|
||||
@@ -131,6 +268,70 @@ export function buildChecks() {
|
||||
strict: true,
|
||||
files: ["docs/README.md", "docs/guides/I18N.md", "AGENTS.md"],
|
||||
},
|
||||
...(() => {
|
||||
const f = readCodeFacts();
|
||||
if (!f)
|
||||
return [
|
||||
{
|
||||
label: "Code-derived counts",
|
||||
actual: 0,
|
||||
docKey: "code facts",
|
||||
strict: false,
|
||||
files: [],
|
||||
},
|
||||
];
|
||||
const claim = (expected, what, opts, files) => ({
|
||||
label: `${what} (live code)`,
|
||||
actual: expected,
|
||||
docKey: what,
|
||||
strict: true,
|
||||
files,
|
||||
validate: makeNumberClaimValidator(expected, { what, ...opts }),
|
||||
});
|
||||
return [
|
||||
{
|
||||
label: "Free-tier headline (live catalog)",
|
||||
actual: `~${(f.freeSteady / 1e9).toFixed(2)}B steady / ${f.freePools} pools`,
|
||||
docKey: "free-tier headline",
|
||||
strict: true,
|
||||
files: ["README.md", "docs/reference/FREE_TIERS.md"],
|
||||
validate: (content) =>
|
||||
checkFreeTierHeadline(content, { s: f.freeSteady, m: f.freeFirst }),
|
||||
},
|
||||
claim(
|
||||
f.engines,
|
||||
"compression engines",
|
||||
{ pattern: /(\d+)[-\s](?:engine stack|composable engines|stacked engines)/gi },
|
||||
["README.md"]
|
||||
),
|
||||
claim(
|
||||
f.mcpTools,
|
||||
"MCP tools",
|
||||
{
|
||||
pattern: /(\d+) tools/gi,
|
||||
// per-module rows ("Memory tool definitions (3 tools)") and the CLI catalog
|
||||
// total ("33 tools (25 CLI Code's …)") are not the MCP aggregate
|
||||
// per-module rows read "… tool definitions (N tools" / "… management tools
|
||||
// (N tools" — the word tool(s)/definitions sits right before the paren. The
|
||||
// aggregate ("MCP Server (104 tools", "all 104 tools") never does.
|
||||
skipBefore: /(tools?|definitions?)\s*\(\s*$/i,
|
||||
skipAfter: /^\s*\(\d+ CLI/,
|
||||
},
|
||||
["README.md", "CLAUDE.md", "AGENTS.md", "docs/frameworks/MCP-SERVER.md"]
|
||||
),
|
||||
claim(f.mcpScopes, "MCP scopes", { pattern: /(\d+) scopes/gi }, [
|
||||
"README.md",
|
||||
"CLAUDE.md",
|
||||
"AGENTS.md",
|
||||
]),
|
||||
claim(
|
||||
f.cliTotal,
|
||||
"CLI tools",
|
||||
{ pattern: /(\d+) tools(?=\s*\(\d+ CLI)/gi },
|
||||
["README.md"]
|
||||
),
|
||||
];
|
||||
})(),
|
||||
{
|
||||
label: "Executors count",
|
||||
actual: countFiles("open-sse/executors"),
|
||||
|
||||
@@ -705,7 +705,7 @@ export default function OAuthModal({
|
||||
// (`{user, accessToken, expires}`), not just the bare token (#6636).
|
||||
if (provider === "codex" && looksLikeCodexSessionJson(callbackUrl)) {
|
||||
const result = parseCodexSessionJson(JSON.parse(callbackUrl.trim()));
|
||||
if (!result.ok) {
|
||||
if (result.ok === false) {
|
||||
setError(result.error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3212,8 +3212,8 @@
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"nonStream": "https://www.notion.so/api/v3/runInferenceTranscript",
|
||||
"stream": "https://www.notion.so/api/v3/runInferenceTranscript"
|
||||
"nonStream": "https://app.notion.com/api/v3/runInferenceTranscript",
|
||||
"stream": "https://app.notion.com/api/v3/runInferenceTranscript"
|
||||
}
|
||||
},
|
||||
"nous-research": {
|
||||
|
||||
@@ -98,3 +98,101 @@ test("the gate exits 0 against the current (synced) repo state", () => {
|
||||
// Throws if exit code is non-zero; current docs are synced so this must pass.
|
||||
assert.doesNotThrow(() => execFileSync("node", [GATE], { encoding: "utf8", stdio: "pipe" }));
|
||||
});
|
||||
|
||||
// --- Free-tier headline gate ------------------------------------------------
|
||||
// Regression guard for the drift found in the v3.8.49 README audit: the README
|
||||
// headlined ~1.6B for seven releases after the catalog had already been corrected
|
||||
// down to 1.37B, because no gate watched that number.
|
||||
import {
|
||||
checkFreeTierHeadline,
|
||||
extractHeadlineClaims,
|
||||
} from "../../scripts/check/check-docs-counts-sync.mjs";
|
||||
|
||||
const checkHeadline = checkFreeTierHeadline as (
|
||||
content: string,
|
||||
totals: { s: number; m: number; p: number }
|
||||
) => { ok: boolean; detail: string };
|
||||
const extractClaims = extractHeadlineClaims as (
|
||||
content: string
|
||||
) => { value: number; text: string }[];
|
||||
|
||||
const TOTALS = { s: 1_371_725_000, m: 1_998_225_000, p: 39 };
|
||||
|
||||
test("free-tier gate accepts a headline that rounds to the live catalog", () => {
|
||||
assert.equal(checkHeadline("~1.4B free tokens per month", TOTALS).ok, true);
|
||||
assert.equal(checkHeadline("up to ~2.0B in the first month", TOTALS).ok, true);
|
||||
});
|
||||
|
||||
test("free-tier gate rejects the stale headlines this audit found", () => {
|
||||
for (const stale of ["~1.6B free tokens/mo", "~1.54B free tokens per month"]) {
|
||||
const r = checkHeadline(stale, TOTALS);
|
||||
assert.equal(r.ok, false, `expected ${stale} to be rejected`);
|
||||
assert.match(r.detail, /live catalog computes/);
|
||||
}
|
||||
assert.equal(checkHeadline("up to ~2.1B in the first month", TOTALS).ok, false);
|
||||
});
|
||||
|
||||
test("free-tier gate ignores non-headline figures", () => {
|
||||
// The theoretical ceiling, the historical value and per-model rows are legitimate
|
||||
// and must never trip the gate — that is why the extractor is a whitelist.
|
||||
const noise =
|
||||
"counting every rate limit 24/7 would read ~10B; not published. " +
|
||||
"Why this dropped from the previous ~1.94B. | `mistral` | recurring | ~1.00B |";
|
||||
assert.deepEqual(extractClaims(noise), []);
|
||||
assert.equal(checkHeadline(noise, TOTALS).ok, true);
|
||||
});
|
||||
|
||||
test("free-tier gate passes when a file carries no headline at all", () => {
|
||||
assert.equal(checkHeadline("no figures here", TOTALS).ok, true);
|
||||
});
|
||||
|
||||
// --- Generic numeric-claim gate (engines / MCP tools / scopes / CLI) --------
|
||||
// Extends the same drift guard to the counts that silently drifted in v3.8.49:
|
||||
// 10→11 engines, 94→104 MCP tools, 30→31 scopes, 26→33 CLI tools.
|
||||
import { makeNumberClaimValidator } from "../../scripts/check/check-docs-counts-sync.mjs";
|
||||
|
||||
const makeValidator = makeNumberClaimValidator as (
|
||||
expected: number,
|
||||
opts: { what: string; pattern: RegExp; skipBefore?: RegExp; skipAfter?: RegExp }
|
||||
) => (content: string) => { ok: boolean; detail: string };
|
||||
|
||||
test("MCP-tools gate accepts the aggregate and rejects a stale one", () => {
|
||||
const v = makeValidator(104, {
|
||||
what: "MCP tools",
|
||||
pattern: /(\d+) tools/gi,
|
||||
skipBefore: /(tools?|definitions?)\s*\(\s*$/i,
|
||||
skipAfter: /^\s*\(\d+ CLI/,
|
||||
});
|
||||
assert.equal(v("MCP Server (104 tools)").ok, true);
|
||||
assert.equal(v("with 104 tools total").ok, true);
|
||||
assert.equal(v("MCP Server (94 tools)").ok, false);
|
||||
});
|
||||
|
||||
test("MCP-tools gate ignores per-module counts and the CLI catalog total", () => {
|
||||
const v = makeValidator(104, {
|
||||
what: "MCP tools",
|
||||
pattern: /(\d+) tools/gi,
|
||||
skipBefore: /(tools?|definitions?)\s*\(\s*$/i,
|
||||
skipAfter: /^\s*\(\d+ CLI/,
|
||||
});
|
||||
// "Memory tool definitions (3 tools)" and "33 tools (25 CLI Code's)" are not the MCP total
|
||||
assert.equal(v("Memory tool definitions (3 tools)").ok, true);
|
||||
assert.equal(v("management tools (8 tools)").ok, true);
|
||||
assert.equal(v("all 33 tools (25 CLI Code's + 8 CLI Agents)").ok, true);
|
||||
});
|
||||
|
||||
test("compression-engines and CLI-tools gates catch their v3.8.49 drift", () => {
|
||||
const eng = makeValidator(11, {
|
||||
what: "compression engines",
|
||||
pattern: /(\d+)[-\s](?:engine stack|composable engines|stacked engines)/gi,
|
||||
});
|
||||
assert.equal(eng("11-engine stack").ok, true);
|
||||
assert.equal(eng("10-engine stack").ok, false);
|
||||
|
||||
const cli = makeValidator(33, {
|
||||
what: "CLI tools",
|
||||
pattern: /(\d+) tools(?=\s*\(\d+ CLI)/gi,
|
||||
});
|
||||
assert.equal(cli("all 33 tools (25 CLI Code's)").ok, true);
|
||||
assert.equal(cli("all 26 tools (25 CLI Code's)").ok, false);
|
||||
});
|
||||
|
||||
@@ -15,14 +15,19 @@ test("web session credential metadata covers every web-cookie provider", () => {
|
||||
});
|
||||
|
||||
test("web session credential metadata identifies cookie, token, and no-auth providers", () => {
|
||||
// Grok needs BOTH sso and sso-rw cookies (#3180)
|
||||
assert.deepEqual(webSessionCredentials.getWebSessionCredentialRequirement("grok-web"), {
|
||||
kind: "cookie",
|
||||
credentialName: "sso + sso-rw",
|
||||
placeholder: "sso=...; sso-rw=...",
|
||||
acceptsFullCookieHeader: true,
|
||||
storageKeys: ["cookie", "sso", "sso-rw"],
|
||||
});
|
||||
// Grok needs BOTH sso and sso-rw cookies (#3180). #7567 added the proactive
|
||||
// cf_clearance/User-Agent hint — assert its intent, don't freeze operator copy.
|
||||
{
|
||||
const req = webSessionCredentials.getWebSessionCredentialRequirement("grok-web");
|
||||
assert.ok(req && req.kind === "cookie");
|
||||
assert.equal(req.credentialName, "sso + sso-rw");
|
||||
assert.equal(req.placeholder, "sso=...; sso-rw=...");
|
||||
assert.equal(req.acceptsFullCookieHeader, true);
|
||||
assert.deepEqual(req.storageKeys, ["cookie", "sso", "sso-rw"]);
|
||||
assert.equal(req.hintKey, "grokWebCookieHint");
|
||||
assert.ok(typeof req.hintFallback === "string" && /cf_clearance/.test(req.hintFallback));
|
||||
assert.ok(/User-Agent/.test(req.hintFallback));
|
||||
}
|
||||
assert.deepEqual(webSessionCredentials.getWebSessionCredentialRequirement("copilot-web"), {
|
||||
kind: "token",
|
||||
credentialName: "access_token",
|
||||
|
||||