browser · diff
git:20260922.7112a2a to git:20260923.deee607
60 added, 47 removed. Audit A to A.
---
name: browser
- description: "Drives a real browser: sites the user is already signed into, forms and clicks, JS-rendered pages, screenshots, web QA, extension popups, and a human handoff for login, CAPTCHA or OTP. Works through the BrowserSkill extension and its bsk CLI, inside the user's own browser, in a separate Agent Window. Use for any interactive browser task; not for a plain search or an unblocked static fetch."
+ description: "Drives a real browser through the omowright library from the js eval kernel: sites the user is already signed into, forms and clicks, JS-rendered pages, screenshots, web QA, extension popups, a human handoff for login, CAPTCHA or OTP, and a browser you own for scraping, bot-scored targets, network capture and QA traces. Use for any interactive browser task; not for a plain search or an unblocked static fetch."
---
# Browser
- Two engines live behind this skill. Choose before you act:
+ One library, two engines. omowright ships inside this skill; choose the engine before you act:
- | You need | Engine | Where |
+ | You need | Engine | Entry point |
|---|---|---|
- | A site the user is signed into, a form, a click-through, a screenshot, web QA, an extension popup | **attached** — the user's own browser | this file |
- | A throwaway profile, bot-scoring evasion, a CAPTCHA, network interception, a QA flight trace, coordinate control | **owned** — a browser your code launches | [references/owned-engine/README.md](references/owned-engine/README.md) |
+ | A site the user is signed into, their open tabs, a form, a click-through, a screenshot, web QA, an extension popup | **attached** — the user's own browser through BrowserSkill | `connectBrowserSkill()` |
+ | A throwaway profile, bot-scoring evasion, a CAPTCHA, network interception, a QA flight trace, coordinate control, headless runs | **owned** — a browser your code launches | `connectPipe()` / `connectCloakProfile()` — [references/owned-engine/README.md](references/owned-engine/README.md) |
| Text out of a URL, a 403 bypass, a platform that blocks fetchers | neither | the `ultimate-browsing` skill |
**Attached is the default,** because it is the only engine carrying the user's logins and the only
- one where a human is a single command away. The owned engine is an opt-in local install, not part
- of this package.
+ one where a human is a single call away. Never substitute one engine for the other silently: if
+ the attached engine is not set up, run the onboarding script and tell the user its one remaining
+ step.
- ## Step 0 — prove the stack before you drive it
+ ## Step 0 — load omowright and prove the stack
- ```bash
- node "<skill-root>/scripts/browser-doctor.mjs"
+ ```js
+ const { loadOmowright } = await import("<skill-root>/scripts/omowright.mjs")
+ const { omowright } = await loadOmowright() // { connectBrowserSkill, bskSnapshot, connectPipe, ... }
```
- It reports one of four states and what to do next:
+ ```bash
+ node "<skill-root>/scripts/browser-doctor.mjs" --json
+ ```
| State | Meaning | Next |
|---|---|---|
| `ready` | CLI, daemon and a connected browser | start a session |
- | `no-extension` | CLI works, no browser is connected | give the user the store link the doctor printed, wait, re-run |
- | `no-cli` | `bsk` is not installed | `node "<skill-root>/scripts/browser-install.mjs"`, then re-run |
- | `no-browser-support` | the platform has no supported browser | say so and stop |
+ | `no-cli` / `no-daemon` / `no-extension` | something is missing | `node "<skill-root>/scripts/browser-install.mjs"` prepares everything it can, then prints the **single** step only the user can do (relaunch the browser and click **Enable**); relay it verbatim, wait, re-run the doctor |
+ | `no-browser-support` | no Chromium-family profile on this machine | say so and stop |
- **Never substitute another browser for a missing one.** A headless browser you launch yourself has
- none of the user's sessions, so every login turns into a ladder you should not be climbing. If the
- attached engine is unavailable, say which state you hit and ask the user.
+ **Never launch a headless browser because the attached one is missing.** It has none of the
+ user's sessions, so every login turns into a ladder you should not be climbing. Say which state
+ you hit and ask.
- ## The loop
+ ## The loop (attached)
- ```bash
- bsk session start --json --no-focus --name "<task>" # keep session_id
- bsk navigate https://example.com --session <id> --json
- bsk observe --session <id> # @eN refs live here
- bsk click @e1 --session <id> --json
- bsk screenshot --session <id> --out ./shot.png --json
- bsk session stop <id> --json # success AND failure
+ ```js
+ const session = await omowright.connectBrowserSkill({ name: "<task>", focused: false })
+ try {
+ await session.navigate("https://example.com/", { waitUntil: "load" })
+ const { tree, refs, css } = await omowright.bskSnapshot(session, { interactive: true }) // OmOWright tree + refs, no trace in the page
+ await session.click({ selector: css.e3 }) // css[ref] is null inside shadow roots:
+ const vom = await session.observe({ maxTokens: 4000 }) // then read the daemon's own tree ...
+ await session.click("@e7") // ... and click its @eN ref
+ await session.fill(css.e5, "hello")
+ await session.press("Enter")
+ await session.waitForNavigation({ waitUntil: "load" })
+ const shot = await session.screenshot() // { buffer, width, height, captureId }
+ } finally {
+ await session.stop() // success AND failure; returns borrowed tabs
+ }
```
- 1. **Observe before every action.** `observe` returns a semantic tree whose `@eN` refs are reissued
- on each call. Read a ref and use it in the same cycle; a ref from two calls ago points somewhere
- else now.
- 2. **Navigation and large DOM changes stale every ref.** Observe again rather than reusing.
+ 1. **Read before every action.** `bskSnapshot` refs and `observe` `@eN` refs are reissued on each
+ call; use a ref in the same cycle you read it.
+ 2. **Navigation and large DOM changes stale every ref.** Read again rather than reusing.
3. **Two identical failures mean change approach, not retry.** A third identical attempt is a defect.
- 4. **Borrow a user tab explicitly** (`tab list --scope user`, `tab borrow <id>`, `tab return <id>`).
+ 4. **Borrow a user tab explicitly** (`tabList({ scope: "user" })`, `tabBorrow(id)`, `tabReturn(id)`).
Borrowing prompts the user; never invent tab ids and never repeat a denied borrow.
- 5. **Always stop the session,** on success and on failure. Stopping also returns borrowed tabs.
+ 5. **Always `stop()` the session,** on success and on failure.
- Command semantics, the flags that behave differently than they read, and the failure table are in
- [references/commands.md](references/commands.md).
+ Every method, its options, and the failure codes are in [references/commands.md](references/commands.md).
## When a human is the only way through
Login, CAPTCHA, OTP, a payment confirmation, a consent dialog:
- ```bash
- bsk request-help --session <id> --prompt "<what you need done>" [--target @eN]
+ ```js
+ const outcome = await session.requestHelp({ prompt: "<what you need done>", targets: ["@e4"], timeoutMs: 300_000 })
```
- Then observe again. Respect a `cancelled` or `timed_out` answer; do not work around it by changing
- the extension's automation settings.
+ Then read the page again. Respect a `cancelled` or `timed_out` outcome; do not work around it by
+ changing the extension's automation settings.
## Rules
- - **Never read credentials through the page.** No `evaluate` that extracts a password, token, cookie
- or recovery code. The value of this engine is that the browser is already signed in.
+ - **Never read credentials through the page.** No `evaluate` that extracts a password, token,
+ cookie or recovery code. The value of the attached engine is that the browser is already signed in.
- **Never clear cookies, cache or site data.** It is the user's real profile; clearing it logs them
out everywhere. No flow here needs it.
- - **`--no-focus` by default.** The browser belongs to someone who is probably using it.
+ - **`focused: false` by default.** The browser belongs to someone who is probably using it.
- **One short, named session per task,** always stopped.
- - Do not toggle the extension's automation settings, and do not restart the browser to fix a state.
+ - **Bot-scored or WAF targets go to the owned engine.** The attached engine's daemon enables console
+ capture on every tab it drives, which is a known automation signal; CloakBrowser through
+ `connectCloakProfile()` is the stealth path.
- ## More
+ ## Where the rest lives
- - [references/install.md](references/install.md) — installing the CLI and the extension, per OS
- - [references/commands.md](references/commands.md) — command semantics, refs, failure table
- - [references/remote.md](references/remote.md) — agent on one machine, browser on another
- - [references/recipes/1password.md](references/recipes/1password.md) — reading a vault the user has unlocked
- - [references/owned-engine/README.md](references/owned-engine/README.md) — the code-driven engine
+ | Topic | Read |
+ |---|---|
+ | Session methods, targets, options, error codes | [references/commands.md](references/commands.md) |
+ | Installing: CLI, daemon, extension, the one human step, blocklisted extension | [references/install.md](references/install.md) |
+ | Agent on one machine, browser on another | [references/remote.md](references/remote.md) |
+ | Owned engine: launch, snapshot ladder, network, frames, human handoff | [references/owned-engine/README.md](references/owned-engine/README.md) |
+ | Reading a 1Password vault the user has unlocked | [references/recipes/1password.md](references/recipes/1password.md) |