browser-use--browser-harness
130 行
4.2 KiB
Python
130 行
4.2 KiB
Python
import os, sys, urllib.request
|
|
|
|
# Windows default stdout encoding is cp1252, which can't encode the 🐴 marker
|
|
# helpers prepend to tab titles (or anything else outside Latin-1). Force UTF-8
|
|
# so `print(page_info())` doesn't UnicodeEncodeError on Windows. Issue #124(4).
|
|
if hasattr(sys.stdout, "reconfigure"):
|
|
try: sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
except Exception: pass
|
|
|
|
from .admin import (
|
|
_version,
|
|
NAME,
|
|
daemon_alive,
|
|
ensure_daemon,
|
|
list_cloud_profiles,
|
|
list_local_profiles,
|
|
print_update_banner,
|
|
restart_daemon,
|
|
run_doctor,
|
|
run_doctor_fix_snap,
|
|
run_update,
|
|
start_remote_daemon,
|
|
stop_remote_daemon,
|
|
sync_local_profile,
|
|
)
|
|
from .helpers import *
|
|
|
|
HELP = """Browser Harness
|
|
|
|
Read SKILL.md for the default workflow and examples.
|
|
|
|
Typical usage:
|
|
browser-harness <<'PY'
|
|
ensure_real_tab()
|
|
print(page_info())
|
|
PY
|
|
|
|
Helpers are pre-imported. The daemon auto-starts and connects to the running browser.
|
|
|
|
Commands:
|
|
browser-harness --version print the installed version
|
|
browser-harness --doctor diagnose install, daemon, and browser state
|
|
browser-harness doctor same as --doctor
|
|
browser-harness doctor --fix-snap print how to fix Snap Chromium blocking CDP (Linux)
|
|
browser-harness --update [-y] pull the latest version (agents: pass -y)
|
|
browser-harness --reload stop the daemon so next call picks up code changes
|
|
"""
|
|
|
|
USAGE = """Usage:
|
|
browser-harness <<'PY'
|
|
print(page_info())
|
|
PY
|
|
"""
|
|
|
|
|
|
# Probe /json/version (not a bare TCP connect) so a non-Chrome process bound to
|
|
# 9222/9223 doesn't masquerade as Chrome and skip the cloud bootstrap. Mirrors
|
|
# daemon.py's fallback probe.
|
|
def _local_chrome_listening():
|
|
for port in (9222, 9223):
|
|
try:
|
|
urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=0.3).close()
|
|
return True
|
|
except OSError: pass
|
|
return False
|
|
|
|
|
|
# BU_CDP_URL / BU_CDP_WS are documented to override local Chrome discovery
|
|
# (install.md:58-59), so they must also block cloud auto-bootstrap. Without this
|
|
# guard, start_remote_daemon() in admin.py overwrites BU_CDP_WS in the daemon
|
|
# env with a cloud WebSocket URL, silently replacing the user's explicit endpoint
|
|
# *and* billing them for a cloud browser they never asked for.
|
|
def _explicit_cdp_configured():
|
|
return bool(os.environ.get("BU_CDP_URL") or os.environ.get("BU_CDP_WS"))
|
|
|
|
|
|
def main():
|
|
args = sys.argv[1:]
|
|
if args and args[0] in {"-h", "--help"}:
|
|
print(HELP)
|
|
return
|
|
if args and args[0] == "--version":
|
|
print(_version() or "unknown")
|
|
return
|
|
if args and args[0] == "--doctor":
|
|
sys.exit(run_doctor())
|
|
if args and args[0] == "doctor":
|
|
rest = args[1:]
|
|
if rest == ["--fix-snap"]:
|
|
sys.exit(run_doctor_fix_snap())
|
|
if rest:
|
|
print("usage: browser-harness doctor [--fix-snap]", file=sys.stderr)
|
|
sys.exit(2)
|
|
sys.exit(run_doctor())
|
|
if args and args[0] == "--update":
|
|
yes = any(a in {"-y", "--yes"} for a in args[1:])
|
|
sys.exit(run_update(yes=yes))
|
|
if args and args[0] == "--reload":
|
|
restart_daemon()
|
|
print("daemon stopped — will restart fresh on next call")
|
|
return
|
|
if args and args[0] == "--debug-clicks":
|
|
os.environ["BH_DEBUG_CLICKS"] = "1"
|
|
args = args[1:]
|
|
if not args and not sys.stdin.isatty():
|
|
code = sys.stdin.read()
|
|
if not code.strip():
|
|
sys.exit(USAGE)
|
|
else:
|
|
sys.exit(USAGE)
|
|
print_update_banner()
|
|
# Auto-bootstrap a cloud browser is opt-in via BU_AUTOSPAWN — BROWSER_USE_API_KEY alone
|
|
# is not enough, since the key is commonly set for unrelated reasons (profile sync,
|
|
# cloud API calls, parent agents managing their own session). An explicit BU_CDP_URL
|
|
# or BU_CDP_WS also blocks the spawn so we honour the precedence install.md promises.
|
|
if (
|
|
not daemon_alive()
|
|
and not _local_chrome_listening()
|
|
and not _explicit_cdp_configured()
|
|
and os.environ.get("BROWSER_USE_API_KEY")
|
|
and os.environ.get("BU_AUTOSPAWN")
|
|
):
|
|
start_remote_daemon(NAME)
|
|
ensure_daemon()
|
|
exec(code, globals())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|