Releases: vibecoder11200/9router
Release list
v0.6.57
Full Changelog: v0.6.56...v0.6.57
v0.6.56
v0.6.55
v0.6.54
v0.6.54 (2026-09-28)
Two usage-dashboard fixes: the live topology now lights up for noAuth/free providers, and the By Provider / Top Models breakdown charts render correctly in both themes.
Fixed — topology live animation never lit for free/noAuth providers
getActiveRequests()derivedactiveRequestsonly frompendingRequests.byAccount, and pending tracking only populatesbyAccountwhen the request carries aconnectionId— so live requests to connection-less (noAuth/free) providers never reached the SSE stream and the usage topology stayed dark for exactly those providers. Same behavior since upstream 0.4.x.- Connection-less pending requests are now surfaced from
byModelwith aFree (no connection)account label. Regression-tested (tests/unit/usage-active-requests.test.js, fails 3/3 without the fix) and verified E2E against a live server: a realoc/mimo-v2.5-freestream went from an always-emptyactiveRequeststo a lit electric edge for the whole request (0/77 → 82/82 lit DOM samples). - The topology's provider list was fetched once on mount, so providers added after the page loaded stayed invisible until reload — now re-polled every 60s.
Fixed — By Provider / Top Models charts vs dark & light mode
- The Tokens/Requests segmented toggle used purged design tokens (
bg-bg-subtle,bg-bg-hover) → transparent container, no hover feedback. Now the same tokens as the Tokens & Cost chart (bg-surface-2,hover:bg-black/5 dark:hover:bg-white/5). - Tooltip text fell back to recharts' default black (the Bar had no Bar-level
fill; colors lived on per-indexCells) → invisible black-on-dark tooltip in dark mode. The tooltip now forcesvar(--color-text-main)for item and label, truncates the provider label at 24 chars (node ids are UUID-length), gains a proper swatch color from the Bar-level fill, and uses a theme-neutral hover cursor band (#888@ 0.15) instead of recharts' harsh default. --color-primary-foregroundwas referenced across the dashboard but never defined — active chips silently inherited per-theme text color (washed-out coral chips in dark mode). Now defined once (#2b1610, ~5:1 contrast on the coral#E56A4Aprimary in both themes), standardizing every coral chip app-wide.
v0.6.53 — Example Run works under Require-API-Key + System One auth gate fix
v0.6.53 (2026-09-28)
Makes the dashboard Example/Test Run buttons work under Require-API-Key = on — the same trusted path the provider-page Test button uses — and fixes the System One endpoint's auth gate that upstream shipped without the fork's internal-caller hardening.
Fixed — System One auth gate (upstream regression vs fork doctrine)
- Upstream's new
handleSystemonecopied the old requireApiKey gate without the fork'sisTrustedInternalRequestbypass (loopback +x-9r-cli-token) that every other media endpoint has — so the provider-page Test button and any internal caller got401 Missing API keyfor systemone models even though they authenticate as trusted-internal. Added the canonical gate (bypass +enforceKeyBudget, matchingfetch/search/stt/tts/embeddings/imageGeneration/videoGeneration/chat).
Added — internal example runner (/api/example/run)
- The Example cards' Run button is a browser call to the public
/v1/*endpoints, which Require-API-Key gates behind a client key — and since S7 the dashboard can't prefill one (masked display values only). New session-authed internal proxy replays the request against the server's own loopback listener with the CLI token, streaming the upstream response (JSON / binary / SSE) back untouched. All five example/test cards (Generic — image/video/music/imageToText/systemone, TTS, STT, Embedding, Combo detail) now:- No key pasted → run via the internal proxy (works regardless of Require-API-Key) — verified E2E against a live server with requireApiKey=on:
oc/jev-1.13-freereturns a real Jev decision. - RAW key pasted → still exercises the real public endpoint exactly like the curl snippet.
- No key pasted → run via the internal proxy (works regardless of Require-API-Key) — verified E2E against a live server with requireApiKey=on:
- The proxy is deliberately not a general relay: POST only, exact-path whitelist from
MEDIA_PROVIDER_KINDS, target always this process's own origin, dashboard session required (proxy-enforced + explicit check). Verified: no session → 401, non-whitelisted path → 400, direct public call without key → still 401 (the gate for real off-box clients is unchanged).
v0.6.52 — masked-key proofing for example cards + drop NEW badges
v0.6.52 (2026-09-28)
Fork-polish release on top of the v0.5.91 sync: drops upstream's NEW badges and fixes the masked-API-key bug that broke every dashboard Example/Test "Run" button (surfaced by the new System One card).
Removed
- NEW badges from upstream v0.5.91: sidebar Media Providers expander tag, per-kind (System One) tag, and the 9Remote tag — this fork doesn't ship promo badges.
Fixed — masked API keys never reach a credential slot (S7 follow-up)
- All five dashboard example/test cards (Generic — image/video/music/imageToText/System One, TTS, STT, Embedding, Combo detail) prefilled their API-Key field from
/api/keys, which since S7 returns the MASKED display value (sk-{id}-••••{last4}). The Run button builtAuthorization: Bearer sk-…••••…and the browser threwFailed to read the 'headers' property from 'RequestInit': String contains non ISO-8859-1 code pointbefore any request was sent — every media-provider example page was affected, not just System One.- The prefill is gone; the key field is now a RAW-key input (empty = local mode, no Authorization header — works with Require-API-Key off, exactly like the server-side model ping's documented behavior). With Require-API-Key on, paste the raw key shown once at creation.
- A guard rejects pasted masked values (
•) with a clear message instead of the cryptic fetch crash.
- Audited every other API-key flow that arrived with v0.5.91: the provider-page Test button and System One probe go through the server-side ping (
x-9r-cli-token, S7-safe); Codex profiles take keys fromApiKeySelect(already S7-filtered); the new aggregator providers use per-connection credentials — all safe.
v0.6.51 — upstream v0.5.91 sync (OpenCode Zen, Qoder CN, aggregators, MiMo v2.6, System One, analytics)
v0.6.51 (2026-09-28)
Upstream-sync release: merges upstream v0.5.91 (79 commits, v0.5.81 → v0.5.91 — includes the v0.5.85, v0.5.86, and v0.5.91 releases). All fork subsystems preserved (xray multi-subscription + binary updates, genspark-web/ds2api/gemini-web/orcarouter/totu-ai providers, masked-key S7 plumbing, strict-proxy pools, archive encryption, key portability). Upstream README changes were skipped per fork policy — this fork keeps its own README.
Upstream highlights now in the fork
New providers & models
- OpenCode Zen (
opencode-zen, aliasocz): free-tier provider with its own executor, registry entry, usage module, and fingerprint gating. - Qoder CN (
qoder-cn, aliasqdcn): qoder.com.cn with OAuth flow, COSY protocol, and CN gateway routing; shares Qoder's executor/usage path with region-correct catalogs. - Aggregators: Token Harbor, dahl, atria, agnes, bai (OpenAI-compatible).
- Claude Opus 5.5 support (spoofed CLI version bumped to 2.1.280); Codex GPT-6 Sol and Luna; Xiaomi MiMo v2.6 pro/flash/pro-ultraspeed with five account clusters (cn/sgp/ams/ru/in) and server-assisted desktop login for headless/Docker; OpenCode Go complete 40-model Go catalog with auto-fetch + family endpoint regex; Cline
cline-free/*tier priced at zero; Hermes multi-role model config (delegation + auxiliary slots).
Features
- System One:
/v1/systemonedecision endpoint (Jev models), sidebar + Media Providers integration with probe testing. - CLI tools: multiple model profiles for Codex CLI; dynamic configuration + logos for Pi, OMP, Crush, ForgeCode, Smelt, CodeWhale; Codex settings refresh after apply; existing
ANTHROPIC_AUTH_TOKENpreserved when applying Claude settings. - Analytics/Usage: Requests mode + provider/model breakdown charts, All Time period, bounded 2-day lastUsed overlay, per-API-key usage attribution keyed by full key (team keys no longer collide), free limit resets shown/redeemed for cc accounts.
- Model capabilities: capability metadata on
/v1/models, aggregated across combo targets; vision adapter models in an ordered combos table. - Claude:
x-claude-code-session-idforwarded on OAuth requests; clientanthropic-betaflags merged + rate-limit headers forwarded; thinking text returned to OpenAI-format clients; upstream response headers propagated onto SSE/error responses. - i18n: React text rewrites translated via characterData mutation observer.
- Tray: native arm64 macOS menubar binary (no Rosetta) built by
scripts/buildTrayArm64.js+tray-binaries.ymlCI. - Docker CI: release pipeline rewritten — native amd64/arm64 runners, per-platform + resolved-manifest health checks, verified manifests,
workflow_dispatchrepublish withpromote_latestopt-in, FIFO concurrency queue, tag/version validation.
Fixes
- Command Code: raw byte-chunk replay preserves every NDJSON line under any chunk split (merged with the fork's C8/N8 peek hardening: raw-chunk happy path + fork line-replay catch path + buffer cap).
- Providers API: POST
/api/providersis O(1) and refuses silent key overwrite; capabilities catalog no longer cached per module copy; model selector filtered by active connections/noAuth. - Qoder: signed-request replay prevention (
403/103 Duplicate request), code 110 billing blocks, upstream SSE error status preserved. - Gemini/STT:
normalizeGeminiContentsguards terminal model turns + unresponded functionCalls; live-API-only Gemini models dispatch over the Live WebSocket transport; Responsesresponse.completedcarries streamed output items and usage. - Translator: Claude
refusalmaps tocontent_filterwith explanation; empty<think>markers no longer emitted into OpenAI content; replayed reasoning fields stripped for Groq/Mistral/Cerebras. - Cursor: AgentService empty turns/hangs (fold system prompts,
ModelDetails, Composer/Grokthinking_delta, reject IDE execs); RTK compresses Cursortool_resultpre-translation. - Antigravity: drops requestType
agent(false 429RESOURCE_EXHAUSTED); weekly vs 5-hour quotas separated and deduplicated; all Hermes identity variants rewritten. - Hugging Face: Inference Providers router migration, expanded image catalog, STT route. Proxy pools: lossless header forwarding through Vercel/Cloudflare/Deno relays. Tailscale: enable-flow health wait capped at 20s. OAuth: Zed paste-token crash fixed + IDE auto-import.
- OpenCode free tier: fingerprint quartet (bash/glob/grep/read) applied to every request with case-canonicalisation and response-side name restoration.
Merge notes & fork deltas on top
- GLM-5.2
reasoning_effortrestored: upstream's new exact-model capability entry forglm-5.2(1M context) accidentally droppedthinkingEffortSupported, regressing reasoning_effort — fails upstream's ownthinking-unifiedtest. The fork re-adds the flag (keeps upstream's 1M context). - strictProxy semantics bridged: the fork's P1 rule (connection-scoped strict ignores env proxies) now only applies when connection-proxy fields are present; executor-level bare
{ strictProxy: true }(upstream's qoder anti-replay guard) honors env proxies and still refuses direct fallback after transport loss. Both the fork'sproxy-fetch-strict-env(P1/P5) and upstream'sqoder-proxy-replaysuites pass. - Provider registry renumbering: fork providers moved p124–p128 → p131–p135 (upstream took p124–p130 for qoder-cn/opencode-zen/aggregators); providers/alias baselines re-snapshotted (93 providers, 117 alias tokens) — also fixes a stale
genspark-webmodelKeys entry shipped since v0.6.50. - CodexToolCard: upstream's auto-select of
apiKeys[0].keyon mount was kept OUT — in this fork that column holds the masked display key (S7), never a credential. - usageRepo byApiKey: fork's raw-key map +
resolveApiKeyMetafingerprint retained (upstream's masked-tail keying is weaker under the fork's S7 schema). - Docker CI adapted: upstream's new pipeline kept with the fork's image namespaces (
vibecoder11200/9routerDocker Hub + derived GHCR), the fork-defining-files release gate (gemini-web/ds2api), and the post-publish deploy trigger. - Tests: 3 fork tests adapted to upstream v0.5.91 behavior (opencode fingerprint quartet on tools assertions; image-generation pins the Codex version from the registry instead of a literal). Suite: 3627→3642 passing; no regressions vs the pre-merge Windows-local baseline.
v0.6.50 — xray multi-subscription + in-dashboard binary updates + hardening
Xray/v2go feature release: multi-subscription support and in-dashboard Xray-core binary updates, with hardened install orchestration.
Multi-subscription sync
The dashboard's V2Ray Proxy now supports multiple subscription sources (v2rayN-style), each with its own schedule and settings:
- Subscriptions manager — add/delete subscriptions, per-sub enable toggle, sync interval (presets, custom, or manual-only), and a "keep dropped servers" retention per subscription (7 days / 24 hours / delete after sync / forever).
- Per-subscription sync — "Sync Now" refreshes exactly one subscription; "Sync All" refreshes every enabled one. A failing subscription never blocks or wipes the others; a broken fetch (HTTP 200 with no parseable servers, or a suspiciously shrunken catalog) aborts only that subscription's sync.
- Traffic & expiry display — subscriptions that report a
subscription-userinfoheader show "X GB / Y GB used" (with percentage) and expiry countdown right on their row. - Source badges — the server table shows which subscription(s) each server came from; the same share link in two subscriptions is stored once and badged twice.
- Safe deletes — deleting a server from the table now tombstones it (recoverable from the new Deleted servers expander with Restore / Delete permanently); subscription syncs never resurrect a deleted server, and automatic retention cleanup never touches one. The model-filter auto-prune keeps physically deleting (never tombstones).
- Automatic migration — the previous single "Subscription URL" setting becomes a "Default" subscription on first boot: same URL, same schedule, all existing servers kept, selected server untouched. Removing all subscriptions later does not re-create it.
- Subscription CRUD + sync routes are local-only (localhost/CLI-token), matching the install route, since subscription URLs can contain provider tokens.
Xray-core binary updates from the dashboard
- Update / Reinstall button (always visible) with an "Installed vX / Latest vY" badge — the latest check compares against the stable release only, so Xray-core pre-releases never trigger the update badge.
- Version picker — lists recent releases (drafts hidden, pre-releases labeled) and supports deliberate downgrades with a confirmation step.
- Auto-restart — if the proxy was running before an update, it restarts automatically on the new binary. If the new binary fails to start, the installer auto-rolls back to the previous version and retries, reporting honestly which version you ended up on.
- Install integrity hardened: version tags are strictly validated before any download URL is built, installs are serialized (a second concurrent install gets a 409), and orphaned install staging directories are reaped at boot.
- Download failures (restricted networks) surface an actionable message; the version check degrades gracefully to "unknown" instead of erroring the dashboard.
Hardening from code review + hands-on E2E testing
Found by a dedicated code-review pass and a manual browser walk against a live dev server (real subscriptions, real binary install → proxy start → stop):
- Pre-release install confirm on fresh installs — the confirmation dialog for pre-release binaries was skipped when no binary was installed yet; it now always shows. Only the downgrade comparison still requires an installed version.
- Rollback reports the truth — a failed update that auto-rolled back no longer leaves the failed version recorded on disk; status/APIs report the version actually running, and reinstalling that tag no longer short-circuits as "already installed".
- Sync scheduler resilience — a transient DB error can no longer leave the auto-sync scheduler disarmed until reboot, and a queued per-subscription sync runs with its own outcome instead of replaying the previous run's error.
- UI fixes — the "Default (inherit)" retention option actually resets to inherit mode (was a silent no-op); the version "Check" button reports the fresh result; no fake uninstallable version is offered when GitHub is unreachable; "Installed vvX" double-prefix fixed; subscription names capped at 128 chars.
- Data hygiene — permanently deleting a server also clears its model-filter cache row (re-syncing the same link can't resurrect a stale pass/fail badge); deleting a subscription resolves inherit-mode retention against the configured global default;
includeDeletedrepo queries combined with other filters no longer risk a SQL parameter mismatch.
v0.6.49 — upstream v0.5.81 sync + genspark-web + xray fix
v0.6.49 (2026-09-20)
Upstream-sync release: merges upstream v0.5.81 (33 commits, v0.5.75 →
v0.5.81), merges PR #12 (genspark-web live AI Chat), and cherry-picks the
xray model-filter prune fix from PR #11. PR #11's opencode fingerprint
commits are superseded by upstream's implementation of the same gates
(better: anti-429 stable sessions, exact CLI id derivation, generic
forceStream); the fork's live-UA + catalog routing deltas are re-ported on
top. PR #13 (auto-release CI) was declined — releases stay manual.
Upstream highlights now in the fork
- Xiaomi MiMo: MiMo Desktop support merged into
xiaomi-mimowith dual
auth (API key + Desktop/OAuth session), Preview models, encrypted-callback
OAuth flow. - Claude Code: 1M-context toggle (
[1m]marker) and
CLAUDE_CODE_AUTO_COMPACT_WINDOWdriven from the dashboard. - OpenCode / OpenCode Go: zen free-tier 403
FreeTierErrorand 429s
fixed with canonical session ids (exact CLI derivation), version-gated UA
relay, stable session per identity (LRU+TTL), decoybash+readtools
for both chat and Responses shapes,forceStreamSSE aggregation for
non-stream clients, Muse Free tool-choice normalization, reasoning-item
stripping, Union Alpha via Messages API, China-region handling. - Kiro: underscores preserved in tool names (
mcp__server__tool) and
client names restored in responses; neutral placeholder for
tool-result-only turns; tool-result images forwarded. - Stream: aborts after HTTP 200 are reported in-band (per-format error
frames carrying a stall/disconnect message) instead of closing silently. - Models: DeepSeek-V4.1-Flash on DeepSeek/CodeBuddy-Intl/Ollama
(deepseek-v4.1-flash:cloud);low..maxeffort levels + vision for
DeepSeek-V4.*. - Command Code: images and
reasoning_effortpreserved on
/alpha/generate; transient stream-error retry without fake stop chunks;
Quota Tracker support. - Zed: OAuth lifecycle hardened (
systemIdpreserved, proxy timeout
renewed), live model resolution with error surfacing on the provider
page. - Antigravity: cached thought signatures scoped to model family; Claude
Code billing headers stripped from system prompts; Hermes identity
sanitized. - Auth: an account is no longer cooled down for request-scoped 4xx
errors; dashboard session cookie maxAge hardened. - Usage: DeepSeek credit balance displayed as currency credit.
- i18n: Persian (fa) translation integrated (merged key-union with the
fork's existing fa literals).
Features (PR #12, qkhalk) — genspark-web overhaul
- Live AI Chat endpoint: genspark.ai retired
/api/copilot/ask("This
feature has been retired. Please use AI Chat instead") — every chat
request through the provider returned that notice as the assistant
message. The executor now speaks the live AI Chat protocol: POST
/api/agent/ask_proxywith body typeai_chat(ai_chat_model,
ai_chat_enable_search, per-message ids,session_statemirror,
user_s_input), including the Python TLS sidecar (curl_cffi Chrome
impersonation) required by genspark's Cloudflare edge, full cookie-jar
parsing (session_id+__cf_bm+c1/c2+gslogin), Cloudflare
cookie auto-refresh and the-searchweb-grounding suffix. The sidecar
lazily bootstraps its venv (fail-open with a clear error when Python is
unavailable). - Dynamic model catalog: the hardcoded 15-id list (mirroring
genspark2api constants.go) shipped stale ids while genspark rotates its
lineup weekly. Newproviders/gensparkCatalog.jsmirrors the AI Chat
selector endpoints (api/moa_models_config+api/models_config) on a
6h background refresh that fails open to the previous snapshot. Model ids
forward verbatim asai_chat_model— brand-new genspark models work
even before the catalog refreshes; MOA comma mixes accepted upstream. - Vector logo: icon resolution gained a per-id extension map
(genspark-web→ svg), provider-icon regexes accept.svg, and a proper
four-point spark SVG ships aspublic/providers/genspark-web.svg;
literal.pngsrcs across providers/media-providers/usage pages now go
throughgetProviderIconSrc.
Fixes (cherry-picked from PR #11, qkhalk) — xray filter prune policy
- The model filter no longer prunes configs whose tunnel works: the
filter pruned ANY config whose probe failed, but a 429/403/5xx probe
result proves the opposite of a dead config — an HTTP response traveled
through the tunnel, so the exit is healthy and the rejection is upstream
(shared-IP free-tier quota, fingerprint gates). Those conditions are
transient; pruning permanently destroyed rotation inventory ("108 tested,
0 usable" was 108 healthy tunnels misread as dead). Probe results now
carrytunnelOk, andfilterPrunePolicy.jsonly ever deletes
connection-level failures (no response at all). Upstream-rejected rows
are still recorded ok=false so rotation skips those exits and the
fail-retry policy re-tests after the quota reset; a wiring regression
that suppressed ALL cache rows when prune was disabled (observed on a
649-config sweep: zero rows persisted) is fixed in the follow-up commit.
+7 unit tests.
Merge notes
- Conflicts resolved preserving fork behavior: opencode executor = upstream
taken wholesale with fork deltas re-ported (live npm-resolved CLI UA +
registry/catalog-driven responses routing);streamHandler.js= fork N7
onFirstChunk + upstream abortMessage terminal; provider detail page =
fork opencode deprecation badges + upstream zed live-catalog error
surfacing;fa.json= key union (16 fork + 16 upstream keys, no
overlap); capabilities/registry = union. - PR version numbers v0.6.50–52 that PR #11 carried are not used; their
content is folded into this single release as decided. - README.md untouched (upstream README changes are not followed on this
fork).
v0.6.48 — Upstream sync v0.5.75 (video generation, Kiro/Cline/Qoder fixes, Codex image models)
Upstream-sync release: merges upstream decolua/9router v0.5.75 (48 commits, v0.5.65 → v0.5.75) into the fork. Every fork subsystem preserved — v2go/Xray, proxy pools, DS2API, TOTU auto-fetch, orcarouter, encrypted backup, alerts, circuit breaker (N7/C7-C9), masked-key ping.
Upstream highlights
- Video: OpenRouter + Vertex AI (Veo) video generation on
/v1/videos/*via a provider adapter layer - Antigravity: weekly quota tracking + free-tier handling; OAuth refresh protected from Google anti-abuse rate limits
- Codex: GPT Image 2.5, Flare, Sunburst image models; Unicode-property tool-schema strip;
Versionheader restored - Qoder: usage reported to all clients; large attachments no longer inlined (upload via
/api/v2/image/upload, context tier auto-escalates) - OpenCode Go: new models — glm-5.3, kimi-k3, deepseek-flash (V4.1), longcat-2.0, hy4-preview, hy3, qwen3.8-max/flash, grok-4.6, gpt-5.6-luna
- Kiro:
q.*surface always first (#3776); no top-levelsystemPrompt(fixes 400 REQUEST_BODY_INVALID) - Cline/ClinePass: live catalog; API keys no longer
workos:-prefixed (fixes #2333); token refresh;{success,data}envelope unwrap - Claude: re-anchored
cache_controlcapped at the 4-marker budget (no more 400 → combo failover) - Tools: Claude tool type defaulting scoped to
requireClaudeToolTypegateways (#3905) - Providers: stale health state cleared on re-validation (#3810/#3830); duplicate
qwenremoved; Airforce free models refreshed - Security: video/Vertex URL path-escape (SSRF) rejection; cowork-mcp-tools SSRF guard (#3783)
- Auth: dashboard session cookie 24h
maxAge - CLI tools: model selector grouped by provider with full-text search
Merge & verification
- 12 conflicts resolved preserving fork behavior (streaming N7 + upstream credentials; masked-key ping + cline unwrap; opencode-go fork rewrite kept)
- Tests: 3164 total — failing set identical to pre-merge baseline (+1 upstream
node:test-styled file whose 4 assertions pass at import); 2 fork tests adapted to upstream's deliberate new behavior, no assertions weakened; 0 pass→fail regression - Production build (next build --webpack) passes; CLI tarball rebuilt
Full Changelog: v0.6.47...v0.6.48
Upstream changelog: https://github.com/decolua/9router/releases/tag/v0.5.75