TCPAGENT는 system()이 도는 동안 통째로 얼어 있어 응답도 진행 보고도 못 한다. 그런데 호스트는 소켓에 30초 고정 타임아웃을 걸고, 만료되면 연결 자체를 버렸다. 그래서 35초짜리 컴파일이 "느린 명령"이 아니라 "죽은 에이전트"로 취급됐다. QEMU는 게스트가 얼어 있어도 계속 돈다. info blockstats의 idle_time_ns로 "작업 중"과 "멈춤"을 구분한다. 실측으로 확인했다: 에이전트가 완전히 벙어리인 동안에도 rd_operations가 7초당 47000씩 증가하고 idle은 0.00s를 유지한다. - EXEC은 짧은 간격으로 깨어나 감시만 하고 소켓은 절대 안 버린다. - --idle-timeout(기본 60s)과 --hard-timeout(기본 900s). 후자는 디스크를 안 쓰는 CPU 바운드 멈춤용 백스톱이다. - 중단은 QEMU 모니터로 Ctrl+C를 주입하고 COMMAND.COM의 "Terminate batch file (Y/N/A)?" 프롬프트에 답한다. - Ctrl+C는 DOS break check에서만 먹는다. FreeDOS 기본값 BREAK=OFF에서 출력을 파일로 돌린 CPU 바운드 자식은 거기 도달 안 할 수 있다. 그래서 중단은 보장이 아니라 요청으로 다루고, 명령이 안 멈춰도 RESULT를 끝까지 수거해 스트림을 깨뜨리지 않는다. - ferro-vm abort 추가. 실행 중에도 응답해야 하므로 파이프 서버를 요청당 스레드로 바꿨다. - 5558 바인딩을 SO_EXCLUSIVEADDRUSE로. Windows의 SO_REUSEADDR는 다른 프로세스가 같은 포트를 잡아 조용히 반쯤 동작하게 만든다. 검증 (QEMU FreeDOS 실측): - 32.4초 명령 정상 완료 (이전에는 30초에 실패) - 실행 중 abort가 0.1초에 응답, exit=95로 종료, 부분 출력 1805B 수거, 연결 유지 - pause처럼 디스크를 안 쓰는 명령을 idle 15s로 검출해 중단 시리얼 시절에 있다가 TCP 전환에서 사라진 TODO 3건을 복구한다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PQm6oAvWX4Lp3iSN5AHGT
Development tools
Host support
Automation currently supports Windows 10/11 only. It requires uv, QEMU
with WHPX support, and ffmpeg.exe on PATH. The Python implementation uses
portable APIs where possible, but other hosts are not supported yet.
Getting started
uv run ferro-vm start
uv run ferro-vm status
The command list lives in the CLI itself, not in this file:
uv run ferro-vm --help
uv run ferro-vm <command> --help
Working rules, verification gates, and DOS build traps are in AGENTS.md.
How it fits together
TCPAGENT.EXE runs inside FreeDOS and dials out to 127.0.0.1:5558; its wire
protocol is documented in tcpagent/README.md. The ferro-vm daemon owns that
connection and the QEMU monitor. Local commands reach the daemon over the
Windows named pipe \\.\pipe\ferrolang-vm — there is no controller or observer
TCP port.
The daemon writes an append-only structured log (uv run ferro-vm logs, which
uses lnav when installed and otherwise falls back to PowerShell Get-Content -Wait). It records command metadata, DOS output, exit status, transfers, and
agent lifecycle events as UTF-8 lines, and deliberately never logs raw binary
payloads or protocol hex.
reset quits QEMU cleanly, restarts it, waits for FreeDOS to boot, submits the
default boot-menu Enter, and requires a TCPAGENT PING/PONG before returning.
QEMU system_reset is intentionally unsupported: repeated soft resets leave the
FreeDOS NE2000 packet driver stuck during initialization.
Standalone OCR
tools/qemu_ocr.py remains available for OCRing an existing image:
uv run python tools/qemu_ocr.py --image .qemu/qemu-screen.png