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.
Files changed (55) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +608 -0
  3. package/backends/chromium/index.ts +50 -0
  4. package/backends/chromium-py/bridge.py +67 -0
  5. package/backends/firefox/index.ts +60 -0
  6. package/backends/firefox-py/bridge.py +64 -0
  7. package/backends/playwright-base/playwright-plugin.ts +1294 -0
  8. package/backends/python-adapter.ts +1141 -0
  9. package/backends/python-base/pi_browser_bridge/__init__.py +71 -0
  10. package/backends/python-base/pi_browser_bridge/accessibility.py +408 -0
  11. package/backends/python-base/pi_browser_bridge/bot_detection.py +115 -0
  12. package/backends/python-base/pi_browser_bridge/bridge.py +598 -0
  13. package/backends/python-base/pi_browser_bridge/playwright_base.py +1222 -0
  14. package/backends/python-base/pi_browser_bridge/transport.py +167 -0
  15. package/backends/python-base/pyproject.toml +15 -0
  16. package/browser-cookies.ts +88 -0
  17. package/browser-profile.ts +260 -0
  18. package/browser-status.ts +84 -0
  19. package/browser-toggle.ts +527 -0
  20. package/core/fetch-backend.ts +466 -0
  21. package/core/guides.ts +467 -0
  22. package/core/plugin-api.ts +302 -0
  23. package/core/plugin-config.ts +388 -0
  24. package/core/plugin-registry.ts +263 -0
  25. package/core/router.ts +1186 -0
  26. package/core/shared/accessibility-tree.ts +408 -0
  27. package/core/shared/bot-detection.ts +187 -0
  28. package/core/shared/browser-events.ts +111 -0
  29. package/core/shared/dom-extractor.ts +550 -0
  30. package/core/shared/nav-settle.ts +187 -0
  31. package/core/shared/paths.ts +56 -0
  32. package/core/shared/session-manager.ts +258 -0
  33. package/core/shared/settings-reader.ts +63 -0
  34. package/core/shared/snapshot-cache.ts +231 -0
  35. package/core/shared/storage-state.ts +560 -0
  36. package/core/shared/task-id.ts +77 -0
  37. package/core/shared/url-safety.ts +164 -0
  38. package/index.ts +253 -0
  39. package/package.json +63 -0
  40. package/ship-manifest.test.ts +12 -0
  41. package/tools/browser-back.ts +50 -0
  42. package/tools/browser-click.ts +74 -0
  43. package/tools/browser-console.ts +160 -0
  44. package/tools/browser-inspect.ts +136 -0
  45. package/tools/browser-navigate.ts +254 -0
  46. package/tools/browser-press.ts +80 -0
  47. package/tools/browser-scroll.ts +56 -0
  48. package/tools/browser-snapshot.ts +90 -0
  49. package/tools/browser-type.ts +60 -0
  50. package/tools/index.ts +19 -0
  51. package/tools/utils.ts +157 -0
  52. package/tools/web-fetch.ts +147 -0
  53. package/tools/web-guide.ts +55 -0
  54. package/tools/web-learn.ts +128 -0
  55. 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;