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)
10 files changed
+44−23
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.294+1.9.295
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.294",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.294/adom-bridge-fusion-v1.9.294.zip",- "sha256": "1d1c618c6d5eace5e0467f1ee85e27e4d249649ccdffad5e8623e2721cbee997",- "size": 583360,+ "version": "1.9.295",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.295/adom-bridge-fusion-v1.9.295.zip",+ "sha256": "a095091c7100fc8191e8cc511da4af6d9e28c161ce96bceee8b60c5226a55f3f",+ "size": 583695, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.294",+ "version": "1.9.295", "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.294"+version = "1.9.295" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.294"+version = "1.9.295" edition = "2021" [dependencies]
describe.py+1−1@@ -206,7 +206,7 @@ _T = [ ("fusion_show_3d_package", "Open the 3D CHIP view (Package3DEnvironment) for a library package - and the RELIABLE way to get a 3D model into a library: pass stepPath and it is attached AFTER the library opens (embedded <packages3d> in hand-built .lbr files is the Fusion quirk that fails; attach-after-open works). stepPath with no library/packageXml auto-generates a minimal 2-pad carrier footprint. Without stepPath it just enters the 3D view of the named package.", {"capture": "optional bool - ONE call = ONE consent: the bridge settles the surface (waits for the add-in main thread + a paint beat) and returns proof inline as shot:{title,image,thumbnailDataUrl,maxWidth} (base64 frame + a ready-to-use data: URL for BROWSER callers who cannot pull a file; set captureMaxWidth to shrink it - 320 keeps a /command body small, default 640. The bridge encodes PNG; the relay may transcode to WebP, so sniff rather than assume). Use this instead of polling fusion_state after a show - page-side settle polling costs one human consent per poll (issue #30).", "name": "str", "libraryPath": "optional str (.lbr)", "packageXml": "optional str (bare <package> fragment)", "stepPath": "optional str (WINDOWS path .step to attach)", "foreground": "optional bool - DEFAULT IS BACKGROUND (no focus steal; response carries windowEtiquette:background). true REQUIRES foregroundReason and shows it to the user as a 3s ab caption before any focus change - only when the USER asked to watch", "foregroundReason": "required WITH foreground:true - a short user-facing sentence shown as the 3s caption (e.g. 'You asked to watch the 3D board build')"}, {"success": "bool", "view": "str", "screenshot": "str path", "wrappedLibrary": "str"}, 300, None, True, {"name": "MYCHIP", "stepPath": "C:/Temp/chip.step"}), ("fusion_demo_progress", "Poll a running fusion_demo tour: {percent, elapsedSec, estimatedSec, step, stepLabel, done, failed, shots, shotCount, stepShot}. Poll ~1-2.5s and render a REAL progress bar. LIVE EVIDENCE (issue #30): each frame MAY carry stepShot {step, title, image} - a ~640px base64 capture the BRIDGE took at that step's completion, one per step, riding the first frame after it completes (render these during the run; the page should not guess-snap). shots[] streams too: mid-run it carries paths for every completed step; pass imagesAs:base64 + sinceShot:<count you have> to receive only NEW shots fully encoded. done:true = the full gallery (base64 by default). Finished tours are DISK-SNAPSHOTTED, so the final frame stays pollable even across a bridge respawn.", {"demoId": "required str (from fusion_demo mode:tour; omitted = the single running tour)", "imagesAs": "optional str paths|base64|both", "sinceShot": "optional int - shots you already received; response windows to the new ones"}, {"percent": "int", "step": "str", "stepLabel": "str", "done": "bool", "shots": "list", "shotCount": "int", "stepShot": "dict|absent"}, 15, None, False, {"demoId": "tour-abc123", "imagesAs": "base64", "sinceShot": 3}), - ("fusion_make_test_project", "Create a NEW electronics test project FROM SCRATCH around a library part - the Web Control demo's PROJECT button, and the answer to 'a customer only has a component page'. Generates a minimal test circuit server-side (the part + a generated 330R series resistor with VCC/GND stubs on a small board) as real EAGLE .sch/.brd, then imports the pair as a new electronics design. NO cloud, NO sign-in, no pre-existing content. After it, fusion_show_schematic / fusion_show_2d_board / fusion_show_3d_board all work on the result. Wiring uses the deviceset's own connects (first two pins), so 2-terminal parts get a real circuit and big ICs get a structurally-valid integration proof.", {"libraryPath": "required str (WINDOWS path .lbr; stage from container first)", "deviceset": "optional str (defaults to the library's first deviceset)"}, {"success": "bool", "schPath": "str", "brdPath": "str", "circuit": "str"}, 300, None, True, {"libraryPath": "C:/Temp/IN-S42ATR.lbr"}),+ ("fusion_make_test_project", "Create a NEW electronics test project FROM SCRATCH around a library part - the Web Control demo's PROJECT button, and the answer to 'a customer only has a component page'. Generates a minimal test circuit server-side (the part + a generated 330R series resistor with VCC/GND stubs on a small board) as real EAGLE .sch/.brd, then imports the pair as a new electronics design. NO cloud, NO sign-in, no pre-existing content. After it, fusion_show_schematic / fusion_show_2d_board / fusion_show_3d_board all work on the result. Wiring uses the deviceset's own connects (first two pins), so 2-terminal parts get a real circuit and big ICs get a structurally-valid integration proof.", {"libraryPath": "required str (WINDOWS path .lbr; stage from container first)", "deviceset": "optional str (defaults to the library's first deviceset)", "device": "optional str: the DEVICE (package variant) to place, e.g. \"-HV\" (default: the deviceset's first device)"}, {"success": "bool", "schPath": "str", "brdPath": "str", "circuit": "str"}, 300, None, True, {"libraryPath": "C:/Temp/IN-S42ATR.lbr"}), ("fusion_screenshot_fusion", "Background PrintWindow capture of Fusion's MAIN window (largest same-title window; works occluded/bottom-of-z-order, never raises anything) or a specific dialog by hwnd. Returns savedTo (PNG/WebP on the box; pull with pull_file). (Live since early versions but undescribed until 2026-08-15 - it was invisible to callers AND to the drift check.)", {"hwnd": "optional int (a dialog; default the main window)"}, {"success": "bool", "savedTo": "str", "sizeKB": "int"}, 60, None, False, {}), ("fusion_bind_library_3d", "LOW-LEVEL primitive: binds ONE STEP onto one deviceset. Prefer fusion_create_library with parts[] (which handles one part or many through a single path); reach for this only when you are driving a single bind deliberately. The CORRECT library in ONE call (the Library button's real bar: the 3D chip renders in the Content Manager preview): mints the part's 3D as a cloud f3d (make_3d_package), injects the wip_urn binding into the .lbr ON DISK (packages3d + package3dinstances), and reopens the library. Needs a signed-in Fusion session - the one cloud-dependent step in the wiki-to-EE circle. Autodesk's thumbnail preview lags ~2-3 min behind a fresh mint. NOTE: distinct from the older fusion_build_library_3d (multi-part batch pipeline).", {"libraryPath": "required str (WINDOWS .lbr path)", "stepPath": "required str (WINDOWS .step path)", "deviceset": "optional str (defaults to the library's first)"}, {"success": "bool", "wip_urn": "str", "bound": "bool", "reopened": "bool"}, 480, None, True, {"libraryPath": "C:/Temp/PART.lbr", "stepPath": "C:/Temp/PART.step"}), ("fusion_show_library", "Eight-surfaces contract: the LIBRARY surface. libraryPath (WINDOWS .lbr) opens it; name alone handles the already-open case (symmetric with kicad_show_library). Background; returns a screenshot path.", {"capture": "optional bool - ONE call = ONE consent: the bridge settles the surface (waits for the add-in main thread + a paint beat) and returns proof inline as shot:{title,image,thumbnailDataUrl,maxWidth} (base64 frame + a ready-to-use data: URL for BROWSER callers who cannot pull a file; set captureMaxWidth to shrink it - 320 keeps a /command body small, default 640. The bridge encodes PNG; the relay may transcode to WebP, so sniff rather than assume). Use this instead of polling fusion_state after a show - page-side settle polling costs one human consent per poll (issue #30).", "libraryPath": "optional str (WINDOWS .lbr)", "name": "optional str (already-open library name)"}, {"success": "bool", "view": "str", "screenshot": "str"}, 240, None, True, {"libraryPath": "C:/Temp/PART.lbr"}),
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.294"+DASH_VERSION="1.9.295" 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.294",+ "version": "1.9.295", "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.294",+ "version": "1.9.295", "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+22−4@@ -9983,7 +9983,7 @@ def _glb_export_async_status(args: dict) -> dict: # design. No cloud, no sign-in, no pre-existing content. The schematic/2D/3D buttons then # work on the result. -def _mtp_extract(lbr_text, deviceset):+def _mtp_extract(lbr_text, deviceset, device=None): """Pull the pieces the generator needs out of the .lbr.""" def block_of(tag, name): m = re.search(r'<%s name="%s"[\s>].*?</%s>' % (tag, re.escape(name), tag), lbr_text, re.S)@@ -9993,7 +9993,15 @@ def _mtp_extract(lbr_text, deviceset): return None, "deviceset %r not found in the library" % deviceset gate = re.search(r'<gate name="([^"]+)" symbol="([^"]+)"', ds) prefix_m = re.search(r'<deviceset name="[^"]+"[^>]*prefix="([^"]+)"', ds)- dev = re.search(r'<device name="[^"]*" package="([^"]+)"', ds)+ # PACKAGE VARIANTS (John, 2026-08-30): pick the named device ("-HV", "-SB", ...) so the+ # test board carries THAT footprint (its vias, its rounded pads); default is the first.+ dev = None+ if device is not None:+ dev = re.search(r'<device name="%s" package="([^"]+)"' % re.escape(device), ds)+ if not dev:+ return None, "deviceset %r has no device named %r" % (deviceset, device)+ if dev is None:+ dev = re.search(r'<device name="[^"]*" package="([^"]+)"', ds) if not (gate and dev): return None, "deviceset %r has no gate/package device mapping" % deviceset connects = re.findall(r'<connect gate="[^"]+" pin="([^"]+)" pad="([^"]+)"', ds)@@ -10313,14 +10321,24 @@ def _orchestrate_make_test_project(args: dict) -> dict: if not m: return {"success": False, "error": "no deviceset found in the library"} deviceset = m.group(1)- ex, err = _mtp_extract(lbr_text, deviceset)+ ex, err = _mtp_extract(lbr_text, deviceset, args.get("device")) if err: return {"success": False, "error": err} lib_name = re.sub(r"[^A-Za-z0-9_-]", "_", os.path.basename(lbr_path).rsplit(".", 1)[0]) # The source library's 3D binding (if any) rides into the generated BOARD - each # element gets package3d_urn so the 3D PCB renders the REAL component model, not a # generic extrusion (John caught every test board showing black boxes, 2026-08-15).- urn_m = re.search(r'wip_urn="([^"]+)"', lbr_text)+ # THIS package's 3D model, not the first urn in the file (variants each bind their own)+ urn_m = None+ try:+ _pk_esc = re.escape(ex["pkgName"])+ for _p3 in re.finditer(r'<package3d [^>]*wip_urn="([^"]+)"[^>]*>(.*?)</package3d>', lbr_text, re.S):+ if re.search(r'<packageinstance name="%s"/>' % _pk_esc, _p3.group(2)):+ urn_m = re.match(r'(.*)', _p3.group(1)); break+ except Exception:+ urn_m = None+ if urn_m is None:+ urn_m = re.search(r'wip_urn="([^"]+)"', lbr_text) solo = not bool(args.get("withResistor")) rex, _rtxt = (None, None) if solo else _mtp_resistor() r_urn = _r3d_cached_urn() if rex else ""