#!/usr/bin/env python3 import json import logging import os import re import signal import subprocess import sys import time from datetime import datetime from typing import Optional import psycopg from psycopg.rows import dict_row DB_URL = os.environ.get("DATABASE_URL") if not DB_URL: print("ERR: DATABASE_URL not set", file=sys.stderr) sys.exit(1) WORKER_ID = int(os.environ.get("WORKER_ID", str(os.getpid()))) LOGS_DIR = os.environ.get("LOGS_DIR", "/Volumes/Omnicron/AI_Brain/logs") os.makedirs(LOGS_DIR, exist_ok=True) FILE_LOG = os.path.join(LOGS_DIR, f"money_agents_worker_{WORKER_ID}.out") logger = logging.getLogger("money_agents.worker") logger.setLevel(logging.INFO) fh = logging.FileHandler(FILE_LOG) fh.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) logger.addHandler(fh) sh = logging.StreamHandler(sys.stdout) sh.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) logger.addHandler(sh) def db_connect(): return psycopg.connect(DB_URL) def db_log(conn, level: str, msg: str, job_id: Optional[int] = None): with conn.cursor() as cur: cur.execute( "INSERT INTO logs(worker_id, job_id, message, level) VALUES (%s,%s,%s,%s)", (WORKER_ID, job_id, msg, level), ) conn.commit() logger.info("[log:%s] job=%s %s", level, str(job_id), msg) ALLOWED_EXEC = { # domain allowlist enforced via regex match below "curl": re.compile( r"^curl\s+https://(api\.(stripe|paypal)\.com|cash\.app|api\.cashapp\.com|[^\s]+)" ), } def safe_execute(payload: str) -> int: payload = payload.strip() if not payload: return 0 parts = payload.split() cmd = parts[0] if cmd not in ALLOWED_EXEC: logger.warning("Blocked command: %s", cmd) return 126 if not ALLOWED_EXEC[cmd].match(payload): logger.warning("Blocked payload by allowlist policy") return 126 try: # Avoid printing secrets: no shell expansion, no env dump res = subprocess.run(parts, capture_output=True, timeout=60) if res.returncode != 0: logger.error("payload stderr: %s", res.stderr.decode(errors="ignore")[:500]) else: logger.info("payload stdout: %s", res.stdout.decode(errors="ignore")[:200]) return res.returncode except Exception as e: logger.exception("payload execution error: %s", e) return 1 def extract_service(payload: str) -> Optional[str]: m = re.search(r"https://([^/\s]+)", payload or "") return m.group(1) if m else None STOP = False def handle_sigterm(signum, frame): global STOP STOP = True signal.signal(signal.SIGTERM, handle_sigterm) signal.signal(signal.SIGINT, handle_sigterm) def main(): poll_sleep = float(os.environ.get("POLL_INTERVAL", "1.5")) heartbeat_every = 10 # seconds last_heartbeat = 0.0 with db_connect() as conn: conn.autocommit = False # initial heartbeat db_log(conn, "info", "worker starting", None) while not STOP: now = time.time() if now - last_heartbeat > heartbeat_every: db_log(conn, "debug", "heartbeat", None) last_heartbeat = now job = None try: with conn.cursor(row_factory=dict_row) as cur: cur.execute("BEGIN") cur.execute( "SELECT id, payload, amount FROM jobs WHERE status='pending' FOR UPDATE SKIP LOCKED LIMIT 1" ) row = cur.fetchone() if row: job = row cur.execute( "UPDATE jobs SET status='claimed', worker_id=%s, started_at=NOW() WHERE id=%s", (WORKER_ID, job["id"]), ) cur.execute("COMMIT") db_log(conn, "info", "claimed job", job["id"]) else: cur.execute("ROLLBACK") except Exception as e: conn.rollback() logger.exception("claim failure: %s", e) time.sleep(poll_sleep) continue if not job: time.sleep(poll_sleep) continue job_id = job["id"] payload = (job.get("payload") or "").strip() amount = job.get("amount") or 0 service = extract_service(payload) or "unknown" try: db_log(conn, "info", "executed payload", job_id) rc = safe_execute(payload) status = "success" if rc == 0 else "error" if amount and float(amount) != 0.0: with conn.cursor() as cur: cur.execute( "INSERT INTO transactions(job_id, amount, service, status) VALUES (%s,%s,%s,%s)", (job_id, amount, service, status), ) conn.commit() db_log( conn, "info", f"transaction status={status} amount={amount} service={service}", job_id, ) with conn.cursor() as cur: cur.execute( "UPDATE jobs SET status=%s, finished_at=NOW() WHERE id=%s", ("completed" if rc == 0 else "error", job_id), ) conn.commit() db_log(conn, "info", "completed" if rc == 0 else "error", job_id) except Exception as e: conn.rollback() logger.exception("job failure: %s", e) try: with db_connect() as c2: with c2, c2.cursor() as cur: cur.execute( "UPDATE jobs SET status='error', finished_at=NOW() WHERE id=%s", (job_id,), ) db_log(c2, "error", f"exception: {e}", job_id) except Exception: pass finally: time.sleep(0.2) if __name__ == "__main__": main()