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.
97 lines
4.5 KiB
Python
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}")
|