9df5e1bd8f
Testing / remote-protocol-compat (0.9.5) (push) Successful in 40s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 42s
Package Extension / package-extension (push) Successful in 32s
Build & Publish Package / publish (push) Successful in 25s
Testing / test (push) Successful in 31s
- Rename the PyPI distribution from browser-cli to real-browser-cli after PyPI rejected the original name as too similar to an existing project. - Keep the installed console command as browser-cli so user-facing CLI usage remains unchanged. - Add README-based package metadata, author information, and project URLs so PyPI renders a proper project description. - Centralize the PyPI distribution name for importlib.metadata version lookups used by the CLI, doctor command, and remote user agent. - Document uv tool install, optional fast extra installation, and upgrade commands. - Bump package and extension metadata to 0.14.3 for the republished release.
153 lines
5.4 KiB
Python
Executable File
153 lines
5.4 KiB
Python
Executable File
#!/usr/bin/env -S uv run
|
|
"""
|
|
browser-cli — Control your running browser from the terminal.
|
|
"""
|
|
import click
|
|
import os
|
|
import shutil
|
|
import re
|
|
from importlib.metadata import PackageNotFoundError, version as package_version
|
|
from pathlib import Path
|
|
from rich.console import Console
|
|
|
|
from browser_cli.commands.navigate import nav_group
|
|
from browser_cli.commands.tabs import tabs_group
|
|
from browser_cli.commands.groups import group_group
|
|
from browser_cli.commands.windows import windows_group
|
|
from browser_cli.commands.dom import dom_group
|
|
from browser_cli.commands.extract import extract_group
|
|
from browser_cli.commands.session import session_group
|
|
from browser_cli.commands.search import search_group
|
|
from browser_cli.commands.page import page_group
|
|
from browser_cli.commands.storage import storage_group
|
|
from browser_cli.commands.perf import perf_group
|
|
from browser_cli.commands.extension import extension_group
|
|
from browser_cli.commands.serve import cmd_serve
|
|
from browser_cli.commands.auth import auth_group
|
|
from browser_cli.commands.clients import clients_group
|
|
from browser_cli.commands.completion import cmd_completion
|
|
from browser_cli.commands.install import cmd_install
|
|
from browser_cli.commands.doctor import cmd_doctor
|
|
from browser_cli.commands.events import cmd_events
|
|
from browser_cli.commands.remote import remote_group
|
|
from browser_cli.commands.script import cmd_script
|
|
from browser_cli.commands.serve_http import cmd_serve_http
|
|
from browser_cli.commands.watch import watch_group
|
|
from browser_cli.commands.workspace import workspace_group
|
|
from browser_cli.commands.raw import cmd_command
|
|
from browser_cli.constants import PYPI_PACKAGE_NAME
|
|
|
|
console = Console()
|
|
|
|
# Click's Group.shell_complete hardcodes no limit for get_short_help_str (defaults to 45 chars);
|
|
# patch to use a wider limit so zsh completion descriptions aren't truncated.
|
|
def _patched_group_shell_complete(self, ctx, incomplete):
|
|
from click.shell_completion import CompletionItem
|
|
results = [
|
|
CompletionItem(name, help=command.get_short_help_str(limit=shutil.get_terminal_size().columns))
|
|
for name, command in self.commands.items()
|
|
if not command.hidden and name.startswith(incomplete)
|
|
]
|
|
results.extend(click.Command.shell_complete(self, ctx, incomplete))
|
|
return results
|
|
|
|
click.Group.shell_complete = _patched_group_shell_complete
|
|
|
|
def _project_version() -> str:
|
|
pyproject_path = Path(__file__).resolve().parent.parent / "pyproject.toml"
|
|
try:
|
|
content = pyproject_path.read_text(encoding="utf-8")
|
|
match = re.search(r'^version\s*=\s*"([^"]+)"', content, re.MULTILINE)
|
|
if match:
|
|
return match.group(1)
|
|
except OSError:
|
|
pass
|
|
|
|
try:
|
|
return package_version(PYPI_PACKAGE_NAME)
|
|
except PackageNotFoundError:
|
|
return "unknown"
|
|
|
|
def _print_version(ctx, param, value):
|
|
if not value or ctx.resilient_parsing:
|
|
return
|
|
click.echo(_project_version())
|
|
ctx.exit()
|
|
|
|
@click.group()
|
|
@click.option(
|
|
"-V", "--version",
|
|
is_flag=True,
|
|
is_eager=True,
|
|
expose_value=False,
|
|
callback=_print_version,
|
|
help="Show the browser-cli version and exit.",
|
|
)
|
|
@click.option(
|
|
"--browser", default=None, metavar="ALIAS",
|
|
help="Browser profile alias to target (required when multiple browsers are active).",
|
|
)
|
|
@click.option(
|
|
"--remote", default=None, metavar="HOST[:PORT]",
|
|
help="Connect to a remote browser exposed via 'browser-cli serve'. Domains default to port 443.",
|
|
)
|
|
@click.option(
|
|
"--key", default=None, metavar="PATH",
|
|
help="Ed25519 private key PEM for pubkey auth with a remote serve instance.",
|
|
)
|
|
@click.pass_context
|
|
def main(ctx, browser, remote, key):
|
|
"""Control your running browser from the terminal via a Chrome extension."""
|
|
ctx.ensure_object(dict)
|
|
ctx.obj["browser"] = browser
|
|
ctx.obj["browser_explicit"] = browser is not None
|
|
if browser:
|
|
os.environ["BROWSER_CLI_PROFILE"] = browser
|
|
ctx.call_on_close(lambda: os.environ.pop("BROWSER_CLI_PROFILE", None))
|
|
ctx.obj["remote"] = remote
|
|
ctx.obj["key"] = key
|
|
if remote:
|
|
os.environ["BROWSER_CLI_REMOTE"] = remote
|
|
ctx.call_on_close(lambda: os.environ.pop("BROWSER_CLI_REMOTE", None))
|
|
if key:
|
|
os.environ["BROWSER_CLI_KEY"] = key
|
|
ctx.call_on_close(lambda: os.environ.pop("BROWSER_CLI_KEY", None))
|
|
|
|
# ── Sub-command groups ─────────────────────────────────────────────────────────
|
|
main.add_command(auth_group)
|
|
main.add_command(nav_group)
|
|
main.add_command(tabs_group)
|
|
main.add_command(group_group)
|
|
main.add_command(windows_group)
|
|
main.add_command(dom_group)
|
|
main.add_command(extract_group)
|
|
main.add_command(session_group)
|
|
main.add_command(search_group)
|
|
main.add_command(page_group)
|
|
main.add_command(storage_group)
|
|
main.add_command(perf_group)
|
|
main.add_command(extension_group)
|
|
main.add_command(cmd_serve)
|
|
main.add_command(clients_group)
|
|
main.add_command(cmd_completion)
|
|
main.add_command(cmd_install)
|
|
main.add_command(cmd_doctor)
|
|
main.add_command(cmd_events)
|
|
main.add_command(remote_group)
|
|
main.add_command(cmd_script)
|
|
main.add_command(cmd_serve_http)
|
|
main.add_command(watch_group)
|
|
main.add_command(workspace_group)
|
|
main.add_command(cmd_command)
|
|
|
|
# ── native-host (hidden, called by Chrome via native messaging) ────────────────
|
|
|
|
@main.command("native-host", hidden=True)
|
|
def cmd_native_host():
|
|
"""Native messaging host — called by Chrome, not for direct use."""
|
|
from browser_cli.native.host import main as _main
|
|
_main()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|