Class: Daytona::Process

Inherits:
Object
  • Object
show all
Includes:
Instrumentation
Defined in:
lib/daytona/process.rb

Overview

rubocop:disable Metrics/ClassLength

Defined Under Namespace

Modules: ArtifactType

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Instrumentation

included

Constructor Details

#initialize(code_toolbox:, sandbox_id:, toolbox_api:, get_preview_link:, otel_state: nil) ⇒ Process

Initialize a new Process instance

Parameters:



30
31
32
33
34
35
36
# File 'lib/daytona/process.rb', line 30

def initialize(code_toolbox:, sandbox_id:, toolbox_api:, get_preview_link:, otel_state: nil)
  @code_toolbox = code_toolbox
  @sandbox_id = sandbox_id
  @toolbox_api = toolbox_api
  @get_preview_link = get_preview_link
  @otel_state = otel_state
end

Instance Attribute Details

#code_toolboxDaytona::SandboxPythonCodeToolbox, (readonly)

Returns Daytona::SandboxPythonCodeToolbox,.

Returns:



12
13
14
# File 'lib/daytona/process.rb', line 12

def code_toolbox
  @code_toolbox
end

Returns Function to get preview link for a port.

Returns:

  • (Proc)

    Function to get preview link for a port



21
22
23
# File 'lib/daytona/process.rb', line 21

def get_preview_link
  @get_preview_link
end

#sandbox_idString (readonly)

Returns The ID of the Sandbox.

Returns:

  • (String)

    The ID of the Sandbox



15
16
17
# File 'lib/daytona/process.rb', line 15

def sandbox_id
  @sandbox_id
end

#toolbox_apiDaytonaToolboxApiClient::ProcessApi (readonly)

Returns API client for Sandbox operations.

Returns:

  • (DaytonaToolboxApiClient::ProcessApi)

    API client for Sandbox operations



18
19
20
# File 'lib/daytona/process.rb', line 18

def toolbox_api
  @toolbox_api
end

Instance Method Details

#code_run(code:, params: nil, timeout: nil) ⇒ ExecuteResponse

Execute code in the Sandbox using the appropriate language runtime

Examples:

# Run Python code
response = sandbox.process.code_run(<<~CODE)
  x = 10
  y = 20
  print(f"Sum: {x + y}")
CODE
puts response.artifacts.stdout  # Prints: Sum: 30

Parameters:

  • code (String)

    Code to execute

  • params (CodeRunParams, nil) (defaults to: nil)

    Parameters for code execution

  • timeout (Integer, nil) (defaults to: nil)

    Maximum time in seconds to wait for the code to complete. 0 means wait indefinitely

Returns:

  • (ExecuteResponse)

    Code execution result containing exit_code, result, and artifacts



98
99
100
# File 'lib/daytona/process.rb', line 98

def code_run(code:, params: nil, timeout: nil)
  exec(command: code_toolbox.get_run_command(code, params), env: params&.env, timeout:)
end

#connect_pty_session(session_id) ⇒ PtyHandle

Connects to an existing PTY session in the Sandbox.

Establishes a WebSocket connection to an existing PTY session, allowing you to interact with a previously created terminal session.

Examples:

# Connect to an existing PTY session
pty_handle = sandbox.process.connect_pty_session("my-pty-session")
pty_handle.wait_for_connection
pty_handle.send_input("echo 'Hello World'\n")
result = pty_handle.wait
pty_handle.disconnect

Parameters:

  • session_id (String)

    Unique identifier of the PTY session to connect to.

Returns:

  • (PtyHandle)

    Handle for managing the connected PTY session.

Raises:



449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/daytona/process.rb', line 449

def connect_pty_session(session_id) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  preview_link = get_preview_link.call(WS_PORT)
  url = URI.parse(preview_link.url)
  url.scheme = url.scheme == 'https' ? 'wss' : 'ws'
  url.path = "/process/pty/#{session_id}/connect"

  PtyHandle.new(
    WebSocket::Client::Simple.connect(
      url.to_s,
      headers: toolbox_api.api_client.default_headers.dup.merge(
        'X-Daytona-Preview-Token' => preview_link.token
      )
    ),
    session_id:,

    handle_resize: ->(pty_size) { resize_pty_session(session_id, pty_size) },
    handle_kill: -> { delete_pty_session(session_id) }
  ).tap(&:wait_for_connection)
end

#create_pty_session(id:, cwd: nil, envs: nil, pty_size: nil) ⇒ PtyHandle

Creates a new PTY (pseudo-terminal) session in the Sandbox.

Creates an interactive terminal session that can execute commands and handle user input. The PTY session behaves like a real terminal, supporting features like command history.

Examples:

# Create a basic PTY session
pty_handle = sandbox.process.create_pty_session(id: "my-pty")

# Create a PTY session with specific size and environment
pty_size = Daytona::PtySize.new(rows: 30, cols: 120)
pty_handle = sandbox.process.create_pty_session(
  id: "my-pty",
  cwd: "/workspace",
  envs: {"NODE_ENV" => "development"},
  pty_size: pty_size
)

# Use the PTY session
pty_handle.wait_for_connection
pty_handle.send_input("ls -la\n")
result = pty_handle.wait
pty_handle.disconnect

Parameters:

  • id (String)

    Unique identifier for the PTY session. Must be unique within the Sandbox.

  • cwd (String, nil) (defaults to: nil)

    Working directory for the PTY session. Defaults to the sandbox’s working directory.

  • envs (Hash<String, String>, nil) (defaults to: nil)

    Environment variables to set in the PTY session. These will be merged with the Sandbox’s default environment variables.

  • pty_size (PtySize, nil) (defaults to: nil)

    Terminal size configuration. Defaults to 80x24 if not specified.

Returns:

  • (PtyHandle)

    Handle for managing the created PTY session. Use this to send input, receive output, resize the terminal, and manage the session lifecycle.

Raises:

  • (Daytona::Sdk::Error)

    If the PTY session creation fails or the session ID is already in use.



417
418
419
420
421
422
423
424
425
426
427
428
429
430
# File 'lib/daytona/process.rb', line 417

def create_pty_session(id:, cwd: nil, envs: nil, pty_size: nil) # rubocop:disable Metrics/MethodLength
  response = toolbox_api.create_pty_session(
    DaytonaToolboxApiClient::PtyCreateRequest.new(
      id:,
      cwd:,
      envs:,
      cols: pty_size&.cols,
      rows: pty_size&.rows,
      lazy_start: true
    )
  )

  connect_pty_session(response.session_id)
end

#create_session(session_id) ⇒ void

This method returns an undefined value.

Creates a new long-running background session in the Sandbox

Sessions are background processes that maintain state between commands, making them ideal for scenarios requiring multiple related commands or persistent environment setup.

Examples:

# Create a new session
session_id = "my-session"
sandbox.process.create_session(session_id)
session = sandbox.process.get_session(session_id)
# Do work...
sandbox.process.delete_session(session_id)

Parameters:

  • session_id (String)

    Unique identifier for the new session



117
118
119
# File 'lib/daytona/process.rb', line 117

def create_session(session_id)
  toolbox_api.create_session(DaytonaToolboxApiClient::CreateSessionRequest.new(session_id:))
end

#delete_pty_session(session_id) ⇒ void

This method returns an undefined value.

Deletes a PTY session, terminating the associated process

Examples:

sandbox.process.delete_pty_session("my-pty")

Parameters:

  • session_id (String)

    Unique identifier of the PTY session to delete



496
497
498
# File 'lib/daytona/process.rb', line 496

def delete_pty_session(session_id)
  toolbox_api.delete_pty_session(session_id)
end

#delete_session(session_id) ⇒ Object

Terminates and removes a session from the Sandbox, cleaning up any resources associated with it

Examples:

# Create and use a session
sandbox.process.create_session("temp-session")
# ... use the session ...

# Clean up when done
sandbox.process.delete_session("temp-session")

Parameters:

  • session_id (String)

    Unique identifier of the session to delete



382
# File 'lib/daytona/process.rb', line 382

def delete_session(session_id) = toolbox_api.delete_session(session_id)

#exec(command:, cwd: nil, env: nil, timeout: nil) ⇒ ExecuteResponse

Execute a shell command in the Sandbox

Examples:

# Simple command
response = sandbox.process.exec("echo 'Hello'")
puts response.artifacts.stdout
=> "Hello\n"

# Command with working directory
result = sandbox.process.exec("ls", cwd: "workspace/src")

# Command with timeout
result = sandbox.process.exec("sleep 10", timeout: 5)

Parameters:

  • command (String)

    Shell command to execute

  • cwd (String, nil) (defaults to: nil)

    Working directory for command execution. If not specified, uses the sandbox working directory

  • env (Hash<String, String>, nil) (defaults to: nil)

    Environment variables to set for the command

  • timeout (Integer, nil) (defaults to: nil)

    Maximum time in seconds to wait for the command to complete.

Returns:

  • (ExecuteResponse)

    Command execution results containing exit_code, result, and artifacts



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/daytona/process.rb', line 57

def exec(command:, cwd: nil, env: nil, timeout: nil) # rubocop:disable Metrics/MethodLength
  if env && !env.empty?
    env.each_key do |key|
      unless key.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
        raise ArgumentError,
              "Invalid environment variable name: '#{key}'"
      end
    end
    safe_env_exports = env.map do |key, value|
      "export #{key}=\"$(printf '%s' '#{Base64.strict_encode64(value)}' | base64 -d)\""
    end.join('; ')
    command = "#{safe_env_exports}; #{command}"
  end

  response = toolbox_api.execute_command(DaytonaToolboxApiClient::ExecuteRequest.new(command:, cwd:, timeout:))
  # Post-process the output to extract ExecutionArtifacts
  artifacts = parse_output(response.result.split("\n", -1))

  # Create new response with processed output and charts
  ExecuteResponse.new(
    exit_code: response.exit_code,
    result: artifacts.stdout,
    artifacts: artifacts
  )
end

#execute_session_command(session_id:, req:) ⇒ Daytona::SessionExecuteResponse

Executes a command in the session

Examples:

# Execute commands in sequence, maintaining state
session_id = "my-session"

# Change directory
req = Daytona::SessionExecuteRequest.new(command: "cd /workspace")
sandbox.process.execute_session_command(session_id:, req:)

# Create a file
req = Daytona::SessionExecuteRequest.new(command: "echo 'Hello' > test.txt")
sandbox.process.execute_session_command(session_id:, req:)

# Read the file
req = Daytona::SessionExecuteRequest.new(command: "cat test.txt")
result = sandbox.process.execute_session_command(session_id:, req:)
puts "Command stdout: #{result.stdout}"
puts "Command stderr: #{result.stderr}"

Parameters:

  • session_id (String)

    Unique identifier of the session to use

  • req (Daytona::SessionExecuteRequest)

    Command execution request containing command and run_async

Returns:



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/daytona/process.rb', line 182

def execute_session_command(session_id:, req:) # rubocop:disable Metrics/MethodLength
  response = toolbox_api.session_execute_command(
    session_id,
    DaytonaToolboxApiClient::SessionExecuteRequest.new(command: req.command, run_async: req.run_async,
                                                       suppress_input_echo: req.suppress_input_echo)
  )

  stdout, stderr = Util.demux(response.output || '')

  SessionExecuteResponse.new(
    cmd_id: response.cmd_id,
    output: response.output,
    stdout:,
    stderr:,
    exit_code: response.exit_code,
    # TODO: DaytonaApiClient::SessionExecuteResponse doesn't have additional_properties attribute
    additional_properties: {}
  )
end

#get_entrypoint_logsDaytona::SessionCommandLogsResponse

Get the sandbox entrypoint logs

Examples:

logs = sandbox.process.get_entrypoint_logs()
puts "Command stdout: #{logs.stdout}"
puts "Command stderr: #{logs.stderr}"

Returns:



287
# File 'lib/daytona/process.rb', line 287

def get_entrypoint_logs = parse_session_command_logs(toolbox_api.get_entrypoint_logs)

#get_entrypoint_logs_async(on_stdout:, on_stderr:) ⇒ WebSocket::Client::Simple::Client

Asynchronously retrieves and processes the sandbox entrypoint logs as they become available

Examples:

sandbox.process.get_entrypoint_logs_async(
  on_stdout: ->(log) { puts "[STDOUT]: #{log}" },
  on_stderr: ->(log) { puts "[STDERR]: #{log}" }
)

Parameters:

  • on_stdout (Proc)

    Callback function to handle stdout log chunks as they arrive

  • on_stderr (Proc)

    Callback function to handle stderr log chunks as they arrive

Returns:

  • (WebSocket::Client::Simple::Client)


300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/daytona/process.rb', line 300

def get_entrypoint_logs_async(on_stdout:, on_stderr:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  preview_link = get_preview_link.call(WS_PORT)
  url = URI.parse(preview_link.url)
  url.scheme = url.scheme == 'https' ? 'wss' : 'ws'
  url.path = '/process/session/entrypoint/logs'
  url.query = 'follow=true'

  completion_queue = Queue.new

  ws = WebSocket::Client::Simple.connect(
    url.to_s,
    headers: toolbox_api.api_client.default_headers.dup.merge(
      'X-Daytona-Preview-Token' => preview_link.token,
      'Content-Type' => 'text/plain',
      'Accept' => 'text/plain'
    )
  )

  ws.on(:message) do |message|
    if message.type == :close
      ws.close
      completion_queue.push(:close)
    else
      stdout, stderr = Util.demux(message.data.to_s)

      on_stdout.call(stdout) unless stdout.empty?
      on_stderr.call(stderr) unless stderr.empty?
    end
  end

  ws.on(:close) do
    completion_queue.push(:close)
  end

  ws.on(:error) do |e|
    completion_queue.push(:error)
    raise Sdk::Error, "WebSocket error: #{e.message}"
  end

  # Wait for completion
  completion_queue.pop
end

#get_entrypoint_sessionDaytonaApiClient::Session

Gets the Sandbox entrypoint session

Examples:

session = sandbox.process.get_entrypoint_session()
session.commands.each do |cmd|
  puts "Command: #{cmd.command}"
end

Returns:

  • (DaytonaApiClient::Session)

    Entrypoint session information including session_id and commands



142
# File 'lib/daytona/process.rb', line 142

def get_entrypoint_session = toolbox_api.get_entrypoint_session

#get_pty_session_info(session_id) ⇒ DaytonaApiClient::PtySessionInfo

Gets detailed information about a specific PTY session

Retrieves comprehensive information about a PTY session including its current state, configuration, and metadata.

Examples:

# Get details about a specific PTY session
session_info = sandbox.process.get_pty_session_info("my-session")
puts "Session ID: #{session_info.id}"
puts "Active: #{session_info.active}"
puts "Working Directory: #{session_info.cwd}"
puts "Terminal Size: #{session_info.cols}x#{session_info.rows}"

Parameters:

  • session_id (String)

    Unique identifier of the PTY session to retrieve information for

Returns:

  • (DaytonaApiClient::PtySessionInfo)

    Detailed information about the PTY session including ID, state, creation time, working directory, environment variables, and more



529
530
531
# File 'lib/daytona/process.rb', line 529

def get_pty_session_info(session_id)
  toolbox_api.get_pty_session(session_id)
end

#get_session(session_id) ⇒ DaytonaApiClient::Session

Gets a session in the Sandbox

Examples:

session = sandbox.process.get_session("my-session")
session.commands.each do |cmd|
  puts "Command: #{cmd.command}"
end

Parameters:

  • session_id (String)

    Unique identifier of the session to retrieve

Returns:

  • (DaytonaApiClient::Session)

    Session information including session_id and commands



131
# File 'lib/daytona/process.rb', line 131

def get_session(session_id) = toolbox_api.get_session(session_id)

#get_session_command(session_id:, command_id:) ⇒ DaytonaApiClient::Command

Gets information about a specific command executed in a session

Examples:

cmd = sandbox.process.get_session_command(session_id: "my-session", command_id: "cmd-123")
if cmd.exit_code == 0
  puts "Command #{cmd.command} completed successfully"
end

Parameters:

  • session_id (String)

    Unique identifier of the session

  • command_id (String)

    Unique identifier of the command

Returns:

  • (DaytonaApiClient::Command)

    Command information including id, command, and exit_code



155
156
157
# File 'lib/daytona/process.rb', line 155

def get_session_command(session_id:, command_id:)
  toolbox_api.get_session_command(session_id, command_id)
end

#get_session_command_logs(session_id:, command_id:) ⇒ Daytona::SessionCommandLogsResponse

Get the logs for a command executed in a session

Examples:

logs = sandbox.process.get_session_command_logs(session_id: "my-session", command_id: "cmd-123")
puts "Command stdout: #{logs.stdout}"
puts "Command stderr: #{logs.stderr}"

Parameters:

  • session_id (String)

    Unique identifier of the session

  • command_id (String)

    Unique identifier of the command

Returns:



212
213
214
215
216
217
218
219
# File 'lib/daytona/process.rb', line 212

def get_session_command_logs(session_id:, command_id:)
  parse_session_command_logs(
    toolbox_api.get_session_command_logs(
      session_id,
      command_id
    )
  )
end

#get_session_command_logs_async(session_id:, command_id:, on_stdout:, on_stderr:) ⇒ WebSocket::Client::Simple::Client

Asynchronously retrieves and processes the logs for a command executed in a session as they become available

Examples:

sandbox.process.get_session_command_logs_async(
  session_id: "my-session",
  command_id: "cmd-123",
  on_stdout: ->(log) { puts "[STDOUT]: #{log}" },
  on_stderr: ->(log) { puts "[STDERR]: #{log}" }
)

Parameters:

  • session_id (String)

    Unique identifier of the session

  • command_id (String)

    Unique identifier of the command

  • on_stdout (Proc)

    Callback function to handle stdout log chunks as they arrive

  • on_stderr (Proc)

    Callback function to handle stderr log chunks as they arrive

Returns:

  • (WebSocket::Client::Simple::Client)


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/daytona/process.rb', line 236

def get_session_command_logs_async(session_id:, command_id:, on_stdout:, on_stderr:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  preview_link = get_preview_link.call(WS_PORT)
  url = URI.parse(preview_link.url)
  url.scheme = url.scheme == 'https' ? 'wss' : 'ws'
  url.path = "/process/session/#{session_id}/command/#{command_id}/logs"
  url.query = 'follow=true'

  completion_queue = Queue.new

  ws = WebSocket::Client::Simple.connect(
    url.to_s,
    headers: toolbox_api.api_client.default_headers.dup.merge(
      'X-Daytona-Preview-Token' => preview_link.token,
      'Content-Type' => 'text/plain',
      'Accept' => 'text/plain'
    )
  )

  ws.on(:message) do |message|
    if message.type == :close
      ws.close
      completion_queue.push(:close)
    else
      stdout, stderr = Util.demux(message.data.to_s)

      on_stdout.call(stdout) unless stdout.empty?
      on_stderr.call(stderr) unless stderr.empty?
    end
  end

  ws.on(:close) do
    completion_queue.push(:close)
  end

  ws.on(:error) do |e|
    completion_queue.push(:error)
    raise Sdk::Error, "WebSocket error: #{e.message}"
  end

  # Wait for completion
  completion_queue.pop
end

#list_pty_sessionsArray<DaytonaApiClient::PtySessionInfo>

Lists all PTY sessions in the Sandbox

Examples:

sessions = sandbox.process.list_pty_sessions
sessions.each do |session|
  puts "PTY Session #{session.id}: #{session.cols}x#{session.rows}"
end

Returns:

  • (Array<DaytonaApiClient::PtySessionInfo>)

    List of PTY session information



509
510
511
# File 'lib/daytona/process.rb', line 509

def list_pty_sessions
  toolbox_api.list_pty_sessions
end

#list_sessionsArray<DaytonaApiClient::Session>

Returns List of all sessions in the Sandbox.

Examples:

sessions = sandbox.process.list_sessions
sessions.each do |session|
  puts "Session #{session.session_id}:"
  puts "  Commands: #{session.commands.length}"
end

Returns:

  • (Array<DaytonaApiClient::Session>)

    List of all sessions in the Sandbox



369
# File 'lib/daytona/process.rb', line 369

def list_sessions = toolbox_api.list_sessions

#resize_pty_session(session_id, pty_size) ⇒ DaytonaApiClient::PtySessionInfo

Resizes a PTY session to the specified dimensions

Examples:

pty_size = Daytona::PtySize.new(rows: 30, cols: 120)
session_info = sandbox.process.resize_pty_session("my-pty", pty_size)
puts "PTY resized to #{session_info.cols}x#{session_info.rows}"

Parameters:

  • session_id (String)

    Unique identifier of the PTY session

  • pty_size (PtySize)

    New terminal size

Returns:

  • (DaytonaApiClient::PtySessionInfo)

    Updated PTY session information



479
480
481
482
483
484
485
486
487
# File 'lib/daytona/process.rb', line 479

def resize_pty_session(session_id, pty_size)
  toolbox_api.resize_pty_session(
    session_id,
    DaytonaToolboxApiClient::PtyResizeRequest.new(
      cols: pty_size.cols,
      rows: pty_size.rows
    )
  )
end

#send_session_command_input(session_id:, command_id:, data:) ⇒ void

This method returns an undefined value.

Sends input data to a command executed in a session

This method allows you to send input to an interactive command running in a session, such as responding to prompts or providing data to stdin.

Parameters:

  • session_id (String)

    Unique identifier of the session

  • command_id (String)

    Unique identifier of the command

  • data (String)

    Input data to send to the command



352
353
354
355
356
357
358
# File 'lib/daytona/process.rb', line 352

def send_session_command_input(session_id:, command_id:, data:)
  toolbox_api.send_input(
    session_id,
    command_id,
    DaytonaToolboxApiClient::SessionSendInputRequest.new(data:)
  )
end