@mrrlin-dev/external-agents 0.2.10 → 0.3.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 +23 -6
- package/cli.js +31 -0
- package/docs/01-dashboard.png +0 -0
- package/docs/02-typed.png +0 -0
- package/docs/03-saved.png +0 -0
- package/docs/ui-walkthrough.gif +0 -0
- package/install.sh +54 -0
- package/package.json +7 -4
- package/scripts/postinstall-banner.mjs +27 -0
package/README.md
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# @mrrlin-dev/external-agents
|
|
2
2
|
|
|
3
|
+
[](#claude-code)
|
|
4
|
+
[](#codex)
|
|
5
|
+
[](#one-command-install)
|
|
6
|
+
[](https://www.npmjs.com/package/@mrrlin-dev/external-agents)
|
|
7
|
+
|
|
3
8
|
**Cut your LLM bill by 10-100x by fanning work across free tiers of a dozen providers.**
|
|
4
9
|
|
|
5
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.
|
|
6
11
|
|
|
7
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.
|
|
8
13
|
|
|
9
|
-
**It's also the substrate for LLM-consensus** — the "ask N different models the same question, adjudicate" pattern
|
|
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.
|
|
10
15
|
|
|
11
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.
|
|
12
17
|
|
|
@@ -34,7 +39,7 @@ The net effect for us has been **10-100x reduction** in per-task cost for the at
|
|
|
34
39
|
|
|
35
40
|
### LLM-consensus — the multi-model panel, made trivial
|
|
36
41
|
|
|
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,
|
|
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:
|
|
38
43
|
|
|
39
44
|
```
|
|
40
45
|
ids = pick_agents({ n: 3, min_distinct_providers: 3, exclude_ids: [primary] })
|
|
@@ -50,9 +55,19 @@ You don't need Mrrlin's gate to use the pattern — the primitives are unopinion
|
|
|
50
55
|
|
|
51
56
|
## Install
|
|
52
57
|
|
|
58
|
+
### One-command install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
curl -fsSL https://raw.githubusercontent.com/mrrlin-dev/external-agents/main/install.sh | bash
|
|
62
|
+
```
|
|
63
|
+
|
|
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
|
|
67
|
+
|
|
53
68
|
```bash
|
|
54
69
|
npm install -g @mrrlin-dev/external-agents
|
|
55
|
-
external-agents
|
|
70
|
+
external-agents init # opens http://127.0.0.1:4711 in your browser
|
|
56
71
|
```
|
|
57
72
|
|
|
58
73
|
Requires Node ≥ 20. Works on macOS and Linux. Windows via WSL.
|
|
@@ -223,10 +238,13 @@ Full schema documented in [docs/registry-schema.md](docs/registry-schema.md).
|
|
|
223
238
|
## Local UI — setting up API keys the easy way
|
|
224
239
|
|
|
225
240
|
```bash
|
|
226
|
-
external-agents
|
|
227
|
-
#
|
|
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
|
|
228
244
|
```
|
|
229
245
|
|
|
246
|
+

|
|
247
|
+
|
|
230
248
|
Loopback dashboard (never exposed to the network). Two things you want it for:
|
|
231
249
|
|
|
232
250
|
### 1) Golden banner — sign up + paste key inline
|
|
@@ -275,7 +293,6 @@ All three paths write to the same `~/.local/state/external-agents/keys.env` —
|
|
|
275
293
|
Total time: ~3 minutes. Result: 7 more free-tier agents active in `pick`.
|
|
276
294
|
```
|
|
277
295
|
|
|
278
|
-
_(TODO: an animated GIF walkthrough belongs here — see [issue #<TBD>](https://github.com/mrrlin-dev/external-agents/issues) for the recording task.)_
|
|
279
296
|
|
|
280
297
|
---
|
|
281
298
|
|
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
|
|
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.
|
|
4
|
-
"description": "One MCP server for every LLM you talk to
|
|
3
|
+
"version": "0.3.0",
|
|
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
|
+
`);
|