#!/usr/bin/env python3 """ Requirements: pip install -r tools/requirements.txt Usage: python3 tools/create_session_browser.py [totp_seed] [--append sessions.jsonl] [--headless] Examples: # Output to terminal python3 tools/create_session_browser.py myusername mypassword TOTP_SECRET # Append to sessions.jsonl python3 tools/create_session_browser.py myusername mypassword TOTP_SECRET --append sessions.jsonl # Headless mode (may increase detection risk) python3 tools/create_session_browser.py myusername mypassword TOTP_SECRET --headless Output: {"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."} """ import asyncio import json import os import shutil import sys import tempfile import zendriver as zd from zendriver import cdp import pyotp # Disable password manager to prevent the "Save password?" bubble from # stealing focus during automated login. _SEED_PREFS = { "credentials_enable_service": False, "profile": {"password_manager_enabled": False}, } _BROWSER_ARGS = [ "--password-store=basic", "--no-first-run", "--no-default-browser-check", "--disable-notifications", ] def _log(*a): print(*a, file=sys.stderr, flush=True) def _make_profile(): """Create a temp Chrome profile with password manager disabled.""" profile = tempfile.mkdtemp(prefix="xsess_") default = os.path.join(profile, "Default") os.makedirs(default) with open(os.path.join(default, "Preferences"), "w") as f: json.dump(_SEED_PREFS, f) return profile def _extract_user_id(cookies_dict): """Extract numeric user ID from the twid cookie.""" twid = cookies_dict.get("twid", "").strip('"') for prefix in ("u%3D", "u="): if prefix in twid: return twid.split(prefix)[1].split("&")[0].strip('"') return None async def _check_login_error(tab): """Check if the login flow is showing an error (wrong password, etc.).""" try: return await tab.evaluate('''(() => { // Check role="alert" elements (X's standard error display) const alert = document.querySelector('[role="alert"]'); if (alert) { const t = alert.textContent.trim(); if (t.length > 0 && t.length < 200) return t; } // Check for common error strings in visible text for (const el of document.querySelectorAll('p, span, div')) { const t = el.textContent.trim(); if (t.length > 5 && t.length < 150 && (t.includes('Wrong password') || t.includes('incorrect') || t.includes('Could not log you in') || t.includes("can\\'t find") || t.includes('cannot find') || t.includes('suspended') || t.includes('locked') || t.includes('unusual login'))) { return t; } } return ''; })()''') except Exception: return '' async def _click_continue(tab): """Click the 'Continue' / 'Log in' button in the jf onboarding flow. The button is a nested
containing

Continue

(or

Log in

), not a standard