# 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:

```bash
pi install npm:pire-browser
```

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

```bash
pi
```

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

### Global CLI

Install the command directly:

```bash
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:

```bash
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.

```bash
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:

```bash
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:

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

PowerShell example:

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

## Quick Start

```bash
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:

```bash
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

```bash
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

```bash
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:

```bash
PIRE_BROWSER_EXTENSION_MODE=xpi pire-browser launch
```

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

```bash
PIRE_BROWSER_EXTENSION_MODE=xpi PIRE_BROWSER_ALLOW_UNSIGNED_XPI=1 pire-browser launch
```

### Inspect And Act

```bash
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

```bash
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

```bash
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

```bash
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

```bash
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

```bash
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

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

### Active-Origin State

```bash
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

```bash
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

```bash
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:

```bash
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:

```text
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:

```bash
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

```bash
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

```bash
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

```bash
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

```bash
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:

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

Run the local Windows smoke test:

```powershell
.\scripts\smoke.ps1
```

Useful smoke variants:

```bash
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:

```bash
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
```

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 before any manual `npm publish`.

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.

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`.

## Manual Windows Zip

The legacy Windows zip helper remains available for manual Windows x64 zip testing:

```powershell
.\scripts\package-windows.ps1
```

The zip includes `pire-browser.exe`, `pire-browser-host.exe`, prebuilt Firefox extension files, compact agent/skill guidance, and `install-windows.ps1`.

Install from PowerShell:

```powershell
New-Item -ItemType Directory -Force "$env:TEMP\pire-browser-install" | Out-Null
gh release download v0.2.0 --repo ryenwang/pire-browser --pattern pire-browser-windows-x64.zip --dir "$env:TEMP\pire-browser-install"
Expand-Archive -Force "$env:TEMP\pire-browser-install\pire-browser-windows-x64.zip" "$env:TEMP\pire-browser-install\pire-browser-windows-x64"
Set-Location "$env:TEMP\pire-browser-install\pire-browser-windows-x64"
.\install-windows.ps1
```

## RBXLX Download Runner

This repository also contains a maintainer workflow for the Discord/Gofile RBXLX archive process:

```powershell
.\scripts\download-rbxlx-from-csv.ps1
```

Useful modes:

```powershell
.\scripts\download-rbxlx-from-csv.ps1 -Limit 1 -StopOnError
.\scripts\download-rbxlx-from-csv.ps1 -Limit 5
.\scripts\download-rbxlx-from-csv.ps1 -IncludeDownloadedDuplicates
```

The runner starts with the oldest CSV row whose `status` is not `downloaded`, opens the Gofile link through `pire-browser`, reads the displayed `.rbxlx` filename, clicks the visible Download button, waits for the browser download to finish, moves the file into the RBXLX folder, and updates the CSV status fields.

For rows where the requested `game_id` differs from the displayed downloaded filename, the displayed filename is treated as the source of truth. Duplicate `game_id` rows are reconciled by keeping the newest verified row and moving older differing versions to the Past Versions folder.
