Class: SwarmSDK::Tools::Bash
- Defined in:
- lib/swarm_sdk/tools/bash.rb
Overview
Bash tool for executing shell commands
Executes commands in a persistent shell session with timeout support. Provides comprehensive guidance on proper usage patterns.
Constant Summary collapse
- ALWAYS_BLOCKED_COMMANDS =
Commands that are ALWAYS blocked for safety reasons These cannot be overridden by permissions configuration
[ %r{^rm\s+-rf\s+/$}, # rm -rf / - delete root filesystem ].freeze
Class Method Summary collapse
Instance Method Summary collapse
- #execute(command:, description: nil, timeout: nil) ⇒ Object
-
#initialize(directory:) ⇒ Bash
constructor
A new instance of Bash.
- #name ⇒ Object
Methods inherited from Base
removable, removable?, #removable?
Constructor Details
#initialize(directory:) ⇒ Bash
Returns a new instance of Bash.
17 18 19 20 |
# File 'lib/swarm_sdk/tools/bash.rb', line 17 def initialize(directory:) super() @directory = File.(directory) end |
Class Method Details
.creation_requirements ⇒ Object
12 13 14 |
# File 'lib/swarm_sdk/tools/bash.rb', line 12 def creation_requirements [:directory] end |
Instance Method Details
#execute(command:, description: nil, timeout: nil) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/swarm_sdk/tools/bash.rb', line 96 def execute(command:, description: nil, timeout: nil) # Validate inputs return validation_error("command is required") if command.nil? || command.empty? # Check against always-blocked commands blocked_pattern = ALWAYS_BLOCKED_COMMANDS.find { |pattern| pattern.match?(command) } if blocked_pattern return blocked_command_error(command, blocked_pattern) end # Validate and set timeout timeout_ms = timeout || SwarmSDK.config.bash_command_timeout timeout_ms = [timeout_ms, SwarmSDK.config.bash_command_max_timeout].min timeout_seconds = timeout_ms / 1000.0 # Execute command with timeout stdout = +"" stderr = +"" exit_status = nil begin require "open3" require "timeout" Timeout.timeout(timeout_seconds) do # CRITICAL: Change to agent's directory for subprocess # This is SAFE because Open3.popen3 creates a subprocess # The subprocess inherits the directory, but the parent fiber is unaffected Dir.chdir(@directory) do Open3.popen3(command) do |stdin, out, err, wait_thr| stdin.close # Close stdin since we don't send input # Read stdout and stderr stdout = out.read || "" stderr = err.read || "" exit_status = wait_thr.value.exitstatus end end end rescue Timeout::Error return format_timeout_error(command, timeout_seconds) rescue Errno::ENOENT => e return error("Command not found or executable not in PATH: #{e.message}") rescue Errno::EACCES return error("Permission denied: Cannot execute command '#{command}'") rescue StandardError => e return error("Failed to execute command: #{e.class.name} - #{e.message}") end # Build output output = format_command_output(command, description, stdout, stderr, exit_status) # Truncate if too long max_output = SwarmSDK.config.output_character_limit if output.length > max_output truncated = output[0...max_output] truncated += "\n\n<system-reminder>Output truncated at #{max_output} characters. The full output was #{output.length} characters.</system-reminder>" output = truncated end # Add usage reminders for certain patterns output = add_usage_reminders(output, command) output rescue StandardError => e error("Unexpected error executing command: #{e.class.name} - #{e.message}") end |
#name ⇒ Object
22 23 24 |
# File 'lib/swarm_sdk/tools/bash.rb', line 22 def name "Bash" end |