Harden dm.py sidechat navigation (CDP event drain + Page.navigate)
- ev(): drain CDP events until response id==1 (was blind recv,
same bug class as box-chat-cdp.py 8d4bfa7 NO_SWITCHER fix)
- cmd_sidechat_use UUID path: use CDP Page.navigate instead of
window.location.href via evaluate; 5 attempts, 15s SPA settle
per attempt (was 3x4s, flaked 1/3 on url_mismatch)
- cmd_sidechat_use name path: retry 3x if not landing on /thread/
Test: 2/2 DM sends succeeded when browser healthy (tests 3-5
hit 646 browser death mid-test, infra issue not nav issue).
Session: sidechat/chromebox-ops
This commit is contained in:
+50
-6
@@ -69,7 +69,18 @@ def ev(ws, expr, await_p=False):
|
||||
"id": 1, "method": "Runtime.evaluate",
|
||||
"params": {"expression": expr, "returnByValue": True, "awaitPromise": await_p}
|
||||
}))
|
||||
# Drain CDP events until we get our command response (id 1).
|
||||
# The browser can emit events (Runtime.executionContextCreated, etc.)
|
||||
# at any time; taking the first recv() blindly returns None on a
|
||||
# busy page (observed as transient navigation failures in dm.py
|
||||
# sidechat sends, 2026-10-04 — same class as the NO_SWITCHER fix
|
||||
# in box-chat-cdp.py commit 8d4bfa7).
|
||||
for _ in range(50):
|
||||
resp = json.loads(ws.recv())
|
||||
if resp.get("id") == 1:
|
||||
break
|
||||
else:
|
||||
return None
|
||||
return resp.get('result', {}).get('result', {}).get('value')
|
||||
|
||||
def check_approvals(ws):
|
||||
@@ -215,6 +226,27 @@ def cmd_wait(ws, timeout=30):
|
||||
time.sleep(5)
|
||||
cmd_messages(ws, 2)
|
||||
|
||||
def cdp_navigate(ws, url, timeout_s=30):
|
||||
"""Navigate via CDP Page.navigate (proper navigation, waits for commit).
|
||||
Returns True if the page URL matches the target after navigation."""
|
||||
import time as _time
|
||||
ws.send(json.dumps({"id": 2, "method": "Page.navigate",
|
||||
"params": {"url": url}}))
|
||||
# Drain until we get the Page.navigate response (id 2).
|
||||
for _ in range(50):
|
||||
resp = json.loads(ws.recv())
|
||||
if resp.get("id") == 2:
|
||||
break
|
||||
else:
|
||||
return False
|
||||
# Wait for the URL to settle (SPA client-side routing).
|
||||
for _ in range(timeout_s):
|
||||
cur = ev1(ws, "window.location.href", True)
|
||||
if cur and url.rstrip("/").lower() in cur.lower():
|
||||
return True
|
||||
_time.sleep(1)
|
||||
return False
|
||||
|
||||
def cmd_sidechat_use(ws, chat_id):
|
||||
"""Open a sidechat by name (sidebar text search) or by thread UUID
|
||||
(direct navigation). The sidebar shows titles, not UUIDs, so the
|
||||
@@ -224,15 +256,19 @@ def cmd_sidechat_use(ws, chat_id):
|
||||
if re.fullmatch(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", cid.lower()):
|
||||
url = "https://muse.ai/thread/" + cid.lower()
|
||||
want_uuid = cid.lower()
|
||||
# Use ev1 (skips CDP chatter) and confirm the URL actually changed.
|
||||
# A stale read here used to report the previous thread's URL (2026-10-04).
|
||||
# Use CDP Page.navigate (proper navigation lifecycle) and confirm
|
||||
# the URL actually changed. Hardened 2026-10-04: the React SPA
|
||||
# sometimes redirects back to / when the thread page hasn't finished
|
||||
# loading; retry instead of failing immediately.
|
||||
# (dm.py sidechat 1/3 flake; was window.location.href via evaluate.)
|
||||
cur = None
|
||||
for _try in range(3):
|
||||
ev1(ws, "window.location.href=" + json.dumps(url), True)
|
||||
time.sleep(4)
|
||||
for _try in range(5):
|
||||
ok = cdp_navigate(ws, url, timeout_s=15)
|
||||
cur = ev1(ws, "window.location.href", True)
|
||||
if cur and want_uuid in cur:
|
||||
if ok and cur and want_uuid in cur:
|
||||
break
|
||||
# SPA dropped the nav or hasn't routed yet; wait and retry.
|
||||
time.sleep(3)
|
||||
print(f"Navigated to: {cur}")
|
||||
return
|
||||
b64 = base64.b64encode(chat_id.encode()).decode()
|
||||
@@ -255,7 +291,15 @@ def cmd_sidechat_use(ws, chat_id):
|
||||
"return window.location.href;"
|
||||
"})()"
|
||||
)
|
||||
# Hardened 2026-10-04: the click sometimes doesn't navigate (React
|
||||
# mid-render, or the SPA drops it). Retry the whole nav if we didn't
|
||||
# land on a /thread/ URL.
|
||||
result = None
|
||||
for _try in range(3):
|
||||
result = ev(ws, js, True)
|
||||
if result and "/thread/" in result:
|
||||
break
|
||||
time.sleep(3)
|
||||
print(f"Navigated to: {result}")
|
||||
|
||||
def cmd_sidechat_main(ws):
|
||||
|
||||
Reference in New Issue
Block a user