mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-25 14:54:50 +00:00
## Problem `spawn cursor local` fails on macOS with four cascading errors: ```bash chmod: /usr/local/bin/caddy: No such file or directory bash: line 4: caddy: command not found bash: /etc/hosts: Permission denied bash: line 24: setsid: command not found ``` These occur in packages/cli/src/shared/cursor-proxy.ts (setupCursorProxy and startCursorProxy). Even though local/agents.ts wires runLocal as the runServer callback, the proxy scripts it constructs were written exclusively for Linux: 1. Caddy downloads the wrong binary — `setupCursorProxy` calls `https://caddyserver.com/api/download?os=linux&arch=amd64` unconditionally. On macOS this downloads a Linux ELF binary that immediately fails to execute. 2. Caddy install path is root-owned on macOS — the script writes to `/usr/local/bin/caddy`. On macOS this directory either doesn't exist (no Homebrew) or is owned by a system process, so `chmod +x` immediately fails without sudo. 3. `/etc/hosts` is read-only without root on macOS — the domain-spoofing step runs echo `"127.0.0.1 ..." >> /etc/hosts` directly, which fails with Permission denied because `/etc/hosts` is only writable by root on macOS (unlike some Linux setups where the invoking user may own it). 4. `setsid` is Linux-only — the non-systemd fallback in startCursorProxy calls `setsid` to detach backend processes. `setsid` is a util-linux command; it doesn't ship on macOS (or BSD, or Windows). `nohup … &` is the portable equivalent. The downstream symptom of all four: Caddy never starts, the proxy is never listening, and Cursor's auth exchange fails — which surfaces as "The provided API key is invalid" even when the key is valid. ## Fix - OS/arch detection — query `process.platform` and `process.arch` (already available in Bun) to select the correct Caddy download (os=darwin, arch=arm64 | amd64 for macOS; os=linux for Linux). - User-writable install path — install Caddy to `~/.local/bin/caddy` (guaranteed writable, always exists after `mkdir -p`) instead of `/usr/local/bin/`. Add `~/.local/bin` to `PATH` in the execution environment. - `sudo` for `/etc/hosts` — prefix the `sed` and `echo` host entries with `sudo`. Wrap in a try/catch with a clear fallback message if `sudo` isn't available (some sandboxed environments). - Replace `setsid` with `nohup` — swap `setsid $NODE ...` for `nohup $NODE ... < /dev/null >> /tmp/cursor-proxy.log 2>&1 &` in the non-systemd branch. `nohup` is POSIX and present on macOS, Linux, and BSD. ## Affected code - `packages/cli/src/shared/cursor-proxy.ts` — `installCaddy` shell fragment, `hosts-spoofing` fragment, `startCursorProxy` non-systemd branch - No changes to the CloudRunner interface, tests, or other agents ## Testing Tested on macOS (Apple Silicon, M3) with SPAWN_CLI_DIR pointing at a local build: - `spawn cursor local` completes all setup steps without errors - Caddy starts on port 443, unary backend on 18644, bidi backend on 18645 - Cursor authenticates and routes completions through OpenRouter
84 lines
3 KiB
JSON
84 lines
3 KiB
JSON
{
|
|
"name": "@openrouter/spawn",
|
|
"version": "1.0.45",
|
|
"lockfileVersion": 3,
|
|
"requires": true,
|
|
"packages": {
|
|
"": {
|
|
"name": "@openrouter/spawn",
|
|
"version": "1.0.45",
|
|
"dependencies": {
|
|
"@clack/prompts": "^1.0.0",
|
|
"picocolors": "^1.1.1"
|
|
},
|
|
"bin": {
|
|
"spawn": "cli.js"
|
|
},
|
|
"devDependencies": {
|
|
"@types/bun": "^1.3.8"
|
|
}
|
|
},
|
|
"node_modules/@clack/core": {
|
|
"version": "1.0.1",
|
|
"resolved": "https://registry.npmjs.org/@clack/core/-/core-1.0.1.tgz",
|
|
"integrity": "sha512-WKeyK3NOBwDOzagPR5H08rFk9D/WuN705yEbuZvKqlkmoLM2woKtXb10OO2k1NoSU4SFG947i2/SCYh+2u5e4g==",
|
|
"dependencies": {
|
|
"picocolors": "^1.0.0",
|
|
"sisteransi": "^1.0.5"
|
|
}
|
|
},
|
|
"node_modules/@clack/prompts": {
|
|
"version": "1.0.1",
|
|
"resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.0.1.tgz",
|
|
"integrity": "sha512-/42G73JkuYdyWZ6m8d/CJtBrGl1Hegyc7Fy78m5Ob+jF85TOUmLR5XLce/U3LxYAw0kJ8CT5aI99RIvPHcGp/Q==",
|
|
"dependencies": {
|
|
"@clack/core": "1.0.1",
|
|
"picocolors": "^1.0.0",
|
|
"sisteransi": "^1.0.5"
|
|
}
|
|
},
|
|
"node_modules/@types/bun": {
|
|
"version": "1.3.9",
|
|
"resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.9.tgz",
|
|
"integrity": "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw==",
|
|
"dev": true,
|
|
"dependencies": {
|
|
"bun-types": "1.3.9"
|
|
}
|
|
},
|
|
"node_modules/@types/node": {
|
|
"version": "25.2.3",
|
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
|
|
"integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==",
|
|
"dev": true,
|
|
"dependencies": {
|
|
"undici-types": "~7.16.0"
|
|
}
|
|
},
|
|
"node_modules/bun-types": {
|
|
"version": "1.3.9",
|
|
"resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.9.tgz",
|
|
"integrity": "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg==",
|
|
"dev": true,
|
|
"dependencies": {
|
|
"@types/node": "*"
|
|
}
|
|
},
|
|
"node_modules/picocolors": {
|
|
"version": "1.1.1",
|
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
|
},
|
|
"node_modules/sisteransi": {
|
|
"version": "1.0.5",
|
|
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
|
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
|
|
},
|
|
"node_modules/undici-types": {
|
|
"version": "7.16.0",
|
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
|
"dev": true
|
|
}
|
|
}
|
|
}
|