# Procyon — autopilot. Scheduled discover -> enrich -> score -> auto-prepare top N. # Everything up to the human approval/send gate. Never sends without approval. import os import db import scraper import tailor import pipeline import emailer BASE = os.path.dirname(os.path.abspath(__file__)) def run(): db.init_db() if db.get_setting('autopilot_enabled', '1') != '1': print('autopilot disabled') return # 1. discover try: added = scraper.discover() except Exception as e: added = 0 print(f'discover error: {e}') print(f'discovered {added} new job(s)') # 2. enrich thin descriptions (firecrawl scrape / http fetch) new_jobs = db.list_jobs(status='new', limit=200) enriched = 0 for j in new_jobs: desc = j.get('description') or '' if len(desc) < 200 and j.get('url'): try: full = scraper.enrich_description(j['url']) if full and len(full) > len(desc): conn = db.get_conn() try: conn.execute('UPDATE jobs SET description = ? WHERE id = ?', (full[:5000], j['id'])) conn.commit() finally: conn.close() enriched += 1 except Exception: pass print(f'enriched {enriched} job description(s)') # 3. score all 'new' jobs new_jobs = db.list_jobs(status='new', limit=200) scored = [] for j in new_jobs: s, o = tailor.score_job(j) db.update_job_score(j['id'], s, o) scored.append((j['id'], s)) print(f'scored {len(scored)} job(s)') # 4. auto-prepare the top N (tailor + audit + PDF) — queued for your approval max_prepare = int(db.get_setting('autopilot_max_prepare', '3')) top = sorted(scored, key=lambda x: -x[1])[:max_prepare] prepared = 0 for jid, s in top: job = db.get_job(jid) if not job: continue try: aid = pipeline.prepare(job) print(f'prepared app #{aid} for "{job["title"]}" (fit {s})') prepared += 1 if db.get_setting('auto_send', '0') == '1': sent, msg = emailer.auto_send(aid) print(f' auto-send: {msg}' if sent else f' auto-send skipped: {msg}') except Exception as e: print(f'prepare failed for {job["title"]}: {e}') print(f'AUTOPILOT_DONE: {added} discovered, {len(scored)} scored, {prepared} prepared') if __name__ == '__main__': run()