Skip to content

ryenwang/pire-browser

Repository files navigation

pire-browser

Firefox automation for AI agents. pire-browser gives Pi and shell-based agents a local CLI for launching Firefox, inspecting pages, clicking and filling by stable refs, handling downloads/uploads, and reusing logged-in Firefox profiles.

It intentionally does not use BiDi or CDP. Firefox loads a WebExtension, the WebExtension talks to a native messaging host, and the CLI talks to that host through current-user IPC: Windows named pipes on Windows and Unix domain sockets on macOS/Linux.

Installation

Pi Package

Install the public Pi package:

pi install npm:pire-browser

Then start Pi and ask it to use the pire-browser tool:

pi
Use pire-browser to open https://example.com and snapshot the page.

Global CLI

Install the command directly:

npm install -g pire-browser
pire-browser setup

setup registers the Firefox Native Messaging host for the current OS user. Install also runs best-effort setup, but it is safe to run again.

Project Install

For projects that want to pin the version:

npm install pire-browser
npx pire-browser setup

If optional dependencies are disabled with --omit=optional, reinstall with optional dependencies enabled. The root package uses a small JS launcher plus one native optional package for your OS/architecture, such as @ryenw/pire-browser-win32-x64, @ryenw/pire-browser-darwin-arm64, or @ryenw/pire-browser-linux-x64.

From Source

Requires Node.js, npm, Rust, and Firefox.

git clone https://github.com/ryenwang/pire-browser
cd pire-browser
npm install
npm --prefix extension install
npm run build:extension
cargo build
cargo run -p pire-browser-cli -- setup

Build a platform package binary pair:

node scripts/build-platform.mjs win32-x64

Use the tuple for your platform: win32-x64, win32-ia32, win32-arm64, darwin-x64, darwin-arm64, linux-x64, or linux-arm64.

Requirements

  • Firefox.
  • Pi 0.75.4 or newer when installing as a Pi package.
  • Supported public beta targets: Windows x64, Windows x86, Windows ARM64, macOS x64, macOS ARM64, Linux glibc x64, and Linux glibc ARM64.
  • Alpine/musl Linux is not part of the beta.

On Linux, distro Firefox builds work best. Snap and Flatpak Firefox are detected, but sandboxed native messaging may require the WebExtensions portal or a non-sandboxed Mozilla Firefox build.

Custom Firefox Path

If Firefox is installed somewhere unusual:

pire-browser setup --firefox-path /path/to/firefox

PowerShell example:

$env:PIRE_BROWSER_FIREFOX_PATH = "D:\Apps\Mozilla Firefox\firefox.exe"
pire-browser setup --firefox-path $env:PIRE_BROWSER_FIREFOX_PATH

Quick Start

pire-browser open https://example.com
pire-browser snapshot -i
pire-browser click '@e1'
pire-browser fill '@e2' "hello@example.com"
pire-browser find role button --name "Submit" click
pire-browser wait --selector "#done"
pire-browser screenshot page.png
pire-browser close

PowerShell treats @ specially, so quote refs such as '@e1'.

Use semantic locators when you do not want to manage refs manually:

pire-browser find label "Email" fill "hello@example.com"
pire-browser find text "Continue" click
pire-browser find role button --name "Submit" click

Use --json when another tool or agent needs structured output.

Commands

Setup And Diagnostics

pire-browser status
pire-browser status --json
pire-browser doctor
pire-browser doctor --json
pire-browser setup
pire-browser setup --firefox-path /path/to/firefox

status and doctor are observational. Browser commands that need auto-launch can run lazy setup when native host registration is missing or mismatched.

Browser Lifecycle

pire-browser launch
pire-browser launch --url https://example.com
pire-browser open https://example.com
pire-browser goto https://example.com
pire-browser navigate https://example.com
pire-browser close

launch starts a managed Firefox profile. open, goto, and navigate open a URL, auto-launching the managed Default profile when no live session exists.

The default launch engine is web-ext, including public packages. The direct Firefox/XPI path is release-validation-only for now:

PIRE_BROWSER_EXTENSION_MODE=xpi pire-browser launch

Unsigned XPI testing is local-only and requires Firefox Developer Edition or Nightly plus:

PIRE_BROWSER_EXTENSION_MODE=xpi PIRE_BROWSER_ALLOW_UNSIGNED_XPI=1 pire-browser launch

Inspect And Act

pire-browser snapshot -i
pire-browser click '@e1'
pire-browser fill '@e2' "hello"
pire-browser type '@e2' "hello"
pire-browser press Enter
pire-browser wait --selector "#done"
pire-browser wait --text "Saved"
pire-browser screenshot out.png

Refs are short lived. Re-run snapshot -i after navigation, reloads, DOM changes, dialogs, downloads, uploads, or failed actions.

Semantic Find

pire-browser find role button --name "Submit"
pire-browser find role button --name "Submit" click
pire-browser find label "Email" fill "hello@example.com"
pire-browser find text "Sign in" click
pire-browser find placeholder "Search" fill "pire-browser"

Supported locator families include role, label, text, placeholder, alt, title, testid, first, last, and nth. Chained actions include click, fill, type, hover, focus, check, uncheck, and text.

Tabs

pire-browser tabs list
pire-browser tabs select t1
pire-browser tabs close t1
pire-browser open https://example.com --label docs

Tab ids are stable strings such as t1, t2, and t3.

Sessions

pire-browser session list
pire-browser session list --json
pire-browser session attach <session-id>
pire-browser session cleanup
pire-browser --session <session-id> snapshot -i
pire-browser --session-name work open https://example.com
pire-browser --session-name work snapshot -i
pire-browser --session-name work close

--session <id> is strict and never launches Firefox. --session-name <name> reuses or launches a managed Firefox profile with that name.

Profile names may contain letters, numbers, internal spaces, _, -, and .. Empty names, path traversal, slashes, and : are rejected.

Downloads

pire-browser snapshot -i
pire-browser download '@e4' ./downloads/report.txt
pire-browser click '@e4'
pire-browser wait --download ./downloads/report.txt --timeout 60000

Firefox downloads are staged under the OS app-data pire-browser/downloads directory for managed profiles, then moved to the requested destination. Destinations must not already exist. Action policy and confirmation use the download category.

Uploads

pire-browser upload '#file' ./fixtures/example.txt
pire-browser upload '#multi-file' ./one.txt ./two.json --json

Uploads assign small local files to page file inputs. Total raw file bytes are capped at 512 KiB. This does not control native OS file-picker dialogs.

Clipboard

pire-browser clipboard read
pire-browser clipboard write "hello"
pire-browser clipboard copy
pire-browser clipboard paste

Active-Origin State

pire-browser --session-name work open https://app.example.com/dashboard
pire-browser --session-name work state save ./.pire-state/app.example.com-review.json
pire-browser state inspect ./.pire-state/app.example.com-review.json
pire-browser state inspect --record ./.pire-state/app.example.com-review.json
pire-browser --session-name review state load --require-inspected ./.pire-state/app.example.com-review.json

State files are plaintext and contain active-origin cookies, localStorage, and sessionStorage. Do not commit or share them. The project gitignores .pire-state/.

state inspect is metadata-only. state inspect --record writes a 24-hour local receipt under the OS app-data directory. Set PIRE_BROWSER_REQUIRE_INSPECTED_STATE=1 to make normal state load require a fresh receipt; use --no-require-inspected only as an explicit one-command override.

This is active-origin state only. It does not export saved passwords, IndexedDB, browser cache, service workers, full profiles, auth vault entries, or cross-origin SSO state.

Skills

pire-browser skills list
pire-browser skills cat core
pire-browser skills cat core --json

Installed agents should use the bundled skill command for version-matched guidance instead of relying on stale cached instructions. The package also ships compact routing context under agent/, agent/workflows/, and agent/references/.

Updates

pire-browser update check --json
pire-browser update apply
pire-browser update configure --mode off|notify|patch

Update checks run in a detached background process so browser launches do not wait on registry network calls. Patch auto-update is allowed only for global npm installs or confirmed Pi-managed installs, and only when no managed Firefox session is active. Local project installs and minor/major updates notify only.

On Windows, close managed Firefox sessions before replacing binaries. Windows can keep running executables locked.

Authentication

The simplest login flow is to use a persistent managed Firefox profile:

pire-browser launch --url https://example.com/login
# Sign in manually in Firefox.
pire-browser status --json

Firefox stores cookies, sessions, and saved passwords inside its managed profile. pire-browser stores launcher metadata, session files, confirmations, receipts, and download staging under the OS app-data directory, but it does not inspect cookies, saved passwords, session tokens, or one-time codes for diagnostics.

The default profile locations are:

Windows: %LOCALAPPDATA%\pire-browser\firefox-profiles\Default
macOS:   ~/Library/Application Support/pire-browser/firefox-profiles/Default
Linux:   $XDG_DATA_HOME/pire-browser/firefox-profiles/Default
         or ~/.local/share/pire-browser/firefox-profiles/Default

Use named profiles to isolate projects:

pire-browser --session-name github open https://github.com
pire-browser --session-name app open https://app.example.com

Deleting a managed profile folder clears that saved browser state.

Guardrails

Domain Allowlist

pire-browser --allowed-domains "app.example.com,*.example.com" open https://app.example.com/dashboard
AGENT_BROWSER_ALLOWED_DOMAINS="app.example.com,*.example.com" pire-browser snapshot -i
pire-browser --no-allowed-domains open https://example.net

The allowlist accepts host patterns such as example.com, *.example.com, localhost, and 127.0.0.1. This is a cooperative wrong-site guardrail, not a browser sandbox. It does not claim containment of redirects, subresources, WebSockets, EventSource, or races where a page navigates between check and action.

Action Policy

cat > policy-deny-eval.json <<'JSON'
{ "default": "allow", "deny": ["eval"] }
JSON

pire-browser --action-policy ./policy-deny-eval.json eval "document.title"

Policy files use optional default, allow, and deny fields. Categories include navigate, click, fill, eval, snapshot, scroll, wait, get, interact, state, network, download, and upload. deny wins over allow, and unknown keys fail closed.

Action Confirmation

pire-browser --confirm-actions eval eval "document.title" --json
pire-browser confirm c_8f3a1234
pire-browser deny c_8f3a1234

When confirmation is required, the command returns ConfirmationRequired with a short-lived id and the follow-up command to run. Agents should ask the user before running returned confirm <id> commands.

Confirmation records live under the OS app-data pire-browser/confirmations directory. They are plaintext, user-scoped, short-lived runtime metadata and may contain the original command arguments.

Security Model

pire-browser installs local native binaries, registers a Firefox Native Messaging host for the current OS user, and exposes a Pi extension. Pi extensions run with the current user's local permissions.

The Native Messaging host exposes only current-user IPC. On Windows, named pipes use a DACL restricted to the current Windows user plus required system/admin principals. On macOS/Linux, Unix domain sockets live in a short current-user runtime directory.

This protects against cross-user and remote access. It does not defend against malicious code already running as the same OS user.

Current Limits

  • DOM-level automation only; no trusted OS input.
  • File uploads, payment/auth flows, and browser-restricted pages can return requires_user_activation.
  • Cross-origin frames are best-effort; inaccessible frames are opaque.
  • Screenshots are visible-viewport only.
  • Snap/Flatpak Firefox native messaging may require additional sandbox support or a non-sandboxed Firefox build.

Development

cargo build
npm --prefix extension install
npm --prefix extension run build
cargo run -p pire-browser-cli -- setup
npx --prefix extension web-ext run --source-dir extension --firefox "C:\Program Files\Mozilla Firefox\firefox.exe"

Common checks:

cargo test -q
npm test
npm run oracle:test
npm pack --dry-run --json

Run the local Windows smoke test:

.\scripts\smoke.ps1

Useful smoke variants:

npm run smoke:named-sessions
npm run smoke:state
npm run smoke:domain-policy
npm run smoke:downloads
npm run smoke:uploads

Package and packed-install checks:

node scripts/package-extension-xpi.mjs
node scripts/build-platform.mjs win32-x64
node scripts/package-platform.mjs win32-x64 --pack --pack-destination target/npm-artifact-smoke
node scripts/smoke-packed-package.mjs --no-browser --artifact-dir target/release-smoke-local

One-time npm trusted publishing setup:

npm run release:npm:trust

Run the printed npm trust github ... commands once for the root package and all seven @ryenw/* platform packages. Configure them for repository ryenwang/pire-browser, workflow npm-publish.yml, environment npm-production, and npm publish permission.

Before publishing, run the Platform Packages workflow in GitHub Actions. Its combined pire-browser-npm-artifacts artifact must contain the root npm tarball plus all seven optional native package tarballs, and the verifier must pass.

Then run the manual Release Smoke workflow with target=all and default web-ext mode. This is the packed-install publish gate for Windows x64, macOS ARM64, and Linux x64.

Publish through the manual Publish npm Package workflow from main. The workflow builds fresh root/platform tarballs, verifies them, publishes sidecar packages first through npm trusted publishing, then publishes the root package and creates the matching v<package.json version> GitHub release.

Signed-XPI smoke is separate. Run Release Smoke with run_signed_xpi=true only when AMO signing secrets are available and before claiming direct-XPI readiness. The public default remains web-ext.