Class: E2B::Services::CommandHandle

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/e2b/services/command_handle.rb

Overview

Handle for an executing command in the sandbox.

Provides methods for waiting on the command, sending stdin input, killing the process, disconnecting from the event stream, and iterating over stdout/stderr/pty output as it arrives.

Instances are returned by E2B::Services::Commands#run (background mode), Pty#create, and Pty#connect.

Examples:

Wait for a command and get output

handle = sandbox.pty.create
result = handle.wait(on_pty: ->(data) { print data })

Iterate over output

handle.each do |stdout, stderr, pty|
  print stdout if stdout
end

Kill a long-running process

handle.kill

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pid:, handle_kill:, handle_send_stdin:, handle_disconnect: nil, events_proc: nil, result: nil) ⇒ CommandHandle

Initialize a new CommandHandle.

Callers should not construct this directly; it is created by service methods such as Pty#create or E2B::Services::Commands#run.

Parameters:

  • pid (Integer, nil)

    Process ID

  • handle_kill (Proc)

    Proc that sends SIGKILL to the process

  • handle_send_stdin (Proc)

    Proc that sends data to stdin/pty

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

    Proc that disconnects the event stream

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

    Proc that accepts a block and yields parsed events as they arrive. Each event is a Hash with keys like “event” => { “Start” => …, “Data” => …, “End” => … }. May be nil if the result is already materialized.

  • result (Hash, nil) (defaults to: nil)

    Pre-materialized result from a synchronous RPC call. Expected keys: :events, :stdout, :stderr, :exit_code.



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/e2b/services/command_handle.rb', line 161

def initialize(pid:, handle_kill:, handle_send_stdin:, handle_disconnect: nil, events_proc: nil, result: nil)
  @pid = pid
  @handle_kill = handle_kill
  @handle_send_stdin = handle_send_stdin
  @handle_disconnect = handle_disconnect
  @events_proc = events_proc
  @result = result
  @disconnected = false
  @finished = false
  @completed = false
  @stdout = ""
  @stderr = ""
  @exit_code = nil
  @error = nil
  @mutex = Mutex.new
  @materialized_event_index = 0
end

Instance Attribute Details

#pidInteger? (readonly)

Returns Process ID of the running command.

Returns:

  • (Integer, nil)

    Process ID of the running command



144
145
146
# File 'lib/e2b/services/command_handle.rb', line 144

def pid
  @pid
end

Instance Method Details

#disconnectvoid

This method returns an undefined value.

Disconnect from the command event stream without killing the process.

After disconnecting, #wait and #each will return immediately with whatever output has been accumulated so far. The underlying process continues running and can be reconnected to via E2B::Services::Commands#connect or Pty#connect.



231
232
233
234
235
236
# File 'lib/e2b/services/command_handle.rb', line 231

def disconnect
  return if @disconnected

  @disconnected = true
  @handle_disconnect&.call
end

#each {|stdout, stderr, pty| ... } ⇒ void

This method returns an undefined value.

Iterate over command output as it arrives.

Yields a triple of [stdout, stderr, pty] for each chunk of output. Exactly one of the three will be non-nil per iteration.

Yields:

  • (stdout, stderr, pty)

    Output chunks

Yield Parameters:

  • stdout (String, nil)

    Stdout data, or nil

  • stderr (String, nil)

    Stderr data, or nil

  • pty (String, nil)

    PTY data, or nil



248
249
250
251
252
253
254
255
256
257
258
# File 'lib/e2b/services/command_handle.rb', line 248

def each(&block)
  return enum_for(:each) unless block_given?

  if @result
    # Iterate over pre-materialized events
    iterate_materialized_events(&block)
  elsif @events_proc
    # Iterate over streaming events
    iterate_streaming_events(&block)
  end
end

#killBoolean

Kill the running command with SIGKILL.

Returns:

  • (Boolean)

    true if the signal was sent successfully, false on error



211
212
213
# File 'lib/e2b/services/command_handle.rb', line 211

def kill
  @handle_kill.call
end

#send_stdin(data) ⇒ void

This method returns an undefined value.

Send data to the command’s stdin (or PTY input).

Parameters:

  • data (String)

    Data to write



219
220
221
# File 'lib/e2b/services/command_handle.rb', line 219

def send_stdin(data)
  @handle_send_stdin.call(data)
end

#wait(on_stdout: nil, on_stderr: nil, on_pty: nil) ⇒ CommandResult

Wait for the command to finish and return the result.

If the command exits with a non-zero exit code, raises CommandExitError. Callbacks are invoked as output arrives (or immediately if the result is already materialized).

Parameters:

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

    Called with each chunk of stdout (String)

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

    Called with each chunk of stderr (String)

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

    Called with each chunk of PTY output (String)

Returns:

Raises:



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/e2b/services/command_handle.rb', line 190

def wait(on_stdout: nil, on_stderr: nil, on_pty: nil)
  consume_events(on_stdout: on_stdout, on_stderr: on_stderr, on_pty: on_pty)
  unless @disconnected || completed?
    raise E2BError, "Command ended without an end event"
  end

  build_result.tap do |cmd_result|
    unless cmd_result.success?
      raise CommandExitError.new(
        stdout: cmd_result.stdout,
        stderr: cmd_result.stderr,
        exit_code: cmd_result.exit_code,
        error: cmd_result.error
      )
    end
  end
end