Environment Variables
Purpose
Define every environment variable the Node CLI reads, its exact default, where it takes effect, and how to verify it is working. Use environment variables for workspace-wide defaults in headless, CI, or agent environments where repeating CLI flags on every command is impractical.
Sources
- Global flag parsing (reads
--log-level,--operator-package,--device,--timeout):apps/node/src/cli/index.ts - Logger construction (reads
CLAWPERATOR_LOG_DIR,CLAWPERATOR_LOG_LEVEL):apps/node/src/adapters/logger.ts - Android SDK tool resolution (reads
ANDROID_HOME,ANDROID_SDK_ROOT):apps/node/src/adapters/android-bridge/runtimeConfig.ts - Skill binary and package resolution (reads
CLAWPERATOR_BIN,CLAWPERATOR_OPERATOR_PACKAGE):apps/node/src/domain/skills/skillsConfig.ts - Skills registry path (reads
CLAWPERATOR_SKILLS_REGISTRY):apps/node/src/adapters/skills-repo/localSkillsRegistry.ts - Per-command operator package and adb path:
apps/node/src/cli/registry.tsplus device-targeting handlers inapps/node/src/cli/commands/ - Execution runtime (reads
CLAWPERATOR_OPERATOR_PACKAGE,ADB_PATH):apps/node/src/domain/executions/runExecution.ts
Precedence Rule
When both a CLI flag and an environment variable control the same setting, the CLI flag wins. The CLI parses global flags before dispatching to any command, so flags like --operator-package and --log-level can appear anywhere on the command line.
Resolution order for a typical setting:
- Explicit CLI flag (highest priority)
- Environment variable
- Hardcoded default (lowest priority)
There is no configuration file layer between the environment variable and the default.
Complete Reference
| Variable | Default if unset | CLI flag override |
|---|---|---|
CLAWPERATOR_OPERATOR_PACKAGE |
com.clawperator.operator |
--operator-package |
CLAWPERATOR_LOG_DIR |
~/.clawperator/logs |
none |
CLAWPERATOR_LOG_LEVEL |
info |
--log-level |
CLAWPERATOR_SKILLS_REGISTRY |
<cwd>/skills/skills-registry.json |
none |
CLAWPERATOR_BIN |
local sibling build if present, otherwise clawperator |
none |
ADB_PATH |
adb (from PATH) |
none |
ANDROID_HOME |
unset | none |
ANDROID_SDK_ROOT |
unset (fallback for ANDROID_HOME) |
none |
CLAWPERATOR_OPERATOR_PACKAGE
Controls which Operator APK package name the runtime targets when --operator-package is not passed.
Read by every device-targeting CLI command (snapshot, click, read, wait, scroll, doctor, record start, record stop, record pull, operator setup, grant-device-permissions, version --check-compat, exec, serve), plus runExecution() and resolveOperatorPackage() in the skills runtime.
Default: com.clawperator.operator
Common values:
| Value | When to use |
|---|---|
com.clawperator.operator |
Release APK. Default. |
com.clawperator.operator.dev |
Local debug APK built from source. |
Example:
export CLAWPERATOR_OPERATOR_PACKAGE=com.clawperator.operator.dev
clawperator snapshot --json --device emulator-5554
That snapshot runs against the debug Operator package. No --operator-package flag needed.
Verification - confirm the env var took effect:
clawperator doctor --json --device emulator-5554
Check report.operatorPackage in the JSON output:
{
"ok": true,
"criticalOk": true,
"deviceId": "emulator-5554",
"operatorPackage": "com.clawperator.operator.dev",
"checks": []
}
If operatorPackage shows the value you set, the env var is active.
Error case:
- if the env var names a package that is not installed, device-targeting commands fail with
OPERATOR_NOT_INSTALLED - if only the alternate known Operator variant is installed,
clawperator doctor --jsonreportsOPERATOR_VARIANT_MISMATCHas a readiness warning
Note: the CLI also accepts --receiver-package as a legacy alias for --operator-package. Both override the same env var. Do not use --receiver-package in new code.
CLAWPERATOR_LOG_DIR
Controls where the CLI writes structured log files.
Default: ~/.clawperator/logs
Log files are named clawperator-YYYY-MM-DD.log and contain NDJSON LogEvent objects.
Guaranteed fields:
tsleveleventmessage
Optional fields, present only when the emitter supplied them:
commandIdtaskIddeviceId
There is no CLI flag to override this. To change the log directory, set the env var.
Example:
export CLAWPERATOR_LOG_DIR=/tmp/clawperator-logs
clawperator snapshot --json
# logs written to /tmp/clawperator-logs/clawperator-2026-03-25.log
This variable affects logging only. It does not change device behavior, result envelopes, or execution outcomes.
CLAWPERATOR_LOG_LEVEL
Controls the logger threshold when --log-level is not passed.
Default: info
Valid values: debug, info, warn, error
If set to an unrecognized value (e.g., trace, verbose, or an empty string), the logger silently falls back to info. This is not an error - it is how normalizeLogLevel() in logger.ts works.
--log-level is a global flag. It can appear anywhere on the command line and takes priority over this env var:
# env var sets warn, but flag overrides to debug
export CLAWPERATOR_LOG_LEVEL=warn
clawperator snapshot --json --log-level debug
# logger threshold is debug for this command
CLAWPERATOR_SKILLS_REGISTRY
Defines the path to the active skills-registry.json used by all skill commands when they do not pass an explicit registry-path argument into loadRegistry().
Read by: skills list, skills get, skills search, skills run, skills validate, skills compile-artifact, skills new, and the /skills serve endpoints.
Default when unset: <cwd>/skills/skills-registry.json where <cwd> is process.cwd().
Current loadRegistry() precedence is:
- explicit
registryPathargument, when a caller supplied one CLAWPERATOR_SKILLS_REGISTRY, when it is set and non-blank- default path
<cwd>/skills/skills-registry.json
Fallback behavior after that initial choice:
- If an explicit
registryPathargument was passed and that read fails,loadRegistry()throws immediately - If
CLAWPERATOR_SKILLS_REGISTRYis set but blank,loadRegistry()throws immediately - If
CLAWPERATOR_SKILLS_REGISTRYis set to a non-blank path and that read fails,loadRegistry()throws a configured-path error immediately - If the default-path read fails with no env var and no explicit
registryPath,loadRegistry()next tries: <cwd>/../../skills/skills-registry.jsonwhen the current working directory isapps/node~/.clawperator/skills/skills/skills-registry.json
After clawperator skills install or clawperator skills sync, the registry lives at ~/.clawperator/skills/skills/skills-registry.json. That path is automatically discovered as a fallback when CLAWPERATOR_SKILLS_REGISTRY is not set, so no env var change is needed after a normal install:
clawperator skills list --json
Set CLAWPERATOR_SKILLS_REGISTRY only when pointing at a non-standard registry path, such as a development checkout outside the installed home. For the normal post-install flow, start with Host Agent Orientation.
Error case: if the path does not exist, skill commands fail with REGISTRY_READ_FAILED. The error message includes the path that was tried.
Current recovery rules:
- missing default path with no env var: run
clawperator skills installto restore the registry at~/.clawperator/skills/skills/skills-registry.json - blank
CLAWPERATOR_SKILLS_REGISTRY: unset it or point it at a valid registry path - wrong
CLAWPERATOR_SKILLS_REGISTRYpath: fix the env var, or unset it to fall back to the installed home path - explicit caller-supplied registry path: fix the explicit path because
loadRegistry()does not fall back from it
CLAWPERATOR_BIN
Controls which Clawperator CLI binary is used when skill scripts call back into Clawperator. This is for skill subprocesses, not for the main CLI bootstrap.
When clawperator skills run executes a skill script, it resolves a command string and injects it into the child process environment as CLAWPERATOR_BIN.
Resolution order in resolveSkillBin():
- Explicit
CLAWPERATOR_BINenv var (if non-empty) - Local sibling build at
apps/node/dist/cli/index.jsrelative to the running module (if the file exists) - Global
clawperatorcommand
Default: the sibling build when running from a local checkout, otherwise clawperator.
The sibling build is preferred over the global binary because it is always in sync with the local Android Operator APK. The global binary may lag behind due to npm publish delays.
Example:
export CLAWPERATOR_BIN=/usr/local/bin/clawperator-nightly
clawperator skills run com.test.echo --device emulator-5554
# the skill script receives CLAWPERATOR_BIN=/usr/local/bin/clawperator-nightly
Orchestrated Skill Runtime Env Vars
runSkill() injects these env vars when a skill declares skill.json.agent
and runs through scripts/run.js. The harness reads them directly. This is
the runtime contract for agent-driven orchestrated skills.
| Variable | Default if unset | Where it takes effect |
|---|---|---|
CLAWPERATOR_SKILL_AGENT_CLI |
skill.json.agent.cli |
injected into the orchestrated harness so it knows the configured CLI name |
CLAWPERATOR_SKILL_AGENT_CLI_PATH |
none | injected into the orchestrated harness after runSkill() resolves the executable path |
CLAWPERATOR_SKILL_AGENT_TIMEOUT_MS |
caller --timeout, otherwise skill.json.agent.timeoutMs, otherwise 120000 |
injected into the orchestrated harness as the effective timeout in milliseconds |
CLAWPERATOR_SKILL_PROGRAM |
none | absolute path to the skill's SKILL.md runtime program |
CLAWPERATOR_SKILL_INPUTS |
JSON serialization of the forwarded skill args array | injected into the orchestrated harness for agent input parity checks |
CLAWPERATOR_SKILL_ID |
none | injected into the orchestrated harness as the invoked skill id |
CLAWPERATOR_DEVICE_ID |
none | selected device serial that the wrapper or CLI passed into the skill run |
Behavior details:
CLAWPERATOR_SKILL_AGENT_CLIcomes fromskill.json.agent.cliunless the caller overrides it with the same env var beforerunSkill()resolves the effective agent configCLAWPERATOR_SKILL_AGENT_CLI_PATHis present only afterresolveAgentCliExecutable()succeedsCLAWPERATOR_SKILL_AGENT_TIMEOUT_MSalways reflects the effective timeout thatrunSkill()will enforce around the harnessCLAWPERATOR_SKILL_PROGRAM,CLAWPERATOR_SKILL_INPUTS, andCLAWPERATOR_SKILL_IDare only present for agent-driven orchestrated runsCLAWPERATOR_DEVICE_IDis skill-scoped consumption: the CLI wrapper sets it whenskills run --device <serial>is used, andrunSkill()forwards it to the child process environment
Verification:
CLAWPERATOR_SKILLS_REGISTRY=/abs/path/to/skills/skills-registry.json \
node apps/node/dist/cli/index.js skills run com.test.agent-skill-result \
--device emulator-5554 \
--json -- valid
Check the parsed skillResult.source and the successful JSON envelope. The
fixture harness under apps/node/src/test/fixtures/skills/com.test.agent-skill-result/
only succeeds when CLAWPERATOR_SKILL_AGENT_CLI_PATH,
CLAWPERATOR_SKILL_AGENT_TIMEOUT_MS, CLAWPERATOR_SKILL_INPUTS,
CLAWPERATOR_SKILL_ID, and CLAWPERATOR_DEVICE_ID are all populated as the
runtime expects.
Failure cases:
- if
CLAWPERATOR_SKILL_AGENT_CLIresolves to an unavailable executable,skills runfails withSKILL_AGENT_CLI_UNAVAILABLE - if
CLAWPERATOR_SKILL_AGENT_CLI_PATHorCLAWPERATOR_SKILL_PROGRAMare missing inside the harness, the harness exits non-zero andrunSkill()returnsSKILL_EXECUTION_FAILED - if
CLAWPERATOR_SKILL_INPUTSis malformed JSON, behavior depends on the harness that parses it; this runtime only defines the environment variable contract, so malformed input should be treated as a harness-specific failure rather than a guaranteed fallback to empty args
ADB_PATH
Overrides the adb binary used by the entire runtime. Affects device listing, doctor checks, execution dispatch, recording, operator setup, permission grants, emulator management, package listing, and the serve server.
Default: adb (resolved from the process PATH).
There is no CLI flag that overrides ADB_PATH. This env var is the only way to specify a non-default adb binary.
Read at two levels:
- CLI command handlers pass
adbPath: process.env.ADB_PATHinto domain functions runExecution()falls back toprocess.env.ADB_PATHwhenoptions.adbPathis not provided
Example:
export ADB_PATH=/opt/android/platform-tools/adb
clawperator devices --json
Error case: if the path does not point to a working adb binary, clawperator doctor reports ADB_NOT_FOUND for the host.adb.presence check.
Verification:
clawperator doctor --json
If host.adb.presence passes, the configured adb binary is usable.
ANDROID_HOME and ANDROID_SDK_ROOT
These are standard Android SDK environment variables, not Clawperator-specific. The runtime reads them when resolving paths to emulator, sdkmanager, and avdmanager.
Resolution in runtimeConfig.ts:
- Check
ANDROID_HOME - If unset, check
ANDROID_SDK_ROOT - If neither is set, fall back to bare command names (
emulator,sdkmanager,avdmanager) resolved fromPATH
These variables matter for emulator provisioning flows (clawperator emulator create, emulator start, and the /android/emulators serve endpoints). They do not affect normal device execution against an already-connected adb target.
Tool paths resolved from the SDK root:
| Tool | Resolved path |
|---|---|
emulator |
<sdk_root>/emulator/emulator |
sdkmanager |
<sdk_root>/cmdline-tools/latest/bin/sdkmanager |
avdmanager |
<sdk_root>/cmdline-tools/latest/bin/avdmanager |
If the resolved path does not exist, the runtime falls back to the bare command name.
Agent Configuration Pattern
For deterministic agent environments, set these variables and forget about per-command flags:
export CLAWPERATOR_OPERATOR_PACKAGE=com.clawperator.operator.dev
export ADB_PATH=/usr/local/bin/adb
Then every command uses consistent defaults:
clawperator doctor --json --device emulator-5554
clawperator snapshot --json --device emulator-5554
clawperator skills run com.test.echo --device emulator-5554
The only flag you still need per-command is --device when multiple targets are connected. There is no environment variable equivalent for --device - device selection must always be explicit.
After clawperator skills install, the registry at ~/.clawperator/skills/skills/skills-registry.json is discovered automatically. Setting CLAWPERATOR_SKILLS_REGISTRY is only needed when overriding to a non-standard path. For the post-install orientation flow, read Host Agent Orientation.