chore(fleet): sync operator memory, hatch menu dialogs, and watchdog alerts
This commit is contained in:
@@ -6,6 +6,7 @@ idempotent (re-clicking the active tab is a harmless no-op), so goto
|
||||
always clicks and reports the click result instead of guessing which
|
||||
tab is active.
|
||||
"""
|
||||
import json
|
||||
import time
|
||||
|
||||
from approvals import cdp_evaluate
|
||||
@@ -16,6 +17,10 @@ TAB_NAMES = ["General", "Connectors", "Wallet", "Secure store",
|
||||
"Data controls", "Help & support", "Legal info"]
|
||||
TABS = TAB_NAMES # legacy alias
|
||||
|
||||
# Tab rail buttons carry bare tab names and live outside any nav
|
||||
# landmark, so row matchers exclude them by exact text (live 2026-10-06).
|
||||
_JS_TABS = json.dumps(TAB_NAMES)
|
||||
|
||||
DOCK_MORE_TESTID = "hatch-dock-more"
|
||||
|
||||
JS_DOCK_RECT = ("(() => { const b = document.querySelector("
|
||||
@@ -41,21 +46,26 @@ JS_GOTO_TAB_TMPL = ("(() => { const b = Array.from(document."
|
||||
JS_CLICK_ROW_TMPL = ("((name) => {"
|
||||
" const d = document.querySelector('[role=\"dialog\"]');"
|
||||
" if (!d) return 'NO_DIALOG';"
|
||||
" const TABS = " + _JS_TABS + ";"
|
||||
" const inNav = (el) => !!el.closest("
|
||||
"'nav, [role=\"tablist\"], [role=\"navigation\"]');"
|
||||
" const els = Array.from(d.querySelectorAll("
|
||||
"'button, [role=\"button\"], a')).filter(e => !inNav(e));"
|
||||
" const t = els.find(e => (e.innerText || '').trim()"
|
||||
".toLowerCase().startsWith(name.toLowerCase()));"
|
||||
" const t = els.find(e => {"
|
||||
" const txt = (e.innerText || '').trim();"
|
||||
" return !TABS.includes(txt) && txt.toLowerCase()"
|
||||
".startsWith(name.toLowerCase()); });"
|
||||
" if (!t) return 'NO_ROW'; t.click(); return 'CLICKED'; })('%s')")
|
||||
|
||||
JS_DESCRIBE_ROWS = ("(() => {"
|
||||
" const d = document.querySelector('[role=\"dialog\"]');"
|
||||
" if (!d) return null;"
|
||||
" const TABS = " + _JS_TABS + ";"
|
||||
" const inNav = (el) => !!el.closest("
|
||||
"'nav, [role=\"tablist\"], [role=\"navigation\"]');"
|
||||
" return Array.from(d.querySelectorAll("
|
||||
"'button, [role=\"button\"], a')).filter(e => !inNav(e))"
|
||||
".filter(e => !TABS.includes((e.innerText || '').trim()))"
|
||||
".map(e => { const lines = (e.innerText || '').trim().split('\\n');"
|
||||
" return {name: (lines[0] || '').slice(0, 80),"
|
||||
" detail: lines.slice(1).join(' / ').slice(0, 120)}; }); })()")
|
||||
@@ -85,7 +95,7 @@ def dialog_text(ws, timeout=8.0, limit=4000):
|
||||
text = cdp_evaluate(ws, JS_DIALOG_TEXT, timeout=timeout)
|
||||
except Exception:
|
||||
return None
|
||||
if not text:
|
||||
if not isinstance(text, str) or not text:
|
||||
return None
|
||||
return text[:limit]
|
||||
|
||||
@@ -135,7 +145,8 @@ def describe_rows(ws, tab=None, timeout=8.0):
|
||||
"""Inventory rows (name/detail) on a tab. [] when unreadable."""
|
||||
if tab is not None and not goto_tab(ws, tab, timeout=timeout):
|
||||
return []
|
||||
return _eval(ws, JS_DESCRIBE_ROWS, timeout=timeout) or []
|
||||
rows = _eval(ws, JS_DESCRIBE_ROWS, timeout=timeout)
|
||||
return rows if isinstance(rows, list) else []
|
||||
|
||||
|
||||
def go_back(ws):
|
||||
|
||||
Reference in New Issue
Block a user