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 105 file(s)
10 files changed
+83−18
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.340+1.9.341
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.340",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.340/adom-bridge-fusion-v1.9.340.zip",- "sha256": "701eba60f60d139c4d1f508a872039cd905ee5ea8b6f85ed1df9674fc9bcfe78",- "size": 609460,+ "version": "1.9.341",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.341/adom-bridge-fusion-v1.9.341.zip",+ "sha256": "0a0121d2c74ce8aad44c3b0cd8ea56864d935f84614ff46a472125dedb8c5ff5",+ "size": 610511, "verbPrefixes": [ "fusion_" ],
aps.py+4@@ -212,6 +212,10 @@ def _launch_url_in_browser(pref: dict, url: str) -> bool: cmd.append("--new-window") cmd.append(url) try:+ # DELIBERATELY LOCAL, not ab's desktop_open_url: that verb takes only+ # {url, browser} and cannot pin --profile-directory, which is the whole point+ # here (the sign-in must land in the profile that owns the Autodesk session).+ # Revisit if desktop_open_url ever grows a profile arg. subprocess.Popen(cmd, close_fds=True, creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0)) return True
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.340",+ "version": "1.9.341", "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.340"+version = "1.9.341" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.340"+version = "1.9.341" edition = "2021" [dependencies]
handlers/close_fusion.py+58@@ -71,8 +71,57 @@ def _close_and_wait(hwnd: int, title: str, timeout: float = 15.0) -> bool: return False +def _ab_process_rows(name_substr):+ """Fusion-family process rows via ab's own process_list verb, or None when ab is+ unreachable. VERB-FIRST DOCTRINE (John, 2026-09-01: "aren't those ab verbs you could+ call instead?"): ab already owns host process ops - one implementation, no child+ process to spawn, and the call lands in ab's activity log with this bridge's caller+ identity, where a raw taskkill is invisible. The tasklist/taskkill spawns below survive+ only as the degraded path for when ad_client is down (unattended VM, ab mid-restart).++ NOTE the deliberate exception: fusion_detect's POLLING uses a local Toolhelp32 snapshot+ instead of this verb, and should keep doing so - a poll every few seconds wants+ microseconds in-process, not an HTTP round trip. Verb for OPERATIONS, snapshot for+ POLLS, hidden spawn only as a fallback."""+ try:+ import ad_client+ if not ad_client.available():+ return None+ r = ad_client.call("process_list", {"name": name_substr}, timeout=10)+ if not isinstance(r, dict):+ return None+ rows = r.get("processes")+ if rows is None and isinstance(r.get("output"), str):+ import json as _j+ try:+ rows = (_j.loads(r["output"]) or {}).get("processes")+ except Exception:+ rows = None+ return rows if isinstance(rows, list) else None+ except Exception:+ return None+++def _ab_process_kill(pid, why):+ """Kill one PID through ab's process_kill verb. True on confirmed kill, False when the+ verb was unavailable or refused (caller falls back to a hidden taskkill)."""+ try:+ import ad_client+ if not ad_client.available():+ return False+ r = ad_client.call("process_kill", {"pid": int(pid), "tree": True, "reason": why},+ timeout=15)+ return bool(isinstance(r, dict) and (r.get("success") or r.get("ok")))+ except Exception:+ return False++ def _fusion_pids() -> list: """PIDs of every surviving Fusion-family process (tasklist CSV, empty on error)."""+ rows = _ab_process_rows("Fusion")+ if rows is not None:+ return [int(r.get("pid")) for r in rows+ if r.get("pid") and str(r.get("name") or "") in FUSION_PROCESS_NAMES] pids = [] for name in FUSION_PROCESS_NAMES: try:@@ -103,6 +152,15 @@ def _force_kill_fusion() -> int: tried - never with an instruction to open Task Manager. """ killed = 0+ # Verb-first: enumerate + kill through ab, so the kills are attributed in ab's+ # activity log. Fall through to taskkill for anything the verb path did not confirm.+ _verb_killed = set()+ for r in (_ab_process_rows("Fusion") or []):+ pid = r.get("pid")+ if pid and str(r.get("name") or "") in FUSION_PROCESS_NAMES:+ if _ab_process_kill(pid, "close Fusion: force-kill surviving %s" % r.get("name")):+ killed += 1+ _verb_killed.add(int(pid)) for name in FUSION_PROCESS_NAMES: try: result = subprocess.run(
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.340"+DASH_VERSION="1.9.341" 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.340",+ "version": "1.9.341", "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.340",+ "version": "1.9.341", "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]"- }-}+ ]+}