Logo ProcessKit API Reference

CommandVerbs Type

Default-runner convenience verbs on `Command`, callable from F# and C# as `command.StartAsync()` / `command.RunAsync()` etc. They use a shared `JobRunner`; for a custom or injected runner, go through `Runner.*` or call the runner directly. The `cancellationToken` is optional and defaults to `CancellationToken.None`.

Static members

Static member Description

CommandVerbs.ConsoleEncoding(command)

Full Usage: CommandVerbs.ConsoleEncoding(command)

Parameters:
Returns: Command

Encode this command's text stdin and decode its captured stdout **and** stderr with the local console encoding instead of UTF-8 — the one-line fix for a legacy Windows console program whose non-ASCII input/output otherwise becomes mojibake. Equivalent to `Encoding(ConsoleEncoding.current ())`, which documents exactly what is resolved: this process's console output code page (or the system OEM code page when it has no console) on Windows, and UTF-8 — the unchanged default, no P/Invoke, nothing to undo — everywhere else. **Opt-in, and the default is untouched.** Without this call captured text is still decoded UTF-8 on every platform, which is correct for every modern tool; reach for it for the pre-UTF-8 programs (`ping`, `netstat`, `chkdsk`, an old in-house CLI) whose output comes back mangled. It is an ordinary builder knob, so `StdoutEncoding`/`StderrEncoding`/`Encoding` later in the same chain override it (and it overrides them) — the last one wins, as everywhere else. **Resolved here, once.** The code page is read as THIS call runs and the resulting `Encoding` is stored in the returned command; a `Command` is immutable, so nothing re-reads it at spawn time or while the child runs. A `chcp` issued after the command was built is picked up only by a command built again — or given `Encoding(ConsoleEncoding.current ())` before the launch — which is worth knowing for a command built once and reused (a long-lived `CliClient`, a template in a field). The captured *bytes* are never affected: `OutputBytesAsync` and the raw tees stay byte-exact regardless of which encoding decodes the text. (An extension member rather than a `Command` method only because of F# compile order: it reads the console code page through the native layer, which compiles after `Command.fs`.)

command : Command
Returns: Command

CommandVerbs.ExitCodeAsync(command, ?cancellationToken)

Full Usage: CommandVerbs.ExitCodeAsync(command, ?cancellationToken)

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

The exit code; a signal kill or timeout errors instead of inventing a sentinel code.

command : Command
?cancellationToken : CancellationToken
Returns: Task<Result<int, ProcessError>>

CommandVerbs.FirstLineAsync(command, predicate, ?cancellationToken)

Full Usage: CommandVerbs.FirstLineAsync(command, predicate, ?cancellationToken)

Parameters:
Returns: Task<Result<string option, ProcessError>>

The first stdout line satisfying `predicate`, or `None` if stdout closes without a match.

command : Command
predicate : Func<string, bool>
?cancellationToken : CancellationToken
Returns: Task<Result<string option, ProcessError>>

CommandVerbs.LaunchDetached(command)

Full Usage: CommandVerbs.LaunchDetached(command)

Parameters:
Returns: Result<DetachedProcess, ProcessError>

Launch this command **outside all containment** and let it go — the library's single, deliberate opt-out from the whole-tree kill-on-dispose guarantee, for the cases containment makes impossible: a self-updater that must outlive the process it replaces, a restart-myself relaunch, a daemon or agent handed off to the OS. On success it returns a `DetachedProcess` — a pid + start-time identity snapshot, nothing more. **What you give up.** There is no `RunningProcess`, no `ProcessGroup`, no `Outcome`, and nothing to dispose: the child is placed in **no Job Object** (Windows) and in **its own session** (`setsid`, POSIX), no handle to it is retained, and no exit is ever observed. Nothing this process does — `Dispose`, GC, even dying — will reach it. That is the entire point; if you want a deadline, output, an exit code, or a kill, use `StartAsync`/`RunAsync` (or a `ProcessGroup`) instead. `ProcessGroup`-level knobs (`ResourceLimits`, `ProcessGroupOptions`) are not merely ignored here but unreachable: they live on the container this verb refuses to create. **Every incompatible builder knob is refused, not ignored** — `Pty`, `KillOnParentDeath`, `IoPriority` (owner-applied, unlike the CPU-axis `Priority`, which is honoured), `Timeout`/`TimeoutGrace`/`IdleTimeout`, `CancelOn`/`CancelGrace`, a feeder `Stdin` source, `KeepStdinOpen`, the line handlers and tees, `StreamBuffer`, and an active `Retry` policy each come back as a typed `ProcessError.Unsupported` naming the knob, before anything is spawned. `StdioMode.Piped` (the default) is the one deliberate exception: with no parent left to drain a pipe it is wired to the null device, so keep output with `StdoutToFile`/`StderrToFile`, or share the caller's own console with `Stdout(StdioMode.Inherit)`. **Synchronous by design.** Like `ProcessGroup.Create`/`Adopt` and `ResolveProgram`, this does one bounded OS call and has nothing to await — there is no run to wait for — so it returns the `Result` directly rather than a `Task` that never yields. **Platform notes.** POSIX: the child gets a new session (no controlling terminal), so a terminal hangup cannot reach it; because `posix_spawn` cannot reparent, it stays this process's direct child while the parent lives, so if it exits *first* a private reaper consumes that leader's wait status and a long-lived host does not accumulate zombies (if the parent exits first, the OS reparents the child and its new supervisor owns reaping). Windows: the child shares the caller's console unless you add `CreateNoWindow()` (or `WindowsCtrlSignals()`, which puts it in its own console process group), so a console-close event still reaches it in the default wiring. `WindowsRestrictedToken()` and `WindowsIntegrityLevel(...)` remain effective on Windows: detaching opts out of containment, not the requested token hardening. On POSIX both remain honestly unsupported, returning the usual typed `ProcessError.Unsupported` before launch. On both platforms this opts out of the containment ProcessKit creates, not one THIS process was itself placed in: a child of a job-bound Windows process joins that job by kernel rule, and a Linux child inherits this process's cgroup (so a `systemctl stop` of the unit still reaps it). The launch deliberately bypasses the `IProcessRunner` seam — it is an opt-out from running under ProcessKit, not a run — so a test double (`ScriptedRunner`, `RecordReplayRunner`) does not intercept it; put your own seam in front of it if a test must avoid launching anything.

command : Command
Returns: Result<DetachedProcess, ProcessError>

CommandVerbs.OutputBytesAsync(command, ?cancellationToken)

Full Usage: CommandVerbs.OutputBytesAsync(command, ?cancellationToken)

Parameters:
Returns: Task<Result<ProcessResult<byte[]>, ProcessError>>

Run to completion, capturing stdout as raw bytes.

command : Command
?cancellationToken : CancellationToken
Returns: Task<Result<ProcessResult<byte[]>, ProcessError>>

CommandVerbs.OutputJsonAsync(command, typeInfo, ?cancellationToken)

Full Usage: CommandVerbs.OutputJsonAsync(command, typeInfo, ?cancellationToken)

Parameters:
Returns: Task<Result<'T, ProcessError>>
Type parameters: 'T

Require a zero/accepted exit and deserialize the trimmed stdout using source-generated `JsonTypeInfo<'T>` metadata. Invalid JSON becomes `ProcessError.Parse`; unlike the `JsonSerializerOptions` overload, this overload is safe for trimmed and NativeAOT applications.

command : Command
typeInfo : JsonTypeInfo<'T>
?cancellationToken : CancellationToken
Returns: Task<Result<'T, ProcessError>>

CommandVerbs.OutputJsonAsync(command, ?options, ?cancellationToken)

Full Usage: CommandVerbs.OutputJsonAsync(command, ?options, ?cancellationToken)

Parameters:
Returns: Task<Result<'T, ProcessError>>
Type parameters: 'T

Require a zero/accepted exit and deserialize the trimmed stdout as JSON into a `'T` via `System.Text.Json` (`options` omitted uses the BCL defaults); invalid JSON becomes `ProcessError.Parse`, just like `ParseAsync`. Give an explicit type argument, e.g. `cmd.OutputJsonAsync<MyRecord>()` — there is no parser argument to infer `'T` from. **Trimming / AOT:** deserializes via reflection-based `System.Text.Json` (`JsonSerializer.Deserialize(string, Type, JsonSerializerOptions)`), so it is not trim-/AOT-safe — pass `options` with a source-generated `JsonSerializerContext`/`JsonTypeInfo<'T>` resolver, or avoid this verb, in a trimmed/NativeAOT app.

command : Command
?options : JsonSerializerOptions
?cancellationToken : CancellationToken
Returns: Task<Result<'T, ProcessError>>

CommandVerbs.OutputStringAsync(command, ?cancellationToken)

Full Usage: CommandVerbs.OutputStringAsync(command, ?cancellationToken)

Parameters:
Returns: Task<Result<ProcessResult<string>, ProcessError>>

Run to completion, capturing stdout as decoded text (a non-zero exit is data).

command : Command
?cancellationToken : CancellationToken
Returns: Task<Result<ProcessResult<string>, ProcessError>>

CommandVerbs.ParseAsync(command, parser, ?cancellationToken)

Full Usage: CommandVerbs.ParseAsync(command, parser, ?cancellationToken)

Parameters:
Returns: Task<Result<'T, ProcessError>>
Type parameters: 'T

Require a zero/accepted exit and parse the trimmed stdout into a `'T`; a thrown parser error becomes `ProcessError.Parse`.

command : Command
parser : Func<string, 'T>
?cancellationToken : CancellationToken
Returns: Task<Result<'T, ProcessError>>

CommandVerbs.ProbeAsync(command, ?cancellationToken)

Full Usage: CommandVerbs.ProbeAsync(command, ?cancellationToken)

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

Read the exit code as a yes/no answer: 0 -> true, 1 -> false, anything else errors.

command : Command
?cancellationToken : CancellationToken
Returns: Task<Result<bool, ProcessError>>

CommandVerbs.ResolveProgram(command)

Full Usage: CommandVerbs.ResolveProgram(command)

Parameters:
Returns: Result<string, ProcessError>

Resolve this command's program to a full path WITHOUT spawning it — a preflight/`doctor` check ("will this command find its program?"), synchronous and side-effect-free (a few `stat`s, no process), unlike probing availability by actually launching it (`ProbeAsync`). Resolution is against the **effective child** `PATH`: the command's own `Env`/`EnvRemove`/`EnvClear` (a `PATH` override) applied to the inherited environment, with its `PreferLocal` directories consulted first — exactly the `PATH`/PATHEXT/executable-bit resolution the real spawn goes through (one shared resolver, no second copy). On success it returns the resolved absolute path; on a miss it returns the SAME typed `ProcessError.NotFound` — with the SAME `Searched` diagnostic — a real spawn of this command would fail with. A relative path-form program (`./tool`, `bin/tool`) is resolved against `CurrentDir` when configured, matching the child's launch directory on every platform. **Differs from `Exec.which`.** `Exec.which` (and `CliClient.EnsureAvailableAsync`) resolves against the CURRENT PROCESS's `PATH`, with no prefer-local — "is this tool installed on the host". This resolves against THIS command's effective environment and prefer-local — "will this command, as configured, find its program". Use `which` for a host-wide install check; use `ResolveProgram` when the command overrides `PATH` (`Env`) or leans on `PreferLocal`.

command : Command
Returns: Result<string, ProcessError>

CommandVerbs.RunAsync(command, ?cancellationToken)

Full Usage: CommandVerbs.RunAsync(command, ?cancellationToken)

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

Require a zero/accepted exit and return stdout, trailing whitespace trimmed. Output the command's `OutputBuffer` policy truncated is refused with `ProcessError.OutputTooLarge`, and output the bounded post-exit drain cut short with `ProcessError.OutputIncomplete`, rather than returned as if whole — use `OutputStringAsync` for the bounded payload plus `Truncated`.

command : Command
?cancellationToken : CancellationToken
Returns: Task<Result<string, ProcessError>>

CommandVerbs.RunUnitAsync(command, ?cancellationToken)

Full Usage: CommandVerbs.RunUnitAsync(command, ?cancellationToken)

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

Require a zero/accepted exit, discarding the captured output — including when the buffer policy truncated it, which this verb makes no claim about.

command : Command
?cancellationToken : CancellationToken
Returns: Task<Result<unit, ProcessError>>

CommandVerbs.StartAsync(command, ?cancellationToken)

Full Usage: CommandVerbs.StartAsync(command, ?cancellationToken)

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

Start the command and return a live `RunningProcess`.

command : Command
?cancellationToken : CancellationToken
Returns: Task<Result<RunningProcess, ProcessError>>

CommandVerbs.TryParseAsync(command, parser, ?cancellationToken)

Full Usage: CommandVerbs.TryParseAsync(command, parser, ?cancellationToken)

Parameters:
Returns: Task<Result<'T, ProcessError>>
Type parameters: 'T

Like `ParseAsync`, but with the standard .NET try-parse shape: pass a BCL parser like `int.TryParse` with an explicit type argument (`TryParseAsync<int>(int.TryParse)` — needed because BCL `TryParse` is overloaded). A `false` return becomes `ProcessError.Parse`. (F# can use the `Result`-returning `Runner.tryParse`.)

command : Command
parser : TryParser<'T>
?cancellationToken : CancellationToken
Returns: Task<Result<'T, ProcessError>>

Type something to start searching.