@askalf/dario 3.31.2 → 3.31.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <h1 align="center">dario</h1>
3
- <p align="center"><strong>A local LLM router. One endpoint, every provider.</strong><br>Runs on your machine. Unifies OpenAI, Groq, OpenRouter, Ollama, vLLM, LiteLLM, any OpenAI-compat URL, and your Claude Max / Pro subscription (via OAuth) behind one endpoint at <code>http://localhost:3456</code>. Speaks both the Anthropic Messages API and the OpenAI Chat Completions API, so your tools stop caring which vendor is upstream. Drops in under the <a href="https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk">Claude Agent SDK</a> as an API-key-compatible backend.</p>
3
+ <p align="center"><strong>A local LLM router. One endpoint, every provider.</strong><br>Runs on your machine. Unifies OpenAI, Groq, OpenRouter, Ollama, vLLM, LiteLLM, any OpenAI-compat URL, and your Claude Max subscription (via OAuth) behind one endpoint at <code>http://localhost:3456</code>. Speaks both the Anthropic Messages API and the OpenAI Chat Completions API, so your tools stop caring which vendor is upstream. Drops in under the <a href="https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk">Claude Agent SDK</a> as an API-key-compatible backend.</p>
4
4
  </p>
5
5
 
6
6
  <p align="center"><em>Zero runtime dependencies. <a href="https://www.npmjs.com/package/@askalf/dario">SLSA-attested</a> on every release. Nothing phones home. Independent, unofficial, third-party — see <a href="DISCLAIMER.md">DISCLAIMER.md</a>.</em></p>
@@ -21,7 +21,7 @@
21
21
  # 1. Install
22
22
  npm install -g @askalf/dario
23
23
 
24
- # 2. Log in to your Claude Max / Pro subscription
24
+ # 2. Log in to your Claude Max subscription
25
25
  dario login # or `dario login --manual` for SSH / headless setups
26
26
 
27
27
  # 3. Start the local Claude API proxy
@@ -32,7 +32,7 @@ export ANTHROPIC_BASE_URL=http://localhost:3456
32
32
  export ANTHROPIC_API_KEY=dario
33
33
  ```
34
34
 
35
- Done. Every tool that honors those env vars — Claude Code, Cursor, Aider, Cline, Roo Code, Continue.dev, Zed, Windsurf, OpenHands, OpenClaw, Hermes, the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk), your own scripts — now routes through your **Claude Max / Pro subscription** instead of per-token API pricing, because dario sends the same request shape Claude Code itself sends, which is the shape the subscription-billing path recognizes.
35
+ Done. Every tool that honors those env vars — Claude Code, Cursor, Aider, Cline, Roo Code, Continue.dev, Zed, Windsurf, OpenHands, OpenClaw, Hermes, the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk), your own scripts — now routes through your **Claude Max subscription** instead of per-token API pricing, because dario sends the same request shape Claude Code itself sends, which is the shape the subscription-billing path recognizes.
36
36
 
37
37
  For OpenAI / Groq / OpenRouter / Ollama / LiteLLM / vLLM, add one backend line and reuse the same proxy:
38
38
 
@@ -99,7 +99,7 @@ Beyond routing, the Claude backend is a **full Claude Code wire-level template**
99
99
  - **Developers using multiple LLMs across multiple tools** tired of juggling base URLs, keys, and per-tool provider configs.
100
100
  - **Teams running local or hosted OpenAI-compat servers** (LiteLLM, vLLM, Ollama, Groq, OpenRouter, self-hosted) who want one stable local endpoint every tool can reuse.
101
101
  - **Anyone building AI coding tools** who wants provider independence without writing an OpenAI ↔ Anthropic translator themselves.
102
- - **Claude Max / Pro subscribers** who want their subscription usable from every tool on their machine, not just Claude Code.
102
+ - **Claude Max subscribers** who want their subscription usable from every tool on their machine, not just Claude Code.
103
103
  - **[Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) users** who want OAuth-subscription routing under the SDK. Point `baseURL: 'http://localhost:3456'` and dario translates API-key calls into your Claude Max auth — agent code stays identical.
104
104
  - **Power users on multi-agent workloads** who want multi-account pooling, session stickiness, and in-flight 429 failover on their own machine, against their own subscriptions.
105
105
  - **Operators who care about wire-level fidelity** — the v3.22 – v3.28 tightening means proxy mode's divergence from CC is observable (via `dario doctor`) and tunable (flags + env vars for each axis).
@@ -150,7 +150,7 @@ Force a backend with a **provider prefix** on the model field (`openai:gpt-4o`,
150
150
 
151
151
  ### 2. Claude subscription backend
152
152
 
153
- OAuth-backed Claude Max / Pro, billed against your plan instead of the API. Activated by `dario login` (or `dario login --manual` for SSH / container setups without a browser, v3.20).
153
+ OAuth-backed Claude Max, billed against your plan instead of the API. Activated by `dario login` (or `dario login --manual` for SSH / container setups without a browser, v3.20). Other plan tiers work if and only if Anthropic gives them Claude Code access — see the FAQ.
154
154
 
155
155
  **What it does.** Every outbound Claude request is rebuilt to match a request Claude Code itself would make — system prompt, tool definitions, identity headers, billing tag, beta flags, **header insertion order, static header values, `anthropic-beta` flag set, and top-level request-body key order** — using a live-extracted template from your actually-installed CC binary that self-heals on every upstream CC release. Because the wire shape matches CC, the upstream subscription-billing path is the one the request follows — instead of API overage.
156
156
 
@@ -208,7 +208,7 @@ Each request picks the account with the highest headroom:
208
208
  headroom = 1 - max(util_5h, util_7d)
209
209
  ```
210
210
 
211
- The response's `anthropic-ratelimit-unified-*` headers are parsed back into the pool so the next selection sees fresh utilization. An account that returns a 429 is marked `rejected` and routed around until its window resets. When every account is exhausted, requests queue for up to 60 seconds waiting for headroom to reappear. Plans can mix freely — Max and Pro accounts sit in the same pool; dario doesn't care about tier, only headroom.
211
+ The response's `anthropic-ratelimit-unified-*` headers are parsed back into the pool so the next selection sees fresh utilization. An account that returns a 429 is marked `rejected` and routed around until its window resets. When every account is exhausted, requests queue for up to 60 seconds waiting for headroom to reappear. Plan tiers mix freely in the same pool dario doesn't care about tier, only headroom.
212
212
 
213
213
  ### Session stickiness
214
214
 
@@ -466,7 +466,7 @@ Some tools use env vars (above works as-is); others want settings-UI entries:
466
466
  - Add `claude-haiku-4-5` (cheap)
467
467
  4. Select one of the new models in the chat input's model picker.
468
468
 
469
- Cursor now routes those model names through dario → your Claude Max / Pro subscription. `gpt-*` and `o*` model names still route through Cursor's default OpenAI path — dario doesn't interfere with non-Claude traffic unless you point Cursor's base URL at it exclusively.
469
+ Cursor now routes those model names through dario → your Claude Max subscription. `gpt-*` and `o*` model names still route through Cursor's default OpenAI path — dario doesn't interfere with non-Claude traffic unless you point Cursor's base URL at it exclusively.
470
470
 
471
471
  #### Continue.dev
472
472
 
@@ -590,7 +590,7 @@ Fix: run dario with `--preserve-tools`. That skips the CC tool remap entirely, p
590
590
  dario proxy --preserve-tools
591
591
  ```
592
592
 
593
- The cost: requests no longer look like CC on the wire, so the subscription-billing wire shape is gone. On a Max/Pro plan, that means the request may be counted against your API usage rather than your subscription quota. [Hybrid tool mode](#hybrid-tool-mode) below is the compromise that keeps both.
593
+ The cost: requests no longer look like CC on the wire, so the subscription-billing wire shape is gone. On a subscription plan, that means the request may be counted against your API usage rather than your subscription quota. [Hybrid tool mode](#hybrid-tool-mode) below is the compromise that keeps both.
594
594
 
595
595
  The OpenAI-compat backend is unaffected — it forwards tool definitions byte-for-byte and doesn't need this flag.
596
596
 
@@ -737,10 +737,13 @@ Four independent senior-engineer-style reviews from frontier LLMs, same prompt,
737
737
  Mechanically: dario's Claude backend uses your existing Claude Code credentials with the same OAuth tokens CC uses. It authenticates you as you, with your subscription, through Anthropic's official API endpoints. Whether any particular use complies with Anthropic's current terms of service is between you and Anthropic — consult their terms and your own subscription agreement. This project is an independent, unofficial, third-party tool and does not provide legal advice. See [DISCLAIMER.md](DISCLAIMER.md).
738
738
 
739
739
  **What subscription plans work on the Claude backend?**
740
- Claude Max and Claude Pro. Any plan that lets you use Claude Code.
740
+ Any plan whose account currently has Claude Code access — Max has it unconditionally; Pro has it as of this writing but that's an upstream decision that has moved once already (see next entry). If `claude /login` on your account works and `claude -p "hi"` returns a response on subscription billing, dario's Claude backend will work too. If Anthropic removes Claude Code from your plan tier, dario's Claude backend stops working on that account there is nothing dario can do at the client side to change that. Swap to a plan with Claude Code access, or use an OpenAI-compat backend instead.
741
+
742
+ **Is it true Anthropic removed Claude Code from Pro plans?**
743
+ On 2026-04-21 Anthropic temporarily removed Claude Code from new Pro signups, per [wheresyoured.at](https://www.wheresyoured.at/news-anthropic-removes-pro-cc/). Existing Pro users reportedly kept access; Anthropic's Head of Growth characterized it as "a small test of 2% of new prosumer signups," and the change was reversed at an unknown time. If you are a Pro user and dario's Claude backend stops billing against your subscription without warning, this is the class of thing to check — run `claude -p "hi"` directly and see whether Anthropic itself routes you to subscription billing. If they don't, dario can't either. The practical mitigation on dario's side is [multi-account pool mode](#multi-account-pool-mode) — having a backup account on a plan Anthropic hasn't moved the goalposts on, so a single plan-tier change doesn't take all your traffic down at once.
741
744
 
742
745
  **Does it work with Team / Enterprise?**
743
- Should work if your plan includes Claude Code access. Not widely tested yet — open an issue with results.
746
+ Yes tested and confirmed working as long as your plan includes Claude Code access.
744
747
 
745
748
  **Do I need Claude Code installed?**
746
749
  Recommended for the Claude backend, not strictly required. With CC installed, `dario login` picks up your credentials automatically, and the live template extractor reads your CC binary on every startup so the template stays current. Without CC, dario runs its own OAuth flow and falls back to the bundled template snapshot (scrubbed of host context at bake time as of v3.21). Drift detection warns you if your installed CC doesn't match the captured template, so upgrade windows don't silently ship stale templates.
@@ -797,7 +800,7 @@ Claude subscriptions have rolling 5-hour and 7-day usage windows. Check utilizat
797
800
  | `seven_day` | You've exhausted (or come close to exhausting) the 5-hour window for this rolling cycle, so Anthropic is charging this request against the 7-day bucket. **Still subscription billing. Still your plan.** Not API pricing, not overage. |
798
801
  | `overage` | Both subscription windows are effectively exhausted. *This* is where per-token Extra Usage charges kick in — if you've enabled Extra Usage on the account. If not, you get 429'd instead. |
799
802
 
800
- Seeing `seven_day` is a healthy state. Your Max/Pro plan is doing exactly what it's supposed to do: letting you keep working past short bursts of heavy use by absorbing them into the larger 7-day bucket. When your 5-hour window rolls forward enough, the claim on new requests will go back to `five_hour` on its own. If the 7-day bucket is painful, add more Claude subscriptions to the pool — each account has its own independent 5h/7d windows, and pool mode routes each request to the account with the most headroom.
803
+ Seeing `seven_day` is a healthy state. Your Max plan is doing exactly what it's supposed to do: letting you keep working past short bursts of heavy use by absorbing them into the larger 7-day bucket. When your 5-hour window rolls forward enough, the claim on new requests will go back to `five_hour` on its own. If the 7-day bucket is painful, add more Claude subscriptions to the pool — each account has its own independent 5h/7d windows, and pool mode routes each request to the account with the most headroom.
801
804
 
802
805
  Standalone writeup: [Discussion #32 — why you see `representative-claim: seven_day` and why it's not a downgrade](https://github.com/askalf/dario/discussions/32).
803
806
 
@@ -28,7 +28,7 @@
28
28
  * "MANUAL_REDIRECT_URL" on platform.claude.com is only used when dario's
29
29
  * local HTTP server can't bind a port; dario never hits that path.)
30
30
  *
31
- * Results are cached per-binary-hash at ~/.dario/cc-oauth-cache-v4.json so
31
+ * Results are cached per-binary-hash at ~/.dario/cc-oauth-cache-v6.json so
32
32
  * startup only re-scans when the user upgrades Claude Code. The cache suffix
33
33
  * is bumped each time scope handling or the fallback config changes, so
34
34
  * upgrading dario picks up the new values without a manual cache clear.
@@ -46,6 +46,23 @@ export interface DetectedOAuthConfig {
46
46
  ccPath?: string;
47
47
  ccHash?: string;
48
48
  }
49
+ /**
50
+ * Normalize the authorize URL to match what CC uses at runtime.
51
+ *
52
+ * The CC binary ships `CLAUDE_AI_AUTHORIZE_URL: "https://claude.com/cai/oauth/authorize"`
53
+ * as a literal string, but at runtime CC's `/login` opens
54
+ * `https://claude.ai/oauth/authorize` directly (empirically verified against
55
+ * CC v2.1.116 by tetsuco in dario#71). Historically the claude.com edge
56
+ * 307-redirected to claude.ai and the browser followed; recent Anthropic-side
57
+ * changes made the post-redirect validation start returning "Invalid request
58
+ * format", while direct requests to claude.ai continue to work. This normalizer
59
+ * rewrites the legacy URL wherever it appears (binary extraction, manual
60
+ * override, cached config) so dario matches CC's runtime behaviour.
61
+ *
62
+ * Intentionally narrow: only the exact legacy URL is rewritten. Any other
63
+ * operator-supplied URL (e.g. a staging endpoint via override) passes through.
64
+ */
65
+ export declare function normalizeAuthorizeUrl(url: string): string;
49
66
  export declare const FALLBACK_FOR_DRIFT_CHECK: Readonly<DetectedOAuthConfig>;
50
67
  /**
51
68
  * Scan binary bytes for the PROD OAuth config block.
@@ -28,7 +28,7 @@
28
28
  * "MANUAL_REDIRECT_URL" on platform.claude.com is only used when dario's
29
29
  * local HTTP server can't bind a port; dario never hits that path.)
30
30
  *
31
- * Results are cached per-binary-hash at ~/.dario/cc-oauth-cache-v4.json so
31
+ * Results are cached per-binary-hash at ~/.dario/cc-oauth-cache-v6.json so
32
32
  * startup only re-scans when the user upgrades Claude Code. The cache suffix
33
33
  * is bumped each time scope handling or the fallback config changes, so
34
34
  * upgrading dario picks up the new values without a manual cache clear.
@@ -42,36 +42,75 @@ import { existsSync } from 'node:fs';
42
42
  import { homedir, platform } from 'node:os';
43
43
  import { join, dirname } from 'node:path';
44
44
  import { createHash } from 'node:crypto';
45
+ /**
46
+ * Normalize the authorize URL to match what CC uses at runtime.
47
+ *
48
+ * The CC binary ships `CLAUDE_AI_AUTHORIZE_URL: "https://claude.com/cai/oauth/authorize"`
49
+ * as a literal string, but at runtime CC's `/login` opens
50
+ * `https://claude.ai/oauth/authorize` directly (empirically verified against
51
+ * CC v2.1.116 by tetsuco in dario#71). Historically the claude.com edge
52
+ * 307-redirected to claude.ai and the browser followed; recent Anthropic-side
53
+ * changes made the post-redirect validation start returning "Invalid request
54
+ * format", while direct requests to claude.ai continue to work. This normalizer
55
+ * rewrites the legacy URL wherever it appears (binary extraction, manual
56
+ * override, cached config) so dario matches CC's runtime behaviour.
57
+ *
58
+ * Intentionally narrow: only the exact legacy URL is rewritten. Any other
59
+ * operator-supplied URL (e.g. a staging endpoint via override) passes through.
60
+ */
61
+ export function normalizeAuthorizeUrl(url) {
62
+ if (url === 'https://claude.com/cai/oauth/authorize') {
63
+ return 'https://claude.ai/oauth/authorize';
64
+ }
65
+ return url;
66
+ }
45
67
  // Last-resort fallback if CC binary can't be found or scanned.
46
68
  // These values are the CC v2.1.104 PROD OAuth config, extracted from
47
- // the `nh$` object in the shipped binary.
69
+ // the `nh$` object in the shipped binary. authorizeUrl is normalized —
70
+ // see normalizeAuthorizeUrl() above for why this matters (dario#71).
48
71
  const FALLBACK = {
49
72
  clientId: '9d1c250a-e61b-44d9-88ed-5944d1962f5e',
50
- authorizeUrl: 'https://claude.com/cai/oauth/authorize',
73
+ authorizeUrl: 'https://claude.ai/oauth/authorize',
51
74
  tokenUrl: 'https://platform.claude.com/v1/oauth/token',
52
- // Scopes match CC v2.1.107+ interactive login: the 5-scope user-only set.
53
- // Between CC v2.1.104 and v2.1.107, Anthropic's authorize endpoint flipped
54
- // its policy on `org:create_api_key` for this client_id the shorter list
55
- // is now the only accepted one, and the 6-scope form returns "Invalid
56
- // request format". CC's own binary dropped `org:create_api_key` from the
57
- // `n36` union to match. Dario #42 (tetsuco, 2026-04-17) surfaced this as
58
- // a fresh-login failure on macOS against CC v2.1.107.
75
+ // Scopes match CC v2.1.116+ interactive login: the 6-scope set including
76
+ // `org:create_api_key` as the FIRST scope. We previously shipped only 5
77
+ // scopes based on dario#42's v2.1.107 observationAnthropic had flipped
78
+ // to rejecting the 6-scope form and CC's own binary dropped
79
+ // `org:create_api_key` from its `n36` union. Between v2.1.107 and v2.1.116
80
+ // Anthropic flipped BACK: v2.1.116's `/login` opens the authorize URL with
81
+ // all 6 scopes (dario#71, tetsuco, 2026-04-23 authorize URL diff across
82
+ // all three scope-variant tests confirmed CC's 6-scope list is the only
83
+ // one accepted by the current `claude.ai/oauth/authorize` endpoint for
84
+ // this client_id).
59
85
  //
60
- // History: dario 3.2.7–3.4.3 once dropped this scope by mistake (misread
61
- // of the "Console-only" name); 3.4.4 added it back after users hit auth
62
- // failures with the dev client_id accepting it but prod rejecting. That
63
- // situation has now inverted prod rejects the longer list.
64
- scopes: 'user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload',
86
+ // Scope-list history on this client_id:
87
+ // - dario 3.2.7–3.4.3: 5 scopes (misread "Console-only" name), dropped
88
+ // `org:create_api_key` wrongly. Users hit auth failures.
89
+ // - dario 3.4.4: 6 scopes restored after prod started rejecting 5.
90
+ // - dario 3.19.5: 5 scopes again, after prod rotated to rejecting 6 on
91
+ // CC v2.1.107.
92
+ // - dario 3.31.4 (this): 6 scopes again, after prod rotated back on
93
+ // CC v2.1.116.
94
+ //
95
+ // The scope list can't be extracted from the binary reliably (see
96
+ // extractFromBinary's comment). If Anthropic flips again the fix is one
97
+ // line here plus a cache-version bump.
98
+ scopes: 'org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload',
65
99
  source: 'fallback',
66
100
  };
67
101
  // Re-export of FALLBACK for scripts/check-cc-authorize-probe.mjs. The probe
68
102
  // needs the exact values the runtime uses — hardcoding them in the script
69
103
  // would drift out of sync silently.
70
104
  export const FALLBACK_FOR_DRIFT_CHECK = FALLBACK;
71
- // -v4 suffix invalidates v3.x caches populated with the 6-scope list that
72
- // Anthropic now rejects (dario #42). On upgrade, users regenerate the cache
73
- // with the new FALLBACK scopes automatically no manual clear required.
74
- const CACHE_PATH = join(homedir(), '.dario', 'cc-oauth-cache-v4.json');
105
+ // -v6 suffix invalidates -v5 caches populated with the 5-scope FALLBACK.
106
+ // Those caches stored scopes from the FALLBACK copy at extract time, so
107
+ // bumping FALLBACK.scopes without bumping the cache version would leave
108
+ // upgraded users still hitting "Invalid request format" until they
109
+ // manually deleted the cache file. On upgrade to v3.31.4 the cache
110
+ // regenerates automatically with the 6-scope set (dario#71). Previous
111
+ // bumps: -v3 → -v4 in v3.19.4 for 6→5 rotation (dario#42); -v4 → -v5 in
112
+ // v3.31.3 for the authorize URL normalization.
113
+ const CACHE_PATH = join(homedir(), '.dario', 'cc-oauth-cache-v6.json');
75
114
  const DEFAULT_OVERRIDE_PATH = join(homedir(), '.dario', 'oauth-config.override.json');
76
115
  function candidatePaths() {
77
116
  const home = homedir();
@@ -162,9 +201,16 @@ function applyManualOverride(config, override) {
162
201
  return config;
163
202
  warnOnNonHttpsOverride('authorizeUrl', override.authorizeUrl);
164
203
  warnOnNonHttpsOverride('tokenUrl', override.tokenUrl);
204
+ // Normalize any override-supplied authorizeUrl too — users who pasted the
205
+ // legacy claude.com URL into ~/.dario/oauth-config.override.json pre-#71
206
+ // shouldn't be silently broken after upgrade.
207
+ const normalizedOverride = { ...override };
208
+ if (normalizedOverride.authorizeUrl) {
209
+ normalizedOverride.authorizeUrl = normalizeAuthorizeUrl(normalizedOverride.authorizeUrl);
210
+ }
165
211
  return {
166
212
  ...config,
167
- ...override,
213
+ ...normalizedOverride,
168
214
  source: 'override',
169
215
  };
170
216
  }
@@ -222,7 +268,7 @@ export function scanBinaryForOAuthConfig(buf) {
222
268
  let authorizeUrl = FALLBACK.authorizeUrl;
223
269
  const authMatch = /CLAUDE_AI_AUTHORIZE_URL\s*:\s*"([^"]+)"/.exec(prodBlock);
224
270
  if (authMatch && authMatch[1])
225
- authorizeUrl = authMatch[1];
271
+ authorizeUrl = normalizeAuthorizeUrl(authMatch[1]);
226
272
  let tokenUrl = FALLBACK.tokenUrl;
227
273
  const tokenMatch = /TOKEN_URL\s*:\s*"(https:\/\/[^"]*\/oauth\/token[^"]*)"/.exec(prodBlock);
228
274
  if (tokenMatch && tokenMatch[1])
@@ -281,7 +281,7 @@ export declare function _resetInstalledVersionProbeForTest(): void;
281
281
  */
282
282
  export declare const SUPPORTED_CC_RANGE: {
283
283
  readonly min: "1.0.0";
284
- readonly maxTested: "2.1.117";
284
+ readonly maxTested: "2.1.118";
285
285
  };
286
286
  /**
287
287
  * Compare two dotted-numeric version strings. Returns negative if `a<b`,
@@ -730,7 +730,7 @@ export function _resetInstalledVersionProbeForTest() {
730
730
  */
731
731
  export const SUPPORTED_CC_RANGE = {
732
732
  min: '1.0.0',
733
- maxTested: '2.1.117',
733
+ maxTested: '2.1.118',
734
734
  };
735
735
  /**
736
736
  * Compare two dotted-numeric version strings. Returns negative if `a<b`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "3.31.2",
3
+ "version": "3.31.4",
4
4
  "description": "A local LLM router. One endpoint, every provider — Claude subscriptions, OpenAI, OpenRouter, Groq, local LiteLLM, any OpenAI-compat endpoint — your tools don't need to change.",
5
5
  "type": "module",
6
6
  "bin": {