# Browser

> Drive a hosted browser from a local Playwright script, signed in with a saved profile, for sites with no API or data behind a login wall.

Source: https://docs.generalinput.com/cli/browser



Some work has no API: a portal that only renders in a browser, a report behind a login, a page that needs a real session. `geni browser` opens a hosted browser you drive from your own machine over the Chrome DevTools Protocol, exactly the way the platform's agents do it.

A **saved browser** (a profile) holds the cookies and storage of the sites you have signed in to. Open a session with `--profile` and it arrives signed in; close it and the session state is saved back.

* [`geni browser profiles`](/cli/browser/profiles) lists your browser and the ones teammates shared with you.
* [`geni browser open`](/cli/browser/open) opens a hosted session and prints its CDP URL.
* [`geni browser close`](/cli/browser/close) saves the login and stops billing.
* [`geni browser request-login`](/cli/browser/request-login) signs in to a site on your own browser through a live view.
* [`geni browser report-login`](/cli/browser/report-login) records whether a saved login still works.

## The flow [#the-flow]

```sh
geni browser profiles                       # find your browser id and its logins
geni browser open --profile bprof_01HX      # arrives signed in
geni exec bash -- 'node scrape.js'          # GI_BROWSER_CDP_URL is exported for you
geni browser close                          # always, including after failures
```

While a session is open, [`geni exec bash`](/cli/exec/bash) exports `GI_BROWSER_CDP_URL` (and a `NO_PROXY` carve-out for it) into the subprocess. Connect with `playwright-core`:

```js
import { chromium } from 'playwright-core'

const browser = await chromium.connectOverCDP(process.env.GI_BROWSER_CDP_URL)
const page =
  browser.contexts()[0].pages()[0] ?? (await browser.contexts()[0].newPage())
await page.goto('https://portal.example.com/reports')
console.log(await page.title())
await browser.close()
```

Run it with `node`, not `bun`. No `--cred` is needed for the browser itself.

## Sessions and billing [#sessions-and-billing]

One session at a time. A session bills hosted browser time until it is closed. A detached keepalive holds the lease while your script runs, and a forgotten session force-closes after two hours so a mistake bills bounded time. Close it yourself as soon as the script is done.

## Login walls [#login-walls]

When a script hits a real sign-in form (a restricted page bounced to login, not a homepage that merely shows a Sign in button):

* On your own browser, run [`request-login <domain>`](/cli/browser/request-login). A live view opens in your real browser; you sign in (2FA and all), the login is verified and saved, and the next `open --profile` arrives signed in.
* On a browser a teammate shared, run [`report-login <domain> logged_out`](/cli/browser/report-login) and ask its owner to reconnect it.
