fd5447cbb9
Testing / remote-protocol-compat (0.9.3) (push) Successful in 42s
Testing / remote-protocol-compat (0.9.5) (push) Successful in 44s
Package Extension / package-extension (push) Successful in 43s
Build & Publish Package / publish (push) Successful in 43s
Testing / test (push) Successful in 45s
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()).
58 lines
2.1 KiB
Python
58 lines
2.1 KiB
Python
"""Remote endpoint string handling — parsing, normalization, display.
|
|
|
|
Pure helpers (no sockets, no I/O) for turning user-facing ``host[:port]``
|
|
strings into the canonical forms the rest of the client uses, and back into the
|
|
short forms shown to humans. Re-exported from :mod:`browser_cli.client` for
|
|
backward compatibility.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from browser_cli.errors import BrowserNotConnected
|
|
|
|
_DEFAULT_REMOTE_PORT = 443
|
|
|
|
def _looks_like_domain(host: str) -> bool:
|
|
"""True if host looks like a domain name rather than an IP address or localhost."""
|
|
if host in {"localhost", "127.0.0.1", "::1"}:
|
|
return False
|
|
if re.match(r'^\d{1,3}(\.\d{1,3}){3}$', host):
|
|
return False
|
|
return '.' in host and any(c.isalpha() for c in host)
|
|
|
|
def _normalize_endpoint(endpoint: str) -> str:
|
|
"""Strip :443 from domain-like endpoints so they are stored without the default port."""
|
|
if not endpoint:
|
|
return endpoint
|
|
host, sep, port = endpoint.rpartition(":")
|
|
if sep and port == "443" and _looks_like_domain(host):
|
|
return host
|
|
return endpoint
|
|
|
|
def _resolve_connect_endpoint(endpoint: str) -> str:
|
|
"""Return host:port for TCP connection; domain without port defaults to :443."""
|
|
_, sep, _ = endpoint.rpartition(":")
|
|
if not sep:
|
|
if _looks_like_domain(endpoint):
|
|
return f"{endpoint}:{_DEFAULT_REMOTE_PORT}"
|
|
raise BrowserNotConnected(
|
|
f"Invalid remote endpoint '{endpoint}': expected host:port"
|
|
)
|
|
return endpoint
|
|
|
|
def display_browser_name(profile_name: str, sock_path: str) -> str:
|
|
from pathlib import Path
|
|
|
|
if profile_name != "default":
|
|
return profile_name
|
|
return Path(sock_path).stem or profile_name
|
|
|
|
def _remote_display_name(endpoint: str, profile_name: str, display_name: str) -> str:
|
|
host, sep, port = endpoint.rpartition(":")
|
|
if sep and (port == "8765" or (port == "443" and _looks_like_domain(host))):
|
|
display_endpoint = host
|
|
else:
|
|
display_endpoint = endpoint # normalized domain (no port) or non-default port
|
|
return f"{display_endpoint}:{display_name or profile_name}"
|