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 103 file(s)
10 files changed
+186−18
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.314+1.9.315
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.314",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.314/adom-bridge-fusion-v1.9.314.zip",- "sha256": "78001812a20ff59e3bbd3dc0f06a632ae17f13159c904b8cbaab08a9435ce209",- "size": 593655,+ "version": "1.9.315",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.315/adom-bridge-fusion-v1.9.315.zip",+ "sha256": "2743689adc299a92ca843d25c35b77eeb93d9fce353d53ae6ded36f9fc0e397d",+ "size": 595389, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.314",+ "version": "1.9.315", "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.9.314"+version = "1.9.315" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.314"+version = "1.9.315" 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.9.314"+DASH_VERSION="1.9.315" 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.9.314",+ "version": "1.9.315", "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−8@@ -1,6 +1,6 @@ { "slug": "fusion-bridge",- "version": "1.9.314",+ "version": "1.9.315", "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,9 +111,5 @@ "label": "Part volume", "prompt": "How much material does each printed part in this assembly use?" }- ],- "author": {- "name": "John Lauer",- "email": "[email protected]"- }-}+ ]+}
server.py+92@@ -2826,6 +2826,7 @@ COMMAND_HANDLERS = { float(args.get("alertsSinceSec", 300))), # Fusion's own error/warning toasts (the CEF overlays no window enumeration can see). "managed_library_create": lambda fi, args: _handle_managed_library_create(fi, args),+ "web_signin": lambda fi, args: _handle_web_signin(fi, args), "alerts": lambda fi, args: { "success": True, "alerts": _fusion_alerts(float(args.get("sinceSec", 300))),@@ -15729,6 +15730,97 @@ def _wait_for_dialog(title_substr: str, timeout_s: float = 20.0): return None +# ── Signing the user into Autodesk's WEB surfaces (John, 2026-09-01) ─────────+# Fusion's desktop app is signed in, but library.io, the hub portal (adom.autodesk360.com) and+# Autodesk Account are WEB properties with their own session, and pup starts with none. Every+# one of them offers "Continue with Google", and Adom already owns the user's Google identity,+# so ONE stored Google credential unlocks all of them for that user - no per-site passwords.+# The password itself never passes through this bridge or the AI: the user stores it once with+# ab's credential_set (OS keychain), and pup fills it in the background.+_AUTODESK_SIGNIN_HOST = "signin.autodesk.com"+++def _handle_web_signin(fusion_info: dict, args: dict) -> dict:+ """Sign a pup window into an Autodesk web surface, via Google SSO by default.++ args: {url (required), sessionId (default "autodesk-web"), provider ("google"|"password"),+ username (optional, for the Google chooser)}+ Returns where it got to, and NEVER guesses a credential: a missing password or a 2FA+ challenge is reported as a typed stop with the exact one-time command the USER runs.+ """+ import time as _t+ url = (args.get("url") or "").strip()+ if not url:+ return {"success": False, "error": "url is required (e.g. https://library.io/ or "+ "https://<hub>.autodesk360.com/)"}+ sess = args.get("sessionId") or "autodesk-web"+ provider = (args.get("provider") or "google").lower()+ user = args.get("username") or ""+ steps = []++ def _pup(verb, a, timeout=90):+ return _ad_call("pup_" + verb, a, timeout=timeout) or {}++ def _state():+ r = _pup("eval", {"sessionId": sess, "expression":+ "({url:location.href,body:document.body.innerText"+ ".replace(/\\s+/g,' ').slice(0,400)})"}, timeout=60)+ return (r.get("result") or {}) if isinstance(r, dict) else {}++ _pup("open_window", {"sessionId": sess, "url": url,+ "reason": args.get("reason") or "sign in to an Autodesk web surface"},+ timeout=120)+ _t.sleep(8)+ st = _state(); steps.append({"step": "open", "url": (st.get("url") or "")[:120]})+ body = str(st.get("body") or "")+ if _AUTODESK_SIGNIN_HOST not in str(st.get("url") or "") and "Sign in to Autodesk" not in body:+ return {"success": True, "alreadySignedIn": True, "sessionId": sess, "steps": steps,+ "_hint": "That surface was already authenticated in this pup profile."}++ if provider == "google":+ r = _pup("eval", {"sessionId": sess, "expression":+ "(()=>{const b=[...document.querySelectorAll('button,a,div[role=button]')]"+ ".find(e=>/Continue with Google/i.test(e.textContent));"+ "if(!b)return {found:false};b.click();return {clicked:true};})()"}, timeout=60)+ steps.append({"step": "google-sso", "result": (r.get("result") if isinstance(r, dict) else None)})+ _t.sleep(9)+ st = _state()+ if "accounts.google.com" not in str(st.get("url") or ""):+ return {"success": False, "errorCode": "google_sso_unavailable", "steps": steps,+ "state": (st.get("body") or "")[:200],+ "_hint": "The page did not hand off to Google. Retry with provider:'password'."}++ # Let pup fill from its vault. It never hands the secret to this bridge or to the AI.+ lg = _pup("login", {"sessionId": sess,+ "url": "https://accounts.google.com/" if provider == "google" else url,+ "username": user or None,+ "reason": "sign the user in so Adom can drive their Autodesk web surfaces"},+ timeout=120)+ steps.append({"step": "vault-login", "used": lg.get("used"), "ok": lg.get("success")})+ _t.sleep(8)+ st = _state()+ at = str(st.get("url") or "")+ body = str(st.get("body") or "")+ if "accounts.google.com" in at or _AUTODESK_SIGNIN_HOST in at:+ needs_2fa = any(k in body.lower() for k in ("verify it", "2-step", "verification",+ "authenticator", "code was sent"))+ return {"success": False,+ "errorCode": "human_credential_required" if not needs_2fa else "second_factor_required",+ "sessionId": sess, "steps": steps, "at": at[:120], "page": body[:200],+ "error": ("Stopped at the sign-in page: pup has no stored credential for this "+ "user" if not needs_2fa else+ "Google is asking for the second factor, which only the user can supply."),+ "_hint": ("ONE-TIME setup, run by the USER so no password ever reaches the AI:\n"+ " adom-bridge credential_set '{\"host\":\"accounts.google.com\",\"username\":"+ "\"<you>@<domain>\",\"password\":\"<your Google password>\"}'\n"+ "then re-run this verb. For 2FA, approve the prompt on your phone; the "+ "session then persists in pup's profile and later runs are silent.")}+ return {"success": True, "signedIn": True, "sessionId": sess, "steps": steps,+ "at": at[:120],+ "_hint": "Signed in. The session persists in pup's profile, so library.io, the hub "+ "portal and Autodesk Account are now drivable for this user."}++ def _handle_managed_library_create(fusion_info: dict, args: dict) -> dict: """Publish the library that is IN FRONT as a library.io managed library.
skills/fusion-web-signin/SKILL.mdadded+73@@ -0,0 +1,73 @@+---+name: fusion-web-signin+description: Sign an Adom user into Autodesk's WEB surfaces (library.io, the Fusion hub portal <hub>.autodesk360.com, Autodesk Account) from a container, using pup plus ONE stored Google credential - because Fusion's desktop app being signed in does NOT sign in the web. Covers the one-time credential_set the user runs, the Continue-with-Google SSO path, what 2FA does, and why the AI never sees the password. Trigger words - library.io login, autodesk sign in, autodesk360, fusion hub portal, hub invite, managed folder sharing, sign into autodesk in pup, continue with google, credential_set, fusion_web_signin.+---++# Signing an Adom user into Autodesk's web surfaces++**The gap this closes.** Fusion the desktop app is signed in as the user, and the Fusion bridge+drives it happily. But the things you need for SHARING - library.io (managed libraries and their+folders), the hub portal `<hub>.autodesk360.com` (Members and Roles, invites), Autodesk Account -+are WEB properties with their own session. pup starts with none, and none of Fusion's tokens carry+over. Measured 2026-09-01: `library.io` and `fusion.autodesk.com` both landed logged out in pup on+two machines, and `adom.autodesk360.com` redirects straight to `signin.autodesk.com`.++## The design: one Google credential per user, every Autodesk surface++Every Autodesk sign-in page offers **Continue with Google**, and Adom already owns the user's+Google identity (that is how `adom-google` works). So an Adom user stores ONE credential and every+Autodesk web surface becomes drivable - no per-site passwords, no password ever reaching the AI.++**One-time, run BY THE USER** (the secret goes to the OS keychain; the index keeps host+username+only, and `credential_set` never echoes it back):++```bash+adom-bridge credential_set '{"host":"accounts.google.com",+ "username":"<you>@<domain>",+ "password":"<your Google password>"}'+```++Then, from any container, for any Autodesk web surface:++```bash+adom-bridge fusion_web_signin '{"url":"https://library.io/"}'+adom-bridge fusion_web_signin '{"url":"https://<hub>.autodesk360.com/","sessionId":"hub"}'+```++`fusion_web_signin` opens a BACKGROUND pup window, detects the Autodesk sign-in page, clicks+Continue with Google, and lets **pup** fill the credential from the vault. The bridge and the AI+never hold the secret.++## What it returns, and the two honest stops++| result | meaning |+|---|---|+| `alreadySignedIn: true` | that pup profile already had the session; nothing to do |+| `signedIn: true` | signed in; the session persists in the pup profile, so later runs are silent |+| `errorCode: human_credential_required` | no stored credential for this user - the response carries the exact `credential_set` line for them to run |+| `errorCode: second_factor_required` | Google wants the second factor; only the user can approve it (phone prompt). After one approval the pup profile keeps the session |+| `errorCode: google_sso_unavailable` | the page did not hand off to Google - retry with `provider:"password"` |++**Never** invent, guess, or ask for the password in chat, and never click through a 2FA prompt on+the user's behalf: those are the two stops this verb exists to report cleanly rather than fake.++## Why an Adom user wants this (the sharing story)++Fusion's own Library Manager banner says it plainly: *"Import private libraries into this Fusion+Hub to avoid permission errors for team members. We recommend using Hub libraries going forward."*+Distribution therefore happens through **hub membership**, and hub membership is managed on the+web:++- hub portal > **Members and Roles > Invite** (email address; the invitee must accept)+- the inviting admin needs an assigned license; a basic-access user cannot invite+- a **team** hub can set Project Administrator / Editor / Viewer per member; a **personal** hub can+ invite to a project but cannot manage permissions at all++None of that is reachable from the Fusion API or the desktop ribbon - it is a web surface, which is+exactly why this verb exists.++## Related++- `dev-skills/fusion-cloud-libraries` - what a `.flbr` is, how a library becomes a library.io+ managed library, and the measured cross-account result (a managed library does NOT cross+ Autodesk accounts on its own).