Leak-free runner backed by forcola.
Every codex invocation runs under forcola's Rust shim, which places
the CLI in its own process group and kills the whole group (SIGTERM,
then SIGKILL) on timeout or when the BEAM dies. That reaps codex and
every stdio MCP server it spawned together, where the default
CodexWrapper.Runner.Port would leave them running as orphans (see
#48 and closed #33).
forcola closes the child's stdin immediately after spawn (forcola
0.3.4, forcola#67), so codex sees EOF naturally -- the /bin/sh ... < /dev/null wrapper the default runner needs is unnecessary
here. Before 0.3.4 forcola left the child's stdin open, and codex exec would print "Reading additional input from stdin..." and hang
until the timeout elapsed. The ~> 0.6.0 dependency floor includes
this fix and raw output observation for one-shot execution.
forcola requires a finite whole-run bound. Synchronous commands with
no :timeout use forcola_default_timeout_ms (five minutes by
default); streams use forcola_default_stream_timeout_ms (one hour
by default), so a long active turn is not cut off after five minutes.
stream_lines/4 is backed by Forcola.Stream.lines/2, so the
NDJSON paths (Exec.stream/2 and friends) get the same group kill
on halt, timeout, or BEAM death. Unlike Forcola.Stream.lines/2,
it does not raise on a non-zero exit -- it ends the stream, which is
what CodexWrapper.Runner.Port has always done and what the
CodexWrapper.Runner contract specifies.
This module compiles only when forcola is a dependency. Select it
with config :codex_wrapper, runner: CodexWrapper.Runner.Forcola
(or the :forcola shorthand). forcola is POSIX-only.
Summary
Functions
The bound forcola will enforce for timeout.
Observe raw output while preserving the terminal one-shot outcome.
Functions
The bound forcola will enforce for timeout.
A caller's nil ("no timeout") becomes
config :codex_wrapper, forcola_default_timeout_ms: <ms>
(default 300000), since forcola requires a finite bound.
@spec run_observed( String.t(), [String.t()], keyword(), timeout() | nil, {pid(), reference()} ) :: {:ok, {binary(), non_neg_integer(), binary()}} | {:error, CodexWrapper.Runner.error()}
Observe raw output while preserving the terminal one-shot outcome.
Requires Forcola's :output_observer support. The execution caller
receives raw chunks from its owned helper before this function returns.
Stderr stays separate.