X5Browser 1.0.0__tar.gz
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.
- x5browser-1.0.0/LICENSE +21 -0
- x5browser-1.0.0/PKG-INFO +218 -0
- x5browser-1.0.0/README.md +193 -0
- x5browser-1.0.0/X5Browser.egg-info/PKG-INFO +218 -0
- x5browser-1.0.0/X5Browser.egg-info/SOURCES.txt +69 -0
- x5browser-1.0.0/X5Browser.egg-info/dependency_links.txt +1 -0
- x5browser-1.0.0/X5Browser.egg-info/requires.txt +5 -0
- x5browser-1.0.0/X5Browser.egg-info/top_level.txt +1 -0
- x5browser-1.0.0/pyproject.toml +39 -0
- x5browser-1.0.0/setup.cfg +4 -0
- x5browser-1.0.0/x5browser/__init__.py +45 -0
- x5browser-1.0.0/x5browser/browser/__init__.py +5 -0
- x5browser-1.0.0/x5browser/browser/actions.py +535 -0
- x5browser-1.0.0/x5browser/browser/api.py +4255 -0
- x5browser-1.0.0/x5browser/browser/console.py +147 -0
- x5browser-1.0.0/x5browser/browser/dialogs.py +73 -0
- x5browser-1.0.0/x5browser/browser/downloads.py +93 -0
- x5browser-1.0.0/x5browser/browser/format.py +92 -0
- x5browser-1.0.0/x5browser/browser/network.py +197 -0
- x5browser-1.0.0/x5browser/browser/observe.py +777 -0
- x5browser-1.0.0/x5browser/browser/permissions.py +100 -0
- x5browser-1.0.0/x5browser/browser/tabs.py +81 -0
- x5browser-1.0.0/x5browser/browser/tools.py +654 -0
- x5browser-1.0.0/x5browser/browser/types.py +57 -0
- x5browser-1.0.0/x5browser/browser/waits.py +90 -0
- x5browser-1.0.0/x5browser/cdp/__init__.py +1 -0
- x5browser-1.0.0/x5browser/cdp/client.py +224 -0
- x5browser-1.0.0/x5browser/cdp/domains.py +94 -0
- x5browser-1.0.0/x5browser/chrome/__init__.py +1 -0
- x5browser-1.0.0/x5browser/chrome/launcher.py +240 -0
- x5browser-1.0.0/x5browser/chrome/profile.py +83 -0
- x5browser-1.0.0/x5browser/config.py +95 -0
- x5browser-1.0.0/x5browser/control/__init__.py +1 -0
- x5browser-1.0.0/x5browser/control/state.py +132 -0
- x5browser-1.0.0/x5browser/defense/__init__.py +13 -0
- x5browser-1.0.0/x5browser/defense/allowlist.py +155 -0
- x5browser-1.0.0/x5browser/defense/allowlist.yaml +69 -0
- x5browser-1.0.0/x5browser/defense/blocklists.py +88 -0
- x5browser-1.0.0/x5browser/defense/flags.py +59 -0
- x5browser-1.0.0/x5browser/defense/heuristics.py +139 -0
- x5browser-1.0.0/x5browser/defense/lists/custom.txt +11 -0
- x5browser-1.0.0/x5browser/defense/manager.py +271 -0
- x5browser-1.0.0/x5browser/defense/network_blocker.py +102 -0
- x5browser-1.0.0/x5browser/defense/policy.json +29 -0
- x5browser-1.0.0/x5browser/defense/profile.py +89 -0
- x5browser-1.0.0/x5browser/defense/selfheal.py +67 -0
- x5browser-1.0.0/x5browser/defense/shadow.py +68 -0
- x5browser-1.0.0/x5browser/defense/site_overrides.yaml +13 -0
- x5browser-1.0.0/x5browser/defense/tab_guard.py +161 -0
- x5browser-1.0.0/x5browser/defense/tree_filter.py +43 -0
- x5browser-1.0.0/x5browser/input/__init__.py +1 -0
- x5browser-1.0.0/x5browser/input/keymap.py +67 -0
- x5browser-1.0.0/x5browser/live/__init__.py +11 -0
- x5browser-1.0.0/x5browser/live/gateway.py +383 -0
- x5browser-1.0.0/x5browser/live/links.py +148 -0
- x5browser-1.0.0/x5browser/live/stack.py +337 -0
- x5browser-1.0.0/x5browser/live/units/x5-cdp-relay.service +13 -0
- x5browser-1.0.0/x5browser/live/units/x5-chrome.service +18 -0
- x5browser-1.0.0/x5browser/live/units/x5-novnc.service +13 -0
- x5browser-1.0.0/x5browser/live/units/x5-vnc.service +15 -0
- x5browser-1.0.0/x5browser/live/units/x5-xvfb.service +12 -0
- x5browser-1.0.0/x5browser/live/web/test.html +209 -0
- x5browser-1.0.0/x5browser/live/web/x5-live.html +163 -0
- x5browser-1.0.0/x5browser/live_browser.py +252 -0
- x5browser-1.0.0/x5browser/logging/__init__.py +1 -0
- x5browser-1.0.0/x5browser/logging/action_log.py +80 -0
- x5browser-1.0.0/x5browser/maintenance.py +165 -0
- x5browser-1.0.0/x5browser/platform.py +66 -0
- x5browser-1.0.0/x5browser/security/__init__.py +1 -0
- x5browser-1.0.0/x5browser/security/policy.py +20 -0
- x5browser-1.0.0/x5browser/task_session.py +238 -0
x5browser-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 X5Coder
|
|
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.
|
x5browser-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: X5Browser
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Browser library for AI agents: real Chrome over CDP (every action returns result + stable map + screenshot), ad-defense shield, live view.
|
|
5
|
+
Author: X5Coder
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: browser,chrome,cdp,ai-agent,automation,live-view,vnc,adblock
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: aiohttp>=3.9
|
|
21
|
+
Requires-Dist: websockets>=15
|
|
22
|
+
Provides-Extra: annotate
|
|
23
|
+
Requires-Dist: Pillow>=10; extra == "annotate"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# X5Browser
|
|
27
|
+
|
|
28
|
+
Real Chrome for AI agents: drive a headed Chromium over raw CDP (no Playwright/Puppeteer/Selenium), read pages as numbered text maps, survive ad-infested sites with a built-in shield, and hand the **same browser** to a human and back.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install X5Browser
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import asyncio
|
|
36
|
+
from x5browser import X5Browser, BrowserConfig
|
|
37
|
+
|
|
38
|
+
async def main():
|
|
39
|
+
async with X5Browser(BrowserConfig.default()) as browser:
|
|
40
|
+
await browser.navigate("https://example.com")
|
|
41
|
+
print(await browser.page_snapshot()) # numbered element map
|
|
42
|
+
refs = await browser.find_elements("Learn more")
|
|
43
|
+
# ... pick a ref, e.g. 3
|
|
44
|
+
result = await browser.click(ref=3) # real mouse click
|
|
45
|
+
print(result.message)
|
|
46
|
+
print(result.observation) # fresh stable map, attached
|
|
47
|
+
|
|
48
|
+
asyncio.run(main())
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Every **acting** tool waits for full page stability, then replies with
|
|
52
|
+
`message` + fresh stable element map + one screenshot — the agent always sees
|
|
53
|
+
the outcome at once, no extra snapshot call needed.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 1. Two facades
|
|
58
|
+
|
|
59
|
+
| Class | Use it when | Import |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `X5Browser` | You wire your own agent loop / UI | `from x5browser import X5Browser, BrowserConfig` |
|
|
62
|
+
| `LiveBrowser` | You want hidden Chrome + live link + timeline in one object | `from x5browser import LiveBrowser` |
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from x5browser import LiveBrowser
|
|
66
|
+
|
|
67
|
+
live = LiveBrowser() # ./profile data, headed Chrome
|
|
68
|
+
await live.start()
|
|
69
|
+
print(live.live_url) # open in any browser / <iframe>
|
|
70
|
+
await live.navigate("https://example.com")
|
|
71
|
+
for step in live.steps(): # timeline rows with screenshots
|
|
72
|
+
...
|
|
73
|
+
await live.take_control() # user drives the SAME browser
|
|
74
|
+
await live.agent_control() # agent resumes with a fresh map
|
|
75
|
+
await live.stop_browser() # quiet close, data kept
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 2. Tool catalog (36 tools, 9 groups)
|
|
79
|
+
|
|
80
|
+
`X5Browser.tools()` returns the default 18-tool surface; `tools(full=True)`
|
|
81
|
+
returns all 36. Model descriptions are concise professional English.
|
|
82
|
+
All interaction is genuine input events (`Input.*`); there is no JS-drive tool.
|
|
83
|
+
|
|
84
|
+
### Perception (read-only)
|
|
85
|
+
|
|
86
|
+
| Tool | What it does | Example |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `page_snapshot` | Numbered map of interactive elements (modals, iframes). Start here | `page_snapshot(scope="viewport")` |
|
|
89
|
+
| `read_text` | Visible page/element text | `read_text(ref=5)` |
|
|
90
|
+
| `find_elements` | Find by name/role, returns refs directly | `find_elements(query="checkout")` |
|
|
91
|
+
| `inspect_element` | Deep diagnosis: attributes, state, box, covering element, HTML | `inspect_element(ref=12)` |
|
|
92
|
+
| `screenshot` | Viewport/element/full-page capture; `annotate` draws ref numbers | `screenshot(annotate=true)` |
|
|
93
|
+
| `extract_data` | Structured JSON: table, list, links, forms, images, meta | `extract_data(kind="table")` |
|
|
94
|
+
| `get_page_info` | Tab state: URL, title, control, viewport, scroll, frames, focus | `get_page_info()` |
|
|
95
|
+
|
|
96
|
+
### Navigation
|
|
97
|
+
|
|
98
|
+
| Tool | What it does | Example |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `navigate` | Open URL / back / forward / reload; replies with fresh map | `navigate(to="https://example.com")` |
|
|
101
|
+
| `wait_for` | Wait for text, element, URL, network idle, download, seconds | `wait_for(condition="text", value="Done")` |
|
|
102
|
+
|
|
103
|
+
### Interaction (real input + fresh stable map in every reply)
|
|
104
|
+
|
|
105
|
+
| Tool | What it does | Example |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `click` | Real click; follows real hrefs directly; absorbs ad hijacks (auto-clean + one re-click) | `click(ref=7)` |
|
|
108
|
+
| `click_verify` | Click plus checks in one call | `click_verify(ref=7, checks=[...])` |
|
|
109
|
+
| `fill` | Type into a field; optional Enter | `fill(ref=8, text="hi", submit=true)` |
|
|
110
|
+
| `fill_form` | Fill typed fields + optional submit (needs approval) | `fill_form(fields=[{ref:8,value:"hi"}])` |
|
|
111
|
+
| `press_keys` | Key/shortcut, optional focus + repeat | `press_keys(keys="Ctrl+A")` |
|
|
112
|
+
| `scroll` | Wheel / to element / in container / to edge | `scroll(direction="down", amount=600)` |
|
|
113
|
+
| `hover` | Gradual hover; replies with appeared elements | `hover(ref=6)` |
|
|
114
|
+
| `hover_click` | Hover menu open + click item, one call | `hover_click(menu_ref=6, target_ref=9)` |
|
|
115
|
+
| `choose_option` | Dropdown value (native/custom), verified | `choose_option(ref=13, label="Egypt")` |
|
|
116
|
+
| `drag_drop` | Gradual drag between refs/coordinates | `drag_drop(from_ref=3, to_ref=9)` |
|
|
117
|
+
| `upload_files` | Attach workspace files (needs approval) | `upload_files(ref=20, paths=["id.png"])` |
|
|
118
|
+
|
|
119
|
+
### Tabs / events / downloads
|
|
120
|
+
|
|
121
|
+
| Tool | What it does | Example |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `manage_tabs` | List, open, switch, close tabs | `manage_tabs(action="list")` |
|
|
124
|
+
| `get_events` | New tabs, dialogs, permissions, downloads, crashes | `get_events()` |
|
|
125
|
+
| `answer_popup` | Answer dialog/permission/file-chooser (do first) | `answer_popup(kind="dialog", accept=true)` |
|
|
126
|
+
| `manage_downloads` | List / wait / cancel downloads | `manage_downloads(action="list")` |
|
|
127
|
+
|
|
128
|
+
### DevTools / task
|
|
129
|
+
|
|
130
|
+
| Tool | What it does | Example |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `read_console` | Last 500 console messages, filterable | `read_console(level="error")` |
|
|
133
|
+
| `console_command` | One JS expression, read value (diagnosis only; needs `allow_unsafe_js=True`) | `console_command(script="document.title")` |
|
|
134
|
+
| `list_requests` | Always-on network log; `id` gives full detail | `list_requests(url_contains="api")` |
|
|
135
|
+
| `export_page` | Save PDF/MHTML/HTML (needs approval) | `export_page(format="pdf", path="p.pdf")` |
|
|
136
|
+
| `verify_state` | Pass/fail checks, cheaper than a snapshot | `verify_state(checks=[...])` |
|
|
137
|
+
| `batch_actions` | Several tools in one call, refs remap | `batch_actions(actions=[...])` |
|
|
138
|
+
|
|
139
|
+
### Lifecycle / handoff
|
|
140
|
+
|
|
141
|
+
| Tool | What it does | Example |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `open_browser` / `close_browser` | Explicit start / quiet close (data kept) | `close_browser()` |
|
|
144
|
+
| `human_control` | Hand the live browser to the user (non-blocking) | `human_control(reason="pay")` |
|
|
145
|
+
| `take_control` / `agent_control` | Hand control over / resume agent | `take_control()` |
|
|
146
|
+
| `live_preview` | Watch-only link + JSON | `live_preview()` |
|
|
147
|
+
|
|
148
|
+
**Ref rules:** refs come from `page_snapshot`/`find_elements` and die on page
|
|
149
|
+
change (`ref_not_found` → re-snapshot). Optional `pre_wait`/`post_wait`
|
|
150
|
+
(0–30s) and test-note `reason` (≤500 chars) on every tool.
|
|
151
|
+
|
|
152
|
+
## 3. Ad-defense shield (automatic, no page JS)
|
|
153
|
+
|
|
154
|
+
A layer between Chrome and the agent. Ads are received normally on the wire
|
|
155
|
+
and cancelled at network/tab/map/display layers, so sites see ordinary HTTP.
|
|
156
|
+
|
|
157
|
+
| Line | What it does | Env |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| `NET` | Block/stub ad requests via `Fetch.*` (incl. empty-VAST for pre-roll) | `DEFENSE_NET=1` |
|
|
160
|
+
| `TABS` | Close ad popups, restore hijacked tabs (≤5), same-tab href bypass + sacrifice re-click in `click` | `DEFENSE_TABS=1` |
|
|
161
|
+
| `TREE` | Hide ad iframes/labels from the agent map | on with `NET` |
|
|
162
|
+
| `HEURISTIC` | Ad paths, affiliate redirects, rotating CDNs, brand creatives, tracking-param strip | `DEFENSE_HEURISTIC=1` |
|
|
163
|
+
| `SELFHEAL` | One calm reload with the harshest layer off per broken site | `DEFENSE_SELFHEAL=1` |
|
|
164
|
+
| `SHADOW` | Inspector stylesheet (CDP CSS, no script): ads render invisibly, clicks pass through | `DEFENSE_SHADOW=1` |
|
|
165
|
+
|
|
166
|
+
`COSMETIC/STUBS/OVERLAY/MEDIA` stay **off by design** (they need injected page
|
|
167
|
+
script). `DNS/EXT` are deployment-time (see `x5browser/defense/policy.json`).
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
print(browser.defense_report()) # per-line counters: the impact meter
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Allowlist** (`x5browser/defense/allowlist.yaml`, editable): OAuth/SSO,
|
|
174
|
+
payments + bank 3DS, captchas, embeds, fonts, CDNs are never touched — with
|
|
175
|
+
built-in fallbacks even if the file is corrupt. Extra hosts:
|
|
176
|
+
```yaml
|
|
177
|
+
extra_hosts:
|
|
178
|
+
- my-sso.example.com
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Blocklists:** built-in core + `defense/lists/custom.txt` (hand-kept) +
|
|
182
|
+
optional full lists (231k hosts):
|
|
183
|
+
```bash
|
|
184
|
+
python scripts/update_adlists.py # refresh lists/*.txt at build time
|
|
185
|
+
python scripts/measure_defense.py # blocked counts per line
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## 4. Configuration
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
BrowserConfig(
|
|
192
|
+
profile_dir="...", # default: inside the package (persistent)
|
|
193
|
+
downloads_dir="...", # default: <profile>/downloads
|
|
194
|
+
debug_port=9222, # loopback only, never public
|
|
195
|
+
window_width=1280, window_height=800,
|
|
196
|
+
headless=False, # headed so live shows everything
|
|
197
|
+
confirm_dangerous=True, # approval gates for pay/delete/send/…
|
|
198
|
+
allow_unsafe_js=False, # enable console_command explicitly
|
|
199
|
+
extra_chrome_args=(), # appended verbatim
|
|
200
|
+
)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## 5. Same-browser handoff cycle
|
|
204
|
+
|
|
205
|
+
`agent → human_control → user → agent_control → agent`, same Chrome, same
|
|
206
|
+
profile, same tabs and cookies. No new session, no Done button. While the user
|
|
207
|
+
holds control, agent tools refuse with `control_not_agent`.
|
|
208
|
+
|
|
209
|
+
## 6. Security notes
|
|
210
|
+
|
|
211
|
+
- CDP binds `127.0.0.1` only; uploads/downloads confined to the workspace.
|
|
212
|
+
- Passwords and card numbers are masked (`••••`) in maps, logs, screenshots.
|
|
213
|
+
- No Playwright/Puppeteer/Selenium; no JS-driven interaction (`Input.*` only).
|
|
214
|
+
- Secrets: copy `.env.example` → `.env` (never committed).
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT — see `LICENSE`.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# X5Browser
|
|
2
|
+
|
|
3
|
+
Real Chrome for AI agents: drive a headed Chromium over raw CDP (no Playwright/Puppeteer/Selenium), read pages as numbered text maps, survive ad-infested sites with a built-in shield, and hand the **same browser** to a human and back.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install X5Browser
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
import asyncio
|
|
11
|
+
from x5browser import X5Browser, BrowserConfig
|
|
12
|
+
|
|
13
|
+
async def main():
|
|
14
|
+
async with X5Browser(BrowserConfig.default()) as browser:
|
|
15
|
+
await browser.navigate("https://example.com")
|
|
16
|
+
print(await browser.page_snapshot()) # numbered element map
|
|
17
|
+
refs = await browser.find_elements("Learn more")
|
|
18
|
+
# ... pick a ref, e.g. 3
|
|
19
|
+
result = await browser.click(ref=3) # real mouse click
|
|
20
|
+
print(result.message)
|
|
21
|
+
print(result.observation) # fresh stable map, attached
|
|
22
|
+
|
|
23
|
+
asyncio.run(main())
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Every **acting** tool waits for full page stability, then replies with
|
|
27
|
+
`message` + fresh stable element map + one screenshot — the agent always sees
|
|
28
|
+
the outcome at once, no extra snapshot call needed.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. Two facades
|
|
33
|
+
|
|
34
|
+
| Class | Use it when | Import |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `X5Browser` | You wire your own agent loop / UI | `from x5browser import X5Browser, BrowserConfig` |
|
|
37
|
+
| `LiveBrowser` | You want hidden Chrome + live link + timeline in one object | `from x5browser import LiveBrowser` |
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from x5browser import LiveBrowser
|
|
41
|
+
|
|
42
|
+
live = LiveBrowser() # ./profile data, headed Chrome
|
|
43
|
+
await live.start()
|
|
44
|
+
print(live.live_url) # open in any browser / <iframe>
|
|
45
|
+
await live.navigate("https://example.com")
|
|
46
|
+
for step in live.steps(): # timeline rows with screenshots
|
|
47
|
+
...
|
|
48
|
+
await live.take_control() # user drives the SAME browser
|
|
49
|
+
await live.agent_control() # agent resumes with a fresh map
|
|
50
|
+
await live.stop_browser() # quiet close, data kept
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 2. Tool catalog (36 tools, 9 groups)
|
|
54
|
+
|
|
55
|
+
`X5Browser.tools()` returns the default 18-tool surface; `tools(full=True)`
|
|
56
|
+
returns all 36. Model descriptions are concise professional English.
|
|
57
|
+
All interaction is genuine input events (`Input.*`); there is no JS-drive tool.
|
|
58
|
+
|
|
59
|
+
### Perception (read-only)
|
|
60
|
+
|
|
61
|
+
| Tool | What it does | Example |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `page_snapshot` | Numbered map of interactive elements (modals, iframes). Start here | `page_snapshot(scope="viewport")` |
|
|
64
|
+
| `read_text` | Visible page/element text | `read_text(ref=5)` |
|
|
65
|
+
| `find_elements` | Find by name/role, returns refs directly | `find_elements(query="checkout")` |
|
|
66
|
+
| `inspect_element` | Deep diagnosis: attributes, state, box, covering element, HTML | `inspect_element(ref=12)` |
|
|
67
|
+
| `screenshot` | Viewport/element/full-page capture; `annotate` draws ref numbers | `screenshot(annotate=true)` |
|
|
68
|
+
| `extract_data` | Structured JSON: table, list, links, forms, images, meta | `extract_data(kind="table")` |
|
|
69
|
+
| `get_page_info` | Tab state: URL, title, control, viewport, scroll, frames, focus | `get_page_info()` |
|
|
70
|
+
|
|
71
|
+
### Navigation
|
|
72
|
+
|
|
73
|
+
| Tool | What it does | Example |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `navigate` | Open URL / back / forward / reload; replies with fresh map | `navigate(to="https://example.com")` |
|
|
76
|
+
| `wait_for` | Wait for text, element, URL, network idle, download, seconds | `wait_for(condition="text", value="Done")` |
|
|
77
|
+
|
|
78
|
+
### Interaction (real input + fresh stable map in every reply)
|
|
79
|
+
|
|
80
|
+
| Tool | What it does | Example |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `click` | Real click; follows real hrefs directly; absorbs ad hijacks (auto-clean + one re-click) | `click(ref=7)` |
|
|
83
|
+
| `click_verify` | Click plus checks in one call | `click_verify(ref=7, checks=[...])` |
|
|
84
|
+
| `fill` | Type into a field; optional Enter | `fill(ref=8, text="hi", submit=true)` |
|
|
85
|
+
| `fill_form` | Fill typed fields + optional submit (needs approval) | `fill_form(fields=[{ref:8,value:"hi"}])` |
|
|
86
|
+
| `press_keys` | Key/shortcut, optional focus + repeat | `press_keys(keys="Ctrl+A")` |
|
|
87
|
+
| `scroll` | Wheel / to element / in container / to edge | `scroll(direction="down", amount=600)` |
|
|
88
|
+
| `hover` | Gradual hover; replies with appeared elements | `hover(ref=6)` |
|
|
89
|
+
| `hover_click` | Hover menu open + click item, one call | `hover_click(menu_ref=6, target_ref=9)` |
|
|
90
|
+
| `choose_option` | Dropdown value (native/custom), verified | `choose_option(ref=13, label="Egypt")` |
|
|
91
|
+
| `drag_drop` | Gradual drag between refs/coordinates | `drag_drop(from_ref=3, to_ref=9)` |
|
|
92
|
+
| `upload_files` | Attach workspace files (needs approval) | `upload_files(ref=20, paths=["id.png"])` |
|
|
93
|
+
|
|
94
|
+
### Tabs / events / downloads
|
|
95
|
+
|
|
96
|
+
| Tool | What it does | Example |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `manage_tabs` | List, open, switch, close tabs | `manage_tabs(action="list")` |
|
|
99
|
+
| `get_events` | New tabs, dialogs, permissions, downloads, crashes | `get_events()` |
|
|
100
|
+
| `answer_popup` | Answer dialog/permission/file-chooser (do first) | `answer_popup(kind="dialog", accept=true)` |
|
|
101
|
+
| `manage_downloads` | List / wait / cancel downloads | `manage_downloads(action="list")` |
|
|
102
|
+
|
|
103
|
+
### DevTools / task
|
|
104
|
+
|
|
105
|
+
| Tool | What it does | Example |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `read_console` | Last 500 console messages, filterable | `read_console(level="error")` |
|
|
108
|
+
| `console_command` | One JS expression, read value (diagnosis only; needs `allow_unsafe_js=True`) | `console_command(script="document.title")` |
|
|
109
|
+
| `list_requests` | Always-on network log; `id` gives full detail | `list_requests(url_contains="api")` |
|
|
110
|
+
| `export_page` | Save PDF/MHTML/HTML (needs approval) | `export_page(format="pdf", path="p.pdf")` |
|
|
111
|
+
| `verify_state` | Pass/fail checks, cheaper than a snapshot | `verify_state(checks=[...])` |
|
|
112
|
+
| `batch_actions` | Several tools in one call, refs remap | `batch_actions(actions=[...])` |
|
|
113
|
+
|
|
114
|
+
### Lifecycle / handoff
|
|
115
|
+
|
|
116
|
+
| Tool | What it does | Example |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `open_browser` / `close_browser` | Explicit start / quiet close (data kept) | `close_browser()` |
|
|
119
|
+
| `human_control` | Hand the live browser to the user (non-blocking) | `human_control(reason="pay")` |
|
|
120
|
+
| `take_control` / `agent_control` | Hand control over / resume agent | `take_control()` |
|
|
121
|
+
| `live_preview` | Watch-only link + JSON | `live_preview()` |
|
|
122
|
+
|
|
123
|
+
**Ref rules:** refs come from `page_snapshot`/`find_elements` and die on page
|
|
124
|
+
change (`ref_not_found` → re-snapshot). Optional `pre_wait`/`post_wait`
|
|
125
|
+
(0–30s) and test-note `reason` (≤500 chars) on every tool.
|
|
126
|
+
|
|
127
|
+
## 3. Ad-defense shield (automatic, no page JS)
|
|
128
|
+
|
|
129
|
+
A layer between Chrome and the agent. Ads are received normally on the wire
|
|
130
|
+
and cancelled at network/tab/map/display layers, so sites see ordinary HTTP.
|
|
131
|
+
|
|
132
|
+
| Line | What it does | Env |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `NET` | Block/stub ad requests via `Fetch.*` (incl. empty-VAST for pre-roll) | `DEFENSE_NET=1` |
|
|
135
|
+
| `TABS` | Close ad popups, restore hijacked tabs (≤5), same-tab href bypass + sacrifice re-click in `click` | `DEFENSE_TABS=1` |
|
|
136
|
+
| `TREE` | Hide ad iframes/labels from the agent map | on with `NET` |
|
|
137
|
+
| `HEURISTIC` | Ad paths, affiliate redirects, rotating CDNs, brand creatives, tracking-param strip | `DEFENSE_HEURISTIC=1` |
|
|
138
|
+
| `SELFHEAL` | One calm reload with the harshest layer off per broken site | `DEFENSE_SELFHEAL=1` |
|
|
139
|
+
| `SHADOW` | Inspector stylesheet (CDP CSS, no script): ads render invisibly, clicks pass through | `DEFENSE_SHADOW=1` |
|
|
140
|
+
|
|
141
|
+
`COSMETIC/STUBS/OVERLAY/MEDIA` stay **off by design** (they need injected page
|
|
142
|
+
script). `DNS/EXT` are deployment-time (see `x5browser/defense/policy.json`).
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
print(browser.defense_report()) # per-line counters: the impact meter
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Allowlist** (`x5browser/defense/allowlist.yaml`, editable): OAuth/SSO,
|
|
149
|
+
payments + bank 3DS, captchas, embeds, fonts, CDNs are never touched — with
|
|
150
|
+
built-in fallbacks even if the file is corrupt. Extra hosts:
|
|
151
|
+
```yaml
|
|
152
|
+
extra_hosts:
|
|
153
|
+
- my-sso.example.com
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Blocklists:** built-in core + `defense/lists/custom.txt` (hand-kept) +
|
|
157
|
+
optional full lists (231k hosts):
|
|
158
|
+
```bash
|
|
159
|
+
python scripts/update_adlists.py # refresh lists/*.txt at build time
|
|
160
|
+
python scripts/measure_defense.py # blocked counts per line
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 4. Configuration
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
BrowserConfig(
|
|
167
|
+
profile_dir="...", # default: inside the package (persistent)
|
|
168
|
+
downloads_dir="...", # default: <profile>/downloads
|
|
169
|
+
debug_port=9222, # loopback only, never public
|
|
170
|
+
window_width=1280, window_height=800,
|
|
171
|
+
headless=False, # headed so live shows everything
|
|
172
|
+
confirm_dangerous=True, # approval gates for pay/delete/send/…
|
|
173
|
+
allow_unsafe_js=False, # enable console_command explicitly
|
|
174
|
+
extra_chrome_args=(), # appended verbatim
|
|
175
|
+
)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 5. Same-browser handoff cycle
|
|
179
|
+
|
|
180
|
+
`agent → human_control → user → agent_control → agent`, same Chrome, same
|
|
181
|
+
profile, same tabs and cookies. No new session, no Done button. While the user
|
|
182
|
+
holds control, agent tools refuse with `control_not_agent`.
|
|
183
|
+
|
|
184
|
+
## 6. Security notes
|
|
185
|
+
|
|
186
|
+
- CDP binds `127.0.0.1` only; uploads/downloads confined to the workspace.
|
|
187
|
+
- Passwords and card numbers are masked (`••••`) in maps, logs, screenshots.
|
|
188
|
+
- No Playwright/Puppeteer/Selenium; no JS-driven interaction (`Input.*` only).
|
|
189
|
+
- Secrets: copy `.env.example` → `.env` (never committed).
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT — see `LICENSE`.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: X5Browser
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Browser library for AI agents: real Chrome over CDP (every action returns result + stable map + screenshot), ad-defense shield, live view.
|
|
5
|
+
Author: X5Coder
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: browser,chrome,cdp,ai-agent,automation,live-view,vnc,adblock
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: aiohttp>=3.9
|
|
21
|
+
Requires-Dist: websockets>=15
|
|
22
|
+
Provides-Extra: annotate
|
|
23
|
+
Requires-Dist: Pillow>=10; extra == "annotate"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# X5Browser
|
|
27
|
+
|
|
28
|
+
Real Chrome for AI agents: drive a headed Chromium over raw CDP (no Playwright/Puppeteer/Selenium), read pages as numbered text maps, survive ad-infested sites with a built-in shield, and hand the **same browser** to a human and back.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install X5Browser
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import asyncio
|
|
36
|
+
from x5browser import X5Browser, BrowserConfig
|
|
37
|
+
|
|
38
|
+
async def main():
|
|
39
|
+
async with X5Browser(BrowserConfig.default()) as browser:
|
|
40
|
+
await browser.navigate("https://example.com")
|
|
41
|
+
print(await browser.page_snapshot()) # numbered element map
|
|
42
|
+
refs = await browser.find_elements("Learn more")
|
|
43
|
+
# ... pick a ref, e.g. 3
|
|
44
|
+
result = await browser.click(ref=3) # real mouse click
|
|
45
|
+
print(result.message)
|
|
46
|
+
print(result.observation) # fresh stable map, attached
|
|
47
|
+
|
|
48
|
+
asyncio.run(main())
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Every **acting** tool waits for full page stability, then replies with
|
|
52
|
+
`message` + fresh stable element map + one screenshot — the agent always sees
|
|
53
|
+
the outcome at once, no extra snapshot call needed.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 1. Two facades
|
|
58
|
+
|
|
59
|
+
| Class | Use it when | Import |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `X5Browser` | You wire your own agent loop / UI | `from x5browser import X5Browser, BrowserConfig` |
|
|
62
|
+
| `LiveBrowser` | You want hidden Chrome + live link + timeline in one object | `from x5browser import LiveBrowser` |
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from x5browser import LiveBrowser
|
|
66
|
+
|
|
67
|
+
live = LiveBrowser() # ./profile data, headed Chrome
|
|
68
|
+
await live.start()
|
|
69
|
+
print(live.live_url) # open in any browser / <iframe>
|
|
70
|
+
await live.navigate("https://example.com")
|
|
71
|
+
for step in live.steps(): # timeline rows with screenshots
|
|
72
|
+
...
|
|
73
|
+
await live.take_control() # user drives the SAME browser
|
|
74
|
+
await live.agent_control() # agent resumes with a fresh map
|
|
75
|
+
await live.stop_browser() # quiet close, data kept
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 2. Tool catalog (36 tools, 9 groups)
|
|
79
|
+
|
|
80
|
+
`X5Browser.tools()` returns the default 18-tool surface; `tools(full=True)`
|
|
81
|
+
returns all 36. Model descriptions are concise professional English.
|
|
82
|
+
All interaction is genuine input events (`Input.*`); there is no JS-drive tool.
|
|
83
|
+
|
|
84
|
+
### Perception (read-only)
|
|
85
|
+
|
|
86
|
+
| Tool | What it does | Example |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `page_snapshot` | Numbered map of interactive elements (modals, iframes). Start here | `page_snapshot(scope="viewport")` |
|
|
89
|
+
| `read_text` | Visible page/element text | `read_text(ref=5)` |
|
|
90
|
+
| `find_elements` | Find by name/role, returns refs directly | `find_elements(query="checkout")` |
|
|
91
|
+
| `inspect_element` | Deep diagnosis: attributes, state, box, covering element, HTML | `inspect_element(ref=12)` |
|
|
92
|
+
| `screenshot` | Viewport/element/full-page capture; `annotate` draws ref numbers | `screenshot(annotate=true)` |
|
|
93
|
+
| `extract_data` | Structured JSON: table, list, links, forms, images, meta | `extract_data(kind="table")` |
|
|
94
|
+
| `get_page_info` | Tab state: URL, title, control, viewport, scroll, frames, focus | `get_page_info()` |
|
|
95
|
+
|
|
96
|
+
### Navigation
|
|
97
|
+
|
|
98
|
+
| Tool | What it does | Example |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `navigate` | Open URL / back / forward / reload; replies with fresh map | `navigate(to="https://example.com")` |
|
|
101
|
+
| `wait_for` | Wait for text, element, URL, network idle, download, seconds | `wait_for(condition="text", value="Done")` |
|
|
102
|
+
|
|
103
|
+
### Interaction (real input + fresh stable map in every reply)
|
|
104
|
+
|
|
105
|
+
| Tool | What it does | Example |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `click` | Real click; follows real hrefs directly; absorbs ad hijacks (auto-clean + one re-click) | `click(ref=7)` |
|
|
108
|
+
| `click_verify` | Click plus checks in one call | `click_verify(ref=7, checks=[...])` |
|
|
109
|
+
| `fill` | Type into a field; optional Enter | `fill(ref=8, text="hi", submit=true)` |
|
|
110
|
+
| `fill_form` | Fill typed fields + optional submit (needs approval) | `fill_form(fields=[{ref:8,value:"hi"}])` |
|
|
111
|
+
| `press_keys` | Key/shortcut, optional focus + repeat | `press_keys(keys="Ctrl+A")` |
|
|
112
|
+
| `scroll` | Wheel / to element / in container / to edge | `scroll(direction="down", amount=600)` |
|
|
113
|
+
| `hover` | Gradual hover; replies with appeared elements | `hover(ref=6)` |
|
|
114
|
+
| `hover_click` | Hover menu open + click item, one call | `hover_click(menu_ref=6, target_ref=9)` |
|
|
115
|
+
| `choose_option` | Dropdown value (native/custom), verified | `choose_option(ref=13, label="Egypt")` |
|
|
116
|
+
| `drag_drop` | Gradual drag between refs/coordinates | `drag_drop(from_ref=3, to_ref=9)` |
|
|
117
|
+
| `upload_files` | Attach workspace files (needs approval) | `upload_files(ref=20, paths=["id.png"])` |
|
|
118
|
+
|
|
119
|
+
### Tabs / events / downloads
|
|
120
|
+
|
|
121
|
+
| Tool | What it does | Example |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `manage_tabs` | List, open, switch, close tabs | `manage_tabs(action="list")` |
|
|
124
|
+
| `get_events` | New tabs, dialogs, permissions, downloads, crashes | `get_events()` |
|
|
125
|
+
| `answer_popup` | Answer dialog/permission/file-chooser (do first) | `answer_popup(kind="dialog", accept=true)` |
|
|
126
|
+
| `manage_downloads` | List / wait / cancel downloads | `manage_downloads(action="list")` |
|
|
127
|
+
|
|
128
|
+
### DevTools / task
|
|
129
|
+
|
|
130
|
+
| Tool | What it does | Example |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `read_console` | Last 500 console messages, filterable | `read_console(level="error")` |
|
|
133
|
+
| `console_command` | One JS expression, read value (diagnosis only; needs `allow_unsafe_js=True`) | `console_command(script="document.title")` |
|
|
134
|
+
| `list_requests` | Always-on network log; `id` gives full detail | `list_requests(url_contains="api")` |
|
|
135
|
+
| `export_page` | Save PDF/MHTML/HTML (needs approval) | `export_page(format="pdf", path="p.pdf")` |
|
|
136
|
+
| `verify_state` | Pass/fail checks, cheaper than a snapshot | `verify_state(checks=[...])` |
|
|
137
|
+
| `batch_actions` | Several tools in one call, refs remap | `batch_actions(actions=[...])` |
|
|
138
|
+
|
|
139
|
+
### Lifecycle / handoff
|
|
140
|
+
|
|
141
|
+
| Tool | What it does | Example |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `open_browser` / `close_browser` | Explicit start / quiet close (data kept) | `close_browser()` |
|
|
144
|
+
| `human_control` | Hand the live browser to the user (non-blocking) | `human_control(reason="pay")` |
|
|
145
|
+
| `take_control` / `agent_control` | Hand control over / resume agent | `take_control()` |
|
|
146
|
+
| `live_preview` | Watch-only link + JSON | `live_preview()` |
|
|
147
|
+
|
|
148
|
+
**Ref rules:** refs come from `page_snapshot`/`find_elements` and die on page
|
|
149
|
+
change (`ref_not_found` → re-snapshot). Optional `pre_wait`/`post_wait`
|
|
150
|
+
(0–30s) and test-note `reason` (≤500 chars) on every tool.
|
|
151
|
+
|
|
152
|
+
## 3. Ad-defense shield (automatic, no page JS)
|
|
153
|
+
|
|
154
|
+
A layer between Chrome and the agent. Ads are received normally on the wire
|
|
155
|
+
and cancelled at network/tab/map/display layers, so sites see ordinary HTTP.
|
|
156
|
+
|
|
157
|
+
| Line | What it does | Env |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| `NET` | Block/stub ad requests via `Fetch.*` (incl. empty-VAST for pre-roll) | `DEFENSE_NET=1` |
|
|
160
|
+
| `TABS` | Close ad popups, restore hijacked tabs (≤5), same-tab href bypass + sacrifice re-click in `click` | `DEFENSE_TABS=1` |
|
|
161
|
+
| `TREE` | Hide ad iframes/labels from the agent map | on with `NET` |
|
|
162
|
+
| `HEURISTIC` | Ad paths, affiliate redirects, rotating CDNs, brand creatives, tracking-param strip | `DEFENSE_HEURISTIC=1` |
|
|
163
|
+
| `SELFHEAL` | One calm reload with the harshest layer off per broken site | `DEFENSE_SELFHEAL=1` |
|
|
164
|
+
| `SHADOW` | Inspector stylesheet (CDP CSS, no script): ads render invisibly, clicks pass through | `DEFENSE_SHADOW=1` |
|
|
165
|
+
|
|
166
|
+
`COSMETIC/STUBS/OVERLAY/MEDIA` stay **off by design** (they need injected page
|
|
167
|
+
script). `DNS/EXT` are deployment-time (see `x5browser/defense/policy.json`).
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
print(browser.defense_report()) # per-line counters: the impact meter
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Allowlist** (`x5browser/defense/allowlist.yaml`, editable): OAuth/SSO,
|
|
174
|
+
payments + bank 3DS, captchas, embeds, fonts, CDNs are never touched — with
|
|
175
|
+
built-in fallbacks even if the file is corrupt. Extra hosts:
|
|
176
|
+
```yaml
|
|
177
|
+
extra_hosts:
|
|
178
|
+
- my-sso.example.com
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Blocklists:** built-in core + `defense/lists/custom.txt` (hand-kept) +
|
|
182
|
+
optional full lists (231k hosts):
|
|
183
|
+
```bash
|
|
184
|
+
python scripts/update_adlists.py # refresh lists/*.txt at build time
|
|
185
|
+
python scripts/measure_defense.py # blocked counts per line
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## 4. Configuration
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
BrowserConfig(
|
|
192
|
+
profile_dir="...", # default: inside the package (persistent)
|
|
193
|
+
downloads_dir="...", # default: <profile>/downloads
|
|
194
|
+
debug_port=9222, # loopback only, never public
|
|
195
|
+
window_width=1280, window_height=800,
|
|
196
|
+
headless=False, # headed so live shows everything
|
|
197
|
+
confirm_dangerous=True, # approval gates for pay/delete/send/…
|
|
198
|
+
allow_unsafe_js=False, # enable console_command explicitly
|
|
199
|
+
extra_chrome_args=(), # appended verbatim
|
|
200
|
+
)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## 5. Same-browser handoff cycle
|
|
204
|
+
|
|
205
|
+
`agent → human_control → user → agent_control → agent`, same Chrome, same
|
|
206
|
+
profile, same tabs and cookies. No new session, no Done button. While the user
|
|
207
|
+
holds control, agent tools refuse with `control_not_agent`.
|
|
208
|
+
|
|
209
|
+
## 6. Security notes
|
|
210
|
+
|
|
211
|
+
- CDP binds `127.0.0.1` only; uploads/downloads confined to the workspace.
|
|
212
|
+
- Passwords and card numbers are masked (`••••`) in maps, logs, screenshots.
|
|
213
|
+
- No Playwright/Puppeteer/Selenium; no JS-driven interaction (`Input.*` only).
|
|
214
|
+
- Secrets: copy `.env.example` → `.env` (never committed).
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT — see `LICENSE`.
|