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
v1.6.69: programmatic APS AUTH-001 self-heal - start_signin probes /authorize server-side, detects 'app lost Data Management API product' BEFORE opening a browser, returns errorCode aps_app_missing_product + the exact ABE-driven recovery recipe; full recovery playbook added to fusion-aps-signin skill
6 files changed
+112−6
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.6.68+1.6.69
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.6.68",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.68/adom-bridge-fusion-v1.6.68.zip",- "sha256": "0d16913c7b3f7bcd17c34d4419534ebc4ac008c43ec93eca56c7061ba90bae82",- "size": 362037,+ "version": "1.6.69",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.69/adom-bridge-fusion-v1.6.69.zip",+ "sha256": "c2445f85d630be582aec04e859ab2d3b5aba6ff34a3666e2892d58f7967d03b5",+ "size": 364866, "verbPrefixes": [ "fusion_" ],
aps.py+68@@ -95,6 +95,54 @@ APS_REQUIRED_HINT = ( "a few minutes to propagate, retry." ) ++def _aps_auth001_recovery_hint(client_id: str) -> str:+ """The programmatic self-heal for APS AUTH-001, returned by start_signin the moment it detects the+ app lost its Data Management API PRODUCT (John 2026-07-14: an AI figured this out live over an hour;+ no future AI should have to). The bridge can't drive aps.autodesk.com itself, so this is the exact+ ABE/nbrowser recipe the calling AI runs - deterministic, verified live."""+ return (+ "⚠ APS AUTH-001 - the APS app (Client ID " + (client_id[:8] + "...") + ") LOST its Data Management "+ "API product, so authorize fails BEFORE any login. This is fixable; here is the exact recovery "+ "(drive with ABE/nbrowser - if not installed, install adom-browser-extension first, it is the "+ "ONLY reliable way to drive the portal): "+ "(1) nbrowser_open_window {sessionId, thread, purpose, profile:<the warm Edge/Chrome profile>, "+ "url:'https://aps.autodesk.com/myapps/'} and open the app whose Client ID is this one. "+ "(2) On the app's App-settings page, API Access section: confirm 'Data Management API' is checked, "+ "then FORCE a re-commit - nbrowser_eval a click on the VISIBLE 'Save changes' button (filter "+ "buttons by /save changes/i AND offsetParent!==null; the bare text matches ~9 hidden duplicates so "+ "text-click is ambiguous). If Save is disabled, toggle any one unneeded API off first to dirty the "+ "form. (3) WAIT ~1-3 min for Autodesk to propagate. (4) Retry fusion_aps_signin; authorize now "+ "reaches the CONSENT screen ('<app> is requesting permission ... View/Manage/Write your data ... "+ "Allow') - nbrowser_navigate the session to the new authUrl, then nbrowser_click 'Allow'; the URL "+ "flips to localhost:<port>/fusion-aps/callback?code=... and the bridge captures the token. "+ "(5) Poll fusion_aps_status until signedIn:true. Verified live 2026-07-14."+ )+++def _probe_authorize_auth001(auth_url: str) -> bool:+ """Server-side probe: does this app's /authorize return AUTH-001 (no api-product access)? The check+ happens BEFORE any user login, so the bridge can detect a broken app WITHOUT a browser and hand back+ the recovery recipe immediately (instead of the AI opening a browser only to hit the JSON error).+ A healthy app 302-redirects to login/consent (no AUTH-001 body); a broken one returns the AUTH-001+ JSON. Never raises; returns False on any ambiguity (don't false-block a real sign-in)."""+ class _NoRedirect(urllib.request.HTTPRedirectHandler):+ def redirect_request(self, *a, **k):+ return None # a redirect means the app is FINE - stop, don't chase the login page+ try:+ opener = urllib.request.build_opener(_NoRedirect)+ try:+ body = opener.open(urllib.request.Request(auth_url), timeout=8).read().decode("utf-8", "replace")+ except urllib.error.HTTPError as e:+ if e.code in (301, 302, 303, 307, 308):+ return False # redirect to login = healthy app+ body = (e.read() or b"").decode("utf-8", "replace")+ except Exception:+ return False+ return ("AUTH-001" in body) or ("does not have access to the api product" in body.lower())+ except Exception:+ return False+ APS_DIR = Path(os.path.expanduser("~")) / ".adom" / "fusion-aps" CONFIG_FILE = APS_DIR / "config.json" TOKENS_FILE = APS_DIR / "tokens.json"@@ -449,6 +497,26 @@ def start_signin(args: dict | None = None) -> dict: } auth_url = AUTH_URL + "?" + urllib.parse.urlencode(params) + # ── PROGRAMMATIC AUTH-001 SELF-HEAL (John 2026-07-14) ────────────────────────────────────────+ # Detect a broken APS app (lost its Data Management API PRODUCT) SERVER-SIDE, before opening any+ # browser, and hand back the exact ABE-driven recovery recipe. Without this, the AI opens a browser,+ # hits a raw AUTH-001 JSON error page, and has to reverse-engineer the fix (it once took an hour).+ if _probe_authorize_auth001(auth_url):+ try:+ server.server_close()+ except Exception:+ pass+ with _signin_lock:+ _signin_state = None+ return {"success": False,+ "error": "APS AUTH-001: the app's Client ID does not have access to the Data Management API product.",+ "errorCode": "aps_app_missing_product",+ "authUrl": auth_url,+ "appManagementUrl": "https://aps.autodesk.com/myapps/",+ "clientId": client_id,+ "recoverable": True,+ "_hint": _aps_auth001_recovery_hint(client_id)}+ # ── SMART BROWSER CHOICE (John, 2026-07-08: "power users have tons of browsers with tons # of profiles ... cache what browser you used when you successfully logged in ... don't # be lazy") ─────────────────────────────────────────────────────────────────────────
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.6.68",+ "version": "1.6.69", "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.",
skills/fusion-aps-signin/SKILL.md+26@@ -245,3 +245,29 @@ Windows reserved ranges (5357, 27339, 50000-50059). The box runs ~67 listeners relevant when reading `fusion_aps_*` results (fields live under `data`). - Don't auto-enter the user's Autodesk **password** or drive their Google **consent** — identity decisions are theirs; automate the OTP + form-filling, not the credentials.++## ⭐ APS AUTH-001 self-heal — the app lost its Data Management API product (verified live 2026-07-14)++**Symptom:** `fusion_aps_signin` now returns `errorCode: "aps_app_missing_product"` (the bridge probes+the /authorize endpoint server-side and detects it BEFORE opening a browser), or you see the raw+`{"errorCode":"AUTH-001","developerMessage":"The client_id specified does not have access to the api+product"}` on the authorize page. It means the org's APS app at aps.autodesk.com/myapps lost its **Data+Management API** product entitlement (it can work for months, then break). The app config often still+*looks* correct — the fix is a forced re-commit.++**Recovery (drive with ABE/nbrowser — install the extension FIRST if it isn't; coordinate-clicking the+portal is fragile and Edge crashes):**+1. `nbrowser_open_window {sessionId, thread, purpose, profile:<warm Edge/Chrome>, url:"https://aps.autodesk.com/myapps/"}` → open the app whose Client ID matches (in the org's "Adom APS" dev hub).+2. App-settings → **API Access**: confirm **Data Management API** is checked. Then force a re-commit —+ `nbrowser_eval` a click on the **visible** Save button:+ `(()=>{var b=[...document.querySelectorAll("button")].filter(x=>/save changes/i.test(x.textContent)&&x.offsetParent!==null);if(b.length)b[0].click();return JSON.stringify({n:b.length})})()`+ (the bare text "Save changes" matches ~9 hidden duplicates, so `nbrowser_click {text}` is ambiguous —+ use the offsetParent filter). If Save is disabled, open the API dropdown and toggle one unneeded API+ off first to dirty the form.+3. **Wait 1–3 min** for Autodesk to propagate the entitlement.+4. Retry `fusion_aps_signin` → it now reaches the **consent** screen ("… is requesting permission …+ View/Manage/Write your data … **Allow**"). `nbrowser_navigate` the session to the fresh `authUrl`,+ then `nbrowser_click {text:"Allow"}` → the URL flips to `localhost:<port>/fusion-aps/callback?code=…`+ and the bridge's listener captures the token.+5. Poll `fusion_aps_status` until `signedIn:true`. Done — APS search is fast and never touches Fusion's+ main thread.
skills/fusion-autodesk-signin/SKILL.md+12@@ -98,3 +98,15 @@ drivable: `fusion_open_lbr`, `fusion_build_library_3d`, exports, GPU/graphics ch ## Related - `fusion-onboarding` — the install that precedes this (streams Fusion, then hands off here). - `fusion-bridge-dev` — how the bridge detects installed/installing/ready (Fusion360.exe completeness).++## ⭐ FIRST RULE FOR ANY BROWSER AUTOMATION: install + use ABE (nbrowser), don't coordinate-click (John, emphatic 2026-07-14)++The MOMENT a task needs you to DRIVE a browser (the Autodesk OAuth, the APS portal at+aps.autodesk.com/myapps, any web form), your FIRST move is to **recommend and install the+adom-browser-extension (ABE / nbrowser)** and drive via `nbrowser_*` (JS-level: read the DOM, click by+selector/text, reliably). Do NOT limp along with `desktop_click`/SendKeys coordinate-driving — on a VM+that Edge crashes, tabs multiply, buttons sit at the nav edge and clicks miss, and you waste enormous+time (learned the hard way here). Tell the user up front: "I need to install ABE to automate this+browser reliably." Install: `bridge_install {manifestUrl: .../adom-browser-extension/files/native-browser-bridge-manifest.json}`,+then the `installing-the-extension` flow, then `nbrowser_readiness` until `state:"ready"`. Coordinate-+driving is the fallback of last resort, never the default.