Files
OmniRoute/docs/providers/CURSOR-DOCKER.md
SB Yoon c130f2aa1c feat(providers): Cursor PKCE login with Bearer quota, auto router, and empty-turn errors (#9909)
5 — Cursor PKCE login com Bearer quota, auto router e empty-turn errors. Feature completa e testada (11 arquivos de teste, 133 testes focados, todos verdes).

**Validação (worktree combinado `.claude/worktrees/fix-9909`, board sobre `origin/release/v3.8.50`):**
- 3 conflitos reais resolvidos: `config/quality/eslint-suppressions.json` (aditivo), `open-sse/config/providers/registry/cursor/index.ts` (dedup de 208 entradas de catálogo, 0 IDs duplicados verificado), `open-sse/executors/cursor.ts` (imports aditivos).
- `npm run typecheck:core`: limpo.
- `check-changelog-integrity`, `check-file-size`, `check-complexity` (2615/2774), `check-cognitive-complexity` (1175/1223), `check-dead-code` (410/416): todos OK.
- `check-public-creds`: 1 entrada obsoleta pré-existente na allowlist (`copilot-m365-web.ts:330`), já presente no tip da release — não é desta PR.
- `npm run lint`: 0 errors (5 warnings pré-existentes).
- Testes focados (`cursor-agent-cli-version`, `cursor-available-models`, `cursor-catalog-combo-compat`, `cursor-errors-classify`, `cursor-login-pkce`, `cursor-model-effort-suffix-7289`, `cursor-streaming`, `cursor-token-extractor`, `cursor-token-refresh-wiring`, `cursor-usage-fetcher`, `empty-stream-no-content-8649`): 133/133 verdes.
- Corrigido durante a validação: 1 teste novo da própria PR (`cursor-model-effort-suffix-7289.test.ts`, "splits effort off legacy grok- ids") colidia com `CURSOR_MODEL_ALIASES` já mesclado na release (mapeia `grok-4.5-high` → `cursor-grok-4.5-high` antes do fallback legado rodar); ajustado para usar um id não-aliasado (`grok-3-high`) que de fato exercita o fallback — commit `68b58ed`.

Obrigado pela contribuição, @yansigit — feature robusta com boa cobertura de testes.
2026-08-21 10:28:01 -03:00

5.6 KiB
Raw Blame History

title, version, lastUpdated
title version lastUpdated
Cursor Provider in Docker Environments 3.8.50 2026-08-17

Cursor Provider in Docker Environments

When OmniRoute runs inside Docker, the legacy Import from Cursor IDE / cursor-agent flows fail because the container cannot see the host Cursor install. Use Login with Cursor (deep-control PKCE) instead.

Why IDE / CLI Import Fails in Docker

  1. Filesystem isolation — Auto-import looks for Linux paths such as ~/.config/Cursor/User/globalStorage/state.vscdb inside the container. On Docker Desktop for macOS the host IDE DB is not mounted by default, and the container OS is Linux even when the host is Darwin.
  2. No cursor-agent binary — Official OmniRoute images do not ship cursor-agent. Available Models previously shelled out to cursor-agent --list-models and fell back to a static catalog.
  3. Wrong binary — Do not bind-mount a macOS cursor-agent into a Linux container. It will not execute.
  1. Open Dashboard → Providers → Cursor.
  2. Choose the Login with Cursor tab.
  3. Click Login with Cursor — OmniRoute opens https://cursor.com/loginDeepControl?… in your host browser.
  4. Approve the login in the browser, then return to the dashboard. OmniRoute polls api2.cursor.sh/auth/poll until tokens arrive.
  5. OmniRoute stores access + refresh tokens and refreshes them via https://api2.cursor.sh/auth/exchange_user_api_key.

This path does not require Cursor IDE or cursor-agent inside the container.

Model discovery

With a logged-in connection, Available Models / Auto-Sync prefers Cursors HTTP AiService/AvailableModels catalog using the connection bearer token. If that fails, OmniRoute still tries host cursor-agent (when present), then the static registry seed.

OmniRoute always exposes auto in the catalog (display “Auto”), plus OpenCodex-style router modes auto-cost, auto-balance, and auto-intelligence. On the wire these map to Cursors default model (with an optimization ModelParameter for the three variants). Prefer cu/auto when premium models are out of usage — Auto often still has budget.

Live catalog is exclusive when synced

After a successful Cursor model sync (cursor-agent --list-models → persisted synced catalog, or the bearer-authenticated AvailableModels fetch above), the dashboard, /v1/models, and Test All list:

  1. Models returned by the live sync
  2. Injected auto-router ids: auto, auto-cost, auto-balance, auto-intelligence
  3. Operator custom models (Import / manual) — never pruned by sync

The large static registry under open-sse/config/providers/registry/cursor/ is offline fallback only. When synced is empty (or discovery fails), listing falls back to that registry.

Effort-suffixed ids (for example claude-4.6-sonnet-high) may still be requested at runtime: resolveRequestedModel strips the suffix into a wire ModelParameter. Exclusive listing intentionally hides those static variants from Test All so probes match what Cursor actually returns as available.

Helpers

  • providerUsesExclusiveSyncedListing("cursor"|"cu")src/lib/providers/modelListingCapability.ts
  • mergeProviderModelListing — dashboard merge
  • ensureCursorAutoCatalogEntry — auto* inject on discovery + listing
  • shouldSuppressStaticModelForExclusiveListing/v1/models static loop

Provider Limits (quota)

Usage → Provider Limits for Cursor uses Bearer APIs on api2.cursor.sh (GetCurrentPeriodUsage → usage summary → auth/usage) after PKCE or token import. The legacy cookie/cursor.com dashboard path remains a last fallback for older IDE-imported sessions.

Windows typically include Total, Auto + Composer, and API. If limits look empty, re-run Login with Cursor or re-import tokens (IDE import alone is no longer required).

Empty turns / out of usage

When Cursor accepts a Run but returns no assistant text (common when premium usage is exhausted), OmniRoute surfaces an actionable 429 (quota cues) or 502 with guidance — not a bare “Provider returned empty content”. Streaming failures such as not_found: AI Model Not Found (usage window exhausted) are classified as Cursor rate limit / usage exceeded and keep that message through the SSE pipeline (the shared empty-stream guard does not overwrite an already-emitted error). Check Provider Limits, try model auto, or raise Cursor plan limits.

Client version (headless)

Without a local cursor-agent install, OmniRoute resolves x-cursor-client-version via env CURSOR_AGENT_CLI_VERSION, then a disk-cached scrape of the Cursor installer script, then a pinned build id. Override with CURSOR_AGENT_CLI_VERSION when needed.

Fallback: Manual Token Import

If you cannot complete browser login:

  1. On the host, extract tokens from Cursors state.vscdb:

    sqlite3 "$HOME/Library/Application Support/Cursor/User/globalStorage/state.vscdb" \
      "SELECT key, value FROM ItemTable WHERE key IN ('cursorAuth/accessToken','cursorAuth/refreshToken','storage.serviceMachineId');"
    
  2. Open Import token in the Cursor auth modal.

  3. Paste Access Token and, when available, Refresh Token (required for automatic refresh). Machine ID is optional.

Access-token-only imports still work but will expire without a refresh token — re-import when chat returns authentication errors.