On this page
  1. tecs.io.Process
  2. Module contents
    1. Constructors
    2. Types
    3. Functions
    4. Values
  3. Constructors
    1. new
  4. Types
    1. CommunicateOptions
    2. ErrorMode
    3. Exit
    4. InputMode
    5. Options
    6. OutputMode
    7. Reader
    8. Result
    9. Writer
  5. Functions
    1. Process:communicate
    2. Process:isRunning
    3. Process:kill
  6. Values
    1. finished
    2. pid
    3. stderr
    4. stdin
    5. stdout

tecs.io.Process

Streaming child processes.

new starts the child immediately and returns its live standard-stream endpoints. Piped stdin is a Writer; piped stdout and stderr are Reader values. The process owns all three endpoints. Closing one endpoint closes only that pipe; closing the process closes every endpoint, terminates a live child, and reaps it. Buffers and byte views passed to an endpoint remain caller-owned.

Process:communicate is the complete-exchange form for tools and build steps. It feeds stdin while draining stdout and stderr concurrently, so neither output pipe can fill and deadlock the child:

tecs.scoped(function(scope: tecs.Scope)
    local child <const> = scope:own(tecs.io.Process.new({
        args = {"git", "status", "--porcelain"},
        timeoutMs = 2000,
    }))

    local result, communicateReason = child:communicate()
    if result == nil then
        error(communicateReason)
    end

    if result:succeeded() then
        print(result.output)
    else
        io.stderr:write(result.errorOutput)
    end
end)

Interactive children use the same reader and writer vocabulary as files, buffers, and transforms. Closing stdin sends EOF:

tecs.scoped(function(scope: tecs.Scope)
    local child <const> = scope:own(tecs.io.Process.new({
        args = {"/bin/cat"}
    }))

    local written, writeReason = child.stdin:write("one request\n")
    if written == nil then
        error(writeReason)
    end
    child.stdin:close()

    local reply, readReason = child.stdout:read(4096)
    if reply == nil then
        error(readReason)
    end
    print(reply)
end)

Frame-driven code uses readAvailable, readAvailableInto, and the matching writer methods. Nil without a reason means an open pipe has no bytes or capacity right now; an empty string or zero means output EOF. These methods make backpressure visible instead of growing an implicit queue. Buffer and byte-view variants avoid constructing Lua strings.

Use "inherit" for a CLI that should share the terminal, "null" to discard a stream, and stderr = "stdout" for one ordered transcript. A nonzero child exit still produces a ready Exit; succeeded performs the separate exit-code check. Spawn failures return nil and a reason. Deadlines and explicit kills set the exit's killed fields.

Module contents

Constructors

Constructor Description
new Starts a caller-owned child process with streaming standard I/O.

Types

Type Kind Description
CommunicateOptions record CommunicateOptions controls a complete duplex exchange.
ErrorMode enum ErrorMode selects where a child writes standard error.
Exit record Exit describes how a child stopped.
InputMode enum InputMode selects where a child reads standard input.
Options record Options configures a child and its standard streams.
OutputMode enum OutputMode selects where a child writes standard output.
Reader interface Reader reads one live child-process output pipe.
Result record Result contains a completed duplex exchange.
Writer interface Writer writes one live child-process input pipe.

Functions

Function Kind Description
communicate Instance Feeds stdin and captures stdout and stderr without pipe deadlock.
isRunning Instance Returns whether the child has not stopped yet.
kill Instance Requests that the child terminate.

Values

Value Type Description
finished Future Read-only. Settles ready with the exit description after the child stops.
pid integer Read-only. Reports the operating-system process identifier.
stderr Reader Read-only. Provides standard error when its mode is "pipe", or nil when error output is inherited, discarded, or...
stdin Writer Read-only. Provides standard input when its mode is "pipe", or nil when input is inherited or discarded.
stdout Reader Read-only. Provides standard output when its mode is "pipe", or nil when output is inherited or discarded.

Constructors

tecs.io.Process.new Static

Starts a caller-owned child process with streaming standard I/O.

Piped endpoints are nonblocking at their readAvailable and writeAvailable boundaries. The process owns them, while buffers and byte views passed to endpoint methods remain caller-owned.

function tecs.io.Process.new(options: Options): Process, string

Arguments

Name Type Description
options Options The caller supplies the program, arguments, environment, working directory, standard-stream modes, and optional deadline.

Returns

Type Description
Process Returns a caller-owned process when the platform starts it.
string Returns the platform reason when the first return is nil.

Examples

tecs.scoped(function(scope: tecs.Scope)
    local child <const> = scope:own(tecs.io.Process.new({
        args = {"git", "status", "--porcelain"},
        cwd = tecs.io.Path.new("workspace"),
    }))

    local result, communicateReason = child:communicate()

    assert(result, communicateReason)
end)
tecs.scoped(function(scope: tecs.Scope)
    local child <const> = scope:own(tecs.io.Process.new({
        args = {"git", "rev-parse", "HEAD"},
        timeoutMs = 2000,
    }))

    local result, communicateReason = child:communicate()

    assert(result, communicateReason)
    assert(result:succeeded())
    print(result.output)
end)

Types

tecs.io.Process.CommunicateOptions record

CommunicateOptions controls a complete duplex exchange.

record tecs.io.Process.CommunicateOptions
    input: string | ByteView
    maxOutputBytes: integer
end

tecs.io.Process.CommunicateOptions.input field

Caller-writable. Supplies complete standard-input bytes as a string, open Buffer, or open ByteView. Omitted input sends EOF immediately.

tecs.io.Process.CommunicateOptions.input: string | ByteView

tecs.io.Process.CommunicateOptions.maxOutputBytes field

Caller-writable. Limits stdout and stderr together. Defaults to 268,435,456 bytes. Exceeding it forcibly terminates the child and returns a failure.

tecs.io.Process.CommunicateOptions.maxOutputBytes: integer

tecs.io.Process.ErrorMode enum

ErrorMode selects where a child writes standard error.

enum tecs.io.Process.ErrorMode
    "inherit"
    "null"
    "pipe"
    "stdout"
end

tecs.io.Process.Exit record

Exit describes how a child stopped.

record tecs.io.Process.Exit
    exitCode: integer
    killed: boolean
    timedOut: boolean

    succeeded: function(self): boolean
end

tecs.io.Process.Exit.exitCode field

Read-only. Reports the platform exit code, or the platform's best answer after termination.

tecs.io.Process.Exit.exitCode: integer

tecs.io.Process.Exit.killed field

Read-only. Reports whether Tecs requested termination.

tecs.io.Process.Exit.killed: boolean

tecs.io.Process.Exit.timedOut field

Read-only. Reports whether the configured deadline requested that termination.

tecs.io.Process.Exit.timedOut: boolean

tecs.io.Process.Exit:succeeded Instance

Returns whether the child exited normally with code zero.

function tecs.io.Process.Exit.succeeded(self): boolean
Arguments
Name Type Description
self ProcessExit The completed exit to inspect.
Returns
Type Description
boolean Returns true only for a normal zero exit.

tecs.io.Process.InputMode enum

InputMode selects where a child reads standard input.

enum tecs.io.Process.InputMode
    "inherit"
    "null"
    "pipe"
end

tecs.io.Process.Options record

Options configures a child and its standard streams.

record tecs.io.Process.Options
    args: {string}
    cwd: string | Path
    env: {string: string}
    clearEnv: boolean
    stdin: ProcessInputMode
    stdout: ProcessOutputMode
    stderr: ProcessErrorMode
    timeoutMs: integer
end

tecs.io.Process.Options.args field

Caller-writable. Supplies the program in args[1] followed by its arguments. A program without a separator resolves through PATH.

tecs.io.Process.Options.args: {string}

tecs.io.Process.Options.cwd field

Caller-writable. Selects the child's working directory as a string or Path, or omits it to inherit the current directory.

tecs.io.Process.Options.cwd: string | Path

tecs.io.Process.Options.env field

Caller-writable. Overlays environment variables on the inherited environment, or supplies the complete environment with clearEnv.

tecs.io.Process.Options.env: {string: string}

tecs.io.Process.Options.clearEnv field

Caller-writable. Starts with an empty environment when true. Defaults to false.

tecs.io.Process.Options.clearEnv: boolean

tecs.io.Process.Options.stdin field

Caller-writable. Selects "pipe", "inherit", or "null" for standard input. Defaults to "pipe".

tecs.io.Process.Options.stdin: ProcessInputMode

tecs.io.Process.Options.stdout field

Caller-writable. Selects "pipe", "inherit", or "null" for standard output. Defaults to "pipe".

tecs.io.Process.Options.stderr field

Caller-writable. Selects "pipe", "inherit", "null", or "stdout" for standard error. Defaults to "pipe".

tecs.io.Process.Options.timeoutMs field

Caller-writable. Forcibly terminates the child after this many milliseconds, or omits the deadline. The deadline begins when the process is created.

tecs.io.Process.Options.timeoutMs: integer

tecs.io.Process.OutputMode enum

OutputMode selects where a child writes standard output.

enum tecs.io.Process.OutputMode
    "inherit"
    "null"
    "pipe"
end

tecs.io.Process.Reader interface

Reader reads one live child-process output pipe.

interface tecs.io.Process.Reader is Reader
    isClosed: function(self): boolean
    isEOF: function(self): boolean
    readAvailable: function(self, maxBytes: integer): string, string
    readAvailableInto: function(
        self, destination: Buffer, offset: integer, maxBytes: integer
    ): integer, string
end

Interfaces

Interface
Reader

tecs.io.Process.Reader:isClosed Instance

Returns whether this endpoint has been closed.

function tecs.io.Process.Reader.isClosed(self): boolean
Arguments
Name Type Description
self ProcessReader The process reader to inspect.
Returns
Type Description
boolean Returns true after close.

tecs.io.Process.Reader:isEOF Instance

Returns whether the child closed its end and every byte was consumed.

function tecs.io.Process.Reader.isEOF(self): boolean
Arguments
Name Type Description
self ProcessReader The process reader to inspect.
Returns
Type Description
boolean Returns true after end of file.

tecs.io.Process.Reader:readAvailable Instance

Reads immediately available bytes without waiting.

Nil without a reason means the pipe remains open but has no bytes ready. An empty string means end of file.

function tecs.io.Process.Reader.readAvailable(
    self, maxBytes: integer
): string, string
Arguments
Name Type Description
self ProcessReader The process reader whose pipe advances.
maxBytes integer The caller supplies 1 through 65,536 bytes or omits it for 16,384.
Returns
Type Description
string Returns available bytes, nil when none are ready, or an empty string at end of file.
string Returns a reason only when reading fails.

Examples

tecs.scoped(function(scope: tecs.Scope)
    local child <const> = scope:own(tecs.io.Process.new({
        args = {"/bin/sh", "-c", "sleep 1; printf ready"},
    }))

    local chunk <const>, readReason = child.stdout:readAvailable()

    assert(chunk == nil)
    assert(readReason == nil)
end)

tecs.io.Process.Reader:readAvailableInto Instance

Reads immediately available bytes into reusable storage.

Nil without a reason means no bytes are ready. Zero means end of file.

function tecs.io.Process.Reader.readAvailableInto(
    self, destination: Buffer, offset: integer, maxBytes: integer
): integer, string
Arguments
Name Type Description
self ProcessReader The process reader whose pipe advances.
destination Buffer The caller supplies an open destination buffer and retains ownership.
offset integer The caller supplies a non-negative destination offset or omits it for zero.
maxBytes integer The caller supplies 1 through 65,536 bytes or omits it for 16,384.
Returns
Type Description
integer Returns bytes read, nil when none are ready, or zero at end of file.
string Returns a reason only when reading fails.

tecs.io.Process.Result record

Result contains a completed duplex exchange.

record tecs.io.Process.Result
    exit: ProcessExit
    output: string
    errorOutput: string

    succeeded: function(self): boolean
end

tecs.io.Process.Result.exit field

Read-only. Contains the child's exit description.

tecs.io.Process.Result.exit: ProcessExit

tecs.io.Process.Result.output field

Read-only. Contains every captured standard-output byte. It is empty when stdout was inherited or discarded.

tecs.io.Process.Result.output: string

tecs.io.Process.Result.errorOutput field

Read-only. Contains every captured standard-error byte. It is empty when stderr was inherited, discarded, or merged into stdout.

tecs.io.Process.Result.errorOutput: string

tecs.io.Process.Result:succeeded Instance

Returns whether the child exited normally with code zero.

function tecs.io.Process.Result.succeeded(self): boolean
Arguments
Name Type Description
self ProcessResult The completed exchange to inspect.
Returns
Type Description
boolean Returns true only for a normal zero exit.

tecs.io.Process.Writer interface

Writer writes one live child-process input pipe.

interface tecs.io.Process.Writer is Writer
    isClosed: function(self): boolean
    writeAvailable: function(
        self, bytes: string, offset: integer, count: integer
    ): integer, string
    writeAvailableFrom: function(
        self, source: Buffer, offset: integer, count: integer
    ): integer, string
    writeAvailableView: function(
        self, source: ByteView, offset: integer, count: integer
    ): integer, string
end

Interfaces

Interface
Writer

tecs.io.Process.Writer:isClosed Instance

Returns whether this endpoint has sent EOF or been closed.

function tecs.io.Process.Writer.isClosed(self): boolean
Arguments
Name Type Description
self ProcessWriter The process writer to inspect.
Returns
Type Description
boolean Returns true after close.

tecs.io.Process.Writer:writeAvailable Instance

Writes as many immediately acceptable string bytes as possible.

Nil without a reason means the pipe has no capacity yet.

function tecs.io.Process.Writer.writeAvailable(
    self, bytes: string, offset: integer, count: integer
): integer, string
Arguments
Name Type Description
self ProcessWriter The process writer whose pipe advances.
bytes string The caller supplies binary bytes including embedded NULs.
offset integer The caller supplies a zero-based string offset or omits it for zero.
count integer The caller supplies a byte count or omits it for the remainder.
Returns
Type Description
integer Returns bytes accepted, or nil when none fit immediately.
string Returns a reason only when writing fails.

tecs.io.Process.Writer:writeAvailableFrom Instance

Writes an immediately acceptable buffer prefix without a Lua string.

function tecs.io.Process.Writer.writeAvailableFrom(
    self, source: Buffer, offset: integer, count: integer
): integer, string
Arguments
Name Type Description
self ProcessWriter The process writer whose pipe advances.
source Buffer The caller supplies an open source buffer and retains ownership.
offset integer The caller supplies a zero-based source offset or omits it for zero.
count integer The caller supplies a byte count or omits it for the remainder.
Returns
Type Description
integer Returns bytes accepted, or nil when none fit immediately.
string Returns a reason only when writing fails.

tecs.io.Process.Writer:writeAvailableView Instance

Writes an immediately acceptable immutable-view prefix.

function tecs.io.Process.Writer.writeAvailableView(
    self, source: ByteView, offset: integer, count: integer
): integer, string
Arguments
Name Type Description
self ProcessWriter The process writer whose pipe advances.
source ByteView The caller supplies an open source view and retains ownership.
offset integer The caller supplies a zero-based source offset or omits it for zero.
count integer The caller supplies a byte count or omits it for the remainder.
Returns
Type Description
integer Returns bytes accepted, or nil when none fit immediately.
string Returns a reason only when writing fails.

Functions

tecs.io.Process:communicate Instance

Feeds stdin and captures stdout and stderr without pipe deadlock.

The call blocks the current Lua thread, drains both output pipes while feeding input, closes stdin after the final byte, and returns only after the child exits and both captured pipes reach EOF. Run it in a worker when blocking is inappropriate.

function tecs.io.Process.communicate(
    self, options: CommunicateOptions
): Result, string

Arguments

Name Type Description
self Process The open process whose piped endpoints are consumed.
options CommunicateOptions The caller supplies complete input and an output limit, or omits both.

Returns

Type Description
Result Returns the complete exchange result.
string Returns a reason when communication fails or exceeds its output limit.

Examples

tecs.scoped(function(scope: tecs.Scope)
    local child <const> = scope:own(tecs.io.Process.new({
        args = {"/bin/cat"}
    }))

    local result, communicateReason = child:communicate({
        input = "request bytes\n",
        maxOutputBytes = 1024 * 1024,
    })

    assert(result, communicateReason)
    assert(result.output == "request bytes\n")
end)

tecs.io.Process:isRunning Instance

Returns whether the child has not stopped yet.

function tecs.io.Process.isRunning(self): boolean

Arguments

Name Type Description
self Process The process to inspect.

Returns

Type Description
boolean Returns true while the child remains live.

tecs.io.Process:kill Instance

Requests that the child terminate.

function tecs.io.Process.kill(self, force: boolean): boolean, string

Arguments

Name Type Description
self Process The process to terminate.
force boolean The caller requests an unhandleable termination when true or a graceful request when false or omitted.

Returns

Type Description
boolean Returns whether the request was delivered or the child had already stopped.
string Returns the platform reason when the first return is false.

Values

tecs.io.Process.finished variable

Read-only. Settles ready with the exit description after the child stops. Canceling this future does not terminate the separately owned process.

tecs.io.Process.finished: Future<Exit>

tecs.io.Process.pid variable

Read-only. Reports the operating-system process identifier.

tecs.io.Process.pid: integer

tecs.io.Process.stderr variable

Read-only. Provides standard error when its mode is "pipe", or nil when error output is inherited, discarded, or merged into stdout. The process owns it.

tecs.io.Process.stderr: Reader

tecs.io.Process.stdin variable

Read-only. Provides standard input when its mode is "pipe", or nil when input is inherited or discarded. The process owns it.

tecs.io.Process.stdin: Writer

tecs.io.Process.stdout variable

Read-only. Provides standard output when its mode is "pipe", or nil when output is inherited or discarded. The process owns it.

tecs.io.Process.stdout: Reader