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)
20 files changed
+69−38
BRIDGE_VERSION+1−1adom-bridge-fusion-manifest.json+4−4bridge.json+1−1dashboard/Cargo.lock+1−1dashboard/Cargo.toml+1−1describe.py+1−1docs/3d-viewer-and-export.md+1−1docs/board-layout-2d.md+3−3docs/electronics-libraries.md+4−4docs/mechanical-bom.md+2−2install.sh+1−1package.json+1−1page.json+11−8server.py+24skills/fusion-build-parts-library/SKILL.md+1−1skills/fusion-driving/SKILL.md+1−1skills/fusion-molecule-export/SKILL.md+2−2skills/fusion-multipart-libraries/SKILL.md+2−2skills/fusion-onboarding/SKILL.md+2−2tools/release.sh+5−1BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.302+1.9.304
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.302",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.302/adom-bridge-fusion-v1.9.302.zip",- "sha256": "844177c1ad593395afe023e808782ee49b9280276f70d67645f24c4d608003bf",- "size": 587028,+ "version": "1.9.304",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.304/adom-bridge-fusion-v1.9.304.zip",+ "sha256": "b5e2b0d5a019b95a8cc6db19c141da13e1b44b3f3977ab0edda5e5ccd2cb8140",+ "size": 587660, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.302",+ "version": "1.9.304", "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.302"+version = "1.9.304" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.302"+version = "1.9.304" edition = "2021" [dependencies]
describe.py+1−1@@ -284,7 +284,7 @@ _T = [ ("fusion_live_feed", "A ~1 fps look INTO Fusion while the bridge drives it, kept as a BOX-SIDE ring buffer (~10 min). Each tick records the Fusion main window (kind:main - the video) and every owned popup over it (kind:popup - dialogs, Browser/tool panels, the Adom banner; Fusion draws these as separate windows a main capture omits), each a path on this machine tied to its tick. Drain from the container with {action:frames, since:<tick>}, read_file each path, and publish where a human looks - tools/live_feed_drain.py does exactly that into shotlog. Proof frames are NOT here: fusion_prove_result evidence[] carries kind:proof with step labels, so the three kinds are distinguishable at the source. Exists so a human can see open tabs and dialogs during boot or a restart instead of guessing.", {"action": "start|stop|status|frames", "intervalSec": "optional float (default 1.0)", "maxSec": "optional float (default 600)", "since": "frames only: return ticks newer than this"}, {"success": "bool", "running": "bool", "tick": "int", "frames": "[{tick, kind:main|popup, path, title, at}]"}, 15, None, True, {"action": "frames", "since": 0}),- ("fusion_restart_for_addin", "Finish add-in setup by restarting Fusion, SAVING the user's open documents first. Call this when any verb returns errorCode addin_restart_required (the add-in was installed/updated while Fusion was already open, and Fusion only loads add-ins at launch). ASK THE USER FIRST when a human is present - the failing verb's payload carries userMessage plus openDocuments/unsavedDocuments so you can show them what is open. Refuses with unsaved_named_document rather than discarding a never-saved document.",+ ("fusion_restart_for_addin", "ASYNC: returns immediately (started:true) and restarts Fusion in the background, SAVING the user's open documents first; poll fusion_readiness until ready with the new addinVersion (a restart takes 60-120s, longer than the relay budget; pass wait:true for the old blocking form). Finish add-in setup by restarting Fusion. Call this when any verb returns errorCode addin_restart_required (the add-in was installed/updated while Fusion was already open, and Fusion only loads add-ins at launch). ASK THE USER FIRST when a human is present - the failing verb's payload carries userMessage plus openDocuments/unsavedDocuments so you can show them what is open. Refuses with unsaved_named_document rather than discarding a never-saved document.", {"save": "optional bool (default true) - save open documents before restarting"}, {"success": "bool", "addinReady": "bool", "tookSec": "float", "steps": "[{step, ok}]"}, 300, None, False, {"save": True}),
docs/3d-viewer-and-export.md+1−1@@ -45,7 +45,7 @@ Result: about **465 KB and ~31 draw calls**. That is the difference between a pa one that hangs. ```bash-adom-bridge fusion_export_optimized_glb '{"outputPath":"C:/tmp/board.glb","silkscreen":true}'+adom-bridge fusion_export_optimized_glb '{"outputPath":"%TEMP%/board.glb","silkscreen":true}' adom-bridge fusion_fetch_optimized_glb '{"jobId":"..."}' # finish a big board ```
docs/board-layout-2d.md+3−3@@ -63,9 +63,9 @@ JLCPCB CAM jobs ship with the bridge in `resources/`, so gerbers come out in the rather than a generic set you have to fix. ```bash-adom-bridge fusion_export_gerbers '{"outputPath":"C:/tmp/fab"}'-adom-bridge fusion_export_bom '{"outputPath":"C:/tmp/bom.csv"}'-adom-bridge fusion_export_cpl '{"outputPath":"C:/tmp/cpl.csv"}'+adom-bridge fusion_export_gerbers '{"outputPath":"%TEMP%/fab"}'+adom-bridge fusion_export_bom '{"outputPath":"%TEMP%/bom.csv"}'+adom-bridge fusion_export_cpl '{"outputPath":"%TEMP%/cpl.csv"}' ``` ### `fusion_export_bom` is electronics-only
docs/electronics-libraries.md+4−4@@ -34,7 +34,7 @@ reaches a board: ## Opening and inspecting ```bash-adom-bridge fusion_open_lbr '{"lbrPath":"C:/tmp/AdomBasicParts.lbr"}'+adom-bridge fusion_open_lbr '{"lbrPath":"%TEMP%/AdomBasicParts.lbr"}' ``` `.lbr` is the EAGLE library format Fusion Electronics uses.@@ -54,10 +54,10 @@ library. ```bash adom-bridge fusion_make_3d_package '{...}' # build one adom-bridge fusion_attach_3d_package '{...}' # bind it to a footprint-adom-bridge fusion_build_library_3d '{"lbrPath":"C:/tmp/newlib/AdomBasicParts2.lbr",+adom-bridge fusion_build_library_3d '{"lbrPath":"%TEMP%/newlib/AdomBasicParts2.lbr", "parts":[{"package":"R_4k7",- "lbrPath":"C:/tmp/newlib/R_4k7.lbr",- "modelPath":"C:/tmp/newlib/R_4k7.step"}]}'+ "lbrPath":"%TEMP%/newlib/R_4k7.lbr",+ "modelPath":"%TEMP%/newlib/R_4k7.step"}]}' adom-bridge fusion_capture_library_views '{...}' # render views for a wiki page ```
docs/mechanical-bom.md+2−2@@ -44,7 +44,7 @@ stops at a physical part or a purchased kit. ```bash adom-bridge fusion_assembly_bom '{}' adom-bridge fusion_assembly_bom '{"treatAsUnit":["with fasteners","bearing unit"],- "outputPath":"C:/tmp/bom.csv"}'+ "outputPath":"%TEMP%/bom.csv"}' ``` | arg | meaning |@@ -95,7 +95,7 @@ the work is still running on the bridge; poll rather than treating it as failure ```bash # 1. structured parts list, CSV for purchasing-adom-bridge fusion_assembly_bom '{"outputPath":"C:/tmp/bom.csv"}'+adom-bridge fusion_assembly_bom '{"outputPath":"%TEMP%/bom.csv"}' # 2. volumes only for the printed parts, to price filament adom-bridge fusion_physical_properties '{"names":["Spool Holder","Guide Block"]}'
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.302"+DASH_VERSION="1.9.304" 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.302",+ "version": "1.9.304", "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.302",+ "version": "1.9.304", "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+24@@ -15505,6 +15505,30 @@ def _handle_restart_for_addin(fusion_info: dict, args: dict) -> dict: return {"success": True, "declined": True, "hold": True, "_hint": "Recorded: the user owns the restart. Any chain waiting on the " "notice stops with user_owns_restart; nothing restarts Fusion."}+ # ASYNC BY DEFAULT (Ray, adom/hydrogen#42): a real Fusion restart takes longer than the+ # relay's 60s budget, so the synchronous form ALWAYS reported "timeout" even when it+ # worked, and a caller could not tell "restarting, wait" from "failed". The verb now+ # returns immediately and does the work on a thread; poll fusion_readiness until+ # ready:true with the expected addinVersion. Pass wait:true for the old blocking form+ # (in-process callers use that).+ if not args.get("wait") and not args.get("_sync"):+ import threading as _rth, json as _rjs+ def _bg():+ try:+ r = _handle_restart_for_addin(fusion_info, dict(args, wait=True))+ print("[Fusion Bridge] async restart_for_addin finished: %s"+ % _rjs.dumps({k: r.get(k) for k in ("success", "errorCode", "error")})[:200])+ _set_bridge_pref("lastRestartResult", {k: r.get(k) for k in ("success", "errorCode", "error")})+ except Exception as e:+ print("[Fusion Bridge] async restart_for_addin crashed: %s" % str(e)[:200])+ _rth.Thread(target=_bg, daemon=True).start()+ return {"success": True, "started": True, "async": True,+ "statusVerb": "fusion_readiness",+ "_hint": ("Restart under way in the background (a Fusion restart takes 60-120s, "+ "longer than the relay budget, so this returns immediately). Poll "+ "fusion_readiness until ready:true and addinVersion matches; the "+ "outcome is also stored in the lastRestartResult preference. A timeout "+ "on this verb no longer exists and never meant failure anyway.")} import time as _t save = args.get("save", True) t0 = _t.time()
skills/fusion-build-parts-library/SKILL.md+1−1@@ -91,7 +91,7 @@ exports STEP in ONE call. Params are datasheet **mm** (auto-converted). markings are separate `Marking` bodies (white in Fusion and in the colored STEP). ```bash-adom-bridge fusion_generate_package '{"type":"soic","params":{"DPins":8,"D":4.9,"E":6.0,"E1":3.9,"e":1.27,"A":1.6,"A1":0.1,"b":0.41,"L":0.84},"etch":"LM358","outputStep":"C:/tmp/ADOM-LM358-SOIC8.step"}'+adom-bridge fusion_generate_package '{"type":"soic","params":{"DPins":8,"D":4.9,"E":6.0,"E1":3.9,"e":1.27,"A":1.6,"A1":0.1,"b":0.41,"L":0.84},"etch":"LM358","outputStep":"%TEMP%/ADOM-LM358-SOIC8.step"}' ``` Read the verb's `_hint` on every call - it carries the band/margin/contrast rules and the per-family
skills/fusion-driving/SKILL.md+1−1@@ -104,7 +104,7 @@ ways to get the array, in order of preference: - ab core `desktop_screenshot_window {hwnd}` saves the PNG **into your container** (e.g. `/tmp/adom-desktop-screenshots/`) - Read it directly. - The bridge's own captures (the auto `dialogArray[].screenshot` paths and `fusion_screenshot_all`)- are saved on the **Windows box** (`C:/tmp/conduit-screenshots/`). To see one, either re-capture that+ are saved on the **Windows box** (`%TEMP%/conduit-screenshots/`). To see one, either re-capture that hwnd with `desktop_screenshot_window {hwnd}` (lands in your container) or `desktop_pull_file` it over. Either way, LOOK - do not describe it from memory.
skills/fusion-molecule-export/SKILL.md+2−2@@ -93,13 +93,13 @@ request timeout), so it is submit-then-poll, never one long blocking call: ``` # 1) export STEP + submit to the optimizer (returns fast with a jobId; small boards may # already come back complete within `wait` seconds)-fusion_export_optimized_glb {"outputPath":"C:/tmp/board.glb", "silkscreen":true, "pin":"medium", "wait":15}+fusion_export_optimized_glb {"outputPath":"%TEMP%/board.glb", "silkscreen":true, "pin":"medium", "wait":15} # -> { pending:true, jobId:"...", stepPath, resultUrl } (big board) # -> { status:"complete", glbPath, sizeBytes, meshesAfter, moleculeAnchored } (small board) # 2) finish it: re-call until status=complete (each call polls ~15s, well under the relay # timeout, and writes the GLB on the box when ready). No Fusion needed for this verb.-fusion_fetch_optimized_glb {"jobId":"...", "outputPath":"C:/tmp/board.glb", "wait":15}+fusion_fetch_optimized_glb {"jobId":"...", "outputPath":"%TEMP%/board.glb", "wait":15} # -> { status:"complete", glbPath, sizeBytes } then pull_file it. ```
skills/fusion-multipart-libraries/SKILL.md+2−2@@ -51,7 +51,7 @@ fusion_build_library_3d { not have to `fusion_aps_browse` for them. - Returns `{partsBound, partsTotal, parts:[{package,success,wip_urn,dims_mm,error}], screenshots: [{package,stage,path}], boundLbr, opened}`. The before/after PNGs land on the box- (`C:/tmp/conduit-screenshots`) - `desktop_pull_file` them for a demo video.+ (`%TEMP%/conduit-screenshots`) - `desktop_pull_file` them for a demo video. - **Each part is independent.** If Fusion resets mid-run, the parts already done kept their f3d uploads; just re-run the whole `parts[]` (re-making a few f3ds is cheap) - the injection rewrites all bindings in one pass, so a single final call always yields a complete library.@@ -64,7 +64,7 @@ still running server-side and finishes fine.** Do NOT treat that as failure. Ins 1. Wait, then **verify by the artifacts, not the response**: read the `boundLbr` and count `<package3d name=` (should equal your part count), and list the before/after PNGs in- `C:/tmp/conduit-screenshots`.+ `%TEMP%/conduit-screenshots`. 2. If a part is missing (the FIRST part of a run is the usual casualty, as Fusion settles from a prior view), **just re-run `fusion_build_library_3d` for the missing parts with `outLbrPath` pointing at the SAME file.** The binding injection is **merge-aware + idempotent** (v1.6.1+): it reads the
skills/fusion-onboarding/SKILL.md+2−2@@ -34,7 +34,7 @@ The installer is the small "Fusion Client Downloader" (it streams the full app d ``` URL: https://dl.appstreaming.autodesk.com/production/installers/Fusion Client Downloader.exe ```-Download it to a no-space path on the laptop (e.g. C:\tmp\):+Download it to a no-space path on the laptop (e.g. %TEMP%\): ``` shell_execute {"command":"curl -L -o C:\\tmp\\FusionDL.exe \"https://dl.appstreaming.autodesk.com/production/installers/Fusion%20Client%20Downloader.exe\""} ```@@ -42,7 +42,7 @@ shell_execute {"command":"curl -L -o C:\\tmp\\FusionDL.exe \"https://dl.appstrea ## Step 2 — Silent install (elevated) ```-"C:\tmp\FusionDL.exe" --globalinstall --quiet # system-wide, no UI+"%TEMP%\FusionDL.exe" --globalinstall --quiet # system-wide, no UI ``` - Needs **admin/elevation** — run it so it elevates (UAC). On the user's own box this is one consent. It then **streams ~several GB**, so it can take 10-30 min — **monitor, don't block**
tools/release.sh+5−1@@ -279,7 +279,11 @@ except Exception: print('')" 2>/dev/null) echo " page moved since our last push: $LAST_MSG" case "$LAST_MSG" in "Publish "*|"Update "*) echo " (our own release pattern - continuing)" ;;- *) die "the page repo changed since our last push (\"$LAST_MSG\") - someone merged a PR or edited it. PULL those changes into this tree FIRST, or this push reverts them. RELEASE_FORCE=1 only once you have confirmed there is nothing to lose." ;;+ *) if [ "${RELEASE_FORCE:-0}" = "1" ]; then+ echo " page-head guard BYPASSED by RELEASE_FORCE=1 (head: \"$LAST_MSG\")"+ else+ die "the page repo changed since our last push (\"$LAST_MSG\") - someone merged a PR or edited it. PULL those changes into this tree FIRST, or this push reverts them. RELEASE_FORCE=1 only once you have confirmed there is nothing to lose."+ fi ;; esac else echo " page head unchanged since our last push"