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 72 file(s)
11 files changed
+85−25
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.230+1.9.231
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.230",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.230/adom-bridge-fusion-v1.9.230.zip",- "sha256": "bf8a05aa093a23c28c711f208bcf495794c39fc6d565280941af035c670a344d",- "size": 525215,+ "version": "1.9.231",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.231/adom-bridge-fusion-v1.9.231.zip",+ "sha256": "23e4abeab4f1da21c506a84abf90c7d8602e32407d90f1c57331ee5e7571debb",+ "size": 526000, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.230",+ "version": "1.9.231", "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.230"+version = "1.9.231" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.230"+version = "1.9.231" edition = "2021" [dependencies]
describe.py+1−1@@ -15,7 +15,7 @@ _STATUS = "fusion_get_app_state" _T = [ # ── Lifecycle ────────────────────────────────────────────────────────── ("fusion_preflight_lbr", "PRE-ANALYZE a .lbr: will Fusion open it? Offline and instant - no Fusion needed, nothing launched. Runs the named checks the bridge learned from real Fusion rejections: file-readable, size-sane (truncated-transfer corpse detection), xml-parses, root-is-eagle, packages-nonempty (Fusion's silent 'No Components'), symbols-present, devicesets-present (missing ones make EDIT raise Create-new prompts), connects-resolve (dead wiring renders fine and connects nothing). fusion_open_lbr and fusion_create_library run the same gate automatically; call this directly to gate a batch BEFORE staging.", {"libraryPath": "str (Windows .lbr path)"}, {"success": "bool", "preflight": "{ok, checks[], errors[]}"}, 10, None, True, {"libraryPath": "C:/Temp/PART.lbr"}),- ("fusion_create_library", "ONE-CALL library creation for web-control (one call = one consent). EVERY LIBRARY IS MULTI-PART - a single-part library is simply parts:[one entry], and there is no separate single-part verb to choose. Pass parts:[{package, lbrPath, modelPath}] for any number of parts; the older {libraryPath, stepPath} shape still works and is normalised into a one-element parts list, so both run the identical code path and the ETA scales with the part count either way. TWO entry shapes: A) {libraryPath, stepPath} - a ready .lbr plus the 3D; B) {kicadSymbolPath, kicadFootprintPath, stepPath, pinMap?} - KiCad sources; needs adom-lbr, which is Linux-container-only by platform rule, so on Windows/Mac boxes shape B refuses IMMEDIATELY (adom_lbr_unavailable, before any staging or Fusion launch - fusion_readiness reports kicadIngest up front). Convert at AUTHORING time in a container and use shape A. pinMap = explicit {pinName: padName} for when pin numbers do not match pad names (BGA-style). Chains convert -> bind 3D -> prove (four evidence frames + verdict) in the BACKGROUND and returns immediately; poll fusion_prove_result (stage narrates; top-level `verdict` proven/not_proven is the score - `stage:done` only means the chain finished). On the FINAL fetch pass {inlineFrames:true} to get each evidence frame as a base64 thumbnailDataUrl a browser can render (plain polls return box paths only). The chain closes its library document on success AND failure - batches need no between-part sweeping. stepPath is required for a PROVEN library - no real 3D body without it.", {"parts": "optional [{package, lbrPath, modelPath}] - THE canonical shape, any count", "libraryPath": "optional str (shape A, or output path for B)", "kicadSymbolPath": "optional str", "kicadFootprintPath": "optional str", "stepPath": "optional str (required for proven)", "pinMap": "optional {pinName: padName}", "name": "optional str"}, {"started": "bool", "statusVerb": "str"}, 30, "fusion_prove_result", True, {"libraryPath": "C:/Temp/PART.lbr", "stepPath": "C:/Temp/PART.step"}),+ ("fusion_create_library", "ONE-CALL library creation for web-control (one call = one consent). EVERY LIBRARY IS MULTI-PART - a single-part library is simply parts:[one entry], and there is no separate single-part verb to choose. Pass parts:[{package, lbrPath, modelPath}] for any number of parts; the older {libraryPath, stepPath} shape still works and is normalised into a one-element parts list, so both run the identical code path and the ETA scales with the part count either way. TWO entry shapes: A) {libraryPath, stepPath} - a ready .lbr plus the 3D; B) {kicadSymbolPath, kicadFootprintPath, stepPath, pinMap?} - KiCad sources; needs adom-lbr, which is Linux-container-only by platform rule, so on Windows/Mac boxes shape B refuses IMMEDIATELY (adom_lbr_unavailable, before any staging or Fusion launch - fusion_readiness reports kicadIngest up front). Convert at AUTHORING time in a container and use shape A. pinMap = explicit {pinName: padName} for when pin numbers do not match pad names (BGA-style). Chains convert -> bind 3D -> prove (four evidence frames + verdict) in the BACKGROUND and returns immediately; poll fusion_prove_result (stage narrates; top-level `verdict` proven/not_proven is the score - `stage:done` only means the chain finished). EVERY PART IS PROVED, not just the first: a 10-component library streams 40 evidence frames, FOUR per component (symbol, footprint, 3d, ganged) in assembly order, each tagged with `part` so a front end can group them as they arrive, and `provedParts[]` reports per-part pass/fail. The library is proven only if EVERY part is - one good part does not vouch for nine unchecked ones. On the FINAL fetch pass {inlineFrames:true} to get each evidence frame as a base64 thumbnailDataUrl a browser can render (plain polls return box paths only). The chain closes its library document on success AND failure - batches need no between-part sweeping. stepPath is required for a PROVEN library - no real 3D body without it.", {"parts": "optional [{package, lbrPath, modelPath}] - THE canonical shape, any count", "libraryPath": "optional str (shape A, or output path for B)", "kicadSymbolPath": "optional str", "kicadFootprintPath": "optional str", "stepPath": "optional str (required for proven)", "pinMap": "optional {pinName: padName}", "name": "optional str"}, {"started": "bool", "statusVerb": "str"}, 30, "fusion_prove_result", True, {"libraryPath": "C:/Temp/PART.lbr", "stepPath": "C:/Temp/PART.step"}), ("fusion_start", "Launch Fusion 360 (if needed) and wait for the AdomBridge add-in to be ready.", {}, {"success": "bool", "addinReady": "bool", "status": "str"}, 150, None, True, {}), ("fusion_stop", "Gracefully STOP Fusion 360: close docs cleanly + WM_CLOSE, then wait. NO force-kill - the clean opposite of fusion_start. If a window will not close it returns failure; use fusion_kill then.",
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.230"+DASH_VERSION="1.9.231" 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.230",+ "version": "1.9.231", "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.230",+ "version": "1.9.231", "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+42−6@@ -11949,12 +11949,48 @@ def _handle_create_library(fusion_info: dict, args: dict) -> dict: state["stageLabel"] = "captured %s (%d of 4)" % (entry.get("step"), len(ev)) _save() - prove = _handle_prove_library(fusion_info, {- "libraryPath": target_lbr, "name": args.get("name"),- # the bind just told us exactly which f3d holds the geometry - hand it on so- # the 3D frame never depends on re-parsing the lbr for it- "f3dUrn": ((state.get("bind") or {}).get("wip_urn") or ""),- "_onFrame": _publish_frame})+ # ⛔ PROVE EVERY PART, NOT JUST THE FIRST (John, 2026-08-28).+ # _handle_prove_library takes a singular `name` and defaults to devs[0], so a+ # ten-component library used to stream FOUR frames and call itself proven -+ # evidence for one part presented as evidence for the library. Same false-pass+ # family as #58 and #62, just at library scale. Now every deviceset is proved,+ # so a 10-part library streams 40 frames, four per component, each tagged with+ # the part it belongs to so a front end can group them as they arrive.+ _want = (args.get("name") or "").strip()+ try:+ _all_devs = _library_artifact_names(target_lbr).get("dev") or []+ except Exception:+ _all_devs = []+ _targets = [_want] if _want else (_all_devs or [""])+ state["partsToProve"] = len(_targets)+ state["provedParts"] = []+ _prove_results, _all_ok = [], True+ for _pi, _dev in enumerate(_targets, 1):+ state["stageLabel"] = ("proving %s (%d of %d)"+ % (_dev or "library", _pi, len(_targets)))+ _save()++ def _frame_for(entry, _d=_dev):+ entry = dict(entry)+ entry["part"] = _d # tag it so 40 frames stay groupable+ return _publish_frame(entry)++ _r = _handle_prove_library(fusion_info, {+ "libraryPath": target_lbr, "name": _dev,+ # the bind just told us exactly which f3d holds the geometry - hand it+ # on so the 3D frame never depends on re-parsing the lbr for it+ "f3dUrn": ((state.get("bind") or {}).get("wip_urn") or ""),+ "_onFrame": _frame_for})+ _prove_results.append({"part": _dev, "proven": bool(_r.get("proven")),+ "error": _r.get("error")})+ state["provedParts"] = _prove_results+ _all_ok = _all_ok and bool(_r.get("proven"))+ _save()+ # The library is proven only if EVERY part is. One good part does not vouch+ # for nine unchecked ones.+ prove = {"proven": _all_ok, "parts": _prove_results,+ "partsProven": sum(1 for r in _prove_results if r["proven"]),+ "partsTotal": len(_prove_results)} _leave("prove") # ⛔ "done" WITH ZERO FRAMES IS A LIE (page issue #65, Colby, 2026-08-27). # A caller polling the documented contract watched this walk launching ->
skills/fusion-web-control/SKILL.md+21@@ -157,3 +157,24 @@ them because "this library only has one part" is the mistake this unification re **History, so nobody re-introduces it:** this used to be a singular family (`create_library{stepPath}`, `bind_library_3d{stepPath}`, `attach_3d_package{modelPath}`) beside a plural one (`build_library_3d{parts[]}`). Two families, split on the wrong axis.++## Evidence scales with the library: 4 frames per component++Every part is proved, not just the first. A **10-component library streams 40 frames** —+four per component (symbol → footprint → 3d → ganged), in assembly order, published the+instant each is captured.++Each frame carries `part`, so a front end that renders frames as they arrive can group them+per component rather than showing one undifferentiated pile:++```json+{ "stageLabel": "proving U7 (7 of 10)",+ "evidenceCount": 26,+ "partsToProve": 10,+ "provedParts": [ {"part":"U1","proven":true}, ... ],+ "evidence": [ {"part":"U7","step":"footprint","ok":true,"screenshot":"..."} ] }+```++**The library is proven only if EVERY part is.** One good part does not vouch for nine+unchecked ones — `prove.partsProven` vs `prove.partsTotal` is the real score, and+`stage: "done"` still only means the chain finished.