pi-lean-portal 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +608 -0
- package/backends/chromium/index.ts +50 -0
- package/backends/chromium-py/bridge.py +67 -0
- package/backends/firefox/index.ts +60 -0
- package/backends/firefox-py/bridge.py +64 -0
- package/backends/playwright-base/playwright-plugin.ts +1294 -0
- package/backends/python-adapter.ts +1141 -0
- package/backends/python-base/pi_browser_bridge/__init__.py +71 -0
- package/backends/python-base/pi_browser_bridge/accessibility.py +408 -0
- package/backends/python-base/pi_browser_bridge/bot_detection.py +115 -0
- package/backends/python-base/pi_browser_bridge/bridge.py +598 -0
- package/backends/python-base/pi_browser_bridge/playwright_base.py +1222 -0
- package/backends/python-base/pi_browser_bridge/transport.py +167 -0
- package/backends/python-base/pyproject.toml +15 -0
- package/browser-cookies.ts +88 -0
- package/browser-profile.ts +260 -0
- package/browser-status.ts +84 -0
- package/browser-toggle.ts +527 -0
- package/core/fetch-backend.ts +466 -0
- package/core/guides.ts +467 -0
- package/core/plugin-api.ts +302 -0
- package/core/plugin-config.ts +388 -0
- package/core/plugin-registry.ts +263 -0
- package/core/router.ts +1186 -0
- package/core/shared/accessibility-tree.ts +408 -0
- package/core/shared/bot-detection.ts +187 -0
- package/core/shared/browser-events.ts +111 -0
- package/core/shared/dom-extractor.ts +550 -0
- package/core/shared/nav-settle.ts +187 -0
- package/core/shared/paths.ts +56 -0
- package/core/shared/session-manager.ts +258 -0
- package/core/shared/settings-reader.ts +63 -0
- package/core/shared/snapshot-cache.ts +231 -0
- package/core/shared/storage-state.ts +560 -0
- package/core/shared/task-id.ts +77 -0
- package/core/shared/url-safety.ts +164 -0
- package/index.ts +253 -0
- package/package.json +63 -0
- package/ship-manifest.test.ts +12 -0
- package/tools/browser-back.ts +50 -0
- package/tools/browser-click.ts +74 -0
- package/tools/browser-console.ts +160 -0
- package/tools/browser-inspect.ts +136 -0
- package/tools/browser-navigate.ts +254 -0
- package/tools/browser-press.ts +80 -0
- package/tools/browser-scroll.ts +56 -0
- package/tools/browser-snapshot.ts +90 -0
- package/tools/browser-type.ts +60 -0
- package/tools/index.ts +19 -0
- package/tools/utils.ts +157 -0
- package/tools/web-fetch.ts +147 -0
- package/tools/web-guide.ts +55 -0
- package/tools/web-learn.ts +128 -0
- package/verify-ship-manifest.ts +126 -0
package/README.md
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
# pi-lean-portal User Guide
|
|
2
|
+
|
|
3
|
+
> **pi-lean-portal** is a plugin-based web browsing extension for the Pi coding
|
|
4
|
+
> agent. It gives the AI agent the ability to fetch web pages, interact with
|
|
5
|
+
> dynamic sites, inspect page structure, take screenshots, run JavaScript,
|
|
6
|
+
> and save/recall navigation guides — all through a set of tools and the `/web`
|
|
7
|
+
> command.
|
|
8
|
+
>
|
|
9
|
+
> Part of the [pi-lean-dimension](https://github.com/coreyryanhanson/pi-lean-dimension)
|
|
10
|
+
> web-tools suite. For SearXNG search support, install
|
|
11
|
+
> [`pi-lean-search`](https://www.npmjs.com/package/pi-lean-search).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
1. [Quick Start](#quick-start)
|
|
18
|
+
2. [`/web` Command — Browser Toggle & Profiles](#web-command--browser-toggle--profiles)
|
|
19
|
+
3. [All 12 Tools](#all-12-tools)
|
|
20
|
+
4. [Stateless Fetching (web-fetch)](#stateless-fetching-web-fetch)
|
|
21
|
+
5. [Navigation Guides (web-guide & web-learn)](#navigation-guides-web-guide--web-learn)
|
|
22
|
+
6. [`/web status` — Detailed Runtime Status](#web-status--detailed-runtime-status)
|
|
23
|
+
7. [Profiles — Persistent Sessions](#profiles--persistent-sessions)
|
|
24
|
+
8. [Cookie Management](#cookie-management)
|
|
25
|
+
9. [Backend Architecture](#backend-architecture)
|
|
26
|
+
10. [Configuration (settings.json)](#configuration-settingsjson)
|
|
27
|
+
11. [Tips & Best Practices](#tips--best-practices)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pi install npm:pi-lean-portal
|
|
35
|
+
npx playwright install chromium firefox
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Once loaded, you'll see a notification like:
|
|
39
|
+
|
|
40
|
+
> 🌐 Browser extension loaded (plugins: chromium, firefox)
|
|
41
|
+
|
|
42
|
+
The browser tools are **enabled by default**. You can:
|
|
43
|
+
|
|
44
|
+
- **Fetch a static page**: The agent uses `web-fetch` for quick, stateless
|
|
45
|
+
content retrieval (no JavaScript, no session).
|
|
46
|
+
- **Browse interactively**: The agent uses `browser-navigate` to visit a page,
|
|
47
|
+
then clicks, types, scrolls, and screenshots using `@e1`/`@e2` element
|
|
48
|
+
references from the accessibility tree.
|
|
49
|
+
|
|
50
|
+
> **Tip:** If you only need Markdown content from a static page, `web-fetch`
|
|
51
|
+
> is faster and lighter than launching a full browser session.
|
|
52
|
+
>
|
|
53
|
+
> **Playwright browser binaries are not downloaded during `npm install`.**
|
|
54
|
+
> Run `npx playwright install chromium firefox` separately. On first
|
|
55
|
+
> `browser-navigate` without them, you'll be prompted with the exact command.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## `/web` Command — Browser Toggle & Profiles
|
|
60
|
+
|
|
61
|
+
The `/web` command controls whether web tools are visible to the AI agent,
|
|
62
|
+
toggles guide-saving mode, and manages browser profiles.
|
|
63
|
+
|
|
64
|
+
### Three-State Toggle
|
|
65
|
+
|
|
66
|
+
| Command | Effect |
|
|
67
|
+
|---------|--------|
|
|
68
|
+
| `/web on` | **Browsing 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. |
|
|
69
|
+
| `/web learn` | **Browsing + guide-saving** — same as `on`, plus the `web-learn` tool is available so the AI can save/update navigation guides on request. |
|
|
70
|
+
| `/web off` | **All web tools hidden** — saves ~1500–2000 tokens per turn by removing tool schemas from the prompt. `web-fetch` and `web-search` are also hidden. |
|
|
71
|
+
|
|
72
|
+
### Check Current State
|
|
73
|
+
|
|
74
|
+
| Command | Effect |
|
|
75
|
+
|---------|--------|
|
|
76
|
+
| `/web` | Show current toggle status and available sub-commands. |
|
|
77
|
+
| `/web status` | **Detailed runtime status** — toggle state, plugin health, active sessions, and profiles on disk. |
|
|
78
|
+
|
|
79
|
+
### Why Toggle?
|
|
80
|
+
|
|
81
|
+
- **`/web off`**: When you're done browsing, turning tools off reduces context
|
|
82
|
+
usage and keeps the agent focused on coding.
|
|
83
|
+
- **`/web learn`**: Only enable this when you want the agent to save navigation
|
|
84
|
+
guidance for a site. The agent never calls `web-learn` unprompted — it must
|
|
85
|
+
be in learn mode.
|
|
86
|
+
- **State persists** across `/reload`, `/resume`, `/fork`, and `/tree`
|
|
87
|
+
navigation — the toggle remembers what you chose.
|
|
88
|
+
|
|
89
|
+
### Persistence
|
|
90
|
+
|
|
91
|
+
The toggle state is stored in the conversation's branch history. A fresh
|
|
92
|
+
conversation starts with the default from `settings.json` (`browserToggle.defaultEnabled`,
|
|
93
|
+
which defaults to `true`).
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## All 12 Tools
|
|
98
|
+
|
|
99
|
+
`pi-lean-portal` registers 12 tools. The first 9 require a **browser session**
|
|
100
|
+
(created by `browser-navigate`). `web-fetch`, `web-guide`, and `web-learn` are stateless.
|
|
101
|
+
|
|
102
|
+
**Auto-captured screenshots:** `browser-navigate` and `browser-snapshot` automatically
|
|
103
|
+
capture a screenshot to a temp file (`/tmp/pi-lean-portal/screenshot-<taskId>.jpg`).
|
|
104
|
+
Use the `read` tool to visually inspect the page when the accessibility tree isn't enough.
|
|
105
|
+
No viewport resizing occurs — screenshots are captured at the native 1280px width.
|
|
106
|
+
|
|
107
|
+
### 1. `browser-navigate` — Visit a Page
|
|
108
|
+
|
|
109
|
+
Navigates to a URL using a browser plugin (default: Chromium). Returns an
|
|
110
|
+
accessibility tree annotated with **@e1, @e2, …** element references.
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
Title: Example Domain
|
|
114
|
+
URL: https://example.com
|
|
115
|
+
Backend: chromium
|
|
116
|
+
Interactive elements: 3
|
|
117
|
+
|
|
118
|
+
link "More information..." [@e1]
|
|
119
|
+
→ https://www.iana.org/domains/example
|
|
120
|
+
…
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Parameters:**
|
|
124
|
+
|
|
125
|
+
- `url` — the target URL
|
|
126
|
+
- `strategy` (optional) — `"auto"` (default) or a plugin name like `"chromium"` or `"firefox"`
|
|
127
|
+
- `timeout` (optional) — seconds, default 30, max 120
|
|
128
|
+
- `profile` (optional) — `"none"`, `"session"`, or a named profile (see
|
|
129
|
+
[Profiles](#profiles--persistent-sessions))
|
|
130
|
+
|
|
131
|
+
**Output includes:**
|
|
132
|
+
|
|
133
|
+
- Page title, URL, backend name, element count
|
|
134
|
+
- Profile info when using profiles
|
|
135
|
+
- Bot detection warning when applicable
|
|
136
|
+
- Screenshot file path for visual inspection
|
|
137
|
+
- Guide footer appended when relevant guides apply
|
|
138
|
+
|
|
139
|
+
### 2. `browser-snapshot` — Refresh the Accessibility Tree
|
|
140
|
+
|
|
141
|
+
Returns the current page's accessibility tree with up-to-date `@e` refs. Also
|
|
142
|
+
captures a screenshot to a temp file (path in output). Use after clicking, typing,
|
|
143
|
+
or scrolling to get fresh element references.
|
|
144
|
+
|
|
145
|
+
- `full=true` — returns the complete tree (by default it's compacted to
|
|
146
|
+
~2500 characters for LLM efficiency)
|
|
147
|
+
|
|
148
|
+
### 3. `browser-click` — Click an Element
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
browser-click ref="@e5"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Clicks the element identified by its `@e` reference. Returns a fresh snapshot
|
|
155
|
+
of the page after the click.
|
|
156
|
+
|
|
157
|
+
### 4. `browser-type` — Type Into a Field
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
browser-type ref="@e3" text="hello world"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Clears the input, then types the given text. Works on textboxes, searchboxes,
|
|
164
|
+
and comboboxes.
|
|
165
|
+
|
|
166
|
+
### 5. `browser-scroll` — Scroll the Page
|
|
167
|
+
|
|
168
|
+
```text
|
|
169
|
+
browser-scroll direction="down"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Scrolls approximately one viewport height. Returns a fresh snapshot.
|
|
173
|
+
|
|
174
|
+
### 6. `browser-back` — Go Back
|
|
175
|
+
|
|
176
|
+
Navigates back in browser history. Returns the previous page's snapshot.
|
|
177
|
+
|
|
178
|
+
### 7. `browser-press` — Press a Keyboard Key
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
browser-press key="Enter"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Useful keys: `Enter`, `Tab`, `Escape`, `ArrowDown`, `ArrowUp`, `/`.
|
|
185
|
+
|
|
186
|
+
### 8. `browser-console` — Read Console / Run JS
|
|
187
|
+
|
|
188
|
+
Three modes:
|
|
189
|
+
|
|
190
|
+
| Usage | Effect |
|
|
191
|
+
|-------|--------|
|
|
192
|
+
| `browser-console expression="document.title"` | Evaluates JS and returns the result |
|
|
193
|
+
| `browser-console` (no params) | Returns captured console messages (log, warn, error, info) |
|
|
194
|
+
| `browser-console clear=true` | Clears the captured console log |
|
|
195
|
+
|
|
196
|
+
### 9. `browser-inspect` — Targeted Element Discovery
|
|
197
|
+
|
|
198
|
+
A lighter alternative to loading a full snapshot. Queries the page for specific
|
|
199
|
+
elements or extracts text content.
|
|
200
|
+
|
|
201
|
+
| Parameter | Example | Effect |
|
|
202
|
+
|-----------|---------|--------|
|
|
203
|
+
| `role` | `"link,button"` | Filter by ARIA role(s) |
|
|
204
|
+
| `name` | `"Submit"` | Filter by accessible name (case-insensitive) |
|
|
205
|
+
| `ref` | `"e5"` | Look up a specific `@e` ref |
|
|
206
|
+
| `subtree` | `"dialog"` | Scope to elements inside a container |
|
|
207
|
+
| `text` | `true` | Run DOM text extractor with `@e` annotations |
|
|
208
|
+
| `maxChars` | `500` | Limit output length; `0` for full content |
|
|
209
|
+
| `query` | `"pricing"` | Keyword filter (only active with `text=true`) |
|
|
210
|
+
|
|
211
|
+
> **Tip:** Use `browser-inspect role="dialog"` to quickly check for consent
|
|
212
|
+
> dialogs without loading the full tree.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Stateless Fetching (`web-fetch`)
|
|
217
|
+
|
|
218
|
+
`web-fetch` is a lightweight, stateless tool that fetches a URL and converts
|
|
219
|
+
HTML to Markdown. It does **not** create a browser session.
|
|
220
|
+
|
|
221
|
+
**When to use:**
|
|
222
|
+
|
|
223
|
+
- Static pages, API docs, READMEs, articles
|
|
224
|
+
- Quick lookups where JavaScript isn't needed
|
|
225
|
+
- When the interactive browser is blocked by bot detection — `web-fetch`
|
|
226
|
+
sometimes succeeds where the browser triggers a challenge
|
|
227
|
+
|
|
228
|
+
**Output handling:**
|
|
229
|
+
|
|
230
|
+
- Content is truncated to ~4000 characters inline
|
|
231
|
+
- Larger content is spilled to a temp file in `/tmp/pi-lean-portal/` — the agent can `read` the file with offset/limit for specific sections. This cache ensures that the agent can access the full page content even when it exceeds the LLM's immediate context window.
|
|
232
|
+
- Bot detection and JS-only shells are detected heuristically
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Navigation Guides (`web-guide` & `web-learn`)
|
|
237
|
+
|
|
238
|
+
### Built-in Pattern Guides
|
|
239
|
+
|
|
240
|
+
`pi-lean-portal` ships with four built-in pattern guides that appear in the guide footer when relevant:
|
|
241
|
+
|
|
242
|
+
| Guide | Trigger | What It Covers |
|
|
243
|
+
|-------|---------|----------------|
|
|
244
|
+
| `bot-detection` | When bot blocking is detected | Cloudflare, challenge pages, what NOT to do |
|
|
245
|
+
| `cookie-consent` | When a dialog is detected in the snapshot | Accept/Reject buttons, Escape key, verification |
|
|
246
|
+
| `pagination` | On-demand | Next buttons, infinite scroll, pages |
|
|
247
|
+
| `search` | On-demand | Search boxes, comboboxes, result lists |
|
|
248
|
+
|
|
249
|
+
### Viewing Guides
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
web-guide → lists all available guides
|
|
253
|
+
web-guide guide="cookie-consent" → shows guidance text
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The output includes the guide's last updated date and source (`builtin` or
|
|
257
|
+
`user`).
|
|
258
|
+
|
|
259
|
+
### Creating Your Own Site Guides (`web-learn`)
|
|
260
|
+
|
|
261
|
+
When the agent is in **learn mode** (`/web learn`), it can save or update
|
|
262
|
+
navigation guidance for specific sites using `web-learn`.
|
|
263
|
+
|
|
264
|
+
```text
|
|
265
|
+
web-learn domain="reddit.com" content="…guidance text…"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
This creates a `.md` file with YAML frontmatter in the `guides/` directory.
|
|
269
|
+
The guide becomes available immediately via `web-guide` and appears in the guide footer on
|
|
270
|
+
future navigations to that domain.
|
|
271
|
+
|
|
272
|
+
**Learn mode is off by default** — the agent never saves guides unprompted.
|
|
273
|
+
You must explicitly enable it with `/web learn`.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## `/web status` — Detailed Runtime Status
|
|
278
|
+
|
|
279
|
+
```text
|
|
280
|
+
/web status
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Shows everything about the browser runtime in one notification:
|
|
284
|
+
|
|
285
|
+
```text
|
|
286
|
+
🌐 Browser tools: ✅ on | 📖 Learn mode: ❌ off
|
|
287
|
+
────────────────────────────────────────
|
|
288
|
+
Status: idle
|
|
289
|
+
Plugins: chromium
|
|
290
|
+
Active sessions: 1
|
|
291
|
+
PW [chromium] https://example.com — Example Domain [profile: session]
|
|
292
|
+
Profiles: 2 on disk
|
|
293
|
+
📋 session (2.1 KB) ← active
|
|
294
|
+
shopping (0.3 KB)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Covers:
|
|
298
|
+
|
|
299
|
+
- **Toggle state** — whether browser/learn tools are active in the agent's context
|
|
300
|
+
- **Backend health** — idle, busy, or error state
|
|
301
|
+
- **All registered plugins** — enabled/disabled status
|
|
302
|
+
- **Active sessions** — current URL, title, profile name per session
|
|
303
|
+
- **Profiles on disk** — state size and which one is currently active
|
|
304
|
+
|
|
305
|
+
When `pi-lean-search` is also installed, the status bar shows two independent
|
|
306
|
+
glyphs: `● idle` (browser state) and `● searxng` (search health/state).
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Profiles — Persistent Sessions
|
|
311
|
+
|
|
312
|
+
Profiles let the AI agent maintain persistent browser state (cookies,
|
|
313
|
+
localStorage) across calls, conversations, and even across different subagents.
|
|
314
|
+
|
|
315
|
+
### Profile Modes
|
|
316
|
+
|
|
317
|
+
| Mode | `browser-navigate profile=` | Behavior |
|
|
318
|
+
|------|-----------------------------|----------|
|
|
319
|
+
| None | `"none"` | Clean slate every time — no cookies, no state |
|
|
320
|
+
| Session | `"session"` (default) | Persists state for the current conversation; survives `/reload` and `/resume` |
|
|
321
|
+
| Named | `"shopping"`, `"work"`, etc. | Shared across conversations and subagents — like browser tabs sharing a profile |
|
|
322
|
+
|
|
323
|
+
### Managing Profiles with `/web profile`
|
|
324
|
+
|
|
325
|
+
All profile management happens through the `/web profile` command:
|
|
326
|
+
|
|
327
|
+
| Sub-command | Effect |
|
|
328
|
+
|-------------|--------|
|
|
329
|
+
| `/web profile list` | List all profiles on disk with their state size |
|
|
330
|
+
| `/web profile create shopping` | Create a new named profile |
|
|
331
|
+
| `/web profile session` | Set conversation-scoped default to session mode |
|
|
332
|
+
| `/web profile none` | Reset default to ephemeral (no persistence) |
|
|
333
|
+
| `/web profile shopping` | Switch default profile to an existing named profile |
|
|
334
|
+
| `/web profile clear shopping` | Delete the saved state for a profile (keeps the directory) |
|
|
335
|
+
| `/web profile clear-all --confirm` | Clear ALL profile states |
|
|
336
|
+
| `/web profile prune --confirm` | Remove stale session profiles for ended conversations |
|
|
337
|
+
|
|
338
|
+
> **How it works:** Profile state is stored at
|
|
339
|
+
> `~/.pi/agent/pi-lean-portal/browser-state/<profile-name>/storage-state.json`.
|
|
340
|
+
> Session-scoped profiles use names like `_session-<piSessionId>` and are
|
|
341
|
+
> auto-cleaned when the pi conversation ends.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Cookie Management
|
|
346
|
+
|
|
347
|
+
The `/web cookies` command lets you inspect and clear session cookies:
|
|
348
|
+
|
|
349
|
+
| Sub-command | Effect |
|
|
350
|
+
|-------------|--------|
|
|
351
|
+
| `/web cookies list` | List all cookies in the current session (name, value, domain, expiry, flags) |
|
|
352
|
+
| `/web cookies clear --confirm` | Clear ALL cookies for the current session |
|
|
353
|
+
|
|
354
|
+
> Cookies are saved as part of profile state. When you switch profiles,
|
|
355
|
+
> the cookies from the old profile are preserved and the new profile's
|
|
356
|
+
> cookies are loaded.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Backend Architecture
|
|
361
|
+
|
|
362
|
+
`pi-lean-portal` uses a **plugin-based architecture**. The core framework is
|
|
363
|
+
backend-agnostic — plugins implement a standard `BrowserPlugin` interface,
|
|
364
|
+
and the router dispatches tool calls to the right plugin based on a
|
|
365
|
+
`strategy` parameter.
|
|
366
|
+
|
|
367
|
+
### Four Shipped Backends
|
|
368
|
+
|
|
369
|
+
The extension ships with **four browser backends** out of the box:
|
|
370
|
+
|
|
371
|
+
| Backend | Engine | Type | Default |
|
|
372
|
+
|---------|--------|------|---------|
|
|
373
|
+
| `chromium` | Chromium | Node/Playwright | **Enabled** (auto strategy) |
|
|
374
|
+
| `firefox` | Firefox | Node/Playwright | **Enabled** |
|
|
375
|
+
| `chromium-py` | Chromium | Python/Playwright | Disabled |
|
|
376
|
+
| `firefox-py` | Firefox | Python/Playwright | Disabled |
|
|
377
|
+
|
|
378
|
+
> **Install Firefox:** `npx playwright install firefox` to use the Node
|
|
379
|
+
> `firefox` backend. For the Python parity backends, install Playwright
|
|
380
|
+
> inside `backends/python-base/.venv`.
|
|
381
|
+
|
|
382
|
+
### Plugin Capabilities
|
|
383
|
+
|
|
384
|
+
All four shipped backends share the same capability set (they all use
|
|
385
|
+
Playwright under the hood). The router adapts based on each plugin's
|
|
386
|
+
capability advertisement:
|
|
387
|
+
|
|
388
|
+
| Capability | Chromium / Firefox (Node) | `-py` backends (Python) |
|
|
389
|
+
|------------|--------------------------|------------------------|
|
|
390
|
+
| Full-page screenshots | ✅ | ✅ |
|
|
391
|
+
| Console message capture | ✅ | ✅ |
|
|
392
|
+
| JavaScript evaluation | ✅ | ✅ |
|
|
393
|
+
| Bot detection | ✅ | ✅ |
|
|
394
|
+
| Dialog auto-dismissal | ✅ | ✅ |
|
|
395
|
+
| AbortSignal support | ✅ | Advertised, silently ignored |
|
|
396
|
+
|
|
397
|
+
> The `-py` Python backends are disabled by default. They ship as parity
|
|
398
|
+
> references for the Python bridge contract and as templates for users
|
|
399
|
+
> authoring their own Python-based backends (see the custom backends
|
|
400
|
+
> section below). Keep `chromium-py` as a reference when reading the
|
|
401
|
+
> bridge code.
|
|
402
|
+
|
|
403
|
+
### How Plugin Selection Works
|
|
404
|
+
|
|
405
|
+
- The **order** of plugins in the config array determines priority — the
|
|
406
|
+
first enabled plugin is the "auto" default (typically Chromium).
|
|
407
|
+
- The **AI agent explicitly selects** which backend to use via the
|
|
408
|
+
`strategy` parameter in `browser-navigate`:
|
|
409
|
+
- `strategy="auto"` → uses the first enabled plugin (typically Chromium)
|
|
410
|
+
- `strategy="firefox"` → uses the Firefox Node backend
|
|
411
|
+
- `strategy="chromium-py"` → uses the Python Chromium backend
|
|
412
|
+
- **No automatic fallbacks** and **no mid-session transitions** — if a
|
|
413
|
+
plugin fails, the agent decides what to do next.
|
|
414
|
+
- The extension auto-detects whether a plugin is Node-based (`index.ts`)
|
|
415
|
+
or Python-based (`bridge.py`) by inspecting the directory.
|
|
416
|
+
|
|
417
|
+
### Stealth & Custom Browser Backends (Planned)
|
|
418
|
+
|
|
419
|
+
This package ships four backends — `chromium`, `firefox`, `chromium-py`,
|
|
420
|
+
and `firefox-py` — all built on Playwright. Additional browser support
|
|
421
|
+
(including stealth engines like **Camoufox**) is intentionally **left
|
|
422
|
+
to users** to author and drop in, rather than being bundled with the
|
|
423
|
+
package.
|
|
424
|
+
|
|
425
|
+
Most of the building blocks are already in place: the `BrowserPlugin`
|
|
426
|
+
interface, the Python bridge base class (`PlaywrightBridge`), and
|
|
427
|
+
config-driven plugin loading that auto-detects `index.ts` (Node) or
|
|
428
|
+
`bridge.py` (Python) entry points. Two pieces of infrastructure are still
|
|
429
|
+
needed before user-authored stealth backends are practical:
|
|
430
|
+
|
|
431
|
+
1. **A quirks system** — so a backend can declare things like a custom
|
|
432
|
+
context factory (e.g. Camoufox's `NewContext` for fingerprint
|
|
433
|
+
injection), an eval-script prefix, or a fingerprint-managed viewport,
|
|
434
|
+
instead of being clobbered by the base class's hardcoded defaults.
|
|
435
|
+
2. **A config channel** from the TypeScript adapter to the Python bridge
|
|
436
|
+
subprocess — so launch options like `headless`, target OS, proxy, and
|
|
437
|
+
binary path can reach the bridge.
|
|
438
|
+
|
|
439
|
+
Once those land, the plan is for users to author additional backends the
|
|
440
|
+
same way they author site guides today — by dropping files into a
|
|
441
|
+
user-owned directory (e.g. `~/.pi/agent/pi-lean-portal/backends/`,
|
|
442
|
+
analogous to the `web-guides/` directory) and registering them in
|
|
443
|
+
`browser.plugins`. The shipped backends live inside the package's own
|
|
444
|
+
`backends/` directory; that directory should not be edited after install
|
|
445
|
+
(modifications would be lost on the next package update), which is exactly
|
|
446
|
+
why a separate user-owned directory is the supported path for custom and
|
|
447
|
+
stealth backends.
|
|
448
|
+
|
|
449
|
+
The shape a custom backend's config entry will take looks like:
|
|
450
|
+
|
|
451
|
+
```jsonc
|
|
452
|
+
{
|
|
453
|
+
"browser": {
|
|
454
|
+
"plugins": [
|
|
455
|
+
{ "name": "chromium", "dir": "chromium", "enabled": true, "config": {} },
|
|
456
|
+
{ "name": "firefox", "dir": "firefox", "enabled": true, "config": {} },
|
|
457
|
+
{ "name": "camoufox-py", "dir": "camoufox-py", "enabled": false, "config": {
|
|
458
|
+
"pythonPath": "/path/to/camoufox-py/.venv/bin/python"
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
]
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
This support will arrive in a future update. Until then, the four shipped
|
|
467
|
+
backends can be toggled via the `enabled` field below.
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## Configuration (`settings.json`)
|
|
472
|
+
|
|
473
|
+
Browser settings are read from `~/.pi/agent/settings.json` (global) and
|
|
474
|
+
`.pi/settings.json` (project-local, overrides global).
|
|
475
|
+
|
|
476
|
+
### `browser.plugins` Array
|
|
477
|
+
|
|
478
|
+
Controls which browser backends are loaded. Entries are processed in order
|
|
479
|
+
(the first enabled plugin is the `"auto"` default).
|
|
480
|
+
|
|
481
|
+
```jsonc
|
|
482
|
+
{
|
|
483
|
+
"browser": {
|
|
484
|
+
"plugins": [
|
|
485
|
+
{
|
|
486
|
+
"name": "chromium", // Required: unique plugin identifier
|
|
487
|
+
"dir": "chromium", // Required: directory under backends/
|
|
488
|
+
"enabled": true, // Optional, defaults to true
|
|
489
|
+
"config": {} // Optional, passed to the plugin's init()
|
|
490
|
+
}
|
|
491
|
+
]
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
Each entry requires only a unique name, a backend directory path, and an
|
|
497
|
+
optional `config` object passed to the plugin's `init()`. For the Python
|
|
498
|
+
backends, `config` carries options like `pythonPath` (the shape shown for
|
|
499
|
+
`camoufox-py` above is representative of how a user-authored Python
|
|
500
|
+
backend will be configured).
|
|
501
|
+
|
|
502
|
+
### `browser.defaultProfile`
|
|
503
|
+
|
|
504
|
+
The profile mode or named profile used when `browser-navigate` doesn't specify
|
|
505
|
+
a `profile` parameter:
|
|
506
|
+
|
|
507
|
+
```jsonc
|
|
508
|
+
{
|
|
509
|
+
"browser": {
|
|
510
|
+
"defaultProfile": "session" // "none", "session", or a named profile string
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### `browserToggle.defaultEnabled`
|
|
516
|
+
|
|
517
|
+
Whether browser tools are enabled on fresh conversations:
|
|
518
|
+
|
|
519
|
+
```jsonc
|
|
520
|
+
{
|
|
521
|
+
"browserToggle": {
|
|
522
|
+
"defaultEnabled": true
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### `browser.maxStorageStateSize`
|
|
528
|
+
|
|
529
|
+
Size threshold for profile state warnings (default: 10 MB):
|
|
530
|
+
|
|
531
|
+
```jsonc
|
|
532
|
+
{
|
|
533
|
+
"browser": {
|
|
534
|
+
"maxStorageStateSize": 10485760
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## Tips & Best Practices
|
|
542
|
+
|
|
543
|
+
### When to use `web-fetch` vs `browser-navigate`
|
|
544
|
+
|
|
545
|
+
| Use `web-fetch` | Use `browser-navigate` |
|
|
546
|
+
|-----------------|----------------------|
|
|
547
|
+
| Static content, docs, READMEs | Interactive pages, JS-heavy SPAs |
|
|
548
|
+
| Quick lookups, no session needed | Form filling, clicking, authentication |
|
|
549
|
+
| Bot-detected pages (fallback) | Visual inspection (auto-captured screenshots) |
|
|
550
|
+
| Content you want as clean Markdown | Pages where you need the accessibility tree |
|
|
551
|
+
|
|
552
|
+
### Working with `@e` Element References
|
|
553
|
+
|
|
554
|
+
- `@e1`, `@e2`, etc. are assigned based on the accessibility tree order
|
|
555
|
+
- After clicking or scrolling, **always take a fresh snapshot** — old `@e`
|
|
556
|
+
refs become stale
|
|
557
|
+
- `browser-inspect` is cheaper than `browser-snapshot full=true` for finding
|
|
558
|
+
specific elements
|
|
559
|
+
|
|
560
|
+
### Navigating Large Pages
|
|
561
|
+
|
|
562
|
+
- Snapshots are automatically compacted to ~2500 characters
|
|
563
|
+
- Very large pages (>8000 chars) preserve the top ~2000 chars
|
|
564
|
+
- The **full tree is cached to disk** at `/tmp/pi-lean-portal/snapshot-*.txt` —
|
|
565
|
+
you can use `read` on the cache file with offset/limit
|
|
566
|
+
- `browser-inspect text=true query="keyword"` finds specific content without
|
|
567
|
+
loading the full tree
|
|
568
|
+
|
|
569
|
+
### Bot Detection
|
|
570
|
+
|
|
571
|
+
When a page triggers anti-automation:
|
|
572
|
+
|
|
573
|
+
1. The agent sees a warning in the navigate output
|
|
574
|
+
2. The **bot-detection guide** footer appears with strategies available via `web-guide`
|
|
575
|
+
3. If very few elements are detected (<5), the navigation is treated as
|
|
576
|
+
a hard failure — the agent won't try to interact with a challenge page
|
|
577
|
+
4. Try `web-fetch` on the same URL — it sometimes succeeds where the
|
|
578
|
+
interactive browser doesn't
|
|
579
|
+
|
|
580
|
+
### Guide Creation Discipline
|
|
581
|
+
|
|
582
|
+
When creating navigation guides with `web-learn`:
|
|
583
|
+
|
|
584
|
+
- Keep guidance **concise** (≤800 chars recommended)
|
|
585
|
+
- Focus on: page structure, consent dialogs, known quirks, and useful
|
|
586
|
+
selector patterns
|
|
587
|
+
- Include domains so the guide appears in the footer on future navigations
|
|
588
|
+
- No runtime enforcement on guide length, but brevity helps the LLM
|
|
589
|
+
|
|
590
|
+
### Security
|
|
591
|
+
|
|
592
|
+
- URLs are validated against private IP ranges (10.x, 172.16-31.x,
|
|
593
|
+
192.168.x, 127.x, 169.254.169.254) — localhost and internal networks
|
|
594
|
+
are blocked by default
|
|
595
|
+
- Dangerous URL schemes are blocked: `file:`, `ftp:`, `data:`,
|
|
596
|
+
`javascript:`, `vbscript:`
|
|
597
|
+
- Secrets in URLs are detected heuristically
|
|
598
|
+
- Profile state is stored with restricted file permissions (0700 dirs,
|
|
599
|
+
0600 files)
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
> `pi-lean-portal` is part of the
|
|
604
|
+
> [pi-lean-dimension](https://github.com/coreyryanhanson/pi-lean-dimension)
|
|
605
|
+
> web-tools suite. For questions, issues, or feature requests, check the
|
|
606
|
+
> project's documentation or open an issue.
|
|
607
|
+
>
|
|
608
|
+
> License: AGPL-3.0-only
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chromium Plugin — Native Node backend using Playwright Chromium.
|
|
3
|
+
*
|
|
4
|
+
* Thin subclass of PlaywrightPluginBase. All shared logic lives in
|
|
5
|
+
* backends/playwright-base/playwright-plugin.ts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { chromium } from "playwright";
|
|
9
|
+
import type { Browser } from "playwright";
|
|
10
|
+
import { PlaywrightPluginBase } from "../playwright-base/playwright-plugin.js";
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_CAPABILITIES,
|
|
13
|
+
type PluginCapabilities,
|
|
14
|
+
} from "../../core/plugin-api.js";
|
|
15
|
+
|
|
16
|
+
// ─── Capabilities ──────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
const CHROMIUM_CAPABILITIES: PluginCapabilities = {
|
|
19
|
+
...DEFAULT_CAPABILITIES,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ─── ChromiumPlugin ───────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export class ChromiumPlugin extends PlaywrightPluginBase {
|
|
25
|
+
readonly name = "chromium";
|
|
26
|
+
readonly capabilities = CHROMIUM_CAPABILITIES;
|
|
27
|
+
|
|
28
|
+
/** Hardcoded Chrome user-agent — no dynamic capture needed. */
|
|
29
|
+
protected get userAgent(): string {
|
|
30
|
+
return "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected async launchBrowser(): Promise<Browser> {
|
|
34
|
+
return chromium.launch({
|
|
35
|
+
headless: true,
|
|
36
|
+
args: [
|
|
37
|
+
"--no-sandbox",
|
|
38
|
+
"--disable-setuid-sandbox",
|
|
39
|
+
"--disable-dev-shm-usage",
|
|
40
|
+
"--disable-gpu",
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected get installHint(): string {
|
|
46
|
+
return "Browser not installed. Run: npx playwright install chromium firefox";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default ChromiumPlugin;
|