← Commit history

fix #196: paths with spaces silently no-op'd in fusion_open_board/open_schematic/import_electronics/open_lbr - executeTextCommand is space-delimited so the unquoted path split at the first space. Now quotes + falls back to the Windows 8.3 short path; specific error hint + SKILL gotcha

John Lauer ·ae2e805ae7 ·2mo ago ·parent cb60f6e
7 files changed +89−14
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.6.89+1.6.90
addin/AdomBridge/commands/open_electronics_file.py+50−3
@@ -19,6 +19,49 @@ import os import time  +def _open_local_document(app, file_path: str):+    """Run Document.newDesignFromLocal for a path that MAY CONTAIN SPACES.++    BUG (Drew's agent, issue #196, 2026-07-16): executeTextCommand is a SPACE-DELIMITED+    command string, so an unquoted path like+      C:/Users/drew/Downloads/e2e-pmcoil/PM COIL Molecule.brd+    was split at the first space - Fusion got ".../e2e-pmcoil/PM" and the open silently+    no-op'd, surfacing only as the generic "Fusion may not support this file" hint. A+    space-free copy of the same file opened instantly, which is what pinned it to path+    handling rather than file support.++    Two-step, so it works whichever way this Fusion build's parser behaves:+      1. QUOTE the path.+      2. If that fails and the path has spaces, retry with the Windows 8.3 SHORT path+         (GetShortPathNameW) - same file, no spaces, no copying.++    Returns (result, how) and raises the LAST exception if every attempt fails.+    """+    attempts = []+    quoted = '"%s"' % file_path.replace('"', '')+    attempts.append((quoted, "quoted"))+    if " " in file_path:+        try:+            import ctypes+            from ctypes import wintypes+            _gsp = ctypes.windll.kernel32.GetShortPathNameW+            _gsp.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD]+            _gsp.restype = wintypes.DWORD+            buf = ctypes.create_unicode_buffer(600)+            if _gsp(file_path, buf, 600) and buf.value and " " not in buf.value:+                attempts.append((buf.value, "shortpath"))+        except Exception:+            pass+    last = None+    for arg, how in attempts:+        try:+            r = app.executeTextCommand("Document.newDesignFromLocal %s" % arg)+            return r, how+        except Exception as e:+            last = e+    raise last if last else RuntimeError("no open attempt ran")++ def _open_electronics(app: adsk.core.Application, file_path: str, file_type: str, expected_ext: str) -> dict:     """Common logic for opening an electronics file in Fusion."""     if not file_path:@@ -57,7 +100,7 @@ def _open_electronics(app: adsk.core.Application, file_path: str, file_type: str         }      try:-        r = app.executeTextCommand(f"Document.newDesignFromLocal {file_path}")+        r, _open_how = _open_local_document(app, file_path)     except Exception as e:         return {             "success": False,@@ -65,7 +108,11 @@ def _open_electronics(app: adsk.core.Application, file_path: str, file_type: str             "_hint": "Call fusion_screenshot to check for a modal dialog blocking Fusion, then fusion_send_key escape to dismiss it, then retry.",             "data": {                 "filePath": file_path,-                "hint": "Fusion 360 may not support this file. Ensure Fusion is fully loaded and responsive.",+                "hint": ("PATH WITH SPACES? This verb now quotes the path and falls back to the "+                         "Windows 8.3 short path, so spaces should work (issue #196). If it still "+                         "fails, verify the file EXISTS at exactly this path and is valid, then try "+                         "a space-free copy to isolate it. Otherwise Fusion may not support this "+                         "file, or it is not fully loaded/responsive."),             },         } @@ -340,7 +387,7 @@ def handle_import_electronics(app: adsk.core.Application, args: dict) -> dict:     file_type = file_type_map[ext]      try:-        r = app.executeTextCommand(f"Document.newDesignFromLocal {file_path}")+        r, _open_how = _open_local_document(app, file_path)     except Exception as e:         return {             "success": False,
addin/AdomBridge/commands/open_lbr.py+4−1
@@ -58,7 +58,10 @@ def handle_open_lbr(app: adsk.core.Application, args: dict) -> dict:     # Step 1: Open the .lbr file via Document.newDesignFromLocal     # (Document.Open fails with error 3 for .lbr files)     try:-        r = app.executeTextCommand(f"Document.newDesignFromLocal {file_path}")+        # issue #196: quote + 8.3 fallback so paths WITH SPACES open (executeTextCommand+        # is space-delimited, so an unquoted path was silently truncated at the space).+        from .open_electronics_file import _open_local_document+        r, _open_how = _open_local_document(app, file_path)         results.append(f"Document.newDesignFromLocal: {r if r else 'ok'}")     except Exception as e:         return {
adom-bridge-fusion-manifest.json+5−5
@@ -1,13 +1,13 @@ {   "manifest_version": 1,   "name": "fusion360",-  "version": "1.6.89",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.89/adom-bridge-fusion-v1.6.89.zip",-  "sha256": "fa347850bbf515ab7497cc594133b5824c696b1b60caa82da725e03516738311",-  "size": 471956,+  "version": "1.6.90",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.90/adom-bridge-fusion-v1.6.90.zip",+  "sha256": "3848365d4400db2136254db595340a2d79ba73d61fedffe4d3a0976a6bb93453",+  "size": 473602,   "verbPrefixes": [     "fusion_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-07-20T11:37:59Z"+  "released_at": "2026-07-20T15:53:10Z" }\ No newline at end of file
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "fusion360",   "displayName": "Autodesk Fusion 360",-  "version": "1.6.89",+  "version": "1.6.90",   "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/adom-desktop-fusion-bridge",   "author": "Adom Inc.",
page.json+2−3
@@ -1,6 +1,6 @@ {   "slug": "adom-desktop-fusion-bridge",-  "version": "1.6.89",+  "version": "1.6.90",   "type": "app",   "description": "Installs this bridge's skills into your container so your AI knows how to drive the bridge. The bridge runtime itself is loaded by Adom Desktop from the release zip.",   "dependencies": {@@ -73,6 +73,5 @@     "set up the fusion bridge"   ],   "discovery_pitch": "Use the Adom Desktop Fusion 360 bridge when the user wants to DRIVE Autodesk Fusion 360 from the cloud: open/search their team's Fusion cloud designs, lay out a PCB or do electronics, export STEP/IGES/STL/Gerbers/BOM, or build a component library with real rendering 3D. It runs Fusion on the user's own machine via Adom Desktop. NOT for web/browser/login/form-filling (that's the native-browser extension) and NOT for KiCad (that's the kicad bridge).",-  "brief": "Drive Autodesk Fusion 360 from the cloud via Adom Desktop: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.",-  "visibility": "public"+  "brief": "Drive Autodesk Fusion 360 from the cloud via Adom Desktop: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling." }\ No newline at end of file
skills/fusion-electronics/SKILL.md+26
@@ -76,3 +76,29 @@ Record with WGC (`desktop_record_window_start`) - background, no focus steal, an GL canvas. PrintWindow (`desktop_screenshot_window`) shows the chrome but does NOT reflect the GL view changing, so verify motion by the verb's `ok` + a WGC frame, not a PrintWindow pixel-diff. A black canvas means an empty/wrong file - see section 1.++## Gotcha: file paths WITH SPACES (fixed in v1.6.90 - issue #196)++`executeTextCommand` is a **space-delimited** command string, so `Document.newDesignFromLocal`+used to receive an unquoted path and split it at the first space:++```+C:/Users/drew/Downloads/e2e-pmcoil/PM COIL Molecule.brd+                                   ^ Fusion only ever saw ".../PM"+```++The open then **silently no-op'd** - no dialog, no document, just the generic "Fusion 360 may not+support this file" hint - which sent the reporting agent hunting for a file-format problem when it+was pure path handling. (Caught by Drew's agent: the identical file opened instantly once copied to+a space-free name.)++Fixed in `_open_local_document()` (`commands/open_electronics_file.py`), now used by+`fusion_open_board`, `fusion_open_schematic`, `fusion_import_electronics` and `fusion_open_lbr`:++1. **Quote the path**, then+2. if that fails and the path has spaces, retry with the Windows **8.3 short path**+   (`GetShortPathNameW`) - same file, no spaces, nothing copied.++**If you are writing a new verb that hands a path to `executeTextCommand`, use that helper.** Never+interpolate a bare path into a text command; assume every user path has spaces in it, because+Downloads and OneDrive folders routinely do.