Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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/",
Expand Down
27 changes: 22 additions & 5 deletions packages/cli/src/shared/cursor-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) ────────────────────────────────
Expand Down Expand Up @@ -304,7 +303,7 @@ export async function setupCursorProxy(runner: CloudRunner): Promise<void> {
"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;
Expand Down Expand Up @@ -335,7 +334,11 @@ export async function setupCursorProxy(runner: CloudRunner): Promise<void> {
"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
Expand All @@ -350,7 +353,21 @@ export async function setupCursorProxy(runner: CloudRunner): Promise<void> {
"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
Expand Down Expand Up @@ -468,7 +485,7 @@ export async function startCursorProxy(runner: CloudRunner): Promise<void> {
'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 {
Expand Down
18 changes: 18 additions & 0 deletions sh/local/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down