From 08799bd656254dad4560c148548b2d88fe8e4494 Mon Sep 17 00:00:00 2001 From: AULORBE <18585860+aulorbe@users.noreply.github.com> Date: Wed, 20 May 2026 12:49:18 -0700 Subject: [PATCH] fix(cursor-proxy): macOS local cursor CLI compatibility - Detect OS/arch at runtime for cross-platform Caddy installation - Install Caddy to ~/.local/bin (user-writable on all platforms) - Fix /etc/hosts configuration to work on both macOS and Linux - Replace setsid (Linux-only) with nohup (POSIX) - Ensure ~/.local/bin is in PATH for Caddy commands - Add comprehensive comments explaining platform-specific choices Co-Authored-By: Claude Opus 4.7 --- packages/cli/package-lock.json | 4 +-- packages/cli/package.json | 2 +- packages/cli/src/shared/cursor-proxy.ts | 39 ++++++++++++++++++------- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/packages/cli/package-lock.json b/packages/cli/package-lock.json index 3417fc66c..6f131b0f7 100644 --- a/packages/cli/package-lock.json +++ b/packages/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@openrouter/spawn", - "version": "0.2.82", + "version": "1.0.45", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@openrouter/spawn", - "version": "0.2.82", + "version": "1.0.45", "dependencies": { "@clack/prompts": "^1.0.0", "picocolors": "^1.1.1" diff --git a/packages/cli/package.json b/packages/cli/package.json index 7ddfc6674..380aa333d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openrouter/spawn", - "version": "1.0.44", + "version": "1.0.46", "type": "module", "bin": { "spawn": "cli.js" diff --git a/packages/cli/src/shared/cursor-proxy.ts b/packages/cli/src/shared/cursor-proxy.ts index b58469aab..4e75eb62e 100644 --- a/packages/cli/src/shared/cursor-proxy.ts +++ b/packages/cli/src/shared/cursor-proxy.ts @@ -291,11 +291,22 @@ export async function setupCursorProxy( logStep("Deploying Cursor→OpenRouter proxy..."); // 1. Install Caddy if not present + // Detect OS and arch at runtime so this works on macOS (darwin/arm64|amd64) + // and Linux (linux/amd64|arm64). Write to ~/.local/bin which is user-writable + // on every platform -- /usr/local/bin is SIP-protected on macOS and requires + // root elsewhere. const installCaddy = [ + "set -e", 'if command -v caddy >/dev/null 2>&1; then echo "caddy already installed"; exit 0; fi', 'echo "Installing Caddy..."', - 'curl -sf "https://caddyserver.com/api/download?os=linux&arch=amd64" -o /usr/local/bin/caddy', - "chmod +x /usr/local/bin/caddy", + 'OS=$(uname -s | tr "[:upper:]" "[:lower:]")', + "ARCH=$(uname -m)", + '[ "$ARCH" = "x86_64" ] && ARCH="amd64"', + '[ "$ARCH" = "aarch64" ] && ARCH="arm64"', + 'mkdir -p "$HOME/.local/bin"', + 'curl -fsSL "https://caddyserver.com/api/download?os=${OS}&arch=${ARCH}" -o "$HOME/.local/bin/caddy"', + 'chmod +x "$HOME/.local/bin/caddy"', + 'export PATH="$HOME/.local/bin:$PATH"', "caddy version", ].join("\n"); @@ -334,18 +345,23 @@ export async function setupCursorProxy( logInfo("Proxy scripts deployed"); // 3. Configure /etc/hosts for domain spoofing + // Requires elevated privileges on all platforms (/etc/hosts is root-owned). + // Use a temp-file swap via sudo cp to avoid sed -i portability issues + // (macOS sed requires a backup suffix with -i; Linux does not). const hostsScript = [ - // Remove any existing cursor entries - 'sed -i "/cursor\\.sh/d" /etc/hosts 2>/dev/null || true', - // Add our entries - `echo "127.0.0.1 ${CURSOR_DOMAINS.join(" ")}" >> /etc/hosts`, + // Build a clean copy without existing cursor entries then append ours + 'grep -v "cursor.sh" /etc/hosts > /tmp/hosts_cursor_new 2>/dev/null || cp /etc/hosts /tmp/hosts_cursor_new', + `echo "127.0.0.1 ${CURSOR_DOMAINS.join(" ")}" >> /tmp/hosts_cursor_new`, + "sudo cp /tmp/hosts_cursor_new /etc/hosts", + "rm -f /tmp/hosts_cursor_new", ].join(" && "); await wrapSshCall(runner.runServer(hostsScript)); logInfo("Hosts spoofing configured"); // 4. Install Caddy's internal CA cert - const trustScript = "caddy trust 2>/dev/null || true"; + // Include ~/.local/bin in PATH since Caddy may have been installed there above. + const trustScript = 'export PATH="$HOME/.local/bin:$PATH" && caddy trust 2>/dev/null || true'; await wrapSshCall(runner.runServer(trustScript, 30)); logInfo("Caddy CA trusted"); @@ -365,7 +381,7 @@ CONF`, /** * Start the Cursor proxy services (Caddy + two Node.js backends). - * Uses systemd if available, falls back to setsid/nohup. + * Uses systemd if available, falls back to nohup (POSIX -- works on macOS and Linux). */ export async function startCursorProxy( runner: CloudRunner, @@ -390,6 +406,8 @@ export async function startCursorProxy( const script = [ "source ~/.spawnrc 2>/dev/null", + // Ensure ~/.local/bin (where Caddy may be installed) is in PATH for this script. + 'export PATH="$HOME/.local/bin:$PATH"', nodeFind, // Start unary backend @@ -415,7 +433,8 @@ export async function startCursorProxy( " $_sudo systemctl daemon-reload", " $_sudo systemctl restart cursor-proxy-unary", " else", - " setsid $NODE ~/.cursor/proxy/unary.mjs < /dev/null &", + // setsid is Linux-only; nohup is POSIX and works on macOS and Linux. + " nohup $NODE ~/.cursor/proxy/unary.mjs < /dev/null > /tmp/cursor-proxy-unary.log 2>&1 &", " fi", "fi", @@ -443,7 +462,7 @@ export async function startCursorProxy( " $_sudo systemctl daemon-reload", " $_sudo systemctl restart cursor-proxy-bidi", " else", - " setsid $NODE ~/.cursor/proxy/bidi.mjs < /dev/null &", + " nohup $NODE ~/.cursor/proxy/bidi.mjs < /dev/null > /tmp/cursor-proxy-bidi.log 2>&1 &", " fi", "fi",