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)
9 files changed
+132−22
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.306+1.9.307
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.306",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.306/adom-bridge-fusion-v1.9.306.zip",- "sha256": "264ee2e707f92350cd585eef8c267fb3e07697da0e358686ba2c9ffa6b6cdeb7",- "size": 587898,+ "version": "1.9.307",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.307/adom-bridge-fusion-v1.9.307.zip",+ "sha256": "345900735e22a1584e43880742c189f82af2800910863342444538bf157eff48",+ "size": 589478, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.306",+ "version": "1.9.307", "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.306"+version = "1.9.307" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.306"+version = "1.9.307" 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.306"+DASH_VERSION="1.9.307" 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.306",+ "version": "1.9.307", "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.306",+ "version": "1.9.307", "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+111−4@@ -1410,6 +1410,104 @@ def _post_open_screenshot(result: dict, settle_time: float = 2.0) -> dict: return _apply_failure_dialogs(result) +# ── Fusion's OWN error toasts, read programmatically (John, 2026-09-01) ──────+# Fusion draws many errors as CEF overlays INSIDE the canvas: no top-level window, so+# neither Win32 enumeration nor UIA can see them, and check_dialogs reported "no dialogs"+# while an Error box sat on the user's screen ("Cannot create a managed library from a+# document not saved in a Fusion Hub"). Fusion DOES log every one of them to its own+# AppLogFile as a `New Alert message` line, so the bridge reads that instead of guessing+# from pixels. Bridge-side on purpose: it works with the add-in wedged and needs no+# Fusion restart to deploy.+_ALERT_RE = re.compile(r"New Alert message\.\s*(?:Tooltip:\s*(?P<tip>.*?))?"+ r"(?:,\s*Short Message:\s*(?P<short>.*?))?"+ r"(?:,\s*Long Message:\s*(?P<long>.*?))?\s*$")+++def _fusion_log_path():+ """Newest Fusion AppLogFile for the signed-in user, or None."""+ try:+ base = os.path.join(os.path.expanduser("~"), "AppData", "Local", "Autodesk",+ "Autodesk Fusion 360")+ best = None+ for entry in os.listdir(base):+ d = os.path.join(base, entry, "logs")+ if not os.path.isdir(d):+ continue+ for f in os.listdir(d):+ if f.startswith("AppLogFile") and f.endswith(".log"):+ fp = os.path.join(d, f)+ m = os.path.getmtime(fp)+ if best is None or m > best[0]:+ best = (m, fp)+ return best[1] if best else None+ except Exception:+ return None+++def _fusion_alerts(since_sec: float = 300.0, tail_bytes: int = 400000) -> list:+ """Fusion's own alert toasts from the last `since_sec` seconds.++ Returns [{at, text}] newest last. Never raises."""+ import time as _t, datetime as _dt+ path = _fusion_log_path()+ if not path:+ return []+ try:+ size = os.path.getsize(path)+ with open(path, "r", encoding="utf-8", errors="replace") as fh:+ if size > tail_bytes:+ fh.seek(size - tail_bytes)+ fh.readline()+ lines = fh.readlines()+ except Exception:+ return []+ cutoff = _t.time() - max(1.0, since_sec)+ out = []+ for line in lines:+ if "New Alert message" not in line:+ continue+ stamp = None+ m0 = re.match(r"\s*(\d{8}T\d{6})", line)+ if m0:+ try:+ stamp = _dt.datetime.strptime(m0.group(1), "%Y%m%dT%H%M%S").timestamp()+ except Exception:+ stamp = None+ if stamp is not None and stamp < cutoff:+ continue+ m = _ALERT_RE.search(line)+ text = ""+ if m:+ text = (m.group("short") or m.group("tip") or "").strip()+ if not text:+ text = line.split("New Alert message.", 1)[-1].strip()[:400]+ out.append({"at": m0.group(1) if m0 else None, "text": text[:400]})+ return out+++def _with_fusion_alerts(resp: dict, since_sec: float = 300.0) -> dict:+ """Attach Fusion's own alert toasts to a dialog report, so an in-canvas CEF error is+ never invisible just because it has no window (John, 2026-09-01)."""+ try:+ alerts = _fusion_alerts(since_sec)+ if alerts:+ resp = dict(resp or {})+ resp["fusionAlerts"] = alerts+ resp["_hint"] = ("Fusion raised %d alert(s) in the last %ds - the newest is %r. "+ "These are in-canvas CEF overlays with no top-level window, so "+ "they do NOT appear in dialogs[]; read fusionAlerts and treat the "+ "driving command as FAILED unless you know otherwise."+ % (len(alerts), int(since_sec), alerts[-1].get("text", "")[:160]))+ elif not (resp or {}).get("dialogs"):+ resp = dict(resp or {})+ resp["fusionAlerts"] = []+ resp["_hint"] = ("No Fusion dialogs, owned popups, or alert toasts in the last "+ "%ds." % int(since_sec))+ except Exception:+ pass+ return resp++ def _capture_dialog_array(settle: float = 0.6) -> dict | None: """After a mutating op, enumerate + screenshot any owned popups / modal dialogs so the AI can SEE and ANALYZE them. Titles alone are not enough: a dialog's body text@@ -2698,11 +2796,20 @@ COMMAND_HANDLERS = { # can ANALYZE before acting. Use while polling a long op (e.g. a Hub upload) or any # time you suspect a dialog is up. Mutating verbs attach this automatically; this is # the manual entry point. See the fusion-driving skill.- "check_dialogs": lambda fi, args: (+ "check_dialogs": lambda fi, args: _with_fusion_alerts( _capture_dialog_array(settle=float(args.get("settle", 0.3)))- or {"success": True, "dialogsDetected": 0, "dialogs": [],- "_hint": "No Fusion dialogs/owned popups are currently up."}- ),+ or {"success": True, "dialogsDetected": 0, "dialogs": []},+ float(args.get("alertsSinceSec", 300))),+ # Fusion's own error/warning toasts (the CEF overlays no window enumeration can see).+ "fusion_alerts": lambda fi, args: {+ "success": True,+ "alerts": _fusion_alerts(float(args.get("sinceSec", 300))),+ "logFile": _fusion_log_path(),+ "_hint": ("Fusion draws many errors as in-canvas CEF overlays with NO top-level "+ "window, so check_dialogs alone cannot see them; these come from Fusion's "+ "own AppLogFile 'New Alert message' entries. An empty list means Fusion "+ "raised no toast in the window - not that a command succeeded."),+ }, "addin_status": lambda fi, args: _handle_addin_status(), # LAST-RESORT human escalation (John 2026-07-07): when the bridge is truly blocked # on the user (password/2FA, UAC), send an ab toast that reaches their MAIN machine