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.

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.
| Dimension | Local terminal browser | Hosted terminal browser |
|---|---|---|
| Where Chromium runs | Your machine or CI runner | Provider's infrastructure |
| Setup | Install Chromium, match versions, manage deps | One API call returns a CDP endpoint |
| Scaling | Limited by local CPU/RAM; one process per session | Sessions provisioned on demand |
| Session isolation | You manage containers or profiles | Isolated per session by default |
| Persistent logins | Manual profile directories | Configurable persistent profiles |
| Debugging | Local DevTools or screenshots | Live viewer plus CDP attach |
| Network egress | Your IP | Configurable proxy settings |
| Operational cost | Your time and hardware | Metered 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
Browserdisconnects 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, andscreenshotas 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.
- Read the documentation for session creation, CDP endpoints, and profile management.
- Compare remote browser options if you are evaluating providers.
- Review /pricing to model cost per task before you scale concurrency.
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.