Krikri::PluginHelpers::PtyCommandRunner
Runs a command with its stdin/stdout/stderr attached to a REAL pty allocated on this host, capturing the merged session output from the master side.
Why this exists: real ansible runs a remote raw: through ssh -tt
(ssh.py adds -tt to every command that sends no stdin, and raw's
action plugin always qualifies), so the remote command's stdout AND
stderr arrive on one pty in execution order. The stream merge itself
is already emulated (ScriptPtyCapture.pty_merged_command's
{ ...; } 2>&1 group), but a real pty also changes how
isatty-sensitive programs BEHAVE, and no post-hoc output shaping can
reproduce that: apt emits its colored progress meter and suppresses
its "does not have a stable CLI interface" warning only when stdout
is a tty (issue #775 - dustinrue.xenial-bootstrap's
raw: ... apt -y update ... captured the progress bar under real and
the warning + plain lines under the pipe emulation). The program has
to actually see a tty.
The plugin process runs on the target host (remote dispatch injects ansible_connection=local), so the pty is allocated right here, the same place real's pty lives - nothing about the transport changes and the JSON result still travels back over clean pipes.
Termios stays at the kernel defaults, which is what real's sshd pty
gives the command too: OPOST|ONLCR on (every \n the command writes
becomes \r\n on the capture, exactly matching the live-verified
ansible capture), ECHO on (nothing is ever written to the master, so
nothing can be echoed back). The child's stdin is the slave, like
real's ssh -tt session - a raw command that reads stdin blocks
there under real ansible too (the master stays open for the whole
session).
Class methods
Cached once per process (the resident remote daemon serves many tasks): a /dev/ptmx-less target (hardened containers) keeps the plain-pipe capture for every raw task instead of re-probing per task. Probing allocates and immediately closes one pty.
Returns nil when no pty could be allocated - always BEFORE anything is spawned, so the caller can fall back to its plain-pipe capture without ever double-executing a side-effectful command. Once the child is spawned a real result always comes back.