""" Unix socket client — sends commands to the native host relay socket. Used by both the CLI and the public Python API. Profile selection order: 1. Explicit `profile` argument to send_command() 2. BROWSER_CLI_PROFILE environment variable 3. First entry in /tmp/browser-cli-registry.json 4. Fallback: /tmp/browser-cli-default.sock """ import json import os import socket import struct import uuid from pathlib import Path from typing import Any REGISTRY_PATH = Path("/tmp/browser-cli-registry.json") DEFAULT_SOCKET = "/tmp/browser-cli-default.sock" class BrowserNotConnected(Exception): """Raised when the native host socket is not available.""" 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(): try: reg = json.loads(REGISTRY_PATH.read_text()) if target in reg: return reg[target] except Exception: pass safe = target.replace(" ", "_").replace("/", "_") return f"/tmp/browser-cli-{safe}.sock" # Auto-detect: use first registered entry if REGISTRY_PATH.exists(): try: reg = json.loads(REGISTRY_PATH.read_text()) if reg: return next(iter(reg.values())) except Exception: pass return DEFAULT_SOCKET def send_command(command: str, args: dict | None = None, profile: str | None = None) -> Any: """Send a command to the browser and return the response data.""" sock_path = _resolve_socket(profile) msg = { "id": str(uuid.uuid4()), "command": command, "args": args or {}, } payload = json.dumps(msg).encode("utf-8") framed = struct.pack(" to select a specific profile" ) result = json.loads(response) if not result.get("success", True): raise RuntimeError(result.get("error", "unknown error from browser")) return result.get("data") def _recv_all(sock: socket.socket) -> bytes: raw_len = _recv_exact(sock, 4) msg_len = struct.unpack(" bytes: buf = b"" while len(buf) < n: chunk = sock.recv(n - len(buf)) if not chunk: raise ConnectionError("Socket closed before full message received") buf += chunk return buf