How to Integrate ego-browser in a Host Application: The Complete SDK Embedding Pattern

Call the installEgoSdk function from the ego-browser npm package to inject browser automation helpers directly into your application's scope, enabling seamless programmatic control without spawning a separate CLI process.

The ego-browser SDK from citrolabs/ego-lite provides a library-style integration that lets host applications embed browser automation capabilities directly. Unlike CLI-based usage, this pattern injects all helpers—openOrReuseTab, click, snapshotText, and more—onto a target object so your code can invoke them as native functions.

Installing the SDK in Your Host Application

The entry point for all embedding scenarios is src/index.ts. Import installEgoSdk and call it with a target object and optional configuration:

// host-app.js (Node)
import { installEgoSdk } from 'ego-browser';

function hostLogger(...args) {
  console.info('[ego]', ...args);
}

// Install onto globalThis for universal access
installEgoSdk(globalThis, { cliLog: hostLogger });

Once installed, all helpers become available on the target without additional imports. This matches the ergonomics agents experience when writing CLI heredoc scripts.

How installEgoSdk Works Internally

The installEgoSdk function in src/index.ts performs six sequential operations:

Step Implementation Purpose
Create helper context Calls helpers.helperContext() from src/helpers.ts Gathers all public helpers and their JSDoc metadata
Wrap with ready signal Wraps each helper to await an optional ready promise Ensures helpers only execute after browser runtime initialization
Inject onto target Uses Object.defineProperty with enumerable: false Adds helpers as non-enumerable properties to avoid pollution
Bind output sink Replaces console.log via createBufferedLog Routes helper output to cliLog or a default buffer
Expose ego runtime Attaches target.ego.helpers and target.ego.learnings Enables low-level CDP access when needed
Prevent double-wrapping Marks runtime with EGO_WRAPPED symbol Skips re-wrapping createTab and task-space APIs on subsequent calls

This architecture lets you treat ego-browser as an in-process library rather than an external tool.

Global Scope vs. Dedicated Namespace Integration

Global Scope Pattern

For maximum convenience—especially when migrating existing CLI scripts—install onto globalThis:

import { installEgoSdk } from 'ego-browser';

installEgoSdk(globalThis, { cliLog: console.log });

async function demo() {
  const task = await useOrCreateTaskSpace('demo-space');
  await openOrReuseTab('https://example.com', { wait: true });
  const text = await snapshotText();
  console.log('Extracted:', text);
}

demo();

Dedicated Namespace Pattern

For type safety and isolation, use a custom object:

import { installEgoSdk } from 'ego-browser';

const ego = {};
installEgoSdk(ego, { cliLog: console.log });

await ego.openOrReuseTab('https://example.com');
await ego.click('button.primary');

Both approaches preserve identical helper semantics; only the binding target differs.

TypeScript Service Integration Example

For production services requiring clean types and structured logging:

// service.ts
import { installEgoSdk } from 'ego-browser';
import { logger } from './logging';

export const ego = {} as Record<string, unknown>;

installEgoSdk(ego, {
  cliLog: (...args) => logger.info('[ego]', ...args),
});

export async function browseAndExtract(url: string): Promise<string> {
  const space = await ego.useOrCreateTaskSpace!('extract-data');
  await ego.openOrReuseTab!(url, { wait: true });
  const text = await ego.snapshotText!();
  await ego.completeTaskSpace!(space.id, { keep: false });
  return text as string;
}

Type assertions (!) or a proper interface definition can eliminate the need for as casts.

Serverless Function Pattern

For ephemeral execution environments where global state must not persist between invocations:

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

export async function handler(event) {
  const ego = {};  // Fresh target per invocation
  installEgoSdk(ego, { cliLog: console.log });

  const { url } = JSON.parse(event.body);
  await ego.useOrCreateTaskSpace('lambda-run');
  await ego.openOrReuseTab(url, { wait: true });
  
  const result = await ego.snapshotText();
  await ego.completeTaskSpace('lambda-run', { keep: false });
  
  return {
    statusCode: 200,
    body: JSON.stringify({ text: result })
  };
}

This pattern ensures complete isolation between concurrent function executions.

Key Source Files for Integration Reference

File Role Link
src/index.ts installEgoSdk implementation, helper injection, output sink binding View source
src/helpers.ts Public helper definitions (snapshotText, click, fill, etc.) View source
src/browser-runtime.ts Low-level CDP transport and session management View source
src/ego-errors.ts Custom error types (ElementResolutionError, etc.) View source

Summary

  • Single entry point: Import installEgoSdk from ego-browser (src/index.ts)
  • Flexible targeting: Install onto globalThis for convenience or a custom object for isolation
  • Non-enumerable injection: Helpers attach without polluting property enumerations
  • Ready-signal wrapping: Automatic synchronization with browser runtime initialization
  • Configurable logging: Route helper output through cliLog to integrate with your logging stack
  • Low-level access: Reach target.ego.helpers and target.ego.learnings for CDP-level operations
  • Idempotent installation: EGO_WRAPPED symbol prevents harmful double-wrapping

Frequently Asked Questions

What is the minimum code required to embed ego-browser?

Import installEgoSdk and call it with any target object. The shortest valid integration is:

import { installEgoSdk } from 'ego-browser';
installEgoSdk(globalThis);
await openOrReuseTab('https://example.com');

This assumes the default buffered logger is acceptable.

Can I use ego-browser without polluting global scope?

Yes. Pass a dedicated object as the first argument to installEgoSdk:

const ego = {};
installEgoSdk(ego);
// All helpers available as ego.openOrReuseTab, etc.

This pattern is recommended for library authors and test suites.

How does helper output get captured by my application?

Supply a cliLog function in the options object. This replaces the default buffered sink:

installEgoSdk(globalThis, {
  cliLog: (level, ...args) => myLogger.log(level, args.join(' '))
});

According to src/index.ts, all console.log calls inside helpers route through this sink.

What happens if I call installEgoSdk twice on the same target?

The function checks for the EGO_WRAPPED symbol on the target's ego property and returns early if already present. Mutating methods like createTab are not double-wrapped, preventing subtle bugs in re-entrant scenarios.

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 →