← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.10.38\ No newline at end of file+1.10.39\ 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.38",+  "version": "1.10.39",   "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.38"+version = "1.10.39" dependencies = [  "serde_json", ]
dashboard/Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.10.38"+version = "1.10.39" 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.38"+DASH_VERSION="1.10.39" 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.38",+  "version": "1.10.39",   "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+10−26
@@ -1,6 +1,6 @@ {   "slug": "fusion-bridge",-  "version": "1.10.38",+  "version": "1.10.39",   "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",@@ -76,6 +80,9 @@     "add a 3d model to a component",     "make a fusion 360 library with 3d",     "set up the fusion bridge",+    "fusion dashboard",+    "fusion status page",+    "open the fusion launch page",     "aps not configured",     "share aps client id across my org",     "set up fusion cloud search for my company",@@ -108,28 +115,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.38"+version = "1.10.39" edition = "2021" license = "MIT" 
rust/bridge/src/main.rs+6
@@ -424,6 +424,12 @@ fn main() {                             "show_schematic" | "show_2d_board" | "show_3d_board" => orchestrations::show_with_document(&cmd, &parsed["args"], &pt),                             "export_source" | "export_eagle_source" => orchestrations::export_in_right_view(&cmd, &parsed["args"], &pt),                             "activate_document" => orchestrations::activate_with_same_name_note(&parsed["args"], &pt),+                            "take_screenshot" if parsed["args"]["outputPath"].as_str().map(|p| p.trim().is_empty()).unwrap_or(true) => {+                                // the contract says outputPath is optional; the add-in insisted on one+                                let mut a = if parsed["args"].is_object() { parsed["args"].clone() } else { json!({}) };+                                a["outputPath"] = json!(orchestrations::shots_dir().join(format!("viewport-{}.png", std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_millis()).unwrap_or(0))).to_string_lossy().replace('\\', "/"));+                                addin::pass_through(&cmd, &a, &ctx)+                            }                             _ => addin::pass_through(&cmd, &parsed["args"], &ctx),                         };                         small_verbs::add_image_delivery(&mut r);
rust/bridge/src/mint.rs+1−1
@@ -322,7 +322,7 @@ pub fn build_library_3d_core(ctx: &Ctx, args: &Value, pass: &dyn Fn(&str, Value)     if let Some(b) = &bound { if args["openWhenDone"].as_bool().unwrap_or(true) { opened = json!(crate::orchestrations::open_lbr(ctx, &json!({"filePath": b}), pass)["success"].as_bool() == Some(true)); } }     let n_ok = results.iter().filter(|r| r["success"].as_bool() == Some(true)).count();     json!({"success": n_ok > 0, "cancelled": cancelled, "boundLbr": bound, "partsBound": n_ok, "partsTotal": total, "parts": results, "screenshots": shots, "opened": opened,-        "_hint": format!("{n_ok}/{total} parts got a RENDERING footprint+chip 3D package; all bindings injected into {} and the library opened once. NEXT: fusion_check_dialogs should be 0 (no broken-ref). For clean device previews + screenshots, save to the cloud and REOPEN from the cloud (fusion_aps_open) - the in-session view often gets an empty 'Untitled' shoved in front. Each device's lower-right 3D preview should show footprint + chip (NOT 'Thumbnail download failed'). The BEFORE (footprint) / AFTER (chip placed) PNGs are in screenshots[] on the Windows box (C:/tmp/conduit-screenshots) - pull them with desktop_pull_file (or re-capture via desktop_screenshot_window) for the demo video. AFTER (recommended): call fusion_capture_library_views per showcase part to grab the symbol / footprint / component (pin<->pad mapped) views - those are what make EEs trust the library, and the 3D-only shots miss them. PITFALLS: needs Fusion running. ⚠️ RELAY TIMEOUT: a many-part run takes minutes but the Adom Bridge relay times out the REQUEST at ~60s - you may get 'Request timed out' even though the build KEEPS RUNNING server-side and finishes. Do NOT assume it failed: wait, then verify by reading the boundLbr (count <package3d name=) + the before/after PNGs in C:/tmp/conduit-screenshots. If a part is missing, just re-run build_library_3d for the missing parts pointing outLbrPath at the SAME file - binding injection is now MERGE-AWARE and idempotent, so it accumulates onto the existing bindings (it no longer wipes the parts that already succeeded). Each part also self-retries once on a transient first-part failure. Full recipe: the fusion-multipart-libraries skill.", bound.as_deref().unwrap_or("None"))})+        "_hint": format!("{n_ok}/{total} parts got a RENDERING footprint+chip 3D package; all bindings injected into {} and the library opened once. NEXT: fusion_check_dialogs should be 0 (no broken-ref). For clean device previews + screenshots, save to the cloud and REOPEN from the cloud (fusion_aps_open) - the in-session view often gets an empty 'Untitled' shoved in front. Each device's lower-right 3D preview should show footprint + chip (NOT 'Thumbnail download failed'). The BEFORE (footprint) / AFTER (chip placed) PNGs are in screenshots[] (on the box in %TEMP%/adom-fusion-shots; ab pulls them to you as localFullPath) - pull them with desktop_pull_file (or re-capture via desktop_screenshot_window) for the demo video. AFTER (recommended): call fusion_capture_library_views per showcase part to grab the symbol / footprint / component (pin<->pad mapped) views - those are what make EEs trust the library, and the 3D-only shots miss them. PITFALLS: needs Fusion running. ⚠️ RELAY TIMEOUT: a many-part run takes minutes but the Adom Bridge relay times out the REQUEST at ~60s - you may get 'Request timed out' even though the build KEEPS RUNNING server-side and finishes. Do NOT assume it failed: wait, then verify by reading the boundLbr (count <package3d name=) + the before/after PNGs (%TEMP%/adom-fusion-shots on the box, pulled to you as localFullPath). If a part is missing, just re-run build_library_3d for the missing parts pointing outLbrPath at the SAME file - binding injection is now MERGE-AWARE and idempotent, so it accumulates onto the existing bindings (it no longer wipes the parts that already succeeded). Each part also self-retries once on a transient first-part failure. Full recipe: the fusion-multipart-libraries skill.", bound.as_deref().unwrap_or("None"))}) }  #[cfg(test)]
rust/bridge/src/orchestrations.rs+13−5
@@ -721,15 +721,23 @@ pub fn fusion_state(ctx: &Ctx, args: &Value) -> Value {     result } -/// `_capture_labeled`: the main window to a labeled PNG under C:/tmp/conduit-screenshots+/// Where the bridge's own captures go: %TEMP%\\adom-fusion-shots. ab auto-pulls a reply's+/// `fullPath` to the caller only from the user-profile and temp roots, so the old+/// C:/tmp/conduit-screenshots folder was never delivered (page issue #98).+pub fn shots_dir() -> std::path::PathBuf {+    let d = std::env::temp_dir().join("adom-fusion-shots");+    let _ = std::fs::create_dir_all(&d);+    d+}++/// `_capture_labeled`: the main window to a labeled PNG under shots_dir() /// (ab captures to its own shot dir; the file is copied so pull paths stay where the skills say). pub fn capture_labeled(ctx: &Ctx, label: &str) -> Option<String> {     if label.is_empty() { return None; }     let shot = crate::small_verbs::screenshot_fusion(&json!({}), &ctx.caller);     let src = shot["savedTo"].as_str().filter(|_| shot["success"].as_bool() == Some(true))?.to_string();     let safe: String = regex::Regex::new(r#"[<>:"/\\|?*]"#).unwrap().replace_all(label, "").replace(' ', "_").chars().take(40).collect();-    let dir = std::path::Path::new("C:/tmp/conduit-screenshots");-    let _ = std::fs::create_dir_all(dir);+    let dir_buf = shots_dir(); let dir = dir_buf.as_path();     let ts = (now_f() * 1000.0) as u64;     let ext = std::path::Path::new(&src).extension().map(|e| e.to_string_lossy().to_string()).unwrap_or_else(|| "png".into());     let dst = dir.join(format!("fusion-{safe}-{ts}.{ext}"));@@ -848,7 +856,7 @@ pub fn capture_library_views(ctx: &Ctx, args: &Value, pass: &dyn Fn(&str, Value)     }     let n_ok = out.iter().filter(|o| o["ok"].as_bool() == Some(true)).count();     json!({"success": n_ok > 0, "captured": out,-        "_hint": format!("Captured {n_ok}/{} library-editor views to C:/tmp/conduit-screenshots (pull with desktop_pull_file). SYMBOL = full pinout; FOOTPRINT = pads + layer stack; COMPONENT = the Content Manager device view with the footprint<->package Mapped check + pin/pad counts. ⚠️ NAMES: the COMPONENT (.dev) view opens by DEVICESET name (e.g. R-0603-10K). But SYMBOL (.sym) and FOOTPRINT (.pac) open by the SYMBOL / PACKAGE name - in a SHARED-FOOTPRINT library (one symbol/footprint per package, many value devicesets) that is the COMBO name (e.g. 'R-0603'), NOT the deviceset name ('R-0603-10K'). Passing a deviceset name to .sym/.pac makes Fusion pop 'Create new symbol/footprint?'; the bridge now auto-dismisses that in the BACKGROUND (Cancel/No, no focus steal - see entries with dialogDismissed) and marks the entry ok:false. Pass libraryPath so names are resolved from the file up front.", out.len())})+        "_hint": format!("Captured {n_ok}/{} library-editor views to %TEMP%/adom-fusion-shots (ab pulls them to you as localFullPath; or pull with desktop_pull_file). SYMBOL = full pinout; FOOTPRINT = pads + layer stack; COMPONENT = the Content Manager device view with the footprint<->package Mapped check + pin/pad counts. ⚠️ NAMES: the COMPONENT (.dev) view opens by DEVICESET name (e.g. R-0603-10K). But SYMBOL (.sym) and FOOTPRINT (.pac) open by the SYMBOL / PACKAGE name - in a SHARED-FOOTPRINT library (one symbol/footprint per package, many value devicesets) that is the COMBO name (e.g. 'R-0603'), NOT the deviceset name ('R-0603-10K'). Passing a deviceset name to .sym/.pac makes Fusion pop 'Create new symbol/footprint?'; the bridge now auto-dismisses that in the BACKGROUND (Cancel/No, no focus steal - see entries with dialogDismissed) and marks the entry ok:false. Pass libraryPath so names are resolved from the file up front.", out.len())}) } fn entry_null(v: &Option<String>) -> bool { v.is_none() } @@ -1093,7 +1101,7 @@ pub fn show_attributes(ctx: &Ctx, args: &Value, pass: &dyn Fn(&str, Value) -> Va                     if let Ok(r) = ab.call("desktop_screenshot_window", json!({"hwnd": hwnd}), &ctx.caller, 20) {                         let img = r["image"].as_str().or(r["data"]["image"].as_str()).unwrap_or("");                         if let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(img) {-                            let dir = std::path::Path::new("C:/tmp/conduit-screenshots"); let _ = std::fs::create_dir_all(dir);+                            let dir_buf = shots_dir(); let dir = dir_buf.as_path();                             let dst = dir.join(format!("fusion-variant-attributes_{}-{}.png", ds.replace(|c: char| !c.is_ascii_alphanumeric(), "_"), (now_f() * 1000.0) as u64));                             if std::fs::write(&dst, bytes).is_ok() { dshot = json!(dst.to_string_lossy().replace('\\', "/")); }                         }
skills/fusion-driving/SKILL.md+1−1
@@ -106,7 +106,7 @@ ways to get the array, in order of preference: - 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_fusion`)-  are saved on the **Windows box** (`%TEMP%/conduit-screenshots/`). To see one, either re-capture that+  are saved on the **Windows box** (`%TEMP%/adom-fusion-shots/`) and, since 1.10.38, pulled to YOU automatically: read `localFullPath` / `localSafePath` in the reply. On older bridges, re-capture that   hwnd with `desktop_screenshot_window {hwnd}` (lands in your container) or `desktop_pull_file` it over.  Either way, LOOK - do not describe it from memory.
skills/fusion-multipart-libraries/SKILL.md+2−2
@@ -51,7 +51,7 @@ fusion_build_library_3d {   not have to `fusion_aps_browse` for them. - Returns `{partsBound, partsTotal, parts:[{package,success,wip_urn,dims_mm,error}], screenshots:   [{package,stage,path}], boundLbr, opened}`. The before/after PNGs land on the box-  (`%TEMP%/conduit-screenshots`) - `desktop_pull_file` them for a demo video.+  (`%TEMP%/adom-fusion-shots`); since 1.10.38 ab pulls them into your container automatically (`localFullPath` in each `screenshots[]` entry) - use those for a demo video. - **Each part is independent.** If Fusion resets mid-run, the parts already done kept their f3d   uploads; just re-run the whole `parts[]` (re-making a few f3ds is cheap) - the injection rewrites   all bindings in one pass, so a single final call always yields a complete library.@@ -93,7 +93,7 @@ still running server-side and finishes fine.** Do NOT treat that as failure. Ins  1. Wait, then **verify by the artifacts, not the response**: read the `boundLbr` and count    `<package3d name=` (should equal your part count), and list the before/after PNGs in-   `%TEMP%/conduit-screenshots`.+   `%TEMP%/adom-fusion-shots` (pulled to you as `localFullPath`). 2. If a part is missing (the FIRST part of a run is the usual casualty, as Fusion settles from a prior    view), **just re-run `fusion_build_library_3d` for the missing parts with `outLbrPath` pointing at    the SAME file.** The binding injection is **merge-aware + idempotent** (v1.6.1+): it reads the
tools/check-naming.sh

  
tools/check-portable.sh

  
tools/promote.sh

  
tools/release.sh