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.
Files changed (71) hide show
  1. x5browser-1.0.0/LICENSE +21 -0
  2. x5browser-1.0.0/PKG-INFO +218 -0
  3. x5browser-1.0.0/README.md +193 -0
  4. x5browser-1.0.0/X5Browser.egg-info/PKG-INFO +218 -0
  5. x5browser-1.0.0/X5Browser.egg-info/SOURCES.txt +69 -0
  6. x5browser-1.0.0/X5Browser.egg-info/dependency_links.txt +1 -0
  7. x5browser-1.0.0/X5Browser.egg-info/requires.txt +5 -0
  8. x5browser-1.0.0/X5Browser.egg-info/top_level.txt +1 -0
  9. x5browser-1.0.0/pyproject.toml +39 -0
  10. x5browser-1.0.0/setup.cfg +4 -0
  11. x5browser-1.0.0/x5browser/__init__.py +45 -0
  12. x5browser-1.0.0/x5browser/browser/__init__.py +5 -0
  13. x5browser-1.0.0/x5browser/browser/actions.py +535 -0
  14. x5browser-1.0.0/x5browser/browser/api.py +4255 -0
  15. x5browser-1.0.0/x5browser/browser/console.py +147 -0
  16. x5browser-1.0.0/x5browser/browser/dialogs.py +73 -0
  17. x5browser-1.0.0/x5browser/browser/downloads.py +93 -0
  18. x5browser-1.0.0/x5browser/browser/format.py +92 -0
  19. x5browser-1.0.0/x5browser/browser/network.py +197 -0
  20. x5browser-1.0.0/x5browser/browser/observe.py +777 -0
  21. x5browser-1.0.0/x5browser/browser/permissions.py +100 -0
  22. x5browser-1.0.0/x5browser/browser/tabs.py +81 -0
  23. x5browser-1.0.0/x5browser/browser/tools.py +654 -0
  24. x5browser-1.0.0/x5browser/browser/types.py +57 -0
  25. x5browser-1.0.0/x5browser/browser/waits.py +90 -0
  26. x5browser-1.0.0/x5browser/cdp/__init__.py +1 -0
  27. x5browser-1.0.0/x5browser/cdp/client.py +224 -0
  28. x5browser-1.0.0/x5browser/cdp/domains.py +94 -0
  29. x5browser-1.0.0/x5browser/chrome/__init__.py +1 -0
  30. x5browser-1.0.0/x5browser/chrome/launcher.py +240 -0
  31. x5browser-1.0.0/x5browser/chrome/profile.py +83 -0
  32. x5browser-1.0.0/x5browser/config.py +95 -0
  33. x5browser-1.0.0/x5browser/control/__init__.py +1 -0
  34. x5browser-1.0.0/x5browser/control/state.py +132 -0
  35. x5browser-1.0.0/x5browser/defense/__init__.py +13 -0
  36. x5browser-1.0.0/x5browser/defense/allowlist.py +155 -0
  37. x5browser-1.0.0/x5browser/defense/allowlist.yaml +69 -0
  38. x5browser-1.0.0/x5browser/defense/blocklists.py +88 -0
  39. x5browser-1.0.0/x5browser/defense/flags.py +59 -0
  40. x5browser-1.0.0/x5browser/defense/heuristics.py +139 -0
  41. x5browser-1.0.0/x5browser/defense/lists/custom.txt +11 -0
  42. x5browser-1.0.0/x5browser/defense/manager.py +271 -0
  43. x5browser-1.0.0/x5browser/defense/network_blocker.py +102 -0
  44. x5browser-1.0.0/x5browser/defense/policy.json +29 -0
  45. x5browser-1.0.0/x5browser/defense/profile.py +89 -0
  46. x5browser-1.0.0/x5browser/defense/selfheal.py +67 -0
  47. x5browser-1.0.0/x5browser/defense/shadow.py +68 -0
  48. x5browser-1.0.0/x5browser/defense/site_overrides.yaml +13 -0
  49. x5browser-1.0.0/x5browser/defense/tab_guard.py +161 -0
  50. x5browser-1.0.0/x5browser/defense/tree_filter.py +43 -0
  51. x5browser-1.0.0/x5browser/input/__init__.py +1 -0
  52. x5browser-1.0.0/x5browser/input/keymap.py +67 -0
  53. x5browser-1.0.0/x5browser/live/__init__.py +11 -0
  54. x5browser-1.0.0/x5browser/live/gateway.py +383 -0
  55. x5browser-1.0.0/x5browser/live/links.py +148 -0
  56. x5browser-1.0.0/x5browser/live/stack.py +337 -0
  57. x5browser-1.0.0/x5browser/live/units/x5-cdp-relay.service +13 -0
  58. x5browser-1.0.0/x5browser/live/units/x5-chrome.service +18 -0
  59. x5browser-1.0.0/x5browser/live/units/x5-novnc.service +13 -0
  60. x5browser-1.0.0/x5browser/live/units/x5-vnc.service +15 -0
  61. x5browser-1.0.0/x5browser/live/units/x5-xvfb.service +12 -0
  62. x5browser-1.0.0/x5browser/live/web/test.html +209 -0
  63. x5browser-1.0.0/x5browser/live/web/x5-live.html +163 -0
  64. x5browser-1.0.0/x5browser/live_browser.py +252 -0
  65. x5browser-1.0.0/x5browser/logging/__init__.py +1 -0
  66. x5browser-1.0.0/x5browser/logging/action_log.py +80 -0
  67. x5browser-1.0.0/x5browser/maintenance.py +165 -0
  68. x5browser-1.0.0/x5browser/platform.py +66 -0
  69. x5browser-1.0.0/x5browser/security/__init__.py +1 -0
  70. x5browser-1.0.0/x5browser/security/policy.py +20 -0
  71. x5browser-1.0.0/x5browser/task_session.py +238 -0
@@ -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.
@@ -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`.