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

> Integrate ego-browser SDK into your host app using the installEgoSdk function. Embed browser automation helpers directly for seamless programmatic control without a separate CLI.

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

---

**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](https://github.com/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`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts). Import `installEgoSdk` and call it with a target object and optional configuration:

```javascript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts) performs six sequential operations:

| Step | Implementation | Purpose |
|------|----------------|---------|
| **Create helper context** | Calls `helpers.helperContext()` from [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`:

```javascript
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:

```javascript
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:

```typescript
// 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:

```javascript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts) | `installEgoSdk` implementation, helper injection, output sink binding | [View source](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/index.ts) |
| [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) | Public helper definitions (`snapshotText`, `click`, `fill`, etc.) | [View source](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) |
| [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts) | Low-level CDP transport and session management | [View source](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) |
| [`src/ego-errors.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ego-errors.ts) | Custom error types (`ElementResolutionError`, etc.) | [View source](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/ego-errors.ts) |

## Summary

- **Single entry point**: Import `installEgoSdk` from `ego-browser` ([`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/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:

```javascript
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`:

```javascript
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:

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

```

According to [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/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.