← Blog

BLOG

Terminal Browser: Run a Real Browser From the CLI

A terminal browser for AI agents: drive hosted Chromium over CDP from the command line, with sessions, profiles, and a live viewer.

9 min readRemote Browser
Ivory computer towers connected across terracotta platforms in a quiet desert landscape
The infrastructure behind your browserREMOTE BROWSER / ILLUSTRATION

A terminal browser is a browser you drive from a shell instead of a window. For AI agents and automation, that usually means a headless or hosted Chromium instance you control over the Chrome DevTools Protocol (CDP), launched from a CLI or a short script. The terminal is the control surface; the browser itself runs somewhere else — a local process, a container, or a hosted runtime like Remote Browser.

This guide covers what a terminal browser actually is, where it fits, how to wire one up with Playwright over CDP, and what to check before you put it in production. If you are still deciding between a local and remote setup, the remote browser overview is a good starting point.

What "terminal browser" means in practice

The phrase gets used for two different things, and conflating them causes bad architecture decisions.

Text-mode browsers like Lynx, w3m, or links render HTML as text inside your terminal. They are useful for reading docs over SSH, but they do not execute modern JavaScript, cannot run a React app, and have no CDP endpoint. An AI agent cannot meaningfully automate a SaaS dashboard through one.

Terminal-driven browsers are the other meaning, and the one that matters for automation. Here the terminal is where you issue commands, but the browser is a full Chromium instance with a rendering engine, a network stack, and a DevTools endpoint. You connect to it with Playwright, Puppeteer, Selenium, or a CLI tool, and you script it. This is what people mean by a "terminal web browser" in the agent context, and it is what the rest of this article covers.

The distinction matters because a terminal-driven browser inherits every capability of a real browser — cookies, localStorage, service workers, WebGL, file downloads — while giving you a scriptable, reproducible interface. That combination is why terminal-driven browsers are the default substrate for web automation tools aimed at agents.

Why agents and developers reach for a terminal browser

The appeal is not nostalgia for the command line. It is that a terminal interface is the natural fit for three workloads:

  • Agent tool calls. An LLM agent does not need a GUI. It needs a small set of verbs — navigate, click, type, read, screenshot — that map cleanly to CLI subcommands or function calls. A terminal browser exposes exactly that surface.
  • CI and cron jobs. A browser that launches from a shell script, runs a task, and exits fits into existing pipelines without a desktop environment or a display server.
  • Remote debugging. When something breaks in production, you want to attach to the live session, inspect the DOM, and read console logs — all from a terminal, without screen-sharing a VM.

A terminal browser also composes well. You can pipe its output into jq, wrap it in a shell loop, or call it from a Node script. That composability is why CLI-first tools like agent-browser exist: they give an agent a stable command vocabulary instead of asking it to generate raw CDP calls.

Local terminal browser vs hosted terminal browser

The first real decision is where Chromium runs. Both options give you a terminal interface; they differ in what you have to operate.

DimensionLocal terminal browserHosted terminal browser
Where Chromium runsYour machine or CI runnerProvider's infrastructure
SetupInstall Chromium, match versions, manage depsOne API call returns a CDP endpoint
ScalingLimited by local CPU/RAM; one process per sessionSessions provisioned on demand
Session isolationYou manage containers or profilesIsolated per session by default
Persistent loginsManual profile directoriesConfigurable persistent profiles
DebuggingLocal DevTools or screenshotsLive viewer plus CDP attach
Network egressYour IPConfigurable proxy settings
Operational costYour time and hardwareMetered per browser-hour (see /pricing)

Local is the right call for development and for tasks that must run on a specific network. Hosted is the right call when you need concurrency, isolation, or a browser that outlives your laptop lid closing.

The failure mode to watch for is the "works on my machine" terminal browser: a local Chromium pinned to a version your CI runner does not have, with a profile directory that accumulates state until a login breaks. Hosted runtimes remove that class of bug by making the browser a disposable, versioned resource.

Wiring a terminal browser with Playwright over CDP

The most portable way to drive a terminal browser is Playwright's connectOverCDP, which attaches to any Chromium exposing a DevTools endpoint. The Playwright CDP documentation is the authoritative reference for the API surface.

Here is a TypeScript example that connects to a hosted session, runs a task, and cleans up. The endpoint comes from your runtime's session API; the shape below is the standard ws:// CDP URL.

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

const CDP_ENDPOINT = process.env.BROWSER_CDP_URL!;

async function runTask(): Promise<void> {
  let browser: Browser | undefined;
  try {
    browser = await chromium.connectOverCDP(CDP_ENDPOINT, {
      timeout: 30_000,
    });

    // Reuse the existing context so cookies and storage persist
    // for the lifetime of the session.
    const context = browser.contexts()[0] ?? (await browser.newContext());
    const page: Page = context.pages()[0] ?? (await context.newPage());

    page.setDefaultTimeout(15_000);

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

    await page.getByRole("button", { name: "Export" }).click();
    await page.waitForEvent("download");

    const title = await page.title();
    console.log(`task complete on: ${title}`);
  } catch (err) {
    console.error("terminal browser task failed:", err);
    throw err;
  } finally {
    // Disconnect without killing the remote browser if you want
    // to inspect it afterwards; close the session via the API instead.
    await browser?.close();
  }
}

runTask();

A few details that matter in production:

  • `connectOverCDP` attaches, it does not launch. You are responsible for creating and terminating the session through the runtime's API. Closing the Playwright Browser disconnects your client; it does not necessarily stop the remote browser.
  • Reuse the default context. Creating a fresh context on every run discards cookies and defeats persistent profiles. If you need isolation, create a new session instead.
  • Set explicit timeouts. Agent-generated selectors can hang. A 15-second default timeout turns a stuck task into a fast failure you can retry.
  • Log the session ID. When a task fails at 3 a.m., the session ID is how you find the recording or attach the live viewer.

If you prefer a CLI over a script, agent-browser wraps the same CDP connection in subcommands, which is often easier to expose to an LLM as a tool. The agent-browser CLI guide walks through that setup.

What a terminal browser needs to be production-grade

A terminal browser that works for a demo and one that works for a thousand agent runs are different products. These are the criteria worth evaluating before you commit.

Session lifecycle and isolation

Each agent task should get its own browser session, with its own cookies, storage, and network state. Shared sessions leak state between tasks and produce bugs that are nearly impossible to reproduce. Look for explicit session creation and termination APIs, not a single long-lived browser you multiplex.

Persistent profiles

Some tasks need a logged-in state across runs — a CRM, an internal tool, a site with MFA. Persistent profiles let you save and reuse browser state without re-authenticating every time. The trade-off is that profiles are sensitive: treat them as credentials and scope them per tenant. See browser-use profiles for how this is typically modeled.

Live viewer and recordings

Terminal output is lossy. When an agent fails, you want to see what the page actually looked like. A live viewer lets you attach to a running session; recordings let you replay it after the fact. Both shorten debugging from hours to minutes.

Network and browser settings

Many automation tasks need a specific egress IP or locale. Hosted runtimes expose configurable proxy and browser settings — region, user agent, timezone — without you running a proxy fleet. Be precise about what you need: "configurable browser settings" is a real capability, so confirm the exact options against the provider's documentation before you build a workflow around them.

Usage controls

Browser-hours add up. You want per-session limits, concurrency caps, and a clear view of what each task consumed. Remote Browser meters usage and publishes current rates on /pricing; check there rather than relying on secondhand numbers.

Compatibility

Your terminal browser should speak standard CDP so that Playwright, Puppeteer, and Selenium all work against it. Vendor-specific protocols lock you in and break the moment you want to switch libraries. The Chrome DevTools Protocol is the stable contract; prefer runtimes that implement it faithfully.

Common terminal browser patterns

Once the plumbing is in place, a few patterns recur across teams:

  • Agent tool backend. Expose navigate, click, type, read, and screenshot as CLI subcommands or MCP tools. The agent picks a verb; the terminal browser executes it against a hosted session. This is the pattern behind most browser agent AI stacks.
  • Scheduled scraping. A cron job spins up a session, logs in with a persistent profile, extracts data, and tears the session down. No display server, no long-running VM.
  • QA harness. A test runner connects over CDP to a hosted browser, runs Playwright specs, and captures traces. The UI test harness guide covers this in detail.
  • Human-in-the-loop. The agent drives until it hits a CAPTCHA or MFA prompt, then hands the live viewer to a human who completes the step. The session persists across the handoff.

Each pattern benefits from the same property: the browser is a remote, addressable resource, not a process tied to one machine.

Where a terminal browser is the wrong tool

Being honest about limits saves time:

  • Interactive visual work. If a human needs to see and click, a GUI browser is better. The terminal is for scripted control.
  • Heavy client-side rendering with no CDP need. If you only need HTML, an HTTP client is cheaper and faster than a full browser.
  • Tasks that require a specific desktop app. Some flows only work in a native app; a browser cannot substitute.

For everything else — agent tasks, scraping, QA, monitoring — a terminal-driven browser is the most flexible interface available.

Getting started

The shortest path is to create a hosted session, copy its CDP endpoint, and connect with the Playwright snippet above. From there, add a persistent profile if you need logins, a proxy setting if you need a specific region, and a live viewer for debugging.

A terminal browser is not a novelty. It is the interface that lets agents, scripts, and humans drive the same real Chromium instance without a desktop in the loop — and that is exactly what production web automation needs.