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
Update 102 file(s)
9 files changed
+166−18
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.309+1.9.310
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.309",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.309/adom-bridge-fusion-v1.9.309.zip",- "sha256": "ff9559d26ca94e102e970038e0f036c6fdcf0379bc3e08c328868abdcc10790b",- "size": 589869,+ "version": "1.9.310",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.310/adom-bridge-fusion-v1.9.310.zip",+ "sha256": "f0db7d034a3ec2baded372676a18384682d585e5d314fb59c050998ebe28e662",+ "size": 592356, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.309",+ "version": "1.9.310", "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/fusion-bridge", "author": "Adom Inc.",
dashboard/Cargo.lock+1−1@@ -4,7 +4,7 @@ version = 4 [[package]] name = "fusion-dashboard"-version = "1.9.309"+version = "1.9.310" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.309"+version = "1.9.310" edition = "2021" [dependencies]
install.sh+1−1@@ -21,7 +21,7 @@ echo "Installed fusion-bridge skills: $(ls "$PKG_DIR/skills" 2>/dev/null | tr '\ # binary (the three-artifact pattern: bridge zip -> ab, THIS pkg -> container skills + # this download, page media -> wiki). Per the wiki's PKG_VS_RELEASE policy, binaries are # platform-tagged RELEASE assets, never tarball content - the installer fetches them.-DASH_VERSION="1.9.309"+DASH_VERSION="1.9.310" DASH_URL="https://wiki.adom.inc/download/adom/fusion-bridge/${DASH_VERSION}/fusion-dashboard" DEST="" if [ -w /usr/local/bin ]; then DEST=/usr/local/bin
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "fusion-bridge",- "version": "1.9.309",+ "version": "1.9.310", "type": "app", "description": "This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Bridge loads the bridge runtime itself from the release zip.", "dependencies": {
page.json+11−8@@ -1,6 +1,6 @@ { "slug": "fusion-bridge",- "version": "1.9.309",+ "version": "1.9.310", "type": "app", "description": "This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Bridge loads the bridge runtime itself from the release zip.", "dependencies": {@@ -11,6 +11,10 @@ "uninstall": "./uninstall.sh" }, "tags": [+ "ab",+ "bridge",+ "fusion",+ "desktop-app", "fusion360", "autodesk", "cad",@@ -75,7 +79,10 @@ "build a basic parts library", "add a 3d model to a component", "make a fusion 360 library with 3d",- "set up the fusion bridge"+ "set up the fusion bridge",+ "fusion dashboard",+ "fusion status page",+ "open the fusion launch page" ], "discovery_pitch": "Use the Adom Bridge Fusion 360 bridge when the user wants to DRIVE Autodesk Fusion 360 from the cloud: open/search their team's Fusion cloud designs, lay out a PCB or do electronics, export STEP/IGES/STL/Gerbers/BOM, or build a component library with real rendering 3D. It runs Fusion on the user's own machine via Adom Bridge. NOT for web/browser/login/form-filling (that's the native-browser extension) and NOT for KiCad (that's the kicad bridge).", "brief": "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.",@@ -104,9 +111,5 @@ "label": "Part volume", "prompt": "How much material does each printed part in this assembly use?" }- ],- "author": {- "name": "John Lauer",- "email": "[email protected]"- }-}+ ]+}
server.py+145@@ -2825,6 +2825,7 @@ COMMAND_HANDLERS = { or {"success": True, "dialogsDetected": 0, "dialogs": []}, float(args.get("alertsSinceSec", 300))), # Fusion's own error/warning toasts (the CEF overlays no window enumeration can see).+ "managed_library_create": lambda fi, args: _handle_managed_library_create(fi, args), "alerts": lambda fi, args: { "success": True, "alerts": _fusion_alerts(float(args.get("sinceSec", 300))),@@ -15619,6 +15620,150 @@ def _handle_live_feed(fusion_info: dict, args: dict) -> dict: "'[main #n] ...'). tools/live_feed_drain.py does exactly that.")} +# ── library.io managed libraries (John, 2026-09-01) ──────────────────────────+# Fusion's LIBRARY.IO commands live in the EAGLE-hosted library editor: they are NOT Fusion+# CommandDefinitions (all 3100 were enumerated; none match) and the ribbon is invisible to UIA,+# so the only drive is coordinates. Everything here is therefore SELF-VERIFYING: each step+# confirms the real dialog window it expects (those ARE top-level and UIA-reachable) and fails+# with the step that broke rather than clicking on blindly. See dev-skills/fusion-cloud-libraries.+_RIBBON_DIP = { # measured on ConfRoomROG, Fusion 2026; DIP so it ports across DPI+ "manage_tab": (296, 64),+ "libraryio_group": (455, 126),+ "push_item": (466, 148),+}+++def _dip_to_screen(dip_xy, info):+ """DIP (device-independent px) -> physical screen px for the Fusion window."""+ scale = float(info.get("dpiScale") or 1.0) or 1.0+ rect = info.get("rect") or {}+ return (int(rect.get("left", 0) + dip_xy[0] * scale),+ int(rect.get("top", 0) + dip_xy[1] * scale))+++def _fusion_main_window():+ """{hwnd, rect, dpiScale} for Fusion's main window, or None."""+ try:+ r = _ad_call("desktop_list_windows", {}, timeout=20) or {}+ out = r.get("output")+ data = json.loads(out) if isinstance(out, str) else (r.get("data") or {})+ for w in ((data.get("data") or data).get("windows") or []):+ t = str(w.get("title") or "")+ if "Autodesk Fusion" in t and w.get("kind") == "normal":+ rect = w.get("rect") or {}+ dpi = 1.0+ try:+ sc = _ad_call("desktop_screenshot_screen", {}, timeout=30) or {}+ dpi = float(((sc.get("coordMap") or {}).get("dpiScale")) or 1.0)+ except Exception:+ pass+ return {"hwnd": w.get("hwnd"), "rect": rect, "dpiScale": dpi, "title": t}+ except Exception:+ pass+ return None+++def _wait_for_dialog(title_substr: str, timeout_s: float = 20.0):+ """Wait for a REAL top-level Fusion dialog whose title contains title_substr."""+ import time as _t+ deadline = _t.time() + timeout_s+ while _t.time() < deadline:+ try:+ d = _capture_dialog_array(settle=0.2) or {}+ for x in d.get("dialogs") or []:+ if title_substr.lower() in str(x.get("title") or "").lower():+ return x+ except Exception:+ pass+ _t.sleep(1.0)+ return None+++def _handle_managed_library_create(fusion_info: dict, args: dict) -> dict:+ """Publish the library that is IN FRONT as a library.io managed library.++ Preconditions Fusion enforces (both refused here rather than prompting the user):+ 1. a LIBRARY document is in front (not a design, not a board)+ 2. that document is saved in a Fusion HUB - a library opened from a local .lbr is not,+ and Fusion answers "Cannot create a managed library from a document not saved in a+ Fusion Hub" as an in-canvas CEF error no window enumeration can see (fusion_alerts).+ """+ import time as _t+ st = _proxy_to_addin("get_app_state", {}, timeout=10) or {}+ data = st.get("data") or {}+ doc = str(data.get("activeDocument") or "")+ ws = str(data.get("activeWorkspace") or "")+ if "librar" not in ws.lower():+ return {"success": False, "errorCode": "not_a_library",+ "error": "The document in front is %r in the %s workspace, not a library."+ % (doc, ws or "unknown"),+ "_hint": "fusion_open_lbr {lbrPath} first - it lands the library editor in front "+ "and verifies it, which activate_document alone does NOT."}+ hub = _proxy_to_addin("run_modeling_script", {"script": (+ "import adsk.core\n"+ "app=adsk.core.Application.get(); d=app.activeDocument\n"+ "df=None\n"+ "try: df=d.dataFile\n"+ "except Exception: df=None\n"+ "result={'name': d.name, 'inHub': df is not None,"+ " 'id': (df.id if df else None)}\n")}, timeout=30) or {}+ hub_res = ((hub.get("data") or {}).get("result")) or {}+ if not hub_res.get("inHub"):+ return {"success": False, "errorCode": "not_in_hub",+ "error": "Library %r is not saved in a Fusion Hub, so Fusion cannot make it a "+ "managed library." % (hub_res.get("name") or doc),+ "remedyVerb": "fusion_save_to_cloud",+ "_hint": "fusion_save_to_cloud {name, projectName, folderPath} on this library "+ "first; a real library save returns a fileId ending in .flbr (a .f3d "+ "means the WRONG document was in front)."}+ win = _fusion_main_window()+ if not win:+ return {"success": False, "errorCode": "no_fusion_window",+ "error": "Could not locate Fusion's main window to drive the ribbon."}+ steps = []+ def _click_dip(key, why):+ x, y = _dip_to_screen(_RIBBON_DIP[key], win)+ _ad_call("desktop_click", {"x": x, "y": y, "reason": why}, timeout=25)+ steps.append({"step": key, "at": [x, y]})+ _click_dip("manage_tab", "open the library editor's MANAGE tab")+ _t.sleep(1.5)+ _click_dip("libraryio_group", "open the LIBRARY.IO command group")+ _t.sleep(1.5)+ # The popup is its own window: a foregrounding click would dismiss it, so use the raw+ # mouse verb (measured 2026-09-01).+ px, py = _dip_to_screen(_RIBBON_DIP["push_item"], win)+ _ad_call("desktop_mouse_click", {"x": px, "y": py,+ "reason": "Push changes to library.io"}, timeout=25)+ steps.append({"step": "push_item", "at": [px, py]})+ dlg = _wait_for_dialog("Create Managed Library", 25)+ if not dlg:+ return {"success": False, "errorCode": "create_dialog_missing", "steps": steps,+ "alerts": _fusion_alerts(120),+ "error": "The Create Managed Library dialog never appeared - the ribbon click "+ "probably missed (the ribbon is invisible to UIA, so this verb drives "+ "measured DIP offsets).",+ "_hint": "Re-run with the Fusion window maximized, or recalibrate _RIBBON_DIP "+ "from a desktop_screenshot_screen coordMap."}+ ok = _ad_call("desktop_ui_click", {"hwnd": dlg.get("hwnd"), "name": "Create",+ "reason": "create the managed library on library.io"},+ timeout=30) or {}+ steps.append({"step": "create", "ok": bool(ok.get("success"))})+ conf = _wait_for_dialog("Create Managed Library", 40) # the confirmation reuses the title+ if conf:+ _ad_call("desktop_ui_click", {"hwnd": conf.get("hwnd"), "name": "OK",+ "reason": "acknowledge the managed-library confirmation"},+ timeout=30)+ steps.append({"step": "ok"})+ alerts = _fusion_alerts(120)+ return {"success": not alerts, "library": hub_res.get("name") or doc, "steps": steps,+ "alerts": alerts,+ "_hint": ("Managed library created; it lives under the account's managed FOLDER on "+ "library.io (not public - a public search will not find it). Share it by "+ "managing that folder on library.io." if not alerts else+ "Fusion raised an alert during the push - read alerts[] and treat this as "+ "failed.")}++ def _handle_restart_for_addin(fusion_info: dict, args: dict) -> dict: """Save open documents, restart Fusion, wait for the add-in, report back.