Files
daniel156161 6fa931aa36
Testing / remote-protocol-compat (0.9.5) (push) Successful in 56s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 59s
Testing / test (push) Successful in 1m1s
Build & Publish Package / publish (push) Successful in 33s
Package Extension / package-extension (push) Successful in 36s
feat: harden remote serve and reuse connections
- Gate TCP serve commands with safe-by-default policies, per-key allow tokens, per-key rate limiting, and audit labels.
- Reuse authenticated encrypted remote sessions and parallelize/caches multi-browser fanout to reduce repeated handshake roundtrips.
- Increase paged native-host batch size with extension-side byte budgeting to speed large tab listings safely.
- Point install output at public Chrome Web Store / Firefox AMO listings by default, with --dev preserving unpacked workflows.
- Share search-engine metadata between CLI and SDK and bump the package/extension version to 0.16.0.
- Cover the new security, pooling, paging, install, and fanout behavior with expanded Python and extension tests.
2026-06-18 14:24:15 +02:00

103 lines
3.3 KiB
Python

"""Browser target discovery and local socket resolution."""
import os
import socket
from dataclasses import dataclass
from pathlib import Path
from browser_cli.endpoints import display_browser_name
from browser_cli.errors import BrowserNotConnected
from browser_cli.platform import endpoint_for_alias, is_windows, registry_path
from browser_cli.registry import load_registry
REGISTRY_PATH = registry_path()
@dataclass(frozen=True)
class BrowserTarget:
profile: str
display_name: str
socket_path: str
remote: str | None = None
browser_name: str | None = None
display_group: str | None = None
# Populated from a remote ``browser-cli.targets`` response when the remote is
# new enough to advertise them, letting ``clients`` skip a redundant
# ``clients.list`` roundtrip. None means "unknown — fall back to a query".
version: str | None = None
extension_version: str | None = None
def is_reachable_unix_endpoint(endpoint: str) -> bool:
"""Return True when a Unix socket path exists and accepts connections."""
path = Path(endpoint)
if not path.exists():
return False
try:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
sock.settimeout(0.2)
sock.connect(endpoint)
return True
except OSError:
return False
def active_endpoints(reg: dict) -> dict:
"""Return only entries whose endpoint appears reachable."""
if is_windows():
return dict(reg)
return {k: v for k, v in reg.items() if is_reachable_unix_endpoint(v)}
def active_local_browser_targets() -> list[BrowserTarget]:
if not REGISTRY_PATH.exists():
return []
reg = load_registry(REGISTRY_PATH)
return [
BrowserTarget(profile=profile, display_name=display_browser_name(profile, sock_path), socket_path=sock_path)
for profile, sock_path in active_endpoints(reg).items()
]
def is_active_local_profile(profile: str | None) -> bool:
"""Return True when profile names a reachable local browser endpoint."""
if not profile:
return False
if REGISTRY_PATH.exists():
reg = load_registry(REGISTRY_PATH)
if profile in active_endpoints(reg):
return True
if not is_windows():
try:
return Path(endpoint_for_alias(profile)).exists()
except Exception:
return False
return False
def resolve_socket(profile: str | None = None) -> str:
"""Return the socket path for the given profile (or auto-detect)."""
target = profile or os.environ.get("BROWSER_CLI_PROFILE")
if target:
if REGISTRY_PATH.exists():
reg = load_registry(REGISTRY_PATH)
if target in reg:
return reg[target]
return endpoint_for_alias(target)
try:
active = active_local_browser_targets()
if len(active) > 1:
aliases = [target.profile for target in active]
examples = "\n".join(f" browser-cli --browser {a} ..." for a in aliases)
raise BrowserNotConnected(
f"Multiple browser instances are active: {', '.join(aliases)}\n"
f"Use --browser <alias> to select one:\n{examples}"
)
if active:
return active[0].socket_path
except BrowserNotConnected:
raise
except Exception:
pass
raise BrowserNotConnected(
"Cannot resolve a browser socket automatically.\n"
"Make sure the browser is running with the browser-cli extension enabled,\n"
"or pass --browser <alias> / set BROWSER_CLI_PROFILE to a known alias."
)