refactor: split compat into package, harden serve proxy (v0.9.3)
Testing / test (push) Failing after 10m21s
Testing / test (push) Failing after 10m21s
- compat.py → compat/ package: auth.py (auth-field normalizers), commands.py (command-format shims), __init__.py (re-exports) - Add _auth_0_9_3 transformer: normalizes pubkey to lowercase before auth so clients < 0.9.3 sending uppercase hex are accepted - adapt_auth() now called before auth check in serve.py; command extracted after adapt_auth so future transformers can rename commands safely - serve.py: deduplicate _recv_exact (import from client), unify resp/resp_payload across Windows/Unix branches, require lowercase hex pubkey (re.fullmatch), reorganize imports, drop unused os import - client.py: move payload/framed construction inside branches (remote path no longer serializes JSON it never uses); fix _is_valid_key_spec operator precedence; import MAX_MSG_BYTES from version_manager - auth.py: narrow except clause (ValueError instead of bare Exception) - Bump version 0.9.2 → 0.9.3 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import click
|
||||
from browser_cli.client import BrowserNotConnected, active_browser_targets, remote_browser_targets, send_command
|
||||
from rich.console import Console
|
||||
|
||||
_console = Console()
|
||||
|
||||
|
||||
def _handle(command, args=None, profile=None):
|
||||
try:
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except BrowserNotConnected as e:
|
||||
_console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
_console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def _handle_multi(command, args=None, profile=None, remote=None):
|
||||
try:
|
||||
if remote:
|
||||
return send_command(command, args or {}, profile=profile, remote=remote)
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except (BrowserNotConnected, RuntimeError):
|
||||
return None
|
||||
|
||||
|
||||
def _multi_browser_targets():
|
||||
root = click.get_current_context().find_root()
|
||||
if root.obj.get("browser_explicit"):
|
||||
return []
|
||||
remote = root.obj.get("remote")
|
||||
key = root.obj.get("key")
|
||||
if remote:
|
||||
targets = remote_browser_targets(remote, key=key)
|
||||
else:
|
||||
targets = active_browser_targets(key=key)
|
||||
if len(targets) <= 1 and not any(target.remote for target in targets):
|
||||
return []
|
||||
return targets
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
import click
|
||||
from browser_cli.client import send_command, BrowserNotConnected
|
||||
from browser_cli.commands import _handle
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def _handle(command, args=None):
|
||||
try:
|
||||
return send_command(command, args or {})
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
@click.group("cookies")
|
||||
def cookies_group():
|
||||
"""Manage browser cookies."""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import click
|
||||
from browser_cli.client import send_command, BrowserNotConnected
|
||||
from browser_cli.commands import _handle
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
import json
|
||||
@@ -7,17 +7,6 @@ import json
|
||||
console = Console()
|
||||
|
||||
|
||||
def _handle(command, args=None):
|
||||
try:
|
||||
return send_command(command, args or {})
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
@click.group("dom")
|
||||
def dom_group():
|
||||
"""Query and interact with page DOM elements."""
|
||||
|
||||
@@ -3,7 +3,7 @@ import re
|
||||
from html.parser import HTMLParser
|
||||
|
||||
import click
|
||||
from browser_cli.client import send_command, BrowserNotConnected
|
||||
from browser_cli.commands import _handle
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
@@ -423,17 +423,6 @@ def _convert_html_to_markdown(html):
|
||||
return _clean_markdown_output(markdown)
|
||||
|
||||
|
||||
def _handle(command, args=None):
|
||||
try:
|
||||
return send_command(command, args or {})
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
@click.group("extract")
|
||||
def extract_group():
|
||||
"""Extract content from the active tab."""
|
||||
|
||||
@@ -1,46 +1,11 @@
|
||||
import click
|
||||
from browser_cli.client import BrowserNotConnected, active_browser_targets, remote_browser_targets, send_command
|
||||
from browser_cli.commands import _handle, _handle_multi, _multi_browser_targets
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def _handle(command, args=None, profile=None):
|
||||
try:
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def _handle_multi(command, args=None, profile=None, remote=None):
|
||||
try:
|
||||
if remote:
|
||||
return send_command(command, args or {}, profile=profile, remote=remote)
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except (BrowserNotConnected, RuntimeError):
|
||||
return None
|
||||
|
||||
|
||||
def _multi_browser_targets():
|
||||
root = click.get_current_context().find_root()
|
||||
if root.obj.get("browser_explicit"):
|
||||
return []
|
||||
remote = root.obj.get("remote")
|
||||
key = root.obj.get("key")
|
||||
if remote:
|
||||
targets = remote_browser_targets(remote, key=key)
|
||||
else:
|
||||
targets = active_browser_targets(key=key)
|
||||
if len(targets) <= 1 and not any(target.remote for target in targets):
|
||||
return []
|
||||
return targets
|
||||
|
||||
|
||||
def _print_groups(groups: list[dict], *, show_browser: bool = False) -> None:
|
||||
if not groups:
|
||||
console.print("[yellow]No groups found[/yellow]")
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
import click
|
||||
from browser_cli.client import send_command, BrowserNotConnected
|
||||
from browser_cli.commands import _handle
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def _handle(command, args):
|
||||
try:
|
||||
return send_command(command, args)
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
@click.group("nav")
|
||||
def nav_group():
|
||||
"""Navigate — open URLs, reload, go back/forward, focus tabs."""
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
import click
|
||||
from browser_cli.client import send_command, BrowserNotConnected
|
||||
from browser_cli.commands import _handle
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def _handle(command, args=None):
|
||||
try:
|
||||
return send_command(command, args or {})
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
@click.group("page")
|
||||
def page_group():
|
||||
"""Inspect current page metadata."""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import click
|
||||
from urllib.parse import quote_plus
|
||||
from browser_cli.client import send_command, BrowserNotConnected
|
||||
from browser_cli.commands import _handle
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
@@ -71,14 +71,7 @@ def _build_command(engine_key: str, help_text: str) -> click.Command:
|
||||
def _cmd(query, bg, window, group):
|
||||
terms = " ".join(query)
|
||||
url = ENGINES[engine_key].format(query=quote_plus(terms))
|
||||
try:
|
||||
send_command("navigate.open", {"url": url, "background": bg, "window": window, "group": group})
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
_handle("navigate.open", {"url": url, "background": bg, "window": window, "group": group})
|
||||
suffix = f" in group '{group}'" if group else (f" in window '{window}'" if window else "")
|
||||
display = _DISPLAY_NAMES.get(engine_key, engine_key.capitalize())
|
||||
console.print(f"[green]Searching[/green] [cyan]{display}[/cyan]: {terms}{suffix}")
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
import re, threading, secrets, socket, struct, click, json, sys, os
|
||||
import re, threading, secrets, socket, struct, click, json, sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from browser_cli.version_manager import PROTOCOL_MIN_CLIENT, parse_version, get_installed_version
|
||||
from browser_cli.compat import adapt_request, adapt_response
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
from browser_cli.client import _recv_exact, _recv_all
|
||||
from browser_cli.compat import adapt_auth, adapt_request, adapt_response
|
||||
from browser_cli.version_manager import PROTOCOL_MIN_CLIENT, MAX_MSG_BYTES, parse_version, get_installed_version
|
||||
|
||||
_UA_PATTERN = re.compile(r"^browser-cli/\d")
|
||||
|
||||
_CONN_LIMIT = threading.BoundedSemaphore(64)
|
||||
_MAX_MSG_BYTES = 32 * 1024 * 1024
|
||||
from rich.console import Console
|
||||
from datetime import datetime
|
||||
|
||||
console = Console()
|
||||
|
||||
def _recv_exact(sock:socket.socket, n:int) -> bytes:
|
||||
buf = b""
|
||||
while len(buf) < n:
|
||||
chunk = sock.recv(n - len(buf))
|
||||
if not chunk:
|
||||
raise ConnectionError("Connection closed")
|
||||
buf += chunk
|
||||
return buf
|
||||
|
||||
def _framed_send(sock: socket.socket, data: bytes) -> None:
|
||||
sock.sendall(struct.pack("<I", len(data)) + data)
|
||||
|
||||
def _log(addr:tuple, command:str, profile:str|None, status:str, error:str|None=None) -> None:
|
||||
ts = datetime.now().strftime("%H:%M:%S")
|
||||
@@ -37,14 +32,21 @@ def _proxy_request(client_sock:socket.socket, addr:tuple, profile:str|None, auth
|
||||
def _send_error(msg_id, msg:str) -> None:
|
||||
err = json.dumps({"id": msg_id, "success": False, "error": msg}).encode()
|
||||
try:
|
||||
client_sock.sendall(struct.pack("<I", len(err)) + err)
|
||||
_framed_send(client_sock, err)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def _send_ok(msg_id, payload) -> None:
|
||||
out = json.dumps({"id": msg_id, "success": True, "data": payload}).encode()
|
||||
try:
|
||||
_framed_send(client_sock, out)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
try:
|
||||
header = _recv_exact(client_sock, 4)
|
||||
msg_len = struct.unpack("<I", header)[0]
|
||||
if msg_len > _MAX_MSG_BYTES:
|
||||
if msg_len > MAX_MSG_BYTES:
|
||||
_send_error(None, f"message too large ({msg_len} bytes)")
|
||||
return
|
||||
payload = _recv_exact(client_sock, msg_len)
|
||||
@@ -58,25 +60,26 @@ def _proxy_request(client_sock:socket.socket, addr:tuple, profile:str|None, auth
|
||||
_log(addr, "?", None, "ERROR", "invalid JSON")
|
||||
return
|
||||
|
||||
msg_id = msg.get("id")
|
||||
command = msg.get("command", "?")
|
||||
|
||||
# ── user-agent + version check ────────────────────────────────────────────
|
||||
msg_id = msg.get("id")
|
||||
ua = msg.get("user_agent") or ""
|
||||
if not _UA_PATTERN.match(ua):
|
||||
_send_error(msg_id, "forbidden: client required")
|
||||
_log(addr, command, None, "DENIED", f"bad user-agent: {ua!r}")
|
||||
_log(addr, msg.get("command", "?"), None, "DENIED", f"bad user-agent: {ua!r}")
|
||||
return
|
||||
client_ver = "0"
|
||||
try:
|
||||
client_ver = ua.split("/", 1)[1]
|
||||
if parse_version(client_ver) < parse_version(PROTOCOL_MIN_CLIENT):
|
||||
_send_error(msg_id, f"client version {client_ver} is too old; please upgrade to >= {PROTOCOL_MIN_CLIENT}")
|
||||
_log(addr, command, None, "DENIED", f"client {client_ver} < min {PROTOCOL_MIN_CLIENT}")
|
||||
_log(addr, msg.get("command", "?"), None, "DENIED", f"client {client_ver} < min {PROTOCOL_MIN_CLIENT}")
|
||||
return
|
||||
except (IndexError, ValueError):
|
||||
pass
|
||||
|
||||
msg = adapt_auth(msg, client_ver)
|
||||
command = msg.get("command", "?")
|
||||
|
||||
# ── auth ──────────────────────────────────────────────────────────────────
|
||||
if auth_keys is not None:
|
||||
pub = msg.get("pubkey") or ""
|
||||
@@ -101,8 +104,7 @@ def _proxy_request(client_sock:socket.socket, addr:tuple, profile:str|None, auth
|
||||
{"profile": target.profile, "displayName": target.display_name}
|
||||
for target in active_browser_targets(include_remotes=False)
|
||||
]
|
||||
data = json.dumps({"id": msg_id, "success": True, "data": targets}).encode()
|
||||
client_sock.sendall(struct.pack("<I", len(data)) + data)
|
||||
_send_ok(msg_id, targets)
|
||||
_log(addr, command, None, "OK")
|
||||
return
|
||||
|
||||
@@ -113,8 +115,7 @@ def _proxy_request(client_sock:socket.socket, addr:tuple, profile:str|None, auth
|
||||
return
|
||||
from browser_cli.auth import load_authorized_keys_with_names
|
||||
entries = [{"pubkey": pk, "name": name} for pk, name in load_authorized_keys_with_names(auth_keys_path)]
|
||||
data = json.dumps({"id": msg_id, "success": True, "data": entries}).encode()
|
||||
client_sock.sendall(struct.pack("<I", len(data)) + data)
|
||||
_send_ok(msg_id, entries)
|
||||
_log(addr, command, None, "OK")
|
||||
return
|
||||
|
||||
@@ -127,13 +128,12 @@ def _proxy_request(client_sock:socket.socket, addr:tuple, profile:str|None, auth
|
||||
args = msg.get("args") or {}
|
||||
pubkey = str(args.get("pubkey") or "")
|
||||
name = str(args.get("name") or "")
|
||||
if len(pubkey) != 64:
|
||||
_send_error(msg_id, "invalid pubkey: expected 64 hex characters")
|
||||
if not re.fullmatch(r"[0-9a-f]{64}", pubkey):
|
||||
_send_error(msg_id, "invalid pubkey: expected 64 lowercase hex characters")
|
||||
_log(addr, command, None, "ERROR", "invalid pubkey")
|
||||
return
|
||||
added = add_authorized_key(auth_keys_path, pubkey, name)
|
||||
data = json.dumps({"id": msg_id, "success": True, "data": {"added": added}}).encode()
|
||||
client_sock.sendall(struct.pack("<I", len(data)) + data)
|
||||
_send_ok(msg_id, {"added": added})
|
||||
_log(addr, command, None, "OK" if added else "ALREADY_TRUSTED")
|
||||
return
|
||||
|
||||
@@ -158,20 +158,18 @@ def _proxy_request(client_sock:socket.socket, addr:tuple, profile:str|None, auth
|
||||
from multiprocessing.connection import Client as PipeClient
|
||||
with PipeClient(sock_path, family="AF_PIPE") as pipe:
|
||||
pipe.send_bytes(clean_payload)
|
||||
resp = pipe.recv_bytes()
|
||||
resp = adapt_response(resp, command, client_ver)
|
||||
client_sock.sendall(struct.pack("<I", len(resp)) + resp)
|
||||
resp_payload = pipe.recv_bytes()
|
||||
resp_payload = adapt_response(resp_payload, command, client_ver)
|
||||
_framed_send(client_sock, resp_payload)
|
||||
else:
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as local:
|
||||
local.connect(sock_path)
|
||||
local.sendall(clean_header + clean_payload)
|
||||
resp_header = _recv_exact(local, 4)
|
||||
resp_len = struct.unpack("<I", resp_header)[0]
|
||||
resp_payload = _recv_exact(local, resp_len)
|
||||
resp_payload = _recv_all(local)
|
||||
resp_payload = adapt_response(resp_payload, command, client_ver)
|
||||
client_sock.sendall(struct.pack("<I", len(resp_payload)) + resp_payload)
|
||||
_framed_send(client_sock, resp_payload)
|
||||
|
||||
resp_data = json.loads(resp_payload if not is_windows() else resp)
|
||||
resp_data = json.loads(resp_payload)
|
||||
if resp_data.get("success", True):
|
||||
_log(addr, command, resolved_profile, "OK")
|
||||
else:
|
||||
@@ -201,7 +199,7 @@ def _handle_client(client_sock:socket.socket, addr:tuple, profile:str|None, auth
|
||||
"min_client_version": PROTOCOL_MIN_CLIENT,
|
||||
}).encode()
|
||||
try:
|
||||
client_sock.sendall(struct.pack("<I", len(challenge)) + challenge)
|
||||
_framed_send(client_sock, challenge)
|
||||
except OSError:
|
||||
return
|
||||
_proxy_request(client_sock, addr, profile, auth_keys, auth_keys_path, nonce)
|
||||
|
||||
@@ -1,45 +1,10 @@
|
||||
import click
|
||||
from browser_cli.client import active_browser_targets, remote_browser_targets, send_command, BrowserNotConnected
|
||||
from browser_cli.commands import _handle, _handle_multi, _multi_browser_targets
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def _handle(command, args=None, profile=None):
|
||||
try:
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def _handle_multi(command, args=None, profile=None, remote=None):
|
||||
try:
|
||||
if remote:
|
||||
return send_command(command, args or {}, profile=profile, remote=remote)
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except (BrowserNotConnected, RuntimeError):
|
||||
return None
|
||||
|
||||
|
||||
def _multi_browser_targets():
|
||||
root = click.get_current_context().find_root()
|
||||
if root.obj.get("browser_explicit"):
|
||||
return []
|
||||
remote = root.obj.get("remote")
|
||||
key = root.obj.get("key")
|
||||
if remote:
|
||||
targets = remote_browser_targets(remote, key=key)
|
||||
else:
|
||||
targets = active_browser_targets(key=key)
|
||||
if len(targets) <= 1 and not any(target.remote for target in targets):
|
||||
return []
|
||||
return targets
|
||||
|
||||
|
||||
@click.group("session")
|
||||
def session_group():
|
||||
"""Save and restore browser sessions."""
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
import json
|
||||
import click
|
||||
from browser_cli.client import send_command, BrowserNotConnected
|
||||
from browser_cli.commands import _handle
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def _handle(command, args=None):
|
||||
try:
|
||||
return send_command(command, args or {})
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
@click.group("storage")
|
||||
def storage_group():
|
||||
"""Read and write the page's localStorage / sessionStorage."""
|
||||
|
||||
@@ -1,48 +1,13 @@
|
||||
import base64
|
||||
import binascii
|
||||
import click
|
||||
from browser_cli.client import BrowserNotConnected, active_browser_targets, remote_browser_targets, send_command
|
||||
from browser_cli.commands import _handle, _handle_multi, _multi_browser_targets
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def _handle(command, args=None, profile=None):
|
||||
try:
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def _handle_multi(command, args=None, profile=None, remote=None):
|
||||
try:
|
||||
if remote:
|
||||
return send_command(command, args or {}, profile=profile, remote=remote)
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except (BrowserNotConnected, RuntimeError):
|
||||
return None
|
||||
|
||||
|
||||
def _multi_browser_targets():
|
||||
root = click.get_current_context().find_root()
|
||||
if root.obj.get("browser_explicit"):
|
||||
return []
|
||||
remote = root.obj.get("remote")
|
||||
key = root.obj.get("key")
|
||||
if remote:
|
||||
targets = remote_browser_targets(remote, key=key)
|
||||
else:
|
||||
targets = active_browser_targets(key=key)
|
||||
if len(targets) <= 1 and not any(target.remote for target in targets):
|
||||
return []
|
||||
return targets
|
||||
|
||||
|
||||
def _print_tabs(tabs: list[dict], *, show_browser: bool = False) -> None:
|
||||
if not tabs:
|
||||
console.print("[yellow]No tabs found[/yellow]")
|
||||
|
||||
@@ -1,46 +1,11 @@
|
||||
import click
|
||||
from browser_cli.client import BrowserNotConnected, active_browser_targets, remote_browser_targets, send_command
|
||||
from browser_cli.commands import _handle, _handle_multi, _multi_browser_targets
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def _handle(command, args=None, profile=None):
|
||||
try:
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def _handle_multi(command, args=None, profile=None, remote=None):
|
||||
try:
|
||||
if remote:
|
||||
return send_command(command, args or {}, profile=profile, remote=remote)
|
||||
return send_command(command, args or {}, profile=profile)
|
||||
except (BrowserNotConnected, RuntimeError):
|
||||
return None
|
||||
|
||||
|
||||
def _multi_browser_targets():
|
||||
root = click.get_current_context().find_root()
|
||||
if root.obj.get("browser_explicit"):
|
||||
return []
|
||||
remote = root.obj.get("remote")
|
||||
key = root.obj.get("key")
|
||||
if remote:
|
||||
targets = remote_browser_targets(remote, key=key)
|
||||
else:
|
||||
targets = active_browser_targets(key=key)
|
||||
if len(targets) <= 1 and not any(target.remote for target in targets):
|
||||
return []
|
||||
return targets
|
||||
|
||||
|
||||
def _print_windows(windows: list[dict], *, show_browser: bool = False) -> None:
|
||||
if not windows:
|
||||
console.print("[yellow]No windows found[/yellow]")
|
||||
|
||||
Reference in New Issue
Block a user