pi-lean-portal 0.1.0 → 0.2.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/README.md +112 -50
- package/backends/chromium/index.ts +5 -13
- package/backends/chromium-py/__pycache__/bridge.cpython-313.pyc +0 -0
- package/backends/chromium-py/bridge.py +0 -2
- package/backends/firefox/index.ts +6 -5
- package/backends/firefox-py/__pycache__/bridge.cpython-313.pyc +0 -0
- package/backends/firefox-py/bridge.py +7 -6
- package/backends/playwright-base/playwright-plugin.ts +241 -398
- package/backends/python-adapter.ts +182 -83
- package/backends/python-base/pi_browser_bridge/__init__.py +1 -42
- package/backends/python-base/pi_browser_bridge/accessibility.py +12 -147
- package/backends/python-base/pi_browser_bridge/bot_detection.py +12 -37
- package/backends/python-base/pi_browser_bridge/bridge.py +249 -322
- package/backends/python-base/pi_browser_bridge/browser_data.py +92 -0
- package/backends/python-base/pi_browser_bridge/patch_playwright.py +321 -0
- package/backends/python-base/pi_browser_bridge/playwright_base.py +511 -299
- package/browser-toggle.ts +33 -69
- package/core/fetch-backend.ts +0 -5
- package/core/plugin-api.ts +6 -33
- package/core/plugin-config.ts +75 -58
- package/core/plugin-registry.ts +11 -49
- package/core/router.ts +59 -98
- package/core/shared/accessibility-tree.ts +10 -143
- package/core/shared/bot-detection.ts +31 -77
- package/core/shared/browser-data.json +183 -0
- package/core/shared/browser-data.ts +50 -0
- package/core/shared/browser-events.ts +6 -6
- package/core/shared/dom-extractor.ts +97 -36
- package/core/shared/nav-settle.ts +12 -15
- package/core/shared/paths.ts +3 -0
- package/core/shared/session-manager.ts +7 -21
- package/{verify-ship-manifest.ts → core/shared/ship-manifest.ts} +34 -9
- package/core/shared/snapshot-cache.ts +7 -6
- package/core/shared/storage-state.ts +40 -9
- package/index.ts +42 -7
- package/package.json +8 -3
- package/ship-manifest.test.ts +8 -3
- package/tools/browser-inspect.ts +2 -6
- package/tools/browser-navigate.ts +7 -5
- package/tools/browser-snapshot.ts +2 -5
- package/tools/utils.ts +22 -4
- package/tools/web-fetch.ts +3 -4
package/README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# pi-lean-portal User Guide
|
|
2
2
|
|
|
3
|
-
> **pi-lean-portal**
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
>
|
|
3
|
+
> **pi-lean-portal** gives the Pi coding agent interactive web browsing —
|
|
4
|
+
> Playwright Chromium/Firefox, accessibility-tree snapshots with `@e` element
|
|
5
|
+
> refs, persistent profiles, cookies, and navigation guides that resurface by
|
|
6
|
+
> domain. A `/web` toggle removes the tools from the agent's context when
|
|
7
|
+
> switched off, so web browsing doesn't consume tokens on sessions that aren't
|
|
8
|
+
> doing web work. If a site blocks the shipped browsers, drop in your own
|
|
9
|
+
> backend (e.g. [Camoufox](https://github.com/nichochar/camoufox)) — as far as
|
|
10
|
+
> we're aware, no other Pi web plugin lets you run a browser backend you wrote
|
|
11
|
+
> yourself.
|
|
8
12
|
>
|
|
9
13
|
> Part of the [pi-lean-dimension](https://github.com/coreyryanhanson/pi-lean-dimension)
|
|
10
14
|
> web-tools suite. For SearXNG search support, install
|
|
@@ -15,16 +19,17 @@
|
|
|
15
19
|
## Table of Contents
|
|
16
20
|
|
|
17
21
|
1. [Quick Start](#quick-start)
|
|
18
|
-
2. [
|
|
19
|
-
3. [
|
|
20
|
-
4. [
|
|
21
|
-
5. [
|
|
22
|
-
6. [
|
|
23
|
-
7. [
|
|
24
|
-
8. [
|
|
25
|
-
9. [
|
|
26
|
-
10. [
|
|
27
|
-
11. [
|
|
22
|
+
2. [Extending it](#extending-it)
|
|
23
|
+
3. [`/web` Command — Browser Toggle & Profiles](#web-command--browser-toggle--profiles)
|
|
24
|
+
4. [All 12 Tools](#all-12-tools)
|
|
25
|
+
5. [Stateless Fetching (web-fetch)](#stateless-fetching-web-fetch)
|
|
26
|
+
6. [Navigation Guides (web-guide & web-learn)](#navigation-guides-web-guide--web-learn)
|
|
27
|
+
7. [`/web status` — Detailed Runtime Status](#web-status--detailed-runtime-status)
|
|
28
|
+
8. [Profiles — Persistent Sessions](#profiles--persistent-sessions)
|
|
29
|
+
9. [Cookie Management](#cookie-management)
|
|
30
|
+
10. [Backend Architecture](#backend-architecture)
|
|
31
|
+
11. [Configuration (settings.json)](#configuration-settingsjson)
|
|
32
|
+
12. [Tips & Best Practices](#tips--best-practices)
|
|
28
33
|
|
|
29
34
|
---
|
|
30
35
|
|
|
@@ -56,6 +61,21 @@ The browser tools are **enabled by default**. You can:
|
|
|
56
61
|
|
|
57
62
|
---
|
|
58
63
|
|
|
64
|
+
## Extending it
|
|
65
|
+
|
|
66
|
+
Beyond the toggle, two surfaces are user-extensible rather than hardcoded:
|
|
67
|
+
|
|
68
|
+
- **Navigation guides** — `web-learn` saves site-specific playbooks that
|
|
69
|
+
auto-match by domain and resurface in later sessions. See
|
|
70
|
+
[Navigation Guides](#navigation-guides-web-guide--web-learn).
|
|
71
|
+
- **Custom browser backends** — if a site blocks the shipped Chromium/Firefox,
|
|
72
|
+
drop a `bridge.py` subclass into `~/.pi/agent/pi-lean-portal/user-backends/`
|
|
73
|
+
and drive a patched engine like [Camoufox](https://github.com/nichochar/camoufox)
|
|
74
|
+
yourself. The full flow lives in [Backend Architecture](#backend-architecture)
|
|
75
|
+
and [`contributed/README.md`](./contributed/README.md).
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
59
79
|
## `/web` Command — Browser Toggle & Profiles
|
|
60
80
|
|
|
61
81
|
The `/web` command controls whether web tools are visible to the AI agent,
|
|
@@ -414,39 +434,76 @@ capability advertisement:
|
|
|
414
434
|
- The extension auto-detects whether a plugin is Node-based (`index.ts`)
|
|
415
435
|
or Python-based (`bridge.py`) by inspecting the directory.
|
|
416
436
|
|
|
417
|
-
### Stealth & Custom Browser Backends
|
|
437
|
+
### Stealth & Custom Browser Backends
|
|
418
438
|
|
|
419
439
|
This package ships four backends — `chromium`, `firefox`, `chromium-py`,
|
|
420
440
|
and `firefox-py` — all built on Playwright. Additional browser support
|
|
421
441
|
(including stealth engines like **Camoufox**) is intentionally **left
|
|
422
442
|
to users** to author and drop in, rather than being bundled with the
|
|
423
|
-
package.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
443
|
+
package. The infrastructure for this is now in place: a **quirks system**
|
|
444
|
+
lets a backend declare how it diverges from the base Playwright behavior,
|
|
445
|
+
and a **config channel** (`browser.init` RPC) forwards launch options
|
|
446
|
+
from `settings.json` to the Python bridge subprocess.
|
|
447
|
+
|
|
448
|
+
Most users will never need a stealth backend (see
|
|
449
|
+
[`contributed/CHOOSING.md`](./contributed/CHOOSING.md) for when to reach
|
|
450
|
+
for one at all). When you do, the flow is:
|
|
451
|
+
|
|
452
|
+
1. **Drop a `bridge.py` into the user-backends tree.** The convention is
|
|
453
|
+
`~/.pi/agent/pi-lean-portal/user-backends/<name>-py/bridge.py` (the
|
|
454
|
+
`-py` suffix mirrors the shipped `chromium-py` / `firefox-py`). This is
|
|
455
|
+
a separate tree from the package's own `backends/` directory, which is
|
|
456
|
+
not edited after install — exactly so custom backends survive updates.
|
|
457
|
+
2. **Create a venv and fetch the engine binary** (e.g.
|
|
458
|
+
`python -m camoufox fetch`). The shared `pi_browser_bridge` library is
|
|
459
|
+
injected onto `PYTHONPATH` automatically at spawn time, so you do not
|
|
460
|
+
need to `pip install` it.
|
|
461
|
+
3. **Register it in `browser.plugins`** with an **absolute** `pythonPath`
|
|
462
|
+
and a `launch` object whose keys are forwarded to the bridge.
|
|
463
|
+
4. **Verify with `/web status`** and a `browser-navigate`.
|
|
464
|
+
|
|
465
|
+
The shipped **Camoufox template** at
|
|
466
|
+
[`contributed/camoufox-py/bridge.py`](./contributed/camoufox-py/bridge.py)
|
|
467
|
+
is a worked example — copy it as a starting point. The full install flow,
|
|
468
|
+
the quirks schema reference, and the security model (user-backends are
|
|
469
|
+
**trusted user code** — never auto-downloaded, no plugin marketplace) live
|
|
470
|
+
in [`contributed/README.md`](./contributed/README.md). The decision doc at
|
|
471
|
+
[`contributed/CHOOSING.md`](./contributed/CHOOSING.md) covers when to use
|
|
472
|
+
a stealth backend at all and the two lifecycle patterns for implementing
|
|
473
|
+
your own.
|
|
474
|
+
|
|
475
|
+
#### Writing your own backend (high level)
|
|
476
|
+
|
|
477
|
+
A custom Python backend is a subclass of `PlaywrightBridge`
|
|
478
|
+
(`backends/python-base/pi_browser_bridge/playwright_base.py`) that sets
|
|
479
|
+
the **quirks flags** its engine needs as class attributes and overrides
|
|
480
|
+
the launch hook matching how the engine owns Playwright. The flags
|
|
481
|
+
(`_fingerprint_managed_context`, `_eval_prefix`, `_scroll_via_wheel`,
|
|
482
|
+
`_skip_default_viewport`, `_skip_networkidle`, `_wrap_mw_eval_in_eval`)
|
|
483
|
+
all default off, so a subclass that sets none of them is bit-identical to
|
|
484
|
+
the shipped `chromium-py` / `firefox-py`. The full table with effects is
|
|
485
|
+
in [`contributed/README.md`](./contributed/README.md#quirks-schema-reference).
|
|
486
|
+
|
|
487
|
+
Node-based custom backends follow the same shape via the
|
|
488
|
+
`PlaywrightPluginBase` class — the auto-detection in `plugin-loading`
|
|
489
|
+
picks up `index.ts` (Node) or `bridge.py` (Python) entry points from the
|
|
490
|
+
user-backends directory.
|
|
491
|
+
|
|
492
|
+
#### Tests are auto-discovered
|
|
493
|
+
|
|
494
|
+
You usually do **not** need to write your own tests. The contributed
|
|
495
|
+
runner at `__tests__/run-contributed-suites.test.ts` discovers every
|
|
496
|
+
backend under `user-backends/*-py/`, loads config from the test-local
|
|
497
|
+
`settings.json`, and runs the shared contract + persistence + parity +
|
|
498
|
+
quirks-introspection suites against it — forwarding your configured
|
|
499
|
+
`launch` options. Opt in with `CONTRIB_RUN=1`:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
npm run setup:miniwob # one-time: clone MiniWoB++ content
|
|
503
|
+
CONTRIB_RUN=1 npx vitest run packages/pi-lean-portal/__tests__/run-contributed-suites.test.ts
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
A custom backend's config entry looks like:
|
|
450
507
|
|
|
451
508
|
```jsonc
|
|
452
509
|
{
|
|
@@ -454,8 +511,9 @@ The shape a custom backend's config entry will take looks like:
|
|
|
454
511
|
"plugins": [
|
|
455
512
|
{ "name": "chromium", "dir": "chromium", "enabled": true, "config": {} },
|
|
456
513
|
{ "name": "firefox", "dir": "firefox", "enabled": true, "config": {} },
|
|
457
|
-
{ "name": "camoufox-py", "dir": "camoufox-py", "enabled":
|
|
458
|
-
"pythonPath": "/
|
|
514
|
+
{ "name": "camoufox-py", "dir": "camoufox-py", "enabled": true, "config": {
|
|
515
|
+
"pythonPath": "/home/me/.pi/agent/pi-lean-portal/user-backends/camoufox-py/.venv/bin/python",
|
|
516
|
+
"launch": { "headless": true, "os": "windows", "humanize": true }
|
|
459
517
|
}
|
|
460
518
|
}
|
|
461
519
|
]
|
|
@@ -463,8 +521,12 @@ The shape a custom backend's config entry will take looks like:
|
|
|
463
521
|
}
|
|
464
522
|
```
|
|
465
523
|
|
|
466
|
-
|
|
467
|
-
|
|
524
|
+
`pythonPath` must be **absolute**; `dir` resolves against the user-backends
|
|
525
|
+
root (multi-root discovery: package `backends/` → `USER_BACKENDS_DIR` →
|
|
526
|
+
absolute). `launch` keys are forwarded to the bridge as
|
|
527
|
+
`plugin_config.launch` via the `browser.init` RPC. Stealth backends are
|
|
528
|
+
never in the default fallback list — a fresh install with no
|
|
529
|
+
`browser.plugins` loads only the four shipped backends.
|
|
468
530
|
|
|
469
531
|
---
|
|
470
532
|
|
|
@@ -495,9 +557,9 @@ Controls which browser backends are loaded. Entries are processed in order
|
|
|
495
557
|
|
|
496
558
|
Each entry requires only a unique name, a backend directory path, and an
|
|
497
559
|
optional `config` object passed to the plugin's `init()`. For the Python
|
|
498
|
-
backends, `config` carries
|
|
499
|
-
`camoufox-py`
|
|
500
|
-
|
|
560
|
+
backends, `config` carries `pythonPath` and a `launch` object (the shape
|
|
561
|
+
shown for `camoufox-py` in the [Stealth & Custom Browser Backends](#stealth--custom-browser-backends)
|
|
562
|
+
section above is the reference for a user-authored Python backend).
|
|
501
563
|
|
|
502
564
|
### `browser.defaultProfile`
|
|
503
565
|
|
|
@@ -3,27 +3,19 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Thin subclass of PlaywrightPluginBase. All shared logic lives in
|
|
5
5
|
* backends/playwright-base/playwright-plugin.ts.
|
|
6
|
+
*
|
|
7
|
+
* Launches Chromium directly and drives its own page; the plugin is the
|
|
8
|
+
* sole browser owner. No external-attach endpoint is exposed.
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
import { chromium } from "playwright";
|
|
9
12
|
import type { Browser } from "playwright";
|
|
10
13
|
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 ───────────────────────────────────────────────
|
|
14
|
+
import { DEFAULT_CAPABILITIES } from "../../core/plugin-api.js";
|
|
23
15
|
|
|
24
16
|
export class ChromiumPlugin extends PlaywrightPluginBase {
|
|
25
17
|
readonly name = "chromium";
|
|
26
|
-
readonly capabilities =
|
|
18
|
+
readonly capabilities = DEFAULT_CAPABILITIES;
|
|
27
19
|
|
|
28
20
|
/** Hardcoded Chrome user-agent — no dynamic capture needed. */
|
|
29
21
|
protected get userAgent(): string {
|
|
Binary file
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
* Thin subclass of PlaywrightPluginBase. All shared logic lives in
|
|
5
5
|
* backends/playwright-base/playwright-plugin.ts.
|
|
6
6
|
*
|
|
7
|
+
* Launches Firefox directly with `firefox.launch()` and drives its own
|
|
8
|
+
* page; the plugin is the sole browser owner. No external-attach
|
|
9
|
+
* endpoint is exposed.
|
|
10
|
+
*
|
|
7
11
|
* Uses probe-then-cache UA capture at first launch, with a hardcoded
|
|
8
|
-
* fallback Firefox UA string.
|
|
9
|
-
* not accept Chromium sandbox flags.
|
|
12
|
+
* fallback Firefox UA string.
|
|
10
13
|
*/
|
|
11
14
|
|
|
12
15
|
import { firefox } from "playwright";
|
|
@@ -47,9 +50,7 @@ export class FirefoxPlugin extends PlaywrightPluginBase {
|
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
protected async launchBrowser(): Promise<Browser> {
|
|
50
|
-
return firefox.launch({
|
|
51
|
-
headless: true,
|
|
52
|
-
});
|
|
53
|
+
return firefox.launch({ headless: true });
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
protected get installHint(): string {
|
|
Binary file
|
|
@@ -26,8 +26,6 @@ Requires
|
|
|
26
26
|
* Playwright Firefox browsers installed (``playwright install firefox``)
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
import sys
|
|
30
|
-
|
|
31
29
|
from pi_browser_bridge.playwright_base import PlaywrightBridge, check_playwright_or_exit
|
|
32
30
|
|
|
33
31
|
|
|
@@ -48,10 +46,13 @@ class FirefoxPyBridge(PlaywrightBridge):
|
|
|
48
46
|
)
|
|
49
47
|
|
|
50
48
|
def _launch_browser(self):
|
|
51
|
-
"""Launch a Firefox browser instance.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
"""Launch a Firefox browser instance.
|
|
50
|
+
|
|
51
|
+
Launches Firefox directly with ``firefox.launch()`` and drives its
|
|
52
|
+
own page; the plugin is the sole browser owner. No external-attach
|
|
53
|
+
endpoint is exposed.
|
|
54
|
+
"""
|
|
55
|
+
return self._pw.firefox.launch(headless=True)
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
# ═══════════════════════════════════════════════════════════════════════
|