{
  "markdown": "# BrowserBash\n\n**Plain-English browser automation. No selectors. Free and open-source.**\n\n[![npm version](https://img.shields.io/npm/v/browserbash-cli)](https://www.npmjs.com/package/browserbash-cli)\n[![npm downloads](https://img.shields.io/npm/dm/browserbash-cli)](https://www.npmjs.com/package/browserbash-cli)\n[![license](https://img.shields.io/npm/l/browserbash-cli)](https://github.com/PramodDutta/browserbash)\n[![Node](https://img.shields.io/node/v/browserbash-cli)](https://nodejs.org)\n\n![BrowserBash demo](site/public/demo.gif)\n\nYou write a plain-English objective. An AI agent drives a **real Chrome browser** step by step — no selectors, no scripts. Ollama-first, so it runs on free local models with **no API keys** and nothing ever leaves your machine.\n\n```bash\nnpm install -g browserbash-cli\nbrowserbash run \"Open news.ycombinator.com and store the top story title as 'top_story'\"\n```\n\n**[Website](https://browserbash.com)** · **[Docs / Learn](https://browserbash.com/learn)** · **[Tutorials](https://browserbash.com/tutorials)** · **[npm](https://www.npmjs.com/package/browserbash-cli)**\n\n---\n\nBoth layers are swappable: the **engine** interprets the English, the **provider** runs the browser.\n\n## Engines (who interprets the English)\n\n| Engine | What it is | License |\n|---|---|---|\n| `stagehand` (default) | [Stagehand](https://www.stagehand.dev) — open-source AI browser automation framework by Browserbase. act/extract/observe/agent primitives, self-healing, supports Anthropic/OpenAI/Google models. | MIT |\n| `builtin` | In-repo Anthropic tool-use loop driving Playwright. Used automatically for grids Stagehand can't attach to (LambdaTest, BrowserStack). | Apache-2.0 |\n\n## Providers (where the browser runs)\n\n| Provider | Where the browser runs | Engine | Auth |\n|---|---|---|---|\n| `local` (default) | Chromium/Chrome on this machine | stagehand or builtin | none |\n| `cdp` | Any Chrome DevTools Protocol endpoint (your grid, docker, Playwright MCP-managed browser) | stagehand or builtin | none |\n| `browserbase` | Browserbase cloud browsers | stagehand only | `BROWSERBASE_API_KEY` / `BROWSERBASE_PROJECT_ID` |\n| `lambdatest` | LambdaTest / TestMu AI cloud grid | builtin (auto) | `LT_USERNAME` / `LT_ACCESS_KEY` |\n| `browserstack` | BrowserStack Automate cloud grid | builtin (auto) | `BROWSERSTACK_USERNAME` / `BROWSERSTACK_ACCESS_KEY` |\n\n## LLM backends (who does the thinking) — open source first\n\nDefault model is `auto`, resolved in this order:\n\n1. **Ollama running locally** → `ollama/<OLLAMA_MODEL or first installed model>` — free, open source, no keys\n2. `ANTHROPIC_API_KEY` set → `claude-opus-4-8`\n3. `OPENAI_API_KEY` set → `openai/gpt-4.1`\n4. otherwise: error with setup guidance\n\n| Backend | Model flag | Needs |\n| --- | --- | --- |\n| **Ollama — local, free, OSS (preferred)** | `auto` or `ollama/<model>` e.g. `ollama/qwen3` | Ollama running; `OLLAMA_BASE_URL` to override `http://localhost:11434/v1`, `OLLAMA_MODEL` to pin auto-detection. Same flag works for any OpenAI-compatible server (vLLM, LM Studio, llama.cpp). |\n| Anthropic | `claude-opus-4-8` | `ANTHROPIC_API_KEY` |\n| OpenAI / Google | `openai/gpt-4.1`, `google/gemini-2.5-flash` | provider key (Stagehand engine) |\n| **OpenRouter — hundreds of models, one key** | `openrouter/<vendor>/<model>` e.g. `openrouter/anthropic/claude-sonnet-4-6`, `openrouter/meta-llama/llama-3.3-70b-instruct` | `OPENROUTER_API_KEY` (https://openrouter.ai/keys); override endpoint with `OPENROUTER_BASE_URL` |\n| Anthropic-compatible gateway | `claude-*` + `ANTHROPIC_BASE_URL` | builtin engine routes through any Anthropic-compatible endpoint (e.g. a LiteLLM proxy fronting local models) |\n\n### Fully free / open-source stack (the default)\n\n```bash\nollama pull qwen3                 # or any tool-capable local model\nbrowserbash run \"Open https://example.com and store the heading as 'h1'\"\n```\n\nStagehand engine (MIT) + local Chromium + Ollama (MIT) — zero cloud cost, no API keys. Tip: small models (≤8B) are flaky on multi-step objectives; Qwen3 / Llama 3.3 70B class works best.\n\nNote: cloud-grid providers (`lambdatest`, `browserstack`) use the builtin engine, which speaks the Anthropic API — pair them with `ANTHROPIC_API_KEY` or an `ANTHROPIC_BASE_URL` gateway.\n\n## Install\n\n```bash\nnpm install\nnpm run build\nnpm link        # exposes the `browserbash` command\n```\n\nRequires Node ≥ 18 and Google Chrome stable (for the `local` provider).\n\n## Quick start\n\n```bash\nexport ANTHROPIC_API_KEY=sk-ant-...\n\n# One-shot objective, local browser, Stagehand engine (default)\nbrowserbash run \"Open https://news.ycombinator.com and store the top story title as 'top_story'\"\n\n# Browserbase cloud (Stagehand native)\nexport BROWSERBASE_API_KEY=... BROWSERBASE_PROJECT_ID=...\nbrowserbash run \"...\" --provider browserbase\n\n# Cloud grid (auto-switches to builtin engine)\nexport LT_USERNAME=... LT_ACCESS_KEY=...\nbrowserbash run \"...\" --provider lambdatest --headless\n\n# Attach to an existing browser (CDP / Playwright MCP)\nbrowserbash run \"...\" --cdp-endpoint ws://localhost:9222/devtools/browser/<id>\n\n# Force the builtin engine\nbrowserbash run \"...\" --engine builtin\n```\n\n## Agent mode (for AI coding tools & CI)\n\n`--agent` switches stdout to NDJSON — one JSON object per line, stable schema:\n\n```bash\nbrowserbash run \"<objective>\" --agent --headless --timeout 120\n```\n\n- Progress events: `{\"type\":\"step\",\"step\":1,\"status\":\"passed\",\"action\":\"navigate\",\"remark\":\"...\",\"cached\":false}`\n- Terminal event: `{\"type\":\"run_end\",\"status\":\"passed|failed|error|timeout\",\"summary\":\"...\",\"final_state\":{...},\"duration_ms\":...,\"provider\":\"local\",\"cache\":\"hit|miss|off\",\"tokens_in\":...,\"tokens_out\":...,\"test_url\":\"...\"}`\n\nExit codes: `0` passed · `1` failed · `2` error · `3` timeout. `cached`, `cache`, `tokens_in`/`tokens_out` are additive fields (present when relevant), so existing consumers are unaffected.\n\nFull agent integration guide: [docs/agents.md](docs/agents.md).\n\n## MCP server (agents consume BrowserBash natively)\n\nBrowserBash is a **validation layer for AI agents**: your coding agent builds a feature, BrowserBash proves it works in a real browser. One line plugs it into any MCP host:\n\n```bash\nclaude mcp add browserbash -- browserbash mcp     # Claude Code\n# Cursor / Windsurf / Zed / Codex: command \"browserbash\", args [\"mcp\"]\n```\n\nTools exposed: `run_objective` (one plain-English objective), `run_test_file` (a *_test.md), `run_suite` (a folder, parallel). Each returns the structured verdict JSON: `status`, `summary`, `final_state`, `assertions`, `cost_usd`, `duration_ms`. A failed test is a successful validation, so the tool call succeeds and the agent reads the verdict. No extra dependencies, stdio only, nothing leaves your machine.\n\n## Dashboards\n\nEvery run is kept in a private on-disk store (`~/.browserbash/runs`, secrets masked, capped at 200). Two ways to see them:\n\n**Local dashboard — free, no account, fully local:**\n\n```bash\nbrowserbash dashboard                 # serve http://localhost:4477 and open it\nbrowserbash run \"...\" --record --dashboard   # run, then open the dashboard on this run\nbrowserbash dashboard --clear         # wipe the local store\n```\n\nLeft panel lists your runs; the main pane shows the verdict, extracted values and the recording — with `--record` you get a screenshot plus a session video (stagehand engine, video needs `ffmpeg`, bundled) or a native Playwright trace you can open at trace.playwright.dev (builtin engine). Nothing leaves your machine.\n\n**Cloud dashboard — optional, opt-in per run:** a hosted dashboard at [browserbash.com/dashboard](https://browserbash.com/dashboard) with run history across machines and shareable per-run pages.\n\n```bash\nbrowserbash connect --key bb_...      # one-time, key from browserbash.com/dashboard\nbrowserbash run \"...\" --record --upload   # push THIS run (verdict + recording) to the cloud\n```\n\nWithout `--upload` nothing is sent to the cloud. BrowserBash is free and open source; cloud runs are kept 15 days.\n\n## Replay cache (warm runs skip the model)\n\nA green run records the actions it took. The next identical run **replays them with zero model calls**, and the agent only steps back in when the page actually changed. Steady-state suites run at close to script speed and cost.\n\n```bash\nbrowserbash testmd run ./checkout_test.md            # run 1: records the journal\nbrowserbash testmd run ./checkout_test.md            # run 2: replays, no model\nbrowserbash testmd run ./checkout_test.md --no-cache      # ignore the cache for this run\nbrowserbash testmd run ./checkout_test.md --refresh-cache # wipe this test's entry, re-record\n```\n\n`run_end.cache` reports `hit` / `miss` / `off`. On by default; `config set cache.enabled false` to disable, `cache.dir` to relocate (default `.browserbash/cache`, gitignored by `init`). Secrets never enter the cache: values arrive through the variables channel (Stagehand) or are re-templatized to `{{name}}` tokens (builtin), and any cached action that types a secret is origin-pinned — replaying it on a different origin fails closed. Builtin journals are also HMAC-signed with a per-machine key (`~/.browserbash/cache.key`); an edited or foreign journal is ignored and simply re-recorded. CI fleets that want to share committed caches can set the same `BROWSERBASH_CACHE_KEY` (64 hex chars) on every runner.\n\n## Parallel suites (`run-all`)\n\nRun a whole folder of `*_test.md` files at once with memory-aware scheduling:\n\n```bash\nbrowserbash run-all .browserbash/tests --concurrency 8 --junit out/junit.xml\n```\n\n- Concurrency is auto-derived from CPU **and** free memory (`min(requested, cpus, floor((mem - 2GB) / budget))`), so big suites do not thrash the machine. Override with `--concurrency`, tune the estimate with `--memory-budget <mb>`. A hard watchdog also kills any test whose whole process tree (Node + Chromium) exceeds `--memory-cap <mb>` (default 2x the budget, `0` disables); the test is reported as an infra error with a `test_kill` event, and retried per `--retries`.\n- Each test runs as an isolated child process with its own `Result.md`; a failure never leaks state to the next test.\n- `--retries <n>` retries infra errors only (not real failures), `--max-failures <n>` stops early, `--stagger <ms>` softens burst load.\n- Outputs: a merged NDJSON stream (`--events`, add `--agent` to also stream on stdout), JUnit XML (`--junit`), and a `RunAll-Result.md` with a flaky column.\n- Run history in `.browserbash/memory/history.json` orders the next run (previously-failed first, then slowest first) and flags flaky tests. `--no-memory` opts out.\n- **Sharding:** `--shard 2/4` runs a deterministic slice, computed on sorted discovery order so parallel CI machines agree without coordination.\n- **Viewport matrix:** `--matrix-viewport 1280x720,390x844` runs every test once per viewport; cells are labeled in events, JUnit and results. Single runs take `--viewport WxH` too.\n- **Budgets:** `--budget-usd 2.50` (or `--budget-tokens`) stops launching new tests once estimated spend crosses the budget; the rest are reported `skipped` and the suite exits `2`. Spend lands in `RunAll-Result.md` and JUnit `<properties>`.\n- **Webhooks:** `--notify <url>` POSTs the suite verdict when it ends (Slack URLs get Slack formatting).\n- Exit code: `0` all passed · `1` any failed · `2` infra error or budget stop · `3` suite timeout.\n\n## Cheap-model routing\n\nPlan on a strong model, execute on a cheap one, escalate back automatically after a failed step:\n\n```bash\nbrowserbash run \"...\" --model claude-opus-4-8 --model-exec claude-haiku-4-5\n```\n\n`run_end` reports `tokens_in` / `tokens_out` (builtin engine) so you can see what a run costs, plus a `cost_usd` estimate from a bundled per-model price table (override at `~/.browserbash/pricing.json`; unknown models get no estimate rather than a wrong one). Set persistently with `config set routing.executionModel <id>`.\n\n## Test files (`*_test.md`)\n\nCommittable, reviewable Markdown tests:\n\n```markdown\n# Login flow\n\n- Open {{base_url}}/login\n- Type {{username}} into the email field\n- Type {{password}} into the password field and press Enter\n- Verify the dashboard heading is visible\n- Store the logged-in user name as 'user_name'\n```\n\n```bash\nbrowserbash testmd run ./.browserbash/tests/login_test.md --provider browserstack\n```\n\nComposition via `@import ./helpers/login.md` (steps are spliced in place). After every run a `Result.md` is written next to the test file.\n\n### testmd v2: assertions and API steps that never lie\n\nAdd `version: 2` frontmatter and steps execute ONE AT A TIME against a single browser session, with two deterministic step types that never touch a model:\n\n```markdown\n---\nversion: 2\nauth: staging\n---\n# Checkout with seeded data\n\n- POST {{base_url}}/api/seed with body {\"sku\": \"tshirt-red\"}\n- Expect status 201, store $.order.id as 'order_id'\n- Open {{base_url}}/cart\n- Click the checkout button\n- Verify the URL contains 'checkout'\n- Verify the 'Thank you for your order!' heading is visible\n- Verify stored 'order_id' equals '{{expected_id}}'\n```\n\n- **API steps** (`GET/POST/PUT/DELETE/PATCH url [with body {...}]` + `Expect status N[, store $.path as 'name']`) run as plain HTTP: seed data, then verify through the UI. Stored values feed `{{variables}}` in later steps.\n- **`Verify` steps** compile to real Playwright checks (URL contains, title is/contains, text visible, `'name' button|link|heading` visible, element counts, stored equals). A pass means the condition held; a fail comes with expected vs actual evidence in `run_end.assertions` and the `Result.md` assertion table. Verify lines outside the grammar still run, agent-judged and flagged `judged: true`.\n- Consecutive plain-English steps run as grouped agent blocks on the same page, so login state and navigation carry through.\n- v1 files (no frontmatter) behave exactly as before. v2 currently drives the builtin engine (needs `ANTHROPIC_API_KEY` or an `ANTHROPIC_BASE_URL` gateway).\n\n## Variables\n\n`{{key}}` placeholders are substituted in objectives and test steps. Load order (highest priority last):\n\n1. Global: `~/.browserbash/variables/*.json`\n2. Project: `./.browserbash/variables/*.json`\n3. `--variables-file <path>`\n4. `--variables '<json>'`\n\nMark sensitive values `{\"value\": \"...\", \"secret\": true}` — they are masked as `*****` in all logs and NDJSON output.\n\n## Saved logins (`browserbash auth`)\n\nReal suites live behind a login. Log in once, reuse the session everywhere:\n\n```bash\nbrowserbash auth save staging --url https://app.example.com/login   # log in, press Enter\nbrowserbash run \"Open the dashboard and store the balance as 'balance'\" --auth staging\nbrowserbash run-all .browserbash/tests --auth staging               # every test, no re-login\nbrowserbash auth list && browserbash auth delete staging\n```\n\nSessions are Playwright storageState files in `~/.browserbash/auth/` (mode 0600, they hold live credentials). Test files can pin their own profile with `auth: staging` frontmatter. A profile whose saved origins do not cover the target URL prints a warning instead of silently doing nothing.\n\n## Author tests without writing them\n\n```bash\n# Record: click through the flow once in a real browser, get a test file\nbrowserbash record https://app.example.com --out .browserbash/tests/checkout_test.md\n\n# Import: convert an existing Playwright suite to plain English\nbrowserbash import ./e2e --out-dir .browserbash/imported\n```\n\n`record` captures clicks, typing and navigation (password values never leave the page; the generated step reads `Type {{password}} into ...`). `import` translates common Playwright calls deterministically and writes everything it could NOT translate to `IMPORT-REPORT.md` instead of guessing. Both outputs are starting points to review, not gospel.\n\n## Monitoring (`browserbash monitor`)\n\nThe same tests double as production checks:\n\n```bash\nbrowserbash monitor .browserbash/tests/checkout_test.md --every 10m \\\n  --notify https://hooks.slack.com/services/T0/B0/xyz\n```\n\nAlerts fire on pass<->fail STATE CHANGES only, both directions, never on every green run. Slack webhook URLs get Slack formatting; any other URL receives the raw JSON payload. With the replay cache warm, a monitor makes zero model calls until the page actually changes.\n\n## Configuration\n\n```bash\nbrowserbash init                          # scaffold ./.browserbash/\nbrowserbash config show\nbrowserbash config set defaultProvider lambdatest\nbrowserbash providers                     # list providers\nbrowserbash login --provider lambdatest --username \"$USER\" --access-key \"$KEY\"\nbrowserbash whoami\n```\n\nPrecedence: **flags > env vars > ~/.browserbash/config.json defaults**.\n\n## CI recipe (GitHub Actions)\n\n```yaml\n- run: npm ci && npm run build\n- run: |\n    node dist/index.js login --provider lambdatest --username \"$LT_USERNAME\" --access-key \"$LT_ACCESS_KEY\"\n    node dist/index.js testmd run .browserbash/tests/smoke_test.md --agent --headless --timeout 180\n  env:\n    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}\n    LT_USERNAME: ${{ secrets.LT_USERNAME }}\n    LT_ACCESS_KEY: ${{ secrets.LT_ACCESS_KEY }}\n```\n\nThe process exit code is the test verdict — no output parsing needed.\n\nOr use the official GitHub Action (PR verdict comment, artifacts, sharded matrix jobs, budget stop):\n\n```yaml\n- uses: PramodDutta/browserbash@main\n  with:\n    tests: .browserbash/tests\n    budget-usd: '2.00'\n  env:\n    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}\n```\n\nFull guide: [docs/github-action.md](docs/github-action.md).\n\n## Architecture\n\n```text\nsrc/\n├── index.ts            # CLI (commander): run, testmd, run-all, monitor, auth, record, import, mcp, ...\n├── runner.ts           # engine routing + provider session + replay cache + vendor status reporting\n├── engine/\n│   ├── stagehand.ts    # default engine: Stagehand agent (stagehand.dev, MIT) — LOCAL / cdpUrl / Browserbase\n│   ├── agent.ts        # builtin engine: Anthropic tool-use loop (manual loop → NDJSON step events)\n│   ├── tools.ts        # builtin browser tools: navigate, snapshot, click, type_text, wait_for, extract, done\n│   ├── assertions.ts   # deterministic Verify grammar + executor (no model in the loop)\n│   ├── replay.ts       # builtin replay-first cache: replay recorded actions, origin-pinned\n│   └── routing.ts      # per-model thinking config + cheap-exec model routing\n├── providers/          # vendor abstraction — add a new vendor by implementing BrowserProvider\n│   ├── types.ts        # BrowserProvider / ProviderSession + context options (auth, viewport)\n│   ├── local.ts        # system Chrome\n│   ├── cdp.ts          # attach to any CDP endpoint (incl. Playwright MCP browsers)\n│   ├── lambdatest.ts   # LambdaTest/TestMu grid + setTestStatus reporting\n│   └── browserstack.ts # BrowserStack Automate grid + setSessionStatus reporting\n├── orchestrator/       # run-all: memory-aware scheduler + child-process suite runner\n│   ├── scheduler.ts    # concurrency formula, admission watermark, shard/matrix, JUnit\n│   └── run-all.ts      # spawn children, aggregate NDJSON, verdicts, retries, budgets\n├── mcp/server.ts       # MCP stdio server: run_objective / run_test_file / run_suite\n├── testmd/             # parser (@import, frontmatter), v1 runner, v2 per-step runner + API steps\n├── import/playwright.ts# heuristic Playwright spec -> *_test.md converter\n├── record/             # interactive recorder: capture script + events -> English steps\n├── monitor.ts          # interval checks, state-change alerts\n├── auth-store.ts       # saved storageState profiles (~/.browserbash/auth)\n├── pricing.ts          # cost_usd estimates (overridable price table)\n├── notify.ts           # webhook payloads (Slack autodetect)\n├── cache-store.ts      # builtin action-journal cache (re-templatized, origin-pinned, HMAC-signed)\n├── memory-store.ts     # run history: ordering + flaky report\n├── config.ts           # ~/.browserbash/config.json + credential resolution\n├── variables.ts        # {{var}} substitution, secrets masking\n└── output.ts           # NDJSON / human reporter\n```\n\nAdding a vendor = one file implementing `BrowserProvider` (`connect()` returning a Playwright `Browser`/`Page`) + one registry line in `providers/index.ts`.\n\n## License\n\nApache-2.0\n",
  "bytes": 20236,
  "sha": "6a06b369d11fff084bd6939fe2a27e9363902d7b3d2e7f528c82dc7ae9d561f7",
  "repo_slug": "pramoddutta/browserbash",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_pramoddutta_browserbash_d0cca710/readme"
}