pi-lean-portal

extensionmaintained

Interactive Playwright web browsing for Pi. Chromium and Firefox built in; author stealth backends on either engine. Snapshots and screenshots cache to disk, saving context. Configurable profiles/cookies. Agents write site guides, auto-matched by domain.

by — · v0.6.0 · published 1w ago

$ pi install npm:pi-lean-portal
downloads/mo
0
stars
1
last push
6d ago
open issues
0

Signals

license: AGPL-3.0-onlytestspi manifest: missinginstall size: —deps: 0peer deps: 0

Download trend

2.7K downloads · last 12 weeks (weekly)

README

pi-lean-portal User Guide

pi-lean-portal gives the Pi coding agent interactive web browsing via Playwright Chromium/Firefox, accessibility-tree snapshots with @e element refs, persistent profiles, cookies, and domain-aware navigation guides. A /web toggle removes the tools from the agent's context when switched off, so web browsing doesn't consume tokens on sessions that aren't doing web work. If a site blocks the shipped browsers, drop in your own backend (e.g. Camoufox).

Part of the pi-lean-dimension web-tools suite. For SearXNG search support, install pi-lean-search.


Quick Start

pi install npm:pi-lean-portal

Then run /web install inside pi to download browser binaries (these are not installed via npm).

Once loaded, you'll see a notification like:

🌐 Browser extension loaded (plugins: chromium, firefox). Try: web-fetch for static pages or browser-navigate for interactive browsing.

The browser tools are enabled by default: web-fetch for static pages, browser-navigate (plus click/type/scroll/screenshots via @e refs) for interactive browsing.


/web Command — Browser Toggle & Profiles

The /web command controls whether web tools are visible to the AI agent, toggles guide-saving mode, and manages browser profiles.

Three-State Toggle

CommandEffect
/web onBrowsing only: all interactive browser tools + web-fetch are available. web-learn is hidden. If pi-lean-search is also installed, web-search is enabled too.
/web learnBrowsing + guide-saving: same as on, plus the web-learn tool is available so the AI can save/update navigation guides on request.
/web offAll web tools hidden: saves ~1500–2000 tokens per turn by removing tool schemas from the prompt. web-fetch and web-search are also hidden.

Check Current State

CommandEffect
/webShow current toggle status and available sub-commands.
/web statusDetailed runtime status including toggle state, plugin health, browser-binary presence, active sessions, and profiles on disk.
/web installInstall browser binaries via the bundled playwright CLI (checkbox dialog in the TUI; prints the manual command elsewhere).
/web install chromium|firefoxDirectly download one engine, no dialog.
🌐 Browser tools: ✅ on  |  📖 Learn mode: ❌ off
────────────────────────────────────────
Status: idle
Plugins: chromium, firefox, chromium-py (disabled), firefox-py (disabled)
Browsers: chromium ✓ firefox ✓
Use web-fetch for stateless HTTP fetches.
Active sessions: 1
  PW [chromium] https://example.com — Example Domain [profile: session]
Profiles: 1 on disk (named)
  shopping  (0.3 KB) ← active
Session profiles: 1 (manage with /web profile)

Persistence

Toggle state persists across /reload, /resume, /fork, and /tree — it's stored in the conversation's branch history. Fresh conversations start from the toolsetDefaults block in settings.json (Configuration).


Profiles — Persistent Sessions

Profiles let the AI agent maintain persistent browser state (cookies, localStorage) across calls, conversations, and even across different subagents.

Profile Modes

Modebrowser-navigate profile=Behavior
None"none"Clean slate every time (no cookies or state)
Session"session" (default)Persists state for the current conversation; survives /reload and /resume
Named"shopping", "work", etc.Shared across conversations and subagents, similar to browser tabs sharing a profile

Managing Profiles with /web profile

Sub-commandEffect
/web profile listList all profiles on disk with their state size
/web profile create shoppingCreate a new named profile
/web profile sessionSet conversation-scoped default to session mode
/web profile noneReset default to ephemeral (no persistence)
/web profile shoppingSwitch default profile to an existing named profile
/web profile clear shoppingDelete the saved state for a profile (keeps the directory)
/web profile clear-all --confirmClear ALL profile states
/web profile prune --confirmRemove stale session profiles for ended conversations

How it works: Profile state is stored at ~/.pi/agent/pi-lean-portal/browser-state/<profile-name>/storage-state.json. Session-scoped profiles are auto-cleaned when the pi conversation ends.


Cookie Management

The /web cookies command lets you inspect and clear session cookies:

Sub-commandEffect
/web cookies listList all cookies in the current session (name, value, domain, expiry, flags)
/web cookies clear --confirmClear ALL cookies for the current session

Cookies are saved as part of profile state. When you switch profiles, the cookies from the old profile are preserved and the new profile's cookies are loaded.


All 12 Tools

pi-lean-portal registers 12 tools. Three are stateless; the rest require a browser session (created by browser-navigate).

ToolWhat it doesState
web-fetchFetch a URL → Markdown, no browser sessionstateless
web-guide / web-learnRead / save navigation guides (learn mode via /web learn)stateless
browser-navigateOpen a page → accessibility tree with @e element refssession
browser-snapshotRefresh the tree (full=true returns the uncompacted tree)session
browser-click / browser-type / browser-scroll / browser-back / browser-pressInteract via @e refs (click, type, scroll, history, keyboard)session
browser-consoleRead captured console messages; also evaluates JS in the pagesession
browser-inspectTargeted element/text queries without loading a full snapshotsession

Automatic Artifacts

Session tools save their full output to disk so the agent never needs a second call:

  • Screenshots: browser-navigate and browser-snapshot auto-capture a viewport-sized (1280×720) JPEG to /tmp/pi-lean-portal/screenshot-<taskId>.jpg1.
  • Full Snapshots: Any snapshot compacted for size is cached in full at /tmp/pi-lean-portal/snapshot-*.txt (last 2 per task).

Both file paths are surfaced as hints in the tool output, which the agent can follow up with the read tool.


Navigation Guides

Built-in Pattern Guides

pi-lean-portal ships with four built-in pattern guides that appear in the guide footer when relevant:

GuideTriggerWhat It Covers
bot-detectionBot blocking detected (botDetected)Cloudflare, challenge pages, what NOT to do
cookie-consentDialog detected (dialogDetected)Accept/Reject buttons, Escape key, verification
paginationOn-demandNext buttons, infinite scroll, pages
searchOn-demandSearch boxes, comboboxes, result lists

Overriding Built-in Guides

A same-named .md file in ~/.pi/agent/pi-lean-portal/web-guides/ (e.g. bot-detection.md) shadows the builtin entirely; the whole guide is replaced, not field-merged. To keep a pattern guide firing, include trigger.signal: botDetected (or dialogDetected) in the frontmatter; omitting it disables the trigger.

---
category: pattern
trigger.signal: botDetected
icon: 🤖
shortName: my bot guide
---
Custom guidance text for bot challenges.

Site guides and pattern guides live in disjoint namespaces — a site guide for www.botdetection.com does not collide with the bot-detection pattern guide; both fire when applicable.

Domain Matching

A guide's declared domain matches the exact hostname or any subdomain of it. Declaring reddit.com covers www.reddit.com, old.reddit.com, and any other subdomain, so a guide saved once resurfaces across a site's URL variations. This is what makes guides cheap to author: you rarely need more than the apex domain. A domain may match multiple guides (a web guide plus one or more API guides) — all matching guides surface together, sorted host-first (API guides before web guides).

API guides from pi-lean-host (co-install)

When pi-lean-host is installed alongside portal and /api is on, its user-authored API guides also surface in the navigate footer alongside your web guides using the same reactive mechanism, with no extra setup. API guides sort first (API access is cheaper than browsing) but both always appear, so a partial-coverage API keeps its web guide for the gaps the API doesn't cover. The footer routes API guides to api-guide({domain, guide}) rather than web-guide. See the host README for the host side of the contract.


Backend Architecture

pi-lean-portal uses a plugin-based architecture. The core framework is backend-agnostic; plugins implement a standard BrowserPlugin interface, and the router dispatches tool calls to the right plugin based on a strategy parameter.

Four Shipped Backends

BackendEngineTypeDefault
chromiumChromiumNode/PlaywrightEnabled (auto strategy)
firefoxFirefoxNode/PlaywrightEnabled
chromium-pyChromiumPython/PlaywrightDisabled
firefox-pyFirefoxPython/PlaywrightDisabled

Install Firefox: run /web install firefox inside pi to download the Firefox binary for the Node firefox backend. For the Python parity backends, install Playwright inside backends/python-base/.venv.

All four shipped backends support screenshots (viewport-sized, not full-page), console capture, JS evaluation, bot detection, and dialog auto-dismissal. The one divergence: AbortSignal is advertised but silently ignored on the Python -py backends.

The -py backends are disabled by default, as they ship as parity references for the Python bridge contract and as templates for authoring your own Python-based backends.

How Plugin Selection Works

  • The order of plugins in the config array determines priority; the first enabled plugin is the "auto" default (typically Chromium).
  • The AI agent explicitly selects which backend to use via the strategy parameter in browser-navigate:
    • strategy="auto" → uses the first enabled plugin (typically Chromium)
    • strategy="firefox" → uses the Firefox Node backend
    • strategy="chromium-py" → uses the Python Chromium backend
  • No automatic fallbacks and no mid-session transitions. If a plugin fails, the agent decides what to do next.

Stealth & Custom Browser Backends

Stealth engines like Camoufox are intentionally left to users to author: drop a bridge.py into ~/.pi/agent/pi-lean-portal/user-backends/<name>-py/ — a separate tree from the package's backends/ directory (never edited after install, so custom backends survive updates) and never in the default fallback list. Register one in browser.plugins with an absolute pythonPath (see Configuration).

The full install flow, the quirks schema, and the worked Camoufox template live in contributed/README.md; contributed/CHOOSING.md covers when to reach for one at all (most users never need one). Node-based custom backends follow the same shape via PlaywrightPluginBase.


Configuration (settings.json)

Browser settings are read from ~/.pi/agent/settings.json (global) and .pi/settings.json (project-local, overrides global).

browser.plugins Array

Controls which browser backends are loaded. Entries are processed in order (the first enabled plugin is the "auto" default).

{
  "browser": {
    "plugins": [
      {
        "name": "chromium",    // Required: unique plugin identifier
        "dir": "chromium",     // Required: backend directory
        "enabled": true,       // Optional, defaults to true
        "config": {}           // Optional, passed to the plugin's init()
      }
    ]
  }
}

A user-installed stealth backend additionally puts an absolute pythonPath and a launch object into config — see contributed/README.md.

browser.defaultProfile

The profile mode or named profile used when browser-navigate doesn't specify a profile parameter:

{
  "browser": {
    "defaultProfile": "session"   // "none", "session", or a named profile string
  }
}

toolsetDefaults

Whether browser tools are enabled on fresh conversations. Read by the pi-tool-masking library at restore time, between the chat-branch tier and the toolset's packaged default:

{
  "toolsetDefaults": {
    "toolset-state:pi-lean-dimension.web": { "enabled": true },
    "toolset-state:pi-lean-dimension.web-learn": { "enabled": false },
    "toolset-state:pi-lean-dimension.search": { "enabled": true }
  }
}
  • Keys are the toolsets' persistKey values (toolset-state:<id>).
  • Omit a toolsetDefaults key to use the toolset's packaged default (web and search default true; web-learn defaults false).
  • The search key only applies when pi-lean-search is installed.
  • Pins do not apply in spawned subagent children (see pi-tool-masking 1.3.0's piToolMasking.childPolicy for the opt-out).

browser.maxStorageStateSize

Size threshold for profile state warnings (default: 10 MB):

{
  "browser": {
    "maxStorageStateSize": 10485760
  }
}

Tips & Best Practices

When to use web-fetch vs browser-navigate

Use web-fetchUse browser-navigate
Static content, docs, READMEsInteractive pages, JS-heavy SPAs
Quick lookups, no session neededForm filling, clicking, authentication
Content you want as clean MarkdownVisual inspection (auto-captured screenshots), pages where you need the accessibility tree

Working with @e Element References & Large Pages

The agent interacts via @e1, @e2 refs from the accessibility tree — no CSS selectors or XPath. Snapshots are auto-compacted to ~2500 chars; full trees and screenshots spill to /tmp/pi-lean-portal/ (paths are surfaced in tool output).

Bot Detection

When a page triggers anti-automation, the agent sees a warning plus a bot-detection guide footer, and challenge pages with <5 visible elements fail hard rather than being interacted with. If a site consistently blocks the shipped Chromium/Firefox, that's the trigger to install a stealth backend (see Stealth & Custom Browser Backends) — not something to configure up front.

Security

  • URLs are parsed with new URL() as input validation. Malformed URLs are rejected, but no SSRF boundary is enforced (a coding agent already has filesystem and shell access, so blocking localhost or private IPs would be theater)
  • Profile state is stored with restricted file permissions (0700 dirs, 0600 files)

License: AGPL-3.0-only

Footnotes

  1. Not full-page. ↩