← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.10.29\ No newline at end of file+1.10.30\ 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.10.29",+  "version": "1.10.30",   "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.10.29"+version = "1.10.30" dependencies = [  "serde_json", ]
dashboard/Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.10.29"+version = "1.10.30" 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.10.29"+DASH_VERSION="1.10.30" 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.10.29",+  "version": "1.10.30",   "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+11−27
@@ -1,6 +1,6 @@ {   "slug": "fusion-bridge",-  "version": "1.10.29",+  "version": "1.10.30",   "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",@@ -75,7 +79,10 @@     "build a basic parts library",     "add a 3d model to a component",     "make a fusion 360 library with 3d",-    "set up the fusion bridge"+    "set up the fusion bridge",+    "fusion dashboard",+    "fusion status page",+    "open the fusion launch page"   ],   "discovery_pitch": "Use the Adom Bridge 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 Bridge. 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 Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.",@@ -104,28 +111,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]"-  }-}\ No newline at end of file+  ]+}
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.10.29"+version = "1.10.30" edition = "2021" license = "MIT" 
rust/bridge/src/main.rs+40−2
@@ -424,6 +424,21 @@ fn main() {                     "fusion_live_feed" | "live_feed" if addin_owner_is_exe(&**h) => { let pids: Vec<u32> = h.fusion_processes().iter().map(|p| p.pid).collect(); let mut r = livefeed::live_feed(&caller, pids, &parsed["args"]); r["shell"] = json!("rust"); r },                     "fusion_check_3d_thumbnail" | "check_3d_thumbnail" if addin_owner_is_exe(&**h) => { let mut r = prove::check_3d_thumbnail(&parsed["args"]); r["shell"] = json!("rust"); r },                     "fusion_resolve_seat_dialog" | "resolve_seat_dialog" if addin_owner_is_exe(&**h) => { let (sc, ca) = (sidecar.clone(), caller.clone()); let side = move |verb: &str, a: Value, t: u64| proxy_with_timeout(&sc, &json!({"command": verb, "args": a}).to_string(), &ca, t); let dlg = |r: Value| r; let ctx = addin::Ctx { host: &**h, fusion_exe: h.fusion_executable(), short_path: |p| host().short_path(p), state_dir: h.bridge_state_dir(), caller: caller.clone(), pids: h.fusion_processes().iter().map(|p| p.pid).collect(), sidecar: &side, dialogs: &dlg }; let mut r = readiness_exec::resolve_seat_dialog_verb(&ctx, &parsed["args"]); r["shell"] = json!("rust"); r },+                    v @ ("fusion_check_dialogs" | "check_dialogs" | "fusion_dismiss_recovery" | "dismiss_recovery" | "fusion_relocate_recovery" | "relocate_recovery" | "fusion_close" | "close") if addin_owner_is_exe(&**h) => {+                        // Legacy window verbs the skills still name (the Python sidecar owned them until+                        // 1.10.0). Each maps onto code the exe already runs on its own at launch and in+                        // the dialog gate, so they answer instead of reaching the sidecar fallback.+                        let (sc, ca) = (sidecar.clone(), caller.clone()); let side = move |verb: &str, a: Value, t: u64| proxy_with_timeout(&sc, &json!({"command": verb, "args": a}).to_string(), &ca, t);+                        let sd = h.bridge_state_dir(); let dlg = |r: Value| r;+                        let ctx = addin::Ctx { host: &**h, fusion_exe: h.fusion_executable(), short_path: |p| host().short_path(p), state_dir: sd.clone(), caller: caller.clone(), pids: h.fusion_processes().iter().map(|p| p.pid).collect(), sidecar: &side, dialogs: &dlg };+                        let mut r = match v.strip_prefix("fusion_").unwrap_or(v) {+                            "check_dialogs" => { let d = lifecycle::dialog_inventory(&ctx); json!({"success": true, "count": d.len(), "dialogs": d, "output": format!("{} dialog(s) open", d.len()), "_hint": "Each dialog carries its title, text and buttons, read through UI Automation (no screenshot needed). The bridge expires unknown dialogs by itself before the next add-in verb; fusion_state reports the same list plus the window capture. In-canvas alerts that have no window are in fusion_alerts."}) },+                            "dismiss_recovery" => { let (done, reloc) = readiness_exec::dismiss_recovery_dialog(&ctx, 5.0, true); json!({"success": true, "dismissed": done, "relocated": reloc, "output": if done { "Recovery prompt dismissed in the background; the recovery files were moved, not deleted." } else { "No recovery prompt was open." }}) },+                            "relocate_recovery" => { let reloc = readiness_exec::relocate_recovery_files(); json!({"success": true, "relocated": reloc, "_hint": "Files are moved to ~/.adom/recovery/fusion/<stamp>/, never deleted. fusion_start does this by itself before every launch."}) },+                            _ => { let mut r = lifecycle::stop(&ctx, &parsed["args"]); if r.is_object() { r["aliasOf"] = json!("fusion_stop"); } r },+                        };+                        r["shell"] = json!("rust"); r+                    },                     v @ ("fusion_stop" | "stop" | "fusion_kill" | "kill" | "fusion_restart_for_addin" | "restart_for_addin") if addin_owner_is_exe(&**h) => {                         let (sc, ca) = (sidecar.clone(), caller.clone()); let side = move |verb: &str, a: Value, t: u64| proxy_with_timeout(&sc, &json!({"command": verb, "args": a}).to_string(), &ca, t);                         let sd = h.bridge_state_dir(); let dlg = |r: Value| r;@@ -525,6 +540,29 @@ fn main() { /// Forward an unported verb to the Python sidecar, identity headers intact. fn proxy(sidecar: &Arc<Mutex<Option<Sidecar>>>, raw: &str, caller: &Caller) -> Value { proxy_with_timeout(sidecar, raw, caller, 600) } +/// Verb names older skills and transcripts still use. The Python sidecar owned them until+/// 1.10.0; each answers with the living replacement so a caller is never left at a dead end.+fn retired_verb(verb: &str) -> Option<Value> {+    let v = verb.strip_prefix("fusion_").unwrap_or(verb);+    let (instead, how): (&str, &str) = match v {+        "send_key" => ("desktop_press_key (an ab verb)", "adom-bridge desktop_press_key {hwnd, key}. For a DIALOG you rarely need a key at all: the bridge reads dialogs through UI Automation (fusion_check_dialogs or fusion_state.dialogs[]) and expires unknown ones by itself before the next add-in verb; a named button is desktop_ui_click {hwnd, name, role:\"button\"}."),+        "click_fusion" | "click" => ("desktop_click (an ab verb)", "adom-bridge desktop_screenshot_window {hwnd} then desktop_click {hwnd, space:\"image\", shotId, x, y, cursor:false}: image-space coordinates from the shot, no math, no foreground. Prefer desktop_ui_click {hwnd, name} whenever the control has an accessible name. fusion_state gives the main hwnd."),+        "screenshot_all" => ("fusion_state", "fusion_state returns every dialog (title, text, buttons, hwnd) plus the main window capture in one read; fusion_screenshot_fusion is the main window alone."),+        "window_info" => ("fusion_state", "fusion_state carries hwnd, title, rect, dialogs[] and the focus events."),+        "detect" => ("fusion_readiness", "fusion_readiness reports installed / running / ready / needsSignin without launching anything."),+        "open_symbol" => ("fusion_show_symbol", "fusion_show_symbol {libraryPath, deviceset, capture:true}."),+        "schematic_info" => ("fusion_electronics_query", "fusion_electronics_query {entity:\"Part\"} (or Sheet, Net, Instance) reads the active schematic as typed rows; fusion_document_info says which document is active."),+        "dark_mode" => ("fusion_set_preference", "fusion_set_preference {theme:\"dark\"} applies live, no restart."),+        "aps_permissions" => ("fusion_aps_status", "fusion_aps_status reports sign-in, scopes and the hub it can see."),+        "mcp_execute" | "mcp_update" | "mcp_electronics_read" => ("fusion_mcp_call", "These are Autodesk MCP TOOL names, not bridge verbs: fusion_mcp_call {name:\"fusion_mcp_execute\", arguments:{...}} (fusion_mcp_tools lists them; fusion_electronics_query wraps electronics_read)."),+        "signout" | "export_molecule_bundle" => ("(no replacement)", "This verb was retired with the Python sidecar and has no exe equivalent yet. If you need it, the source is open: see the fusion-contributing skill, or file an issue on the fusion-bridge wiki page."),+        _ => return None,+    };+    Some(json!({"success": false, "errorCode": "verb_retired", "verb": verb, "useInstead": instead,+        "error": format!("'{verb}' was retired when the exe replaced the Python sidecar (1.10.0). Use {instead}."),+        "_hint": how}))+}+ fn verb_of(raw: &str) -> String { serde_json::from_str::<Value>(raw).ok().and_then(|v| v["command"].as_str().or(v["verb"].as_str()).map(String::from)).unwrap_or_else(|| "?".into()) }  fn proxy_with_timeout(sidecar: &Arc<Mutex<Option<Sidecar>>>, raw: &str, caller: &Caller, timeout_secs: u64) -> Value {@@ -534,10 +572,10 @@ fn proxy_with_timeout(sidecar: &Arc<Mutex<Option<Sidecar>>>, raw: &str, caller:         return json!({"success": false, "errorCode": "sidecar_exited", "error": format!("The Python sidecar exited with code {code}."),                       "_hint": "Read ~/.adom/fusion-bridge/sidecar.log for the traceback, then bridge_kill {name:'fusion360'} so ab respawns the shell and its sidecar."});     }-    let (url, young) = match sidecar.lock().unwrap().as_ref() { Some(s) => { *launch::SIDECAR_URL.lock().unwrap() = Some(s.url("/command")); (s.url("/command"), s.started.elapsed() < Duration::from_secs(45)) }, None => return json!({+    let (url, young) = match sidecar.lock().unwrap().as_ref() { Some(s) => { *launch::SIDECAR_URL.lock().unwrap() = Some(s.url("/command")); (s.url("/command"), s.started.elapsed() < Duration::from_secs(45)) }, None => { if let Some(r) = retired_verb(&verb_of(raw)) { return r; } return json!({         "success": false, "errorCode": "sidecar_absent", "verb": verb_of(raw),         "error": format!("No handler in this exe owns the verb '{}', and there is no Python sidecar to fall back to (the exe ships alone since 1.10.0).", verb_of(raw)),-        "_hint": "This is a verb-name problem, not a Fusion or add-in problem: check the name against fusion_describe (the frozen contract) and do not restart Fusion or diagnose the add-in for it. If the name IS in the contract, file it on the fusion-bridge wiki page: a listed verb reached the fallback."}) };+        "_hint": "This is a verb-name problem, not a Fusion or add-in problem: check the name against fusion_describe (the frozen contract) and do not restart Fusion or diagnose the add-in for it. If the name IS in the contract, file it on the fusion-bridge wiki page: a listed verb reached the fallback."})  } };     let mut r = ureq::post(&url).timeout(Duration::from_secs(timeout_secs)).set("Content-Type", "application/json");     if !caller.thread.is_empty() { r = r.set("X-Adom-Caller-Thread", &caller.thread); }     if !caller.container.is_empty() { r = r.set("X-Adom-Caller-Container", &caller.container); }
skills/fusion-driving/SKILL.md+16−13
@@ -41,7 +41,7 @@ the schematic/board/3D view, running scripts, exporting, and screenshotting are   window. - **Run the add-in:** `fusion_run_modeling_script`, `fusion_execute_text_command` / `electron_run`,   every export, BOM, parameters, `fusion_attach_3d_package`, `fusion_close_document`.-- **Capture by hwnd:** `fusion_screenshot_all`, `fusion_screenshot_fusion`,+- **Capture by hwnd:** `fusion_state` (dialogs as text plus the window capture), `fusion_screenshot_fusion`,   `desktop_screenshot_window {hwnd}` (PrintWindow/WGC, occlusion-independent, works minimized). - **Dismiss a dialog in the background:** `fusion_close_window {hwnd}` (WM_CLOSE via PostMessage, no   focus steal).@@ -49,9 +49,11 @@ the schematic/board/3D view, running scripts, exporting, and screenshotting are **Foreground (raises Fusion / steals focus, use only deliberately):** - **`fusion_start`** - launching Fusion brings it up. This is the one legitimate foregrounding action   (it is the boot).-- **`fusion_send_key`, `fusion_click_fusion`** - OS-level input injection calls `SetForegroundWindow`-  to deliver the key/click, which yanks Fusion to the front (caught 2026-06-29). Prefer the add-in-  API or `close_window` over synthetic input for anything you can express that way.+- **Synthetic keys and clicks.** The old `fusion_send_key` / `fusion_click_fusion` verbs are RETIRED (they+  answer `verb_retired` with the replacement): they called `SetForegroundWindow` and yanked Fusion to+  the front (caught 2026-06-29). Prefer the add-in API or `close_window`. When only a click will do, use+  ab: `desktop_ui_click {hwnd, name}` by accessible name, else `desktop_screenshot_window {hwnd}` then+  `desktop_click {hwnd, space:"image", shotId, x, y, cursor:false}`. - **`desktop_screenshot_screen`** (whole-desktop capture) - screen-DC capture forces the target   toward the foreground AND often grabs the wrong window. Never use it on Fusion (see Rule 2). - Any explicit OS window activation (`SetForegroundWindow`, focus/raise verbs).@@ -93,8 +95,8 @@ ways to get the array, in order of preference:    `screenshot` path you can Read), and a top-level `_hint`. **When `dialogsDetected > 0, STOP and    analyze the dialogs[] before your next call.** Never ignore that hint. (See "How the bridge    enforces this" below.)-2. **`fusion_screenshot_all`** - captures the main window PLUS every Qt dialog window as separate-   shots, and returns the list of saved paths. Read each.+2. **`fusion_check_dialogs`** (or `fusion_state`) - every open dialog as DATA: title, text, buttons and+   hwnd, read through UI Automation. Read the text first; a screenshot is the fallback, not the start. 3. **`desktop_screenshot_window {"hwnd": <fusion-hwnd>}`** (ab core) - returns `screenshots[]` and    **`ownedPopupCount`**. `ownedPopupCount > 0` means dialogs are stacked on the window. Read the    shots. Get the hwnd from `fusion_state {screenshot:false}` (its `hwnd` field) or `desktop_find_window@@ -103,7 +105,7 @@ ways to get the array, in order of preference: **To actually SEE a shot - mind WHERE it lands:** - ab core `desktop_screenshot_window {hwnd}` saves the PNG **into your container** (e.g.   `/tmp/adom-desktop-screenshots/`) - Read it directly.-- The bridge's own captures (the auto `dialogArray[].screenshot` paths and `fusion_screenshot_all`)+- The bridge's own captures (the auto `dialogArray[].screenshot` paths and `fusion_screenshot_fusion`)   are saved on the **Windows box** (`%TEMP%/conduit-screenshots/`). To see one, either re-capture that   hwnd with `desktop_screenshot_window {hwnd}` (lands in your container) or `desktop_pull_file` it over. @@ -115,7 +117,7 @@ Either way, LOOK - do not describe it from memory.   foreground (often the user's browser, not Fusion), and screen-DC capture paths force the target   toward the foreground - **which disrupts the user, who may be doing something else.** It also   routinely captures the WRONG window.-- ✅ **Always capture a specific `hwnd`** (`fusion_screenshot_all`, `fusion_screenshot_fusion`,+- ✅ **Always capture a specific `hwnd`** (`fusion_state`, `fusion_screenshot_fusion`,   `desktop_screenshot_window {hwnd}`). These use `PrintWindow` / WGC and capture the **exact window   in the BACKGROUND** without bringing it forward. Fusion keeps running behind whatever the user is   doing. This is the only acceptable way to look at Fusion.@@ -130,14 +132,15 @@ A dialog has a correct answer that depends on what it says. Clicking the default 3. Only use a generic dismiss as a LAST resort, and only for dialogs you have confirmed are safe to    cancel/escape. -To act on a specific dialog, target its `hwnd`: `fusion_click_fusion {"hwnd":<dlg>, ...}`,-`fusion_send_key {"hwnd":<dlg>, "key":"escape"}`, or `fusion_close_window {"hwnd":<dlg>}`.+To act on a specific dialog, target its `hwnd`: ab `desktop_ui_click {"hwnd":<dlg>, "name":"Cancel", "role":"button"}`+for a named button, or `fusion_close_window {"hwnd":<dlg>}`. The bridge also expires unknown dialogs by+itself before the next add-in verb, so most of the time there is nothing for you to do.  ### ⛔ Dismiss in the BACKGROUND - `close_window` (WM_CLOSE), NEVER `send_key`/Escape  When you DO dismiss a dialog, use **`fusion_close_window {"hwnd":<dlg>}`** (WM_CLOSE via PostMessage =-Cancel/No). It dismisses **without stealing focus**. ⛔ **Do NOT use `fusion_send_key`/Escape to expire-a dialog** - `send_key` calls `SetForegroundWindow` and **YANKS Fusion to the foreground**, disrupting+Cancel/No). It dismisses **without stealing focus**. ⛔ **Do NOT send Escape to expire+a dialog** - a synthetic key calls `SetForegroundWindow` and **YANKS Fusion to the foreground**, disrupting the user (caught 2026-06-29: an Escape pulled Fusion to the front mid-task). WM_CLOSE is also more reliable than Escape on Qt dialogs. The bridge's own auto-dismiss paths (`_dismiss_dialogs_bg`, the long-command pre-dismiss) all use `close_window` for exactly this reason. Background screenshots +@@ -153,7 +156,7 @@ background dismiss = the user never sees Fusion jump around. | **"select electronics design", "linked to multiple"** | A file links to multiple electronics designs; CEF picker. | Its list is NOT Win32/keyboard navigable. Open the specific design by URN/API instead, or select on the desktop. See [fusion-electronics](../fusion-electronics/SKILL.md). | | **"recovery", "recover unsaved", "document recovery"** | Document Recovery prompt. | `fusion_dismiss_recovery`, or `fusion_relocate_recovery` BEFORE `fusion_start` to prevent it. | | **"save changes", "unsaved changes", "do you want to save"** | Unsaved-changes prompt on close. | Use `fusion_close_document` (closes without the save modal). Only Escape/Cancel if you mean to keep editing. |-| **"what do you want to design"** | Fusion's start picker (no doc open). | `fusion_send_key {"key":"escape"}`. |+| **"what do you want to design"** | Fusion's start picker (no doc open). | Nothing: `fusion_start` dismisses it, and the bridge expires it before the next add-in verb. `fusion_close_window {hwnd}` if you must. | | **"create new symbol/footprint/device '<name>'?"** | An `EDIT <name>.sym/.pac/.dev` was run with a name Fusion can't find as that object - usually a DEVICESET name passed to `.sym`/`.pac` whose symbol/package is named by the shared combo (e.g. `R-0603`, not `R-0603-10K`). | Dismiss in the BACKGROUND (`fusion_close_window {hwnd}` = No - never create a stray). Then re-`EDIT` with the correct symbol/package name. `fusion_capture_library_views` now auto-handles this. | | bare **"Fusion360"** modal during an open | Usually the linked-design picker. | `fusion_screenshot_fusion` to confirm, then handle as the picker. | 
skills/fusion-libraries/SKILL.md+1−1
@@ -62,7 +62,7 @@ See **[LIBRARY_FINDINGS.md](LIBRARY_FINDINGS.md)** for the full live findings lo  ## 3. THE WINDOWS-STAGING GOTCHA (read this first) -`fusion_open_lbr` (and `fusion_import_file`) take a **filePath on the WINDOWS machine** where Fusion runs (`C:/...`). The whole chip-fetcher/adom-lbr pipeline runs in the **cloud container**, so the `.lbr` / `.step` is on the Linux box, which Fusion cannot read. **There is NO container-to-Windows push verb** (`desktop_pull_*` only goes Windows -> container).+`fusion_open_lbr` (and `fusion_import_step`) take a **filePath on the WINDOWS machine** where Fusion runs (`C:/...`). The whole chip-fetcher/adom-lbr pipeline runs in the **cloud container**, so the `.lbr` / `.step` is on the Linux box, which Fusion cannot read. **There is NO container-to-Windows push verb** (`desktop_pull_*` only goes Windows -> container).  Stage the file onto Windows first. Proven method (same proxy that serves shotlog): 
skills/fusion-onboarding/SKILL.md+1−1
@@ -48,7 +48,7 @@ shell_execute {"command":"curl -L -o C:\\tmp\\FusionDL.exe \"https://dl.appstrea   consent. It then **streams ~several GB**, so it can take 10-30 min — **monitor, don't block**   (poll for the FusionLauncher under `%LOCALAPPDATA%\Autodesk\webdeploy\production\<hash>\`). - Uninstall (if ever needed): `--globalinstall --process uninstall --quiet`.-- When done, `fusion_detect` flips to `installed:true` (the bridge finds the newest webdeploy+- When done, `fusion_readiness` flips to `installed:true` (the bridge finds the newest webdeploy   launcher by mtime).  ## Step 3 — First launch + Autodesk login (free account / trial)
tools/check-naming.sh

  
tools/check-portable.sh

  
tools/promote.sh

  
tools/release.sh