# ego‑browser CLI Startup Path: How Entry Points Work in the ego‑lite Project

> Discover the ego-browser CLI startup path. Learn how runMain and isDirectCli initiate the ego-lite project's entry points for seamless command-line execution.

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

---

**When executed as a CLI, ego‑browser starts via `runMain()` in [`package/ego-browser/src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/index.ts) after `isDirectCli()` detects direct execution.**

The **ego‑browser** package from the citrolabs/ego‑lite repository provides a programmable browser automation tool that can run either as a standalone command‑line utility or as an imported module. Understanding the startup path for CLI execution reveals how the package conditionally branches between these two modes.

## CLI Detection and Conditional Entry Point

The entry point [`package/ego-browser/src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/index.ts) implements a runtime check to determine execution context. The `isDirectCli()` function compares the current script path against the module's own URL:

```typescript
function isDirectCli() {
  return (
    process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url
  );
}

```

This comparison ensures the code detects when ego‑browser is invoked directly from a built CLI binary rather than being `require()`'d or `import`ed by another script.

## The Startup Path for CLI Execution

When `isDirectCli()` returns **true**, the CLI startup path executes:

```typescript
if (isDirectCli()) {
  process.exitCode = await runMain();   // ← CLI startup path
} else {
  installEgoSdk();                     // ← Module‑import path
}

```

The **`runMain()`** function—imported from [`package/ego-browser/src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/run.ts)—handles the complete CLI workflow:

- Reads JavaScript source from **stdin**
- Injects the helper context from [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts)
- Executes the user script
- Returns an exit code captured in `process.exitCode`

This path is mutually exclusive with the module‑import path, which only calls **`installEgoSdk()`** to register the SDK in the host environment.

## Practical CLI Usage Example

The ego‑browser CLI accepts JavaScript via stdin and executes it in a browser context:

```bash

# Run ego‑browser as a CLI tool

echo "await page.goto('https://example.com');" | npx ego-browser

```

When this command runs, the startup sequence is:
1. Node.js loads [`package/ego-browser/src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/index.ts)
2. `isDirectCli()` confirms direct execution via `npx`
3. `runMain()` is awaited with exit code propagation
4. The script reads from stdin, navigates to example.com, and terminates

## Module Import vs. CLI Execution

| Execution Mode | Trigger Condition | Primary Function | Use Case |
|---|---|---|---|
| **CLI** | `isDirectCli()` returns `true` | `runMain()` | Standalone scripts, shell pipelines, automation tasks |
| **Module** | `isDirectCli()` returns `false` | `installEgoSdk()` | Programmatic integration in larger Node.js applications |

The dual‑mode design allows ego‑browser to serve both as a drop‑in CLI tool and as a library dependency without separate package distributions.

## Key Source Files

Understanding the ego‑browser startup path requires familiarity with three core files:

- **[`package/ego-browser/src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/index.ts)** — Entry point with `isDirectCli()` check and branch logic
- **[`package/ego-browser/src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/run.ts)** — Implements `runMain()` for stdin handling and script execution
- **[`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts)** — Provides the execution context injected by `runMain()`

These files collectively implement the CLI startup architecture in the citrolabs/ego‑lite repository.

## Summary

- **The startup path for ego‑browser CLI execution is `runMain()`**, triggered when `isDirectCli()` validates direct invocation.
- `isDirectCli()` uses `process.argv[1]` and `import.meta.url` comparison to detect CLI context.
- The CLI path (`runMain()`) and module path (`installEgoSdk()`) are mutually exclusive branches in [`package/ego-browser/src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/index.ts).
- `runMain()` sources JavaScript from stdin, injects helpers, and propagates exit codes via `process.exitCode`.

## Frequently Asked Questions

### How does ego‑browser know it's running as a CLI versus being imported?

ego‑browser uses the `isDirectCli()` helper to compare `process.argv[1]` (the executed script path) against `import.meta.url` (the module's own location). When these match, the package assumes direct CLI execution and routes to `runMain()`. Otherwise, it treats the call as a module import and runs `installEgoSdk()`.

### What happens if I run ego‑browser without piping input to stdin?

The `runMain()` function expects JavaScript input from stdin. If no input is provided, the function will likely hang waiting for data or exit with an error depending on the implementation in [`package/ego-browser/src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/run.ts). Always provide script content via pipe or redirection when using the CLI.

### Can I use ego‑browser programmatically without triggering the CLI path?

Yes. When you `import` or `require` ego‑browser from another Node.js file, `isDirectCli()` returns `false` because `process.argv[1]` points to your calling script rather than ego‑browser's module URL. This triggers `installEgoSdk()` instead of `runMain()`, making the SDK available for programmatic use without stdin handling.