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.5.0: ship fusion_describe (self-describe 41 verbs for AD Verbs tab)
6 files changed
+150−8
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.4.9+1.5.0
adom-bridge-fusion-manifest.json+6−6@@ -1,9 +1,9 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.4.9",- "url": "https://wiki.adom.inc/api/v1/pages/adom-desktop-fusion-bridge/files/adom-bridge-fusion-v1.4.9.zip",- "sha256": "201fd9afecc8d1f9afb10f4d017004c57896959db23dc61d85b5d65a1f55c6e9",- "size": 187168,- "released_at": "2026-06-26T00:00:00Z"-}+ "version": "1.5.0",+ "url": "https://wiki.adom.inc/api/v1/pages/adom-desktop-fusion-bridge/files/adom-bridge-fusion-v1.5.0.zip",+ "sha256": "77e8bd788215264c30ad346a2011640b7f6597a623669a743ede8b58853d8f84",+ "size": 191002,+ "released_at": "2026-06-27T14:56:41Z"+}
adom-bridge-fusion-v1.5.0.zipadded⋯ 1 unchanged line ⋯
bridge.json+2−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.4.9",+ "version": "1.5.0", "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://github.com/adom-inc/adom-desktop-fusion-bridge", "author": "Adom Inc.",@@ -19,6 +19,7 @@ "fusion_" ], "verbs": [+ "fusion_describe", "fusion_start", "fusion_close_window", "fusion_apply_instapcb_rules",
describe.pyadded+139@@ -0,0 +1,139 @@+"""fusion_describe — self-describe the bridge's verb surface for Adom Desktop.++AD v1.9.23+ added a Verbs tab to each bridge's detail pop-out (lists verbs,+expands each for its I/O + timeout + status schema, with an inline runner). AD+does NOT hardcode a cloud-owned bridge's verbs, so the bridge must self-describe.+AD POSTs {command:"describe", args:{}} and caches the result at ~/.adom/bridge-verbs/.++Each entry: name, summary, input, output, timeoutSeconds, statusVerb, longRunning,+example. Keep this in step with the actual handlers as verbs are added.+"""++_STATUS = "fusion_get_app_state"++# Compact source table: name -> (summary, input, output, timeout, statusVerb, longRunning, example)+_T = [+ # ── Lifecycle ──────────────────────────────────────────────────────────+ ("fusion_start", "Launch Fusion 360 (if needed) and wait for the AdomBridge add-in to be ready.",+ {}, {"success": "bool", "addinReady": "bool", "status": "str"}, 150, None, True, {}),+ ("fusion_close_window", "Close the active Fusion document/window.",+ {"name": "optional str (default: active)"}, {"success": "bool"}, 30, None, False, {}),+ ("fusion_get_app_state", "Current app state: running, active document, workspace, isElectronics. The poll/status verb for long-running calls.",+ {}, {"activeDocument": "str", "activeWorkspace": "str", "isElectronics": "bool"}, 20, None, False, {}),++ # ── Cloud search & files (APS) ─────────────────────────────────────────+ ("fusion_aps_status", "APS state: configured, signed in, token live, never-charge meter.",+ {}, {"configured": "bool", "signedIn": "bool", "neverCharge": "bool"}, 20, None, False, {}),+ ("fusion_aps_set_client_id", "Set the org's APS (PKCE) client id used for cloud search.",+ {"clientId": "required str"}, {"success": "bool"}, 20, None, False, {"clientId": "D5nn...EPAq"}),+ ("fusion_aps_signin", "Open the Autodesk sign-in in the browser; a background listener captures the token.",+ {}, {"success": "bool", "authUrl": "str"}, 30, "fusion_aps_status", False, {}),+ ("fusion_aps_search", "Server-indexed search across the whole team hub (seconds, not a 30-min folder walk).",+ {"query": "required str", "limit": "optional int (default 50)", "hubId": "optional str", "projectId": "optional str"},+ {"count": "int", "results": "[{name,id,projectName,lastModified}]"}, 60, None, False, {"query": "Molecule", "limit": 10}),+ ("fusion_aps_open", "Find a cloud design by name and open it in Fusion (any folder depth). Fire-and-poll.",+ {"query": "required str", "wait": "optional bool"}, {"opening": "bool", "match": "{name}", "statusVerb": "str"}, 60, _STATUS, True, {"query": "BME690 Molecule"}),+ ("fusion_open_by_urn", "Open a cloud design directly by its APS/Fusion URN.",+ {"urn": "required str"}, {"success": "bool", "output": "str"}, 240, _STATUS, True, {"urn": "urn:adsk.wipprod:fs.file:vf.xxxx?version=1"}),+ ("fusion_aps_browse", "Browse hubs/projects/folders without launching Fusion.",+ {"hubId": "optional str", "projectId": "optional str", "folderId": "optional str"}, {"items": "[{name,type,id}]"}, 60, None, False, {}),+ ("fusion_aps_recent", "Most-recently-modified designs across the team hub, newest first.",+ {"limit": "optional int (default 25)"}, {"count": "int", "results": "[...]"}, 60, None, False, {"limit": 25}),+ ("fusion_aps_file_info", "Version history + who/when/size for a file (tip + recent versions).",+ {"query": "str OR projectId+urn"}, {"versionCount": "int", "tip": "{...}", "versions": "[...]"}, 40, None, False, {"query": "BME690 Molecule"}),+ ("fusion_aps_versions", "Full (uncapped) version history of a file.",+ {"query": "str OR projectId+urn"}, {"versionCount": "int", "versions": "[...]"}, 40, None, False, {"query": "BME690 Molecule"}),+ ("fusion_aps_download", "Download a cloud file to the local machine (no Fusion needed).",+ {"query": "str OR projectId+urn", "saveDir": "optional str", "fileName": "optional str"}, {"path": "str", "bytes": "int"}, 90, None, False, {"query": "BME690 Molecule"}),+ ("fusion_aps_upload", "Upload a local file as a NEW cloud file (free Data Management; needs data:write scope).",+ {"projectId": "required str", "folderId": "required str", "localPath": "required str", "fileName": "optional str"},+ {"itemUrn": "str", "bytes": "int"}, 90, None, False, {"projectId": "a.YnVz...", "folderId": "urn:...", "localPath": "C:/x.f3d"}),+ ("fusion_aps_create_folder", "Create a subfolder in a project (free; needs data:create scope).",+ {"projectId": "required str", "parentFolderId": "required str", "name": "required str"}, {"folderId": "str"}, 50, None, False, {"projectId": "a.YnVz...", "parentFolderId": "urn:...", "name": "new-folder"}),+ ("fusion_aps_get", "Raw authenticated GET against a free APS path (dev/verify helper).",+ {"path": "required str"}, {"status": "int", "body": "obj"}, 40, None, False, {"path": "/project/v1/hubs"}),+ ("fusion_walk_cloud_tree", "Slow in-app folder-by-folder cloud walk (legacy; prefer fusion_aps_search).",+ {}, {"tree": "obj"}, 600, _STATUS, True, {}),+ ("fusion_search_cloud_files", "Slow in-app cloud search (legacy; prefer fusion_aps_search).",+ {"query": "required str"}, {"results": "[...]"}, 180, _STATUS, True, {"query": "Molecule"}),++ # ── Electronics ────────────────────────────────────────────────────────+ ("fusion_show_2d_board", "Switch to the 2D PCB Editor (board layout) view.",+ {}, {"activeWorkspace": "str"}, 70, None, True, {}),+ ("fusion_show_3d_board", "Switch to the 3D PCB view (populated board).",+ {}, {"activeWorkspace": "str", "screenshots": "[...]"}, 70, None, True, {}),+ ("fusion_open_by_urn_note", "(see fusion_open_by_urn)", {}, {}, 30, None, False, {}),+ ("fusion_board_info", "Raw board geometry XML (copper/arcs/lines) for the open board.",+ {}, {"rawXml": "str"}, 60, None, False, {}),+ ("fusion_open_lbr", "Import an EAGLE .lbr library into Fusion's Electronics library editor.",+ {"filePath": "required str (.lbr on the machine)", "symbolName": "optional str", "verify": "optional bool"},+ {"success": "bool", "postOpenScreenshot": "{savedTo}"}, 50, None, False, {"filePath": "C:/libs/nRF52840-CKAA.lbr"}),+ ("fusion_run_modeling_script", "Run an adsk.fusion modeling script in the live session (sketches/extrudes/params). Free programmatic CAD.",+ {"script": "required str (Python using adsk; globals: adsk, app, ui, design)"},+ {"result": "obj", "documentName": "str"}, 180, None, True, {"script": "import adsk.fusion\n# ...build geometry..."}),++ # ── CAD & 3D export ────────────────────────────────────────────────────+ ("fusion_export_step", "Export the active design to STEP (needs the 3D view).",+ {"outputPath": "required str"}, {"format": "str", "fileSizeKB": "int"}, 300, None, True, {"outputPath": "C:/out/board.step"}),+ ("fusion_export_iges", "Export to IGES.", {"outputPath": "required str"}, {"format": "str"}, 300, None, True, {"outputPath": "C:/out/board.iges"}),+ ("fusion_export_stl", "Export to STL mesh.", {"outputPath": "required str"}, {"format": "str"}, 300, None, True, {"outputPath": "C:/out/part.stl"}),+ ("fusion_export_3mf", "Export to 3MF mesh.", {"outputPath": "required str"}, {"format": "str"}, 300, None, True, {"outputPath": "C:/out/part.3mf"}),+ ("fusion_export_usdz", "Export to USDZ (AR / Hydrogen 3D).", {"outputPath": "required str"}, {"format": "str"}, 300, None, True, {"outputPath": "C:/out/part.usdz"}),+ ("fusion_export_obj", "Export to OBJ mesh.", {"outputPath": "required str"}, {"format": "str"}, 300, None, True, {"outputPath": "C:/out/part.obj"}),+ ("fusion_export_dxf", "Export 2D DXF.", {"outputPath": "required str"}, {"format": "str"}, 120, None, True, {"outputPath": "C:/out/sketch.dxf"}),+ ("fusion_export_dwg", "Export 2D DWG.", {"outputPath": "required str"}, {"format": "str"}, 120, None, True, {"outputPath": "C:/out/sketch.dwg"}),++ # ── Manufacturing ──────────────────────────────────────────────────────+ ("fusion_export_gerbers", "Run the bundled JLCPCB CAM job (auto 2/4-layer) to produce the gerber + drill ZIP.",+ {"outputDir": "optional str (default C:/tmp/adom-gerbers)"}, {"fileCount": "int", "files": "[{name,size}]", "zipPath": "str"}, 180, None, True, {"outputDir": "C:/tmp/jlc"}),+ ("fusion_export_bom", "Export a Bill of Materials CSV (grouped by value+package) from the open board.",+ {"outputPath": "optional str", "grouped": "optional bool (default true)"}, {"componentCount": "int", "grouped": "bool"}, 60, None, False, {"outputPath": "C:/tmp/bom.csv"}),+ ("fusion_export_cpl", "Export the Component Placement List (pick-and-place) CSV.",+ {"outputPath": "optional str", "side": "optional str (all|top|bottom)"}, {"topCount": "int", "bottomCount": "int"}, 60, None, False, {"outputPath": "C:/tmp/cpl.csv"}),+ ("fusion_set_design_rules", "Load manufacturing-grade DRC into the open board (JLCPCB 2/4-layer profiles).",+ {"action": "optional str (apply|export|show)", "layers": "optional str (auto|2|4)"}, {"activeWorkspace": "str", "description": "str"}, 90, None, False, {"action": "apply", "layers": "2"}),+ ("fusion_load_design_rules", "Load a bundled .edru design-rule set into the board.",+ {"layers": "optional str (auto|2|4)"}, {"success": "bool"}, 90, None, False, {}),+ ("fusion_detect_layers", "Detect 2-layer vs 4-layer board (ULP + CAM comparison).",+ {}, {"layerCount": "int", "copperLayers": "[int]"}, 90, None, False, {}),+ ("fusion_dismiss_blocking_dialogs", "Detect + classify (and optionally dismiss) blocking Fusion dialogs.",+ {}, {"dialogs": "[...]"}, 30, None, False, {}),+ ("fusion_electron_run", "Run an EAGLE/Electron command in the open board (the generic extension point, e.g. RUN <ulp>).",+ {"command": "required str"}, {"success": "bool", "output": "str"}, 90, None, False, {"command": "set confirm yes;RUN 'C:/tmp/jlcpcb_smta_exporter.ulp' 'C:/tmp/jlc'"}),++ # ── 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, {}),+]+++def _verbs():+ out = []+ for name, summary, inp, outp, timeout, status, longr, ex in _T:+ if name.endswith("_note"):+ continue+ out.append({+ "name": name,+ "summary": summary,+ "input": inp,+ "output": outp,+ "timeoutSeconds": timeout,+ "statusVerb": status,+ "longRunning": longr,+ "example": ex,+ })+ return out+++def handle_describe(args: dict) -> dict:+ verbs = _verbs()+ # `verbs` at the TOP LEVEL is what AD's Verbs tab reads from the raw /command+ # response. AD's relay strips non-standard top-level fields on the CLI proxy+ # path, so we ALSO nest under `data` (which the relay forwards) for CLI/debug.+ return {+ "success": True,+ "bridge": "fusion360",+ "verbCount": len(verbs),+ "verbs": verbs,+ "data": {"bridge": "fusion360", "verbCount": len(verbs), "verbs": verbs},+ }
server.py+2@@ -36,6 +36,7 @@ from handlers.fusion_ui import ( from handlers.dialog_classify import classify_blocking_dialogs from install_addin import install as install_addin import aps+import describe DEFAULT_PORT = 8773 ADDIN_PORT = 8774@@ -462,6 +463,7 @@ def _handle_relocate_recovery(fusion_info: dict, args: dict) -> dict: COMMAND_HANDLERS = {+ "describe": lambda fi, args: describe.handle_describe(args), "open_design": handle_open_design, "close": handle_close_fusion, "launch": _handle_launch,