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 13 file(s)
13 files changed
+103−18
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.13.82+1.13.83
CHANGELOG.md+4@@ -3,6 +3,10 @@ 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.83 (2026-11-05)++`fusion_save_to_cloud` makes the missing folders on `folderPath` for a mechanical design too, as its description says: a save to `fusion-lane-tests/drawings` answered "Folder 'drawings' not found" when `drawings` did not exist yet. Add-in 1.0.41: Fusion needs one restart.+ ## 1.13.82 (2026-11-05) Add-in 1.0.40, so a running Fusion is restarted onto 1.13.81's drawing fix (1.13.81 left the add-in at 1.0.39, and the bridge restarts Fusion for an add-in change by its version). Fusion needs one restart.
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.40",+ "version": "1.0.41", "runOnStartup": true, "supportedOS": "windows" }
addin/AdomBridge/commands/cloud_documents.py+13−3@@ -121,8 +121,9 @@ def _resolve_project(app, project_name=None): return None, f"Project '{project_name}' not found" -def _resolve_folder(project, folder_path=None):+def _resolve_folder(project, folder_path=None, create=False): """Navigate to a subfolder within a project. Returns root folder if no path given.+ create=True makes each missing folder on the path (save_to_cloud promises it). Robustness: each `dataFolders.item(i).name` access is wrapped in try/except so a single corrupted subfolder doesn't crash navigation.@@ -152,6 +153,14 @@ def _resolve_folder(project, folder_path=None): break except Exception: continue+ if not found and create:+ try:+ made = subfolders.add(part)+ except Exception as e:+ return None, f"Could not create folder '{part}' in '{folder.name}': {e}"+ if made is None:+ return None, f"Could not create folder '{part}' in '{folder.name}'"+ folder, found = made, True if not found: return None, f"Folder '{part}' not found in '{folder.name}'" @@ -192,12 +201,13 @@ def handle_save_to_cloud(app: adsk.core.Application, args: dict) -> dict: "_hint": "Call fusion_list_cloud_projects to see available project names, then retry with a valid projectName.", } - folder, err = _resolve_folder(project, folder_path or None)+ # missing folders on the path are created, as the verb's description says+ folder, err = _resolve_folder(project, folder_path or None, create=True) if err: return { "success": False, "error": err,- "_hint": "Call fusion_list_cloud_files with this projectName to see valid folderPath values, or call fusion_create_cloud_folder to create it.",+ "_hint": "Check the projectName with fusion_list_cloud_projects; the folders on folderPath are created when missing.", } try:
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.13.82",+ "version": "1.13.83", "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.82"+version = "1.13.83" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.13.82"+version = "1.13.83" 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.82"+DASH_VERSION="1.13.83" 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.82",+ "version": "1.13.83", "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.82",+ "version": "1.13.83", "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.82"+version = "1.13.83" dependencies = [ "adom-eda-checks", "base64",@@ -299,7 +299,7 @@ dependencies = [ [[package]] name = "fusion-core"-version = "1.13.82"+version = "1.13.83" dependencies = [ "serde", "serde_json",@@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "fusion-lbr"-version = "1.13.82"+version = "1.13.83" dependencies = [ "regex", "roxmltree",@@ -322,14 +322,14 @@ dependencies = [ [[package]] name = "fusion-os-mac"-version = "1.13.82"+version = "1.13.83" dependencies = [ "fusion-core", ] [[package]] name = "fusion-os-win"-version = "1.13.82"+version = "1.13.83" 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.82"+version = "1.13.83" edition = "2021" license = "MIT"
tests/test_cloud_folders.pyadded+71@@ -0,0 +1,71 @@+"""fusion_save_to_cloud makes the folders on folderPath that are missing (its description promises it);+every other cloud verb only navigates. Found live 2026-11-05: a mechanical save to fusion-lane-tests/drawings+answered "Folder 'drawings' not found"."""+import importlib+from pathlib import Path+import sys+from types import SimpleNamespace as NS+import unittest++sys.path.insert(0, str(Path(__file__).resolve().parent))+import test_mechanical # noqa: F401 (installs the adsk stand-ins and the commands package)++cd = importlib.import_module('mechanical_test_commands.cloud_documents')+++class Folders:+ def __init__(self, owner, names=()):+ self.owner, self.values, self.added = owner, [Folder(n) for n in names], []++ @property+ def count(self):+ return len(self.values)++ def item(self, i):+ return self.values[i]++ def add(self, name):+ made = Folder(name)+ self.values.append(made)+ self.added.append(name)+ return made+++class Folder:+ def __init__(self, name, children=()):+ self.name = name+ self.dataFolders = Folders(self, children)+++class ResolveFolder(unittest.TestCase):+ def setUp(self):+ self.root = Folder("Default Project")+ self.tests = Folder("fusion-lane-tests")+ self.root.dataFolders.values.append(self.tests)+ self.project = NS(rootFolder=self.root, name="Default Project")++ def test_navigation_alone_never_creates(self):+ folder, err = cd._resolve_folder(self.project, "fusion-lane-tests/drawings")+ self.assertIsNone(folder)+ self.assertIn("'drawings' not found", err)+ self.assertEqual(self.tests.dataFolders.added, [])++ def test_create_makes_each_missing_folder_once(self):+ folder, err = cd._resolve_folder(self.project, "Fusion-Lane-Tests\\drawings/2026", create=True)+ self.assertIsNone(err)+ self.assertEqual(folder.name, "2026")+ # the existing folder is matched by name in any case; only the missing ones are made+ self.assertEqual((self.root.dataFolders.added, self.tests.dataFolders.added), ([], ["drawings"]))+ again, _ = cd._resolve_folder(self.project, "fusion-lane-tests/drawings/2026", create=True)+ self.assertIs(again, folder)+ self.assertEqual(self.tests.dataFolders.added, ["drawings"])++ def test_a_folder_fusion_will_not_make_is_reported(self):+ self.tests.dataFolders.add = lambda name: None+ folder, err = cd._resolve_folder(self.project, "fusion-lane-tests/drawings", create=True)+ self.assertIsNone(folder)+ self.assertIn("Could not create folder 'drawings'", err)+++if __name__ == "__main__":+ unittest.main()