Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions lib/tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -744,10 +744,18 @@ tool_shell() {
fi

local output exit_code
local had_errexit=0
if [[ "$-" == *e* ]]; then
had_errexit=1
set +e
fi

if is_command_available timeout; then
output="$(timeout "$timeout_val" bash -c "$cmd" 2>&1)" || true
output="$(timeout "$timeout_val" bash -c "$cmd" 2>&1)"
exit_code=$?
elif is_command_available gtimeout; then
output="$(gtimeout "$timeout_val" bash -c "$cmd" 2>&1)" || true
output="$(gtimeout "$timeout_val" bash -c "$cmd" 2>&1)"
exit_code=$?
else
# Pure-bash timeout fallback (macOS/Termux)
local _tmpout
Expand All @@ -761,15 +769,20 @@ tool_shell() {
done
if kill -0 "$_pid" 2>/dev/null; then
kill -9 "$_pid" 2>/dev/null
wait "$_pid" 2>/dev/null || true
wait "$_pid" 2>/dev/null
output="[command timed out after ${timeout_val}s]"
exit_code=124
else
wait "$_pid" 2>/dev/null || true
wait "$_pid" 2>/dev/null
exit_code=$?
output="$(cat "$_tmpout")"
fi
rm -f "$_tmpout"
fi
exit_code=$?

if [[ "$had_errexit" -eq 1 ]]; then
set -e
fi

# Truncate output to 100KB
if [ "${#output}" -gt 102400 ]; then
Expand Down Expand Up @@ -1203,9 +1216,9 @@ tool_list_files() {
local count=0

if [[ "$recursive" == "true" ]]; then
local find_args=""
local find_args=()
if [[ -n "$pattern" ]]; then
find_args="-name $pattern"
find_args=(-name "$pattern")
fi
local f
local entries_ndjson=""
Expand All @@ -1223,7 +1236,7 @@ tool_list_files() {
entries_ndjson="${entries_ndjson}$(jq -nc --arg n "$rel" --arg t "$ftype" '{name: $n, type: $t}')"$'\n'
count=$((count + 1))
done <<EOF
$(find "$path" -maxdepth 10 ${find_args} 2>/dev/null | head -n "$TOOL_LIST_FILES_MAX")
$(find "$path" -maxdepth 10 "${find_args[@]}" 2>/dev/null | head -n "$TOOL_LIST_FILES_MAX")
EOF
if [[ -n "$entries_ndjson" ]]; then
entries="$(printf '%s' "$entries_ndjson" | jq -s '.')"
Expand Down
13 changes: 9 additions & 4 deletions tests/test_tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,10 @@ teardown_test_env

test_start "tool_shell captures exit codes"
setup_test_env
# Note: due to || true in shell execution, exit code capture may return 0
# Test that the JSON is valid and contains exitCode field
result="$(tool_shell '{"command":"exit 42"}')"
assert_json_valid "$result"
exit_code="$(printf '%s' "$result" | jq -r '.exitCode')"
# exitCode should be a number (may be 0 due to || true in implementation)
assert_match "$exit_code" '^[0-9]+$'
assert_eq "$exit_code" "42"
teardown_test_env

test_start "tool_shell blocks rm -rf /"
Expand All @@ -138,6 +135,14 @@ result="$(tool_shell '{"command":"dd if=/dev/zero of=/dev/sda"}' 2>/dev/null)" |
assert_contains "$result" "blocked"
teardown_test_env

test_start "tool_shell returns timeout exit code"
setup_test_env
result="$(tool_shell '{"command":"sleep 2","timeout":1}' 2>/dev/null)" || true
assert_json_valid "$result"
exit_code="$(printf '%s' "$result" | jq -r '.exitCode')"
assert_eq "$exit_code" "124"
teardown_test_env

test_start "tool_shell allows safe commands"
setup_test_env
result="$(tool_shell '{"command":"date +%s"}')"
Expand Down