refactor: reorganize client transport and extension internals

- Split client, native, remote, serve, markdown, and SDK internals into focused packages with direct imports.
- Move local and remote transport framing/protocol helpers behind clearer module boundaries.
- Break up the extension injected DOM logic into a separate content dispatch bundle and dedicated content modules.
- Add explicit client handling for passive remote discovery without noisy PQ warnings.
- Keep behavior covered with updated unit, integration, and extension tests.
This commit is contained in:
2026-06-13 23:31:24 +02:00
parent fd5447cbb9
commit 076914e5b7
88 changed files with 7491 additions and 5228 deletions
+48 -8
View File
@@ -67,7 +67,7 @@ The `install` command will:
After install, **fully restart your browser** (Quit and reopen — not just close the window). The extension will connect to the native host automatically on startup.
Only the `browser-cli` command needs to be on your `PATH`. The browser launches the native host wrapper directly from its absolute path in the native messaging manifest, and that wrapper points to the internally installed `native_host.py` copy. On Windows the install command also registers the host in the current user's Registry for the selected browser.
Only the `browser-cli` command needs to be on your `PATH`. The browser launches the native host wrapper directly from its absolute path in the native messaging manifest, and that wrapper imports the installed `browser_cli.native.host` entry point. On Windows the install command also registers the host in the current user's Registry for the selected browser.
---
@@ -78,9 +78,19 @@ browser-cli/
├── browser_cli/
│ ├── __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 SDK
│ ├── client/ # Client-side command routing used by CLI and SDK
│ │ ├── core.py # send_command and remote command routing
│ │ ├── targets.py # Browser target discovery and socket resolution
│ │ ├── auth.py # Remote auth fields and key lookup
│ │ └── messages.py # Request/response helpers
│ ├── models.py # Tab and Group helper models
│ ├── native_host.py # Native messaging host launched by the browser
│ ├── native/ # Native messaging host internals
│ │ ├── host.py # Browser-launched native host entry point
│ │ ├── local_server.py # Local CLI IPC server
│ │ └── protocol.py # Chrome Native Messaging framing
│ ├── remote/ # Client-side remote browser support
│ │ ├── transport.py # TCP/TLS remote transport
│ │ └── registry.py # Saved remote endpoints/keys
│ └── commands/
│ ├── navigate.py # nav open/reload/back/forward/focus
│ ├── search.py # search engine shortcuts
@@ -94,7 +104,8 @@ browser-cli/
│ ├── manifest.json # MV3 extension manifest
│ ├── content.js # Content-script helpers
│ └── src/ # TypeScript source split by command area
── index.ts # Builds generated extension/background.js
── index.ts # Builds generated extension/background.js
│ └── content/ # Builds generated extension/content-dispatch.js
├── examples/
│ ├── demo.py # Python SDK walkthrough
│ └── demo.sh # Bash CLI walkthrough
@@ -285,12 +296,12 @@ browser-cli completion zsh --script # output raw completion script
## Python SDK
```python
from browser_cli import BrowserCLI
from browser_cli import AsyncBrowserCLI, BrowserCLI
b = BrowserCLI()
```
Commands are grouped into namespaces on the client (`b.tabs`, `b.dom`, `b.session`, ...). Each call blocks until the browser responds and returns the data directly as a Python object.
Commands are grouped into namespaces on the client (`b.tabs`, `b.dom`, `b.session`, ...). Each sync call blocks until the browser responds and returns the data directly as a Python object. For asyncio programs, `AsyncBrowserCLI` exposes the same namespaces as native awaitable methods over async Unix/TCP transport.
```python
# Navigation ── b.nav
@@ -385,6 +396,35 @@ b.perf.status()
b.perf.set_profile("gentle")
b.extension.reload()
# Workflow decorators ── b.decorators
@b.decorators.new_tab("https://example.com", wait=True, close=True)
def scrape(*, tab):
return b.extract.markdown("article")
@b.decorators.wait_for_selector("#ready", visible=True)
def run_after_page_ready():
return b.dom.text("#ready")
@b.decorators.performance_profile("ultra")
def restore_big_session():
return b.session.load("work", lazy=True)
@b.decorators.retry(times=3, delay=1)
@b.decorators.save_session_before("before-risky-step")
def risky_workflow():
b.tabs.close_duplicates()
# Async SDK: same namespaces, native awaitable methods
async def async_example():
ab = AsyncBrowserCLI()
tabs = await ab.tabs.list()
@ab.decorators.new_tab("https://example.com", wait=True, close=True)
async def scrape(*, tab):
return await ab.extract.markdown("article")
return tabs, await scrape()
# Misc
clients = b.clients()
raw = b.command("tabs.count", {"pattern": "github"}) # escape hatch for raw commands
@@ -436,7 +476,7 @@ bash examples/demo.sh
```sh
npm ci
npm run check:extension # type-check, build extension/background.js, syntax-check bundle
npm run check:extension # type-check, build extension bundles, syntax-check bundle
uv run pytest -q
```
@@ -447,7 +487,7 @@ nix-shell # automatically runs npm ci when node_modules is missing/outdated
npm run check:extension
```
The extension source lives in `extension/src/`. `extension/background.js` is generated and ignored by git. Run `npm run build:extension` before using `Load unpacked` with `extension/`. On NixOS, use `nix-shell` first if npm is not installed globally.
The extension source lives in `extension/src/`. `extension/background.js` and `extension/content-dispatch.js` are generated and ignored by git. Run `npm run build:extension` before using `Load unpacked` with `extension/`. On NixOS, use `nix-shell` first if npm is not installed globally.
---