browser-agent-server 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,128 @@
1
+ """System-wide Browser Agent SDK: control real headful Chrome + X11 mouse from any Python project."""
2
+ from __future__ import annotations
3
+
4
+ import base64
5
+ import json
6
+ import os
7
+ import urllib.error
8
+ import urllib.request
9
+ from pathlib import Path
10
+
11
+ from .chrome import RealChromeSession, is_cloudflare_challenge, is_warp_proxy_ready
12
+ from .x11_input import X11Display, bezier_curve
13
+
14
+ DEFAULT_AGENT_URL = os.environ.get("BROWSER_AGENT_URL", "http://127.0.0.1:8765")
15
+
16
+
17
+ class BrowserClient:
18
+ """Zero-dependency HTTP client for the local Browser Agent daemon (127.0.0.1:8765)."""
19
+
20
+ def __init__(self, base_url: str = DEFAULT_AGENT_URL, timeout: float = 75.0):
21
+ self.base_url = base_url.rstrip("/")
22
+ self.timeout = timeout
23
+
24
+ def _request(self, method: str, path: str, payload: dict | None = None, timeout: float | None = None) -> dict:
25
+ url = f"{self.base_url}{path}"
26
+ data = json.dumps(payload).encode("utf-8") if payload is not None else None
27
+ headers = {"Content-Type": "application/json"} if data is not None else {}
28
+ req = urllib.request.Request(url, data=data, headers=headers, method=method)
29
+ with urllib.request.urlopen(req, timeout=timeout or self.timeout) as resp:
30
+ return json.loads(resp.read().decode("utf-8"))
31
+
32
+ def is_available(self) -> bool:
33
+ try:
34
+ res = self._request("GET", "/health", timeout=1.2)
35
+ return bool(res.get("ok"))
36
+ except Exception: # noqa: BLE001
37
+ return False
38
+
39
+ def status(self) -> dict:
40
+ return self._request("GET", "/status", timeout=3.0)
41
+
42
+ def fetch(
43
+ self,
44
+ url: str,
45
+ wait_sec: float = 3.5,
46
+ solve_cloudflare: bool = True,
47
+ use_proxy: bool | None = None,
48
+ include_screenshot: bool = False,
49
+ ) -> dict:
50
+ return self._request(
51
+ "POST",
52
+ "/fetch",
53
+ {
54
+ "url": url,
55
+ "wait_sec": wait_sec,
56
+ "solve_cloudflare": solve_cloudflare,
57
+ "use_proxy": use_proxy,
58
+ "include_screenshot": include_screenshot,
59
+ },
60
+ )
61
+
62
+ def goto(self, url: str, wait_sec: float = 2.5, solve_cloudflare: bool = True) -> dict:
63
+ return self._request(
64
+ "POST",
65
+ "/action",
66
+ {"action": "goto", "url": url, "wait_sec": wait_sec, "solve_cloudflare": solve_cloudflare},
67
+ )
68
+
69
+ def move_mouse(self, x: int, y: int) -> dict:
70
+ return self._request("POST", "/action", {"action": "move", "x": int(x), "y": int(y)})
71
+
72
+ def click(self, x: int | None = None, y: int | None = None, button: int = 1, double: bool = False) -> dict:
73
+ return self._request(
74
+ "POST",
75
+ "/action",
76
+ {"action": "click", "x": x, "y": y, "button": button, "double": double},
77
+ )
78
+
79
+ def type_text(self, text: str, submit: bool = False) -> dict:
80
+ return self._request("POST", "/action", {"action": "type", "text": text, "submit": submit})
81
+
82
+ def press_key(self, key: str) -> dict:
83
+ return self._request("POST", "/action", {"action": "key", "key": key})
84
+
85
+ def scroll(self, clicks: int = 4, direction: str = "down", x: int | None = None, y: int | None = None) -> dict:
86
+ return self._request(
87
+ "POST",
88
+ "/action",
89
+ {"action": "scroll", "clicks": clicks, "direction": direction, "x": x, "y": y},
90
+ )
91
+
92
+ def screenshot(self, save_path: str | Path | None = None) -> bytes:
93
+ res = self._request("POST", "/action", {"action": "screenshot"})
94
+ raw = base64.b64decode(res.get("screenshot_b64") or "")
95
+ if save_path and raw:
96
+ Path(save_path).write_bytes(raw)
97
+ return raw
98
+
99
+ def run_agent(self, url: str, goal: str, max_steps: int = 8, use_proxy: bool | None = None) -> dict:
100
+ return self._request(
101
+ "POST",
102
+ "/agent",
103
+ {"url": url, "goal": goal, "max_steps": max_steps, "use_proxy": use_proxy},
104
+ timeout=180.0,
105
+ )
106
+
107
+ def stop(self) -> dict:
108
+ return self._request("POST", "/action", {"action": "stop"}, timeout=10.0)
109
+
110
+
111
+ __all__ = [
112
+ "BrowserClient",
113
+ "RealChromeSession",
114
+ "X11Display",
115
+ "bezier_curve",
116
+ "is_cloudflare_challenge",
117
+ "is_warp_proxy_ready",
118
+ ]
119
+
120
+ from importlib.metadata import PackageNotFoundError # noqa: E402
121
+ from importlib.metadata import version as _v # noqa: E402
122
+
123
+ try:
124
+ __version__ = _v("browser-agent-server")
125
+ except PackageNotFoundError:
126
+ __version__ = "0.0.0+local"
127
+
128
+ __all__ = [*__all__, "__version__"]