{"id":51449850,"url":"https://github.com/josharsh/pixelpi","last_synced_at":"2026-07-05T19:30:35.179Z","repository":{"id":365481372,"uuid":"1272278054","full_name":"josharsh/pixelpi","owner":"josharsh","description":"A minimal browser-agent harness - six tools, any model. pi for the browser.","archived":false,"fork":false,"pushed_at":"2026-06-27T09:33:21.000Z","size":1599,"stargazers_count":12,"open_issues_count":16,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-27T11:15:52.017Z","etag":null,"topics":["ai-agent","anthropic","browser-agent","browser-automation","chrome-devtools-protocol","cli","llm","openai","playwright-alternative","typescript","web-automation"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/pixelpi","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/josharsh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-17T13:06:31.000Z","updated_at":"2026-06-27T09:37:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/josharsh/pixelpi","commit_stats":null,"previous_names":["josharsh/pixelpi"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/josharsh/pixelpi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josharsh%2Fpixelpi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josharsh%2Fpixelpi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josharsh%2Fpixelpi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josharsh%2Fpixelpi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/josharsh","download_url":"https://codeload.github.com/josharsh/pixelpi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josharsh%2Fpixelpi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35167259,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","anthropic","browser-agent","browser-automation","chrome-devtools-protocol","cli","llm","openai","playwright-alternative","typescript","web-automation"],"created_at":"2026-07-05T19:30:33.012Z","updated_at":"2026-07-05T19:30:35.147Z","avatar_url":"https://github.com/josharsh.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# pixelpi\n\n**A browser your AI can afford.**\n\n*Record a task once. Run it on a thousand rows for the cost of one.*\n\n[![npm version](https://img.shields.io/npm/v/pixelpi.svg)](https://www.npmjs.com/package/pixelpi)\n[![npm downloads](https://img.shields.io/npm/dm/pixelpi.svg)](https://www.npmjs.com/package/pixelpi)\n[![CI](https://github.com/josharsh/pixelpi/actions/workflows/ci.yml/badge.svg)](https://github.com/josharsh/pixelpi/actions/workflows/ci.yml)\n[![license MIT](https://img.shields.io/npm/l/pixelpi.svg)](https://github.com/josharsh/pixelpi/blob/main/LICENSE)\n\n```bash\nnpm i -g pixelpi\n```\n\n\u003cimg src=\"https://raw.githubusercontent.com/josharsh/pixelpi/main/assets/pixelpi.gif\" alt=\"pixelpi running a recorded task across a dataset in parallel\" width=\"760\" /\u003e\n\n\u003c/div\u003e\n\npixelpi lets any AI model use a real web browser, and it reads each page in about 2,000 tokens instead of 180,000. Then it goes further: once a task works, you record it, and pixelpi repeats those exact steps with no model in the loop, across a whole dataset, in parallel, self-healing when a page changes. It is pi, the minimal coding agent, applied to the browser: six tools, raw Chrome DevTools Protocol, any model, your key.\n\n## What you get\n\n- Read any page in **~2,000 tokens**, 37x to 100x fewer than raw DOM.\n- **Record** a solved task once; **replay** it with no model, deterministically, for free.\n- **Run** a recorded task across a CSV or JSONL **in parallel**, about 0 tokens per row.\n- **Self-heals**: when a page changes, the model fixes one step and every other row reuses it.\n- **Callable from code, readable by other AI agents** (typed SDK plus a clean `--json` contract).\n\n\u003e A normal browser agent does 5,000 inputs as 5,000 full loops. pixelpi does one recorded run plus 4,999 free replays. The model thinks once; the rest is nearly free.\n\n## Quickstart\n\n```bash\nnpm i -g pixelpi\npixelpi auth                                        # set provider + key (any model)\npixelpi \"find the top story on Hacker News\"         # run a task live\npixelpi \"search Hacker News for rust\" --record hn   # record it, then name the input\npixelpi run hn --over queries.csv --concurrency 8   # run it across a dataset, in parallel\n```\n\nNo key is needed to reproduce the token numbers or to replay a recorded trace. A key is only for the live model loop, and pixelpi works with any provider.\n\n## The token win, measured\n\n`look()` versus a raw-DOM dump, across the sites [WebVoyager](https://github.com/MinorJerry/WebVoyager) tests on. Full table and a reproducible script (no key needed) are in [`bench/`](https://github.com/josharsh/pixelpi/tree/main/bench):\n\n| Site | `look()` | raw DOM | factor |\n|---|---|---|---|\n| Coursera | 1,997 tok | 202,892 tok | **101.6×** |\n| GitHub | 1,955 tok | 146,787 tok | **75.1×** |\n| Apple | 2,254 tok | 96,507 tok | **42.8×** |\n| Hugging Face | 1,932 tok | 45,300 tok | **23.4×** |\n| ArXiv | 1,588 tok | 10,652 tok | **6.7×** |\n\n`look()` holds around 2k tokens whatever the page weighs, while the raw DOM keeps growing. The heavier the site, the bigger the win.\n\n## Why it's different\n\n| | pixelpi | Playwright MCP | Chrome DevTools MCP |\n|---|---|---|---|\n| Tools in context | **6** | 21 | 31 |\n| Tool-def + prompt tokens | **~1,055** | ~13,700 | ~18,000 |\n| Page representation | a11y tree (bounded) | mixed | mixed |\n| Substrate | **raw CDP** (no Playwright) | Playwright | CDP |\n| Self-extension | agent writes JS skills at runtime | no | no |\n| Replay | record once, replay with **0 tokens** | no | no |\n| Parallel fan-out | record once, run a dataset for ~0 tokens/row | no | no |\n\n## Use it for\n\nBulk form submissions, scraping at scale, price, stock, or account checks across a list, nightly portal exports, or giving your own AI agent a browser tool it can afford and call.\n\n## Record, replay, and run over data\n\nSave a solved run as a trace, then replay it with no model in the loop. The first run is the compile step; every replay is the binary: free, deterministic, and fast.\n\n```bash\npixelpi \"find the top story on Hacker News\" --record hn-top   # solve once, save a trace\npixelpi replay hn-top                                         # rerun it, no model, 0 tokens\npixelpi replay hn-top --heal                                  # repair one step if the page drifted\n```\n\nTraces key on the accessibility role and name of each element, not CSS selectors or coordinates, so they survive most layout changes. A bare name lives in `~/.pixelpi/traces/`; pass a path (or a name ending in `.json`) to keep a trace inside a repo. Strict `replay` needs no API key and exits `3` on drift, naming the step that no longer matches. `--heal` re-derives just that step with the model and rewrites the trace, so it self-corrects over time. Replay reproduces actions, not intent: it shines for stable, repeated flows.\n\nA parametrized trace is a function. Record it once with an example input, then run it across a list:\n\n```bash\npixelpi \"search Hacker News for rust\" --record hn   # then name \"rust\" as the input q\npixelpi run hn --query rust                          # one input\npixelpi run hn --over queries.csv --concurrency 8    # map over a CSV/JSONL, in parallel\n```\n\nEach row runs in its own headless Chrome, bounded by `--concurrency` (default 4). Outcomes stream to a JSONL file (`--out`), `--resume` skips rows already done, and the first row runs alone as a warm-up so a single `--heal` repair benefits every other row. A 5,000-row job costs one model run plus, at most, a handful of repairs.\n\n## Describe a trace (for humans and agents)\n\nEvery trace is an introspectable function. `describe` shows its inputs and output:\n\n```bash\npixelpi describe hn            # human card: task, inputs, output, usage\npixelpi describe hn --json     # {\"type\":\"description\",\"params\":[...],\"output\":{...}}\n```\n\nUnder `--json`, every command emits one NDJSON stream (progress, results, and errors as `{\"type\":\"error\",\"code\":...}`), so an AI agent can drive pixelpi and parse a single clean contract.\n\n## Sessions and login\n\nEvery run uses a fresh, disposable Chrome profile by default. To stay logged in across runs, use a persistent profile:\n\n```bash\npixelpi login https://github.com                    # sign in once, press Enter to save\npixelpi --profile \"check my GitHub notifications\"   # reuse the saved session, headless\n```\n\n`--profile` uses `~/.pixelpi/profile`; `--profile=\u003cdir\u003e` uses a custom one. pixelpi finds Chrome automatically on macOS, Linux, and Windows; set `PIXELPI_CHROME=/path/to/chrome` to override.\n\n## The six primitives\n\n```\nlook · act · fill · nav · eval · store\n```\n\n- **`look(mode?, filter?)`**: compact, ref-indexed accessibility snapshot. The `read`.\n- **`act(ref, op, value?)`**: mutate the page by stable ref via trusted CDP input events. The `write`.\n- **`fill(fields[])`**: batched form fill in one call.\n- **`nav(action, arg?)`**: navigate, tabs, `waitfor`.\n- **`eval(fn, args?, opts?)`**: arbitrary JS in the page realm. The escape hatch, the `bash` of the browser.\n- **`store(action, key?, value?)`**: durable host-side JSON KV. The filesystem.\n\nElements are addressed by **stable ref**, not CSS or coordinates: cheap, deterministic, resilient to layout churn. Everything else composes from `eval`; the agent writes its own higher-level tools as JSON skills at runtime, and only each skill's one-line description enters the prompt.\n\n## Use it from code\n\nLoad a saved trace as a callable function, no model or API key required:\n\n```ts\nimport { loadTrace } from \"pixelpi\";\n\nconst hn = loadTrace(\"hn\");                          // by name (home library) or path\nconsole.log(hn.describe());                          // { params, output, ... }\nconst r  = await hn({ query: \"rust\" });              // run once  -\u003e { ok, output }\nconst rs = await hn.over(rows, { concurrency: 4 });  // map over a dataset, results in input order\n```\n\nOr drive the full agent loop, or the six primitives against raw CDP with no model. More in [`examples/`](https://github.com/josharsh/pixelpi/tree/main/examples).\n\n## Philosophy\n\nThe model is the harness now, so you expose the substrate's irreducible primitives and let the agent compose the rest. This is the bet pi made for the terminal, applied to the browser. See [docs/how-it-works.md](https://github.com/josharsh/pixelpi/blob/main/docs/how-it-works.md) for the moving parts (why six tools, why raw CDP, why no MCP).\n\n## Status\n\nSubstrate (`look`/`eval`) is validated live against real sites. The agent loop, guards, stores, replay, run, and provider adapters are unit-tested (216 tests, mock provider, no network in tests). The full model-to-browser loop runs once you supply an API key. Requires Node \u003e= 20 and Google Chrome (macOS, Linux, or Windows). Pre-1.0 and moving fast.\n\n## Contributing\n\nIssues and PRs welcome. Run `pnpm install \u0026\u0026 pnpm build \u0026\u0026 pnpm test` before opening a PR. See [CONTRIBUTING.md](https://github.com/josharsh/pixelpi/blob/main/CONTRIBUTING.md).\n\n## License\n\nMIT © 2026 Harsh Joshi\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosharsh%2Fpixelpi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosharsh%2Fpixelpi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosharsh%2Fpixelpi/lists"}