How ego‑lite's installEgoSdk Injects Helper Functions into the Global Scope
installEgoSdk creates a helper context, wraps every function with readiness logic, and attaches each helper to globalThis using Object.defineProperty with non‑enumerable descriptors.
The ego-lite browser automation SDK exposes dozens of helper functions—navigate, click, snapshot, page.title, and more—directly to agent scripts without requiring explicit imports. This global injection is orchestrated by the installEgoSdk function in package/ego-browser/src/index.ts. According to the citrolabs/ego-lite source code, the process combines context generation, asynchronous readiness handling, and careful property definition to produce a clean, testable API.
What installEgoSdk Does
At its core, installEgoSdk transforms a target object (typically globalThis) into a populated runtime environment. The function accepts two parameters:
target– the object to decorate (defaults toglobalThis)options– configuration including areadypromise for initialization gating
The typical call pattern appears throughout the codebase:
import { installEgoSdk } from 'ego-browser';
// Default installation on globalThis
installEgoSdk(globalThis, { ready: cdpConnectionPromise });
Step 1: Building the Helper Context
Before any injection occurs, installEgoSdk generates the full set of available helpers by calling helperContext() from package/ego-browser/src/helpers.ts. This function returns a structured object containing:
- Page helpers:
navigate,goBack,reload,screenshot - Interaction helpers:
click,type,select,scroll - Observation helpers:
snapshot,query,waitFor - Namespace objects:
page,taskSpace,observewith nested methods
The helperContext implementation spans lines 822–862 in helpers.ts, dynamically assembling the helper tree based on available protocol capabilities.
Step 2: Wrapping Helpers with Readiness Logic
Not all helpers execute immediately. If the SDK awaits a CDP connection or browser initialization, every asynchronous helper must pause until the ready promise resolves. installEgoSdk handles this through the wrapReady utility (lines 19–38 in index.ts):
// Simplified excerpt from package/ego-browser/src/index.ts
function wrapReady<T>(fn: T, ready: Promise<void>, isFactory = false): T {
if (typeof fn !== 'function') {
// Recurse into namespace objects
return Object.fromEntries(
Object.entries(fn).map(([k, v]) => [k, wrapReady(v, ready, SYNC_HELPERS.has(k))])
) as T;
}
return (async (...args: unknown[]) => {
await ready; // Wait for initialization
return (fn as Function)(...args);
}) as T;
}
The wrapper distinguishes between:
- Synchronous helpers (
SYNC_HELPERSset) – exposed directly without wrapping - Synchronous factory helpers (
SYNC_FACTORY_HELPERSset) – wrapped withisFactory: trueto control argument evaluation timing - Asynchronous helpers – fully wrapped to await the readiness promise and propagate initialization errors
Step 3: Defining Properties on the Target
With wrapped helpers prepared, installEgoSdk iterates the context entries and attaches each to the target object (lines 44–73 in index.ts):
const target = providedTarget ?? globalThis;
for (const [name, value] of Object.entries(helperContext())) {
const isSync = SYNC_HELPERS.has(name) || SYNC_FACTORY_HELPERS.has(name);
const finalValue = isSync ? value : wrapReady(value, options.ready);
Object.defineProperty(target, name, {
value: finalValue,
writable: true,
configurable: true,
enumerable: false // Hides from for...in loops
});
}
The non‑enumerable descriptor is deliberate—helpers don't appear in object enumerations, reducing noise when scripts inspect globalThis or log variables.
Step 4: Buffering Console Output
The SDK intercepts console.log to capture script output for host consumption (lines 76–84 in index.ts):
const originalLog = console.log;
const logBuffer: unknown[] = [];
console.log = (...args: unknown[]) => {
logBuffer.push(args);
originalLog.apply(console, args);
};
// Expose flush mechanism
(target as any).flushLogs = () => {
const logs = logBuffer.splice(0, logBuffer.length);
return logs;
};
This redirection ensures that even early console.log calls before host attachment are preserved and can be flushed on demand.
Step 5: Integrating with the ego Runtime
When installEgoSdk detects an existing ego object on the target (common when running inside the full ego runtime), it performs additional setup (lines 87–104 in index.ts):
if (target.ego) {
// Emit browser version information
emitUpdateNotice(target.ego);
// Store reference to helpers for runtime introspection
target.ego.helpers = helperContext();
// One‑time wrapping of mutating methods
if (!target[EGO_WRAPPED]) {
target[EGO_WRAPPED] = true;
const originalCreateTab = target.ego.createTab;
target.ego.createTab = async (...args: unknown[]) => {
const tab = await originalCreateTab(...args);
// Re‑inject helpers into new tab's context
installEgoSdk(tab, { ready: options.ready });
return tab;
};
}
}
The EGO_WRAPPED symbol prevents double‑wrapping if installEgoSdk is called multiple times during the same session.
Complete Usage Example
import { installEgoSdk } from 'ego-browser';
// Standard installation
installEgoSdk(globalThis, {
ready: connectToBrowser()
});
// Helpers are now globally available
await navigate('https://example.com');
await click('button#submit');
const title = await page.title();
console.log(`Loaded: ${title}`); // Captured by buffered sink
// Custom target for isolated testing
const sandbox: Record<string, unknown> = {};
installEgoSdk(sandbox, { ready: Promise.resolve() });
await (sandbox.navigate as Function)('https://test.local');
Key Design Decisions in the Injection Mechanism
| Aspect | Implementation | Rationale |
|---|---|---|
| Default target | globalThis |
Maximizes convenience for agent scripts |
| Property descriptors | writable: true, configurable: true, enumerable: false |
Allows mutation and cleanup while hiding from enumeration |
| Readiness wrapping | Per‑helper async wrappers | Fine‑grained error propagation; sync helpers remain unwrapped for performance |
| Console interception | Buffer + flush pattern | Reliable log capture without breaking existing code |
| EGO_WRAPPED symbol | Guard property on target | Idempotent installation prevents method stacking |
Summary
installEgoSdkgenerates helpers viahelperContext()fromhelpers.tsand attaches them to a target object, defaulting toglobalThis.- Asynchronous helpers are wrapped with readiness logic through the
wrapReadyutility, ensuring the SDK only executes after initialization completes. - Properties are defined with
writable,configurable, andenumerable: falsedescriptors for a clean, non‑polluting global API. - Console output is buffered for host retrieval, and integration with the full
egoruntime adds version notices and tab‑aware re‑injection.
Frequently Asked Questions
Can I install ego‑lite helpers on an object other than globalThis?
Yes. Pass any object as the first argument to installEgoSdk. This is commonly used in tests to create isolated sandbox environments without modifying the real global scope.
Why are the injected helpers non‑enumerable?
The enumerable: false descriptor prevents helpers from appearing in for…in loops and Object.keys() results. This reduces noise when agent scripts iterate over variables or log the global object, while still allowing direct access by name.
What happens if installEgoSdk is called multiple times?
The function is idempotent. The EGO_WRAPPED symbol guards against double‑wrapping runtime methods, and Object.defineProperty silently overwrites existing properties (since configurable: true). The helpers are re‑injected fresh each time, which is useful when spawning new browser tabs.
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 →