Skip to content

Latest commit

 

History

History
58 lines (36 loc) · 7.16 KB

js-tool.md

File metadata and controls

58 lines (36 loc) · 7.16 KB

Common operations also have typed MCP tools and equivalent helper methods. Discover with tabs_list, claim existing tabs with claim_tab, and reuse them with navigate_tab. close_tab closes an explicit session tab. Use open_tab only for a genuinely separate page. All helper calls use the current session; no imports or setup are needed.

Browser REPL quickstart

js runs a persistent Node REPL with top-level await. End with an expression to return its value; do not use top-level return (it is only valid inside functions). Pre-bound browser, sites, tools, recon, agent, session, nodeRepl; no imports or bootstrap. Await API methods. Each call drains already-dispatched API work before returning; an ordinary error preserves bindings. Variables, functions and classes survive calls. Prefer let for working bindings; reuse bindings across calls; redeclaration is also supported.

Use browser.chrome.call(method, argsArray) for native browser management, tab.cdp.send(method, params) for Tab-level CDP, and tab.evaluate(script, {arg}) for Main World page code. These execute in different contexts from the host REPL. Query browser.chrome.describe("tabs.query") or read capabilities/chrome for native API signatures, events, and tab/window/group organization recipes. Create groups with tabs.group; edit them with tabGroups.update.

Open and inspect:

let tab = await browser.tabs.new('https://example.com/');
await tab.observe();

Or borrow a user tab:

let matches = await browser.user.openTabs({query: 'example.com', limit: 20});
matches.items;

The result reports totalVisible, totalInScope, excluded, matched, and hasMore. scope:"unclaimed" excludes actively owned and unsupported tabs. Query is a trimmed case-insensitive literal substring of URL OR title, without whitespace token splitting. For more results, pass offset: matches.nextOffset with the same query and limit; each call is a fresh snapshot. Zero matches is a valid result; invalid transport responses are errors.

Copy an item’s numeric tabId into await browser.user.claimTab({tabId: ...}). Store the returned Tab handle, then observe it. Session handles come from await browser.tabs.list() and await browser.tabs.get(id).

After reading the observation, call await tab.act({action:'click', target:{ref: '...'}}) using a returned ref, then await tab.observe() or await tab.expect({text:'...'}) with the expected result. await tab.read() returns an object, not a string: read result.text, and pass result.next to tab.read to continue. Use read({target:{role:"main"}}) or a specific article to exclude sidebars. Batch determined steps; return at new decision points.

When ARIA lacks detail, await tab.observe({format:'dom'}) returns visible controls with live attributes and the same actionable refs. await tab.read({target:{ref:'...'}}) reads full text/attributes without scrolling. DOM previews flag truncated; pass dom.next directly to tab.observe to continue the live control list. Child refs automatically retain their observed frame route. Explicit semantic queries and whole-document reads accept the returned frame path. A screenshot remains the evidence for canvas, layout and colors.

The last expression is returned. nodeRepl.write(value) adds output; await tab.screenshot() returns an image. Keep large results in variables and return summaries; truncation is explicit. API handles print only their public identity. Tab.id is a property; methods are awaited.

For example, return a screenshot as the cell result:

let shot = await tab.screenshot({format: 'jpeg', quality: 80});
shot;

Refs are opaque Element identities shared by ARIA, DOM and find. Copy them unchanged; routing to their observed frame is automatic. They remain valid while the same Element lives in that document; after replacement or navigation, stale_ref means observe again. A semantic locator can find a replacement Element; an old ref does not.

Finish with session_finish. Use doctor for connection/execution status and js_reset to stop JavaScript, clear bindings and close explicit subscriptions; tabs stay open. Read repl-session for Node declaration semantics, errors, timeout/reset and images. Request api-reference with member:"Tab.act", "Tab.network", "Browser" or another class/member for exact signatures; omit member for the full reference.

Reuse the same target for verification: await tab.expect({target, value:"42"}). Continue captured output with tab.observe(result.next) or tab.read(result.next). Pass an openedTabs entry directly to browser.tabs.get, and the returned download object to tab.download(result.download).

Tab handles expose async methods: use await tab.url() and await tab.title(). Entries returned by browser.tabs.list() are snapshots with string url and title fields. Inspecting or converting a remote method to a string describes the method locally; it never reads the page.

Page evaluation contract

Use await tab.evaluate(value => value, {arg: data}). The second argument is a strict options object with only arg, frame, and timeoutMs; extra positional arguments and unknown fields fail before dispatch. Source strings do not accept arg. Functions run in the page and cannot capture REPL variables.

Arguments are plain JSON data: null, booleans, strings, finite numbers (excluding negative zero), dense arrays and plain objects. Root arg: undefined is allowed. Nested undefined, non-finite numbers, BigInt, functions, special objects, accessors, symbol keys and cycles are rejected, never silently rewritten. Convert special values explicitly. API handles can be passed to APIs that accept them; they are not page data. Errors with dispatched:false mean the rejected invocation had no page effects. This does not undo earlier calls in the same cell.

Readiness and bounded page work

observe() defaults to the viewport; observe({viewport:false}) covers the rendered document. It does not scroll to load an entire feed. Empty wrappers and pointer annotations are omitted; unique labels and control states remain.

Use await tab.goto(url, {ready:{target:{role:'main'}, text:'Expected content'}, timeoutMs:15000}) or await tab.expect(condition, {timeoutMs:15000}) after an action. Targets must be unambiguous. DOM settling is not a content-ready guarantee.

evaluate defaults to 10000 ms. Pass {timeoutMs:30000} for known longer work. A timeout does not cancel page JavaScript and is never automatically retried. For feeds, collect and deduplicate in page-local state in short batches, return counts/progress each batch, and fetch final rows once. Page window state survives calls in the same document, but navigation/reload clears it. No generic API can infer how far arbitrary JavaScript got after a timeout.

The MCP REPL provides browser, sites, tools, recon, helper and nodeRepl; describeTool is not a global here. helper exposes the same typed browser commands as MCP. Use docs_get for signatures and return types. tab.close() returns {tabId,closed:true}; optional chaining without a result is not evidence of closure. Releasing a user tab relinquishes control; it does not undo navigation, scrolling or edits.