# How to Use the ego-browser Node.js CLI for Browser Automation

> Automate browser tasks with the ego-browser Node.js CLI. Execute JavaScript heredocs for seamless navigation, interaction, and screenshots with ego-lite. No boilerplate needed.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-08-26

---

**The ego-browser Node.js CLI executes JavaScript heredocs inside the ego-lite runtime, automatically injecting browser automation helpers that enable navigation, interaction, and screenshotting without boilerplate setup.**

The `ego-browser` package from the [citrolabs/ego-lite](https://github.com/citrolabs/ego-lite) repository provides a lightweight command-line interface for automating browser tasks. Unlike traditional automation frameworks that require verbose configuration, this tool runs scripts via standard input heredocs and injects a comprehensive set of helpers directly into the execution scope. This architecture enables rapid scripting for web scraping, testing, and workflow automation using nothing but JavaScript and shell commands.

## Understanding the CLI Architecture

The CLI entry point at [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts) handles the heavy lifting of script execution. When you invoke `ego-browser nodejs`, the tool reads the heredoc input, constructs an async function wrapper, and injects the **helper context** assembled in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts). This context exposes high-level actions such as `openOrReuseTab`, `click`, `snapshotText`, and `captureScreenshot`, along with low-level Chrome DevTools Protocol (CDP) access via [`src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/cdp-eval.ts) and [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts).

The runtime maintains a **ref map** that tracks DOM elements between commands. When `snapshotText()` executes, it refreshes this map and generates stable `@N` references that survive DOM mutations, allowing reliable interaction even with dynamic web applications. Under the hood, [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) handles selector resolution across multiple formats—CSS, XPath, `@N` syntax, and `loc=` coordinates—while automatically retrying transient failures.

## The Three-Step Automation Workflow

Every automation session follows a consistent lifecycle managed through task spaces.

### 1. Initialize a Task Space with `useOrCreateTaskSpace`

A **task space** is an isolated browsing context that persists login state and cookies across script executions. Create or reconnect to one using the helper exported from [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts):

```javascript
const task = await useOrCreateTaskSpace('unique-task-name')

```

This returns a task object containing an `id` used for subsequent management calls.

### 2. Execute Automation Scripts

Once initialized, scripts execute within the ego-lite runtime with full access to browser helpers. Pass your JavaScript via heredoc syntax:

```bash
ego-browser nodejs <<'EOF'
  // Automation code here
EOF

```

Inside the heredoc, call helpers like `openOrReuseTab(url, { wait: true })` to navigate, or `snapshotText()` to capture the semantic DOM structure and populate the ref map for interaction.

### 3. Terminate the Session with `completeTaskSpace`

When automation finishes, explicitly close the task space to release resources:

```javascript
await completeTaskSpace(task.id, { keep: false })

```

Pass `{ keep: true }` to leave the browser page open for manual inspection.

## Core Browser Automation Helpers

The helper context defined in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) exports these primary functions:

- **`openOrReuseTab(url, options)`** – Navigates to URLs or activates existing tabs
- **`click(selector, options)`** – Interacts with elements using CSS, XPath, `@N` refs, or `loc=` coordinates
- **`snapshotText()`** – Captures accessible text and visual layout, refreshing the ref map
- **`captureScreenshot()`** – Saves viewport or full-page images
- **`js(code)`** – Executes arbitrary JavaScript in the page context
- **`cdp(method, params)`** – Sends raw CDP commands through [`src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/cdp-eval.ts)
- **`cliLog(message)`** – Prints structured output to stdout
- **`waitForLoad()`** – Pauses execution until network idle

## Practical CLI Examples

The following patterns demonstrate complete automation cycles using the heredoc syntax documented in [`skills/ego-browser/SKILL.md`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/SKILL.md).

### Capturing Screenshots

This script opens a page, waits for load completion, captures the viewport, and logs the file path:

```bash
ego-browser nodejs <<'EOF'
const task = await useOrCreateTaskSpace('example screenshot')
await openOrReuseTab('https://example.com', { wait: true })
const img = await captureScreenshot()
cliLog('Screenshot saved: ' + img.path)
await completeTaskSpace(task.id, { keep: false })
EOF

```

### Clicking Elements and Extracting Data

Using `@N` references generated by `snapshotText` provides resilient selectors for dynamic content:

```bash
ego-browser nodejs <<'EOF'
await useOrCreateTaskSpace('login flow')
await openOrReuseTab('https://login.example.com', { wait: true })
await snapshotText()               // populates @N refs
await click('@23', { label: 'Click Sign‑In' })
await waitForLoad()
const data = await js(String.raw`() => {
  const el = document.querySelector('#user-info')
  return { name: el?.innerText }
}`)
cliLog('User name: ' + data.name)
await completeTaskSpace(task.id, { keep: false })
EOF

```

### Executing Raw CDP Commands

For advanced scenarios such as configuring download behavior, invoke CDP methods directly through the runtime:

```bash
ego-browser nodejs <<'EOF'
await useOrCreateTaskSpace('download pdf')
await openOrReuseTab('https://files.example.com/report.pdf')
await cdp('Page.setDownloadBehavior', {
  behavior: 'allow',
  downloadPath: '/tmp'
})
await cdp('Page.reload')
cliLog('Download started')
await completeTaskSpace(task.id, { keep: false })
EOF

```

## Selector Resolution and Error Handling

The [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) module provides robust element location across multiple identifier types. When calling `click()` or similar methods, the resolver attempts matches in this priority: explicit `@N` refs from the latest `snapshotText()`, `loc=` coordinates, standard CSS selectors, and XPath expressions.

The resolver distinguishes between **transient failures** (network latency, animation frames) and **permanent errors** (missing elements). It automatically retries transient issues with exponential backoff, eliminating the need for manual sleep statements in scripts. Classification logic in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) analyzes error codes and DOM stability signals to determine retry eligibility.

## Summary

- The **ego-browser Node.js CLI** executes heredoc scripts via [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts), injecting automation helpers from [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) automatically.
- Automation follows a three-step lifecycle: create a task space with `useOrCreateTaskSpace`, execute commands, then close with `completeTaskSpace`.
- **Helper functions** include `openOrReuseTab`, `click`, `snapshotText`, `captureScreenshot`, and raw CDP access via `cdp()`.
- **Selector flexibility** supports CSS, XPath, `@N` references, and `loc=` coordinates, with automatic resolution in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts).
- The runtime handles **transient error retries** automatically, while [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts) manages CDP transport and session state.

## Frequently Asked Questions

### How do I pass variables from my shell environment into the ego-browser script?

Environment variables are not automatically exposed inside the heredoc execution context. Instead, interpolate values directly in the shell before the heredoc opens, or use Node.js `process.env` access if you export variables before invoking the CLI. For sensitive data, pass values as literal strings in the heredoc content rather than through global scope.

### Can I reuse the same task space across multiple separate CLI invocations?

Yes. Call `useOrCreateTaskSpace('my-task')` with an identical name in subsequent heredocs to reconnect to the existing browsing context. The task space preserves cookies, localStorage, and session state. Remember to call `completeTaskSpace(id, { keep: false })` only when you want to destroy the context permanently.

### What is the difference between `snapshotText()` and `captureScreenshot()`?

`snapshotText()` captures the semantic DOM structure as accessible text and generates the `@N` reference map used for reliable element targeting, but produces no image files. `captureScreenshot()` generates actual PNG images of the viewport or full page using CDP commands. Most automation workflows call `snapshotText()` before interactions to establish stable selectors, then `captureScreenshot()` afterward to document visual state.

### How does the CLI handle element locations that change between page loads?

The **ref map** refreshes every time you invoke `snapshotText()`, assigning new `@N` identifiers based on the current DOM structure. Additionally, [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) implements automatic retry logic for transient failures caused by animations or loading states. For elements with dynamic positions, combine `snapshotText()` immediate usage with the `loc=` coordinates or prefer stable CSS selectors when available.