# `NetRunner`
[🔗](https://github.com/nyo16/net_runner/blob/v1.0.4/lib/net_runner.ex#L1)

Safe OS process execution for Elixir.

Combines NIF-based async I/O with a persistent shepherd binary to guarantee
zero zombie processes, even under BEAM SIGKILL.

## Quick start

    # Simple command execution
    {output, 0} = NetRunner.run(~w(echo hello))

    # Streaming with input
    NetRunner.stream!(~w(cat), input: "hello world")
    |> Enum.to_list()
    # => ["hello world"]

    # Piping data through a command
    NetRunner.stream!(~w(wc -c), input: "hello")
    |> Enum.join()
    # => "       5\n"

# `run`

Runs a command and collects all output.

Returns `{output, exit_status}` where output is the concatenated stdout.

## Options

  * `:stderr` - `:consume` (default, captured internally), `:redirect` (merged with stdout),
    or `:disabled`
  * `:input` - data to write to stdin (binary or enumerable)
  * `:timeout` - maximum wall-clock time in milliseconds. Sends SIGTERM then SIGKILL
    on timeout. Returns `{:error, :timeout}` instead of `{output, exit_status}`.
  * `:max_output_size` - maximum bytes to collect from stdout. Kills the process
    and returns `{:error, {:max_output_exceeded, partial_output}}` if exceeded.

## Examples

    {output, 0} = NetRunner.run(~w(echo hello))
    {"hello\n", 0} = {output, 0}

    {output, 0} = NetRunner.run(~w(cat), input: "from stdin")

    {:error, :timeout} = NetRunner.run(~w(sleep 100), timeout: 100)

    {:error, {:max_output_exceeded, _partial}} =
      NetRunner.run(["sh", "-c", "yes"], max_output_size: 1000)

# `stream`

Like `stream!/2` but returns `{:ok, stream}` or `{:error, reason}`.

# `stream!`

Creates a stream for incremental I/O with the command.

Returns a `Stream` that yields stdout binary chunks.
Raises on process start failure.

## Options

  * `:input` - data to write to stdin (binary, list, or Stream)
  * `:stderr` - `:consume` (default), `:redirect`, or `:disabled`

## Examples

    # Stream through a command
    NetRunner.stream!(~w(sort))
    |> Enum.to_list()

    # With input
    NetRunner.stream!(~w(tr a-z A-Z), input: "hello")
    |> Enum.join()
    # => "HELLO"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
