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 170 file(s)
15 files changed
+119−36
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.11.11+1.11.12
bridge.json+5−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.11.11",+ "version": "1.11.12", "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.",@@ -27,6 +27,7 @@ "fusion_addin_status", "fusion_alerts", "fusion_apply_fusion_update",+ "fusion_apply_pours", "fusion_aps_browse", "fusion_aps_create_folder", "fusion_aps_download",@@ -126,6 +127,7 @@ "fusion_mcp_resources", "fusion_mcp_status", "fusion_mcp_tools",+ "fusion_move_parts", "fusion_narrate", "fusion_new_electronics_from_eagle", "fusion_notify_owner",@@ -138,8 +140,10 @@ "fusion_open_lbr", "fusion_open_schematic", "fusion_physical_properties",+ "fusion_place_vias", "fusion_plan_library_import", "fusion_plan_route",+ "fusion_pour_state", "fusion_preflight_lbr", "fusion_prefs_close", "fusion_prefs_open",
contracts/describe.json+84−1@@ -1,6 +1,6 @@ { "contract": "fusion-bridge verb catalog",- "frozen": "2026-10-04",+ "frozen": "2026-10-05", "note": "The Python bridge and the Rust bridge must both serve this catalog byte-for-byte in meaning: same names, same args, same result shapes, same timeouts. Regenerate ONLY from describe.py and diff in CI.", "source": "describe.py", "verbs": [@@ -98,6 +98,29 @@ "summary": "Readiness EXECUTOR (Rust port, Phase 3): click Fusion's update offer IN THE BACKGROUND when auto-update is on (sticky pref; fusion_set_auto_update). Never the exit-class buttons while a host op is active. Answers {applied, button, why}.", "timeoutSeconds": 60 },+ {+ "example": {},+ "input": {+ "boardId": "required",+ "expectDocument": "required exact board name",+ "expectedRevision": "required",+ "pours": "required [{net, layer, points:[[x,y]...], isolate (mm, default 0.25), rank (default 1), width (mm, default 0.25), thermals (default true), orphans (default false)}]"+ },+ "longRunning": false,+ "name": "fusion_apply_pours",+ "output": {+ "added": "[net]",+ "filledAreaByLayerMm2": "{layer:mm2}",+ "kept": "[net]",+ "pours": "[{net,layer,outline,filledMm2,islands,filled}]",+ "revision": "str",+ "success": "bool",+ "verified": "bool"+ },+ "statusVerb": "fusion_routing_state",+ "summary": "Draw copper pours from a plan: POLYGON per pour (net, layer 1 or 16, outline points, isolate, rank 1..6, width, thermals, orphans), skipping any already on the board with the same net, layer and outline, then RATSNEST fills them. The reply carries Fusion's own fill per pour (filledMm2, islands) and per layer. Removing a pour is not done here.",+ "timeoutSeconds": 120+ }, { "example": {}, "input": {@@ -2021,6 +2044,27 @@ "summary": "Full tool list from Autodesk's Fusion MCP server INCLUDING input schemas, so you can construct a valid fusion_mcp_call. These are Autodesk's tools, not this bridge's.", "timeoutSeconds": 45 },+ {+ "example": {},+ "input": {+ "boardId": "required from routing_state",+ "expectDocument": "required exact board name",+ "expectedRevision": "required from routing_state",+ "parts": "required [{ref, x, y, rotation}] rotation in degrees or an EAGLE rotation like R90 / MR180"+ },+ "longRunning": false,+ "name": "fusion_move_parts",+ "output": {+ "mismatched": "[{ref,want,got}]",+ "moved": "[ref]",+ "revision": "str",+ "success": "bool",+ "verified": "bool"+ },+ "statusVerb": "fusion_routing_state",+ "summary": "Place parts on the live board: one MOVE (absolute, mm) and ROTATE per part, each its own undo step, then a fresh export reads every element back. The layout half of the AI flow's Fusion lane (adom-aiflow place land). Coordinates are Fusion's (y up).",+ "timeoutSeconds": 120+ }, { "example": {}, "input": {@@ -2246,6 +2290,26 @@ "summary": "Per-component physical properties (volume cm3, mass kg, density, area, center of mass) for the active design. Reads them directly so BOM/costing tools no longer have to run getPhysicalProperties inside a modeling script. Pass names to scope to specific components.", "timeoutSeconds": 120 },+ {+ "example": {},+ "input": {+ "boardId": "required",+ "expectDocument": "required exact board name",+ "expectedRevision": "required",+ "vias": "required [{net, x, y, diameter (mm, default 0.6), drill (mm, default 0.3)}]"+ },+ "longRunning": false,+ "name": "fusion_place_vias",+ "output": {+ "missing": "list",+ "revision": "str",+ "success": "bool",+ "verified": "bool"+ },+ "statusVerb": "fusion_routing_state",+ "summary": "Add vias at exact points (stitching, thermal, escape): VIA per via with its drill and diameter, then read back.",+ "timeoutSeconds": 120+ }, { "example": { "parts": [@@ -2305,6 +2369,25 @@ "summary": "Calculate a read-only, bounded orthogonal A* path between two pads on a live two-layer board. Uses existing copper and the shared conservative clearance model. Returns explicit route args for route_net; native DRC is still required after mutation.", "timeoutSeconds": 60 },+ {+ "example": {},+ "input": {+ "boardId": "optional",+ "expectDocument": "required exact board name"+ },+ "longRunning": false,+ "name": "fusion_pour_state",+ "output": {+ "filledAreaByLayerMm2": "{layer:mm2}",+ "filledAreaByNetMm2": "{net:mm2}",+ "pours": "list",+ "revision": "str",+ "unfilled": "[net]"+ },+ "statusVerb": "fusion_routing_state",+ "summary": "Read every pour on the live board with Fusion's own computed fill: filled area per pour, per layer and per net, and which pours were never filled (no RATSNEST since they were drawn). The measure step of the AI flow's Fusion lane.",+ "timeoutSeconds": 60+ }, { "example": { "libraryPath": "C:/Temp/PART.lbr"
dashboard/Cargo.lock+1−1@@ -4,7 +4,7 @@ version = 4 [[package]] name = "fusion-dashboard"-version = "1.11.11"+version = "1.11.12" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.11.11"+version = "1.11.12" edition = "2021" [dependencies]
describe.py+12@@ -22,6 +22,18 @@ _T = [ ("fusion_route_net", "Create explicit net copper in the live Fusion canvas. Conservative clearance preflight, board/revision guards and exact copper readback. One or several trace segments per call; no general autorouting. Use routing_drc for native DRC.", {"expectDocument":"required exact board name","boardId":"required from routing_state","expectedRevision":"required from routing_state","net":"required exact name","points":"waypoints [x,y] or {pad} or {x,y,layer}","paths":"optional list of paths instead of points","width":"mm, default .25","layer":"1 or 16, default 1","viaDiameter":"mm, default .6","viaDrill":"mm, default .3","clearance":"mm lower bound, default .2","dryRun":"optional bool"}, {"success":"bool","mutated":"bool","verified":"bool","revision":"str","plan":"obj","remainingNets":"list"},120,"fusion_routing_state",False,{}),+ ("fusion_move_parts", "Place parts on the live board: one MOVE (absolute, mm) and ROTATE per part, each its own undo step, then a fresh export reads every element back. The layout half of the AI flow's Fusion lane (adom-aiflow place land). Coordinates are Fusion's (y up).",+ {"expectDocument":"required exact board name","boardId":"required from routing_state","expectedRevision":"required from routing_state","parts":"required [{ref, x, y, rotation}] rotation in degrees or an EAGLE rotation like R90 / MR180"},+ {"success":"bool","verified":"bool","moved":"[ref]","mismatched":"[{ref,want,got}]","revision":"str"},120,"fusion_routing_state",False,{}),+ ("fusion_place_vias", "Add vias at exact points (stitching, thermal, escape): VIA per via with its drill and diameter, then read back.",+ {"expectDocument":"required exact board name","boardId":"required","expectedRevision":"required","vias":"required [{net, x, y, diameter (mm, default 0.6), drill (mm, default 0.3)}]"},+ {"success":"bool","verified":"bool","missing":"list","revision":"str"},120,"fusion_routing_state",False,{}),+ ("fusion_apply_pours", "Draw copper pours from a plan: POLYGON per pour (net, layer 1 or 16, outline points, isolate, rank 1..6, width, thermals, orphans), skipping any already on the board with the same net, layer and outline, then RATSNEST fills them. The reply carries Fusion's own fill per pour (filledMm2, islands) and per layer. Removing a pour is not done here.",+ {"expectDocument":"required exact board name","boardId":"required","expectedRevision":"required","pours":"required [{net, layer, points:[[x,y]...], isolate (mm, default 0.25), rank (default 1), width (mm, default 0.25), thermals (default true), orphans (default false)}]"},+ {"success":"bool","verified":"bool","added":"[net]","kept":"[net]","pours":"[{net,layer,outline,filledMm2,islands,filled}]","filledAreaByLayerMm2":"{layer:mm2}","revision":"str"},120,"fusion_routing_state",False,{}),+ ("fusion_pour_state", "Read every pour on the live board with Fusion's own computed fill: filled area per pour, per layer and per net, and which pours were never filled (no RATSNEST since they were drawn). The measure step of the AI flow's Fusion lane.",+ {"expectDocument":"required exact board name","boardId":"optional"},+ {"pours":"list","filledAreaByLayerMm2":"{layer:mm2}","filledAreaByNetMm2":"{net:mm2}","unfilled":"[net]","revision":"str"},60,"fusion_routing_state",False,{}), ("fusion_routing_drc", "Run fresh native Autodesk DRC and read UL_BOARD.errors, including approved errors, warnings and airwires. Does not accept or clear any violation.", {"expectDocument":"required exact board name","boardId":"required","expectedRevision":"required"}, {"drc":"{checked,clean,errorCount,warningCount,airwireCount,errors}","revision":"str"},120,"fusion_routing_state",False,{}),
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.11.11"+DASH_VERSION="1.11.12" 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.11.11",+ "version": "1.11.12", "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+10−26@@ -1,6 +1,6 @@ { "slug": "fusion-bridge",- "version": "1.11.11",+ "version": "1.11.12", "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",@@ -76,6 +80,9 @@ "add a 3d model to a component", "make a fusion 360 library with 3d", "set up the fusion bridge",+ "fusion dashboard",+ "fusion status page",+ "open the fusion launch page", "aps not configured", "share aps client id across my org", "set up fusion cloud search for my company",@@ -108,28 +115,5 @@ "label": "Part volume", "prompt": "How much material does each printed part in this assembly use?" }- ],- "agent_permissions": {- "allow_bash": [],- "allow": [- "adom-desktop-fusion-bridge read-only verbs (via the adom-bridge CLI): fusion_readiness, fusion_get_app_state, fusion_addin_status, fusion_check_dialogs - state queries; they change nothing.",- "adom-desktop-fusion-bridge read-only verbs: fusion_alerts (reads Fusion's own log for toasts), fusion_document_info, fusion_window_info, fusion_board_info, fusion_library_parts - inspect only.",- "adom-desktop-fusion-bridge read-only verbs: fusion_prove_result (a persisted run report), fusion_plan_library_import (report-only preflight), fusion_list_cloud_projects, fusion_describe.",- "adom-desktop-fusion-bridge evidence captures fusion_screenshot_fusion and fusion_capture_library_views read pixels and write local image files only."- ],- "soft_deny": [- "adom-desktop-fusion-bridge lifecycle verbs fusion_stop, fusion_kill, fusion_restart_for_addin close or kill the Fusion application - keep the human prompt.",- "adom-desktop-fusion-bridge fusion_install_fusion silently installs Autodesk Fusion on the user's machine.",- "adom-desktop-fusion-bridge fusion_cleanup_cloud_files deletes files from the user's Autodesk cloud hub.",- "adom-desktop-fusion-bridge managed-library verbs (fusion_managed_library_push/pull/create/link/unlink) publish to or overwrite the user's library.io libraries."- ],- "environment": [- "adom-desktop-fusion-bridge is the Fusion 360 bridge for Adom Bridge; its fusion_* verbs are invoked through the adom-bridge CLI and drive Autodesk Fusion on the user's own desktop.",- "adom-desktop-fusion-bridge desktop actions also pass Adom Bridge's own human-approval gate (caller identity, per-call reason, Activity Log) - a container approval is the first of two gates."- ]- },- "author": {- "name": "John Lauer",- "email": "[email protected]"- }-}+ ]+}
rust/Cargo.toml+1−1@@ -4,7 +4,7 @@ members = ["core", "lbr", "os-win", "os-mac", "bridge"] # One version for the whole bridge, in lockstep with BRIDGE_VERSION (tools/release.sh bumps both). [workspace.package]-version = "1.11.11"+version = "1.11.12" edition = "2021" license = "MIT"
rust/bridge/src/routing.rs+2−2@@ -2,7 +2,7 @@ //! script transport. Embedding the implementation avoids replacing a loaded add-in. use serde_json::{json, Value}; const SOURCE: &str = concat!(include_str!("routing_script.py"), "\n", include_str!("routing_planner.py"));-pub fn owns(cmd: &str) -> bool { matches!(cmd, "routing_state" | "route_net" | "routing_drc" | "plan_route") }+pub fn owns(cmd: &str) -> bool { matches!(cmd, "routing_state" | "route_net" | "routing_drc" | "plan_route" | "move_parts" | "place_vias" | "apply_pours" | "pour_state") } pub fn call(cmd: &str, args: &Value, pass: &dyn Fn(&str, Value) -> Value) -> Value { let script = format!("import json\nexec({})\nresult=handle(app,json.loads({}),{})\n", serde_json::to_string(SOURCE).unwrap(),@@ -19,7 +19,7 @@ pub fn call(cmd: &str, args: &Value, pass: &dyn Fn(&str, Value) -> Value) -> Val if !r["fusionUnexpectedCondition"].is_null() { out["_hint"] = json!(format!("{} Fusion Electronics raised its 'unexpected condition' assertion after this edit and the bridge clicked OK; the copper read-back above is the truth. Checkpoint now (fusion_export_eagle_source).", out["_hint"].as_str().unwrap_or("")).trim().to_string()); } out }- None => json!({"success":false,"errorCode":"invalid_routing_response","error":"Fusion did not return a structured routing result", "mutationMayHaveOccurred":cmd=="route_net"}),+ None => json!({"success":false,"errorCode":"invalid_routing_response","error":"Fusion did not return a structured routing result", "mutationMayHaveOccurred":matches!(cmd, "route_net" | "move_parts" | "place_vias" | "apply_pours")}), } }
tools/check-naming.shtools/check-portable.shtools/promote.shtools/release.sh