pi-lightpanda0.3.0
Lightweight headless browser for the Pi agent via Lightpanda (CDP-compatible, ~16x less memory than Chrome)
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-lightpanda
A Pi extension that gives the agent a lightweight headless browser via Lightpanda — ~16x less memory and ~9x faster than Chrome headless, CDP-compatible.
Keywords: pi browser extension, lightpanda headless browser, AI agent web automation, CDP browser without Chromium, lightweight scraping for coding agents.
The problem
Coding agents need a browser for fetching pages, scraping, testing flows, and verifying web output — but every existing Pi browser extension launches Chromium (~1 GB+ per instance, seconds to start). For an agent that opens dozens of pages per task, that is slow, expensive, and wasteful — especially in CI or on small machines.
Most of that weight is code the agent never uses: rendering, GPU, codecs, font caches, UI. A headless agent needs JS execution + DOM + CDP, nothing else.
The solution
pi-lightpanda drives a local Lightpanda binary instead of Chromium:
| Lightpanda | Headless Chrome | |
|---|---|---|
| Memory (100 pages) | ~123 MB | ~2 GB |
| Time (100 pages) | ~5 s | ~46 s |
| Startup | instant | seconds |
| Engine | from-scratch (Zig) | Blink + V8 + Skia + … |
The extension manages the whole lifecycle: one-shot fetch for reading pages
(no server needed), a persistent serve + puppeteer-core client for multi-step
interaction (navigate → snapshot → act), and PandaScript run for
reproducible automation. puppeteer-core is the Chromium-free CDP client;
typebox is provided by Pi as a host peer dependency. No puppeteer/playwright,
which would drag a full Chromium download back in.
This is not a full Chromium replacement: Lightpanda has partial Web API coverage and no graphical rendering (no screenshots). For complex apps that need full Chrome, keep a Chromium-backed extension alongside and pick per task.
Installation
pi install npm:pi-lightpanda
or from git:
pi install git:github.com/tonyputi/pi-lightpanda
or from a local checkout:
pi install /absolute/path/to/pi-lightpanda
Setup
The whole setup runs inside Pi — no separate commands, no restart juggling:
pi install npm:pi-lightpanda # then start pi and run:
/lightpanda setup
What happens: confirm the download → the right nightly binary for your
platform is fetched from the lightpanda-io/browser GitHub releases into
~/.pi/agent/bin/lightpanda (download progress appears in Pi's status line)
and verified against GitHub's SHA-256 metadata and with lightpanda version → done.
Setup and update stream to a private temporary directory next to the destination
and replace the binary atomically only after verification. Downloads have a
five-minute timeout and a 256 MB safety limit; failures leave the previous binary
untouched and clean up partial files. Version checks are asynchronous, so a slow
executable cannot freeze Pi's UI.
The extension resolves the binary dynamically (see Configuration), so CLI tools
use it immediately — no reload, no .envrc. At startup Pi checks the installed
binary's SHA-256 against the latest GitHub asset in the background (once per
extension runtime); this never delays the session or updates automatically.
If the digest differs, Pi suggests /lightpanda update. A different digest does
not prove the build is newer: custom/stable builds may also differ. Network/API
errors are silent; session shutdown cancels work and suppresses late notifications.
After an update, an already-running CDP server still uses the previous build.
Pi asks before restarting a managed server because restarting discards its
page, cookies and session state. Declining keeps that server running and explains
how to restart later with /lightpanda stop then /lightpanda start. External
servers are never restarted or stopped. Check any time with /lightpanda status.
Prefer the manual route? Install from
lightpanda.io/docs (one-liner, brew, docker,
nightly builds) and set PI_LIGHTPANDA_BIN if the binary is not on PATH.
Native Windows is unsupported — install and run both Pi and Lightpanda inside WSL2.
Configuration
No configuration is required after /lightpanda setup. The binary is
resolved at each tool call, in this order:
PI_LIGHTPANDA_BIN(if set — e.g. via direnv)~/.pi/agent/bin/lightpanda(where/lightpanda setupinstalls)lightpandaonPATH
The remaining variables are optional tuning:
| Variable | Meaning | Default |
|---|---|---|
PI_LIGHTPANDA_BIN |
binary path or command name on PATH (optional — see resolution order above) |
resolved automatically |
PI_LIGHTPANDA_HOST |
CDP host — loopback only | 127.0.0.1 |
PI_LIGHTPANDA_PORT |
CDP port | 9222 |
PI_LIGHTPANDA_TIMEOUT_MS |
per-operation timeout | 30000 |
PI_LIGHTPANDA_MAX_BYTES |
max text bytes returned per tool | 12288 |
PI_LIGHTPANDA_MAX_REFS |
max snapshot refs | 50 |
Tools, setup and update share binary resolution, including command-name overrides
on PATH. An override naming a missing command cannot be an install destination:
set an explicit file path or unset it to use the managed install location.
Example .envrc (only if you keep the binary somewhere else):
export PI_LIGHTPANDA_BIN="$HOME/tools/lightpanda"
How it works
lightpanda_fetchshells out tolightpanda fetch <url> --dump markdown— the cheapest path, no server process at all. Default for read-only work.lightpanda_navigate/snapshot/act/evalshare one persistentlightpanda serve --host 127.0.0.1 --port 9222subprocess, spawned on first use. An already-running server is reused untouched; a server we spawned is killed onsession_shutdown.- Missing/non-executable binaries and servers that exit before readiness return errors instead of crashing Pi. Concurrent startup requests share one child; shutdown waits for a managed child to exit. Our browser context is closed on disconnect, without closing an external browser.
- The CDP client is
puppeteer-core(Lightpanda's documented automation path: connect overws://host:port, open a context + page). It ships no browser, so unlikepuppeteer/playwrightit does not drag Chromium back in. - Snapshot refs (
e1,e2, …) map to unique, temporary DOM attributes, not ambiguous IDs/classes. Identical buttons remain distinct, and DOM reordering does not move a marker to another element. A new snapshot removes old markers. Refs expire after navigation, actions (including failed ones),lightpanda_eval, stop or restart; stale or duplicated targets fail safely. Password input values are omitted. CDP tools execute sequentially so they cannot race over the shared page/ref map; one persistent page per session keeps logins and state. lightpanda_runexecutes a trusted.jsPandaScript file headlessly (lightpanda run) — reproducible automation without an LLM, same caution as running a shell script.- Every tool truncates output (
PI_LIGHTPANDA_MAX_BYTES, default 12 KB) and onlyhttp(s)URLs are accepted. The host guard refuses non-loopback CDP endpoints: CDP controls cookies and sessions, so remote browsers are out of scope.
Tools & commands
Tools (called by the agent): lightpanda_fetch, lightpanda_navigate,
lightpanda_snapshot, lightpanda_act (click/fill/type/select/press/hover),
lightpanda_extract, lightpanda_eval, lightpanda_run, lightpanda_status.
Command: /lightpanda status | start | stop | setup | update — server health,
lifecycle, in-Pi binary install and confirmed updates (external servers are
never stopped, only ones this extension spawned).
Suggested loop for the agent: fetch for reading → navigate → snapshot
→ act → re-snapshot → … → run to save a working flow as PandaScript.
Structured extraction
Use lightpanda_extract when you need named data fields, not a page dump
or repeated ad-hoc JavaScript. For example, Pi can extract a product name,
price and availability, or collect documentation links as JSON.
The agent supplies CSS selectors, usually after inspecting an unfamiliar page
with fetch in HTML format. The tool does not guess selectors, infer facts,
convert currencies/numbers, or make nested LLM calls.
Example tool input:
{
"url": "https://shop.example/product",
"waitForSelector": ".price",
"fields": [
{ "name": "name", "selector": "h1", "required": true },
{ "name": "price", "selector": ".price", "required": true },
{ "name": "stock", "selector": ".availability" },
{ "name": "docs", "selector": "a.docs", "attribute": "href", "many": true }
]
}
urlis optional. With it, the tool navigates the persistent CDP page, invalidating snapshot refs. Without it, extraction reads the current page without navigating or changing snapshot markers. A page must already be open.waitForSelectoris optional. It waits up to the configured operation timeout before reading JS-populated content; otherwise extraction reads the DOM available after page readiness. Cancellation reaches navigation, readiness and selector waits; an aborted request does not start a subsequent DOM read.- Fields return strings/nulls, not invented values. Text whitespace is
normalized. Attributes are raw DOM attributes: an
hrefmay remain relative, and an input'svalueattribute is not necessarily its current live value. Prices such as€ 19,90remain strings. - Ambiguity is explicit. By default a selector must match exactly one
element. Use
many: truefor lists;maxValuesdefaults to 20 and is capped at 50 per field. You can request 1–12 uniquely named fields.
Every field includes name, selector, status, matches, values and
truncated (plus the requested attribute and an explanatory message when
relevant). A two-field request might return:
{
"source": {
"url": "https://shop.example/product",
"title": "Example product",
"retrievedAt": "2026-10-15T12:00:00.000Z"
},
"fields": [
{ "name": "price", "selector": ".price", "status": "ok", "matches": 1, "values": ["€ 19,90"], "truncated": false },
{ "name": "stock", "selector": ".availability", "status": "missing", "matches": 0, "values": [], "truncated": false }
],
"complete": false,
"truncated": false
}
The source URL is the final URL after redirects; the title is limited to 200
code units and the acquisition timestamp is UTC. Statuses distinguish ok,
missing, ambiguous, redacted and error. A missing element gives [];
a missing attribute gives null; an observed empty string remains "".
Password input value reads are redacted to null.
Invalid/unsupported CSS and ambiguous scalar selectors mark the tool as failed.
A required: true field also fails if missing, redacted or truncated. Failures
at field level still return all available structured results. Optional missing
fields are not a tool error, but complete is false. complete is true only
when every requested field succeeded without truncation.
Results are schema-validated and available both as JSON text and
structuredContent (for programmatic callers/codemode). The byte limit applies
to complete serialized JSON, never a cut-off JSON document. Values are
limited to 2048 code units and the total output budget is
PI_LIGHTPANDA_MAX_BYTES (capped at 64 KB for extraction). Partial values/lists
set truncated. Required fields receive value space before optional fields;
within each priority tier, fields and list values share the available budget.
Field order does not determine priority, and results keep the requested order.
Required fields still fail explicitly if their own data cannot fit the limits.
If metadata alone will not fit, request fewer/shorter fields or raise the
configured limit. Schema validation checks structure, not the
truth of a website's claims.
Security notes
- CDP grants full control of the connected browser. This extension only talks
to loopback endpoints and never exposes the server beyond
127.0.0.1. lightpanda_evalandlightpanda_runexecute JavaScript — treat target pages and script files as code. Onlyrunscripts you trust.- Extraction output is untrusted page data, not instructions. It can contain private content, URLs/query strings and attributes from your active session. Password input redaction is not a general secrets/PII filter; do not publish results from private pages without review.
- Packages run with full system access — review third-party extension source before installing.
Troubleshooting
Run /lightpanda status inside Pi.
- binary … not found — run
/lightpanda setup, or setPI_LIGHTPANDA_BINto the binary path. No restart needed: resolution happens at every call. - did not answer … within …ms — the binary may be the
wrong platform asset or missing system libs; try
<bin> versionmanually. - fetch/navigate/eval failures on a specific site — likely partial Web API coverage in Lightpanda; fall back to a Chromium-backed extension for that site (no screenshots here by design — no rendering engine).
- PI_LIGHTPANDA_HOST must be loopback — by design; remote CDP endpoints are refused.
Development checks
npx tsc --noEmit
npm test
The default suite requires no network or Lightpanda binary. It uses fake executables, HTTP streams and DOM elements to exercise failure paths as well as successful installs/updates.
With Lightpanda installed, run the optional integration check:
npm run test:smoke
It serves local fixtures on ephemeral loopback ports, verifies real CDP snapshots/clicks and stale refs, structured extraction with JS-populated data, redirect provenance and required/missing fields, and managed-server cleanup and external-server preservation. It does not download/update binaries or use external sites.
Contributing
See the contribution guide and Code of Conduct. Use the issue forms for bugs and feature requests. Do not include secrets or private browsing data; report security issues privately as described in the contribution guide.
License
MIT