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.ProcessProcess output goes nowhere it isn't sent. By default, Process.toFlow retains all stdout and a 64 KiB
diagnostic tail of stderr; streaming retains 64 KiB tails on both channels. Neither forwards anything to the
console — that takes an explicit route.
| Execution boundary | Default forwarding | Stdout retention | Unmerged stderr retention |
|---|---|---|---|
toFlow, direct Process.tryRun / Process.run |
None | All | 64 KiB tail |
stream* constructors |
None | 64 KiB tail | 64 KiB tail |
Explicit settings override these defaults. Runners applied to an existing Flow preserve its configured policies.
Show page setup
// Setup for the checked examples on this page.
open Axial
open Axial.Console
open Axial.Process
open Axial.Process.DSL
/// 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 actuallet inspect () : ProcessResult =
cmd $"printf 'status: ok'" |> Process.run
inspect: unit -> ProcessResultAxial.Process.ProcessResultCompletion of the complete topology; ExitCode belongs to the final stage.
cmd: FormattableString -> ProcessCommand(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.Process.Processrun: ^source -> ^resultRuns with live services and returns the value; raises ProcessRunException on failure.
Requires & types
Requires: open Axial.Process; open Axial.Process.DSL (packages: Axial.Process)val inspect: unit -> ProcessResultlet result = inspect ()
shouldEqual "status: ok" result.StdOut
shouldEqual 10 result.StdOutCapture.Bytes.Length
result: ProcessResultinspect: unit -> ProcessResultshouldEqual: 'a -> 'a -> unitStdOut: stringLength: intGets the total number of elements in all the dimensions of the . The total number of elements in all the dimensions of the ; zero if there are no elements in the array. The array is multidimensional and contains more than elements.
StdOutCapture: CapturedOutputBytes: byte arrayRequires & types
Requires: open Axial.Process (packages: Axial.Process)val result: ProcessResultProcess.run returns ProcessResult. In that result, StdOutCapture and
StdErrCapture are CapturedOutput values: retained Bytes, decoded Text, Truncated, and Available.
Choose retention independently of routing
Retention and routing are separate choices. captureAll, captureTail bytes, and captureNothing affect final
effective stdout only; captureStderrAll, captureStderrTail bytes, and captureStderrNothing affect readable
unmerged stderr. Explicit capture settings override the execution mode and topology defaults.
stdoutToConsole, stdoutToFile, stdoutToAppendFile, stdoutToDiscard, and stdoutToInheritHandles
configure routes. Stderr has matching shortcuts. stdoutTo and stderrTo accept arbitrary targets, including
callbacks, sinks, and OutputTarget.Tee. A file or console destination does not disable capture.
let diagnose =
cmd $"printf diagnostics"
|> stdoutToConsole
|> captureTail 5
diagnose: ProcessCommandcmd: FormattableString -> ProcessCommand(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
stdoutToConsole: ProcessCommand -> ProcessCommandcaptureTail: int -> ^a -> 'bSame as Process.captureTail. Bounds final stdout retention in bytes.
Requires & types
Requires: open Axial.Process.DSL (packages: Axial.Process)val diagnose: ProcessCommandTo check console routing without writing to the terminal, supply a test console for this execution:
let consoleEnvironment = ProcessHostEnvironment.liveWithConsole (Console.test())
let diagnosed = diagnose |> Process.runWith consoleEnvironment
shouldEqual "stics" diagnosed.StdOut
shouldEqual true diagnosed.StdOutTruncated
consoleEnvironment: ProcessHostEnvironmentAxial.Process.ProcessHostEnvironmentModuleliveWithConsole: IConsole -> ProcessHostEnvironmentAxial.Console.ConsoleModuletest: unit -> TestConsoleCreates an in-memory console test double that captures output and reads scripted input.
diagnosed: ProcessResultdiagnose: ProcessCommand(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.Process.ProcessrunWith: 'env -> ^source -> ^resultRuns with supplied services and returns the value; raises ProcessRunException on failure.
shouldEqual: 'a -> 'a -> unitStdOut: stringStdOutTruncated: boolRequires & types
Requires: open Axial.Console; open Axial.Process (packages: Axial.Console, Axial.Process)val consoleEnvironment: ProcessHostEnvironmentval diagnosed: ProcessResultWhen the exit status is all you need, captureNothing drops stdout retention. It does not drop the stderr
diagnostic tail; apply captureStderrNothing for that too. Full capture holds the whole stream in memory, so a
tail or captureNothing is safer when output can be large.
Fail instead of truncating
maxOutputBytes stdout stderr sets independent total byte caps for each stage's original stdout and stderr.
Pass Some limit for a channel or None to leave it unlimited. The cap counts pipe bytes before framing,
forwarding, or capture, including intermediate stages. Exceeding it kills the process tree and returns
ProcessError.OutputTooLarge with the stage, original channel, limit, and command. Unlike captureTail,
this does not return a truncated success. Inherited handles cannot be counted.
let capped = cmd $"printf hello" |> maxOutputBytes (Some 5L) None |> Process.run
shouldEqual "hello" capped.StdOut
capped: ProcessResultcmd: FormattableString -> ProcessCommand(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
maxOutputBytes: int64 option -> int64 option -> ^a -> ProcessSpecSame as Process.maxOutputBytes.
SomeThe representation of "Value of type 'T" The input value. An option representing the value.
NoneThe representation of "No value"
Axial.Process.Processrun: ^source -> ^resultRuns with live services and returns the value; raises ProcessRunException on failure.
shouldEqual: 'a -> 'a -> unitStdOut: stringRequires & types
Requires: open Axial.Process; open Axial.Process.DSL (packages: Axial.Process)val capped: ProcessResulthello is exactly five bytes, so it fits the cap. A sixth byte exceeds it and returns OutputTooLarge with the
stage, channel, and limit:
let tooLarge = cmd $"printf 'hello!'" |> maxOutputBytes (Some 5L) None |> Process.tryRun
match tooLarge with
| Exit.Failure(Cause.Fail(ProcessError.OutputTooLarge failure)) ->
shouldEqual 0 failure.Stage
shouldEqual OutputChannel.StdOut failure.Channel
shouldEqual 5L failure.Limit
| other -> failwithf "Expected OutputTooLarge, got %A" other
tooLarge: Exit<ProcessResult,ProcessError>cmd: FormattableString -> ProcessCommand(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
maxOutputBytes: int64 option -> int64 option -> ^a -> ProcessSpecSame as Process.maxOutputBytes.
SomeThe representation of "Value of type 'T" The input value. An option representing the value.
NoneThe representation of "No value"
Axial.Process.ProcesstryRun: ^source -> ^resultRuns with live services and returns Exit, including typed failures and interruptions.
Axial.Exit`2Represents the final outcome of a workflow execution. The type of the success value. The type of the domain-specific failure value.
FailureThe workflow failed due to a specific cause.
Axial.Cause`1Represents the cause of a failed workflow. The type of the domain-specific failure value.
FailAn expected domain-specific failure.
Axial.Process.ProcessErrorExpected process errors. Runtime cancellation remains Cause.Interrupt.
OutputTooLargefailure: ProcessOutputFailureshouldEqual: 'a -> 'a -> unitStage: intAxial.Process.OutputChannelIdentifies one standard output channel.
StdOutChannel: OutputChannelLimit: int64other: Exit<ProcessResult,ProcessError>failwithf: Printf.StringFormat<'T,'Result> -> 'TPrint to a string buffer and raise an exception with the given result. Helper printers must return strings. The formatter. The formatted result. See Printf.failwithf (link: ) for examples.
Requires & types
Requires: open Axial; open Axial.Process; open Axial.Process.DSL (packages: Axial, Axial.Process)val tooLarge: Exit<ProcessResult,ProcessError>Exact bytes and availability
Capture is byte-based, and the text view is derived from those bytes. CapturedOutput.Bytes holds the exact
retained bytes; Text decodes them with the command's encoding. A byte tail can split a multibyte character,
producing replacement characters in the text view. Truncated reports omitted bytes. Available reports whether
Axial could read the channel at all.
Inherited handles go directly to the child and cannot be observed, captured, or included in a tee.
Piped intermediate channels cannot be independently captured or observed. Final stdout capture includes merged
stderr only after explicit mergeStderr. Aggregate stderr is bounded separately; stage captures expose the
individual diagnostics.
Flow callbacks run under the process cancellation token; native stream adapters pass it to
reads, writes, and flushes. They must cooperate with cancellation. Typed callback failures become
OutputFailed; defects remain defects. Native process I/O failures use IoFailed.
Adapter streams and text writers remain caller-owned. Output.textWriter
creates a fresh decoder per execution. On netstandard2.1, TextWriter operations lack token overloads.

