#!/usr/bin/env bash # e2e-opencode-stack.sh — one-shot verifier for the "day one" OpenCode + LiteLLM + # agentmemory scratch-dir test (docs/TEST-opencode-litellm-agentmemory.md, Steps 2–5). # # Served publicly from the static host, so you can run it in one line: # curl -fsSL https://static-ai-stack.dc7.io/verify | bash # tests $PWD # curl -fsSL https://static-ai-stack.dc7.io/verify | bash -s -- /path # tests /path # # Or locally: # scripts/static/e2e-opencode-stack.sh /path/to/scratch-dir # # Run it against an already-installed scratch dir (where you ran the two # `curl … | bash install` one-liners and `opencode mcp auth agentmemory`). # Defaults to the current directory if no path is given. Exits non-zero if any # check fails, so it's CI/task friendly. # # What it checks (no browser needed once install+auth are done): # 1. config — .opencode/opencode.json merged: default model, ~28 LiteLLM models, # memory MCP = remote, capture plugin listed # 2. plugin — installed plugin is the self-updating loader (has If-None-Match) # 3. inference — `opencode run` round-trips through LiteLLM (Step 3) # 4. memory — agentmemory MCP memory_recall returns a result (Step 4) # 5. selfupdate — loader does a conditional GET (200 first / 304 cached) (Step 5) set -uo pipefail DIR="${1:-$PWD}" DIR="$(cd "$DIR" 2>/dev/null && pwd || true)" PASS=0; FAIL=0 ok() { echo " ✅ $1"; PASS=$((PASS+1)); } bad() { echo " ❌ $1"; FAIL=$((FAIL+1)); } hdr() { echo; echo "── $1 ──"; } [ -n "$DIR" ] || { echo "❌ target dir not found: ${1:-$PWD}"; exit 2; } echo "Target scratch dir: $DIR" CFG="$DIR/.opencode/opencode.json" PLUGIN="$DIR/.opencode/plugin/agentmemory-capture.ts" # --- deps --- for bin in opencode jq; do command -v "$bin" >/dev/null 2>&1 || { echo "❌ missing dependency: $bin"; exit 2; } done # --- 1. merged config --- hdr "1. merged config ($CFG)" if [ -f "$CFG" ]; then model=$(jq -r '.model // empty' "$CFG") nmodels=$(jq -r '.provider["ai-stack"].models | keys | length' "$CFG" 2>/dev/null || echo 0) mcp=$(jq -r '.mcp.agentmemory.type // empty' "$CFG") plug=$(jq -r '.plugin[0] // empty' "$CFG") [ "$model" = "ai-stack/cliproxy/claude-sonnet-5" ] \ && ok "default model: $model" \ || bad "default model unexpected: '${model:-}'" [ "${nmodels:-0}" -ge 20 ] \ && ok "LiteLLM models: $nmodels" \ || bad "too few LiteLLM models: ${nmodels:-0} (expected ~28)" [ "$mcp" = "remote" ] \ && ok "memory MCP type: $mcp" \ || bad "memory MCP type unexpected: '${mcp:-}'" [ -n "$plug" ] \ && ok "capture plugin listed: $plug" \ || bad "no capture plugin in config" else bad "config not found — did you run the two installers in this dir?" fi # --- 2. self-updating loader --- hdr "2. self-updating loader" if [ -f "$PLUGIN" ] && grep -q 'If-None-Match' "$PLUGIN"; then ok "plugin is the self-updating loader (If-None-Match present)" else bad "plugin missing or not the self-updating loader: $PLUGIN" fi # --- 3. inference (LiteLLM) --- hdr "3. inference via LiteLLM" inf_out="$(cd "$DIR" && opencode run "Reply with exactly: LITELLM OK" 2>&1)" if grep -q "LITELLM OK" <<<"$inf_out"; then ok "opencode run round-tripped through LiteLLM" else bad "inference did not return 'LITELLM OK'" echo " ↳ last output: $(tail -n1 <<<"$inf_out")" fi # --- 4. memory (agentmemory MCP) --- hdr "4. memory via agentmemory MCP" mem_out="$(cd "$DIR" && opencode run \ 'Call the agentmemory MCP tool memory_recall with query "opencode" and limit 1. Print ONLY the title of the top result.' 2>&1)" if grep -qiE 'auth|logout|login|401|403|unauthor' <<<"$mem_out"; then bad "memory call looks unauthenticated — run: (cd $DIR && opencode mcp auth agentmemory)" echo " ↳ last output: $(tail -n1 <<<"$mem_out")" elif grep -q 'agentmemory_memory_recall' <<<"$mem_out" || [ -n "$(tail -n1 <<<"$mem_out")" ]; then ok "memory_recall invoked and returned output" echo " ↳ top result: $(tail -n1 <<<"$mem_out")" else bad "memory_recall produced no output" fi # --- 5. self-update at session start (200 first / 304 cached) --- hdr "5. loader conditional GET" su_out="$(cd "$DIR" && OPENCODE_AGENTMEMORY_DEBUG=1 opencode run "list two files in this dir" 2>&1 | grep agentmemory-loader)" if grep -qE '304 not-modified|updated capture plugin' <<<"$su_out"; then ok "loader did conditional GET: $(tail -n1 <<<"$su_out" | sed 's/^[[:space:]]*//')" else bad "no agentmemory-loader debug line seen" [ -n "$su_out" ] && echo " ↳ $su_out" fi # --- summary --- echo echo "════════════════════════════════" echo " RESULT: PASS=$PASS FAIL=$FAIL" if [ "$FAIL" -eq 0 ]; then echo " ✅ ALL CHECKS PASSED" exit 0 else echo " ❌ $FAIL CHECK(S) FAILED" exit 1 fi