Download

Browser (Puppeteer) verbs

Drive Chrome-for-Testing: navigate, eval, screenshot, record, tabs/windows, credentials.

Part of the Adom Desktop verb reference. Invoke as adom-desktop <verb> '<json>'.

browser_alert_window

Flash the Chrome window's taskbar icon (does NOT steal foreground)

Args:

  • sessionId — required

browser_close

Close pup sessions. With sessionId: closes ONLY that session (Chrome window dies, others stay). Without sessionId: closes ALL sessions and Chromes (the bridge itself stays running). Use the sessionId form for targeted cleanup; use the no-arg form when you genuinely want a clean slate.

Args:

  • sessionId — optional — close just this one session

Note: If you want the SESSION's tab closed but the Chrome window kept alive (other sessions on the same profile share Chrome), use browser_close_window or browser_close_tab instead.

browser_close_tab

Close one tab; session + other tabs remain

Args:

  • sessionId — required
  • tabId — required

Returns: remaining tab count

browser_close_window

Close a session's Chrome window (closes ALL tabs in that session)

Args:

  • sessionId — required

browser_errors

Get console errors and failed network requests. Optional tabId for per-tab errors. v1.4.10: a stale tabId now returns errorCode:"tab_not_found" instead of silently returning the active tab's errors.

Args:

  • clear — optional bool (default true)
  • sessionId — optional
  • tabId — optional

browser_eval

Evaluate JavaScript in page context. Pass optional tabId to target a specific tab; omit to use the active tab. v1.4.10: passing a stale tabId now returns errorCode:"tab_not_found" with currentTabs + lastKnownUrl (when the missing tab was a recently-closed popup) instead of silently evaluating against the active tab.

Args:

  • expr — required JS string
  • sessionId — optional
  • tabId — optional

browser_fetch_url

Fetch an arbitrary URL with the session's cookies and return the raw bytes. Bypasses Chrome's PDF Viewer wrapper — critical for grabbing PDF binaries from popup tabs where in-page fetch(location.href) returns the viewer HTML wrapper (~200 KB stub) instead of the actual PDF. Also handles ZIPs, CAD bundles, anything served as a binary content-type.

Args:

  • body — optional raw string body for POST etc.
  • desktopSaveTo — optional DESKTOP-side absolute path (Windows) — bridge writes via fs.writeFileSync. Use when the user wants the file local on the desktop (e.g. dropping a CAD bundle into Downloads). Returns desktopSavedTo. Independent of saveTo; you can pass both.
  • headers — optional object — Cookie auto-included from session
  • method — optional (default GET)
  • saveTo — optional CONTAINER-side absolute path. CLI handles the write after receiving bytes from the bridge; parent dirs auto-created. Returned savedTo is the canonical absolute path verified to exist on disk (v1.4.9+ — v1.4.8 had a bug where this could lie). saveTo and bodyBase64 are mutually exclusive in the response.
  • sessionId — optional (defaults to active session)
  • tabId — optional (picks cookie context — defaults to active tab; cookies are per-browser-context so any tab in the session works)
  • url — required

Returns: {ok, bytes, contentType, status, sessionId, tabId, plus: savedTo + savedToFilesystem:"container" if saveTo was set; desktopSavedTo (or desktopSaveError) if desktopSaveTo was set; bodyBase64 if neither saveTo nor desktopSaveTo was set}

Note: Use this when a click spawns a popup tab whose Content-Type is application/pdf — Chrome PDF Viewer wraps it and in-page fetch returns the wrapper HTML. browser_fetch_url re-issues the request through puppeteer's BrowserContext.request which doesn't go through the page render layer. RIGHT PATTERN with popup tabs: read the popup's url from browser_list_tabs (immediately — popups can auto-close), then browser_fetch_url with that URL. NEVER eval-against-popup-then-fetch (popup may have closed; eval would error with errorCode:tab_not_found and a hint pointing here).

browser_focus_window

Activate the session's tab WITHIN Chrome (Puppeteer page.bringToFront). Does NOT raise the OS window — for that, use browser_raise_os_window.

Args:

  • sessionId — required

browser_input_dispatch

Dispatch TRUSTED input via Chromium's CDP-backed page.mouse / page.keyboard. Events have isTrusted=true (vs page-side dispatchEvent which is isTrusted=false and silently no-ops on framework gates / anti-automation checks). USE THIS instead of browser_eval-clicks when a click 'lands but does nothing' — most modern frameworks (React/Vue/Svelte) and vendor anti-automation gates check event.isTrusted before running handlers.

Args:

  • button — click only — left|right|middle (default left)
  • clickCount — click only — int (default 1; use 2 for double-click)
  • delay — click/type/key only — ms between mousedown+mouseup or keystrokes (default 0)
  • firstMatch — click only — bool (default false). When true, skips the visible+text+area filter and uses document-order first match. Useful when you've explicitly verified the first match is correct.
  • key — key only — Enter|Escape|Tab|ArrowUp|ArrowDown|ArrowLeft|ArrowRight|F1|... (any Puppeteer KeyInput name)
  • selector — click only — CSS selector. v1.4.8+ uses smart-pick when the selector matches multiple elements: filters to visible elements with non-empty text, tiebreaks by largest visual area. v1.4.9 added modal-scoped restriction: if a modal is open, candidates are restricted to elements inside it. v1.4.10 added the isPlausibleModal filter: empty overlay containers (Vue-Toastification toast wrappers, notification mount points) no longer fool modal-detection — only roots containing at least one visible interactive element with text count as modals. Pass firstMatch:true to skip all this and use document-order first match.
  • sessionId — optional (defaults to active session)
  • steps — move only — number of intermediate positions for human-like movement (default 1)
  • tabId — optional (defaults to active tab)
  • text — type only — string to type into the focused element. Pass selector to focus that element first.
  • type — required: click | move | type | key
  • x — click/move only — viewport X coord
  • y — click/move only — viewport Y coord

Returns: click via selector: {ok, sessionId, tabId, dispatched:"click", via:"selector", selector, matchedCount, chosenIndex, clickedRect:{x,y,w,h}, clickedText, insideModal, modalDetected, modalRoot:{tag,id,cls,z,role,ariaModal}|null, pickStrategy:"only-match"|"first-match"|"modal-scoped-largest"|"visible-text-largest"|"visible-text-largest-no-modal-match"|"visible-largest"|"fallback-first-document-order"}. click via coords: {ok, ..., via:"coords", x, y}. move/type/key return {ok, dispatched, ...}.

Note: Common cases: vendor 'Generate' / 'Download' buttons that open popups (use type=click + selector); login forms (selector to focus + type=type for username/password + key=Enter); modal dismissal (key=Escape). After a click that should produce a popup tab, follow up with browser_list_tabs — popup tabs auto-track since v1.4.7 with opener="popup" + openerTabId. To grab bytes from a PDF popup (Chrome PDF viewer otherwise serves only the wrapper HTML on in-page fetch), use browser_fetch_url with the popup's URL. v1.4.10: when smart-pick lands on the wrong element, inspect the response's modalRoot field to see which DOM node won the modal-detection heuristic — useful for diagnosing toast-container false-positives.

browser_list_tabs

List all tabs in a session, including auto-tracked popups. Tabs that the page itself opens via window.open() / target=_blank / form-submit-with-target=_blank are tracked automatically since v1.4.7 (Target.targetCreated listener) and appear here alongside Claude-opened tabs.

Args:

  • sessionId — required

Returns: tabs: [{tabId, url, title, active, errorCount, opener, openerTabId}], activeTabId, count. opener="user" for tabs Claude opened (browser_open_window/browser_open_tab); opener="popup" for tabs the page spawned (openerTabId names the originating tab — e.g. a product page that spawned a datasheet popup).

browser_list_windows

List all open browser sessions with URLs and titles

browser_lower_os_window

Minimize the OS window hosting this pup session (give the user back their desktop after Claude is done with a flow).

Args:

  • sessionId — required

Returns: sessionId, hwnd, lowered, error?

browser_navigate

Navigate to a new URL. Optional tabId to target a specific tab; omit to use the active tab. v1.4.10: passing a stale tabId now returns errorCode:"tab_not_found" with currentTabs + lastKnownUrl (when the missing tab was a recently-closed popup) instead of silently navigating the active tab.

Args:

  • sessionId — optional
  • tabId — optional
  • url — required

browser_open_tab

Add a tab to an existing session (same Chrome window as other tabs in that session — avoids new taskbar icon)

Args:

  • sessionId — required
  • url — required

Returns: tabId, url, title, active

browser_open_window

Open a Chrome window with a URL (auto-starts Puppeteer bridge). v1.6.3+: every session opens in FULL CAPABILITY MODE by default — JS-triggered downloads are allowed (Page.setDownloadBehavior=allow on every page + framenavigated), clipboard read/write is granted for paste-into-form flows, notifications + geolocation are DENIED so prompts don't pop up under automation. Pup is a tool-driving surface, not a user-facing browser; nearly every chip-fetcher-style flow needs these defaults. Pass strictPermissions:true to opt out (rare).

Args:

  • downloadPath — optional Windows abs path (default %USERPROFILE%\Downloads). Where scripted downloads land. Defaults to the OS Downloads folder, which is what desktop_watch_files polls by default — the two ends meet without configuration.
  • freshProfile — optional bool
  • profile — required string
  • sessionId — required string
  • strictPermissions — optional bool (default false). When true, skip ALL v1.6.3+ defaults: no clipboard permissions, no scripted-download policy, no notification/geolocation suppression. Chromium's normal permission prompts apply, scripted downloads may be silently dropped. Use only when the user genuinely needs to gate everything (very rare).
  • url — required string

Returns: {ok, sessionId, profile, url, tabId, defaultPermissionsApplied (true unless strictPermissions was passed), defaultPermissions (e.g. ['clipboard-read=granted','clipboard-write=granted','notifications=denied','geolocation=denied','midi=denied']), downloadsEnabled (true unless strictPermissions), downloadPath (the resolved absolute path for downloads), restoredTabsClosed, ...}

Note: v1.6.3+ unblocks the silent-download-failure scenario. Before v1.6.3, JS-triggered downloads (clicks on <a download>, fetch+blob saves, vendor 'Download Symbol' buttons that do window.location = url) could be silently dropped by Chrome — no error, no UI, no file in ~/Downloads. The fix: applyDownloadBehavior() sets Page.setDownloadBehavior on every page + framenavigated. Combined with desktop_watch_files for the wait-for-arrival pattern, downloads now Just Work without any caller setup.

browser_raise_os_window

Raise the OS window hosting this pup session above all desktop apps (real foreground, defeats Windows foreground lock). Use BEFORE recording / screenshots / animations so Chrome doesn't throttle the page (document.hidden=true → ~1Hz rAF). Internally: focuses the tab, then EnumWindows-finds the Chrome window by title containing '(session: )' and runs the AttachThreadInput + keybd_event bypass.

Args:

  • sessionId — required

Returns: sessionId, hwnd, title, raised, error?

browser_record_list

List completed tab recording .webm files on disk with sidecar metadata.

Returns: recordings: [{recordingId, filePath, sizeKB, durationMs, fps, sessionId, tabId, mtime}]

browser_record_start

Record a pup tab via CDP Page.startScreencast + Page.screencastFrameAck (Chrome's native compositor pipeline). Tab-scoped at the protocol level — no cross-tab/cross-Chrome leakage. Output is a single finished VP9 .webm file (no tar bundle, no Docker-side mux). Real-time playback via VFR concat-mux. Concurrent: one recording per tab; many tabs OK. CALL browser_raise_os_window FIRST so Chrome doesn't paint-throttle the page (anti-throttle flags help when occluded but a foregrounded window is always best).

Args:

  • fps — optional int (default 30) — actual fps may exceed target on rAF-animated pages
  • maxDurationMs — optional int (default 600000 = 10 min)
  • quality — optional int 0-100 JPEG quality (default 85)
  • sessionId — required
  • tabId — optional (defaults to active tab)

Returns: recordingId, sessionId, tabId, filePath (.webm), startedAt

browser_record_status

List active tab recordings with live capture stats.

Args:

  • sessionId — optional (filter to one session)

Returns: active: [{recordingId, sessionId, tabId, filePath, fpsTarget, fpsActual, frameCount, durationMs, sizeBytesApprox, startedAt}]

browser_record_stop

Stop a tab recording. Mux runs synchronously on stop (ffmpeg concat demuxer with per-frame wall-clock durations). Returns a single finished .webm — pull_file directly, no extraction step.

Args:

  • recordingId — required
  • sessionId — optional

Returns: recordingId, sessionId, tabId, filePath (.webm), sizeKB, durationMs, frameCount, fpsTarget, fpsActual, stopReason

Note: fpsActual reports the measured rate. If it falls below ~20 when target was 30, the pup window was occluded — call browser_raise_os_window before the next start, and verify the pup Chrome was launched with the anti-throttle flags (sessions opened before bridge v1.3.32 don't have them — close + reopen the session to refresh).

browser_reload

Reload page and clear error log. Optional tabId. v1.4.10: a stale tabId now returns errorCode:"tab_not_found" instead of silently reloading the active tab.

Args:

  • sessionId — optional
  • tabId — optional

browser_rescan

Recover orphaned Chrome windows whose CDP socket dropped (network blip, sleep/wake, puppeteer-side hiccup). Walks every known profile (in-memory + on-disk session files), reconnects via the persisted DevToolsActivePort, then walks every page and rebuilds session entries by parsing the (session: X) tag from each page's title. v1.5.1+ — paired with strict resolveSession (passing a known-but-disconnected sessionId now returns errorCode:"session_disconnected" instead of silently retargeting an unrelated window).

Args:

  • adoptOrphans — optional bool (default false). When true, pages with no (session: X) title tag get attached under a generated sessionId so the caller can drive them. Useful when Chrome has tabs the bridge has never seen (user opened them manually). Default false to avoid surprising callers with random tabs in their session list.

Returns: {ok, rescanned, profilesReconnected, profilesUnreachable, reattached, orphansAdopted, liveSessions, disconnectedSessions, _hint}

Note: Idempotent — safe to call repeatedly. The 30s health check now also auto-attempts reconnect, so most transient blips recover before you notice. Use this manually when you suspect the bridge has lost a window (e.g. browser_navigate suddenly errors with errorCode:"session_disconnected").

browser_screenshot

Capture lossless PNG screenshot (auto-resized to <=1568px at the bridge, then capped to <=1500px in the CLI's defense-in-depth layer). Optional tabId for per-tab capture. Safe to Read into Claude — the two-layer resize is mandatory so the image always fits the many-image API limit.

Args:

  • fullPage — optional bool
  • maxWidth — optional int (default 1568)
  • sessionId — optional
  • tabId — optional

Returns: savedTo (file path), sizeKB, width (final on-disk px), height, origWidth (pre-resize, what Chrome captured), origHeight, resized (bool — true if the cap fired), tabId. v1.8.40+ adds the width/height/origWidth/origHeight/resized fields so the caller knows exactly what dims they got without having to probe the file.

browser_screenshot_full_res

Capture FULL RESOLUTION screenshot (NO resize). WARNING: Do NOT Read the resulting file in Claude — images >2000px crash many-image sessions at the Claude API limit. Use browser_screenshot for AI-readable screenshots. This verb is for saving to disk / wiki / external export only (e.g. handing a designer a high-DPI render). v1.8.40+.

Args:

  • fullPage — optional bool
  • sessionId — optional
  • tabId — optional

Returns: savedTo (file path), sizeKB, width, height, tabId, _warning. Width/height are the actual capture dimensions (typically 2559x1398 on a standard 1920x1080 viewport with hi-DPI). _warning field repeats the do-not-Read advisory so it's visible in the response payload.

Note: Implementation symmetry with browser_screenshot: same path resolution, same disk-save flow at the bridge. Only difference is the sharp resize+recompress step is skipped + the Rust-side save_screenshot's resize cap (defense-in-depth) is bypassed.

browser_status

Check all sessions: URLs, error counts, alive status

browser_switch_tab

Make a specific tab the active one (calls bringToFront within Chrome)

Args:

  • sessionId — required
  • tabId — required

browser_switch_window

Switch the bridge's active session (which window commands without explicit sessionId target)

Args:

  • sessionId — required

browser_wait

Wait for content to settle

Args:

  • ms — optional int (default 3000)

credential_delete

Remove a credential entry by exact host pattern. Drops the password from the OS keychain and the entry from the index.

Args:

  • host — required (must match the host pattern used at credential_set time, exactly)

Returns: {ok, host, removedFromKeychain, removedFromIndex}

credential_list

List stored host patterns + their associated usernames. Passwords are NEVER returned.

Returns: {ok, credentials: [{host, username, addedAt, updatedAt}]}

credential_set

Store HTTP Basic Auth credentials for a host pattern. The password goes into the OS keychain (encrypted at rest by the user's session key); the index file holds host+username only. Subsequent pup navigations to a matching host auto-authenticate.

Args:

  • host — required (e.g. "*.componentsearchengine.com" or exact "nxp.componentsearchengine.com")
  • password — required (NEVER echoed back via list/get)
  • username — required

Returns: {ok, host, username}