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

53 lines
2.0 KiB
Python

from __future__ import annotations
import json
import time
from dataclasses import asdict, is_dataclass
import click
from rich.console import Console
from browser_cli.commands import client_from_ctx, handle_errors
console = Console()
def _snapshot(client):
tabs = client.tabs.list()
return {str(t.id): asdict(t) if is_dataclass(t) else dict(t) for t in tabs}
def _emit(event, json_output: bool):
if json_output:
click.echo(json.dumps(event, default=str), flush=True)
else:
kind = event.get("type")
tab = event.get("tab") or {}
console.print(f"[cyan]{kind}[/cyan] {tab.get('id', '')} {tab.get('title') or ''} [dim]{tab.get('url') or ''}[/dim]")
@click.command("events")
@click.option("--interval", type=float, default=1.0, show_default=True, help="Polling interval in seconds")
@click.option("--once", is_flag=True, help="Emit initial snapshot and exit")
@click.option("--json", "json_output", is_flag=True, default=True, help="Emit JSON Lines (default)")
@click.option("--pretty", is_flag=True, help="Render human-readable events instead of JSON")
@handle_errors
def cmd_events(interval: float, once: bool, json_output: bool, pretty: bool):
"""Stream tab events as JSON Lines using a lightweight polling watcher."""
json_output = json_output and not pretty
client = client_from_ctx()
previous = _snapshot(client)
for tab in previous.values():
_emit({"type": "tabs.snapshot", "tab": tab}, json_output)
if once:
return
while True:
time.sleep(interval)
current = _snapshot(client)
for tab_id, tab in current.items():
if tab_id not in previous:
_emit({"type": "tabs.created", "tab": tab}, json_output)
elif tab != previous[tab_id]:
_emit({"type": "tabs.updated", "tab": tab, "previous": previous[tab_id]}, json_output)
for tab_id, tab in previous.items():
if tab_id not in current:
_emit({"type": "tabs.closed", "tab": tab}, json_output)
previous = current