pi-lean-dimension 0.2.4 → 0.3.1
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 +1 -1
- package/node_modules/pi-lean-portal/AGENTS.md +15 -23
- package/node_modules/pi-lean-portal/README.md +35 -7
- package/node_modules/pi-lean-portal/__tests__/browser-toggle-profile.test.ts +12 -1
- package/node_modules/pi-lean-portal/__tests__/browser-toggle.test.ts +263 -1126
- package/node_modules/pi-lean-portal/__tests__/fetch-backend.test.ts +6 -32
- package/node_modules/pi-lean-portal/__tests__/helpers/mock-python-bridge.py +1 -1
- package/node_modules/pi-lean-portal/__tests__/helpers/probe-user-backend.ts +1 -2
- package/node_modules/pi-lean-portal/__tests__/helpers/setup-temp-dir.ts +26 -0
- package/node_modules/pi-lean-portal/__tests__/plugin-config-browser.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/plugin-contract.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/plugin-loading.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/probe-user-backend.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/python-adapter.test.ts +65 -69
- package/node_modules/pi-lean-portal/__tests__/router-dispatch.test.ts +1 -14
- package/node_modules/pi-lean-portal/__tests__/run-contributed-suites.test.ts +31 -16
- package/node_modules/pi-lean-portal/__tests__/snapshot-cache.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/web-guides.test.ts +146 -19
- package/node_modules/pi-lean-portal/backends/playwright-base/playwright-plugin.ts +1 -1
- package/node_modules/pi-lean-portal/backends/python-adapter.ts +3 -3
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__init__.py +2 -4
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/playwright_base.py +572 -81
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_playwright_base_quirks.py +13 -43
- package/node_modules/pi-lean-portal/browser-profile.ts +2 -3
- package/node_modules/pi-lean-portal/browser-toggle.ts +168 -385
- package/node_modules/pi-lean-portal/contributed/CHOOSING.md +2 -2
- package/node_modules/pi-lean-portal/contributed/README.md +65 -6
- package/node_modules/pi-lean-portal/contributed/camoufox-py/__pycache__/bridge.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/contributed/camoufox-py/bridge.py +108 -14
- package/node_modules/pi-lean-portal/contributed/camoufox-py/pin.json +4 -0
- package/node_modules/pi-lean-portal/core/fetch-backend.ts +10 -30
- package/node_modules/pi-lean-portal/core/guides.ts +14 -18
- package/node_modules/pi-lean-portal/core/router.ts +2 -20
- package/node_modules/pi-lean-portal/core/shared/bot-detection.ts +19 -30
- package/node_modules/pi-lean-portal/core/shared/browser-data.ts +4 -4
- package/node_modules/pi-lean-portal/core/shared/nav-settle.ts +2 -2
- package/node_modules/pi-lean-portal/core/shared/paths.ts +16 -2
- package/node_modules/pi-lean-portal/core/shared/settings-reader.ts +2 -7
- package/node_modules/pi-lean-portal/core/shared/snapshot-cache.ts +1 -1
- package/node_modules/pi-lean-portal/index.ts +8 -1
- package/node_modules/pi-lean-portal/package.json +2 -1
- package/node_modules/pi-lean-portal/tools/web-learn.ts +1 -4
- package/node_modules/pi-lean-search/__tests__/web-search.test.ts +87 -0
- package/node_modules/pi-lean-search/index.ts +134 -64
- package/node_modules/pi-lean-search/package.json +2 -1
- package/node_modules/playwright/README.md +3 -3
- package/node_modules/playwright/lib/agents/copilot-setup-steps.yml +2 -2
- package/node_modules/playwright/lib/cli/reportActions.js +2 -2
- package/node_modules/playwright/lib/common/index.js +77 -52
- package/node_modules/playwright/lib/common/index.js.txt +10 -11
- package/node_modules/playwright/lib/errorContext.js +2 -3
- package/node_modules/playwright/lib/index.js +30 -8
- package/node_modules/playwright/lib/matchers/expect.js +4811 -4379
- package/node_modules/playwright/lib/matchers/expect.js.LICENSE +87 -140
- package/node_modules/playwright/lib/matchers/expect.js.txt +30 -35
- package/node_modules/playwright/lib/program.js +19 -0
- package/node_modules/playwright/lib/runner/index.js +309 -153
- package/node_modules/playwright/lib/runner/index.js.txt +13 -12
- package/node_modules/playwright/lib/transform/babelBundle.js +12899 -11654
- package/node_modules/playwright/lib/transform/babelBundle.js.LICENSE +379 -354
- package/node_modules/playwright/lib/transform/babelBundle.js.txt +39 -39
- package/node_modules/playwright/lib/transform/esmLoader.js +3129 -1116
- package/node_modules/playwright/lib/transform/esmLoader.js.LICENSE +81 -61
- package/node_modules/playwright/lib/transform/esmLoader.js.txt +21 -18
- package/node_modules/playwright/lib/util.js +2 -29
- package/node_modules/playwright/lib/worker/workerProcessEntry.js +14 -32
- package/node_modules/playwright/lib/worker/workerProcessEntry.js.txt +7 -8
- package/node_modules/playwright/package.json +3 -6
- package/node_modules/playwright/types/test.d.ts +391 -4
- package/node_modules/playwright/types/testReporter.d.ts +79 -4
- package/node_modules/playwright-core/browsers.json +12 -18
- package/node_modules/playwright-core/lib/bootstrap.js +1 -1
- package/node_modules/playwright-core/lib/coreBundle.js +11344 -9900
- package/node_modules/playwright-core/lib/server/electron/loader.js +5 -2
- package/node_modules/playwright-core/lib/serverRegistry.js +5 -1
- package/node_modules/playwright-core/lib/tools/cli-client/help.json +21 -6
- package/node_modules/playwright-core/lib/tools/cli-client/program.js +1 -1
- package/node_modules/playwright-core/lib/tools/cli-client/session.js +4 -0
- package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/SKILL.md +21 -5
- package/node_modules/playwright-core/lib/tools/{cli-client/skill/references/spec-driven-testing.md → skills/playwright-cli/references/test-generation.md} +135 -7
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/SKILL.md +143 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/migration.md +85 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/react.md +67 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/vue.md +75 -0
- package/node_modules/playwright-core/lib/tools/utils/extension.js +31 -8
- package/node_modules/playwright-core/lib/utilsBundle.js +37698 -33670
- package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +391 -268
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-CyWAfh-p.js +11 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-DhC616m4.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/index.html +2 -2
- package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -1
- package/node_modules/playwright-core/lib/vite/htmlReport/report.js +15 -55
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-CwYYHbcZ.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-DYjdXIbE.js +129 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-l_lX622x.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/index.html +2 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-By56iMx7.js +32 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-B34OrIms.js +181 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-COQkjINf.js +7 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.BLFoOugd.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.Dl36UVQT.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.html +5 -5
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +2 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +3 -4
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.C7UW1sC9.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.D962mr9b.js +5 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +5 -5
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +338 -137
- package/node_modules/playwright-core/package.json +2 -2
- package/node_modules/playwright-core/types/protocol.d.ts +249 -36
- package/node_modules/playwright-core/types/structs.d.ts +3 -1
- package/node_modules/playwright-core/types/types.d.ts +2009 -266
- package/package.json +3 -3
- package/node_modules/pi-lean-portal/__tests__/url-safety.test.ts +0 -188
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/bridge.py +0 -525
- package/node_modules/pi-lean-portal/core/shared/url-safety.ts +0 -164
- package/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json +0 -2739
- package/node_modules/playwright-core/lib/tools/cli-client/skill/references/test-generation.md +0 -134
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-BY2S1tHT.css +0 -1
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-C_5TMfeg.js +0 -52
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DYBRYzYX.css +0 -1
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DeBYQozu.js +0 -32
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-4ZiSSCmn.css +0 -1
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-Bq-mQf8S.js +0 -193
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-LEHpjmcn.js +0 -32
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-BNmKHKpQ.js +0 -264
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-BYQrIQwR.js +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-CsJ4vdCR.js +0 -9
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.DYBRYzYX.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.CjdS-WJx.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/index.CzXZzn5A.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/index.DMMX1gXU.js +0 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.v8KI4P3m.js +0 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.BZQ54Kgt.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.Ut8wwJNp.js +0 -6
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.DYP7pi_n.css +0 -32
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/element-attributes.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/playwright-tests.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/request-mocking.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/running-code.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/session-management.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/storage-state.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/tracing.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/video-recording.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{trace → skills/playwright-trace}/SKILL.md +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pi-lean-dimension
|
|
2
2
|
|
|
3
|
-
> Web browsing and search tools for [Pi](https://github.com/earendil-works/pi
|
|
3
|
+
> Web browsing and search tools for [Pi](https://github.com/earendil-works/pi), the AI coding agent.
|
|
4
4
|
|
|
5
5
|
A monorepo housing three Pi extension packages that give your AI agent the
|
|
6
6
|
ability to browse the web interactively, fetch static pages as Markdown, and
|
|
@@ -39,16 +39,11 @@ Capabilities (`PluginCapabilities`) advertise quirks. The router checks them at
|
|
|
39
39
|
## Browser launch hook
|
|
40
40
|
|
|
41
41
|
The portal's own backends launch their browser directly and drive
|
|
42
|
-
their own page — there is **no external-attach path
|
|
43
|
-
|
|
44
|
-
`core/shared/cdp-endpoint.ts`, the `launchServer()` / `connect()`
|
|
45
|
-
hop, and the `_cdpEndpoint` / `_wsEndpoint` / `_browserServer` /
|
|
46
|
-
`_reconnectBrowser()` scaffolding were all removed (see
|
|
42
|
+
their own page — there is **no external-attach path** and no
|
|
43
|
+
post-launch hook for third-party subclasses (see
|
|
47
44
|
[`docs/decisions/miniwob-and-host-setup.md`](../../docs/decisions/miniwob-and-host-setup.md)).
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
path was considered and dropped as YAGNI; either will be re-added
|
|
51
|
-
alongside a real consumer that needs it.
|
|
45
|
+
A host-owns-browser path ("Mode B") was considered and dropped as
|
|
46
|
+
YAGNI; either will be re-added alongside a real consumer that needs it.
|
|
52
47
|
|
|
53
48
|
## Stealth backends (user-managed)
|
|
54
49
|
|
|
@@ -140,22 +135,20 @@ as class attributes on your subclass:
|
|
|
140
135
|
|------|---------|-----------------|
|
|
141
136
|
| `_fingerprint_managed_context` | `False` | `create_browser_context()` skips hardcoded `viewport`/`user_agent`; lets the fingerprint package set them. |
|
|
142
137
|
| `_eval_prefix` | `""` | Prepended to every `page.evaluate` expression in `do_evaluate` (e.g. Camoufox's `"mw:"` routes writes to the main world). |
|
|
143
|
-
| `_scroll_via_wheel` | `False` | `do_scroll` uses `page.mouse.wheel` instead of `page.evaluate("window.scrollBy")
|
|
138
|
+
| `_scroll_via_wheel` | `False` | `do_scroll` uses `page.mouse.wheel` instead of `page.evaluate("window.scrollBy")`. Legacy Camoufox (``135.0.1-beta.24``) needed this; current binary (``152.0.4-beta.28``) is the reverse (wheel no-ops, eval scrollBy works), so Camoufox leaves the default `False`. |
|
|
144
139
|
| `_skip_default_viewport` | `False` | Skips Playwright's `Browser.setDefaultViewport` CDP call (Camoufox binary rejects its `isMobile` prop). |
|
|
145
140
|
| `_skip_networkidle` | `False` | Nav-settle uses `load` instead of `networkidle` (patched binaries don't fire `networkidle` reliably). |
|
|
146
|
-
| `_wrap_mw_eval_in_eval` | `False` | `do_evaluate` rewrites the expression as `eval(<JSON-string of expression>)` before prepending `_eval_prefix`, so multi-statement scripts survive Camoufox's `let _s = (${script})` main-world wrapper
|
|
141
|
+
| `_wrap_mw_eval_in_eval` | `False` | `do_evaluate` rewrites the expression as `eval(<JSON-string of expression>)` before prepending `_eval_prefix`, so multi-statement scripts survive Camoufox's `let _s = (${script})` main-world wrapper. |
|
|
142
|
+
| `_settle_budget_ms` | `400` | Poll budget (ms) for `_wait_for_navigation_settle` when no `framenavigated` event fires. Stealth backends with high Juggler event latency (e.g. Camoufox) override to 2000. |
|
|
143
|
+
| `_url_stability_settle` | `False` | When True, the no-nav poll confirms URL is stable at a new value for 150 ms before exiting, rather than a fixed budget. |
|
|
147
144
|
| `_csp_safe_readonly_via_init_script` | `False` | `do_evaluate(read_only=True)` (the EXTRACTOR_SCRIPT) reads its JSON result from a `<meta id="__pi-extract">` tag that `create_browser_context` registers as a `context.add_init_script` (isolated world, CSP-free) at `DOMContentLoaded`, instead of `page.evaluate`. For patched-Firefox stealth binaries that route `page.evaluate` through `eval()` in the page's main world (CSP-subject) — Camoufox is NOT affected (its binary keeps Juggler's CSP-free isolated-world). The adapter plumbs the script via the `browser.init` config key `readOnlyExtractorScript`. Stale across SPA route changes (no new load) — fine for navigate→inspect. |
|
|
148
145
|
|
|
149
146
|
All flags default off → `chromium-py` / `firefox-py` behavior is
|
|
150
|
-
bit-identical to a pre-stealth install.
|
|
151
|
-
|
|
152
|
-
when `camoufox.NewContext` turned out to be broken on the current
|
|
153
|
-
binary (`Protocol error (Browser.setDefaultViewport)` from the same
|
|
154
|
-
`isMobile` rejection `_skip_default_viewport` handles). Camoufox
|
|
155
|
-
injects the fingerprint at **browser launch** via `camoufox.NewBrowser`,
|
|
147
|
+
bit-identical to a pre-stealth install. Camoufox injects the
|
|
148
|
+
fingerprint at **browser launch** via `camoufox.NewBrowser`,
|
|
156
149
|
so standard `browser.new_context()` with
|
|
157
|
-
`_fingerprint_managed_context = True` is correct — do not
|
|
158
|
-
`NewContext`.
|
|
150
|
+
`_fingerprint_managed_context = True` is correct — do not
|
|
151
|
+
re-attempt `NewContext`.
|
|
159
152
|
|
|
160
153
|
### Camoufox: the shipped example template
|
|
161
154
|
|
|
@@ -204,14 +197,13 @@ All tool calls dispatch through the router. Key responsibilities:
|
|
|
204
197
|
|
|
205
198
|
## Known Constraints & Debt
|
|
206
199
|
|
|
207
|
-
- **Console capture in Python backends** — Both `chromium-py` and `firefox-py` inherit console capture (500-entry ring buffer) and dialog auto-dismissal from `PlaywrightBridge._setup_page_session()` in `python-base`.
|
|
200
|
+
- **Console capture in Python backends** — Both `chromium-py` and `firefox-py` inherit console capture (500-entry ring buffer) and dialog auto-dismissal from `PlaywrightBridge._setup_page_session()` in `python-base`. All Python browser backends must subclass `PlaywrightBridge` to get session setup and JSON-RPC dispatch.
|
|
208
201
|
- **AbortSignal not supported on Python bridge** — the router passes `signal` through unconditionally (no capability check). The Python adapter accepts and silently ignores the signal. `supportsAbortSignal` is advertised but unenforced.
|
|
209
202
|
- **Sessions are per taskId** — mapped to `browser-NNN` keys via `_sessionKeys`/`_sessionCounter` in `core/shared/task-id.ts`. Created on first navigate, cleaned up on `session_shutdown`.
|
|
210
|
-
- **Python shared-context machinery removed
|
|
203
|
+
- **Python shared-context machinery removed** — Named profiles now use disk persistence (load-on-navigate via `storageState`) matching the TS Chromium plugin. Both backends use `ensure_session(task_id, config)` for all sessions.
|
|
211
204
|
- **Python bridge reuses BrowserContexts across navigations** — `ensure_session()` returns the existing session on re-navigate (unlike the TS Chromium plugin which creates a fresh context per navigate). This means in-process cookies survive re-navigation without explicit save, but also means `storageState` from the router is ignored on re-navigate (the context already exists). The Python adapter's `_persistState()` saves current cookies to disk before the navigate RPC for cross-process persistence.
|
|
212
205
|
- **`_persistState()` helper in both backends** — both `PlaywrightPluginBase._persistState` (direct `context.storageState()` call) and `PythonPluginAdapter._persistState` (JSON-RPC `browser.getStorageState` retrieval) delegate to the shared `persistSessionState()` helper in `core/shared/storage-state.ts`, which owns the `session?.persistState` gate, the `saveStorageState()` call, and the warn-and-swallow error path. Called both from `cleanup()` and — on re-navigate — from `getOrCreateContext()` (Chromium) or `navigate()` (Python) before the old context is closed/reused.
|
|
213
206
|
- **Role-based locators only**: never XPath/CSS — always `getByRole()` via `buildLocator()` with positional `.nth()` for duplicates. The `INTERACTIVE_ROLES` set defines which roles get @e refs.
|
|
214
|
-
- **All URLs go through `url-safety.ts`** — blocks localhost, private IPs (10.x, 172.16-31.x, 192.168.x, 169.254.169.254), dangerous schemes (file:, ftp:, data:, javascript:, vbscript:), and heuristically detects secrets in URLs.
|
|
215
207
|
- **Screenshot**: JPEG 80% quality, viewport constrained to 1280px wide, returns data URI.
|
|
216
208
|
- **Accessibility tree parser is single-pass, no-cap**: both TypeScript (`core/shared/accessibility-tree.ts`) and Python (`backends/python-base/pi_browser_bridge/accessibility.py`) use an identical single-pass algorithm — every interactive element gets an @e ref, no dialog prioritization, no element cap. Full ARIA trees beyond truncation are cached to disk via `snapshot-cache.ts`.
|
|
217
209
|
- **Bot detection has three tiers**: checked against page title (challenge phrases), body text (challenge phrases + CDN patterns), and raw HTML (CAPTCHA widget embed codes). Both the TypeScript (`core/shared/bot-detection.ts`) and Python (`python-base/pi_browser_bridge/bot_detection.py`) backends share the same HTML-level signal set.
|
|
@@ -220,7 +212,7 @@ All tool calls dispatch through the router. Key responsibilities:
|
|
|
220
212
|
- **`browser-inspect`** (`core/shared/dom-extractor.ts`): runs inline JS via `page.evaluate()`. Requires `getElementCache()` on the plugin. Text output truncated at ~2500 chars by default; pass `maxChars=0` for full. Keyword filtering via `query` parameter (case-insensitive substring on text, href, src).
|
|
221
213
|
- **`parentRef` on `AriaCachedNode`**: enables `subtree=...` queries in `browser-inspect`. Set by depth-based parent stack in `parseSnapshot()`'s single pass. Dialogs become parent of interior elements.
|
|
222
214
|
- **`dialogDetected` is resolved from element cache**: computed from the parsed `ElementCache` via `Array.some()` matching `role="dialog"` or `role="alertdialog"`. Not affected by snapshot truncation (unlike the old string-scan approach).
|
|
223
|
-
- **Guide staleness**: no builtin site guides shipped — entirely user-authored via `~/.pi/agent/pi-lean-portal/web-guides/*.md`. Guides carry `updated` date and `currentDate` timestamp in output.
|
|
215
|
+
- **Guide staleness**: no builtin site guides shipped — entirely user-authored via `~/.pi/agent/pi-lean-portal/web-guides/*.md`. User `.md` files override builtins by name collision (the file name minus `.md` becomes the guide key; a user `bot-detection.md` shadows the builtin `bot-detection` pattern). Guides carry `updated` date and `currentDate` timestamp in output.
|
|
224
216
|
- **Learn mode toggle**: `/web learn` enables `web-learn` tool; `/web on` removes it. Agent never calls `web-learn` unprompted. Default is off on fresh sessions.
|
|
225
217
|
- **Navigation settle** (`core/shared/nav-settle.ts`): after click or press, detects page navigation via a `framenavigated` listener and waits for `load + networkidle` (capped, errors swallowed) before reading URL/title/snapshot. Replaces the old fixed `waitForTimeout(300)` pattern that caused URL/DOM mismatches. Framework-agnostic via a lightweight `NavigationSettlePage` interface for testability.
|
|
226
218
|
- **Stealth backends are user-managed, not shipped** — they live under `~/.pi/agent/pi-lean-portal/user-backends/`, are never in the npm tarball, and the extension never auto-downloads them. The user-side install burden is real: a per-engine venv, a ~100 MB patched-binary fetch, and an explicit `settings.json` entry with an **absolute** `pythonPath`. See `contributed/README.md` for the install flow.
|
|
@@ -61,7 +61,7 @@ The browser tools are **enabled by default**. You can:
|
|
|
61
61
|
|
|
62
62
|
## Extending it
|
|
63
63
|
|
|
64
|
-
Beyond the toggle,
|
|
64
|
+
Beyond the toggle, three surfaces are extensible or shared rather than hardcoded:
|
|
65
65
|
|
|
66
66
|
- **Navigation guides** — `web-learn` saves site-specific playbooks that
|
|
67
67
|
auto-match by domain and resurface in later sessions. See
|
|
@@ -71,6 +71,14 @@ Beyond the toggle, two surfaces are user-extensible rather than hardcoded:
|
|
|
71
71
|
and drive a patched engine like [Camoufox](https://github.com/daijro/camoufox)
|
|
72
72
|
yourself. The full flow lives in [Backend Architecture](#backend-architecture)
|
|
73
73
|
and [`contributed/README.md`](./contributed/README.md).
|
|
74
|
+
- **Toolset toggles** — the `/web` toggle is powered by the shared
|
|
75
|
+
[`pi-tool-masking`](https://github.com/coreyryanhanson/pi-tool-masking/) peer dep,
|
|
76
|
+
which handles active-set masking, sibling-tool union math, and the
|
|
77
|
+
`TOOLSET_EVENTS` protocol that keeps the `browser`/`search` status-bar
|
|
78
|
+
glyphs in sync across extensions. It's published as a general toggle
|
|
79
|
+
mechanism — any extension that hides/shows its own tools can build on the
|
|
80
|
+
same scheme so masking and glyph events stay consistent across the whole
|
|
81
|
+
agent, rather than each toggle reinventing its own.
|
|
74
82
|
|
|
75
83
|
---
|
|
76
84
|
|
|
@@ -265,6 +273,28 @@ HTML to Markdown. It does **not** create a browser session.
|
|
|
265
273
|
| `pagination` | On-demand | Next buttons, infinite scroll, pages |
|
|
266
274
|
| `search` | On-demand | Search boxes, comboboxes, result lists |
|
|
267
275
|
|
|
276
|
+
### Overriding Built-in Guides
|
|
277
|
+
|
|
278
|
+
A same-named `.md` file in `~/.pi/agent/pi-lean-portal/web-guides/` (e.g.
|
|
279
|
+
`bot-detection.md`) **shadows the builtin entirely** — the whole guide is
|
|
280
|
+
replaced, not field-merged. To keep a pattern guide firing, include
|
|
281
|
+
`trigger.signal: botDetected` (or `dialogDetected`) in the frontmatter;
|
|
282
|
+
omitting it disables the trigger.
|
|
283
|
+
|
|
284
|
+
```yaml
|
|
285
|
+
---
|
|
286
|
+
category: pattern
|
|
287
|
+
trigger.signal: botDetected
|
|
288
|
+
icon: 🤖
|
|
289
|
+
shortName: my bot guide
|
|
290
|
+
---
|
|
291
|
+
Custom guidance text for bot challenges.
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Site guides and pattern guides live in **disjoint namespaces** — a site guide
|
|
295
|
+
for `www.botdetection.com` does not collide with the `bot-detection` pattern
|
|
296
|
+
guide; both fire when applicable.
|
|
297
|
+
|
|
268
298
|
### Viewing Guides
|
|
269
299
|
|
|
270
300
|
```text
|
|
@@ -661,12 +691,10 @@ When creating navigation guides with `web-learn`:
|
|
|
661
691
|
|
|
662
692
|
### Security
|
|
663
693
|
|
|
664
|
-
- URLs are
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
`javascript:`, `vbscript:`
|
|
669
|
-
- Secrets in URLs are detected heuristically
|
|
694
|
+
- URLs are parsed with `new URL()` as input validation — malformed
|
|
695
|
+
URLs are rejected, but no SSRF boundary is enforced (a coding agent
|
|
696
|
+
already has filesystem and shell access, so blocking localhost or
|
|
697
|
+
private IPs would be theater)
|
|
670
698
|
- Profile state is stored with restricted file permissions (0700 dirs,
|
|
671
699
|
0600 files)
|
|
672
700
|
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import { describe, it, expect, beforeEach, vi, type Mock } from "vitest";
|
|
12
12
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
13
|
-
import
|
|
13
|
+
import { EventEmitter } from "node:events";
|
|
14
|
+
import type { ExtensionAPI, EventBus } from "@earendil-works/pi-coding-agent";
|
|
14
15
|
|
|
15
16
|
// Mock node:fs to control the filesystem for profile tests
|
|
16
17
|
vi.mock("node:fs", () => ({
|
|
@@ -30,6 +31,7 @@ import { listProfiles, formatProfileList } from "../browser-profile.js";
|
|
|
30
31
|
// ─── Helpers ─────────────────────────────────────────────────────
|
|
31
32
|
|
|
32
33
|
function mockPi(tools?: string[]): ExtensionAPI {
|
|
34
|
+
const emitter = new EventEmitter();
|
|
33
35
|
const allTools = (tools ?? []).map((name) => ({ name }));
|
|
34
36
|
// Return a full active set by default
|
|
35
37
|
const active = tools ?? [];
|
|
@@ -40,6 +42,15 @@ function mockPi(tools?: string[]): ExtensionAPI {
|
|
|
40
42
|
appendEntry: vi.fn(),
|
|
41
43
|
registerCommand: vi.fn(),
|
|
42
44
|
on: vi.fn(),
|
|
45
|
+
get events() {
|
|
46
|
+
return {
|
|
47
|
+
emit: (channel: string, data: unknown) => emitter.emit(channel, data),
|
|
48
|
+
on: (channel: string, handler: (data: unknown) => void) => {
|
|
49
|
+
emitter.on(channel, handler);
|
|
50
|
+
return () => emitter.off(channel, handler);
|
|
51
|
+
},
|
|
52
|
+
} as EventBus;
|
|
53
|
+
},
|
|
43
54
|
} as unknown as ExtensionAPI;
|
|
44
55
|
}
|
|
45
56
|
|