@askalf/dario 3.33.0 → 3.34.0
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 +119 -710
- package/dist/cc-template.d.ts +30 -0
- package/dist/cc-template.js +73 -2
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +78 -2
- package/dist/doctor.js +27 -1
- package/dist/proxy.d.ts +19 -0
- package/dist/proxy.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<h1 align="center">dario</h1>
|
|
3
|
-
<p align="center"><strong>
|
|
3
|
+
<p align="center"><strong>Use your Claude Pro/Max subscription with Cursor, Aider, Cline, Zed, the Claude Agent SDK — any tool that speaks Anthropic or OpenAI.</strong></p>
|
|
4
|
+
<p align="center">A local LLM router. One endpoint, every provider. Your Claude subscription — Pro ($20), Max 5x ($100), or Max 20x ($200) — stops sitting idle in Claude Code while you pay per-token everywhere else. Speaks both the Anthropic Messages API and the OpenAI Chat Completions API at <code>http://localhost:3456</code>.</p>
|
|
4
5
|
</p>
|
|
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>
|
|
7
|
-
|
|
8
7
|
<p align="center">
|
|
9
8
|
<a href="https://www.npmjs.com/package/@askalf/dario"><img src="https://img.shields.io/npm/v/@askalf/dario?color=blue" alt="npm version"></a>
|
|
10
9
|
<a href="https://github.com/askalf/dario/actions/workflows/ci.yml"><img src="https://github.com/askalf/dario/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
@@ -13,6 +12,15 @@
|
|
|
13
12
|
<a href="https://www.npmjs.com/package/@askalf/dario"><img src="https://img.shields.io/npm/dm/@askalf/dario" alt="Downloads"></a>
|
|
14
13
|
</p>
|
|
15
14
|
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="https://x.com/ask_alf"><img src="https://img.shields.io/badge/follow-@ask_alf-1da1f2?style=flat-square" alt="Follow on X"></a>
|
|
17
|
+
<a href="https://askalf.org"><img src="https://img.shields.io/badge/askalf.org-platform-00ff88?style=flat-square" alt="askalf"></a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<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
|
+
|
|
22
|
+
> **dario is the open-source wedge of [askalf](https://askalf.org)** — the AI workforce platform we're building. Dario solves the Claude subscription problem so the rest of the workforce can run on flat-rate billing. Star this repo or follow [@ask_alf](https://x.com/ask_alf) for platform updates.
|
|
23
|
+
|
|
16
24
|
---
|
|
17
25
|
|
|
18
26
|
## 30 seconds
|
|
@@ -21,7 +29,7 @@
|
|
|
21
29
|
# 1. Install
|
|
22
30
|
npm install -g @askalf/dario
|
|
23
31
|
|
|
24
|
-
# 2. Log in to your Claude Max
|
|
32
|
+
# 2. Log in to your Claude subscription (Pro, Max 5x, or Max 20x)
|
|
25
33
|
dario login # or `dario login --manual` for SSH / headless setups
|
|
26
34
|
|
|
27
35
|
# 3. Start the local Claude API proxy
|
|
@@ -32,7 +40,7 @@ export ANTHROPIC_BASE_URL=http://localhost:3456
|
|
|
32
40
|
export ANTHROPIC_API_KEY=dario
|
|
33
41
|
```
|
|
34
42
|
|
|
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
|
|
43
|
+
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 subscription** (Pro / Max 5x / Max 20x) instead of per-token API pricing. Dario sends the same request shape Claude Code itself sends, which is the shape the subscription-billing path recognizes.
|
|
36
44
|
|
|
37
45
|
For OpenAI / Groq / OpenRouter / Ollama / LiteLLM / vLLM, add one backend line and reuse the same proxy:
|
|
38
46
|
|
|
@@ -50,13 +58,11 @@ Switching providers is a **model-name change** in your tool — `claude-opus-4-7
|
|
|
50
58
|
|
|
51
59
|
Something not right? `dario doctor` prints a single paste-ready health report. Paste that when you file an issue.
|
|
52
60
|
|
|
53
|
-
> **Background reading:** [#68 — dario vs LiteLLM / OpenRouter / Kong AI Gateway (when each one wins)](https://github.com/askalf/dario/discussions/68) · [#13 — Claude Code's "defaults" are detection signals, not optimizations](https://github.com/askalf/dario/discussions/13) · [#39 — Why your Claude Max usage is burning in minutes](https://github.com/askalf/dario/discussions/39) · [#1 — What Claude's rate limit headers actually reveal](https://github.com/askalf/dario/discussions/1) · [#14 — Template Replay: why we stopped matching signals](https://github.com/askalf/dario/discussions/14)
|
|
54
|
-
|
|
55
61
|
---
|
|
56
62
|
|
|
57
63
|
## What it actually does
|
|
58
64
|
|
|
59
|
-
You point every tool at one URL. Dario reads each request, decides which backend owns it, and forwards
|
|
65
|
+
You point every tool at one URL. Dario reads each request, decides which backend owns it, and forwards in that backend's native protocol.
|
|
60
66
|
|
|
61
67
|
| Client speaks | Model in request | dario routes to | What happens |
|
|
62
68
|
|---|---|---|---|
|
|
@@ -68,27 +74,59 @@ You point every tool at one URL. Dario reads each request, decides which backend
|
|
|
68
74
|
|
|
69
75
|
The tool doesn't know. The backend doesn't know. Dario is the seam.
|
|
70
76
|
|
|
71
|
-
Beyond routing, the Claude backend is a **full Claude Code wire-level template** — every observable axis (bytes, headers, body key order, TLS stack, inter-request timing, session-id lifecycle, stream-consumption shape) is captured from your installed CC binary and mirrored on outbound requests so the upstream subscription-billing path is the one the request follows. See [
|
|
77
|
+
Beyond routing, the Claude backend is a **full Claude Code wire-level template** — every observable axis (bytes, headers, body key order, TLS stack, inter-request timing, session-id lifecycle, stream-consumption shape) is captured from your installed CC binary and mirrored on outbound requests so the upstream subscription-billing path is the one the request follows. See [`docs/wire-fidelity.md`](./docs/wire-fidelity.md).
|
|
72
78
|
|
|
73
79
|
---
|
|
74
80
|
|
|
75
|
-
##
|
|
81
|
+
## Cost comparison
|
|
82
|
+
|
|
83
|
+
Claude subscription tiers: **Pro** ($20/mo) · **Max 5x** ($100/mo) · **Max 20x** ($200/mo). Dario routes through whichever you have — pick by your usage volume, not by what dario needs.
|
|
84
|
+
|
|
85
|
+
| Setup | Monthly cost (heavy single-tool user) |
|
|
86
|
+
|---|---|
|
|
87
|
+
| Cursor + Anthropic API direct | $80–$300 |
|
|
88
|
+
| Cursor + ChatGPT Plus | $20 + per-token overage |
|
|
89
|
+
| **Cursor + Claude Pro/Max + dario** | **$20 (Cursor) + $20–200 (your Claude tier) flat — every Claude call routes through your subscription** |
|
|
90
|
+
| Multi-tool heavy use (Cursor + Aider + Cline + Continue) without dario | $200–$600+ |
|
|
91
|
+
| **Same multi-tool use with dario** | **$20–200 flat — one Pro/Max subscription routes all of them** |
|
|
92
|
+
|
|
93
|
+
Already have **Pro + Max** stacked? Pool mode (`dario accounts add work` / `dario accounts add personal`) routes across both, with session stickiness keeping multi-turn agents pinned to one account so the prompt cache survives. Tiers mix freely — dario only cares about headroom, not which plan an account is on.
|
|
94
|
+
|
|
95
|
+
---
|
|
76
96
|
|
|
77
|
-
|
|
97
|
+
## Why you'll install this
|
|
78
98
|
|
|
79
|
-
**
|
|
99
|
+
- **One URL for every provider.** Cursor, Aider, Continue, Zed, OpenHands, Claude Code, your own scripts — every tool you own has its own per-provider config. Dario collapses that into a single `localhost:3456` that speaks both Anthropic and OpenAI protocols and routes by model name.
|
|
100
|
+
- **Your Claude subscription stops sitting idle.** Cursor, Aider, Zed, Continue all want API keys and bill per-token while your Pro / Max 5x / Max 20x plan only gets used in Claude Code. Dario routes them through your plan via Claude Code's exact wire shape.
|
|
101
|
+
- **You hit rate limits on long agent runs.** Add a second / third Claude subscription with `dario accounts add work` and pool mode routes each request to whichever account has the most headroom. Session stickiness pins multi-turn conversations; in-flight 429 failover retries on a different account before your client sees the error. See [`docs/multi-account-pool.md`](./docs/multi-account-pool.md).
|
|
102
|
+
- **You run a coding agent that isn't Claude Code.** Cline, Roo Code, Cursor, Windsurf, Continue.dev, GitHub Copilot, OpenHands, OpenClaw, Hermes, hands — dario's universal `TOOL_MAP` (59 schema-verified entries) pre-maps their tool names to Claude Code's native set. No flag, no validator errors. See [`docs/agent-compat.md`](./docs/agent-compat.md).
|
|
103
|
+
- **You want the proxy off the wire entirely.** Shim mode is an in-process `globalThis.fetch` patch — no HTTP hop, no port to bind, no `BASE_URL`. `dario shim -- claude --print "hi"` and CC thinks it's talking directly to `api.anthropic.com`. See [`docs/shim.md`](./docs/shim.md).
|
|
104
|
+
- **You want CC's behavioral constraints out of your prompt.** `dario proxy --system-prompt=partial` strips CC's Tone-and-style / Text-output / verbosity / no-comments-by-default bullets and recovers ~1.2–2.8× output capability on open-ended work — empirically without flipping subscription billing (the classifier doesn't read this slot). RLHF refusals on harmful content are unaffected (alignment is in the weights, not the prompt). See [`docs/system-prompt.md`](./docs/system-prompt.md) and the empirical writeup in [`docs/research/system-prompt.md`](./docs/research/system-prompt.md).
|
|
105
|
+
- **You want dario reachable from inside Claude Code or any MCP client.** `dario subagent install` registers a CC sub-agent for in-session diagnostics ([`docs/sub-agent.md`](./docs/sub-agent.md)). `dario mcp` turns dario into a read-only MCP server ([`docs/mcp-server.md`](./docs/mcp-server.md)).
|
|
106
|
+
- **You want to actually audit it.** ~12,650 lines of TypeScript across 27 files. Zero runtime dependencies. Credentials at `~/.dario/` with `0600` permissions. `127.0.0.1`-only by default. Every release [SLSA-attested](https://www.npmjs.com/package/@askalf/dario). Nothing phones home. Small enough to read in a weekend.
|
|
80
107
|
|
|
81
|
-
|
|
108
|
+
---
|
|
82
109
|
|
|
83
|
-
|
|
110
|
+
## Independently reviewed (4 LLMs)
|
|
84
111
|
|
|
85
|
-
|
|
112
|
+
Same prompt to all four ([`reviews/PROMPT.md`](./reviews/PROMPT.md)). Each reviewer signed a verdict line. Push-back triaged in [`review-feedback`](https://github.com/askalf/dario/issues?q=label%3Areview-feedback).
|
|
86
113
|
|
|
87
|
-
|
|
114
|
+
| Reviewer | Verdict | Full review |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| **Grok 4** | "Adopt if the use-case fits." | [→](./reviews/grok-4-2026-04-21.md) |
|
|
117
|
+
| **Claude Opus 4.7** | "The fingerprint-replay claim is backed by the code." | [→](./reviews/claude-opus-4-7-2026-04-21.md) |
|
|
118
|
+
| **Gemini 2.0 Pro** | "Technically elite, zero-dependency proxy." | [→](./reviews/gemini-2-pro-2026-04-21.md) |
|
|
119
|
+
| **GPT-5.3** | "Disciplined, intentional engineering. Not vibe-coded." | [→](./reviews/gpt-5.3-2026-04-21.md) |
|
|
88
120
|
|
|
89
|
-
|
|
121
|
+
Highlights:
|
|
90
122
|
|
|
91
|
-
|
|
123
|
+
> "This is not vibe-coded; it reads like production-grade infrastructure that happens to be open-source." — Grok 4
|
|
124
|
+
>
|
|
125
|
+
> "Comments consistently cite the issue number that motivated the code — which is what scar-tissue code looks like in a project that has actual users." — Claude Opus 4.7
|
|
126
|
+
>
|
|
127
|
+
> "The implementation isn't just a simple header swap; it is a sophisticated request-level deepfake." — Gemini 2.0 Pro
|
|
128
|
+
>
|
|
129
|
+
> "Not 'best-effort mimicry'; it's capture-and-replay of a real client." — GPT-5.3
|
|
92
130
|
|
|
93
131
|
---
|
|
94
132
|
|
|
@@ -96,585 +134,108 @@ Beyond routing, the Claude backend is a **full Claude Code wire-level template**
|
|
|
96
134
|
|
|
97
135
|
**Best fit:**
|
|
98
136
|
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
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
|
-
- **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).
|
|
137
|
+
- Developers using multiple LLMs across multiple tools tired of juggling base URLs, keys, and per-tool provider configs.
|
|
138
|
+
- Claude Pro / Max subscribers who want their subscription usable from every tool on their machine, not just Claude Code.
|
|
139
|
+
- 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.
|
|
140
|
+
- [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 subscription auth — agent code stays identical.
|
|
141
|
+
- 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.
|
|
106
142
|
|
|
107
|
-
**Not a fit
|
|
143
|
+
**Not a fit:**
|
|
108
144
|
|
|
109
145
|
- You need vendor-managed production SLAs on every request. Use the provider APIs directly.
|
|
110
|
-
- You need a hosted, multi-tenant, managed routing platform with a dashboard, team auth, and support contracts. Dario is a local, single-user tool.
|
|
146
|
+
- You need a hosted, multi-tenant, managed routing platform with a dashboard, team auth, and support contracts. Dario is a local, single-user tool — the [askalf platform](https://askalf.org) is the right surface for the team / fleet case.
|
|
111
147
|
- You want a chat UI. Use claude.ai or chatgpt.com.
|
|
112
148
|
|
|
113
149
|
---
|
|
114
150
|
|
|
115
151
|
## Backends
|
|
116
152
|
|
|
117
|
-
Dario's routing is organized around **backends**. Each is a swappable adapter — add one, your tools reach it through `localhost:3456` in whichever API shape they already speak.
|
|
153
|
+
Dario's routing is organized around **backends**. Each is a swappable adapter — add one, your tools reach it through `localhost:3456` in whichever API shape they already speak. Run zero, one, or all concurrently.
|
|
118
154
|
|
|
119
155
|
### 1. OpenAI-compat backend
|
|
120
156
|
|
|
121
157
|
Any provider that speaks the OpenAI Chat Completions API.
|
|
122
158
|
|
|
123
159
|
```bash
|
|
124
|
-
|
|
125
|
-
dario backend add
|
|
126
|
-
|
|
127
|
-
# Groq
|
|
128
|
-
dario backend add groq --key=gsk_... --base-url=https://api.groq.com/openai/v1
|
|
129
|
-
|
|
130
|
-
# OpenRouter
|
|
160
|
+
dario backend add openai --key=sk-proj-...
|
|
161
|
+
dario backend add groq --key=gsk_... --base-url=https://api.groq.com/openai/v1
|
|
131
162
|
dario backend add openrouter --key=sk-or-... --base-url=https://openrouter.ai/api/v1
|
|
132
|
-
|
|
133
|
-
# Local LiteLLM / vLLM / Ollama openai-compat mode
|
|
134
|
-
dario backend add local --key=anything --base-url=http://127.0.0.1:4000/v1
|
|
163
|
+
dario backend add local --key=anything --base-url=http://127.0.0.1:4000/v1
|
|
135
164
|
```
|
|
136
165
|
|
|
137
|
-
Credentials live at `~/.dario/backends/<name>.json` with mode `0600`.
|
|
138
|
-
|
|
139
|
-
**How it routes.** On `/v1/chat/completions` the request is inspected and forwarded:
|
|
140
|
-
|
|
141
|
-
| Request model | Route |
|
|
142
|
-
|---|---|
|
|
143
|
-
| `gpt-*`, `o1-*`, `o3-*`, `o4-*`, `chatgpt-*`, `text-davinci-*`, `text-embedding-*` | OpenAI-compat backend |
|
|
144
|
-
| `claude-*` (or `opus` / `sonnet` / `haiku`) | Claude subscription backend |
|
|
145
|
-
| Anything else | Claude backend with OpenAI-compat translation |
|
|
146
|
-
|
|
147
|
-
The request body goes upstream as-is; only the `Authorization` header is swapped and the URL is pointed at `baseUrl + /chat/completions`. Streaming is forwarded byte-for-byte.
|
|
148
|
-
|
|
149
|
-
Force a backend with a **provider prefix** on the model field (`openai:gpt-4o`, `groq:llama-3.3-70b`, `claude:opus`, `local:qwen-coder`) regardless of what the model name looks like — see [Provider prefix](#provider-prefix).
|
|
166
|
+
Credentials live at `~/.dario/backends/<name>.json` with mode `0600`. Body forwarded as-is, only the `Authorization` header is swapped, streaming forwarded byte-for-byte. Force a specific backend with a [provider prefix](./docs/usage.md#provider-prefix) on the model field.
|
|
150
167
|
|
|
151
168
|
### 2. Claude subscription backend
|
|
152
169
|
|
|
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
|
|
170
|
+
OAuth-backed Claude Pro / Max 5x / Max 20x, billed against your plan instead of the API. Activated by `dario login` (or `dario login --manual` for SSH / container setups, v3.20). Any tier with Claude Code access works — see [`docs/faq.md`](./docs/faq.md).
|
|
154
171
|
|
|
155
|
-
|
|
172
|
+
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, 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.
|
|
156
173
|
|
|
157
|
-
|
|
174
|
+
Key mechanisms in brief: live template extraction from your installed `claude` binary, drift detection with forced refresh on mismatch, OAuth config auto-detection (so dario picks up Anthropic-side rotations on the next run), atomic cache writes, framework scrubbing (third-party identity markers stripped from system prompt), Bun auto-relaunch (so the TLS ClientHello matches CC's runtime). `dario proxy --passthrough` does an OAuth swap and nothing else — use it when the upstream tool already builds a Claude-Code-shaped request.
|
|
158
175
|
|
|
159
|
-
|
|
160
|
-
- **Drift detection** (v3.17). On startup dario probes the installed `claude` binary and compares against the captured template. Mismatch triggers a forced refresh and prints a one-line warning. Users never silently sit on a stale template again.
|
|
161
|
-
- **Compat matrix** (v3.17, bumped in v3.19.5). `SUPPORTED_CC_RANGE` is encoded in code; installed CC outside the band prints a warn (untested above) or fail (below min) — zero-dep dotted-numeric comparator, no `semver` import per the dep policy.
|
|
162
|
-
- **Billing tag** reconstructed using CC's own algorithm: `x-anthropic-billing-header: cc_version=<version>.<build_tag>; cc_entrypoint=cli; cch=<5-char-hex>;` where `build_tag = SHA-256(seed + chars[4,7,20] of user message + version).slice(0,3)`.
|
|
163
|
-
- **OAuth config auto-detection** from the installed CC binary. When Anthropic rotates `client_id`, authorize URL, or scopes, dario picks up the new values on the next run without needing a release. Cache at `~/.dario/cc-oauth-cache-v6.json`, keyed by the CC binary fingerprint. Cache path bumps each time the canonical OAuth config shape changes so stale caches regenerate automatically on upgrade.
|
|
164
|
-
- **Multi-account pool mode** — see [Multi-account pool mode](#multi-account-pool-mode). Automatic when 2+ accounts are configured.
|
|
165
|
-
- **Framework scrubbing** — known third-party identity markers (`OpenClaw`, `sessions_*` prefixes, orchestration tags) stripped from system prompt and message content before the request leaves your machine.
|
|
166
|
-
- **Atomic cache writes + cache corruption recovery** (v3.17). Template cache writes go through pid-qualified `.tmp` + `rename`, so an OS crash mid-write doesn't leave a half-written file. Unparseable cache files get quarantined to `cc-template.live.json.bad-<timestamp>` and dario self-heals on the next capture.
|
|
167
|
-
- **OAuth single-flight** (v3.17). Two concurrent refreshes for the same account alias now share one outbound `POST /oauth/token`, so the pool's background refresh timer and a user-triggered request at the same millisecond can't race and invalidate each other's refresh token.
|
|
168
|
-
- **Bun auto-relaunch.** When Bun is installed, dario relaunches under it so the TLS ClientHello matches CC's runtime (Bun uses BoringSSL; Node uses OpenSSL — distinct JA3/JA4 hashes). Without Bun, dario runs on Node.js — `dario doctor` surfaces the mismatch as of v3.23 and `--strict-tls` refuses to start proxy mode until it's resolved.
|
|
169
|
-
|
|
170
|
-
**Passthrough mode** (`dario proxy --passthrough`) does an OAuth swap and nothing else — no template, no identity, no scrubbing. Use it when the upstream tool already builds a Claude-Code-shaped request on its own.
|
|
171
|
-
|
|
172
|
-
**Scope.** The Claude backend operates at the per-request level. Template mirroring and scrubbing produce requests that match CC at the request level. What they cannot address on their own is any cumulative per-OAuth session behavior. The v3.22 – v3.28 wire-fidelity track closed six of those cumulative axes (body order, TLS, pacing, stream-drain, session-id lifecycle, MCP/sub-agent surface); for anything left, **pool mode** distributes load across multiple subscriptions so no single account accumulates signal along any single dimension.
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
## Wire-fidelity axes
|
|
177
|
-
|
|
178
|
-
Between v3.22 and v3.28, dario's Claude backend closed six axes along which a proxy can diverge from real Claude Code. Each is a separate knob, each ships with its own test suite, each is surfaced through `dario doctor` where the axis has something to report. Defaults are chosen so existing setups don't regress.
|
|
179
|
-
|
|
180
|
-
| Axis | Release | What it does | How to tune |
|
|
181
|
-
|---|---|---|---|
|
|
182
|
-
| **Request body key order** | v3.22 | Top-level JSON key order of the outbound `/v1/messages` body is captured from CC's wire serialization and replayed byte-for-byte. Schema bumped v2 → v3; stale caches quarantined. | Automatic once a live capture exists. The baked fallback carries a v2.1.112 snapshot. |
|
|
183
|
-
| **Runtime / TLS ClientHello** | v3.23 | Classifies the runtime as `bun-match` / `bun-bypassed` / `node-only` and surfaces the class + hint in `dario doctor`. Bun yields the BoringSSL ClientHello CC presents; Node yields OpenSSL's (distinct JA3). | `--strict-tls` (or `DARIO_STRICT_TLS=1`) refuses to start proxy mode unless `bun-match`. `DARIO_QUIET_TLS=1` silences the startup banner in known-fine environments. |
|
|
184
|
-
| **Inter-request timing** | v3.24 | Replaces the hardcoded 500 ms floor with a configurable floor + uniform jitter. A fixed 500 ms minimum-inter-arrival is an observable edge at scale; jitter dissolves the edge. | `--pace-min=MS`, `--pace-jitter=MS`, or `DARIO_PACE_MIN_MS` / `DARIO_PACE_JITTER_MS`. Legacy `DARIO_MIN_INTERVAL_MS` still honored. |
|
|
185
|
-
| **Stream-consumption shape** | v3.25 | When a downstream client disconnects mid-stream, CC keeps reading SSE to EOF. Dario now offers the same: drain upstream to completion even when the consumer has left. Default off — don't silently burn tokens. | `--drain-on-close` / `DARIO_DRAIN_ON_CLOSE=1`. Bounded by the existing 5-minute upstream timeout. |
|
|
186
|
-
| **Session-ID lifecycle** | v3.28 | Generalizes the v3.19 hardcoded 15-minute idle rotation into a tunable `SessionRegistry` with jitter, max-age, and per-client bucketing. Fixes a v3.27 body/header rotation race as a side effect. | `--session-idle-rotate=MS` (default 900000), `--session-rotate-jitter=MS`, `--session-max-age=MS`, `--session-per-client`. Env mirrors `DARIO_SESSION_*`. Defaults are bit-identical to v3.27. |
|
|
187
|
-
| **MCP / sub-agent reach** | v3.26 + v3.27 | Not a wire axis — a *surface* axis. CC-aware tools can now address dario directly (sub-agent from inside CC, MCP server for any MCP client), so operators don't have to switch terminals to introspect the proxy. Read-only by design. | `dario subagent install` / `dario mcp`. See dedicated sections below. |
|
|
188
|
-
|
|
189
|
-
The six-direction wire-fidelity roadmap is complete. Subsequent releases return to responding to issues and upstream template drift.
|
|
176
|
+
What this addresses: per-request fidelity. What it can't address alone: cumulative per-OAuth-session aggregates. The v3.22 – v3.28 wire-fidelity track closed six of those axes (body order, TLS, pacing, stream-drain, session-id lifecycle, MCP/sub-agent surface — see [`docs/wire-fidelity.md`](./docs/wire-fidelity.md)); for anything left, [pool mode](./docs/multi-account-pool.md) distributes load across multiple subscriptions.
|
|
190
177
|
|
|
191
178
|
---
|
|
192
179
|
|
|
193
180
|
## Multi-account pool mode
|
|
194
181
|
|
|
195
|
-
Pool mode activates automatically when `~/.dario/accounts/` contains 2+ accounts.
|
|
182
|
+
Pool mode activates automatically when `~/.dario/accounts/` contains 2+ accounts. Each request picks the account with the highest headroom; multi-turn agent sessions pin to one account so the Anthropic prompt cache survives; in-flight 429s retry on a different account before the client sees an error.
|
|
196
183
|
|
|
197
184
|
```bash
|
|
198
185
|
dario accounts add work
|
|
199
186
|
dario accounts add personal
|
|
200
|
-
dario accounts add side-project
|
|
201
|
-
dario accounts list
|
|
202
187
|
dario proxy
|
|
203
188
|
```
|
|
204
189
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
Each request picks the account with the highest headroom:
|
|
208
|
-
|
|
209
|
-
```
|
|
210
|
-
headroom = 1 - max(util_5h, util_7d)
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
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.
|
|
214
|
-
|
|
215
|
-
### Session stickiness
|
|
216
|
-
|
|
217
|
-
Multi-turn agent sessions pin to one account for the life of the conversation, so the Anthropic prompt cache isn't destroyed by account rotation between turns.
|
|
218
|
-
|
|
219
|
-
**The problem.** Claude prompt cache is scoped to `{account × cache_control key}`. When the pool rotates a long agent conversation across accounts on headroom alone, turn 1 builds a cache entry on account A, turn 2 lands on account B and reads nothing from A's cache — paying full cache-create cost again. For a long agent session that's a **5–10× token-cost multiplier** on every turn after the first.
|
|
220
|
-
|
|
221
|
-
**The fix.** Dario hashes a conversation's first user message into a 16-hex-char `stickyKey` (SHA-256 truncated, deterministic) and binds the key to whichever account `select()` would have picked on turn 1. Subsequent turns re-use that account as long as it's still healthy (not rejected, token not near expiry, headroom > 2%). On 429 failover, dario rebinds the key to the new account so the next turn doesn't re-select the exhausted one. 6h TTL, 2,000-entry cap, lazy cleanup. No client cooperation required.
|
|
222
|
-
|
|
223
|
-
### In-flight 429 failover
|
|
224
|
-
|
|
225
|
-
When a Claude request hits a 429 mid-flight, dario retries the *same request* against a different account before the client sees an error. The client sees one successful response; the pool sees the rejected account go cold until its window resets. Combined with session stickiness, long agent runs survive pool-level exhaustion without dropping user-facing turns.
|
|
226
|
-
|
|
227
|
-
### Inspection
|
|
228
|
-
|
|
229
|
-
```bash
|
|
230
|
-
curl http://localhost:3456/accounts # per-account utilization, claim, sticky bindings, status
|
|
231
|
-
curl http://localhost:3456/analytics # per-account / per-model stats, burn rate, exhaustion predictions
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Every request carries a `billingBucket` field (`subscription` / `subscription_fallback` / `extra_usage` / `api` / `unknown`) so you can see which bucket each request billed against and a `subscriptionPercent` headline number tells you at a glance whether dario is actually routing through your subscription or silently falling to API overage.
|
|
190
|
+
Full details, headroom math, sticky-key implementation, inspection endpoints: [`docs/multi-account-pool.md`](./docs/multi-account-pool.md).
|
|
235
191
|
|
|
236
192
|
---
|
|
237
193
|
|
|
238
|
-
## Shim mode
|
|
239
|
-
|
|
240
|
-
*Experimental, opt-in. The proxy is still the default — shim mode is a second transport, not a replacement.*
|
|
194
|
+
## Shim mode (experimental)
|
|
241
195
|
|
|
242
|
-
|
|
196
|
+
Take the proxy off the wire entirely. `dario shim -- <child cmd>` patches `globalThis.fetch` inside the child via `NODE_OPTIONS=--require`. No localhost HTTP hop. No port to bind. No `BASE_URL`.
|
|
243
197
|
|
|
244
198
|
```bash
|
|
245
199
|
dario shim -- claude --print "hello"
|
|
246
|
-
dario shim -v -- claude --print "hello" # verbose
|
|
247
200
|
```
|
|
248
201
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
**Why it matters.** A proxy has observable surface — TLS, headers, IP, `BASE_URL` env. Shim mode has none of that: the request goes out through CC's own network stack, unchanged. It's the transport with the smallest observable footprint.
|
|
252
|
-
|
|
253
|
-
**Hardening (v3.13+)** added runtime detection (canary for upstream runtime changes), template mtime-based auto-reload (long-running children pick up mid-session template refreshes without restart), strict defensive `rewriteBody` (requires exactly 3 text blocks, passes through on any mismatch instead of inventing structure), and header-order replay (honors captured CC header sequence so the shim matches CC wire-exact).
|
|
254
|
-
|
|
255
|
-
**When to use shim mode:**
|
|
256
|
-
- Running a single CC instance on a locked-down machine where binding a local port is inconvenient.
|
|
257
|
-
- Wrapping one-off scripts (`dario shim -- node my-agent.js`) without setting up environment variables.
|
|
258
|
-
- Debugging a specific child process in isolation — verbose logs are scoped to that child.
|
|
259
|
-
- You want to take the proxy layer off the wire entirely — no local port, no `BASE_URL`, no extra network hop.
|
|
260
|
-
|
|
261
|
-
**When to stay on the proxy** (default):
|
|
262
|
-
- Multi-client routing. The proxy serves every tool on the machine through one endpoint; shim wraps one child at a time.
|
|
263
|
-
- Multi-account pool mode. Pooling across subscriptions needs a shared OAuth pool the proxy owns — a shim patch inside one child can't see pool state across other processes.
|
|
264
|
-
- Anything that isn't a Node / Bun child. The shim relies on `NODE_OPTIONS`, so Python SDKs or Go CLIs still need the proxy.
|
|
202
|
+
When to use it, when to stay on the proxy, hardening detail: [`docs/shim.md`](./docs/shim.md).
|
|
265
203
|
|
|
266
204
|
---
|
|
267
205
|
|
|
268
206
|
## Agent compatibility
|
|
269
207
|
|
|
270
|
-
Dario's
|
|
271
|
-
|
|
272
|
-
| Agent | Covered tool names (subset) |
|
|
273
|
-
|---|---|
|
|
274
|
-
| Claude Code / Claude Agent SDK | default — CC / SDK tools (same schema as of CC v2.1.114 / `@anthropic-ai/claude-agent-sdk@0.2.x`) |
|
|
275
|
-
| Cline / Roo Code / Kilo Code | `execute_command`, `write_to_file`, `replace_in_file`, `apply_diff`, `list_files`, `search_files`, `read_file` |
|
|
276
|
-
| Cursor | `run_terminal_cmd`, `edit_file`, `search_replace`, `codebase_search`, `grep_search`, `file_search`, `list_dir`, `read_file` (`target_file`) |
|
|
277
|
-
| Windsurf | `run_command`, `view_file`, `write_to_file`, `replace_file_content`, `find_by_name`, `grep_search`, `list_dir`, `search_web`, `read_url_content` |
|
|
278
|
-
| Continue.dev | `builtin_run_terminal_command`, `builtin_read_file`, `builtin_create_new_file`, `builtin_edit_existing_file`, `builtin_file_glob_search`, `builtin_grep_search`, `builtin_ls` |
|
|
279
|
-
| GitHub Copilot | `run_in_terminal`, `insert_edit_into_file`, `semantic_search`, `codebase_search`, `list_dir`, `fetch_webpage` |
|
|
280
|
-
| OpenHands | `execute_bash`, `str_replace_editor` |
|
|
281
|
-
| OpenClaw | `exec`, `process`, `web_search`, `web_fetch`, `browser`, `message` |
|
|
282
|
-
| Hermes Agent (Nous Research) | `terminal`, `process`, `read_file`, `write_file`, `patch`, `search_files`, `web_search`, `web_extract`, `todo` mapped directly. Hermes-specific tools (`browser_*`, `vision_analyze`, `image_generate`, `skill_*`, `memory`, `session_search`, `cronjob`, `send_message`, `ha_*`, `mixture_of_agents`, `delegate_task`, `execute_code`, `text_to_speech`) have no CC equivalent and auto-preserve through the identity detector (`You are Hermes Agent` or `created by Nous Research` in the system prompt flips dario into preserve-tools for Hermes sessions automatically — v3.30.13). Also consider `--max-tokens=client` so Hermes's 64k/128k per-model caps survive dario's outbound pin. |
|
|
283
|
-
|
|
284
|
-
Text-tool clients (Cline / Kilo Code / Roo Code and forks) are auto-detected via system-prompt identity markers and automatically flipped into preserve-tools mode, because mixing CC's `tools` array with their XML protocol makes the model emit `<function_calls><invoke>` that their parsers can't read. The same identity path also catches `arnie` (askalf's portable IT-troubleshooting CLI) — its tool names overlap with `TOOL_MAP` but its schemas diverge, so identity match → preserve-tools is the only correct routing. If you run dario specifically for wire-level fidelity and would rather pick `--preserve-tools` yourself, `--no-auto-detect` (v3.20.1, aka `--no-auto-preserve`) disables the heuristic — explicit operator choice then wins.
|
|
285
|
-
|
|
286
|
-
Beyond the identity path, dario falls back to a **structural** check: when a request carries 3+ tools and ≥80% of them aren't in `TOOL_MAP`, that's a custom client whose tool surface has effectively no overlap with CC's, and round-robin remap onto CC fallback slots silently corrupts the calls. The structural fallback flips those requests to preserve-tools too, with `client: 'unknown-non-cc'` in the request log. This catches in-house agents and OpenClaw derivatives that we haven't added an explicit pattern for, without needing per-client maintenance. `--no-auto-detect` disables both paths.
|
|
287
|
-
|
|
288
|
-
If your agent's tool names aren't pre-mapped and its tools carry fields CC's schema doesn't have, there are two escape hatches: **`--preserve-tools`** (forward your schema verbatim, lose the CC wire shape) or **`--hybrid-tools`** (keep the CC wire shape, fill request-context fields from headers). See [Custom tool schemas](#custom-tool-schemas).
|
|
208
|
+
Dario's `TOOL_MAP` (59 schema-verified entries) covers every major coding agent — Cline, Roo Code, Kilo Code, Cursor, Windsurf, Continue.dev, GitHub Copilot, OpenHands, OpenClaw, Hermes, [hands](https://github.com/askalf/hands). Tool calls translate to CC's native set on the outbound path (subscription wire shape preserved) and rebuild to your agent's exact expected shape on the inbound path.
|
|
289
209
|
|
|
290
|
-
|
|
210
|
+
Text-tool clients (Cline / Kilo / Roo) and identity-detected agents (`hands`, `arnie`, Hermes) auto-flip into preserve-tools mode via system-prompt identity markers. A structural fallback catches in-house non-CC agents (3+ tools, ≥80% unmapped) and flips them too.
|
|
291
211
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
## dario as MCP server (v3.27)
|
|
295
|
-
|
|
296
|
-
`dario mcp` turns dario itself into a **stdio JSON-RPC 2.0 MCP server**. Claude Desktop, Cursor, Zed, any MCP-aware editor can introspect dario's state without leaving the editor.
|
|
297
|
-
|
|
298
|
-
```bash
|
|
299
|
-
dario mcp # spawns the MCP server on stdin/stdout — wire it up to your MCP client
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
**Strictly read-only.** The exposed tool set is:
|
|
303
|
-
|
|
304
|
-
| Tool | What it reports |
|
|
305
|
-
|---|---|
|
|
306
|
-
| `doctor` | Full aggregated health report — same output as `dario doctor` |
|
|
307
|
-
| `status` | OAuth authentication state (authenticated / no-credentials / expired-but-refreshable) |
|
|
308
|
-
| `accounts_list` | Pool accounts + expiry times. Never touches API keys. |
|
|
309
|
-
| `backends_list` | Configured OpenAI-compat backends — keys redacted completely (not even a `sk-…` prefix) |
|
|
310
|
-
| `subagent_status` | CC sub-agent install and version-match state |
|
|
311
|
-
| `fingerprint_info` | Runtime / TLS classification, template source + schema version |
|
|
312
|
-
|
|
313
|
-
Mutations (`login`, `logout`, `accounts add/remove`, `backend add/remove`, `subagent install/remove`, `proxy` start/stop) are **not** exposed. An MCP client can observe dario; changing dario's state stays a CLI action the user types with intent. The test suite asserts the forbidden-tool set stays forbidden so a future accidental drift gets caught.
|
|
314
|
-
|
|
315
|
-
Zero runtime deps — the JSON-RPC dispatcher is hand-rolled over Node's `readline`. `src/mcp/protocol.ts` + `src/mcp/tools.ts` + `src/mcp/server.ts` are each pure over their inputs (streams are injectable, data sources are injectable) so the e2e test runs in-process against a `PassThrough` pair.
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
## Claude Code sub-agent hook (v3.26)
|
|
320
|
-
|
|
321
|
-
`dario subagent install` writes `~/.claude/agents/dario.md` so Claude Code has a named handle for running dario diagnostics and template-refresh inside an ongoing CC session. No more `Ctrl+Z → dario doctor → fg` when you hit a `[WARN]` row mid-conversation.
|
|
322
|
-
|
|
323
|
-
```bash
|
|
324
|
-
dario subagent install # writes ~/.claude/agents/dario.md
|
|
325
|
-
dario subagent status # {not-installed, installed+current, installed+stale} + hint
|
|
326
|
-
dario subagent remove # idempotent
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
**Tool-scoped.** The sub-agent is restricted to `Bash, Read` and its prompt forbids destructive operations (credential mutation, account pool changes, backend config changes) without explicit user confirmation. `dario proxy` is also off-limits from inside the sub-agent — it would block the parent CC session. CC can ask dario to *report*, not to *change state*. (MCP server has the same read-only boundary for the same reason.)
|
|
330
|
-
|
|
331
|
-
A version marker (`<!-- dario-sub-agent-version: X -->`) embedded in the markdown lets `dario doctor` distinguish installed-and-current from installed-and-stale; the "Sub-agent" row appears between Backends and Home with an inline refresh command when stale.
|
|
332
|
-
|
|
333
|
-
---
|
|
334
|
-
|
|
335
|
-
## Commands
|
|
336
|
-
|
|
337
|
-
| Command | Description |
|
|
338
|
-
|---|---|
|
|
339
|
-
| `dario login [--manual]` | Log in to the Claude backend. Detects CC credentials or runs its own OAuth flow. `--manual` (v3.20) mirrors CC's code-paste flow for SSH / container setups without a browser. |
|
|
340
|
-
| `dario proxy` | Start the local API proxy on port 3456 |
|
|
341
|
-
| `dario doctor [--probe] [--auth-check] [--json] [--bun-bootstrap]` | Aggregated health report — dario / Node / runtime-TLS / CC binary + compat / template + drift / **per-request overhead** / OAuth / pool + **pool routing** (next account in rotation when 2+ loaded) / backends / sub-agent. `--probe` (v3.31.7) hits the live `claude.ai/oauth/authorize` endpoint and surfaces the verdict, so scope-policy drift is catchable from a user's machine (not just CI). `--auth-check` (v3.31.9) opens a one-shot `x-api-key` listener and classifies whatever a client actually sends (match / mismatch / no-auth / timeout), with only redacted previews in output. `--json` (v3.31.8) emits structured output for claude-bridge's `/status`, deepdive's health probes, and CI scrapers. `--bun-bootstrap` runs the canonical bun.sh installer when the runtime/TLS check is warning that Bun isn't on PATH. |
|
|
342
|
-
| `dario usage [--port=N] [--json]` | Burn-rate summary of the running proxy's traffic over the last 60 minutes: requests, input/output tokens, avg latency, error rate, subscription % vs. extra-usage, estimated API-equivalent cost, plus per-account breakdown when pool mode is active. Hits `/analytics` on the local proxy. When the proxy isn't reachable, prints a hint pointing at `dario doctor --usage` (the one-off rate-limit probe). `--json` emits the raw `/analytics` payload for status bars / CI dashboards. Also exposed as the `usage` tool in `dario mcp`. |
|
|
343
|
-
| `dario config [--json]` | Prints the effective dario configuration with credentials redacted. Complementary to `doctor` — doctor answers *is it working?*, config answers *what IS it?* (v3.31.10) |
|
|
344
|
-
| `dario upgrade` | Safe wrapper over `npm install -g @askalf/dario@latest` — probes npm for the `@latest` version first (3s timeout, 60s cache), refuses to run if already on latest, fails with a clear hint if npm is missing. (v3.31.10) |
|
|
345
|
-
| `dario status` | Show Claude backend OAuth token health and expiry |
|
|
346
|
-
| `dario refresh` | Force an immediate Claude token refresh |
|
|
347
|
-
| `dario logout` | Delete stored Claude credentials |
|
|
348
|
-
| `dario accounts list` / `add <alias>` / `remove <alias>` | Multi-account pool management. `add <alias>` on a fresh pool auto back-fills your existing `dario login` credentials as `login`, so your first `add` trips the 2+ pool threshold on its own — see [Multi-account pool mode](#multi-account-pool-mode). |
|
|
349
|
-
| `dario backend list` / `add <name> --key=<key> [--base-url=<url>]` / `remove <name>` | OpenAI-compat backend management |
|
|
350
|
-
| `dario shim -- <cmd> [args...]` | Run a child process with the in-process fetch patch (see [Shim mode](#shim-mode)) |
|
|
351
|
-
| `dario subagent install` / `remove` / `status` | CC sub-agent lifecycle (v3.26 — see [sub-agent hook](#claude-code-sub-agent-hook-v326)) |
|
|
352
|
-
| `dario mcp` | Run dario as an MCP server over stdio (v3.27 — see [dario as MCP server](#dario-as-mcp-server-v327)) |
|
|
353
|
-
| `dario help` | Full command reference |
|
|
354
|
-
|
|
355
|
-
### Proxy options
|
|
356
|
-
|
|
357
|
-
| Flag / env | Description | Default |
|
|
358
|
-
|---|---|---|
|
|
359
|
-
| `--passthrough` / `--thin` | Thin proxy for the Claude backend — OAuth swap only, no template injection | off |
|
|
360
|
-
| `--preserve-tools` / `--keep-tools` | Keep client tool schemas instead of remapping to CC's. Required for clients whose tools have fields CC doesn't — see [Custom tool schemas](#custom-tool-schemas). Auto-enabled for Cline / Kilo Code / Roo Code and forks (detected via system-prompt identity markers). | off (auto for text-tool clients) |
|
|
361
|
-
| `--no-auto-detect` / `--no-auto-preserve` | Disable the text-tool-client detector so the CC wire shape stays intact on Cline/Kilo/Roo prompts (v3.20.1, dario#40). Explicit `--preserve-tools` still wins. | off |
|
|
362
|
-
| `--hybrid-tools` / `--context-inject` | Remap to CC tools **and** inject request-context values (`sessionId`, `requestId`, `channelId`, `userId`, `timestamp`) into client-declared fields CC's schema doesn't carry. See [Hybrid tool mode](#hybrid-tool-mode). | off |
|
|
363
|
-
| `--merge-tools` / `--append-tools` | **EXPERIMENTAL.** Send CC's canonical tools first, append the client's custom tools after (deduped by name, case-insensitive). Model can call either side; tool calls flow back unchanged. Mutually exclusive with `--preserve-tools` and `--hybrid-tools`. Anthropic's billing classifier may flip routing on the appended suffix — validate with `--verbose` and watch the `billing: <bucket>` line on the first 1-2 requests before relying on it. | off |
|
|
364
|
-
| `--model=<name>` | Force a model. Shortcuts (`opus`, `sonnet`, `haiku`), full IDs (`claude-opus-4-7`), or a **provider prefix** (`openai:gpt-4o`, `groq:llama-3.3-70b`, `claude:opus`, `local:qwen-coder`) to force the backend server-wide. | passthrough |
|
|
365
|
-
| `--port=<n>` | Port to listen on | `3456` |
|
|
366
|
-
| `--host=<addr>` / `DARIO_HOST` | Bind address. Use `0.0.0.0` for LAN, or a specific IP (e.g. a Tailscale interface). When non-loopback, also set `DARIO_API_KEY`. | `127.0.0.1` |
|
|
367
|
-
| `--verbose` / `-v` | Log every request (one line per request — method + path + billing bucket) | off |
|
|
368
|
-
| `--verbose=2` / `-vv` / `DARIO_LOG_BODIES=1` | Also dump the outbound request body (redacted: bearer tokens, `sk-ant-*` keys, JWTs stripped; capped at 8KB). For wire-level client-compat debugging. | off |
|
|
369
|
-
| `--log-file=<path>` / `DARIO_LOG_FILE` | Append one JSON-ND record per completed request to PATH. Useful for backgrounded proxies where stdout is unobserved (where `--verbose` can't help). Field set: `ts`, `req`, `method`, `path`, `model`, `status`, `latency_ms`, `in_tokens`, `out_tokens`, `cache_read`, `cache_create`, `claim`, `bucket`, `account`, `client`, `preserve_tools`, `stream`, plus `reject` / `error` on failure paths. Secrets scrubbed via the same redactor that `--verbose-bodies` uses; no request bodies. | off |
|
|
370
|
-
| `--passthrough-betas=<csv>` / `DARIO_PASSTHROUGH_BETAS` | Beta flags ALWAYS forwarded upstream regardless of CC's captured set or the client's `anthropic-beta` header. Bypasses the billable-beta filter (so `extended-cache-ttl-*` survives if you opt in). Per-account rejection cache still applies — a pinned flag the upstream 400's gets dropped on retry rather than re-sent forever. Use when you know a beta works on your account but isn't in the captured template, or when client traffic should be force-augmented. Empty flag value (`--passthrough-betas=`) clears the env-default. | off |
|
|
371
|
-
| `--strict-tls` / `DARIO_STRICT_TLS=1` | Refuse to start proxy mode unless runtime classifies as `bun-match` — i.e. the TLS ClientHello matches CC's. See [Wire-fidelity axes](#wire-fidelity-axes). (v3.23) | off |
|
|
372
|
-
| `--pace-min=<ms>` / `DARIO_PACE_MIN_MS` | Minimum inter-request gap in ms. Replaces the legacy hardcoded 500 ms. (v3.24) | `500` |
|
|
373
|
-
| `--pace-jitter=<ms>` / `DARIO_PACE_JITTER_MS` | Uniform random jitter added to each gap. Dissolves the minimum-inter-arrival observable edge. (v3.24) | `0` |
|
|
374
|
-
| `--drain-on-close` / `DARIO_DRAIN_ON_CLOSE=1` | When a downstream client disconnects mid-stream, keep reading upstream SSE to completion (match CC's consumption shape). Bounded by the 5-min upstream timeout. (v3.25) | off |
|
|
375
|
-
| `--session-idle-rotate=<ms>` / `DARIO_SESSION_IDLE_ROTATE_MS` | Idle threshold before a session-id rotates. (v3.28) | `900000` (15 min) |
|
|
376
|
-
| `--session-rotate-jitter=<ms>` / `DARIO_SESSION_JITTER_MS` | Jitter sampled once per session at creation — hides the exact idle floor. (v3.28) | `0` |
|
|
377
|
-
| `--session-max-age=<ms>` / `DARIO_SESSION_MAX_AGE_MS` | Hard ceiling on a session-id's lifetime regardless of activity. (v3.28) | off |
|
|
378
|
-
| `--session-per-client` / `DARIO_SESSION_PER_CLIENT=1` | Split session-id registry by a per-client header so multi-UI fan-out doesn't collapse onto one id. (v3.28) | off |
|
|
379
|
-
| `DARIO_API_KEY` | If set, all endpoints (except `/health`) require a matching `x-api-key` or `Authorization: Bearer` header. Required when `--host` binds non-loopback. | unset (open) |
|
|
380
|
-
| `DARIO_CORS_ORIGIN` | Override browser CORS origin | `http://localhost:${port}` |
|
|
381
|
-
| `DARIO_QUIET_TLS` | Suppress the runtime/TLS mismatch startup banner | unset |
|
|
382
|
-
| `DARIO_NO_BUN` | Disable automatic Bun relaunch | unset |
|
|
383
|
-
| `DARIO_MIN_INTERVAL_MS` | Legacy name for `DARIO_PACE_MIN_MS`. Still honored; new name wins when both are set. | — |
|
|
384
|
-
| `DARIO_CC_PATH` | Override path to the Claude Code binary for OAuth detection | auto-detect |
|
|
385
|
-
| `DARIO_OAUTH_CLIENT_ID` | Override the detected Claude OAuth client id as an emergency escape hatch | unset |
|
|
386
|
-
| `DARIO_OAUTH_AUTHORIZE_URL` | Override the detected Claude OAuth authorize URL | unset |
|
|
387
|
-
| `DARIO_OAUTH_TOKEN_URL` | Override the detected Claude OAuth token URL | unset |
|
|
388
|
-
| `DARIO_OAUTH_SCOPES` | Override the detected Claude OAuth scopes | unset |
|
|
389
|
-
| `DARIO_OAUTH_OVERRIDE_PATH` | Override file path for JSON OAuth overrides | `~/.dario/oauth-config.override.json` |
|
|
390
|
-
| `DARIO_OAUTH_DISABLE_OVERRIDE=1` | Ignore env/file OAuth overrides entirely | unset |
|
|
391
|
-
|
|
392
|
-
---
|
|
393
|
-
|
|
394
|
-
## Usage
|
|
395
|
-
|
|
396
|
-
### Python (Anthropic SDK)
|
|
397
|
-
|
|
398
|
-
```python
|
|
399
|
-
import anthropic
|
|
400
|
-
|
|
401
|
-
client = anthropic.Anthropic(
|
|
402
|
-
base_url="http://localhost:3456",
|
|
403
|
-
api_key="dario",
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
msg = client.messages.create(
|
|
407
|
-
model="claude-opus-4-7",
|
|
408
|
-
max_tokens=1024,
|
|
409
|
-
messages=[{"role": "user", "content": "Hello!"}],
|
|
410
|
-
)
|
|
411
|
-
print(msg.content[0].text)
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
### Python (OpenAI SDK — same proxy, different provider)
|
|
415
|
-
|
|
416
|
-
```python
|
|
417
|
-
from openai import OpenAI
|
|
418
|
-
|
|
419
|
-
client = OpenAI(
|
|
420
|
-
base_url="http://localhost:3456/v1",
|
|
421
|
-
api_key="dario",
|
|
422
|
-
)
|
|
423
|
-
|
|
424
|
-
# gpt-4o routes to the configured OpenAI backend
|
|
425
|
-
msg = client.chat.completions.create(
|
|
426
|
-
model="gpt-4o",
|
|
427
|
-
messages=[{"role": "user", "content": "Hello!"}],
|
|
428
|
-
)
|
|
429
|
-
|
|
430
|
-
# claude-opus-4-7 routes to the Claude subscription backend — same SDK, same URL
|
|
431
|
-
claude_msg = client.chat.completions.create(
|
|
432
|
-
model="claude-opus-4-7",
|
|
433
|
-
messages=[{"role": "user", "content": "Hello!"}],
|
|
434
|
-
)
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### TypeScript / Node.js
|
|
438
|
-
|
|
439
|
-
```typescript
|
|
440
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
441
|
-
|
|
442
|
-
const client = new Anthropic({
|
|
443
|
-
baseURL: "http://localhost:3456",
|
|
444
|
-
apiKey: "dario",
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
const msg = await client.messages.create({
|
|
448
|
-
model: "claude-opus-4-7",
|
|
449
|
-
max_tokens: 1024,
|
|
450
|
-
messages: [{ role: "user", content: "Hello!" }],
|
|
451
|
-
});
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
### OpenAI-compatible tools (Cursor, Continue, Aider, LiteLLM, …)
|
|
455
|
-
|
|
456
|
-
Any tool that accepts an OpenAI-compatible base URL + API key works with dario. The universal env-var setup:
|
|
457
|
-
|
|
458
|
-
```bash
|
|
459
|
-
export OPENAI_BASE_URL=http://localhost:3456/v1
|
|
460
|
-
export OPENAI_API_KEY=dario
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
Use Claude model names (`claude-opus-4-7`, `claude-sonnet-4-6`, `claude-haiku-4-5`, or shortcuts `opus` / `sonnet` / `haiku`) for the Claude subscription backend, or GPT-family / Llama / any-other-model names for your configured OpenAI-compat backends.
|
|
464
|
-
|
|
465
|
-
Some tools use env vars (above works as-is); others want settings-UI entries:
|
|
466
|
-
|
|
467
|
-
#### Cursor
|
|
468
|
-
|
|
469
|
-
1. **Cmd/Ctrl + ,** to open Settings → **Models**
|
|
470
|
-
2. Under the **OpenAI API Key** section:
|
|
471
|
-
- Check **Override OpenAI Base URL**: `http://localhost:3456/v1`
|
|
472
|
-
- API key: `dario`
|
|
473
|
-
3. Under the **Model Names** section (or the Add Model button):
|
|
474
|
-
- Add `claude-sonnet-4-6`
|
|
475
|
-
- Add `claude-opus-4-7` (premium)
|
|
476
|
-
- Add `claude-haiku-4-5` (cheap)
|
|
477
|
-
4. Select one of the new models in the chat input's model picker.
|
|
478
|
-
|
|
479
|
-
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.
|
|
480
|
-
|
|
481
|
-
#### Continue.dev
|
|
482
|
-
|
|
483
|
-
In `~/.continue/config.yaml` (or the Continue settings UI, which edits the same file):
|
|
484
|
-
|
|
485
|
-
```yaml
|
|
486
|
-
models:
|
|
487
|
-
- name: Claude Sonnet (dario)
|
|
488
|
-
provider: anthropic
|
|
489
|
-
model: claude-sonnet-4-6
|
|
490
|
-
apiBase: http://localhost:3456
|
|
491
|
-
apiKey: dario
|
|
492
|
-
- name: Claude Opus (dario)
|
|
493
|
-
provider: anthropic
|
|
494
|
-
model: claude-opus-4-7
|
|
495
|
-
apiBase: http://localhost:3456
|
|
496
|
-
apiKey: dario
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
`provider: anthropic` + `apiBase: http://localhost:3456` points Continue's Anthropic SDK path at dario instead of `api.anthropic.com`. dario runs the full Claude Code wire replay on the outbound path.
|
|
500
|
-
|
|
501
|
-
#### Aider
|
|
502
|
-
|
|
503
|
-
```bash
|
|
504
|
-
export ANTHROPIC_BASE_URL=http://localhost:3456
|
|
505
|
-
export ANTHROPIC_API_KEY=dario
|
|
506
|
-
aider --model sonnet
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
Aider's Anthropic path honors `ANTHROPIC_BASE_URL` directly. `--model opus`, `--model haiku`, or any explicit `claude-*` model name works.
|
|
510
|
-
|
|
511
|
-
#### Cline / Roo Code / Kilo Code
|
|
512
|
-
|
|
513
|
-
Cline and its forks use a UI-based "API Provider" dropdown. Pick **Anthropic** as the provider and fill in:
|
|
514
|
-
|
|
515
|
-
- **API Key**: `dario`
|
|
516
|
-
- **Anthropic Base URL**: `http://localhost:3456`
|
|
517
|
-
- **Model**: `claude-sonnet-4-6` / `claude-opus-4-7` / `claude-haiku-4-5`
|
|
518
|
-
|
|
519
|
-
Cline's tool-invocation protocol is XML-based (`<execute_command>`, `<write_to_file>`, etc.), not Anthropic's tool-use format. Dario auto-detects Cline-family clients via system-prompt identity markers and flips into preserve-tools mode automatically — Cline's own tool schema passes through, your commands route back to Cline's parser. No flag required. Override: `--no-auto-detect` if you'd rather force the CC wire shape and deal with the parser mismatch yourself (see [Agent compatibility](#agent-compatibility)).
|
|
520
|
-
|
|
521
|
-
#### Zed
|
|
522
|
-
|
|
523
|
-
Zed's Anthropic provider config (`~/.config/zed/settings.json` or Cmd/Ctrl+,):
|
|
524
|
-
|
|
525
|
-
```json
|
|
526
|
-
{
|
|
527
|
-
"language_models": {
|
|
528
|
-
"anthropic": {
|
|
529
|
-
"api_url": "http://localhost:3456",
|
|
530
|
-
"version": "2023-06-01"
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
Set the `ANTHROPIC_API_KEY` env var to `dario` before launching Zed. Model picker then shows Claude models routed through your subscription.
|
|
537
|
-
|
|
538
|
-
#### OpenHands
|
|
539
|
-
|
|
540
|
-
```bash
|
|
541
|
-
export LLM_BASE_URL=http://localhost:3456
|
|
542
|
-
export LLM_API_KEY=dario
|
|
543
|
-
export LLM_MODEL=anthropic/claude-sonnet-4-6
|
|
544
|
-
python -m openhands.core.main -t "task description"
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
Prefix the model with `anthropic/` so LiteLLM (OpenHands' inner routing layer) knows to hit the Anthropic path, which dario is now fronting.
|
|
548
|
-
|
|
549
|
-
#### Everything else
|
|
550
|
-
|
|
551
|
-
If your tool isn't listed, check whether it reads `OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL` from the environment. Most do. For tools that don't, look in their settings for "Base URL" / "API URL" / "Endpoint" / "OpenAI-compatible endpoint" — all of those map to dario's `http://localhost:3456` (Anthropic-protocol) or `http://localhost:3456/v1` (OpenAI-protocol). If the tool only accepts `https://`, you'll need a loopback TLS shim (out of scope here — open an issue if you need one for a specific tool).
|
|
552
|
-
|
|
553
|
-
### curl
|
|
554
|
-
|
|
555
|
-
```bash
|
|
556
|
-
# Claude backend via Anthropic format
|
|
557
|
-
curl http://localhost:3456/v1/messages \
|
|
558
|
-
-H "Content-Type: application/json" \
|
|
559
|
-
-H "anthropic-version: 2023-06-01" \
|
|
560
|
-
-d '{"model":"claude-opus-4-7","max_tokens":1024,"messages":[{"role":"user","content":"Hello!"}]}'
|
|
561
|
-
|
|
562
|
-
# OpenAI backend via OpenAI format
|
|
563
|
-
curl http://localhost:3456/v1/chat/completions \
|
|
564
|
-
-H "Content-Type: application/json" \
|
|
565
|
-
-H "Authorization: Bearer dario" \
|
|
566
|
-
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello!"}]}'
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
### Streaming, tool use, prompt caching, extended thinking
|
|
570
|
-
|
|
571
|
-
All supported. Claude backend: full Anthropic SSE format plus OpenAI-SSE translation for tool_use streaming. OpenAI-compat backend: streaming body forwarded byte-for-byte. See [Wire-fidelity axes](#wire-fidelity-axes) for the v3.25 `--drain-on-close` knob that matches CC's read-to-EOF stream-consumption pattern.
|
|
572
|
-
|
|
573
|
-
### Provider prefix
|
|
574
|
-
|
|
575
|
-
Any request's `model` field can be written as `<provider>:<name>` to force which backend handles it, regardless of what the model name looks like.
|
|
576
|
-
|
|
577
|
-
| Prefix | Backend |
|
|
578
|
-
|---|---|
|
|
579
|
-
| `openai:` | OpenAI-compat backend |
|
|
580
|
-
| `groq:` | OpenAI-compat backend |
|
|
581
|
-
| `openrouter:` | OpenAI-compat backend |
|
|
582
|
-
| `local:` | OpenAI-compat backend |
|
|
583
|
-
| `compat:` | OpenAI-compat backend |
|
|
584
|
-
| `claude:` | Claude subscription backend |
|
|
585
|
-
| `anthropic:` | Claude subscription backend |
|
|
586
|
-
|
|
587
|
-
The prefix gets stripped before the request goes upstream — the backend only sees the bare model name. Unrecognized prefixes are ignored, so Ollama-style `llama3:8b` passes through untouched. `dario proxy --model=openai:gpt-4o` applies the prefix to every request server-wide.
|
|
212
|
+
Per-tool setup (Cursor, Continue, Aider, Cline, Zed, OpenHands), the `--preserve-tools` / `--hybrid-tools` decision matrix, and the full agent table all live in [`docs/agent-compat.md`](./docs/agent-compat.md).
|
|
588
213
|
|
|
589
214
|
### Custom tool schemas
|
|
590
215
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
The trade-off shows up when you're running something that *isn't* in the pre-mapped list and whose tools carry fields CC's schema doesn't have — a `sessionId`, a custom request id, a channel-bound context token, a `confidence` score the model is supposed to emit. Those fields don't survive the round trip.
|
|
594
|
-
|
|
595
|
-
Symptom: your tool calls come back looking stripped-down, or your runtime complains about a required field being absent *only when routed through dario's Claude backend*.
|
|
596
|
-
|
|
597
|
-
Fix: run dario with `--preserve-tools`. That skips the CC tool remap entirely, passes your client's tool definitions through to the model unchanged, and lets the model populate every field your schema expects.
|
|
598
|
-
|
|
599
|
-
```bash
|
|
600
|
-
dario proxy --preserve-tools
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
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.
|
|
604
|
-
|
|
605
|
-
The OpenAI-compat backend is unaffected — it forwards tool definitions byte-for-byte and doesn't need this flag.
|
|
606
|
-
|
|
607
|
-
### Hybrid tool mode
|
|
608
|
-
|
|
609
|
-
For the very common case where the "missing" fields on your client's tool are **request context** — `sessionId`, `requestId`, `channelId`, `userId`, `timestamp` — dario can remap to CC tools *and* inject those values on the reverse path. The CC wire shape stays intact, the model still sees only CC's tools (so subscription billing still routes), and your validator still sees the fields it requires because dario fills them from request headers on the way back.
|
|
610
|
-
|
|
611
|
-
```bash
|
|
612
|
-
dario proxy --hybrid-tools
|
|
613
|
-
```
|
|
614
|
-
|
|
615
|
-
**How it works.** On each request, dario builds a `RequestContext` from headers (`x-session-id`, `x-request-id`, `x-channel-id`, `x-user-id`) plus its own generated ids and the current timestamp. After `translateBack` produces the client-shaped tool call on the response path, any field declared on the client's tool schema whose name matches a known context field (`sessionId`/`session_id`, `requestId`/`request_id`, `channelId`/`channel_id`, `userId`/`user_id`, `timestamp`/`created_at`/`createdAt`) and isn't already populated gets filled from the context. Fields the model genuinely populated are never overwritten.
|
|
616
|
-
|
|
617
|
-
**When to use which flag:**
|
|
618
|
-
|
|
619
|
-
| Your situation | Flag | Why |
|
|
620
|
-
|---|---|---|
|
|
621
|
-
| Your agent is listed in [Agent compatibility](#agent-compatibility) | *(neither)* | Pre-mapped in `TOOL_MAP`; the default path already handles it. |
|
|
622
|
-
| Your custom fields are request context (session/request/channel/user ids, timestamps) | `--hybrid-tools` | Keeps the CC wire shape *and* your validator is satisfied. |
|
|
623
|
-
| Your custom fields need the model's reasoning (e.g. `confidence`, `reasoning_trace`, `tool_selection_rationale`) | `--preserve-tools` | The model has to see the real schema to populate these. Accept the CC-wire-shape loss. |
|
|
624
|
-
| Your client's tools are already a subset of CC's `Bash/Read/Write/Edit/Grep/Glob/WebSearch/WebFetch` | *(neither)* | Default mode works as-is. |
|
|
625
|
-
| You're on a text-tool client (Cline / Kilo Code / Roo Code) and want to override the auto-detect | `--no-auto-detect` (plus `--preserve-tools` or not, your call) | Operator choice outranks the heuristic. |
|
|
626
|
-
|
|
627
|
-
### Library mode
|
|
628
|
-
|
|
629
|
-
```typescript
|
|
630
|
-
import { startProxy, getAccessToken, getStatus, listBackends } from "@askalf/dario";
|
|
631
|
-
|
|
632
|
-
await startProxy({ port: 3456, verbose: true });
|
|
633
|
-
const token = await getAccessToken();
|
|
634
|
-
const status = await getStatus();
|
|
635
|
-
const backends = await listBackends();
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
### Health check
|
|
639
|
-
|
|
640
|
-
```bash
|
|
641
|
-
curl http://localhost:3456/health
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
---
|
|
216
|
+
If your agent's tool names aren't pre-mapped and its tools carry fields CC's schema doesn't have, you have two escape hatches:
|
|
645
217
|
|
|
646
|
-
|
|
218
|
+
- **`--preserve-tools`** — forward your schema verbatim, lose the CC wire shape (and likely the subscription-billing wire shape with it).
|
|
219
|
+
- **`--hybrid-tools`** — keep the CC wire shape, fill request-context fields (`sessionId`, `requestId`, `channelId`, `userId`, `timestamp`) from headers on the reverse path. The compromise that keeps both.
|
|
647
220
|
|
|
648
|
-
|
|
649
|
-
|---|---|
|
|
650
|
-
| `POST /v1/messages` | Anthropic Messages API (Claude backend) |
|
|
651
|
-
| `POST /v1/chat/completions` | OpenAI-compatible Chat API (routes by model name) |
|
|
652
|
-
| `GET /v1/models` | Model list (Claude models — OpenAI models come from the OpenAI backend directly) |
|
|
653
|
-
| `GET /health` | Proxy health + OAuth status + request count |
|
|
654
|
-
| `GET /status` | Detailed Claude OAuth token status |
|
|
655
|
-
| `GET /accounts` | Pool snapshot including sticky binding count (pool mode only) |
|
|
656
|
-
| `GET /analytics` | Per-account / per-model stats, burn rate, exhaustion predictions, `billingBucket` + `subscriptionPercent` per request |
|
|
221
|
+
Full when-to-use-which decision matrix and request-context field set: [`docs/agent-compat.md#custom-tool-schemas`](./docs/agent-compat.md#custom-tool-schemas).
|
|
657
222
|
|
|
658
223
|
---
|
|
659
224
|
|
|
660
225
|
## Trust and transparency
|
|
661
226
|
|
|
662
|
-
Dario handles your OAuth tokens and API keys locally. Here's why you can trust it:
|
|
663
|
-
|
|
664
227
|
| Signal | Status |
|
|
665
228
|
|---|---|
|
|
666
|
-
| **Source code** | ~
|
|
229
|
+
| **Source code** | ~12,650 lines of TypeScript across 27 files — small enough to audit in a weekend |
|
|
667
230
|
| **Dependencies** | 0 runtime dependencies. Verify: `npm ls --production` |
|
|
668
231
|
| **npm provenance** | Every release is [SLSA-attested](https://www.npmjs.com/package/@askalf/dario) via GitHub Actions with sigstore provenance attached to the transparency log |
|
|
669
|
-
| **Security scanning** | [CodeQL](https://github.com/askalf/dario/actions/workflows/codeql.yml)
|
|
670
|
-
| **Test footprint** | ~1,
|
|
232
|
+
| **Security scanning** | [CodeQL](https://github.com/askalf/dario/actions/workflows/codeql.yml) on every push and weekly |
|
|
233
|
+
| **Test footprint** | ~1,535 assertions across 57 test suites. Full `npm test` green on every release |
|
|
671
234
|
| **Credential handling** | Tokens and API keys never logged, redacted from errors, stored with `0600` permissions. MCP server (v3.27) redacts keys at the tool boundary too — not even a `sk-…` prefix leaks. |
|
|
672
|
-
| **OAuth flow** | PKCE
|
|
673
|
-
| **Network scope** | Binds to `127.0.0.1` by default. `--host` allows LAN/mesh with `DARIO_API_KEY` gating. Upstream traffic goes only to
|
|
235
|
+
| **OAuth flow** | PKCE, no client secret. `--manual` flow for headless setups (v3.20). |
|
|
236
|
+
| **Network scope** | Binds to `127.0.0.1` by default. `--host` allows LAN/mesh with `DARIO_API_KEY` gating. Upstream traffic goes only to configured backend target URLs over HTTPS. |
|
|
674
237
|
| **SSRF protection** | `/v1/messages` hits `api.anthropic.com` only; `/v1/chat/completions` hits the configured backend `baseUrl` only — hardcoded allowlist |
|
|
675
|
-
| **Telemetry** | None. Zero analytics, tracking, or data collection. The MCP server
|
|
676
|
-
| **Atomic cache writes + corruption recovery** | v3.17 — template cache writes are pid-qualified `.tmp` + `rename`, corrupt cache files are quarantined and regenerated instead of crashing startup |
|
|
677
|
-
| **Baked template scrub** | v3.21 — the bundled fallback template is stripped of host-identifying paths and `mcp__*` tool names at bake time; the nightly drift watcher guards against regression |
|
|
238
|
+
| **Telemetry** | None. Zero analytics, tracking, or data collection. The MCP server and CC sub-agent are read-only by design. |
|
|
678
239
|
| **Audit trail** | [CHANGELOG.md](CHANGELOG.md) documents every release with file-level rationale |
|
|
679
240
|
|
|
680
241
|
Verify the npm tarball matches this repo:
|
|
@@ -687,167 +248,51 @@ cd $(npm root -g)/@askalf/dario && npm ls --production
|
|
|
687
248
|
|
|
688
249
|
---
|
|
689
250
|
|
|
690
|
-
##
|
|
691
|
-
|
|
692
|
-
Four independent senior-engineer-style reviews from frontier LLMs, same prompt, each asked to read the code and make concrete calls instead of hedging. Full review text is committed in [`reviews/`](./reviews/) — including the initial-draft / revised-draft trail for GPT-5.3 — so readers can evaluate methodology alongside conclusions.
|
|
693
|
-
|
|
694
|
-
### Grok 4 — *"Adopt if the use-case fits."* · [full review](./reviews/grok-4-2026-04-21.md)
|
|
695
|
-
|
|
696
|
-
> Production-ready local router with unusually strong engineering and transparency.
|
|
697
|
-
|
|
698
|
-
> This is not vibe-coded; it reads like production-grade infrastructure that happens to be open-source.
|
|
699
|
-
|
|
700
|
-
> No hand-waving; the mechanism is coherent and evidenced in both code and public testing.
|
|
701
|
-
|
|
702
|
-
**Push-back:** `npm audit` CI gate, surface test coverage % in README, `--no-live-capture` flag for air-gapped environments, hard default guard on `0.0.0.0` binding without `DARIO_API_KEY`.
|
|
703
|
-
|
|
704
|
-
### Claude Opus 4.7 — *"The fingerprint-replay claim is backed by the code."* · [full review](./reviews/claude-opus-4-7-2026-04-21.md)
|
|
705
|
-
|
|
706
|
-
> A meaningfully well-engineered piece of reverse-engineered infrastructure; the fingerprint-replay claim is backed by the code, and the author has been honest about what replay can and cannot defend against.
|
|
707
|
-
|
|
708
|
-
> Comments consistently cite the issue number that motivated the code — which is what scar-tissue code looks like in a project that has actual users.
|
|
709
|
-
|
|
710
|
-
> Zero runtime dependencies in a TypeScript project that ships OAuth flows, multi-provider routing, an MCP server, and a process shim.
|
|
711
|
-
|
|
712
|
-
**Push-back:** switch the `npm test` chain to `node --test` for parallelism and proper failure reporting; bundled `cc-template-data.json` should declare its own `SUPPORTED_CC_RANGE` so too-new installs fail closed; hoist the 0.02 headroom threshold in `selectSticky` to a named `POOL_HEADROOM_FLOOR` constant.
|
|
713
|
-
|
|
714
|
-
### Gemini 2.0 Pro — *"Technically elite, zero-dependency proxy."* · [full review](./reviews/gemini-2-pro-2026-04-21.md)
|
|
715
|
-
|
|
716
|
-
> The implementation isn't just a simple header swap; it is a sophisticated **"request-level deepfake."**
|
|
717
|
-
|
|
718
|
-
> This is a serious project, not a script.
|
|
719
|
-
|
|
720
|
-
> The source code is legible enough that a 10-minute audit confirms no data exfiltration.
|
|
721
|
-
|
|
722
|
-
> dario is a technically elite, zero-dependency proxy that successfully bridges the gap between consumer subscriptions and developer tooling through high-fidelity binary emulation.
|
|
723
|
-
|
|
724
|
-
**Push-back:** make orchestration-tag scrubbing (`<system-reminder>`, etc.) a toggle for users whose workflows need the tags preserved; concurrency limit lacks a fair-use queue — high-volume clients can hit dario-level 429s before upstream.
|
|
725
|
-
|
|
726
|
-
### ChatGPT (GPT-5.3) — *"Disciplined, intentional engineering. Not vibe-coded."* · [full review](./reviews/gpt-5.3-2026-04-21.md)
|
|
727
|
-
|
|
728
|
-
*Initial pass was priors-based and skeptical; after being pushed to fetch the source directly, the reviewer retracted several specific concerns and revised the engineering grade upward — the before / after trail is preserved in the linked review.*
|
|
729
|
-
|
|
730
|
-
> A legitimately well-engineered, low-dependency local proxy with precise wire-replay mechanics; trustworthy as a tool, but built on a fundamentally unstable (and potentially adversarial) contract with an upstream classifier.
|
|
731
|
-
|
|
732
|
-
> This is not "best-effort mimicry"; it's capture-and-replay of a real client.
|
|
733
|
-
|
|
734
|
-
> Security hygiene is strong for a local dev tool. Risk comes from what it is, not sloppy implementation.
|
|
251
|
+
## Commands
|
|
735
252
|
|
|
736
|
-
|
|
253
|
+
`dario login`, `dario proxy`, `dario doctor`, `dario accounts {list,add,remove}`, `dario backend {list,add,remove}`, `dario shim`, `dario mcp`, `dario subagent {install,status,remove}`, `dario usage`, `dario config`, `dario upgrade`, `dario status`, `dario refresh`, `dario logout`, `dario help`.
|
|
737
254
|
|
|
738
|
-
|
|
255
|
+
Full flag/env reference + endpoint list: [`docs/commands.md`](./docs/commands.md).
|
|
739
256
|
|
|
740
|
-
|
|
257
|
+
SDK examples (Python / TypeScript / curl) + per-tool setup: [`docs/usage.md`](./docs/usage.md).
|
|
741
258
|
|
|
742
259
|
---
|
|
743
260
|
|
|
744
261
|
## FAQ
|
|
745
262
|
|
|
746
|
-
|
|
747
|
-
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).
|
|
748
|
-
|
|
749
|
-
**What subscription plans work on the Claude backend?**
|
|
750
|
-
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.
|
|
263
|
+
The most-asked questions. Full FAQ in [`docs/faq.md`](./docs/faq.md).
|
|
751
264
|
|
|
752
|
-
**
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
**Does it work with Team / Enterprise?**
|
|
756
|
-
Yes — tested and confirmed working as long as your plan includes Claude Code access.
|
|
265
|
+
**Does this violate Anthropic's terms of service?**
|
|
266
|
+
Mechanically: dario 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 endpoints. Whether any particular use complies with their current terms is between you and Anthropic — consult their terms and your subscription agreement. Independent, unofficial, third-party — see [DISCLAIMER.md](DISCLAIMER.md).
|
|
757
267
|
|
|
758
268
|
**Do I need Claude Code installed?**
|
|
759
|
-
Recommended
|
|
269
|
+
Recommended, 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. Without CC, dario runs its own OAuth flow and falls back to the bundled (scrubbed) template snapshot.
|
|
760
270
|
|
|
761
271
|
**Do I need Bun?**
|
|
762
|
-
Optional, strongly recommended for Claude-backend requests
|
|
272
|
+
Optional, strongly recommended for Claude-backend requests so the TLS ClientHello matches CC's runtime. Without Bun it works fine — `dario doctor` surfaces the mismatch as of v3.23 and `--strict-tls` refuses to start until resolved.
|
|
763
273
|
|
|
764
274
|
**Can I use dario without a Claude subscription?**
|
|
765
|
-
Yes. Skip `dario login`,
|
|
766
|
-
|
|
767
|
-
**Can I route non-OpenAI providers through dario?**
|
|
768
|
-
Yes — anything that speaks the OpenAI Chat Completions API. Groq, OpenRouter, LiteLLM, vLLM, Ollama's openai-compat mode, your own vLLM server, any hosted inference endpoint that exposes `/v1/chat/completions`. Just `dario backend add <name> --key=... --base-url=...`.
|
|
275
|
+
Yes. Skip `dario login`, `dario backend add openai --key=...` and you have a local OpenAI-compat router with no Claude involvement.
|
|
769
276
|
|
|
770
277
|
**Something's wrong. Where do I start?**
|
|
771
|
-
`dario doctor`. One command,
|
|
278
|
+
`dario doctor`. One command, paste-ready report. If you're inside CC, `dario subagent install` once and ask CC to "use the dario sub-agent to run doctor."
|
|
772
279
|
|
|
773
|
-
**
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
Three fixes, in order of simplicity:
|
|
777
|
-
|
|
778
|
-
1. **Use loopback.** `dario proxy --host=127.0.0.1` — auth only enforced on non-loopback binds, no `DARIO_API_KEY` required, no OpenClaw changes. Best if you don't actually need LAN reach to dario.
|
|
779
|
-
2. **Delete the Anthropic auth profile.** Remove the `"anthropic:default"` entry from `~/.openclaw/agents/main/agent/auth-profiles.json`. OpenClaw then falls back through the config chain and picks up `ANTHROPIC_API_KEY=dario` from the env. Confirmed working by [@tetsuco in #97](https://github.com/askalf/dario/issues/97).
|
|
780
|
-
3. **Overwrite the auth profile.** `openclaw models auth paste-token --provider anthropic` and paste `dario`. Replaces whatever key was in there — keep a backup if you use it elsewhere.
|
|
781
|
-
|
|
782
|
-
Diagnose with `dario proxy -v` — the reject log (v3.31.2+) reports header-name only (never the value, since it may be a real credential you mistyped) and tells you which of the three configs is actually being hit.
|
|
783
|
-
|
|
784
|
-
**What happens when Anthropic rotates the OAuth config?**
|
|
785
|
-
Dario auto-detects OAuth config from the installed Claude Code binary. When CC ships a new version with rotated values, dario picks them up on the next run. Cache at `~/.dario/cc-oauth-cache-v6.json`, keyed by the CC binary fingerprint. The cache path version bumps each time the canonical OAuth config shape changes so stale caches regenerate automatically on upgrade — v3 → v4 in v3.19.4 (scope-list flip CC v2.1.104 → v2.1.107), v4 → v5 in v3.31.3 (authorize URL `claude.com/cai/` → `claude.ai/` host normalization), v5 → v6 in v3.31.4 (6-scope restore after CC v2.1.116).
|
|
786
|
-
|
|
787
|
-
If Anthropic rotates the values before the detector is updated, you can temporarily override any field with env vars (`DARIO_OAUTH_CLIENT_ID`, `DARIO_OAUTH_AUTHORIZE_URL`, `DARIO_OAUTH_TOKEN_URL`, `DARIO_OAUTH_SCOPES`) or by writing `~/.dario/oauth-config.override.json`:
|
|
788
|
-
|
|
789
|
-
```json
|
|
790
|
-
{
|
|
791
|
-
"clientId": "...",
|
|
792
|
-
"authorizeUrl": "https://claude.com/cai/oauth/authorize",
|
|
793
|
-
"tokenUrl": "https://platform.claude.com/v1/oauth/token",
|
|
794
|
-
"scopes": "user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload"
|
|
795
|
-
}
|
|
796
|
-
```
|
|
797
|
-
|
|
798
|
-
Env vars win over the file. Set `DARIO_OAUTH_DISABLE_OVERRIDE=1` to force pure auto-detection.
|
|
799
|
-
|
|
800
|
-
**What happens when Anthropic changes the CC request template?**
|
|
801
|
-
Dario extracts the live request template from your installed Claude Code binary on startup — the system prompt, tool schemas, user-agent, beta flags, header insertion order, static header values, and top-level request-body key order — and uses those to replay requests instead of a version pinned into dario itself. When CC ships a new version with a tweaked template, the next `dario proxy` run picks it up automatically. Drift detection forces a refresh when the installed CC version changes under dario, and the nightly `cc-drift-watch` workflow catches upstream rotations (client_id, URLs, tool set, version) the day they ship on npm.
|
|
802
|
-
|
|
803
|
-
**Why does `dario accounts list` show an account called `login` I never added?**
|
|
804
|
-
That's your existing `dario login` credentials, back-filled into the pool automatically on your first `dario accounts add <alias>`. Pool mode activates at 2+ accounts in `~/.dario/accounts/`, and the single-account `credentials.json` store lives outside that directory — so without the back-fill, one `accounts add` would leave you at 1 pool entry and your login account orphaned. The `login` alias is reserved for this path. Safe to `dario accounts remove login` if you don't want it pooled; the original `credentials.json` is untouched by the back-fill, so single-account mode resumes reading it after removal drops you below the 2+ threshold. See [Multi-account pool mode](#multi-account-pool-mode) for the full picture.
|
|
805
|
-
|
|
806
|
-
**First time setup on a fresh Claude account.**
|
|
807
|
-
If dario is the first thing you run against a brand-new Claude account, prime the account with a few real Claude Code commands first:
|
|
808
|
-
```bash
|
|
809
|
-
claude --print "hello"
|
|
810
|
-
claude --print "hello"
|
|
811
|
-
```
|
|
812
|
-
This establishes a session baseline. Without priming, brand-new accounts occasionally see billing classification issues on first use.
|
|
813
|
-
|
|
814
|
-
**I'm hitting rate limits on the Claude backend. What do I do?**
|
|
815
|
-
Claude subscriptions have rolling 5-hour and 7-day usage windows. Check utilization with Claude Code's `/usage` command or the [statusline](https://code.claude.com/docs/en/statusline). For multi-agent workloads, add more accounts and let pool mode distribute the load: `dario accounts add <alias>`. Session stickiness keeps long conversations pinned to one account so the prompt cache isn't destroyed by rotation.
|
|
816
|
-
|
|
817
|
-
**I'm seeing `representative-claim: seven_day` in my rate-limit headers instead of `five_hour`. Am I being downgraded to API billing?**
|
|
818
|
-
|
|
819
|
-
**No.** You're still on subscription billing. Both `five_hour` and `seven_day` are the same subscription billing mode — two different accounting buckets inside it.
|
|
820
|
-
|
|
821
|
-
| Claim | What it means |
|
|
822
|
-
|---|---|
|
|
823
|
-
| `five_hour` | You're well inside your 5-hour window; billing against the short-term bucket. |
|
|
824
|
-
| `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. |
|
|
825
|
-
| `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. |
|
|
826
|
-
|
|
827
|
-
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.
|
|
828
|
-
|
|
829
|
-
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).
|
|
830
|
-
|
|
831
|
-
**My multi-agent workload is getting reclassified to overage even though dario mirrors the CC wire shape per request. Why?**
|
|
832
|
-
Reclassification at high agent volume is not a per-request problem. The upstream billing logic takes cumulative per-OAuth-session aggregates into account — token throughput, conversation depth, streaming duration, inter-arrival timing, thinking-block volume. Dario's Claude backend can make each individual request match Claude Code and still hit this wall on a long-running agent session. Thorough diagnostic work was contributed by [@belangertrading](https://github.com/belangertrading) in [#23](https://github.com/askalf/dario/issues/23). The practical answer at the dario layer is **pool mode** — distribute load across multiple subscriptions so no single account accumulates signal along any single dimension. See [Multi-account pool mode](#multi-account-pool-mode). The v3.22 – v3.28 wire-fidelity track (pacing, stream-drain, session-id lifecycle) also narrows the cumulative signal on a single account — see [Wire-fidelity axes](#wire-fidelity-axes).
|
|
833
|
-
|
|
834
|
-
**My proxy is on Node, not Bun. What's the actual risk?**
|
|
835
|
-
Node uses OpenSSL, Bun uses BoringSSL — the TLS ClientHello differs enough to yield a distinct JA3/JA4 hash. The upstream service can see the hash. Whether any routing decisions depend on it today is not published; making the axis visible is the v3.23 contribution. If certainty matters to you, install Bun (dario auto-relaunches under it) or run `dario proxy --strict-tls` to fail loud. If it doesn't, the warning is ignorable — dario still works, the TLS ClientHello is just the one observable axis left.
|
|
836
|
-
|
|
837
|
-
**Why "dario"?**
|
|
838
|
-
It's a name, not an acronym. Don't overthink it.
|
|
280
|
+
**I'm seeing `representative-claim: seven_day` in my rate-limit headers — am I being downgraded?**
|
|
281
|
+
**No.** Both `five_hour` and `seven_day` are subscription billing — different accounting buckets inside the same subscription mode. `overage` is the one that flips you to per-token. See [Discussion #1](https://github.com/askalf/dario/discussions/1) for the full rate-limit-header breakdown.
|
|
839
282
|
|
|
840
283
|
---
|
|
841
284
|
|
|
842
285
|
## Technical deep dives
|
|
843
286
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
- [
|
|
847
|
-
- [
|
|
848
|
-
- [
|
|
849
|
-
- [
|
|
850
|
-
- [
|
|
287
|
+
- [#178 — Anthropic's billing classifier fingerprints `openclaw.inbound_meta.v1`](https://github.com/askalf/dario/discussions/178) — reproducing Theo Browne's finding with controlled variants
|
|
288
|
+
- [#172 — Re-testing #13: the system prompt is not a fingerprint signal](https://github.com/askalf/dario/discussions/172)
|
|
289
|
+
- [#68 — dario vs LiteLLM / OpenRouter / Kong AI Gateway (when each one wins)](https://github.com/askalf/dario/discussions/68)
|
|
290
|
+
- [#14 — Template Replay: why we stopped matching signals](https://github.com/askalf/dario/discussions/14)
|
|
291
|
+
- [#13 — Claude Code's "defaults" are detection signals, not optimizations](https://github.com/askalf/dario/discussions/13)
|
|
292
|
+
- [#39 — Why your Claude Max usage is burning in minutes](https://github.com/askalf/dario/discussions/39)
|
|
293
|
+
- [#9 — Why Opus feels worse through other proxies and how to fix it](https://github.com/askalf/dario/discussions/9)
|
|
294
|
+
- [#8 — Billing tag algorithm and fingerprint analysis](https://github.com/askalf/dario/discussions/8)
|
|
295
|
+
- [#1 — Rate limit header analysis](https://github.com/askalf/dario/discussions/1)
|
|
851
296
|
|
|
852
297
|
The CHANGELOG documents every v3.22 – v3.28 wire-fidelity release with file-level rationale; each one is worth reading as a standalone post on the axis it closes.
|
|
853
298
|
|
|
@@ -855,41 +300,14 @@ The CHANGELOG documents every v3.22 – v3.28 wire-fidelity release with file-le
|
|
|
855
300
|
|
|
856
301
|
## Contributing
|
|
857
302
|
|
|
858
|
-
PRs welcome.
|
|
859
|
-
|
|
860
|
-
| File | Purpose |
|
|
861
|
-
|---|---|
|
|
862
|
-
| `src/proxy.ts` | HTTP proxy server, request handler, rate governor, Claude backend dispatch, OpenAI-compat routing, pool failover, session registry wiring, stream-drain gating |
|
|
863
|
-
| `src/cc-template.ts` | CC request template engine, universal `TOOL_MAP` (~66 schema-verified entries), orchestration and framework scrubbing, header-order + body-field-order replay |
|
|
864
|
-
| `src/cc-template-data.json` | Bundled fallback CC request template (used when live-fingerprint extraction isn't possible). Scrubbed of host-identifying paths and `mcp__*` tools at bake time (v3.21). |
|
|
865
|
-
| `src/scrub-template.ts` | Host-context scrubber for the baked fallback — strips per-session sections, replaces user-dir paths with a placeholder, drops `mcp__*` tools |
|
|
866
|
-
| `src/cc-oauth-detect.ts` | OAuth config auto-detection from the installed CC binary |
|
|
867
|
-
| `src/live-fingerprint.ts` | Live extraction of the CC request template (system prompt, tools, user-agent, beta flags, header order, static header values, body field order) from the installed Claude Code binary, drift detection, compat matrix, atomic cache writes, corruption recovery |
|
|
868
|
-
| `src/runtime-fingerprint.ts` | Runtime / TLS classifier (`bun-match` / `bun-bypassed` / `node-only`) surfaced through `dario doctor` and `--strict-tls` (v3.23) |
|
|
869
|
-
| `src/pacing.ts` | Pure inter-request delay calculator with configurable floor + uniform jitter (v3.24) |
|
|
870
|
-
| `src/stream-drain.ts` | Pure decision function for client-disconnect handling — `abort` / `drain` / `noop` (v3.25) |
|
|
871
|
-
| `src/session-rotation.ts` | `SessionRegistry` with LRU eviction + pure `decideSessionRotation` — idle, jitter, max-age, per-client bucketing (v3.28) |
|
|
872
|
-
| `src/subagent.ts` | CC sub-agent install / remove / status lifecycle; `buildSubagentFile(version)` is pure and pinned (v3.26) |
|
|
873
|
-
| `src/mcp/protocol.ts` | Hand-rolled JSON-RPC 2.0 + MCP method dispatcher — zero deps, pure over inputs, tested without streams (v3.27) |
|
|
874
|
-
| `src/mcp/tools.ts` | Six read-only MCP tools — `doctor`, `status`, `accounts_list`, `backends_list`, `subagent_status`, `fingerprint_info`. Redacts credentials at the tool boundary (v3.27) |
|
|
875
|
-
| `src/mcp/server.ts` | Stdio event loop — ordered serial dispatch, back-pressure-aware writes, injectable streams for testing (v3.27) |
|
|
876
|
-
| `src/doctor.ts` | `dario doctor` health report aggregator — dario / Node / runtime-TLS / CC / template / drift / OAuth / pool / backends / sub-agent |
|
|
877
|
-
| `src/oauth.ts` | Single-account token storage, PKCE flow, auto-refresh, manual/headless flow (v3.20) |
|
|
878
|
-
| `src/accounts.ts` | Multi-account credential storage, independent OAuth lifecycle, refresh single-flight |
|
|
879
|
-
| `src/pool.ts` | Account pool, headroom-aware routing, session stickiness, failover target selection |
|
|
880
|
-
| `src/analytics.ts` | Rolling request history, per-account / per-model stats, burn-rate, billing bucket classification |
|
|
881
|
-
| `src/openai-backend.ts` | OpenAI-compat backend credential storage and request forwarder |
|
|
882
|
-
| `src/shim/runtime.cjs` | Hand-written CJS payload loaded into child processes via `NODE_OPTIONS=--require`; patches `globalThis.fetch` for Anthropic messages requests only |
|
|
883
|
-
| `src/shim/host.ts` | Parent-side orchestrator for `dario shim` — spawns the child, owns the telemetry socket / named pipe, feeds analytics |
|
|
884
|
-
| `src/cli.ts` | CLI entry point, command routing, Bun auto-relaunch, proxy flag parsing |
|
|
885
|
-
| `src/index.ts` | Library exports |
|
|
303
|
+
PRs welcome. Small TypeScript codebase — ~12,650 lines, 27 files, zero runtime dependencies. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the architecture overview and the file-by-file map.
|
|
886
304
|
|
|
887
305
|
```bash
|
|
888
306
|
git clone https://github.com/askalf/dario
|
|
889
307
|
cd dario
|
|
890
308
|
npm install
|
|
891
309
|
npm run dev # runs with tsx, no build step
|
|
892
|
-
npm test # ~1,
|
|
310
|
+
npm test # ~1,535 assertions across 57 suites
|
|
893
311
|
npm run e2e # live proxy + OAuth (requires a working Claude backend)
|
|
894
312
|
```
|
|
895
313
|
|
|
@@ -902,26 +320,17 @@ npm run e2e # live proxy + OAuth (requires a working Claude backend)
|
|
|
902
320
|
| [@GodsBoy](https://github.com/GodsBoy) | Proxy authentication, token redaction, error sanitization ([#2](https://github.com/askalf/dario/pull/2)) |
|
|
903
321
|
| [@belangertrading](https://github.com/belangertrading) | Billing classification investigation ([#4](https://github.com/askalf/dario/issues/4)), cache_control fingerprinting ([#6](https://github.com/askalf/dario/issues/6)), billing reclassification root cause ([#7](https://github.com/askalf/dario/issues/7)), OAuth client_id discovery ([#12](https://github.com/askalf/dario/issues/12)), multi-agent session-level billing analysis ([#23](https://github.com/askalf/dario/issues/23)) |
|
|
904
322
|
| [@iNicholasBE](https://github.com/iNicholasBE) | macOS keychain credential detection ([#30](https://github.com/askalf/dario/pull/30)) |
|
|
905
|
-
| [@boeingchoco](https://github.com/boeingchoco) | Reverse-direction tool parameter translation ([#29](https://github.com/askalf/dario/issues/29)), SSE event-group framing regression catch (v3.7.1), provider-comparison diagnostic
|
|
906
|
-
| [@tetsuco](https://github.com/tetsuco) | Framework-name path corruption in scrubber ([#35](https://github.com/askalf/dario/issues/35)), OpenClaw Bash/Glob reverse-mapping collisions ([#37](https://github.com/askalf/dario/issues/37)), 20x-tier
|
|
323
|
+
| [@boeingchoco](https://github.com/boeingchoco) | Reverse-direction tool parameter translation ([#29](https://github.com/askalf/dario/issues/29)), SSE event-group framing regression catch (v3.7.1), provider-comparison diagnostic, motivating case for hybrid tool mode ([#33](https://github.com/askalf/dario/issues/33), v3.9.0), OpenClaw tool-mapping root cause ([#36](https://github.com/askalf/dario/issues/36)) |
|
|
324
|
+
| [@tetsuco](https://github.com/tetsuco) | Framework-name path corruption in scrubber ([#35](https://github.com/askalf/dario/issues/35)), OpenClaw Bash/Glob reverse-mapping collisions ([#37](https://github.com/askalf/dario/issues/37)), 20x-tier capture-artifact + OAuth-scope rejection report ([#42](https://github.com/askalf/dario/issues/42)) |
|
|
907
325
|
| [@mikelovatt](https://github.com/mikelovatt) | Silent subscription-percent drain surfaced via friendly billing buckets ([#34](https://github.com/askalf/dario/issues/34)) |
|
|
908
|
-
| [@ringge](https://github.com/ringge) | Fingerprint-fidelity concern motivating
|
|
326
|
+
| [@ringge](https://github.com/ringge) | Fingerprint-fidelity concern motivating `--no-auto-detect` for text-tool-client auto-preserve ([#40](https://github.com/askalf/dario/issues/40), v3.20.1) |
|
|
327
|
+
| [@earlvanze](https://github.com/earlvanze) | OpenClaw tool mappings + missing CC tools ([#19](https://github.com/askalf/dario/pull/19)), OAuth manual-override escape hatch ([#47](https://github.com/askalf/dario/pull/47)), HTTPS warning for non-secure overrides ([#53](https://github.com/askalf/dario/pull/53)) |
|
|
909
328
|
|
|
910
329
|
---
|
|
911
330
|
|
|
912
331
|
## Disclaimers
|
|
913
332
|
|
|
914
|
-
**dario is an independent, unofficial, third-party project.**
|
|
915
|
-
|
|
916
|
-
**The Software is provided "AS IS", without warranty of any kind.** There is no warranty of merchantability, fitness for a particular purpose, non-infringement, availability, or accuracy. The project operates on a volunteer, best-effort basis — there is no service-level agreement, no support commitment, and no guarantee of continued operation, backward compatibility, or interoperability with any upstream service.
|
|
917
|
-
|
|
918
|
-
**You are solely responsible** for your use of any third-party service reached through dario, your compliance with that service's terms of service and acceptable-use policy, the security of your credentials and local environment, the content you send or receive through the Software, and compliance with all laws and regulations applicable to you.
|
|
919
|
-
|
|
920
|
-
**The Software is not intended for, and is not warranted as suitable for, safety-critical, regulated, or production-grade environments** (HIPAA, PCI-DSS, FedRAMP, SOC 2, etc.) without your own independent review, hardening, and diligence.
|
|
921
|
-
|
|
922
|
-
**To the maximum extent permitted by applicable law, the project and its contributors disclaim all liability** for direct, indirect, incidental, special, consequential, or punitive damages of any kind, including loss of profits, data, goodwill, or subscriptions, arising out of or in connection with the Software.
|
|
923
|
-
|
|
924
|
-
For the full text, see [DISCLAIMER.md](DISCLAIMER.md). For the governing license, see [LICENSE](LICENSE).
|
|
333
|
+
**dario is an independent, unofficial, third-party project.** Not affiliated with, endorsed by, or sponsored by Anthropic, OpenAI, or any other vendor referenced in the code or documentation. Provided as-is with no warranty. You are solely responsible for compliance with your subscription's terms of service, the security of your credentials, and the content you send through the proxy. Not intended for safety-critical, regulated, or production-grade environments without your own independent review. See [DISCLAIMER.md](DISCLAIMER.md) for full text.
|
|
925
334
|
|
|
926
335
|
---
|
|
927
336
|
|