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 9 file(s)
9 files changed
+143−16
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.113+1.9.114
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.113",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.128/adom-bridge-fusion-v1.9.113.zip",- "sha256": "2fb4e6334c4fa174d69eb987f513e6dc7a7ee6fe8d4d009a55e2cc7adbca0eac",- "size": 449019,+ "version": "1.9.114",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.129/adom-bridge-fusion-v1.9.114.zip",+ "sha256": "21824c866d5dfcef3afa70dcb34b6640f56cda1dee1ca9626b42a5537b7f952e",+ "size": 450971, "verbPrefixes": [ "fusion_" ],
aps.py+57@@ -1422,3 +1422,60 @@ HANDLERS = { "aps_upload": handle_upload, "aps_get": handle_get, }+++# ── 3D thumbnail state: deterministic, free-endpoint, no pixels ─────────────────────────+# John, 2026-08-21: "you must ensure [the 3D thumbnail] is there ... is there a programmatic+# way to test for a thumbnail working correctly so you don't have to do a screenshot+# analysis?" There is: the Library Manager thumbnail is rendered by Autodesk's CLOUD against+# the bound f3d, and Data Management exposes it as a RELATIONSHIP on the file's version -+# present once rendered, absent while Fusion "takes its good old time". Reading that+# relationship is a free /data/v1 call: an HTTP/JSON state check, never image analysis.+++def thumbnail_state(project_id: str, folder_id: str, wip_urn: str = "", file_name: str = "") -> dict:+ """State of the cloud thumbnail for a file in a folder we uploaded to.++ Match by wip_urn (the .lbr package3d urn, minus ?version=) or by displayName.+ Returns {state: ready|pending|not_found|not_signed_in|error, item, thumbnailId}.+ """+ if not project_id or not folder_id:+ return {"state": "error", "why": "projectId and folderId required"}+ want_urn = (wip_urn or "").split("?", 1)[0].strip()+ status, data = _aps_get(+ "/data/v1/projects/%s/folders/%s/contents" % (project_id, folder_id),+ {"include": "versions"})+ if status is None:+ return {"state": "not_signed_in"}+ if status != 200 or not isinstance(data, dict):+ return {"state": "error", "http": status,+ "why": (data or {}).get("error") if isinstance(data, dict) else None}+ items = data.get("data") or []+ included = data.get("included") or []+ # join tip versions to items; match by version urn or display name+ hit_item = None+ hit_version = None+ for v in included:+ vid = str(v.get("id") or "").split("?", 1)[0]+ vname = str((v.get("attributes") or {}).get("displayName") or "")+ if (want_urn and vid == want_urn) or (file_name and vname.lower().startswith(file_name.lower())):+ hit_version = v+ break+ if hit_version is None and file_name:+ for it in items:+ nm = str((it.get("attributes") or {}).get("displayName") or "")+ if nm.lower().startswith(file_name.lower()):+ hit_item = it+ break+ src = hit_version or hit_item+ if src is None:+ return {"state": "not_found", "searched": {"urn": want_urn, "name": file_name},+ "itemCount": len(items)}+ rel = ((src.get("relationships") or {}).get("thumbnails") or {})+ tid = ((rel.get("data") or {}) or {}).get("id")+ if tid:+ return {"state": "ready", "thumbnailId": tid,+ "item": str((src.get("attributes") or {}).get("displayName") or "")}+ return {"state": "pending",+ "item": str((src.get("attributes") or {}).get("displayName") or ""),+ "why": "version exists but Fusion's cloud has not rendered a thumbnail yet"}
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.113",+ "version": "1.9.114", "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.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.128"+version = "1.9.129" 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.128"+DASH_VERSION="1.9.129" 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.128",+ "version": "1.9.129", "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+10−7@@ -1,6 +1,6 @@ { "slug": "fusion-bridge",- "version": "1.9.128",+ "version": "1.9.129", "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",@@ -70,7 +74,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.",@@ -99,9 +106,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+67@@ -9182,6 +9182,21 @@ def _handle_prove_library(fusion_info: dict, args: dict) -> dict: evidence.append({"step": "ganged", "view": "library-manager", "ok": False, "error": str(r.get("error"))[:160]}) + # THE THUMBNAIL IS PART OF THE VERDICT (John: "you must ensure its there"). A bound urn+ # whose cloud thumbnail never rendered shows the user a grey slab in the Library Manager+ # while every local check passes. Cloud state, not pixels; waits up to 3 min because+ # Fusion renders it asynchronously after a bind.+ thumb = None+ if artifacts["package3d"]["present"]:+ try:+ thumb = _handle_check_3d_thumbnail(fusion_info,+ {"libraryPath": lbr, "waitSeconds": 180})+ artifacts["package3d"]["thumbnail"] = (thumb.get("thumbnail") or {}).get("state")+ if not thumb.get("success"):+ missing.append("thumbnail(%s)" % ((thumb.get("thumbnail") or {}).get("state")+ or thumb.get("errorCode") or "unknown"))+ except Exception as _e:+ missing.append("thumbnail(check-failed)") captured = [e for e in evidence if e.get("ok") and e.get("screenshot")] proven = (not missing) and len(captured) == 4 out = {"success": proven, "proven": proven, "libraryPath": lbr, "deviceset": want,@@ -9525,6 +9540,58 @@ def _handle_create_library(fusion_info: dict, args: dict) -> dict: COMMAND_HANDLERS["create_library"] = _handle_create_library ++def _handle_check_3d_thumbnail(fusion_info: dict, args: dict) -> dict:+ """Is the Library Manager's 3D THUMBNAIL actually rendered? Deterministic, no pixels.++ The thumbnail is rendered by Autodesk's cloud against the bound f3d and exposed as a+ relationship on the file's version - a free /data/v1 read. Fusion takes its time after a+ bind, so `waitSeconds` polls (default 0 = single check). This is the check John named as+ "an area you always screw up": a bound urn with NO rendered thumbnail shows the user a+ grey slab in the Library Manager while everything else reports success.++ args: {libraryPath | urn, fileName?, projectId?, folderId?, waitSeconds?}+ """+ import time as _t+ import aps as _aps+ urn = (args.get("urn") or "").strip()+ lbr = (args.get("libraryPath") or "").replace("\\", "/")+ file_name = args.get("fileName") or ""+ if not urn and lbr:+ try:+ import xml.etree.ElementTree as _ET+ root = _ET.parse(lbr).getroot()+ for p3 in root.iter("package3d"):+ urn = (p3.get("urn") or "").strip() or urn+ file_name = file_name or (p3.get("name") or "")+ except Exception:+ pass+ if not urn and not file_name:+ return {"success": False, "errorCode": "no_3d_reference",+ "error": "No urn/fileName given and the .lbr carries no package3d urn.",+ "_hint": "Bind first (fusion_bind_library_3d), then check the thumbnail."}+ project_id, folder_id = _discover_upload_target(args)+ if not folder_id:+ return {"success": False, "errorCode": "no_work_folder",+ "error": "Cannot resolve the upload folder to look in (APS signed in?)."}+ deadline = _t.time() + float(args.get("waitSeconds") or 0)+ while True:+ st = _aps.thumbnail_state(project_id, folder_id, wip_urn=urn, file_name=file_name)+ if st.get("state") == "ready" or _t.time() >= deadline:+ break+ _t.sleep(10)+ ready = st.get("state") == "ready"+ return {"success": ready, "thumbnail": st, "urn": urn or None,+ "_hint": ("Thumbnail is RENDERED in the cloud - the Library Manager will show the "+ "real chip." if ready else+ "Not rendered yet (state=%s). Fusion uploads + renders asynchronously "+ "after a bind - re-call with waitSeconds:180 to poll, and treat a "+ "still-pending thumbnail as NOT proven." % st.get("state"))}+++COMMAND_HANDLERS["check_3d_thumbnail"] = _handle_check_3d_thumbnail++ def dispatch_command(command: str, args: dict, caller_identity: dict = None) -> dict: """Thin wrapper: run the verb, then guarantee the windowEtiquette annotation lands on EVERY backgrounded window-raising result - the inner dispatcher's closure only covered