← Commit history

Upload 1 file(s)

John Lauer ·9587605eab ·2mo ago ·parent b2b7896
1 file changed +182−1
server.py+182−1
@@ -11,6 +11,7 @@ Usage:  import json import os+import time as _t2 import sys import traceback import urllib.request@@ -1193,6 +1194,17 @@ def _handle_install_fusion(fusion_info: dict, args: dict) -> dict:     }  +def _aps_quick_state() -> dict:+    """Cheap APS state for readiness: {configured, signedIn}. Never raises, never blocks."""+    try:+        import aps as _aps+        st = _aps.handle_status({}) or {}+        d = st.get("data", st)+        return {"configured": bool(d.get("configured")), "signedIn": bool(d.get("signedIn"))}+    except Exception:+        return {"configured": None, "signedIn": None}++ def _handle_fusion_readiness(fusion_info: dict, args: dict) -> dict:     """FAST readiness check for the AI: is the Fusion host app present + running + the bridge ready?     Does NOT launch Fusion (fast-fails when it's not running). AD DETECTS Fusion (never installs it)@@ -1354,7 +1366,25 @@ def _handle_fusion_readiness(fusion_info: dict, args: dict) -> dict:     elif ready and stale_info.get("addinStale"):         hint = stale_info["_staleHint"]     elif ready:-        hint = "READY - drive Fusion (fusion_open_lbr / build_library_3d / etc.)."+        # APS is checked on EVERY launch now (John, 2026-07-22: "why aren't those just driven from+        # 1 login?"). Fusion sign-in and APS are both Autodesk logins; the user should authenticate+        # ONCE. They cannot share a token (Autodesk DPAPI-encrypts Fusion's store), but they CAN+        # share the warm browser SSO session - so APS consent is silent right after a Fusion login.+        _aps = _aps_quick_state()+        if _aps.get("signedIn"):+            hint = ("READY - drive Fusion (fusion_open_lbr / build_library_3d / etc.). APS cloud "+                    "search is signed in too, so use fusion_aps_search for ~2s cloud lookups.")+        elif _aps.get("configured"):+            hint = ("READY to drive Fusion, but APS CLOUD SEARCH IS NOT SIGNED IN - cloud file "+                    "search will be slow/blocked. DO IT NOW, do not make the user log in a second "+                    "time: call fusion_aps_signin RIGHT AFTER a Fusion sign-in, while the browser "+                    "profile still has a warm Autodesk SSO session - it consents silently (no "+                    "password, no 2FA). Then fusion_aps_search works.")+        else:+            hint = ("READY to drive Fusion. APS cloud search is NOT SET UP (no client ID) - cloud "+                    "search is the slow path without it. Offer to set it up: fusion_aps_set_client_id "+                    "then fusion_aps_signin (silent while the browser SSO session is warm). APS is "+                    "how cloud search gets to ~2 seconds.")     elif installed and running and licensing:         hint = ("Fusion is blocked by a SEAT/LICENSING dialog ('Active Sessions Exceeded' / 'Suspend "                 "Remote Session') - detected DETERMINISTICALLY as an OWNED POPUP of the main Fusion "@@ -3949,6 +3979,61 @@ def _capture_fresh_authorize(wait_sec: int = 30, max_age: int = 120):         _t.sleep(2)  +_SSO_BUTTONS = {"google": "Continue with Google", "apple": "Continue with Apple",+                "microsoft": "Continue with Microsoft", "facebook": "Continue with Facebook"}+++def _drive_sso_signin(profile: str, tab_id, email: str, provider: str = "google") -> dict:+    """Finish the Autodesk sign-in over CDP using SSO - NO password, NO 2FA, NO foreground.++    PROVEN LIVE (John, 2026-07-22) after password/2FA attempts kept stalling. If the browser+    PROFILE is already signed into the identity provider (a work Google account is the common+    case), "Continue with Google" -> pick the account -> "Open Product" completes the whole login+    silently and hands the token back to Fusion via the autodesk:// callback.++    TWO THINGS MATTER:+      1. It must be a FRESH flow. Resuming a flowId that already advanced past the provider+         choice lands you on the PASSWORD screen with no way back - that dead end cost us an hour.+      2. Click the provider BEFORE typing any email; entering an email commits to the password path.+    """+    steps = []++    def ev(expr):+        r = _unwrap(_ad_call("nbrowser_eval", {"profile": profile, "tabId": tab_id,+                                               "expression": expr}, timeout=45))+        return r.get("result") or r.get("value")++    def click(text=None, selector=None):+        a = {"profile": profile, "tabId": tab_id}+        if text: a["text"] = text+        if selector: a["selector"] = selector+        r = _unwrap(_ad_call("nbrowser_click", a, timeout=45))+        return bool(r.get("ok") or r.get("success") or r.get("changed"))++    btn = _SSO_BUTTONS.get((provider or "google").lower(), _SSO_BUTTONS["google"])+    if not click(text=btn):+        return {"done": False, "steps": steps,+                "why": "%r not on the page - this is probably NOT a fresh flow (a resumed flowId "+                       "goes straight to the password screen)." % btn}+    steps.append("clicked %r" % btn)+    _t2.sleep(7)++    # provider account chooser - pick by the exact email+    if email and click(text=email):+        steps.append("picked the %s account" % email)+        _t2.sleep(8)++    state = str(ev("JSON.stringify({u:location.href.slice(0,120),b:document.body.innerText.slice(0,200)})") or "")+    if "You're signed in" in state or "signed in" in state.lower():+        steps.append("provider returned 'You're signed in'")+        # hand the token back to Fusion (autodesk:// protocol callback)+        if click(text="Open Product"):+            steps.append("clicked 'Open Product' to hand the token to Fusion")+        return {"done": True, "steps": steps, "state": state[:200]}+    return {"done": False, "steps": steps, "state": state[:200],+            "why": "did not reach the signed-in page; a provider password/2FA may be required"}++ def _orchestrate_signin_2fa(args: dict) -> dict:     """Submit Autodesk's emailed 6-digit verification code, IN THE BACKGROUND. @@ -4150,6 +4235,41 @@ def _orchestrate_signin(args: dict) -> dict:     tdesc = next((_describe_profile(c) for c in choices if c.get("profile") == target), target)     import time as _tt     sess = "fusion-signin-%d" % int(_tt.time())   # unique per attempt (a reused id can be refused)+    # ── Is Fusion ALREADY using the right profile? (John, 2026-07-22: "they both opened in my+    # work profile, so you're being lazy") ──────────────────────────────────────────────────+    # Chrome profile DIRECTORIES are named Default / Profile 1 / Profile 2, and the user's work+    # account is very often just "Default". We capture the authorize URL from a profile DIR, but+    # the target is an EMAIL (chrome:[email protected]). Without mapping email -> dir we cannot tell+    # they are the SAME profile, so we "fixed" a non-problem by re-opening the identical URL in+    # the identical profile - a redundant second sign-in window on the user's screen.+    target_dir = None+    for c in choices:+        if c.get("profile") == target:+            target_dir = c.get("profileDir") or c.get("dir")+            break+    if target_dir and auth.get("sourceProfileDir") and \+       str(target_dir).strip().lower() == str(auth["sourceProfileDir"]).strip().lower():+        steps.append("Fusion already opened the sign-in in %s (profile dir %r) - no re-open needed"+                     % (target, target_dir))+        wall0 = _detect_signin_wall()+        notified0 = bool(_notify_signin_wall(wall0["wall"]).get("delivered")) if wall0.get("wall") else False+        return {"success": True, "stage": "already_right_profile", "done": False, "steps": steps,+                "signinProfile": target, "signinProfileDir": target_dir,+                "signinWhere": tdesc if False else target,+                "signinWall": wall0.get("wall"), "signinWallHwnd": wall0.get("hwnd"),+                "notifyDelivered": notified0,+                "signinWallHint": _wall_hint(wall0["wall"], notified0) if wall0.get("wall") else None,+                "signinAuthUrl": auth["url"],+                "narrate": ("Fusion opened its sign-in in the RIGHT profile already (%s), so I did "+                            "not open a second window." % target),+                "_hint": ("NO re-open was needed - Fusion's OS-default browser IS the user's Autodesk "+                          "profile here (dir %r). Do NOT open another window; that just litters their "+                          "screen with a duplicate sign-in. The existing window is the one to finish. "+                          "%s" % (target_dir,+                                  _wall_hint(wall0["wall"], notified0) if wall0.get("wall")+                                  else "Poll fusion_readiness until ready:true.")),+                "statusVerb": "fusion_readiness"}+     # snapshot the Autodesk windows that already exist, so a STALE expired tab from an earlier     # attempt is never mistaken for this attempt's wall     pre_hwnds = _autodesk_window_hwnds()@@ -4176,6 +4296,67 @@ def _orchestrate_signin(args: dict) -> dict:     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])))) +    # ── 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+    # the window (nbrowser_open_window => agent-opened => CDP-drivable) and the profile is already+    # signed into the identity provider, this completes the whole login silently.+    sso = None+    if opened and open_via == "extension" and args.get("driveSso", True):+        tabs = _unwrap(_ad_call("nbrowser_list_tabs", {"profile": target}, timeout=45)).get("tabs") or []+        my_tab = None+        for t in tabs:+            if "autodesk" in str(t.get("url", "")).lower() and t.get("owner") == "agent-opened":+                my_tab = t.get("tabId") or t.get("id")+        if my_tab:+            email = None+            for c in choices:+                if c.get("profile") == target:+                    email = c.get("email")+            sso = _drive_sso_signin(target, my_tab, email, args.get("ssoProvider", "google"))+            steps.extend(sso.get("steps") or [])+            if sso.get("done"):+                for _ in range(6):+                    _t2.sleep(5)+                    if _unwrap(_handle_fusion_readiness({}, {})).get("ready"):+                        steps.append("Fusion reports ready:true - SIGNED IN")+                        # ── ONE LOGIN, BOTH SYSTEMS (John, 2026-07-22) ───────────────────────+                        # "why should the user have to login twice to autodesk to sign in to+                        # fusion and setup aps? why aren't those just driven from 1 login?"+                        # Right now the browser profile has a WARM Autodesk SSO session (we just+                        # used it). APS consent rides that session silently, so do it HERE while+                        # it is warm instead of making the user authenticate a second time.+                        aps_state = _aps_quick_state()+                        if aps_state.get("configured") and not aps_state.get("signedIn"):+                            try:+                                import aps as _apsmod+                                _apsmod.start_signin({"profile": target})+                                steps.append("APS not signed in - started its consent on the SAME "+                                             "warm SSO session (no second login for the user)")+                                for _ in range(5):+                                    _t2.sleep(4)+                                    if _aps_quick_state().get("signedIn"):+                                        steps.append("APS signed in too - one login covered both")+                                        break+                            except Exception as e:+                                steps.append("APS auto-setup skipped (%s)" % str(e)[:80])+                        aps_state = _aps_quick_state()+                        return {"success": True, "stage": "signed_in", "done": True, "steps": steps,+                                "signinProfile": target, "signinWhere": tdesc,+                                "signinOpenedVia": open_via,+                                "aps": aps_state,+                                "narrate": ("Fusion is signed in as %s. No password or 2FA was needed - "+                                            "I used the browser profile's existing SSO session.%s"+                                            % (email or target,+                                               " APS cloud search is set up too, from the same login."+                                               if aps_state.get("signedIn") else "")),+                                "_hint": ("DONE - Fusion is signed in (ready:true). Nothing else to do; "+                                          "do not tell the user to sign in. %s Drive Fusion normally now."+                                          % ("APS is signed in as well, so fusion_aps_search is live."+                                             if aps_state.get("signedIn") else+                                             "APS is NOT signed in - run fusion_aps_signin NOW while the "+                                             "browser SSO session is still warm so it consents silently.")),+                                "statusVerb": "fusion_readiness"}+     # Did the reopened sign-in immediately hit a HUMAN WALL (2FA, credentials, protocol dialog)?     # If so, toast the user AND tell the AI how to clear it for them.     wall = _detect_signin_wall(ignore_hwnds=pre_hwnds) if opened else {}