DaytonaDocs
Working with Sandboxes

Running Commands

Execute commands and code in a sandbox, and manage long-running sessions.

Every sandbox exposes a process API through sandbox.process, giving you three ways to run things: one-shot shell commands, direct code execution in a configured language runtime, and long-lived sessions that keep shell state between commands. One-shot execution is the right default for independent commands; sessions are the tool when commands need to build on each other — an install followed by a run, a background server you interact with, or anything that depends on a working directory or environment set up earlier.

One-shot execution

exec (Python; execute_command is an alias) / executeCommand (TypeScript) runs a single shell command and returns its exit code and output. You can set the working directory, extra environment variables, and a timeout per call. Each invocation is independent — no state carries over to the next.

Python
response = sandbox.process.exec(
    "python --version",
    cwd="/workspace",
    env={"PYTHONUNBUFFERED": "1"},
    timeout=30,
)
print(response.exit_code)   # 0
print(response.result)      # "Python 3.12.4"
TypeScript
const response = await sandbox.process.executeCommand(
  'python --version',
  '/workspace',                  // cwd
  { PYTHONUNBUFFERED: '1' },     // env
  30,                            // timeout
)
console.log(response.exitCode)   // 0
console.log(response.result)     // "Python 3.12.4"

Running code directly

code_run / codeRun executes a source string in the sandbox's configured language runtime, without you writing the file-and-invoke boilerplate. The language comes from the sandbox's code-toolbox-language label, set at create time; calling code_run on a sandbox without that label raises an error.

Python
from rlp import CodeRunParams, CreateSandboxFromImageParams, Daytona
 
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromImageParams(
    image="python:3.12-slim",
    labels={"code-toolbox-language": "python"},
))
 
response = sandbox.process.code_run(
    "import sys; print(sys.argv[1])",
    params=CodeRunParams(argv=["hello"], env={"DEBUG": "1"}),
)
print(response.result)   # "hello"
TypeScript
const response = await sandbox.process.codeRun(
  'import sys; print(sys.argv[1])',
  { argv: ['hello'], env: { DEBUG: '1' } },
)
console.log(response.result)   // "hello"

Sessions: long-lived shells

A session is a persistent shell inside the sandbox. State accumulates across the commands you send to it — the working directory, environment variables, and background processes you start all persist until the session is deleted. That makes sessions the natural fit for multi-step workflows where one-shot exec calls would each start from scratch.

You create a session under an id you choose, then execute commands in it:

Python
from rlp import SessionExecuteRequest
 
sandbox.process.create_session("build")
 
# State persists between commands: cd + env survive into the next call.
sandbox.process.execute_session_command(
    "build", SessionExecuteRequest(command="cd /workspace && export STAGE=ci"))
result = sandbox.process.execute_session_command(
    "build", SessionExecuteRequest(command="pwd && echo $STAGE"))
print(result["output"])   # /workspace \n ci
 
sandbox.process.delete_session("build")
TypeScript
await sandbox.process.createSession('build')
 
await sandbox.process.executeSessionCommand('build', {
  command: 'cd /workspace && export STAGE=ci',
})
const result = await sandbox.process.executeSessionCommand('build', {
  command: 'pwd && echo $STAGE',
})
console.log(result.output)   // /workspace \n ci
 
await sandbox.process.deleteSession('build')

Async commands and logs

Passing run_async: true / runAsync: true returns immediately with a command id instead of waiting for completion — the way to launch servers or long jobs. You can then poll the command's status, fetch its accumulated logs, or send it input:

Python
from rlp import SessionExecuteRequest
 
sandbox.process.create_session("server")
started = sandbox.process.execute_session_command(
    "server",
    SessionExecuteRequest(command="python -m http.server 8000", run_async=True),
)
cmd_id = started["cmdId"]
 
# Later: check on it and read its output so far.
info = sandbox.process.get_session_command("server", cmd_id)
logs = sandbox.process.get_session_command_logs("server", cmd_id)
print(logs["output"])
 
# Interactive commands can receive stdin:
sandbox.process.send_session_command_input("server", cmd_id, "y\n")
TypeScript
await sandbox.process.createSession('server')
const started = await sandbox.process.executeSessionCommand('server', {
  command: 'python -m http.server 8000',
  runAsync: true,
})
 
const info = await sandbox.process.getSessionCommand('server', started.cmdId)
const logs = await sandbox.process.getSessionCommandLogs('server', started.cmdId)
console.log(logs.output)
 
await sandbox.process.sendSessionCommandInput('server', started.cmdId, 'y\n')

Managing sessions

list_sessions() / listSessions() returns all active sessions, get_session(id) / getSession(id) returns one with its command history, and delete_session(id) / deleteSession(id) tears one down along with anything still running in it.

Note: a common pattern is one session per logical task — install deps in it, then run in the same session so the environment carries over — and a separate session per background server so its lifetime is explicit.

See also

  • Working with Files — get code and data in and out of the sandbox.
  • SSH Access — an interactive shell when you want a terminal instead of an API.

On this page