export interface Listing { id: number title: string price: number | null currency: string price_raw: string price_usd: number | null time_left: string time_left_mins: number | null link: string score: number keyword: string site_name: string timestamp: string price_updated_at: string | null ai_match: 1 | 0 | null ai_reason: string | null location: string | null images: string[] // parsed from JSON string at API layer closing_alerts_sent: number[] // parsed from JSON string at API layer } export interface Keyword { id: number term: string weight: number ai_target: string | null min_price: number | null max_price: number | null sort_order: number } export interface TargetSite { id: number name: string url_template: string search_selector: string enabled: 0 | 1 max_pages: number last_error: string | null error_count: number consecutive_failures: number last_success_at: string | null cooldown_until: string | null requires_login: 0 | 1 login_url: string | null login_check_selector: string | null login_enabled: 0 | 1 sort_order: number } export interface SiteSelectors { site_id: number confidence: number container_sel: string | null title_sel: string | null price_sel: string | null time_sel: string | null link_sel: string | null next_page_sel: string | null stale: boolean provider: 'groq' | 'ollama' | null generated_at: string | null } export interface Stats { total_scanned: number total_alerts: number last_cycle: string engine_status: 'Idle' | 'Running' | 'Paused' uptime_start: number uptime_seconds: number } export interface Config { key: string value: string } export interface ScoringRule { id: number signal: string delta: number // positive = boost, negative = penalty category: 'positive' | 'negative' | 'custom' notes: string }