# PlayMode console monitor: polls UnitySkills console_get_logs every few seconds # and appends newly (re)appearing entries to a timestamped capture file. # Used for 2026-09-21 manual acceptance runs (user operates, AI records evidence). import sys, time, hashlib sys.path.insert(0, r'C:\Users\LB\.agents\skills\shared-unity-skills\scripts') import unity_skills OUT = r'C:\projects\ft\CodexOutput\upserver\playmode-console-2026-09-21.log' PORT = 8091 POLL_SECONDS = 4 HEARTBEAT_SECONDS = 60 def sig(entry): raw = str(entry.get('type')) + '|' + entry.get('message', '') return hashlib.md5(raw.encode('utf-8', errors='ignore')).hexdigest() def fmt(entry): ts = time.strftime('%H:%M:%S') typ = entry.get('type', '?') msg = entry.get('message', '').replace('\r', ' ').replace('\n', ' | ')[:400] return f"{ts} [{typ}] {msg}" def main(): prev = set() last_beat = 0.0 c = unity_skills.connect(port=PORT) with open(OUT, 'a', encoding='utf-8') as f: f.write(f"\n==== monitor start {time.strftime('%Y-%m-%d %H:%M:%S')} port={PORT} ====\n") while True: try: logs = c.call('console_get_logs', count=100) entries = logs.get('logs', []) cur = set() for e in entries: s = sig(e) cur.add(s) if s not in prev: f.write(fmt(e) + '\n') prev = cur except Exception as ex: # domain reload / server restart window: retry and reconnect f.write(f"{time.strftime('%H:%M:%S')} [monitor] connection gap: {type(ex).__name__}; reconnecting\n") try: c = unity_skills.connect(port=PORT) except Exception: pass time.sleep(3) continue now = time.time() if now - last_beat > HEARTBEAT_SECONDS: f.write(f"{time.strftime('%H:%M:%S')} [monitor] alive, window={len(entries)}\n") last_beat = now f.flush() time.sleep(POLL_SECONDS) if __name__ == '__main__': main()