{"id":50550602,"url":"https://github.com/hormold/claude-runner","last_synced_at":"2026-06-04T03:01:01.681Z","repository":{"id":342072478,"uuid":"1172279480","full_name":"Hormold/claude-runner","owner":"Hormold","description":"Claude Code as a service — orchestrator with persistent contexts, task queue, and REST API","archived":false,"fork":false,"pushed_at":"2026-03-04T19:39:28.000Z","size":243,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-04T23:04:59.121Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Hormold.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-04T06:02:46.000Z","updated_at":"2026-03-04T19:39:31.000Z","dependencies_parsed_at":"2026-03-04T23:05:00.545Z","dependency_job_id":null,"html_url":"https://github.com/Hormold/claude-runner","commit_stats":null,"previous_names":["hormold/claude-runner"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Hormold/claude-runner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hormold%2Fclaude-runner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hormold%2Fclaude-runner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hormold%2Fclaude-runner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hormold%2Fclaude-runner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hormold","download_url":"https://codeload.github.com/Hormold/claude-runner/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hormold%2Fclaude-runner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33887124,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-04T03:01:00.176Z","updated_at":"2026-06-04T03:01:01.661Z","avatar_url":"https://github.com/Hormold.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Claude Runner\n\nRun AI agents in isolated Docker containers. Fork → customize → deploy.\n\n![Claude Runner Architecture](diagram.png)\n\n## Why\n\nYou're building a product where users interact with an AI agent — a website builder, support bot, code reviewer, data pipeline. You need:\n\n- **Isolation** — each user gets their own container, can't affect others\n- **Custom tools** — agent calls your APIs via shell scripts you write\n- **Structured output** — you define JSON schema, agent returns it\n- **Cost tracking** — know exactly how much each task costs\n- **Session memory** — agent remembers previous interactions\n- **Real-time streaming** — show users what the agent is doing via WebSocket\n- **Abort** — kill any task instantly\n\nClaude Runner gives you all of this in **one file** (`server.mjs`, ~400 lines, zero dependencies).\n\n## Quick Start\n\n\u003e **📖 Detailed walkthrough: [QUICKSTART.md](QUICKSTART.md)**\n\n```bash\ngit clone https://github.com/Hormold/claude-runner\ncd claude-runner\ndocker build -t claude-runner .\nexport CLAUDE_CODE_OAUTH_TOKEN=\"sk-ant-oat...\"\nnode server.mjs\n```\n\n```bash\ncurl -X POST http://localhost:3456/task \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sessionId\":\"user-1\",\"message\":\"Hello, what can you do?\"}'\n```\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────────┐\n│  Your Application (frontend + backend)                       │\n│                                                              │\n│  ┌─────────────┐    ┌──────────────┐    ┌──────────────────┐ │\n│  │ Web UI      │───▶│ Your Backend │───▶│ Claude Runner    │ │\n│  │             │◀──▶│              │    │ POST /task       │ │\n│  │ WebSocket   │    │ - auth       │    │ WS /stream/:id   │ │\n│  │ for live    │    │ - billing    │    │                  │ │\n│  │ updates     │    │ - rate limit │    │ Manages:         │ │\n│  └─────────────┘    └──────────────┘    │ - Docker         │ │\n│                                         │ - Sessions       │ │\n│                                         │ - Queue          │ │\n│                                         │ - Streaming      │ │\n│                                         └────────┬─────────┘ │\n└───────────────────────────────────────────────────┼──────────┘\n                                                    │\n                    ┌───────────────────────────────┼──────────┐\n                    │  Docker Container (per task)  │          │\n                    │                               ▼          │\n                    │  /workspace/                             │\n                    │    AGENTS.md    ← agent prompt           │\n                    │    OUTPUT.md    ← output schema          │\n                    │    tools/       ← your CLI scripts       │\n                    │    data/        ← agent workspace        │\n                    │                                          │\n                    │  Claude Agent SDK                        │\n                    │    reads prompts → runs tools → JSON     │\n                    │                               │          │\n                    └───────────────────────────────┼──────────┘\n                                                    │\n                    ┌───────────────────────────────┼──────────┐\n                    │  Your External APIs           ▼          │\n                    │  (CRM, deployment, database, etc.)       │\n                    │  Called via tools/acme-cli.sh + curl     │\n                    └──────────────────────────────────────────┘\n```\n\n## API Reference\n\n### `POST /task` — Submit Task\n\n```json\n{\n  \"sessionId\": \"user-123\",\n  \"message\": \"Look up my account, email is alice@startup.io\",\n  \"context\": \"Channel: web-chat\",\n  \"env\": { \"ACME_API_URL\": \"http://host.docker.internal:3457\", \"ACME_API_TOKEN\": \"secret\" }\n}\n```\n\n**Response:**\n```json\n{\n  \"taskId\": \"a1b2c3d4\",\n  \"sessionId\": \"user-123\",\n  \"output\": {\n    \"action\": \"resolve\",\n    \"response\": \"Hi Alice! You have 188 calls remaining on the Growth plan.\",\n    \"user\": { \"name\": \"Alice Chen\", \"email\": \"alice@startup.io\", \"plan\": \"growth\" },\n    \"confidence\": 1.0\n  },\n  \"cost\": 0.034,\n  \"duration\": 20665,\n  \"tools\": [\"Bash\"],\n  \"resumed\": false\n}\n```\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `sessionId` | string | Required. Reuse to continue a conversation |\n| `message` | string | Required. The user's message |\n| `context` | string | Optional. Extra context (channel, user info) |\n| `env` | object | Optional. Env vars passed to Docker container |\n\n### `POST /task/:sessionId/abort` — Abort Task\nKills the Docker container immediately. Rejects all queued tasks for this session.\n\n### `GET /sessions` — List All Sessions\nReturns array of sessions with state, busy status, and queue depth.\n\n### `GET /session/:id` — Session Details\n### `DELETE /session/:id` — Delete Session (kills container + wipes data)\n### `POST /session/:id/reset` — Reset to Clean State (re-copies agent/ template)\n### `GET /health` — Health Check\n\n### `WS /stream/:sessionId` — Live Event Stream\n\nConnect via WebSocket to receive real-time events:\n\n```javascript\nconst ws = new WebSocket('ws://localhost:3456/stream/user-123');\n\nws.onmessage = ({ data }) =\u003e {\n  const event = JSON.parse(data);\n  switch (event.type) {\n    case 'connected':     // {busy, turns}\n    case 'task_start':    // {taskId, sessionId}\n    case 'init':          // {sessionId, model, tools}\n    case 'thinking':      // {text} — partial thinking tokens\n    case 'text':          // {text} — partial output text\n    case 'tool_start':    // {tool, input}\n    case 'tool_end':      // {result}\n    case 'task_complete': // {taskId, output, cost, duration, tools}\n    case 'abort':         // {taskId}\n  }\n};\n```\n\nConnect **before** sending `/task` to catch all events.\n\n## Customization Guide\n\n### Agent Prompt (`agent/AGENTS.md`)\nWho the agent is. What tools it has. What rules it follows.\n\n### Output Format (`agent/OUTPUT.md`)\nJSON schema the agent must return. Written in plain English — the agent reads it as part of its prompt.\n\n### Tools (`agent/tools/*.sh`)\nShell scripts that call your APIs. They receive arguments, read env vars, output JSON.\n\n```bash\n#!/bin/bash\n# tools/deploy.sh \u003cenvironment\u003e\ncurl -s -X POST -H \"Authorization: Bearer $DEPLOY_TOKEN\" \\\n  \"$PLATFORM_API/deploy\" -d \"{\\\"env\\\":\\\"$1\\\"}\"\n```\n\n### Environment Variables\nPass per-session secrets in `env` field of `/task`. Available inside Docker as normal env vars.\n\n## File Structure\n\n```\nclaude-runner/\n├── server.mjs        ← Task coordinator (HTTP + WebSocket, ~400 lines)\n├── Dockerfile         ← Docker image (Node.js + Claude SDK + curl)\n├── agent/             ← Agent template (customize this!)\n│   ├── AGENTS.md      ← Agent prompt\n│   ├── OUTPUT.md      ← Output JSON schema\n│   └── tools/         ← CLI scripts for the agent\n├── mock-api.mjs       ← Example external API (for testing)\n├── test.sh            ← Full end-to-end test\n├── QUICKSTART.md      ← Step-by-step guide\n├── CLAUDE.md          ← Instructions for AI agents working on this repo\n├── .env.example       ← Environment variables template\n└── LICENSE            ← MIT\n```\n\n## Got a Legacy Product? Add AI in an Afternoon\n\nYou don't need a clean REST API. If your product has a web UI, you can reverse-engineer its API in minutes and turn it into agent tools.\n\n### Step 1: Record your API with Chrome DevTools\n\n1. Open your product in Chrome\n2. Open DevTools (`F12`) → **Network** tab\n3. Check **\"Preserve log\"** (keeps requests across page navigations)\n4. Go to **DevTools Settings** (gear icon) → scroll down → check **\"Allow to generate HAR with sensitive data\"**\n5. **Use your product** — click through every feature you want the agent to use:\n   - Create things, edit them, delete them\n   - Search, filter, sort\n   - Change settings, update profiles\n   - The more you do, the more the agent will know how to do\n\n6. When done → right-click the Network log → **\"Save all as HAR with content\"**\n\n### Step 2: Generate CLI tools from HAR\n\nGive the HAR file to Claude and say:\n\n```\nHere's a HAR file from our product. Create a CLI tool (bash + curl) that can:\n- List, create, update, delete [resources]\n- Use environment variables for auth: $API_URL, $API_TOKEN\n- Output JSON\n- Handle errors\n\nGroup related operations into one script with subcommands.\n```\n\nClaude will analyze every request/response and generate shell scripts that replicate the API calls.\n\n### Step 3: Handle authentication\n\nThe tricky part is auth. Add a note to your prompt:\n\n```\nAuthentication works like this: [describe your auth flow]\n- API key in header: Authorization: Bearer $TOKEN\n- Or: session cookie (include how to get it)\n- Or: OAuth flow (describe the endpoint)\n```\n\nIf your product uses simple API keys or bearer tokens — just pass them as env vars. If it uses cookies/sessions, your CLI tool can include a `login` subcommand.\n\n### Step 4: Drop scripts into tools/ and go\n\n```bash\ncp generated-cli.sh agent/tools/\nchmod +x agent/tools/generated-cli.sh\n```\n\nUpdate `agent/AGENTS.md` with instructions on what the tool does, and you're done. Your legacy product now has an AI agent.\n\n### The key insight\n\nYour product already has the hard part — the business logic, the data, years of development. The agent doesn't need to understand your codebase. It just needs a way to call your API and instructions on when to use it.\n\n## Testing\n\n```bash\n./test.sh\n```\n\nStarts mock API + server → runs 12 tests → cleans up:\n1. Health check\n2. New session with tools + structured output\n3. Independent session (isolation)\n4. Resume session (memory persistence)\n5. Sessions list\n6. Session details\n7. Reset session\n8. Delete session\n9. WebSocket streaming (80+ events)\n10. Workspace isolation\n\nTotal cost: ~$0.07 per run.\n\n## FAQ\n\n**Q: What's the difference between `CLAUDE_CODE_OAUTH_TOKEN` and `ANTHROPIC_API_KEY`?**\nOAuth tokens (from Claude.ai subscription, `sk-ant-oat*`) → use `CLAUDE_CODE_OAUTH_TOKEN`. API keys (from console.anthropic.com, `sk-ant-api*`) → use `ANTHROPIC_API_KEY`.\n\n**Q: How do tools call my APIs from inside Docker?**\nUse `host.docker.internal` as the hostname. The server automatically adds `--add-host=host.docker.internal:host-gateway` to Docker. Your API must bind to `0.0.0.0` (not just localhost).\n\n**Q: Can I use this in production?**\nThis is a starter template. For production, add: authentication on the API, rate limiting, proper error handling, logging, and consider deploying the server + Docker on a dedicated machine.\n\n**Q: How much does it cost per task?**\nDepends on complexity. Simple Q\u0026A: ~$0.01. Tool-heavy tasks: ~$0.03-0.05. The `cost` field in every response gives you exact USD.\n\n**Q: Can I use a different model?**\nSet `MODEL=claude-opus-4-6` (or any Anthropic model) in env or `.env`.\n\n## License\n\nMIT — fork it, ship it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhormold%2Fclaude-runner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhormold%2Fclaude-runner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhormold%2Fclaude-runner/lists"}