eddiekudo/OpenCursor

Design and research for the OpenCursor universal AI coding proxy

★ 0Forks 0TypeScriptGitHub ↗Compare

README

OpenCursor

OpenCursor is a local, Cursor-focused model router with a browser dashboard. Add API providers, expose their models through one OpenAI-compatible /v1 endpoint, and create ordered fallback combos that Cursor can select like ordinary models.

OpenCursor accepts Cursor's OpenAI chat-completions requests and adapts them to:

  • OpenAI-compatible APIs, including OpenAI, NVIDIA NIM, Kimi/Moonshot, Nemotron endpoints, and custom routers
  • Anthropic's native Messages API
  • Google's native Gemini GenerateContent API

The supplied local OpenAI-compatible provider at http://localhost:20128/v1 has been smoke-tested with claude-opus-design. No provider credential is stored in this repository.

How It Works

flowchart LR
    A["Cursor"] -->|"OpenAI API request"| B["OpenCursor localhost:10101"]
    B --> C{"Selected model"}
    C -->|"Direct model"| D["Provider adapter"]
    C -->|"Combo"| E["Ordered fallback targets"]
    E --> D
    D --> F["OpenAI / Anthropic / Gemini / NVIDIA / Kimi"]
    F -->|"Normalized response"| B
    B -->|"OpenAI-compatible response"| A
Loading

Cursor connects to one base URL, such as http://127.0.0.1:10101/v1, using your OpenCursor admission token. OpenCursor replaces that token with the selected provider credential, translates the request when needed, and returns an OpenAI-compatible response. Managed models use IDs such as anthropic/claude-sonnet-4-5; combos use the ID you assign, such as coding-primary.

For a combo, OpenCursor tries each target in order. Retries and fallback happen before a response is committed, so Cursor sees one model and one response.

Features

  • local browser dashboard for providers, combos, and Cursor setup
  • provider presets for OpenAI, Anthropic, Gemini, NVIDIA NIM, Kimi/Moonshot, Nemotron, and custom APIs
  • encrypted local storage for dashboard-managed API keys using AES-256-GCM and a separate random key file
  • environment-backed providers for configuration-as-code
  • authenticated GET /v1/models and POST /v1/chat/completions
  • direct managed model IDs and named priority fallback combos
  • OpenAI-compatible JSON and native SSE relay, including tool-call fields
  • Anthropic and Gemini request/response/tool normalization
  • bounded request, response, upstream error, and SSE-frame sizes
  • total and stream inactivity timeouts, heartbeats, and cancellation propagation
  • HTTPS enforcement, private-network opt-in, redirect rejection, and per-connection DNS/IP validation
  • optional official cloudflared child-process lifecycle management

Anthropic and Gemini streaming requests currently return a valid OpenAI SSE response after the native JSON response completes. OpenAI-compatible providers retain token-by-token native streaming.

Requirements

  • Node.js 22.19 or newer
  • npm
  • at least one supported provider credential
  • the official cloudflared executable on PATH only when using --tunnel

Install

npm ci
npm run build

Copy the example configuration:

Copy-Item open-cursor.config.example.json open-cursor.config.json

On macOS or Linux:

cp open-cursor.config.example.json open-cursor.config.json

Both open-cursor.config.json and .open-cursor/ are gitignored. Environment provider credentials belong in environment variables. Credentials added in the dashboard are encrypted under .open-cursor/ and are never redisplayed.

Configure

The example configuration contains an environment-backed local provider and one combo:

{
  "host": "127.0.0.1",
  "port": 10100,
  "admissionTokenEnv": "OPEN_CURSOR_TOKEN",
  "statePath": ".open-cursor/state.json",
  "providers": {
    "local-router": {
      "displayName": "Local Router",
      "adapter": "openai-compatible",
      "baseUrl": "http://localhost:20128/v1",
      "apiKeyEnv": "OPEN_CURSOR_UPSTREAM_KEY",
      "models": ["claude-opus-design"],
      "allowPrivateNetwork": true
    }
  },
  "modelRoutes": {
    "claude-opus-design": {
      "provider": "local-router",
      "upstreamModel": "claude-opus-design"
    }
  },
  "combos": {
    "coding-primary": {
      "targets": [
        { "provider": "local-router", "model": "claude-opus-design" }
      ]
    }
  }
}

adapter is one of openai-compatible, anthropic, or gemini. Set allowPrivateNetwork only for an intentional loopback or LAN endpoint.

Generate an admission token with at least 32 random bytes:

node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))"

Set the token and configured provider credential for the current PowerShell session:

$env:OPEN_CURSOR_TOKEN = '<generated-admission-token>'
$env:OPEN_CURSOR_UPSTREAM_KEY = '<provider-api-key>'

On macOS or Linux:

export OPEN_CURSOR_TOKEN='<generated-admission-token>'
export OPEN_CURSOR_UPSTREAM_KEY='<provider-api-key>'

OpenCursor refuses to start when configuration is invalid, required environment secrets are absent, or the admission token is shorter than 32 bytes.

Run

Build and start OpenCursor:

npm start -- start --config open-cursor.config.json

After the server is listening, OpenCursor opens /dashboard in your browser. Use --no-open to suppress this:

npm start -- start --config open-cursor.config.json --no-open

Enter the admission token in the dashboard, then add providers and create combos. Provider models are entered explicitly so no credential is used for undocumented discovery calls.

An optional Cloudflare Quick Tunnel can be started with --tunnel. It creates public ingress to your machine, so use a fresh admission token and stop it when finished:

npm start -- start --config open-cursor.config.json --tunnel

Cursor Setup

In Cursor's custom OpenAI-compatible provider settings, use:

  • Base URL: the dashboard's Cursor base URL, for example http://127.0.0.1:10101/v1
  • API key: the value of OPEN_CURSOR_TOKEN
  • Model: a model shown in the dashboard, such as local-router/claude-opus-design, a configured alias such as claude-opus-design, or a combo such as coding-primary

Environment providers expose aliases declared in modelRoutes; dashboard-managed providers expose provider-id/model-id. Every valid combo is also advertised by GET /v1/models.

API

Liveness and static dashboard files do not require authentication. All /api/* and /v1/* requests require the admission token.

curl http://127.0.0.1:10100/healthz
curl http://127.0.0.1:10100/v1/models -H "Authorization: Bearer $OPEN_CURSOR_TOKEN"
curl http://127.0.0.1:10100/v1/chat/completions \
  -H "Authorization: Bearer $OPEN_CURSOR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"model":"coding-primary","messages":[{"role":"user","content":"Hello"}],"stream":true}'

Security Model

  • Authentication runs before request-body parsing or provider resolution.
  • Admission tokens are compared as fixed-length SHA-256 digests with a timing-safe comparison.
  • Dashboard-managed credentials are encrypted at rest with AES-256-GCM; the state and key files are created with owner-only permissions where the platform supports it.
  • Credentials are omitted from provider and overview responses and are never redisplayed in the dashboard.
  • Public destinations require HTTPS. Loopback and private destinations require explicit opt-in.
  • Metadata, link-local, private, loopback, multicast, reserved, and mapped-private addresses are blocked for public providers.
  • DNS results are validated before dispatch and by the socket connection lookup callback.
  • Redirects are disabled, outbound headers are allowlisted, and inbound authorization/cookies are not forwarded.
  • Logs contain generic request metadata only, never prompts, completions, headers, URLs, or credentials.
  • The service binds to loopback by default. No public tunnel starts unless --tunnel is supplied.

The dashboard HTML is locally accessible but contains no credentials or provider data. Its management calls require the admission token, retained only in browser sessionStorage.

Validation

npm run typecheck
npm test
npm run build
npm audit

Automated tests cover configuration and secret validation, encrypted state, provider and combo management, all protocol adapters, ordered JSON/SSE fallback, authentication, destination policy, bounded transport behavior, dashboard security headers/assets, real Node HTTP handling, and cloudflared fixtures.

No public tunnel is opened during automated validation. Public Cursor cloud compatibility remains an explicit, separately approved acceptance step.

Attribution

The provider-registry and combo-routing concepts were informed by OmniRoute, used as an MIT-licensed reference. OpenCursor is a separate Cursor-specific implementation rather than a wholesale copy of OmniRoute's application.

Contributors

eddiekudo

Issues