@mrrlin-dev/external-agents 0.2.10 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,326 +1,135 @@
1
1
  # @mrrlin-dev/external-agents
2
2
 
3
- **Cut your LLM bill by 10-100x by fanning work across free tiers of a dozen providers.**
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
+ [![npm](https://img.shields.io/npm/v/@mrrlin-dev/external-agents?style=for-the-badge)](https://www.npmjs.com/package/@mrrlin-dev/external-agents)
4
7
 
5
- `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.
8
+ **Route work from your coding agent across 20+ free-tier LLMs. Cut your bill 10-100×.**
6
9
 
7
- **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.
10
+ ![architecture: primary agent external-agents six free-tier providers, one pool of tokens](docs/hero.png)
8
11
 
9
- **It's also the substrate for LLM-consensus** the "ask N different models the same question, adjudicate" pattern popularized in [LLM-as-a-Judge](https://arxiv.org/abs/2306.05685) work and echoed frequently by [Andrej Karpathy](https://x.com/karpathy) as an "LLM Council" idea: 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.
10
-
11
- 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.
12
-
13
- > **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.
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.
14
13
 
15
14
  ---
16
15
 
17
- ## Why this exists — the money argument first
18
-
19
- 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:
20
-
21
- - **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).
22
- - **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.
23
- - **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.
24
- - **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.
25
-
26
- 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.
27
-
28
- ### What else you get (the zoo-management layer)
16
+ ## 🚀 2-minute setup
29
17
 
30
- - **Unified dispatch.** `dispatch(agent_id, prompt)` runs a specific agent; `pick_agents` picks N healthy candidates by round-robin with cross-provider diversity guaranteed.
31
- - **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.
32
- - **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).
33
- - **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.
34
-
35
- ### LLM-consensus — the multi-model panel, made trivial
36
-
37
- 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 [LLM-as-a-Judge](https://arxiv.org/abs/2306.05685) benchmarks, "LLM Council" experiments, [self-consistency decoding](https://arxiv.org/abs/2203.11171), and Karpathy's [recurring observation](https://x.com/karpathy) that mixed panels are strong. But it works only if you can (a) reach N different providers cheaply and (b) fan out in parallel. `external-agents` gives you both:
38
-
39
- ```
40
- ids = pick_agents({ n: 3, min_distinct_providers: 3, exclude_ids: [primary] })
41
- outs = Promise.all(ids.map(id => dispatch({ agent_id: id, prompt })))
42
- // three distinct-provider verdicts, ~$0, in one wall-clock round.
18
+ ```bash
19
+ curl -fsSL https://raw.githubusercontent.com/mrrlin-dev/external-agents/main/install.sh | bash
43
20
  ```
44
21
 
45
- **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.
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:
46
23
 
47
- 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.
24
+ ![paste-and-save walkthroughbanner password input Save confirmation](docs/ui-walkthrough.gif)
48
25
 
49
- ---
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.**
50
27
 
51
- ## Install
28
+ <details>
29
+ <summary>Or wire it up manually (three commands)</summary>
52
30
 
53
31
  ```bash
54
32
  npm install -g @mrrlin-dev/external-agents
55
- external-agents --version
56
- ```
57
-
58
- Requires Node ≥ 20. Works on macOS and Linux. Windows via WSL.
59
-
60
- ---
61
-
62
- ## Two-minute quickstart
63
33
 
64
- ```bash
65
- # 1. See what's available (starts with zero-config defaults)
66
- external-agents status
67
-
68
- # 2. Wire an API-key provider (e.g. DeepSeek — sets DEEPSEEK_API_KEY for aider)
69
- external-agents auth deepseek
70
-
71
- # 3. Dispatch something (auto-pick + run)
72
- id=$(external-agents pick -n 1)
73
- external-agents dispatch "$id" "Summarize this in one line: <paste any text>"
74
-
75
- # 4. See what just happened
76
- external-agents stats
77
- ```
78
-
79
- Open the local dashboard with `external-agents ui` (see the [UI section](#local-ui) below).
80
-
81
- ---
82
-
83
- ## Wire into your MCP client
84
-
85
- Once installed, add one block to your MCP client config.
86
-
87
- 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.
88
-
89
- **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.
90
-
91
- ### Claude Code
92
-
93
- ```bash
34
+ # Register with whichever host(s) you use
94
35
  claude mcp add external-agents external-agents-mcp
95
- ```
36
+ codex mcp add external-agents -- external-agents-mcp
96
37
 
97
- (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.)
98
-
99
- ### Codex
100
-
101
- ```bash
102
- codex mcp add external-agents -- external-agents-mcp
38
+ # Set up keys
39
+ external-agents init # opens http://127.0.0.1:4711
103
40
  ```
104
41
 
105
- (Uses the native `codex mcp add <name> -- <command>` the CLI writes `[mcp_servers.external-agents]` into `~/.codex/config.toml`.)
106
-
107
- ### Cursor
108
-
109
- Settings → MCP → Add server → command `external-agents-mcp`, no args.
42
+ Requires Node 20. Works on macOS and Linux; Windows via WSL.
110
43
 
111
- ### Manual (any other client)
112
-
113
- If your MCP client doesn't have a one-liner, the config block is:
114
-
115
- ```json
116
- {
117
- "mcpServers": {
118
- "external-agents": {
119
- "command": "external-agents-mcp",
120
- "args": []
121
- }
122
- }
123
- }
124
- ```
125
-
126
- or TOML equivalent:
127
-
128
- ```toml
129
- [mcp_servers.external-agents]
130
- command = "external-agents-mcp"
131
- args = []
132
- ```
133
-
134
- 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.
135
-
136
- Your primary agent now has these low-level tools (build your own exec/review flows on top):
137
-
138
- | Tool | What it does |
139
- | --- | --- |
140
- | `pick_agents` | Pick N distinct healthy candidates (round-robin, cooldown-aware). Optional `min_distinct_providers` for diverse fan-out. |
141
- | `dispatch` | Run a specific agent with a prompt. `escalate_to_pro: true` retries on the same provider's strong tier. |
142
- | `probe_agent` | Health-check one provider (cheap prompt). |
143
- | `list_agents` | Registry with live state. |
144
- | `get_state` | Full state file — for building your own dashboard. |
145
- | `get_stats` | Aggregated dispatch metrics from local JSONL. |
146
- | `set_credential` | Store a provider credential via the entry's auth surface. |
147
-
148
- **Composition example** — build "review by a panel of 3 diverse providers" client-side:
149
- ```
150
- ids = pick_agents({ n: 3, min_distinct_providers: 3, exclude_ids: [primary] })
151
- outs = Promise.all(ids.map(id => dispatch({ agent_id: id, prompt })))
152
- ```
153
- 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>
154
45
 
155
46
  ---
156
47
 
157
- ## Supported providers (v0.1.0)
158
-
159
- ### Free tier — no card required to get started
160
-
161
- 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).
162
-
163
- | Provider | Models | Free tier |
164
- | --- | --- | --- |
165
- | **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 |
166
- | **Groq** ⚡ | llama-3.3-70b-versatile, deepseek-r1-distill-llama-70b | ✅ 30 rpm — fastest inference on the market (~500-800 tok/s) |
167
- | **OpenRouter** 🌐 | 50+ models tagged `:free` (deepseek-r1, qwen-coder-32b, llama-3.3-70b, ...) | ✅ 20 rpd free without card |
168
- | **Cerebras** ⚡⚡ | llama3.3-70b, qwen-3-coder-480b | ✅ 30 rpm — ~2000 tok/s (fastest on the planet) |
169
- | **Z.ai** (GLM) | glm-4.7-flash | ✅ free API tier |
170
- | **Ollama Cloud** | gpt-oss:20b-cloud, gpt-oss:120b-cloud | ✅ free with Ollama account |
171
-
172
- ### Paid tier / per-token
48
+ ## What you get
173
49
 
174
- | Provider | Models | Notes |
175
- | --- | --- | --- |
176
- | **DeepSeek** (direct API) | deepseek-chat (Flash), deepseek-reasoner | Very cheap per-token; not on free tier but often <$1/day for atomic-task workloads |
177
- | **Codex** (subscription) | gpt-5.2-codex (primary orchestrator) | Uses your Codex Code subscription; not an API key |
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).
178
53
 
179
- ### Direct-CLI agentic (repo-aware reviewers)
180
-
181
- - cursor-agent, opencode, Ollama (local, no quota)
182
-
183
- 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.
184
-
185
- ---
186
-
187
- ## Config reference
188
-
189
- Registry file at `~/.config/external-agents/agents.yaml`:
190
-
191
- ```yaml
192
- runtime:
193
- primary_agent: codex # ignored when there's no orchestrator concept
194
- review_diversity:
195
- min_distinct_providers: 3
196
- max_rounds_default: 3
197
-
198
- telemetry_sink: # optional: pipe events to your own endpoint
199
- url: https://your-backend.example/telemetry
200
- headers:
201
- Authorization: "Bearer {ENV_TOKEN}"
202
- batch_size: 50
203
-
204
- agents:
205
- aider-gemini-flash:
206
- provider: google
207
- model: gemini-3-flash-preview
208
- tier: weak
209
- auth: "env:GEMINI_API_KEY"
210
- transports:
211
- generate_new:
212
- url: "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions"
213
- env: GEMINI_API_KEY
214
- model: gemini-3-flash-preview
215
- edit_exists: "aider --model gemini/gemini-3-flash-preview"
216
- # ... more entries
217
- ```
218
-
219
- Full schema documented in [docs/registry-schema.md](docs/registry-schema.md).
54
+ Your primary agent (Claude Code, Codex, Cursor) gets these as MCP tools automatically after the setup script above.
220
55
 
221
56
  ---
222
57
 
223
- ## Local UI setting up API keys the easy way
58
+ ## Providers in the pool (out of the box, 25 agents)
224
59
 
225
- ```bash
226
- external-agents ui
227
- # external-agents ui: http://127.0.0.1:4711
228
- ```
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 |
229
70
 
230
- 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.
231
72
 
232
- ### 1) Golden banner — sign up + paste key inline
73
+ ---
233
74
 
234
- 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
235
76
 
236
- - 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`"; …)
237
- - 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.
238
- - **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.
239
78
 
240
- 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.
241
80
 
242
- ### 2) Registry table + Missing-your-model form
81
+ ---
243
82
 
244
- - Every registry entry with a live status badge (healthy / needs_auth / quota_exhausted / rate_limited / not_installed)
245
- - 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, …)
246
- - "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
247
84
 
248
- ### CLI equivalent (if you prefer scripting)
85
+ <details>
86
+ <summary><b>Do you send my API keys anywhere?</b></summary>
249
87
 
250
- 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.
251
89
 
252
- ```bash
253
- # arg form (bash-history exposed — fine for scripts)
254
- external-agents set-credential CEREBRAS_API_KEY csk-…
90
+ </details>
255
91
 
256
- # stdin form (nothing in bash-history)
257
- 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>
258
94
 
259
- # interactive form (typed prompt)
260
- external-agents set-credential CEREBRAS_API_KEY
261
- ```
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.
262
96
 
263
- All three paths write to the same `~/.local/state/external-agents/keys.env` — pick whichever is comfortable.
97
+ </details>
264
98
 
265
- ### One-minute walkthrough
99
+ <details>
100
+ <summary><b>How does it handle 429s?</b></summary>
266
101
 
267
- ```
268
- 1. external-agents ui # opens http://127.0.0.1:4711
269
- 2. Golden banner shows 3 unlockable providers (Groq, OpenRouter, Cerebras)
270
- 3. Click "Get free key ↗" on Groq → console.groq.com opens, sign up, copy key
271
- 4. Paste key into the row's input, click Save → "✓ persisted to keys.env"
272
- 5. Repeat for OpenRouter, Cerebras
273
- 6. Restart your MCP client (Codex/Claude Code) — banner shrinks, pool grows
274
-
275
- Total time: ~3 minutes. Result: 7 more free-tier agents active in `pick`.
276
- ```
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.
277
103
 
278
- _(TODO: an animated GIF walkthrough belongs here — see [issue #<TBD>](https://github.com/mrrlin-dev/external-agents/issues) for the recording task.)_
104
+ </details>
279
105
 
280
- ---
106
+ <details>
107
+ <summary><b>Can I use this with just a subscription (no API keys)?</b></summary>
281
108
 
282
- ## 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.
283
110
 
284
- **Do you send my API keys anywhere?**
285
- 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>
286
112
 
287
- **Do you phone home?**
288
- 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>
289
115
 
290
- **Is Mrrlin required?**
291
116
  No. `external-agents` is standalone. Mrrlin uses it internally, but the package works for anyone building a multi-model workflow.
292
117
 
293
- **Can I use this with just a subscription (no API keys)?**
294
- 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.
295
-
296
- **How do you avoid rate-limit surprises?**
297
- 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>
298
119
 
299
- **Why `aider` for API-key providers?**
300
- [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.
301
-
302
- ---
120
+ <details>
121
+ <summary><b>What about adding a new provider?</b></summary>
303
122
 
304
- ## Contributing
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.
305
124
 
306
- Contributions welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
307
-
308
- We use MIT + DCO (sign-off in commits), no CLA. Bug reports and provider additions are especially appreciated.
309
-
310
- ---
311
-
312
- ## License
313
-
314
- MIT. See [LICENSE](LICENSE).
125
+ </details>
315
126
 
316
127
  ---
317
128
 
318
129
  ## About Mrrlin
319
130
 
320
- `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.
321
-
322
- If you like `external-agents`, you'll probably like the rest of Mrrlin.
323
-
324
- ---
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.
325
132
 
133
+ ## License
326
134
 
135
+ MIT. Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
package/cli.js CHANGED
@@ -208,6 +208,35 @@ async function cmdSetCredential(args) {
208
208
  }
209
209
  }
210
210
 
211
+ // `external-agents init` — one-shot setup: launch the UI AND open the default
212
+ // browser to it. Meant for the "just installed the package, what now" moment.
213
+ // The UI process stays foregrounded (Ctrl-C to quit) so the operator can watch
214
+ // key-save events land in stderr.
215
+ function cmdInit(flags) {
216
+ const port = Number(flags.port) || 4711;
217
+ const host = String(flags.host || "127.0.0.1");
218
+ const url = `http://${host}:${port}/`;
219
+ const opener =
220
+ process.platform === "darwin" ? "open" :
221
+ process.platform === "win32" ? "cmd" :
222
+ "xdg-open";
223
+ const openerArgs = process.platform === "win32" ? ["/c", "start", "", url] : [url];
224
+ // Spawn UI first, then open browser after it starts listening.
225
+ const uiPath = path.join(path.dirname(new URL(import.meta.url).pathname), "ui.js");
226
+ const env = { ...process.env, EXTERNAL_AGENTS_UI_PORT: String(port), EXTERNAL_AGENTS_UI_HOST: host };
227
+ const child = spawn(process.execPath, [uiPath], { stdio: "inherit", env });
228
+ // Give the UI ~600ms to bind before opening the browser (loopback listen is
229
+ // usually instantaneous but we do not want the browser to open on a not-yet-
230
+ // bound port).
231
+ setTimeout(() => {
232
+ try { spawn(opener, openerArgs, { stdio: "ignore", detached: true }).unref(); }
233
+ catch { /* browser open is best-effort; UI is still up on ${url} */ }
234
+ }, 600);
235
+ child.on("exit", (code) => process.exit(code ?? 0));
236
+ process.on("SIGINT", () => child.kill("SIGINT"));
237
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
238
+ }
239
+
211
240
  // `external-agents ui` — spawn the loopback dashboard (ui.js) inline so the CLI
212
241
  // stays the single entry point. ui.js runs its server at top level and blocks;
213
242
  // we spawn it as a child so cli.js does not need to import server-lifecycle code
@@ -234,6 +263,7 @@ switch (subcmd) {
234
263
  case "probe": cmdProbe(args); break;
235
264
  case "stats": cmdStats(flags); break;
236
265
  case "ui": cmdUi(flags); break;
266
+ case "init": cmdInit(flags); break;
237
267
  case "set-credential": await cmdSetCredential(args); break;
238
268
  case "help":
239
269
  case "--help":
@@ -245,6 +275,7 @@ switch (subcmd) {
245
275
  probe <agent-id>
246
276
  stats [--since ISO] [--json]
247
277
  ui [--port N] [--host H] # local dashboard for setting keys + inspecting state (default http://127.0.0.1:4711)
278
+ init [--port N] [--host H] # launch UI AND open it in the default browser — the "just installed" one-shot
248
279
  set-credential <ENV_NAME> [<value> | -] # persist a key to ~/.local/state/external-agents/keys.env (0600); '-' or omitted = read from stdin`);
249
280
  process.exit(subcmd ? 0 : 2);
250
281
  default: die(`unknown subcommand: ${subcmd}`, 2);
Binary file
Binary file
Binary file
package/docs/hero.png ADDED
Binary file
Binary file
package/install.sh ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bash
2
+ # @mrrlin-dev/external-agents — one-command installer.
3
+ #
4
+ # What it does:
5
+ # 1. Installs the package globally via `npm i -g @mrrlin-dev/external-agents`.
6
+ # 2. Registers the MCP server with EVERY supported host that is on your PATH
7
+ # (Claude Code + Codex). Missing hosts are skipped, not fatal.
8
+ # 3. Launches `external-agents init` — brings the local dashboard up on
9
+ # http://127.0.0.1:4711 and opens it in your default browser so you can
10
+ # paste API keys for the free-tier providers (Groq, Cerebras, OpenRouter).
11
+ #
12
+ # Usage:
13
+ # curl -fsSL https://raw.githubusercontent.com/mrrlin-dev/external-agents/main/install.sh | bash
14
+ #
15
+ # The script is idempotent: re-running it upgrades the npm package and re-runs
16
+ # the MCP-registration one-liners (both hosts' `mcp add` is a no-op when the
17
+ # entry already matches). Nothing is deleted, nothing is elevated.
18
+ set -euo pipefail
19
+
20
+ say() { printf '\033[1m→\033[0m %s\n' "$*" >&2; }
21
+ ok() { printf '\033[32m✓\033[0m %s\n' "$*" >&2; }
22
+ warn() { printf '\033[33m!\033[0m %s\n' "$*" >&2; }
23
+
24
+ # 1) npm install (global)
25
+ if ! command -v npm >/dev/null 2>&1; then
26
+ printf '\033[31m✗\033[0m npm is not on PATH. Install Node.js (>=20) first.\n' >&2
27
+ exit 1
28
+ fi
29
+
30
+ say "Installing @mrrlin-dev/external-agents globally via npm…"
31
+ npm install -g @mrrlin-dev/external-agents
32
+ ok "Installed. Binaries: external-agents, external-agents-mcp"
33
+
34
+ # 2) Register the MCP server with every host we can find. Errors are non-fatal
35
+ # because the operator may only use one of them.
36
+ if command -v claude >/dev/null 2>&1; then
37
+ say "Registering with Claude Code (claude mcp add)…"
38
+ claude mcp add external-agents external-agents-mcp 2>/dev/null && ok "Claude Code wired." || warn "claude mcp add returned non-zero (already registered? or CLI is too old). Manual: claude mcp add external-agents external-agents-mcp"
39
+ else
40
+ warn "Claude Code CLI not found on PATH — skipping. Run this yourself later: claude mcp add external-agents external-agents-mcp"
41
+ fi
42
+
43
+ if command -v codex >/dev/null 2>&1; then
44
+ say "Registering with Codex CLI (codex mcp add)…"
45
+ codex mcp add external-agents -- external-agents-mcp 2>/dev/null && ok "Codex CLI wired." || warn "codex mcp add returned non-zero (already registered? or CLI is too old). Manual: codex mcp add external-agents -- external-agents-mcp"
46
+ else
47
+ warn "Codex CLI not found on PATH — skipping. Run this yourself later: codex mcp add external-agents -- external-agents-mcp"
48
+ fi
49
+
50
+ # 3) Launch the dashboard. `init` foregrounds the UI process AND opens the
51
+ # browser; the operator hits Ctrl-C when done.
52
+ say "Launching the local dashboard — paste your free-tier API keys inline…"
53
+ say "(Press Ctrl-C to quit the dashboard once you are done.)"
54
+ exec external-agents init
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mrrlin-dev/external-agents",
3
- "version": "0.2.10",
4
- "description": "One MCP server for every LLM you talk to direct-API dispatcher across Gemini, DeepSeek, Groq, OpenRouter, Cerebras, and more. Part of mrrlin.com.",
3
+ "version": "0.3.1",
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",
7
7
  "bin": {
@@ -11,7 +11,8 @@
11
11
  "scripts": {
12
12
  "start": "node server.js",
13
13
  "ui": "node ui.js",
14
- "cli": "node cli.js"
14
+ "cli": "node cli.js",
15
+ "postinstall": "node scripts/postinstall-banner.mjs || true"
15
16
  },
16
17
  "keywords": [
17
18
  "mcp",
@@ -49,7 +50,9 @@
49
50
  "agents.yaml",
50
51
  "docs/",
51
52
  "README.md",
52
- "LICENSE"
53
+ "LICENSE",
54
+ "scripts/",
55
+ "install.sh"
53
56
  ],
54
57
  "dependencies": {
55
58
  "@modelcontextprotocol/sdk": "latest",
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ // Prints a short banner on `npm i -g @mrrlin-dev/external-agents` completion.
3
+ // Skipped when npm runs in a CI or non-interactive context so we do not spam
4
+ // build logs. We stay well within npm etiquette — no auto-launch, no network
5
+ // calls, just a hint.
6
+ if (process.env.CI || process.env.npm_config_production === "true") process.exit(0);
7
+ if (!process.stderr.isTTY) process.exit(0);
8
+
9
+ const G = "\x1b[32m"; // green
10
+ const B = "\x1b[1m"; // bold
11
+ const D = "\x1b[2m"; // dim
12
+ const R = "\x1b[0m"; // reset
13
+
14
+ process.stderr.write(`
15
+ ${G}${B}✓ @mrrlin-dev/external-agents installed.${R}
16
+
17
+ ${B}Next step (one command):${R} ${G}external-agents init${R}
18
+ ${D}↳ opens the local dashboard on http://127.0.0.1:4711${R}
19
+ ${D}↳ paste API keys inline for Groq / Cerebras / OpenRouter (free tiers)${R}
20
+ ${D}↳ each provider unlocks in ~30 seconds${R}
21
+
22
+ Wire the MCP server into your primary agent:
23
+ ${G}claude mcp add external-agents external-agents-mcp${R}
24
+ ${G}codex mcp add external-agents -- external-agents-mcp${R}
25
+
26
+ More at ${B}https://github.com/mrrlin-dev/external-agents${R}
27
+ `);