#!/usr/bin/env bash # # job-loop.sh — self-contained headless test for the Buzz agent-job loop. # # Two `amy` accounts (alice = requester, bot = agent) talk through an embedded # relay (`amy serve`, i.e. geode — no external binary). Exercises the whole # drive-an-agent loop end to end: # # alice: buzz job request (kind-43001, targeting the bot) # bot: buzz agent serve --once (accept 43002 → progress 43003 → # run --exec → result 43004) # alice: buzz job show (BuzzJobAggregator folds it to state=completed) # # It also proves the permission gate: a responder whose --accept-from allowlist # excludes alice handles NOTHING; only an allowlisted requester is obeyed. # # Usage: ./job-loop.sh [--port N] [--no-build] # set -uo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd -- "$SCRIPT_DIR/../../.." && pwd)" STATE_DIR="$SCRIPT_DIR/state-job-loop" AMY_BIN="$REPO_ROOT/cli/build/install/amy/bin/amy" PORT=7799 BUILD=1 while [[ $# -gt 0 ]]; do case "$1" in --port) PORT="$2"; shift 2 ;; --no-build) BUILD=0; shift ;; *) echo "unknown arg: $1" >&2; exit 2 ;; esac done RELAY="ws://127.0.0.1:$PORT" if [[ $BUILD -eq 1 ]]; then echo "> building amy…" >&2 (cd "$REPO_ROOT" && ./gradlew -q :cli:installDist) || { echo "build failed" >&2; exit 1; } fi [[ -x "$AMY_BIN" ]] || { echo "amy not built at $AMY_BIN (drop --no-build)" >&2; exit 1; } rm -rf "$STATE_DIR"; mkdir -p "$STATE_DIR" REQ_HOME="$STATE_DIR/alice"; AGENT_HOME="$STATE_DIR/bot"; mkdir -p "$REQ_HOME" "$AGENT_HOME" RELAY_LOG="$STATE_DIR/relay.log" PASS=0; FAIL=0 RELAY_PID="" cleanup() { [[ -n "$RELAY_PID" ]] && kill "$RELAY_PID" 2>/dev/null; } trap cleanup EXIT run_req() { HOME="$REQ_HOME" "$AMY_BIN" --account alice --secret-backend plaintext --json "$@" 2>/dev/null; } run_agent() { HOME="$AGENT_HOME" "$AMY_BIN" --account bot --secret-backend plaintext --json "$@" 2>/dev/null; } jkey() { python3 -c ' import sys, json v = json.load(sys.stdin).get("'"$1"'", "") print(str(v).lower() if isinstance(v, bool) else v)' } check() { # check