← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.222\ No newline at end of file+1.9.223\ No newline at end of file
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]"-  }-}\ No newline at end of file+  ]+}
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