Your First Remote Command
With an agent online, you run commands on a machine across the room from your browser. You pick a command in the studio and fill in its arguments, and the output of the machine it ran on comes back into the same panel.
The simplest command is shell: the studio sends a script, the agent runs it with bash on the
machine that owns the hardware, and the output returns.
Run a command from the studio
Open the studio, go to the Agents page and select an online agent. The command panel carries the whole command vocabulary: pick the command from the list, fill in the arguments it takes, and press Send.
The form is generated from the command's contract, so the fields on screen are exactly the
arguments the agent reads. shell asks for the script and the working directory; open_serial
asks for a port path and a baud rate; get_system_info asks for nothing at all.
The panel shows the result as it lands: the standard output, the standard error, the exit code and how long the script took.
Run it on the bench itself
The agent's own page carries a local command test panel: the same command list and the same argument form, but the command runs on the machine under test, without the studio in the loop. It is the quickest way to reproduce a failure on the bench.
Try it
A script that reports the machine's identity:
#!/bin/bash
echo "hostname: $(hostname)"
echo "kernel: $(uname -sr)"
echo "cwd: $(pwd)"You should see the three lines in the result's output, with exit code 0.
The shell command
| Argument | Default | Meaning |
|---|---|---|
script | β | The shell script to run. Required. |
cwd | /tmp | Where the script is written and executed. |
The timeout is not an argument: it travels beside args, as the request's own timeout, and
defaults to 300000 ms.
The studio stores the command as pending, hands it to the agent, and the agent reports back. The
command moves through three states:
pending βββΆ in_progress βββΆ completed
pending means the studio accepted it; in_progress means the agent claimed it; completed means
the result is stored. The result holds the standard output, the standard error, the exit code and
the duration, and its status is success, error or timeout.
What the agent actually does
The agent does not pipe your script into a shell. It writes the script to a file in the working directory, makes it executable, and spawns it:
<cwd>/.agent_script_<timestamp>.shIt runs /bin/bash on that file with the working directory set, then streams stdout and stderr
back. The script file is left behind in the working directory after the run β worth knowing if you
point cwd at a directory you care about.
The exit code tells you what happened:
| Exit code | Meaning |
|---|---|
0 | The script succeeded. |
| other | The script's own failure code. |
-1 | The process could not be spawned at all. |
-2 | The command hit its timeout and was killed. |
On timeout the agent sends SIGTERM, writes Command timed out after <ms>ms to stderr, and reports
exit code -2. The timeout is enforced on the agent, so a hung script holds that agent's
command queue until the agent's own timeout fires.
The other commands
| Command | Arguments | What you get back |
|---|---|---|
get_system_info | β | The machine's hostname, platform, arch, cpuCount, totalMemory, freeMemory, uptime, loadAverage and networkInterfaces, as a JSON string in output.stdout. |
get_serial_ports | β | A JSON array of {path, manufacturer, serialNumber, vendorId, productId}. |
open_serial | path, baudRate (default 115200) | Opens the port on the agent. |
close_serial | path | Closes it again. |
serial_write | path, data | Writes bytes to an open port. |
Serial ports are opened, written and closed on the agent itself; the studio records the port metadata β path, manufacturer, serial number, vendor id and product id β as it arrives.
The two delivery paths
Commands reach the agent by one of two routes, and both write to the same table:
Push β the fast path. The studio emits a command request over the Socket.IO connection with the
command, its args and the timeout. The agent executes it immediately and answers with a
command response over the same socket. This is what the web UI uses, and it is why a command
usually returns within a second.
Poll β the fallback. If the socket is down, the agent polls GET /api/agent/commands every five
seconds. Commands it finds flip from pending to in_progress, and the agent posts the outcome to
POST /api/agent/commands/result.
The observable difference is latency, not behaviour: the command history and the result look the same either way.
Doing it over the API
The same thing from a script:
curl -X POST http://localhost:3000/api/agents/command \
-H 'Content-Type: application/json' \
-H 'Cookie: session=<your session cookie>' \
-d '{
"agentId": "<agent id>",
"command": "shell",
"args": {
"script": "uname -a",
"cwd": "/tmp"
},
"timeout": 30000
}'The response carries the commandId; poll GET /api/agents/commands/<commandId> for the result.
The studio refuses to queue a command for an agent that has no live socket β it answers
Agent is offline. The database row is only written for an agent that is connected.
Next steps
- Remote Shell and Serial β the full command set, including system info and serial port control.
- Command Reference β every command, its arguments and its result.