> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Using the walkerOS CLI

## Overview[​](#overview "Direct link to Overview")

The walkerOS CLI (`walkeros`) bundles, tests, and runs event collection flows.

**Core workflow:**

1. **Configure** - Write Flow\.Json JSON config
2. **Bundle** - Generate optimized JS bundle
3. **Test** - Simulate events (mocked) or push (real)
4. **Deploy** - Run locally or deploy to production

## Quick Start[​](#quick-start "Direct link to Quick Start")

```
# Install

npm install -g @walkeros/cli



# Bundle a flow

walkeros bundle flow.json



# Test with simulated event

walkeros push flow.json -e '{"entity":"page","action":"view"}' --simulate destination.demo



# Push real event

walkeros push flow.json -e '{"entity":"page","action":"view"}'
```

## Commands Overview[​](#commands-overview "Direct link to Commands Overview")

| Command    | Purpose                                                     | Safe? |
| ---------- | ----------------------------------------------------------- | ----- |
| `bundle`   | Generate JS bundle from config                              | ✅    |
| `push`     | Execute with real API calls (or `--simulate` for mocked)    | ⚠️    |
| `run`      | Local HTTP event collection                                 | ✅    |
| `setup`    | Run a component's `setup()` to provision external resources | ⚠️    |
| `deploy`   | Deploy flows to cloud                                       | ⚠️    |
| `previews` | Manage preview bundles for testing on live sites            | ⚠️    |
| `observe`  | Start a live observation session for a flow (app login)     | ⚠️    |
| `validate` | Validate configs/events                                     | ✅    |
| `cache`    | Manage caching                                              | ✅    |

For detailed command reference, see [commands-reference.md](https://github.com/elbwalker/walkerOS/blob/main/skills/walkeros-using-cli/commands-reference.md).

***

## Common Workflows[​](#common-workflows "Direct link to Common Workflows")

### Development Workflow[​](#development-workflow "Direct link to Development Workflow")

```
1. Write flow.json config

2. Bundle: walkeros bundle flow.json

3. Simulate: walkeros push flow.json -e event.json --simulate destination.demo

4. Fix issues, repeat 2-3

5. Push test: walkeros push flow.json -e event.json

6. Deploy: walkeros deploy start <flowId>
```

### Multi-Flow Development[​](#multi-flow-development "Direct link to Multi-Flow Development")

```
# Bundle specific flow

walkeros bundle flow.json --flow myFlow



# Bundle all flows

walkeros bundle flow.json --all



# Test specific flow

walkeros push flow.json --flow myFlow -e event.json --simulate destination.demo
```

### Local Development Server[​](#local-development-server "Direct link to Local Development Server")

```
# HTTP event collection server

walkeros run flow.json --port 3000
```

**Server port note:** The `--port` flag (or `PORT` env var) is forwarded at runtime to all source configs that have a `port` setting. You don't need to hardcode ports in the flow config — set `port: 8080` as a default and let the runtime override it.

***

## Flow\.Json Configuration[​](#flowjson-configuration "Direct link to Flow.Json Configuration")

### Minimal Config[​](#minimal-config "Direct link to Minimal Config")

```
{

  "version": 4,

  "flows": {

    "default": {

      "config": {

        "platform": "web",

        "bundle": {

          "packages": {

            "@walkeros/web-destination-gtag": {}

          }

        }

      },

      "destinations": {

        "gtag": {

          "package": "@walkeros/web-destination-gtag",

          "config": { "measurementId": "G-XXXXXX" }

        }

      }

    }

  }

}
```

### Config Structure[​](#config-structure "Direct link to Config Structure")

```
{

  "version": 4,

  "flows": {

    "<flowName>": {

      "config": {

        "platform": "web" | "server",  // Platform (required)

        "settings": {},                 // Platform-specific settings (optional)

        "bundle": {

          "packages": {},               // NPM packages pacote will install

          "overrides": {},              // Transitive dep version pins (npm-style)

          "traceInclude": []            // Optional: nft escape hatch (paths/globs)

        },

        "observe": {}                   // Optional: public observe connect pair (url + binding)

      },

      "sources": {},                    // Event sources

      "destinations": {},               // Event destinations

      "transformers": {},               // Transformer chain (optional)

      "mappings": {},                   // Event transformation rules

      "collector": {}                   // Collector configuration

    }

  }

}
```

**You do NOT need `npm install` for step packages.** flow\.json's `config.bundle.packages` is the single source of truth. Pacote installs them transparently when you run `walkeros bundle`. Only `@walkeros/cli` belongs in your project's `package.json` (as a devDependency).

**`config.bundle.overrides`** pins transitive dependency versions. Use it when a vendor SDK's declared peer/dep range conflicts with another required version in the same tree. Example: `{"@amplitude/analytics-types": "2.11.1"}` forces that exact version everywhere in the install graph. Direct `packages` specs always win over overrides; overrides only substitute transitive resolution.

**Schema version stays at 4.** Build-time fields live under `flow.<name>.config.bundle.{packages, overrides, traceInclude}`. The `flow.<name>.config.bundle.external` sub-field is no longer supported in @walkeros/cli\@4.x.

**`config.observe` bakes only public values.** For web flows, `walkeros bundle` emits the `url` + `binding` connect pair statically into the bundle; the per-session credential arrives out-of-band at runtime (`?elbObserve=` URL param), so no secret ever enters the artifact. A partial pair (only one of the two set) warns at build time and wires nothing.

For detailed configuration options, see [flow-configuration.md](https://github.com/elbwalker/walkerOS/blob/main/skills/walkeros-using-cli/flow-configuration.md).

***

## Testing with Step Examples[​](#testing-with-step-examples "Direct link to Testing with Step Examples")

### Simulate with `--step`[​](#simulate-with---step "Direct link to simulate-with---step")

Target a specific step and provide input as `SourceInput` (`{ content, trigger? }`):

```
# Simulate a source step with trigger metadata

walkeros push flow.json --simulate source.browser --event '{"content":"<html>...","trigger":{"type":"click"}}'



# Simulate a destination step with an event

walkeros push flow.json --simulate destination.gtag -e '{"entity":"order","action":"complete","data":{"total":149.97}}'
```

Example output:

```
Step: destinations.gtag

  in:  { name: "order complete", data: { id: "ORD-123", total: 149.97 } }

  out: ["event", "purchase", { transaction_id: "ORD-123", value: 149.97 }]

  Status: PASS
```

### Same flow via MCP (`flow_simulate`)[​](#same-flow-via-mcp-flow_simulate "Direct link to same-flow-via-mcp-flow_simulate")

From an AI assistant the equivalent tool is `flow_simulate`. A few specifics:

* **`step` is required.** Pass the target as `"type.name"`, e.g. `"source.browser"`, `"collector.default"`, `"destination.gtag"`, or `"transformer.router"`. There is no all-steps mode.
* **Four step types: `source`, `collector`, `transformer`, `destination`.** Source-step `event` shape is `{ content, trigger? }`, where `content` is the walkerOS event `{ name, data }` and `trigger` is optional `{ type?, options? }`. There is no `env` field. Destination and transformer steps take a plain walkerOS event `{ name, data, consent? }`.
* **`collector` is the enrichment step.** It takes a post-`next` partial event plus an optional state snapshot `{ consent?, user?, globals?, timing? }`, applies the collector's `createEvent`, and returns the fully enriched event.
* **`transformer` steps accept an optional `ingest`** (a raw ingest without `_meta`). Supply it to test a request decoder standalone, for example a GA4 decoder reading `ctx.ingest.url`: pass `ingest: { url: "..." }` with the event.
* **Sources are simulatable as a step**, including the `@walkeros/source-demo` demo source.
* **`configPath` accepts a cloud flow id** (`flow_...` / `cfg_...`), resolved the same way `flow_load` does, so you can simulate a saved flow without a file round-trip. Repeated simulations of the same configuration reuse a prebuilt bundle for faster runs; local file paths always rebuild. `flow_bundle` accepts a cloud flow id the same way.

`flow_load` loads a flow from a local path, URL, inline JSON, or a cloud flow/config ID (`flow_...` / `cfg_...`). Configs returned by `flow_load` and `flow_manage` are round-trip safe: structural values (package names, platform, IDs) come back literally, so a returned config can be edited and sent back to `flow_manage({ action: "update" })` unchanged.

When an MCP request fails, the `diagnostics` tool (read-only, no parameters, works logged out) reports the MCP and CLI versions, the resolved app URL, app `/api/health` reachability, the bundled OpenAPI contract version, and which source served the last package lookup.

### Validate flow config[​](#validate-flow-config "Direct link to Validate flow config")

Validate schema, references, and cross-step example compatibility:

```
walkeros validate flow.json
```

All checks run automatically — schema validation, reference checking, and cross-step example compatibility. No flags needed for full validation.

For full details on writing and testing with step examples, see [using-step-examples](https://www.walkeros.io/skills/walkeros-using-step-examples.md).

***

## $code: Prefix (Inline JavaScript)[​](#code-prefix-inline-javascript "Direct link to $code: Prefix (Inline JavaScript)")

Embed JavaScript functions in JSON configs:

```
{

  "fn": "$code:(event) => event.data.price * 100",

  "condition": "$code:(event) => event.data?.value > 100"

}
```

**Important:** The CLI bundler converts `$code:` strings to actual JavaScript functions during build. This is essential for mappings in JSON configs.

For mapping patterns, see [understanding-mapping](https://www.walkeros.io/skills/walkeros-understanding-mapping.md).

***

## Quick Reference[​](#quick-reference "Direct link to Quick Reference")

### Bundle Command[​](#bundle-command "Direct link to Bundle Command")

```
walkeros bundle <config> [options]



Options:

  --flow <name>     Bundle specific flow (default: "default")

  --all             Bundle all flows

  --stats           Show bundle statistics

  --json            JSON output

  --no-cache        Skip build cache

  -v, --verbose     Verbose output

  -s, --silent      Silent mode
```

Output:

* Web: `dist/walker.js` (single self-contained IIFE)
* Server: `dist/{flow.mjs, package.json, node_modules/}` (always a directory; nft-traced)
* Server archive: `flow.tar.gz` / `flow.tgz` (the server bundle directory packed into a single gzip file)

Use `-o ./dist/walker.js` for web, `-o ./dist/` for a server directory, or `-o ./flow.tar.gz` for a server archive. Web single-file bundles do not support archive output.

### Push Command[​](#push-command "Direct link to Push Command")

```
walkeros push <config|bundle> [options]



Options:

  -e, --event <json|file|url>   Event to process (required)

  --flow <name>                  Flow to use

  -p, --platform <web|server>   Platform override

  --simulate <step>              Simulate a step (repeatable for destination.*). Format: source.NAME | destination.NAME | transformer.NAME

  --mock <step=value>            Mock a step with a specific return value (repeatable)

  --snapshot <source>            JS file to eval before execution (sets global state)
```

### Validate Command[​](#validate-command "Direct link to Validate Command")

```
walkeros validate <input> [options]



Options:

  --type <type>     Validation type (default: flow). Also: event, mapping, contract

  --path <path>     Validate entry against package schema (e.g. destinations.snowplow)

  --flow <name>     Flow name for multi-flow configs

  --strict          Treat warnings as errors

  --json            JSON output



Exit codes:

  0 = Valid

  1 = Errors found

  2 = Warnings (with --strict)

  3 = Input error
```

### Run Command[​](#run-command "Direct link to Run Command")

```
# HTTP event collection server

walkeros run <config|bundle|archive> [options]



Options:

  -p, --port <number>   Port (default: 8080)

  -h, --host <string>   Host (default: 0.0.0.0)
```

`run` accepts a flow config, a pre-built bundle, or a `.tar.gz`/`.tgz` flow archive (URL or local file). For an archive, the CLI fetches or reads the gzip, extracts the bundle and its sibling `node_modules/`, and runs the entry. This lets server flows whose step packages are external resolve those packages at runtime from the extracted `node_modules/`.

```
# Run a packed server bundle from a local archive

walkeros run flow.tar.gz --port 8080



# Run a packed server bundle from a URL

walkeros run https://example.com/flow.tar.gz
```

### Setup Command[​](#setup-command "Direct link to Setup Command")

```
# Provision external resources for one component (explicit only, never

# triggered by push, simulate, deploy, or the runtime)

walkeros setup <kind>.<name> [-c ./flow.json] [-f <flow>] [--json] [--verbose] [--silent]
```

The target uses the same `<kind>.<name>` syntax as `walkeros push --simulate` (`source`, `destination`, or `store`). Package resolution follows the flow's pins exactly like `bundle`: the version from `config.bundle.packages` (or inline `@scope/pkg@x.y.z`, bundle pin wins) is downloaded through the shared pacote pipeline and cache, then imported from the extracted tree. Setup works via npx without a local install; `path:` entries in `config.bundle.packages` support local development with a built package directory.

***

## Bundler Gotchas[​](#bundler-gotchas "Direct link to Bundler Gotchas")

* **Circular copies:** Never include the output directory itself (e.g., `include: ["./dist"]` when output is `dist/`). The CLI detects this and errors.
* **Runtime paths:** The runner sets CWD to the bundle directory. File paths in `settings` resolve relative to the bundle, not the project root.
* **Component names:** Source, transformer, destination, and store names must be valid JavaScript identifiers (camelCase). Hyphens like `gtag-wrapper` cause syntax errors — use `gtagWrapper` instead.
* **Range conflicts:** When two transitive consumers declare incompatible ranges for the same dep (e.g., `arrify@^3.0.0` vs `arrify@^2.0.0`), the bundler resolves the chosen range to a concrete version and nests non-satisfying specs under their consumer. If a post-install warning surfaces declared-vs-installed mismatches, pin the dep in `config.bundle.overrides`. Set `BUNDLER_STRICT_RANGES=0` to bypass strict range validation when the npm registry is unreachable.

***

## Server bundles use nft tracing[​](#server-bundles-use-nft-tracing "Direct link to Server bundles use nft tracing")

Server flows are bundled with [`@vercel/nft`](https://github.com/vercel/nft). The CLI:

1. Pacote installs every package declared in `flow.<name>.config.bundle.packages` into a per-build install root. Users do **not** run `npm install` for step packages; only `@walkeros/cli` lives in their `package.json`.
2. esbuild stage 1 externalizes all step packages.
3. esbuild stage 2 emits a small ESM `flow.mjs` that imports from those externalized packages.
4. nft traces `flow.mjs`, finds every file actually reachable at runtime (including `__dirname`-loaded `.proto` files and other assets), and copies only those files into `dist/node_modules/`.

There is no `walkerOS.bundle.external` annotation. nft figures it out.

**Bundle directory (the server flow's unpacked artifact):**

```
dist/

├── flow.mjs        # ESM entry, expects to be at /app/flow/flow.mjs in prod

├── package.json     # informational sidecar (not used by the runner)

└── node_modules/    # only the files nft traced
```

The same directory can be packed into a `.tar.gz`/`.tgz` archive (see the Bundle Command section), and `walkeros run` accepts either form. Web flows are unchanged: a single `dist/walker.js`.

### Canonical Dockerfile[​](#canonical-dockerfile "Direct link to Canonical Dockerfile")

```
FROM node:22.23.0-alpine AS builder

WORKDIR /build

RUN npm init -y && npm install --save-dev @walkeros/cli

COPY flow.json ./

RUN npx walkeros bundle flow.json -o dist/



FROM walkeros/flow:4

WORKDIR /app/flow

COPY --from=builder /build/dist/ ./

ENV PORT=8080

EXPOSE 8080
```

Notes:

* The build stage only needs `@walkeros/cli`. flow\.json drives every step package install; pacote handles it.
* `COPY --from=builder /build/dist/ ./` copies the whole directory (flow\.mjs + package.json + node\_modules/) into `/app/flow/`.
* The runner image's defaults match `/app/flow/flow.mjs`. No `BUNDLE` env var needed.

### Escape hatch: `traceInclude`[​](#escape-hatch-traceinclude "Direct link to escape-hatch-traceinclude")

If nft cannot statically reach a runtime asset (rare: `require()` of a path constructed from a runtime variable), declare it explicitly under `flow.<name>.config.bundle.traceInclude`. Paths and globs both work; both resolve against the install root, not the project directory:

```
"flows": {

  "default": {

    "config": {

      "platform": "server",

      "bundle": {

        "packages": { "@walkeros/server-destination-gcp": {} },

        "traceInclude": [

          "node_modules/some-pkg/data/*.json",

          "node_modules/another-pkg/lib/runtime-loaded.js"

        ]

      }

    }

  }

}
```

### Cache (CI)[​](#cache-ci "Direct link to Cache (CI)")

The bundler caches pacote downloads under `process.env.NPM_CACHE_DIR` (default `<tmpDir>/cache/npm`). On CI, persist that path with `actions/cache`:

```
- uses: actions/cache@v4

  with:

    path: .walkeros-cache/npm

    key: walkeros-${{ hashFiles('**/flow.json') }}

- run: WALKEROS_TMP_DIR=.walkeros-cache npx walkeros bundle flow.json -o dist/
```

**CI smoke check:** `cd dist && node -e "import('./flow.mjs').then(()=>console.log('ok'))"` plus `du -sh node_modules` (typical: 30-50MB for GCP destination, 10k+ files; use `.dockerignore`).

***

## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting")

### Bundle Fails[​](#bundle-fails "Direct link to Bundle Fails")

1. **Check JSON syntax**: `walkeros validate flow.json --flow`
2. **Check package names**: Ensure packages exist on npm
3. **Clear cache**: `walkeros cache clear`

### Events Not Processing[​](#events-not-processing "Direct link to Events Not Processing")

1. **Validate event**: `walkeros validate event.json`
2. **Check mapping**: Event must match entity/action in mapping
3. **Use simulate first**: `walkeros push flow.json -e event.json --simulate destination.demo -v`

### Destination Not Found in Simulation[​](#destination-not-found-in-simulation "Direct link to Destination Not Found in Simulation")

If `--simulate destination.NAME` errors with "not found in collector", the destination likely has `require: ["consent"]` in its config. This delays initialization until a `walker consent` event fires — which doesn't happen during simulation.

**Fix:** Remove or comment out the `require` field for simulation testing:

```
{

  "destinations": {

    "gtag": {

      "package": "@walkeros/web-destination-gtag",

      "config": {

        "settings": { "measurementId": "G-XXXXXX" }

      }

    }

  }

}
```

### Destination Silent (0 Events Received)[​](#destination-silent-0-events-received "Direct link to Destination Silent (0 Events Received)")

If the destination is found but receives 0 events:

1. **Check consent**: If destination has `consent: { marketing: true }`, the event must include matching consent. Add to event JSON: `{ "name": "page view", "data": {...}, "consent": { "marketing": true } }`
2. **Check mapping**: The event name must match a mapping rule (entity/action keys). Unmapped events pass through unmodified.
3. **Check policy**: Policy runs BEFORE mapping — verify policy isn't redacting fields needed by mapping rules.

### Web Simulation Transport[​](#web-simulation-transport "Direct link to Web Simulation Transport")

Web simulations run in JSDOM. `fetch` and `navigator.sendBeacon` are polyfilled as tracked no-ops -- no real HTTP requests are made. Captured network calls are included in `PushResult.networkCalls` when present.

### Local Packages Not Found[​](#local-packages-not-found "Direct link to Local Packages Not Found")

Use absolute or relative paths in `flow.<name>.config.bundle.packages`:

```
{

  "config": {

    "platform": "web",

    "bundle": {

      "packages": {

        "my-destination": {

          "path": "./local/my-destination"

        }

      }

    }

  }

}
```

### CLI Prints "Upgrade Required" and Exits With Code 2[​](#cli-prints-upgrade-required-and-exits-with-code-2 "Direct link to CLI Prints \"Upgrade Required\" and Exits With Code 2")

The walkerOS app requires a newer `@walkeros/cli` version for the endpoint you just called. Upgrade and retry:

```
npm install -g @walkeros/cli@latest
```

See [Upgrading](https://walkeros.io/docs/upgrading) for the full version-negotiation rules.

***

## Where CLI Lives[​](#where-cli-lives "Direct link to Where CLI Lives")

| Location                     | Purpose                 |
| ---------------------------- | ----------------------- |
| `packages/cli/`              | CLI source code         |
| `packages/cli/src/commands/` | Command implementations |
| `packages/cli/examples/`     | Example flow configs    |
| `packages/cli/README.md`     | Full CLI documentation  |

***

## Networking[​](#networking "Direct link to Networking")

Outbound requests to a configured `WALKEROS_APP_URL` carry an `X-Walkeros-Client: walkeros-cli/{version}` header so the host can attribute usage. No PII; the header is the only client identifier.

## Telemetry[​](#telemetry "Direct link to Telemetry")

The CLI supports anonymous usage telemetry (installation id, command name, outcome, duration, environment) to help improve the tool. Telemetry is **off by default** and requires explicit opt-in. No persistent identifier is written before consent.

* **Opt in:** `walkeros telemetry enable`.
* **Opt out:** `walkeros telemetry disable`.
* **Current state:** `walkeros telemetry status`.
* **Forced off (override):** set `DO_NOT_TRACK=1` or `WALKEROS_TELEMETRY_DISABLED=1`.
* **Debug:** set `WALKEROS_TELEMETRY_DEBUG=1` to print the payload to stderr (requires opt-in; no network traffic).
* **Docs:** see [apps/telemetry](https://www.walkeros.io/docs/apps/telemetry.md) for the full event list, privacy details, and legal basis.
* **Contract (source of truth):** [`packages/cli/src/telemetry/flow.json`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/src/telemetry/flow.json).

***

## Related Skills[​](#related-skills "Direct link to Related Skills")

* [understanding-mapping](https://www.walkeros.io/skills/walkeros-understanding-mapping.md) - Mapping configuration
* [understanding-flow](https://www.walkeros.io/skills/walkeros-understanding-flow.md) - Data flow architecture
* [create-destination](https://www.walkeros.io/skills/walkeros-create-destination.md) - Creating destinations
* [create-source](https://www.walkeros.io/skills/walkeros-create-source.md) - Creating sources
* [debugging](https://www.walkeros.io/skills/walkeros-debugging.md) - Troubleshooting event flow

**Detailed References:**

* [commands-reference.md](https://github.com/elbwalker/walkerOS/blob/main/skills/walkeros-using-cli/commands-reference.md) - All commands with full options
* [flow-configuration.md](https://github.com/elbwalker/walkerOS/blob/main/skills/walkeros-using-cli/flow-configuration.md) - Complete Flow\.Json reference
* [server-deployment.md](https://github.com/elbwalker/walkerOS/blob/main/skills/walkeros-using-cli/server-deployment.md) - Server flow deployment guide
