feat(sdk): improve Python SDK ergonomics
- Position browser-cli as a CLI plus Python SDK in docs and package metadata. - Add public target properties and a raw command escape hatch for unsupported commands. - Add convenience helpers for opening, finding, closing, and accessing tabs. - Add plural group aliases and a wait_for_selector DOM convenience alias. - Extend bound Tab objects with screenshot, pin, refresh, load wait, and URL watch helpers. - Preserve remote auth key configuration when binding remote Tab and Group objects. - Bump project and extension versions to 0.9.9 and cover SDK additions with tests.
This commit is contained in:
@@ -1,19 +1,16 @@
|
||||
# browser-cli
|
||||
|
||||
Control your real, running browser from the terminal or a Python script — no headless browser, no Playwright, no virtual display. Your actual open tabs, windows, and tab groups respond to your commands.
|
||||
Control your real, running browser from the terminal or the Python SDK — no headless browser, no Playwright, no virtual display. Your actual open tabs, windows, and tab groups respond to your commands.
|
||||
|
||||
---
|
||||
|
||||
## What it does
|
||||
|
||||
You have 40 tabs open. You want to close all the duplicates, group the GitHub ones, save your session before a meeting, and open a few URLs into a specific group — all from a script. That is what browser-cli is for.
|
||||
|
||||
It works by pairing a small browser extension with a Python CLI tool. The extension has full access to your browser's tabs, windows, groups, and page DOM. The CLI talks to it in real time over a local IPC channel.
|
||||
It works by pairing a small browser extension with a Python package that provides both a CLI and SDK. The extension has full access to your browser's tabs, windows, groups, and page DOM. The CLI and SDK talk to it in real time over a local IPC channel.
|
||||
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
```
|
||||
terminal / python script
|
||||
│
|
||||
@@ -79,9 +76,9 @@ Only the `browser-cli` command needs to be on your `PATH`. The browser launches
|
||||
```text
|
||||
browser-cli/
|
||||
├── browser_cli/
|
||||
│ ├── __init__.py # Python API — BrowserCLI class and Python API entry point
|
||||
│ ├── __init__.py # Python SDK — BrowserCLI class and SDK entry point
|
||||
│ ├── cli.py # Click CLI entry point
|
||||
│ ├── client.py # Local IPC client used by CLI and API
|
||||
│ ├── client.py # Local IPC client used by CLI and SDK
|
||||
│ ├── models.py # Tab and Group helper models
|
||||
│ ├── native_host.py # Native messaging host launched by the browser
|
||||
│ └── commands/
|
||||
@@ -99,7 +96,7 @@ browser-cli/
|
||||
│ └── src/ # TypeScript source split by command area
|
||||
│ └── index.ts # Builds generated extension/background.js
|
||||
├── examples/
|
||||
│ ├── demo.py # Python API walkthrough
|
||||
│ ├── demo.py # Python SDK walkthrough
|
||||
│ └── demo.sh # Bash CLI walkthrough
|
||||
├── tests/
|
||||
│ ├── conftest.py # shared pytest fixtures
|
||||
@@ -285,7 +282,7 @@ browser-cli completion zsh --script # output raw completion script
|
||||
|
||||
---
|
||||
|
||||
## Python API
|
||||
## Python SDK
|
||||
|
||||
```python
|
||||
from browser_cli import BrowserCLI
|
||||
@@ -293,11 +290,13 @@ from browser_cli import BrowserCLI
|
||||
b = BrowserCLI()
|
||||
```
|
||||
|
||||
Every CLI command has a corresponding method. The call blocks until the browser responds and returns the data directly as a Python object.
|
||||
Every CLI command has a corresponding SDK method. The call blocks until the browser responds and returns the data directly as a Python object.
|
||||
|
||||
```python
|
||||
# Navigation
|
||||
b.open("https://example.com")
|
||||
tab = b.open_tab("https://example.com") # returns a bound Tab object
|
||||
tab = b.open_tab("https://example.com", wait=True, timeout=10)
|
||||
b.open("https://example.com", background=True)
|
||||
b.open("https://example.com", window="work")
|
||||
b.reload()
|
||||
@@ -308,8 +307,14 @@ b.focus_url("github")
|
||||
|
||||
# Tabs
|
||||
tabs = b.tabs_list() # list[Tab]; in multi-browser mode each tab.browser is set
|
||||
tabs = b.tabs() # short alias for tabs_list()
|
||||
active = b.active_tab() # active Tab object
|
||||
tab = b.tab(1234) # tab by ID
|
||||
tab = b.find_tab("github") # first matching tab or None
|
||||
tabs = b.find_tabs("github") # alias for tabs_query()
|
||||
b.tabs_active(1234)
|
||||
b.tabs_close(1234)
|
||||
b.close_tab(tab) # accepts Tab or tab ID
|
||||
b.tabs_close_inactive()
|
||||
b.tabs_close_duplicates()
|
||||
b.tabs_filter("youtube") # list of matching tabs
|
||||
@@ -320,9 +325,19 @@ b.tabs_sort(by="domain")
|
||||
b.tabs_merge_windows()
|
||||
b.tabs_dedupe()
|
||||
|
||||
# Bound Tab helpers
|
||||
tab = b.active_tab()
|
||||
tab.pin()
|
||||
tab.screenshot()
|
||||
tab.refresh()
|
||||
tab.wait_for_load(timeout=10)
|
||||
tab.watch_url(r"/done$")
|
||||
|
||||
# Tab groups
|
||||
groups = b.group_list() # list[Group]; in multi-browser mode each group.browser is set
|
||||
b.group_open("research") # creates group, returns { id, name }
|
||||
groups = b.groups() # short alias for group_list()
|
||||
b.groups_create("research") # plural alias for group_create()
|
||||
b.group_create("research") # creates group, returns Group
|
||||
b.group_close(42)
|
||||
b.group_tabs(42) # tabs inside a group
|
||||
b.group_count() # int, or BrowserCounts(...) in multi-browser mode
|
||||
@@ -341,6 +356,7 @@ attrs = b.dom_attr("a", "href") # list of strings
|
||||
exists = b.dom_exists(".cookie-banner")# bool
|
||||
b.dom_click(".accept-button")
|
||||
b.dom_type("#search", "hello world")
|
||||
b.wait_for_selector("#results", visible=True, timeout=10)
|
||||
|
||||
# Extract
|
||||
links = b.extract_links() # list of { text, href }
|
||||
@@ -359,6 +375,7 @@ b.session_auto_save(True)
|
||||
|
||||
# Misc
|
||||
clients = b.clients()
|
||||
raw = b.command("tabs.count", {"pattern": "github"}) # escape hatch for raw commands
|
||||
```
|
||||
|
||||
**Error handling**
|
||||
|
||||
Reference in New Issue
Block a user