Restore Cowork feature while closing the RCE vector: drop user-defined
stdio commands so only hard-coded preset plugins (browsermcp) may spawn.
Custom MCP now accepts remote URL only. Routes stay gated to localhost.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add custom-server.js: inject unspoofable socket IP, strip client XFF
(wired into Docker CMD + CLI spawn + build-cli copy)
- loginLimiter: key on trusted x-9r-real-ip, TRUST_PROXY opt-in, global fallback
- Force password change on first remote login while default is in use
- Add /api/auth/reset-password (local-only) so CLI reset writes live SQLite
- CLI settings: reset via API instead of stale db.json
- Fix OAuth modals opening duplicate browser tabs on add-connection
- Add cli:pack / cli:publish scripts
Co-authored-by: Cursor <cursoragent@cursor.com>
Tailscale installed via apt/snap/brew was reported "Not Installed/Logged In"
because 9Router only probed its custom userspace socket. Probe the system
socket (/var/run/tailscale/tailscaled.sock) as fallback, add /usr/sbin and
/snap/bin to candidate paths and EXTENDED_PATH, and report separate
customDaemonRunning/systemDaemonRunning flags. Caching/non-blocking behavior
preserved.
Co-authored-by: Stefan Pirker <stefan.pirker86@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Add a "Bulk Add" button on /dashboard/providers/codex that imports
multiple OAuth accounts at once by pasting a JSON array, single object,
or { accounts: [...] } wrapper.
- New endpoint POST /api/oauth/codex/bulk-import (serial loop, no token echo)
- New BulkImportCodexModal component with JSON textarea + success/failure summary
- Persist idToken/lastRefreshAt on first insert via OPTIONAL_FIELDS
- Backfill email/chatgptAccountId/chatgptPlanType from JWT when missing
- Derive expiresAt from expiresIn when missing
- Gated to providerId === "codex" only; other providers unaffected
Co-authored-by: Cursor <cursoragent@cursor.com>
AWS OIDC IDC/Builder-ID tokens omit profileArn, so CodeWhisperer calls
return 403 "User is not authorized". Resolve it natively via the
ListAvailableProfiles API instead of reading Kiro IDE profile.json.
- providers.js: add fetchKiroProfileArn() and resolve on poll (new logins)
- tokenRefresh.js: backfill profileArn on refresh so existing IDC
connections self-heal without re-login
Co-authored-by: Cursor <cursoragent@cursor.com>
Kiro IDE changed its API endpoint from q.us-east-1.amazonaws.com to
runtime.us-east-1.kiro.dev. Old domains kept as fallback.
- mitm/config.js: add new hosts to TARGET_HOSTS + getToolForHost()
- cliTools.js: update mitmDomain to new endpoint
- mitmToolHosts.js: prepend new domain (keep legacy for compat)
- dataDir.js: on Windows ignore Unix-style DATA_DIR paths
Co-authored-by: joutvhu <joutvhu@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Convert isTailscaleLoggedIn to cached non-blocking getter and turn
isTailscaleRunningStrict / isTailscaleLoggedInStrict into async execAsync
probes. The status poll no longer blocks the event loop, so dashboard
navigation stays responsive while tunnel/tailscale checks run.
Co-authored-by: Cursor <cursoragent@cursor.com>
LLM combos created with kind:"llm" were hidden because the page only
listed no-kind combos. Include kind==="llm" while still filtering out
media provider (webSearch/webFetch) combos.
Closes#1682
Co-authored-by: Cursor <cursoragent@cursor.com>
Move shutdown into header popup + profile, move remote into sidebar above
settings, add flag-only language switcher in header, and add language card
plus shutdown/logout actions to the profile page.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add VIRTUAL_IFACE_REGEX to filter utun/awdl/bridge from network fingerprint
- Trust cloudflared/tailscale while process is alive, never kill on force restart
Co-authored-by: Cursor <cursoragent@cursor.com>
Added a new translation for the message "Endpoint is exposed without an API key." to various language files, enhancing user awareness regarding API security. This update ensures that users are informed about potential risks associated with unprotected endpoints in their respective languages.
Add wenyan-lite/wenyan/wenyan-ultra levels for max token compression,
sync SHARED_EXAMPLES/AUTO_CLARITY/PERSISTENCE across all levels, and
expose 3 wenyan buttons in endpoint settings UI.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add shared OAuth credential lifecycle manager with provider-aware refresh
decisions. Implement CodexExecutor.refreshCredentials so 401/403 retry
refresh works for Codex, track lastRefreshAt and refresh before the
upstream stale-token window, preserve omitted idToken, and add
per-connection single-flight refresh to avoid refresh-token rotation races.
Merged from PR #1664.
Co-authored-by: Cursor <cursoragent@cursor.com>
MODEL_NO_MAP guard never re-routes Antigravity tab-autocomplete (tab_* models)
so latency-critical inline completion stays native. Flags gemini-3.5-flash-low
(agent/Default) as mandatory in the dashboard.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add qmodel_latest to QODER_MODEL_MAP
- Expose qmodel_latest in static Qoder provider catalog (qd)
- Generalize executor comment so model set does not go stale
- Add unit coverage for the new model key + catalog
Closes#1638
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add gemini-3.5-flash-extra-low across CLI menu, provider models, usage, pricing
- Add MITM synonyms (high/medium/extra-low) and split pattern so Low no longer falls through to Medium
- Strip models/ prefix in getMappedModel for AG public name normalization
Co-authored-by: Cursor <cursoragent@cursor.com>
Kiro sends modelId "auto" for the main agent turn; without a defaultModels
slot getMappedModel returned null and the call leaked to AWS instead of the
configured provider. Adds the slot + guard test.
Co-authored-by: Cursor <cursoragent@cursor.com>
OpenAI/Anthropic Compatible and Custom Embedding nodes allow exactly one
connection each. The guards were dropped during the bun:sqlite refactor
(v0.4.28), so duplicate POSTs were accepted (201) instead of rejected (400).
Restore the per-node existing-connection check in the POST handler.
Test: tests/unit/compatible-provider-connections.test.js now passes.
Co-authored-by: Cursor <cursoragent@cursor.com>
Route image model tests to /api/v1/images/generations and STT to
/api/v1/audio/transcriptions instead of forcing all non-embedding
models through chat completions. Adds kind-aware pingModelByKind,
hf->huggingface alias, and silent WAV sample for STT reachability.
Scoped to dashboard/internal model testing only; runtime inference
routing is unchanged.
Author: yicone <yicone@gmail.com>
Closes#1628
The "opencode-free" suggested-models filter only kept models whose id ends
in "-free", so free-but-unsuffixed models like `big-pickle` (routed via
open-sse/executors/opencode.js MESSAGES_MODELS) never appeared in the
import UI.
Extract the filters into a testable module and include a
KNOWN_FREE_OPENCODE_MODELS allowlist (big-pickle) alongside the "-free"
suffix check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Qoder's API returns `remaining` as an absolute credit count (e.g. 348
out of 3000), but ProviderLimits' getRemainingPercentage and QuotaTable
treat the `remaining` field as a 0-100 percentage. Result: "348%" with
red status.
Stop forwarding `remaining` from the qoder parser. The percentage is
computed from used/total via calculatePercentage, which gives the
correct ~12% remaining for the example case.
Wire Qoder credits into the Quota Tracker card grid:
- Add `qoder` to USAGE_SUPPORTED_PROVIDERS so the connection passes the
isUsageEligible filter at /api/providers/client and shows up in
providerOptions on the dashboard.
- Reshape getQoderUsage so quota records (user, organization) live under
`quotas` and scalar metadata (totalUsagePercentage, isQuotaExceeded,
expiresAt) are siblings — the parser used to walk Object.entries(quotas)
and would have rendered `totalUsagePercentage: 0.42` as a "0/0" row.
- Surface Qoder's expiresAt as resetAt on each quota record so the card
shows when credits reset.
- Add a parser branch in ProviderLimits/utils.js: rename internal keys
(user → "Personal", organization → "Organization"), drop empty org
buckets so personal accounts don't render a misleading "0/0 Organization"
row, and forward remaining/unit so the QuotaProgressBar can use them.
- Add Qoder's brand color (#EC4899) to ProviderLimitCard's color map.
42 tests still pass; build clean.
Move device-flow / poll / userinfo / parseExpiry from src/lib/qoder/auth.js
into a QoderService class at src/lib/oauth/services/qoder.js, matching how
KiroService is organized. Also re-add the QoderService re-export from
services/index.js.
The split now mirrors Kiro:
src/lib/oauth/services/qoder.js OAuth flow (was auth.js)
src/lib/qoder/cosy.js Per-request signing (unchanged)
src/lib/qoder/encoding.js WAF-bypass body (unchanged)
src/lib/qoder/constants.js Endpoints + model map (unchanged)
Behavior is unchanged — same functions, same signatures, just relocated
into a class so the import path lines up with `import { QoderService } from
"@/lib/oauth/services"` like every other OAuth provider. parseExpiry is now
a static method so callers and tests can use it without instantiating.
42 tests still pass; build still clean.
Adds 18 new tests covering the bugs fixed in the previous commit so they
can't silently regress:
- parseExpiry (7 tests): numeric ms-epoch input, numeric strings handled
before Date.parse so "1700000000" doesn't get year-interpreted, RFC3339
strings, expires_in:0 honored as already-expired, 30-day fallback only
when both inputs are missing/invalid
- normalizeMessages (4 tests): system hoisting, multipart text flatten,
multiple system joining, empty input
- wrapQoderSSE (6 tests): the fixed cases — trailing partial line drained
in flush(), no chunks forwarded after [DONE], embedded newlines stripped
from inner body, error envelope produces error chunk + [DONE], non-ok
responses returned unchanged
- expose parseExpiry from auth.js, expose normalizeMessages/wrapQoderSSE
via __test__ from the executor (internals only — not part of the public
API). Marked with comment so the surface is intentional.
42 tests total (24 original + 18 new). Build still clean.
Correctness:
- testUtils: drop checkExpiry so the userinfo URL probe actually runs (revoked
tokens used to look "active" until local 30-day expiry passed)
- auth.parseExpiry: handle numeric expiresAt, swap parseInt before Date.parse
so "2026" doesn't get interpreted as year-2026, treat expires_in:0 as
already-expired instead of fabricating a 30-day default
- providers.mapTokens: synthesize email from userId when fetchUserInfo fails
so OAuth dedup works (re-logins no longer accumulate "Account N" rows)
SSE wrapper:
- wrapQoderSSE: add !doneEmitted guard on success branch (chunks could leak
past [DONE] when an error envelope shared a TCP packet with a valid one)
- flush(): finalize TextDecoder + drain trailing buffer so the chunk carrying
finish_reason is delivered when upstream closes without a final \n
- sanitize literal \n inside inner OpenAI body so SSE framing stays intact
Robustness:
- executor: wrap buildCosyHeaders in try/catch so a missing accessToken
returns 401 (re-auth) instead of bubbling as 500
- executor: short-circuit on missing accessToken before signing
- executor: plumb proxyOptions/signal through buildQoderRequestBody so
proxy-only networks can fetch the model_config catalog
- qoderModels: dedupe concurrent first-time misses with an in-flight Promise
map (parallel chat windows now do 1 upstream fetch instead of N)
- qoderModels: check signal.aborted before addEventListener so a pre-aborted
parent signal cancels the inner fetch immediately
- auth: AbortController + 15s timeout on pollDeviceToken / fetchUserInfo to
prevent hung sockets when openapi.qoder.sh stalls mid-response
UX:
- OAuthModal: derive polling deadline from device-code expires_in (qoder
publishes 300s; the previous fixed 120s caused timeouts when users took
more than 2 minutes on the consent page)
Cleanup:
- delete src/lib/oauth/services/qoder.js — referenced removed config fields
(clientId/clientSecret/tokenUrl/authorizeUrl) and was re-exported from
services/index.js, so any future caller would TypeError on first use
## Fixes
- Codex: auto-retry when upstream drops mid-stream (no more hangs)
- Codex: fix random 400/404 errors, tool-calling failures, and unstable prompt cache
- MITM: support Antigravity 2.x (updated IDE version detection and DNS/cert flow)
- Sanitize Read tool args to prevent retry loops from non-Anthropic models (#1144)
- Implement json_schema fallback for OpenAI-compatible providers without native Structured Output (#1343)
- Strip empty Read pages argument in OpenAI-to-Claude translator (#1354)
- Forward Gemini output dimensions for embeddings (#1366)
- Resolve setState-in-effect errors in dashboard components (#1362)
- Gemini CLI: reuse stored OAuth project IDs for quota checks and show clearer setup guidance when the project is missing (#1271, #1428)
- LanguageSwitcher: remove mounted state + useLayoutEffect pattern
Portal renders directly based on open state (SSR-safe without client check)
- UsageStats: replace stats-null check with isInitialLoad ref to avoid
setState in effect body (cascading render issue)
commit 6561679f5c396bb07f5f7ba5bc5ec75e81c803a4
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 16:26:01 2026 -0700
fix: never dedup access_token connections
Access tokens should always create new entries. User decides
which to keep (refresh-based OAuth vs no-expiry website token)
and removes the other manually.
commit d773451657999a2965ca4a094a7f0b7a54066693
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 16:24:30 2026 -0700
fix: support ChatGPT website token format (account_id, plan_type)
ChatGPT website access tokens use top-level 'account_id' and
'plan_type' fields, while OAuth id_tokens use nested claims
under 'https://api.openai.com/auth'. Now both formats are
handled, so workspace dedup works for website tokens too.
commit cb895a5f6be59c51267874f11567646fa1f43016
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 16:12:56 2026 -0700
fix: detect JWT in manual callback URL field
When user pastes a JWT access token (starts with eyJ) in the
'paste callback URL' input field, skip URL parsing and send
it directly to the exchange endpoint as the code. Fixes
'Failed to construct URL: Invalid URL' error.
commit 29650d4a6732e3cf0958c9963b53209e41c8281e
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 15:37:02 2026 -0700
feat: auto-detect access token in OAuth exchange
When the exchange endpoint receives a JWT (starts with eyJ)
instead of an OAuth authorization code, it detects this and
creates an access_token connection directly — skipping the
OAuth token exchange flow.
This lets users paste a ChatGPT access token where the OAuth
code would normally go, and have it work automatically.
commit e8e7c5709a783abd0c45246a44de1cc6abdba100
Author: OpenClaw Patch <patch@openclaw.local>
Date: Tue May 19 15:14:48 2026 -0700
feat: workspace-aware dedup + ChatGPT access token import
1. Dedup now checks email AND workspace (chatgptAccountId)
- Same email in different workspaces = separate connections
- Backward compatible: non-workspace providers still dedup by email
2. New authType 'access_token' for ChatGPT website tokens
- POST /api/oauth/codex/import-token accepts raw access tokens
- Extracts email, workspace, plan from JWT claims
- Deduplicates by email+workspace like OAuth
- No refresh token needed (avoids OAuth relogin issues)
## Features
- Xiaomi MiMo Token Plan: region selector (Singapore / China / Europe) — keys are cluster-specific
- Antigravity: risk confirmation dialog before first connection
- Gemini CLI: surface upstream retry delay on 429 errors
## Fixes
- MITM: cannot kill process on macOS under sudo (lsof not found in PATH)
- Stream: false-positive stall timeout on Claude reasoning / Kiro responses
- Tunnel: cannot re-enable after disable (stuck state)
- Tunnel: cloudflared error messages now include log tail for easier debugging
- Language switcher: applies selected locale immediately on close (#1234)
- Antigravity OAuth: metadata now matches the official client
## Improvements
- Gemini CLI: bump engine to 0.34.0
- Re-hide `qwen` (OAuth EOL) and `iflow` (not ready) providers
Remove `hidden: true` so the providers appear in the UI list while
keeping their existing deprecation/risk notices.
Co-authored-by: Cursor <cursoragent@cursor.com>
Fixes#1226
The Antigravity OAuth flow sent inconsistent client metadata between
the token acquisition phase and the API usage phase. String enum values
(IDE_UNSPECIFIED, PLATFORM_UNSPECIFIED) were used during OAuth token
exchange + loadCodeAssist + onboardUser, while numeric enums (ideType: 9,
platform: <computed>, pluginType: 2) were used in runtime API calls.
Google detected this fingerprint mismatch and blocked 9router accounts.
Replace all string enum occurrences with the correct numeric values:
- src/lib/oauth/constants/oauth.js: loadCodeAssistClientMetadata now
uses getOAuthPlatformEnum() for platform and numeric 9/2 for
ideType/pluginType, matching getOAuthClientMetadata()
- src/lib/oauth/services/antigravity.js: getMetadata() now delegates
to getOAuthClientMetadata() instead of returning hardcoded strings
- src/lib/oauth/providers.js: postExchange metadata now uses
getOAuthClientMetadata() instead of inline string enums
- open-sse/services/usage.js: getGeminiSubscriptionInfo body now uses
CLIENT_METADATA (already imported from appConstants.js) instead of
inline string enums
## Features
- Add Vercel AI Gateway provider support (#1183)
- rtk: Kiro format tool result compression — handle conversationState.history & currentMessage, preserve error results, ~13.6% savings (#1194)
## Fixes
- openclaw: normalize agent.model object form `{primary, fallbacks}` before .startsWith → fix TypeError & 'not configured' status (#1216)
- Usage Details pagination: stay inside mobile viewport <640px (#1218)
- Fix test model error
- Fix MIMO provider in Codex
- Disable log file creation when using MITM AG
OpenClaw 2026.5.x writes agents[].model as either a plain string
or as an object { primary, fallbacks }. The status enrichment and
POST cleanup both called .startsWith() on the raw value, which threw
TypeError when the object form was present and made the dashboard
report 'not configured'.
Add a resolveAgentModel helper that accepts both shapes and returns
the string id (model.primary for the object form, empty string for
missing/invalid). Use it when enriching agents in GET so consumers
receive a string model field, and when filtering the list in POST.
Refs decolua/9router#1196
On viewports below 640px the pagination row at Dashboard > Usage >
Details rendered the rows-per-page select plus the full numbered page
list plus the prev/next chevrons in one unwrapped flex row with no
horizontal padding. On a 390px phone the Rows: label was clipped on
the left and the next chevron was clipped on the right.
Add px-2 to the outer container so the pagination card has horizontal
breathing room. Switch the inner controls group to flex-wrap with
gap-2 on mobile (and gap-4 from sm: up) so the rows-per-page select
can wrap below the chevrons. Hide the numbered page buttons, the
1 / last anchors, and the ... ellipses below sm: -- only the prev,
current page indicator, and next chevrons render on mobile, which
keeps every control reachable while fitting inside the card.
Desktop layout (>= 640px) is unchanged.
Refs decolua/9router#1146
- Fix duplicate tray icon on macOS when hiding to tray
- Fix tray not showing in background mode on macOS
- Fix hide to tray broken on Windows/Linux
- Fix Shutdown button in web UI not working
## Features
- Add Blackbox provider with `bb` alias (#1143)
- Add Xiaomi token plan provider
- Enhance model select modal UX + modal traffic lights (#1111)
- Default Usage dashboard period to Today (#1141)
## Fixes
- Fix Cowork model selection and Windows CLI packaging (#1129)
- Update provider name retrieval for compatibility provider (#1135)
- Update JWT_SECRET handling
Cherry-picked from upstream PR #1129 + local improvements:
- dedupe inline remove-model handler -> use handleRemoveModel
- add .next-cli-build/ and cli/.build-home/ to .gitignore
* feat(model-select-modal): highlight added models and support bulk selection
- Add addedModelValues prop to highlight already-added models with primary color
- Sort models alphabetically per provider, with added models floated to top
- Replace green highlight with primary brand color (orange #E56A4A)
- Use check icon (10px) inline with model name instead of check_circle
- Replace Done button with info bar explaining click-to-toggle behavior
- Add ProviderIcon to provider group headers replacing colored dot
- Import ProviderIcon, remove unused Button import
* feat(cli-tools): wire addedModelValues, onDeselect, and auto-save to model select modals
- Pass selectedModels as addedModelValues to ModelSelectModal in OpenCode and Copilot cards
- Add onDeselect handler to remove model from list on second click
- Set closeOnSelect=false to allow bulk model selection
- Remove manual setModalOpen(false) from onSelect callbacks
- Add saveModels() silent auto-save triggered on modal close (OpenCodeToolCard)
- Use useRef to track latest selectedModels in closure-safe way
* feat(modal): functional traffic light close button with hover icon and tooltip
- Make red dot a clickable button that closes the modal
- Show ✕ icon inside red dot on hover via group-hover opacity transition
- Gray out yellow and green dots (cursor-not-allowed, no tooltip)
- Increase dot size from w-3 h-3 to w-4 h-4
- Add Tooltip with brand-matched color #FF5F56 on red dot
- Remove X close button from modal header
* feat(tooltip): add color prop for themed tooltip backgrounds
* feat(i18n): add translations for model select info bar and close tooltip
- Add 'Click to add, click again to remove. Changes are saved automatically.' to all 32 locales
- Add 'Close' translation to all 32 locales
* fix(ui): address code review feedback on modal UX and auto-save
- Modal: remove showCloseButton prop, use showTrafficLights for header
condition, hide traffic lights on mobile (hidden md:flex), add mobile
X button (md:hidden) with aria-label, add aria-label and title on
traffic light close button
- OpenCodeToolCard: validate activeModel membership before saving —
fallback to models[0] or empty string; clear/reassign activeModel
on deselect when removed model was the active one
- CopilotToolCard: add useRef + selectedModelsRef, add saveModels()
using /api/cli-tools/copilot-settings, wire auto-save on modal close
- ModelSelectModal: fix JSX formatting — separate info bar closing div
from Search comment onto its own line
* feat(usage): add Today period option to Usage & Analytics
Bổ sung lựa chọn Today vào bộ lọc thời gian của trang Usage & Analytics
(trước đây chỉ có 24h, 7D, 30D, 60D).
Khác biệt với 24h:
- 24h: cuộn 24 giờ trước → hiện tại
- Today: cố định từ 00:00 hôm nay (giờ local) → hiện tại
Thay đổi:
- page.js, UsageStats.js: thêm option Today vào danh sách PERIODS,
đổi grid mobile từ 4 cột sang 5 cột để fit option mới.
- api/usage/stats, api/usage/chart: cho phép giá trị period today.
- usageRepo.js:
+ getUsageStats: dùng nhánh live history khi period = today,
cutoff lấy từ 00:00 hôm nay theo local time.
+ getChartData: thêm 24 bucket theo giờ từ 00:00 → 23:59 hôm nay.
* feat(usage): đặt mặc định period là Today khi mở dashboard/usage
Người dùng thường quan tâm usage trong ngày hôm nay nhiều hơn 7 ngày,
nên hiển thị Today ngay khi vừa mở trang để giảm 1 thao tác chuyển period.
Đồng bộ luôn fallback trong UsageStats để giữ nhất quán khi component
dùng standalone.
The visibility, copy, and delete buttons in the API Keys list used , which never reveals on touch devices because they don't fire :hover. Switch all three to — visible by default on mobile, hover-revealed on sm+ — matching the pattern already used in providers/ and media-providers/
Co-authored-by: Muhammad Ridwan Ramadhan <ridwanramadhan8888@gmail.com>
Bổ sung lựa chọn Today vào bộ lọc thời gian của trang Usage & Analytics
(trước đây chỉ có 24h, 7D, 30D, 60D).
Khác biệt với 24h:
- 24h: cuộn 24 giờ trước → hiện tại
- Today: cố định từ 00:00 hôm nay (giờ local) → hiện tại
Thay đổi:
- page.js, UsageStats.js: thêm option Today vào danh sách PERIODS,
đổi grid mobile từ 4 cột sang 5 cột để fit option mới.
- api/usage/stats, api/usage/chart: cho phép giá trị period today.
- usageRepo.js:
+ getUsageStats: dùng nhánh live history khi period = today,
cutoff lấy từ 00:00 hôm nay theo local time.
+ getChartData: thêm 24 bucket theo giờ từ 00:00 → 23:59 hôm nay.
Fallback to default user directory (~/.9router) when configured
DATA_DIR is not writable (EACCES/EPERM). Other errors still throw.
Co-authored-by: Thiên Toán <toanalien@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Fixed variable declaration order in CLIToolsPageClient.js (functions before useEffect)
- Added lazy initialization for useState in BasicChatPageClient.js to read from localStorage
- Reduced ESLint errors by ~23%
Co-authored-by: yuangejiaozhu <leegajone@email.com>
- Replaced hardcoded LINUX_CERT_DIR with dynamic filesystem probing to support Debian, Arch, Fedora, and openSUSE system trust stores.
- Added updateNssDatabases helper to seamlessly inject root certificates directly into browser NSS databases (e.g., ~/.pki/nssdb, ~/.mozilla/firefox).
- Supported standard and snap-based Chrome/Chromium and Firefox installations.
- Made browser cert injection resilient, executing under the current user to prevent file ownership issues, and safely falling back if certutil is absent.
Internal model test routes fetched 127.0.0.1:UPDATER_CONFIG.appPort
(hardcoded 20128). When PORT env is set to a different value, the app
listens on PORT but the internal fetch still targets 20128, causing
"fetch failed" on /api/models/test and /api/providers/[id]/test-models.
Fall back to UPDATER_CONFIG.appPort only when process.env.PORT is unset.
## Features
- Add Cline & Kilo Code tool cards
- Tailscale TUN mode for stable Funnel TLS
- Sort APIKEY providers by usage, collapse to top 20
## Improvements
- Local Material Symbols font (no Google Fonts)
- Docker base: Bun → Node 22-alpine
- MITM reads aliases from JSON cache (no native sqlite)
- Stream stall timeout (2 min) in open-sse
## Fixes
- Fal.ai key test: use stable models endpoint
Two findings, neither blocked by anything else:
1. src/app/callback/page.js — the OAuth callback page posted the
{ code, state } payload to window.opener with targetOrigin "*", so any
page that opened the popup against the well-known redirect_uri received
the live OAuth code. The expectedOrigins list was already computed but
never used. Iterate over it and pass the origin per send.
2. open-sse/utils/proxyFetch.js — createBypassRequest() set
rejectUnauthorized: false on the HTTPS request that runs after the
Google-DNS-resolved real-IP fallback (used for cloudcode-pa.googleapis,
GitHub Copilot, Cursor, AWS LLM endpoints). Combined with servername:
parsedUrl.hostname this gave SNI-correct connections that nonetheless
ignored cert validation, so an on-path attacker could swap in their
own cert and read the user's API tokens / prompts. Drop the flag.
Detected by Aeon + semgrep (javascript.browser.security.wildcard-postmessage-configuration
+ problem-based-packs.insecure-transport.js-node.bypass-tls-verification).
Severity: HIGH (#1) / MEDIUM (#2).
CWEs: CWE-1385 (#1), CWE-295 (#2).
Co-authored-by: aeonframework <aeon@aeonframework.dev>
- Add global CSS rules for select elements in dark mode
- Use color-scheme property to signal dark mode to browser
- Explicitly style option elements with dark theme colors
- Fix UsageStats dropdown to use correct CSS variables (bg-surface, text-text-main)
Fixes dropdown text being unreadable in dark theme on usage page:
- Provider filter dropdown
- Table view selector (Model/Account/API Key/Endpoint)
- Pagination page size selector
Tested in Chrome and Firefox with both light and dark themes.
## Features
- Add bun:sqlite adapter with automatic runtime detection (Bun/Node)
- Add bulk API key import (format: `name|sk-key`, one per line)
## Fixes
- Fix add API key for custom providers
Update all DeepSeek model prices to match current V4 Flash pricing
($0.14/$0.28 per 1M input/output tokens), and add V4 Pro model with
its own pricing ($0.435/$0.87). Also add deepseek-v4-pro to the
provider model list.
Co-authored-by: smarthomeblack <truongbber@gmail.com>
- Introduced OllamaLocalExecutor to handle requests for the "ollama-local" provider.
- Removed the direct URL construction for "ollama-local" from BaseExecutor.
- Updated index.js to include the new OllamaLocalExecutor in the executors mapping.
- Enhanced the ProvidersPage component to support dynamic addition of OpenAI/Anthropic compatible providers.
The totalRequests on dashboard/usage overview always showed lifetime total
instead of respecting the selected period (24h/7d/30d/60d). Now calculated
from period-filtered data like other stats.
Co-authored-by: Tuan-TC <tuan-tc@users.noreply.github.com>
- Implemented handleDeselectModel function to allow users to deselect models in both ComboFormModal and ComboDetailPage.
- Updated ModelSelectModal to handle deselection and visually indicate selected models.
- Enhanced user experience by allowing models to be removed from the selection without closing the modal.
- Introduced DEFAULT_QUICK_TUNNEL_PROTOCOL and QUICK_TUNNEL_PROTOCOLS to allow users to specify the transport protocol for quick tunnels.
- Updated spawnQuickTunnel function to utilize the specified protocol from environment variables, defaulting to HTTP/2 if not provided.
- Enhanced the child process environment to include the selected tunnel transport protocol.
- Request translation: add codeWhispererToMessages() to convert CodeWhisperer conversationState to OpenAI messages array
- Response format: replace pipeSSE() with pipeOpenAIasEventStream() to return AWS EventStream binary frames instead of SSE text
- Protocol headers: add required Smithy system headers (:message-type, :event-type, :content-type) to every EventStream frame
- Tool support: add extractTools() to convert CodeWhisperer toolSpecification to OpenAI tool format
- Tool arguments: add safeArgsString() to prevent '[object Object]' corruption during SSE accumulation
- Multi-turn context: add convertAssistantResponseMessage() and convertUserInputMessage() to preserve toolUses and toolResults in history
- Tool dispatch: send toolUseEvent.input as raw JSON string instead of parsed object to match Kiro's internal JSON.parse() expectation
- BaseUrlSelect: add cloud endpoint option, custom URL local state, always
default to first option; new cliEndpointMatch helper; CLI tool cards refactor
- API: new /v1/audio/voices and /v1/models/info; /v1/models filters disabled
models, drop unused timestamp
- initializeApp: guard tunnel/tailscale auto-resume to once-per-process
- geminiHelper: ensureObjectType for schemas with properties but no type
- skills: minor SKILL.md tweaks (chat/embeddings/image/stt/tts/web-*)
- Introduced a caching mechanism for in-flight token refresh requests to prevent race conditions and reduce unnecessary API calls.
- Added error handling for unrecoverable refresh errors, ensuring that the application can gracefully handle token reuse and invalidation scenarios.
- Updated the MITM server management to handle port 443 conflicts, allowing users to kill processes occupying the port before starting the server.
- Improved user feedback in the MitmServerCard component regarding port conflicts and admin privileges.
- Refactored the ComboList component to streamline the display of media provider combos.
This update aims to enhance the reliability and user experience of the token management and MITM functionalities.
- Updated global CSS to implement a new brand color palette and improve light/dark theme consistency.
- Enhanced the MitmServerCard component to provide clearer user feedback regarding admin privileges.
- Filtered LLM combos in the CombosPage to ensure only relevant data is displayed.
- Improved APIPageClient layout for better usability and visual consistency.
- Added functionality to save and load DNS tool states in the MITM manager.
- Updated OAuth configuration URLs for Qwen to reflect the new endpoint structure.
- Refined tunnel management logic to improve reliability and user experience.
commit 8b2ab7c9e05689c1bf55002cc79db8d22a398c75
Author: kundeng <kundeng@live.com>
Date: Mon Apr 20 11:26:58 2026 -0400
fix: send providerSpecificData in Edit modal validate calls
The Check button in the Edit modal was sending only apiKey without the
Azure endpoint/deployment/org, causing validation to fail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
commit c894fa838d035ecd9a160339342371042697c327
Author: kundeng <kundeng@live.com>
Date: Mon Apr 20 01:45:13 2026 -0400
fix: persist Azure providerSpecificData and add connection test
- Read body.providerSpecificData in POST /api/providers so Azure fields
(endpoint, deployment, apiVersion, organization) are actually stored
- Add azure case to testApiKeyConnection so the Test button works
correctly instead of falling through to "not supported"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
commit 00bd1a4151f4e73616969e25d1786c87d1ec0d5e
Author: kundeng <kundeng@live.com>
Date: Mon Apr 20 01:24:39 2026 -0400
fix: add Azure validation and make Organization required
- Add Azure case to /api/providers/validate that sends a test chat
completion with api-key header and organization
- Pass Azure-specific data (endpoint, deployment, apiVersion, org) from
Add modal to validate endpoint
- Make Organization field required (needed for billing)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
commit a66a04daab69b07baa4cc6b28772249e4b25ea19
Author: kundeng <kundeng@live.com>
Date: Mon Apr 20 01:15:53 2026 -0400
fix: add Azure config fields to Add API Key modal
The Add modal was missing the Azure-specific fields (endpoint, deployment,
API version, organization) — only the Edit modal had them.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
commit 6ac3f4a97af8468d210594495ce754f5d7a7978a
Author: kundeng <kundeng@live.com>
Date: Mon Apr 20 01:06:45 2026 -0400
feat: add Azure OpenAI as a dedicated provider
Azure OpenAI uses a different URL scheme (deployments-based) and api-key
header auth instead of Bearer tokens. This adds a dedicated AzureExecutor
that constructs the correct URL and headers, plus dashboard UI fields for
endpoint, deployment, API version, and organization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add reusable EndpointPresetControl for CLI tool Base URL/API key
presets, stored in browser localStorage. Wire into Claude, Codex,
OpenCode, Droid, OpenClaw, Hermes, and Copilot cards. Allow
selecting preset API keys not in dashboard keys list.
Thanks @dmdfami for the contribution!
Co-authored-by: dmdfami <dmdfami@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Ensure /v1/models includes provider models added through alias mappings so compatible provider entries like huoshan/kimi-k2.6 are discoverable by clients.
Made-with: Cursor
Co-authored-by: Ken Lin <kenlin1988@foxmail.com>
The ReadableStream cancel() callback is not reliably invoked on client
disconnect under Next.js, causing emitter listeners (line/clear) and the
keepalive interval to accumulate, eventually triggering
MaxListenersExceededWarning.
Use request.signal as the primary disconnect trigger, with cancel() and
enqueue failures as fallbacks. Cleanup is idempotent via state.closed.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Removed the isCloud check from proxyFetch.js, simplifying the fetch patching logic.
- Updated MediaProviderDetailPage to include a new section for API key retrieval, improving user experience with clearer layout and additional notice text.
- Enhanced ConnectionRow to better handle email display names.
- Improved ProviderDetailPage to conditionally render provider notices and API key links.
- Refactored localDb, requestDetailsDb, and usageDb to remove unnecessary isCloud checks, streamlining database interactions.
- Updated OAuthModal to combine waiting and manual input steps for a more cohesive user flow.
- Added API key URLs to several providers in providers.js for better accessibility.
- Introduced Xiaomi MiMo as a new provider in providerModels.js and providers.js.
- Updated model alias mapping in model.js to include Xiaomi MiMo.
- Enhanced validation route to support Xiaomi MiMo API endpoints.
- Added Xiaomi MiMo to APIKEY_PROVIDERS with relevant details.
This update expands the range of supported providers, improving integration capabilities.
When a custom OpenAI-compatible provider uses a prefix that matches
a built-in alias (e.g. 'ark' -> 'volcengine-ark'), resolveProviderAlias()
converts it to the built-in provider ID, causing the provider-node
matching logic to be skipped. The request is then routed to the wrong
provider, resulting in a 404 model_not_found error.
Fix: always check provider-node prefix matching using the original
user input (parsed.providerAlias) before falling back to the resolved
alias, regardless of whether resolveProviderAlias() transformed it.
Co-authored-by: H1d3rOne <H1d3rOne@users.noreply.github.com>
Made-with: Cursor
- Refactored handleChatCore to include Caveman functionality, allowing for terse-style system prompts to reduce output token usage.
- Updated APIPageClient to manage Caveman settings, including enabling/disabling and selecting compression levels.
- Adjusted AntigravityExecutor to consolidate function declarations for compatibility with Gemini.
- Removed unnecessary console logs during translator initialization across multiple routes.
- Updated refreshCredentials methods in various executors (Antigravity, Base, Default, Github, Kiro) to accept optional proxyOptions for improved proxy handling.
- Modified token refresh logic to utilize proxy-aware fetch for better network management.
- Enhanced usage retrieval functions to support proxy options, ensuring seamless integration with proxy configurations.
- Updated ModelSelectModal and ProviderInfoCard components to incorporate kind filtering for improved user experience in model selection.
- Added validation for API keys in the provider validation route, including support for webSearch/webFetch providers.
- Introduced Cloudflare AI as a new provider with specific configurations in providerModels.js and providers.js.
- Updated DefaultExecutor to handle account ID resolution for Cloudflare AI connections.
- Enhanced AddApiKeyModal and EditConnectionModal to include account ID input for Cloudflare AI.
- Implemented validation for Cloudflare AI API key connections in testUtils.js and route.js.
- Updated UI components to reflect changes in provider management and connection handling.
- Added new image models for GPT 5.2, 5.3, and 5.4, including capabilities for text-to-image and editing.
- Updated embedding handling to include optional dimensions in requests.
- Introduced support for custom embedding providers, allowing dynamic fetching and validation of custom nodes.
- Improved image generation handling with Codex integration, including progress tracking and error handling.
- Enhanced UI components to support adding custom embeddings and displaying their status.
Add Volcengine Ark as a first-class API key provider with official model presets, endpoint configuration, API key validation, model discovery, connection testing, provider logo, and runtime alias mapping for `ark/*` model IDs.
Made-with: Cursor
Co-authored-by: kingsy <kingsylin@vip.qq.com>
- Introduced OpenCode Go provider with relevant configurations.
- Enhanced model management by allowing users to add and delete custom models.
- Updated UI components to support model selection for image types.
- Adjusted sidebar visibility to include image media kinds.
Add a shared resolveOllamaLocalHost() helper and wire it through the
executor, models/validate/test routes, so users can point ollama-local
at a remote Ollama instance instead of being locked to localhost:11434.
Also expose the host as an "Ollama Host URL" field in AddApiKeyModal
(empty = default localhost:11434), making the option reachable from the
dashboard without hand-editing db.json.
Co-authored-by: anuragg-saxenaa <anuragg.saxenaa@gmail.com>
Made-with: Cursor
- Add optional chaining check for navigator.clipboard
- Fallback to textarea + execCommand for SSR/non-HTTPS contexts
- Fixes TypeError when clipboard API is unavailable
Co-authored-by: Дмитрий Золотарь <d.zolotar@solarl.ru>
The sync icon has counter-clockwise arrows by design, making the spin
animation appear counter-clockwise even though CSS rotates clockwise.
Keep play_arrow as the icon in both loading and idle states so the
clockwise spin is visually unambiguous.
* fix: add multi-model support for Factory Droid CLI tool (closes#521)
* Add Claude Opus 4.7 to cc and cl provider lists
RESEARCH confirmed GA release April 16, 2026. Adding to:
- cc (Claude Code): claude-opus-4-7
- cl (Cline): anthropic/claude-opus-4.7
Refs: TICKET-20260416-ENG-O4.7-001
* fix: add Blackbox AI as a supported provider (closes#599)
- Integrated Google TTS languages from a separate module for better maintainability.
- Updated local device voice fetching to support both macOS and Windows, improving cross-platform compatibility.
- Enhanced dashboard route protection by adding dynamic settings for login requirements and tunnel access.
- Introduced UI elements for managing security settings related to API key requirements and dashboard access via tunnel.
- Added default TTS response example in the media provider page for better user guidance.
- Updated constants to reflect changes in TTS provider configurations.
This commit improves the overall user experience and security of the TTS features.