Scripts and CI
Run espx unattended: wait for log lines, read --json output, act on exit codes.
espx is built to run without a person watching. Every choice is a flag, nothing prompts, and the monitor can decide on its own when a run passed.
Nothing prompts
No espx command waits for input unless you pass -i. A missing choice is an error that names the flag to pass, and espflash is always told --non-interactive so its board picker never opens. -i is the opt-in for a person at a terminal:
| Command | -i does |
|---|---|
espx new -i | opens esp-generate's terminal UI |
espx flash -i | lets espflash ask which board when several are connected |
espx uninstall -i | asks about each item |
-i without a terminal is an error (not_a_tty).
The one thing espx detects instead of being told is the serial monitor. run, monitor and flash --monitor keep espflash's key handling when stdin is a terminal, and read the port themselves when it is not. So espx run | grep panic works.
Wait for a line
These flags work on run, monitor and replay. Each pattern is a regular expression in the syntax of Rust's regex-lite crate, tested against the text of each monitor line.
| Flag | Repeatable | Effect |
|---|---|---|
--until RE | no | Exit 0 on the first line that matches |
--expect RE | yes | An ordered script. Each regex must match a line after the line the previous one matched. Each match is an expected event with its index; the last one exits 0 |
--reject RE | yes | Fail at once, with exit 1, on any line that matches. Tested before the expectations on every line |
--timeout D | no | Stop after D and exit 124. Takes 30s, 2m, 1h, or a bare number of seconds |
--until is a one-pattern --expect, added after any --expects. On run the timeout clock starts when the flashed board boots, not when the build starts. When a timeout runs out, the output up to that point is still printed, and the error names the pattern still pending.
If the stream ends first (a replay reaches the end of its file) with a pattern still pending, espx exits 1 with monitor_closed. With nothing pending it exits 0.
JSON output
--json changes only stdout. There are two shapes.
| Commands | --json prints |
|---|---|
devices, info, chips, doctor, boards, board, deps, context, agent, log | One pretty-printed JSON document |
build, flash, run, monitor, replay | One JSON event per line, each stamped with ts, host milliseconds since the epoch |
devices --watch | One attached or detached event per line |
A plain-text log line keeps esp-println's colour codes in text; a decoded defmt line carries level, module and location as separate fields. A stream ends with matched, rejected, timeout or done. build and flash end with done. Progress lines and warnings stay on stderr either way, and in --json mode every child process (cargo, espflash) has its stdout sent to stderr too, so stdout parses line by line. The JSON events reference lists every event and field, and schema/espx.json in the espx repository is the JSON Schema of all of them.
Errors
A failing command prints one error: line on stderr that says what to do next, and exits with a code from the table below. The line is text even with --json. The stable, machine-readable error codes (build_failed, board_not_attached, policy_denied) appear in MCP tool results and in the agent audit log; see Exit codes.
Exit codes
| Code | Meaning |
|---|---|
0 | Success, including a --until match or the last --expect match |
1 | Any other failure: a build error, a rejected line, a missing board, a bad flag |
124 | --timeout ran out (the coreutils convention) |
130 | A child process was interrupted, usually your Ctrl+C reaching cargo or espflash. Nothing is printed |
In CI
A CI job with a board attached to the runner can build, flash and check the firmware in a few lines:
espx setupis idempotent, so it costs nothing on a runner that already has the tools.ESPX_NO_UPDATE_CHECK(any value) stops the daily background update check.- Bind the project to the runner's board with
[board] idinespx.toml, or pass--port, when the runner has more than one board attached. - Add
--record run.jsonltoespx monitorto keep a session as a build artifact;espx replayplays it back anywhere, without the board. See Monitor and replay.
Builds and replays need no board, so the parts of a pipeline that check recorded output can run on any machine.