Line-by-line output from a shim-supervised process as an Enumerable.
For CLIs that emit NDJSON or line-oriented progress (agent CLIs,
docker events, git clone --progress). The child runs in its own
process group; halting the stream, timing out, or BEAM death all kill
the whole group.
Forcola.Stream.lines(["claude", "-p", prompt], timeout_ms: 300_000)
|> Stream.map(&:json.decode/1)
|> Enum.to_list()Termination semantics
- A zero exit ends the stream cleanly.
- A non-zero exit, death by signal, timeout, or spawn failure raises
Forcola.Stream.Errorafter every line produced before death has been emitted. The consumers this is built for are NDJSON CLIs where mid-stream death matters: silently dropping the exit reproduces the leak this library exists to close, and a tagged final element would force everyStream.map(&decode/1)pipeline to special-case it. Stderr captured during the run rides in the exception. - Halting the stream early (
Enum.take/2,Stream.take_while/2, an exception downstream) kills the process group and blocks until the shim confirms the group is dead. - If the consuming process dies, the port closes, the shim sees stdin EOF, and the group is killed.
Summary
Functions
Run argv and return its stdout as a lazy stream of lines.
Functions
@spec lines( [String.t(), ...], keyword() ) :: Enumerable.t()
Run argv and return its stdout as a lazy stream of lines.
Takes the same options as Forcola.run/2; :timeout_ms is required
and bounds the whole run, not the gap between lines (an idle-timeout
option is tracked in
#33).
Lines are emitted without their trailing newline. A partial line held across frame boundaries is emitted once its newline arrives; a final partial line with no newline is emitted before the stream terminates.
See the module docs for how termination is surfaced.