diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a6574f6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.git +.gitignore +*.md +node_modules +.env +.env.* +scripts/ +start.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..56a86f5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY server.mjs ./ +COPY setup.mjs ./ +COPY package.json ./ + +ENV CLAUDE_SESSION_TOKEN="" \ + CLAUDE_COOKIES="" + +EXPOSE 3456 + +CMD ["node", "server.mjs"] diff --git a/README.md b/README.md index 105cf0a..0fbf08f 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,25 @@ The proxy translates OpenAI-compatible `/v1/chat/completions` requests into `cla ## Quick Install +### Docker (recommended) + +```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 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..770157c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +services: + claude-proxy: + build: . + ports: + - "3456:3456" + env_file: .env + restart: unless-stopped diff --git a/package.json b/package.json index 78a6eb8..d3b3cd4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openclaw-claude-proxy", - "version": "1.3.1", + "version": "1.4.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/server.mjs b/server.mjs index 065153d..981fea5 100644 --- a/server.mjs +++ b/server.mjs @@ -26,6 +26,9 @@ const TIMEOUT = parseInt(process.env.CLAUDE_TIMEOUT || "300000", 10); const POOL_SIZE = parseInt(process.env.CLAUDE_POOL_SIZE || "1", 10); const POOL_MAX_IDLE = parseInt(process.env.CLAUDE_POOL_MAX_IDLE || "60000", 10); // max idle time before recycle +const VERSION = "1.4.0"; +const START_TIME = Date.now(); + // Model alias mapping: request model → claude CLI --model arg const MODEL_MAP = { "claude-opus-4-6": "opus", @@ -292,13 +295,27 @@ const server = createServer(async (req, res) => { return handleChatCompletions(req, res); } - // GET /health — includes pool status + // GET /health — includes pool status, version, uptime if (req.url === "/health") { const poolStatus = {}; for (const [model, arr] of pool) { - poolStatus[model] = { total: arr.length, ready: arr.filter(e => e.ready).length }; + const readyCount = arr.filter(e => e.ready).length; + const errorCount = arr.filter(e => !e.ready).length; + poolStatus[model] = { + total: arr.length, + ready: readyCount, + error: errorCount, + status: readyCount > 0 ? "ready" : "error", + }; } - return jsonResponse(res, 200, { status: "ok", pool: poolStatus }); + const uptimeMs = Date.now() - START_TIME; + return jsonResponse(res, 200, { + status: "ok", + version: VERSION, + uptime: uptimeMs, + uptimeHuman: `${Math.floor(uptimeMs / 3600000)}h ${Math.floor((uptimeMs % 3600000) / 60000)}m ${Math.floor((uptimeMs % 60000) / 1000)}s`, + pool: poolStatus, + }); } // Catch-all: try to handle any POST with messages @@ -312,8 +329,8 @@ const server = createServer(async (req, res) => { // ── Start ────────────────────────────────────────────────────────────── initPool(); -server.listen(PORT, "127.0.0.1", () => { - console.log(`openclaw-claude-proxy v1.3.1 listening on http://127.0.0.1:${PORT}`); +server.listen(PORT, "0.0.0.0", () => { + console.log(`openclaw-claude-proxy v${VERSION} listening on http://0.0.0.0:${PORT}`); console.log(`Models: ${MODELS.map((m) => m.id).join(", ")}`); console.log(`Claude binary: ${CLAUDE}`); console.log(`Timeout: ${TIMEOUT}ms`);