from __future__ import annotations import logging import threading from pathlib import Path from typing import Any, Callable from openai import OpenAI from .agent import ScreenJobAgent from .models import AgentResult, RunArtifacts, RuntimeOptions from .utils import setup_artifacts, setup_logger try: import pyautogui except Exception as import_exc: pyautogui = None # type: ignore[assignment] _PYAUTOGUI_IMPORT_ERROR = import_exc else: _PYAUTOGUI_IMPORT_ERROR = None def create_openai_client(api_key: str) -> OpenAI: return OpenAI(api_key=api_key) def run_job( *, api_key: str, objective: str, options: RuntimeOptions, runs_base: Path, no_failsafe: bool = False, cancel_event: threading.Event | None = None, event_callback: Callable[[dict[str, Any]], None] | None = None, logger: logging.Logger | None = None, ) -> tuple[AgentResult, RunArtifacts]: if pyautogui is None: raise RuntimeError( "pyautogui is required for runtime execution. " "Install dependencies and ensure GUI access. " f"Import error: {_PYAUTOGUI_IMPORT_ERROR}" ) pyautogui.FAILSAFE = not no_failsafe pyautogui.PAUSE = 0.05 artifacts = setup_artifacts(runs_base) active_logger = logger or setup_logger(artifacts.log_file, verbose=True) active_logger.info("ScreenJob booting. Artifacts: %s", str(artifacts.root_dir.resolve())) active_logger.info("PyAutoGUI FAILSAFE=%s", pyautogui.FAILSAFE) client = create_openai_client(api_key) agent = ScreenJobAgent( client=client, logger=active_logger, artifacts=artifacts, options=options, cancel_event=cancel_event, event_callback=event_callback, ) result = agent.run(objective) active_logger.info("Run finished. completed=%s elapsed=%.2fs", result.completed, result.ended_at - result.started_at) return result, artifacts