Class: E2B::Services::Commands

Inherits:
BaseService show all
Includes:
LiveStreamable
Defined in:
lib/e2b/services/commands.rb

Overview

Command execution service for E2B sandbox

Provides methods to run terminal commands inside the sandbox. Uses Connect RPC protocol to communicate with the envd process service.

Examples:

Basic usage

result = sandbox.commands.run("ls -la")
puts result.stdout

With streaming callbacks

sandbox.commands.run("npm install",
  on_stdout: ->(data) { print data },
  on_stderr: ->(data) { warn data }
)

Background command

handle = sandbox.commands.run("sleep 10", background: true)
handle.kill

Constant Summary

Constants inherited from BaseService

BaseService::DEFAULT_USERNAME, BaseService::ENVD_DEFAULT_USER_VERSION, BaseService::ENVD_PORT, BaseService::ENVD_RECURSIVE_WATCH_VERSION

Instance Method Summary collapse

Methods inherited from BaseService

#initialize

Constructor Details

This class inherits a constructor from E2B::Services::BaseService

Instance Method Details

#close_stdin(pid, request_timeout: nil, headers: nil) ⇒ void

This method returns an undefined value.

Close the stdin of a running process.

After calling this, no more input can be sent to the process via #send_stdin.

Parameters:

  • pid (Integer)

    Process ID

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

    Request timeout in seconds

Raises:



178
179
180
181
182
183
# File 'lib/e2b/services/commands.rb', line 178

def close_stdin(pid, request_timeout: nil, headers: nil)
  envd_rpc("process.Process", "CloseStdin",
    body: { process: { pid: pid } },
    headers: headers,
    timeout: request_timeout || 30)
end

#connect(pid, timeout: 60, request_timeout: nil) ⇒ CommandHandle

Connect to a running process

Parameters:

  • pid (Integer)

    Process ID to connect to

  • timeout (Integer) (defaults to: 60)

    Connection timeout in seconds

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

    Request timeout in seconds

Returns:



191
192
193
194
195
196
197
198
# File 'lib/e2b/services/commands.rb', line 191

def connect(pid, timeout: 60, request_timeout: nil)
  build_live_handle(
    rpc_method: "Connect",
    body: { process: { pid: pid } },
    headers: user_auth_headers(nil),
    timeout: request_timeout || (timeout + 30)
  )
end

#kill(pid, request_timeout: nil, headers: nil) ⇒ Boolean

Kill a running process

Parameters:

  • pid (Integer)

    Process ID to kill

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

    Request timeout in seconds

Returns:

  • (Boolean)

    true if killed, false if not found



138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/e2b/services/commands.rb', line 138

def kill(pid, request_timeout: nil, headers: nil)
  envd_rpc("process.Process", "SendSignal",
    body: {
      process: { pid: pid },
      signal: 9 # SIGKILL
    },
    headers: headers,
    timeout: request_timeout || 30)
  true
rescue E2B::NotFoundError
  false
rescue E2B::E2BError
  false
end

#list(request_timeout: nil) ⇒ Array<Hash>

List running processes

Parameters:

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

    Request timeout in seconds

Returns:

  • (Array<Hash>)

    List of running processes with pid, config, tag



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/e2b/services/commands.rb', line 117

def list(request_timeout: nil)
  response = envd_rpc("process.Process", "List",
    body: {},
    timeout: request_timeout || 30)

  processes = []
  events = response[:events] || []
  events.each do |event|
    next unless event.is_a?(Hash)
    if event["processes"]
      processes.concat(Array(event["processes"]))
    end
  end
  processes
end

#run(cmd, background: false, envs: nil, user: nil, cwd: nil, on_stdout: nil, on_stderr: nil, timeout: 60, request_timeout: nil, &block) ⇒ CommandResult, CommandHandle

Run a command in the sandbox

Parameters:

  • cmd (String)

    Command to execute (run via /bin/bash -l -c)

  • background (Boolean) (defaults to: false)

    Run in background, returns CommandHandle

  • envs (Hash{String => String}, nil) (defaults to: nil)

    Environment variables

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

    User to run the command as

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

    Working directory

  • on_stdout (Proc, nil) (defaults to: nil)

    Callback for stdout data

  • on_stderr (Proc, nil) (defaults to: nil)

    Callback for stderr data

  • timeout (Integer) (defaults to: 60)

    Command timeout in seconds (default: 60)

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

    HTTP request timeout in seconds

Returns:

Raises:



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/e2b/services/commands.rb', line 42

def run(cmd, background: false, envs: nil, user: nil, cwd: nil,
        on_stdout: nil, on_stderr: nil, timeout: 60, request_timeout: nil, &block)
  # Build the process spec - official SDK always uses /bin/bash -l -c
  process_spec = {
    cmd: "/bin/bash",
    args: ["-l", "-c", cmd.to_s]
  }

  if envs && !envs.empty?
    env_map = {}
    envs.each { |k, v| env_map[k.to_s] = v.to_s }
    process_spec[:envs] = env_map
  end

  process_spec[:cwd] = cwd if cwd

  body = { process: process_spec }
  headers = user_auth_headers(user)

  # Set up streaming callback
  stream_block = block if block_given?
  streaming_callback = nil
  if on_stdout || on_stderr || block_given?
    streaming_callback = lambda { |event_data|
      stdout_chunk = event_data[:stdout]
      stderr_chunk = event_data[:stderr]

      on_stdout&.call(stdout_chunk) if stdout_chunk && !stdout_chunk.empty?
      on_stderr&.call(stderr_chunk) if stderr_chunk && !stderr_chunk.empty?

      stream_block&.call(:stdout, stdout_chunk) if stdout_chunk && !stdout_chunk.empty?
      stream_block&.call(:stderr, stderr_chunk) if stderr_chunk && !stderr_chunk.empty?
    }
  end

  effective_timeout = request_timeout || (timeout + 30)

  if background
    return build_live_handle(
      rpc_method: "Start",
      body: body,
      timeout: effective_timeout,
      headers: headers,
      on_stdout: on_stdout,
      on_stderr: on_stderr,
      &block
    )
  end

  response = envd_rpc("process.Process", "Start",
    body: body,
    timeout: effective_timeout,
    headers: headers,
    on_event: streaming_callback)

  # Return CommandResult for foreground processes
  result = build_result(response)

  # Raise on non-zero exit code (matching official SDK behavior)
  if result.exit_code != 0
    raise CommandExitError.new(
      stdout: result.stdout,
      stderr: result.stderr,
      exit_code: result.exit_code,
      error: result.error
    )
  end

  result
end

#send_stdin(pid, data, request_timeout: nil, headers: nil) ⇒ Object

Send stdin data to a running process

Parameters:

  • pid (Integer)

    Process ID

  • data (String)

    Data to send to stdin

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

    Request timeout in seconds



158
159
160
161
162
163
164
165
166
167
# File 'lib/e2b/services/commands.rb', line 158

def send_stdin(pid, data, request_timeout: nil, headers: nil)
  encoded = Base64.strict_encode64(data.to_s)
  envd_rpc("process.Process", "SendInput",
    body: {
      process: { pid: pid },
      input: { stdin: encoded }
    },
    headers: headers,
    timeout: request_timeout || 30)
end