@mrrlin-dev/external-agents 0.3.0 → 0.3.2

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.
Files changed (4) hide show
  1. package/README.md +68 -276
  2. package/docs/hero.png +0 -0
  3. package/package.json +1 -1
  4. package/ui.js +13 -5
package/README.md CHANGED
@@ -1,343 +1,135 @@
1
1
  # @mrrlin-dev/external-agents
2
2
 
3
- [![Install to Claude Code](https://img.shields.io/badge/Install_to-Claude_Code-4a90e2?style=for-the-badge&logo=anthropic&logoColor=white)](#claude-code)
4
- [![Install to Codex](https://img.shields.io/badge/Install_to-Codex-24292f?style=for-the-badge&logo=openai&logoColor=white)](#codex)
5
- [![One-command install](https://img.shields.io/badge/curl_%7C_bash-one_command-4a8?style=for-the-badge)](#one-command-install)
3
+ [![Install to Claude Code](https://img.shields.io/badge/Install_to-Claude_Code-4a90e2?style=for-the-badge&logo=anthropic&logoColor=white)](#-2-minute-setup)
4
+ [![Install to Codex](https://img.shields.io/badge/Install_to-Codex-24292f?style=for-the-badge&logo=openai&logoColor=white)](#-2-minute-setup)
5
+ [![One-command install](https://img.shields.io/badge/curl_%7C_bash-one_command-4a8?style=for-the-badge)](#-2-minute-setup)
6
6
  [![npm](https://img.shields.io/npm/v/@mrrlin-dev/external-agents?style=for-the-badge)](https://www.npmjs.com/package/@mrrlin-dev/external-agents)
7
7
 
8
- **Cut your LLM bill by 10-100x by fanning work across free tiers of a dozen providers.**
8
+ **Route work from your coding agent across 20+ free-tier LLMs. Cut your bill 10-100×.**
9
9
 
10
- `external-agents` is an MCP server + CLI that routes work from your primary coding agent (Claude Code, Codex, Cursor) to a **pool of 20+ free-tier LLMs** — Gemini, Groq, Cerebras, OpenRouter :free, Z.ai, Ollama Cloud, and paid tiers of DeepSeek — via one clean surface: round-robin, cooldown-aware, auto-fallback on 429, with a local dashboard for setting keys and tracking usage.
10
+ ![architecture: primary agent external-agents six free-tier providers, one pool of tokens](docs/hero.png)
11
11
 
12
- **The core value is economic.** You have separate free-tier quotas at Google + Groq + Cerebras + OpenRouter + Z.ai. `external-agents` treats them as **one pool of tokens** and dispatches to the next healthy bucket. What used to be one 30-req/min Gemini limit is now ~150 req/min across five providers, all at $0 enough to run entire agentic loops (implementations, reviews, refactors) that would burn $10-100/day on a single paid model.
13
-
14
- **It's also the substrate for LLM-consensus** — the "ask N different models the same question, adjudicate" pattern — see [karpathy/llm-council](https://github.com/karpathy/llm-council) ("LLM Council works together to answer your hardest questions") and the broader [LLM-as-a-Judge](https://arxiv.org/abs/2306.05685) literature. An ensemble of frontier models routinely beats any single one. `pick_agents` gives you N distinct-provider picks in one call, so a "fleet of subagents deliberating in parallel" is one primitive away. **[Mrrlin](https://mrrlin.com) uses exactly this** — its consensus gate resolves two dynamic terminal reviewers from this pool every round, so every design and every diff gets stress-tested by different model families before it merges.
15
-
16
- Beyond savings and consensus, you also get the zoo-manager niceties: per-provider auth, cooldowns keyed to the *provider's* reset time (not a made-up default), quota tracking in a local JSONL, and a config-free `status` view of who is healthy right now.
17
-
18
- > **Part of [mrrlin.com](https://mrrlin.com)** — the AI orchestration platform for developers. `external-agents` is the open-source layer we use internally for cost-efficient atomic execution and multi-model consensus. Ships MIT so anyone can adopt it standalone.
19
-
20
- ---
21
-
22
- ## Why this exists — the money argument first
23
-
24
- If you're paying for a single frontier model to do everything (planning, atomic edits, reviews, unit-test scaffolding, docstrings), you're leaving a **lot** of money on the table:
25
-
26
- - **Free-tier quotas stack.** Google gives you generous Gemini quotas. Groq gives you 30 rpm Llama 3.3 70B at 500-800 tok/s. Cerebras gives you 30 rpm at ~2000 tok/s. OpenRouter gives you 20 rpd of `:free`-tagged frontier models with no card. Z.ai gives you GLM-4.7-flash. Ollama Cloud gives you gpt-oss:120b. Each of these has a *separate* bucket — `external-agents` treats them as one pool, so effective throughput is Σ(free-tier limits).
27
- - **Round-robin, not "always pick the smartest".** Weak-tier atomic tasks (rename, refactor, write test, fix lint) don't need frontier reasoning; they need a competent model that's currently under quota. `pick --tier weak` finds one; escalation to strong-tier only fires when a task genuinely fails twice.
28
- - **Fallback is automatic.** A 429 on Groq flips you to Cerebras without a retry loop in your code. The exhausted provider is marked with the reset time the provider itself reports in headers (not a 1-hour fallback), so you don't waste calls probing it.
29
- - **Consensus is cheap.** Fan `pick_agents --n 4 --min-distinct-providers 4` and dispatch in parallel — four independent verdicts across four provider families, all on free-tier buckets, in the wall-time of the slowest one.
30
-
31
- The net effect for us has been **10-100x reduction** in per-task cost for the atomic-executor workload that used to run on a single paid model. Your mileage varies with task mix, but the direction is the same for anyone who fans work out.
32
-
33
- ### What else you get (the zoo-management layer)
34
-
35
- - **Unified dispatch.** `dispatch(agent_id, prompt)` runs a specific agent; `pick_agents` picks N healthy candidates by round-robin with cross-provider diversity guaranteed.
36
- - **State that heals itself.** Quota exhaustion detected from live responses + rate-limit headers; cooldown honors the *provider's* reset time; healthy calls auto-clear stale cooldowns.
37
- - **Auth surfaces you actually have.** Subscription CLI (Codex, Claude), env-var API keys via [`aider`](https://aider.chat) → direct-to-provider through LiteLLM (100+ providers, no gateway proxy), direct CLI (cursor-agent, opencode, ollama).
38
- - **Statistics that answer your questions.** How many dispatches went to Gemini this week? What did they cost? Which provider is failing the most? Local JSONL log + dashboard, no cloud required.
39
-
40
- ### LLM-consensus — the multi-model panel, made trivial
41
-
42
- Ask several distinct LLMs the same thing and pick the majority answer — an ensemble of frontier models routinely beats any single one. This pattern shows up as [karpathy/llm-council](https://github.com/karpathy/llm-council) (Karpathy's own experimental repo — LLM Council answering hard questions collectively), [LLM-as-a-Judge](https://arxiv.org/abs/2306.05685) benchmarks, and [self-consistency decoding](https://arxiv.org/abs/2203.11171). But it works only if you can (a) reach N different providers cheaply and (b) fan out in parallel. `external-agents` gives you both:
43
-
44
- ```
45
- ids = pick_agents({ n: 3, min_distinct_providers: 3, exclude_ids: [primary] })
46
- outs = Promise.all(ids.map(id => dispatch({ agent_id: id, prompt })))
47
- // three distinct-provider verdicts, ~$0, in one wall-clock round.
48
- ```
49
-
50
- **Mrrlin's consensus gate does exactly this.** Every design/spec and every PR diff goes through a 4-reviewer panel — GPT + Gemini (over MCP) + two dynamic terminal reviewers resolved from this pool (`external-agents pick --n 2 --min-distinct-providers 2 --exclude-providers openai,google`). The primary coding agent commits a blind verdict first, then adjudicates the panel. Free-tier terminals mean the gate is essentially free to run on every substantial change.
51
-
52
- You don't need Mrrlin's gate to use the pattern — the primitives are unopinionated. Build your own reviewer panel, self-consistency check, jury-of-N verifier, whatever fits.
12
+ Your Google + Groq + Cerebras + OpenRouter + Z.ai + Ollama Cloud free tiers all have **separate quota buckets**. `external-agents` treats them as one pool: round-robin dispatch, cooldown-aware, auto-fallback on 429. Same agentic workload that used to cost $10-100/day on one paid model runs at effectively $0. Also the perfect substrate for [LLM-Council](https://github.com/karpathy/llm-council)-style multi-model panels `pick_agents` gives you N distinct-provider picks in one call.
53
13
 
54
14
  ---
55
15
 
56
- ## Install
57
-
58
- ### One-command install
16
+ ## 🚀 2-minute setup
59
17
 
60
18
  ```bash
61
19
  curl -fsSL https://raw.githubusercontent.com/mrrlin-dev/external-agents/main/install.sh | bash
62
20
  ```
63
21
 
64
- The script (a) `npm i -g`s the package, (b) registers the MCP server with every supported host on your PATH (Claude Code, Codex missing hosts are skipped, not fatal), then (c) launches the local dashboard so you can paste free-tier API keys inline. Read [`install.sh`](./install.sh) before piping if that flow makes you nervous — it's ~35 lines.
65
-
66
- ### Manual
22
+ That's it. The script installs the package, registers the MCP server with Claude Code + Codex (whichever you have), and opens the local dashboard so you can paste free-tier API keys inline:
67
23
 
68
- ```bash
69
- npm install -g @mrrlin-dev/external-agents
70
- external-agents init # opens http://127.0.0.1:4711 in your browser
71
- ```
24
+ ![paste-and-save walkthrough — banner → password input → Save → confirmation](docs/ui-walkthrough.gif)
72
25
 
73
- Requires Node 20. Works on macOS and Linux. Windows via WSL.
26
+ Sign up (60 sec, usually no card), paste, Save, restart your MCP client. Done. **Every new key adds a provider to the round-robin pool.**
74
27
 
75
- ---
76
-
77
- ## Two-minute quickstart
28
+ <details>
29
+ <summary>Or wire it up manually (three commands)</summary>
78
30
 
79
31
  ```bash
80
- # 1. See what's available (starts with zero-config defaults)
81
- external-agents status
82
-
83
- # 2. Wire an API-key provider (e.g. DeepSeek — sets DEEPSEEK_API_KEY for aider)
84
- external-agents auth deepseek
85
-
86
- # 3. Dispatch something (auto-pick + run)
87
- id=$(external-agents pick -n 1)
88
- external-agents dispatch "$id" "Summarize this in one line: <paste any text>"
89
-
90
- # 4. See what just happened
91
- external-agents stats
92
- ```
93
-
94
- Open the local dashboard with `external-agents ui` (see the [UI section](#local-ui) below).
95
-
96
- ---
97
-
98
- ## Wire into your MCP client
99
-
100
- Once installed, add one block to your MCP client config.
101
-
102
- The package ships a dedicated `external-agents-mcp` binary — the MCP server entry — so client configs are plain command lines with no args. **Both Claude Code and Codex expose a one-liner** for this; you should never have to hand-edit config files unless you want to.
103
-
104
- **How the client finds it.** `npm i -g @mrrlin-dev/external-agents` puts two symlinks on your global bin directory (`/opt/homebrew/bin`, `/usr/local/bin`, or wherever your Node global-bin lives) — `external-agents` (the CLI) and `external-agents-mcp` (the MCP server). Because that directory is on your `PATH`, running `external-agents-mcp` from any shell just works. `claude mcp add external-agents external-agents-mcp` stores the literal string `external-agents-mcp` in `~/.claude.json`; when Claude Code starts, it spawns that as a child process the same way your shell would, and shell PATH resolution finds the binary. No hosting, no registry lookup, no daemon. If you skipped the `npm i -g` step, `claude mcp add` succeeds but the server fails at startup with "command not found" — install first.
105
-
106
- ### Claude Code
32
+ npm install -g @mrrlin-dev/external-agents
107
33
 
108
- ```bash
34
+ # Register with whichever host(s) you use
109
35
  claude mcp add external-agents external-agents-mcp
110
- ```
111
-
112
- (Uses the native `claude mcp add <name> <command>` — the CLI writes the block into `~/.claude.json` for you and Claude Code picks it up on next start.)
113
-
114
- ### Codex
36
+ codex mcp add external-agents -- external-agents-mcp
115
37
 
116
- ```bash
117
- codex mcp add external-agents -- external-agents-mcp
38
+ # Set up keys
39
+ external-agents init # opens http://127.0.0.1:4711
118
40
  ```
119
41
 
120
- (Uses the native `codex mcp add <name> -- <command>` the CLI writes `[mcp_servers.external-agents]` into `~/.codex/config.toml`.)
121
-
122
- ### Cursor
123
-
124
- Settings → MCP → Add server → command `external-agents-mcp`, no args.
42
+ Requires Node 20. Works on macOS and Linux; Windows via WSL.
125
43
 
126
- ### Manual (any other client)
127
-
128
- If your MCP client doesn't have a one-liner, the config block is:
129
-
130
- ```json
131
- {
132
- "mcpServers": {
133
- "external-agents": {
134
- "command": "external-agents-mcp",
135
- "args": []
136
- }
137
- }
138
- }
139
- ```
140
-
141
- or TOML equivalent:
142
-
143
- ```toml
144
- [mcp_servers.external-agents]
145
- command = "external-agents-mcp"
146
- args = []
147
- ```
148
-
149
- No publishing step — the MCP server is just `external-agents-mcp` on your PATH once you `npm i -g @mrrlin-dev/external-agents`. Nothing to host, nothing to expose over the network; it's a stdio-transport MCP server that runs locally, same as any other npm-installed CLI.
150
-
151
- Your primary agent now has these low-level tools (build your own exec/review flows on top):
152
-
153
- | Tool | What it does |
154
- | --- | --- |
155
- | `pick_agents` | Pick N distinct healthy candidates (round-robin, cooldown-aware). Optional `min_distinct_providers` for diverse fan-out. |
156
- | `dispatch` | Run a specific agent with a prompt. `escalate_to_pro: true` retries on the same provider's strong tier. |
157
- | `probe_agent` | Health-check one provider (cheap prompt). |
158
- | `list_agents` | Registry with live state. |
159
- | `get_state` | Full state file — for building your own dashboard. |
160
- | `get_stats` | Aggregated dispatch metrics from local JSONL. |
161
- | `set_credential` | Store a provider credential via the entry's auth surface. |
162
-
163
- **Composition example** — build "review by a panel of 3 diverse providers" client-side:
164
- ```
165
- ids = pick_agents({ n: 3, min_distinct_providers: 3, exclude_ids: [primary] })
166
- outs = Promise.all(ids.map(id => dispatch({ agent_id: id, prompt })))
167
- ```
168
- The package is deliberately unopinionated about *what* you compose. Mrrlin uses these primitives for its own consensus gate and atomic-executor loop; your workflow probably has its own vocabulary — that's the whole point.
44
+ </details>
169
45
 
170
46
  ---
171
47
 
172
- ## Supported providers (v0.1.0)
173
-
174
- ### Free tier — no card required to get started
175
-
176
- Most of the pool runs on generous free tiers. **20 agents across 8 providers, 17 of which cost $0** when the request is on the provider's free plan (all get their own quota bucket, so round-robin dramatically extends what you can do without paying).
48
+ ## What you get
177
49
 
178
- | Provider | Models | Free tier |
179
- | --- | --- | --- |
180
- | **Google Gemini** (via AI Studio) | 3.5-flash-lite, 3.1-flash-lite, 3.6-flash, 3.5-flash, 3-flash-preview, 3/3.1-pro-preview | generous per-model quotas |
181
- | **Groq** ⚡ | llama-3.3-70b-versatile, deepseek-r1-distill-llama-70b | ✅ 30 rpm — fastest inference on the market (~500-800 tok/s) |
182
- | **OpenRouter** 🌐 | 50+ models tagged `:free` (deepseek-r1, qwen-coder-32b, llama-3.3-70b, ...) | ✅ 20 rpd free without card |
183
- | **Cerebras** ⚡⚡ | llama3.3-70b, qwen-3-coder-480b | ✅ 30 rpm — ~2000 tok/s (fastest on the planet) |
184
- | **Z.ai** (GLM) | glm-4.7-flash | ✅ free API tier |
185
- | **Ollama Cloud** | gpt-oss:20b-cloud, gpt-oss:120b-cloud | ✅ free with Ollama account |
50
+ - **`dispatch(agent_id, prompt)`** an MCP tool your primary agent calls. Auto-picks a healthy provider, retries on a different one if the first is rate-limited, honors the provider's own reset time (not a made-up 1h default).
51
+ - **`pick_agents(n, min_distinct_providers)`** the primitive for multi-model panels. Fan out 2-4 distinct-provider votes in parallel for jury-style review, self-consistency checks, or your own consensus loop.
52
+ - **Local dashboard** `external-agents init` opens a loopback page where you paste keys inline, see live provider state, and check usage. Loopback only, never over the network, keys stored at `~/.local/state/external-agents/keys.env` (mode 0600).
186
53
 
187
- ### Paid tier / per-token
188
-
189
- | Provider | Models | Notes |
190
- | --- | --- | --- |
191
- | **DeepSeek** (direct API) | deepseek-chat (Flash), deepseek-reasoner | Very cheap per-token; not on free tier but often <$1/day for atomic-task workloads |
192
- | **Codex** (subscription) | gpt-5.2-codex (primary orchestrator) | Uses your Codex Code subscription; not an API key |
193
-
194
- ### Direct-CLI agentic (repo-aware reviewers)
195
-
196
- - cursor-agent, opencode, Ollama (local, no quota)
197
-
198
- Missing a provider? Adding one is a ~15-line YAML addition — see [docs/adding-a-provider.md](docs/adding-a-provider.md). aider (our downstream) supports 100+ providers via LiteLLM.
54
+ Your primary agent (Claude Code, Codex, Cursor) gets these as MCP tools automatically after the setup script above.
199
55
 
200
56
  ---
201
57
 
202
- ## Config reference
203
-
204
- Registry file at `~/.config/external-agents/agents.yaml`:
205
-
206
- ```yaml
207
- runtime:
208
- primary_agent: codex # ignored when there's no orchestrator concept
209
- review_diversity:
210
- min_distinct_providers: 3
211
- max_rounds_default: 3
212
-
213
- telemetry_sink: # optional: pipe events to your own endpoint
214
- url: https://your-backend.example/telemetry
215
- headers:
216
- Authorization: "Bearer {ENV_TOKEN}"
217
- batch_size: 50
218
-
219
- agents:
220
- aider-gemini-flash:
221
- provider: google
222
- model: gemini-3-flash-preview
223
- tier: weak
224
- auth: "env:GEMINI_API_KEY"
225
- transports:
226
- generate_new:
227
- url: "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions"
228
- env: GEMINI_API_KEY
229
- model: gemini-3-flash-preview
230
- edit_exists: "aider --model gemini/gemini-3-flash-preview"
231
- # ... more entries
232
- ```
233
-
234
- Full schema documented in [docs/registry-schema.md](docs/registry-schema.md).
235
-
236
- ---
237
-
238
- ## Local UI — setting up API keys the easy way
239
-
240
- ```bash
241
- external-agents init # launches UI + opens the browser (recommended for first-time setup)
242
- # or
243
- external-agents ui # launches UI only; open http://127.0.0.1:4711 yourself
244
- ```
58
+ ## Providers in the pool (out of the box, 25 agents)
245
59
 
246
- ![external-agents dashboard walkthrough — banner shows 3 free-tier providers waiting; paste key, click Save, get confirmation](docs/ui-walkthrough.gif)
60
+ | | | |
61
+ |---|---|---|
62
+ | **Gemini** (Google AI Studio) | Groq | Cerebras |
63
+ | 7 model variants, per-model quota | 30 rpm, ~500-800 tok/s | 30 rpm, ~2000 tok/s |
64
+ | **OpenRouter** :free | Z.ai (GLM) | Ollama Cloud |
65
+ | 50+ models, 20 rpd, no card | GLM-4.7-flash free | gpt-oss 20B/120B |
66
+ | **DeepSeek** | Anthropic Claude | Codex |
67
+ | Cheap direct API | Subscription (Opus + Sonnet) | Subscription (GPT-5) |
68
+ | **cursor-agent** | **opencode** | **kiro-cli** |
69
+ | CLI agentic reviewer | CLI agentic reviewer | AWS Kiro headless |
247
70
 
248
- Loopback dashboard (never exposed to the network). Two things you want it for:
71
+ Missing a provider? [Suggest it](https://github.com/mrrlin-dev/external-agents/issues/new?labels=missing-model) the built-in UI has a form that opens a pre-filled issue.
249
72
 
250
- ### 1) Golden banner — sign up + paste key inline
73
+ ---
251
74
 
252
- For every free-tier provider that's currently `needs_auth` (no env var set), a golden row appears at the top with:
75
+ ## Mrrlin uses this
253
76
 
254
- - What the provider gives you ("Groq: ~500-800 tok/sfastest on the market"; "Cerebras: ~2000 tok/s, 30 rpm free"; "OpenRouter: 50+ models tagged `:free`"; …)
255
- - A green **"Get free key ↗"** link that opens the provider's signup page in a new tab. Signup is usually 60 seconds and does not ask for a card.
256
- - **A password input + Save button** — paste the key here and click Save. It persists to `~/.local/state/external-agents/keys.env` (mode 0600, loopback only, never sent anywhere). Enter also submits.
77
+ [Mrrlin](https://mrrlin.com) is the platform this was extracted from. Its consensus gate every design and every PR diff runs a 4-reviewer panel: GPT + Gemini over MCP + **two dynamic terminal reviewers pulled from this exact pool** every round. Free-tier terminals mean the gate is essentially free to run on every substantial change, and cross-model diversity beats any single-model reviewer.
257
78
 
258
- Once saved, restart your MCP client (Codex / Claude Code) so `external-agents-mcp` re-reads `keys.env` at startup. The provider drops from the banner and joins the pool.
79
+ You don't need Mrrlin's gate to use the pattern. Build your own reviewer panel, self-consistency check, jury-of-N verifier the primitives are unopinionated.
259
80
 
260
- ### 2) Registry table + Missing-your-model form
81
+ ---
261
82
 
262
- - Every registry entry with a live status badge (healthy / needs_auth / quota_exhausted / rate_limited / not_installed)
263
- - Per-row Verify button (re-probes the entry) + Usage link (opens the provider's own billing dashboard for entries that publish one — Gemini, DeepSeek, Z.ai, Ollama Cloud, …)
264
- - "Missing your model?" form at the bottom — submits a pre-filled GitHub issue on [`mrrlin-dev/external-agents/issues`](https://github.com/mrrlin-dev/external-agents/issues) with label `missing-model`, so requests are visible + trackable (and also logged locally as backup)
83
+ ## FAQ
265
84
 
266
- ### CLI equivalent (if you prefer scripting)
85
+ <details>
86
+ <summary><b>Do you send my API keys anywhere?</b></summary>
267
87
 
268
- Every UI action has a CLI equivalent. To set a key without opening the browser:
88
+ No. Keys live in `~/.local/state/external-agents/keys.env` (mode 0600, loopback-set) and are read into the MCP server's env at startup. Subscription tokens live where the subscription CLI puts them (`codex login`, `claude login`). Nothing is ever transmitted by `external-agents` itself.
269
89
 
270
- ```bash
271
- # arg form (bash-history exposed — fine for scripts)
272
- external-agents set-credential CEREBRAS_API_KEY csk-…
90
+ </details>
273
91
 
274
- # stdin form (nothing in bash-history)
275
- pbpaste | external-agents set-credential CEREBRAS_API_KEY -
92
+ <details>
93
+ <summary><b>How does <code>claude mcp add</code> find <code>external-agents-mcp</code>?</b></summary>
276
94
 
277
- # interactive form (typed prompt)
278
- external-agents set-credential CEREBRAS_API_KEY
279
- ```
95
+ `npm i -g` puts a symlink to `external-agents-mcp` on your global bin dir (usually `/opt/homebrew/bin` on macOS, `/usr/local/bin` on Linux). That dir is on your `PATH`. `claude mcp add` writes the literal string `external-agents-mcp` into `~/.claude.json`; when Claude Code starts, it spawns that as a child process — shell PATH resolution finds the binary. No hosting, no daemon, no registry lookup.
280
96
 
281
- All three paths write to the same `~/.local/state/external-agents/keys.env` — pick whichever is comfortable.
97
+ </details>
282
98
 
283
- ### One-minute walkthrough
99
+ <details>
100
+ <summary><b>How does it handle 429s?</b></summary>
284
101
 
285
- ```
286
- 1. external-agents ui # opens http://127.0.0.1:4711
287
- 2. Golden banner shows 3 unlockable providers (Groq, OpenRouter, Cerebras)
288
- 3. Click "Get free key ↗" on Groq → console.groq.com opens, sign up, copy key
289
- 4. Paste key into the row's input, click Save → "✓ persisted to keys.env"
290
- 5. Repeat for OpenRouter, Cerebras
291
- 6. Restart your MCP client (Codex/Claude Code) — banner shrinks, pool grows
292
-
293
- Total time: ~3 minutes. Result: 7 more free-tier agents active in `pick`.
294
- ```
102
+ Every real call updates state from response headers and error signals. Cooldown honors the provider's own reset time (parsed from `x-ratelimit-reset-*`, `Retry-After`, and error bodies). If Google says "resets in 42h", we wait 42h — not a 1h fallback.
295
103
 
104
+ </details>
296
105
 
297
- ---
106
+ <details>
107
+ <summary><b>Can I use this with just a subscription (no API keys)?</b></summary>
298
108
 
299
- ## FAQ
109
+ Yes — Codex subscription and Claude subscription are registered as `cli:*` entries. Zero API-key setup for those cases. Free-tier providers stack on top.
300
110
 
301
- **Do you send my API keys anywhere?**
302
- No. API-key credentials live in env vars only (read by aider at spawn time). Subscription tokens live where the subscription CLI keeps them (`codex login`, `claude login`, ...). `external-agents` never persists or transmits your credentials.
111
+ </details>
303
112
 
304
- **Do you phone home?**
305
- Not by default. Telemetry writes to a local JSONL file. If you configure `telemetry_sink` in your registry, events go to *your* endpoint — no default sink.
113
+ <details>
114
+ <summary><b>Is Mrrlin required?</b></summary>
306
115
 
307
- **Is Mrrlin required?**
308
116
  No. `external-agents` is standalone. Mrrlin uses it internally, but the package works for anyone building a multi-model workflow.
309
117
 
310
- **Can I use this with just a subscription (no API keys)?**
311
- Yes — Codex subscription with `--model` overrides gives you access to cheaper models on the same plan. Same story with Claude when v0.2 lands. Zero API-key setup for those cases.
312
-
313
- **How do you avoid rate-limit surprises?**
314
- Every real call updates state from response headers and error signals. Cooldown honors the provider's own reset time (parsed from `x-ratelimit-reset-*`, `Retry-After`, and error bodies). If Google says "resets in 42h", we wait 42h — not a 1h default.
118
+ </details>
315
119
 
316
- **Why `aider` for API-key providers?**
317
- [aider](https://aider.chat) is a mature (4-year) agentic CLI that talks to 100+ providers directly through [LiteLLM](https://litellm.ai) — no gateway proxy. Every provider is just an env var (`GEMINI_API_KEY`, `DEEPSEEK_API_KEY`, `ANTHROPIC_API_KEY`, ...) and a `--model {provider}/{id}` flag. We layer state / round-robin / cooldown on top instead of writing our own provider client for every LLM ourselves.
120
+ <details>
121
+ <summary><b>What about adding a new provider?</b></summary>
318
122
 
319
- ---
123
+ ~15-line YAML addition — see [docs/adding-a-provider.md](docs/adding-a-provider.md). aider (used for `edit_exists` transport) supports 100+ providers via LiteLLM.
320
124
 
321
- ## Contributing
322
-
323
- Contributions welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
324
-
325
- We use MIT + DCO (sign-off in commits), no CLA. Bug reports and provider additions are especially appreciated.
326
-
327
- ---
328
-
329
- ## License
330
-
331
- MIT. See [LICENSE](LICENSE).
125
+ </details>
332
126
 
333
127
  ---
334
128
 
335
129
  ## About Mrrlin
336
130
 
337
- `external-agents` is one piece of [**Mrrlin**](https://mrrlin.com) — an AI orchestration platform for solo developers and small teams. Mrrlin uses this package internally to power its multi-model consensus gate and cost-efficient atomic executor, and layers on top of it a Director agent that decides what work to route where.
338
-
339
- If you like `external-agents`, you'll probably like the rest of Mrrlin.
340
-
341
- ---
131
+ `external-agents` is one piece of [**Mrrlin**](https://mrrlin.com) — an AI orchestration platform for solo developers and small teams. If you like this package, you'll probably like the rest of Mrrlin.
342
132
 
133
+ ## License
343
134
 
135
+ MIT. Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
package/docs/hero.png ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrrlin-dev/external-agents",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "One MCP server for every LLM you talk to \u2014 direct-API dispatcher across Gemini, DeepSeek, Groq, OpenRouter, Cerebras, and more. Part of mrrlin.com.",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/ui.js CHANGED
@@ -313,20 +313,28 @@ function renderUnlock(agents) {
313
313
  // Ollama-cloud uses its own CLI ('ollama' — no API key input needed),
314
314
  // so its row skips the input + Save and just shows the signup/download link.
315
315
  const hasEnvInput = !!m.env && !m.env.startsWith("(");
316
+ // Shared button metrics so Save + Get-free-key line up pixel-perfect.
317
+ // 32px total height (7px vertical padding + 14px font + border), 13px font,
318
+ // min-width so short-text buttons ("Save") do not feel dwarfed next to
319
+ // long-text ones ("Get free key ↗").
320
+ const btnStyle = 'display:inline-flex;align-items:center;justify-content:center;height:32px;box-sizing:border-box;padding:0 14px;min-width:110px;font-size:13px;font-weight:600;border-radius:4px;text-decoration:none;white-space:nowrap;border:1px solid transparent;cursor:pointer;';
321
+ const saveStyle = btnStyle + 'background:#4a90e2;color:#fff;border-color:#3878c0;';
322
+ const signupStyle = btnStyle + 'background:#4a8;color:#fff;';
323
+ const inputStyle = 'flex:1;min-width:180px;height:32px;box-sizing:border-box;padding:0 10px;border:1px solid #d4b96b;border-radius:4px;font:inherit;';
324
+
316
325
  const keyInput = hasEnvInput
317
326
  ? '<input id="k-' + m.env + '" type="password" placeholder="paste ' + m.env + ' here" ' +
318
- 'style="flex:1;min-width:180px;padding:5px 8px;border:1px solid #d4b96b;border-radius:4px;font:inherit;" ' +
319
- 'onkeydown="if(event.key===\\'Enter\\')saveKey(\\'' + m.env + '\\')">' +
320
- '<button class="primary" onclick="saveKey(\\'' + m.env + '\\')">Save</button>' +
327
+ 'style="' + inputStyle + '" onkeydown="if(event.key===\\'Enter\\')saveKey(\\'' + m.env + '\\')">' +
328
+ '<button onclick="saveKey(\\'' + m.env + '\\')" style="' + saveStyle + '">Save</button>' +
321
329
  '<span id="s-' + m.env + '" class="status" style="font-size:12px;"></span>'
322
330
  : '<span style="color:#8a7532;font-size:12px;">' + m.env + '</span>';
323
331
  return '<div class="row" style="display:grid;grid-template-columns:180px 1fr auto;gap:10px 16px;align-items:start;padding:12px 0;border-top:1px solid #e6d78e;">' +
324
332
  '<div><div class="prov">' + m.label + '</div>' +
325
333
  '<div style="font-size:11px;color:#8a7532">+' + count + ' model' + (count>1?"s":"") + ' waiting</div></div>' +
326
334
  '<div><div class="desc" style="margin-bottom:6px;">' + m.pitch + '</div>' +
327
- '<div style="display:flex;gap:6px;align-items:center;flex-wrap:wrap;">' + keyInput + '</div></div>' +
335
+ '<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">' + keyInput + '</div></div>' +
328
336
  '<a class="signup" href="' + m.signup + '" target="_blank" rel="noopener" ' +
329
- 'style="align-self:center;padding:6px 12px;background:#4a8;color:#fff;text-decoration:none;border-radius:4px;font-size:13px;white-space:nowrap;">Get free key ↗</a>' +
337
+ 'style="align-self:center;' + signupStyle + '">Get free key ↗</a>' +
330
338
  '</div>';
331
339
  }).join("");
332
340
  box.innerHTML =