Logo ProcessKit API Reference

PtySession Type

An expect-style conversation with a live child: wait for a pattern in its terminal output, send it input, repeat — the classic automation loop for an interactive program (`ssh`, a REPL, an installer, a credential prompt). Built over a started `RunningProcess`, and designed for one from a `Command.Pty` run, which is what makes an interactive child prompt at all. **Why not `WaitForLineAsync`.** A terminal prompt is not a line: `Password: `, `> `, `(y/N) ` carry no line terminator, so a line-framed wait cannot see them until a newline finally arrives — often never, because the child is waiting for the very input the prompt is asking for. A session therefore reads the child's merged terminal output as **raw text** and matches patterns against a sliding window of it, framing nothing. **One conversation, in order.** The verbs are meant to be called sequentially, the way the script they automate reads: expect, send, expect. Two `ExpectAsync` calls racing on one session are safe (the window is guarded, so exactly one of them consumes a given match) but arbitrary — which of the two sees the prompt is a coin toss, the same as concurrently driving any other single handle. **This session owns the output pipes.** Creating it claims the handle exactly like `OutputEventsAsync`/`StdoutLinesAsync` do, so a capturing or streaming verb on the same handle afterwards is refused ("already consumed by another verb"), and constructing a session over a handle another verb already claimed throws `InvalidOperationException`. Use `WaitForExitAsync` for the child's outcome, and dispose the `RunningProcess` (or its owning `ProcessGroup`) to reap the tree — do not layer a second consuming verb on top. **Line handlers do not fire here.** `Command.LineTerminator`, `OnStdoutLine`/`OnStderrLine` and the per-stream line counters describe a framed stream, and this session deliberately does not frame one; the byte-exact tees (`Command.StdoutTee`/`StderrTee`) are still fed, exactly as they are on the line paths. On a plain (non-PTY) run a session still works, but the child decides whether you ever see a prompt: without a terminal most programs switch their stdout to block buffering, so the prompt sits in the child's own buffer — that is the child's behaviour, and precisely what `Command.Pty` exists to change. **`StreamBuffer` is inapplicable here.** A session has no queued line/frame backlog: raw output feeds its sliding match window directly. `PtySessionOptions.WindowChars` and `TranscriptChars` are its explicit bounded-memory policy, so a `Command.StreamBuffer` setting has no effect. **Secret-safety.** Sent input is never logged, traced, or added to `Transcript` — but a terminal echoes input back into its OUTPUT by default, so with `PtyConfig.Echo = true` a sent password arrives in the child's output stream and therefore in the transcript. For a credential exchange use `Echo = false` (POSIX), or turn `CaptureTranscript` off, or both.

Constructors

Constructor Description

PtySession(running)

Full Usage: PtySession(running)

Parameters:
Returns: PtySession

A raw-output session over `running` with the default tuning (`PtySessionOptions.Default`).

running : RunningProcess
Returns: PtySession

PtySession(running, options)

Full Usage: PtySession(running, options)

Parameters:
Returns: PtySession

A raw-output session over `running` with explicit tuning. ANSI/VT escape sequences remain in pattern matching, `Pending`, and `Transcript`; use `WithAnsiFiltering` to remove them.

running : RunningProcess
options : PtySessionOptions
Returns: PtySession

Instance members

Instance member Description

this.CloseStdinAsync

Full Usage: this.CloseStdinAsync

Returns: Task<Result<unit, ProcessError>>

Close the child's stdin without cancellation. Equivalent to `CloseStdinAsync(CancellationToken.None)`.

Returns: Task<Result<unit, ProcessError>>

this.CloseStdinAsync

Full Usage: this.CloseStdinAsync

Parameters:
Returns: Task<Result<unit, ProcessError>>

Close the child's stdin, so it sees end-of-file — how a conversation that feeds input ends for a child that reads until EOF. Idempotent; returns the same typed `Unsupported` as the send verbs when the run has no interactive stdin. A `Command.Stdin` source is awaited before closing so its bytes cannot be truncated by the interactive close. `cancellationToken` bounds only that pre-delivery wait; once stdin is claimed, end-of-input delivery is not cancellable. On a **PTY** there is no stdin pipe to close — input goes into the same terminal the conversation's output comes from — so the end of input is delivered as that terminal's own end-of-input gesture instead (see `ProcessStdin.FinishAsync`): on POSIX the pty's configured end-of-input character (`termios.c_cc[VEOF]`, Ctrl-D on a default terminal), on Windows the console's Ctrl-Z followed by Enter. The terminal itself stays open either way, so the child keeps its output — and, on Windows, its console session — for the rest of the run. Being a gesture the terminal interprets, it ends the input only of a child still reading in cooked mode (POSIX canonical mode, or a Windows console its own `CONIN$` mode has not switched to raw); one that reads its terminal raw receives those bytes as ordinary input, which is the terminal's contract rather than something this verb can paper over. A genuine failure to deliver it is a typed `Io` — never a silently dropped close that would leave the child waiting — while a child that has already closed its terminal is reported as the `Ok` it is: there is nothing left to tell it.

cancellationToken : CancellationToken
Returns: Task<Result<unit, ProcessError>>

this.ExpectAsync

Full Usage: this.ExpectAsync

Parameters:
Returns: Task<Result<ExpectMatch, ProcessError>>

Wait until `pattern` matches somewhere in the child's terminal output — the regular-expression form of the overload above, with the identical timeout, consumption, early-end, and error contract. The `Regex` is used exactly as given, including its own options and `MatchTimeout`. Matching runs over the same sliding, unframed session view as the string overload, so `^`/`$` anchor against the window's bounds rather than the child's line structure unless you pass `RegexOptions.Multiline`. A zero-width match is rejected with a typed `Unsupported` result, because it cannot advance the session window.

pattern : Regex
timeout : TimeSpan
?cancellationToken : CancellationToken
Returns: Task<Result<ExpectMatch, ProcessError>>

this.ExpectAsync

Full Usage: this.ExpectAsync

Parameters:
Returns: Task<Result<ExpectMatch, ProcessError>>

Wait until `pattern` appears in the child's terminal output, or fail with `NotReady` after `timeout` — a budget for THIS pattern alone, entirely separate from the run-wide `Command.Timeout`/`IdleTimeout` (which kill the child; this one does not). `Cancelled` if `cancellationToken` fires first. The match is an ordinal substring search over the unframed session view (raw by default, control-free from `WithAnsiFiltering`), so a prompt that never ends its line (`Password: `) is found the moment it arrives. Note that a terminal ends its own lines with `\r\n`: match on the prompt text itself rather than on a trailing `\n`. Everything up to and including the match is consumed, so the next call starts after it; a pattern that has not arrived by the deadline leaves the window untouched, so a longer retry still sees what did arrive. `pattern` must be non-null; an empty pattern returns a typed `Unsupported` result because it cannot advance the session window. Ends promptly with `NotReady` if the child's output ends first (it exited, or closed the terminal) rather than waiting out the whole `timeout`, and with `ProcessError.Io` if reading it genuinely failed.

pattern : string
timeout : TimeSpan
?cancellationToken : CancellationToken
Returns: Task<Result<ExpectMatch, ProcessError>>

this.Pending

Full Usage: this.Pending

Returns: string

The output received but not yet consumed by a pattern — what the next `ExpectAsync` will match against. Reading it consumes nothing; it is for diagnosing a pattern that did not arrive ("what did the child actually print?") without having to keep a transcript.

Returns: string

this.SendAsync

Full Usage: this.SendAsync

Parameters:
Returns: Task<Result<unit, ProcessError>>

Send `text` to the child exactly as given — no line ending appended — encoded with the command's terminal encoding (`Command.StdoutEncoding`, UTF-8 by default). Use it for a control character (U+0003 is Ctrl+C to a terminal) or to answer a prompt that reads a single keystroke rather than a line. On Windows ConPTY, U+0003 does not interrupt the child by default: ProcessKit's unconditional `CREATE_NEW_PROCESS_GROUP` isolation disables default CTRL+C handling. Returns a typed `Unsupported` when the run has no interactive stdin (build the command with `Command.KeepStdinOpen`), `Cancelled` if `cancellationToken` fires, and `Io` if the child has closed its input — never a silently dropped write. With `Command.Stdin(source)` plus `Command.KeepStdinOpen`, the source feeder is awaited here before the first interactive byte; construction itself does not wait for that source. As with any cancellable stream write, a cancelled send may already have delivered *some* of its bytes, so recover by abandoning the conversation rather than resending (which would duplicate the delivered prefix). Sent bytes are never logged, traced, or added to `Transcript`; a terminal with echo on will nevertheless reflect them back into the child's output (see the type-level secret-safety note).

text : string
?cancellationToken : CancellationToken
Returns: Task<Result<unit, ProcessError>>

this.SendLineAsync

Full Usage: this.SendLineAsync

Parameters:
Returns: Task<Result<unit, ProcessError>>

Send `text` followed by the session's line ending (`PtySessionOptions.LineEnding`, by default a carriage return on a PTY run — what a terminal sends for Enter — and a line feed otherwise). The answer to a prompt. Same encoding, refusal, and secret-safety contract as `SendAsync`.

text : string
?cancellationToken : CancellationToken
Returns: Task<Result<unit, ProcessError>>

this.Transcript

Full Usage: this.Transcript

Returns: string

Everything the child has emitted this session, in order — empty when `CaptureTranscript` is off, and holding at most `TranscriptChars` characters (oldest dropped first, see `TranscriptTruncated`). Readable at any point, during the conversation as well as after it. Contains only the child's output; input sent through this session is never recorded here, though a terminal with echo on reflects it into the child's output anyway.

Returns: string

this.TranscriptTruncated

Full Usage: this.TranscriptTruncated

Returns: bool

Whether the transcript has already dropped output to stay within `TranscriptChars` — so a diagnosis reading `Transcript` knows it is looking at the tail of the session, not all of it.

Returns: bool

this.WaitForExitAsync

Full Usage: this.WaitForExitAsync

Returns: Task<Outcome>

Wait for the child to exit and for this session's readers to finish draining its terminal, then report how it concluded. A non-zero or killed exit is data, not a raised error. Does **not** reap: dispose the `RunningProcess` (or its owning `ProcessGroup`) for that, exactly as after `OutputEventsAsync`. It shares this handle's one exit wait, so it never starts a second wait racing the session's own readers.

Returns: Task<Outcome>

this.WindowTruncated

Full Usage: this.WindowTruncated

Returns: bool

Whether the match window has already dropped output to stay within `WindowChars` — a pattern expecting context that far back can no longer match, and `ExpectMatch.Before` is correspondingly incomplete.

Returns: bool

Static members

Static member Description

PtySession.WithAnsiFiltering(running)

Full Usage: PtySession.WithAnsiFiltering(running)

Parameters:
Returns: PtySession

Create an ANSI-filtered session with the default tuning (`PtySessionOptions.Default`).

running : RunningProcess
Returns: PtySession

PtySession.WithAnsiFiltering(running, options)

Full Usage: PtySession.WithAnsiFiltering(running, options)

Parameters:
Returns: PtySession

Create a session that removes ANSI/VT escape sequences before matching or retaining terminal output. Filtering is incremental across read boundaries and covers CSI sequences, OSC strings terminated by BEL or ST, and single ESC forms. Byte-exact output tees remain unfiltered.

running : RunningProcess
options : PtySessionOptions
Returns: PtySession

Type something to start searching.