""" Shared browser configuration for login_once.py and apply.py. Both MUST use the identical BrowserProfile so the LinkedIn session you create during login is reused at apply time. The session lives in ./chrome-profile (git-ignored). Why a dedicated profile instead of your real Chrome: Chrome 149 blocks remote-debugging on the default profile (a 2025 anti-infostealer change), and there's no default Chrome profile on this machine anyway. A dedicated profile gives the same thing — a real, logged-in, persistent session you control — without sharing any password with our code. """ import os from browser_use import BrowserProfile HERE = os.path.dirname(os.path.abspath(__file__)) CHROME_PATH = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" PROFILE_DIR = os.path.join(HERE, "chrome-profile") RESUME_PATH = os.path.join(HERE, "Resume.pdf") def make_profile(headless: bool = False, persist: bool = True) -> BrowserProfile: # persist=False -> fresh browser (no user_data_dir): right for public ATS forms that need no # login, and for unattended sweeps where many runs shouldn't share one profile lock. kwargs = dict( executable_path=CHROME_PATH, headless=headless, # False = you watch it; True = unattended sweep keep_alive=True, # don't tear the browser down between steps # --- Hardened pacing (defaults are 0.1 / 0.25 / 0.5) --- # Slower, human-like timing. Critically, wait_for_network_idle lets Ashby's ASYNC résumé # upload finish before the next action fires — the flaky step that got us spam-flagged. # Slower pacing also reduces the bot fingerprint that feeds the spam heuristic. wait_between_actions=1.2, minimum_wait_page_load_time=1.0, wait_for_network_idle_page_load_time=2.5, ) if persist: kwargs["user_data_dir"] = PROFILE_DIR # persists cookies/login across runs return BrowserProfile(**kwargs)