Adom Desktop Control
Public Made by Adomby adom
Drive the user's real desktop from the cloud: their Chrome/Edge, headless pup, native UI, the Hydrogen editor, sibling Claude tabs, plus screen recording and video post.
name: native-browser-control description: > Drive the user's REAL logged-in Chrome or Edge from the cloud via the Adom browser extension (CDP), in the background, no focus steal. Use to click/scroll/ read a page in their actual browser, upload a file, or log them in through OAuth using their existing sessions. Trigger words: nbrowser, drive my chrome, drive edge, real browser, logged-in browser, upload to a website, sign in with google, oauth login, CDP eval browser.
Native browser control (nbrowser)
nbrowser_* drives the user's real, logged-in Chrome/Edge through the
adom-browser-extension over CDP. Because it's their actual browser, their cookies
and logins are already there, so a JLCPCB/Autodesk/Google flow "just works" with no
credentials handed over. It runs in the background (no focus steal).
Core verbs
adom-desktop nbrowser_status '{}' # {ok, ext, browser:"chrome"|"edge"}
adom-desktop nbrowser_list_tabs '{}' # all tabs (tabId, url, windowId, active)
adom-desktop nbrowser_open_tab '{"url":"https://..."}' # opens a DEDICATED tab, returns tabId
adom-desktop nbrowser_eval '{"tabId":"<id>","expr":"<js>"}' # run JS in that tab (background)
adom-desktop nbrowser_screenshot '{"tabId":"<id>"}' # page PNG as base64 (background)
adom-desktop nbrowser_navigate '{"tabId":"<id>","url":"..."}'
Chrome vs Edge, this trips everyone
nbrowser defaults to Chrome. To drive Edge, pass "browser":"edge" on EVERY
verb. nbrowser_list_tabs without it shows only Chrome tabs.
adom-desktop nbrowser_list_tabs '{"browser":"edge"}'
adom-desktop nbrowser_eval '{"browser":"edge","tabId":"<id>","expr":"location.href"}'
Gotchas (each cost a real debugging session)
- It refuses to drive PWA / installed-app tabs (e.g. a taskbar Gmail/Chat PWA) to
avoid hijacking a standalone app. Open a dedicated tab with
nbrowser_open_tab. - A first
evalcan returnnullright after opening a tab (timing). Retry; it works. - No frame-targeting.
evalruns in the TOP frame only. Params likeallFrames,frameUrl,frameare ignored (they silently return the top frame). You CANNOT reach into a cross-origin iframe (iframe.contentDocumentthrows /accessible:false). This is why deeply-nested webviews (the Hydrogen Claude panel) are unreachable, see hydrogen-web-control. nbrowser_evalruns in the PAGE context, so JS can click, scroll, read, and set inputs, but only same-origin.
Upload a file (DataTransfer, no OS picker)
CDP can't touch the native file dialog, so inject the bytes and set the
<input type=file> directly. Base64 the file, stream it into the page in <40k
chunks (the CLI arg cap), then build a File and set it via DataTransfer:
# per chunk: comma operator = ONE expression (nbrowser_eval requires a single expression)
adom-desktop nbrowser_eval '{"tabId":"<id>","expr":"(window.__b += \"<chunk>\", window.__b.length)"}'
# then decode + set the input:
adom-desktop nbrowser_eval '{"tabId":"<id>","expr":"(function(){var s=atob(window.__b),u=new Uint8Array(s.length);for(var i=0;i<s.length;i++)u[i]=s.charCodeAt(i);var f=new File([u],\"board.zip\",{type:\"application/zip\"});var inp=document.querySelectorAll(\"input[type=file]\")[0];var dt=new DataTransfer();dt.items.add(f);inp.files=dt.files;inp.dispatchEvent(new Event(\"change\",{bubbles:true}));return inp.files[0].name})()"}'
The input often clears itself right after the change event (the site read the file already), that is NORMAL, don't treat the empty input as failure. Verify by screenshot.
Drive an OAuth login yourself (NEVER ask the user)
You can log the user in end to end. Proven on JLCPCB "Sign in with Google":
- Click the page's "Sign in with Google" button (
nbrowser_eval.click()). - On
accounts.google.com, CDP eval WORKS (it's not blocked). Click the user's account row, then the OAuth Continue button, both viaeval.click(). - It redirects back logged in; any file you already uploaded persists in the session.
This is one-time per site; afterwards the session stays logged in and every run is fully background. Pick the user's primary work Google account if there's a chooser. NEVER ask the user to click or log in, you can do all of it.
When CDP eval can't reach a control
If a page genuinely blocks eval on an element, screenshot the window, map the pixel, and fall back to a foregrounded OS click, see desktop-ui-control. But try eval first, it's background and doesn't disturb the user.
Related: pup-browser-control (anonymous browsers), screen-recording (recording a live drive).
---
name: native-browser-control
description: >
Drive the user's REAL logged-in Chrome or Edge from the cloud via the Adom
browser extension (CDP), in the background, no focus steal. Use to click/scroll/
read a page in their actual browser, upload a file, or log them in through OAuth
using their existing sessions. Trigger words: nbrowser, drive my chrome, drive
edge, real browser, logged-in browser, upload to a website, sign in with google,
oauth login, CDP eval browser.
---
# Native browser control (nbrowser)
`nbrowser_*` drives the user's **real, logged-in Chrome/Edge** through the
`adom-browser-extension` over CDP. Because it's their actual browser, their cookies
and logins are already there, so a JLCPCB/Autodesk/Google flow "just works" with no
credentials handed over. It runs in the **background** (no focus steal).
## Core verbs
```bash
adom-desktop nbrowser_status '{}' # {ok, ext, browser:"chrome"|"edge"}
adom-desktop nbrowser_list_tabs '{}' # all tabs (tabId, url, windowId, active)
adom-desktop nbrowser_open_tab '{"url":"https://..."}' # opens a DEDICATED tab, returns tabId
adom-desktop nbrowser_eval '{"tabId":"<id>","expr":"<js>"}' # run JS in that tab (background)
adom-desktop nbrowser_screenshot '{"tabId":"<id>"}' # page PNG as base64 (background)
adom-desktop nbrowser_navigate '{"tabId":"<id>","url":"..."}'
```
## Chrome vs Edge, this trips everyone
`nbrowser` defaults to **Chrome**. To drive Edge, pass `"browser":"edge"` on EVERY
verb. `nbrowser_list_tabs` without it shows only Chrome tabs.
```bash
adom-desktop nbrowser_list_tabs '{"browser":"edge"}'
adom-desktop nbrowser_eval '{"browser":"edge","tabId":"<id>","expr":"location.href"}'
```
## Gotchas (each cost a real debugging session)
- **It refuses to drive PWA / installed-app tabs** (e.g. a taskbar Gmail/Chat PWA) to
avoid hijacking a standalone app. Open a **dedicated tab** with `nbrowser_open_tab`.
- **A first `eval` can return `null`** right after opening a tab (timing). Retry; it works.
- **No frame-targeting.** `eval` runs in the TOP frame only. Params like `allFrames`,
`frameUrl`, `frame` are **ignored** (they silently return the top frame). You CANNOT
reach into a **cross-origin iframe** (`iframe.contentDocument` throws / `accessible:false`).
This is why deeply-nested webviews (the Hydrogen Claude panel) are unreachable, see
[hydrogen-web-control](../hydrogen-web-control/SKILL.md).
- `nbrowser_eval` runs in the PAGE context, so JS can click, scroll, read, and set
inputs, but only same-origin.
## Upload a file (DataTransfer, no OS picker)
CDP can't touch the native file dialog, so inject the bytes and set the
`<input type=file>` directly. Base64 the file, **stream it into the page in <40k
chunks** (the CLI arg cap), then build a `File` and set it via `DataTransfer`:
```bash
# per chunk: comma operator = ONE expression (nbrowser_eval requires a single expression)
adom-desktop nbrowser_eval '{"tabId":"<id>","expr":"(window.__b += \"<chunk>\", window.__b.length)"}'
# then decode + set the input:
adom-desktop nbrowser_eval '{"tabId":"<id>","expr":"(function(){var s=atob(window.__b),u=new Uint8Array(s.length);for(var i=0;i<s.length;i++)u[i]=s.charCodeAt(i);var f=new File([u],\"board.zip\",{type:\"application/zip\"});var inp=document.querySelectorAll(\"input[type=file]\")[0];var dt=new DataTransfer();dt.items.add(f);inp.files=dt.files;inp.dispatchEvent(new Event(\"change\",{bubbles:true}));return inp.files[0].name})()"}'
```
The input often **clears itself right after** the change event (the site read the file
already), that is NORMAL, don't treat the empty input as failure. Verify by screenshot.
## Drive an OAuth login yourself (NEVER ask the user)
You can log the user in end to end. Proven on JLCPCB "Sign in with Google":
1. Click the page's "Sign in with Google" button (`nbrowser_eval` `.click()`).
2. On `accounts.google.com`, **CDP eval WORKS** (it's not blocked). Click the user's
account row, then the OAuth **Continue** button, both via `eval` `.click()`.
3. It redirects back logged in; any file you already uploaded persists in the session.
This is one-time per site; afterwards the session stays logged in and every run is
fully background. Pick the user's **primary work** Google account if there's a chooser.
NEVER ask the user to click or log in, you can do all of it.
## When CDP eval can't reach a control
If a page genuinely blocks eval on an element, screenshot the window, map the pixel,
and fall back to a foregrounded OS click, see [desktop-ui-control](../desktop-ui-control/SKILL.md).
But try eval first, it's background and doesn't disturb the user.
Related: [pup-browser-control](../pup-browser-control/SKILL.md) (anonymous browsers),
[screen-recording](../screen-recording/SKILL.md) (recording a live drive).