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 10 file(s)
10 files changed
+83−24
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.78+1.9.79
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.78",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.90/adom-bridge-fusion-v1.9.78.zip",- "sha256": "842531e5ad61f2db892b253dfbb7c76144b861a0977745d79c88221d35b6dac1",- "size": 417979,+ "version": "1.9.79",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.91/adom-bridge-fusion-v1.9.79.zip",+ "sha256": "3bb33e40f9a332140a43ad008d3e2cbc76b918d669db7efc07b906e6d1c3942f",+ "size": 418806, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.78",+ "version": "1.9.79", "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.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.90"+version = "1.9.91" edition = "2021" [dependencies]
handlers/close_fusion.py+54−3@@ -71,8 +71,36 @@ def _close_and_wait(hwnd: int, title: str, timeout: float = 15.0) -> bool: return False +def _fusion_pids() -> list:+ """PIDs of every surviving Fusion-family process (tasklist CSV, empty on error)."""+ pids = []+ for name in FUSION_PROCESS_NAMES:+ try:+ out = subprocess.check_output(+ ["tasklist", "/FI", "IMAGENAME eq %s" % name, "/FO", "CSV", "/NH"],+ stderr=subprocess.DEVNULL, text=True, timeout=5)+ except Exception:+ continue+ for line in out.splitlines():+ parts = [p.strip('"') for p in line.split('","')]+ if len(parts) > 1 and parts[0].lower().endswith(".exe"):+ try:+ pids.append(int(parts[1]))+ except Exception:+ pass+ return pids++ def _force_kill_fusion() -> int:- """Force kill all Fusion processes. Returns number of processes killed."""+ """Force kill all Fusion processes. Returns number of processes killed.++ ESCALATES (doctrine, 2026-08-19): `taskkill /F /IM` is not the end of the road, and+ when it left a Fusion360.exe standing the bridge used to answer "ask the user to kill+ it via Task Manager" - handing the human a chore the code can do itself. So a survivor+ is now killed BY PID with /T (the whole process tree), which clears the case /IM+ misses. Only if a PID survives THAT do we report, and then with the pids and what was+ tried - never with an instruction to open Task Manager.+ """ killed = 0 for name in FUSION_PROCESS_NAMES: try:@@ -85,6 +113,19 @@ def _force_kill_fusion() -> int: killed += 1 except Exception: pass+ # Escalate on survivors: per-PID, whole tree.+ try:+ time.sleep(1.0)+ for pid in _fusion_pids():+ try:+ r = subprocess.run(["taskkill", "/F", "/T", "/PID", str(pid)],+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)+ if r.returncode == 0:+ killed += 1+ except Exception:+ pass+ except Exception:+ pass return killed @@ -204,7 +245,12 @@ def handle_close_fusion(fusion_info: dict, args: dict) -> dict: return { "success": False, "error": "Some Fusion processes survived force-kill.",- "_hint": "Report to the user and ask them to manually kill remaining Fusion processes via Task Manager, then retry.",+ "_hint": ("The bridge tried taskkill /F /IM for every Fusion process and then "+ "taskkill /F /T per surviving PID, and something still holds on. Do NOT "+ "hand this to the user as a Task Manager chore: check whether the survivor "+ "is a crash-report host (fusion_readiness reports fusionCrashed) or is "+ "pinned by a modal, and re-call fusion_kill after clearing that."),+ "survivingPids": _fusion_pids(), } return { "success": True,@@ -343,7 +389,12 @@ def handle_fusion_kill(fusion_info: dict, args: dict) -> dict: return { "success": False, "error": "Some Fusion processes survived force-kill.",- "_hint": "Ask the user to kill remaining Fusion processes via Task Manager, then retry.",+ "_hint": ("The bridge tried taskkill /F /IM for every Fusion process and then "+ "taskkill /F /T per surviving PID, and something still holds on. Do NOT hand "+ "this to the user as a Task Manager chore: check whether the survivor is a "+ "crash-report host (fusion_readiness reports fusionCrashed) or is pinned by a "+ "modal, and re-call fusion_kill after clearing that."),+ "survivingPids": _fusion_pids(), } return { "success": True,
handlers/open_design.py+4−1@@ -64,7 +64,10 @@ def handle_open_design(fusion_info: dict, args: dict) -> dict: "success": False, "error": "Fusion 360 is not installed on this machine.", "errorCode": "fusion_not_installed",- "_hint": "Fusion 360 cannot be installed programmatically. Report this to the user and ask them to install it from https://www.autodesk.com/products/fusion-360, then retry.",+ "_hint": ("Fusion 360 isn't installed. Do NOT tell the user to install it themselves - "+ "this claim was simply STALE (the bridge silent-installs Fusion via the "+ "fusion-onboarding skill and drives the Autodesk sign-in). OFFER to install it "+ "for them and do it on a yes, then retry this open."), } # Check if Fusion is running (live process check — never auto-launch)
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.90"+DASH_VERSION="1.9.91" 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.90",+ "version": "1.9.91", "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,16 +1,20 @@ { "slug": "fusion-bridge",- "version": "1.9.90",+ "version": "1.9.91", "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": {- "adom/adom-desktop": "*"+ "adom/adom-desktop": "^1.9.0" }, "scripts": { "install": "./install.sh", "uninstall": "./uninstall.sh" }, "tags": [+ "ab",+ "bridge",+ "fusion",+ "desktop-app", "fusion360", "autodesk", "cad",@@ -70,7 +74,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.",@@ -99,9 +106,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+5−3@@ -5057,9 +5057,11 @@ def _handle_mcp_enable(fusion_info: dict, args: dict) -> dict: "_hint": ("MCP server ENABLED - fusion_mcp_status lists its tools. Tell the user it is " "handled; they do not need to touch Preferences." if ok else- "The toggle did not take. Ask the user to set it manually: profile icon (top "- "right) -> Preferences -> General -> API -> tick 'Fusion MCP Server'. Do NOT "- "restart or kill Fusion before they click Apply, that discards the setting."),+ "The toggle did not take. Re-call fusion_mcp_enable - driving Preferences is "+ "racy on a busy main thread and a second pass usually lands it. Do NOT hand the "+ "user a Preferences walkthrough (describe.py says NEVER ask them to do this by "+ "hand); if a second pass also fails, report what the steps[] show and that the "+ "setting is discarded when Fusion restarts before Apply."), "statusVerb": "fusion_mcp_status"}