Class: Ask::Tools::Bash
- Inherits:
-
Ask::Tool
- Object
- Ask::Tool
- Ask::Tools::Bash
- Defined in:
- lib/ask/tools/shell/bash.rb
Overview
Execute shell commands in a sandboxed environment. Returns stdout, stderr, exit code, and a timed_out flag. Output is truncated to 100KB.
Constant Summary collapse
- MAX_OUTPUT_SIZE =
102_400
Instance Method Summary collapse
Instance Method Details
#execute(command:, timeout: 30, workdir: nil) ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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 |
# File 'lib/ask/tools/shell/bash.rb', line 24 def execute(command:, timeout: 30, workdir: nil) Dir.mktmpdir("ask_bash") do |dir| workdir ||= dir stdout = StringIO.new stderr = StringIO.new timed_out = false exit_code = -1 begin Open3.popen3("bash", "-c", command, chdir: workdir) do |stdin, out, err, wait_thr| stdin.close threads = [ Thread.new { IO.copy_stream(out, stdout) rescue nil }, Thread.new { IO.copy_stream(err, stderr) rescue nil } ] unless wait_thr.join(timeout) Process.kill("-KILL", wait_thr.pid) rescue nil timed_out = true end threads.each(&:join) exit_code = timed_out ? -1 : wait_thr.value.exitstatus end rescue => e return Ask::Result.error(message: "Bash execution failed: #{e.}", metadata: { stdout: stdout.string, stderr: stderr.string }) end out_text = stdout.string err_text = stderr.string if out_text.length > MAX_OUTPUT_SIZE header = "[Output truncated to #{MAX_OUTPUT_SIZE / 1024}KB]\n" out_text = "#{header}#{out_text[-(MAX_OUTPUT_SIZE - header.length)..]}" end if err_text.length > MAX_OUTPUT_SIZE header = "[Error output truncated to #{MAX_OUTPUT_SIZE / 1024}KB]\n" err_text = "#{header}#{err_text[-(MAX_OUTPUT_SIZE - header.length)..]}" end Ask::Result.ok(data: { stdout: out_text, stderr: err_text, exit_code: exit_code, timed_out: timed_out }) end end |