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()fromsrc/index.tsfor direct programmatic control in Node.js applications. -
Pipe scripts to stdin via
npx ego-browserfor CLI and CI pipeline integration, handled bysrc/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →