DK Studio/Documentation
ProductChangelog

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:

CommandArgumentsResult
shellscript, cwd, timeoutstdout, stderr, exitCode, durationMs
get_system_infoβ€”A JSON string in output.stdout
get_serial_portsβ€”A JSON array in output.stdout
open_serialpath, baudRateConfirmation that the port is open
close_serialpathConfirmation that the port is closed
serial_writepath, dataWhether 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
ArgumentDefaultNotes
scriptβ€”Required. The script body.
cwd/tmpWhere the script file is written and run.
timeout300000 msEnforced on the agent. The process is killed with SIGTERM.

The exit code reports the outcome:

Exit codeMeaning
0The script succeeded.
OtherThe script's own failure code.
-1The process could not be spawned.
-2The command timed out. stderr reads Command timed out after <ms>ms.
The script file is left behind

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

ArgumentDefaultNotes
pathβ€”Required. The port path, for example /dev/ttyUSB0.
baudRate115200The baud rate to open at.
Only path and baudRate reach the agent

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

ArgumentDefaultNotes
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 timeout argument is enforced on the agent, not in the studio. On expiry the process is killed with SIGTERM and the exit code is -2.
  • A timed-out script may leave child processes running: SIGTERM goes 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 0755 and left in place, so anything the script wrote there stays readable by the agent user.
  • The agent host needs bash at /bin/bash and Node with tsx. Serial commands additionally need the serialport native bindings; without them get_serial_ports returns 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:3001 on 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, exitCode and durationMs.
  • 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