fix(tui): show /browser connect progress like CLI
Return CLI-style browser connect status messages from the gateway and render them in the TUI so local Chrome launch attempts are visible instead of ending in a silent delayed failure.
This commit is contained in:
parent
69ff114ee2
commit
7d39a45749
@ -2811,7 +2811,8 @@ def test_browser_manage_status_reads_env_var(monkeypatch):
|
||||
{"id": "1", "method": "browser.manage", "params": {"action": "status"}}
|
||||
)
|
||||
|
||||
assert resp["result"] == {"connected": True, "url": "http://127.0.0.1:9222"}
|
||||
assert resp["result"]["connected"] is True
|
||||
assert resp["result"]["url"] == "http://127.0.0.1:9222"
|
||||
|
||||
|
||||
def test_browser_manage_status_falls_back_to_config_cdp_url(monkeypatch):
|
||||
@ -2874,7 +2875,9 @@ def test_browser_manage_connect_sets_env_and_cleans_twice(monkeypatch):
|
||||
}
|
||||
)
|
||||
|
||||
assert resp["result"] == {"connected": True, "url": "http://127.0.0.1:9222"}
|
||||
assert resp["result"]["connected"] is True
|
||||
assert resp["result"]["url"] == "http://127.0.0.1:9222"
|
||||
assert resp["result"]["messages"] == ["Chrome is already listening on port 9222"]
|
||||
assert os.environ.get("BROWSER_CDP_URL") == "http://127.0.0.1:9222"
|
||||
# First cleanup runs against the OLD env (none here), second against the NEW.
|
||||
assert cleanup_calls == ["", "http://127.0.0.1:9222"]
|
||||
@ -2892,7 +2895,9 @@ def test_browser_manage_connect_defaults_to_loopback(monkeypatch):
|
||||
{"id": "1", "method": "browser.manage", "params": {"action": "connect"}}
|
||||
)
|
||||
|
||||
assert resp["result"] == {"connected": True, "url": "http://127.0.0.1:9222"}
|
||||
assert resp["result"]["connected"] is True
|
||||
assert resp["result"]["url"] == "http://127.0.0.1:9222"
|
||||
assert resp["result"]["messages"] == ["Chrome is already listening on port 9222"]
|
||||
assert urls[0] == "http://127.0.0.1:9222/json/version"
|
||||
|
||||
|
||||
@ -2907,12 +2912,18 @@ def test_browser_manage_connect_default_local_reports_launch_hint(monkeypatch):
|
||||
with patch("hermes_cli.browser_connect.try_launch_chrome_debug", return_value=False), \
|
||||
patch("hermes_cli.browser_connect.get_chrome_debug_candidates", return_value=[]):
|
||||
resp = server.handle_request(
|
||||
{"id": "1", "method": "browser.manage", "params": {"action": "connect"}}
|
||||
{
|
||||
"id": "1",
|
||||
"method": "browser.manage",
|
||||
"params": {"action": "connect", "url": "http://localhost:9222"},
|
||||
}
|
||||
)
|
||||
|
||||
assert resp["error"]["code"] == 5031
|
||||
assert "No Chrome/Chromium executable was found" in resp["error"]["message"]
|
||||
assert "--remote-debugging-port=9222" in resp["error"]["message"]
|
||||
assert resp["result"]["connected"] is False
|
||||
assert resp["result"]["url"] == "http://127.0.0.1:9222"
|
||||
assert resp["result"]["messages"][0] == "Chrome isn't running with remote debugging — attempting to launch..."
|
||||
assert any("No Chrome/Chromium executable was found" in line for line in resp["result"]["messages"])
|
||||
assert any("--remote-debugging-port=9222" in line for line in resp["result"]["messages"])
|
||||
assert "BROWSER_CDP_URL" not in os.environ
|
||||
|
||||
|
||||
@ -2950,7 +2961,12 @@ def test_browser_manage_connect_default_local_retries_after_launch(monkeypatch):
|
||||
{"id": "1", "method": "browser.manage", "params": {"action": "connect"}}
|
||||
)
|
||||
|
||||
assert resp["result"] == {"connected": True, "url": "http://127.0.0.1:9222"}
|
||||
assert resp["result"]["connected"] is True
|
||||
assert resp["result"]["url"] == "http://127.0.0.1:9222"
|
||||
assert resp["result"]["messages"] == [
|
||||
"Chrome isn't running with remote debugging — attempting to launch...",
|
||||
"Chrome launched and listening on port 9222",
|
||||
]
|
||||
assert os.environ["BROWSER_CDP_URL"] == "http://127.0.0.1:9222"
|
||||
|
||||
|
||||
|
||||
@ -4777,6 +4777,15 @@ def _browser_connect_error(url: str, port: int) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _browser_connect_failure_messages(url: str, port: int) -> list[str]:
|
||||
command = _browser_connect_error(url, port)
|
||||
return [
|
||||
"Chrome isn't running with remote debugging — attempting to launch...",
|
||||
*command.splitlines(),
|
||||
"Browser not connected — start Chrome with remote debugging and retry /browser connect",
|
||||
]
|
||||
|
||||
|
||||
@method("browser.manage")
|
||||
def _(rid, params: dict) -> dict:
|
||||
action = params.get("action", "status")
|
||||
@ -4793,6 +4802,7 @@ def _(rid, params: dict) -> dict:
|
||||
from hermes_cli.browser_connect import DEFAULT_BROWSER_CDP_URL
|
||||
|
||||
url = params.get("url", DEFAULT_BROWSER_CDP_URL)
|
||||
messages: list[str] = []
|
||||
try:
|
||||
import urllib.request
|
||||
from urllib.parse import urlparse
|
||||
@ -4801,6 +4811,9 @@ def _(rid, params: dict) -> dict:
|
||||
parsed = urlparse(url if "://" in url else f"http://{url}")
|
||||
if parsed.scheme not in {"http", "https", "ws", "wss"}:
|
||||
return _err(rid, 4015, f"unsupported browser url: {url}")
|
||||
if _is_default_local_cdp(parsed):
|
||||
url = DEFAULT_BROWSER_CDP_URL
|
||||
parsed = urlparse(url)
|
||||
|
||||
# A concrete ``ws[s]://.../devtools/browser/<id>`` endpoint is
|
||||
# already directly connectable — those are the URLs Browserbase
|
||||
@ -4846,8 +4859,10 @@ def _(rid, params: dict) -> dict:
|
||||
from hermes_cli.browser_connect import try_launch_chrome_debug
|
||||
|
||||
port = parsed.port or 9222
|
||||
if try_launch_chrome_debug(port, platform.system()):
|
||||
for _ in range(10):
|
||||
messages.append("Chrome isn't running with remote debugging — attempting to launch...")
|
||||
launched = try_launch_chrome_debug(port, platform.system())
|
||||
if launched:
|
||||
for _ in range(20):
|
||||
time.sleep(0.5)
|
||||
for probe in probe_urls:
|
||||
try:
|
||||
@ -4859,10 +4874,15 @@ def _(rid, params: dict) -> dict:
|
||||
continue
|
||||
if ok:
|
||||
break
|
||||
if ok:
|
||||
messages.append(f"Chrome launched and listening on port {port}")
|
||||
if not ok:
|
||||
return _err(rid, 5031, _browser_connect_error(url, port))
|
||||
messages.extend(_browser_connect_failure_messages(url, port)[1:])
|
||||
return _ok(rid, {"connected": False, "url": url, "messages": messages})
|
||||
else:
|
||||
return _err(rid, 5031, f"could not reach browser CDP at {url}")
|
||||
elif _is_default_local_cdp(parsed):
|
||||
messages.append(f"Chrome is already listening on port {parsed.port or 9222}")
|
||||
|
||||
# Persist a normalized URL for downstream CDP resolution.
|
||||
# Discovery-style inputs (`http://host:port` or
|
||||
@ -4898,7 +4918,10 @@ def _(rid, params: dict) -> dict:
|
||||
cleanup_all_browsers()
|
||||
except Exception as e:
|
||||
return _err(rid, 5031, str(e))
|
||||
return _ok(rid, {"connected": True, "url": normalized})
|
||||
payload = {"connected": True, "url": normalized}
|
||||
if messages:
|
||||
payload["messages"] = messages
|
||||
return _ok(rid, payload)
|
||||
if action == "disconnect":
|
||||
try:
|
||||
from tools.browser_tool import cleanup_all_browsers
|
||||
|
||||
@ -207,6 +207,33 @@ describe('createSlashHandler', () => {
|
||||
expect(ctx.gateway.gw.request).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('renders browser connect progress messages from the gateway', async () => {
|
||||
const rpc = vi.fn(() =>
|
||||
Promise.resolve({
|
||||
connected: false,
|
||||
messages: [
|
||||
"Chrome isn't running with remote debugging — attempting to launch...",
|
||||
'Browser not connected — start Chrome with remote debugging and retry /browser connect'
|
||||
],
|
||||
url: 'http://127.0.0.1:9222'
|
||||
})
|
||||
)
|
||||
const ctx = buildCtx({ gateway: { ...buildGateway(), rpc } })
|
||||
|
||||
expect(createSlashHandler(ctx)('/browser connect')).toBe(true)
|
||||
expect(ctx.transcript.sys).toHaveBeenCalledWith('checking Chrome remote debugging at http://127.0.0.1:9222...')
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(ctx.transcript.sys).toHaveBeenCalledWith(
|
||||
"Chrome isn't running with remote debugging — attempting to launch..."
|
||||
)
|
||||
expect(ctx.transcript.sys).toHaveBeenCalledWith(
|
||||
'Browser not connected — start Chrome with remote debugging and retry /browser connect'
|
||||
)
|
||||
expect(ctx.transcript.sys).not.toHaveBeenCalledWith('browser connect failed')
|
||||
})
|
||||
})
|
||||
|
||||
it('routes /rollback through native RPC when a session is active', () => {
|
||||
patchUiState({ sid: 'sid-abc' })
|
||||
const rpc = vi.fn(() => Promise.resolve({}))
|
||||
|
||||
@ -108,12 +108,15 @@ export const opsCommands: SlashCommand[] = [
|
||||
|
||||
if (action === 'connect') {
|
||||
payload.url = requested || 'http://127.0.0.1:9222'
|
||||
ctx.transcript.sys(`checking Chrome remote debugging at ${payload.url}...`)
|
||||
}
|
||||
|
||||
ctx.gateway
|
||||
.rpc<BrowserManageResponse>('browser.manage', payload)
|
||||
.then(
|
||||
ctx.guarded<BrowserManageResponse>(r => {
|
||||
r.messages?.forEach(message => ctx.transcript.sys(message))
|
||||
|
||||
if (action === 'status') {
|
||||
return ctx.transcript.sys(
|
||||
r.connected
|
||||
@ -124,13 +127,14 @@ export const opsCommands: SlashCommand[] = [
|
||||
|
||||
if (action === 'connect') {
|
||||
if (r.connected) {
|
||||
ctx.transcript.sys(`browser connected: ${r.url || '(url unavailable)'}`)
|
||||
ctx.transcript.sys(`Browser connected to live Chrome via CDP`)
|
||||
ctx.transcript.sys(`Endpoint: ${r.url || '(url unavailable)'}`)
|
||||
ctx.transcript.sys('next browser tool call will use this CDP endpoint')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return ctx.transcript.sys('browser connect failed')
|
||||
return
|
||||
}
|
||||
|
||||
ctx.transcript.sys('browser disconnected')
|
||||
|
||||
@ -314,6 +314,7 @@ export interface ProcessStopResponse {
|
||||
|
||||
export interface BrowserManageResponse {
|
||||
connected?: boolean
|
||||
messages?: string[]
|
||||
url?: string
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user