{"_id":"@aliildan/openclaude","_rev":"2-79df14d58762982c251a92255270bade","name":"@aliildan/openclaude","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@aliildan/openclaude","version":"0.1.0","keywords":["claude","claude-code","ollama","anthropic","llm","router","proxy","cli"],"author":{"name":"Ali Ildan","email":"ali.ildan@gmail.com"},"license":"MIT","_id":"@aliildan/openclaude@0.1.0","maintainers":[{"name":"aliildan","email":"ali.ildan@gmail.com"}],"homepage":"https://github.com/aliildan/openclaude#readme","bugs":{"url":"https://github.com/aliildan/openclaude/issues"},"bin":{"oc":"bin/openclaude","openclaude":"bin/openclaude"},"dist":{"shasum":"f9d8b4e73b156d37ced2fb62535d47a70df2e4cd","tarball":"https://registry.npmjs.org/@aliildan/openclaude/-/openclaude-0.1.0.tgz","fileCount":22,"integrity":"sha512-g58LE2gSBLVzqo2Ek9WxGb2cznTwjcXsbg8VAypGHyKC0Ud16nwaBi4VJN+O9g04ptUpM7JptThsVxkCGcOp+A==","signatures":[{"sig":"MEUCIHXK5iFU+7VCYcFxHuVI0pcB7bxpHCyYgWyOHJldeu/OAiEArtvetporGPAV8EXP0yHaOQ49lWSvRW4jq9+628kxb4g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":234610},"type":"module","engines":{"node":">=20"},"gitHead":"9645da3915f60c07c589a78b073a3e2d8f5f4208","scripts":{"test":"node --test test/router-routing.test.js test/router-e2e.test.js test/router-stream-error.test.js test/stream-fixup.test.js test/capabilities.test.js test/sanitize.test.js test/model-subagent.test.js test/model-internal-classifier.test.js test/auth.test.js","prepublishOnly":"npm test"},"_npmUser":{"name":"aliildan","email":"ali.ildan@gmail.com"},"repository":{"url":"git+https://github.com/aliildan/openclaude.git","type":"git"},"_npmVersion":"11.16.0","description":"Tiny multi-provider router for Claude Code: mix Anthropic Claude (subscription auth), Ollama Cloud, and local Ollama with per-duty model assignments.","directories":{},"_nodeVersion":"24.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/openclaude_0.1.0_1782164551535_0.6342310554058035","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aliildan/openclaude","version":"0.1.1","description":"Tiny multi-provider router for Claude Code: mix Anthropic Claude (subscription auth), Ollama Cloud, and local Ollama with per-duty model assignments.","type":"module","bin":{"openclaude":"bin/openclaude","oc":"bin/openclaude"},"engines":{"node":">=20"},"license":"MIT","author":{"name":"Ali Ildan","email":"ali.ildan@gmail.com"},"keywords":["claude","claude-code","ollama","anthropic","llm","router","proxy","cli"],"repository":{"type":"git","url":"git+https://github.com/aliildan/openclaude.git"},"bugs":{"url":"https://github.com/aliildan/openclaude/issues"},"homepage":"https://github.com/aliildan/openclaude#readme","publishConfig":{"access":"public"},"scripts":{"test":"node --test test/router-routing.test.js test/router-e2e.test.js test/router-stream-error.test.js test/stream-fixup.test.js test/capabilities.test.js test/sanitize.test.js test/model-subagent.test.js test/model-internal-classifier.test.js test/auth.test.js","prepublishOnly":"npm test"},"gitHead":"72f27551c08581fe4de195d0ae2d5b3395034a17","_id":"@aliildan/openclaude@0.1.1","_nodeVersion":"24.16.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-GnKuooMMnObBSsFpBtxVRSXRcT0aWDpZmVV4+DtAkVexePu7cntfwnAOG/Fse2/62dulIQEIykPZGOFE5oep/A==","shasum":"7a7a986a01c51698c8d06269372d6e1c3be402d1","tarball":"https://registry.npmjs.org/@aliildan/openclaude/-/openclaude-0.1.1.tgz","fileCount":22,"unpackedSize":232995,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDYT+5c0TXmLOLbLKFtZsVLGdRgCqpwiLg1Ih2KK6pX8wIgeT9WbaRzflLLR953QPaMqUNn9Jv7DlTn6jfRlu2Xkqo="}]},"_npmUser":{"name":"aliildan","email":"ali.ildan@gmail.com"},"directories":{},"maintainers":[{"name":"aliildan","email":"ali.ildan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/openclaude_0.1.1_1782165528146_0.8321502090811792"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-22T21:42:31.387Z","modified":"2026-06-22T21:58:48.442Z","0.1.0":"2026-06-22T21:42:31.728Z","0.1.1":"2026-06-22T21:58:48.331Z"},"bugs":{"url":"https://github.com/aliildan/openclaude/issues"},"author":{"name":"Ali Ildan","email":"ali.ildan@gmail.com"},"license":"MIT","homepage":"https://github.com/aliildan/openclaude#readme","keywords":["claude","claude-code","ollama","anthropic","llm","router","proxy","cli"],"repository":{"type":"git","url":"git+https://github.com/aliildan/openclaude.git"},"description":"Tiny multi-provider router for Claude Code: mix Anthropic Claude (subscription auth), Ollama Cloud, and local Ollama with per-duty model assignments.","maintainers":[{"name":"aliildan","email":"ali.ildan@gmail.com"}],"readme":"# openclaude\n\n[![npm version](https://img.shields.io/npm/v/@aliildan/openclaude.svg)](https://www.npmjs.com/package/@aliildan/openclaude)\n[![node](https://img.shields.io/node/v/@aliildan/openclaude.svg)](https://nodejs.org)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n\n> **One Claude Code session, every model.** Keep your Anthropic Claude subscription for the main chat and route subagents, background tasks, or whole conversations to **local or cloud Ollama** models — all from the same `/model` picker, without leaving Claude Code.\n\nopenclaude is a tiny router that sits between the `claude` CLI and the model backends. Your Claude subscription auth is forwarded untouched; everything Ollama is auto-discovered and dispatched by name. No API keys to juggle, no build step — just Node.\n\n![openclaude /model picker screenshot](openclaude.png)\n\n## Quickstart\n\n**Requirements:** [Claude Code](https://claude.com/claude-code) ≥ v2.1.129 (logged in), [Node.js](https://nodejs.org) ≥ 20, and — optionally — [Ollama](https://ollama.com/download) if you want to route to Ollama models.\n\n```bash\n# 1. Install\nnpm install -g @aliildan/openclaude\n\n# 2. (optional) make sure Ollama is running with at least one model\nollama serve &\nollama pull qwen2.5-coder\n\n# 3. Boot the router and drop into Claude Code\noc start\n```\n\n`oc start` prints what it wired up, then execs `claude`:\n\n```\n[openclaude] router on http://127.0.0.1:11436\n[openclaude] found 3 Ollama model(s):\n  - qwen2.5-coder:7b\n  - gemma4:31b-cloud\n  - llama3.2:3b\n[openclaude] mode: conservative (safe) · discovery: ON (all Ollama models in /model)\n[openclaude] /model picker bindings (friendly names):\n  (Custom) → qwen2.5-coder:7b\n  Default + Sonnet + Opus + Haiku → real Anthropic via your subscription\n```\n\nThen inside Claude Code:\n\n```\n/model\n# pick any Ollama entry (auto-discovered) or the (Custom) slot\n# or paste a raw \"/model ollama-local:<name>\" command\n```\n\nUpgrade any time with `npm install -g @aliildan/openclaude@latest`. The package is scoped (`@aliildan/openclaude`), but the commands stay `oc` / `openclaude`, and `oc start` works from any directory on Linux, macOS, and Windows.\n\n## Features\n\n- **🔌 Keep your Claude subscription** — Claude Code's OAuth bearer is forwarded to `api.anthropic.com` untouched. No `ANTHROPIC_API_KEY` required.\n- **🧩 Every Ollama model in `/model`** — auto-discovered from your Ollama install and selectable like any other model in the picker.\n- **☁️ Frontier open-weight models without a GPU** — route to [Ollama Cloud](https://ollama.com/cloud) (`gpt-oss:120b`, `qwen3:480b`, `deepseek-v3.1:671b`, `kimi-k2:1t`, …) through the same workflow.\n- **💸 Cheap subagents** — send the Explore / Plan / general-purpose subagents to a local model while your main conversation stays on Anthropic Claude.\n- **🛡️ Cross-provider safety** — sanitizes tool-use ids, thinking-block signatures, images, and malformed SSE streams so switching models mid-conversation just works.\n- **🪶 Thin & local** — single-file Node.js ESM, no build step, bound to `127.0.0.1`.\n\n## How it works\n\n```\n┌──────────────┐  ANTHROPIC_BASE_URL      ┌────────────────────┐\n│ claude (CLI) │ ────────────────────────►│ openclaude router  │\n│  /model      │  Authorization passed    │  127.0.0.1:11436   │\n└──────────────┘  through                 └─────────┬──────────┘\n                                                    │ parse \"provider:modelId\"\n                                  ┌─────────────────┼─────────────────┐\n                                  ▼                 ▼                 ▼\n                       api.anthropic.com    localhost:11434      ollama.com\n                       (your OAuth)         (no auth)            (x-api-key)\n```\n\nWhen you select a model in `/model`:\n- Picker entry **`Default` / `Sonnet` / `Opus` / `Haiku`** → routed to **real Anthropic** via your subscription.\n- Picker entries **`<ollama-name> (ollama-local)`** (auto-discovered from your Ollama install) → routed to **local Ollama**.\n- Picker entry **`<name> (Ollama)`** (the Custom alias slot) → routed to **local Ollama**.\n- Anything you type, e.g. `/model ollama-local:gemma4:31b-cloud` → routed by parsing the prefix.\n\n### Discovery mode (default ON)\n\nTo get **all** your Ollama models in the picker, openclaude sets `ANTHROPIC_AUTH_TOKEN` to a sentinel before launching `claude`. This makes Claude Code trigger gateway model discovery — Claude Code calls `GET /v1/models` on our router and the router enumerates every installed Ollama model.\n\nFor inference, the router intercepts the sentinel token in the `Authorization` header and substitutes the **live OAuth bearer** read from `~/.claude/.credentials.json` (re-read on every request, so token rotation is handled). Your subscription still pays for Anthropic-bound traffic.\n\n> **Side-effect of setting `ANTHROPIC_AUTH_TOKEN`** (per Claude Code's own changelog): the following are **disabled** for the session — Remote Control, `/schedule`, claude.ai MCP connectors, notification preferences. Subscription inference and everything else is unaffected. Pass `oc start --no-discovery` to keep them, at the cost of seeing fewer Ollama models in the picker.\n\n## Guides\n\n### Subagent model — cheaper background work\n\nClaude Code's Explore, Plan, and general-purpose subagents can be expensive because they read many files and digest verbose output. `oc model-subagent` redirects subagent traffic to a cheaper model (like a local Ollama model) while keeping your main conversation on Anthropic Claude.\n\n```bash\noc model-subagent          # show current setting + available models\noc model-subagent 2        # select a model by number\noc model-subagent 0        # reset to Anthropic default (or: oc model-subagent default)\n```\n\n**How it works:** `oc model-subagent` writes a `subagentModel` key to `~/.openclaude/config.json`. On the next `oc start`, the router sets `CLAUDE_CODE_SUBAGENT_MODEL` before launching `claude`. Claude Code reads this once at startup, so **changes do not take effect in a running session** — restart `oc` to apply them. The numbered menu offers `0` (default/unset), `1..N` (installed Ollama models routed as `ollama-local:<name>`), and Anthropic models (e.g. Haiku, using your subscription). If the configured model is no longer installed at `oc start`, the router falls back to the Anthropic default and warns. `oc status` shows both the configured value and the value active in the current session.\n\n### Bridge modes for the alias slots\n\nEven with discovery on, openclaude can rebind Claude Code's built-in alias slots (Custom / Sonnet / Opus) to specific Ollama models so they get **friendly display names** in the picker. Aliases are filled from your Ollama list in order.\n\n| Mode                       | What it does                                                                  | Anthropic aliases lost |\n| -------------------------- | ---------------------------------------------------------------------------- | ---------------------- |\n| **conservative** (default) | Bind the **Custom** slot to your first Ollama model.                          | none                   |\n| **aggressive**             | Also bind **Sonnet** and **Opus** alias slots to your next two Ollama models. | Sonnet, Opus           |\n\nEnable aggressive bridging with `oc start --bridge=aggressive`.\n\n> **🚫 Haiku is never bridged**, in either mode. Claude Code uses the `haiku` alias for its background safety classifier, title generation, and summarization. Routing those to Ollama makes Bash and other tools fail with `\"default is temporarily unavailable\"`. (To override Haiku deliberately, use `oc internal-classifier`.)\n\n### Ollama: local & cloud\n\n**Local Ollama** — the default config assumes Ollama at `http://127.0.0.1:11434`. Since v0.14, Ollama serves the Anthropic Messages API at `/v1/messages` natively, so no translation layer is needed.\n\n```bash\nollama serve &           # if not already running\nollama pull qwen2.5-coder\noc list                  # confirm openclaude sees it\n```\n\n**Ollama Cloud** ([ollama.com/cloud](https://ollama.com/cloud)) hosts large open-weight models (200B–1T parameters) behind the same Anthropic-compatible endpoint, so openclaude routes to it identically. Mix your Claude subscription with frontier OSS models like `gpt-oss:120b-cloud`, `qwen3:480b-cloud`, `deepseek-v3.1:671b-cloud`, or `kimi-k2:1t-cloud` — none of which fit on consumer hardware — without leaving Claude Code.\n\n```bash\n# Option A — pull cloud models into local Ollama (most common).\n# They appear in /model via discovery and route through ollama-local.\nollama pull gpt-oss:120b-cloud\noc start\n\n# Option B — hit ollama.com directly, no local registration.\nexport OLLAMA_API_KEY=<your-key-from-ollama.com>\noc start\n# In /model: paste \"/model ollama-cloud:<name>\"\n```\n\nThe default config ships `ollama-cloud` with `apiKey: \"$OLLAMA_API_KEY\"`; the router substitutes the env var at request time. Get a key at [ollama.com](https://ollama.com/cloud).\n\n## Reference\n\n### Commands\n\n| Command                        | What it does                                                |\n| ------------------------------ | ----------------------------------------------------------- |\n| `oc start`                     | Boot router (if needed); exec `claude` with router env set. |\n| `oc start --bridge=aggressive` | Also bind Sonnet+Opus picker slots to Ollama (3 slots vs 1).|\n| `oc start --no-discovery`      | Don't set the AUTH_TOKEN sentinel; alias slots only.        |\n| `oc stop`                      | Shut down the router daemon.                                |\n| `oc status`                    | Show daemon state + configured providers.                   |\n| `oc list`                      | List installed Ollama models with paste-ready /model lines. |\n| `oc model-subagent`            | Show/select subagent model for Claude Code.                 |\n| `oc model-subagent <n>`        | Set subagent model by number (takes effect on next start).  |\n| `oc internal-classifier`       | Show/select model for Claude Code's safety classifier.      |\n| `oc internal-classifier <n>`   | Set classifier model by number (default: Anthropic Haiku).  |\n\n### Configuration\n\nConfig lives at `~/.openclaude/config.json` (auto-seeded on first run). Add providers by editing it — changes are picked up on the next request, no daemon restart needed:\n\n```json\n{\n  \"port\": 11436,\n  \"defaultProvider\": \"claude\",\n  \"providers\": {\n    \"claude\":        { \"type\": \"anthropic-passthrough\", \"baseUrl\": \"https://api.anthropic.com\" },\n    \"ollama-local\":  { \"type\": \"ollama\", \"baseUrl\": \"http://127.0.0.1:11434\" },\n    \"ollama-cloud\":  { \"type\": \"ollama\", \"baseUrl\": \"https://ollama.com\", \"apiKey\": \"$OLLAMA_API_KEY\" },\n    \"remote-ollama\": { \"type\": \"ollama\", \"baseUrl\": \"http://192.168.1.50:11434\" }\n  }\n}\n```\n\n> The daemon writes its pid/log/config under `~/.openclaude/` (or `%USERPROFILE%\\.openclaude\\` on Windows). Override with `OPENCLAUDE_HOME=/some/path`.\n\n### Auth model\n\n| Provider type           | Auth                                                                        |\n| ----------------------- | --------------------------------------------------------------------------- |\n| `anthropic-passthrough` | Claude Code's `Authorization` header is forwarded untouched. No key needed. |\n| `ollama` (local)        | None — Ollama ignores the key.                                              |\n| `ollama` (cloud)        | `x-api-key: $OLLAMA_API_KEY` (env-var-interpolated).                        |\n\n<details>\n<summary><strong>Under the hood</strong> — cross-provider sanitization, image-stripping, SSE fixup, tests</summary>\n\n### Conversation-history sanitization\n\nCross-provider sessions accumulate metadata that the *next* provider can't validate. Two known cases, both fixed unconditionally on every outgoing request:\n\n1. **Tool-use ids.** Anthropic requires `tool_use.id` to match `^[a-zA-Z0-9_-]+$`. Ollama's compat layer can emit ids with `.`, `:`, or `#`. Claude Code stores those in history and replays them — so when you switch back to a Claude model, Anthropic returns `400 messages.N.content.M.tool_use.id: String should match pattern ...`. The router rewrites dirty ids to `toolu_oc_<sanitized>` form and remaps the matching `tool_result.tool_use_id` so the call/result graph stays coherent.\n2. **Thinking-block signatures.** Anthropic cryptographically signs every `thinking` block it emits and validates the signature on replay. Non-Anthropic upstreams (Ollama, our stream-fixup synthesizer) emit thinking blocks with empty or placeholder signatures, which Anthropic later rejects with `400 messages.N.content.M: Invalid signature in thinking block`. The router scans for thinking blocks whose signature looks fake (missing or shorter than ~64 chars) and replaces them with a `[thinking from a prior model omitted]` text marker. Real Anthropic signatures are preserved untouched.\n\nLog lines confirm when either fires: `sanitized N dirty tool_use id(s)` / `dropped N thinking block(s) with non-Anthropic signature`.\n\n### Image-stripping for text-only models\n\nClaude Code re-sends the full conversation history every turn, so an image you attached three turns ago is still in the request when you switch to a text-only model like `gpt-oss:120b-cloud`. Without intervention, that model returns 400 `\"this model does not support image input\"` and you can't continue.\n\nThe router probes each Ollama model's capabilities via `/api/show` (cached in-process), and for models that don't list `vision`, strips `image` content blocks — replacing them with a `[image omitted]` text marker. Vision-capable models are unaffected; nested images inside tool-result blocks are handled too. A startup line `[openclaude] stripped N image block(s) for text-only model X` confirms when this fires.\n\n### SSE stream sanitizer\n\nOllama's Anthropic-compat layer occasionally emits a `content_block_delta` event for an index it never opened with `content_block_start` — Claude Code's stream parser then aborts with `\"Content block not found\"` and retries forever (seen on multimodal models like `kimi-k2.6:cloud`). The router wraps every Ollama streaming response in a sanitizer (`src/router/stream-fixup.js`) that tracks open content-block indices, synthesizes the missing `content_block_start` for orphan deltas (matching type — text, thinking, or tool_use with a placeholder id), tracks `content_block_stop` so re-used indices re-synthesize, and passes everything else through byte-for-byte. Anthropic-passthrough responses (real Claude) are never touched.\n\nThe router is also resilient to upstream streams that fail *after* forwarding has begun (e.g. an upstream body timeout): the partial response is closed cleanly and the daemon keeps serving rather than crashing.\n\n### Tests\n\n```bash\nnpm test\n```\n\nNode's built-in test runner covers `parseModelTarget` routing; a real-router-vs-stub e2e (OAuth pass-through, Ollama `x-api-key`, no header leakage, discovery decoding); the SSE sanitizer; image stripping; tool-use id + thinking-signature sanitization; subagent-model resolution/config round-trip; and the mid-stream upstream-failure regression.\n\n</details>\n\n## Limitations\n\n- **`tool_choice` forcing isn't supported** by Ollama's Anthropic-compat layer; some models may degrade tool-use behavior.\n- **Synthesized `tool_use` blocks** (from an orphan `input_json_delta` with no preceding start) get a placeholder `id` and `name: \"unknown\"` — the call still executes parser-side but downstream tool routing may fail. Vision/text deltas have no such caveat.\n- Only `anthropic-passthrough` and `ollama` provider types are supported today. OpenAI / OpenRouter / Bedrock / Vertex are deferred (the abstraction is ready).\n- **Discovery mode disables Remote Control / `/schedule` / claude.ai MCP / notification prefs** for the session (a Claude Code requirement). Use `--no-discovery` to keep them.\n\n## Status\n\n**v0.1.0** — small Node.js ESM router + CLI, no build step, no runtime dependencies. [MIT licensed](https://opensource.org/licenses/MIT).\n","readmeFilename":"README.md"}