# How to Debug ego-lite Applications: CLI Flags, CDP Tracing, and Best Practices

> Debug ego-lite applications effectively using CLI flags like --debug-clicks and EGO_BROWSER_DEBUG_CDPMESSAGES. Inspect CDP traffic and console logs for efficient troubleshooting.

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

---

**Enable `--debug-clicks` or `EGO_BROWSER_DEBUG_CLICKS=1` to see per-click logging, use `EGO_BROWSER_DEBUG_CDPMESSAGES=1` to inspect raw Chrome DevTools Protocol traffic, and leverage `console.log` captured by the output sink for ad-hoc state inspection.**

ego-lite executes user-supplied JavaScript inside the **ego-browser** harness, routing all output through a centralized `console.log` sink. When automation scripts fail, the runtime provides multiple built-in mechanisms to inspect state, trace CDP traffic, and diagnose element resolution issues. This guide covers the complete debugging toolkit based on the citrolabs/ego-lite source code.

---

## Debug Flags and Environment Variables

The entry point in [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts) parses CLI flags and environment variables that control logging verbosity. These are the primary levers for debugging ego-lite applications.

### `--debug-clicks` for Per-Click Visibility

Set this flag to log every click resolution, including the resolved DOM element and click coordinates.

```bash

# CLI flag

node dist/out/index.js --debug-clicks < script.js

# Or environment variable

EGO_BROWSER_DEBUG_CLICKS=1 echo "await page.click('@5')" | node dist/out/index.js

```

The flag handling appears at lines 85-86 in [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts), which injects the debug context into the runtime before script execution.

Expected output format:

```

[debug] click resolved to backendNodeId 42 (CSS selector "a.more")
[debug] click coordinates: x=34 y=12

```

### `EGO_BROWSER_DEBUG_CDPMESSAGES` for Raw CDP Traffic

Set this environment variable to stream every Chrome DevTools Protocol request and response. This is essential when verifying that the correct CDP methods are being invoked.

```bash
EGO_BROWSER_DEBUG_CDPMESSAGES=1 node dist/out/index.js < script.js

```

Sample output showing the request-response cycle:

```

→ Runtime.evaluate {"expression":"document.title"}
← Runtime.evaluate {"result":{"type":"string","value":"Example Domain"}}
→ Page.screenshot {"path":"/tmp/shot.png"}
← Page.screenshot {"data":"iVBORw0KGgoAAAANSUhEUg..."}

```

This low-level tracing is implemented in [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts), which manages the CDP transport and session lifecycle.

---

## Using console.log and the Output Sink

All `console.log` calls from your script are captured by [`src/output-sink.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/output-sink.ts), which buffers and formats output before it reaches the host. This makes `console.log` the primary tool for ad-hoc debugging.

### Basic State Inspection

```javascript
const info = await page.info()
console.log('Page info →', info)

const title = await page.evaluate('document.title')
console.log('Current title:', title)

```

The output sink ensures these logs appear in the CLI stream with proper formatting and timestamps.

### Overriding the Sink with `--doctor`

The `--doctor` flag reconfigures the output sink to dump raw CDP messages or additional metadata, useful when the default formatting obscures critical details.

---

## Debugging Element Resolution Failures

Element resolution is handled by [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts), which converts locators (`@N`, `loc=css:...`, `xpath=...`) to CDP object IDs. When resolution fails, the system classifies errors and prints diagnostic messages.

### Common Error Types

| Error Class | Meaning | Resolution |
|-------------|---------|------------|
| **Transient** | Element not yet in DOM | Add `await page.waitForSelector()` |
| **Permanent** | Invalid or malformed selector | Check locator syntax in error message |
| **Stale ref** | Element removed after snapshot | Call `await page.snapshot()` to refresh |

### Force Ref-Map Refresh with page.snapshot()

DOM mutations invalidate the internal ref-map. When you encounter "ref-not-found" errors, explicitly refresh the snapshot:

```javascript
await page.navigate('https://example.com')
await page.click('button#load-more')   // triggers AJAX content load
await page.snapshot()                  // forces fresh ref-map
await page.click('@7')                 // now resolves to newly added element

```

The snapshot method is exposed in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) as part of the `page.*` API surface.

---

## Task-Space Diagnostics

Task spaces isolate browser sessions and state. When spaces hang or disappear, use the `taskSpaces.*` helpers for introspection.

### Listing and Claiming Spaces

```javascript
// Dump all known task spaces
console.log('All spaces:', await taskSpaces.list())

// Attach to specific space for debugging
await taskSpaces.claimTaskSpace('my-space')
console.log('Current space:', await taskSpaces.current())

// Take over a space that may be in bad state
await taskSpaces.takeOverTaskSpace('my-space')

```

The underlying state structures are defined in [`src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/state.ts), which tracks space registry and session bindings.

---

## Running the Test Harness with Debug Output

The repository's end-to-end tests in `src/taskspace-e2e.test.mjs` exercise the full stack and can be run with verbose logging.

```bash
DEBUG=* npm test

```

The `DEBUG=*` environment variable activates every internal `debug()` call, showing:

- Resolver attempts and selector parsing
- Session re-attachment events
- Event-queue statistics

This provides the same diagnostic output as production runs, but with assertion safety.

### Watch Mode for Iterative Debugging

The test runner automatically rebuilds and reruns `.test.mjs` files on changes. This is the fastest way to iterate on fixes while observing debug output.

---

## Complete Debugging Workflows

### Workflow 1: Investigating a Failing Click

```javascript
// click-debug.js
await page.navigate('https://example.com')
await page.waitForSelector('a.more')
await page.click('@12')   // suspect selector

```

Execute with full visibility:

```bash
EGO_BROWSER_DEBUG_CLICKS=1 \
EGO_BROWSER_DEBUG_CDPMESSAGES=1 \
node dist/out/index.js < click-debug.js

```

### Workflow 2: Verifying CDP Method Calls

```javascript
// cdp-verify.js
await page.evaluate('document.title')
await page.screenshot({ path: '/tmp/shot.png' })

```

Trace the exact protocol exchange:

```bash
EGO_BROWSER_DEBUG_CDPMESSAGES=1 node dist/out/index.js < cdp-verify.js

```

### Workflow 3: Post-AJAX Element Interaction

```javascript
// ajax-handler.js
await page.navigate('https://example.com')
await page.click('button#load-more')
await page.snapshot()      // critical: refresh after DOM change
const items = await page.evaluate(() => document.querySelectorAll('.item').length)
console.log('Items after load:', items)
await page.click('@7')     // interact with new element

```

---

## Key Source Files for Debugging Reference

| File | Purpose |
|------|---------|
| [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts) | CLI flag parsing, debug context injection, console redirection |
| [`src/output-sink.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/output-sink.ts) | Log buffering, formatting, `--doctor` override support |
| [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) | Public API surface (`page.*`, `browser.*`, `taskSpaces.*`) |
| [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) | Locator parsing, error classification, diagnostic messages |
| [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts) | CDP transport, session lifecycle, message tracing |
| [`src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/state.ts) | Task-space and session state data structures |
| `src/taskspace-e2e.test.mjs` | Reference e2e implementation with debug hooks |

---

## Summary

- **Use `--debug-clicks` or `EGO_BROWSER_DEBUG_CLICKS=1`** to see exactly which element is being clicked and where
- **Enable `EGO_BROWSER_DEBUG_CDPMESSAGES=1`** to trace raw CDP traffic and verify protocol-level behavior
- **Leverage `console.log` with the output sink** for ad-hoc state inspection; all logs are captured and formatted by [`src/output-sink.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/output-sink.ts)
- **Call `page.snapshot()` after DOM mutations** to refresh the ref-map and prevent stale selector errors
- **Inspect task-space state with `taskSpaces.list()` and `taskSpaces.takeOverTaskSpace()`** when sessions hang or disappear
- **Run `DEBUG=* npm test`** to see detailed internal logs during end-to-end test execution

---

## Frequently Asked Questions

### How do I see which element is actually being clicked?

Enable per-click logging with the `--debug-clicks` flag or `EGO_BROWSER_DEBUG_CLICKS=1` environment variable. According to the ego-lite source code in [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts), this prints the resolved `backendNodeId`, CSS selector, and click coordinates for every `page.click()` call.

### Why does my selector work sometimes but fail other times?

This indicates a **stale ref-map**. The element resolver in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) maintains a mapping of `@N` references to CDP object IDs. When JavaScript modifies the DOM, these references invalidate. Insert `await page.snapshot()` before the failing operation to force a refresh.

### Can I see the raw Chrome DevTools Protocol messages?

Yes. Set `EGO_BROWSER_DEBUG_CDPMESSAGES=1` before running your script. The [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts) file implements this tracing, streaming every CDP request and response through the output sink. This is invaluable for verifying that `Runtime.evaluate`, `Page.screenshot`, and other methods receive correct parameters.

### How do I debug a task-space that appears to hang?

Use the `taskSpaces.*` helpers from [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts). First call `await taskSpaces.list()` to see all registered spaces, then `await taskSpaces.takeOverTaskSpace('name')` to attach and inspect internal state. The underlying registry is maintained in [`src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/state.ts).