feat(sophia): wire OS pipes between SSH and kefka shell
Previously the SSH session handed the kefka shell a strings.NewReader
for stdin and a *term.Terminal for stdout/stderr. wazero only takes
its fast *os.File path when host stdio is genuinely *os.File; the
term-backed handles fell back to a treat-stdio-as-block-device path
that broke wasi-libc isatty detection inside python.wasm and qjs.wasm,
so REPLs would not start a real interactive loop.
Replace that with three os.Pipe() pairs threaded through interp.StdIO,
plus a goroutine that pumps SSH bytes into the shared stdin pipe with
software line discipline that a kernel PTY would normally do:
- ICRNL: translate Enter (\r on a raw SSH channel) to \n, so
line-buffered WASI readers like Python's fgets recognize line
boundaries. term.Terminal accepts either, so the prompt is
unaffected.
- ECHO: while a foreground command is active (gated by an
atomic.Bool flipped around sh.Run), echo typed bytes back to the
client so REPLs aren't typing blind. term.Terminal already
handles its own echo during the prompt.
stdout/stderr drain into the terminal via io.Copy goroutines so its
writeWithCRLF still does \n -> \r\n on the way back out.
Assisted-by: Claude Opus 4.7 via Claude Code
Signed-off-by: Xe Iaso <me@xeiaso.net>