How to Integrate ego-lite with Other Tools: 5 Integration Patterns for Browser Automation

You can integrate ego-lite with other tools by importing installEgoSdk() for programmatic use, piping scripts via CLI for CI pipelines, embedding in test runners with __testing overrides, orchestrating isolated task spaces with newTaskSpace(), or extending via site-specific learnings in skills/ego-browser/learnings/.

ego-lite from citrolabs/ego-lite is a lightweight, Chrome DevTools Protocol (CDP)-based browser automation harness. Its architecture exposes a compact set of async helpers through the global ego object, making it straightforward to plug into CLI scripts, test runners, CI pipelines, and higher-level orchestrators. This guide covers how to integrate ego-lite with other tools using patterns derived directly from the source code.


Core Architecture for Integration

Understanding ego-lite's internal structure helps you choose the right integration point.

Component Role Source File
Entry point Decides CLI (runMain()) vs module (installEgoSdk()) mode src/index.ts
Helper Context Generates public API (nav, click, type, waitFor, etc.) src/helpers.ts
Browser Runtime Manages CDP transport, session attach/re-attach, event buffering src/browser-runtime.ts
Element Resolver Resolves locators (@ref, loc=css:…, xpath=…) with failure classification src/element-resolver.ts
Ref Management Maps numeric refs (@21) to backend node IDs, triggers re-snapshot src/ref-map.ts, src/ref-state.ts
Task Spaces Isolates browsing contexts, handles ownership (agent vs user) src/state.ts, src/env.ts
Learning Subsystem Loads site-specific tools from skills/ego-browser/learnings/<site>/manifest.json src/learning/index.ts
CLI Runner Reads JavaScript from stdin, wraps in async function, executes src/run.ts

All helpers are pure async functions returning promises, enabling seamless await from any JavaScript environment. Because helpers are generated at runtime, they stay synchronized with CDP session and task-space state automatically.


Integration Pattern 1: Direct Node.js Script Import

Programmatic integration via installEgoSdk() gives you full control for embedding ego-lite in applications, services, or custom automation frameworks.

In package/ego-browser/src/index.ts, the installEgoSdk() function injects helpers into a provided context (typically globalThis):

// my-automation.js
import { installEgoSdk } from 'ego-browser';

installEgoSdk(globalThis);  // injects helpers into global scope

async function demo() {
  await nav('https://example.com');
  const btn = await locate('css:#login');
  await click(btn);
  await type('#password', 's3cr3t');
  await waitFor(() => locate('css:.dashboard'), 10);
}

demo().catch(console.error);

This pattern is ideal when you need to combine ego-lite with existing Node.js code, databases, or API clients.


Integration Pattern 2: CLI Pipeline Integration

Shell-piped execution reads scripts from stdin via src/run.ts, perfect for CI/CD pipelines and one-off commands.

echo "await nav('https://example.com'); await click('#login');" \
  | npx ego-browser

The run() function in src/run.ts handles the wrapping and execution:

// From src/run.ts - conceptual flow
const userCode = await readStdin();
const wrapped = `(async () => { ${userCode} })()`;
await eval(wrapped);  // with helpers pre-injected

Use this pattern for GitHub Actions, GitLab CI, or shell-based workflows where installing Node.js dependencies is acceptable.


Integration Pattern 3: Test Runner Embedding

Test framework integration leverages the __testing API (exposed in src/helpers.test.mjs) to stub the CDP layer for fast, deterministic unit tests.

import { installEgoSdk, __testing } from 'ego-browser';

// Setup: stub CDP responses before initializing
__testing.setOverrides({
  sendCDPMessage: async (method, params) => {
    if (method === 'Runtime.evaluate') {
      return { result: { type: 'string', value: 'stubbed-title' } };
    }
    return {};
  }
});

installEgoSdk(globalThis);

test('page navigation returns correct title', async () => {
  await nav('https://example.com');
  const title = await evaluate(() => document.title);
  expect(title).toBe('stubbed-title');
});

For end-to-end tests with real browsers, reference src/taskspace-e2e.test.mjs which demonstrates full runtime exercise patterns.


Integration Pattern 4: Multi-Task Space Orchestration

Parallel browser isolation uses newTaskSpace(), switchTaskSpace(), and completeTaskSpace() from src/state.ts to run concurrent operations or segregate long-running jobs.

import { installEgoSdk } from 'ego-browser';

installEgoSdk(globalThis);

async function worker(name, url) {
  const ts = await newTaskSpace(name);      // create isolated space
  await switchTaskSpace(ts.id);              // activate it
  await nav(url);
  const title = await evaluate(() => document.title);
  console.log(`[${name}] Title: ${title}`);
  await completeTaskSpace(ts.id, { keep: false });  // cleanup
}

// Run parallel crawls
await Promise.all([
  worker('github-scrape', 'https://github.com'),
  worker('npm-scrape', 'https://npmjs.com'),
  worker('docs-scrape', 'https://docs.github.com')
]);

Each task space maintains independent CDP session state, enabling scenarios like:

  • Per-user browser sessions in multi-tenant applications
  • Background jobs that don't block interactive automation
  • A/B testing across isolated cookie/storage contexts

Integration Pattern 5: Site-Specific Learning Extensions

High-level tool integration via the learning subsystem in src/learning/index.ts lets other tools call semantic actions without knowing underlying selectors.

Add custom site logic under skills/ego-browser/learnings/<site>/:


skills/ego-browser/learnings/
├── github/
│   └── manifest.json
├── twitter/
│   └── manifest.json
└── custom-crm/
    └── manifest.json

Then invoke from any integration point:

// After installing SDK
await nav('https://github.com');

// Semantic action - no CSS selectors needed
await runSiteTool('github', 'createIssue', {
  repo: 'myorg/myrepo',
  title: 'Automated bug report from CI',
  body: 'Generated by ego-lite integration'
});

// Extract structured data
const issues = await runSiteTool('github', 'listOpenIssues', {
  repo: 'myorg/myrepo',
  limit: 10
});

This pattern enables product teams to build reusable automation libraries that abstract away site-specific implementation details.


Practical Integration Examples

Complete Node.js Service Integration

// services/scraper.js
import { installEgoSdk } from 'ego-browser';
import { saveToDatabase } from './db.js';

installEgoSdk(globalThis);

export async function scrapeProduct(url) {
  await nav(url);
  const price = await evaluate(() => 
    document.querySelector('.price').textContent
  );
  const inStock = await exists('css:.in-stock-badge');
  
  await saveToDatabase({ url, price, inStock, scrapedAt: new Date() });
  return { price, inStock };
}

GitHub Actions CI Workflow


# .github/workflows/data-quality.yml

name: Daily Data Quality Check
on:
  schedule:
    - cron: '0 6 * * *'  # 6 AM daily

jobs:
  verify-portal:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - run: npm ci
      
      - name: Run ego-lite validation
        run: |
          cat << 'EOF' | npx ego-browser
          await nav('https://internal-portal.example.com');
          await runSiteTool('portal', 'login', { 
            user: process.env.PORTAL_USER,
            pass: process.env.PORTAL_PASS 
          });
          const health = await runSiteTool('portal', 'checkHealth');
          if (!health.healthy) throw new Error(`Unhealthy: ${health.reason}`);
          console.log('Portal health verified');
          EOF
        env:
          PORTAL_USER: ${{ secrets.PORTAL_USER }}
          PORTAL_PASS: ${{ secrets.PORTAL_PASS }}

Jest Test Suite with Stubbed CDP

// __tests__/checkout-flow.test.js
import { installEgoSdk, __testing } from 'ego-browser';

beforeAll(() => {
  __testing.setOverrides({
    // Stub navigation to avoid real network calls
    sendCDPMessage: jest.fn(async (method, params) => {
      if (method === 'Page.navigate') {
        mockedCurrentUrl = params.url;
        return { frameId: 'mock-frame-1' };
      }
      if (method === 'Runtime.evaluate') {
        return { result: { value: `<html><body>Mock: ${mockedCurrentUrl}</body></html>` } };
      }
      return {};
    })
  });
  installEgoSdk(globalThis);
});

test('completes guest checkout', async () => {
  await nav('https://shop.example.com');
  await click('css:[data-testid="guest-checkout"]');
  await type('css:#email', 'test@example.com');
  await click('css:#place-order');
  
  const confirmation = await locate('css:.order-confirmation');
  expect(confirmation).toBeTruthy();
});

Summary

  • Import installEgoSdk() from src/index.ts for direct programmatic control in Node.js applications.

  • Pipe scripts to stdin via npx ego-browser for CLI and CI pipeline integration, handled by src/run.ts.

  • Use __testing.setOverrides() to stub CDP responses for fast, isolated unit tests in any test framework.

  • Orchestrate newTaskSpace() calls for parallel, isolated browser contexts suitable for multi-tenant or concurrent scenarios.

  • Extend via site learnings in skills/ego-browser/learnings/<site>/ to expose semantic, reusable actions to other tools.

All patterns rely on ego-lite's core design: async helper functions generated at runtime that automatically synchronize with underlying CDP state in src/browser-runtime.ts.


Frequently Asked Questions

Can I use ego-lite with Python or other non-JavaScript tools?

Not directly. ego-lite is a Node.js/TypeScript project built around CDP and JavaScript execution. For Python integration, use a subprocess approach: spawn npx ego-browser with Python's subprocess, pipe JavaScript code via stdin, and parse stdout. Alternatively, use a pure Python CDP library like PyCDP or playwright-python for native integration.

How do I handle authentication sessions across multiple ego-lite integrations?

Use switchTaskSpace() to maintain persistent browser state. Create a task space once, perform login, then reuse that space ID across separate script invocations by storing the ID externally (database, Redis, file). Task spaces in src/state.ts persist until completeTaskSpace() is called with { keep: false }.

What's the performance overhead of installEgoSdk() versus CLI execution?

Both use the same underlying runtime initialization in src/browser-runtime.ts. The installEgoSdk() approach avoids subprocess overhead and allows connection reuse, making it faster for batch operations. CLI execution incurs ~200-500ms Node.js startup per invocation—acceptable for CI jobs, suboptimal for high-frequency automation loops.

Does ego-lite support running without a real Chrome instance?

No. ego-lite requires a CDP-capable browser. The __testing.setOverrides() API in test files enables stubbing for unit tests, but production execution needs Chrome, Chromium, or a compatible browser launched with --remote-debugging-port. The runtime in src/browser-runtime.ts manages this connection transparently.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →