feat: add Cursor as a cloud LLM provider via local cursor-bridge sidecar - #5504
feat: add Cursor as a cloud LLM provider via local cursor-bridge sidecar#5504AnmolKamboj wants to merge 10 commits into
Conversation
Adds scripts/cursor-bridge, a small Node service that adapts the Cursor SDK to an OpenAI-compatible surface (/v1/models + /v1/chat/completions with SSE). Cursor.models.list() entries are expanded into per-variant model ids so reasoning effort / thinking / context presets are selectable straight from the existing model dropdowns. Registers a cursor preset in both builtin provider catalogs pointing at the sidecar.
Replaces the flat variant list with a Cursor-specific picker in the AI settings model field: base models sorted alphabetically (display name + model id), with separate dropdowns below for each supported parameter (reasoning effort / thinking, context window, fast mode). Selections serialize to a composable '~p=param:value' model id grammar that the cursor-bridge decodes back into SDK model params, so options combine freely instead of exploding the list.
# Conflicts: # src/openhuman/config/schema/cloud_providers.rs
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis change adds Cursor as a built-in provider, adds Cursor-specific model selection and localization, and introduces a local bridge that exposes Cursor agents through an OpenAI-compatible API. ChangesCursor provider integration
Estimated code review effort: 4 (Complex) | ~45 minutes Mergeability Score: 🟡 Moderate · up to The new local bridge can expose reusable Cursor API keys to another process that claims its configured port, and its Windows launcher can report success even when the bridge fails to start. These are bounded but concrete security and availability risks that should be fixed or explicitly accepted before merging. Sequence Diagram(s)sequenceDiagram
participant OpenAIClient
participant CursorBridge
participant CursorAgent
OpenAIClient->>CursorBridge: POST /v1/chat/completions
CursorBridge->>CursorBridge: Validate request and resolve model
CursorBridge->>CursorAgent: Agent.prompt with normalized prompt
CursorAgent-->>CursorBridge: Completion text
CursorBridge-->>OpenAIClient: JSON or SSE completion
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (4)
app/src/components/settings/panels/ai/ModelEntryField.tsx (1)
49-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLog the malformed-parameter branch.
The
catchat Line 64 discards the decode error and returns the whole raw string asid, including the~p=suffix. That value then becomes a catalog group key at Line 122 and a serialization input at Line 139, so a laterserializeCursorSelectioncall re-encodes an id that already carries an encoded suffix. The fallback is safe, but it is silent, and the sibling code inAIPanel.tsxlogs provider failures under an[ai-settings]prefix.Add a grep-friendly diagnostic on this branch. Log the parameter key only, not the decoded value.
As per coding guidelines: "Add verbose, grep-friendly diagnostics for new or changed flows, including branches, external calls, retries, state transitions, and errors; never log secrets or full PII."
🔍 Proposed diagnostic
} catch { + console.warn('[ai-settings][cursor] malformed encoded model parameters; ignoring parameters'); return { id: value, parameters: new Map() }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/components/settings/panels/ai/ModelEntryField.tsx` around lines 49 - 69, Update the decode-error catch in parseCursorSelection to emit a grep-friendly diagnostic using the [ai-settings] prefix and the malformed parameter key only; do not log the decoded value or full raw input. Preserve the existing safe fallback return behavior.Source: Coding guidelines
scripts/cursor-bridge/src/index.ts (3)
285-288: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winGuard the catch-all against a response that already started.
streamTextcallsres.writeHeadat Line 172 and then writes SSE frames. If any error reaches this handler after that point,sendErrorcallssendJson, which callsres.writeHeada second time. Node then throwsERR_HTTP_HEADERS_SENTinside the catch block, and the client connection is left open.Check
res.headersSentbefore writing an error response.🐛 Proposed guard
} catch (err) { const message = err instanceof Error ? err.message : String(err); + if (res.headersSent) { + console.error("[cursor-bridge] error after response started:", message); + res.end(); + return; + } sendError(res, 502, message); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cursor-bridge/src/index.ts` around lines 285 - 288, Update the catch-all handler in the request flow to check res.headersSent before calling sendError. Only write the 502 response when headers have not been sent; otherwise avoid sendError and ensure the already-started response is closed so the client connection does not remain open.
192-196: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winBound the header phase and the request body.
Line 293 sets
headersTimeout = 0. The comment at Line 291 justifies a long deadline for agent runs, which applies torequestTimeout, not to the header phase. With no header deadline, a client can open a socket, send partial headers, and hold the connection open without limit.
readBodyat Lines 192-196 accumulates every chunk into memory with no size cap. Combined withrequestTimeout = 0, a single request has neither a size bound nor a time bound.Keep
requestTimeout = 0for the agent run. Restore a finiteheadersTimeoutand reject oversized bodies.🛡️ Proposed bounds
+const MAX_BODY_BYTES = 8 * 1024 * 1024; + async function readBody(req: http.IncomingMessage): Promise<string> { const chunks: Buffer[] = []; + let total = 0; - for await (const chunk of req) chunks.push(chunk as Buffer); + for await (const chunk of req) { + const buf = chunk as Buffer; + total += buf.length; + if (total > MAX_BODY_BYTES) throw new Error("request body too large"); + chunks.push(buf); + } return Buffer.concat(chunks).toString("utf8").replace(/^\ufeff/, ""); }-// Agent runs can take minutes; disable Node's default request timeouts. +// Agent runs can take minutes, so the request deadline is disabled. The header +// phase is unrelated to the run duration and keeps a finite deadline. server.requestTimeout = 0; -server.headersTimeout = 0; +server.headersTimeout = 60_000;Also applies to: 291-293
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cursor-bridge/src/index.ts` around lines 192 - 196, Update the server configuration near requestTimeout and headersTimeout to restore a finite headersTimeout while keeping requestTimeout at 0 for long agent runs. Modify readBody to enforce a maximum request-body size and reject requests that exceed it before accumulating unbounded data, preserving UTF-8 decoding and BOM removal for accepted bodies.
243-246: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winState that streaming replays a completed run.
Agent.promptat Line 230 resolves only when the whole run finishes.streamTextthen splits the finished text into 48-character frames. The client therefore receives nothing until the agent completes, and the SSE frames arrive in a burst afterwards.This removes the latency benefit that a client expects from
stream: true. A client or an intermediary with an idle-read deadline can also drop the connection during the silent wait, because the first byte arrives only at the end.Send SSE keep-alive comment lines while the run is in progress, or document the limitation in the file header at Lines 5-8.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cursor-bridge/src/index.ts` around lines 243 - 246, Update the streaming path around Agent.prompt and streamText so stream requests emit SSE keep-alive comment lines while the run is still in progress, preventing an idle connection before the completed result is replayed. Preserve the existing final text framing, or document the completed-run replay limitation in the file header if live keep-alives cannot be added.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/components/settings/panels/ai/ModelEntryField.tsx`:
- Around line 168-176: Update the parameter field rendering in ModelEntryField
to compute the resolved label once from CURSOR_PARAMETER_LABELS[parameter] ??
parameter, then reuse it for both the visible label and SettingsSelect
aria-label. Associate the label and select with matching htmlFor and id values
so the accessible name matches the localized visible text.
- Around line 35-41: Update CursorModelSelector to call useT() and route all
newly introduced UI text through t(...), including CURSOR_PARAMETER_LABELS
values, option text, “Select a model” (reuse settings.ai.selectModel), and
“Default”. Add the corresponding locale keys for parameter labels, option text,
and the default option, preserving the existing rendered behavior.
In `@app/src/components/settings/panels/builtinCloudProviders.ts`:
- Around line 227-236: Add the `cursor` provider entry to the Rust
cloud-provider catalog consumed by
`src/openhuman/config/schema/cloud_providers.rs`, matching the JavaScript
definition’s slug and relevant metadata. Ensure the Rust catalog remains
synchronized with `builtinCloudProviders.ts`; do not remove the synchronization
claim.
In `@scripts/cursor-bridge/package.json`:
- Around line 8-12: Update scripts/cursor-bridge/package.json by pinning
`@cursor/sdk` to ^1.0.27, adding an engines.node requirement of >=22.18.0, and
adding `@types/node` under devDependencies for type-checking and editor support.
In `@scripts/cursor-bridge/src/index.ts`:
- Around line 262-289: Update the request handling in the http.createServer
callback to validate the Host header against the configured HOST authority and
return an error before apiKeyFrom or any agent handler runs when it does not
match. Also reject every request containing an Origin header before reaching
handleModels, handleCompletion, or Agent.prompt, while preserving the existing
health-route behavior only for validated requests.
---
Nitpick comments:
In `@app/src/components/settings/panels/ai/ModelEntryField.tsx`:
- Around line 49-69: Update the decode-error catch in parseCursorSelection to
emit a grep-friendly diagnostic using the [ai-settings] prefix and the malformed
parameter key only; do not log the decoded value or full raw input. Preserve the
existing safe fallback return behavior.
In `@scripts/cursor-bridge/src/index.ts`:
- Around line 285-288: Update the catch-all handler in the request flow to check
res.headersSent before calling sendError. Only write the 502 response when
headers have not been sent; otherwise avoid sendError and ensure the
already-started response is closed so the client connection does not remain
open.
- Around line 192-196: Update the server configuration near requestTimeout and
headersTimeout to restore a finite headersTimeout while keeping requestTimeout
at 0 for long agent runs. Modify readBody to enforce a maximum request-body size
and reject requests that exceed it before accumulating unbounded data,
preserving UTF-8 decoding and BOM removal for accepted bodies.
- Around line 243-246: Update the streaming path around Agent.prompt and
streamText so stream requests emit SSE keep-alive comment lines while the run is
still in progress, preventing an idle connection before the completed result is
replayed. Preserve the existing final text framing, or document the
completed-run replay limitation in the file header if live keep-alives cannot be
added.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 69bbabf3-81fc-4679-82af-0a8519b26b4a
📒 Files selected for processing (6)
app/src/components/settings/panels/AIPanel.tsxapp/src/components/settings/panels/ai/ModelEntryField.tsxapp/src/components/settings/panels/builtinCloudProviders.tsscripts/cursor-bridge/.gitignorescripts/cursor-bridge/package.jsonscripts/cursor-bridge/src/index.ts
There was a problem hiding this comment.
Requesting changes: 1 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0677 · 73,894 in / 19,072 out · 53,915 cached (73%) · z-ai/glm-5.2
critique: $0.0301 · 21,890 in / 9,617 out · 14,923 cached (68%) · z-ai/glm-5.2
security: $0.0120 · 16,837 in / 2,880 out · 12,302 cached (73%) · z-ai/glm-5.2
tests: $0.0073 · 8,535 in / 2,083 out · 6,628 cached (78%) · z-ai/glm-5.2
description: $0.0118 · 9,737 in / 3,865 out · 7,639 cached (78%) · z-ai/glm-5.2
What this change touches23 files, +920 -2 across 7 components. The code graph knows nothing about these files yet — normal for newly added files, and a cold index otherwise. flowchart LR
n0["scripts/cursor-bridge/src<br/>1 file +366 -0"]:::changed
n1["scripts/cursor-bridge<br/>3 files +250 -0"]:::changed
n2["app/src/components/settings/panels/ai<br/>1 file +188 -1"]:::changed
n3["app/src/lib/i18n<br/>14 files +70 -0"]:::changed
n4["app/src/components/settings/panels/__tests__<br/>1 file +31 -1"]:::changed
n5["app/src/components/settings/panels<br/>2 files +14 -0"]:::changed
n6["root<br/>1 file +1 -0<br/>1 finding"]:::flagged
classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Green: changed. Grey: untouched, reached through an import or a call. Orange: has findings. Red: has a finding that blocks the merge.
Changed files
|
Harden the local bridge and make Cursor model selection accessible, localized, and regression-tested. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
scripts/cursor-bridge/src/index.ts (5)
263-283: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy liftSensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information
Reachability: External
Do not send bearer credentials over plaintext HTTP.
The bridge accepts
Authorization: Bearer <key>onhttp://127.0.0.1:8790. Host and Origin validation do not provide transport confidentiality. Keep the key in the bridge environment, or protect this hop with authenticated encryption.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cursor-bridge/src/index.ts` around lines 263 - 283, Update the request handling in the server callback to stop accepting bearer credentials over plaintext HTTP; remove the Authorization-header API-key path from apiKeyFrom and require the bridge-environment key instead, unless this hop is protected by authenticated encryption. Preserve the existing Host and Origin validation and unauthenticated health endpoint behavior.
293-295: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winInformation Disclosure (CWE-209): Generation of Error Message Containing Sensitive Information
Reachability: External
Return fixed public messages for backend failures.
The catch block and
result.error?.messageforward internal error details throughsendError. Log sanitized diagnostics separately.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cursor-bridge/src/index.ts` around lines 293 - 295, Update the backend failure handling in the catch block and the result.error path to stop exposing internal error messages through sendError. Return fixed public-safe messages for these failures, while logging the sanitized diagnostic details separately for troubleshooting.
152-191: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winInformation Disclosure (CWE-524)
Reachability: External
Mark identity-varying responses as
no-store.Add
Cache-Control: no-storeinsendJsonandstreamText./v1/modelsand chat completions depend on the request API key.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cursor-bridge/src/index.ts` around lines 152 - 191, Update sendJson and streamText to include Cache-Control: no-store in their response headers, while preserving the existing content type and streaming headers.
152-191: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy liftBroken Authentication (CWE-306): Missing Authentication for Critical Function
Reachability: External
Require bridge authentication separate from the Cursor API key.
When
CURSOR_API_KEYis set, any local process that sends the expectedHostheader and omitsOriginandAuthorizationcan reachAgent.promptwith the environment key. Add a per-run bridge token or authenticated IPC channel.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cursor-bridge/src/index.ts` around lines 152 - 191, Update the bridge authentication flow around apiKeyFrom and the request handler so access requires a per-run bridge token or authenticated IPC channel independent of CURSOR_API_KEY. Do not allow missing Authorization to fall back directly to process.env.CURSOR_API_KEY; validate the bridge credential before invoking Agent.prompt, while preserving CURSOR_API_KEY only for upstream Cursor API authentication.
299-305: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winKeep finite request deadlines and bound request bodies.
server.requestTimeout = 0andserver.headersTimeout = 0remove Node’s request and header deadlines. BecausereadBodybuffers all chunks until end-of-stream without a size or rate limit, a local client can hold connections open and grow memory. Keep finite timeout values and reject oversized request bodies. Agent execution begins after the request body is read, so it does not require disabling these deadlines.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cursor-bridge/src/index.ts` around lines 299 - 305, Update the server configuration near server.listen to retain finite requestTimeout and headersTimeout values instead of setting them to zero, and update readBody to enforce a maximum request-body size while buffering chunks. Reject oversized bodies promptly and preserve normal request handling for bodies within the limit.
🧹 Nitpick comments (1)
app/src/components/settings/panels/ai/ModelEntryField.tsx (1)
137-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd diagnostics for Cursor selection changes.
updateParameterchanges the persisted Cursor model selection throughonModelChange. The new flow has no diagnostic event. Add privacy-safe, grep-friendly diagnostics for model and parameter selection transitions. Do not log credentials or user-authored text.As per coding guidelines: “Add verbose, grep-friendly diagnostics for new or changed flows, including branches, external calls, retries, state transitions, and errors; never log secrets or full PII.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/components/settings/panels/ai/ModelEntryField.tsx` around lines 137 - 141, Add privacy-safe, grep-friendly diagnostics in updateParameter for Cursor selection transitions, recording the model identifier and parameter name along with the change direction or resulting selection state. Do not include parameter values, credentials, or other user-authored text, and preserve the existing Map update and onModelChange behavior.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/lib/i18n/de.ts`:
- Around line 4990-4991: Update the German translations for
settings.ai.cursorParameterReasoningEffort and
settings.ai.cursorParameterThinking to use distinct, idiomatic labels that
clearly differentiate reasoning intensity from thinking mode, such as
“Denkintensität” and “Denkmodus”.
In `@app/src/lib/i18n/id.ts`:
- Line 4878: Update the translation value for the
`settings.ai.cursorParameterDefault` key in the Indonesian locale from the
English label to the existing Indonesian term `Bawaan`, matching the value used
by the related translation at line 4788.
---
Outside diff comments:
In `@scripts/cursor-bridge/src/index.ts`:
- Around line 263-283: Update the request handling in the server callback to
stop accepting bearer credentials over plaintext HTTP; remove the
Authorization-header API-key path from apiKeyFrom and require the
bridge-environment key instead, unless this hop is protected by authenticated
encryption. Preserve the existing Host and Origin validation and unauthenticated
health endpoint behavior.
- Around line 293-295: Update the backend failure handling in the catch block
and the result.error path to stop exposing internal error messages through
sendError. Return fixed public-safe messages for these failures, while logging
the sanitized diagnostic details separately for troubleshooting.
- Around line 152-191: Update sendJson and streamText to include Cache-Control:
no-store in their response headers, while preserving the existing content type
and streaming headers.
- Around line 152-191: Update the bridge authentication flow around apiKeyFrom
and the request handler so access requires a per-run bridge token or
authenticated IPC channel independent of CURSOR_API_KEY. Do not allow missing
Authorization to fall back directly to process.env.CURSOR_API_KEY; validate the
bridge credential before invoking Agent.prompt, while preserving CURSOR_API_KEY
only for upstream Cursor API authentication.
- Around line 299-305: Update the server configuration near server.listen to
retain finite requestTimeout and headersTimeout values instead of setting them
to zero, and update readBody to enforce a maximum request-body size while
buffering chunks. Reject oversized bodies promptly and preserve normal request
handling for bodies within the limit.
---
Nitpick comments:
In `@app/src/components/settings/panels/ai/ModelEntryField.tsx`:
- Around line 137-141: Add privacy-safe, grep-friendly diagnostics in
updateParameter for Cursor selection transitions, recording the model identifier
and parameter name along with the change direction or resulting selection state.
Do not include parameter values, credentials, or other user-authored text, and
preserve the existing Map update and onModelChange behavior.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: fbfe50a1-40ad-4432-b22a-50ffc6a882f1
⛔ Files ignored due to path filters (1)
scripts/cursor-bridge/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (19)
.gitignoreapp/src/components/settings/panels/__tests__/ModelEntryField.test.tsxapp/src/components/settings/panels/ai/ModelEntryField.tsxapp/src/lib/i18n/ar.tsapp/src/lib/i18n/bn.tsapp/src/lib/i18n/de.tsapp/src/lib/i18n/en.tsapp/src/lib/i18n/es.tsapp/src/lib/i18n/fr.tsapp/src/lib/i18n/hi.tsapp/src/lib/i18n/id.tsapp/src/lib/i18n/it.tsapp/src/lib/i18n/ko.tsapp/src/lib/i18n/pl.tsapp/src/lib/i18n/pt.tsapp/src/lib/i18n/ru.tsapp/src/lib/i18n/zh-CN.tsscripts/cursor-bridge/package.jsonscripts/cursor-bridge/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- scripts/cursor-bridge/package.json
- bridge: authenticate callers with a bridge-local token (CURSOR_BRIDGE_TOKEN or generated .bridge-token); CURSOR_API_KEY stays in the bridge environment and is used only for upstream Cursor auth, so client credentials never transit the plaintext HTTP hop - bridge: public-safe error responses (diagnostics logged locally), Cache-Control: no-store, finite request/header timeouts, and a 10 MiB request-body limit with prompt 413 rejection - i18n: idiomatic German (Denkintensitaet/Denkmodus) and Indonesian (Bawaan) labels for Cursor parameter controls - ui: privacy-safe debug log on Cursor parameter changes (model id and parameter name only, never values) Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
scripts/cursor-bridge/src/index.ts (1)
321-357: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftAdd automated route tests for this security boundary.
Add tests for Host rejection, Origin rejection, missing and invalid bearer tokens, oversized bodies, invalid JSON, and streamed responses. Manual end-to-end testing does not protect these branches from regressions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cursor-bridge/src/index.ts` around lines 321 - 357, Add automated route-level tests for the HTTP server created in the request handler, covering Host rejection, Origin rejection, missing and invalid bearer tokens, oversized request bodies, invalid JSON, and streamed responses. Exercise each branch through actual HTTP requests and assert the status, response body, and streaming behavior without changing the existing security handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/cursor-bridge/src/index.ts`:
- Around line 58-61: Update the token-generation logging around the bridge
output in the main flow to remove the generated token from console output. Log
only TOKEN_FILE and instruct the user to read the saved file locally for the
provider API key, without exposing the bearer token in stdout or logs.
- Around line 284-289: Before the Agent.prompt call, validate or constrain
CURSOR_BACKEND_URL to a trusted HTTPS Cursor endpoint, rejecting arbitrary or
http:// values before CURSOR_API_KEY can be used. Preserve the existing
Agent.prompt options and ensure the validated backend configuration is what the
SDK uses.
---
Nitpick comments:
In `@scripts/cursor-bridge/src/index.ts`:
- Around line 321-357: Add automated route-level tests for the HTTP server
created in the request handler, covering Host rejection, Origin rejection,
missing and invalid bearer tokens, oversized request bodies, invalid JSON, and
streamed responses. Exercise each branch through actual HTTP requests and assert
the status, response body, and streaming behavior without changing the existing
security handling.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3660cbd7-9a13-4cf5-b077-7afd2daee8a9
📒 Files selected for processing (6)
app/src/components/settings/panels/ai/ModelEntryField.tsxapp/src/components/settings/panels/builtinCloudProviders.tsapp/src/lib/i18n/de.tsapp/src/lib/i18n/id.tsscripts/cursor-bridge/.gitignorescripts/cursor-bridge/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- scripts/cursor-bridge/.gitignore
- app/src/lib/i18n/de.ts
- app/src/lib/i18n/id.ts
- app/src/components/settings/panels/ai/ModelEntryField.tsx
- app/src/components/settings/panels/builtinCloudProviders.ts
There was a problem hiding this comment.
The previously-blocking findings are resolved. Clearing the changes request.
$0.0755 · 77,126 in / 23,065 out · 60,665 cached (79%) · z-ai/glm-5.2
critique: $0.0316 · 19,697 in / 11,167 out · 15,834 cached (80%) · z-ai/glm-5.2
security: $0.0160 · 19,571 in / 4,624 out · 15,748 cached (80%) · z-ai/glm-5.2
tests: $0.0152 · 18,329 in / 4,186 out · 13,928 cached (76%) · z-ai/glm-5.2
description: $0.0127 · 19,529 in / 3,088 out · 15,155 cached (78%) · z-ai/glm-5.2
Drop the generated bridge-token split. OpenHuman's Cursor field takes the user's crsr_... key (Bearer), matching OpenAI/Groq. The sidecar still binds 127.0.0.1 and keeps Host/Origin checks, no-store, body limits, and public-safe errors. Also start the bridge from run-dev-win.sh / start.ps1 so Cursor models work without a separate terminal. Co-authored-by: Cursor <cursoragent@cursor.com>
The Cursor SDK reads CURSOR_BACKEND_URL from the environment. Allow only https://api2.cursor.sh and https://api.cursor.com so a poisoned override cannot send the caller's API key to an arbitrary host. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/cursor-bridge/src/index.ts`:
- Around line 153-157: Update the bridge health/authentication flow around
apiKeyFrom and the local /health endpoint so the local client is authenticated
before any Cursor API key is accepted or forwarded. Replace the unauthenticated
loopback HTTP trust with platform-authenticated IPC or another authenticated
local transport, and ensure start.ps1 validates that authentication rather than
accepting any { "ok": true } response.
In `@scripts/cursor-bridge/start.ps1`:
- Around line 7-10: Update Test-BridgeUp and the other bridge URL usages to
build the health-probe and startup requests with the configured
CURSOR_BRIDGE_PORT value instead of hardcoded port 8790, while preserving 8790
as the default when the environment variable is unset.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 490c6eda-2f9e-4dc4-a076-2a25a77bbd7a
📒 Files selected for processing (5)
app/src/components/settings/panels/builtinCloudProviders.tsscripts/cursor-bridge/.gitignorescripts/cursor-bridge/src/index.tsscripts/cursor-bridge/start.ps1scripts/run-dev-win.sh
🚧 Files skipped from review as they are similar to previous changes (2)
- scripts/cursor-bridge/.gitignore
- app/src/components/settings/panels/builtinCloudProviders.ts
Health probes and status messages used a hardcoded 8790, so a custom CURSOR_BRIDGE_PORT would start a second process while thinking the default port was already up. Default remains 8790 when the env var is unset. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/cursor-bridge/src/index.ts`:
- Line 349: Ensure backend validation failures from assertTrustedCursorBackend
reach the launcher with a non-zero status. Update the timeout/error path in
start.ps1 to exit 1, or propagate the cursor-bridge child process exit status,
while preserving normal successful startup behavior.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: cef71ed0-d155-4856-b8a3-06e685d6f234
📒 Files selected for processing (1)
scripts/cursor-bridge/src/index.ts
Start the node process with -PassThru and exit with its status if assertTrustedCursorBackend (or any other boot error) kills the child before /health is up. Successful startup and the 15s timeout path are unchanged (timeout still exits 1). Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
scripts/cursor-bridge: a small local Node/TypeScript sidecar that adapts the agent-based Cursor SDK (Agent.prompt) to the OpenAI-compatible HTTP surface (/v1/models,/v1/chat/completions) OpenHuman already speaks. No core changes required.http://127.0.0.1:8790/v1, bearer = Cursor API key).~p=param:value) and surfaced in Settings as a grouped selector: one sorted base-model dropdown plus per-parameter dropdowns below it, instead of a long flat variant list.Problem
Solution
scripts/cursor-bridgeruns locally, translates OpenAI-style requests into Cursor SDK calls (dedicated workspace cwd, per-key model cache, optional SSE streaming), and advertises the base model plus each supported parameter value as distinct model IDs.ModelEntryFieldparses the~p=encoding for thecursorprovider and renders a base-model select (sorted, showing display name + model ID) with separate selects for each available parameter; selections are serialized back into the encoded ID, so routing/storage are unchanged.tinymemoryrepo — the UI preset alone is sufficient; happy to open a companion PR there if you want it first-class.Submission Checklist
ModelEntryField.test.tsxcovers parse/serialize round-trip, malformed encoded IDs, and existing picker behavior (10 tests). Bridge HTTP paths stay outside the app harness.scripts/cursor-bridge— that sidecar is not in Vitest/llvm-cov; UI changed lines are covered by the ModelEntryField tests above.Impact
127.0.0.1only and requires the user's own Cursor API key as bearer; no keys are logged.Related
tinymemorycatalog entry for a Rust-side builtin preset.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
AnmolKamboj:feat/cursor-providere13cee83dValidation Run
pnpm --filter openhuman-app format:check— passespnpm typecheck— passespnpm exec vitest run --config test/vitest.config.ts src/components/settings/panels/__tests__/ModelEntryField.test.tsx— 10 passedAIPanel.tsx, untouched by this PR)Validation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
Parity Contract
ModelEntryField/AIPanelprop additions.Duplicate / Superseded PR Handling