harb/scripts/harb-evaluator/run-resources.sh
johba b616953313 fix: add missing shell scripts and fix contract interface in run-protocol
- Add scripts/harb-evaluator/run-resources.sh: collects disk, RAM,
  Anthropic API usage, and Woodpecker CI queue metrics
- Add scripts/harb-evaluator/run-protocol.sh: collects TVL, fees,
  position data, and rebalance events from LiquidityManager
- Fix run-protocol.toml: positions accessed via positions(uint8) not
  named getters (floorPosition/anchorPosition/discoveryPosition)
- Fix event signature: Recentered(int24,bool) not Recenter(int24,int24,int24)

Addresses review findings: missing implementation files and contract
interface mismatch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 21:00:14 +00:00

169 lines
6.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# run-resources.sh — Infrastructure resource snapshot.
#
# Collects disk usage, RAM usage, Anthropic API call counts and budget burn,
# and Woodpecker CI queue depth. Writes evidence/resources/YYYY-MM-DD.json.
#
# Exit codes:
# 0 snapshot written successfully
# 2 infrastructure error
#
# Environment:
# DISK_PATH filesystem to measure (default: /)
# ANTHROPIC_BUDGET_USD_LIMIT budget ceiling in USD (default: 50)
# WOODPECKER_API_URL Woodpecker CI base URL (default: http://localhost:8090; empty to skip)
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
DATE="$(date -u +%Y-%m-%d)"
OUT_DIR="$REPO_ROOT/evidence/resources"
OUT_FILE="$OUT_DIR/$DATE.json"
CALL_LOG="$REPO_ROOT/tmp/anthropic-call-log.jsonl"
DISK_PATH="${DISK_PATH:-/}"
ANTHROPIC_BUDGET_USD_LIMIT="${ANTHROPIC_BUDGET_USD_LIMIT:-50}"
WOODPECKER_API_URL="${WOODPECKER_API_URL:-http://localhost:8090}"
die() { echo "ERROR: $*" >&2; exit 2; }
mkdir -p "$OUT_DIR"
# ── collect-disk ──────────────────────────────────────────────────────────────
disk_used_bytes=0
disk_total_bytes=0
disk_used_pct=0
if command -v df >/dev/null 2>&1; then
# -B1 gives bytes; tail -1 skips header; awk grabs used, total, pct
read -r disk_total_bytes disk_used_bytes disk_used_pct < <(
df -B1 "$DISK_PATH" | tail -1 | awk '{gsub(/%/,"",$5); print $2, $3, $5}'
)
else
echo "WARN: df not available, disk metrics will be 0" >&2
fi
# ── collect-ram ───────────────────────────────────────────────────────────────
ram_used_bytes=0
ram_total_bytes=0
ram_used_pct=0
if command -v free >/dev/null 2>&1; then
# free -b: second line (Mem:) has total, used
read -r ram_total_bytes ram_used_bytes < <(
free -b | awk '/^Mem:/ {print $2, $3}'
)
if [[ "$ram_total_bytes" -gt 0 ]]; then
ram_used_pct=$(awk "BEGIN {printf \"%.1f\", $ram_used_bytes / $ram_total_bytes * 100}")
fi
elif command -v vm_stat >/dev/null 2>&1; then
# macOS fallback
page_size=$(vm_stat | head -1 | grep -o '[0-9]*')
pages_active=$(vm_stat | awk '/Pages active/ {gsub(/\./,"",$3); print $3}')
pages_wired=$(vm_stat | awk '/Pages wired/ {gsub(/\./,"",$4); print $4}')
pages_free=$(vm_stat | awk '/Pages free/ {gsub(/\./,"",$3); print $3}')
pages_inactive=$(vm_stat | awk '/Pages inactive/ {gsub(/\./,"",$3); print $3}')
ram_used_bytes=$(( (pages_active + pages_wired) * page_size ))
ram_total_bytes=$(( (pages_active + pages_wired + pages_free + pages_inactive) * page_size ))
if [[ "$ram_total_bytes" -gt 0 ]]; then
ram_used_pct=$(awk "BEGIN {printf \"%.1f\", $ram_used_bytes / $ram_total_bytes * 100}")
fi
else
echo "WARN: neither free nor vm_stat available, RAM metrics will be 0" >&2
fi
# ── collect-api ───────────────────────────────────────────────────────────────
anthropic_calls_24h=0
anthropic_budget_usd_used=0
anthropic_budget_pct=0
if [[ -f "$CALL_LOG" ]]; then
cutoff=$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%S 2>/dev/null \
|| date -u -v-24H +%Y-%m-%dT%H:%M:%S 2>/dev/null \
|| echo "")
today=$(date -u +%Y-%m-%d)
if [[ -n "$cutoff" ]]; then
anthropic_calls_24h=$(awk -F'"ts"' -v cutoff="$cutoff" '
NF>1 { split($2,a,"\""); if (a[2] >= cutoff) count++ }
END { print count+0 }
' "$CALL_LOG")
fi
anthropic_budget_usd_used=$(awk -F'"' -v today="$today" '
/"ts"/ && $0 ~ today {
match($0, /"cost_usd"[[:space:]]*:[[:space:]]*([0-9.]+)/, m)
if (m[1] != "") sum += m[1]
}
END { printf "%.2f", sum+0 }
' "$CALL_LOG")
fi
if [[ "$ANTHROPIC_BUDGET_USD_LIMIT" != "0" ]]; then
anthropic_budget_pct=$(awk "BEGIN {printf \"%.1f\", $anthropic_budget_usd_used / $ANTHROPIC_BUDGET_USD_LIMIT * 100}")
fi
# ── collect-ci ────────────────────────────────────────────────────────────────
woodpecker_queue_depth="null"
woodpecker_running="null"
if [[ -n "$WOODPECKER_API_URL" ]]; then
if ci_json=$(curl -sf --max-time 5 "$WOODPECKER_API_URL/api/queue/info" 2>/dev/null); then
woodpecker_queue_depth=$(echo "$ci_json" | jq '.pending // .waiting // 0')
woodpecker_running=$(echo "$ci_json" | jq '.running // 0')
else
echo "WARN: Woodpecker CI unreachable at $WOODPECKER_API_URL, CI metrics will be null" >&2
fi
fi
# ── verdict ───────────────────────────────────────────────────────────────────
verdict="ok"
for pct in "$disk_used_pct" "$ram_used_pct" "$anthropic_budget_pct"; do
# Strip trailing % if present
pct_num="${pct//%/}"
if awk "BEGIN {exit !($pct_num >= 95)}"; then
verdict="critical"
break
fi
if awk "BEGIN {exit !($pct_num >= 80)}"; then
verdict="warn"
fi
done
# ── write JSON ────────────────────────────────────────────────────────────────
cat > "$OUT_FILE" <<ENDJSON
{
"date": "$DATE",
"disk": {
"used_bytes": $disk_used_bytes,
"total_bytes": $disk_total_bytes,
"used_pct": $disk_used_pct
},
"ram": {
"used_bytes": $ram_used_bytes,
"total_bytes": $ram_total_bytes,
"used_pct": $ram_used_pct
},
"api": {
"anthropic_calls_24h": $anthropic_calls_24h,
"anthropic_budget_usd_used": $anthropic_budget_usd_used,
"anthropic_budget_usd_limit": $ANTHROPIC_BUDGET_USD_LIMIT,
"anthropic_budget_pct": $anthropic_budget_pct
},
"ci": {
"woodpecker_queue_depth": $woodpecker_queue_depth,
"woodpecker_running": $woodpecker_running
},
"staleness_threshold_days": 1,
"verdict": "$verdict"
}
ENDJSON
echo "evidence/resources/$DATE.json written — verdict: $verdict"