← Commit history
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]"-  }+  ] }\ No newline at end of file
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"}