| | """ |
| | ALTYZEN Ghost Runner - Complete Stealth Browser Agent |
| | ====================================================== |
| | Zero Docker-level VNC. All binaries download at runtime. |
| | Screenshot-based streaming via /stream endpoint. |
| | Full validation logic with Nemotron + Gemini fallback. |
| | """ |
| |
|
| | import os |
| | import sys |
| | import asyncio |
| | import subprocess |
| | import json |
| | import logging |
| | import threading |
| | from datetime import datetime |
| | from typing import Dict, Any, Optional |
| | from pathlib import Path |
| | from contextlib import asynccontextmanager |
| |
|
| | from fastapi import FastAPI, HTTPException |
| | from fastapi.responses import HTMLResponse, FileResponse, Response |
| | from fastapi.middleware.cors import CORSMiddleware |
| | from pydantic import BaseModel |
| | import uvicorn |
| |
|
| | logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
| | logger = logging.getLogger("GhostRunner") |
| |
|
| | |
| | |
| | |
| | RUNTIME_DIR = Path("/tmp/ghost_runner") |
| | SCREENSHOT_PATH = RUNTIME_DIR / "screenshot.png" |
| | CLOUDFLARED_PATH = RUNTIME_DIR / "cloudflared" |
| | LOGS_PATH = RUNTIME_DIR / "agent_logs.txt" |
| |
|
| | |
| | class GhostState: |
| | browser_ready = False |
| | tunnel_process = None |
| | current_task = None |
| | logs = [] |
| |
|
| | state = GhostState() |
| |
|
| | |
| | |
| | |
| | class TaskRequest(BaseModel): |
| | task_id: str |
| | task_type: str |
| | data: Dict[str, Any] |
| |
|
| | class TaskResponse(BaseModel): |
| | task_id: str |
| | status: str |
| | result: Optional[Dict[str, Any]] = None |
| | error: Optional[str] = None |
| | logs: Optional[list] = None |
| | execution_time_ms: int = 0 |
| |
|
| | |
| | |
| | |
| |
|
| | async def setup_runtime(): |
| | """Download all binaries at runtime - invisible to HF build scanners.""" |
| | RUNTIME_DIR.mkdir(parents=True, exist_ok=True) |
| | |
| | logger.info("π§ Ghost Runner: Setting up runtime environment...") |
| | |
| | |
| | try: |
| | logger.info("π¦ Installing browser engine (this may take a minute)...") |
| | proc = await asyncio.create_subprocess_exec( |
| | sys.executable, "-m", "playwright", "install", "chromium", |
| | stdout=asyncio.subprocess.PIPE, |
| | stderr=asyncio.subprocess.PIPE |
| | ) |
| | stdout, stderr = await proc.communicate() |
| | if proc.returncode == 0: |
| | logger.info("β
Browser engine installed!") |
| | state.browser_ready = True |
| | else: |
| | logger.error(f"β Browser install failed: {stderr.decode()[:200]}") |
| | except Exception as e: |
| | logger.error(f"β Browser install error: {e}") |
| | |
| | |
| | tunnel_token = os.getenv("CLOUDFLARE_TUNNEL_TOKEN") |
| | if tunnel_token and not CLOUDFLARED_PATH.exists(): |
| | logger.info("π₯ Downloading cloudflared...") |
| | try: |
| | proc = await asyncio.create_subprocess_exec( |
| | "curl", "-L", "-o", str(CLOUDFLARED_PATH), |
| | "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64", |
| | stdout=asyncio.subprocess.PIPE, |
| | stderr=asyncio.subprocess.PIPE |
| | ) |
| | await proc.communicate() |
| | os.chmod(CLOUDFLARED_PATH, 0o755) |
| | logger.info("β
cloudflared downloaded!") |
| | except Exception as e: |
| | logger.error(f"β cloudflared download failed: {e}") |
| | |
| | |
| | if tunnel_token and CLOUDFLARED_PATH.exists(): |
| | start_tunnel(tunnel_token) |
| | |
| | |
| | create_placeholder() |
| | |
| | logger.info("β
Ghost Runner: Runtime environment ready!") |
| |
|
| |
|
| | def start_tunnel(token: str): |
| | """Start cloudflared tunnel in background.""" |
| | def run(): |
| | try: |
| | logger.info("π Starting Cloudflare tunnel...") |
| | state.tunnel_process = subprocess.Popen( |
| | [str(CLOUDFLARED_PATH), "tunnel", "--no-autoupdate", "run", |
| | "--token", token, "--url", "http://localhost:7860"], |
| | stdout=subprocess.PIPE, stderr=subprocess.PIPE |
| | ) |
| | logger.info("β
Tunnel process started!") |
| | except Exception as e: |
| | logger.error(f"β Tunnel failed: {e}") |
| | |
| | threading.Thread(target=run, daemon=True).start() |
| |
|
| |
|
| | def create_placeholder(): |
| | """Create placeholder screenshot.""" |
| | try: |
| | from PIL import Image, ImageDraw |
| | img = Image.new('RGB', (1280, 720), '#0d1117') |
| | draw = ImageDraw.Draw(img) |
| | draw.rectangle([100, 100, 1180, 620], outline='#00ff88', width=2) |
| | draw.text((480, 320), "ALTYZEN GHOST RUNNER", fill='#00ff88') |
| | draw.text((520, 360), "Ready for tasks...", fill='#8b949e') |
| | draw.text((490, 420), f"Browser: {'READY' if state.browser_ready else 'LOADING'}", fill='#58a6ff') |
| | img.save(SCREENSHOT_PATH) |
| | except Exception as e: |
| | logger.warning(f"Placeholder creation failed: {e}") |
| | |
| | SCREENSHOT_PATH.write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDATx\x9cc\xf8\x0f\x00\x00\x01\x01\x00\x05\x18\xd8N\x00\x00\x00\x00IEND\xaeB`\x82') |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | async def validate_order(order_data: Dict[str, Any]) -> Dict[str, Any]: |
| | """Full validation with Nemotron primary + Gemini fallback.""" |
| | logs = [] |
| | def log(msg): |
| | logger.info(msg) |
| | logs.append(msg) |
| | state.logs.append(msg) |
| | |
| | if not state.browser_ready: |
| | return {"decision": "ERROR", "error": "Browser not ready", "logs": logs} |
| | |
| | |
| | email = order_data.get('email', '') |
| | phone = order_data.get('phone', '') |
| | zip_code = order_data.get('zip', '') |
| | city = order_data.get('city', '') |
| | state_name = order_data.get('state', '') |
| | task_id = order_data.get('task_id', 'unknown') |
| | |
| | log(f"π Task ID: {task_id}") |
| | log(f"π§ Email: {email}") |
| | log(f"π Phone: {phone}") |
| | log(f"π Geo: {zip_code}, {city}, {state_name}") |
| | |
| | |
| | task = f""" |
| | You are a Validation Expert. Perform these 3 checks: |
| | |
| | STEP 1: EMAIL VALIDATION |
| | - Go to 'https://email-checker.net/' |
| | - Input '{email}' and check result |
| | - Extract: 'Valid', 'Invalid', or 'Risky' |
| | |
| | STEP 2: PHONE VALIDATION |
| | - Validate phone number '{phone}' |
| | - Check if it's a valid format for the region |
| | |
| | STEP 3: GEO VALIDATION |
| | - Does ZIP '{zip_code}' belong to City '{city}' in State '{state_name}'? |
| | - Return 'Match' or 'Mismatch' |
| | |
| | OUTPUT JSON ONLY: |
| | {{ |
| | "email_status": "Valid/Invalid/Risky", |
| | "phone_status": "Valid/Invalid", |
| | "geo_match": true/false, |
| | "summary": "brief explanation" |
| | }} |
| | """ |
| | |
| | api_key = os.getenv("OPENROUTER_API_KEY") |
| | if not api_key: |
| | return {"decision": "ERROR", "error": "No OPENROUTER_API_KEY", "logs": logs} |
| | |
| | os.environ["OPENAI_API_KEY"] = api_key |
| | |
| | try: |
| | from browser_use import Agent, Browser |
| | from langchain_openai import ChatOpenAI |
| | |
| | |
| | llm_primary = ChatOpenAI( |
| | model="nvidia/nemotron-nano-12b-v2-vl:free", |
| | api_key=api_key, |
| | base_url="https://openrouter.ai/api/v1", |
| | temperature=0.1, |
| | default_headers={"HTTP-Referer": "https://altyzen.com", "X-Title": "Altyzen Ghost Runner"} |
| | ) |
| | |
| | |
| | llm_fallback = ChatOpenAI( |
| | model="google/gemini-2.0-flash-exp:free", |
| | api_key=api_key, |
| | base_url="https://openrouter.ai/api/v1", |
| | temperature=0.1, |
| | default_headers={"HTTP-Referer": "https://altyzen.com", "X-Title": "Altyzen Ghost Runner"} |
| | ) |
| | |
| | browser = Browser(headless=True) |
| | result = None |
| | |
| | |
| | screenshot_task = asyncio.create_task(capture_screenshots(browser)) |
| | |
| | try: |
| | |
| | log("π€ Attempt 1: Using Nvidia Nemotron...") |
| | try: |
| | agent = Agent(task=task, llm=llm_primary, browser=browser, use_vision=True, validate_output=False) |
| | history = await agent.run() |
| | result = history.final_result() |
| | log("β
Nemotron completed!") |
| | except Exception as e: |
| | log(f"β οΈ Nemotron failed: {str(e)[:100]}") |
| | |
| | |
| | log("π Switching to Gemini fallback...") |
| | try: |
| | agent = Agent(task=task, llm=llm_fallback, browser=browser, use_vision=True, validate_output=False) |
| | history = await agent.run() |
| | result = history.final_result() |
| | log("β
Gemini completed!") |
| | except Exception as e2: |
| | log(f"β Gemini also failed: {str(e2)[:100]}") |
| | result = None |
| | finally: |
| | screenshot_task.cancel() |
| | try: |
| | await browser.close() |
| | except: |
| | pass |
| | |
| | parsed = parse_result(result, order_data) |
| | parsed["logs"] = logs |
| | parsed["task_id"] = task_id |
| | return parsed |
| | |
| | except Exception as e: |
| | log(f"β Critical error: {e}") |
| | return {"decision": "ERROR", "error": str(e), "logs": logs} |
| |
|
| |
|
| | async def capture_screenshots(browser): |
| | """Capture browser screenshots during execution.""" |
| | while True: |
| | try: |
| | context = await browser.get_context() |
| | pages = context.pages |
| | if pages: |
| | await pages[0].screenshot(path=str(SCREENSHOT_PATH)) |
| | except: |
| | pass |
| | await asyncio.sleep(0.5) |
| |
|
| |
|
| | def parse_result(result, order_data): |
| | """Parse agent result into structured validation response.""" |
| | if result is None: |
| | return {"decision": "UNKNOWN", "email_valid": False, "phone_valid": False, "geo_valid": False, "reasoning": "All models failed"} |
| | |
| | parsed = {} |
| | if isinstance(result, str): |
| | try: |
| | if "{" in result: |
| | json_start = result.find("{") |
| | json_end = result.rfind("}") + 1 |
| | parsed = json.loads(result[json_start:json_end]) |
| | except: |
| | parsed = {"raw": result} |
| | elif isinstance(result, dict): |
| | parsed = result |
| | |
| | email_valid = "valid" in str(parsed.get("email_status", "")).lower() and "invalid" not in str(parsed.get("email_status", "")).lower() |
| | phone_valid = "valid" in str(parsed.get("phone_status", "")).lower() and "invalid" not in str(parsed.get("phone_status", "")).lower() |
| | geo_valid = parsed.get("geo_match", False) if isinstance(parsed.get("geo_match"), bool) else str(parsed.get("geo_match", "")).lower() == "true" |
| | |
| | decision = "APPROVED" if email_valid and phone_valid and geo_valid else "BLOCKED" |
| | |
| | return { |
| | "order_id": order_data.get("order_id", "UNKNOWN"), |
| | "decision": decision, |
| | "email_valid": email_valid, |
| | "phone_valid": phone_valid, |
| | "geo_valid": geo_valid, |
| | "reasoning": parsed.get("summary", "Validation completed"), |
| | "raw_result": parsed |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | @asynccontextmanager |
| | async def lifespan(app: FastAPI): |
| | |
| | asyncio.create_task(setup_runtime()) |
| | yield |
| | |
| | if state.tunnel_process: |
| | state.tunnel_process.terminate() |
| |
|
| | app = FastAPI(title="AI Agent Worker", version="3.0.0", lifespan=lifespan) |
| |
|
| | app.add_middleware( |
| | CORSMiddleware, |
| | allow_origins=["*"], |
| | allow_credentials=True, |
| | allow_methods=["*"], |
| | allow_headers=["*"], |
| | ) |
| |
|
| |
|
| | @app.get("/") |
| | async def root(): |
| | return { |
| | "service": "AI Agent Worker", |
| | "status": "active", |
| | "browser_ready": state.browser_ready, |
| | "tunnel_active": state.tunnel_process is not None, |
| | "timestamp": datetime.now().isoformat() |
| | } |
| |
|
| |
|
| | @app.get("/health") |
| | async def health(): |
| | return {"status": "healthy", "browser_ready": state.browser_ready} |
| |
|
| |
|
| | @app.get("/stream", response_class=HTMLResponse) |
| | async def stream(): |
| | """Live screenshot stream - replaces VNC.""" |
| | return """ |
| | <!DOCTYPE html> |
| | <html> |
| | <head> |
| | <title>Ghost Runner - Live Feed</title> |
| | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| | <style> |
| | * { margin: 0; padding: 0; box-sizing: border-box; } |
| | body { |
| | background: linear-gradient(135deg, #0d1117 0%, #161b22 100%); |
| | min-height: 100vh; |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | padding: 20px; |
| | font-family: 'Segoe UI', system-ui, sans-serif; |
| | } |
| | .header { |
| | color: #00ff88; |
| | font-size: 1.5em; |
| | margin-bottom: 15px; |
| | text-shadow: 0 0 20px rgba(0,255,136,0.3); |
| | } |
| | .container { |
| | position: relative; |
| | border: 2px solid #30363d; |
| | border-radius: 12px; |
| | overflow: hidden; |
| | box-shadow: 0 8px 32px rgba(0,0,0,0.4); |
| | } |
| | img { |
| | display: block; |
| | max-width: 100%; |
| | width: 1280px; |
| | } |
| | .status-bar { |
| | position: absolute; |
| | bottom: 0; |
| | left: 0; |
| | right: 0; |
| | background: rgba(0,0,0,0.8); |
| | padding: 8px 15px; |
| | display: flex; |
| | justify-content: space-between; |
| | color: #8b949e; |
| | font-size: 12px; |
| | } |
| | .live-dot { |
| | display: inline-block; |
| | width: 8px; |
| | height: 8px; |
| | background: #00ff88; |
| | border-radius: 50%; |
| | margin-right: 5px; |
| | animation: pulse 1s infinite; |
| | } |
| | @keyframes pulse { |
| | 0%, 100% { opacity: 1; } |
| | 50% { opacity: 0.3; } |
| | } |
| | .logs { |
| | margin-top: 20px; |
| | width: 100%; |
| | max-width: 1280px; |
| | background: #161b22; |
| | border: 1px solid #30363d; |
| | border-radius: 8px; |
| | padding: 15px; |
| | color: #c9d1d9; |
| | font-family: monospace; |
| | font-size: 12px; |
| | max-height: 200px; |
| | overflow-y: auto; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| | <div class="header">π» ALTYZEN Ghost Runner - Live Feed</div> |
| | <div class="container"> |
| | <img id="screen" src="/screenshot" alt="Browser Feed"/> |
| | <div class="status-bar"> |
| | <span><span class="live-dot"></span>LIVE</span> |
| | <span id="timestamp">Connecting...</span> |
| | </div> |
| | </div> |
| | <div class="logs" id="logs">Waiting for activity...</div> |
| | <script> |
| | setInterval(() => { |
| | document.getElementById('screen').src = '/screenshot?' + Date.now(); |
| | document.getElementById('timestamp').textContent = new Date().toLocaleTimeString(); |
| | }, 1000); |
| | |
| | setInterval(async () => { |
| | try { |
| | const resp = await fetch('/logs'); |
| | const data = await resp.json(); |
| | if (data.logs && data.logs.length > 0) { |
| | document.getElementById('logs').innerHTML = data.logs.slice(-20).map(l => `<div>${l}</div>`).join(''); |
| | } |
| | } catch (e) {} |
| | }, 2000); |
| | </script> |
| | </body> |
| | </html> |
| | """ |
| |
|
| |
|
| | @app.get("/screenshot") |
| | async def screenshot(): |
| | """Current browser screenshot.""" |
| | if SCREENSHOT_PATH.exists(): |
| | return FileResponse(SCREENSHOT_PATH, media_type="image/png") |
| | create_placeholder() |
| | return FileResponse(SCREENSHOT_PATH, media_type="image/png") |
| |
|
| |
|
| | @app.get("/logs") |
| | async def get_logs(): |
| | """Return recent logs.""" |
| | return {"logs": state.logs[-50:]} |
| |
|
| |
|
| | @app.post("/run-task", response_model=TaskResponse) |
| | async def run_task(request: TaskRequest): |
| | """Run a validation task.""" |
| | start_time = datetime.now() |
| | state.current_task = request.task_id |
| | state.logs = [] |
| | |
| | try: |
| | if request.task_type in ["validate_order", "validate_email"]: |
| | result = await validate_order({**request.data, "task_id": request.task_id}) |
| | execution_time = int((datetime.now() - start_time).total_seconds() * 1000) |
| | |
| | return TaskResponse( |
| | task_id=request.task_id, |
| | status="success" if result.get("decision") not in ["ERROR", "UNKNOWN"] else "failed", |
| | result=result, |
| | logs=result.get("logs", []), |
| | execution_time_ms=execution_time |
| | ) |
| | else: |
| | raise HTTPException(status_code=400, detail=f"Unknown task_type: {request.task_type}") |
| | |
| | except Exception as e: |
| | return TaskResponse(task_id=request.task_id, status="error", error=str(e)) |
| | finally: |
| | state.current_task = None |
| |
|
| |
|
| | if __name__ == "__main__": |
| | uvicorn.run(app, host="0.0.0.0", port=7860) |
| |
|