Skip to main content
Glama
peekaboo-perf.sh5.08 kB
#!/bin/bash set -euo pipefail NAME="" RUNS=10 LOG_ROOT="${LOG_ROOT:-$PWD/.artifacts/playground-tools}" BIN="${PEEKABOO_BIN:-$PWD/peekaboo}" usage() { cat <<'EOF' Usage: peekaboo-perf.sh --name <slug> [--runs N] [--log-root DIR] [--bin PATH] -- <peekaboo args...> Runs a Peekaboo CLI command N times, captures JSON output per run, and writes a summary JSON with mean/median/p95/min/max based on `data.execution_time` (falls back to wall time if missing). Examples: ./Apps/Playground/scripts/peekaboo-perf.sh --name see-click-fixture --runs 10 -- \ see --app boo.peekaboo.playground.debug --mode window --window-title "Click Fixture" --json-output ./Apps/Playground/scripts/peekaboo-perf.sh --name click-single --runs 20 -- \ click "Single Click" --snapshot <id> --app boo.peekaboo.playground.debug --json-output EOF } while [[ $# -gt 0 ]]; do case "$1" in --name) NAME="${2:-}" shift 2 ;; --runs) RUNS="${2:-}" shift 2 ;; --log-root) LOG_ROOT="${2:-}" shift 2 ;; --bin) BIN="${2:-}" shift 2 ;; -h|--help) usage exit 0 ;; --) shift break ;; *) echo "Unknown option: $1" >&2 usage >&2 exit 2 ;; esac done if [[ -z "$NAME" ]]; then echo "--name is required" >&2 usage >&2 exit 2 fi if [[ $# -eq 0 ]]; then echo "Missing peekaboo args after --" >&2 usage >&2 exit 2 fi if [[ ! -x "$BIN" ]]; then echo "Peekaboo binary not executable: $BIN" >&2 echo "Tip: set PEEKABOO_BIN=/path/to/peekaboo or pass --bin" >&2 exit 2 fi mkdir -p "$LOG_ROOT" TS="$(date +%Y%m%d-%H%M%S)" PATTERN="$LOG_ROOT/${TS}-${NAME}-*.json" SUMMARY="$LOG_ROOT/${TS}-${NAME}-summary.json" echo "Running $RUNS iterations:" echo "- bin: $BIN" echo "- out: $LOG_ROOT" echo "- cmd: $*" for i in $(seq 1 "$RUNS"); do OUT="$LOG_ROOT/${TS}-${NAME}-${i}.json" START="$(python3 - <<'PY' import time print(time.time()) PY )" set +e "$BIN" "$@" >"$OUT" EXIT_CODE="$?" set -e END="$(python3 - <<'PY' import time print(time.time()) PY )" WALL="$(python3 - <<PY start=float("$START") end=float("$END") print(end-start) PY )" if [[ "$EXIT_CODE" -ne 0 ]]; then echo "Run $i failed (exit=$EXIT_CODE): $OUT" >&2 fi python3 - <<PY import json from pathlib import Path path = Path("$OUT") raw = path.read_text() try: data = json.loads(raw) except Exception: data = {"success": False, "data": {}, "raw_output": raw} if not isinstance(data, dict): data = {"success": False, "data": {}, "raw_output": raw} if "data" not in data or not isinstance(data.get("data"), dict): data["data"] = {} data["data"]["wall_time"] = float("$WALL") data["data"]["exit_code"] = int("$EXIT_CODE") path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n") PY echo "- $i/$RUNS -> $OUT (wall=${WALL}s, exit=$EXIT_CODE)" done export PEEKABOO_PERF_COMMAND="$BIN $*" python3 - <<PY import glob import json import math import os from pathlib import Path paths = [p for p in sorted(glob.glob("$PATTERN")) if not p.endswith("-summary.json")] summary_path = Path("$SUMMARY") command_str = os.environ.get("PEEKABOO_PERF_COMMAND", "") def percentile(sorted_values, pct): if not sorted_values: return None if len(sorted_values) == 1: return sorted_values[0] k = (len(sorted_values) - 1) * pct f = math.floor(k) c = math.ceil(k) if f == c: return sorted_values[int(k)] d0 = sorted_values[int(f)] * (c - k) d1 = sorted_values[int(c)] * (k - f) return d0 + d1 execution_times = [] wall_times = [] failures = [] for p in paths: raw = Path(p).read_text() payload = json.loads(raw) data = payload.get("data", {}) or {} exit_code = int(data.get("exit_code", 0)) if exit_code != 0: failures.append({"path": p, "exit_code": exit_code}) exec_t = data.get("execution_time") if exec_t is None: exec_t = data.get("executionTime") if exec_t is None: exec_t = data.get("execution_time_s") if exec_t is None: exec_t = data.get("executionTimeSeconds") wall_t = data.get("wall_time") if isinstance(exec_t, (int, float)): execution_times.append(float(exec_t)) if isinstance(wall_t, (int, float)): wall_times.append(float(wall_t)) execution_times_sorted = sorted(execution_times) wall_times_sorted = sorted(wall_times) def stats(values_sorted): if not values_sorted: return None n = len(values_sorted) mean = sum(values_sorted) / n median = percentile(values_sorted, 0.50) p95 = percentile(values_sorted, 0.95) return { "n": n, "samples_s": values_sorted, "mean_s": mean, "median_s": median, "p95_s": p95, "min_s": values_sorted[0], "max_s": values_sorted[-1], } summary = { "pattern": "$PATTERN", "command": command_str, "timestamp": "$TS", "execution_time": stats(execution_times_sorted), "wall_time": stats(wall_times_sorted), "failures": failures, } summary_path.write_text(json.dumps(summary, indent=2, sort_keys=True) + "\n") print(str(summary_path)) PY echo "Summary: $SUMMARY"

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/steipete/Peekaboo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server