Skip to content

JNode Serial Skill

opencode-agent[bot] edited this page Sep 13, 2026 · 1 revision

JNode Serial Skill

OpenCode skill for persistent single-client serial console interaction with VirtualBox JNode VMs.

Overview

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.

Key Components

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)

How It Works

The Pipe Stall Problem

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.

The Mux Solution

serial_mux.py holds one pipe client for the entire session, never disconnecting, so the stuck state is never entered. It:

  1. Connects to /tmp/jnode.serial2 once
  2. Waits for [JNODE_AGENT_READY] prompt
  3. Accepts batches of commands from serial_cmd.py
  4. Sends commands and streams output back
  5. Recovers via runtime changeuartmode2 replug if the link drops

Runtime Replug Recovery

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.serial2

Job Control

Ctrl-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.

Usage

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 --suspend

Multi-Line File Writes

JNode'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.

Key Constraints

  • 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: cd and classpath --add survive across calls (same shell process).
  • Implicit console: SerialConsolePlugin starts automatically. Do NOT run serialconsole manually.

Gotchas

  • 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.console via plugin --unload breaks 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).

Related Pages

Clone this wiki locally