Remote Shell and Serial
This page introduces the commands an agent executes: the shell command, the serial commands, how timeouts and long output behave, and what each side of the connection keeps.
The console
The studio's Agents page lists the connected agents and lets you pick one and send it a command from the browser. Pick the command from the list, fill in the arguments it takes, and the studio sends it to the agent and shows the result.
The form is generated from the command's contract, so the fields on screen are exactly the
arguments the agent reads: script and cwd for shell, path and baudRate for
open_serial, and nothing at all for get_system_info. A command that takes no arguments needs
only the Send button.
Each command produces its own result, which the studio stores against the command record.
Over the API the same call takes the agent id, a command name and an argument object:
curl -X POST http://localhost:3000/api/agents/command \
-H 'Content-Type: application/json' \
-d '{
"agentId": "<agent id>",
"command": "shell",
"args": { "script": "uname -a", "cwd": "/tmp" },
"timeout": 30000
}'The studio answers with a commandId. The result is read back with:
curl http://localhost:3000/api/agents/commands/<commandId>The studio refuses the request when the agent is not connected (Agent is offline) or not
active (Agent is not active), and answers Agent not found for an unknown id.
These are the commands that exist:
| Command | Arguments | Result |
|---|---|---|
shell | script, cwd, timeout | stdout, stderr, exitCode, durationMs |
get_system_info | β | A JSON string in output.stdout |
get_serial_ports | β | A JSON array in output.stdout |
open_serial | path, baudRate | Confirmation that the port is open |
close_serial | path | Confirmation that the port is closed |
serial_write | path, data | Whether the bytes were written |
Two more names belong to the command vocabulary β run_test_suite and upload_artifact. Their
contract is in Command Reference.
Identifying the machine
get_system_info takes a snapshot of the machine at the moment it runs β hostname, platform,
architecture, CPU count, memory, uptime, load average and network interfaces. The payload is a
JSON string inside output.stdout, not a structured object:
{ "output": { "stdout": "{\"hostname\":\"bench-01\",\"platform\":\"linux\",\"cpuCount\":16}" } }It is the quickest way to tell two benches apart, or to confirm that a command landed on the machine you expected.
Shell commands
shell
The agent writes the script to a file in the working directory, makes it executable and runs it
with /bin/bash:
<cwd>/.agent_script_<timestamp>.sh| Argument | Default | Notes |
|---|---|---|
script | β | Required. The script body. |
cwd | /tmp | Where the script file is written and run. |
timeout | 300000 ms | Enforced on the agent. The process is killed with SIGTERM. |
The exit code reports the outcome:
| Exit code | Meaning |
|---|---|
0 | The script succeeded. |
| Other | The script's own failure code. |
-1 | The process could not be spawned. |
-2 | The command timed out. stderr reads Command timed out after <ms>ms. |
The agent does not delete the temporary script after the run. Point cwd at a scratch directory
if it matters what is left on the machine.
A shell script has no access to the agent's serial manager, so it cannot open a port the agent
already owns. Use the serial commands for hardware and shell for programs on the machine.
Serial commands
The agent owns the serial ports. It tracks which devices are attached, reports when one is plugged in or unplugged, and can open a port at a given baud rate.
get_serial_ports
Lists the ports on the machine. The payload is a JSON array in output.stdout:
[
{ "path": "/dev/ttyUSB0", "manufacturer": "FTDI", "serialNumber": "FT9ABCDE", "vendorId": "0403", "productId": "6001" }
]If the serial port bindings are not installed on the machine, the list is empty.
The agent also watches for hot-plug events and emits a device_event message over the socket when
a device appears or disappears. The studio records those events; the agent's own page shows them
live.
open_serial
| Argument | Default | Notes |
|---|---|---|
path | β | Required. The port path, for example /dev/ttyUSB0. |
baudRate | 115200 | The baud rate to open at. |
The socket command path passes path and baudRate to the serial library and nothing else.
Settings such as dataBits, stopBits and parity are accepted in the argument object but
are ignored here β they exist only on the agent's own local serial endpoint. The port is
therefore opened with the library's defaults for those settings: 8 data bits, 1 stop bit, no
parity.
serial_write
| Argument | Default | Notes |
|---|---|---|
path | β | Required. The port to write to. |
data | β | Required. The bytes to write, as a string. |
The write is refused if the port is not open or not writable.
close_serial
Closes the port. A port closed by unplugging the device is closed by the agent automatically.
Timeouts and long output
- The
timeoutargument is enforced on the agent, not in the studio. On expiry the process is killed withSIGTERMand the exit code is-2. - A timed-out script may leave child processes running:
SIGTERMgoes to the script, not to its whole process group, so work the script backgrounded can keep running. - The result stores the output as text, with no size cap enforced by the agent and no truncation flag in the result. Keep scripts quiet and write bulk output to a file instead.
- Creating a command returns immediately with a
commandId, so a long command does not hold the request open. Read the result separately.
The agent's local bench
The agent's own page β http://localhost:3001 on the machine under test β carries a local
command test panel. It carries the same command list and the same argument form, but the command
runs on the machine itself, with the studio out of the loop, and the result is printed under the
form.
It is the bench-side half of the same implementation the studio reaches over the socket: the command that runs locally is the command that runs remotely, so a failure can be reproduced on the machine in seconds without a command record, a session or a connection.
The same run is available over the agent's own API:
curl -X POST http://localhost:3001/api/agent/commands/execute \
-H 'Content-Type: application/json' \
-d '{
"command": "get_system_info",
"args": {}
}'The answer is the command's own result envelope β { "ok": true, "data": { β¦ } } on success and
{ "ok": false, "error": { "code": β¦, "message": β¦ } } on failure.
Safety notes
- A shell script runs with the agent user's own rights. It can do anything that user can do: read and write files, install packages, change the network configuration, reach other machines on the network. Treat every script as arbitrary code execution on the bench.
- Scripts execute in the working directory you choose. There is no sandbox, no chroot and no
resource limit beyond the timeout, and the script file is written with mode
0755and left in place, so anything the script wrote there stays readable by the agent user. - The agent host needs
bashat/bin/bashand Node withtsx. Serial commands additionally need theserialportnative bindings; without themget_serial_portsreturns an empty list. - Serial commands act on a path. A path that does not exist, or a port held by another process, fails at open time β nothing is written to the wrong device, but check the path before opening.
What each side keeps
- The serial byte stream stays on the agent. The studio can open a port and write to it, but
the bytes coming back are delivered to the agent, not to the studio's browser. The
interactive serial console lives on the agent's own page at
http://localhost:3001on the machine under test, where the port's output is streamed live. Use the studio for commands and the agent's page for a console. - The studio keeps the serial metadata: path, manufacturer, serial number, vendor id and product id, recorded as a device event alongside the command record.
- The studio keeps the command record: the command type, its arguments, its status, and a
result holding
stdout,stderr,exitCodeanddurationMs. - The commands are independent of one another. A typical sequence over one agent is
get_system_info,get_serial_ports,open_serial,serial_write,close_serial.
See also
- Command Reference β every argument, every result field.
- Socket Protocol β how the commands travel.
- Your First Remote Command β a walkthrough.
- The Test Case Model β how these commands become the steps of a case.