diff --git a/nexus/evennia_ws_bridge.py b/nexus/evennia_ws_bridge.py index 3820d377..c578cfae 100644 --- a/nexus/evennia_ws_bridge.py +++ b/nexus/evennia_ws_bridge.py @@ -243,24 +243,108 @@ async def playback(log_path: Path, ws_url: str): await ws.send(json.dumps(event)) +async def inject_event(event_type: str, ws_url: str, **kwargs): + """Inject a single Evennia event into the Nexus WS gateway. Dev/test use.""" + from nexus.evennia_event_adapter import ( + actor_located, command_issued, command_result, + room_snapshot, session_bound, + ) + + builders = { + "room_snapshot": lambda: room_snapshot( + kwargs.get("room_key", "Gate"), + kwargs.get("title", "Gate"), + kwargs.get("desc", "The entrance gate."), + exits=kwargs.get("exits"), + objects=kwargs.get("objects"), + ), + "actor_located": lambda: actor_located( + kwargs.get("actor_id", "Timmy"), + kwargs.get("room_key", "Gate"), + kwargs.get("room_name"), + ), + "command_result": lambda: command_result( + kwargs.get("session_id", "dev-inject"), + kwargs.get("actor_id", "Timmy"), + kwargs.get("command_text", "look"), + kwargs.get("output_text", "You see the Gate."), + success=kwargs.get("success", True), + ), + "command_issued": lambda: command_issued( + kwargs.get("session_id", "dev-inject"), + kwargs.get("actor_id", "Timmy"), + kwargs.get("command_text", "look"), + ), + "session_bound": lambda: session_bound( + kwargs.get("session_id", "dev-inject"), + kwargs.get("account", "Timmy"), + kwargs.get("character", "Timmy"), + ), + } + + if event_type not in builders: + print(f"[inject] Unknown event type: {event_type}", flush=True) + print(f"[inject] Available: {', '.join(builders)}", flush=True) + sys.exit(1) + + event = builders[event_type]() + payload = json.dumps(event) + + if websockets is None: + print(f"[inject] websockets not installed, printing event:\n{payload}", flush=True) + return + + try: + async with websockets.connect(ws_url, open_timeout=5) as ws: + await ws.send(payload) + print(f"[inject] Sent {event_type} -> {ws_url}", flush=True) + print(f"[inject] Payload: {payload}", flush=True) + except Exception as e: + print(f"[inject] Failed to send to {ws_url}: {e}", flush=True) + sys.exit(1) + + def main(): parser = argparse.ArgumentParser(description="Evennia -> Nexus WebSocket Bridge") sub = parser.add_subparsers(dest="mode") - + live = sub.add_parser("live", help="Live tail Evennia logs and stream to Nexus") live.add_argument("--log-dir", default="/root/workspace/timmy-academy/server/logs", help="Evennia logs directory") live.add_argument("--ws", default="ws://127.0.0.1:8765", help="Nexus WebSocket URL") - + replay = sub.add_parser("playback", help="Replay a telemetry JSONL file") replay.add_argument("log_path", help="Path to Evennia telemetry JSONL") replay.add_argument("--ws", default="ws://127.0.0.1:8765", help="Nexus WebSocket URL") - + + inject = sub.add_parser("inject", help="Inject a single Evennia event (dev/test)") + inject.add_argument("event_type", choices=["room_snapshot", "actor_located", "command_result", "command_issued", "session_bound"]) + inject.add_argument("--ws", default="ws://127.0.0.1:8765", help="Nexus WebSocket URL") + inject.add_argument("--room-key", default="Gate", help="Room key (room_snapshot, actor_located)") + inject.add_argument("--title", default="Gate", help="Room title (room_snapshot)") + inject.add_argument("--desc", default="The entrance gate.", help="Room description (room_snapshot)") + inject.add_argument("--actor-id", default="Timmy", help="Actor ID") + inject.add_argument("--command-text", default="look", help="Command text (command_result, command_issued)") + inject.add_argument("--output-text", default="You see the Gate.", help="Command output (command_result)") + inject.add_argument("--session-id", default="dev-inject", help="Hermes session ID") + args = parser.parse_args() - + if args.mode == "live": asyncio.run(live_bridge(args.log_dir, args.ws)) elif args.mode == "playback": asyncio.run(playback(Path(args.log_path).expanduser(), args.ws)) + elif args.mode == "inject": + asyncio.run(inject_event( + args.event_type, + args.ws, + room_key=args.room_key, + title=args.title, + desc=args.desc, + actor_id=args.actor_id, + command_text=args.command_text, + output_text=args.output_text, + session_id=args.session_id, + )) else: parser.print_help()