# ego-lite CLI Mode vs SDK Mode: Key Differences Explained

> Discover the key differences between ego-lite CLI mode and SDK mode. Learn how each invocation method impacts entry-point detection and process lifecycle for your projects.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: deep-dive
- Published: 2026-08-24

---

**ego-lite operates in two distinct modes: CLI mode for stand-alone command execution via `runMain()` and SDK mode for library integration via `installEgoSdk()`, differing primarily in invocation method, entry-point detection, and process lifecycle.**

The `citrolabs/ego-lite` repository provides a flexible browser automation tool that adapts its behavior based on how it is invoked. Understanding the distinction between **ego-lite CLI mode and SDK mode** is essential for choosing the right integration approach for your automation workflows.

## How Each Mode Is Invoked

The fundamental difference begins with how you access the library's functionality.

**CLI mode** is triggered when you execute ego-lite as a stand-alone command. The binary reads JavaScript from *stdin* and immediately runs it:

```bash
ego-browser <<'JS'
await page.goto('https://example.com')
console.log(await page.title())
JS

```

**SDK mode** requires importing the package as a module and explicitly installing the helpers onto a target object, typically `globalThis`:

```javascript
import { installEgoSdk } from "ego-browser";

installEgoSdk();            // installs helpers on globalThis
await page.goto('https://example.com');
console.log(await page.title());

```

## Entry Point Detection Logic

In [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts), ego-lite determines which mode to activate by inspecting `process.argv[1]`. If the current process matches the bundled [`index.js`](https://github.com/citrolabs/ego-lite/blob/main/index.js) path, the code invokes `runMain()` to enter CLI mode【/cache/repos/github.com/citrolabs/ego-lite/main/package/ego-browser/src/index.ts#L75-L78】.

When this detection fails—meaning the package was imported as a module rather than executed directly—the fallback automatically calls `installEgoSdk()`【/cache/repos/github.com/citrolabs/ego-lite/main/package/ego-browser/src/index.ts#L64-L66】. This conditional branching ensures the correct initialization path without requiring manual configuration flags.

## Primary Purpose and Use Cases

Each mode serves distinct automation needs.

**CLI mode** provides a command-line interface designed for quick one-off scripts. It handles argument parsing for flags like `--help`, `--doctor`, and `--reload`, then reads the script body from *stdin*. The implementation lives in `runMain()` within [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts), which orchestrates argument handling, context creation, and execution flow.

**SDK mode** exposes the full browser-automation API—including `page.goto`, `waitForSelector`, and task-space APIs—as properties on the target object. This allows Node programs or libraries to embed ego-lite without spawning separate CLI processes, making it ideal for test harnesses, custom automation frameworks, or applications requiring programmatic control.

## Output Handling Differences

Both modes buffer `console.log` output, but they manage sinks differently.

In **CLI mode**, [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts) implements a per-run output sink using `resetSink()` and `flushSink()`. The runtime buffers all logs during execution and flushes them when the script finishes, ensuring clean terminal output for short-lived processes.

In **SDK mode**, the sink behavior depends on the host configuration. When `installEgoSdk()` executes, it checks whether the host supplies a custom `cliLog` function. If not, it sets `usingDefaultLog` to true and uses the default buffered sink【/cache/repos/github.com/citrolabs/ego-lite/main/package/ego-browser/src/index.ts#L75-L82】. The sink resets only when the host explicitly provides logging overrides, allowing persistent buffers across multiple SDK calls.

## Process Lifecycle

The operational lifespan differs significantly between modes.

**CLI mode** runs in a short-lived Node process. After `runMain()` completes the script execution and flushes the output sink, the process exits immediately. This matches the ephemeral nature of command-line tools.

**SDK mode** persists for the lifetime of the host process. Once `installEgoSdk()` attaches helpers to `globalThis` or another target object, the installation remains active, allowing multiple scripts or API calls to share the same runtime state and browser instance.

## Shared Infrastructure

Despite their different entry points, both modes leverage common internal modules:

- [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) provides `helperContext()`, ensuring CLI and SDK paths expose identical browser-automation surfaces
- [`src/output-sink.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/output-sink.ts) manages buffered logging via `bufferOutput`, `resetSink`, and `flushSink` for both modes
- [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts) contains runtime utilities that underpin helper functions regardless of invocation method

## Summary

- **ego-lite CLI mode** executes via `runMain()` in [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts), reads scripts from *stdin*, processes arguments like `--doctor`, and exits after flushing buffered output
- **ego-lite SDK mode** activates via `import { installEgoSdk }`, attaches helpers to `globalThis`, and persists for the host process lifetime
- Entry detection occurs in [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts) by checking `process.argv[1]` against the bundled script path
- CLI mode suits ad-hoc terminal automation; SDK mode suits embedded library usage and programmatic control
- Both modes share [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) and [`src/output-sink.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/output-sink.ts) for consistent API surfaces and logging behavior

## Frequently Asked Questions

### How does ego-lite detect whether to run in CLI or SDK mode?

The detection logic in [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts) compares `process.argv[1]` against the bundled [`index.js`](https://github.com/citrolabs/ego-lite/blob/main/index.js) path. If they match, the code calls `runMain()` to initiate CLI mode; otherwise, it automatically invokes `installEgoSdk()` to enter SDK mode【/cache/repos/github.com/citrolabs/ego-lite/main/package/ego-browser/src/index.ts#L75-L78】.

### Can I use ego-lite in SDK mode without polluting globalThis?

Yes. While `installEgoSdk()` typically installs helpers on `globalThis`, you can pass a custom target object as an argument to isolate the SDK installation to a specific namespace, preventing global scope pollution in your application.

### What happens to console output in CLI mode versus SDK mode?

CLI mode buffers output in a per-run sink implemented in [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts) and flushes it when the script terminates. SDK mode also buffers logs, but the sink management depends on whether you provide a custom `cliLog` function; without one, it uses the default buffered sink that persists across calls【/cache/repos/github.com/citrolabs/ego-lite/main/package/ego-browser/src/index.ts#L75-L82】.

### Is there a performance difference between CLI and SDK mode?

CLI mode incurs process startup overhead for each invocation because it spawns a new Node process via `runMain()`. SDK mode avoids this overhead by remaining resident in memory after `installEgoSdk()` initializes, making it more efficient for scenarios requiring multiple sequential browser operations.