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 161 file(s)
11 files changed
+58−37
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.10.2+1.10.3
addin/AdomBridge/commands/electronics.py+7−2@@ -18,7 +18,7 @@ Workspace hierarchy: Commands that BLOCK (open dialogs — avoid from automation): WRITE — Opens Save As dialog ADD — Opens component picker dialog- SHOW <name> — Opens interactive highlight mode+ SHOW — bare SHOW waits for a click (blocked); SHOW <name> highlights and returns (allowed) GRID (no params) — Opens grid settings dialog EDIT <new>.sym — Opens "Create new?" confirmation dialog @@ -390,7 +390,12 @@ def handle_electron_run(app: adsk.core.Application, args: dict) -> dict: # a bare CHANGE (which enters an interactive mode) stays blocked. _swap = re.match(r"^\s*CHANGE\s+PACKAGE\s+'[^']+'\s*\(\s*-?[0-9.]+\s+-?[0-9.]+\s*\)\s*;?\s*$", eagle_cmd or "", re.I)- if cmd_verb in BLOCKED_COMMANDS and not _swap:+ # SHOW with an explicit object name ("SHOW NET_6", "SHOW GND", "SHOW R1") is a complete,+ # non-interactive command: it highlights the named objects and returns; only a bare SHOW+ # (which waits for a click) blocks automation. Issue #81: the acceptance thread's explicit+ # net highlight was refused while the published videos used exactly this form.+ _show_named = re.match(r"^\s*SHOW\s+[^;\s]+.*$", eagle_cmd or "", re.I)+ if cmd_verb in BLOCKED_COMMANDS and not _swap and not _show_named: return { "success": False, "error": f"Command '{cmd_verb}' is blocked for automation: {BLOCKED_COMMANDS[cmd_verb]}",
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.10.2",+ "version": "1.10.3", "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.2"+version = "1.10.3" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.10.2"+version = "1.10.3" 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.2"+DASH_VERSION="1.10.3" 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.2",+ "version": "1.10.3", "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.2",+ "version": "1.10.3", "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]"- }-}+ ]+}
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.2"+version = "1.10.3" edition = "2021" license = "MIT"
rust/bridge/src/services.rs+26@@ -204,12 +204,33 @@ fn design_rules_pick(idx: &Value, fab: &str, process: &str, layers: i64) -> Resu match hit { Some(h) => Ok((*h).clone()), None => { let mut lc: Vec<i64> = rows.iter().filter(|v| v["process"].as_str() == Some(&proc_)).map(|v| v["layerCount"].as_i64().unwrap_or(0)).collect::<std::collections::BTreeSet<_>>().into_iter().collect(); lc.sort(); Err(json!({"processes": procs, "layerCounts": lc})) } } } +/// Rules in a generated profile that native DRC turns into noise: a Copper Clearance with+/// samesignal="yes" over an all/all scope flags every joint where a trace meets its own pad,+/// via or next segment as an Overlap warning (31 on the six-net fixture, page issue #82).+fn profile_rule_warnings(edru: &str) -> Vec<Value> {+ let mut out = Vec::new();+ for line in edru.lines() {+ let l = line.trim();+ if !l.starts_with("<rule ") || !l.contains("samesignal=\"yes\"") || l.contains("enabled=\"no\"") { continue; }+ let attr = |k: &str| l.split(&format!("{k}=\"")).nth(1).and_then(|r| r.split('"').next()).unwrap_or("").to_string();+ let (one, other) = (attr("onescope"), attr("otherscope"));+ let broad = |s: &str| s.is_empty() || s == "all" || s.contains("wire");+ if broad(&one) && broad(&other) {+ out.push(json!({"rule": attr("name"), "type": attr("type"), "value": attr("value"), "onescope": one, "otherscope": other,+ "why": "same-signal clearance over traces: every trace joint, pad entry and via joint is at zero distance, so native DRC reports each one as an Overlap warning",+ "fix": "disable or rescope this rule in the profile on adom/pcb-design-rules (an smd-to-pad same-signal check is fine; all/all is not)"}));+ }+ }+ out+}+ /// `fusion_set_design_rules`: a fab's rule pack from the wiki into the open board. pub fn set_design_rules(ctx: &Ctx, args: &Value, pass: &dyn Fn(&str, Value) -> Value) -> Value { let mut a = args.clone(); if !a.is_object() { a = json!({}); } let action = a["action"].as_str().unwrap_or("apply").to_lowercase(); let fab = a["fab"].as_str().unwrap_or("").trim().to_string(); let mut dru_path = a["druPath"].as_str().unwrap_or("").replace('\\', "/");+ let mut rule_warnings: Vec<Value> = Vec::new(); if action == "list" { let idx = match design_rules_index(&ctx.state_dir) { Ok(i) => i, Err(e) => return json!({"success": false, "errorCode": "design_rules_page_unreachable", "error": e.chars().take(200).collect::<String>(), "_hint": "The wiki page adom/pcb-design-rules did not answer; the bundled Adom InstaPCB rules still apply with no fab argument."}) }; let rows: Vec<Value> = idx["vendors"].as_array().cloned().unwrap_or_default().iter().map(|v| json!({"fab": v["vendorSlug"], "vendor": v["vendor"], "process": v["process"], "layers": v["layerCount"], "available": v["filesGenerated"].as_bool().unwrap_or(false)})).collect();@@ -228,6 +249,7 @@ pub fn set_design_rules(ctx: &Ctx, args: &Value, pass: &dyn Fn(&str, Value) -> V let (code, edru) = fusion_core::aps::http_bytes(&format!("{DESIGN_RULES_PAGE}/{rel}")); if code != 200 { return json!({"success": false, "errorCode": "design_rules_download_failed", "error": format!("HTTP {code}"), "file": rel}); } if !edru.windows(12).any(|w| w == b"<designrules") { return json!({"success": false, "errorCode": "design_rules_invalid", "file": rel, "error": "The downloaded file is not an EAGLE design rules document."}); }+ rule_warnings = profile_rule_warnings(&String::from_utf8_lossy(&edru)); let stage = std::env::var("TEMP").ok().filter(|s| !s.is_empty()).map(std::path::PathBuf::from).unwrap_or_else(std::env::temp_dir).join("adom-fusion").join("design-rules"); let _ = std::fs::create_dir_all(&stage); dru_path = stage.join(rel.rsplit('/').next().unwrap_or(&rel)).to_string_lossy().replace('\\', "/");@@ -237,6 +259,10 @@ pub fn set_design_rules(ctx: &Ctx, args: &Value, pass: &dyn Fn(&str, Value) -> V } a["druPath"] = json!(dru_path); a["action"] = json!("apply"); let mut res = pass("set_design_rules", a);+ if res.is_object() && !rule_warnings.is_empty() {+ res["ruleWarnings"] = json!(rule_warnings);+ res["_hintRuleWarnings"] = json!("The loaded profile carries at least one rule that native DRC will report at every ordinary trace joint (see ruleWarnings). Expect Overlap WARNINGS (code 19, type 2) at pad entries, via joints and segment joints until the profile is corrected on the adom/pcb-design-rules page; they are not defects in the copper. Different-signal overlaps stay ERRORS and still count.");+ } if res.is_object() { if pack.is_object() { if !res["data"].is_object() { res["data"] = json!({}); } res["data"]["pack"] = pack; res["data"]["source"] = json!("https://wiki.adom.inc/adom/pcb-design-rules"); } res["_hint"] = json!(format!("{} Confirm with fusion_alerts {{sinceSec:60}}: an empty list means Fusion accepted the rules; a toast means it refused the file.", res["_hint"].as_str().unwrap_or("")).trim().to_string());
skills/fusion-routing/SKILL.md+7−1@@ -72,7 +72,13 @@ presentation tasks, not prerequisites for routing a board. ## Validate and save Refill copper before measuring connectivity. Run native DRC before and after with the same-selected rules, reporting inherited and new violations separately. `fusion_routing_drc` reads+selected rules, reporting inherited and new violations separately. Since 1.10.3 each entry in+`drc.errors` carries `layer`, `x`, `y` (mm) and `drc.summary` counts them by description; use+the coordinates to find the objects (UL_ERROR has no object members). Overlap WARNINGS (code 19,+type 2) at pad entries, via joints and segment joints are same-signal overlaps raised by a+profile rule such as "Same-net track spacing" (all/all, samesignal=yes); `fusion_set_design_rules`+reports such rules in `ruleWarnings` when it loads a profile. They are rule noise, not copper+defects; a different-signal overlap is an ERROR (type 0) and still counts. `fusion_routing_drc` reads approved errors as well; require `drc.checked`, clean applicable rules and no remaining connections before calling the result connected and DRC-clean. A diagnostic rule profile is not certification under the manufacturer's full profile. Check schematic parity/ERC and