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
profile selection by EVIDENCE: probe every live profile with nbrowser_login_state against accounts.autodesk.com and pick the one holding a real Autodesk session; report the reason + flag ambiguity instead of guessing 'active'
6 files changed
+81−19
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.6.88+1.6.89
adom-bridge-fusion-manifest.json+5−5@@ -1,13 +1,13 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.6.88",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.88/adom-bridge-fusion-v1.6.88.zip",- "sha256": "1561b38456bdbaac98e91936a0e02f6e9adc8d6d48f9185887b6e67c0eae2ef1",- "size": 470464,+ "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, "verbPrefixes": [ "fusion_" ], "healthEndpoint": "/status",- "released_at": "2026-07-20T11:35:43Z"+ "released_at": "2026-07-20T11:37:59Z" }
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.6.88",+ "version": "1.6.89", "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.88",+ "version": "1.6.89", "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." }
server.py+54−9@@ -3715,6 +3715,7 @@ def _orchestrate_demo(args: dict) -> dict: nb_data = nb.get("data", nb) if isinstance(nb, dict) else {} nb_state = nb_data.get("state") opened, where, profile_used = False, "", ""+ pick_reason, ambiguous = "", False choices = [] if nb_state == "ready": profs = _ad_call("nbrowser_profiles", {}, timeout=40)@@ -3734,10 +3735,46 @@ def _orchestrate_demo(args: dict) -> dict: "extensionInstalled": p.get("extensionInstalled", True), "describe": _describe_profile(p), })- # prefer: currently ACTIVE + live, then any live, then anything- pick = (next((c for c in choices if c["active"] and c["live"]), None)- or next((c for c in choices if c["live"]), None)- or (choices[0] if choices else None))+ # PROBE each profile for a REAL Autodesk session - never guess by "active".+ # (John, 2026-07-20: picking the active profile grabbed his PERSONAL Chrome with+ # zero analysis. A power user's Autodesk login can live in any profile, so ASK THE+ # BROWSER, don't assume.) nbrowser_login_state reports auth cookies per profile.+ for c in choices:+ if not c["live"]:+ c["autodesk"] = "unprobed(asleep)"+ continue+ ls = _ad_call("nbrowser_login_state",+ {"profile": c["profile"], "url": "https://accounts.autodesk.com/"},+ timeout=45)+ lsd = ls.get("data", ls) if isinstance(ls, dict) else {}+ if lsd.get("ok") is None and lsd.get("loggedIn") is None:+ c["autodesk"] = "unprobed"+ else:+ c["autodesk"] = "signed-in" if lsd.get("loggedIn") else "signed-out"+ c["autodeskConfidence"] = lsd.get("confidence")+ c["autodeskCookies"] = lsd.get("cookieCount")+ _CONF = {"high": 3, "medium": 2, "low": 1}+ signed = [c for c in choices if c.get("autodesk") == "signed-in"]+ signed.sort(key=lambda c: (_CONF.get(c.get("autodeskConfidence"), 0),+ c.get("autodeskCookies") or 0), reverse=True)+ pick = signed[0] if signed else None+ if pick:+ pick_reason = ("it is the profile actually signed into Autodesk (%s auth cookies, %s "+ "confidence)" % (pick.get("autodeskCookies"), pick.get("autodeskConfidence")))+ else:+ # NOTHING is signed in - do not silently guess. Prefer a work/corporate identity+ # over a consumer mailbox (Autodesk seats are usually work accounts), but SAY SO+ # and offer the alternatives.+ _CONSUMER = ("gmail.com", "outlook.com", "hotmail.com", "yahoo.com", "icloud.com", "live.com")+ def _work_first(c):+ em = (c.get("email") or "").lower()+ return (0 if (em and not any(em.endswith("@" + d) for d in _CONSUMER)) else 1,+ 0 if c["live"] else 1)+ ranked = sorted([c for c in choices if c["live"]] or choices, key=_work_first)+ pick = ranked[0] if ranked else None+ pick_reason = ("no profile has a live Autodesk session, so I picked the most likely "+ "one (work/corporate identity first) - CONFIRM WITH THE USER")+ ambiguous = not signed or len(signed) > 1 if pick: profile_used = pick["profile"] where = pick["describe"]@@ -3754,10 +3791,11 @@ def _orchestrate_demo(args: dict) -> dict: else: steps.append("could not auto-open the browser sign-in: %s" % (nb.get("_adCallError") or nb_state or "unknown")) if opened:- narrate = ("I've opened the Autodesk sign-in for you in %s - it's sitting in the "- "background so it won't interrupt what you're doing. Finish it whenever you "- "like, or say the word and I'll bring it up front, or I can just complete it "- "for you." % where)+ narrate = ("I've opened the Autodesk sign-in in %s - I chose that profile because %s. "+ "It's sitting in the background so it won't interrupt what you're doing. "+ "Finish it whenever you like, tell me to bring it up front, or I can complete "+ "it for you. If that's the wrong account, say which profile to use."+ % (where, pick_reason)) else: narrate = ("Fusion needs your Autodesk sign-in. I'd like to use your own browser - you're " "probably already signed in there, so it should take one click.")@@ -3767,6 +3805,12 @@ def _orchestrate_demo(args: dict) -> dict: if opened else "Auto-open failed - open it yourself: nbrowser_open_window " "{sessionId:'fusion-signin', url:'https://accounts.autodesk.com/', background:true, " "profile:'<chrome:their-account>'}. ") ++ "The profile was CHOSEN BY EVIDENCE, not guessed: each live profile was probed with "+ "nbrowser_login_state against accounts.autodesk.com, and the one holding a real Autodesk "+ "session wins (see signinProfileReason + per-profile autodesk/autodeskConfidence/"+ "autodeskCookies in signinProfileChoices). RELAY THAT REASON to the user. If "+ "signinProfileAmbiguous is true (nothing signed in, or several are), ASK them which "+ "account their Autodesk login belongs to instead of assuming. " "NAME THE EXACT PROFILE - never say just 'your browser' or 'your Chrome'. Power users " "run several profiles (personal / work / media), so say the browser AND the identity " "from `signinWhere` (e.g. 'Chrome - John Personal ([email protected])'). "@@ -3789,7 +3833,8 @@ def _orchestrate_demo(args: dict) -> dict: "fusion-autodesk-signin."), needsSignin=True, signinOpened=opened, signinWhere=where, signinProfile=profile_used, signinSession="fusion-signin" if opened else None,- signinProfileChoices=choices, nbState=nb_state)+ signinProfileChoices=choices, signinProfileReason=pick_reason,+ signinProfileAmbiguous=ambiguous, nbState=nb_state) if not rd.get("ready"): return _out("waiting",
skills/fusion-demo/SKILL.md+18@@ -56,6 +56,24 @@ interrupted. The response tells you where it landed: | `signinProfileChoices` | every profile (describe/email/displayName/active/live/extensionInstalled) | | `signinProfile` / `signinSession` | the ABE profile + sessionId (`fusion-signin`) to drive it | +### Pick the profile by EVIDENCE, never by "whichever is active"++`fusion_demo` probes **every live profile** with+`nbrowser_login_state {profile, url:"https://accounts.autodesk.com/"}` and picks the one that+actually holds an Autodesk session (auth cookies), reporting why in `signinProfileReason` and the+per-profile `autodesk` / `autodeskConfidence` / `autodeskCookies` in `signinProfileChoices`.++This exists because the first version just grabbed the *active* profile and landed in John's+**personal** Chrome with no analysis at all - on another machine that would have been simply wrong.+A user's Autodesk seat can live in any profile, and it is frequently NOT the one they happen to be+using. **Ask the browser; do not assume.**++- **Relay the reason** to the user ("I used X because it's the profile actually signed into Autodesk").+- If `signinProfileAmbiguous` is true (nothing signed in anywhere, or several are), **ASK which+ account their Autodesk login belongs to** rather than picking for them.+- Profiles that are asleep or lack the extension come back `unprobed` - say so instead of silently+ skipping them.+ **Never say "it's in your browser" or even "your Chrome".** A power user runs several profiles - personal, work, media - and a vague pointer sends them hunting through windows. Always name the browser AND the identity from `signinWhere`. If `signinProfileChoices` has more than one, say which