From 53c806fed7b11450762fb5a421695534f7b5c1a6 Mon Sep 17 00:00:00 2001 From: cloudycotton <168998965+cloudycotton@users.noreply.github.com> Date: Mon, 16 Feb 2026 13:09:31 +0530 Subject: [PATCH] feat: update commands --- README.md | 216 ++++++++++++++-------------------------- src/commands/back.ts | 2 +- src/commands/forward.ts | 2 +- src/commands/go.ts | 2 +- src/commands/tab.ts | 2 +- 5 files changed, 80 insertions(+), 144 deletions(-) diff --git a/README.md b/README.md index 73a68f6..e4ee120 100644 --- a/README.md +++ b/README.md @@ -1,165 +1,101 @@ -``` - ┌─────────────────────────────────────────┐ - │ ███╗ ███╗██████╗ │ - │ ████╗ ████║██╔══██╗ │ - │ ██╔████╔██║██████╔╝ │ - │ ██║╚██╔╝██║██╔══██╗ │ - │ ██║ ╚═╝ ██║██████╔╝ │ - │ ╚═╝ ╚═╝╚═════╝ │ - │ │ - │ mini-browser for agents │ - └─────────────────────────────────────────┘ -``` -A lightweight browser CLI for AI agents. Navigate, observe, interact — all from the command line. +# mb · mini-browser for agents ---- +Browser CLI for agents. Each command is a small Unix tool — reads args, writes stdout, composes with pipes and `&&`. -## Quick Start +## Setup ```bash -# 1. Install dependencies -bun install - -# 2. Build the CLI -bun run build - -# 3. Start Chrome with remote debugging -./start-chrome.sh - -# 4. You're ready! -node dist/mb.js --help +npm install -g @runablehq/mini-browser +mb-start-chrome # starts Chrome with --remote-debugging-port=9222 +mb go "https://example.com" ``` ---- +Also ships `mb-restart-chrome` to kill and relaunch the debug Chrome instance. -## Demo - -```bash -# Navigate to a page -$ mb go "https://example.com" -https://example.com/ - -# Get page text -$ mb text -Example Domain - -This domain is for use in documentation examples without needing permission. - -Learn more - -# Find interactive elements with coordinates -$ mb snap -[0] link "Learn more" (246, 223) - -# Click by coordinates -$ mb click 246 223 - -# Check where we ended up -$ mb url -https://www.iana.org/help/example-domains - -# Go back -$ mb back - -# Take a screenshot -$ mb shot page.png -page.png -``` - ---- +For local development: `bun install && bun run build`, then use `node dist/mb.js`. ## Commands ``` -┌─────────────────────────────────────────────────────────────────────┐ -│ NAVIGATION │ -├─────────────────────────────────────────────────────────────────────┤ -│ go Navigate to URL │ -│ url Print current URL │ -│ back Go back │ -│ forward Go forward │ -├─────────────────────────────────────────────────────────────────────┤ -│ OBSERVE │ -├─────────────────────────────────────────────────────────────────────┤ -│ shot [file] Screenshot (default: ./shot.png) │ -│ snap Interactive elements with (x, y) coords │ -│ text [selector] Visible text content │ -├─────────────────────────────────────────────────────────────────────┤ -│ INTERACT │ -├─────────────────────────────────────────────────────────────────────┤ -│ click Click at coordinates │ -│ type [x y] Type text (triple-clicks to select first) │ -│ fill Fill form: Email=a@b.com "Name=Jo Do" │ -│ key Press keys (Enter, Tab, Meta+a) │ -│ move Move mouse / hover │ -│ drag Drag from point to point │ -│ scroll [px] Scroll up/down/left/right │ -├─────────────────────────────────────────────────────────────────────┤ -│ TABS │ -├─────────────────────────────────────────────────────────────────────┤ -│ tab list List open tabs │ -│ tab new [url] Open new tab, print index │ -│ tab close [n] Close tab (default: last) │ -├─────────────────────────────────────────────────────────────────────┤ -│ OTHER │ -├─────────────────────────────────────────────────────────────────────┤ -│ js Run JavaScript in page context │ -│ wait Wait for ms/selector/networkidle/url:... │ -│ audit Design audit (colors, fonts, contrast) │ -│ logs Stream console logs (Ctrl+C to stop) │ -└─────────────────────────────────────────────────────────────────────┘ +Navigation: + go Navigate (waits for networkidle) + url Print current URL + back / forward History navigation + +Observe: + text [selector] Visible text (selector uses querySelector, default: body) + shot [file] Screenshot (default: ./shot.png) + snap Interactive elements via Accessibility Tree + +Interact: + click Click at coordinates + type [x y] Type text (with coords: triple-clicks to select first) + fill Fill form fields by label/name/placeholder/selector + key Press keys — supports combos: Meta+a, Ctrl+Shift+T + move Hover + drag Drag between points + scroll [dir] [px] Scroll (default: down 500) + +Tabs: + tab list / new [url] / close [n] + +Other: + js Eval JS in page — strings print raw, objects print JSON + wait + audit Design audit (colors, fonts, contrast) + logs Stream console logs + +Flags: + --timeout (default: 30000) + --tab target tab (default: 0) + --json structured output (snap, tab list, logs) + --right right-click + --double double-click ``` -### Flags +## Notes -| Flag | Description | -|------|-------------| -| `--timeout ` | Command timeout (default: 30000) | -| `--tab ` | Target tab index (default: 0) | -| `--json` | JSON output | -| `--right` | Right-click | -| `--double` | Double-click | +All output goes to stdout. Pipe to grep, jq, wc, or redirect to files as usual. ---- +`js` reads from stdin with `-`: + echo 'document.title' | mb js - + cat scrape.js | mb js - -## How It Works +`text` calls querySelector — returns first match only. `text "p"` may return empty +if the first `

` on the page is empty. Use a scoped selector like `text "main"` or +`text "#content"` for better results on noisy pages. -``` -┌──────────┐ CDP ┌─────────────────┐ -│ mb │ ◄──────────► │ Chrome/Chromium │ -│ CLI │ (9222) │ (debug mode) │ -└──────────┘ └─────────────────┘ -``` +`snap` output format: + [0] button "Submit" (512, 380) + [1] textbox "Email" (512, 245) [disabled=true] +It returns role, accessible name, center (x,y) coords, and state flags (checked, +expanded, disabled, selected, pressed, haspopup). Only elements in the current +viewport are returned — scroll down and snap again to find more. -1. **Start Chrome** with `--remote-debugging-port=9222` -2. **Run commands** — `mb` connects via Chrome DevTools Protocol -3. **Observe + Interact** — screenshots, text, clicks, forms, etc. +`fill` matches fields by accessible name, label text, placeholder, then CSS selector +in that order. Use `fill "#my-input=value"` to target by selector when labels are +missing. ---- +`type` without coordinates types at current focus. With coordinates it triple-clicks +the field first (selects all existing text) then types the replacement. -## For Agents +`wait` picks its strategy from the argument format: + wait 2000 sleep (ms) + wait ".modal" wait for selector + wait networkidle wait for no network activity + wait url:/dashboard wait for URL to contain string -The `snap` command is your best friend: +`scroll` defaults to `scroll down 500` if called with no args. -```bash -$ mb snap -[0] button "Sign In" (845, 32) -[1] input[type=email] (512, 245) -[2] input[type=password] (512, 312) -[3] button "Submit" (512, 380) -[4] link "Forgot password?" (512, 420) -``` +`go` waits for networkidle0 before returning, so SPAs render before the next command +runs. For heavy SPAs that fetch data after mount, follow up with `wait ".selector"` +for a content element. -Each element has **coordinates** — use them with `click`, `type`, or `fill`. +`--json` on snap returns `[{role, name, x, y, state}]`. On tab list returns +`[{index, url, title}]`. On logs emits JSON lines `{tab, type, time, message}`. -```bash -# Click the Sign In button -mb click 845 32 - -# Or fill a form by label -mb fill Email=user@example.com Password=secret123 -mb click 512 380 -``` - ---- +Overlays (cookie banners, modals) block clicks on elements underneath. Dismiss them +first, or remove via JS: + mb js 'document.querySelector("[class*=cookie]")?.remove()' + mb js 'document.body.style.overflow="auto"' diff --git a/src/commands/back.ts b/src/commands/back.ts index 22a9c87..8c6f2c2 100644 --- a/src/commands/back.ts +++ b/src/commands/back.ts @@ -3,7 +3,7 @@ import type { Flags } from "../lib/flags" export const back = async (args: string[], flags: Flags) => { const { page, close } = await connect(flags.tab) - await page.goBack({ waitUntil: "domcontentloaded", timeout: flags.timeout }) + await page.goBack({ waitUntil: "networkidle0", timeout: flags.timeout }) console.log(page.url()) await close() } diff --git a/src/commands/forward.ts b/src/commands/forward.ts index 0ea0598..2ab3f60 100644 --- a/src/commands/forward.ts +++ b/src/commands/forward.ts @@ -3,7 +3,7 @@ import type { Flags } from "../lib/flags" export const forward = async (args: string[], flags: Flags) => { const { page, close } = await connect(flags.tab) - await page.goForward({ waitUntil: "domcontentloaded", timeout: flags.timeout }) + await page.goForward({ waitUntil: "networkidle0", timeout: flags.timeout }) console.log(page.url()) await close() } diff --git a/src/commands/go.ts b/src/commands/go.ts index efb95b9..e01bc6c 100644 --- a/src/commands/go.ts +++ b/src/commands/go.ts @@ -9,7 +9,7 @@ export const go = async (args: string[], flags: Flags) => { } const { page, close } = await connect(flags.tab) - await page.goto(url, { waitUntil: "domcontentloaded", timeout: flags.timeout }) + await page.goto(url, { waitUntil: "networkidle0", timeout: flags.timeout }) console.log(page.url()) await close() } diff --git a/src/commands/tab.ts b/src/commands/tab.ts index 9d9850b..821396c 100644 --- a/src/commands/tab.ts +++ b/src/commands/tab.ts @@ -53,7 +53,7 @@ async function newTab(args: string[], flags: Flags) { if (url) { try { - await page.goto(url, { waitUntil: "domcontentloaded", timeout: flags.timeout }) + await page.goto(url, { waitUntil: "networkidle0", timeout: flags.timeout }) } catch (e) { await page.close() await browser.disconnect()