← Commit history

Upload 1 file(s)

John Lauer ·7218d97413 ·2mo ago ·parent e799fac
1 file changed +74
server.py+74
@@ -11,6 +11,7 @@ Usage:  import json import os+import threading import time as _t2 import sys import traceback@@ -3789,6 +3790,75 @@ def _fusion_window_hwnd(): # tree so page buttons/fields ARE reachable by accessible name. So: ALWAYS try background UIA # first and only fall back to a foreground click for things with no UIA node (Fusion's Sign In # button is a webview with none - that is the one documented exception).+# ── SIGN-IN WINDOW JANITOR (John, 2026-07-22) ────────────────────────────────────────────────+# "if you open a browser window for sign in, you must run a loop on a schedule to know when to+# close it so you never leave the user's desktop messy."+# We open browser windows to complete OAuth. Those windows are OURS and must not be left as+# litter once they have served their purpose. Every window we open is tracked here, and a+# background loop closes it as soon as the sign-in it belongs to is DONE (or it is clearly dead+# or too old). Never touches a window the user opened.+_signin_windows = []          # [{sessionId, profile, kind, openedAt}]+_signin_windows_lock = threading.Lock()+_JANITOR_MAX_AGE = 900        # 15 min: an OAuth window older than this is dead weight either way+++def _track_signin_window(session_id, profile, kind="fusion"):+    if not session_id:+        return+    with _signin_windows_lock:+        _signin_windows.append({"sessionId": session_id, "profile": profile,+                                "kind": kind, "openedAt": _t2.time()})+++def _close_tracked_window(w) -> bool:+    """Close one window WE opened. nbrowser_close_window only closes our own sessions."""+    r = _ad_call("nbrowser_close_window",+                 {"sessionId": w["sessionId"], "profile": w.get("profile")}, timeout=30)+    return isinstance(r, dict) and not r.get("_adCallError")+++def _signin_goal_met(kind) -> bool:+    """Has the sign-in this window exists for actually completed?"""+    try:+        if kind == "aps":+            return bool(_aps_quick_state().get("signedIn"))+        return bool(_unwrap(_handle_fusion_readiness({}, {})).get("ready"))+    except Exception:+        return False+++def _signin_janitor_loop():+    """Poll until each tracked sign-in window can be closed, then close it."""+    while True:+        try:+            _t2.sleep(20)+            with _signin_windows_lock:+                pending = list(_signin_windows)+            if not pending:+                continue+            done_goals = {}+            for w in pending:+                kind = w.get("kind", "fusion")+                if kind not in done_goals:+                    done_goals[kind] = _signin_goal_met(kind)+                aged = (_t2.time() - w["openedAt"]) > _JANITOR_MAX_AGE+                if done_goals[kind] or aged:+                    if _close_tracked_window(w):+                        print("[Fusion Bridge] janitor: closed %s sign-in window %s (%s)"+                              % (kind, w["sessionId"], "signed in" if done_goals[kind] else "expired"))+                    with _signin_windows_lock:+                        if w in _signin_windows:+                            _signin_windows.remove(w)+        except Exception:+            continue+++def _start_signin_janitor():+    t = threading.Thread(target=_signin_janitor_loop, daemon=True, name="signin-janitor")+    t.start()+    return t++ def _announce_foreground(reason: str) -> bool:     """ALWAYS tell the user WHY, right before we steal their foreground. @@ -4295,6 +4365,9 @@ def _orchestrate_signin(args: dict) -> dict:                 r = r2     steps.append((("re-opened it in %s%s" % (tdesc, " (extension-free)" if open_via == "extension_free" else ""))                   if opened else ("could not open %s (raw: %s)" % (tdesc, str(r)[:160]))))+    if opened:+        # hand it to the janitor so it is never left as litter on the user's desktop+        _track_signin_window(sess, target, kind="fusion")      # ── FINISH IT: drive the SSO path ourselves (no password, no 2FA, no foreground) ──────────     # John, 2026-07-22: the goal is a SIGNED-IN Fusion, not a handed-off checklist. When we own@@ -5118,6 +5191,7 @@ def main():         pass      _prune_bridge_logs()+    _start_signin_janitor()      port = DEFAULT_PORT     if "--port" in sys.argv: