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.6.61: fusion_notify_owner SELF-DELIVERS via adom-desktop --target all when the in-process ad_client is unavailable (headless VM) - the toast actually leaves the box and reaches the user's real machine, no AI relay to forget
4 files changed
+66−9
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.6.60+1.6.61
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.6.60",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.60/adom-bridge-fusion-v1.6.60.zip",- "sha256": "03b4b9b136925eb216517e1051d0fc4fb378fdc5a32c0db7820657fe3d22599f",- "size": 355794,+ "version": "1.6.61",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.61/adom-bridge-fusion-v1.6.61.zip",+ "sha256": "a867c2099758e3b2363c65c07ede5acdd9a837d39939d96cd74fe0c5306e6cc2",+ "size": 357063, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.6.60",+ "version": "1.6.61", "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/adom-desktop-fusion-bridge", "author": "Adom Inc.",
server.py+60−3@@ -198,6 +198,47 @@ def _resolve_seat_dialog(max_clicks: int = 8) -> dict: return {"sawDialog": saw, "clicks": clicks, "verified": verified} +def _find_adom_desktop_cli() -> str | None:+ """Locate the adom-desktop CLI/exe so the bridge can drive AD's relay directly (used when the+ in-process ad_client is unavailable, e.g. on a headless VM). Checks the per-user Windows install+ dir, then PATH, then the common Linux/dev locations. Returns a path or None."""+ import shutil+ la = os.environ.get("LOCALAPPDATA", "")+ candidates = [+ os.path.join(la, "Adom Desktop", "adom-desktop.exe") if la else None,+ os.path.join(la, "Programs", "Adom Desktop", "adom-desktop.exe") if la else None,+ ]+ for c in candidates:+ if c and os.path.exists(c):+ return c+ return shutil.which("adom-desktop") or shutil.which("adom-desktop.exe")+++def _cli_notify_all(title: str, body: str, level: str) -> dict:+ """Deliver a toast to EVERY connected desktop via `adom-desktop --target all notify_user`. This is+ the reliable cross-AD path when the bridge's in-process ad_client is down (headless VM): the CLI+ joins the relay itself and `--target all` reaches the user's real machine (not just this VM).+ Best-effort + never raises. Returns {delivered, targets, error}. (John 2026-07-14: the notify MUST+ actually leave the box - a returned-but-unsent payload is the bug, not a feature.)"""+ import subprocess as _sp+ exe = _find_adom_desktop_cli()+ if not exe:+ return {"delivered": False, "error": "adom-desktop CLI not found"}+ payload = json.dumps({"title": title, "body": body, "level": level})+ try:+ r = _sp.run([exe, "--target", "all", "notify_user", payload],+ capture_output=True, text=True, timeout=30,+ creationflags=getattr(_sp, "CREATE_NO_WINDOW", 0))+ out = (r.stdout or "") + (r.stderr or "")+ # AD's CLI returns {status:'ok', action:'displayed'} per target; treat a zero exit or a+ # 'displayed'/'ok' in the output as delivered. The Windows exe may print nothing yet still+ # deliver, so a clean exit code is accepted too.+ delivered = (r.returncode == 0) or ("displayed" in out) or ('"status": "ok"' in out) or ("status':'ok" in out)+ return {"delivered": bool(delivered), "targets": "all", "error": None if delivered else out[:200]}+ except Exception as e:+ return {"delivered": False, "error": str(e)[:200]}++ def _handle_notify_owner(args: dict) -> dict: """LAST-RESORT: toast the user's MAIN desktop (cross-AD) to ask for a human step. @@ -210,12 +251,28 @@ def _handle_notify_owner(args: dict) -> dict: body = args.get("body") or args.get("message") or "A human step is required to continue." level = args.get("level") or "warning" if not ad_client.available():+ # ad_client is the bridge's IN-PROCESS AD API - it is routinely UNAVAILABLE when this bridge+ # runs on an unattended Hyper-V VM (found live 2026-07-14: the toast silently never reached the+ # user, and the AI "forgot" to relay it - exactly the failure John demanded we engineer out).+ # SELF-DELIVER via the adom-desktop CLI, which connects to the relay independently: `--target+ # all` fans the toast out to EVERY connected desktop (the VM + the user's real machines), so it+ # reliably lands on the computer the user is actually AT. No AI relay step to forget.+ cli = _cli_notify_all(title, body, level)+ if cli.get("delivered"):+ return {"success": True, "via": "cli:--target all", "targets": cli.get("targets"),+ "_hint": ("Toast fanned out to ALL connected desktops (the user's main machine "+ "included) via the adom-desktop CLI, because the bridge's in-process AD "+ "API was unavailable (this VM). WAIT and poll fusion_readiness; do not "+ "re-toast within a few minutes. Only notify when the box is ACTUALLY "+ "ready for the user to act - do not toast for a step you can do yourself.")}+ # CLI fallback also failed - return the payload so the AI can relay as the true last resort. return { "success": False,- "error": "AD direct API unavailable - cannot send a toast from the bridge.",+ "error": "AD in-process API unavailable AND the adom-desktop CLI fallback failed: "+ + str(cli.get("error"))[:200], "notifyUser": {"title": title, "body": body, "level": level},- "_hint": ("Relay the included notifyUser payload yourself via AD's notify_user verb "- "(add a cross-AD `target` if this bridge runs on a VM and the user is elsewhere)."),+ "_hint": ("Relay the notifyUser payload yourself: `adom-desktop --target all notify_user "+ "{title, body, level}` (or `--target <the user's host>`)."), } peers = [] try: