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 15 file(s)
9 files changed
+146−19
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.222+1.9.223
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.222",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.222/adom-bridge-fusion-v1.9.222.zip",- "sha256": "9efebec63b8355b56f0308792c807c631e6e6622861fcaebdbf74a1a2687cca8",- "size": 514182,+ "version": "1.9.223",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.223/adom-bridge-fusion-v1.9.223.zip",+ "sha256": "62d6221f7c49756da8bb9cacef3e0d9a6dd5636feaae1e77b8eb192782996ea3",+ "size": 516833, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.222",+ "version": "1.9.223", "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.222"+version = "1.9.223" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.222"+version = "1.9.223" 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.222"+DASH_VERSION="1.9.223" 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.222",+ "version": "1.9.223", "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.222",+ "version": "1.9.223", "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+125−1@@ -11519,9 +11519,19 @@ def _handle_create_library(fusion_info: dict, args: dict) -> dict: # STAGE of this chain, never a refusal: probe once, and when the add-in is not # answering, launch Fusion ourselves and wait for the round trip. _needs_launch = not _addin_round_trip_ready(tries=1)+ # ⛔ THE BIND ESTIMATE MUST SCALE WITH THE PART COUNT (John, 2026-08-27).+ # It was a flat 60s no matter how many parts the wiki sent. A multi-part library+ # imports ONE STEP PER PART - the slowest thing this chain does - so a 7-part+ # bundle showed a bar that filled in a minute and then sat at 100% for many more.+ # A progress bar that lies is worse than none: the user concludes it has hung.+ # Per-part seconds are MEASURED per machine under their own key, so the bar is+ # paced by what THIS box actually does, and only the seed is a literal.+ _n_parts = max(1, len((args.get("parts") or [])) or 1)+ _per_part, _pp_conf = _tmc.estimate("stage:bind_per_part", 60.0)+ _bind_est = _per_part * _n_parts _STAGES = [("launch", 60.0, _needs_launch), ("convert", 5.0, bool(sym_p or mod_p)),- ("bind", 60.0, bool(step_p)),+ ("bind", _bind_est, bool(step_p)), ("prove", 300.0, True)] state["plan"] = [{"stage": n, "estimatedSec": round(_tmc.estimate("stage:" + n, f)[0] or f, 1), "confidence": _tmc.estimate("stage:" + n, f)[1]}@@ -12721,6 +12731,119 @@ result = {"units": "mm", "worldSpace": bool(_WORLD), ''' +def _handle_plan_library_import(fusion_info: dict, args: dict) -> dict:+ """PRE-FLIGHT: what a library import will do, how long, and what state Fusion is in.++ John, 2026-08-27: when the wiki asks to import a library, does the bridge pre-check+ that Fusion is installed, whether it is running or must be launched, hand back honest+ progress-bar data with informational states, and scale the estimate to how much library+ the wiki is actually sending? The facts existed (fusion_readiness) and the chain had a+ measured plan, but nothing REPORTED them before work began, so the wiki could not show+ the user anything until the import was already under way.++ This does NO work and changes nothing: it answers so the wiki can render the summary+ and the user can decide. The same stage names come back from fusion_prove_result while+ the run executes, so one renderer draws the plan and then the bar.+ """+ import time as _t+ import timings as _tmc++ parts = args.get("parts") or []+ n_parts = max(1, len(parts) or 1)+ step_paths = [p.get("modelPath") for p in parts if p.get("modelPath")] or (+ [args.get("stepPath")] if args.get("stepPath") else [])++ inst = bool(fusion_info.get("installed")) or _is_fusion_running()+ running = _is_fusion_running()+ addin_ok = _addin_round_trip_ready(tries=1) if running else False++ # The informational line John asked for, in the app's own voice.+ if not inst:+ status, note = "not_installed", ("Fusion does not look installed on this machine. "+ "I can install it for you - it is a silent install "+ "plus an Autodesk sign-in.")+ elif not running:+ status, note = "installed_not_running", ("Fusion is installed but not running. "+ "I can launch it, which usually takes about "+ "a minute, and then carry on.")+ elif not addin_ok:+ status, note = "addin_restart_required", ("Fusion is running but the Adom add-in is "+ "not loaded - it needs a quick restart to "+ "finish setup. I can save your documents "+ "and do it.")+ else:+ status, note = "ready", "Fusion is running and ready - I can start straight away."++ def _est(key, fallback):+ v, c = _tmc.estimate(key, fallback)+ return round(float(v), 1), c++ steps = []+ if not inst:+ # DO NOT PROMISE AN INSTALL AND THEN LEAVE IT OUT OF THE ESTIMATE. My own test of+ # this verb caught it: the summary offered to install Fusion while the total quoted+ # only the import stages, so a first-run user would have been told ~12 minutes for+ # something that starts with a multi-gigabyte download. Fusion's own installer+ # streams the app and takes 10-30 minutes on a real connection (fusion-onboarding),+ # so it is seeded wide and flagged low-confidence rather than quietly omitted.+ v, c = _est("stage:install_fusion", 1200.0)+ steps.append({"stage": "install", "label": "install Fusion (streams the app)",+ "estimatedSec": v, "confidence": c, "needsUserConsent": True,+ "note": "10-30 minutes depending on connection, plus an Autodesk sign-in"})+ if inst and not running:+ v, c = _est("stage:launch", 60.0)+ steps.append({"stage": "launch", "label": "open Fusion",+ "estimatedSec": v, "confidence": c})+ if status == "addin_restart_required":+ v, c = _est("stage:launch", 60.0)+ steps.append({"stage": "restart", "label": "restart Fusion to load the add-in",+ "estimatedSec": v, "confidence": c, "needsUserConsent": True})+ if step_paths:+ per, c = _est("stage:bind_per_part", 60.0)+ steps.append({"stage": "bind",+ "label": "attach 3D to %d part%s" % (n_parts, "" if n_parts == 1 else "s"),+ "estimatedSec": round(per * n_parts, 1), "confidence": c,+ "parts": n_parts, "perPartSec": per,+ "note": "one STEP import per part - the slowest stage"})+ v, c = _est("stage:prove", 300.0)+ steps.append({"stage": "prove", "label": "capture 4 evidence views",+ "estimatedSec": v, "confidence": c})++ total = round(sum(x["estimatedSec"] for x in steps), 1)+ ready_total = round(sum(x["estimatedSec"] for x in steps+ if x["stage"] not in ("launch", "restart")), 1)+ return {+ "success": True,+ "fusion": {"installed": inst, "running": running, "addinReady": addin_ok,+ "status": status, "message": note},+ "plan": steps,+ "partsTotal": n_parts,+ "estimatedSec": total,+ "estimatedSecIfAlreadyOpen": ready_total,+ "confidence": ("measured" if all(x["confidence"] == "measured" for x in steps)+ else "typical"),+ "summary": ("%s This will take about %s. %s"+ % (note, _human_secs(total),+ ("Had Fusion already been open, about %s." % _human_secs(ready_total))+ if ready_total != total else "")).strip(),+ "_hint": ("Pre-flight only - NOTHING was launched, imported or changed. Render "+ "`summary` plus `plan[]` as the progress bar's steps, then call "+ "fusion_create_library (or open_lbr + build_library_3d) and poll "+ "fusion_prove_result, which reports the SAME stage names so one renderer "+ "draws both. Estimates marked confidence:'measured' come from this "+ "machine's own recorded runs; 'typical' means seeded and not yet observed "+ "here."),+ }+++def _human_secs(v: float) -> str:+ v = int(round(float(v or 0)))+ if v < 60:+ return "%ds" % v+ m, sec = divmod(v, 60)+ return "%dm %02ds" % (m, sec) if sec else "%dm" % m++ def _handle_restart_for_addin(fusion_info: dict, args: dict) -> dict: """Save open documents, restart Fusion, wait for the add-in, report back. @@ -12829,6 +12952,7 @@ def _handle_inspect_bodies(fusion_info: dict, args: dict) -> dict: COMMAND_HANDLERS["inspect_bodies"] = _handle_inspect_bodies COMMAND_HANDLERS["restart_for_addin"] = _handle_restart_for_addin+COMMAND_HANDLERS["plan_library_import"] = _handle_plan_library_import