← Blog

BLOG

CLI Agent-Browser: Run AI Browser Agents from the Terminal

A cli agent-browser gives AI agents a real Chromium session from the terminal. Learn the schema, skills, and how to wire it to a hosted runtime.

October 11, 20268 min readRemote Browser

# CLI Agent-Browser: Run AI Browser Agents from the Terminal

A cli agent-browser is a command-line tool that lets an AI agent drive a real browser: navigate, click, type, extract, and screenshot, all from shell commands or a small script. The pattern got popular with vercel-labs/agent-browser, which wraps browser automation into a CLI that an LLM can call as a tool. The catch is that the CLI is only half the story. The other half is *where the browser actually runs*. This guide covers the CLI surface, the schema and skills that make it agent-friendly, and how to point it at a hosted Chromium runtime instead of a local Chrome install.

If you already know you want a remote runtime, you can skim to the connection section. If you are still deciding whether the CLI approach fits your stack, read the trade-offs first.

What a CLI agent-browser actually is

Strip away the branding and a cli agent-browser is three things glued together:

  1. A command surface. Subcommands like open, click, type, snapshot, screenshot, eval. Each maps to a browser action.
  2. A state model. The CLI keeps a session (a browser context, a page, a set of refs) between invocations, usually via a daemon or a persistent connection.
  3. An agent-facing schema. A machine-readable description of every command, its arguments, and its return shape, so an LLM can call it without guessing.

That third piece is what separates an agent-browser from a plain automation script. A human can read --help. An agent needs a schema it can parse, validate against, and retry on.

The typical flow looks like this:

agent-browser open https://example.com
agent-browser snapshot          # returns an accessibility tree with refs
agent-browser click @e3         # click the element with ref e3
agent-browser type @e5 "hello"
agent-browser screenshot out.png

The snapshot step is the important one. Instead of dumping raw HTML, a good CLI returns a compact, ref-annotated view of the page. The agent reasons over that, picks a ref, and acts. This keeps token usage low and makes the agent's decisions auditable.

Why the CLI pattern works for agents

Agents are bad at holding large, mutable state in their context window. A CLI sidesteps that by keeping state *outside* the model. The agent issues a command, gets a small result, and moves on. The browser session lives in the daemon or the remote runtime, not in the prompt.

Three practical benefits:

  • Composability. Shell commands pipe, chain, and log. You can wrap a cli agent-browser in a script, a cron job, or a CI step without writing a bespoke SDK integration.
  • Debuggability. Every action is a discrete command with a discrete result. When an agent fails, you can replay the exact sequence.
  • Model-agnosticism. Any model that can emit a shell command can drive the CLI. You are not locked to one vendor's tool-calling format.

The trade-off is latency and orchestration overhead. Each command is a round trip. For long, branching workflows, a persistent Playwright script may be faster. For exploratory or tool-driven agents, the CLI wins on flexibility.

The schema and skills layer

Two related keywords show up constantly in this space: agent-browser schema and skills agent-browser. They refer to the same underlying need: telling the agent what it can do.

Schema. A JSON (or TypeScript) definition of each command: name, description, parameters, types, and return shape. If you are building your own CLI, expose this at a stable path, for example agent-browser schema --json. Agents that support structured tool definitions can ingest it directly.

Skills. Higher-level, named workflows built on top of primitives. A skill might be "log in to a site and download the latest invoice" or "search a docs site and return the top result." Skills reduce the number of steps an agent has to reason about, which cuts both latency and error rate.

A minimal schema entry looks like this:

{
  "name": "click",
  "description": "Click the element identified by a snapshot ref.",
  "parameters": {
    "type": "object",
    "properties": {
      "ref": { "type": "string", "description": "Ref from the latest snapshot, e.g. @e3" }
    },
    "required": ["ref"]
  }
}

If your CLI does not ship a schema, write one. It is the difference between an agent that flails and an agent that plans.

Local Chrome vs hosted Chromium

Here is where most teams hit a wall. The CLI works fine on a laptop. In production, you need the browser to run somewhere reliable, isolated, and reachable from your workers.

ConcernLocal Chrome + CLIHosted Chromium runtime
SetupInstall Chrome, manage versions, handle headless flagsConnect over CDP to a managed session
IsolationShared profile, cookies leak between runsSession-per-task isolation
ScalingOne machine, manual parallelismSessions provisioned per request
PersistenceLocal profile dir, fragilePersistent profiles as a first-class feature
DebuggingLocal screenshots and logsLive viewer plus recorded sessions
Proxies / networkManual configConfigurable browser settings per session
Ops burdenYou own patching, crashes, cleanupRuntime owns lifecycle

The local path is fine for development. The moment you run more than a handful of concurrent agents, or you need sessions to survive a worker restart, the hosted path pays for itself.

This is the same argument behind remote browsers for AI agents: the browser becomes infrastructure, not a dependency you install.

Connecting the CLI to a hosted runtime

Most cli agent-browser tools support a connection URL or a CDP endpoint. The pattern is consistent: the runtime gives you a WebSocket endpoint, and the CLI (or the Playwright/Puppeteer process underneath it) attaches to it.

If you are driving the browser directly with Playwright, the connection looks like this:

import { chromium, Browser, Page } from "playwright";

// The runtime returns a CDP WebSocket endpoint for a fresh session.
const cdpEndpoint = process.env.REMOTE_BROWSER_CDP_URL!;

async function runTask(): Promise<void> {
  const browser: Browser = await chromium.connectOverCDP(cdpEndpoint);

  // Reuse the default context so persistent profile state is honored.
  const context = browser.contexts()[0] ?? (await browser.newContext());
  const page: Page = context.pages()[0] ?? (await context.newPage());

  await page.goto("https://example.com", { waitUntil: "domcontentloaded" });

  // Snapshot-style extraction: pull a compact view for the agent.
  const title = await page.title();
  const links = await page.$$eval("a", (els) =>
    els.slice(0, 10).map((el) => ({
      text: (el.textContent ?? "").trim(),
      href: (el as HTMLAnchorElement).href,
    }))
  );

  console.log({ title, links });

  // Do not call browser.close() on a hosted session unless you want it torn down.
  await browser.close();
}

runTask().catch((err) => {
  console.error("task failed", err);
  process.exit(1);
});

Two things to note. First, connectOverCDP attaches to an existing browser rather than launching one, which is exactly what a hosted runtime provides. Second, whether browser.close() terminates the remote session depends on the runtime; check the docs before assuming either behavior. The Playwright connectOverCDP reference documents the client side of this contract.

If your CLI wraps Playwright internally, you usually just set an environment variable or pass a --cdp flag. The CLI then forwards the endpoint to the underlying driver. That is the cleanest integration: you keep the agent-facing command surface and swap the browser backend.

Production criteria for a cli agent-browser setup

Before you commit, evaluate against these:

  • Session lifecycle. Can you create, reuse, and destroy sessions explicitly? Persistent profiles matter for logged-in workflows; ephemeral sessions matter for parallel scraping.
  • Isolation. Are cookies, storage, and cache scoped per session? Shared state across agents is a correctness bug waiting to happen.
  • Observability. Can you watch a session live and replay it after the fact? A live viewer plus recorded sessions turns "the agent failed" into "here is exactly where."
  • Network controls. Proxies and configurable browser settings per session, not global. Different tasks need different egress.
  • Concurrency model. How many sessions can you run, and how is that metered? Check the current limits on the pricing page rather than assuming a fixed ceiling.
  • Failure semantics. What happens when a session dies mid-task? Does the CLI reconnect, or does the agent get a clean error it can retry?

If a runtime cannot answer these, it is a demo, not infrastructure.

Where the CLI fits in a larger stack

A cli agent-browser is a good fit when:

  • Your agent already works through tool calls and shell commands.
  • You want a thin, inspectable layer between the model and the browser.
  • You need to run the same workflow locally and in CI.

It is a weaker fit when:

  • You need tight, low-latency control flow across dozens of steps in one page.
  • Your workflow is better expressed as a deterministic Playwright test.
  • You need fine-grained parallelism inside a single page (multiple tabs, workers).

In those cases, drop the CLI and connect Playwright or Puppeteer directly to the same hosted endpoint. The runtime does not care which client attaches.

For a broader comparison of connection styles, see remote web browser and remote control browser.

Getting started

The shortest path:

  1. Install the CLI (npm i -g agent-browser or the equivalent for your tool).
  2. Provision a hosted session and grab its CDP endpoint.
  3. Point the CLI at the endpoint via env var or flag.
  4. Run open, snapshot, click against a test page.
  5. Wire the schema into your agent's tool definitions.

The documentation covers session creation, CDP endpoints, and profile handling in detail. If you want to try the runtime without local setup first, remote browser online walks through the browser-only path.

The takeaway

The cli agent-browser pattern is a clean abstraction: a small command surface, a schema the agent can read, and skills that bundle common workflows. What it does not solve is where the browser lives. Pair the CLI with a hosted Chromium runtime and you get the ergonomics of a terminal tool with the isolation, persistence, and observability that production agents need. Start with the CLI, connect it to a real runtime, and keep the browser out of your dependency tree.