DK Studio/Documentation
ProductChangelog

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

ArgumentDefaultMeaning
scriptβ€”The shell script to run. Required.
cwd/tmpWhere 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>.sh

It 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 codeMeaning
0The script succeeded.
otherThe script's own failure code.
-1The process could not be spawned at all.
-2The 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

CommandArgumentsWhat 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_serialpath, baudRate (default 115200)Opens the port on the agent.
close_serialpathCloses it again.
serial_writepath, dataWrites 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 agent must be online

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