WHY: an unlicensed CODESYS Control runtime drops out of demo mode every 2
hours. systemctl is-active reports "active" even after the binary has
died, so a TCP probe on the runtime port (default 11740) is the only
honest liveness signal. The new tool gives MCP a one-call path to bring
the runtime back without dropping into a terminal.
Implementation choices:
- ssh2 (npm) instead of spawning ssh/sshpass: sshpass is not on the
default Windows path, and the target Pi's sshd 10.x rejects pubkey
signatures from this client environment in practice. ssh2 handles
password auth + remote stdin + exit-code capture cross-platform.
- sudo -S with the password fed on remote stdin -- avoids a NOPASSWD
sudoers entry on the PLC.
- After issuing the restart, polls 'ss -tln | grep :<port>' once per
second until the listen port is up or livenessWaitSeconds expires.
This is what catches a half-dead runtime that systemctl reports as
fine.
Defaults match the only Pi we currently target (codesys-pi.local /
karstein / codesys123 / codesyscontrol / port 11740) but every field
is overridable.
Smoke-tested against codesys-pi.local: restart exit 0, port back up
after ~3s.
v0.3.0 returning-watcher architecture — background thread polls for commands
and marshals execution onto the CODESYS UI thread, keeping the IDE fully
responsive between operations. File-based IPC with atomic writes, async mutex
command serialization, headless fallback, and 35 passing tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>