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
v1.9.4: uninstall cascade (fusion_uninstall) + adom-bridge CLI naming
5 files changed
+96−10
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.3+1.9.4
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.3",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.3/adom-bridge-fusion-v1.9.3.zip",- "sha256": "6a1b1ec071e8eb9c347f3cfc3bd386e370f73098cce9a0c7ec7e4df0ecebe93e",- "size": 301185,+ "version": "1.9.4",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.4/adom-bridge-fusion-v1.9.4.zip",+ "sha256": "165d0f93a4778832e1eedf623d19ded7877172b0276c9ff3da04afcfbe0bb86f",+ "size": 302854, "verbPrefixes": [ "fusion_" ],
bridge.json+3−2@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.3",+ "version": "1.9.4", "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.",@@ -182,5 +182,6 @@ "seconds": 150 } ]- }+ },+ "uninstall": "fusion_uninstall" }
describe.py+5@@ -220,6 +220,11 @@ _T = [ "bodies": "[{name,owner,bbox:{x,y,z},sizeMm,volumeMm3,areaMm2,faceCount,cylindricalFaceCount,appearance,material,isVisible}]"}, 130, _STATUS, False, {"worldSpace": True}), + ("fusion_uninstall", "CASCADE CLEANUP, called by ab's bridge_uninstall BEFORE this bridge's cache is deleted (ab 2.0.35+). Removes the artifacts this bridge planted OUTSIDE its cache: the Fusion ADD-IN in Autodesk's AddIns directories (all three historical locations), plus this bridge's prefs (~/.adom/fusion-bridge) and APS OAuth tokens (~/.adom/fusion-aps). This matters more here than for most bridges: a stale AdomBridge add-in keeps loading on every Fusion launch and binding its port long after the bridge is gone. Fusion itself is never touched. Pass dryRun:true to see exactly what would be removed. If Fusion is RUNNING it holds the add-in files open, so removal may fail - the response says so and names the remedy.",+ {"dryRun": "optional bool (report the paths, delete nothing)", "cascade": "optional bool (set by ab)", "name": "optional str (set by ab)"},+ {"success": "bool", "dryRun": "bool", "fusionRunning": "bool", "removed": "[{path,kind}]", "failed": "[{path,kind,error}]", "skipped": "[{path,kind,why}]"},+ 60, None, False, {"dryRun": True}),+ # ── Self-describe ────────────────────────────────────────────────────── ("fusion_describe", "Self-describe every verb the bridge exposes (this list) for AD's Verbs tab + runner.", {}, {"success": "bool", "verbs": "[{name,summary,input,output,timeoutSeconds,statusVerb,longRunning,example}]"}, 20, None, False, {}),
server.py+83−3@@ -269,13 +269,20 @@ def _find_adom_desktop_cli() -> str | None: if os.path.exists(c): return c # PATH: the CLI shim only. On Windows NEVER resolve the bare name - that is the GUI exe.+ if os.name != "nt":+ # Linux/mac: `adom-bridge` IS the CLI as of ab 2.0.35 (the -cli suffix was dropped; a+ # back-compat symlink lingers). Prefer the canonical name.+ for shim in ("adom-bridge", "adom-bridge-cli", "adom-desktop-cli", "adom-desktop"):+ hit = shutil.which(shim)+ if hit:+ return hit+ return None+ # Windows: the -cli shim ONLY. Never the bare name - there `adom-bridge.exe` is the GUI app,+ # and shelling it with verb args foregrounds it on the user's screen every call (issue #295). for shim in ("adom-bridge-cli", "adom-desktop-cli"): hit = shutil.which(shim) if hit: return hit- if os.name != "nt":- # On Linux/dev the bare name IS the CLI.- return shutil.which("adom-bridge") or shutil.which("adom-desktop") return None @@ -6290,6 +6297,79 @@ def _handle_inspect_bodies(fusion_info: dict, args: dict) -> dict: COMMAND_HANDLERS["inspect_bodies"] = _handle_inspect_bodies ++# ── Uninstall cascade (ab 2.0.35) ────────────────────────────────────────────────────────────+# ab's bridge_uninstall calls this verb FIRST, while our files still exist, so we can remove the+# artifacts we planted OUTSIDE our own cache dir. That matters more for this bridge than for most:+# we install a Fusion ADD-IN into Autodesk's own directories, and a stale AdomBridge add-in keeps+# loading on every Fusion launch, binding port 8774, and reaching for a bridge that no longer+# exists. We also hold two user-data stores (~/.adom/fusion-bridge prefs, ~/.adom/fusion-aps+# OAuth tokens). Uninstall means all of it goes.+#+# Pass {"dryRun": true} to see exactly what WOULD be removed without touching anything.+def _handle_uninstall(fusion_info: dict, args: dict) -> dict:+ """fusion_uninstall - remove this bridge's out-of-cache artifacts (add-in, prefs, APS tokens)."""+ import shutil as _sh+ args = args or {}+ dry = bool(args.get("dryRun"))+ home = pathlib.Path(os.path.expanduser("~"))++ targets = []+ try:+ from install_addin import TARGET_CANDIDATES as _TC+ targets += [pathlib.Path(str(t)) / "AdomBridge" for t in _TC]+ except Exception:+ targets += [home / "AppData" / "Roaming" / "Autodesk" / "FusionAddins" / "AdomBridge",+ home / "AppData" / "Roaming" / "Autodesk" / "Autodesk Fusion" / "API" / "AddIns" / "AdomBridge",+ home / "AppData" / "Roaming" / "Autodesk" / "Autodesk Fusion 360" / "API" / "AddIns" / "AdomBridge"]+ addin_targets = list(targets)+ data_targets = [home / ".adom" / "fusion-bridge", home / ".adom" / "fusion-aps"]++ removed, failed, skipped = [], [], []+ for p in addin_targets + data_targets:+ kind = "addin" if p in addin_targets else "userdata"+ if not p.exists():+ skipped.append({"path": str(p), "kind": kind, "why": "not present"})+ continue+ if dry:+ removed.append({"path": str(p), "kind": kind, "dryRun": True})+ continue+ try:+ _sh.rmtree(p)+ removed.append({"path": str(p), "kind": kind})+ except Exception as e:+ failed.append({"path": str(p), "kind": kind, "error": str(e)})++ running = False+ try:+ running = bool(_is_fusion_running())+ except Exception:+ pass++ out = {"success": not failed, "dryRun": dry, "cascade": bool(args.get("cascade")),+ "fusionRunning": running, "removed": removed, "failed": failed, "skipped": skipped}+ if failed and running:+ out["_hint"] = ("Fusion is RUNNING and holds the add-in files open, so %d path(s) could not be "+ "removed. Close Fusion (fusion_stop) and re-run fusion_uninstall, or delete "+ "those paths by hand - otherwise the stale add-in keeps loading inside Fusion "+ "on every launch and binding its port." % len(failed))+ elif failed:+ out["_hint"] = ("%d path(s) could not be removed (permissions?). They are listed in `failed`; "+ "delete them by hand so no stale add-in is left inside Fusion." % len(failed))+ elif dry:+ out["_hint"] = ("DRY RUN - nothing was touched. These are the paths a real uninstall removes: "+ "the Fusion add-in in Autodesk's AddIns dirs, plus this bridge's prefs and "+ "APS OAuth tokens under ~/.adom.")+ else:+ out["_hint"] = ("Cleaned up: the Fusion add-in is gone from Autodesk's directories, and this "+ "bridge's prefs + APS OAuth tokens are removed. Fusion itself is untouched. "+ "Re-installing the bridge redeploys the add-in on its next start.")+ return out+++COMMAND_HANDLERS["uninstall"] = _handle_uninstall++ if __name__ == "__main__": # Surface any fatal startup error to stderr (which AD captures into # ~/.adom/bridge-logs/fusion360.log) so a spawn-crash is debuggable, then