Class: AgentHarness::CommandExecutor

Inherits:
Object
  • Object
show all
Defined in:
lib/agent_harness/command_executor.rb

Overview

Executes shell commands with timeout support

Provides a clean interface for running CLI commands with proper error handling, timeout support, and result capture.

Examples:

Basic usage

executor = AgentHarness::CommandExecutor.new
result = executor.execute(["claude", "--print", "--prompt", "Hello"])
puts result.stdout

With timeout

result = executor.execute("claude --print", timeout: 300)

Direct Known Subclasses

DockerCommandExecutor

Defined Under Namespace

Classes: Result

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(logger: nil) ⇒ CommandExecutor

Returns a new instance of CommandExecutor.



34
35
36
# File 'lib/agent_harness/command_executor.rb', line 34

def initialize(logger: nil)
  @logger = logger
end

Instance Attribute Details

#loggerObject (readonly)

Returns the value of attribute logger.



32
33
34
# File 'lib/agent_harness/command_executor.rb', line 32

def logger
  @logger
end

Instance Method Details

#available?(binary) ⇒ Boolean

Check if a binary is available

Parameters:

  • binary (String)

    binary name

Returns:

  • (Boolean)

    true if available



110
111
112
# File 'lib/agent_harness/command_executor.rb', line 110

def available?(binary)
  !which(binary).nil?
end

#execute(command, timeout: nil, idle_timeout: nil, env: {}, stdin_data: nil, on_stdout_chunk: nil, on_stderr_chunk: nil, on_heartbeat: nil, heartbeat_interval: 1.0, observer: nil) ⇒ Result

Execute a command with optional timeout

Parameters:

  • command (Array<String>, String)

    command to execute

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

    timeout in seconds

  • idle_timeout (Integer, Float, nil) (defaults to: nil)

    idle timeout in seconds based on output activity

  • env (Hash) (defaults to: {})

    environment variables

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

    data to send to stdin

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

    callback for stdout chunks as they are produced

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

    callback for stderr chunks as they are produced

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

    callback invoked periodically while the command is running

  • heartbeat_interval (Integer, Float) (defaults to: 1.0)

    heartbeat interval in seconds

  • observer (Object, nil) (defaults to: nil)

    optional observer responding to on_stdout_chunk, on_stderr_chunk, and on_heartbeat

Returns:

  • (Result)

    execution result

Raises:



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
# File 'lib/agent_harness/command_executor.rb', line 54

def execute(command, timeout: nil, idle_timeout: nil, env: {}, stdin_data: nil,
  on_stdout_chunk: nil, on_stderr_chunk: nil, on_heartbeat: nil,
  heartbeat_interval: 1.0, observer: nil)
  validate_duration!(timeout, name: :timeout, allow_nil: true)
  validate_duration!(idle_timeout, name: :idle_timeout, allow_nil: true)
  validate_duration!(heartbeat_interval, name: :heartbeat_interval, allow_nil: true)

  cmd_array = normalize_command(command)
  cmd_string = cmd_array.shelljoin

  log_debug("Executing command",
    command: cmd_string,
    timeout: timeout,
    idle_timeout: idle_timeout)

  start_time = Time.now

  stdout, stderr, status = execute_streaming(
    cmd_array,
    timeout: timeout,
    idle_timeout: idle_timeout,
    env: env,
    stdin_data: stdin_data,
    on_stdout_chunk: on_stdout_chunk,
    on_stderr_chunk: on_stderr_chunk,
    on_heartbeat: on_heartbeat,
    heartbeat_interval: heartbeat_interval,
    observer: observer
  )

  duration = Time.now - start_time

  Result.new(
    stdout: stdout,
    stderr: stderr,
    exit_code: status.exitstatus,
    duration: duration
  )
end

#which(binary) ⇒ String?

Check if a binary exists in PATH

Parameters:

  • binary (String)

    binary name

Returns:

  • (String, nil)

    full path or nil



98
99
100
101
102
103
104
# File 'lib/agent_harness/command_executor.rb', line 98

def which(binary)
  ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
    full_path = File.join(path, binary)
    return full_path if File.executable?(full_path)
  end
  nil
end