browsercli 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.
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: browsercli
3
+ Version: 1.0.0
4
+ Summary: Python client for the browsercli browser workspace daemon
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/justinhuangcode/browsercli
7
+ Project-URL: Repository, https://github.com/justinhuangcode/browsercli
8
+ Project-URL: Documentation, https://github.com/justinhuangcode/browsercli#readme
9
+ Project-URL: Bug Reports, https://github.com/justinhuangcode/browsercli/issues
10
+ Keywords: browser,automation,rpc,cdp,devtools
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Testing
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+
24
+ # browsercli Python Client
25
+
26
+ Zero-dependency Python client for the [browsercli](https://github.com/justinhuangcode/browsercli) browser workspace daemon.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install -e clients/python # from the repo root
32
+ ```
33
+
34
+ ## Platform Support
35
+
36
+ The client auto-detects the RPC transport from the session file:
37
+
38
+ - **macOS / Linux** — connects via Unix socket (`socket_path` in session.json)
39
+ - **Windows** — connects via TCP localhost (`rpc_port` in session.json)
40
+
41
+ No code changes are needed — `BrowserCLI.connect()` handles both transports.
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from browsercli import BrowserCLI
47
+
48
+ # Connect to a running daemon
49
+ # macOS/Linux: reads ~/.browsercli/session.json
50
+ # Windows: reads %LOCALAPPDATA%\browsercli\session.json
51
+ ac = BrowserCLI.connect()
52
+
53
+ # Navigate and inspect
54
+ ac.goto("/")
55
+ title = ac.dom_query("h1", mode="text")
56
+ print(f"Title: {title}")
57
+
58
+ # Evaluate JavaScript
59
+ result = ac.eval("1 + 1")
60
+ print(f"Result: {result}")
61
+
62
+ # Screenshot
63
+ ac.screenshot(out="page.png")
64
+
65
+ # Stop the daemon
66
+ ac.stop()
67
+ ```
68
+
69
+ ## Error Handling
70
+
71
+ All exceptions inherit from `BrowserCLIError`, so you can catch the whole family or handle specific cases:
72
+
73
+ ```python
74
+ from browsercli import (
75
+ BrowserCLI,
76
+ BrowserCLIError,
77
+ ConnectionError,
78
+ AuthenticationError,
79
+ SessionError,
80
+ NotFoundError,
81
+ ServerError,
82
+ )
83
+
84
+ try:
85
+ ac = BrowserCLI.connect()
86
+ ac.dom_query("#missing-element", mode="text")
87
+ except SessionError:
88
+ print("Daemon not running — start it with: browsercli start")
89
+ except ConnectionError:
90
+ print("Cannot reach daemon — is the socket file valid?")
91
+ except AuthenticationError:
92
+ print("Token rejected — daemon may have restarted, reconnect")
93
+ except NotFoundError as e:
94
+ print(f"Element or endpoint not found: {e}")
95
+ except ServerError as e:
96
+ print(f"Daemon internal error (HTTP {e.status_code}): {e.error_message}")
97
+ except BrowserCLIError as e:
98
+ print(f"Unexpected client error: {e}")
99
+ ```
100
+
101
+ ### Exception Hierarchy
102
+
103
+ ```
104
+ BrowserCLIError # Base — catch-all
105
+ ├── SessionError # session.json missing/invalid
106
+ ├── ConnectionError # RPC endpoint unreachable (socket or TCP)
107
+ ├── AuthenticationError # HTTP 401 (bad token)
108
+ └── RPCError # Any HTTP 4xx/5xx with status_code + error_message
109
+ ├── BadRequestError # HTTP 400
110
+ ├── NotFoundError # HTTP 404
111
+ └── ServerError # HTTP 5xx
112
+ ```
113
+
114
+ ## Constructor
115
+
116
+ ```python
117
+ BrowserCLI(socket_path="", token="", timeout=30.0, *, rpc_port=0)
118
+ ```
119
+
120
+ | Parameter | Type | Description |
121
+ | --- | --- | --- |
122
+ | `socket_path` | `str` | Unix socket path (macOS/Linux). Mutually exclusive with `rpc_port`. |
123
+ | `token` | `str` | Bearer token for daemon authentication |
124
+ | `timeout` | `float` | Request timeout in seconds (default `30.0`) |
125
+ | `rpc_port` | `int` | TCP port on localhost (Windows). Keyword-only. |
126
+
127
+ You must provide either `socket_path` or `rpc_port`. In most cases, use the `connect()` factory instead of calling the constructor directly.
128
+
129
+ ## API Reference
130
+
131
+ | Method | Description |
132
+ | --- | --- |
133
+ | `BrowserCLI.connect(session_path=None, timeout=30.0)` | Create client from session file (auto-detects Unix socket or TCP) |
134
+ | `status()` | Daemon and browser status |
135
+ | `version()` | RPC and schema version info |
136
+ | `goto(url)` | Navigate to a path or URL |
137
+ | `eval(expression)` | Evaluate JavaScript |
138
+ | `reload()` | Reload the page |
139
+ | `dom_query(selector, mode)` | Query a single DOM element |
140
+ | `dom_all(selector, mode)` | Query all matching elements |
141
+ | `dom_attr(selector, name)` | Get an element attribute |
142
+ | `dom_click(selector)` | Click an element |
143
+ | `dom_type(selector, text, clear)` | Type text into an input |
144
+ | `dom_wait(selector, state, timeout_ms)` | Wait for element state |
145
+ | `screenshot(selector, out)` | Capture screenshot (PNG bytes) |
146
+ | `console(level, limit, clear)` | Fetch console entries |
147
+ | `network(limit, clear)` | Fetch network log entries |
148
+ | `perf()` | Page performance metrics |
149
+ | `stop()` | Stop the daemon |
150
+ | `plugin_list()` | List installed plugins |
151
+ | `plugin_rpc(rpc_path, body=None)` | Call a custom plugin RPC endpoint (`/x/...`) |
152
+
153
+ ### Valid Parameter Values
154
+
155
+ | Parameter | Valid Values | Default |
156
+ | --- | --- | --- |
157
+ | `dom_query` / `dom_all` `mode` | `"outer_html"`, `"text"` | `"outer_html"` |
158
+ | `dom_wait` `state` | `"visible"`, `"hidden"`, `"attached"`, `"detached"` | `"visible"` |
159
+ | `console` `level` | `""` (all), `"log"`, `"warn"`, `"error"`, `"info"` | `""` |
160
+
161
+ Invalid values raise `ValueError` before any RPC call is made.
162
+
163
+ ## Logging
164
+
165
+ The client uses Python's `logging` module under the logger name `browsercli`:
166
+
167
+ ```python
168
+ import logging
169
+ logging.basicConfig(level=logging.DEBUG)
170
+ # Now all RPC requests and responses are logged.
171
+ ```
172
+
173
+ ## Running Tests
174
+
175
+ ```bash
176
+ cd clients/python
177
+ python -m unittest tests.test_client -v
178
+ ```
179
+
180
+ Tests include:
181
+ - **Session parsing**: valid/invalid/missing session files (including `rpc_port` for Windows)
182
+ - **Parameter validation**: client-side checks for all method arguments
183
+ - **Contract tests**: mock server (Unix socket on macOS/Linux, TCP on Windows) emulating the Rust daemon
184
+ - **Error handling**: auth failures, 400/404/500 errors, connection errors
185
+ - **Exception hierarchy**: inheritance and attribute verification
186
+
187
+ ## Requirements
188
+
189
+ - Python 3.9+
190
+ - A running `browsercli` daemon (`browsercli start`)
191
+ - No external dependencies (stdlib only)
@@ -0,0 +1,168 @@
1
+ # browsercli Python Client
2
+
3
+ Zero-dependency Python client for the [browsercli](https://github.com/justinhuangcode/browsercli) browser workspace daemon.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install -e clients/python # from the repo root
9
+ ```
10
+
11
+ ## Platform Support
12
+
13
+ The client auto-detects the RPC transport from the session file:
14
+
15
+ - **macOS / Linux** — connects via Unix socket (`socket_path` in session.json)
16
+ - **Windows** — connects via TCP localhost (`rpc_port` in session.json)
17
+
18
+ No code changes are needed — `BrowserCLI.connect()` handles both transports.
19
+
20
+ ## Quick Start
21
+
22
+ ```python
23
+ from browsercli import BrowserCLI
24
+
25
+ # Connect to a running daemon
26
+ # macOS/Linux: reads ~/.browsercli/session.json
27
+ # Windows: reads %LOCALAPPDATA%\browsercli\session.json
28
+ ac = BrowserCLI.connect()
29
+
30
+ # Navigate and inspect
31
+ ac.goto("/")
32
+ title = ac.dom_query("h1", mode="text")
33
+ print(f"Title: {title}")
34
+
35
+ # Evaluate JavaScript
36
+ result = ac.eval("1 + 1")
37
+ print(f"Result: {result}")
38
+
39
+ # Screenshot
40
+ ac.screenshot(out="page.png")
41
+
42
+ # Stop the daemon
43
+ ac.stop()
44
+ ```
45
+
46
+ ## Error Handling
47
+
48
+ All exceptions inherit from `BrowserCLIError`, so you can catch the whole family or handle specific cases:
49
+
50
+ ```python
51
+ from browsercli import (
52
+ BrowserCLI,
53
+ BrowserCLIError,
54
+ ConnectionError,
55
+ AuthenticationError,
56
+ SessionError,
57
+ NotFoundError,
58
+ ServerError,
59
+ )
60
+
61
+ try:
62
+ ac = BrowserCLI.connect()
63
+ ac.dom_query("#missing-element", mode="text")
64
+ except SessionError:
65
+ print("Daemon not running — start it with: browsercli start")
66
+ except ConnectionError:
67
+ print("Cannot reach daemon — is the socket file valid?")
68
+ except AuthenticationError:
69
+ print("Token rejected — daemon may have restarted, reconnect")
70
+ except NotFoundError as e:
71
+ print(f"Element or endpoint not found: {e}")
72
+ except ServerError as e:
73
+ print(f"Daemon internal error (HTTP {e.status_code}): {e.error_message}")
74
+ except BrowserCLIError as e:
75
+ print(f"Unexpected client error: {e}")
76
+ ```
77
+
78
+ ### Exception Hierarchy
79
+
80
+ ```
81
+ BrowserCLIError # Base — catch-all
82
+ ├── SessionError # session.json missing/invalid
83
+ ├── ConnectionError # RPC endpoint unreachable (socket or TCP)
84
+ ├── AuthenticationError # HTTP 401 (bad token)
85
+ └── RPCError # Any HTTP 4xx/5xx with status_code + error_message
86
+ ├── BadRequestError # HTTP 400
87
+ ├── NotFoundError # HTTP 404
88
+ └── ServerError # HTTP 5xx
89
+ ```
90
+
91
+ ## Constructor
92
+
93
+ ```python
94
+ BrowserCLI(socket_path="", token="", timeout=30.0, *, rpc_port=0)
95
+ ```
96
+
97
+ | Parameter | Type | Description |
98
+ | --- | --- | --- |
99
+ | `socket_path` | `str` | Unix socket path (macOS/Linux). Mutually exclusive with `rpc_port`. |
100
+ | `token` | `str` | Bearer token for daemon authentication |
101
+ | `timeout` | `float` | Request timeout in seconds (default `30.0`) |
102
+ | `rpc_port` | `int` | TCP port on localhost (Windows). Keyword-only. |
103
+
104
+ You must provide either `socket_path` or `rpc_port`. In most cases, use the `connect()` factory instead of calling the constructor directly.
105
+
106
+ ## API Reference
107
+
108
+ | Method | Description |
109
+ | --- | --- |
110
+ | `BrowserCLI.connect(session_path=None, timeout=30.0)` | Create client from session file (auto-detects Unix socket or TCP) |
111
+ | `status()` | Daemon and browser status |
112
+ | `version()` | RPC and schema version info |
113
+ | `goto(url)` | Navigate to a path or URL |
114
+ | `eval(expression)` | Evaluate JavaScript |
115
+ | `reload()` | Reload the page |
116
+ | `dom_query(selector, mode)` | Query a single DOM element |
117
+ | `dom_all(selector, mode)` | Query all matching elements |
118
+ | `dom_attr(selector, name)` | Get an element attribute |
119
+ | `dom_click(selector)` | Click an element |
120
+ | `dom_type(selector, text, clear)` | Type text into an input |
121
+ | `dom_wait(selector, state, timeout_ms)` | Wait for element state |
122
+ | `screenshot(selector, out)` | Capture screenshot (PNG bytes) |
123
+ | `console(level, limit, clear)` | Fetch console entries |
124
+ | `network(limit, clear)` | Fetch network log entries |
125
+ | `perf()` | Page performance metrics |
126
+ | `stop()` | Stop the daemon |
127
+ | `plugin_list()` | List installed plugins |
128
+ | `plugin_rpc(rpc_path, body=None)` | Call a custom plugin RPC endpoint (`/x/...`) |
129
+
130
+ ### Valid Parameter Values
131
+
132
+ | Parameter | Valid Values | Default |
133
+ | --- | --- | --- |
134
+ | `dom_query` / `dom_all` `mode` | `"outer_html"`, `"text"` | `"outer_html"` |
135
+ | `dom_wait` `state` | `"visible"`, `"hidden"`, `"attached"`, `"detached"` | `"visible"` |
136
+ | `console` `level` | `""` (all), `"log"`, `"warn"`, `"error"`, `"info"` | `""` |
137
+
138
+ Invalid values raise `ValueError` before any RPC call is made.
139
+
140
+ ## Logging
141
+
142
+ The client uses Python's `logging` module under the logger name `browsercli`:
143
+
144
+ ```python
145
+ import logging
146
+ logging.basicConfig(level=logging.DEBUG)
147
+ # Now all RPC requests and responses are logged.
148
+ ```
149
+
150
+ ## Running Tests
151
+
152
+ ```bash
153
+ cd clients/python
154
+ python -m unittest tests.test_client -v
155
+ ```
156
+
157
+ Tests include:
158
+ - **Session parsing**: valid/invalid/missing session files (including `rpc_port` for Windows)
159
+ - **Parameter validation**: client-side checks for all method arguments
160
+ - **Contract tests**: mock server (Unix socket on macOS/Linux, TCP on Windows) emulating the Rust daemon
161
+ - **Error handling**: auth failures, 400/404/500 errors, connection errors
162
+ - **Exception hierarchy**: inheritance and attribute verification
163
+
164
+ ## Requirements
165
+
166
+ - Python 3.9+
167
+ - A running `browsercli` daemon (`browsercli start`)
168
+ - No external dependencies (stdlib only)
@@ -0,0 +1,26 @@
1
+ """browsercli Python client — zero-dependency wrapper for the Unix socket RPC API."""
2
+
3
+ from browsercli.client import BrowserCLI
4
+ from browsercli.exceptions import (
5
+ BrowserCLIError,
6
+ AuthenticationError,
7
+ BadRequestError,
8
+ ConnectionError,
9
+ NotFoundError,
10
+ RPCError,
11
+ ServerError,
12
+ SessionError,
13
+ )
14
+
15
+ __all__ = [
16
+ "BrowserCLI",
17
+ "BrowserCLIError",
18
+ "AuthenticationError",
19
+ "BadRequestError",
20
+ "ConnectionError",
21
+ "NotFoundError",
22
+ "RPCError",
23
+ "ServerError",
24
+ "SessionError",
25
+ ]
26
+ __version__ = "0.4.0"