app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
← Commit history
v1.6.66: find_licensing_dialog never matches the MAIN app window (was false-firing on a non-maximized main window inside the shape gate, and once ad_client came online the seat resolver churned on it and hung readiness). Exclude the largest family window; bound the seat resolver (max_clicks 3, 4s AD-call timeout) so a readiness tick can't hang.
5 files changed
+27−10
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.6.65+1.6.66
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.6.65",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.65/adom-bridge-fusion-v1.6.65.zip",- "sha256": "c8b457a911913e54afa2679cddc4e321129fa3aec02bbaffb6aab1bc5876f445",- "size": 359576,+ "version": "1.6.66",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.66/adom-bridge-fusion-v1.6.66.zip",+ "sha256": "64833c2f7d513837e4a70b86f2de3939c57c7dee06b5ad46f2a326c03869e479",+ "size": 360022, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.6.65",+ "version": "1.6.66", "description": "Drive Autodesk Fusion 360 from the cloud: launch Fusion, electronics board layout, design rules, exports (STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers/BOM/CPL), fast APS server-indexed cloud search plus browse/recent/file-info/versions, cloud file download/upload and folder creation, and in-app parametric modeling (fusion_run_modeling_script). Never-charge: APS calls are capped to the free tier.", "homepage": "https://wiki.adom.inc/adom/adom-desktop-fusion-bridge", "author": "Adom Inc.",
fusion_detect.py+15−1@@ -564,13 +564,27 @@ def find_licensing_dialog(): narrow panels), height >= 180 (excludes toolbar strips), and smaller than the maximized main app.""" wins = family_windows()+ if not wins:+ return None+ # NEVER match the MAIN app window. A seat/sign-in modal is a smaller popup; the main Fusion+ # window is the LARGEST family window. The old no-owner fallback matched the main window whenever+ # it happened to fall inside the shape gate (e.g. a non-maximized 1300x820 window fits+ # 480<=w<=1500, 180<=h<=900) - so find_licensing_dialog false-fired on the drivable main app,+ # and once ad_client came online the seat resolver churned 8x on it and HUNG readiness (John,+ # live 2026-07-14). Excluding the largest window is size-independent and safe: a dialog is never+ # bigger than the app that owns it.+ main_hwnd = max(wins, key=lambda w: w["width"] * w["height"])["hwnd"] # Primary: an OWNED popup that is DIALOG-SHAPED (wide modal, not a narrow side panel). for w in wins:+ if w["hwnd"] == main_hwnd:+ continue ww, hh = w["width"], w["height"] if w.get("owner") and 480 <= ww <= 1500 and 180 <= hh <= 900: return w- # Fallback (owner==0 for all - owner detection failed): same shape gate, no owner req.+ # Fallback (owner==0 for all - owner detection failed): same shape gate, still NOT the main window. for w in wins:+ if w["hwnd"] == main_hwnd:+ continue ww, hh = w["width"], w["height"] if 480 <= ww <= 1500 and 180 <= hh <= 900: return w
server.py+6−3@@ -131,7 +131,7 @@ def _resolve_seat_via_uia(dlg: dict) -> bool: hwnd = dlg.get("hwnd") if not hwnd or not ad_client.available(): return False- res = ad_client.call("desktop_ui_click", {"hwnd": hwnd, "name": "Continue"})+ res = ad_client.call("desktop_ui_click", {"hwnd": hwnd, "name": "Continue"}, timeout=4) return bool(res and (res.get("success") or res.get("status") == "ok")) except Exception: return False@@ -147,7 +147,7 @@ def _owned_popup_count(main_hwnd) -> int: try: if not main_hwnd or not ad_client.available(): return -1- res = ad_client.call("desktop_screenshot_window", {"hwnd": int(main_hwnd)})+ res = ad_client.call("desktop_screenshot_window", {"hwnd": int(main_hwnd)}, timeout=4) if isinstance(res, dict): c = res.get("ownedPopupCount") if c is None and isinstance(res.get("data"), dict):@@ -159,7 +159,10 @@ def _owned_popup_count(main_hwnd) -> int: return -1 -def _resolve_seat_dialog(max_clicks: int = 8) -> dict:+def _resolve_seat_dialog(max_clicks: int = 3) -> dict:+ # max_clicks kept LOW (was 8): readiness calls this every tick and it SELF-HEALS across ticks, so+ # a single call must stay responsive - 8 clicks x (UIA click + screenshot-verify, each up to its+ # timeout) could blow readiness's whole budget and make the bridge look hung (John 2026-07-14). """Detect + resolve the seat/licensing modal deterministically, IN THE BACKGROUND, and VERIFY it truly cleared by SCREENSHOTTING the parent window's ownedPopupCount - not by re-running a size heuristic (that false-'resolved' bit John hard, 2026-07-06: