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 (
helpand a few others listed inSYNC_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()insrc/helpers.tsassembles raw drivers into high-level façades (page,browser,site, etc.) and returns a composite object with introspection support.installEgoSdk()insrc/index.tsprojects this composite ontoglobalThisas non-enumerable properties and attaches it toego.helpersfor native bridge access.- The
wrapReadywrapper 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →