"""Command message/response helpers shared by sync and async clients.""" import json import os import uuid from typing import Any from browser_cli import transport from browser_cli.endpoints import _normalize_endpoint from browser_cli.errors import BrowserNotConnected def base_message(command: str, args: dict | None) -> dict: return {"id": str(uuid.uuid4()), "command": command, "args": args or {}} def requested_target(profile: str | None, remote: str | None) -> tuple[str | None, str | None]: requested_profile = profile or os.environ.get("BROWSER_CLI_PROFILE") remote_endpoint = remote or os.environ.get("BROWSER_CLI_REMOTE") return requested_profile, _normalize_endpoint(remote_endpoint) if remote_endpoint else None def encode_payload(msg: dict) -> bytes: return json.dumps(msg).encode("utf-8") def decode_response(response: bytes | None) -> Any: if response is None: raise ConnectionError("Connection closed before full response received") result = transport.decode_response(response) if not result.get("success", True): raise RuntimeError(result.get("error", "unknown error from browser")) return result.get("data") def local_connection_error(profile: str | None) -> BrowserNotConnected: profile_hint = f" (profile: {profile})" if profile else "" return BrowserNotConnected( f"Cannot connect to browser{profile_hint}.\n" "Make sure:\n" " 1. The browser-cli extension is installed and enabled\n" " 2. The native host is registered: uv run browser-cli install \n" " 3. Your browser is running\n" " Tip: use BROWSER_CLI_PROFILE= to select a specific profile" ) def remote_connection_error(remote_endpoint: str) -> BrowserNotConnected: return BrowserNotConnected( f"Cannot connect to remote browser at {remote_endpoint}.\n" "Make sure browser-cli serve is running on the remote host." )