← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.336\ No newline at end of file+1.9.337\ No newline at end of file
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]"-  }-}\ No newline at end of file+  ]+}
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.