Repository navigation
JNode Serial Skill
OpenCode skill for persistent single-client serial console interaction with VirtualBox JNode VMs.
The jnode-serial skill provides agent-driven interaction with JNode's implicit serial console (SerialConsolePlugin) via a VirtualBox named pipe. It solves the VirtualBox single-client pipe stall problem by holding one persistent connection and multiplexing all commands through it.
| File | Purpose |
|---|---|
.opencode/skills/jnode-serial/SKILL.md |
Skill definition and usage instructions |
.opencode/skills/jnode-serial/scripts/serial_mux.py |
Persistent single-client proxy daemon |
.opencode/skills/jnode-serial/scripts/serial_cmd.py |
Agent CLI client (preferred) |
.opencode/skills/jnode-serial/scripts/jnode_agent_cmd.py |
Legacy one-shot script (compatibility) |
VirtualBox's named pipe server (/tmp/jnode.serial2) supports only one client at a time. The legacy pattern (one connection per command via jnode_agent_cmd.py) eventually wedges the pipe server: new attaches get an immediate FIN/EOF while the guest stays healthy. VBox.log shows NamedPipe0: only single connection supported. Only a power cycle used to clear it.
serial_mux.py holds one pipe client for the entire session, never disconnecting, so the stuck state is never entered. It:
- Connects to
/tmp/jnode.serial2once - Waits for
[JNODE_AGENT_READY]prompt - Accepts batches of commands from
serial_cmd.py - Sends commands and streams output back
- Recovers via runtime
changeuartmode2 replugif the link drops
If the pipe server wedges (VBox FINs new attaches), the mux resets it without a VM reboot:
vboxmanage controlvm "JNode" changeuartmode2 disconnected
sleep 1
vboxmanage controlvm "JNode" changeuartmode2 server /tmp/jnode.serial2Ctrl-C (ETX, 0x03) and Ctrl-Z (SUB, 0x1A) are delivered through the held connection and translated by the guest into keyboard events via RawKeyboardReader and AbstractConsole.enqueueKeyboardEvent(). This gives shell job control (background, interrupt) over serial just like on VGA.
S=.opencode/skills/jnode-serial/scripts
# Send multiple commands in a batch
python3 $S/serial_cmd.py "date" "echo hello" "pwd"
# Long-running commands (no silence timeout)
python3 $S/serial_cmd.py "javac /jnode/tmp/ox/Big.java"
# Mux management
python3 $S/serial_cmd.py --status
python3 $S/serial_cmd.py --stop
python3 $S/serial_cmd.py --restart
python3 $S/serial_cmd.py --interrupt
python3 $S/serial_cmd.py --suspendJNode's RedirectingInterpreter only handles <, >, | (no heredoc, no >>). Multi-line files are written via a single echo command with shell escape sequences (\n, \t, \r). The --write mode on serial_cmd.py handles escaping and line-boundary chunking for byte-exact large file writes.
- Single client: Only the mux may hold the pipe. No concurrent raw connections.
- No silence timeout: The mux waits for the prompt regardless of command duration.
-
No
&&: JNode shell does not support&&. Send multiple commands as separate arguments. -
State persists:
cdandclasspath --addsurvive across calls (same shell process). -
Implicit console:
SerialConsolePluginstarts automatically. Do NOT runserialconsolemanually.
-
Pipe recreation: If the socket is deleted while VM is running, VirtualBox does NOT recreate it. Reattach at runtime via
changeuartmode2. -
Plugin unload: Unloading
org.jnode.shell.command.driver.consoleviaplugin --unloadbreaks the serial console. Reload is broken; restart the VM. -
Thread.stop() limitation: Ctrl-C delivers the keystroke but
Thread.stop()is ineffective against timer-sleeping proclet workers (JVM-level issue).
- Serial-Port-Driver -- Serial port hardware driver and serial console architecture
- Boot-Log -- Serial debug output configuration
- Shell-Commands -- Command framework and job control
- GitHub-Actions-Workflows -- CI/CD automation that uses this skill