- Respect the globally selected browser when renaming client aliases.
- Pass the resolved local profile into sync and async local transports so
BROWSER_CLI_PROFILE is honored consistently.
- Stop tabs.active from explicitly focusing the OS browser window, avoiding
virtual-desktop jumps during tab activation.
- Make window merging skip audible, unmuted windows so video playback windows
are not selected as merge targets.
- Bump the Python package and extension manifest versions to 0.12.2.
- Add regression coverage for browser selection and focus-stealing behavior.
- 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.
Restructure the Python API and internals around composable namespaces and
a standalone transport/endpoint layer. Bump to 0.12.0.
Python API:
- Replace flat methods (b.tabs_list(), b.group_list()) with namespaces:
b.nav, b.tabs, b.groups, b.windows, b.dom, b.extract, b.page, b.storage,
b.cookies, b.session, b.perf, b.extension.
- Shrink browser_cli/__init__.py to a thin composition root; move all
behaviour into browser_cli/sdk/ (one module per namespace + factories,
base, routing).
Internals:
- Add browser_cli/transport.py and remote_transport.py to isolate IPC from
command logic; client.py now delegates instead of owning transport.
- Add browser_cli/endpoints.py for endpoint resolution and
browser_cli/errors.py for shared error types.
- Extract markdown rendering into browser_cli/markdown.py (out of extract).
- Add USER_AGENT to version_manager.
Tooling & tests:
- Add justfile with common dev tasks.
- Update CLI commands and demo to the namespaced API.
- Rework tests for the new layout; add test_transport.py and
test_refactor_boundaries.py to lock in module boundaries.
BREAKING CHANGE: flat API methods are removed in favour of namespaces
(e.g. b.tabs_list() -> b.tabs.list(), b.group_list() -> b.groups.list()).
Closing many tabs previously meant one IPC round-trip per tab
(tab.close() in a loop). Add a single batched path so callers can
close N tabs in one command, reusing the existing large-operation
throttle so the browser UI stays responsive.
- extension: tabs.close accepts tabIds: number[]; new branch feeds
the array through processInBatches/chrome.tabs.remove
- sdk: tabs_close(tab_ids=...) takes tab IDs or Tab objects; the
payload always carries "tabIds" (null when unused)
- tests: cover id-list and Tab-object batch close in test_api.py
- bump 0.10.3 -> 0.10.4 (pyproject.toml, manifest.json)
Patch bump after the class-based extension refactor (ba01be1) — no
behavior, command, or API change.
Bumped in pyproject.toml, extension/manifest.json and uv.lock.
Verification: uv run browser-cli -V -> 0.10.3; uv run pytest -q -> 409
passed, 105 skipped.
- Check Unix socket reachability with a real connection attempt instead of treating any existing path as active.
- Report ambiguous host-only remote aliases with actionable --remote/--browser examples.
- Update client tests to use listening Unix sockets and cover ambiguous remote alias errors.
- Bump package and extension versions to 0.10.2.
- Avoid resolving a saved remote alias when the requested profile is currently reachable as a local endpoint.
- Add a helper that checks the registry and local socket path before remote alias discovery.
- Cover the routing precedence with a client unit test.
- Bump package and extension versions to 0.10.1.
- Add throttled large-operation handling for tab, group, and session commands.
- Introduce performance profiles, audible-tab aware gentle mode, and job progress tracking.
- Support background session restores with status/cancel commands and lazy placeholders.
- Expose new perf and extension CLI groups plus matching Python SDK methods.
- Preserve pinned tabs during session snapshots and debounce auto-save updates.
- Bump browser-cli and extension versions to 0.10.0 and add pytest-cov to dev deps.
- Add coverage for performance controls, background jobs, lazy restores, and tab metadata.
- 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.
- Add shared browser error URL detection for Chrome, Edge, Brave, and Firefox-style about:error pages.
- Short-circuit read-only DOM and HTML commands with safe fallbacks when tabs are already on browser error pages.
- Fail navigation waits, DOM waits, polling, and URL watches with clearer error-page messages.
- Bump package and extension version to 0.9.8 and extend regression coverage for cross-browser error-page handling.
- Treat chrome error page script failures as transient during injection retries.
- Return safe fallback values for read-only DOM commands when tabs land on browser error pages.
- Improve URL watch handling by checking pending URLs and reporting last seen URL/status on timeout.
- Bump package and extension version to 0.9.6 and add regression coverage for error-page behavior.
- Domain-like --remote endpoints default to port 443; :443 is optional
- _normalize_endpoint strips :443 before storage in remotes.json
- _load_remotes normalises keys on load (backward compat migration)
- _remote_display_name omits :443 for domain endpoints
- _resolve_connect_endpoint adds :443 back for TCP connection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- compat.py → compat/ package: auth.py (auth-field normalizers),
commands.py (command-format shims), __init__.py (re-exports)
- Add _auth_0_9_3 transformer: normalizes pubkey to lowercase before auth
so clients < 0.9.3 sending uppercase hex are accepted
- adapt_auth() now called before auth check in serve.py; command extracted
after adapt_auth so future transformers can rename commands safely
- serve.py: deduplicate _recv_exact (import from client), unify
resp/resp_payload across Windows/Unix branches, require lowercase hex
pubkey (re.fullmatch), reorganize imports, drop unused os import
- client.py: move payload/framed construction inside branches (remote path
no longer serializes JSON it never uses); fix _is_valid_key_spec
operator precedence; import MAX_MSG_BYTES from version_manager
- auth.py: narrow except clause (ValueError instead of bare Exception)
- Bump version 0.9.2 → 0.9.3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Security:
- serve.py: server now sends nonce challenge before accepting any command;
clients sign nonce + SHA256(canonical_payload) with Ed25519 key
- New --authorized-keys FILE option for serve; token auth still works as fallback
- Connection limit: BoundedSemaphore(64) in serve.py
- Secure file creation with os.open(..., 0o600) for token/key files
- New auth.py module: keygen, file key load/save, SSH agent protocol (pure Python),
sign/verify helpers compatible with both file keys and agent-held keys (YubiKey,
TPM, gpg-agent)
Features:
- YubiKey support via SSH agent protocol — no new runtime deps, just $SSH_AUTH_SOCK
- New `browser-cli auth` command group: keygen, trust, show, keys
- Global --key PATH flag (or BROWSER_CLI_KEY env) selects signing key;
pass "agent" or "agent:<selector>" to use SSH agent key
- BrowserCLI Python API gains key= parameter
Bug fixes (11 issues across two review passes):
- client.py: check response is not None before json.loads
- native_host.py: _read_exact_stream loop handles EINTR short reads; fix Windows
Listener leak on accept error
- __init__.py: open_wait / tabs_watch_url raise RuntimeError instead of silent None
- extension/tabs.ts: dedupe skips tabs without URL; tabsSort uses pendingUrl fallback
- extension/session.ts: removeListener before addListener prevents duplicate handlers
Breaking: TCP serve protocol now sends a challenge frame first (v0.9.0)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes a local browser over a TCP socket so remote machines can
control it using the same CLI and Python API. Token auth (auto-generated
via secrets.token_urlsafe) is on by default; --no-auth disables it.
Profile routing via _route message field lets clients target specific
browser instances on the remote host. BROWSER_CLI_PROFILE is forwarded
automatically so --browser flag works transparently over remote.
- browser-cli serve [--host] [--port] [--token] [--no-auth]
- browser-cli --remote HOST:PORT --token TOKEN <command>
- BrowserCLI(remote="host:port", token="...").tabs_list()