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
name the EXACT browser profile in the signin stage (profiles are dicts, my filter matched none and fell back to 'your browser'); return signinProfileChoices + forbid vague phrasing in the hints
6 files changed
+72−15
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.6.87+1.6.88
adom-bridge-fusion-manifest.json+5−5@@ -1,13 +1,13 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.6.87",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.87/adom-bridge-fusion-v1.6.87.zip",- "sha256": "9db224ca217b64d811f7b8d08be7357e35ff0ba2cad989a9ea9fbd29fd4610ed",- "size": 469400,+ "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, "verbPrefixes": [ "fusion_" ], "healthEndpoint": "/status",- "released_at": "2026-07-20T11:29:10Z"+ "released_at": "2026-07-20T11:35:43Z" }
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.6.87",+ "version": "1.6.88", "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+1−1@@ -1,6 +1,6 @@ { "slug": "adom-desktop-fusion-bridge",- "version": "1.6.87",+ "version": "1.6.88", "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": {
server.py+55−6@@ -3578,6 +3578,28 @@ def _orchestrate_fetch_optimized_glb(args: dict) -> dict: "message": "Job %s still %s; re-call fusion_fetch_optimized_glb to finish." % (job_id, res.get("status") or "processing")} +def _describe_profile(p: dict) -> str:+ """Human, UNAMBIGUOUS name for a browser profile - never just 'your browser'.++ A power user has several (personal / work / media), so a demo that says "it's in your+ Chrome" is useless (John, 2026-07-20). Produce e.g.+ "Chrome - John Personal ([email protected])" or "Edge - Default"."""+ browser = (p.get("browser") or "browser").strip()+ browser = {"chrome": "Chrome", "edge": "Edge", "brave": "Brave"}.get(browser.lower(), browser.title())+ name = (p.get("displayName") or "").strip()+ email = (p.get("email") or "").strip()+ bits = browser+ if name and email and name.lower() not in email.lower():+ bits += " - %s (%s)" % (name, email)+ elif email:+ bits += " - %s" % email+ elif name:+ bits += " - %s" % name+ elif p.get("profileDir"):+ bits += " - %s" % p.get("profileDir")+ return bits++ def _ad_call(verb: str, args: dict, timeout: int = 60) -> dict: """Call another AD verb (nbrowser_*, desktop_*) from inside this bridge. @@ -3693,20 +3715,40 @@ 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, "", ""+ choices = [] if nb_state == "ready": profs = _ad_call("nbrowser_profiles", {}, timeout=40) pdata = profs.get("data", profs) if isinstance(profs, dict) else {}- plist = [p for p in (pdata.get("profiles") or []) if isinstance(p, str) and ":" in p]- profile_used = plist[0] if plist else ""+ # profiles are DICTS: {profile,label,email,browser,displayName,profileDir,active,live,...}+ for p in (pdata.get("profiles") or []):+ if not isinstance(p, dict):+ continue+ if p.get("blocked") or p.get("unresolved") or not p.get("profile"):+ continue+ choices.append({+ "profile": p.get("profile"), "browser": (p.get("browser") or "").lower(),+ "email": p.get("email") or "", "displayName": p.get("displayName") or "",+ "profileDir": p.get("profileDir") or "",+ "active": bool(p.get("active")), "live": bool(p.get("live")),+ "asleep": bool(p.get("asleep")),+ "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))+ if pick:+ profile_used = pick["profile"]+ where = pick["describe"] oa = {"sessionId": "fusion-signin", "url": "https://accounts.autodesk.com/", "background": True, "thread": "fusion-demo", "purpose": "Autodesk sign-in for Fusion"} if profile_used: oa["profile"] = profile_used r = _ad_call("nbrowser_open_window", oa, timeout=60) opened = bool((r.get("data", r) if isinstance(r, dict) else {}).get("sessionId") or r.get("ok") or r.get("success"))- where = (profile_used.split(":", 1)[0].title() if profile_used else "your browser")- if profile_used and "@" in profile_used:- where += " (%s)" % profile_used.split(":", 1)[1]+ if not where:+ where = "your browser" if opened: steps.append("opened Autodesk sign-in in %s (background)" % (where or "browser")) else:@@ -3725,6 +3767,13 @@ 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>'}. ") ++ "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])'). "+ "`signinProfileChoices` lists every profile with describe/email/displayName/active - if "+ "there is more than one, say which you used and OFFER TO SWITCH (re-run with a different "+ "profile). If the one you want shows extensionInstalled:false it needs the one-time "+ "extension install in THAT profile. " "TELL THE USER WHERE IT IS WAITING and OFFER ALL THREE: (a) they finish it themselves " "whenever they want, (b) you foreground that window for them " "(nbrowser_switch_window / browser window state), or (c) THEY LET YOU DRIVE IT - ask "@@ -3740,7 +3789,7 @@ 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,- nbState=nb_state)+ signinProfileChoices=choices, nbState=nb_state) if not rd.get("ready"): return _out("waiting",
skills/fusion-demo/SKILL.md+9−1@@ -52,9 +52,17 @@ interrupted. The response tells you where it landed: | Field | Meaning | |---|---| | `signinOpened` | true if the background tab is up |-| `signinWhere` | human phrasing for WHERE, e.g. "Chrome ([email protected])" - say this out loud |+| `signinWhere` | the EXACT place, e.g. "Chrome - John Personal ([email protected])" - say this verbatim |+| `signinProfileChoices` | every profile (describe/email/displayName/active/live/extensionInstalled) | | `signinProfile` / `signinSession` | the ABE profile + sessionId (`fusion-signin`) to drive it | +**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+one you used and offer to switch to another. If the profile they want shows+`extensionInstalled:false`, it needs the one-time extension install in THAT specific profile+(extensions are per-profile).+ **Always tell the user where it is waiting, then offer all three paths:** 1. **They finish it themselves**, whenever they like - it is sitting there, backgrounded.