From a8ddc384e7fd893a76c17c7d55efee39298b701f Mon Sep 17 00:00:00 2001 From: AULORBE <18585860+aulorbe@users.noreply.github.com> Date: Wed, 20 May 2026 22:21:19 -0700 Subject: [PATCH 1/2] fix(cursor-proxy): fix double-wrapped Result hiding setup failures The error handling in setupCursorProxy and startCursorProxy used asyncTryCatchIf(isOperationalError, () => wrapSshCall(...)) which double-wrapped the Result: wrapSshCall returns Err() on failure (doesn't throw), so asyncTryCatchIf always saw a successful return and wrapped it in Ok(Err(...)). This meant result.ok was always true, causing misleading output like "Cursor proxy started" even when every step failed. Fix: call wrapSshCall directly and check its returned Result. Also adds macOS DNS cache flush after /etc/hosts modification and early-return on deploy/hosts failure with accurate warning messages. --- packages/cli/src/shared/cursor-proxy.ts | 27 ++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/shared/cursor-proxy.ts b/packages/cli/src/shared/cursor-proxy.ts index dee88866f..4e9a14c6d 100644 --- a/packages/cli/src/shared/cursor-proxy.ts +++ b/packages/cli/src/shared/cursor-proxy.ts @@ -13,7 +13,6 @@ import type { CloudRunner } from "./agent-setup.js"; import { wrapSshCall } from "./agent-setup.js"; -import { asyncTryCatchIf, isOperationalError } from "./result.js"; import { logInfo, logStep, logWarn } from "./ui.js"; // ── Protobuf helpers (used in proxy scripts) ──────────────────────────────── @@ -304,7 +303,7 @@ export async function setupCursorProxy(runner: CloudRunner): Promise { "caddy version", ].join("\n"); - const caddyResult = await asyncTryCatchIf(isOperationalError, () => wrapSshCall(runner.runServer(installCaddy, 60))); + const caddyResult = await wrapSshCall(runner.runServer(installCaddy, 60)); if (!caddyResult.ok) { logWarn("Caddy install failed — Cursor proxy will not work"); return; @@ -335,7 +334,11 @@ export async function setupCursorProxy(runner: CloudRunner): Promise { "chmod 644 ~/.cursor/proxy/Caddyfile", ].join(" && "); - await wrapSshCall(runner.runServer(deployScript)); + const deployResult = await wrapSshCall(runner.runServer(deployScript)); + if (!deployResult.ok) { + logWarn("Proxy script deploy failed — Cursor proxy will not work"); + return; + } logInfo("Proxy scripts deployed"); // 3. Configure /etc/hosts for domain spoofing @@ -350,7 +353,21 @@ export async function setupCursorProxy(runner: CloudRunner): Promise { "rm -f /tmp/hosts_cursor_new", ].join(" && "); - await wrapSshCall(runner.runServer(hostsScript)); + const hostsResult = await wrapSshCall(runner.runServer(hostsScript)); + if (!hostsResult.ok) { + logWarn("Hosts spoofing failed — Cursor proxy will not intercept traffic"); + return; + } + + // macOS caches DNS aggressively; flush so /etc/hosts changes take effect immediately. + const flushDns = [ + 'if [ "$(uname -s)" = "Darwin" ]; then', + " sudo dscacheutil -flushcache 2>/dev/null || true", + " sudo killall -HUP mDNSResponder 2>/dev/null || true", + "fi", + ].join("\n"); + await wrapSshCall(runner.runServer(flushDns)); + logInfo("Hosts spoofing configured"); // 4. Install Caddy's internal CA cert @@ -468,7 +485,7 @@ export async function startCursorProxy(runner: CloudRunner): Promise { 'echo "Cursor proxy failed to start"; exit 1', ].join("\n"); - const result = await asyncTryCatchIf(isOperationalError, () => wrapSshCall(runner.runServer(script, 60))); + const result = await wrapSshCall(runner.runServer(script, 60)); if (result.ok) { logInfo("Cursor proxy started"); } else { From 0ee13606da637ac2c0cd60d74ffecbe1c45a4d2f Mon Sep 17 00:00:00 2001 From: Audrey Sage Lorberfeld <18585860+aulorbe@users.noreply.github.com> Date: Thu, 21 May 2026 17:38:07 -0700 Subject: [PATCH 2/2] fix(cli): inject SPAWN_CLI_DIR in dev script so local source is always used (#3429) * fix(cli): inject SPAWN_CLI_DIR in dev script so local source is always used Without SPAWN_CLI_DIR, `bun run dev` downloads the spawn script from the published CDN release, ignoring any local source changes. This is especially confusing when developing on a branch with fixes that haven't been published yet. Set SPAWN_CLI_DIR to the repo root (via git rev-parse) in the dev script so the local checkout is always used during development. * docs(cursor): add model picker and provider notes for local spawn (#3431) Add a section to sh/local/README.md noting that the "Custom model" setup option must be selected to get the OpenRouter model picker when spawning Cursor locally, and that only Cursor IDE-supported providers work (linking to Cursor's supported providers docs). --- packages/cli/package.json | 2 +- sh/local/README.md | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 380aa333d..81473b650 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -6,7 +6,7 @@ "spawn": "cli.js" }, "scripts": { - "dev": "bun run src/index.ts", + "dev": "SPAWN_CLI_DIR=$(git rev-parse --show-toplevel) bun run src/index.ts", "build": "bun build src/index.ts --outfile cli.js --target bun --minify --packages bundle", "compile": "bun build src/index.ts --compile --outfile spawn", "lint": "biome lint src/", diff --git a/sh/local/README.md b/sh/local/README.md index e731ffab4..019bf1087 100644 --- a/sh/local/README.md +++ b/sh/local/README.md @@ -43,6 +43,24 @@ OPENROUTER_API_KEY=sk-or-v1-xxxxx \ bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/claude.sh) ``` +## Cursor CLI — Local Model Picker + +When spawning Cursor locally, select the **Custom model** setup option to get the +OpenRouter model picker. Without it, Cursor uses its default model. + +```bash +# Install Spawn +curl -fsSL https://openrouter.ai/labs/spawn/cli/install.sh | bash + +# Launch — select "Custom model" when prompted for setup options +spawn cursor local +``` + +> **Supported providers:** Only providers supported by Cursor's API key +> integration work locally. See +> [Cursor docs — What providers are supported?](https://cursor.com/help/models-and-usage/api-keys#what-providers-are-supported) +> for the current list. + ## What It Does Local scripts will: