Files
browser-cli/browser_cli/sdk/factories.py
T
daniel156161 479a0f1964
Testing / remote-protocol-compat (0.9.3) (push) Successful in 43s
Testing / test (push) Successful in 1m1s
Testing / remote-protocol-compat (0.9.5) (push) Successful in 39s
Build & Publish Package / publish (push) Successful in 58s
Package Extension / package-extension (push) Successful in 1m15s
feat: improve remote browser tree routing
- Allow remote host aliases passed via --browser to fan out for read-only
  multi-browser SDK paths while preserving strict routing for mutating commands.
- Add remote host grouping and scoped profile labels to tabs tree output so
  global views avoid repeated host prefixes.
- Carry browser family metadata through remote targets, tabs, and groups and
  style tree browser labels by family.
- Split CLI rendering helpers into a typed rendering package with dedicated
  common, label, tabs-tree, and windows-tree modules.
- Bump browser-cli and extension versions to 0.15.5.
- Cover the new routing and rendering behavior with unit and CLI tests.
2026-06-18 00:12:17 +02:00

118 lines
4.5 KiB
Python

"""Object-factory mixin for :class:`~browser_cli.BrowserCLI`.
Builds the typed :class:`~browser_cli.models.Tab` / :class:`~browser_cli.models.Group`
dataclasses from raw command responses and binds each one to the client that
should run its actions. In multi-browser mode an object is bound to a sibling
client targeting the browser it came from, so ``tab.close()`` routes correctly.
"""
from __future__ import annotations
from typing import Any, Protocol, cast
from browser_cli.models import Group, Tab
class _FactoryClient(Protocol):
_key: str | None
class FactoryMixin:
"""Turn raw response dicts into bound ``Tab``/``Group`` objects.
Mixed into :class:`~browser_cli.BrowserCLI`; relies on the client providing
``_browser``/``_remote``/``_key`` and being constructible via ``type(self)``.
"""
def tab_from(
self,
data: dict,
*,
browser_profile: str | None = None,
browser_name: str | None = None,
browser_remote: str | None = None,
browser_type: str | None = None,
browser_group: str | None = None,
) -> Tab:
tab = Tab(
id=data["id"],
window_id=data.get("windowId", 0),
active=data.get("active", False),
muted=data.get("muted", False),
title=data.get("title") or "",
url=data.get("url") or "",
group_id=data.get("groupId") or None,
index=data.get("index", 0) or 0,
browser=browser_name,
browser_name=browser_type,
browser_group=browser_group,
)
client = cast(_FactoryClient, self)
tab._browser = self if browser_profile is None else cast(Any, type(self))(
browser=browser_profile,
remote=browser_remote,
key=client._key,
_command_sender=getattr(self, "_command_sender", None),
)
return tab
def require_tab_response(self, data, error: str) -> Tab:
"""Build a bound Tab from a tab-shaped response, or raise ``RuntimeError(error)``."""
if not isinstance(data, dict) or "id" not in data:
raise RuntimeError(error)
return self.tab_from(data)
def group_from(
self,
data: dict,
*,
browser_profile: str | None = None,
browser_name: str | None = None,
browser_remote: str | None = None,
browser_type: str | None = None,
browser_group: str | None = None,
) -> Group:
group = Group(
id=data["id"],
title=data.get("title") or "",
color=data.get("color") or "",
collapsed=data.get("collapsed", False),
tab_count=data.get("tabCount", 0),
window_id=data.get("windowId"),
browser=browser_name,
browser_name=browser_type,
browser_group=browser_group,
)
client = cast(_FactoryClient, self)
group._browser = self if browser_profile is None else cast(Any, type(self))(
browser=browser_profile,
remote=browser_remote,
key=client._key,
_command_sender=getattr(self, "_command_sender", None),
)
return group
def tab_from_target(self, data: dict, target) -> Tab:
"""Build a Tab, tagging it with *target* in multi-browser mode (``None`` = local)."""
return self.tab_from(
data,
browser_profile=target.profile if target else None,
browser_name=target.display_name if target else None,
browser_remote=target.remote if target else None,
browser_type=getattr(target, "browser_name", None) if target else None,
browser_group=getattr(target, "display_group", None) if target else None,
)
def group_from_target(self, data: dict, target) -> Group:
"""Build a Group, tagging it with *target* in multi-browser mode (``None`` = local)."""
return self.group_from(
data,
browser_profile=target.profile if target else None,
browser_name=target.display_name if target else None,
browser_remote=target.remote if target else None,
browser_type=getattr(target, "browser_name", None) if target else None,
browser_group=getattr(target, "display_group", None) if target else None,
)
@staticmethod
def tag_browser(item: dict, target) -> dict:
"""Return *item* as-is locally, or with a ``browser`` key in multi-browser mode."""
return item if target is None else {**item, "browser": target.display_name}