@fusengine/browser-mcp 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 +21 -0
- package/README.md +175 -0
- package/dist/actions/act-by-ref.d.ts +13 -0
- package/dist/actions/act-by-ref.d.ts.map +1 -0
- package/dist/actions/act-by-ref.js +25 -0
- package/dist/actions/act-by-ref.js.map +1 -0
- package/dist/actions/human.d.ts +8 -0
- package/dist/actions/human.d.ts.map +1 -0
- package/dist/actions/human.js +6 -0
- package/dist/actions/human.js.map +1 -0
- package/dist/actions/login.d.ts +17 -0
- package/dist/actions/login.d.ts.map +1 -0
- package/dist/actions/login.js +11 -0
- package/dist/actions/login.js.map +1 -0
- package/dist/actions/navigation.d.ts +15 -0
- package/dist/actions/navigation.d.ts.map +1 -0
- package/dist/actions/navigation.js +33 -0
- package/dist/actions/navigation.js.map +1 -0
- package/dist/actions/perform.d.ts +15 -0
- package/dist/actions/perform.d.ts.map +1 -0
- package/dist/actions/perform.js +41 -0
- package/dist/actions/perform.js.map +1 -0
- package/dist/actions/smart-click.d.ts +9 -0
- package/dist/actions/smart-click.d.ts.map +1 -0
- package/dist/actions/smart-click.js +55 -0
- package/dist/actions/smart-click.js.map +1 -0
- package/dist/actions/smart-fill.d.ts +9 -0
- package/dist/actions/smart-fill.d.ts.map +1 -0
- package/dist/actions/smart-fill.js +46 -0
- package/dist/actions/smart-fill.js.map +1 -0
- package/dist/actions/wait-for.d.ts +21 -0
- package/dist/actions/wait-for.d.ts.map +1 -0
- package/dist/actions/wait-for.js +28 -0
- package/dist/actions/wait-for.js.map +1 -0
- package/dist/agent/actions-loop.d.ts +18 -0
- package/dist/agent/actions-loop.d.ts.map +1 -0
- package/dist/agent/actions-loop.js +44 -0
- package/dist/agent/actions-loop.js.map +1 -0
- package/dist/agent/browser-agent.d.ts +24 -0
- package/dist/agent/browser-agent.d.ts.map +1 -0
- package/dist/agent/browser-agent.js +36 -0
- package/dist/agent/browser-agent.js.map +1 -0
- package/dist/agent/compact.d.ts +8 -0
- package/dist/agent/compact.d.ts.map +1 -0
- package/dist/agent/compact.js +24 -0
- package/dist/agent/compact.js.map +1 -0
- package/dist/agent/config.d.ts +30 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +49 -0
- package/dist/agent/config.js.map +1 -0
- package/dist/agent/detect.d.ts +17 -0
- package/dist/agent/detect.d.ts.map +1 -0
- package/dist/agent/detect.js +9 -0
- package/dist/agent/detect.js.map +1 -0
- package/dist/agent/network.d.ts +16 -0
- package/dist/agent/network.d.ts.map +1 -0
- package/dist/agent/network.js +16 -0
- package/dist/agent/network.js.map +1 -0
- package/dist/agent/probe-run.d.ts +6 -0
- package/dist/agent/probe-run.d.ts.map +1 -0
- package/dist/agent/probe-run.js +90 -0
- package/dist/agent/probe-run.js.map +1 -0
- package/dist/agent/report.d.ts +29 -0
- package/dist/agent/report.d.ts.map +1 -0
- package/dist/agent/report.js +53 -0
- package/dist/agent/report.js.map +1 -0
- package/dist/agent/run-steps.d.ts +22 -0
- package/dist/agent/run-steps.d.ts.map +1 -0
- package/dist/agent/run-steps.js +59 -0
- package/dist/agent/run-steps.js.map +1 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +84 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/bin/mcp.d.ts +3 -0
- package/dist/bin/mcp.d.ts.map +1 -0
- package/dist/bin/mcp.js +28 -0
- package/dist/bin/mcp.js.map +1 -0
- package/dist/captcha/solve.d.ts +17 -0
- package/dist/captcha/solve.d.ts.map +1 -0
- package/dist/captcha/solve.js +42 -0
- package/dist/captcha/solve.js.map +1 -0
- package/dist/consent/booking-currency.d.ts +14 -0
- package/dist/consent/booking-currency.d.ts.map +1 -0
- package/dist/consent/booking-currency.js +82 -0
- package/dist/consent/booking-currency.js.map +1 -0
- package/dist/consent/consent.d.ts +11 -0
- package/dist/consent/consent.d.ts.map +1 -0
- package/dist/consent/consent.js +27 -0
- package/dist/consent/consent.js.map +1 -0
- package/dist/consent/currency-url.d.ts +10 -0
- package/dist/consent/currency-url.d.ts.map +1 -0
- package/dist/consent/currency-url.js +18 -0
- package/dist/consent/currency-url.js.map +1 -0
- package/dist/consent/currency.d.ts +14 -0
- package/dist/consent/currency.d.ts.map +1 -0
- package/dist/consent/currency.js +48 -0
- package/dist/consent/currency.js.map +1 -0
- package/dist/engine/cdp-engine.d.ts +21 -0
- package/dist/engine/cdp-engine.d.ts.map +1 -0
- package/dist/engine/cdp-engine.js +36 -0
- package/dist/engine/cdp-engine.js.map +1 -0
- package/dist/engine/cdp-launch.d.ts +11 -0
- package/dist/engine/cdp-launch.d.ts.map +1 -0
- package/dist/engine/cdp-launch.js +54 -0
- package/dist/engine/cdp-launch.js.map +1 -0
- package/dist/engine/context.d.ts +14 -0
- package/dist/engine/context.d.ts.map +1 -0
- package/dist/engine/context.js +22 -0
- package/dist/engine/context.js.map +1 -0
- package/dist/engine/launch.d.ts +6 -0
- package/dist/engine/launch.d.ts.map +1 -0
- package/dist/engine/launch.js +53 -0
- package/dist/engine/launch.js.map +1 -0
- package/dist/engine/loader.d.ts +13 -0
- package/dist/engine/loader.d.ts.map +1 -0
- package/dist/engine/loader.js +24 -0
- package/dist/engine/loader.js.map +1 -0
- package/dist/engine/patchright-engine.d.ts +4 -0
- package/dist/engine/patchright-engine.d.ts.map +1 -0
- package/dist/engine/patchright-engine.js +10 -0
- package/dist/engine/patchright-engine.js.map +1 -0
- package/dist/engine/playwright-engine.d.ts +8 -0
- package/dist/engine/playwright-engine.d.ts.map +1 -0
- package/dist/engine/playwright-engine.js +24 -0
- package/dist/engine/playwright-engine.js.map +1 -0
- package/dist/engine/registry.d.ts +13 -0
- package/dist/engine/registry.d.ts.map +1 -0
- package/dist/engine/registry.js +18 -0
- package/dist/engine/registry.js.map +1 -0
- package/dist/extraction/challenges.d.ts +9 -0
- package/dist/extraction/challenges.d.ts.map +1 -0
- package/dist/extraction/challenges.js +24 -0
- package/dist/extraction/challenges.js.map +1 -0
- package/dist/extraction/hotel-offers.d.ts +11 -0
- package/dist/extraction/hotel-offers.d.ts.map +1 -0
- package/dist/extraction/hotel-offers.js +63 -0
- package/dist/extraction/hotel-offers.js.map +1 -0
- package/dist/extraction/main-text.d.ts +13 -0
- package/dist/extraction/main-text.d.ts.map +1 -0
- package/dist/extraction/main-text.js +20 -0
- package/dist/extraction/main-text.js.map +1 -0
- package/dist/extraction/prices.d.ts +18 -0
- package/dist/extraction/prices.d.ts.map +1 -0
- package/dist/extraction/prices.js +74 -0
- package/dist/extraction/prices.js.map +1 -0
- package/dist/extraction/snapshot-diff.d.ts +28 -0
- package/dist/extraction/snapshot-diff.d.ts.map +1 -0
- package/dist/extraction/snapshot-diff.js +30 -0
- package/dist/extraction/snapshot-diff.js.map +1 -0
- package/dist/extraction/snapshot.d.ts +14 -0
- package/dist/extraction/snapshot.d.ts.map +1 -0
- package/dist/extraction/snapshot.js +26 -0
- package/dist/extraction/snapshot.js.map +1 -0
- package/dist/extraction/structured.d.ts +26 -0
- package/dist/extraction/structured.d.ts.map +1 -0
- package/dist/extraction/structured.js +28 -0
- package/dist/extraction/structured.js.map +1 -0
- package/dist/extraction/visual.d.ts +9 -0
- package/dist/extraction/visual.d.ts.map +1 -0
- package/dist/extraction/visual.js +29 -0
- package/dist/extraction/visual.js.map +1 -0
- package/dist/guardrails/preflight.d.ts +25 -0
- package/dist/guardrails/preflight.d.ts.map +1 -0
- package/dist/guardrails/preflight.js +27 -0
- package/dist/guardrails/preflight.js.map +1 -0
- package/dist/identity/country-profiles.d.ts +12 -0
- package/dist/identity/country-profiles.d.ts.map +1 -0
- package/dist/identity/country-profiles.js +58 -0
- package/dist/identity/country-profiles.js.map +1 -0
- package/dist/identity/identity.d.ts +19 -0
- package/dist/identity/identity.d.ts.map +1 -0
- package/dist/identity/identity.js +31 -0
- package/dist/identity/identity.js.map +1 -0
- package/dist/identity/resolve.d.ts +25 -0
- package/dist/identity/resolve.d.ts.map +1 -0
- package/dist/identity/resolve.js +21 -0
- package/dist/identity/resolve.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/engine-types.d.ts +15 -0
- package/dist/interfaces/engine-types.d.ts.map +1 -0
- package/dist/interfaces/engine-types.js +6 -0
- package/dist/interfaces/engine-types.js.map +1 -0
- package/dist/interfaces/engine.d.ts +30 -0
- package/dist/interfaces/engine.d.ts.map +1 -0
- package/dist/interfaces/engine.js +2 -0
- package/dist/interfaces/engine.js.map +1 -0
- package/dist/interfaces/extraction.d.ts +63 -0
- package/dist/interfaces/extraction.d.ts.map +1 -0
- package/dist/interfaces/extraction.js +6 -0
- package/dist/interfaces/extraction.js.map +1 -0
- package/dist/interfaces/net.d.ts +48 -0
- package/dist/interfaces/net.d.ts.map +1 -0
- package/dist/interfaces/net.js +6 -0
- package/dist/interfaces/net.js.map +1 -0
- package/dist/interfaces/report.d.ts +87 -0
- package/dist/interfaces/report.d.ts.map +1 -0
- package/dist/interfaces/report.js +2 -0
- package/dist/interfaces/report.js.map +1 -0
- package/dist/interfaces/types.d.ts +90 -0
- package/dist/interfaces/types.d.ts.map +1 -0
- package/dist/interfaces/types.js +6 -0
- package/dist/interfaces/types.js.map +1 -0
- package/dist/lib/errors.d.ts +19 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +30 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/evaluate.d.ts +17 -0
- package/dist/lib/evaluate.d.ts.map +1 -0
- package/dist/lib/evaluate.js +10 -0
- package/dist/lib/evaluate.js.map +1 -0
- package/dist/lib/fs.d.ts +11 -0
- package/dist/lib/fs.d.ts.map +1 -0
- package/dist/lib/fs.js +34 -0
- package/dist/lib/fs.js.map +1 -0
- package/dist/lib/logger.d.ts +14 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +22 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/output-dir.d.ts +12 -0
- package/dist/lib/output-dir.d.ts.map +1 -0
- package/dist/lib/output-dir.js +37 -0
- package/dist/lib/output-dir.js.map +1 -0
- package/dist/lib/retry.d.ts +28 -0
- package/dist/lib/retry.d.ts.map +1 -0
- package/dist/lib/retry.js +41 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/text.d.ts +9 -0
- package/dist/lib/text.d.ts.map +1 -0
- package/dist/lib/text.js +13 -0
- package/dist/lib/text.js.map +1 -0
- package/dist/net/captcha-client.d.ts +10 -0
- package/dist/net/captcha-client.d.ts.map +1 -0
- package/dist/net/captcha-client.js +48 -0
- package/dist/net/captcha-client.js.map +1 -0
- package/dist/net/captcha-inject.d.ts +10 -0
- package/dist/net/captcha-inject.d.ts.map +1 -0
- package/dist/net/captcha-inject.js +24 -0
- package/dist/net/captcha-inject.js.map +1 -0
- package/dist/net/navigate.d.ts +19 -0
- package/dist/net/navigate.d.ts.map +1 -0
- package/dist/net/navigate.js +32 -0
- package/dist/net/navigate.js.map +1 -0
- package/dist/net/throttle.d.ts +3 -0
- package/dist/net/throttle.d.ts.map +1 -0
- package/dist/net/throttle.js +23 -0
- package/dist/net/throttle.js.map +1 -0
- package/dist/proxy/country-map.d.ts +13 -0
- package/dist/proxy/country-map.d.ts.map +1 -0
- package/dist/proxy/country-map.js +38 -0
- package/dist/proxy/country-map.js.map +1 -0
- package/dist/server/env-defaults.d.ts +11 -0
- package/dist/server/env-defaults.d.ts.map +1 -0
- package/dist/server/env-defaults.js +20 -0
- package/dist/server/env-defaults.js.map +1 -0
- package/dist/server/map-options.d.ts +6 -0
- package/dist/server/map-options.d.ts.map +1 -0
- package/dist/server/map-options.js +43 -0
- package/dist/server/map-options.js.map +1 -0
- package/dist/server/resources.d.ts +4 -0
- package/dist/server/resources.d.ts.map +1 -0
- package/dist/server/resources.js +28 -0
- package/dist/server/resources.js.map +1 -0
- package/dist/server/result.d.ts +12 -0
- package/dist/server/result.d.ts.map +1 -0
- package/dist/server/result.js +18 -0
- package/dist/server/result.js.map +1 -0
- package/dist/server/schemas.d.ts +191 -0
- package/dist/server/schemas.d.ts.map +1 -0
- package/dist/server/schemas.js +72 -0
- package/dist/server/schemas.js.map +1 -0
- package/dist/server/server.d.ts +14 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +37 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/tools/act.d.ts +10 -0
- package/dist/server/tools/act.d.ts.map +1 -0
- package/dist/server/tools/act.js +62 -0
- package/dist/server/tools/act.js.map +1 -0
- package/dist/server/tools/connect.d.ts +10 -0
- package/dist/server/tools/connect.d.ts.map +1 -0
- package/dist/server/tools/connect.js +37 -0
- package/dist/server/tools/connect.js.map +1 -0
- package/dist/server/tools/extract-schema.d.ts +9 -0
- package/dist/server/tools/extract-schema.d.ts.map +1 -0
- package/dist/server/tools/extract-schema.js +29 -0
- package/dist/server/tools/extract-schema.js.map +1 -0
- package/dist/server/tools/extract.d.ts +9 -0
- package/dist/server/tools/extract.d.ts.map +1 -0
- package/dist/server/tools/extract.js +35 -0
- package/dist/server/tools/extract.js.map +1 -0
- package/dist/server/tools/navigate.d.ts +9 -0
- package/dist/server/tools/navigate.d.ts.map +1 -0
- package/dist/server/tools/navigate.js +27 -0
- package/dist/server/tools/navigate.js.map +1 -0
- package/dist/server/tools/probe.d.ts +8 -0
- package/dist/server/tools/probe.d.ts.map +1 -0
- package/dist/server/tools/probe.js +44 -0
- package/dist/server/tools/probe.js.map +1 -0
- package/dist/server/tools/run.d.ts +10 -0
- package/dist/server/tools/run.d.ts.map +1 -0
- package/dist/server/tools/run.js +30 -0
- package/dist/server/tools/run.js.map +1 -0
- package/dist/server/tools/screenshot.d.ts +10 -0
- package/dist/server/tools/screenshot.d.ts.map +1 -0
- package/dist/server/tools/screenshot.js +26 -0
- package/dist/server/tools/screenshot.js.map +1 -0
- package/dist/server/tools/session.d.ts +9 -0
- package/dist/server/tools/session.d.ts.map +1 -0
- package/dist/server/tools/session.js +33 -0
- package/dist/server/tools/session.js.map +1 -0
- package/dist/server/tools/snapshot.d.ts +12 -0
- package/dist/server/tools/snapshot.d.ts.map +1 -0
- package/dist/server/tools/snapshot.js +59 -0
- package/dist/server/tools/snapshot.js.map +1 -0
- package/dist/server/tools/wait.d.ts +9 -0
- package/dist/server/tools/wait.d.ts.map +1 -0
- package/dist/server/tools/wait.js +32 -0
- package/dist/server/tools/wait.js.map +1 -0
- package/dist/server/tools/with-session.d.ts +10 -0
- package/dist/server/tools/with-session.d.ts.map +1 -0
- package/dist/server/tools/with-session.js +16 -0
- package/dist/server/tools/with-session.js.map +1 -0
- package/dist/session/manager.d.ts +39 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +76 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/session/session.d.ts +28 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +51 -0
- package/dist/session/session.js.map +1 -0
- package/dist/state/dom-signature.d.ts +8 -0
- package/dist/state/dom-signature.d.ts.map +1 -0
- package/dist/state/dom-signature.js +13 -0
- package/dist/state/dom-signature.js.map +1 -0
- package/dist/state/realtime.d.ts +11 -0
- package/dist/state/realtime.d.ts.map +1 -0
- package/dist/state/realtime.js +34 -0
- package/dist/state/realtime.js.map +1 -0
- package/dist/state/replay.d.ts +4 -0
- package/dist/state/replay.d.ts.map +1 -0
- package/dist/state/replay.js +21 -0
- package/dist/state/replay.js.map +1 -0
- package/dist/state/site-memory.d.ts +35 -0
- package/dist/state/site-memory.d.ts.map +1 -0
- package/dist/state/site-memory.js +56 -0
- package/dist/state/site-memory.js.map +1 -0
- package/package.json +70 -0
- package/proxies.example.json +9 -0
- package/scripts/postinstall.mjs +24 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fusengine
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# fuse-browser
|
|
2
|
+
|
|
3
|
+
Agentic browser for AI agents — an **MCP server** and **CLI** on top of
|
|
4
|
+
[Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright-nodejs) (stealth) with a
|
|
5
|
+
Playwright fallback.
|
|
6
|
+
|
|
7
|
+
It gives an LLM a **real browser** with: per-country identity (locale/currency/timezone/geo),
|
|
8
|
+
stealth, self-healing actions, an accessibility-style snapshot with stable refs,
|
|
9
|
+
multi-step plans, semantic waits, structured extraction, and **human guardrails** for
|
|
10
|
+
payments and bookings. Because it drives a real Chromium, it reads **Next.js / SPA**
|
|
11
|
+
pages after hydration — not just static HTML.
|
|
12
|
+
|
|
13
|
+
TypeScript port of the original Python `fusengine-browser-agent`, redesigned around the
|
|
14
|
+
official MCP SDK with a persistent session model, image-content screenshots, and a
|
|
15
|
+
swappable engine layer (Playwright / Patchright / CDP attach).
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Node.js >= 20 (runtime). Bun is the dev toolchain.
|
|
20
|
+
- A Chromium build for the bundled engine:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx patchright install chromium # stealth engine (default)
|
|
24
|
+
npx playwright install chromium # fallback engine
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Three ways to get a browser
|
|
28
|
+
|
|
29
|
+
| Mode | How | Use |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| **Bundled** | default | Downloaded stealth Chromium (Patchright) |
|
|
32
|
+
| **Installed** | `channel: "chrome" \| "msedge"` | The Chrome/Edge installed on the machine |
|
|
33
|
+
| **Attach (CDP)** | `cdpEndpoint` or `browser_connect` | Drive an already-running browser — **Chrome, Edge, Dia, Arc, Brave** — using the user's real session |
|
|
34
|
+
|
|
35
|
+
`browser_connect` launches an installed browser with a remote-debugging port and attaches
|
|
36
|
+
to it automatically. Verified end-to-end against **Dia** (Chromium 148).
|
|
37
|
+
|
|
38
|
+
## CLI
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# One-shot probe of a real page
|
|
42
|
+
npx fuse-browser probe https://example.com --extract-prices --observe-visual
|
|
43
|
+
|
|
44
|
+
# Hotels with country identity, proxy routing, replay
|
|
45
|
+
npx fuse-browser probe 'https://www.booking.com/searchresults.html?ss=Tokyo' \
|
|
46
|
+
--country JP --proxy-map proxies.json --replay --auto-consent --extract-prices
|
|
47
|
+
|
|
48
|
+
# Use the installed Chrome
|
|
49
|
+
npx fuse-browser probe https://example.com --channel chrome
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Sensitive actions (`pay`, `book`, `checkout`, `confirm`, …) are **blocked** unless
|
|
53
|
+
`--approved` is passed.
|
|
54
|
+
|
|
55
|
+
## MCP server
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"fuse-browser": { "command": "npx", "args": ["-y", "@fusengine/browser-mcp"] }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Default browser via env
|
|
66
|
+
|
|
67
|
+
By default the server uses the bundled stealth Chromium. To pin a different
|
|
68
|
+
default **for every tool call** (without the agent specifying it each time), set
|
|
69
|
+
`FUSE_*` env vars in the MCP config — an explicit per-call argument still wins:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"fuse-browser": {
|
|
75
|
+
"command": "npx",
|
|
76
|
+
"args": ["-y", "@fusengine/browser-mcp"],
|
|
77
|
+
"env": { "FUSE_CHANNEL": "chrome", "FUSE_CDP_ENDPOINT": "http://localhost:9222" }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Supported: `FUSE_ENGINE`, `FUSE_CHANNEL`, `FUSE_CDP_ENDPOINT`, `FUSE_EXECUTABLE_PATH`,
|
|
84
|
+
`FUSE_HEADLESS`, `FUSE_COUNTRY`, `FUSE_CURRENCY`, `FUSE_USER_DATA_DIR`,
|
|
85
|
+
`FUSE_STORAGE_STATE`, `FUSE_OUTPUT_DIR`.
|
|
86
|
+
|
|
87
|
+
### Tools (23)
|
|
88
|
+
|
|
89
|
+
| Group | Tools |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| **One-shot** | `browser_probe`, `browser_probe_html` |
|
|
92
|
+
| **Session** | `browser_open`, `browser_connect`, `browser_status`, `browser_close` |
|
|
93
|
+
| **Navigate** | `browser_navigate`, `browser_back`, `browser_forward`, `browser_wait`, `browser_wait_for` |
|
|
94
|
+
| **Act** | `browser_click`, `browser_fill`, `browser_login`, `browser_scroll`, `browser_press`, `browser_select` |
|
|
95
|
+
| **Agentic** | `browser_snapshot` (indexed refs), `browser_act` (by ref + page diff), `browser_run` (multi-step plan) |
|
|
96
|
+
| **Extract** | `browser_extract` (text/prices/hotels/challenges), `browser_extract_schema` (typed, by CSS selectors) |
|
|
97
|
+
| **Vision** | `browser_screenshot` (page or single element by `ref`) |
|
|
98
|
+
|
|
99
|
+
Key agentic patterns:
|
|
100
|
+
|
|
101
|
+
- **`browser_snapshot` → `browser_act`** — snapshot tags each interactive element with a
|
|
102
|
+
stable `ref`; act on a `ref` deterministically (or by text fallback). `browser_act`
|
|
103
|
+
returns a **diff** of what changed (added/removed/text/url).
|
|
104
|
+
- **`browser_wait_for`** — wait on a condition (`text` / `selector` / `gone` / `urlContains`),
|
|
105
|
+
not a fixed delay.
|
|
106
|
+
- **`browser_run`** — execute an ordered plan (navigate/act/wait/extract) in one call,
|
|
107
|
+
stopping at the first failure. Guardrails apply to the whole plan.
|
|
108
|
+
|
|
109
|
+
A `runs` resource exposes the JSON reports and screenshots written under `runs/`.
|
|
110
|
+
|
|
111
|
+
## Library
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { BrowserAgent } from "fuse-browser";
|
|
115
|
+
|
|
116
|
+
const agent = new BrowserAgent({ countryCode: "CH", engine: "patchright" });
|
|
117
|
+
const report = await agent.probe("https://example.com", { extractPrices: true });
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Development
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
bun install
|
|
124
|
+
bun run typecheck # tsc strict, no emit
|
|
125
|
+
bun test tests/unit # pure unit tests
|
|
126
|
+
bun run test:integration # real headless Chromium + MCP in-process
|
|
127
|
+
bun run build # emit dist/ (JS + .d.ts)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
CI (`.github/workflows/ci.yml`) runs typecheck + unit + build + integration on every push.
|
|
131
|
+
|
|
132
|
+
## Output & data location
|
|
133
|
+
|
|
134
|
+
Artifacts (reports, screenshots, site-memory, replay) are written **outside the repo by default**:
|
|
135
|
+
|
|
136
|
+
- nested under the detected host-agent config dir when present —
|
|
137
|
+
`.claude/fuse-browser/`, `.cursor/`, `.codex/`, `.windsurf/`, `.gemini/`, `.continue/`, `.junie/`, `.github/`;
|
|
138
|
+
- otherwise `~/.fuse-browser/`.
|
|
139
|
+
|
|
140
|
+
Override per run with `outputDir` (library / MCP arg) or `--output-dir` (CLI). Cookies
|
|
141
|
+
(`storageStatePath`) and the Chromium profile (`userDataDir`) are separate paths you set explicitly.
|
|
142
|
+
|
|
143
|
+
> ⚠️ Reports and `storage-state` files contain page content, screenshots and **session cookies in
|
|
144
|
+
> clear text** — never commit them. The bundled `.gitignore` already excludes the defaults.
|
|
145
|
+
|
|
146
|
+
## Guardrails & limits
|
|
147
|
+
|
|
148
|
+
- No payment/booking/ticketing/card action without explicit human approval.
|
|
149
|
+
- Passwords masked in reports; proxy credentials redacted.
|
|
150
|
+
- CAPTCHA / 2FA: detection by default; an **opt-in** solver is available for authorized testing only — see *Resilience & captcha*.
|
|
151
|
+
- Concurrent sessions are capped (`maxSessions`, default 8) to avoid OOM.
|
|
152
|
+
- CDP attach: the remote-debugging port is a local attack surface — use a dedicated profile.
|
|
153
|
+
|
|
154
|
+
## Resilience & captcha
|
|
155
|
+
|
|
156
|
+
- **Navigation retry** (on by default): transient failures and HTTP `429/502/503/504`
|
|
157
|
+
are retried with full-jitter exponential backoff, honoring `Retry-After`. Tune via
|
|
158
|
+
`retry: { maxAttempts, baseMs, capMs, throttleMs }` (defaults `3 / 300 / 10000 / 0`).
|
|
159
|
+
`throttleMs` enforces a minimum gap between hits on the same host.
|
|
160
|
+
- **Captcha solver** (off by default, **opt-in**): set `solveCaptcha: true` on a probe and
|
|
161
|
+
provide `captcha: { provider, apiKey }` (`2captcha` | `anticaptcha` | `capmonster`). It
|
|
162
|
+
solves reCAPTCHA v2 / Cloudflare Turnstile via the provider's API; the result is reported
|
|
163
|
+
as `captcha: { attempted, solved, kind, provider, reason }`. Failures are reported, never thrown.
|
|
164
|
+
|
|
165
|
+
## Disclaimer
|
|
166
|
+
|
|
167
|
+
Provided **as-is** under MIT, with no warranty. `fuse-browser` is a neutral, dual-use
|
|
168
|
+
automation tool. **You alone are responsible** for how you use it and for complying with
|
|
169
|
+
applicable law, the target sites' Terms of Service, `robots.txt`, and data-protection rules
|
|
170
|
+
(e.g. GDPR). The opt-in captcha solver is intended for **authorized testing only** — on
|
|
171
|
+
systems you own or are explicitly permitted to test.
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute an action on an element referenced by its `data-fuse-ref`
|
|
3
|
+
* (produced by {@link captureSnapshot}). Deterministic counterpart to the
|
|
4
|
+
* text/heuristic targeting of smart-click/smart-fill.
|
|
5
|
+
* @module actions/act-by-ref
|
|
6
|
+
*/
|
|
7
|
+
import type { Page } from "playwright";
|
|
8
|
+
import type { ActionResult } from "../interfaces/types.js";
|
|
9
|
+
/** Action kinds that can target a snapshot ref. */
|
|
10
|
+
export type RefActionKind = "click" | "fill" | "select";
|
|
11
|
+
/** Run `kind` on the element carrying `data-fuse-ref="ref"`. */
|
|
12
|
+
export declare function actByRef(page: Page, ref: number, kind: RefActionKind, value?: string): Promise<ActionResult>;
|
|
13
|
+
//# sourceMappingURL=act-by-ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"act-by-ref.d.ts","sourceRoot":"","sources":["../../src/actions/act-by-ref.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,mDAAmD;AACnD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExD,gEAAgE;AAChE,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,aAAa,EACnB,KAAK,SAAK,GACT,OAAO,CAAC,YAAY,CAAC,CAkBvB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { REF_ATTRIBUTE } from "../extraction/snapshot.js";
|
|
2
|
+
/** Run `kind` on the element carrying `data-fuse-ref="ref"`. */
|
|
3
|
+
export async function actByRef(page, ref, kind, value = "") {
|
|
4
|
+
const selector = `[${REF_ATTRIBUTE}="${ref}"]`;
|
|
5
|
+
const locator = page.locator(selector).first();
|
|
6
|
+
try {
|
|
7
|
+
if ((await locator.count()) === 0) {
|
|
8
|
+
return { type: kind, ok: false, ref, error: "ref_not_found" };
|
|
9
|
+
}
|
|
10
|
+
if (kind === "click") {
|
|
11
|
+
await locator.click({ timeout: 5_000 });
|
|
12
|
+
}
|
|
13
|
+
else if (kind === "fill") {
|
|
14
|
+
await locator.fill(value, { timeout: 5_000 });
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
await locator.selectOption(value, { timeout: 5_000 });
|
|
18
|
+
}
|
|
19
|
+
return { type: kind, ok: true, ref, strategy: "ref" };
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return { type: kind, ok: false, ref, error: String(err).split("\n")[0] ?? "error" };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=act-by-ref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"act-by-ref.js","sourceRoot":"","sources":["../../src/actions/act-by-ref.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK1D,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAU,EACV,GAAW,EACX,IAAmB,EACnB,KAAK,GAAG,EAAE;IAEV,MAAM,QAAQ,GAAG,IAAI,aAAa,KAAK,GAAG,IAAI,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;IAC/C,IAAI,CAAC;QACH,IAAI,CAAC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;QAChE,CAAC;QACD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,MAAM,OAAO,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1C,CAAC;aAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YAC3B,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACxD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;IACtF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-like behaviors: random pauses.
|
|
3
|
+
* @module actions/human
|
|
4
|
+
*/
|
|
5
|
+
import type { Page } from "playwright";
|
|
6
|
+
/** Short random pause to mimic a real user. */
|
|
7
|
+
export declare function humanPause(page: Page): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=human.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human.d.ts","sourceRoot":"","sources":["../../src/actions/human.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC,+CAA+C;AAC/C,wBAAsB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human.js","sourceRoot":"","sources":["../../src/actions/human.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,+CAA+C;AAC/C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAU;IACzC,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured login action (username + password + submit), password masked.
|
|
3
|
+
* @module actions/login
|
|
4
|
+
*/
|
|
5
|
+
import type { Page } from "playwright";
|
|
6
|
+
import type { ActionResult } from "../interfaces/types.js";
|
|
7
|
+
/** Fields accepted by the login action (loose, like the Python dict). */
|
|
8
|
+
export interface LoginAction {
|
|
9
|
+
usernameTarget?: string;
|
|
10
|
+
passwordTarget?: string;
|
|
11
|
+
submitTarget?: string;
|
|
12
|
+
username?: string;
|
|
13
|
+
password?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Fill credentials then submit; masks the password in the report. */
|
|
16
|
+
export declare function login(page: Page, action: LoginAction, humanMode?: boolean): Promise<ActionResult>;
|
|
17
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/actions/login.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAI3D,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,sEAAsE;AACtE,wBAAsB,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CAYrG"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { smartClick } from "./smart-click.js";
|
|
2
|
+
import { smartFill } from "./smart-fill.js";
|
|
3
|
+
/** Fill credentials then submit; masks the password in the report. */
|
|
4
|
+
export async function login(page, action, humanMode = false) {
|
|
5
|
+
const username = await smartFill(page, action.usernameTarget ?? "email", action.username ?? "", "", humanMode);
|
|
6
|
+
const password = await smartFill(page, action.passwordTarget ?? "password", action.password ?? "", "", humanMode);
|
|
7
|
+
const submit = await smartClick(page, action.submitTarget ?? "Sign in", "", humanMode);
|
|
8
|
+
const ok = Boolean(username.ok && password.ok && submit.ok);
|
|
9
|
+
return { type: "login", ok, username, password: { ...password, value: "***" }, submit };
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/actions/login.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAW5C,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAU,EAAE,MAAmB,EAAE,SAAS,GAAG,KAAK;IAC5E,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,cAAc,IAAI,OAAO,EAAE,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,CAAC,CAAC;IAC/G,MAAM,QAAQ,GAAG,MAAM,SAAS,CAC9B,IAAI,EACJ,MAAM,CAAC,cAAc,IAAI,UAAU,EACnC,MAAM,CAAC,QAAQ,IAAI,EAAE,EACrB,EAAE,EACF,SAAS,CACV,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,YAAY,IAAI,SAAS,EAAE,EAAE,EAAE,SAAS,CAAC,CAAC;IACvF,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,EAAE,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,GAAG,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC;AAC1F,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-level page interactions: scroll, key press, select, history.
|
|
3
|
+
* @module actions/navigation
|
|
4
|
+
*/
|
|
5
|
+
import type { Page } from "playwright";
|
|
6
|
+
import type { ActionResult } from "../interfaces/types.js";
|
|
7
|
+
/** Scroll the page by a pixel delta (positive = down/right). */
|
|
8
|
+
export declare function scroll(page: Page, deltaY: number, deltaX?: number): Promise<ActionResult>;
|
|
9
|
+
/** Press a key or shortcut (e.g. "Enter", "ArrowDown", "Control+a"). */
|
|
10
|
+
export declare function pressKey(page: Page, key: string): Promise<ActionResult>;
|
|
11
|
+
/** Select option(s) in a <select> by value, label or index. */
|
|
12
|
+
export declare function selectOption(page: Page, target: string, value: string): Promise<ActionResult>;
|
|
13
|
+
/** Go back or forward in session history. */
|
|
14
|
+
export declare function navigateHistory(page: Page, direction: "back" | "forward"): Promise<ActionResult>;
|
|
15
|
+
//# sourceMappingURL=navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/actions/navigation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,gEAAgE;AAChE,wBAAsB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAG1F;AAED,wEAAwE;AACxE,wBAAsB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAO7E;AAED,+DAA+D;AAC/D,wBAAsB,YAAY,CAChC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,YAAY,CAAC,CAOvB;AAED,6CAA6C;AAC7C,wBAAsB,eAAe,CACnC,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAAC,YAAY,CAAC,CAMvB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Scroll the page by a pixel delta (positive = down/right). */
|
|
2
|
+
export async function scroll(page, deltaY, deltaX = 0) {
|
|
3
|
+
await page.mouse.wheel(deltaX, deltaY);
|
|
4
|
+
return { type: "scroll", ok: true, deltaX, deltaY };
|
|
5
|
+
}
|
|
6
|
+
/** Press a key or shortcut (e.g. "Enter", "ArrowDown", "Control+a"). */
|
|
7
|
+
export async function pressKey(page, key) {
|
|
8
|
+
try {
|
|
9
|
+
await page.keyboard.press(key);
|
|
10
|
+
return { type: "press", ok: true, key };
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
return { type: "press", ok: false, key, error: String(err).split("\n")[0] ?? "error" };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Select option(s) in a <select> by value, label or index. */
|
|
17
|
+
export async function selectOption(page, target, value) {
|
|
18
|
+
try {
|
|
19
|
+
const selected = await page.locator(target).first().selectOption(value, { timeout: 5_000 });
|
|
20
|
+
return { type: "select", ok: selected.length > 0, target, selected };
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
return { type: "select", ok: false, target, error: String(err).split("\n")[0] ?? "error" };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Go back or forward in session history. */
|
|
27
|
+
export async function navigateHistory(page, direction) {
|
|
28
|
+
const response = direction === "back"
|
|
29
|
+
? await page.goBack({ waitUntil: "domcontentloaded", timeout: 20_000 })
|
|
30
|
+
: await page.goForward({ waitUntil: "domcontentloaded", timeout: 20_000 });
|
|
31
|
+
return { type: direction, ok: response !== null, url: page.url() };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/actions/navigation.ts"],"names":[],"mappings":"AAOA,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAU,EAAE,MAAc,EAAE,MAAM,GAAG,CAAC;IACjE,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACtD,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAU,EAAE,GAAW;IACpD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;IACzF,CAAC;AACH,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAU,EACV,MAAc,EACd,KAAa;IAEb,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5F,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACvE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;IAC7F,CAAC;AACH,CAAC;AAED,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAU,EACV,SAA6B;IAE7B,MAAM,QAAQ,GACZ,SAAS,KAAK,MAAM;QAClB,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QACvE,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/E,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,KAAK,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch actions to their specialized implementations.
|
|
3
|
+
* @module actions/perform
|
|
4
|
+
*/
|
|
5
|
+
import type { Page } from "playwright";
|
|
6
|
+
import type { ActionResult } from "../interfaces/types.js";
|
|
7
|
+
/** Loose runtime action (may carry `preferredStrategy` injected by site memory). */
|
|
8
|
+
export type ActionInput = Record<string, unknown> & {
|
|
9
|
+
type: string;
|
|
10
|
+
};
|
|
11
|
+
/** Execute an action based on its `type`. */
|
|
12
|
+
export declare function performAction(page: Page, action: ActionInput, humanMode?: boolean): Promise<ActionResult>;
|
|
13
|
+
/** Copy of an action for the report, password masked. */
|
|
14
|
+
export declare function safeActionForReport(action: ActionInput): Record<string, unknown>;
|
|
15
|
+
//# sourceMappingURL=perform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perform.d.ts","sourceRoot":"","sources":["../../src/actions/perform.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAM3D,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,6CAA6C;AAC7C,wBAAsB,aAAa,CACjC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,WAAW,EACnB,SAAS,UAAQ,GAChB,OAAO,CAAC,YAAY,CAAC,CA2BvB;AAED,yDAAyD;AACzD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIhF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { login } from "./login.js";
|
|
2
|
+
import { navigateHistory, pressKey, scroll, selectOption } from "./navigation.js";
|
|
3
|
+
import { smartClick } from "./smart-click.js";
|
|
4
|
+
import { smartFill } from "./smart-fill.js";
|
|
5
|
+
/** Execute an action based on its `type`. */
|
|
6
|
+
export async function performAction(page, action, humanMode = false) {
|
|
7
|
+
const target = String(action.target ?? "");
|
|
8
|
+
const preferred = String(action.preferredStrategy ?? "");
|
|
9
|
+
switch (action.type) {
|
|
10
|
+
case "click":
|
|
11
|
+
return smartClick(page, target, preferred, humanMode);
|
|
12
|
+
case "fill":
|
|
13
|
+
return smartFill(page, target, String(action.value ?? ""), preferred, humanMode);
|
|
14
|
+
case "login":
|
|
15
|
+
return login(page, action, humanMode);
|
|
16
|
+
case "scroll":
|
|
17
|
+
return scroll(page, Number(action.deltaY ?? 600), Number(action.deltaX ?? 0));
|
|
18
|
+
case "press":
|
|
19
|
+
return pressKey(page, String(action.key ?? ""));
|
|
20
|
+
case "select":
|
|
21
|
+
return selectOption(page, target, String(action.value ?? ""));
|
|
22
|
+
case "back":
|
|
23
|
+
case "forward":
|
|
24
|
+
return navigateHistory(page, action.type);
|
|
25
|
+
case "wait": {
|
|
26
|
+
const ms = Number(action.ms ?? 500);
|
|
27
|
+
await page.waitForTimeout(ms);
|
|
28
|
+
return { type: "wait", ok: true, ms };
|
|
29
|
+
}
|
|
30
|
+
default:
|
|
31
|
+
return { type: action.type, ok: false, error: "unknown_action" };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Copy of an action for the report, password masked. */
|
|
35
|
+
export function safeActionForReport(action) {
|
|
36
|
+
const safe = { ...action };
|
|
37
|
+
if ("password" in safe)
|
|
38
|
+
safe.password = "***";
|
|
39
|
+
return safe;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=perform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perform.js","sourceRoot":"","sources":["../../src/actions/perform.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAoB,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAU,EACV,MAAmB,EACnB,SAAS,GAAG,KAAK;IAEjB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;IACzD,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,OAAO;YACV,OAAO,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QACxD,KAAK,MAAM;YACT,OAAO,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QACnF,KAAK,OAAO;YACV,OAAO,KAAK,CAAC,IAAI,EAAE,MAAqB,EAAE,SAAS,CAAC,CAAC;QACvD,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;QAChF,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;QAClD,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;QAChE,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS;YACZ,OAAO,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5C,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC;YACpC,MAAM,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YAC9B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACxC,CAAC;QACD;YACE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IACrE,CAAC;AACH,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,mBAAmB,CAAC,MAAmB;IACrD,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;IAC3B,IAAI,UAAU,IAAI,IAAI;QAAE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;IAC9C,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart multi-strategy click with heuristic fallback.
|
|
3
|
+
* @module actions/smart-click
|
|
4
|
+
*/
|
|
5
|
+
import type { Page } from "playwright";
|
|
6
|
+
import type { ActionResult } from "../interfaces/types.js";
|
|
7
|
+
/** Try several locating strategies, ordered by the preferred strategy. */
|
|
8
|
+
export declare function smartClick(page: Page, target: string, preferredStrategy?: string, humanMode?: boolean): Promise<ActionResult>;
|
|
9
|
+
//# sourceMappingURL=smart-click.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-click.d.ts","sourceRoot":"","sources":["../../src/actions/smart-click.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAgB3D,0EAA0E;AAC1E,wBAAsB,UAAU,CAC9B,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,iBAAiB,SAAK,EACtB,SAAS,UAAQ,GAChB,OAAO,CAAC,YAAY,CAAC,CAoCvB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { evalScriptArg } from "../lib/evaluate.js";
|
|
2
|
+
import { escapeRegExp } from "../lib/text.js";
|
|
3
|
+
import { humanPause } from "./human.js";
|
|
4
|
+
const HEURISTIC_CLICK = `(target) => {
|
|
5
|
+
const needle = target.toLowerCase();
|
|
6
|
+
const els = [...document.querySelectorAll('button,a,[role=button],input[type=button],input[type=submit]')];
|
|
7
|
+
const el = els.find(e => (
|
|
8
|
+
(e.innerText || '') + ' ' + (e.value || '') + ' ' + (e.getAttribute('aria-label') || '')
|
|
9
|
+
).toLowerCase().includes(needle));
|
|
10
|
+
if (!el) return false;
|
|
11
|
+
el.click();
|
|
12
|
+
return true;
|
|
13
|
+
}`;
|
|
14
|
+
/** Try several locating strategies, ordered by the preferred strategy. */
|
|
15
|
+
export async function smartClick(page, target, preferredStrategy = "", humanMode = false) {
|
|
16
|
+
const rx = new RegExp(escapeRegExp(target), "i");
|
|
17
|
+
const strategies = [
|
|
18
|
+
["selector", () => page.locator(target).first()],
|
|
19
|
+
["role", () => page.getByRole("button", { name: rx }).first()],
|
|
20
|
+
["text", () => page.getByText(rx).first()],
|
|
21
|
+
["label", () => page.getByLabel(rx).first()],
|
|
22
|
+
];
|
|
23
|
+
if (preferredStrategy) {
|
|
24
|
+
strategies.sort((a, b) => (a[0] === preferredStrategy ? 0 : 1) - (b[0] === preferredStrategy ? 0 : 1));
|
|
25
|
+
}
|
|
26
|
+
let lastError = "not_tried";
|
|
27
|
+
for (const [strategy, factory] of strategies) {
|
|
28
|
+
try {
|
|
29
|
+
const locator = factory();
|
|
30
|
+
if ((await locator.count()) > 0) {
|
|
31
|
+
if (humanMode) {
|
|
32
|
+
await humanPause(page);
|
|
33
|
+
await locator.scrollIntoViewIfNeeded({ timeout: 2_000 });
|
|
34
|
+
await locator.hover({ timeout: 2_000 });
|
|
35
|
+
await humanPause(page);
|
|
36
|
+
}
|
|
37
|
+
await locator.click({ timeout: 2_000 });
|
|
38
|
+
return { type: "click", target, ok: true, strategy };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
lastError = String(err).split("\n")[0] ?? "error";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const clicked = await evalScriptArg(page, HEURISTIC_CLICK, target);
|
|
47
|
+
if (clicked)
|
|
48
|
+
return { type: "click", target, ok: true, strategy: "heuristic" };
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
lastError = String(err).split("\n")[0] ?? "error";
|
|
52
|
+
}
|
|
53
|
+
return { type: "click", target, ok: false, error: lastError };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=smart-click.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-click.js","sourceRoot":"","sources":["../../src/actions/smart-click.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart multi-strategy fill with human-like typing.
|
|
3
|
+
* @module actions/smart-fill
|
|
4
|
+
*/
|
|
5
|
+
import type { Page } from "playwright";
|
|
6
|
+
import type { ActionResult } from "../interfaces/types.js";
|
|
7
|
+
/** Fill a field via selector, label or placeholder, with a preferred strategy. */
|
|
8
|
+
export declare function smartFill(page: Page, target: string, value: string, preferredStrategy?: string, humanMode?: boolean): Promise<ActionResult>;
|
|
9
|
+
//# sourceMappingURL=smart-fill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-fill.d.ts","sourceRoot":"","sources":["../../src/actions/smart-fill.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAkB3D,kFAAkF;AAClF,wBAAsB,SAAS,CAC7B,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,iBAAiB,SAAK,EACtB,SAAS,UAAQ,GAChB,OAAO,CAAC,YAAY,CAAC,CAwBvB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { escapeRegExp, randInt } from "../lib/text.js";
|
|
2
|
+
import { humanPause } from "./human.js";
|
|
3
|
+
async function humanType(page, locator, value) {
|
|
4
|
+
await humanPause(page);
|
|
5
|
+
try {
|
|
6
|
+
await locator.scrollIntoViewIfNeeded({ timeout: 2_000 });
|
|
7
|
+
await locator.hover({ timeout: 2_000 });
|
|
8
|
+
await locator.click({ timeout: 2_000 });
|
|
9
|
+
await page.keyboard.type(value, { delay: randInt(35, 95) });
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// Some composite widgets expose a textbox but intercept pointer events:
|
|
13
|
+
// keep the human attempt, then fall back to a direct fill.
|
|
14
|
+
await locator.fill(value, { timeout: 2_000 });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Fill a field via selector, label or placeholder, with a preferred strategy. */
|
|
18
|
+
export async function smartFill(page, target, value, preferredStrategy = "", humanMode = false) {
|
|
19
|
+
const rx = new RegExp(escapeRegExp(target), "i");
|
|
20
|
+
const strategies = [
|
|
21
|
+
["selector", () => page.locator(target).first()],
|
|
22
|
+
["label", () => page.getByLabel(rx).first()],
|
|
23
|
+
["placeholder", () => page.getByPlaceholder(rx).first()],
|
|
24
|
+
];
|
|
25
|
+
if (preferredStrategy) {
|
|
26
|
+
strategies.sort((a, b) => (a[0] === preferredStrategy ? 0 : 1) - (b[0] === preferredStrategy ? 0 : 1));
|
|
27
|
+
}
|
|
28
|
+
let lastError = "not_tried";
|
|
29
|
+
for (const [strategy, factory] of strategies) {
|
|
30
|
+
try {
|
|
31
|
+
const locator = factory();
|
|
32
|
+
if ((await locator.count()) > 0) {
|
|
33
|
+
if (humanMode)
|
|
34
|
+
await humanType(page, locator, value);
|
|
35
|
+
else
|
|
36
|
+
await locator.fill(value, { timeout: 2_000 });
|
|
37
|
+
return { type: "fill", target, ok: true, strategy };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
lastError = String(err).split("\n")[0] ?? "error";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { type: "fill", target, ok: false, error: lastError };
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=smart-fill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-fill.js","sourceRoot":"","sources":["../../src/actions/smart-fill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic waits: wait for a condition (text/selector/url/gone) instead of a
|
|
3
|
+
* fixed delay. Essential for reliable agentic loops on slow pages and SPAs.
|
|
4
|
+
* @module actions/wait-for
|
|
5
|
+
*/
|
|
6
|
+
import type { Page } from "playwright";
|
|
7
|
+
import type { ActionResult } from "../interfaces/types.js";
|
|
8
|
+
/**
|
|
9
|
+
* A semantic wait condition. Set one field; if several are set they are
|
|
10
|
+
* evaluated in priority order: text > selector > gone > urlContains.
|
|
11
|
+
*/
|
|
12
|
+
export interface WaitCondition {
|
|
13
|
+
text?: string;
|
|
14
|
+
selector?: string;
|
|
15
|
+
urlContains?: string;
|
|
16
|
+
gone?: string;
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
}
|
|
19
|
+
/** Wait until the condition holds; returns ok=false on timeout. */
|
|
20
|
+
export declare function waitForCondition(page: Page, cond: WaitCondition): Promise<ActionResult>;
|
|
21
|
+
//# sourceMappingURL=wait-for.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for.d.ts","sourceRoot":"","sources":["../../src/actions/wait-for.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mEAAmE;AACnE,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAwB7F"}
|