""" browser_cli — Python SDK for controlling your running browser. Usage: from browser_cli import BrowserCLI b = BrowserCLI() tabs = b.tabs.list() # list[Tab] tabs[0].close() tabs[0].move(forward=True) groups = b.groups.list() # list[Group] groups[0].tabs() groups[0].add_tab("https://example.com") b.nav.open("https://example.com") b.dom.click("#submit") b.session.save("work") # When multiple browser instances are active, pass the alias: b = BrowserCLI(browser="brave") Commands are grouped into namespaces on the client: b.nav navigation (open, reload, back, forward, focus, search) b.tabs tabs (list, open, close, move, status, mute, sort, ...) b.groups tab groups (list, create, add_tab, move, close) b.windows browser windows (list, open, close, rename) b.dom page elements (query, click, type, wait_for, eval, ...) b.extract content extraction (links, images, text, json, markdown) b.page page info b.storage localStorage / sessionStorage b.cookies cookies (list, get, set) b.session sessions (save, load, list, diff, ...) b.perf performance profile + background jobs b.extension control the extension itself """ from browser_cli.client import BrowserNotConnected, active_browser_targets, remote_browser_targets, send_command from browser_cli.models import BrowserCounts, Group, Tab from browser_cli.sdk import ( CookiesNS, DomNS, ExtensionNS, ExtractNS, GroupsNS, NavigationNS, PageNS, PerfNS, SessionNS, StorageNS, TabsNS, WindowsNS, ) from browser_cli.sdk.factories import FactoryMixin from browser_cli.sdk.routing import RoutingMixin __all__ = ["BrowserCLI", "BrowserCounts", "BrowserNotConnected", "Tab", "Group"] class BrowserCLI(FactoryMixin, RoutingMixin): """Client for a running browser, with commands grouped into namespaces. The client itself holds the connection target (browser/remote/key) and the shared machinery; the actual commands live on namespace accessors such as :attr:`tabs`, :attr:`dom`, and :attr:`session`. Object construction (``Tab``/``Group``) comes from :class:`~browser_cli.sdk.factories.FactoryMixin` and multi-browser fan-out from :class:`~browser_cli.sdk.routing.RoutingMixin`. """ def __init__(self, browser: str | None = None, remote: str | None = None, key: str | None = None): """ Args: browser: Profile alias to target. Required when multiple browser instances are active. Equivalent to ``--browser`` on the CLI. remote: Connect to a remote browser exposed via ``browser-cli serve``. Format: ``"host:port"`` (e.g. ``"192.168.1.10:8765"``). Can be combined with ``browser`` to route to a specific remote profile. key: Path to Ed25519 private key PEM for pubkey auth, or ``"agent"`` to use a key from the SSH agent (YubiKey, gpg-agent, etc.). Defaults to ``~/.config/browser-cli/client.key.pem`` if that file exists. """ self._browser = browser self._remote = remote self._key = key if key else None # Command namespaces. self.nav = NavigationNS(self) self.tabs = TabsNS(self) self.groups = GroupsNS(self) self.windows = WindowsNS(self) self.dom = DomNS(self) self.extract = ExtractNS(self) self.page = PageNS(self) self.storage = StorageNS(self) self.cookies = CookiesNS(self) self.session = SessionNS(self) self.perf = PerfNS(self) self.extension = ExtensionNS(self) @property def browser(self) -> str | None: """Target browser/profile alias, equivalent to ``--browser``.""" return self._browser @property def remote(self) -> str | None: """Remote endpoint used by this client, if any.""" return self._remote @property def key(self) -> str | None: """Ed25519 key spec used for remote auth, if explicitly configured.""" return self._key def _cmd(self, command: str, args: dict | None = None): return send_command(command, args, profile=self._browser, remote=self._remote, key=self._key) def command(self, command: str, args: dict | None = None): """Send a raw browser-cli command and return its response. This is the SDK escape hatch for commands that do not have a dedicated namespace method yet. """ return self._cmd(command, args or {}) def clients(self) -> list[dict]: """Return the active browser clients known to this connection.""" return self._cmd("clients.list", {})