diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..56af1fd --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,18 @@ +# AGENTS.md — pool-anything + +## Standing rules (always apply) + +- **Landing page:** `https://pool-anything.pages.dev/` (Pages project `pool-anything`, source dir `landing/`). Deploy with `npx wrangler pages deploy landing --project-name pool-anything`. Pushes to `main` touching `landing/**` redeploy automatically via `pages.yml`. +- **GitHub repo:** `https://github.com/Parithosh-Varma/pool-anything`. Already authenticated — use `git`/`gh` directly, never ask for login. +- **Browser autonomy:** browser / webfetch / websearch use is allowed and encouraged. Research and configure external services end-to-end yourself (e.g. Search Console SEO) instead of asking the user to do it manually. + +## Full instructions + +Read `instructions.md` for the complete guide: install, `serve` flags, assistant shell, Web UI routes, REST + proxy usage, env config, security, troubleshooting. + +## Working conventions + +- Node >= 22.5 (`node:sqlite`, no DB to install). Server core is dependency-free (Node built-ins only); do not add runtime deps. +- `npm run typecheck` must pass (strict TS). `npm test` before finishing. `npm run dev` serves `http://localhost:3000`. +- Keys print masked only (`gsk_…ab`); never commit `data/pool-anything.db`; redact keys from logs/bug reports. +- Default bind `127.0.0.1` with no auth — set `POOL_API_TOKEN` if binding non-loopback. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..56af1fd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,18 @@ +# AGENTS.md — pool-anything + +## Standing rules (always apply) + +- **Landing page:** `https://pool-anything.pages.dev/` (Pages project `pool-anything`, source dir `landing/`). Deploy with `npx wrangler pages deploy landing --project-name pool-anything`. Pushes to `main` touching `landing/**` redeploy automatically via `pages.yml`. +- **GitHub repo:** `https://github.com/Parithosh-Varma/pool-anything`. Already authenticated — use `git`/`gh` directly, never ask for login. +- **Browser autonomy:** browser / webfetch / websearch use is allowed and encouraged. Research and configure external services end-to-end yourself (e.g. Search Console SEO) instead of asking the user to do it manually. + +## Full instructions + +Read `instructions.md` for the complete guide: install, `serve` flags, assistant shell, Web UI routes, REST + proxy usage, env config, security, troubleshooting. + +## Working conventions + +- Node >= 22.5 (`node:sqlite`, no DB to install). Server core is dependency-free (Node built-ins only); do not add runtime deps. +- `npm run typecheck` must pass (strict TS). `npm test` before finishing. `npm run dev` serves `http://localhost:3000`. +- Keys print masked only (`gsk_…ab`); never commit `data/pool-anything.db`; redact keys from logs/bug reports. +- Default bind `127.0.0.1` with no auth — set `POOL_API_TOKEN` if binding non-loopback. diff --git a/instructions.md b/instructions.md new file mode 100644 index 0000000..e8c1824 --- /dev/null +++ b/instructions.md @@ -0,0 +1,167 @@ +# pool-anything — Instructions + +Gather free-tier API keys into one pool. Rotate through them automatically via round-robin + drop-in HTTP proxy. + +## 0. Agent operating notes + +See `AGENTS.md` — it is the single source of truth for standing instructions (landing page deploys, GitHub auth, browser autonomy). Follow it before anything below. + +## 1. Prerequisites + +- Node.js >= 22.5 (uses built-in `node:sqlite`, no DB to install) +- A provider API key (e.g. Groq `gsk_…`, OpenRouter `sk-or-v1-…`, Gemini key) +- Optional: `curl` for API testing + +## 2. Install + +### Option A — Global (recommended) + +```bash +npm install -g pool-anything +pool-anything +``` + +Data lives in `~/.pool-anything/pool-anything.db` so it works from any directory and survives upgrades. + +### Option B — From source + +```bash +git clone https://github.com/Parithosh-Varma/pool-anything.git +cd pool-anything +npm install +npm run dev # watch mode, http://localhost:3000 +npm run db:init # init local SQLite (data/pool-anything.db) +``` + +| Script | Purpose | +| --- | --- | +| `npm run dev` | Watch mode server on `:3000` | +| `npm run build` + `npm run start:dist` | Compile to `dist/` and run it | +| `npm run typecheck` | `tsc --noEmit` (must pass) | +| `npm test` | `tsx --test "tests/**/*.test.ts"` | + +## 3. Start the server + +```bash +pool-anything serve --port 4000 --open # web UI + API on :4000, open browser +pool-anything --help # all flags +PORT=4000 pool-anything serve # env-var equivalent +``` + +Flags: `-p/--port`, `-H/--host` (default `127.0.0.1`), `--db `, `--data-dir `, `--open`. + +Running bare `pool-anything` (TTY) opens the interactive assistant shell instead of the server. + +## 4. Create a pool + add keys + +### Web UI (easiest) + +1. Open `http://localhost:3000`, search provider (try `groq`). +2. Paste one or more keys, hit **Gather**. +3. Pool is live. Manage at `/pools`, `/keys`, test at `/playground`, monitor at `/analytics`. + +Routes: `/` (search + dashboard), `/provider/:id`, `/pools`, `/keys`, `/playground`, `/analytics`. + +### Assistant shell + +No subcommands — plain language: + +| Say | Does | +| --- | --- | +| `list pools` / `list providers` | Show pools / 36 preconfigured providers | +| `create pool Prod Groq for groq` | New pool for provider `groq` | +| `add 5 keys to groq` | Paste keys, one per line, blank line finishes | +| `show usage for groq` | Tokens, quota, per-key breakdown | +| `next for groq` / `consume 100 on groq` | Rotate / record usage | +| `watch groq` | Live usage, 2s refresh (`q` exits) | +| `serve` | Start web UI + API from inside shell | +| `exit` | Quit | + +One-shot (no shell): + +```bash +pool-anything "list pools" +pool-anything -e "add keys to groq" +echo "gsk_abc" | pool-anything -e "add keys to groq" +``` + +Keys always print masked (`gsk_…ab`). + +### REST API + +```bash +# Create pool for groq +curl -X POST http://localhost:3000/api/pools \ + -H 'content-type: application/json' \ + -d '{"provider":"groq","name":"Prod Groq"}' # -> {"id":1,...} + +# Add a key to pool 1 +curl -X POST http://localhost:3000/api/pools/1/keys \ + -H 'content-type: application/json' \ + -d '{"label":"key 1","api_key":"gsk_abc"}' + +# Rotate (masked) / record usage / usage rollup +curl http://localhost:3000/api/pools/1/next +curl -X POST http://localhost:3000/api/pools/1/consume \ + -H 'content-type: application/json' -d '{"tokens":100}' +curl http://localhost:3000/api/pools/1/usage +``` + +## 5. Proxy requests through the pool + +Point your client at pool-anything; it injects the key + auth header, forwards to the provider `baseUrl`, records usage. + +```bash +curl -X POST http://localhost:3000/api/pools/1/proxy \ + -H 'content-type: application/json' \ + -d '{"path":"/chat/completions","method":"POST", + "body":{"model":"llama-3.3-70b","messages":[{"role":"user","content":"hi"}]}, + "tokens":25}' +``` + +Body fields: `path` (appended to provider `baseUrl`), `method` (default `POST`), `body` (forwarded as-is), `headers` (extra, override defaults), `tokens` (usage to record). + +Response reports who served it: + +```json +{"key_id":3,"label":"key 1","masked":"gsk_…ab","status":200,"body":"{ …first 4000 chars… }"} +``` + +Flow: `nextKeyRaw()` round-robin (`cursor % keys.length`) → inject key → forward → record per-key usage in SQLite. Recent attempts: `GET /api/pools/:id/calls?limit=50`. + +## 6. Configuration + +Copy `.env.example` to `.env`. All optional. + +| Variable | Default | Purpose | +| --- | --- | --- | +| `HOST` | `127.0.0.1` | Bind interface (keep loopback unless behind auth) | +| `PORT` | `3000` | Server port | +| `POOL_API_TOKEN` | _(unset)_ | Bearer gate for raw-key reads, writes, rotation, proxy. Send `Authorization: Bearer ` | +| `ALLOWED_ORIGINS` | Pages hosts | CORS allowlist (localhost always allowed) | +| `LOCAL_DB_PATH` / `--db` | `data/pool-anything.db` or `~/.pool-anything/*.db` | Exact SQLite file | +| `POOL_DATA_DIR` / `--data-dir` | _(unset)_ | Dir holding `pool-anything.db` | +| `PROXY_TIMEOUT_MS` | `30000` | Upstream timeout per attempt | +| `POOL_DEBUG` | _(unset)_ | `1` = proxy trace to stderr (no bodies/keys) | +| `ALLOW_PRIVATE_UPSTREAM` | _(unset)_ | `1` = disable SSRF guard. Tests/loopback only | + +Providers: `data/providers.json` (36 preconfigured: Groq, OpenRouter, Gemini, OpenAI, Anthropic, …). Add one with a single JSON entry — see `CONTRIBUTING.md#adding-a-provider`. `keyHeader` can be a header, `query:`, or custom header; `keyPrefix` handles `Bearer `/`Token ` schemes. + +## 7. Security + +- DB file (`data/pool-anything.db`) is git-ignored — never commit it. +- List endpoints return masked keys only; raw key only from `GET /api/pools/:id/keys/:keyId`. +- Default bind is loopback with no auth. If you bind non-loopback or expose publicly, set `POOL_API_TOKEN` and keep it behind your own auth layer. + +## 8. Troubleshooting + +| Symptom | Fix | +| --- | --- | +| `Unknown option` / CLI confusion | `pool-anything --help`; server needs `serve` or a server flag (`-p`, `--db`, `--open`) | +| DB locked / wrong DB | Check `--db` / `LOCAL_DB_PATH` / `--data-dir`; global uses `~/.pool-anything/` | +| Proxy 4xx/5xx | Check `GET /api/pools/:id/calls` for upstream status/body; verify key + `path` + provider `baseUrl` | +| CORS blocked (Pages UI) | Set `ALLOWED_ORIGINS` to include your UI origin | +| Ink shell won't start | Falls back to plain REPL automatically; pipe mode uses `-e` | +| Type/test failure | `npm run typecheck`, `npm test`, Node >= 22.5 | + +Health checks: `GET /health` → `ok`, `GET /api/db/ping` → `{ok:true}`, `GET /api/analytics` → totals + 7-day deltas + 14-day series.