Core tests / core (push) Successful in 9m57s
Full verification / application (push) Failing after 5m16s
Full verification / local-brain (push) Failing after 6m15s
Realm simulation / simulated-live-world (push) Failing after 5m32s
WoW integration lab / real-worldserver (push) Canceled after 17m23s
Source branch: feature/ai-brain-router Source commit: c2a09e0460465201a58aaab56c71424e9a96405e
411 lines
17 KiB
Bash
411 lines
17 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
BOT_COUNT="${AZEROTHAI_RANDOM_BOTS:-10}"
|
|
BOT_ACCOUNT_COUNT="${AZEROTHAI_RANDOM_BOT_ACCOUNTS:-30}"
|
|
|
|
if [[ ! "$BOT_COUNT" =~ ^[0-9]+$ ]] || (( BOT_COUNT < 1 || BOT_COUNT > 60 )); then
|
|
echo "AZEROTHAI_RANDOM_BOTS must be an integer from 1 to 60." >&2
|
|
exit 2
|
|
fi
|
|
if [[ ! "$BOT_ACCOUNT_COUNT" =~ ^[0-9]+$ ]] || (( BOT_ACCOUNT_COUNT < 1 || BOT_ACCOUNT_COUNT > 100 )); then
|
|
echo "AZEROTHAI_RANDOM_BOT_ACCOUNTS must be an integer from 1 to 100." >&2
|
|
exit 2
|
|
fi
|
|
|
|
find_lab_root(){
|
|
if [[ -n "${AZEROTHAI_PLAYABLE_ROOT:-}" && -d "$AZEROTHAI_PLAYABLE_ROOT/azerothcore" ]]; then
|
|
printf '%s' "$AZEROTHAI_PLAYABLE_ROOT"; return
|
|
fi
|
|
for root in "$HOME/azerothai-pc-lab" "$HOME/azerothai-playable"; do
|
|
if [[ -d "$root/azerothcore" ]]; then printf '%s' "$root"; return; fi
|
|
done
|
|
}
|
|
|
|
LAB_ROOT="$(find_lab_root || true)"
|
|
if [[ -z "$LAB_ROOT" ]]; then
|
|
echo "Could not find the playable AzerothCore lab. Set AZEROTHAI_PLAYABLE_ROOT first." >&2
|
|
exit 3
|
|
fi
|
|
ACORE_ROOT="$LAB_ROOT/azerothcore"
|
|
RUNTIME_ENV="$LAB_ROOT/runtime.env"
|
|
DEV_VARS="$REPO_ROOT/.dev.vars"
|
|
PLAYERBOTS_CONF=""
|
|
|
|
need(){ command -v "$1" >/dev/null 2>&1 || { echo "Missing required command: $1" >&2; exit 1; }; }
|
|
need docker
|
|
need curl
|
|
need jq
|
|
need openssl
|
|
need python3
|
|
need pnpm
|
|
|
|
cd "$ACORE_ROOT"
|
|
docker compose version >/dev/null
|
|
|
|
if [[ ! -f "$RUNTIME_ENV" ]]; then
|
|
echo "Missing $RUNTIME_ENV. Run the playable bootstrap first." >&2
|
|
exit 4
|
|
fi
|
|
# shellcheck disable=SC1090
|
|
source "$RUNTIME_ENV"
|
|
: "${ALE_BRIDGE_TOKEN:?Missing ALE_BRIDGE_TOKEN}"
|
|
: "${ALE_OPERATOR_TOKEN:?Missing ALE_OPERATOR_TOKEN}"
|
|
: "${DOCKER_DB_ROOT_PASSWORD:?Missing DOCKER_DB_ROOT_PASSWORD}"
|
|
|
|
echo "==> Running fail-fast preflight"
|
|
AZEROTHAI_PLAYABLE_ROOT="$LAB_ROOT" bash "$REPO_ROOT/deploy/playable/preflight.sh"
|
|
|
|
if [[ -z "${AUTONOMY_TOKEN:-}" ]]; then
|
|
AUTONOMY_TOKEN="$(openssl rand -hex 32)"
|
|
printf '\nAUTONOMY_TOKEN=%s\n' "$AUTONOMY_TOKEN" >> "$RUNTIME_ENV"
|
|
chmod 600 "$RUNTIME_ENV"
|
|
fi
|
|
|
|
echo "==> Updating AzerothAI private runtime token"
|
|
python3 - "$DEV_VARS" "$AUTONOMY_TOKEN" <<'PY'
|
|
from pathlib import Path
|
|
import sys
|
|
p=Path(sys.argv[1]); token=sys.argv[2]
|
|
lines=p.read_text().splitlines() if p.exists() else []
|
|
out=[]; seen=False
|
|
for line in lines:
|
|
if line.startswith("AUTONOMY_TOKEN="):
|
|
out.append("AUTONOMY_TOKEN="+token); seen=True
|
|
else:
|
|
out.append(line)
|
|
if not seen: out.append("AUTONOMY_TOKEN="+token)
|
|
p.write_text("\n".join(out).rstrip()+"\n")
|
|
PY
|
|
chmod 600 "$DEV_VARS"
|
|
|
|
echo "==> Installing latest AzerothAI ALE bridge"
|
|
mkdir -p "$ACORE_ROOT/azerothai-lua"
|
|
cp "$REPO_ROOT/ale/azerothai_bridge.lua" "$ACORE_ROOT/azerothai-lua/azerothai_bridge.lua"
|
|
python3 - "$ACORE_ROOT/azerothai-lua/azerothai_bridge.lua" <<'PY'
|
|
from pathlib import Path
|
|
import sys
|
|
p=Path(sys.argv[1])
|
|
s=p.read_text()
|
|
s=s.replace('local POLL_MS = tonumber(GetConfigValue("AzerothAI.PollMs")) or 750','local POLL_MS = 750')
|
|
s=s.replace('local PROTECTED = GetConfigValue("AzerothAI.ProtectedCharacter")','local PROTECTED = "Zaku"')
|
|
p.write_text(s)
|
|
PY
|
|
|
|
echo "==> Enabling native Playerbots that stay alive without a human online"
|
|
python3 - "$ACORE_ROOT/docker-compose.override.yml" "$BOT_COUNT" "$BOT_ACCOUNT_COUNT" <<'PY'
|
|
from pathlib import Path
|
|
import re,sys
|
|
p=Path(sys.argv[1]); count=sys.argv[2]; accounts=sys.argv[3]
|
|
s=p.read_text()
|
|
replacements={
|
|
"AC_AI_PLAYERBOT_ENABLED":'"1"',
|
|
"AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN":'"1"',
|
|
"AC_AI_PLAYERBOT_MIN_RANDOM_BOTS":f'"{count}"',
|
|
"AC_AI_PLAYERBOT_MAX_RANDOM_BOTS":f'"{count}"',
|
|
"AC_AI_PLAYERBOT_RANDOM_BOT_ACCOUNT_COUNT":f'"{accounts}"',
|
|
"AC_AI_PLAYERBOT_DISABLED_WITHOUT_REAL_PLAYER":'"0"',
|
|
"AC_AI_PLAYERBOT_BOT_ACTIVE_ALONE":'"100"',
|
|
"AC_AI_PLAYERBOT_BOT_ACTIVE_ALONE_SMART_SCALE":'"0"',
|
|
"AC_AI_PLAYERBOT_RANDOM_BOTS_PER_INTERVAL":'"12"',
|
|
"AC_AI_PLAYERBOT_AUTO_DO_QUESTS":'"1"',
|
|
"AC_AI_PLAYERBOT_ENABLE_NEW_RPG_STRATEGY":'"1"',
|
|
"AC_AI_PLAYERBOT_MIN_RANDOM_BOT_RANDOMIZE_TIME":'"31104000"',
|
|
"AC_AI_PLAYERBOT_MAX_RANDOM_BOT_RANDOMIZE_TIME":'"31104000"',
|
|
"AC_AI_PLAYERBOT_MIN_RANDOM_BOT_TELEPORT_INTERVAL":'"31104000"',
|
|
"AC_AI_PLAYERBOT_MAX_RANDOM_BOT_TELEPORT_INTERVAL":'"31104000"',
|
|
"AC_AI_PLAYERBOT_PERMANENTLY_IN_WORLD_TIME":'"31104000"',
|
|
"AC_AI_PLAYERBOT_RANDOM_BOT_TALK":'"0"',
|
|
"AC_AI_PLAYERBOT_RANDOM_BOT_EMOTE":'"0"',
|
|
"AC_AI_PLAYERBOT_RANDOM_BOT_SAY_WITHOUT_MASTER":'"0"',
|
|
"AC_AI_PLAYERBOT_COMMAND_PREFIX":'"!"',
|
|
"AC_AI_PLAYERBOT_RANDOM_BOT_JOIN_LFG":'"1"',
|
|
}
|
|
lines=s.splitlines()
|
|
present=set()
|
|
out=[]
|
|
for line in lines:
|
|
m=re.match(r'(\s*)(AC_[A-Z0-9_]+):\s*.*$',line)
|
|
if m and m.group(2) in replacements:
|
|
key=m.group(2); out.append(f'{m.group(1)}{key}: {replacements[key]}'); present.add(key)
|
|
else:
|
|
out.append(line)
|
|
# Add missing settings to the ac-worldserver environment block directly after the known autologin line.
|
|
for key,value in replacements.items():
|
|
if key not in present:
|
|
inserted=False
|
|
for i,line in enumerate(out):
|
|
if 'AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN:' in line:
|
|
indent=line[:len(line)-len(line.lstrip())]
|
|
out.insert(i+1,f'{indent}{key}: {value}')
|
|
inserted=True
|
|
break
|
|
if not inserted:
|
|
raise SystemExit(f'Could not locate ac-worldserver Playerbot environment block for {key}')
|
|
p.write_text("\n".join(out)+"\n")
|
|
PY
|
|
|
|
# AzerothCore itself maps AC_* environment variables to config keys. If a
|
|
# writable module config already exists, mirror the same settings there for
|
|
# operator readability, but do not require a particular module-config layout.
|
|
for candidate in "$LAB_ROOT/etc/modules/playerbots.conf" "$LAB_ROOT/etc/playerbots.conf"; do
|
|
if [[ -f "$candidate" ]]; then PLAYERBOTS_CONF="$candidate"; break; fi
|
|
done
|
|
if [[ -n "$PLAYERBOTS_CONF" ]]; then
|
|
echo "==> Mirroring autonomous settings into $PLAYERBOTS_CONF"
|
|
python3 - "$PLAYERBOTS_CONF" "$BOT_COUNT" "$BOT_ACCOUNT_COUNT" <<'PY'
|
|
from pathlib import Path
|
|
import re,sys
|
|
p=Path(sys.argv[1]); count=sys.argv[2]; accounts=sys.argv[3]
|
|
updates={
|
|
"AiPlayerbot.Enabled":"1",
|
|
"AiPlayerbot.RandomBotAutologin":"1",
|
|
"AiPlayerbot.MinRandomBots":count,
|
|
"AiPlayerbot.MaxRandomBots":count,
|
|
"AiPlayerbot.RandomBotAccountCount":accounts,
|
|
"AiPlayerbot.DisabledWithoutRealPlayer":"0",
|
|
"AiPlayerbot.BotActiveAlone":"100",
|
|
"AiPlayerbot.botActiveAloneSmartScale":"0",
|
|
"AiPlayerbot.RandomBotsPerInterval":"12",
|
|
"AiPlayerbot.AutoDoQuests":"1",
|
|
"AiPlayerbot.EnableNewRpgStrategy":"1",
|
|
"AiPlayerbot.MinRandomBotRandomizeTime":"31104000",
|
|
"AiPlayerbot.MaxRandomBotRandomizeTime":"31104000",
|
|
"AiPlayerbot.MinRandomBotTeleportInterval":"31104000",
|
|
"AiPlayerbot.MaxRandomBotTeleportInterval":"31104000",
|
|
"AiPlayerbot.PermanentlyInWorldTime":"31104000",
|
|
"AiPlayerbot.RandomBotTalk":"0",
|
|
"AiPlayerbot.RandomBotEmote":"0",
|
|
"AiPlayerbot.RandomBotSayWithoutMaster":"0",
|
|
"AiPlayerbot.CommandPrefix":"!",
|
|
"AiPlayerbot.RandomBotJoinLfg":"1",
|
|
"AiPlayerbot.EnablePeriodicOnlineOffline":"0",
|
|
}
|
|
lines=p.read_text().splitlines(); seen=set(); out=[]
|
|
for line in lines:
|
|
m=re.match(r'\s*([A-Za-z0-9_.]+)\s*=.*$',line)
|
|
if m and m.group(1) in updates:
|
|
key=m.group(1); out.append(f'{key} = {updates[key]}'); seen.add(key)
|
|
else:
|
|
out.append(line)
|
|
for key,value in updates.items():
|
|
if key not in seen: out.append(f'{key} = {value}')
|
|
p.write_text("\n".join(out)+"\n")
|
|
PY
|
|
else
|
|
echo "==> No writable playerbots.conf found; using AzerothCore AC_* environment configuration"
|
|
fi
|
|
|
|
echo "==> Rebuilding/restarting AzerothAI with live autonomy endpoint"
|
|
cd "$REPO_ROOT"
|
|
pnpm build
|
|
if [[ -f "$LAB_ROOT/azerothai.pid" ]]; then
|
|
pid="$(cat "$LAB_ROOT/azerothai.pid" 2>/dev/null || true)"
|
|
[[ -n "$pid" ]] && kill "$pid" 2>/dev/null || true
|
|
fi
|
|
pkill -f "wrangler.*8787" 2>/dev/null || true
|
|
pkill -f "pnpm.*start:lan" 2>/dev/null || true
|
|
sleep 2
|
|
nohup pnpm start:lan >"$LAB_ROOT/azerothai.log" 2>&1 &
|
|
echo $! > "$LAB_ROOT/azerothai.pid"
|
|
for _ in {1..90}; do
|
|
curl -fsS http://127.0.0.1:8787/api/health >/dev/null 2>&1 && break
|
|
sleep 1
|
|
done
|
|
curl -fsS http://127.0.0.1:8787/api/health >/tmp/azerothai-autonomy-health.json
|
|
|
|
echo "==> Starting durable ALE database relay"
|
|
bash "$REPO_ROOT/deploy/playable/install-ale-relay-service.sh"
|
|
|
|
if jq -e '.llmConfigured == true' /tmp/azerothai-autonomy-health.json >/dev/null 2>&1; then
|
|
echo "==> Selecting Qwen/Ollama for foreground and hero high-level intentions"
|
|
curl -fsS http://127.0.0.1:8787/api/state \
|
|
| jq '{type:"settings",connector:"azerothcore",provider:"ollama",model:(.settings.brains.residentModel // "qwen3.5:4b"),brains:(.settings.brains + {backgroundUseLLM:true}),hardcore:.settings.hardcore,population:.settings.population}' \
|
|
>/tmp/azerothai-autonomy-settings.json
|
|
curl -fsS -X POST -H "Content-Type: application/json" --data-binary @/tmp/azerothai-autonomy-settings.json \
|
|
http://127.0.0.1:8787/api/actions >/dev/null
|
|
fi
|
|
|
|
echo "==> Restarting worldserver with autonomous Playerbot configuration"
|
|
cd "$ACORE_ROOT"
|
|
docker compose up -d --no-deps --force-recreate ac-worldserver
|
|
|
|
for _ in {1..180}; do
|
|
if curl -fsS http://127.0.0.1:8787/api/world-monitor 2>/dev/null | jq -e '.ale.bridgeReady == true' >/dev/null 2>&1; then break; fi
|
|
sleep 2
|
|
done
|
|
|
|
rndbot_accounts(){
|
|
docker exec ac-database mysql -uroot -p"$DOCKER_DB_ROOT_PASSWORD" -Nse "SELECT COUNT(*) FROM acore_auth.account WHERE UPPER(username) LIKE 'RNDBOT%'" 2>/dev/null || echo 0
|
|
}
|
|
RND_ACCOUNTS="$(rndbot_accounts)"
|
|
if [[ "$RND_ACCOUNTS" =~ ^[0-9]+$ ]] && (( RND_ACCOUNTS == 0 )); then
|
|
echo "==> Initializing the native random Playerbot pool for the first time"
|
|
echo " This creates a persistent character pool; later runs preserve it."
|
|
curl -fsS -X POST \
|
|
-H "Authorization: Bearer $ALE_OPERATOR_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
--data '{"kind":"command","command":"playerbots rndbot init"}' \
|
|
http://127.0.0.1:8787/api/ale/operator >/tmp/azerothai-rndbot-init.json
|
|
for _ in {1..240}; do
|
|
RND_ACCOUNTS="$(rndbot_accounts)"
|
|
[[ "$RND_ACCOUNTS" =~ ^[0-9]+$ ]] && (( RND_ACCOUNTS >= BOT_ACCOUNT_COUNT )) && break
|
|
sleep 2
|
|
done
|
|
if ! [[ "$RND_ACCOUNTS" =~ ^[0-9]+$ ]] || (( RND_ACCOUNTS == 0 )); then
|
|
echo "Random bot initialization was queued but no RNDBOT accounts appeared yet." >&2
|
|
echo "Check: cd $ACORE_ROOT && docker compose logs --tail=200 ac-worldserver" >&2
|
|
exit 6
|
|
fi
|
|
echo "Random bot accounts created: $RND_ACCOUNTS"
|
|
fi
|
|
|
|
echo "==> Teaching the ALE bridge which accounts are native Playerbots"
|
|
BOT_ACCOUNT_IDS="$(docker exec ac-database mysql -uroot -p"$DOCKER_DB_ROOT_PASSWORD" -Nse "SELECT id FROM acore_auth.account WHERE UPPER(username) LIKE 'RNDBOT%';" 2>/dev/null | tr '\n' ' ' | xargs || true)"
|
|
if [[ -z "$BOT_ACCOUNT_IDS" ]]; then
|
|
echo "Could not read RNDBOT account IDs for ALE bot classification." >&2
|
|
exit 6
|
|
fi
|
|
python3 - "$ACORE_ROOT/azerothai-lua/azerothai_bridge.lua" "$BOT_ACCOUNT_IDS" <<'PY'
|
|
from pathlib import Path
|
|
import sys
|
|
p=Path(sys.argv[1])
|
|
ids=[x for x in sys.argv[2].split() if x.isdigit()]
|
|
table=", ".join(f"[{x}]=true" for x in ids)
|
|
s=p.read_text()
|
|
old="local KNOWN_BOT_ACCOUNTS = {}"
|
|
new=f"local KNOWN_BOT_ACCOUNTS = {{{table}}}"
|
|
if old not in s:
|
|
raise SystemExit("ALE bridge bot-account marker not found")
|
|
p.write_text(s.replace(old,new,1))
|
|
PY
|
|
|
|
|
|
# Give Zaku a physical Undead Warlock body from the persistent random-bot pool.
|
|
# Database identity work happens only while worldserver is stopped.
|
|
EXISTING_ZAKU="$(docker exec ac-database mysql -uroot -p"$DOCKER_DB_ROOT_PASSWORD" -Nse "SELECT CONCAT(c.guid,'|',c.account) FROM acore_characters.characters c WHERE LOWER(c.name)='zaku' LIMIT 1" 2>/dev/null || true)"
|
|
if [[ -z "$EXISTING_ZAKU" ]]; then
|
|
ZAKU_ROW="$(docker exec ac-database mysql -uroot -p"$DOCKER_DB_ROOT_PASSWORD" -Nse "SELECT CONCAT(c.guid,'|',c.account,'|',c.name) FROM acore_characters.characters c JOIN acore_auth.account a ON a.id=c.account WHERE UPPER(a.username) LIKE 'RNDBOT%' AND c.race=5 AND c.class=9 ORDER BY c.guid LIMIT 1" 2>/dev/null || true)"
|
|
if [[ -n "$ZAKU_ROW" ]]; then
|
|
IFS='|' read -r ZAKU_GUID ZAKU_ACCOUNT ZAKU_OLD_NAME <<<"$ZAKU_ROW"
|
|
echo "==> Binding persistent hero Zaku to a real Undead Warlock Playerbot"
|
|
docker compose stop ac-worldserver >/dev/null
|
|
docker exec ac-database mysql -uroot -p"$DOCKER_DB_ROOT_PASSWORD" acore_characters -e "UPDATE characters SET name='Zaku' WHERE guid=$ZAKU_GUID;"
|
|
docker exec ac-database mysql -uroot -p"$DOCKER_DB_ROOT_PASSWORD" acore_playerbots -e "INSERT INTO playerbots_account_type (account_id,account_type) VALUES ($ZAKU_ACCOUNT,1) ON DUPLICATE KEY UPDATE account_type=1, assignment_date=CURRENT_TIMESTAMP;"
|
|
echo "Zaku body: guid $ZAKU_GUID (renamed from $ZAKU_OLD_NAME)"
|
|
else
|
|
echo "No Undead Warlock candidate was found in the Playerbot pool; Zaku remains unbound until one exists." >&2
|
|
fi
|
|
else
|
|
echo "Zaku character already exists; preserving it."
|
|
fi
|
|
|
|
echo "==> Restarting worldserver so the autonomous pool can login"
|
|
docker compose up -d --no-deps --force-recreate ac-worldserver
|
|
|
|
echo "==> Waiting for autonomous Playerbots to be physically online"
|
|
RND_ONLINE=0
|
|
for _ in {1..180}; do
|
|
RND_ONLINE="$(docker exec ac-database mysql -uroot -p"$DOCKER_DB_ROOT_PASSWORD" -Nse "SELECT COUNT(*) FROM acore_characters.characters c JOIN acore_auth.account a ON a.id=c.account WHERE c.online=1 AND UPPER(a.username) LIKE 'RNDBOT%'" 2>/dev/null || echo 0)"
|
|
[[ "$RND_ONLINE" =~ ^[0-9]+$ ]] && (( RND_ONLINE > 0 )) && break
|
|
sleep 2
|
|
done
|
|
if ! [[ "$RND_ONLINE" =~ ^[0-9]+$ ]] || (( RND_ONLINE == 0 )); then
|
|
echo "No autonomous Playerbots are physically online after restart." >&2
|
|
echo "Check: cd $ACORE_ROOT && docker compose logs --tail=250 ac-worldserver" >&2
|
|
exit 7
|
|
fi
|
|
echo "Physical Playerbots online: $RND_ONLINE"
|
|
|
|
echo "==> Starting persistent AzerothAI high-level autonomy service"
|
|
bash "$REPO_ROOT/deploy/playable/install-autonomy-service.sh"
|
|
|
|
echo "==> Waiting for authoritative Playerbot snapshots"
|
|
OBSERVED=0
|
|
MANAGED=0
|
|
for _ in {1..180}; do
|
|
data="$(curl -fsS http://127.0.0.1:8787/api/world-monitor 2>/dev/null || true)"
|
|
if [[ -n "$data" ]]; then
|
|
OBSERVED="$(printf '%s' "$data" | jq -r '.playerbots.observedBots // 0' 2>/dev/null || echo 0)"
|
|
fi
|
|
if [[ "$OBSERVED" =~ ^[0-9]+$ ]] && (( OBSERVED > 0 )); then
|
|
sleep 2
|
|
MANAGED="$(curl -fsS http://127.0.0.1:8787/api/state | jq '[.bots[] | select(.autonomy.managed == true)] | length' 2>/dev/null || echo 0)"
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
if ! [[ "$OBSERVED" =~ ^[0-9]+$ ]] || (( OBSERVED == 0 )); then
|
|
echo "Playerbots are online, but AzerothAI received no authoritative Playerbot snapshots." >&2
|
|
echo "Check ALE bridge/playerbot bindings in the worldserver log." >&2
|
|
exit 8
|
|
fi
|
|
if ! [[ "$MANAGED" =~ ^[0-9]+$ ]] || (( MANAGED == 0 )); then
|
|
echo "AzerothAI received Playerbot events but did not adopt any managed live residents." >&2
|
|
exit 9
|
|
fi
|
|
|
|
echo "==> Verifying ALE command round trip through the durable relay"
|
|
VERIFY_RESPONSE="$(curl -fsS -X POST \
|
|
-H "Authorization: Bearer $ALE_OPERATOR_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
--data '{"kind":"global","method":"GetPlayerCount","args":[]}' \
|
|
http://127.0.0.1:8787/api/ale/operator)"
|
|
VERIFY_COMMAND_ID="$(printf '%s' "$VERIFY_RESPONSE" | jq -r '.command.id // empty')"
|
|
if [[ -z "$VERIFY_COMMAND_ID" ]]; then
|
|
echo "Could not queue ALE verification command." >&2
|
|
printf '%s\n' "$VERIFY_RESPONSE" >&2
|
|
exit 10
|
|
fi
|
|
|
|
VERIFY_STATUS=""
|
|
for _ in {1..120}; do
|
|
VERIFY_STATUS="$(curl -fsS -H "Authorization: Bearer $ALE_OPERATOR_TOKEN" http://127.0.0.1:8787/api/ale/status \
|
|
| jq -r --arg id "$VERIFY_COMMAND_ID" '.commands[]? | select(.id==$id) | .status' | head -n1)"
|
|
if [[ "$VERIFY_STATUS" == "completed" ]]; then break; fi
|
|
if [[ "$VERIFY_STATUS" == "failed" ]]; then
|
|
echo "ALE verification command failed." >&2
|
|
curl -fsS -H "Authorization: Bearer $ALE_OPERATOR_TOKEN" http://127.0.0.1:8787/api/ale/status \
|
|
| jq -r --arg id "$VERIFY_COMMAND_ID" '.commands[]? | select(.id==$id)' >&2 || true
|
|
exit 10
|
|
fi
|
|
sleep 1
|
|
done
|
|
if [[ "$VERIFY_STATUS" != "completed" ]]; then
|
|
echo "ALE command round trip did not complete within 120 seconds." >&2
|
|
echo "Relay service:" >&2
|
|
sudo systemctl status azerothai-ale-relay.service --no-pager -l >&2 || true
|
|
echo "Relay log:" >&2
|
|
tail -80 "$LAB_ROOT/ale-relay.log" >&2 || true
|
|
echo "Spool counts:" >&2
|
|
docker exec ac-database mysql -uroot -p"$DOCKER_DB_ROOT_PASSWORD" -Nse \
|
|
"SELECT 'commands',COUNT(*) FROM acore_auth.azerothai_command_inbox WHERE consumed_at IS NULL UNION ALL SELECT 'events',COUNT(*) FROM acore_auth.azerothai_event_outbox WHERE delivered_at IS NULL UNION ALL SELECT 'results',COUNT(*) FROM acore_auth.azerothai_result_outbox WHERE delivered_at IS NULL" >&2 || true
|
|
exit 10
|
|
fi
|
|
|
|
echo
|
|
echo "============================================================"
|
|
echo " AUTONOMOUS AZEROTHAI WORLD ENABLED"
|
|
echo "============================================================"
|
|
echo "Target native Playerbots : $BOT_COUNT"
|
|
echo "Physical bots online : $RND_ONLINE"
|
|
echo "RNDBOT accounts : $RND_ACCOUNTS"
|
|
echo "Authoritative bot events : $OBSERVED"
|
|
echo "Managed AzerothAI lives : $MANAGED"
|
|
echo "ALE command round trip : completed"
|
|
echo "Autonomy worker PID : $(cat "$LAB_ROOT/autonomy.pid")"
|
|
if sudo systemctl is-active --quiet azerothai-ale-relay.service 2>/dev/null; then
|
|
echo "ALE transport : durable DB relay active"
|
|
else
|
|
echo "ALE transport : relay NOT active"
|
|
fi
|
|
echo
|
|
echo "The native Playerbot AI now keeps playing with no human logged in."
|
|
echo "AzerothAI adds periodic identity/personality/memory-driven high-level intentions"
|
|
echo "only for Playerbots that are authoritatively observed in the live realm."
|
|
echo
|
|
echo "Watch:"
|
|
echo " tail -f $LAB_ROOT/autonomy.log"
|
|
echo " bash $REPO_ROOT/deploy/playable/status.sh"
|