F# async child-process management for .NET: whole-tree kill-on-drop (no orphans), streaming, pipelines, timeouts, and supervision.
Status: pre-1.0, API in progress. ProcessKit is an F# port of the Rust crate
ProcessKit-rsand is being built feature by feature. The public API is not yet frozen — expect additions until the 1.0 release. SeeCHANGELOG.mdfor what has landed.
dotnet add package ProcessKitEvery run returns Task<Result<_, ProcessError>> — a non-zero exit is data for the
capture verbs (OutputString/OutputBytes/ExitCode/Probe) and an error only for the
success-requiring verbs (Run/RunUnit).
open ProcessKit
task {
// Require a zero exit; return stdout, trailing whitespace trimmed.
let head = Command.create "git" |> Command.args [ "rev-parse"; "HEAD" ]
match! head.Run() with
| Ok sha -> printfn $"HEAD is {sha}"
| Error err -> eprintfn $"{err.Message}"
// Shell-free pipeline: each stage's stdout feeds the next stage's stdin, all in one
// kill-on-dispose group. The exit status follows pipefail.
let pipeline =
(Command.create "cat" |> Command.arg "access.log")
.Pipe(Command.create "grep" |> Command.arg "ERROR")
.Pipe(Command.create "wc" |> Command.arg "-l")
match! pipeline.Run() with
| Ok count -> printfn $"{count} error lines"
| Error err -> eprintfn $"{err.Message}"
}From C# the same surface is available as fluent methods (command.Run(),
command.Pipe(next).OutputString(), …).
- Whole-tree kill-on-drop — a process and everything it spawns is reaped on dispose
(Windows Job Object
KILL_ON_JOB_CLOSE; Linux/macOS POSIX process group), or on GC finalization as a safety net. - Honest results —
ProcessErrordistinguishes spawn / not-found / non-zero exit / signal / timeout / cancellation; the verb you choose decides whether a non-zero exit is data or an error. - Streaming & interactive I/O —
Command.Start()returns a liveRunningProcesswithStdoutLines()/OutputEvents()asIAsyncEnumerable, interactive stdin, and readiness probes (WaitForLine/WaitForPort/WaitFor). - Timeouts, cancellation, retry —
Command.Timeout/TimeoutGrace/CancelOn/Retry, plusCommand.OkCodesto accept non-zero exits as success. - Shell-free pipelines —
Command.Pipewith pipefail semantics andUncheckedInPipe. - Supervision —
Supervisorkeeps a command alive with restart policies, exponential backoff + jitter, and a failure-storm guard. - Tree control & resource limits —
ProcessGroup.Signal/Suspend/Resume/Members, andProcessGroup.Create(options)withResourceLimits(memory / process count / CPU) enforced by a Windows Job Object or a Linux cgroup v2. - Stats & profiling —
ProcessGroup.Stats/SampleStatsandRunningProcess.Profile(CPU / peak memory). - Ergonomics —
CliClient(a program with shared defaults), top-levelExec.run/Exec.outputAll, and shared-group running (ProcessGroupis itself anIProcessRunner). - Observability — optional
Command.WithLoggerlifecycle events (argv/env never logged), andProcessKit.Extensions.DependencyInjection'sAddProcessKit. - Testable —
ProcessKit.Testing.ScriptedRunnerandRecordReplayRunner(record/replay cassettes) are subprocess-freeIProcessRunners for hermetic tests.
MIT