From 1085514c56bbaf41a632f3ca56d4564d07366afa Mon Sep 17 00:00:00 2001 From: Chirag Singhal <76880977+chirag127@users.noreply.github.com> Date: Wed, 1 Jul 2026 07:22:44 +0530 Subject: [PATCH 01/21] Fix grammatical errors in readme (#5738) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d17a351455..4f6d5abb86 100644 --- a/README.md +++ b/README.md @@ -292,7 +292,7 @@ Result: 4 layers of fallback = zero downtime > Recent highlights from **v3.8.20 → v3.8.41**. Full history in [`CHANGELOG.md`](CHANGELOG.md). -- **⚖️ Quota-Share routing** — a dedicated combo strategy that spreads load across accounts by _available quota_: Deficit-Round-Robin scheduling, per-connection `max_concurrent` with cooldown-wait queueing, multi-window usage buckets (5h / 7d / per-model), per-(key,model) caps, session stickiness for prompt-cache integrity, and proactive saturation from upstream token-usage headers. → [Resilience Guide](docs/architecture/RESILIENCE_GUIDE.md) +- **⚖️ Quota-Share routing** — a dedicated combo strategy that spreads load across accounts by _available quota_: Deficit-Round-Robin scheduling, per-connection `max_concurrent` with cooldown-wait queueing, multi-window usage buckets (5h / 7d / per-model), per-(key, model) caps, session stickiness for prompt-cache integrity, and proactive saturation from upstream token-usage headers. → [Resilience Guide](docs/architecture/RESILIENCE_GUIDE.md) - **🤖 One-command CLI/agent setup** — a dedicated `setup-*` command configures each coding tool to route through OmniRoute (Claude Code, Codex, Cline, Continue, Cursor, Roo Code, Kilo Code, Crush, Goose, Qwen Code, Aider, OpenCode); `omniroute launch` / `omniroute launch-codex` are zero-config launchers. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md) - **🛰️ Remote mode** — drive a remote OmniRoute from any machine with scoped access tokens (`omniroute connect` / `omniroute contexts` / `omniroute tokens`), plus an `omniroute login antigravity` helper that runs Google "native/desktop" OAuth on your own machine and pastes a credential blob into a remote/VPS install (where the loopback redirect is unreachable). → [Remote Mode](docs/guides/REMOTE-MODE.md) - **🧭 Smarter auto-routing** — OpenRouter-style `auto/:` combos (e.g. `auto/coding:fast`, `auto/reasoning:pro`), a **Fusion** strategy (fan out to a panel of models in parallel, then synthesize via a judge), **task-aware routing** (best-fit connection per task type), per-request `X-Route-Model` override, live Arena-ELO + models.dev model intelligence, per-step account allowlists, provider-wildcard combo steps, nested combo-ref execution, sticky weighted selection, and `web_search`-aware routing. → [Auto-Combo](docs/routing/AUTO-COMBO.md) @@ -301,7 +301,7 @@ Result: 4 layers of fallback = zero downtime - **💸 Cost telemetry everywhere** — `X-OmniRoute-*` cost/usage headers on every endpoint (including media), a non-token cost engine, a cache-HIT `X-OmniRoute-Cost-Saved` header, and per-key USD spend quotas. → [API Reference](docs/reference/API_REFERENCE.md) - **🧠 Memory you control** — opt-in int8 vector quantization (Qdrant + sqlite-vec), memory off by default, and a per-request `x-omniroute-no-memory` header. → [Memory](docs/frameworks/MEMORY.md) - **🛡️ Security** — a prompt-injection guard across every LLM route (backed by a red-team suite), plus a free DuckDuckGo last-resort web search. → [Guardrails](docs/security/GUARDRAILS.md) -- **🤝 More providers & agents** — Cursor Cloud Agent (a 4th cloud agent), CodeBuddy CN (`copilot.tencent.com`), a Google Flow video-generation provider, new gateways **DGrid** and **Pioneer AI** (Fastino Labs), inbound **xAI Grok** translators plus **Grok Build (xAI)** with an OAuth import-token flow, GPT-4 / GPT-4o-mini on the GitHub Copilot provider, multi-model **Factory Droid**, **ZenMux Free** (session-cookie free tier), **Alibaba DashScope** text-to-video (`wan2.7-t2v`), a refreshed 236-provider catalog (OrcaRouter, Wafer AI, OpenAdapter, dit.ai, TokenRouter, …), Vertex AI media generation (speech / transcription / music / video), and one-click account import from CLIProxyAPI (`~/.cli-proxy-api/`). → [Providers](docs/reference/PROVIDER_REFERENCE.md) +- **🤝 More providers & agents** — Cursor Cloud Agent (a 4th cloud agent), CodeBuddy CN (`copilot.tencent.com`), a Google Flow video-generation provider, new gateways **DGrid** and **Pioneer AI** (Fastino Labs), inbound **xAI Grok** translators plus **Grok Build (xAI)** with an OAuth import-token flow, GPT-4 / GPT-4o-mini on the GitHub Copilot provider, multi-model **Factory Droid**, **ZenMux Free** (session-cookie free tier), **Alibaba DashScope** text-to-video (`wan2.7-t2v`), a refreshed 236-provider catalog (OrcaRouter, Wafer AI, OpenAdapter, dit.ai, TokenRouter, …), Vertex AI media generation (speech/transcription/music/video), and one-click account import from CLIProxyAPI (`~/.cli-proxy-api/`). → [Providers](docs/reference/PROVIDER_REFERENCE.md) - **⚡ Local performance & infra** — a one-click local Redis launcher (`omniroute redis up`, plus a dashboard Redis panel), one-click **Cloudflare Workers** and **Deno Deploy** relay deployers wired into the proxy pool, and an optional Bifrost Go sidecar that offloads the hottest relay path (`BIFROST_BASE_URL`, with automatic fallback to the TypeScript path on timeout) — now with a relay-backend selector (`OMNIROUTE_RELAY_BACKEND=ts|bifrost|auto`) so the `/v1/relay` endpoint stays the stable surface while choosing the fastest backend internally. → [Environment](docs/reference/ENVIRONMENT.md)
@@ -479,7 +479,7 @@ claude mcp add-server omniroute --type http --url http://localhost:20128/api/mcp -> **Why use many token when few token do trick?** Every request passes through OmniRoute's compression pipeline **transparently** — no client changes. It's now a **stack of 9 composable engines** that run in order and mix & match per routing combo — building on ideas from [RTK](https://github.com/rtk-ai/rtk), [Caveman](https://github.com/JuliusBrussee/caveman) (⭐ 51K+), [LLMLingua-2](https://github.com/microsoft/LLMLingua), and [Troglodita](https://github.com/leninejunior/troglodita) (PT-BR). +> **Why use many tokens when few tokens do the trick?** Every request passes through OmniRoute's compression pipeline **transparently** — no client changes. It's now a **stack of 9 composable engines** that run in order and mix & match per routing combo — building on ideas from [RTK](https://github.com/rtk-ai/rtk), [Caveman](https://github.com/JuliusBrussee/caveman) (⭐ 51K+), [LLMLingua-2](https://github.com/microsoft/LLMLingua), and [Troglodita](https://github.com/leninejunior/troglodita) (PT-BR). ### 🧱 The 9-engine stack From b9d717f3c96f0b784207d042b6c7c926e99f90e1 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Wed, 1 Jul 2026 04:34:22 -0300 Subject: [PATCH 02/21] fix(dashboard): neutral badge for unsupported validation + clickable OAuth error links (#5442, #5486) (#5795) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #5442 LMArena (and any provider with no live validator) returns { unsupported: true } from /api/providers/validate and Save succeeds, but the Add-API-Key modal only had success/failed states so it rendered a red 'Invalid' badge. Add an 'unsupported' result → neutral info 'N/A' badge via the pure leaf validationBadgeProps(); both validate handlers now map data.unsupported to it. - #5486 GitLab Duo's OAuth setup error embeds a registration URL (gitlab.com/-/profile/applications) but the OAuth error step rendered it as dead red text. New LinkifiedText component (+ pure ReDoS-safe linkify util) makes any http(s) URL in an OAuth error clickable; the GitLab Duo backend message already carries the full setup steps. Regression guards: tests/unit/validation-badge-unsupported-5442.test.ts, tests/unit/oauth-error-linkify-5486.test.ts. Frozen god-files kept within cap (AddApiKeyModal 868/868, OAuthModal 968/969). --- .../[id]/components/modals/AddApiKeyModal.tsx | 9 +++-- .../providers/[id]/providerPageHelpers.ts | 16 ++++++++ src/shared/components/LinkifiedText.tsx | 30 +++++++++++++++ src/shared/components/OAuthModal.tsx | 5 ++- src/shared/utils/linkify.ts | 37 +++++++++++++++++++ tests/unit/oauth-error-linkify-5486.test.ts | 35 ++++++++++++++++++ .../validation-badge-unsupported-5442.test.ts | 25 +++++++++++++ 7 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 src/shared/components/LinkifiedText.tsx create mode 100644 src/shared/utils/linkify.ts create mode 100644 tests/unit/oauth-error-linkify-5486.test.ts create mode 100644 tests/unit/validation-badge-unsupported-5442.test.ts diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx index 973d79ff1c..704a3ec7e9 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx @@ -19,6 +19,7 @@ import { normalizeAndValidateHttpBaseUrl, extractCommandCodeCredentialInput, providerText, + validationBadgeProps, type CommandCodeAuthFlowState, } from "../../providerPageHelpers"; import { getWebSessionCredentialRequirement } from "../../webSessionCredentials"; @@ -202,7 +203,7 @@ export default function AddApiKeyModal({ }); const data = await res.json(); const ok = !!data.valid; - setValidationResult(ok ? "success" : "failed"); + setValidationResult(ok ? "success" : data.unsupported ? "unsupported" : "failed"); // #5088: surface the detailed reason the backend returns (e.g. a TLS/EACCES // environment error for claude-web/chatgpt-web) instead of only a bare // "invalid" badge — otherwise the real cause is hidden and users are stuck. @@ -276,7 +277,7 @@ export default function AddApiKeyModal({ if (!isValid && data.error) { validationError = data.error; } - setValidationResult(isValid ? "success" : "failed"); + setValidationResult(isValid ? "success" : isUnsupported ? "unsupported" : "failed"); } catch { setValidationResult("failed"); } finally { @@ -674,8 +675,8 @@ export default function AddApiKeyModal({ /> )} {validationResult && ( - - {validationResult === "success" ? t("valid") : t("invalid")} + + {t(validationBadgeProps(validationResult).labelKey)} )} {saveError && ( diff --git a/src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts b/src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts index 23ea2fd1f5..500732e067 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts +++ b/src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts @@ -118,6 +118,22 @@ export function providerText( return fallback; } +/** + * #5442 — Badge variant + i18n label key for an add-credential validation result. + * A provider with no live validator returns `unsupported` (Save still succeeds); + * previously the modal only had success/failed states, so it rendered a red + * "Invalid" badge for those providers even though saving worked (LMArena, PiAPI…). + * "unsupported" now maps to a neutral `info` badge ("N/A"), not "Invalid". + */ +export function validationBadgeProps(result: string): { + variant: "success" | "error" | "info"; + labelKey: string; +} { + if (result === "success") return { variant: "success", labelKey: "valid" }; + if (result === "unsupported") return { variant: "info", labelKey: "notApplicable" }; + return { variant: "error", labelKey: "invalid" }; +} + /** A single model's outcome from a `/api/models/test-all` response. */ export interface TestAllModelOutcome { status: "ok" | "error"; diff --git a/src/shared/components/LinkifiedText.tsx b/src/shared/components/LinkifiedText.tsx new file mode 100644 index 0000000000..c798a049a9 --- /dev/null +++ b/src/shared/components/LinkifiedText.tsx @@ -0,0 +1,30 @@ +import { Fragment } from "react"; +import { linkifyText } from "@/shared/utils/linkify"; + +/** + * #5486 — Render a string with any embedded http(s) URLs as clickable links. + * Used by the OAuth error step so setup instructions (e.g. GitLab Duo's + * "register an OAuth application at https://gitlab.com/-/profile/applications …") + * are actionable instead of dead text. Links open in a new tab with a safe rel. + */ +export default function LinkifiedText({ text }: { text: string | null | undefined }) { + return ( + <> + {linkifyText(text || "").map((seg, i) => + seg.href ? ( + + {seg.text} + + ) : ( + {seg.text} + ) + )} + + ); +} diff --git a/src/shared/components/OAuthModal.tsx b/src/shared/components/OAuthModal.tsx index dac9e6fb22..0766a31f68 100644 --- a/src/shared/components/OAuthModal.tsx +++ b/src/shared/components/OAuthModal.tsx @@ -5,6 +5,7 @@ import { useTranslations } from "next-intl"; import Modal from "./Modal"; import Button from "./Button"; import Input from "./Input"; +import LinkifiedText from "./LinkifiedText"; import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard"; import { parseResponseBody, getErrorMessage } from "@/shared/utils/api"; import { isCredentialBlob, submitCredentialBlob } from "@/shared/components/oauthBlobSubmit"; @@ -948,7 +949,9 @@ export default function OAuthModal({ error

{t("error")}

-

{error}

+

+ +