Adom Chip Fetcher
Public Made by Adomby adom
Your whole parts library — manufacturer-grade chip CAD (symbol, footprint, 3D) one tap from your EDA tool.
adom-desktop v1.4.7 follow-ups — three small fixes
Paste this into Claude on the desktop where the adom-desktop Rust + plugins/puppeteer source lives. Three loosely-related items I hit while integrating the v1.4.7 features into chip-fetcher today.
Background
I'm running adom-desktop from inside an Adom container. Just verified v1.4.7's two new features end-to-end via the WAGO datasheet recipe:
browser_input_dispatch type:"click"with{x, y}coords ✅ (trusted click,isTrusted=true, framework handler fires)browser_list_tabsshowingopener:"popup"fortarget=_blankform-submit popups ✅
These work. Three small refinements would have saved me ~20 min of debugging:
Item 1 — browser_input_dispatch with selector: should disambiguate or error on ambiguity
What bit me
WAGO's modal has a Generate data sheet button at the bottom of the modal. The button's class is wg-button wg-button--primary. I called:
adom-desktop browser_input_dispatch '{
"sessionId":"chip-fetcher",
"type":"click",
"selector":"button.wg-button--primary"
}'
The relay returned {"ok":true, "dispatched":"click"}. I assumed the click landed on the Generate button. It didn't — the modal stayed up. After a browser_eval to inspect, the page actually had four button.wg-button--primary elements:
- The cookie/consent dismiss button (54x54, no text, top-right corner)
- "Add to shopping cart" (visible)
- "Add to shopping cart" (
display:noneduplicate) - "Generate data sheet" (the one I wanted, at y≈779)
Puppeteer's page.click(selector) resolves to the first match in document order, so the cookie X button got clicked. The relay reported success — because, mechanically, a trusted click was dispatched — but it wasn't the click I wanted.
What I want
Either of these would have saved the debug round:
Option A — error on ambiguity by default:
{"ok":false,"error":"selector matched 4 elements; pass exactNth or use coords","matched":4}
With an opt-in firstMatch:true arg if the caller knows the first match is correct.
Option B — pick smartly (preferred):
Filter the matched set to visible elements with non-zero text content before picking, and tie-break by largest visual area or by being inside the topmost stacking context (modals/dialogs). This is what a human would do — "click the visible Generate button," not "click the first DOM-order match." Most call sites benefit; ambiguity is usually the cookie-banner-still-up + button-of-the-same-class case I hit.
Either way, return the matched count + the rect of the element actually clicked so the caller can sanity-check:
{
"ok": true,
"dispatched": "click",
"matchedCount": 4,
"clickedRect": {"x":890, "y":803, "w":193, "h":48},
"clickedText": "Generate data sheet"
}
Workaround in the meantime
I'm computing rects via browser_eval and passing {x, y} coords to browser_input_dispatch instead of selectors. Works fine but verbose; selectors should "just work" for the obvious case.
Item 2 — fetching a PDF from a Chrome PDF-viewer popup tab returns the viewer HTML, not the binary
What bit me
WAGO's "Generate Datasheet" form-submit POSTs to wago.priintcloud.com/datasheets/<MPN>/<lang> with target="_blank". The response is Content-Type: application/pdf. Chrome opens it as a popup tab, which v1.4.7's browser_list_tabs correctly tracks as opener:"popup".
But when I do this against the popup tab to grab the PDF binary:
adom-desktop browser_eval '{
"sessionId":"chip-fetcher",
"tabId":"tab-2",
"expr":"(async()=>{const r=await fetch(location.href,{credentials:\"include\"}); const ab=await r.arrayBuffer(); /* base64-encode and return */})()",
"awaitPromise":true
}'
…I get a 194 KB stub HTML wrapper that starts with newlines, not the multi-MB PDF binary. Confirmed: document.contentType reports application/pdf but the in-page fetch is being intercepted by Chrome's PDF Viewer Plugin shell rather than hitting the network.
The PDF binary IS reachable via direct POST replay (I did that as a workaround — read the bundled JS, extracted the priintcloud endpoint + form params, ran the POST inside a regular tab via fetch() and got 3.8 MB of real PDF). But that defeats the purpose of the popup feature — the whole point of the popup was to avoid the bundle-reading reverse-engineering.
What I want
A way to grab the popup tab's actual served bytes without hitting the PDF viewer wrapper. Options:
Option A — browser_save_tab_as <path>: Pup uses puppeteer's page.pdf() for HTML pages, or for application/pdf tabs, simply re-issues the original request via the network layer with the tab's session cookies and saves the response body. Returns {ok, savedTo, bytes, contentType}.
adom-desktop browser_save_tab_as '{
"sessionId":"chip-fetcher",
"tabId":"tab-2",
"saveTo":"/path/in/container/wago.pdf"
}'
Option B — browser_fetch_url <url>: Generic "fetch this URL with the active session's cookies and return base64 binary." Caller passes the popup's URL (read via browser_eval tabId:"tab-2", expr:"location.href"). Implementation: puppeteer-side page.context.request.get(url, {headers:{...}}) returns the body without going through the rendered tab.
adom-desktop browser_fetch_url '{
"sessionId":"chip-fetcher",
"url":"https://wago.priintcloud.com/datasheets/2601-3105/en/abc?attempt=1&signature=...",
"saveTo":"/tmp/wago.pdf"
}'
# returns {ok:true, bytes:3798336, contentType:"application/pdf"}
Option B is more general (also handles the case where the click doesn't open a popup but just downloads). Option A is more focused on "I have this popup tab and want its bytes." Either solves the WAGO case cleanly.
Either way: include Content-Type + size in the result so the caller can validate before writing to disk.
Workaround
For now I'm reading the JS bundle, finding the underlying API the form submit hits, and replaying the POST directly. Brittle (bundle hashes change every deploy) but works.
Item 3 — document node.exe in the bridge-stale-after-upgrade list
What bit me
After v1.4.6 → v1.4.7 desktop upgrade, the relay rejected browser_input_dispatch with "Unknown command" even though the new Linux CLI binary's strings output had the command and --version was correct. Restarting adom-desktop serve on the container side didn't help — the relay was fine; the forwarded-to puppeteer bridge (node.exe on Windows) was stale, still loaded the old plugins/puppeteer/server.js from before the upgrade.
The fix was taskkill //f //im node.exe on the desktop. That's documented in CLAUDE.md for the KiCad / Fusion bridges (python.exe) but node.exe (puppeteer) wasn't mentioned in that list.
What I want
Add node.exe to whatever auto-graceful-restart logic / docs the python.exe bridges use. If there's an installer post-step that does taskkill on stale bridges, include node.exe there. If it's documented in CLAUDE.md / README, mention node.exe next to python.exe.
A user-facing message in the relay would also help: when the relay sees the bridge return "Unknown command" for a command that's in the relay's known-commands table, it could automatically respond:
{
"ok": false,
"error": "puppeteer bridge appears stale (returned Unknown command for a known action)",
"hint": "On Windows: taskkill /f /im node.exe; the bridge will auto-respawn on next browser_* call",
"stale_bridge_suspected": true
}
That self-diagnostic + hint would have saved most of the debug time.
Suggested PR shape
- Item 1 — add visibility-filter + ambiguity-error to
browser_input_dispatch's selector path inplugins/puppeteer/server.js. ReturnmatchedCount+clickedRect+clickedTextin the response so callers can verify. - Item 2 — add
browser_fetch_urlaction that uses puppeteer'sBrowserContext.request(orpage.context().request) to fetch arbitrary URLs with the session's cookies, bypassing PDF-viewer interception. OptionalsaveTofor direct disk write; otherwise base64 in the response. - Item 3 — add
node.exeto the bridge-respawn docs + ideally the relay-side stale-bridge self-diagnostic hint.
Once shipped, please bump the version + remind me to refresh /usr/local/bin/adom-desktop from the wiki + taskkill //f //im node.exe on first run.
— sent from john's container running chip-fetcher v0.1.x
# adom-desktop v1.4.7 follow-ups — three small fixes
Paste this into Claude on the desktop where the `adom-desktop` Rust + plugins/puppeteer source lives. Three loosely-related items I hit while integrating the v1.4.7 features into chip-fetcher today.
---
## Background
I'm running `adom-desktop` from inside an Adom container. Just verified v1.4.7's two new features end-to-end via the WAGO datasheet recipe:
- `browser_input_dispatch type:"click"` with `{x, y}` coords ✅ (trusted click, `isTrusted=true`, framework handler fires)
- `browser_list_tabs` showing `opener:"popup"` for `target=_blank` form-submit popups ✅
These work. Three small refinements would have saved me ~20 min of debugging:
---
## Item 1 — `browser_input_dispatch` with `selector:` should disambiguate or error on ambiguity
### What bit me
WAGO's modal has a `Generate data sheet` button at the bottom of the modal. The button's class is `wg-button wg-button--primary`. I called:
```bash
adom-desktop browser_input_dispatch '{
"sessionId":"chip-fetcher",
"type":"click",
"selector":"button.wg-button--primary"
}'
```
The relay returned `{"ok":true, "dispatched":"click"}`. I assumed the click landed on the Generate button. It didn't — the modal stayed up. After a `browser_eval` to inspect, the page actually had **four** `button.wg-button--primary` elements:
1. The cookie/consent dismiss button (54x54, no text, top-right corner)
2. "Add to shopping cart" (visible)
3. "Add to shopping cart" (`display:none` duplicate)
4. "Generate data sheet" (the one I wanted, at y≈779)
Puppeteer's `page.click(selector)` resolves to the **first match in document order**, so the cookie X button got clicked. The relay reported success — because, mechanically, *a* trusted click was dispatched — but it wasn't the click I wanted.
### What I want
Either of these would have saved the debug round:
**Option A — error on ambiguity by default:**
```json
{"ok":false,"error":"selector matched 4 elements; pass exactNth or use coords","matched":4}
```
With an opt-in `firstMatch:true` arg if the caller knows the first match is correct.
**Option B — pick smartly (preferred):**
Filter the matched set to **visible elements with non-zero text content** before picking, and tie-break by largest visual area or by being inside the topmost stacking context (modals/dialogs). This is what a human would do — "click the visible Generate button," not "click the first DOM-order match." Most call sites benefit; ambiguity is usually the cookie-banner-still-up + button-of-the-same-class case I hit.
Either way, return the matched count + the rect of the element actually clicked so the caller can sanity-check:
```json
{
"ok": true,
"dispatched": "click",
"matchedCount": 4,
"clickedRect": {"x":890, "y":803, "w":193, "h":48},
"clickedText": "Generate data sheet"
}
```
### Workaround in the meantime
I'm computing rects via `browser_eval` and passing `{x, y}` coords to `browser_input_dispatch` instead of selectors. Works fine but verbose; selectors should "just work" for the obvious case.
---
## Item 2 — fetching a PDF from a Chrome PDF-viewer popup tab returns the viewer HTML, not the binary
### What bit me
WAGO's "Generate Datasheet" form-submit POSTs to `wago.priintcloud.com/datasheets/<MPN>/<lang>` with `target="_blank"`. The response is `Content-Type: application/pdf`. Chrome opens it as a popup tab, which v1.4.7's `browser_list_tabs` correctly tracks as `opener:"popup"`.
But when I do this against the popup tab to grab the PDF binary:
```bash
adom-desktop browser_eval '{
"sessionId":"chip-fetcher",
"tabId":"tab-2",
"expr":"(async()=>{const r=await fetch(location.href,{credentials:\"include\"}); const ab=await r.arrayBuffer(); /* base64-encode and return */})()",
"awaitPromise":true
}'
```
…I get a **194 KB stub HTML wrapper** that starts with newlines, not the multi-MB PDF binary. Confirmed: `document.contentType` reports `application/pdf` but the in-page fetch is being intercepted by Chrome's PDF Viewer Plugin shell rather than hitting the network.
The PDF binary IS reachable via direct POST replay (I did that as a workaround — read the bundled JS, extracted the priintcloud endpoint + form params, ran the POST inside a regular tab via `fetch()` and got 3.8 MB of real PDF). But that defeats the purpose of the popup feature — the whole point of the popup was to avoid the bundle-reading reverse-engineering.
### What I want
A way to grab the popup tab's actual served bytes without hitting the PDF viewer wrapper. Options:
**Option A — `browser_save_tab_as <path>`:** Pup uses puppeteer's `page.pdf()` for HTML pages, or for `application/pdf` tabs, simply re-issues the original request via the network layer with the tab's session cookies and saves the response body. Returns `{ok, savedTo, bytes, contentType}`.
```bash
adom-desktop browser_save_tab_as '{
"sessionId":"chip-fetcher",
"tabId":"tab-2",
"saveTo":"/path/in/container/wago.pdf"
}'
```
**Option B — `browser_fetch_url <url>`:** Generic "fetch this URL with the active session's cookies and return base64 binary." Caller passes the popup's URL (read via `browser_eval` `tabId:"tab-2", expr:"location.href"`). Implementation: puppeteer-side `page.context.request.get(url, {headers:{...}})` returns the body without going through the rendered tab.
```bash
adom-desktop browser_fetch_url '{
"sessionId":"chip-fetcher",
"url":"https://wago.priintcloud.com/datasheets/2601-3105/en/abc?attempt=1&signature=...",
"saveTo":"/tmp/wago.pdf"
}'
# returns {ok:true, bytes:3798336, contentType:"application/pdf"}
```
Option B is more general (also handles the case where the click doesn't open a popup but just downloads). Option A is more focused on "I have this popup tab and want its bytes." Either solves the WAGO case cleanly.
**Either way: include `Content-Type` + size in the result so the caller can validate before writing to disk.**
### Workaround
For now I'm reading the JS bundle, finding the underlying API the form submit hits, and replaying the POST directly. Brittle (bundle hashes change every deploy) but works.
---
## Item 3 — document `node.exe` in the bridge-stale-after-upgrade list
### What bit me
After v1.4.6 → v1.4.7 desktop upgrade, the relay rejected `browser_input_dispatch` with `"Unknown command"` even though the new Linux CLI binary's `strings` output had the command and `--version` was correct. Restarting `adom-desktop serve` on the container side didn't help — the relay was fine; the forwarded-to puppeteer bridge (`node.exe` on Windows) was stale, still loaded the old `plugins/puppeteer/server.js` from before the upgrade.
The fix was `taskkill //f //im node.exe` on the desktop. That's documented in CLAUDE.md for the **KiCad / Fusion bridges (`python.exe`)** but `node.exe` (puppeteer) wasn't mentioned in that list.
### What I want
Add `node.exe` to whatever auto-graceful-restart logic / docs the python.exe bridges use. If there's an installer post-step that does `taskkill` on stale bridges, include `node.exe` there. If it's documented in CLAUDE.md / README, mention `node.exe` next to `python.exe`.
A user-facing message in the relay would also help: when the relay sees the bridge return `"Unknown command"` for a command that's in the relay's known-commands table, it could automatically respond:
```json
{
"ok": false,
"error": "puppeteer bridge appears stale (returned Unknown command for a known action)",
"hint": "On Windows: taskkill /f /im node.exe; the bridge will auto-respawn on next browser_* call",
"stale_bridge_suspected": true
}
```
That self-diagnostic + hint would have saved most of the debug time.
---
## Suggested PR shape
- **Item 1** — add visibility-filter + ambiguity-error to `browser_input_dispatch`'s selector path in `plugins/puppeteer/server.js`. Return `matchedCount` + `clickedRect` + `clickedText` in the response so callers can verify.
- **Item 2** — add `browser_fetch_url` action that uses puppeteer's `BrowserContext.request` (or `page.context().request`) to fetch arbitrary URLs with the session's cookies, bypassing PDF-viewer interception. Optional `saveTo` for direct disk write; otherwise base64 in the response.
- **Item 3** — add `node.exe` to the bridge-respawn docs + ideally the relay-side stale-bridge self-diagnostic hint.
Once shipped, please bump the version + remind me to refresh `/usr/local/bin/adom-desktop` from the wiki + `taskkill //f //im node.exe` on first run.
— sent from john's container running chip-fetcher v0.1.x