How Helpers Are Injected into the ego-browser Runtime via helperContext

Helpers are injected into the ego-browser runtime through a two-step pipeline: helperContext() assembles driver-level primitives into high-level façades, and installEgoSdk() projects those façades onto globalThis and the native ego.helpers bridge.

The ego-browser package exposes a specialized runtime environment where agent scripts run with direct access to browser automation primitives. Understanding how helperContext populates this environment is essential for debugging helper availability, creating custom contexts, or extending the SDK's capabilities. This article explains the complete injection mechanism based on the source code in citrolabs/ego-lite.

How helperContext Builds the Helper Façade

The helperContext function in src/helpers.ts serves as the factory for all runtime helpers. It does not expose raw driver modules directly; instead, it composes higher-level façade objects that wrap lower-level functionality.

Individual Drivers → Façade Objects

helperContext gathers driver objects and transforms them through dedicated factory functions:

Factory Function Wrapped Drivers Exposed As
createPageFacade() pointer, keyboard, locator, nav, observe, waits page.goto(), page.click(), page.locator()
createBrowserFacade() Browser-level CDP operations browser.close(), browser.version()
createTaskSpacesFacade() Task space management taskSpaces.create(), taskSpaces.switch()
createSiteFacade() Site-scoped operations site.runTool(), site.getState()

Additional standalone utilities include fetch (with server and browser variants) and direct cdp access for Chrome DevTools Protocol commands.

The helperContext Implementation

The composite object construction happens at lines 222–250 of src/helpers.ts:

export function helperContext(extra: any = {}) {
  const all = {
    page: createPageFacade(),
    browser: createBrowserFacade(),
    taskSpaces: createTaskSpacesFacade(),
    site: createSiteFacade(),
    fetch: { server: serverFetch, browser: browserFetch },
    cdp,
    ...extra,
  };
  return {
    ...all,
    help: (...names: string[]) => {
      // Delegates to helpRuntime / formatHelp for introspection
    },
  };
}

The help method enables runtime introspection—agents can call help('page') to receive formatted documentation for specific helpers.

How installEgoSdk Injects Helpers into the Runtime

With the façade assembled, installEgoSdk in src/index.ts handles the actual projection into the JavaScript environment. This function runs automatically when the ego-browser package loads or when the CLI invokes a script.

Target Object and Property Definition

By default, installEgoSdk uses globalThis as its target. It defines each helper as a non-enumerable property to avoid polluting Object.keys(globalThis) output while remaining accessible to agent code.

export function installEgoSdk(
  target: InstallTarget = globalThis,
  options: InstallEgoSdkOptions = {},
) {
  const context = options.context || helpers.helperContext();
  
  // Remove legacy helper names to prevent collisions
  for (const legacy of LEGACY_GLOBAL_HELPERS) {
    if (legacy in target) {
      delete (target as any)[legacy];
    }
  }
  
  for (const [name, value] of Object.entries(context)) {
    const exposed = SYNC_HELPERS.has(name)
      ? value
      : wrapReady(value, readySignal, () => readyError, [name]);
    Object.defineProperty(target, name, {
      value: exposed,
      writable: true,
      configurable: true,
      enumerable: false,  // Hidden from enumeration
    });
    installed[name] = exposed;
  }
  
  // Expose to native bridge via ego.helpers
  if (target.ego && typeof target.ego === 'object') {
    target.ego.helpers = installed;
  }
}

Synchronous vs. Asynchronous Helper Handling

Not all helpers wait for the same lifecycle events:

  • Synchronous helpers (help and a few others listed in SYNC_HELPERS) execute immediately. These are typically introspection utilities that don't require browser state.
  • Asynchronous helpers are wrapped by wrapReady, which intercepts calls and queues them until the SDK emits a ready signal. This ensures browser session initialization completes before any navigation or element interaction occurs.

Factory Helper Special Wrapping

Factory helpers like page.locator receive recursive wrapping. The factory function itself executes synchronously and returns a locator object, but that object's methods (click(), fill(), textContent()) are individually wrapped to respect the ready state. This design allows:

// Valid: synchronous factory call
const link = page.locator('a.external');

// Deferred: method executes only after ready signal
await link.click();

Practical Code Examples

Standard Agent Script Usage

In typical ego-browser agent scripts, no imports are required—helpers exist as globals:

// Legacy global helper (backwards compatible)
click('button#submit');

// Façade-based navigation
await page.goto('https://example.com');

// Factory pattern with deferred execution
await page.locator('a.link').click();
await site.runTool('extractTable', { selector: 'table.data' });

Custom Helper Context

For testing or specialized environments, create a modified context:

import { helperContext } from 'ego-browser/helpers.js';

const custom = helperContext({ 
  myVar: 42,
  customLogger: console.debug 
});

// Manually project to global scope for legacy script compatibility
globalThis.myVar = custom.myVar;

SDK Installation Internals

The bootstrap sequence that prepares any ego-browser execution environment:

import * as helpers from './helpers.js';
import { installEgoSdk } from './index.js';

// Default installation: globalThis + ego.helpers
installEgoSdk();

// Custom target for isolated environments
const isolatedScope = {};
installEgoSdk(isolatedScope, {
  context: helpers.helperContext({ sandboxed: true })
});

Key Source Files Reference

File Path Responsibility
src/helpers.ts helperContext() implementation; façade factories; driver aggregation
src/index.ts installEgoSdk(); property injection; legacy cleanup; ego.helpers attachment
src/run.ts CLI entry point that invokes installEgoSdk before executing user scripts
src/browser-runtime.ts CDP session management; ready signal source that wrapReady awaits
src/driver/*.ts Low-level modules: pointer.ts, keyboard.ts, locator.ts, nav.ts, observe.ts, waits.ts, files.ts, screencast.ts, http.ts

Summary

  • helperContext() in src/helpers.ts assembles raw drivers into high-level façades (page, browser, site, etc.) and returns a composite object with introspection support.
  • installEgoSdk() in src/index.ts projects this composite onto globalThis as non-enumerable properties and attaches it to ego.helpers for native bridge access.
  • The wrapReady wrapper ensures asynchronous helpers defer execution until browser session initialization completes.
  • Factory helpers receive recursive wrapping so their returned objects also respect the ready state.
  • Legacy helper names are explicitly removed before injection to prevent naming collisions.

Frequently Asked Questions

What is the difference between helperContext and installEgoSdk?

helperContext is a pure factory function that creates the helper object structure without any side effects. installEgoSdk is the mutating installer that takes that structure and attaches it to globalThis and ego.helpers. You can call helperContext multiple times to create independent context instances, but installEgoSdk should typically run once per environment.

Why are some helpers synchronous while others wait for a ready signal?

The SYNC_HELPERS set contains only utilities that don't depend on browser state—primarily help for documentation introspection. All navigation, element interaction, and CDP-dependent helpers are wrapped with wrapReady to prevent errors from calling page.goto() before the Chrome DevTools Protocol session is established.

How can I extend helpers with custom functionality?

Pass additional properties to helperContext() via its extra parameter, then either use that custom context directly or pass it through installEgoSdk's options.context. Custom helpers follow the same wrapping rules: unless added to SYNC_HELPERS, they will wait for the ready signal.

Where can access the helpers if globalThis is polluted or unavailable?

The SDK mirrors all installed helpers onto globalThis.ego.helpers (or your custom target's .ego.helpers). This provides a reliable secondary access point and enables the native bridge to expose helpers to external agents or debugging tools.

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 →