Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Console
open Axial.FileSystem
open Axial.PlatformService
open Axial.Process

Processes

Axial.Process lets a command-line program start an external command without giving up typed failures, cancellation, or cleanup. You describe the command first, choose what happens to its input and output, then run it.

A script

For a small command-line program, describe commands, connect them, choose where output goes, then call run:

Show page setup
// Setup for the checked examples on this page.
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Console
open Axial.FileSystem
open Axial.PlatformService
open Axial.Process
/// Fails the docs test when an example's result differs from the value shown.
let shouldEqual expected actual =
    if actual <> expected then failwithf "Expected %A but got %A" expected actual
Verified
// The examples on this page start real processes with the live service.
let host : ProcessHostEnvironment =
    { Process = Process.live Clock.live FileSystem.live Console.live
      Console = Console.live }

let stdoutOf (workflow: Flow<ProcessHostEnvironment, ProcessError, ProcessResult>) : string =
    match Flow.run host workflow with
    | Exit.Success result -> result.StdOut
    | Exit.Failure cause -> failwithf "%A" cause
Verified
open Axial.Process.DSL // Short pipeline helpers: cwd, env, timeout, =>, capture, run

let recentCommits (repository: string) =
    cmd $"git log --oneline -20"
    |> cwd repository
    |> env "NO_COLOR" "1"
    |> timeout (TimeSpan.FromSeconds 5.0)
    => cmd $"head -5"
    |> capture

// In a script, finish with `|> run`: it runs the workflow with live services and returns an exit code.
let script () = recentCommits "." |> run
Verified
let lines = stdoutOf (recentCommits ".") |> _.Split('\n', StringSplitOptions.RemoveEmptyEntries)
(lines.Length >= 1 && lines.Length <= 5) |> shouldEqual true

Process.command (and DSL cmd) preserves each interpolation hole as one native argument. cwd, env, and timeout return updated command specifications. => connects stdout to the next command's stdin. capture creates a Flow that retains stdout and stderr; none of those steps starts a child process. The final run starts the Flow with live clock, filesystem, console, and process services. It returns 0 on success, or writes a redacted error and returns an appropriate nonzero exit code. Use it at a .NET command-line application's outermost boundary.

For a server, worker, or application Flow, see run a process in an application Flow. That guide explains the explicit process capability, live wiring, test fakes, and the difference between Process.toFlow and Flow.run.

Choose how the command communicates

Need DSL Full API What you receive
Inspect output after the command exits capture Process.capture ProcessResult: stdout, stderr, bytes, exit codes, and timing
Show output as it arrives console Process.console Structured completion data
React to output before completion stream Process.stream A backpressured stream of output and completion events
Send final output elsewhere writeTo / appendTo Process.stdout / Process.stderr A configured command

New process specifications capture stdout and stderr by default. capture makes that choice explicit and replaces a prior output policy. Output capture and destinations explains bounded capture, files, console forwarding, and tees. Streaming output covers long-running commands and incremental handling.

What Axial owns

The process service starts the native topology; Axial owns its lifetime. A timeout or interruption terminates every started stage, including partially started pipelines, before the enclosing Flow finishes. Expected process problems remain ProcessError values instead of becoming unstructured exceptions.

Guides