Commit Graph

1375 Commits

Author SHA1 Message Date
39101b3416 Merge origin/master (v0.5.91) into gitea/new_feature
Resolve conflicts:
- streamingHandler.js: adopt upstreamResponseHeaders while keeping 0-token detail row avoidance
- capabilities.js: preserve user-asserted caps and globalThis slots without local caching of catalogSource
- AddCustomModelModal.js & providers/[id]/page.js: wire STT transport marker with custom model edits/assertions
- models/custom/route.js & aliasRepo.js: persist custom model transport and invalidate user caps
- usageRepo.js: key byApiKey live stats by full API key and keep tail in maskApiKey
- UsageStats.js: lazy load charts dynamically
2026-09-28 21:17:33 +07:00
decolua
f01fb909e3 # v0.5.91 (2026-09-26)
## Features
- **Providers**: add Token Harbor provider and four OpenAI-compatible aggregator providers (dahl, atria, agnes, bai)
- **Claude**: forward `x-claude-code-session-id` on OAuth requests; merge client `anthropic-beta` flags and forward rate-limit headers; return thinking text to OpenAI-format clients
- **Codex**: add GPT-6 Sol and Luna support
- **CLI Tools**: support multiple model profiles for Codex CLI
- **Hermes**: multi-role model config (delegation + auxiliary slots)
- **OpenCode Go**: complete the Go catalog (40 models) with auto-fetch + family endpoint regex
- **Usage**: show and redeem free limit resets for cc accounts
- **Cline**: expose the `cline-free/*` tier and price it at zero
- **Combos**: display vision adapter models in an ordered table view

## Fixes
- **Claude**: decloak tool names when `toolNameMap` misses (#4342); update spoofed cli version to 2.1.280 to support Opus 5.5
- **Providers API**: make POST `/api/providers` O(1) and refuse silent key overwrite (#4350)
- **Capabilities**: stop caching the catalog source per module copy (#4351)
- **OAuth**: stop Zed paste-token crash and add IDE auto-import (#4359)
- **Dashboard**: resolve combo limits with the server's capabilities (#4360); lazy-load charts and `marked`, preload in background on idle
- **Responses**: carry the streamed output items in `response.completed` (#4307)
- **STT**: dispatch live-API-only Gemini models over the Live WebSocket transport (#4006)
- **Gemini**: guard terminal model turns and unresponded functionCalls in `normalizeGeminiContents`
- **Command Code**: replay raw byte chunks to preserve all NDJSON lines
- **Translator**: stop emitting empty `<think>` markers into OpenAI content
- **CLI Tools**: refresh Codex settings after apply (#4347); keep existing `ANTHROPIC_AUTH_TOKEN` when applying Claude settings
- **Tray**: native arm64 macOS menubar binary, no Rosetta required
- **CLI**: filter model selector by active connections and noAuth providers
- **Usage**: key live byApiKey stats by full api key to prevent team-key collision and preserve API key usage attribution
- **Tailscale**: cap enable-flow health wait at 20s
2026-09-26 17:40:15 +07:00
decolua
c4690307ce fix(cli-tools): keep existing ANTHROPIC_AUTH_TOKEN when applying Claude settings
Only write the token when settings.json has none, so a real API key or
earlier config is never clobbered by Apply. Reset still clears it,
re-enabling key selection on the next Apply.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 17:30:59 +07:00
decolua
b54a3f9bb5 test(claude): update decloak tests for suffix-stripping fallback 2026-09-26 17:30:02 +07:00
Gesya Gayatree Solih
b65d2d0a6a fix(claude): decloak tool names when toolNameMap misses (#4342)
- Add stripCloakSuffix fallback in decloakToolNames and decloakStreamChunk
- Prevent client errors when toolNameMap is missing or lost on retry
2026-09-26 17:28:51 +07:00
decolua
6aea3875ef feat(claude): forward x-claude-code-session-id on OAuth requests 2026-09-26 17:15:12 +07:00
decolua
0249464d74 feat(cli-tools): support multiple model profiles for Codex CLI
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 17:03:06 +07:00
Nikan Wystaf
239bcfc568 perf(providers): make POST /api/providers O(1) and refuse silent key overwrite (#4350)
Fixes #4311

- Drop full-pool renumber on insert: new row gets MAX(priority)+1 directly,
  turning an O(pool) rewrite into O(1) per insert.
- For apikey connections, query by (provider, authType, name) and count via SQL
  aggregate instead of reading the entire pool into memory.
- Refuse silent apikey overwrite on name collision with 409 PROVIDER_NAME_CONFLICT,
  unless caller explicitly sets allowOverwrite: true.
- Add 8 unit tests covering priority ordering and name collisions.
2026-09-26 16:37:25 +07:00
Nick Nyanjui
199173fe58 feat(cline): expose the cline-free/* tier and price it at zero (#4334) 2026-09-26 16:31:25 +07:00
Beru
8f20daac6b fix(cli-tools): refresh Codex settings after apply (#4347) 2026-09-26 16:21:21 +07:00
Mohammad Hijjawi
fdcba3e1b2 fix(capabilities): stop caching the catalog source per module copy (#4351) 2026-09-26 16:20:59 +07:00
agent
dd293d3c62 feat(opencode-go): add the seven models upstream serves but the registry omits (#4357)
Upstream /zen/go/v1/models serves 35 ids; the registry listed 28. Add the seven
missing models with their corresponding supported and target formats:
- deepseek-v4.1-flash
- mimo-v2.6-flash, mimo-v2.6-pro
- space-bunny-free
- omen-alpha
- grok-4.7 (responses-only)
- gpt-6-luna (responses-only)
2026-09-26 16:15:43 +07:00
Amir Seify
7a436d209c fix(oauth): stop Zed paste-token crash and add IDE auto-import (#4359)
- Fix Zed Connect crash by gating paste-token UI when provider has no paste-token config
- Add ZedAuthModal with local IDE keyring auto-import, browser OAuth, and manual callback paste
- Add localhost-only GET /api/oauth/zed/auto-import and POST /api/oauth/zed/import routes
2026-09-26 16:12:46 +07:00
Nikan Wystaf
737b1f4d0a feat(providers): add Token Harbor provider 2026-09-26 16:09:52 +07:00
Spoon94
37a6b7e0f2 fix(dashboard): resolve combo limits with the server's capabilities (#4360)
The combos page computed combo capabilities in the browser using
aggregateComboCapabilities, falling back to pattern defaults for models
without exact entries because the synced model catalog is server-only.

Allow callers to supply a resolveCaps callback (e.g. from useModelCaps)
merged over the local tables, preserving non-limit capability flags.
2026-09-26 16:07:03 +07:00
Nikan Wystaf
06112c136c feat(providers): add four OpenAI-compatible aggregator providers (dahl, atria, agnes, bai)
Adds Dahl Inference, Atria Dawn, Agnes AI, and B.AI following the existing
registry pattern. Each uses DefaultExecutor with no custom translator needed.
2026-09-26 16:04:59 +07:00
MrBeanDev
90b0693423 feat(thinking): return Claude thinking text to OpenAI-format clients 2026-09-26 12:02:07 +07:00
Nick Nyanjui
fe347e4ea5 fix(stt): dispatch live-API-only Gemini models over the Live WebSocket transport (#4006)
Addresses #4006 by letting Gemini STT models that only exist on the Live
API transcribe instead of failing.

transcribeGemini sends audio to :generateContent and that is the only Gemini
path. A model that is realtime-only (exposed by the Live API's
bidiGenerateContent WebSocket) therefore fails outright, even though the account
can transcribe it.

open-sse/handlers/geminiLiveStt.js owns the WebSocket lifecycle: opens
:bidiGenerateContent, sends setup frame, waits for setupComplete, streams
audio as realtimeInput media chunks, and settles on turnComplete.
Dispatch is driven by transport marker 'gemini-live'. Adds custom model transport
persistence and selection on the dashboard.
2026-09-26 12:00:39 +07:00
ANIRUDDHA ADAK
273f0c32cd fix(responses): carry the streamed output items in response.completed (#4307) 2026-09-26 12:00:00 +07:00
Christian Gennari
c2148179c0 fix(commandcode): replay raw byte chunks to preserve all NDJSON lines 2026-09-26 11:53:54 +07:00
MrBeanDev
5d2cfbf3c5 fix(translator): stop emitting empty <think> markers into OpenAI content 2026-09-26 11:45:35 +07:00
decolua
3e4323e2fd feat(combos): display vision adapter models in an ordered table view
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 11:43:22 +07:00
decolua
975f28a57c test(zed): isolate zed-live-models suite DB via temp DATA_DIR
Seeding via createProviderConnection wrote zed-live-* accounts into the
real ~/.9router DB, showing up as junk accounts in the running dashboard.
Set DATA_DIR to a mkdtemp dir before dynamic-importing the DB-backed
modules and clean it up afterwards, matching the pattern in
compatible-provider-connections.test.js.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 11:40:08 +07:00
decolua
ddfdb0df04 perf(dashboard): lazy-load charts and marked, preload in background on idle
- Drop UsageStats from shared barrel so recharts (~589KB) stays out of
  the initial bundle of every page; usage page imports it directly
- Convert UsageChart/ProviderBarChart/TopModelsChart to next/dynamic
- Preload chart chunks via requestIdleCallback in DashboardLayout so
  navigating to Usage is still instant
- Lazy-import marked inside ChangelogModal (only when opened)
- Route Sidebar/EndpointPageClient through settingsStore: coalesce
  concurrent in-flight GET, merge PATCH response over cache (PATCH
  omits GET-only hasPassword) to kill duplicate /api/settings calls
- Delay /api/version npm check 2.5s after first render

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 11:34:08 +07:00
akmal safari pellu
30464bc227 fix(gemini): guard terminal model turns and unresponded functionCalls in normalizeGeminiContents 2026-09-26 11:18:44 +07:00
Doan Anh Dung
249f6c2fb8 fix(tray): native arm64 macOS menubar binary, no Rosetta required
systray2 ships only an x86_64 tray_darwin_release and selects it by
process.platform with no process.arch branch, so there is no native slice to
choose. Apple Silicon users therefore need Rosetta 2, and without it the tray
dies with EBADARCH ("bad CPU type in executable") and no icon appears.

Overlay a native arm64 build of the same upstream source
(felixhao28/systray-portable @ 6eddc91) instead. On darwin/arm64,
ensureArm64TrayBin() detects the Intel binary by parsing the Mach-O cputype,
downloads the artifact from the pinned tray-binaries release, verifies it
against a sha256 constant, atomically renames it over systray2's binary, and
busts systray2's copyDir cache — that cached copy is what actually executes, so
without the bust the swap has no effect.

Intel Macs keep using systray2's binary unchanged and Windows is unaffected
(PowerShell NotifyIcon, no binary). Any download or checksum failure leaves the
Intel binary in place and tells the user how to install Rosetta; a marker file
throttles retries to once per 24h because ensureTrayRuntime runs synchronously
on every CLI start, and is cleared on success so a clobbered binary recovers
immediately. Binaries stay out of the npm tarball per the existing Kaspersky
false-positive constraint — the artifact is fetched on demand.

Adds cli/scripts/buildTrayArm64.js (-trimpath, bit-for-bit reproducible for a
given Go version and macOS SDK) and a workflow_dispatch action that builds on a
macos-15 runner and refuses to publish when the sha diverges from the pin.

Also corrects comments claiming the systray -> systray2 switch fixed Apple
Silicon; it only fixed the dyld header rejection on macOS 14+, the binary was
still amd64-only.
2026-09-26 11:16:38 +07:00
decolua
f462837536 fix(cli): filter model selector by active connections and noAuth providers
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 11:14:04 +07:00
DavidArthurCole
dc198dff1f feat(claude): merge client anthropic-beta flags and forward rate-limit headers 2026-09-26 11:08:57 +07:00
29ccb84faf feat(commandcode): align quota usage with the official CLI /usage flow
Rewrites services/usage/commandcode.js to mirror the command-code CLI: whoami
resolves the org id, credits+subscriptions report the 5-hour/weekly windows and
plan, then usage/summary is queried with since=currentPeriodStart. Endpoints are
read from the provider registry usage block instead of hardcoded constants.

Tests updated to cover the new request order and response shapes.
2026-09-25 16:09:09 +07:00
3481058e09 fix(usage): drop duplicated commandcode import and features block
The previous origin/master merges (11089ab1 and earlier) left two
auto-merged artifacts behind because both sides added the same lines in
different places:

- services/usage.js declared `getCommandCodeUsage` twice (import + handler),
  which is an ESM SyntaxError on a clean checkout. The working tree masked
  it, so `next build` passed locally while the committed tree did not parse.
- providers/registry/commandcode.js carried two `features` blocks; the
  second one shadowed the first for `usage`/`usageApikey`.

Verified with `node --check` on the committed blob.
2026-09-25 16:09:04 +07:00
272dbcb9cc Merge remote-tracking branch 'origin/master' into gitea/new_feature
# Conflicts:
#	open-sse/executors/qoder.js
#	open-sse/handlers/chatCore.js
#	open-sse/handlers/chatCore/sseToJsonHandler.js
#	open-sse/providers/registry/commandcode.js
#	src/app/(dashboard)/dashboard/combos/page.js
#	src/app/api/v1/models/route.js
#	src/lib/db/repos/usageRepo.js
#	src/shared/components/UsageStats.js
2026-09-25 15:14:28 +07:00
Rafli Ahmad Zulfikar
4a57df8bf9 fix(usage): key live byApiKey stats by full api key to prevent team-key collision
maskApiKey kept only the first 8 characters of the key. API keys minted
from the same machine id share that prefix, so every key of an instance
collapsed into one sk-XXXXXXX*** bucket per model/provider, and the
dashboard attributed one holder's usage to another.

Key the live path by the full api key - matching the daily rollup
(aggregateEntryToDay) and the lastUsed overlay - and keep the last 4
characters in maskApiKey so masked keys stay distinguishable in the UI.
2026-09-23 14:53:01 +07:00
Deepanshu
a406381fad fix(usage): preserve API key usage attribution in live stats
The 24h/today branch of getUsageStats keyed byApiKey buckets on the
masked key, while the daily rollup and the lastUsed overlay key on the
full key. Every key minted by one instance shares the sk-{machineId}
prefix, so the mask collapsed all of them into a single bucket and
attributed one key's usage to another.

Key the live branch by the full api key too; the masked value is still
carried on the bucket for display.
2026-09-23 14:42:56 +07:00
Reid Nguyen
e571a8b6da feat(usage): show and redeem free limit resets for cc accounts
Adds the free usage-limit reset (the desktop app's "Reset for free")
to the Quota Tracker for cc OAuth accounts, mirroring the existing
Codex reset-credit button.

- Reset button with remaining count on the card; tooltip shows use-by
  date and which limits get refilled.
- Expiry modal (clock button) listing each grant: label, resets left,
  refills (session / weekly), use-by date, time remaining, status.
- Confirm dialog before redeeming, since a reset is irreversible.
- Usage and reset calls send the CLI User-Agent required for cedar_ember.
- Usage cache is dropped after a redeem so the card shows the refilled limits.
2026-09-23 12:21:07 +07:00
Ilhom (MBP M5 Pro)
8403576095 fix(claude): update spoofed cli version to 2.1.280 to support Opus 5.5
Anthropic's newly released Claude Opus 5.5 model strictly requires
claude-cli version 2.1.280 or newer. Spoofing the older 2.1.258
version results in an HTTP 400 `claude_code_version_too_old` error.

This commit updates the hardcoded `CLAUDE_CLI_VERSION` in
`open-sse/providers/shared.js` and aligns the corresponding unit
tests and baselines to bypass Anthropic's version gating.
2026-09-23 12:18:39 +07:00
decolua
e7c269b8c9 fix(tailscale): cap enable-flow health wait at 20s
Enable waited out the full 180s HEALTH_CHECK.timeoutMs when funnel URL
DNS was unreachable, making the toggle hang ~3 minutes before returning
success. waitForHealth now takes an optional timeoutMs; enable uses
HEALTH_CHECK.enableTimeoutMs (20s) while watchdog/cloudflare keep 180s.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 12:12:10 +07:00
mxlanparty@dp14
95600db17b feat(codex): add GPT-6 Sol and Luna support
- Add GPT-6 Sol and Luna to the Codex model registry.
- Send both models using the Codex 0.155 Responses Lite request shape, including `reasoning.context: "all_turns"`.
2026-09-23 12:11:49 +07:00
decolua
69cc1aa987 feat(hermes): multi-role model config (delegation + auxiliary slots)
POST /api/cli-tools/hermes-settings now accepts selections:
[{role, model}] and writes model/delegation/auxiliary.<role> blocks
into ~/.hermes/config.yaml non-destructively (comments and other keys
preserved). GET returns the current delegation/auxiliary config,
DELETE removes all 9router-managed blocks. Dashboard card gains a
collapsible Model Roles section aligned with the existing rows; the
bare `model` payload from the CLI quick setup still works.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 11:46:21 +07:00
decolua
001b292876 fix(dashboard): replace Hermes icon with official Nous Research logo
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 11:46:13 +07:00
decolua
a61fc6a01e fix(antigravity): rewrite all Hermes identity variants, not just the legacy sentence
The old rule only matched 'You are Hermes Agent, an intelligent AI
assistant created by Nous Research.' Current Hermes versions use
'You are Hermes Agent, built by Nous Research.' and similar variants,
which slipped through and got a fake 429 RESOURCE_EXHAUSTED. Replace
every variant with a neutral 'You are an AI assistant.' identity.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 11:46:05 +07:00
decolua
1b72f02e3b fix(opencode-go): clamp deepseek reasoning_effort "max" to "high" for mimo backends that reject it
mimo-v2.5-pro/v2.6 on the Go lane return 400 on reasoning_effort "max"
(probed live; mimo-v2.5 accepts it). The deepseek applyFormat case now honors
the declared thinking levels, and mimo-v2.5-pro gets a levels entry.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 11:45:40 +07:00
decolua
2aa99d9f39 feat(opencode-go): complete Go catalog (40 models) with auto-fetch + family endpoint regex
- Add 12 missing models from live /zen/go/v1/models (glm-5, kimi-k2.5,
  mimo v2/v2.6 line, qwen3.5-plus, grok-4.5/4.7, omen-alpha, ...)
- modelsFetcher + passthroughModels + "opencode-go" suggested-models filter
- Family regex fallback (opencodeFamilyFormats) keeps unknown/passthrough ids
  on the right endpoint lane (/responses, /messages, /chat/completions);
  curated registry entries always win
- isResponsesModel now routes passthrough grok/gpt ids to /responses

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 11:45:34 +07:00
decolua
39e36d3d0c # v0.5.86 (2026-09-23)
## Features
- **Xiaomi MiMo**: server-assisted desktop login for headless/Docker deployments, five account clusters (cn/sgp/ams/ru/in), and v2.6 pro/flash/pro-ultraspeed models with dual-route (account service vs. cloud API)
- **Claude**: add Claude Opus 5.5 support
- **i18n**: translate React text rewrites via characterData mutation observer

## Fixes
- **Proxy Pools**: keep request headers intact through Vercel/Cloudflare/Deno relays (spreading a `Headers` instance yielded `{}`, dropping auth and content-type)
- **Xiaomi MiMo login**: keep the session in the httpOnly cookie only, require dashboard auth on the proxy branch, and stop forwarding authorization headers upstream
2026-09-23 10:05:33 +07:00
wismyzhizi
910db749aa feat(xiaomi-mimo): server-assisted desktop login, five account clusters, v2.6 models
Reproduce the MiMo Desktop login surface server-side so headless/Docker
deployments can link a Xiaomi account without the Desktop client. The
account session (passToken) is captured during the proxied login and
stored per connection.

- Five account clusters (cn/sgp/ams/ru/in): per-region mimo-server host
  and SSO sid, unknown region falls back to sgp
- mimo-v2.6-pro/flash/pro-ultraspeed dual-route models: account-service
  route when desktop credentials exist, cloud API (sk- key) otherwise;
  drops obsolete mimo-x-*-preview ids
- Desktop ServiceTokenManager 2-phase handshake (single serviceLogin with
  target sid, raw 64-bit nonce preserved), per-region session cache
- reasoning_effort bridged to output_config.effort; i18n runtime now
  observes characterData mutations so React text rewrites get translated
- Security hardening on the login proxy: session travels only in the
  httpOnly cookie (never in the URL), proxy branch requires dashboard
  auth, authorization/proxy-authorization never forwarded upstream, and
  upstream Set-Cookie is not replayed onto the app origin
2026-09-23 09:43:54 +07:00
savioruz
6af26a9ee8 fix(proxy-pools): lossless header forwarding for relay pools
Preserve request headers through the Vercel/Cloudflare/Deno relay.

- proxyFetch: normalize options.headers via Object.fromEntries before
  spreading into the relay headers. Spreading a Headers instance yields
  {} and silently dropped every entry (auth + content-type), which is why
  the same pool worked on one path and failed on another.
- vercel relay: build the forwarded header object from req.headers.entries()
  instead of new Headers(req.headers), avoiding edge-runtime normalization
  of casing/duplicate keys that some providers reject.
2026-09-23 09:12:00 +07:00
Reid Nguyen
cbffeb9770 feat(claude): support Claude Opus 5.5 2026-09-23 09:01:51 +07:00
decolua
21583c03e5 # v0.5.85 (2026-09-22)
## Features
- **System One**: add `/v1/systemone` decision endpoint for Jev models (OpenCode Zen and OpenRouter lanes), wire into sidebar and Media Providers page with interactive probe testing
- **CLI Tools**: add dynamic configuration, settings APIs, and official logos for Pi, OMP, Crush, ForgeCode, Smelt, and CodeWhale
- **Analytics & Usage**: add Requests mode, provider/model breakdown charts, All Time period filter, and refined overview cards
- **Combos**: add Cursor/Claude Default presets; support bulk select/delete and bulk strategy changes (Fallback / Round Robin / Fusion)
- **Model Capabilities**: expose model capability metadata on `/v1/models` and aggregate capabilities across combo targets
- **OpenCode Zen & MiMo**: add OpenCode Zen (`opencode-zen`) provider with free-tier fingerprint; switch default vision fallback to MiMo V2.6 Flash Free
- **Qoder CN**: add `qoder-cn` provider for qoder.com.cn with OAuth flow, COSY protocol, and CN gateway routing

## Fixes
- **Translator**: map Claude `refusal` stop_reason to `content_filter` and surface explanation; strip replayed reasoning fields for Groq, Mistral, and Cerebras (#4220)
- **Antigravity**: drop requestType `agent` to avoid false 429 `RESOURCE_EXHAUSTED`; separate weekly and short-window (5-hour) quotas and deduplicate dashboard rows
- **Responses API**: report usage on `response.completed` so clients can auto-compact (#3432)
- **Hugging Face**: migrate to Inference Providers router (`router.huggingface.co`), expand image models catalog, and add STT route
- **Qoder**: prevent signed request replay (`403/103 Duplicate request`), handle code 110 billing blocks, and preserve upstream SSE error status
- **Performance**: bound usage `lastUsed` scan to a 2-day window; map large budget tokens to `max` reasoning tier
- **Docker**: publish verified multi-platform images (linux/amd64 and linux/arm64) with configurable apk build mirrors
2026-09-22 15:43:30 +07:00
Welington
0f488c7027 fix(translator): map Claude "refusal" stop_reason to content_filter and surface its explanation
Anthropic's API-level refusal (streaming classifier / ToS) ends the stream
with stop_reason "refusal", stop_details carrying the reason, zero output
tokens and no content blocks. Map refusal to content_filter in both
directions, surface stop_details.explanation as message text, and add
CLAUDE_STOP.REFUSAL to schema.
2026-09-22 15:32:48 +07:00
decolua
1a02713150 feat(combos): hide preset buttons and migrate legacy mimo vision adapter
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 15:32:10 +07:00
decolua
b53260ca54 feat(usage): add All Time period option and refine overview cards
- Add "all" period option to usage dashboard and chart API
- Aggregate all days in getChartData when period is "all"
- Center overview card metrics and adjust font size to prevent truncation

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 15:31:24 +07:00