5cec57e06d
Testing / remote-protocol-compat (0.9.3) (push) Successful in 40s
Testing / remote-protocol-compat (0.9.5) (push) Successful in 38s
Testing / test (push) Failing after 1m3s
Package Extension / package-extension (push) Successful in 29s
Build & Publish Package / publish (push) Successful in 33s
- Add safe-by-default policy gates for raw command surfaces: command, script, and serve-http /command. - Require explicit opt-ins for page reads, browser control, and high-risk commands such as dom.eval, storage.*, and screenshots. - Remove all cookies support from CLI, SDK, extension commands, permissions, constants, docs, and tests. - Add diagnostic, events, watch, workspace, remote, raw command, script, HTTP gateway, tree-view, session import/export, and extension info/capability commands. - Add Chrome Web Store packaging that strips manifest.key while keeping local packages with a stable native-messaging extension ID. - Bump browser-cli and extension version to 0.14.1 and cover the new behavior with pytest and extension packaging tests. BREAKING CHANGE: cookies commands and the b.cookies SDK namespace have been removed; generic raw command execution now blocks non-safe commands unless explicitly allowed.
82 lines
3.0 KiB
Python
82 lines
3.0 KiB
Python
"""Shared helpers for the Click CLI command modules.
|
|
|
|
Every CLI command is a thin presentation layer over the Python SDK: it builds a
|
|
:class:`~browser_cli.BrowserCLI` from the global ``--browser/--remote/--key``
|
|
options, calls the matching SDK namespace method, and renders the result. The
|
|
SDK is the single source of truth for command strings, argument shapes, and
|
|
multi-browser routing.
|
|
"""
|
|
import functools
|
|
import click
|
|
from rich.console import Console
|
|
from rich.table import Table
|
|
|
|
from browser_cli import BrowserCLI, BrowserCounts
|
|
from browser_cli.client import BrowserNotConnected
|
|
from browser_cli.constants import GENTLE_MODES
|
|
|
|
_console = Console()
|
|
|
|
# Reusable ``--tab`` option: select a tab by ID (default: the active tab).
|
|
tab_option = click.option("--tab", "tab_id", type=int, default=None, help="Tab ID (default: active tab)")
|
|
|
|
|
|
def gentle_mode_option(help_text: str):
|
|
"""Reusable ``--gentle-mode`` Click option (throttle mode for large operations)."""
|
|
return click.option(
|
|
"--gentle-mode",
|
|
type=click.Choice(GENTLE_MODES),
|
|
default="auto",
|
|
show_default=True,
|
|
help=help_text,
|
|
)
|
|
|
|
def print_counts(result, noun: str, *, single_suffix: str = "") -> None:
|
|
"""Render a count result.
|
|
|
|
In multi-browser mode (*result* is a :class:`~browser_cli.BrowserCounts`) print a
|
|
per-browser table with a Total row; otherwise print a single ``N noun(s)`` line.
|
|
"""
|
|
if isinstance(result, BrowserCounts):
|
|
table = Table(show_header=True, header_style="bold cyan")
|
|
table.add_column("Browser")
|
|
table.add_column(f"{noun.capitalize()}s", justify="right")
|
|
for name, count in result.by_browser.items():
|
|
table.add_row(name, str(count))
|
|
table.add_row("Total", str(result.total))
|
|
_console.print(table)
|
|
else:
|
|
_console.print(f"[bold]{result}[/bold] {noun}(s){single_suffix}")
|
|
|
|
def client_from_ctx() -> BrowserCLI:
|
|
"""Build a BrowserCLI from the root context's global options.
|
|
|
|
Reads ``browser``/``remote``/``key`` set by the top-level ``main`` group.
|
|
Falls back to an unconfigured client when a command group is invoked
|
|
standalone (e.g. in unit tests).
|
|
"""
|
|
obj = click.get_current_context().find_root().obj or {}
|
|
return BrowserCLI(browser=obj.get("browser"), remote=obj.get("remote"), key=obj.get("key"))
|
|
|
|
def handle_errors(fn):
|
|
"""Decorate a CLI command so SDK exceptions become clean errors + exit(1).
|
|
|
|
Apply as the innermost decorator (directly above ``def``) so Click's option
|
|
decorators attach their params to the wrapper.
|
|
"""
|
|
@functools.wraps(fn)
|
|
def wrapper(*args, **kwargs):
|
|
try:
|
|
return fn(*args, **kwargs)
|
|
except BrowserNotConnected as e:
|
|
_console.print(f"[red]Error:[/red] {e}")
|
|
raise SystemExit(1)
|
|
except PermissionError as e:
|
|
_console.print(f"[red]Blocked:[/red] {e}")
|
|
raise SystemExit(1)
|
|
except RuntimeError as e:
|
|
_console.print(f"[red]Browser error:[/red] {e}")
|
|
raise SystemExit(1)
|
|
|
|
return wrapper
|