CodexWrapper.Runner.Forcola (CodexWrapper v0.4.0)

Copy Markdown View Source

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 runs the child one-shot with no stdin writer, so codex sees EOF naturally -- the /bin/sh ... < /dev/null wrapper the default runner needs is unnecessary here.

forcola requires a finite whole-run bound, so a command with no :timeout runs under forcola_default_timeout_ms instead of unbounded. See effective_timeout/1.

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.

Functions

effective_timeout(timeout)

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.