""" Maya Inspector v6 — Token input + WS connect + REST map Runs from HF Space (no egress proxy blocking WS). """ import os, json, uuid, asyncio, datetime, threading, base64 import urllib.request, urllib.parse import gradio as gr from huggingface_hub import HfApi HF_TOKEN = os.environ.get("HF_TOKEN", "") MEMORY_REPO = "Melofhell00/maya-memory" STATE_FILE = "state.json" FIREBASE_KEY = "AIzaSyDtC7Uwb5pGAsdmrH2T4Gqdk5Mga07jYPM" hf = HfApi(token=HF_TOKEN) def read_state(): try: url = f"https://huggingface.co/datasets/{MEMORY_REPO}/resolve/main/{STATE_FILE}" req = urllib.request.Request(url, headers={"Authorization": f"Bearer {HF_TOKEN}"}) with urllib.request.urlopen(req, timeout=10) as r: return json.loads(r.read()) except: return {} def write_state(state): hf.upload_file( path_or_fileobj=json.dumps(state, indent=2).encode(), path_in_repo=STATE_FILE, repo_id=MEMORY_REPO, repo_type="dataset", commit_message=f"inspector {datetime.datetime.utcnow().isoformat()}" ) def decode_token(t): try: payload = t.split('.')[1] + '===' return json.loads(base64.b64decode(payload)) except: return {} def exchange_refresh(rt): url = f"https://securetoken.googleapis.com/v1/token?key={FIREBASE_KEY}" data = urllib.parse.urlencode({"grant_type":"refresh_token","refresh_token":rt}).encode() req = urllib.request.Request(url, data=data, headers={"Content-Type":"application/x-www-form-urlencoded"}) with urllib.request.urlopen(req, timeout=15) as r: body = json.loads(r.read()) return body.get("id_token",""), body.get("refresh_token", rt) def run_inspection(token_input): """Main function: take token, hit all endpoints, connect WS, map Maya.""" log = [] results = {} token = token_input.strip() if token_input else "" # Try refresh from bridge if no token given if not token: log.append("No token provided, trying bridge refresh...") state = read_state() rt = state.get("refresh_token", "") ft = state.get("full_id_token", "") if ft: claims = decode_token(ft) import time if claims.get("exp", 0) > time.time(): token = ft log.append(f"Using stored token (expires {datetime.datetime.fromtimestamp(claims['exp']).isoformat()})") else: log.append("Stored token expired") if not token and rt: try: token, new_rt = exchange_refresh(rt) if token: log.append(f"Refreshed token OK ({len(token)} chars)") state["refresh_token"] = new_rt state["full_id_token"] = token write_state(state) except Exception as e: log.append(f"Refresh failed: {e}") if not token: return "NO TOKEN", "\n".join(log), "" # Decode token claims = decode_token(token) provider = claims.get("firebase",{}).get("sign_in_provider","?") email = claims.get("email","?") uid = claims.get("sub","?") exp = claims.get("exp", 0) log.append(f"Token: provider={provider} email={email} uid={uid}") log.append(f"Expires: {datetime.datetime.fromtimestamp(exp).isoformat()}") import time if exp < time.time(): log.append("TOKEN EXPIRED!") return "EXPIRED", "\n".join(log), "" # Save token to bridge try: state = read_state() state["full_id_token"] = token state["last_updated"] = datetime.datetime.utcnow().isoformat() state["source"] = "inspector-v6" write_state(state) log.append("Saved to bridge") except Exception as e: log.append(f"Bridge save error: {e}") # REST endpoints log.append("\n=== REST API ===") rest_endpoints = [ ("GET", "/external/agents"), ("GET", "/external/user"), ("GET", "/external/user/responders"), ] for method, path in rest_endpoints: try: req = urllib.request.Request( f"https://sesameai.app{path}", headers={"Authorization": f"Bearer {token}"} ) with urllib.request.urlopen(req, timeout=15) as r: body = r.read().decode() try: data = json.loads(body) results[path] = data log.append(f"{path}: {json.dumps(data)[:300]}") except: results[path] = body[:200] log.append(f"{path}: {body[:200]}") except urllib.error.HTTPError as e: body = e.read().decode() log.append(f"{path}: HTTP {e.code} - {body[:200]}") results[path] = f"HTTP {e.code}: {body[:200]}" except Exception as e: log.append(f"{path}: {e}") # WebSocket connection log.append("\n=== WEBSOCKET ===") ws_results = [] def run_ws(): import asyncio as aio try: import websockets except ImportError: ws_results.append("websockets not installed") return async def connect(): client_name = str(uuid.uuid4()) tz = urllib.parse.quote(json.dumps({"timezone":"Europe/Athens"})) url = f"wss://sesameai.app/agent-service-0/v1/connect?id_token={token}&client_name={client_name}&usercontext={tz}&character=Maya" ws_results.append(f"Connecting to Maya...") try: async with websockets.connect(url, additional_headers={"Origin":"https://sesameai.app"}, open_timeout=15) as ws: ws_results.append("CONNECTED!") session_id = None ice_servers = None for i in range(10): try: msg = await aio.wait_for(ws.recv(), timeout=8) try: data = json.loads(msg) msg_type = data.get("type", "unknown") ws_results.append(f"MSG[{msg_type}]: {json.dumps(data)[:400]}") if msg_type == "initialize": session_id = data.get("session_id") elif msg_type == "webrtc_config": ice_servers = data.get("ice_servers", []) except json.JSONDecodeError: ws_results.append(f"RAW: {msg[:200]}") except aio.TimeoutError: ws_results.append(f"(timeout after msg {i})") break if session_id: ws_results.append(f"\nSESSION_ID: {session_id}") if ice_servers: ws_results.append(f"ICE_SERVERS: {json.dumps(ice_servers)[:300]}") # Try sending ping try: await ws.send(json.dumps({"type": "ping"})) pong = await aio.wait_for(ws.recv(), timeout=3) ws_results.append(f"PING->PONG: {pong[:200]}") except: pass except Exception as e: ws_results.append(f"WS ERROR: {type(e).__name__}: {e}") loop = aio.new_event_loop() aio.set_event_loop(loop) loop.run_until_complete(connect()) loop.close() t = threading.Thread(target=run_ws) t.start() t.join(timeout=60) log.extend(ws_results) # If we got agents, try to get agent details agents_data = results.get("/external/agents") if isinstance(agents_data, list) and len(agents_data) > 0: log.append("\n=== AGENT DETAILS ===") for agent in agents_data: agent_uuid = agent.get("uuid") or agent.get("id") agent_name = agent.get("name") or agent.get("character") if agent_uuid: log.append(f"Agent: {agent_name} UUID: {agent_uuid}") # Try unread messages for sub_path in [f"/external/agent/{agent_uuid}/unread_count", f"/external/agent/{agent_uuid}/last_read_message"]: try: req = urllib.request.Request( f"https://sesameai.app{sub_path}", headers={"Authorization": f"Bearer {token}"} ) with urllib.request.urlopen(req, timeout=10) as r: body = r.read().decode() log.append(f" {sub_path}: {body[:200]}") except Exception as e: log.append(f" {sub_path}: {e}") # Compile results all_results = json.dumps(results, indent=2, default=str) status = "CONNECTED" if "CONNECTED!" in str(ws_results) else "WS FAILED" if isinstance(agents_data, list): status += f" | {len(agents_data)} agents" return status, "\n".join(log), all_results # Console script for extracting token from sesameai.app CONSOLE_SCRIPT = """(async()=>{const db=await new Promise(r=>{const q=indexedDB.open('firebaseLocalStorageDb');q.onsuccess=e=>r(e.target.result)});const s=db.transaction('firebaseLocalStorage','readonly').objectStore('firebaseLocalStorage');const d=await new Promise(r=>{const q=s.getAll();q.onsuccess=e=>r(e.target.result)});for(const i of d){const m=i?.value?.stsTokenManager;if(m?.accessToken){console.log('TOKEN:',m.accessToken);console.log('REFRESH:',m.refreshToken);prompt('Copy this token:',m.accessToken);return;}}console.log('No token found');})()""" with gr.Blocks(title="Maya Inspector v6", theme=gr.themes.Monochrome()) as app: gr.Markdown("# Maya Inspector v6\nPaste Firebase token → REST + WS mapping") gr.Markdown(f"**Get token:** Open sesameai.app, sign in, open console, paste:\n```\n{CONSOLE_SCRIPT}\n```") with gr.Row(): token_in = gr.Textbox(label="Firebase ID Token", placeholder="paste token from console...", lines=3) run_btn = gr.Button("INSPECT", variant="primary", size="lg") status_out = gr.Textbox(label="Status", interactive=False) log_out = gr.Textbox(label="Log", interactive=False, lines=25) results_out = gr.Textbox(label="Raw Results", interactive=False, lines=15) run_btn.click(fn=run_inspection, inputs=[token_in], outputs=[status_out, log_out, results_out]) app.launch(server_name="0.0.0.0", server_port=7860)