Skip to content

harn browse

harn browse [options] <url>

Playwright-backed headless Chromium with a persistent profile and the shared cookie jar. Reach for it when you need a real browser: post-JavaScript DOM, a screenshot, console/network diagnostics, an interaction flow, or an assertion that a page actually renders the way it should. It is the Playwright sister of harn browse-ai (which returns an accessibility tree instead of DOM/HTML).

By default it writes a trio of files (<prefix>.png, <prefix>.html, <prefix>.json) for the iterate-and-inspect loop. The .json carries diagnostics: title, url, status, viewport, console events, console errors, page errors, and failed requests. <prefix> defaults to ~/.cache/harnery/browse/last; override with --out. The print-mode flags (--snapshot, --html, --json) skip the files and write to stdout instead, which is what makes the scrape pipeline harn browse <url> --html | harn read - work.

The trio’s <prefix>.html is a standalone snapshot, not a raw DOM dump. Stylesheets are fetched and inlined, fonts and images are embedded as data: URIs, and every remaining reference is rewritten to an absolute URL on the captured origin, so the file renders the same wherever it is opened later. Root-relative paths like /_next/static/app.css would otherwise resolve against whatever host serves the saved file and 404, which is what makes an unprocessed snapshot render as unstyled text. Scripts and preload hints are dropped, since neither can work in a saved page. The .json reports what happened under htmlSnapshot. Print mode (--html) is unaffected and still emits the raw serialization; a --selector capture is a DOM fragment rather than a document, so it stays raw too.

Flag Description
--out <prefix> Output prefix for the trio (<prefix>.png, .html, .json). Default ~/.cache/harnery/browse/last.
--no-screenshot Skip the .png (DOM + JSON only).
--no-full-page Capture only the viewport, not the full scrollable page.
--snapshot Print body innerText to stdout (skips file writes).
--html Print raw outer HTML to stdout (skips file writes; pair with harn read -).
--json Print the full JSON envelope to stdout (skips file writes).
--selector <css> Scope --html / --snapshot to one element.
--viewport <preset|WxH> mobile (390×844), tablet (820×1180), desktop (1280×800), hd (1920×1080), or explicit 1920x1080. Default desktop.
--color-scheme <scheme> Emulate prefers-color-scheme for the whole session: light | dark. Renders theme-aware pages in that scheme without page-specific toggles. Absent: the browser default (unchanged behavior).
--network-har <path> Record network traffic to a HAR file (finalized on close).
Flag Description
--click <selector> Click this selector after navigation.
--fill <selector=>value> Fill an input, e.g. input[name=q]=>hello. The separator is => (not =) so attribute selectors don’t collide.
--press <key> Press a key after navigation/fill (e.g. Enter).
--wait-for <selector> Wait for this selector before capturing output.
--evaluate <js> Run JS in the page context after navigation; the result is printed to stdout and recorded in the envelope under eval.
--batch <steps> Run multiple steps in one session, semicolon-separated (see Batch flows).
--wait-until <strategy> Navigation wait strategy: load | domcontentloaded | networkidle | commit. Default load.
--timeout <ms> Navigation timeout in milliseconds. Default 30000.
Flag Description
--login Headed mode for a one-time auth flow; cookies persist in the profile.
--login-close-file <path> With --login, keep the process open until another process creates this signal file, then remove it and close cleanly.
--control-file <path> With --login, publish an owner-only descriptor for repeated harn browse-session control.
--headed Headed mode for a one-off (no auth-flow framing).
--browser-arg <flag> Extra Chromium launch flag, passed straight to the browser (repeatable). Also settable machine-wide via HARNERY_BROWSER_ARGS (whitespace-separated). See WSLg note below.
--proxy-from-env Pass HTTPS_PROXY/HTTP_PROXY to Playwright as an authenticated browser proxy. Credentials remain in the child environment instead of command arguments.
--no-cookies Skip cookie-jar attach and persist. Also skips a host extraCookies hook.
--store <path> Cookie store path. Default ~/.cache/harnery/cookies.json.
--profile <dir> Persistent Chromium profile dir. Default ~/.cache/harnery/browser-profile.
--export-cookies <path> Export the live profile to an owner-only Netscape cookies.txt file before closing.

--proxy-from-env reads HTTPS_PROXY, https_proxy, HTTP_PROXY, then http_proxy. It separates userinfo from the proxy server URL and passes the credentials through Playwright’s native proxy option, so Chromium can answer a Basic-authentication 407 without showing a prompt. The secret is never part of the harn browse argument vector.

When a host wrapper sets both HARNERY_BROWSER_PROXY_EXPECTED_IP and HARNERY_BROWSER_PROXY_CHECK_URL, browse opens the HTTPS check URL first and requires its ip, query, or origin value to match exactly. A missing field, bad response, or mismatch stops the command before the requested URL opens. Proxy-enabled launches also set Chromium’s WebRTC IP handling policy to disable_non_proxied_udp. Playwright does not fall back to a direct connection when its configured proxy becomes unavailable.

Terminal window
HTTPS_PROXY='http://user:password@proxy.example:7000' \
HARNERY_BROWSER_PROXY_EXPECTED_IP='192.0.2.10' \
HARNERY_BROWSER_PROXY_CHECK_URL='https://ip.example/json' \
harn browse --proxy-from-env https://example.com

Keep fixed-route checks in the host wrapper when it owns the proxy inventory. For example, a wrapper can validate the route with curl, inject the same expected IP into the child, and let Chromium repeat the gate before target navigation.

For an agent-driven visible flow, pair --login with either --control-file or --login-close-file. The owner-only control descriptor lets repeated browse-session processes inspect and act on the original Playwright context. This avoids relying on an operating-system window handle while the operator can still use the visible tab directly.

The signal-file form remains useful when another trusted control surface drives the window. A second process creates the signal only after the flow is complete; browse removes it, exports cookies if requested, persists the profile, and closes Chromium. The signal must not exist at launch, which prevents a stale file from closing a new session immediately.

Terminal window
harn browse --login --login-close-file /tmp/auth-complete https://example.com
# From a second process, after the visible flow is complete:
touch /tmp/auth-complete

For live CLI control, create a private directory and launch with a descriptor:

Terminal window
install -d -m 700 ~/.cache/harnery/live-session
harn browse --login \
--control-file ~/.cache/harnery/live-session/control.json \
https://example.com

These assert on the rendered layout and surface findings in the JSON envelope; each --*-fail variant makes the check exit non-zero, so they slot into deploy/CI scripts. By default each check also annotates the screenshot with colored boxes.

Flag Description
--check-visible <selector> Occlusion check: samples an n×n grid inside the element’s rect and reports visibleRatio plus the dominant occluder. Repeatable.
--check-visible-threshold <num> visibleRatio below which a target counts as occluded (0–1). Default 0.9.
--check-visible-fail Exit non-zero if any target falls below threshold.
--check-visible-sample-grid <n> Grid size for occlusion sampling (n×n points). Default 3 (9 samples).
--no-check-visible-annotate Skip drawing target/occluder boxes (JSON still emitted).
--check-width <selector> Assert the selector’s rect width is at least --check-width-threshold of the viewport. Reports viewportFill + parentFill. Repeatable.
--check-width-threshold <ratio> viewportFill below which a target counts as too narrow (0–1). Default 0.9.
--check-width-fail Exit non-zero if any width target falls below threshold.
--no-check-width-annotate Skip drawing width-check boxes.
--check-overflow Assert no horizontal overflow (document.scrollWidth <= window.innerWidth); surfaces protruding elements.
--check-overflow-fail Run the overflow check when needed, then exit 2 if horizontal overflow is detected.
--no-check-overflow-annotate Skip drawing overflow annotations.
--check-runts [selector] Scan text blocks for runts (a single word alone on a block’s last visual line), counting words via per-word Range rects. Optional selector scopes the sweep.
--check-runts-min-chars <n> Minimum block text length to scan. Default 40.
--check-runts-fail Run a document-wide check when needed, then exit 2 for a runt, missing scope, or incomplete sweep.
--no-check-runts-annotate Skip drawing runt boxes.
--check-align <selector> Compare rendered child-content centers inside a container. Uses text fragments, then descendant SVGs, then border boxes. Repeatable.
--check-align-axis <axis> auto, x, or y. Auto checks y-centers for rows and x-centers for flex columns. Default auto.
--check-align-threshold <px> Maximum center drift in CSS pixels. Default 2.
--check-align-fail Exit 2 when an alignment target fails, is unknown, or is missing.
--no-check-align-annotate Skip alignment annotations.
--check-gap <selector> Compare adjacent rendered gaps inside a container. Groups of 3+ infer their median gap. Repeatable.
--check-gap-axis <axis> auto, x, or y. Default auto.
--check-gap-expected <px> Supply the expected gap. Required to make a two-item group conclusive.
--check-gap-threshold <px> Maximum gap deviation in CSS pixels. Default 2.
--check-gap-fail Exit 2 when a gap target fails, is unknown, or is missing. A two-item group needs --check-gap-expected to produce a conclusive result.
--no-check-gap-annotate Skip gap annotations.
--check-clip <selector> Check every matching container for descendant boxes or text paint leaving its rectangular clipping chain or nearest block parent. In-flow boxes are checked horizontally against that parent even when CSS overflow is visible. Repeatable.
--check-clip-threshold <px> Allowed rectangular overrun in CSS pixels. Default 0.
--check-clip-fail Exit 2 when rectangular clipping fails, is unknown, or the selector is missing.
--no-check-clip-annotate Skip clipping annotations.
--check-overlap <selector> Detect area intersections among measurable in-flow siblings. Repeatable.
--check-overlap-threshold <px> Allowed overlap depth on either axis. Default 0.
--check-overlap-fail Exit 2 when an overlap target fails, is unknown, or is missing.
--no-check-overlap-annotate Skip overlap annotations.
--check-crowd <selector> Flag adjacent card panels (a full border, a modest corner radius, or a box-shadow) that touch or nearly touch. Wrappers that contain panels count as peers too; separation uses the nearest face panels inside them. Walks the whole subtree, so one selector catches nested cases. Repeatable.
--check-crowd-min <px> Minimum acceptable edge gap between adjacent panel faces. Default 6. Negative separation (overlap) always flags.
--check-crowd-fail Exit 2 when a crowd target fails, is unknown, or is missing.
--no-check-crowd-annotate Skip crowd annotations.
--check-hit [selector] Check interactive target size and spacing in the document or optional scope. Repeatable.
--check-hit-profile <profile> wcag-aa uses 24px; comfortable uses 44px as a product-quality policy. Default wcag-aa.
--check-hit-fail Exit 2 on target-size fail, unknown, or a missing scope.
--no-check-hit-annotate Skip target-size annotations.

Alignment and gap checks cluster wrapped rows or columns before comparing them. They measure the rendered result rather than trusting align-items, gap, or margin declarations. Do not aim center alignment at an intentional align-items: baseline row.

Clipping is deliberately rectangular. The selector is a scope, not a single element lookup: every match is checked. Within that scope, the check measures element boxes against clipping ancestors and rendered text fragments against their nearest non-inline parent. That catches an unbreakable label painting out of a narrow card even when every element box still fits. An axis stops being checked at the first ancestor that scrolls it — including the queried container — because content the reader can scroll to is reachable by design, so a capped table or a wide code block reports nothing. Anything an ancestor inside that scroller clips is still reported, since scrolling the outer box cannot reveal it. Content inside a collapsed <details> is treated as hidden rather than clipped. The check ignores sub-half-pixel rasterization noise and normal vertical glyph overhang; neither is visible clipping. Hanging indents remain valid when their paint stays inside the block border. The check uses the rectangular padding box for border radius. Arbitrary clip-path, non-axis-aligned transforms, closed shadow roots, and cross-origin frame content are reported as unknown, not certified. Overlap covers sibling geometry; paint-order and sticky-layer obstruction remain --check-visible concerns.

Crowd sits between overlap and gap: overlap needs a real 2D intersection and gap flags uneven spacing, so a stack of cards all sitting flush passes both. Crowd targets exactly that: distinct panels with no breathing room. Peers are card-like panels (a full border, a modest corner radius, or a box-shadow) or wrappers that contain at least one panel. Separation is measured between the nearest face panels inside each peer, not the wrapper boxes, so a tall section with a card near the top and prose below does not false-fail against the next sibling, while a card-grid flush against the next card still fails. Plain blocks with no panel chrome stay out of the peer list. Table cells, list and definition rows, and divided segments carry no card boundary of their own and are skipped, as are pills, chips, and badges (corner radius at least half their shorter side). Per-pair results carry the two face panels, beforeKind / afterKind (panel or composite), the edge separationPx, the axis (x side-by-side, y stacked, overlap intersecting), and the shared parentLabel.

All requested checks run before one screenshot is captured. Fail gates are evaluated only after JSON and screenshot evidence is complete. Missing selectors and layout unknown results fail every enabled gate. The JSON still records the unknown reason so the recipe can replace an unsupported selector or supply a missing expectation.

A fail flag never succeeds without running its check. Whole-document checks can infer their scope: overflow, runts, placeholders, images, truncation, contrast, target size, and critique all run against the page when their fail flag is used alone. Selector-based fail flags cannot invent a target, so --check-clip-fail without --check-clip <selector> (and the same pattern for visibility, width, alignment, gap, overlap, and crowd) stops with a usage error before Chromium opens. --assert-fail requires --assert, and --diff-fail requires --diff.

Runt results carry scope, found, and outcome. A safety-cap truncation is unknown, not a clean result; a requested selector that matches nothing is fail.

These cover failures a human catches at a glance even when the geometry is perfect: leaked template tokens, broken images, clipped text, unreadable contrast. Each takes an optional selector (default: the whole body).

Flag Description
--check-placeholder [selector] Flag unrendered tells in the visible text: {{x}} and JS template tokens, [object Object], Invalid Date, NaN, or an element whose whole text is literally “undefined” / “null”.
--check-placeholder-fail Run a document-wide check when needed; exit 2 unless it passes.
--check-images [selector] Audit <img> for a failed load (naturalWidth 0), a still-loading image, or a stretched one (rendered aspect far from intrinsic, uncorrected by object-fit).
--check-images-tolerance <ratio> Aspect-ratio deviation above which an image counts as stretched. Default 0.1.
--check-images-fail Run a document-wide check when needed; exit 2 unless it passes.
--check-truncation [selector] Flag text actively cut off by an ellipsis or -webkit-line-clamp. Stays quiet on plain overflow: hidden.
--check-truncation-tolerance <px> Overflow past the clip box, in px, above which truncation counts. Default 2.
--check-truncation-fail Run a document-wide check when needed; exit 2 unless it passes.
--check-contrast [selector] Flag rendered text below the WCAG AA ratio (4.5:1 normal, 3:1 large) against its effective background.
--check-contrast-fail Run a document-wide check when needed; exit 2 on failed, unknown, or missing results.
--no-check-<name>-annotate Skip that check’s screenshot annotations (JSON still emitted).

Placeholder is prose-safe: bare “undefined” / “null” only flag when they are an element’s whole text, not the word inside a sentence. Do not point it at a page that documents template syntax. Contrast resolves the effective background by walking ancestors to the first opaque color; text over an image or gradient is unknown, not failed, and the check reads whatever theme the page is in, so toggle the theme with --batch to cover light and dark. Truncation is deliberately narrow (an active ellipsis or line-clamp only), so a decorative overflow: hidden does not read as a bug.

Heuristic checks only catch what you can enumerate. --check-critique hands the rendered page to a vision model for the long tail: the thing that looks off without tripping a named rule.

Flag Description
--check-critique [selector] Send the page to a vision model, tile by tile. Default tiling cuts vertical bands whose seams snap into content gaps; a selector tiles one screenshot per matching element, banding any element taller than the band budget internally.
--check-critique-band <px> Band height budget for tiling. Seams may move up to 30% above the budget to land in a gap, never below it. Default 1400.
--check-critique-overlap <px> Vertical overlap below a seam that had to cut through content, so a straddling finding stays visible. Clean seams (nothing straddles them) carry a 16px margin instead. Default 120.
--check-critique-max-tiles <n> Cap on tiles sent to the model, to bound cost. Default 24.
--check-critique-rubric <text> Override the default rubric.
--check-critique-fail Run a whole-page critique when needed; exit 2 unless it conclusively passes.

The page is tiled because a tall screenshot downscaled to a model’s input budget loses the detail the critique depends on; each tile is captured at full resolution and judged on its own, and findings carry their tile and scroll offset for locality. Band seams are content-aware: the page’s text lines, images, and small bordered boxes are extracted first, and each cut lands in the cheapest nearby gap rather than at a fixed offset, so tiles rarely open or close mid-element. Where no clean cut exists (an element taller than the band budget), the cut falls back to the fixed offset with the full overlap, and the rubric tells the model to treat slice-edge clipping there as an artifact. harnery ships no model client, so a host injects a critiqueProvider into HarneryProgramContext (the extraHeaders pattern); without one, the result is skipped, and --check-critique-fail exits 2 rather than certifying a review that did not happen. A provider that throws becomes a high-severity finding for that tile rather than aborting the run.

The layout and content checks answer “does the page look right”; --assert answers “does it say the right thing”. One repeatable flag, <op> <selector> => <expected>:

Op Passes when
text the first match’s trimmed text equals <expected>
contains the first match’s text includes <expected>
matches the first match’s text matches the <expected> regex
count the match count equals <expected>, or satisfies a >= / <= / > / < comparator
exists / absent at least one / zero matches (no => expected)

For example --assert 'text h1 => Welcome', --assert 'count .card => >=3', --assert 'absent .error'. Results land under asserts in the JSON envelope (each carries the observed actual); --assert-fail exits 2 on any failure. A malformed spec reports an error and fails rather than throwing. This is the portable primitive a host layers domain assertions on (funnel totals, warehouse reconciliation).

Flag Description
--baseline <name> Save the captured screenshot as a named baseline at ~/.cache/harnery/visual-baselines/<name>.png.
--diff <name> Pixel-diff the capture against the named baseline; writes a diff PNG next to the screenshot and reports mismatchedPixels + similarity.
--diff-threshold <ratio> mismatchRatio below which the diff is a match (0–1). Default 0.01 (1%).
--diff-fail Exit non-zero if mismatchRatio exceeds the threshold.

Vision review costs the same whether you changed one word or the whole theme, unless something classifies the change first. --qa-plan compares the rendered page against a persisted baseline signature (per-element structure, attributes, direct-text digests, applied-stylesheet digests) and emits a review manifest under qaPlan in the JSON envelope before any model call is made: the change class, the stable scope selectors that contain it, the render contexts worth reviewing, and the provider-call ceiling.

Flag Description
--qa-plan Classify this render against the persisted baseline and emit the review manifest. No baseline classifies as unknown and widens to full coverage.
--qa-snapshot Persist this render (signature + DOM + full-page screenshot) as the QA baseline for the target + context, atomically. Run on a page state you trust.
--qa-target <key> Override the snapshot-store key (default: the url argument), so a production render can seed the baseline for a local route.
--qa-theme <theme> Context label for the store: light | dark (default light). Labels only; it does not switch the page theme.
--qa-state <name> Context state label (default default).
--qa-scope <selector> Explicit scope selector from the producing task (repeatable); overrides lifted anchors.
--qa-states <names> Comma-separated interaction states to review; promotes the plan to interaction-state.
--qa-reuse With --check-critique: reuse the baseline run’s verdicts for tiles whose pixels provably didn’t change (band-diff against the persisted snapshot screenshot). Only clean regions reuse; baseline findings or any pixel drift force a fresh review. Stats land under qaReuse.
--qa-reuse-threshold <ratio> Mismatch ratio at or below which a clean tile region is reused (default 0.001; lower toward 0 for stricter reuse).

The classes: text-data-only (only direct text changed, so zero vision calls), local-visual (structural changes contained by stable anchors, so review scopes to them in light contexts), interaction-state (explicit states requested), large-structural (stylesheet changed, unprovable boundary, or multi-region change: the full desktop/mobile and light/dark matrix), and unknown (no or truncated baseline: widened, never treated as text-only). Ambiguity always widens. A one-line edit to a linked stylesheet changes zero DOM bytes and every pixel, so DOM equality alone never proves a change is text-only; stylesheet digests (external sheets fetched and hashed) must match too. Stable anchors are unique #ids or data-qa-scope attributes; nested anchors dedupe to the outermost root so a container is reviewed once, not per descendant.

Reuse closes the loop: --qa-snapshot on a critique run persists the verdicts with the screenshot, and the next --check-critique --qa-reuse run pixel-diffs each tile’s document rect against that baseline and skips the model call for clean, finding-free regions that match at or below the strict threshold. Reuse can produce false re-reviews, never false passes: a region with baseline findings is always re-reviewed, a rubric or contract change is a whole-run miss, and a partially-reused run is never persisted as the next baseline.

--review-pack is the capture half of the capture-then-judge split behind harn review-pack and harn qa-run. The page is rendered once; its full-page screenshot, critique tiles (as PNG files, cut with the --check-critique-* knobs), serialized DOM, and QA signature are written into <dir>/contexts/<id>/; and the browser closes. No vision call is made. A judge reads the tiles back later, with no browser open, through the same host-injected provider --check-critique uses.

Flag Description
--review-pack <dir> Write this render into the pack at <dir>. Pair with --no-screenshot: the pack holds the full-page image.
--review-pack-context <id> Context id inside the pack (default <viewport>-<theme>-<state> from --viewport, --qa-theme, --qa-state).
--review-pack-scope <selector> Also tile one screenshot per matching element (repeatable). Scope tiles follow the full-page bands in the tile order.
--review-pack-plan Emit all native tile candidates and stable capture metadata without capturing the pack. Known gate failures still emit a plan.
--review-pack-allocation <file> Capture exactly the selected IDs from a validated context allocation. Source or geometry changes require a new plan. Page edges remain required.
--review-pack-reservation <file> Use the preliminary allocation only as a tile-count reservation. Repeat supplied gates and allocate on the current page; require unchanged full source and complete native coverage. One source-change retry is allowed. Cannot combine with allocation, expansion or inline critique.
--review-pack-hit-rect <x,y,w,h> A document-space rectangle a deterministic gate flagged (repeatable, at most 50). A band that contains one is captured as a hit band even when it lies below the tile cap, so the reviewer gets the tile that shows the defect. qa-run passes each context’s gate rectangles automatically.
--review-pack-expand <tile-id> Instead of capturing a context, re-render one existing tile of --review-pack-context at --device-scale-factor and write it beside the original as tiles/<tile>@<dpr>x.png. Existing tiles are untouched. This is the child harn review-pack expand runs.
--device-scale-factor <n> Device scale factor for the browser context (1 to 4; default 1). Every screenshot renders at that multiple of its CSS pixels.

The JSON envelope gains reviewPack (dir, context_id, tiles, scopes, coverage, and the pack-relative files; plus expanded after --review-pack-expand). The context’s coverage is the band coverage when bands were kept, else the scopes folded together.

Before the browser closes, the capture checks its own fidelity. Two probe bands (the top band and the middle band) are re-shot by scrolling to them and compared with the same region of the full-page screenshot using the band-diff mismatch threshold. When a probe disagrees, every band is re-captured by scrolling and the tiles are cut from those captures instead; the context record’s capture_fidelity says which source the tiles came from and which probes mismatched. This is how a blank block or a clipped callout that exists only in a tall full-page screenshot stays out of the judge’s findings, without any browser opening after capture.

Flag Description
--no-dev-overlay Skip auto-capture of Next.js dev-overlay issues (see Dev-overlay capture).

--batch runs several steps in a single browser session, semicolon-separated. Each step is one verb: click <selector>, fill <selector=>value>, press <key>, wait <selector|ms>, eval <js>, reload, or clipboard [<label>]. reload preserves sessionStorage and cookies, which is how you reproduce sessionStorage-restored UI state. clipboard reads the system clipboard via navigator.clipboard.readText() and records each read in the envelope under batchClipboardReads.

Terminal window
harn browse https://example.com --batch "click button.load-more; wait 1500; reload; wait 3000"

On by default. When a <nextjs-portal> shadow root is present, browse reads the queued dev-overlay issues (kind/code/message/stack) and reports them under devOverlay in the envelope. This matters because Next.js 16 + React 19 route hydration errors and most React warnings through onCaughtError into next-devtools’ error queue, not through console.error, so they never appear in consoleErrors. Pass --no-dev-overlay to skip the capture.

In print mode (--json) or the trio’s .json file, the envelope carries: url, title, status, viewport, consoleEvents, consoleErrors, pageErrors, failedRequests, and, when the corresponding flag ran, eval, har, visibility, width, overflow, runts, align, gap, clip, overlap, hit, devOverlay, batchClipboardReads, baselineSaved, and diff. failedRequests includes network and HTTP failures but omits canceled Next.js React Server Component prefetches. The trio’s .json additionally carries htmlSnapshot (stylesheetsInlined, stylesheetsLinked, resourcesInlined, resourcesLinked, inlinedBytes).

Terminal window
# Default trio: screenshot + DOM + diagnostics JSON at the default prefix
harn browse https://example.com
# Scrape-to-markdown pipeline
harn browse https://example.com --html | harn read -
# Mobile viewport, custom output prefix, viewport-only screenshot
harn browse https://example.com --viewport mobile --no-full-page --out /tmp/run
# Fill a search box, submit, and wait for results before capturing
harn browse https://example.com --fill 'input[name=q]=>hello' --click 'button[type=submit]' --wait-for 'main .results'
# Assert layout on mobile: break the build on overflow or narrow tables
harn browse https://example.com --viewport 430x932 --check-width 'table' --check-overflow --check-overflow-fail
# Run the rendered-layout suite in one browser state
harn browse https://example.com --viewport mobile \
--check-align 'nav' --check-gap 'nav' --check-clip 'main' \
--check-overlap '.toolbar' --check-hit 'header' \
--check-align-fail --check-gap-fail --check-clip-fail \
--check-overlap-fail --check-hit-fail
# Save a visual baseline, then diff a later run against it
harn browse https://example.com --baseline home-desktop
harn browse https://example.com --diff home-desktop --diff-fail
# One-time headed login; cookies persist to the shared profile for later runs
harn browse --login https://example.com
# Export one dedicated profile for a consumer that requires Netscape cookies.txt
harn browse --profile ~/.cache/harnery/profiles/media --no-cookies \
--export-cookies ~/.cache/harnery/exports/media-cookies.txt https://example.com