feat(browser): auto-launch Chrome when /browser connect finds no debugger
When /browser connect detects that port 9222 isn't open, it now: 1. Finds Chrome/Chromium/Brave/Edge on the system (macOS app bundles or Linux PATH lookup) 2. Launches it with --remote-debugging-port=9222 (detached) 3. Waits up to 5 seconds for the port to come up 4. Falls back to manual instructions if auto-launch fails This means GUI-only users can just type /browser connect without needing to know about terminal flags or Chrome launch commands.
This commit is contained in:
parent
9d1483c7e6
commit
73f39a7761
1 changed files with 93 additions and 22 deletions
115
cli.py
115
cli.py
|
|
@ -3455,6 +3455,49 @@ class HermesCLI:
|
|||
self._background_tasks[task_id] = thread
|
||||
thread.start()
|
||||
|
||||
@staticmethod
|
||||
def _try_launch_chrome_debug(port: int, system: str) -> bool:
|
||||
"""Try to launch Chrome/Chromium with remote debugging enabled.
|
||||
|
||||
Returns True if a launch command was executed (doesn't guarantee success).
|
||||
"""
|
||||
import shutil
|
||||
import subprocess as _sp
|
||||
|
||||
candidates = []
|
||||
if system == "Darwin":
|
||||
# macOS: try common app bundle locations
|
||||
for app in (
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
||||
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
||||
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
||||
):
|
||||
if os.path.isfile(app):
|
||||
candidates.append(app)
|
||||
else:
|
||||
# Linux: try common binary names
|
||||
for name in ("google-chrome", "google-chrome-stable", "chromium-browser",
|
||||
"chromium", "brave-browser", "microsoft-edge"):
|
||||
path = shutil.which(name)
|
||||
if path:
|
||||
candidates.append(path)
|
||||
|
||||
if not candidates:
|
||||
return False
|
||||
|
||||
chrome = candidates[0]
|
||||
try:
|
||||
_sp.Popen(
|
||||
[chrome, f"--remote-debugging-port={port}"],
|
||||
stdout=_sp.DEVNULL,
|
||||
stderr=_sp.DEVNULL,
|
||||
start_new_session=True, # detach from terminal
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _handle_browser_command(self, cmd: str):
|
||||
"""Handle /browser connect|disconnect|status — manage live Chrome CDP connection."""
|
||||
import platform as _plat
|
||||
|
|
@ -3471,8 +3514,6 @@ class HermesCLI:
|
|||
connect_parts = cmd.strip().split(None, 2) # ["/browser", "connect", "ws://..."]
|
||||
cdp_url = connect_parts[2].strip() if len(connect_parts) > 2 else _DEFAULT_CDP
|
||||
|
||||
os.environ["BROWSER_CDP_URL"] = cdp_url
|
||||
|
||||
# Clear any existing browser sessions so the next tool call uses the new backend
|
||||
try:
|
||||
from tools.browser_tool import cleanup_all_browsers
|
||||
|
|
@ -3481,38 +3522,68 @@ class HermesCLI:
|
|||
pass
|
||||
|
||||
print()
|
||||
print("🌐 Browser connected to live Chrome via CDP")
|
||||
print(f" Endpoint: {cdp_url}")
|
||||
print()
|
||||
|
||||
# Platform-specific launch instructions
|
||||
sys_name = _plat.system()
|
||||
if sys_name == "Darwin":
|
||||
chrome_cmd = '/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --remote-debugging-port=9222'
|
||||
elif sys_name == "Windows":
|
||||
chrome_cmd = 'chrome.exe --remote-debugging-port=9222'
|
||||
else:
|
||||
chrome_cmd = "google-chrome --remote-debugging-port=9222"
|
||||
|
||||
print(" If Chrome isn't running with remote debugging yet:")
|
||||
print(f" $ {chrome_cmd}")
|
||||
print()
|
||||
|
||||
# Quick connectivity test
|
||||
# Extract port for connectivity checks
|
||||
_port = 9222
|
||||
try:
|
||||
_port = int(cdp_url.rsplit(":", 1)[-1].split("/")[0])
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Check if Chrome is already listening on the debug port
|
||||
import socket
|
||||
_already_open = False
|
||||
try:
|
||||
import socket
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(1)
|
||||
s.connect(("127.0.0.1", _port))
|
||||
s.close()
|
||||
print(f" ✓ Port {_port} is open — Chrome is reachable")
|
||||
_already_open = True
|
||||
except (OSError, socket.timeout):
|
||||
print(f" ⚠ Port {_port} is not open — launch Chrome with the command above first")
|
||||
pass
|
||||
|
||||
if _already_open:
|
||||
print(f" ✓ Chrome is already listening on port {_port}")
|
||||
elif cdp_url == _DEFAULT_CDP:
|
||||
# Try to auto-launch Chrome with remote debugging
|
||||
print(" Chrome isn't running with remote debugging — attempting to launch...")
|
||||
_launched = self._try_launch_chrome_debug(_port, _plat.system())
|
||||
if _launched:
|
||||
# Wait for the port to come up
|
||||
import time as _time
|
||||
for _wait in range(10):
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(1)
|
||||
s.connect(("127.0.0.1", _port))
|
||||
s.close()
|
||||
_already_open = True
|
||||
break
|
||||
except (OSError, socket.timeout):
|
||||
_time.sleep(0.5)
|
||||
if _already_open:
|
||||
print(f" ✓ Chrome launched and listening on port {_port}")
|
||||
else:
|
||||
print(f" ⚠ Chrome launched but port {_port} isn't responding yet")
|
||||
print(" You may need to close existing Chrome windows first and retry")
|
||||
else:
|
||||
print(f" ⚠ Could not auto-launch Chrome")
|
||||
# Show manual instructions as fallback
|
||||
sys_name = _plat.system()
|
||||
if sys_name == "Darwin":
|
||||
chrome_cmd = 'open -a "Google Chrome" --args --remote-debugging-port=9222'
|
||||
elif sys_name == "Windows":
|
||||
chrome_cmd = 'chrome.exe --remote-debugging-port=9222'
|
||||
else:
|
||||
chrome_cmd = "google-chrome --remote-debugging-port=9222"
|
||||
print(f" Launch Chrome manually: {chrome_cmd}")
|
||||
else:
|
||||
print(f" ⚠ Port {_port} is not reachable at {cdp_url}")
|
||||
|
||||
os.environ["BROWSER_CDP_URL"] = cdp_url
|
||||
print()
|
||||
print("🌐 Browser connected to live Chrome via CDP")
|
||||
print(f" Endpoint: {cdp_url}")
|
||||
print()
|
||||
|
||||
# Inject context message so the model knows
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue