On-demand Scout service (replaces the nightly aggregator): - FastAPI agent-mesh service; card name "matchmaking-service" (HTTP /a2a/tasks + Redis-stream worker matching the sibling-service pattern) - run_search: parallel multi-board sweep (Naukri/blackfalcondata + Foundit + LinkedIn), city-filtered at the board, cheapest-per-result first - per-board adapters + normalizers -> ScoutJob with rich read-only details and offsite apply links; recall mode + MVQ guard - result cache for dev replay ($0); per-board cost knobs; retry-on-5xx - research/: engine design, board cost economics, India-actor shortlist, POC
41 lines
1.9 KiB
Python
41 lines
1.9 KiB
Python
"""
|
|
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)
|