diff --git a/README.md b/README.md index 0fbf08f..83d5580 100644 --- a/README.md +++ b/README.md @@ -26,33 +26,13 @@ The proxy translates OpenAI-compatible `/v1/chat/completions` requests into `cla - **Claude CLI** installed and authenticated (`claude login`) - **OpenClaw** installed -## Quick Install - -### Docker (recommended) +## Quick Start (Node.js) ```bash git clone https://github.com/dtzp555-max/openclaw-claude-proxy.git cd openclaw-claude-proxy -cp .env.example .env # add your CLAUDE_SESSION_TOKEN / CLAUDE_COOKIES -docker compose up -d -``` -Or as a single command if you already have a `.env` ready: - -```bash -git clone https://github.com/dtzp555-max/openclaw-claude-proxy.git && cd openclaw-claude-proxy && docker compose up -d -``` - -Health check: `curl http://localhost:3456/health` - -### Node.js (local) - -```bash -# Clone -git clone https://github.com/dtzp555-max/openclaw-claude-proxy.git -cd openclaw-claude-proxy - -# Auto-configure OpenClaw + start proxy +# Auto-configure OpenClaw + start proxy + install auto-start node setup.mjs ``` @@ -61,6 +41,7 @@ That's it. The setup script will: 2. Add `claude-local` provider to `openclaw.json` 3. Add auth profiles to all agents 4. Start the proxy +5. Install auto-start on login (launchd on macOS, systemd on Linux) Then set your preferred Claude model as default: ```bash @@ -68,6 +49,17 @@ openclaw config set agents.defaults.model.primary "claude-local/claude-opus-4-6" openclaw gateway restart ``` +## Security + +- **Localhost only** — the proxy binds to `127.0.0.1` and is not exposed to the internet or your local network +- **No API keys** — authentication goes through Claude CLI's OAuth session, no credentials are stored in the proxy +- **Auto-start via launchd/systemd** — `node setup.mjs` installs a user-level launch agent (macOS) or systemd user service (Linux) so the proxy starts automatically on login +- **Remove auto-start** at any time: + +```bash +node uninstall.mjs +``` + ## Manual Install ### 1. Start the proxy @@ -148,13 +140,25 @@ openclaw gateway restart - `POST /v1/chat/completions` — Chat completion (streaming + non-streaming) - `GET /health` — Health check -## Auto-start on Login (macOS) +## Server / Advanced: Docker + +For server deployments or if you prefer Docker: -Add to your `~/.zshrc`: ```bash -bash ~/.openclaw/projects/claude-proxy/start.sh 2>/dev/null +git clone https://github.com/dtzp555-max/openclaw-claude-proxy.git +cd openclaw-claude-proxy +cp .env.example .env # add your CLAUDE_SESSION_TOKEN / CLAUDE_COOKIES +docker compose up -d ``` +Or as a single command if you already have a `.env` ready: + +```bash +git clone https://github.com/dtzp555-max/openclaw-claude-proxy.git && cd openclaw-claude-proxy && docker compose up -d +``` + +Health check: `curl http://localhost:3456/health` + ## Recovery after OpenClaw upgrade OpenClaw upgrades (`npm update -g openclaw`) **do not overwrite** the user config at `~/.openclaw/openclaw.json`. However, if the claude-local models stop working after an upgrade, follow these steps: diff --git a/package.json b/package.json index d3b3cd4..498591f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openclaw-claude-proxy", - "version": "1.4.0", + "version": "1.5.0", "description": "OpenAI-compatible proxy that routes requests through Claude CLI — use your Claude Pro/Max subscription as an OpenClaw model provider", "type": "module", "bin": { diff --git a/setup.mjs b/setup.mjs index 713026c..3f7b0d2 100755 --- a/setup.mjs +++ b/setup.mjs @@ -279,3 +279,96 @@ if (!SKIP_START && !DRY_RUN) { execSync(`bash "${startPath}"`, { stdio: "inherit" }); } catch { /* ignore */ } } + +// ── Step 7: Install auto-start on boot ────────────────────────────────── +if (!DRY_RUN) { + console.log("\n🔄 Installing auto-start on login...\n"); + + const platform = process.platform; + const nodeBin = process.execPath; + + // Ensure logs dir exists + const logsDir = join(OPENCLAW_DIR, "logs"); + if (!existsSync(logsDir)) mkdirSync(logsDir, { recursive: true }); + + if (platform === "darwin") { + // macOS: launchd + const plistDir = join(HOME, "Library", "LaunchAgents"); + if (!existsSync(plistDir)) mkdirSync(plistDir, { recursive: true }); + + const plistPath = join(plistDir, "ai.openclaw.proxy.plist"); + const logPath = join(logsDir, "proxy.log"); + + const plistXml = ` + + + + Label + ai.openclaw.proxy + ProgramArguments + + ${nodeBin} + ${serverPath} + + EnvironmentVariables + + CLAUDE_PROXY_PORT + ${PORT} + + RunAtLoad + + KeepAlive + + StandardOutPath + ${logPath} + StandardErrorPath + ${logPath} + + +`; + + writeFileSync(plistPath, plistXml); + log(`Plist written: ${plistPath}`); + + // Unload first (in case it was already loaded) then load + try { execSync(`launchctl unload "${plistPath}" 2>/dev/null`); } catch { /* ignore */ } + execSync(`launchctl load "${plistPath}"`); + log(`launchctl loaded ai.openclaw.proxy`); + + } else if (platform === "linux") { + // Linux: systemd user service + const systemdDir = join(HOME, ".config", "systemd", "user"); + if (!existsSync(systemdDir)) mkdirSync(systemdDir, { recursive: true }); + + const servicePath = join(systemdDir, "openclaw-proxy.service"); + const logPath = join(logsDir, "proxy.log"); + + const serviceUnit = `[Unit] +Description=OpenClaw Claude Proxy +After=network.target + +[Service] +ExecStart=${nodeBin} ${serverPath} +Environment=CLAUDE_PROXY_PORT=${PORT} +Restart=always +StandardOutput=append:${logPath} +StandardError=append:${logPath} + +[Install] +WantedBy=default.target +`; + + writeFileSync(servicePath, serviceUnit); + log(`Service file written: ${servicePath}`); + + execSync(`systemctl --user daemon-reload`); + execSync(`systemctl --user enable openclaw-proxy`); + execSync(`systemctl --user start openclaw-proxy`); + log(`systemd user service enabled and started`); + + } else { + warn(`Auto-start not supported on ${platform} — start manually with: bash ${startPath}`); + } + + console.log("\n✅ Auto-start installed — proxy will start automatically on login\n"); +} diff --git a/uninstall.mjs b/uninstall.mjs new file mode 100644 index 0000000..938cac6 --- /dev/null +++ b/uninstall.mjs @@ -0,0 +1,60 @@ +#!/usr/bin/env node +/** + * openclaw-claude-proxy uninstaller + * + * Stops and removes the launchd (macOS) or systemd (Linux) auto-start entry. + * Run: node uninstall.mjs + */ +import { existsSync, unlinkSync } from "node:fs"; +import { execSync } from "node:child_process"; +import { join } from "node:path"; +import { homedir } from "node:os"; + +const HOME = homedir(); + +function log(msg) { console.log(` ✓ ${msg}`); } +function warn(msg) { console.log(` ⚠ ${msg}`); } + +console.log("\n🗑 Uninstalling openclaw-claude-proxy auto-start...\n"); + +const platform = process.platform; + +if (platform === "darwin") { + const plistPath = join(HOME, "Library", "LaunchAgents", "ai.openclaw.proxy.plist"); + + if (existsSync(plistPath)) { + try { + execSync(`launchctl unload "${plistPath}" 2>/dev/null`); + log("launchd service stopped and unloaded"); + } catch { + warn("launchctl unload failed (service may not have been running)"); + } + unlinkSync(plistPath); + log(`Plist removed: ${plistPath}`); + } else { + warn(`Plist not found: ${plistPath}`); + } + +} else if (platform === "linux") { + const servicePath = join(HOME, ".config", "systemd", "user", "openclaw-proxy.service"); + + try { execSync(`systemctl --user stop openclaw-proxy 2>/dev/null`); } catch { /* ignore */ } + log("systemd service stopped"); + + try { execSync(`systemctl --user disable openclaw-proxy 2>/dev/null`); } catch { /* ignore */ } + log("systemd service disabled"); + + if (existsSync(servicePath)) { + unlinkSync(servicePath); + log(`Service file removed: ${servicePath}`); + } else { + warn(`Service file not found: ${servicePath}`); + } + + try { execSync(`systemctl --user daemon-reload`); } catch { /* ignore */ } + +} else { + warn(`Auto-start not supported on ${platform} — nothing to remove`); +} + +console.log("\n✅ Auto-start removed — proxy will no longer start on login\n");