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 105 file(s)
13 files changed
+333−23
BRIDGE_VERSION+1−1addin/AdomBridge/commands/modeling.py+15−1adom-bridge-fusion-manifest.json+4−4bridge.json+1−1dashboard/Cargo.lock+1−1dashboard/Cargo.toml+1−1fusion_detect.py+14install.sh+1−1package.json+1−1page.json+11−8server.py+45−4skills/fusion-cloud-library-import/SKILL.md+125skills/fusion-managed-libraries/SKILL.md+113BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.336+1.9.337
addin/AdomBridge/commands/modeling.py+15−1@@ -71,7 +71,21 @@ def handle_run_modeling_script(app: adsk.core.Application, args: dict) -> dict: if not script: return {"success": False, "error": "No script provided.", "_hint": "Pass {\"script\": \"<python using adsk.fusion>\"}. Globals available: "- "adsk, app, ui, design (active Design, created if none open)."}+ "adsk, app, ui, design (the ACTIVE Design, or None - see _hintDesign).",+ "_hintDesign": "`design` is None whenever the front document is not a Design "+ "(a library, a drawing, the data panel). This verb will NOT "+ "displace what the user is looking at to make one. Pass "+ "createDesign:true if your script genuinely needs a fresh Design; "+ "the response then reports createdDocument.",+ "_hintPartVsAssembly": "A Fusion PART DESIGN document holds exactly ONE "+ "component. Importing a second body into one fails with "+ "'2 : InternalValidationError : results', and the real "+ "reason is only visible as a CEF toast (read it with "+ "fusion_alerts): 'Part Design documents can only contain "+ "one component'. To gang several models into one view, "+ "create a fresh document with "+ "app.documents.add(adsk.core.DocumentTypes."+ "FusionDesignDocumentType) inside the script."} ui = app.userInterface # Ensure there's a design to model into — but ONLY if this script actually models.
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.336",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.336/adom-bridge-fusion-v1.9.336.zip",- "sha256": "ae61a19e611d8ecce81bea7d0b25e1c6608ccd34a4281b57003eb6df8763b6a3",- "size": 606982,+ "version": "1.9.337",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.337/adom-bridge-fusion-v1.9.337.zip",+ "sha256": "81133169acd9a22d0d70c4ea504b688ec6296a1f4ce7cc7f9adf935af3c5336c",+ "size": 608661, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.336",+ "version": "1.9.337", "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.336"+version = "1.9.337" dependencies = [ "serde_json", ]
dashboard/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.336"+version = "1.9.337" edition = "2021" [dependencies]
fusion_detect.py+14@@ -744,6 +744,20 @@ def find_licensing_dialog(): ww, hh = w["width"], w["height"] if ww < 480 or hh < 180: return False # narrow side panels / toolbar strips are not modals+ # ORIENTATION is the tell that finally separates a modal from a DOCKED PANEL.+ # Fusion's docked rails (Content Manager, the layer/properties column) are owned,+ # titled "Fusion360" exactly like the seat dialog, and pass every size gate above:+ # measured 600x1620 on ConfRoomROG, which reported licensingDialog:true and pinned+ # ready:false on a completely healthy Fusion (twice, 2026-09-01). Every real seat /+ # sign-in modal is LANDSCAPE and SHORT - 823x262, 1235x527, 1646x702 - i.e. at least+ # ~2.3x wider than tall and never more than about a third of the app's height. A+ # docked rail is portrait (600x1620 = 0.37 wide-to-tall) and spans ~78% of the app.+ # Both tests are ratios, so they stay right on any DPI or display (the lesson the+ # absolute-pixel comments above were already shouting about).+ if ww < hh * 1.3:+ return False # portrait = a docked rail, not a modal+ if hh > 0.60 * max(1, _main["height"]):+ return False # full-height = a docked rail, not a modal return (ww * hh) <= 0.85 * _main_area # Primary: an OWNED popup that is DIALOG-SHAPED (a modal, not a narrow side panel).
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.336"+DASH_VERSION="1.9.337" 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.336",+ "version": "1.9.337", "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.336",+ "version": "1.9.337", "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+45−4@@ -12055,9 +12055,26 @@ def _handle_prove_library(fusion_info: dict, args: dict) -> dict: # packages (LAN7800 "", -HV, -SB, -RR). The symbol is one frame; the footprint and the # bound 3D are one frame PER PACKAGE, each named by the package, so every variant is # proved, not just the first device's.- _pkgs = ((_library_artifact_names(lbr).get("dev_pac") or {}).get(want) or [])+ _names = _library_artifact_names(lbr)+ _pkgs = ((_names.get("dev_pac") or {}).get(want) or [])+ # A FOOTPRINT is always addressed by its PACKAGE name, never by the deviceset's. When the+ # deviceset lookup comes back empty - which happens when the caller passed a LIBRARY name+ # (or any label that is not a deviceset) as `want` - the old code fell back to `want` and+ # issued EDIT <want>.pac, producing the nonsense "No footprint named 'Ray_FT60F210'" on a+ # bind that had actually succeeded. Fall back to the library's ONE package when it is+ # unambiguous, and otherwise say plainly that the name does not resolve.+ _pkg_fallback_note = None+ if not _pkgs:+ _all_pac = _names.get("pac") or []+ if len(_all_pac) == 1:+ _pkgs = list(_all_pac)+ _pkg_fallback_note = ("deviceset %r is not in this library; it has exactly one "+ "package (%s), so the footprint was proved against that"+ % (want, _all_pac[0]))+ elif want in _all_pac:+ _pkgs = [want] # `want` IS a package name _multi = len(_pkgs) > 1- _pk_iter = _pkgs if _multi else [None]+ _pk_iter = _pkgs if _pkgs else [None] # 1 + 2: the two editors, each full screen. if "symbol" not in _skip: r = _show_library_view(want, "sym", lbr)@@ -12068,12 +12085,24 @@ def _handle_prove_library(fusion_info: dict, args: dict) -> dict: evidence.append(_emit(_prove_capture("symbol", "symbol-editor"))) if "footprint" not in _skip: for _pk in _pk_iter:- r = _show_library_view(_pk or want, "pac", lbr)+ if _pk is None:+ evidence.append(_emit({+ "step": "footprint", "view": "footprint-editor", "ok": False,+ "package": None,+ "error": "Cannot resolve a package to prove: %r is neither a deviceset nor "+ "a package in this library." % want,+ "_hint": "Pass the DEVICESET name (the part), not the library name. This "+ "library has devicesets %s and packages %s."+ % ((_names.get("dev") or [])[:8], (_names.get("pac") or [])[:8])}))+ continue+ r = _show_library_view(_pk, "pac", lbr) if not r.get("success"): evidence.append(_emit({"step": "footprint", "view": "footprint-editor", "ok": False, "package": _pk, "error": str(r.get("error"))[:160]})) continue _ef = _prove_capture("footprint", "footprint-editor"); _ef["package"] = _pk+ if _pkg_fallback_note:+ _ef["_note"] = _pkg_fallback_note evidence.append(_emit(_ef)) # 3: the 3D editor. Only reachable when a real package3d is bound - with none, Fusion # shows its Placeholder, which is exactly the false pass this verb exists to prevent.@@ -13355,7 +13384,19 @@ def _handle_create_library(fusion_info: dict, args: dict) -> dict: "kicadFootprintPath (KiCad sources).", "_hint": "Shape A: {libraryPath, stepPath}. Shape B: {kicadSymbolPath, " "kicadFootprintPath, stepPath, pinMap?}. stepPath is required for "- "a PROVEN library - without it there is no real 3D body."}+ "a PROVEN library - without it there is no real 3D body.",+ "_hintParts": "`parts:[...]` is a SUPPLEMENT, not an input on its own: it says "+ "which STEP binds to which package INSIDE the library you also "+ "pass as libraryPath. Passing only parts[] lands here.",+ "_hintLibraryPath": "libraryPath is the INPUT .lbr that must already exist. It "+ "is NOT an output path - naming a file you have not written "+ "yet fails preflight with lbr_preflight_failed "+ "('file-readable: cannot find the file'). To write the bound "+ "library somewhere else, use outLbrPath.",+ "_hintWholeLibrary": "Importing a whole wiki Cloud Library? Do not assemble "+ "parts[] by hand - `adom-wiki library export <ref> --format "+ "fusion` gives a zip, and fusion_install_library_bundle "+ "{zipPath} pairs every deviceset to its STEP for you."} # SHAPE B FAILS AT THE FRONT DOOR, never three stages in (#43 follow-up, 2026-08-21): # the old flow staged files, launched Fusion (a minute of the user's time), and THEN # discovered adom-lbr is absent - which on Windows/Mac it always is, by platform rule.
skills/fusion-cloud-library-import/SKILL.mdadded+125@@ -0,0 +1,125 @@+---+name: fusion-cloud-library-import+description: Take a wiki Cloud Library (a cart of component pages) all the way into Fusion as a real EAGLE library with a rendering 3D chip on every package, and PROVE it with screenshots. Covers adom-wiki library create/add/availability/export, staging the bundle onto the Windows box, fusion_install_library_bundle, polling fusion_prove_result to a verdict, what the four evidence frames mean (symbol, footprint, package3d, ganged), and the traps that make a healthy run look broken. Use when a user says import my cloud library into Fusion, add these parts to Fusion, build a Fusion library from the wiki, prove the 3D came across, or asks why an import is stuck. Trigger words - cloud library, fusion import, install library bundle, library export fusion, prove library, ganged view, package3d, prove_result, partsBound, verdict proven.+---++# Wiki Cloud Library to Fusion, end to end++The lane a normal user actually walks: they have a Cloud Library on the wiki (component pages in+a cart) and they want it in Fusion with real 3D. Measured over three full runs 2026-09-01, 15+parts total.++## The whole flow++```bash+# 1. Build the cart (or the user already has one)+adom-wiki library create "My Parts" --description "..." # -> lib_xxxxxxxx+adom-wiki library add lib_xxxxxxxx <owner-slug> # repeat per part+adom-wiki library show lib_xxxxxxxx # confirm items resolved++# 2. CHECK BEFORE YOU BUILD - which parts can even produce a Fusion library+adom-wiki library availability lib_xxxxxxxx # summary {kicad, fusion, altium}++# 3. Export the Fusion bundle (zip: merged .lbr + parts/*.lbr + 3dmodels/*.step)+adom-wiki library export lib_xxxxxxxx --format fusion -o parts.zip+```++```jsonc+// 4. Stage onto the Windows box (Docker -> desktop). NOTE the arg names.+send_files {"filePaths":["/abs/container/parts.zip"], "reason":"..."} // lands in Downloads+// 5. One verb does pairing + bind + prove+fusion_install_library_bundle {"zipPath":"C:/Users/<user>/Downloads/parts.zip"}+// -> {runId, stage:"bind", parts:[{deviceset, package, modelPath}], unbound:[]}+// 6. Poll to a verdict (minutes, not seconds)+fusion_prove_result {"runId":"<runId>"}+```++**Read `unbound[]` on the install response.** It names any deviceset that got no STEP. Those+parts ship 2D-only; everything else still binds.++## Timing, so you do not think it hung++Measured on a 4K desktop: **~7 minutes to bind 5 packages**, then **~10 minutes** to capture 20+evidence frames. `partsBound` stays `None` for the whole bind phase and then jumps to N at once;+`evidenceCount` climbs one frame at a time afterwards. Poll every ~30s. A 20-minute total is+normal, not a hang.++## The four evidence frames, per part++| step | view | what it proves |+|---|---|---|+| `symbol` | symbol-editor | the pinout drew |+| `footprint` | footprint-editor | the land pattern drew (one frame PER package for a multi-variant deviceset) |+| `package3d` | 3d-editor | a REAL chip body is bound, not Fusion's Placeholder |+| `ganged` | library-manager | Content Manager: symbol + package table + **Mapped ✓** + the 3D preview together |++**`ganged` here means the Content Manager frame, NOT a multi-chip scene.** If someone asks for+"the ganged-up view" meaning all the chips side by side in one 3D window, no verb produces that -+build it yourself (recipe below).++`verdict: "proven"` plus `evidence` with zero `ok:false` entries is the pass. Anything else,+read the failing frames; they carry the reason.++## partsBound can be LESS than the part count, and that is correct++A library SHARES footprints. Two parts on the same package produce one `package3d`, and the+second part's prove step reports `sharedPackageProof: "<the other part>"`. Measured: 5 parts,+`partsBound: 4`, because UCC28C44DR and AL3353S-13 are both SOIC-8. Judge the run by+`provedParts` (one entry per part, each `proven: true`), not by `partsBound`.++## Pin 1 is checked for you++Every bound part reports `pin1: {status, rotatedDeg, mark_mm, pad1_mm}`. `aligned` means the+chip's pin-1 mark and pad 1 agree. `rotatedDeg` non-zero means the binder rotated the model to+make them agree. `no_mark_on_chip` is honest, not a failure: that STEP has no pin-1 feature to+align against. This is the number one thing EEs check, so quote it in your summary.++## Traps++- **`fusion_create_library` shapes.** `libraryPath` is the **INPUT** `.lbr` and must already+ exist. Naming an output file there fails preflight with `lbr_preflight_failed`+ ("file-readable: cannot find the file"). `parts:[...]` is a supplement that binds STEPs to+ packages inside that library, never an input by itself - passing only `parts` returns+ `no_input`. For a whole cloud library, do not hand-assemble `parts[]` at all; use+ `fusion_install_library_bundle`.+- **`send_files` takes `filePaths` (array) and lands in the user's Downloads** unless you pass+ `dest`. `pull_file` also takes `filePaths`, not `path`.+- **A stuck `bind` with no dialog and no alert is usually a broken document, not a broken+ chain.** Screenshot Fusion and check `fusion_alerts` - Fusion's real complaint is often a CEF+ toast invisible to both Win32 and UIA.+- **`fusion_readiness` can report `ready:false` on a healthy Fusion.** The install does not gate+ on it, so try the verb before believing the flag; screenshot to confirm.+- **The export refuses broken parts rather than shipping them.** `LIB_EXPORT_PARTIAL` names each+ skipped part and why. A real one seen live: `pin "VCC" -> pad "8" (no such pad in package+ 'SOT-23-6')`. That refusal is doing you a favour: ONE bad connect/pad pair makes Fusion reject+ the ENTIRE merged library. Swap the part out and re-export.++## Building a real ganged 3D view (all chips in one window)++No verb does this. Import the staged STEPs into ONE fresh design and lay them out:++```python+# fusion_run_modeling_script - MUST create its own document: a Part Design holds ONE component,+# and importing a second body into one fails "InternalValidationError : results".+doc = app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)+d = adsk.fusion.Design.cast(app.activeProduct); root = d.rootComponent; im = app.importManager+x = 0.0+for fn in files:+ im.importToTarget(im.createSTEPImportOptions(base + fn), root); adsk.doEvents()+ oc = root.occurrences.item(root.occurrences.count - 1); bb = oc.boundingBox+ # rotate the THINNEST axis into Z so the chip lies flat, then butt it up against the last one+ # (full recipe: fusion-multipart-libraries)+ x += width + 0.4+vp = app.activeViewport; cam = vp.camera+cam.viewOrientation = adsk.core.ViewOrientations.IsoTopRightViewOrientation+cam.isSmoothTransition = False; vp.camera = cam; vp.fit(); vp.refresh()+```++Then `desktop_screenshot_window {hwnd}` on Fusion's main window. Chips land in a row, flat,+proportional, pin-1 dimples visible. That is the frame people actually want in a demo.++## Clean up after yourself++The chain leaves scratch `Untitled` documents. `fusion_close_document {name:"Untitled",+discard:true}` in a small loop; the API reports every scratch doc as plain `Untitled` even when+the tab strip shows `Untitled(2)`, so loop rather than matching the visible name.
skills/fusion-managed-libraries/SKILL.mdadded+113@@ -0,0 +1,113 @@+---+name: fusion-managed-libraries+description: Fusion's library.io MANAGED libraries - create, link, unlink, push, pull, and the ONLY way to share one across Autodesk accounts (managed-folder membership, invited by email from library.io's website). Every step is a verb; none of it needs the AI to click a ribbon. Also documents why managed libraries do NOT scale as a distribution channel and what to do instead. Use when a user asks to publish a library to library.io, share a library with another account or another user, link a library to a managed library, push a new version, or asks why a shared library is not showing up in someone else's Fusion. Trigger words - managed library, library.io, share library, publish library, managed folder, cross account, link library, push library, pull library, Library Manager, private libraries.+---++# Managed libraries on library.io++The verbs (all shipped, all proven live 2026-09-01 on ConfRoomROG):++| Verb | Does |+|---|---|+| `fusion_managed_library_create` | make a NEW managed library from the hub library in front |+| `fusion_managed_library_link {name?}` | no name: opens the picker, returns its rows as records, cancels. With name: selects + confirms |+| `fusion_managed_library_unlink` | detach from the managed library |+| `fusion_managed_library_push` | push a NEW VERSION up |+| `fusion_managed_library_pull {onUpdatePrompt?}` | pull the newest version down |+| `fusion_library_manager {tab}` | open Library Manager on hub / public / private |+| `fusion_alerts {sinceSec}` | Fusion's own toasts, which are invisible to Win32 AND UIA |++**A LIBRARY must be in front** for any of these. `fusion_open_by_urn {urn}` or+`fusion_open_lbr {filePath}` first - and verify, because `fusion_activate_document` can report+success while the visible tab never changed. The honest oracle is the window TITLE plus the+ribbon in a screenshot (a library shows COMPONENT / DOCUMENT / MANAGE / UTILITIES; a design+shows SOLID / SURFACE / MESH).++## ⛔ Every action ends in a dialog, and every one BLOCKS the add-in++This is the single most important operational fact. Clicking a LIBRARY.IO menu item never+completes an action; it opens a dialog, and an unanswered dialog leaves the add-in reporting an+empty `activeDocument` ("add-in is busy with a long-running command") until something answers it.++| Action | Dialog | Answer |+|---|---|---|+| Push (linked) | **Create New Version** [Create] [Cancel] | Create - this is what actually pushes |+| Push (unlinked) | **Create Managed Library** | that is `..._create`'s job |+| Link (confirmed) | **Update** [Merge] [**Replace**] [Cancel] | Cancel by default: Replace DESTROYS the local library |+| Pull (differs) | **Update** (same 3 buttons) | Merge |+| Pull (current) | **Up to Date** [OK] | OK, reported as `upToDate` |++So the success test for each verb is "the dialog I expected appeared AND I answered it AND it+closed" - never "no alerts". These dialogs are CEF-drawn and expose NOTHING to UIA, so they are+answered by clicking a fraction of the dialog's own rect from `desktop_list_windows` (those rects+are already in the input verbs' physical-px space - do NOT route through a screenshot coordMap,+the shotId is not readable from inside the bridge).++Verify a dialog CLOSED by waiting for its ABSENCE. A helper that returns the moment it SEES the+window reports the window list's one-beat lag as "still open".++## Sharing across Autodesk accounts: managed-folder membership++**A managed library does NOT cross accounts on its own.** Proven: a library created under one+account was absent from the other account's Library Manager. Both accounts even show a folder+with the SAME name (each account's own personal folder), so name matching means nothing.++The real mechanism is one dialog on library.io's WEBSITE. There is no API: the authenticated+library.io endpoints reject the APS token and there is no published write API.++1. Sign in to <https://library.io/> as the OWNING account. It lands on+ `library.io/hubs/<id>-<Folder>/libraries`.+2. Click **Manage Members** (Owner only) -> the *Managed Folder Members* dialog: a member table+ plus "Invite new members". The fine print matters: "Invited collaborators will be able to view+ **and edit** all contents in this Managed Folder". There is no read-only role.+3. Send the invite. The invitee shows as **Pending**.+4. The invitee gets mail from `[email protected]` with **Accept** / **Reject**. Accept -> Autodesk+ sign-in -> "Congratulations! You've been successfully added to <Folder>."+5. They now see the folder as **Member** and can **Import into Fusion Hub** each library, which+ brings the **3D models across**, not just symbols and footprints.++### ⚠️ The step nobody would guess: PUSH after they join++Membership alone does not surface a library in the member's Fusion. Measured: after the invite+was accepted, only the library that had been pushed since the invite appeared (13 -> 14 entries);+two others stayed invisible even after reopening Library Manager to force a refetch. Pushing a+new version of a second library made it appear too (14 -> 15).++**So distribution is: invite once, then `fusion_managed_library_push` each library once.** A+library not pushed since a member joined looks like sharing is broken when it is not.++The member's folder picker (Source > Library.io > My Folders) then lists TWO folders with the+same name, theirs and yours. Filter by folder, not by name.++## ⛔ Do not build mass distribution on this++For a team of ten it is fine. As a channel for many users it fails on its own terms:++- membership is granted only through a website, one email at a time, with no API;+- each user must accept an invite by hand;+- every library must be re-pushed after each new member joins;+- the invite grants **edit** rights on everything in the folder, with no read-only role;+- import is slow (minutes for a handful of packages).++**The scalable alternative** is the ordinary bind chain: mint each package's `.f3d` ONCE, cache+the bytes centrally, then per user upload that same f3d into THEIR OWN hub and inject THEIR urn.+A `urn:adsk.wipprod:fs.file:vf.<id>` is scoped to a hub and access-controlled, so pasting one+account's urn into another account's library is not a shortcut - Fusion's own Library Manager+banner warns "Import private libraries into this Fusion Hub to avoid **permission errors for team+members**", and Autodesk's own sharing COPIES content into the consumer's hub rather than+referencing the owner's. See `fusion-multipart-libraries` for the bind chain.++## Signing in to the web half: pup already has the session++Do NOT reach for `credential_set` first, and do NOT drive the user's real browser. pup's default+shared **adom-you** profile carries live Google sessions, so library.io sign-in is passwordless:+`pup_open_window {url:"https://library.io/"}`, click **Sign In**, click **Continue with Google**,+click the account row. Done.++Two pup quirks that cost a cycle: `pup_click` needs `selector` or `x`+`y` (there is no `text`), so+tag the element first with `pup_eval` (`e.id="..."`) and click that; and `pup_fill` takes+**`text`**, not `value`.++pup cannot act as an account it has no session for. To read mail for a DIFFERENT identity (say to+accept an invite), use **`adom-google --account <name>`** rather than driving Gmail in a browser -+see the `adom-google` skill.