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 12 file(s)
12 files changed
+40−16
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.13.54+1.13.55
CHANGELOG.md+6−1@@ -3,11 +3,16 @@ 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.55 (2026-11-02)++`fusion_sch_swap_part` keeps the designator when the new device's prefix differs (see 1.13.54), with a regression test on the command form.+ ## 1.13.54 (2026-11-02) - `fusion_sch_swap_part` works: REPLACE takes the part first, then the device, and the old order answered "Device not found", so the verb has done nothing since it shipped (only its dry run was tested). When the new device's prefix differs Fusion renames- the part, so the name is put back, then `keepComment` runs. Where pins end elsewhere Fusion keeps the nets itself:+ the part, so the name is put back, then `keepComment` runs (1.13.55: Fusion ignores NAME right after REPLACE until its events+ are pumped, so the rename pumps them first). Where pins end elsewhere Fusion keeps the nets itself: `pinsJoined` reports them, and a wire joins any it lost. - `fusion_bom` honours `dnp` and `excludeFromBom` from the board. `fusion_preferences` routes correctly. `fusion_select_items` checks each part exists (Fusion answers "Selected X" for any name). `fusion_update_pcb` does not report `executed` while
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.13.54",+ "version": "1.13.55", "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.54"+version = "1.13.55" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.13.54"+version = "1.13.55" 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.54"+DASH_VERSION="1.13.55" 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.54",+ "version": "1.13.55", "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.54",+ "version": "1.13.55", "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.54"+version = "1.13.55" dependencies = [ "adom-eda-checks", "base64",@@ -299,7 +299,7 @@ dependencies = [ [[package]] name = "fusion-core"-version = "1.13.54"+version = "1.13.55" dependencies = [ "serde", "serde_json",@@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "fusion-lbr"-version = "1.13.54"+version = "1.13.55" dependencies = [ "regex", "roxmltree",@@ -322,14 +322,14 @@ dependencies = [ [[package]] name = "fusion-os-mac"-version = "1.13.54"+version = "1.13.55" dependencies = [ "fusion-core", ] [[package]] name = "fusion-os-win"-version = "1.13.54"+version = "1.13.55" 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.54"+version = "1.13.55" edition = "2021" license = "MIT"
rust/bridge/src/sch_script.py+7−1@@ -1162,7 +1162,13 @@ def sch_swap_part(app, args): if not any(p_.get('name') == ref for p_ in e.after.iter('part')): fresh = [p_.get('name') for p_ in e.after.iter('part') if p_.get('name') not in names_before] if len(fresh) == 1:- e.again(["SET CONFIRM YES; NAME %s %s; SET CONFIRM OFF;" % (quote(fresh[0]), quote(ref))])+ # right after REPLACE Fusion ignores NAME without a word until its events have been pumped (live, 2706)+ import adsk, time as _t+ for _ in range(3):+ adsk.doEvents(); _t.sleep(0.5); adsk.doEvents()+ e.again(["SET CONFIRM YES; NAME %s %s; SET CONFIRM OFF;" % (quote(fresh[0]), quote(ref))])+ if any(p_.get('name') == ref for p_ in e.after.iter('part')):+ break if args.get('keepComment') and old_value: # after the name is back e.again(['SET CONFIRM YES; VALUE %s %s; SET CONFIRM OFF;' % (quote(ref), quote(old_value[:40]))]) now_part = next((p_ for p_ in e.after.iter('part') if p_.get('name') == ref), None)
tests/test_sch.py+13@@ -161,5 +161,18 @@ class SchematicDataTests(unittest.TestCase): self.assertIsInstance(names, list) ++class SwapPartCommand(unittest.TestCase):+ """REPLACE takes the part first, then the device. The other order answers "Device not found" from+ Fusion and changes nothing: the verb did nothing from 1.13.8 to 1.13.53, and only its dry run was tested."""+ def test_replace_names_the_part_first(self):+ import os, re+ src = open(os.path.join(os.path.dirname(__file__), '..', 'rust', 'bridge', 'src', 'sch_script.py'), encoding='utf-8').read()+ body = src[src.index('def sch_swap_part'):src.index('def sch_split_wire')]+ self.assertIn("REPLACE %s '%s@%s'; SET CONFIRM OFF;\" % (quote(ref), full, libname)", body)+ self.assertNotRegex(body, r"REPLACE '%s@%s' %s")+ # Fusion ignores NAME right after REPLACE until its events are pumped+ self.assertRegex(body, r"doEvents[\s\S]{0,120}e\.again\(\[\"SET CONFIRM YES; NAME")+ if __name__ == '__main__': unittest.main()