diff --git a/README.md b/README.md index cc5c01d..8520804 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,14 @@ openclaw gateway restart | `claude-haiku-4` | haiku | Fastest, lightweight | ## Authentication +The proxy supports optional Bearer token authentication via the `PROXY_API_KEY` environment variable. + +**When `PROXY_API_KEY` is set**, all requests (except `GET /health`) must include a valid `Authorization: Bearer ` header. Requests with a missing or invalid token receive a `401 Unauthorized` response. + +**When `PROXY_API_KEY` is not set**, authentication is disabled and all requests are accepted (backwards compatible with v1.6.x and earlier). + +For local Claude Max / OAuth usage, avoid exporting `ANTHROPIC_API_KEY` globally into the shell that launches the proxy. `v1.7.1` also sanitizes `ANTHROPIC_*` variables before spawning Claude subprocesses to reduce accidental auth-path pollution. + The proxy supports optional Bearer token authentication via the `PROXY_API_KEY` environment variable. diff --git a/package.json b/package.json index 24f9a96..8110e5e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "openclaw-claude-proxy", - "version": "1.7.0", - "description": "OpenAI-compatible proxy that routes requests through Claude CLI — use your Claude Pro/Max subscription as an OpenClaw model provider", + "version": "1.7.1", + "description": "OpenAI-compatible proxy that routes requests through Claude CLI \u2014 use your Claude Pro/Max subscription as an OpenClaw model provider", "type": "module", "bin": { "openclaw-claude-proxy": "./server.mjs" @@ -10,7 +10,13 @@ "start": "node server.mjs", "setup": "node setup.mjs" }, - "keywords": ["openclaw", "claude", "proxy", "openai", "anthropic"], + "keywords": [ + "openclaw", + "claude", + "proxy", + "openai", + "anthropic" + ], "license": "MIT", "engines": { "node": ">=18" diff --git a/server.mjs b/server.mjs index 387e1d4..6506785 100644 --- a/server.mjs +++ b/server.mjs @@ -78,6 +78,9 @@ const poolBackoff = new Map(); // model → { failures: number, timer: TimeoutId function spawnWarm(cliModel) { const env = { ...process.env }; delete env.CLAUDECODE; + delete env.ANTHROPIC_API_KEY; + delete env.ANTHROPIC_BASE_URL; + delete env.ANTHROPIC_AUTH_TOKEN; const proc = spawn(CLAUDE, [ "-p", "--model", cliModel, @@ -276,6 +279,9 @@ function callClaude(model, messages) { console.log(`[pool] no warm process for model=${cliModel}, cold starting...`); const env = { ...process.env }; delete env.CLAUDECODE; + delete env.ANTHROPIC_API_KEY; + delete env.ANTHROPIC_BASE_URL; + delete env.ANTHROPIC_AUTH_TOKEN; proc = spawn(CLAUDE, [ "-p", "--model", cliModel, "--output-format", "text", @@ -390,6 +396,10 @@ async function handleChatCompletions(req, res) { } } catch (err) { console.error(`[proxy] error: ${err.message}`); + if (res.headersSent || res.writableEnded || res.destroyed) { + try { res.end(); } catch {} + return; + } jsonResponse(res, 500, { error: { message: err.message, type: "proxy_error" } }); } } diff --git a/setup.mjs b/setup.mjs index 3f7b0d2..9888f55 100755 --- a/setup.mjs +++ b/setup.mjs @@ -119,7 +119,7 @@ try { const out = execSync('claude -p --output-format text --no-session-persistence -- "ping"', { encoding: "utf-8", timeout: 30000, - env: { ...process.env, CLAUDECODE: undefined }, + env: { ...process.env, CLAUDECODE: undefined, ANTHROPIC_API_KEY: undefined, ANTHROPIC_BASE_URL: undefined, ANTHROPIC_AUTH_TOKEN: undefined }, }).trim(); if (out.length > 0) { log(`Claude CLI authenticated (test response: "${out.slice(0, 40)}...")`); diff --git a/start.sh b/start.sh index 4c3f386..4f84f4f 100755 --- a/start.sh +++ b/start.sh @@ -1,11 +1,12 @@ #!/bin/bash -# Start claude-proxy if not already running -if ! lsof -i :3456 -sTCP:LISTEN &>/dev/null; then +# Start openclaw-claude-proxy if not already running +PORT=${CLAUDE_PROXY_PORT:-3456} +if ! lsof -i :$PORT -sTCP:LISTEN &>/dev/null; then unset CLAUDECODE - nohup /opt/homebrew/bin/node /Users/taodeng/.openclaw/projects/claude-proxy/server.mjs \ - >> ~/.openclaw/logs/claude-proxy.log \ - 2>> ~/.openclaw/logs/claude-proxy.err.log & - echo "claude-proxy started (pid $!)" + nohup node "/Users/taodeng/.openclaw/projects/claude-proxy/openclaw-claude-proxy/server.mjs" \ + >> "/Users/taodeng/.openclaw/logs/claude-proxy.log" \ + 2>> "/Users/taodeng/.openclaw/logs/claude-proxy.err.log" & + echo "claude-proxy started on port $PORT (pid $!)" else - echo "claude-proxy already running" + echo "claude-proxy already running on port $PORT" fi