add new search engine commands
This commit is contained in:
+17
-14
@@ -17,6 +17,7 @@ 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.client import send_command, BrowserNotConnected
|
||||
|
||||
console = Console()
|
||||
@@ -52,6 +53,7 @@ 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)
|
||||
|
||||
|
||||
# ── clients ────────────────────────────────────────────────────────────────────
|
||||
@@ -118,20 +120,12 @@ def cmd_rename_profile(alias):
|
||||
def cmd_install(browser):
|
||||
"""Register the native messaging host and print extension load instructions."""
|
||||
|
||||
# Find the venv entry point for the native host (stable regardless of project location)
|
||||
venv_script = Path(sys.executable).parent / "browser-cli-native-host"
|
||||
if not venv_script.exists():
|
||||
console.print(f"[red]Cannot find browser-cli-native-host in venv ({venv_script})[/red]")
|
||||
console.print(" Run [cyan]uv sync[/cyan] first to install entry points.")
|
||||
sys.exit(1)
|
||||
|
||||
# Install wrapper to ~/.local/bin so the manifest path never changes
|
||||
local_bin = Path.home() / ".local" / "bin"
|
||||
local_bin.mkdir(parents=True, exist_ok=True)
|
||||
wrapper_path = local_bin / "browser-cli-native-host"
|
||||
wrapper_content = f"""#!/bin/sh
|
||||
exec "{venv_script}" "$@"
|
||||
"""
|
||||
# Install wrapper outside PATH — Chrome uses the absolute path from the manifest,
|
||||
# so it doesn't need to be a shell command.
|
||||
share_dir = Path.home() / ".local" / "share" / "browser-cli"
|
||||
share_dir.mkdir(parents=True, exist_ok=True)
|
||||
wrapper_path = share_dir / "native-host"
|
||||
wrapper_content = f'#!/bin/sh\nexec "{sys.executable}" -m browser_cli.native_host "$@"\n'
|
||||
wrapper_path.write_text(wrapper_content)
|
||||
wrapper_path.chmod(wrapper_path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
||||
|
||||
@@ -181,6 +175,15 @@ exec "{venv_script}" "$@"
|
||||
console.print(" After restarting Chrome, try: [cyan]browser-cli tabs list[/cyan]")
|
||||
|
||||
|
||||
# ── 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()
|
||||
|
||||
|
||||
# ── completion ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@main.command("completion")
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import click
|
||||
from urllib.parse import quote_plus
|
||||
from browser_cli.client import send_command, BrowserNotConnected
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
|
||||
ENGINES = {
|
||||
"google": "https://www.google.com/search?q={query}",
|
||||
"brave": "https://search.brave.com/search?q={query}",
|
||||
"duckduckgo": "https://duckduckgo.com/?q={query}",
|
||||
"ddg": "https://duckduckgo.com/?q={query}",
|
||||
"youtube": "https://www.youtube.com/results?search_query={query}",
|
||||
"yt": "https://www.youtube.com/results?search_query={query}",
|
||||
"spotify": "https://open.spotify.com/search/{query}",
|
||||
"amazon": "https://www.amazon.com/s?k={query}",
|
||||
"ecosia": "https://www.ecosia.org/search?q={query}",
|
||||
"furaffinity": "https://www.furaffinity.net/search/?q={query}",
|
||||
"fa": "https://www.furaffinity.net/search/?q={query}",
|
||||
"bing": "https://www.bing.com/search?q={query}",
|
||||
"github": "https://github.com/search?q={query}",
|
||||
"wikipedia": "https://en.wikipedia.org/wiki/Special:Search?search={query}",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Special:Search?search={query}",
|
||||
"reddit": "https://www.reddit.com/search/?q={query}",
|
||||
"stackoverflow": "https://stackoverflow.com/search?q={query}",
|
||||
"so": "https://stackoverflow.com/search?q={query}",
|
||||
}
|
||||
|
||||
_DISPLAY_NAMES = {
|
||||
"google": "Google", "brave": "Brave Search", "duckduckgo": "DuckDuckGo",
|
||||
"ddg": "DuckDuckGo", "youtube": "YouTube", "yt": "YouTube",
|
||||
"spotify": "Spotify", "amazon": "Amazon", "ecosia": "Ecosia",
|
||||
"furaffinity": "FurAffinity", "fa": "FurAffinity", "bing": "Bing",
|
||||
"github": "GitHub", "wikipedia": "Wikipedia", "wiki": "Wikipedia",
|
||||
"reddit": "Reddit", "stackoverflow": "Stack Overflow", "so": "Stack Overflow",
|
||||
}
|
||||
|
||||
_SUBCOMMANDS = [
|
||||
("google", "Search with Google."),
|
||||
("brave", "Search with Brave Search."),
|
||||
("duckduckgo", "Search with DuckDuckGo."),
|
||||
("ddg", "Search with DuckDuckGo (alias for duckduckgo)."),
|
||||
("youtube", "Search YouTube videos."),
|
||||
("yt", "Search YouTube (alias for youtube)."),
|
||||
("spotify", "Search Spotify."),
|
||||
("amazon", "Search Amazon."),
|
||||
("ecosia", "Search with Ecosia."),
|
||||
("furaffinity", "Search FurAffinity."),
|
||||
("fa", "Search FurAffinity (alias for furaffinity)."),
|
||||
("bing", "Search with Bing."),
|
||||
("github", "Search GitHub."),
|
||||
("wikipedia", "Search Wikipedia."),
|
||||
("wiki", "Search Wikipedia (alias for wikipedia)."),
|
||||
("reddit", "Search Reddit."),
|
||||
("stackoverflow", "Search Stack Overflow."),
|
||||
("so", "Search Stack Overflow (alias for stackoverflow)."),
|
||||
]
|
||||
|
||||
|
||||
@click.group("search")
|
||||
def search_group():
|
||||
"""Search the web — open a query in a search engine."""
|
||||
|
||||
|
||||
def _build_command(engine_key: str, help_text: str) -> click.Command:
|
||||
@click.command(engine_key, help=help_text)
|
||||
@click.argument("query", nargs=-1, required=True)
|
||||
@click.option("--bg", is_flag=True, help="Open in background (no focus)")
|
||||
@click.option("--window", "window", default=None, help="Open in named window")
|
||||
@click.option("--group", "group", default=None, help="Open in tab group (name or ID)")
|
||||
def _cmd(query, bg, window, group):
|
||||
terms = " ".join(query)
|
||||
url = ENGINES[engine_key].format(query=quote_plus(terms))
|
||||
try:
|
||||
send_command("navigate.open", {"url": url, "background": bg, "window": window, "group": group})
|
||||
except BrowserNotConnected as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
except RuntimeError as e:
|
||||
console.print(f"[red]Browser error:[/red] {e}")
|
||||
raise SystemExit(1)
|
||||
suffix = f" in group '{group}'" if group else (f" in window '{window}'" if window else "")
|
||||
display = _DISPLAY_NAMES.get(engine_key, engine_key.capitalize())
|
||||
console.print(f"[green]Searching[/green] [cyan]{display}[/cyan]: {terms}{suffix}")
|
||||
|
||||
return _cmd
|
||||
|
||||
|
||||
for _name, _help in _SUBCOMMANDS:
|
||||
search_group.add_command(_build_command(_name, _help))
|
||||
Reference in New Issue
Block a user