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 15 file(s)
15 files changed
+88−46
BRIDGE_VERSION+1−1CHANGELOG.md+1−1addin/AdomBridge/AdomBridge.manifest+1−1addin/AdomBridge/commands/app_state.py+8−16addin/AdomBridge/commands/document_info.py+4−14addin/AdomBridge/commands/ui_state.py+37bridge.json+1−1dashboard/Cargo.lock+1−1dashboard/Cargo.toml+1−1install.sh+1−1package.json+1−1page.json+2−2rust/Cargo.lock+5−5rust/Cargo.toml+1−1tests/test_polled_reads_run_no_commands.py+23BRIDGE_VERSION+1−1@@ -1 +1 @@-1.13.37+1.13.38
CHANGELOG.md+1−1@@ -3,7 +3,7 @@ All notable changes to the Adom Bridge Fusion 360 bridge, newest first. Canonical: https://wiki.adom.inc/adom/fusion-bridge ยท GitHub backup: `adom-inc/fusion-bridge` -## 1.13.38 (unreleased)+## 1.13.38 (2026-10-30) - `fusion_export_manufacturing` (the standard's export_manufacturing): silkscreen prep, Gerbers and drill (JLCPCB CAM job), pick and place with JLCPCB's CPL, the BOM order files, and ODB++ / IPC-2581 with `formats`, in one folder.
addin/AdomBridge/AdomBridge.manifest+1−1@@ -6,7 +6,7 @@ "description": { "": "Adom Desktop Conduit bridge add-in. Exposes a localhost HTTP API for external automation of Fusion 360." },- "version": "1.0.28",+ "version": "1.0.29", "runOnStartup": true, "supportedOS": "windows" }
addin/AdomBridge/commands/app_state.py+8−16@@ -9,22 +9,13 @@ def handle_get_app_state(app: adsk.core.Application, args: dict) -> dict: Returns information about every open document/tab, which one is active, the current workspace, and product type. """- # Active workspace- active_workspace = "unknown"- try:- ws = app.executeTextCommand("DebugCommands.ActiveWorkspace")- if ws:- active_workspace = ws.strip()- except Exception:- pass-- # Check if Electronics editor is active- is_electronics = False- try:- app.executeTextCommand("Electron.run DISPLAY")- is_electronics = True- except Exception:- pass+ # The workspace and whether an electronics editor is in front, read from the API: this read is+ # polled (readiness, the dashboard, waits), and a text command here (Electron.run DISPLAY) ended+ # the user's MOVE in progress every time (issue #131)+ from .ui_state import active_workspace as _ws, is_electronics as _is_el, user_command_active as _busy+ active_workspace = _ws(app)+ is_electronics = _is_el(app)+ user_command = _busy(app) # Active document active_doc_name = None@@ -104,6 +95,7 @@ def handle_get_app_state(app: adsk.core.Application, args: dict) -> dict: "activeDocument": active_doc_name, "productType": product_type, "isElectronics": is_electronics,+ "userCommandActive": user_command, "documents": documents, }, "_hint": hint,
addin/AdomBridge/commands/document_info.py+4−14@@ -36,13 +36,8 @@ def handle_document_info(app: adsk.core.Application, args: dict) -> dict: and which one is focused, without expensive cloud searches. """ # Active workspace- active_workspace = "unknown"- try:- ws = app.executeTextCommand("DebugCommands.ActiveWorkspace")- if ws:- active_workspace = ws.strip()- except Exception:- pass+ from .ui_state import active_workspace as _ws+ active_workspace = _ws(app) # API only: this read is polled (issue #131) # Active document name (for isActive flag) active_doc_name = None@@ -312,13 +307,8 @@ def handle_activate_document(app: adsk.core.Application, args: dict) -> dict: } # Query workspace after activation- new_workspace = "unknown"- try:- ws = app.executeTextCommand("DebugCommands.ActiveWorkspace")- if ws:- new_workspace = ws.strip()- except Exception:- pass+ from .ui_state import active_workspace as _ws+ new_workspace = _ws(app) response = { "success": True,
addin/AdomBridge/commands/ui_state.pyadded+37@@ -0,0 +1,37 @@+"""Side-effect-free reads of what Fusion has in front, for the reads the bridge polls (get_app_state,+document_info). Text commands are not used here: Electron.run DISPLAY starts an EAGLE command, and+starting any command in an electronics editor ends the one the user has in progress (a MOVE snaps+back; issue #131). These read the API only."""++import adsk.core+++def active_workspace(app: adsk.core.Application) -> str:+ """The workspace in front by its id (BoardLayoutEnvironement, SchematicEnvironment ...: the same+ names DebugCommands.ActiveWorkspace answered), or 'unknown'."""+ try:+ ws = app.userInterface.activeWorkspace+ if ws:+ return ws.id or ws.name or "unknown"+ except Exception:+ pass+ return "unknown"+++def is_electronics(app: adsk.core.Application) -> bool:+ """True when an electronics editor (schematic, board or library) is in front: its product is an+ adsk::electron object."""+ try:+ p = app.activeProduct+ return bool(p) and str(p.objectType).startswith("adsk::electron::")+ except Exception:+ return False+++def user_command_active(app: adsk.core.Application) -> bool:+ """True while the user is in the middle of a command (anything but the plain select)."""+ try:+ cmd = app.userInterface.activeCommand or ""+ return cmd not in ("", "SelectCommand")+ except Exception:+ return False
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.13.37",+ "version": "1.13.38", "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.13.37"+version = "1.13.38" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.13.37"+version = "1.13.38" edition = "2021" [dependencies]
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.13.37"+DASH_VERSION="1.13.38" 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.13.37",+ "version": "1.13.38", "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+2−2@@ -1,6 +1,6 @@ { "slug": "fusion-bridge",- "version": "1.13.37",+ "version": "1.13.38", "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": {@@ -152,4 +152,4 @@ "confirmed_user_skills": [ "fusion-design-efficiency" ]-}+}
rust/Cargo.lock+5−5@@ -19,7 +19,7 @@ dependencies = [ [[package]] name = "adom-fusion-bridge"-version = "1.13.37"+version = "1.13.38" dependencies = [ "adom-eda-checks", "base64",@@ -299,7 +299,7 @@ dependencies = [ [[package]] name = "fusion-core"-version = "1.13.37"+version = "1.13.38" dependencies = [ "serde", "serde_json",@@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "fusion-lbr"-version = "1.13.37"+version = "1.13.38" dependencies = [ "regex", "roxmltree",@@ -322,14 +322,14 @@ dependencies = [ [[package]] name = "fusion-os-mac"-version = "1.13.37"+version = "1.13.38" dependencies = [ "fusion-core", ] [[package]] name = "fusion-os-win"-version = "1.13.37"+version = "1.13.38" dependencies = [ "fusion-core", "windows-sys 0.59.0",
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.13.37"+version = "1.13.38" edition = "2021" license = "MIT"
tests/test_polled_reads_run_no_commands.pyadded+23@@ -0,0 +1,23 @@+"""Issue #131: the reads the bridge polls must never start a Fusion command (a text command in an+electronics editor ends the user's command in progress)."""+import ast+from pathlib import Path++ADDIN = Path(__file__).resolve().parents[1] / "addin" / "AdomBridge" / "commands"+POLLED = {"app_state.py": "handle_get_app_state", "document_info.py": "handle_document_info", "ui_state.py": None}+++def calls_in(node):+ return {n.func.attr for n in ast.walk(node) if isinstance(n, ast.Call) and isinstance(n.func, ast.Attribute)}+++def test_polled_reads_never_run_a_text_command():+ for name, fn in POLLED.items():+ tree = ast.parse((ADDIN / name).read_text(encoding="utf-8"))+ scope = tree if fn is None else next(n for n in ast.walk(tree) if isinstance(n, ast.FunctionDef) and n.name == fn)+ assert "executeTextCommand" not in calls_in(scope), f"{name}:{fn or 'module'} runs a text command"+++if __name__ == "__main__":+ test_polled_reads_never_run_a_text_command()+ print("ok")