#!/usr/bin/env python3 from __future__ import annotations import json import os import shutil import subprocess import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[2] if str(REPO_ROOT) not in sys.path: sys.path.insert(0, str(REPO_ROOT)) from evennia_tools.telemetry import event_log_path, session_meta_path from evennia_tools.training import WORLD_BASICS_STEPS, example_trace_path from scripts.evennia import evennia_mcp_server as bridge EVENNIA_BIN = Path.home() / '.timmy' / 'evennia' / 'venv' / 'bin' / 'evennia' GAME_DIR = Path.home() / '.timmy' / 'evennia' / 'timmy_world' SESSION_ID = os.environ.get("TIMMY_EVENNIA_SAMPLE_SESSION_ID", "sample-evennia-world-basics") EVAL_USERNAME = os.environ.get("TIMMY_EVENNIA_EVAL_USERNAME", "TimmyEval") EVAL_PASSWORD = os.environ.get("TIMMY_EVENNIA_EVAL_PASSWORD", "timmy-eval-world-dev") def reset_timmy_to_gate(): env = dict(**os.environ) env['PYTHONPATH'] = str(REPO_ROOT) + ':' + env.get('PYTHONPATH', '') code = f"from evennia.accounts.models import AccountDB; from evennia.accounts.accounts import DefaultAccount; from evennia.utils.search import search_object; acc=AccountDB.objects.filter(username='{EVAL_USERNAME}').first(); acc = acc or DefaultAccount.create(username='{EVAL_USERNAME}', password='{EVAL_PASSWORD}')[0]; char=list(acc.characters)[0]; gate=search_object('Gate', exact=True)[0]; char.home=gate; char.move_to(gate, quiet=True, move_hooks=False); char.save(); print('RESET_OK')" subprocess.run([str(EVENNIA_BIN), 'shell', '-c', code], cwd=GAME_DIR, env=env, check=True, capture_output=True, text=True, timeout=120) def normalize_to_gate() -> None: output = bridge._observe("timmy").get("output", "") if not output: output = bridge._command("look", name="timmy", wait_ms=400).get("output", "") for _ in range(6): if "Gate" in output: return if "Courtyard" in output: output = bridge._command("gate", name="timmy", wait_ms=400).get("output", "") continue if any(room in output for room in ("Workshop", "Archive", "Chapel")): output = bridge._command("courtyard", name="timmy", wait_ms=400).get("output", "") continue output = bridge._command("look", name="timmy", wait_ms=400).get("output", "") def main(): try: bridge._disconnect("timmy") except Exception: pass reset_timmy_to_gate() bridge._save_bound_session_id(SESSION_ID) bridge._connect(name="timmy", username=EVAL_USERNAME, password=EVAL_PASSWORD) normalize_to_gate() for step in WORLD_BASICS_STEPS: bridge._command(step["command"], name="timmy", wait_ms=400) bridge._disconnect("timmy") log_path = event_log_path(SESSION_ID) meta_path = session_meta_path(SESSION_ID) repo_trace = example_trace_path(REPO_ROOT) repo_trace.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(log_path, repo_trace) print(json.dumps({"session_id": SESSION_ID, "log_path": str(log_path), "meta_path": str(meta_path), "repo_trace": str(repo_trace)}, indent=2)) if __name__ == "__main__": main()