Files
daniel156161 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
feat!: harden raw browser control and packaging
- 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.
2026-06-14 14:33:15 +02:00

97 lines
4.5 KiB
Python

import click
from browser_cli.commands import client_from_ctx, handle_errors, tab_option
from rich.console import Console
console = Console()
@click.group("nav")
def nav_group():
"""Navigate — open URLs, reload, go back/forward, focus tabs."""
@nav_group.command("open")
@click.argument("url")
@click.option("--focus", is_flag=True, help="Bring the opened tab/window to the front")
@click.option("--window", "window_name", default=None, help="Open in named window")
@click.option("--group", "group_name", default=None, help="Open directly into a tab group (name or ID)")
@click.option("--reuse", is_flag=True, help="Reuse an existing tab with exactly this URL")
@click.option("--reuse-domain", is_flag=True, help="Reuse an existing tab with the same domain")
@click.option("--reuse-title", default=None, metavar="TEXT", help="Reuse an existing tab whose title contains TEXT")
@handle_errors
def cmd_open(url, focus, window_name, group_name, reuse, reuse_domain, reuse_title):
"""Open URL in a new tab without stealing focus by default."""
client_from_ctx().nav.open(url, focus=focus, window=window_name, group=group_name, reuse=reuse, reuse_domain=reuse_domain, reuse_title=reuse_title)
suffix = ""
if group_name:
suffix = f" in group '{group_name}'"
elif window_name:
suffix = f" in window '{window_name}'"
console.print(f"[green]Opened:[/green] {url}{suffix}")
@nav_group.command("reload")
@click.argument("tab_id", type=int, required=False)
@handle_errors
def cmd_reload(tab_id):
"""Reload the active (or specified) tab."""
client_from_ctx().nav.reload(tab_id)
console.print("[green]Reloaded[/green]")
@nav_group.command("hard-reload")
@click.argument("tab_id", type=int, required=False)
@handle_errors
def cmd_hard_reload(tab_id):
"""Hard reload (bypass cache) the active (or specified) tab."""
client_from_ctx().nav.hard_reload(tab_id)
console.print("[green]Hard reloaded[/green]")
@nav_group.command("back")
@click.argument("tab_id", type=int, required=False)
@handle_errors
def cmd_back(tab_id):
"""Navigate back in the active (or specified) tab."""
client_from_ctx().nav.back(tab_id)
console.print("[green]Navigated back[/green]")
@nav_group.command("forward")
@click.argument("tab_id", type=int, required=False)
@handle_errors
def cmd_forward(tab_id):
"""Navigate forward in the active (or specified) tab."""
client_from_ctx().nav.forward(tab_id)
console.print("[green]Navigated forward[/green]")
@nav_group.command("focus")
@click.argument("pattern")
@handle_errors
def cmd_focus(pattern):
"""Jump to a tab by URL pattern or tab ID."""
result = client_from_ctx().nav.focus(pattern)
if result:
console.print(f"[green]Focused:[/green] {result.get('url', result)}")
else:
console.print(f"[yellow]No tab found matching:[/yellow] {pattern}")
@nav_group.command("open-wait")
@click.argument("url")
@click.option("--timeout", type=float, default=30.0, show_default=True, help="Max seconds to wait for load")
@click.option("--focus", is_flag=True, help="Bring the opened tab/window to the front")
@click.option("--window", "window_name", default=None, help="Open in named window")
@click.option("--group", "group_name", default=None, help="Open in tab group")
@click.option("--reuse", is_flag=True, help="Reuse an existing tab with exactly this URL")
@click.option("--reuse-domain", is_flag=True, help="Reuse an existing tab with the same domain")
@click.option("--reuse-title", default=None, metavar="TEXT", help="Reuse an existing tab whose title contains TEXT")
@handle_errors
def cmd_open_wait(url, timeout, focus, window_name, group_name, reuse, reuse_domain, reuse_title):
"""Open URL in a new tab and wait until fully loaded."""
tab = client_from_ctx().nav.open_wait(url, timeout=timeout, focus=focus, window=window_name, group=group_name, reuse=reuse, reuse_domain=reuse_domain, reuse_title=reuse_title)
console.print(f"[green]Loaded:[/green] {url}" + (f"{tab.title}" if tab.title else ""))
@nav_group.command("wait")
@tab_option
@click.option("--timeout", type=float, default=30.0, show_default=True, help="Max seconds to wait")
@click.option("--ready-state", type=click.Choice(["complete", "interactive"]), default="complete", show_default=True, help="Target ready state")
@handle_errors
def cmd_wait(tab_id, timeout, ready_state):
"""Wait until tab finishes loading."""
tab = client_from_ctx().tabs.wait_for_load(tab_id, timeout=timeout, ready_state=ready_state)
console.print(f"[green]Ready:[/green] {tab.url}{tab.title}")