# Actions (https://oh-my-webbridge.github.io/en/reference/actions/)

> **Developer preview**
>
> omw is in developer preview and iterating rapidly. **THERE WILL BE COMPATIBILITY-BREAKING CHANGES.**

Every browser action goes through `POST /command` on the daemon at `http://127.0.0.1:17373`. Requests carry the Bearer token printed by `omw connection`. The CLI wraps it in `omw call`, the SDK in `call` (see [CLI](https://oh-my-webbridge.github.io/md/en/reference/cli/content.md) and [SDK](https://oh-my-webbridge.github.io/md/en/reference/sdk/content.md)).

## Requests [#requests]

| Field       | Type   | Meaning                                                                                                                                           |
| ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `session`   | string | Session name, 1 to 96 letters (including CJK), digits, `_`, `.` or `-`. Required.                                                                 |
| `action`    | string | Action name from the tables below. Required.                                                                                                      |
| `args`      | object | Action arguments. Defaults to `{}`.                                                                                                               |
| `timeoutMs` | number | Deadline in milliseconds, 1 to 120000, default 30000. It covers queueing and execution. Another value, including 0, fails with `invalid_request`. |
| `browser`   | string | `chrome` or `firefox`. Omit to use the browser already bound to the session, or the only connected one, or Chrome.                                |

The body is one JSON object of at most 2 MiB with no unknown fields. A session name that repeats a binding is routed by the rules on [Sessions and tabs](https://oh-my-webbridge.github.io/md/en/sessions/content.md).

```json
{
  "session": "research",
  "action": "navigate",
  "args": { "url": "https://example.com", "group_title": "Research" },
  "timeoutMs": 30000
}
```

The body can also carry `id`, a string that the reply repeats. Without it, the daemon assigns a [Snowflake ID](https://en.wikipedia.org/wiki/Snowflake_ID) in lowercase base 36. A successful call returns the id and the action result:

```json
{
  "id": "uvwoyqsusjk",
  "result": { "success": true, "tabId": 412, "url": "https://example.com/" }
}
```

A failed call returns the id and a fault instead of a result, with an HTTP status from the [Errors](#errors) table:

```json
{
  "id": "uvwoyqsusjk",
  "error": { "code": "stale_ref", "message": "element reference expired; take a fresh snapshot" }
}
```

Page actions run in the session's current tab. Pass `tabId` (or `tab`) to address another tab the session holds without switching to it.

## Navigation and tabs [#navigation-and-tabs]

| Action          | Arguments                                     | Notes                                                                                                                               |
| --------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `navigate`      | `url`, `newTab`, `group_title`, preset fields | Opens a background task tab unless the session already has a current tab or `newTab` is set. URLs must be HTTP(S) or `about:blank`. |
| `find_tab`      | `url` (exact URL or hostname), `active`       | Claims an existing tab for the session. `active: true` borrows the browser's active tab. Returns `tabId`, `url`, `borrowed`.        |
| `list_tabs`     | none                                          | Lists the tabs this session owns or borrowed, with `tabId`, `url`, `title`, `active`, `borrowed`, `groupTitle`.                     |
| `switch_tab`    | `tabId`                                       | Makes the tab current. A discarded tab is reloaded first; the result reports `revived`.                                             |
| `window_new`    | `url`, preset fields                          | Opens a separate window, claims its tab, and returns `tabId` and `windowId`.                                                        |
| `close_tab`     | `tabId`                                       | Closes a tab the session created. Borrowed tabs are not closed.                                                                     |
| `close_session` | none                                          | Closes task tabs, releases borrowed ones and drops the browser binding. Returns the number of closed tabs.                          |
| `history`       | `cmd`: `list`, `back`, `forward`, `reload`    | `list` returns the navigation entries. Every `history` call drops the tab's references and frame selection.                         |

A session holds at most 32 tabs across task and borrowed ones.

## Reading [#reading]

| Action          | Arguments                                               | Notes                                                                                                                                                                    |
| --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `snapshot`      | `interactive`, `maxDepth`                               | Accessibility tree with `@e` references, plus `url`, `title`, `truncated`. Depth is capped at 64. Output stops at 4000 nodes or 192000 characters.                       |
| `find`          | `query`, snapshot fields                                | Case-insensitive search over snapshot lines. At most 100 matches.                                                                                                        |
| `read_page`     | none                                                    | `url`, `title` and the first 192000 characters of `document.body.innerText`.                                                                                             |
| `evaluate`      | `code`, `contextId`                                     | Runs an expression and returns `{ type: "value", value }`. Use an async IIFE for `await`.                                                                                |
| `get`           | `selector`, `property`, `attribute`                     | `text`, `html`, `value`, `attr`, `count`, `box`, `styles`, `title`, `url`.                                                                                               |
| `is`            | `selector`, `property`                                  | `visible`, `enabled`, `checked`, `editable`. Returns a boolean `value`.                                                                                                  |
| `locate`        | `by`, `value`, `name`, `nth`, `cmd`, `text`, `property` | Builds a selector from `by` (`role`, `text`, `label`, `placeholder`, `alt`, `title`, `testid`, `css`, `xpath`) and runs `cmd`, an element action that defaults to `get`. |
| `dialog_status` | none                                                    | Returns the pending JavaScript dialog as `dialog`, or `null`.                                                                                                            |

## Input [#input]

| Action                             | Arguments                                                          | Notes                                                                                                                                                                         |
| ---------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `click`, `dblclick`, `mouse_click` | `selector` or `x` and `y`, `button`                                | Dispatches mouse events at the element center.                                                                                                                                |
| `hover`                            | `selector`                                                         | Moves the pointer to the element.                                                                                                                                             |
| `fill`                             | `selector`, `value`                                                | Replaces the content of an input, textarea or contenteditable element. An empty `value` clears it.                                                                            |
| `key_type`                         | `text`, `selector`                                                 | Inserts text at the focused element, or focuses `selector` first.                                                                                                             |
| `send_keys`                        | `keys`                                                             | One key or a chord such as `Control+A` or `Meta+A`. Named keys include `Enter`, `Tab`, `Escape`, `Backspace`, `Delete`, arrows, `Home`, `End`, `PageUp`, `PageDown`, `Space`. |
| `keyboard`                         | `cmd`: `down`, `up`, `insertText`; `key`, `text`                   | Lower-level key control for held modifiers.                                                                                                                                   |
| `mouse`                            | `cmd`: `move`, `down`, `up`, `wheel`; `x`, `y`, `button`, `amount` | Raw pointer control.                                                                                                                                                          |
| `scroll`                           | `selector`, or `direction`, `amount`, `x`, `y`                     | With a selector, scrolls it to the middle. Otherwise wheels by `amount` (0 to 100000, default 600) in `up`, `down`, `left` or `right`.                                        |
| `select_option`                    | `selector`, `value`                                                | Matches an option by value or by visible text, then fires `input` and `change`.                                                                                               |
| `drag`                             | `from`, `to`                                                       | Presses at `from`, moves in ten steps and releases at `to`.                                                                                                                   |
| `focus`, `check`, `uncheck`        | `selector`                                                         | `check` and `uncheck` read the current state first and verify the result; repeating them does not toggle. Radios support `check` only.                                        |
| `dialog`                           | `accept`, `promptText`                                             | Answers the pending dialog. `accept` defaults to `false`.                                                                                                                     |

## Waiting [#waiting]

`wait` returns as soon as the condition holds, and checks it again every 75 ms. When the condition has not held 500 ms before the request deadline, `wait` fails with `wait_timeout`. It checks at least once, also under a deadline of 500 ms or less. Set the deadline with `timeoutMs`, or `--timeout-ms` in `omw call`. Set one of these fields:

| Field        | Meaning                                                                                                     |
| ------------ | ----------------------------------------------------------------------------------------------------------- |
| `selector`   | The first match exists, has a box and is not `visibility: hidden`.                                          |
| `text`       | `document.body.innerText` contains the string.                                                              |
| `url`        | The current URL matches. `*` matches any run of characters.                                                 |
| `expression` | The JavaScript expression is truthy.                                                                        |
| `load`       | `load` waits for `readyState === "complete"`; `domcontentloaded` waits for the document to leave `loading`. |
| `state`      | `visible` (default) or `hidden`, which inverts the condition.                                               |

## Frames [#frames]

| Action or argument                                                          | Meaning                                                                         |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `frame {"cmd": "list"}`                                                     | Lists frames with `id`, `name` and `url`, including cross-process child frames. |
| `frame {"selector": "iframe#panel"}`                                        | Selects the frame that holds the matching iframe element.                       |
| `frame {"frameId": "..."}`, `frame {"name": "..."}`, `frame {"url": "..."}` | Selects by id, name or exact URL.                                               |
| `frame {"cmd": "main"}`                                                     | Returns to the main frame and drops the frame's references.                     |

While a frame is selected, page actions run in it. Selecting a frame drops the previous references and requires a new snapshot.

A selection ends when its frame navigates or is removed, or, in Chrome, when the tab's debugger session ends, for example after the user cancels Chrome's debugging bar or the extension's worker restarts. The next action that would run in the frame fails once with `frame_unavailable`; later actions use the main frame.

## Files and capture [#files-and-capture]

| Action        | Arguments                                                                                                                           | Notes                                                                                                                                                                                                                             |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `screenshot`  | `format` (`png`, `jpeg`), `quality` (0 to 100), `selector`, `fullPage`, `path`                                                      | Returns `path`, `sizeBytes` and `mimeType`. Without `path` the file goes under the daemon's artifacts directory. An existing path is rejected.                                                                                    |
| `save_as_pdf` | `paper_format` (default `letter`; also `legal`, `a4`, `a3`, `tabloid`), `landscape`, `scale` (0.1 to 2), `print_background`, `path` | Streams the PDF to disk in chunks and returns `path`, `sizeBytes`, `mimeType` and `pageTitle`. Chrome only.                                                                                                                       |
| `upload`      | `selector`, `files`                                                                                                                 | Absolute paths on the machine running the browser. Chrome needs "Allow access to file URLs" on for the extension in `chrome://extensions`. The daemon inlines file contents for Firefox: 16 MiB per command and 64 MiB in flight. |
| `download`    | `url` (HTTP(S)), `filename`                                                                                                         | Returns `path`, `sizeBytes`, `mimeType` and `id` on the browser's host. A cancel or timeout interrupts the download.                                                                                                              |

A screenshot whose encoded result exceeds 32 MiB fails with `result_too_large`.

## Network, console, storage and cookies [#network-console-storage-and-cookies]

| Action    | Arguments                                                                                    | Notes                                                                                                                                                                                                                                                                                         |
| --------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `network` | `cmd`: `start`, `stop`, `list`, `detail`; `filter`, `requestId`                              | Keeps the last 500 requests per tab. `filter` is a URL substring. `detail` returns the response body. In Chrome, a recording covers the frame selected at `start`: the main frame with its same-process iframes, or one cross-process iframe. Selecting another frame later does not move it. |
| `console` | `cmd`: `start`, `list`, `clear`, `stop`                                                      | Stores up to 200 entries per tab, each truncated to 8192 characters.                                                                                                                                                                                                                          |
| `storage` | `cmd`: `list`, `get`, `set`, `remove`, `clear`; `kind` (`local`, `session`), `name`, `value` | Reads and writes the current origin only. Returns `origin`, `kind` and either `value` or the whole store.                                                                                                                                                                                     |
| `cookies` | `cmd`: `list`, `set`, `delete`, `clear`; `data`, `name`                                      | Scoped to the current URL. `set` passes `data` through to the cookie fields.                                                                                                                                                                                                                  |

## Emulation [#emulation]

| Field                         | Meaning                                                                                  |
| ----------------------------- | ---------------------------------------------------------------------------------------- |
| `width`, `height`             | Viewport in CSS pixels, 1 to 16384 each. Both are required to change the device metrics. |
| `deviceScaleFactor`, `mobile` | Device pixel ratio and mobile flag for the metrics override.                             |
| `colorScheme`                 | `dark` or `light` for `prefers-color-scheme`.                                            |
| `latitude`, `longitude`       | Geolocation. Both are required together.                                                 |
| `offline`                     | Takes the page offline or back online.                                                   |
| `data`                        | Extra HTTP headers: at most 128 string values and 32 KiB, without CR or LF.              |

Settings persist for the session and apply to new tabs before they navigate. Chrome drops them when it ends a tab's debugger session, for example after the extension's worker restarts; the next command on a task tab of the session applies them again. Borrowed tabs are excluded.

## Batch [#batch]

`batch` takes `steps`, an array of `{ action, args }` objects, 1 to 64 entries. Steps run in order in one request and stop at the first error. The error keeps the code of the failed step, and its message starts with `batch step N of M (action):`, so steps 1 to N-1 completed. Completed steps are not rolled back. `batch`, `close_session`, `screenshot`, `save_as_pdf`, `artifact_read` and `artifact_close` cannot appear inside a batch.

## Presets [#presets]

| Field         | Meaning                                                                    |
| ------------- | -------------------------------------------------------------------------- |
| `preset`      | `anti-ad` (default), `zen` or `none`.                                      |
| `environment` | Installs bundled cosmetics and scriptlets. Applies from the next document. |
| `zen`         | `images`, `fonts`, `css`, `javascript`. `true` blocks that resource type.  |
| `cmd`         | `status` reads the tab's counters, interception and fallback reasons.      |

The configuration is stored on the session and applied to task tabs. Borrowed tabs are excluded. `navigate` accepts the same fields and stores them once the navigation starts. See [Page presets](https://oh-my-webbridge.github.io/md/en/presets/content.md).

## CDP [#cdp]

`cdp` sends one Chrome DevTools Protocol command to the tab: `method`, `params`, and an optional `sessionId` for a child target. It returns the protocol result unchanged.

Two methods change the bridge's own state. `Runtime.disable` ends the frame selection of its session, and `Page.disable` without a session id invalidates the tab's `@e` references; the next snapshot or navigation re-enables the events.

The daemon uses `cdp_attach`, `cdp_detach`, `artifact_read`, `artifact_close` and `list_targets` internally. Do not send them yourself.

## Selectors [#selectors]

Every action that takes `selector` accepts these forms.

| Form                             | Example                              | Matching                                                                                        |
| -------------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------- |
| CSS                              | `button[type=submit]`, `css=.card a` | Selector query. Also searches open shadow roots.                                                |
| `@e` reference                   | `@e<generation>.<index>`             | A node from the latest `snapshot`.                                                              |
| `role=`                          | `role=button[name="Submit"]`         | Accessibility role and optional name. The name is a JSON string and matches exactly.            |
| `text=`                          | `text=Sign in`, `text="Sign in"`     | Rendered text. Unquoted values match a case-insensitive substring; quoted values match exactly. |
| `label=`                         | `label=Email`                        | The associated label text or `aria-label`.                                                      |
| `placeholder=`, `alt=`, `title=` | `placeholder=Search`                 | The matching attribute.                                                                         |
| `testid=`                        | `testid=search`                      | The `data-testid` attribute.                                                                    |
| `xpath=`                         | `xpath=//table//a`                   | XPath in the current document only. Shadow roots are not traversed.                             |

Append ` >>nth=0` to take the first match or ` >>nth=-1` to take the last. Text, CSS and attribute locators traverse open shadow roots. Closed shadow roots stay invisible to them.

## Reference lifetime [#reference-lifetime]

`@e` references belong to one document generation. Each snapshot numbers its references under a new generation, a Snowflake ID in base 36. In Chrome, a new snapshot, a frame selection, a new main document (navigation or reload) or a change of the tab's URL invalidates them. A snapshot taken inside a selected frame also expires when that frame navigates, reloads, is removed, or the selection ends. Other iframes loading or navigating leave it valid. In Firefox, references live in the document that produced them; see [Firefox](https://oh-my-webbridge.github.io/md/en/firefox/content.md).

## Errors [#errors]

Every fault carries `code` and `message`. The HTTP status is shown below; a code not listed here returns 502.

| Code                    | HTTP | Meaning                                                                                                                                                                                                                                                                          | What to do                                                                                                                                 |
| ----------------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `invalid_request`       | 400  | The body was rejected: an unknown field, a missing action or session, a session name outside the allowed characters, a bad `browser`, or `timeoutMs` outside 1 to 120000.                                                                                                        | Fix the JSON. The message names the field.                                                                                                 |
| `invalid_args`          | 400  | An argument is missing, has the wrong type, or is outside its range, including a selector that does not parse. A `screenshot` or `save_as_pdf` path is rejected when it exists, when its directory cannot be created or written, or when its file system cannot make hard links. | Read the message; it names the action and argument.                                                                                        |
| `unsupported`           | 501  | The action or one of its arguments does not exist in the target browser.                                                                                                                                                                                                         | Do not retry. Use another action, or a Chrome session.                                                                                     |
| `busy`                  | 429  | A capacity limit is full: command slots, 32 tabs per session, 128 sessions per browser, the Firefox upload budget, or two concurrent artifact jobs. The session also moved to the other browser while the command was routed.                                                    | Reduce concurrency and retry after earlier commands finish.                                                                                |
| `browser_offline`       | 503  | The target browser has no extension connection, including a session bound to a browser that is not connected.                                                                                                                                                                    | Check `omw doctor` and the extension, or continue in a new session.                                                                        |
| `session_browser`       | 409  | The session is bound to the other browser and the request named this one.                                                                                                                                                                                                        | Drop `browser`, or close the session and start a new one.                                                                                  |
| `outcome_unknown`       | 504  | The command may have run: the deadline, a cancellation, a transport write or a disconnect cut off the reply, or the page navigated or the debugger detached during the command. The message names the cause.                                                                     | Observe the page or network state before repeating. Never replay a submit, payment or publish blindly.                                     |
| `wait_timeout`          | 408  | The `wait` condition did not hold before the deadline. `wait` does not change the page.                                                                                                                                                                                          | Send `wait` again with a longer deadline, or check the condition.                                                                          |
| `tab_busy`              | 409  | The tab belongs to another session.                                                                                                                                                                                                                                              | Use a tab this session holds, or another session.                                                                                          |
| `stale_ref`             | 409  | An `@e` reference outlived its document.                                                                                                                                                                                                                                         | Take a new `snapshot`.                                                                                                                     |
| `no_tab`                | 502  | A page action ran before the session had a tab, or after its current tab closed other than by `close_tab`.                                                                                                                                                                       | Run `navigate` or `find_tab`, or pass the `tabId` of another session tab.                                                                  |
| `tab_missing`           | 502  | `find_tab` matched no tab, or the tab the command addresses was closed.                                                                                                                                                                                                          | Open the page in this session first, or pass `active: true` to borrow the browser's active tab. For a closed tab, continue in another tab. |
| `restricted_tab`        | 502  | The tab is not a normal, non-incognito HTTP(S) page.                                                                                                                                                                                                                             | Navigate to an HTTP(S) page. Browser-internal pages are not scriptable.                                                                    |
| `borrowed_tab`          | 502  | The action needs a tab the session created: closing it, or applying a preset to a borrowed tab.                                                                                                                                                                                  | Open a task tab with `navigate`.                                                                                                           |
| `element_missing`       | 502  | The selector matched nothing.                                                                                                                                                                                                                                                    | Check the selector, or take a new snapshot.                                                                                                |
| `element_hidden`        | 502  | The element has no box, so it has no center to click.                                                                                                                                                                                                                            | Scroll it into view or wait for it to render.                                                                                              |
| `element_action_failed` | 502  | The page refused the operation, such as filling a non-editable element or selecting an option that does not exist.                                                                                                                                                               | Inspect with `get` or `is` and adjust the action.                                                                                          |
| `state_mismatch`        | 502  | `check` or `uncheck` left the control in the wrong state.                                                                                                                                                                                                                        | Re-read it with `is`, then retry or click it instead.                                                                                      |
| `evaluation_failed`     | 502  | `evaluate` threw, or an internal page script did. In Firefox, also an `evaluate` whose result cannot be returned, such as a DOM node, `Window` or `BigInt`; the code ran.                                                                                                        | Fix the expression, or return plain data. The message carries the page exception.                                                          |
| `frame_missing`         | 502  | `frame` found no frame for the given selector, id, name or URL.                                                                                                                                                                                                                  | List frames with `frame {"cmd":"list"}` and select one.                                                                                    |
| `frame_unavailable`     | 502  | The selected frame was replaced, removed or lost its execution context, or the tab's debugger session ended.                                                                                                                                                                     | Later commands use the main frame; select the frame again if the work continues there.                                                     |
| `history_missing`       | 502  | `history back` or `forward` has no entry in that direction.                                                                                                                                                                                                                      | Read `history {"cmd":"list"}` first.                                                                                                       |
| `request_missing`       | 502  | No network record exists for the request, or it was evicted.                                                                                                                                                                                                                     | Start recording, reproduce, then read `list`.                                                                                              |
| `request_pending`       | 502  | The response body is not complete yet.                                                                                                                                                                                                                                           | Wait for `complete: true` in `list`, then retry `detail`.                                                                                  |
| `request_failed`        | 502  | The request itself failed.                                                                                                                                                                                                                                                       | Read the error text on the entry.                                                                                                          |
| `body_unavailable`      | 502  | Firefox did not keep the response body: a WebSocket, a body evicted by newer bodies, or an interrupted response.                                                                                                                                                                 | Repeat the request with `network` recording, or read the data from the page.                                                               |
| `download_missing`      | 502  | The browser does not list the download.                                                                                                                                                                                                                                          | Retry the `download`.                                                                                                                      |
| `download_failed`       | 502  | The browser interrupted the download.                                                                                                                                                                                                                                            | Read the message and retry.                                                                                                                |
| `file_access_required`  | 502  | `upload` in Chrome needs the extension's "Allow access to file URLs" switch, which is off by default.                                                                                                                                                                            | In `chrome://extensions`, open the details of oh-my-webbridge, turn on "Allow access to file URLs" and retry.                              |
| `window_failed`         | 502  | The new window had no tab.                                                                                                                                                                                                                                                       | Retry `window_new`.                                                                                                                        |
| `result_too_large`      | 502  | A result exceeded 32 MiB on the wire, a saved screenshot or PDF exceeded 100 MiB, or a network body exceeded 2 Mi characters in Chrome or 1 MiB in Firefox.                                                                                                                      | Narrow the result: use `selector` or `jpeg` for screenshots, and read smaller pieces.                                                      |
| `debugger_unavailable`  | 502  | Chrome refused the debugger attach, for example because DevTools is open or policy blocks it.                                                                                                                                                                                    | Close DevTools for that tab and retry.                                                                                                     |
| `browser_error`         | 502  | The extension reported an error without a code.                                                                                                                                                                                                                                  | Read the message, retry once, then report it.                                                                                              |
| `internal`              | 502  | A daemon-side failure, such as encoding a request.                                                                                                                                                                                                                               | Retry. If it persists, restart the daemon.                                                                                                 |
