SYMBOL INDEX (886 symbols across 128 files) FILE: chrome-extension/background.js constant GOOFISH_HOST_PATTERN (line 3) | const GOOFISH_HOST_PATTERN = "*://*.goofish.com/*"; constant MAX_STORAGE_ENTRY_LENGTH (line 4) | const MAX_STORAGE_ENTRY_LENGTH = 4096; function mapSameSiteValue (line 6) | function mapSameSiteValue(chromeSameSite) { function headersArrayToObject (line 17) | function headersArrayToObject(headers = []) { function getActiveGoofishTab (line 27) | async function getActiveGoofishTab() { function capturePageData (line 39) | async function capturePageData(tabId) { function filterEnvData (line 120) | function filterEnvData(env = {}) { function pruneStorageEntries (line 151) | function pruneStorageEntries(entries = {}) { function filterHeaders (line 165) | function filterHeaders(rawHeaders = {}) { function captureHeaders (line 195) | async function captureHeaders(tabId) { function captureCookies (line 246) | async function captureCookies(url) { function buildSnapshot (line 260) | async function buildSnapshot() { FILE: chrome-extension/popup.js function setLoading (line 10) | function setLoading(isLoading) { function updateStatus (line 15) | function updateStatus(message, isSuccess = false) { function renderSnapshot (line 24) | function renderSnapshot(snapshot) { function captureSnapshot (line 29) | async function captureSnapshot() { function copySnapshot (line 51) | function copySnapshot() { FILE: desktop_launcher.py function _prepare_environment (line 17) | def _prepare_environment() -> None: function run_app (line 24) | def run_app() -> None: FILE: spider_v2.py function main (line 15) | async def main(): FILE: src/ai_handler.py function _positive_int (line 53) | def _positive_int(value, default: int) -> int: function safe_print (line 66) | def safe_print(text): function _build_debug_request_summary (line 79) | def _build_debug_request_summary(api_mode: str, request_params: dict) ->... function _extract_message_content_types (line 107) | def _extract_message_content_types(message: dict) -> list[str]: function _download_single_image (line 117) | async def _download_single_image(url, save_path): function _build_image_save_path (line 132) | def _build_image_save_path( function download_all_images (line 147) | async def download_all_images(product_id, image_urls, task_name="default... function cleanup_task_images (line 199) | def cleanup_task_images(task_name): function cleanup_ai_logs (line 212) | def cleanup_ai_logs(logs_dir: str, keep_days: int = 1) -> None: function encode_image_to_base64 (line 228) | def encode_image_to_base64(image_path): function validate_ai_response_format (line 240) | def validate_ai_response_format(parsed_response): function send_ntfy_notification (line 280) | async def send_ntfy_notification(product_data, reason): function get_ai_analysis (line 298) | async def get_ai_analysis(product_data, image_paths=None, prompt_text=""): FILE: src/ai_message_builder.py function build_analysis_text_prompt (line 12) | def build_analysis_text_prompt( function build_user_message_content (line 36) | def build_user_message_content( FILE: src/api/dependencies.py function set_process_service (line 22) | def set_process_service(service: ProcessService): function set_scheduler_service (line 28) | def set_scheduler_service(service: SchedulerService): function set_task_generation_service (line 34) | def set_task_generation_service(service: TaskGenerationService): function get_task_service (line 41) | def get_task_service() -> TaskService: function get_notification_service (line 47) | def get_notification_service() -> NotificationService: function get_ai_service (line 52) | def get_ai_service() -> AIAnalysisService: function get_process_service (line 58) | def get_process_service() -> ProcessService: function get_scheduler_service (line 65) | def get_scheduler_service() -> SchedulerService: function get_task_generation_service (line 72) | def get_task_generation_service() -> TaskGenerationService: FILE: src/api/routes/accounts.py class AccountCreate (line 19) | class AccountCreate(BaseModel): class AccountUpdate (line 24) | class AccountUpdate(BaseModel): function _strip_quotes (line 28) | def _strip_quotes(value: str) -> str: function _state_dir (line 36) | def _state_dir() -> str: function _ensure_state_dir (line 41) | def _ensure_state_dir(path: str) -> None: function _validate_name (line 45) | def _validate_name(name: str) -> str: function _account_path (line 52) | def _account_path(name: str) -> str: function _validate_json (line 57) | def _validate_json(content: str) -> None: function list_accounts (line 65) | async def list_accounts(): function get_account (line 81) | async def get_account(name: str): function create_account (line 92) | async def create_account(data: AccountCreate): function update_account (line 106) | async def update_account(name: str, data: AccountUpdate): function delete_account (line 120) | async def delete_account(name: str): FILE: src/api/routes/dashboard.py function get_dashboard_summary (line 15) | async def get_dashboard_summary( FILE: src/api/routes/login_state.py class LoginStateUpdate (line 14) | class LoginStateUpdate(BaseModel): function update_login_state (line 20) | async def update_login_state( function delete_login_state (line 41) | async def delete_login_state(): FILE: src/api/routes/logs.py function _read_tail_lines (line 17) | async def _read_tail_lines( function get_logs (line 56) | async def get_logs( function get_logs_tail (line 105) | async def get_logs_tail( function clear_logs (line 165) | async def clear_logs( FILE: src/api/routes/prompts.py class PromptUpdate (line 13) | class PromptUpdate(BaseModel): function list_prompts (line 19) | async def list_prompts(): function get_prompt (line 28) | async def get_prompt(filename: str): function update_prompt (line 43) | async def update_prompt( FILE: src/api/routes/results.py function _build_download_headers (line 29) | def _build_download_headers(export_name: str) -> dict[str, str]: function get_result_files (line 43) | async def get_result_files(): function download_result_file (line 49) | async def download_result_file(filename: str): function delete_result_file (line 63) | async def delete_result_file(filename: str): function get_result_file_content (line 76) | async def get_result_file_content( function get_result_file_insights (line 121) | async def get_result_file_insights(filename: str): function export_result_file_content (line 131) | async def export_result_file_content( FILE: src/api/routes/settings.py function _reload_env (line 45) | def _reload_env() -> None: function _env_bool (line 50) | def _env_bool(key: str, default: bool = False) -> bool: function _env_int (line 57) | def _env_int(key: str, default: int) -> int: function _normalize_bool_value (line 67) | def _normalize_bool_value(value: bool) -> str: class NotificationSettingsModel (line 71) | class NotificationSettingsModel(BaseModel): class NotificationTestRequest (line 91) | class NotificationTestRequest(BaseModel): class AISettingsModel (line 98) | class AISettingsModel(BaseModel): class RotationSettingsModel (line 108) | class RotationSettingsModel(BaseModel): function get_notification_settings (line 122) | async def get_notification_settings(): function update_notification_settings (line 127) | async def update_notification_settings(settings: NotificationSettingsMod... function test_notification_settings (line 148) | async def test_notification_settings(payload: NotificationTestRequest): function get_rotation_settings (line 177) | async def get_rotation_settings(): function update_rotation_settings (line 193) | async def update_rotation_settings(settings: RotationSettingsModel): function get_system_status (line 209) | async def get_system_status( function get_ai_settings (line 249) | async def get_ai_settings(): function update_ai_settings (line 259) | async def update_ai_settings(settings: AISettingsModel): function test_ai_settings (line 280) | async def test_ai_settings(settings: dict): FILE: src/api/routes/tasks.py function _reload_scheduler_if_needed (line 33) | async def _reload_scheduler_if_needed( function _has_keyword_rules (line 41) | def _has_keyword_rules(rules) -> bool: function _validate_final_account_strategy (line 45) | def _validate_final_account_strategy(existing_task, task_update: TaskUpd... function get_tasks (line 59) | async def get_tasks( function get_task (line 67) | async def get_task( function create_task (line 78) | async def create_task( function generate_task (line 88) | async def generate_task( function get_task_generation_job (line 131) | async def get_task_generation_job( function update_task (line 141) | async def update_task( function delete_task (line 215) | async def delete_task( function start_task (line 253) | async def start_task( function stop_task (line 271) | async def stop_task( FILE: src/api/routes/websocket.py function websocket_endpoint (line 16) | async def websocket_endpoint( function broadcast_message (line 39) | async def broadcast_message(message_type: str, data: dict): FILE: src/app.py function _sync_task_runtime_status (line 42) | async def _sync_task_runtime_status(task_id: int, is_running: bool) -> N... function lifespan (line 66) | async def lifespan(app: FastAPI): function health_check (line 129) | async def health_check(): class LoginRequest (line 139) | class LoginRequest(BaseModel): function auth_status (line 145) | async def auth_status(payload: LoginRequest): function read_root (line 156) | async def read_root(request: Request): function serve_spa (line 169) | async def serve_spa(request: Request, full_path: str): FILE: src/config.py function get_ai_request_params (line 89) | def get_ai_request_params(**kwargs): FILE: src/core/cron_utils.py function normalize_cron_expression (line 27) | def normalize_cron_expression(value: Optional[str]) -> Optional[str]: function build_cron_trigger (line 38) | def build_cron_trigger( function validate_cron_expression (line 69) | def validate_cron_expression(value: Optional[str]) -> Optional[str]: FILE: src/domain/models/task.py class TaskStatus (line 18) | class TaskStatus(str, Enum): function _normalize_keyword_values (line 26) | def _normalize_keyword_values(value) -> List[str]: function _extract_keywords_from_legacy_groups (line 52) | def _extract_keywords_from_legacy_groups(groups) -> List[str]: function _normalize_payload_keywords (line 67) | def _normalize_payload_keywords(payload: Any) -> Any: function _has_keyword_rules (line 85) | def _has_keyword_rules(keyword_rules: List[str]) -> bool: function _normalize_optional_string (line 89) | def _normalize_optional_string(value): function _validate_cron_expression (line 95) | def _validate_cron_expression(value: Optional[str]) -> Optional[str]: function _normalize_price_value (line 99) | def _normalize_price_value(value): class Task (line 107) | class Task(BaseModel): method normalize_legacy_keyword_payload (line 136) | def normalize_legacy_keyword_payload(cls, values): method normalize_keyword_rules (line 141) | def normalize_keyword_rules(cls, value): method can_start (line 144) | def can_start(self) -> bool: method can_stop (line 148) | def can_stop(self) -> bool: method apply_update (line 152) | def apply_update(self, update: "TaskUpdate") -> "Task": class TaskCreate (line 158) | class TaskCreate(BaseModel): method normalize_legacy_keyword_payload (line 185) | def normalize_legacy_keyword_payload(cls, values): method convert_price_to_str (line 190) | def convert_price_to_str(cls, value): method normalize_cron (line 195) | def normalize_cron(cls, value): method normalize_account_state_file (line 200) | def normalize_account_state_file(cls, value): method validate_cron (line 205) | def validate_cron(cls, value): method normalize_keyword_rules (line 210) | def normalize_keyword_rules(cls, value): method validate_decision_mode_payload (line 214) | def validate_decision_mode_payload(self): class TaskUpdate (line 225) | class TaskUpdate(BaseModel): method normalize_legacy_keyword_payload (line 253) | def normalize_legacy_keyword_payload(cls, values): method convert_price_to_str (line 258) | def convert_price_to_str(cls, value): method normalize_cron (line 263) | def normalize_cron(cls, value): method normalize_account_state_file (line 268) | def normalize_account_state_file(cls, value): method validate_cron (line 273) | def validate_cron(cls, value): method normalize_keyword_rules (line 278) | def normalize_keyword_rules(cls, value): method validate_partial_keyword_payload (line 282) | def validate_partial_keyword_payload(self): class TaskGenerateRequest (line 292) | class TaskGenerateRequest(BaseModel): method normalize_legacy_keyword_payload (line 316) | def normalize_legacy_keyword_payload(cls, values): method convert_price_to_str (line 321) | def convert_price_to_str(cls, value): method empty_str_to_none (line 326) | def empty_str_to_none(cls, value): method validate_cron (line 331) | def validate_cron(cls, value): method empty_account_to_none (line 336) | def empty_account_to_none(cls, value): method empty_str_to_none_for_strings (line 341) | def empty_str_to_none_for_strings(cls, value): method normalize_keyword_rules (line 346) | def normalize_keyword_rules(cls, value): method validate_decision_mode_payload (line 350) | def validate_decision_mode_payload(self): FILE: src/domain/models/task_generation.py class TaskGenerationStep (line 15) | class TaskGenerationStep(BaseModel): class TaskGenerationJob (line 24) | class TaskGenerationJob(BaseModel): FILE: src/domain/repositories/task_repository.py class TaskRepository (line 12) | class TaskRepository(ABC): method find_all (line 16) | async def find_all(self) -> List[Task]: method find_by_id (line 21) | async def find_by_id(self, task_id: int) -> Optional[Task]: method save (line 26) | async def save(self, task: Task) -> Task: method delete (line 31) | async def delete(self, task_id: int) -> bool: FILE: src/failure_guard.py function _load_tz (line 25) | def _load_tz(name: str): function _load_tz (line 31) | def _load_tz(name: str): function _as_int (line 35) | def _as_int(value: Any, default: int) -> int: function _now (line 42) | def _now(tz_name: str, now: Optional[datetime] = None) -> datetime: function _today_str (line 51) | def _today_str(tz_name: str, now: Optional[datetime] = None) -> str: function _dt_to_str (line 55) | def _dt_to_str(dt: Optional[datetime]) -> Optional[str]: function _str_to_dt (line 61) | def _str_to_dt(value: Optional[str]) -> Optional[datetime]: function _get_mtime (line 70) | def _get_mtime(path: Optional[str]) -> Optional[float]: function _cookie_changed (line 79) | def _cookie_changed( class _FileLock (line 90) | class _FileLock: method __init__ (line 91) | def __init__(self, fh): method __enter__ (line 94) | def __enter__(self): method __exit__ (line 103) | def __exit__(self, exc_type, exc, tb): function _ensure_parent_dir (line 113) | def _ensure_parent_dir(path: str) -> None: function _read_json_file (line 119) | def _read_json_file(path: str) -> dict: function _atomic_write_json (line 136) | def _atomic_write_json(path: str, data: dict) -> None: class SkipDecision (line 147) | class SkipDecision: class FailureGuard (line 155) | class FailureGuard: method __init__ (line 156) | def __init__( method _load (line 179) | def _load(self) -> dict: method _save (line 186) | def _save(self, data: dict) -> None: method _update_task (line 189) | def _update_task(self, task_key: str, updater) -> dict: method record_success (line 204) | def record_success(self, task_key: str, *, now: Optional[datetime] = N... method should_skip_start (line 220) | def should_skip_start( method record_failure (line 291) | def record_failure( FILE: src/infrastructure/config/env_manager.py class EnvManager (line 16) | class EnvManager: method __init__ (line 19) | def __init__(self, env_file: str = ".env"): method _ensure_env_file_exists (line 23) | def _ensure_env_file_exists(self): method read_env (line 28) | def read_env(self) -> Dict[str, str]: method get_value (line 40) | def get_value(self, key: str, default: Optional[str] = None) -> Option... method update_values (line 49) | def update_values(self, updates: Dict[str, str]) -> bool: method apply_changes (line 53) | def apply_changes( method set_value (line 69) | def set_value(self, key: str, value: str) -> bool: method delete_keys (line 73) | def delete_keys(self, keys: List[str]) -> bool: method _write_env (line 84) | def _write_env(self, env_vars: Dict[str, str]) -> bool: method _serialize_value (line 95) | def _serialize_value(self, value: str) -> str: FILE: src/infrastructure/config/settings.py function _env_field (line 18) | def _env_field(default, env_name: str, **kwargs): class _EnvSettings (line 25) | class _EnvSettings(BaseSettings): class Config (line 34) | class Config: class _EnvSettings (line 33) | class _EnvSettings(BaseSettings): class Config (line 34) | class Config: class AISettings (line 41) | class AISettings(_EnvSettings): method is_configured (line 52) | def is_configured(self) -> bool: class NotificationSettings (line 57) | class NotificationSettings(_EnvSettings): method has_any_notification_enabled (line 78) | def has_any_notification_enabled(self) -> bool: class ScraperSettings (line 90) | class ScraperSettings(_EnvSettings): class AppSettings (line 98) | class AppSettings(_EnvSettings): method __init__ (line 110) | def __init__(self, **kwargs): function get_settings (line 119) | def get_settings() -> AppSettings: function reload_settings (line 127) | def reload_settings() -> None: FILE: src/infrastructure/external/ai_client.py class AIClient (line 36) | class AIClient: method __init__ (line 39) | def __init__(self): method _load_settings (line 44) | def _load_settings(self) -> None: method refresh (line 48) | def refresh(self) -> None: method _initialize_client (line 52) | def _initialize_client(self) -> Optional[AsyncOpenAI]: method is_available (line 72) | def is_available(self) -> bool: method close (line 76) | async def close(self) -> None: method encode_image (line 89) | def encode_image(image_path: str) -> Optional[str]: method analyze (line 100) | async def analyze( method _build_messages (line 129) | def _build_messages(self, product_data: Dict, image_paths: List[str], ... method _call_ai (line 146) | async def _call_ai( method _parse_response (line 223) | def _parse_response(self, response_text: str) -> Optional[Dict]: FILE: src/infrastructure/external/notification_clients/bark_client.py class BarkClient (line 10) | class BarkClient(NotificationClient): method __init__ (line 16) | def __init__(self, bark_url: str = None, pcurl_to_mobile: bool = True): method send (line 20) | async def send(self, product_data: Dict, reason: str) -> None: FILE: src/infrastructure/external/notification_clients/base.py class NotificationMessage (line 13) | class NotificationMessage: class NotificationClient (line 24) | class NotificationClient(ABC): method __init__ (line 30) | def __init__(self, enabled: bool = False, pcurl_to_mobile: bool = True): method is_enabled (line 34) | def is_enabled(self) -> bool: method send (line 39) | async def send(self, product_data: Dict, reason: str) -> bool: method _build_message (line 52) | def _build_message(self, product_data: Dict, reason: str) -> Notificat... FILE: src/infrastructure/external/notification_clients/factory.py function build_notification_clients (line 14) | def build_notification_clients(settings: NotificationSettings): FILE: src/infrastructure/external/notification_clients/gotify_client.py class GotifyClient (line 12) | class GotifyClient(NotificationClient): method __init__ (line 18) | def __init__( method send (line 31) | async def send(self, product_data: Dict, reason: str) -> None: FILE: src/infrastructure/external/notification_clients/ntfy_client.py class NtfyClient (line 10) | class NtfyClient(NotificationClient): method __init__ (line 16) | def __init__(self, topic_url: str = None, pcurl_to_mobile: bool = True): method send (line 20) | async def send(self, product_data: Dict, reason: str) -> None: FILE: src/infrastructure/external/notification_clients/telegram_client.py class TelegramClient (line 14) | class TelegramClient(NotificationClient): method __init__ (line 20) | def __init__( method send (line 34) | async def send(self, product_data: Dict, reason: str) -> None: FILE: src/infrastructure/external/notification_clients/webhook_client.py class WebhookClient (line 14) | class WebhookClient(NotificationClient): method __init__ (line 20) | def __init__( method send (line 38) | async def send(self, product_data: Dict, reason: str) -> None: method _build_url (line 68) | def _build_url(self, message: NotificationMessage) -> str: method _build_body (line 81) | def _build_body( method _parse_json (line 106) | def _parse_json( method _render_template (line 122) | def _render_template(self, value: Any, message: NotificationMessage) -... method _replace_placeholders (line 134) | def _replace_placeholders(self, value: str, message: NotificationMessa... FILE: src/infrastructure/external/notification_clients/wecom_bot_client.py class WeComBotClient (line 12) | class WeComBotClient(NotificationClient): method __init__ (line 18) | def __init__(self, bot_url: str | None = None, pcurl_to_mobile: bool =... method send (line 22) | async def send(self, product_data: Dict, reason: str) -> None: FILE: src/infrastructure/persistence/json_task_repository.py class JsonTaskRepository (line 11) | class JsonTaskRepository(TaskRepository): method __init__ (line 14) | def __init__(self, config_file: str = "config.json"): method find_all (line 17) | async def find_all(self) -> List[Task]: method find_by_id (line 37) | async def find_by_id(self, task_id: int) -> Optional[Task]: method save (line 44) | async def save(self, task: Task) -> Task: method delete (line 59) | async def delete(self, task_id: int) -> bool: method _write_tasks (line 68) | async def _write_tasks(self, tasks: List[Task]): FILE: src/infrastructure/persistence/sqlite_bootstrap.py function bootstrap_sqlite_storage (line 28) | def bootstrap_sqlite_storage( function _table_is_empty (line 43) | def _table_is_empty(conn, table_name: str) -> bool: function _load_json_file (line 48) | def _load_json_file(path: Path): function _import_tasks_if_needed (line 57) | def _import_tasks_if_needed(conn, legacy_config_file: str | None) -> None: function _import_results_if_needed (line 116) | def _import_results_if_needed(conn, legacy_result_dir: str) -> None: function _import_price_snapshots_if_needed (line 146) | def _import_price_snapshots_if_needed(conn, legacy_price_history_dir: st... function _insert_result_record (line 174) | def _insert_result_record(conn, record: dict, *, keyword: str, filename:... function _insert_price_snapshot (line 225) | def _insert_price_snapshot(conn, record: dict) -> None: function _as_int (line 256) | def _as_int(value) -> int: function _parse_price (line 264) | def _parse_price(value): function _bootstrap_completed (line 281) | def _bootstrap_completed(conn, key: str) -> bool: function _mark_bootstrap_completed (line 289) | def _mark_bootstrap_completed(conn, key: str) -> None: FILE: src/infrastructure/persistence/sqlite_connection.py function get_database_path (line 120) | def get_database_path() -> str: function _prepare_database_file (line 124) | def _prepare_database_file(path: str) -> None: function _apply_pragmas (line 128) | def _apply_pragmas(conn: sqlite3.Connection) -> None: function init_schema (line 134) | def init_schema(conn: sqlite3.Connection) -> None: function sqlite_connection (line 141) | def sqlite_connection( FILE: src/infrastructure/persistence/sqlite_task_repository.py function _row_to_task (line 16) | def _row_to_task(row) -> Task: function find_task_by_name_sync (line 27) | def find_task_by_name_sync(task_name: str) -> Task | None: class SqliteTaskRepository (line 37) | class SqliteTaskRepository(TaskRepository): method __init__ (line 40) | def __init__( method find_all (line 48) | async def find_all(self) -> List[Task]: method find_by_id (line 51) | async def find_by_id(self, task_id: int) -> Optional[Task]: method save (line 54) | async def save(self, task: Task) -> Task: method delete (line 57) | async def delete(self, task_id: int) -> bool: method _find_all_sync (line 60) | def _find_all_sync(self) -> List[Task]: method _find_by_id_sync (line 69) | def _find_by_id_sync(self, task_id: int) -> Optional[Task]: method _save_sync (line 78) | def _save_sync(self, task: Task) -> Task: method _delete_sync (line 109) | def _delete_sync(self, task_id: int) -> bool: method _next_task_id (line 119) | def _next_task_id(self, conn) -> int: method _task_values (line 123) | def _task_values(self, task: Task) -> dict: FILE: src/infrastructure/persistence/storage_names.py function build_result_filename (line 11) | def build_result_filename(keyword: str) -> str: function normalize_keyword_from_filename (line 15) | def normalize_keyword_from_filename(filename: str) -> str: function normalize_keyword_slug (line 19) | def normalize_keyword_slug(keyword: str) -> str: FILE: src/keyword_rule_engine.py function normalize_text (line 13) | def normalize_text(value: str) -> str: function _collect_text_fragments (line 17) | def _collect_text_fragments(value: Any, bucket: List[str]) -> None: function build_search_text (line 37) | def build_search_text(record: Dict[str, Any]) -> str: function _normalize_keywords (line 49) | def _normalize_keywords(values: Iterable[str]) -> List[str]: function _uses_ascii_token_match (line 61) | def _uses_ascii_token_match(keyword: str) -> bool: function _keyword_matches (line 65) | def _keyword_matches(keyword: str, normalized_text: str) -> bool: function evaluate_keyword_rules (line 72) | def evaluate_keyword_rules(keywords: List[str], search_text: str) -> Dic... FILE: src/parsers.py function _parse_search_results_json (line 8) | async def _parse_search_results_json(json_data: dict, source: str) -> list: function calculate_reputation_from_ratings (line 67) | async def calculate_reputation_from_ratings(ratings_json: list) -> dict: function _parse_user_items_data (line 101) | async def _parse_user_items_data(items_json: list) -> list: function parse_user_head_data (line 124) | async def parse_user_head_data(head_json: dict) -> dict: function parse_ratings_data (line 145) | async def parse_ratings_data(ratings_json: list) -> list: FILE: src/prompt_utils.py function _report_progress (line 39) | async def _report_progress( function _read_reference_text (line 48) | def _read_reference_text(reference_file_path: str) -> str: function _request_generated_text (line 58) | async def _request_generated_text(ai_client: AIClient, prompt: str) -> str: function _close_ai_client (line 75) | async def _close_ai_client( function generate_criteria (line 87) | async def generate_criteria( function update_config_with_new_task (line 123) | async def update_config_with_new_task(new_task: dict, config_file: str =... FILE: src/rotation.py class RotationItem (line 9) | class RotationItem: class RotationPool (line 14) | class RotationPool: method __init__ (line 15) | def __init__(self, items: List[str], blacklist_ttl: int = 300, name: s... method _cleanup_blacklist (line 21) | def _cleanup_blacklist(self) -> None: method available_items (line 27) | def available_items(self) -> List[RotationItem]: method pick_random (line 31) | def pick_random(self) -> Optional[RotationItem]: method mark_bad (line 37) | def mark_bad(self, item: Optional[RotationItem], reason: str = "") -> ... function parse_proxy_pool (line 46) | def parse_proxy_pool(value: Optional[str]) -> List[str]: function load_state_files (line 54) | def load_state_files(state_dir: str) -> List[str]: FILE: src/scraper.py class RiskControlError (line 66) | class RiskControlError(Exception): class LoginRequiredError (line 70) | class LoginRequiredError(Exception): function _is_login_url (line 78) | def _is_login_url(url: str) -> bool: function _resolve_browser_channel (line 85) | def _resolve_browser_channel() -> str: function _should_analyze_images (line 98) | def _should_analyze_images(task_config: dict) -> bool: function _format_failure_reason (line 105) | def _format_failure_reason(reason: str, limit: int = 500) -> str: function _notify_task_failure (line 114) | async def _notify_task_failure( function _as_bool (line 167) | def _as_bool(value, default: bool = False) -> bool: function _as_int (line 175) | def _as_int(value, default: int) -> int: function _get_rotation_settings (line 184) | def _get_rotation_settings(task_config: dict) -> dict: function _get_ai_analysis_concurrency (line 237) | def _get_ai_analysis_concurrency(task_config: dict) -> int: function _get_seller_profile_cache_ttl (line 243) | def _get_seller_profile_cache_ttl(task_config: dict) -> int: function _default_context_options (line 249) | def _default_context_options() -> dict: function _clean_kwargs (line 264) | def _clean_kwargs(options: dict) -> dict: function _looks_like_mobile (line 268) | def _looks_like_mobile(ua: str) -> Optional[bool]: function _build_context_overrides (line 279) | def _build_context_overrides(snapshot: dict) -> dict: function _build_extra_headers (line 329) | def _build_extra_headers(raw_headers: Optional[dict]) -> dict: function scrape_user_profile (line 341) | async def scrape_user_profile(context, user_id: str) -> dict: function scrape_xianyu (line 445) | async def scrape_xianyu(task_config: dict, debug_limit: int = 0): FILE: src/services/account_strategy_service.py function clean_account_state_file (line 10) | def clean_account_state_file(value: Optional[str]) -> Optional[str]: function normalize_account_strategy (line 19) | def normalize_account_strategy( function resolve_account_runtime_plan (line 31) | def resolve_account_runtime_plan( FILE: src/services/ai_request_compat.py function build_responses_input (line 38) | def build_responses_input(messages: Iterable[Dict[str, Any]]) -> List[Di... function add_json_text_format (line 52) | def add_json_text_format( function add_json_response_format (line 67) | def add_json_response_format( function is_json_output_unsupported_error (line 78) | def is_json_output_unsupported_error(error: Exception) -> bool: function is_responses_api_unsupported_error (line 87) | def is_responses_api_unsupported_error(error: Exception) -> bool: function is_chat_completions_api_unsupported_error (line 92) | def is_chat_completions_api_unsupported_error(error: Exception) -> bool: function build_ai_request_params (line 97) | def build_ai_request_params( function create_ai_response_async (line 127) | async def create_ai_response_async( function create_ai_response_sync (line 140) | def create_ai_response_sync( function is_temperature_unsupported_error (line 153) | def is_temperature_unsupported_error(error: Exception) -> bool: function remove_temperature_param (line 164) | def remove_temperature_param(request_params: Dict[str, Any]) -> Dict[str... function _is_api_unsupported_error (line 171) | def _is_api_unsupported_error( function _build_input_content (line 191) | def _build_input_content(content: Any) -> List[Dict[str, Any]]: function _coerce_content_item (line 200) | def _coerce_content_item(item: Any) -> Dict[str, Any]: function _build_image_input_item (line 217) | def _build_image_input_item(item: Dict[str, Any]) -> Dict[str, Any]: FILE: src/services/ai_response_parser.py class EmptyAIResponseError (line 8) | class EmptyAIResponseError(ValueError): function extract_ai_response_content (line 12) | def extract_ai_response_content(response: Any) -> str: function parse_ai_response_json (line 39) | def parse_ai_response_json(content: str) -> dict: function _coerce_content_parts (line 48) | def _coerce_content_parts(content: Any) -> str: function _normalize_text_content (line 74) | def _normalize_text_content(content: str) -> str: function _strip_code_fences (line 81) | def _strip_code_fences(content: str) -> str: function _extract_first_json_value (line 92) | def _extract_first_json_value( FILE: src/services/ai_service.py class AIAnalysisService (line 9) | class AIAnalysisService: method __init__ (line 12) | def __init__(self, ai_client: AIClient): method analyze_product (line 15) | async def analyze_product( method _validate_result (line 48) | def _validate_result(self, result: Dict) -> bool: FILE: src/services/dashboard_payloads.py function normalize_text (line 17) | def normalize_text(value: str | None) -> str: function parse_timestamp (line 21) | def parse_timestamp(value: str | None) -> datetime | None: function serialize_timestamp (line 33) | def serialize_timestamp(value: datetime | None) -> str | None: function build_empty_summary (line 37) | def build_empty_summary(task: Task) -> dict[str, Any]: function build_activity (line 58) | def build_activity( function sort_key_by_latest_time (line 83) | def sort_key_by_latest_time(item: dict[str, Any]) -> tuple[float, str]: function sort_key_by_activity_time (line 88) | def sort_key_by_activity_time(item: dict[str, Any]) -> tuple[float, str]: function _build_fallback_summary (line 93) | def _build_fallback_summary(task_name: str, keyword: str) -> dict[str, A... function _resolve_task (line 114) | def _resolve_task( function _collect_record_metrics (line 129) | def _collect_record_metrics(records: list[dict[str, Any]]) -> dict[str, ... function _build_recommendation_activity (line 172) | def _build_recommendation_activity( function _build_scan_activity (line 202) | def _build_scan_activity( function summarize_result_file (line 227) | async def summarize_result_file( function build_task_state_activities (line 277) | def build_task_state_activities(tasks: list[Task]) -> list[dict[str, Any]]: FILE: src/services/dashboard_service.py function _build_summary_metrics (line 24) | def _build_summary_metrics(tasks: list[Task], summary_list: list[dict[st... function build_dashboard_snapshot (line 37) | async def build_dashboard_snapshot(tasks: list[Task]) -> dict[str, Any]: FILE: src/services/item_analysis_dispatcher.py class ItemAnalysisJob (line 22) | class ItemAnalysisJob: class ItemAnalysisDispatcher (line 35) | class ItemAnalysisDispatcher: method __init__ (line 38) | def __init__( method submit (line 59) | def submit(self, job: ItemAnalysisJob) -> None: method join (line 64) | async def join(self) -> None: method _process_with_limit (line 68) | async def _process_with_limit(self, job: ItemAnalysisJob) -> None: method _process_job (line 72) | async def _process_job(self, job: ItemAnalysisJob) -> None: method _load_seller_info (line 81) | async def _load_seller_info(self, job: ItemAnalysisJob) -> dict: method _build_analysis_result (line 93) | async def _build_analysis_result(self, job: ItemAnalysisJob, record: d... method _build_keyword_result (line 100) | def _build_keyword_result(self, job: ItemAnalysisJob, record: dict) ->... method _build_skip_ai_result (line 104) | def _build_skip_ai_result(self) -> dict: method _build_ai_error_result (line 112) | def _build_ai_error_result(self, reason: str, *, error: str = "") -> d... method _run_ai_analysis (line 123) | async def _run_ai_analysis(self, job: ItemAnalysisJob, record: dict) -... method _download_images (line 146) | async def _download_images(self, job: ItemAnalysisJob, record: dict) -... method _cleanup_images (line 159) | def _cleanup_images(self, image_paths: list[str]) -> None: method _notify_if_recommended (line 167) | async def _notify_if_recommended(self, item_data: dict, analysis_resul... FILE: src/services/notification_config_service.py class NotificationSettingsValidationError (line 60) | class NotificationSettingsValidationError(ValueError): function model_dump (line 64) | def model_dump(model, *, exclude_unset: bool = False) -> dict: function build_notification_settings_response (line 70) | def build_notification_settings_response( function build_notification_status_flags (line 101) | def build_notification_status_flags( function build_configured_channels (line 118) | def build_configured_channels( function prepare_notification_settings_update (line 138) | def prepare_notification_settings_update( function _notification_settings_to_values (line 172) | def _notification_settings_to_values(settings: NotificationSettings) -> ... function load_notification_settings (line 179) | def load_notification_settings() -> NotificationSettings: function _build_notification_settings_model (line 204) | def _build_notification_settings_model(values: dict) -> NotificationSett... function _normalize_patch_value (line 210) | def _normalize_patch_value(env_name: str, value): function _normalize_existing_text (line 219) | def _normalize_existing_text(value: str | None) -> str | None: function _env_bool (line 226) | def _env_bool(value: str | None, default: bool) -> bool: function _normalize_notification_values (line 232) | def _normalize_notification_values(values: dict) -> dict: function _validate_notification_settings (line 255) | def _validate_notification_settings(settings: NotificationSettings) -> N... function _validate_http_url (line 305) | def _validate_http_url(field_name: str, value: str) -> None: function _validate_pair (line 313) | def _validate_pair( function _parse_json_field (line 326) | def _parse_json_field( FILE: src/services/notification_service.py class NotificationService (line 14) | class NotificationService: method __init__ (line 17) | def __init__(self, clients: List[NotificationClient]): method send_notification (line 20) | async def send_notification( method send_test_notification (line 41) | async def send_test_notification(self) -> Dict[str, Dict[str, str | bo... method _send_with_result (line 52) | async def _send_with_result( function build_notification_service (line 75) | def build_notification_service( FILE: src/services/price_history_service.py function normalize_keyword_slug (line 21) | def normalize_keyword_slug(keyword: str) -> str: function build_price_history_path (line 29) | def build_price_history_path(keyword: str) -> str: function parse_price_value (line 36) | def parse_price_value(value: Any) -> Optional[float]: function _safe_iso_datetime (line 53) | def _safe_iso_datetime(value: Optional[str]) -> str: function _to_day (line 59) | def _to_day(iso_text: str) -> str: function _build_snapshot_record (line 63) | def _build_snapshot_record( function record_market_snapshots (line 96) | def record_market_snapshots( function load_price_snapshots (line 159) | def load_price_snapshots(keyword: str) -> list[dict]: function delete_price_snapshots (line 194) | def delete_price_snapshots(keyword: str) -> int: function _dedupe_latest (line 205) | def _dedupe_latest(records: Iterable[dict], group_key: str) -> list[dict]: function _summarize_prices (line 215) | def _summarize_prices(records: Iterable[dict]) -> dict: function _build_daily_trend (line 236) | def _build_daily_trend(snapshots: list[dict]) -> list[dict]: function _recent_window_snapshots (line 250) | def _recent_window_snapshots(snapshots: list[dict], window_days: int) ->... function _resolve_deal_label (line 263) | def _resolve_deal_label(score: int) -> str: function build_item_price_context (line 273) | def build_item_price_context( function build_market_reference (line 333) | def build_market_reference( function build_price_history_insights (line 362) | def build_price_history_insights( FILE: src/services/process_service.py class ProcessService (line 25) | class ProcessService: method __init__ (line 28) | def __init__(self): method set_lifecycle_hooks (line 38) | def set_lifecycle_hooks( method _invoke_hook (line 47) | async def _invoke_hook(self, hook: LifecycleHook | None, task_id: int)... method _resolve_cookie_path (line 54) | def _resolve_cookie_path(self, task_name: str) -> str | None: method is_running (line 65) | def is_running(self, task_id: int) -> bool: method _drain_finished_process (line 70) | async def _drain_finished_process(self, task_id: int) -> None: method _open_log_file (line 83) | def _open_log_file(self, task_id: int, task_name: str) -> tuple[str, T... method _build_spawn_command (line 89) | def _build_spawn_command(self, task_name: str) -> list[str]: method _spawn_process (line 102) | async def _spawn_process( method _register_runtime (line 119) | def _register_runtime( method start_task (line 133) | async def start_task(self, task_id: int, task_name: str) -> bool: method _notify_skip (line 163) | async def _notify_skip(self, task_name: str, decision) -> None: method _watch_process_exit (line 186) | async def _watch_process_exit(self, process: asyncio.subprocess.Proces... method _find_task_id_by_process (line 194) | def _find_task_id_by_process(self, process: asyncio.subprocess.Process... method _cleanup_runtime (line 200) | def _cleanup_runtime( method _close_log_handle (line 213) | def _close_log_handle(self, log_handle: TextIO | None) -> None: method _append_stop_marker (line 219) | def _append_stop_marker(self, log_path: str | None) -> None: method stop_task (line 229) | async def stop_task(self, task_id: int) -> bool: method _terminate_process (line 254) | async def _terminate_process( method _await_exit_watcher (line 280) | async def _await_exit_watcher(self, task_id: int) -> None: method reindex_after_delete (line 286) | def reindex_after_delete(self, deleted_task_id: int) -> None: method _reindex_mapping (line 294) | def _reindex_mapping(self, mapping: Dict[int, object], deleted_task_id... method stop_all (line 303) | async def stop_all(self) -> None: FILE: src/services/result_export_service.py function build_results_csv (line 29) | def build_results_csv(records: list[dict]) -> str: FILE: src/services/result_file_service.py function validate_result_filename (line 13) | def validate_result_filename(filename: str) -> None: function enrich_records_with_price_insight (line 18) | def enrich_records_with_price_insight(records: list[dict], filename: str... FILE: src/services/result_storage_service.py function _get_link_unique_key (line 24) | def _get_link_unique_key(link: str) -> str: function _fallback_unique_key (line 28) | def _fallback_unique_key(record: dict, item: dict) -> str: function _parse_raw_record (line 38) | def _parse_raw_record(raw_json: str) -> dict: function _build_query_conditions (line 42) | def _build_query_conditions( function _sort_expression (line 61) | def _sort_expression(sort_by: str, sort_order: str) -> str: function save_result_record (line 67) | async def save_result_record(record: dict, keyword: str) -> bool: function _save_result_record_sync (line 71) | def _save_result_record_sync(record: dict, keyword: str) -> bool: function load_processed_link_keys (line 115) | def load_processed_link_keys(keyword: str) -> set[str]: function list_result_filenames (line 126) | async def list_result_filenames() -> list[str]: function _list_result_filenames_sync (line 130) | def _list_result_filenames_sync() -> list[str]: function result_file_exists (line 144) | async def result_file_exists(filename: str) -> bool: function _result_file_exists_sync (line 148) | def _result_file_exists_sync(filename: str) -> bool: function delete_result_file_records (line 158) | async def delete_result_file_records(filename: str) -> int: function _delete_result_file_records_sync (line 162) | def _delete_result_file_records_sync(filename: str) -> int: function query_result_records (line 173) | async def query_result_records( function _query_result_records_sync (line 195) | def _query_result_records_sync( function load_all_result_records (line 231) | async def load_all_result_records( function _load_all_result_records_sync (line 249) | def _load_all_result_records_sync( function build_result_ndjson (line 276) | async def build_result_ndjson(filename: str) -> str: function load_result_summary (line 287) | async def load_result_summary(filename: str) -> dict | None: function _load_result_summary_sync (line 291) | def _load_result_summary_sync(filename: str) -> dict | None: FILE: src/services/scheduler_service.py class SchedulerService (line 14) | class SchedulerService: method __init__ (line 17) | def __init__(self, process_service: ProcessService): method start (line 21) | def start(self): method stop (line 27) | def stop(self): method get_next_run_time (line 33) | def get_next_run_time(self, task_id: int): method reload_jobs (line 52) | async def reload_jobs(self, tasks: List[Task]): method _run_task (line 78) | async def _run_task(self, task_id: int, task_name: str): FILE: src/services/search_pagination.py class PageAdvanceResult (line 24) | class PageAdvanceResult: function is_search_results_response (line 30) | def is_search_results_response( function advance_search_page (line 40) | async def advance_search_page( FILE: src/services/seller_profile_cache.py class _CacheEntry (line 15) | class _CacheEntry: class SellerProfileCache (line 20) | class SellerProfileCache: method __init__ (line 23) | def __init__( method _now (line 34) | def _now(self) -> float: method _clone (line 37) | def _clone(self, value: dict) -> dict: method _get_entry_value (line 40) | def _get_entry_value(self, user_id: str) -> Optional[dict]: method get_or_load (line 49) | async def get_or_load(self, user_id: str, loader: SellerProfileLoader)... method _load_and_store (line 60) | async def _load_and_store(self, user_id: str, loader: SellerProfileLoa... FILE: src/services/task_generation_runner.py function build_criteria_filename (line 14) | def build_criteria_filename(keyword: str) -> str: function build_task_create (line 22) | def build_task_create(req: TaskGenerateRequest, criteria_file: str) -> T... function save_generated_criteria (line 46) | async def save_generated_criteria(output_filename: str, generated_criter... function reload_scheduler (line 55) | async def reload_scheduler( function advance_job (line 63) | async def advance_job( function run_ai_generation_job (line 72) | async def run_ai_generation_job( FILE: src/services/task_generation_service.py class TaskGenerationService (line 23) | class TaskGenerationService: method __init__ (line 26) | def __init__(self, step_specs: Iterable[tuple[str, str]] = DEFAULT_GEN... method create_job (line 32) | async def create_job(self, task_name: str) -> TaskGenerationJob: method get_job (line 45) | async def get_job(self, job_id: str) -> Optional[TaskGenerationJob]: method track (line 52) | def track(self, coroutine: Awaitable[None]) -> None: method advance (line 69) | async def advance(self, job_id: str, step_key: str, message: str) -> T... method complete (line 89) | async def complete(self, job_id: str, task: Task, message: str) -> Tas... method fail (line 102) | async def fail( method _require_job (line 122) | def _require_job(self, job_id: str) -> TaskGenerationJob: method _find_step (line 128) | def _find_step(self, job: TaskGenerationJob, step_key: str) -> Optiona... method _find_step_index (line 134) | def _find_step_index(self, job: TaskGenerationJob, step_key: str) -> int: FILE: src/services/task_log_cleanup_service.py function cleanup_task_logs (line 10) | def cleanup_task_logs( FILE: src/services/task_payloads.py function serialize_timestamp (line 12) | def serialize_timestamp(value: datetime | None) -> str | None: function serialize_task (line 16) | def serialize_task(task: Task, scheduler_service) -> dict[str, Any]: function serialize_tasks (line 25) | def serialize_tasks(tasks: list[Task], scheduler_service) -> list[dict[s... FILE: src/services/task_service.py class TaskService (line 10) | class TaskService: method __init__ (line 13) | def __init__(self, repository: TaskRepository): method get_all_tasks (line 16) | async def get_all_tasks(self) -> List[Task]: method get_task (line 20) | async def get_task(self, task_id: int) -> Optional[Task]: method create_task (line 24) | async def create_task(self, task_create: TaskCreate) -> Task: method update_task (line 29) | async def update_task(self, task_id: int, task_update: TaskUpdate) -> ... method delete_task (line 38) | async def delete_task(self, task_id: int) -> bool: method update_task_status (line 42) | async def update_task_status(self, task_id: int, is_running: bool) -> ... FILE: src/utils.py function retry_on_failure (line 18) | def retry_on_failure(retries=3, delay=5): function safe_get (line 51) | async def safe_get(data, *keys, default="暂无"): function random_sleep (line 61) | async def random_sleep(min_seconds: float, max_seconds: float): function log_time (line 68) | def log_time(message: str, prefix: str = "") -> None: function sanitize_filename (line 77) | def sanitize_filename(value: str) -> str: function build_task_log_path (line 86) | def build_task_log_path(task_id: int, task_name: str) -> str: function resolve_task_log_path (line 93) | def resolve_task_log_path(task_id: int, task_name: str) -> str: function convert_goofish_link (line 105) | def convert_goofish_link(url: str) -> str: function get_link_unique_key (line 117) | def get_link_unique_key(link: str) -> str: function save_to_jsonl (line 122) | async def save_to_jsonl(data_record: dict, keyword: str): function format_registration_days (line 131) | def format_registration_days(total_days: int) -> str: FILE: tests/conftest.py function fixtures_dir (line 25) | def fixtures_dir() -> Path: function load_json_fixture (line 30) | def load_json_fixture(fixtures_dir): function sample_task_payload (line 38) | def sample_task_payload(): class FakeProcessService (line 57) | class FakeProcessService: method __init__ (line 58) | def __init__(self): method set_lifecycle_hooks (line 65) | def set_lifecycle_hooks(self, *, on_started=None, on_stopped=None): method start_task (line 69) | async def start_task(self, task_id: int, task_name: str) -> bool: method stop_task (line 75) | async def stop_task(self, task_id: int): method reindex_after_delete (line 80) | def reindex_after_delete(self, deleted_task_id: int): class FakeSchedulerService (line 84) | class FakeSchedulerService: method __init__ (line 85) | def __init__(self): method reload_jobs (line 89) | async def reload_jobs(self, tasks): method get_next_run_time (line 98) | def get_next_run_time(self, task_id: int): function api_context (line 103) | def api_context(tmp_path): function api_client (line 158) | def api_client(api_context): FILE: tests/integration/test_api_dashboard.py function _write_jsonl (line 13) | def _write_jsonl(path, records): function test_dashboard_summary_aggregates_tasks_and_results (line 19) | def test_dashboard_summary_aggregates_tasks_and_results(tmp_path, monkey... FILE: tests/integration/test_api_results.py function _write_jsonl (line 10) | def _write_jsonl(path, records): function test_results_filter_and_sort_for_keyword_recommendations (line 16) | def test_results_filter_and_sort_for_keyword_recommendations(tmp_path, m... function test_results_insights_and_export_csv (line 85) | def test_results_insights_and_export_csv(tmp_path, monkeypatch): function test_results_export_csv_supports_unicode_filename (line 201) | def test_results_export_csv_supports_unicode_filename(tmp_path, monkeypa... FILE: tests/integration/test_api_settings.py class _IdleProcessService (line 43) | class _IdleProcessService: method __init__ (line 44) | def __init__(self) -> None: function _build_settings_client (line 48) | def _build_settings_client() -> TestClient: function _clear_settings_env (line 55) | def _clear_settings_env(monkeypatch) -> None: function test_rotation_settings_include_account_rotation_fields (line 60) | def test_rotation_settings_include_account_rotation_fields(tmp_path, mon... function test_notification_settings_redact_sensitive_values_and_expose_flags (line 110) | def test_notification_settings_redact_sensitive_values_and_expose_flags(... function test_update_notification_settings_rejects_invalid_channel_config (line 157) | def test_update_notification_settings_rejects_invalid_channel_config(tmp... function test_system_status_includes_notification_channel_flags (line 191) | def test_system_status_includes_notification_channel_flags(tmp_path, mon... function test_notification_test_endpoint_merges_stored_secret_values (line 226) | def test_notification_test_endpoint_merges_stored_secret_values(tmp_path... function test_ai_settings_fall_back_to_runtime_environment_when_env_file_missing (line 277) | def test_ai_settings_fall_back_to_runtime_environment_when_env_file_miss... function test_notification_settings_fall_back_to_runtime_environment_when_env_file_missing (line 305) | def test_notification_settings_fall_back_to_runtime_environment_when_env... function test_ai_test_endpoint_falls_back_to_responses_when_chat_completions_api_404 (line 331) | def test_ai_test_endpoint_falls_back_to_responses_when_chat_completions_... FILE: tests/integration/test_api_tasks.py function test_create_list_update_delete_task (line 5) | def test_create_list_update_delete_task(api_client, api_context, sample_... function test_start_stop_task_updates_status (line 36) | def test_start_stop_task_updates_status(api_client, api_context, sample_... function test_generate_keyword_mode_task_without_ai_criteria (line 59) | def test_generate_keyword_mode_task_without_ai_criteria(api_client): function test_generate_ai_task_returns_job_and_completes_async (line 78) | def test_generate_ai_task_returns_job_and_completes_async(api_client, ap... function test_create_task_accepts_cron_alias (line 124) | def test_create_task_accepts_cron_alias(api_client, sample_task_payload): function test_create_task_rejects_fixed_account_strategy_without_state_file (line 134) | def test_create_task_rejects_fixed_account_strategy_without_state_file(a... function test_create_task_accepts_rotate_account_strategy (line 143) | def test_create_task_accepts_rotate_account_strategy(api_client, sample_... function test_update_task_accepts_six_field_cron_expression (line 154) | def test_update_task_accepts_six_field_cron_expression(api_client, sampl... function test_create_task_rejects_invalid_cron_expression (line 167) | def test_create_task_rejects_invalid_cron_expression(api_client, sample_... function test_delete_task_stops_runtime_and_reindexes_process_state (line 176) | def test_delete_task_stops_runtime_and_reindexes_process_state( FILE: tests/integration/test_cli_spider.py function test_cli_runs_single_task_with_prompt (line 8) | def test_cli_runs_single_task_with_prompt(tmp_path, load_json_fixture, m... function test_cli_runs_keyword_mode_without_prompt_files (line 59) | def test_cli_runs_keyword_mode_without_prompt_files(tmp_path, load_json_... FILE: tests/integration/test_pipeline_parse.py function test_parse_search_results (line 12) | def test_parse_search_results(load_json_fixture): function test_parse_user_head_and_items (line 24) | def test_parse_user_head_and_items(load_json_fixture): function test_parse_ratings_and_reputation (line 37) | def test_parse_ratings_and_reputation(load_json_fixture): FILE: tests/live/_support.py class LiveTestSettings (line 39) | class LiveTestSettings: class LiveServer (line 52) | class LiveServer: function env_flag (line 60) | def env_flag(name: str, default: bool = False) -> bool: function env_int (line 67) | def env_int(name: str, default: int) -> int: function load_runtime_env (line 74) | def load_runtime_env(repo_root: Path) -> dict[str, str]: function build_ai_test_payload (line 88) | def build_ai_test_payload(runtime_env: dict[str, str]) -> dict[str, str]: function resolve_account_source (line 102) | def resolve_account_source(repo_root: Path) -> Path: function load_live_settings (line 115) | def load_live_settings(repo_root: Path) -> LiveTestSettings: function mirror_path (line 130) | def mirror_path(source: Path, destination: Path) -> None: function prepare_workspace (line 142) | def prepare_workspace(workspace: Path, settings: LiveTestSettings) -> Path: function build_server_env (line 155) | def build_server_env(workspace: Path, repo_root: Path, port: int) -> dic... function find_free_port (line 179) | def find_free_port() -> int: function wait_for_server_ready (line 186) | def wait_for_server_ready(base_url: str, process: subprocess.Popen, log_... function terminate_process (line 210) | def terminate_process(process: subprocess.Popen, timeout_seconds: int = ... FILE: tests/live/conftest.py function pytest_collection_modifyitems (line 25) | def pytest_collection_modifyitems(config, items): function live_settings (line 36) | def live_settings(): function live_server (line 51) | def live_server(live_settings, request, tmp_path_factory): FILE: tests/live/test_live_smoke.py function api_request (line 25) | def api_request(session: requests.Session, method: str, url: str, **kwar... function fetch_task (line 29) | def fetch_task(session: requests.Session, base_url: str, task_id: int) -... function fetch_results_or_none (line 34) | def fetch_results_or_none( function find_task_log (line 52) | def find_task_log(workspace: Path, task_id: int) -> Path | None: function read_task_log (line 57) | def read_task_log(workspace: Path, task_id: int) -> tuple[Path | None, s... function assert_log_is_clean (line 63) | def assert_log_is_clean(log_text: str, log_path: Path | None) -> None: function wait_for_task_running (line 68) | def wait_for_task_running( function wait_for_task_completion (line 83) | def wait_for_task_completion( function delete_task_safely (line 117) | def delete_task_safely(session: requests.Session, base_url: str, task_id... function build_live_task_payload (line 121) | def build_live_task_payload(account_state_file: Path, task_name: str, ke... function test_live_preflight_smoke (line 137) | def test_live_preflight_smoke(live_server): function test_live_real_traffic_task_smoke (line 154) | def test_live_real_traffic_task_smoke(live_server): function test_live_ai_task_generation_job (line 215) | def test_live_ai_task_generation_job(live_server): FILE: tests/test_failure_guard.py function test_failure_guard_opens_circuit_after_threshold_and_rate_limits (line 8) | def test_failure_guard_opens_circuit_after_threshold_and_rate_limits(tmp... function test_failure_guard_auto_recovers_on_cookie_change (line 49) | def test_failure_guard_auto_recovers_on_cookie_change(tmp_path): FILE: tests/test_frontend_build_paths.py function read_repo_file (line 10) | def read_repo_file(relative_path: str) -> str: function test_frontend_build_output_path_is_consistent_across_configs (line 14) | def test_frontend_build_output_path_is_consistent_across_configs(): FILE: tests/unit/test_ai_client.py function _build_fake_client (line 10) | def _build_fake_client(responses_create_impl, chat_create_impl=None): function test_build_messages_without_images_uses_text_only_content (line 18) | def test_build_messages_without_images_uses_text_only_content(): function test_build_messages_with_images_uses_multimodal_content (line 33) | def test_build_messages_with_images_uses_multimodal_content(monkeypatch): function test_build_responses_input_converts_multimodal_messages (line 49) | def test_build_responses_input_converts_multimodal_messages(): function test_call_ai_retries_without_structured_output_when_model_rejects_it (line 77) | def test_call_ai_retries_without_structured_output_when_model_rejects_it(): function test_call_ai_falls_back_to_responses_when_chat_completions_api_is_missing (line 113) | def test_call_ai_falls_back_to_responses_when_chat_completions_api_is_mi... function test_call_ai_retries_without_temperature_when_gateway_rejects_it (line 149) | def test_call_ai_retries_without_temperature_when_gateway_rejects_it(): function test_call_ai_retries_when_response_content_is_empty (line 179) | def test_call_ai_retries_when_response_content_is_empty(): function test_call_ai_raises_after_all_empty_response_retries_are_exhausted (line 202) | def test_call_ai_raises_after_all_empty_response_retries_are_exhausted(): function test_close_closes_underlying_async_client_and_clears_reference (line 223) | def test_close_closes_underlying_async_client_and_clears_reference(): function test_parse_response_uses_first_json_object_when_response_contains_multiple_objects (line 238) | def test_parse_response_uses_first_json_object_when_response_contains_mu... FILE: tests/unit/test_ai_handler_analysis.py function _build_fake_client (line 10) | def _build_fake_client(responses_create_impl, chat_create_impl=None): function test_get_ai_analysis_stops_after_internal_retries_when_content_is_none (line 18) | def test_get_ai_analysis_stops_after_internal_retries_when_content_is_none( function test_get_ai_analysis_returns_parsed_json (line 45) | def test_get_ai_analysis_returns_parsed_json(monkeypatch, tmp_path): function test_get_ai_analysis_retries_without_structured_output_when_model_rejects_it (line 75) | def test_get_ai_analysis_retries_without_structured_output_when_model_re... function test_get_ai_analysis_falls_back_to_responses_when_chat_completions_api_is_missing (line 123) | def test_get_ai_analysis_falls_back_to_responses_when_chat_completions_a... function test_get_ai_analysis_retries_without_temperature_when_gateway_rejects_it (line 175) | def test_get_ai_analysis_retries_without_temperature_when_gateway_reject... function test_get_ai_analysis_uses_first_json_object_when_model_returns_multiple_objects (line 216) | def test_get_ai_analysis_uses_first_json_object_when_model_returns_multi... FILE: tests/unit/test_ai_handler_downloads.py function test_download_all_images_runs_with_concurrency (line 7) | def test_download_all_images_runs_with_concurrency(tmp_path, monkeypatch): FILE: tests/unit/test_ai_request_compat.py function test_is_temperature_unsupported_error_detects_unsupported_message (line 8) | def test_is_temperature_unsupported_error_detects_unsupported_message(): function test_remove_temperature_param_removes_only_temperature (line 13) | def test_remove_temperature_param_removes_only_temperature(): function test_is_responses_api_unsupported_error_detects_gemini_plain_404 (line 22) | def test_is_responses_api_unsupported_error_detects_gemini_plain_404(): FILE: tests/unit/test_ai_response_parser.py function test_parse_ai_response_json_uses_first_object_when_multiple_json_objects_are_concatenated (line 6) | def test_parse_ai_response_json_uses_first_object_when_multiple_json_obj... function test_parse_ai_response_json_extracts_json_from_wrapped_text (line 17) | def test_parse_ai_response_json_extracts_json_from_wrapped_text(): function test_parse_ai_response_json_raises_when_no_json_exists (line 31) | def test_parse_ai_response_json_raises_when_no_json_exists(): FILE: tests/unit/test_app_lifespan.py class _FakeTaskService (line 6) | class _FakeTaskService: method __init__ (line 7) | def __init__(self, _repo): method get_all_tasks (line 10) | async def get_all_tasks(self): method update_task_status (line 13) | async def update_task_status(self, task_id, is_running): class _FakeSchedulerService (line 17) | class _FakeSchedulerService: method __init__ (line 18) | def __init__(self): method reload_jobs (line 23) | async def reload_jobs(self, tasks): method start (line 26) | def start(self): method stop (line 29) | def stop(self): class _FakeProcessService (line 33) | class _FakeProcessService: method __init__ (line 34) | def __init__(self): method stop_all (line 37) | async def stop_all(self): function test_lifespan_cleans_task_logs_on_startup (line 41) | def test_lifespan_cleans_task_logs_on_startup(monkeypatch): FILE: tests/unit/test_cron_utils.py function test_validate_cron_expression_normalizes_alias (line 4) | def test_validate_cron_expression_normalizes_alias(): function test_validate_cron_expression_accepts_six_fields (line 8) | def test_validate_cron_expression_accepts_six_fields(): function test_build_cron_trigger_accepts_alias_and_timezone (line 12) | def test_build_cron_trigger_accepts_alias_and_timezone(): function test_validate_cron_expression_rejects_invalid_value (line 19) | def test_validate_cron_expression_rejects_invalid_value(): FILE: tests/unit/test_domain_task.py function test_task_can_start_and_stop (line 4) | def test_task_can_start_and_stop(): function test_task_apply_update (line 29) | def test_task_apply_update(): function test_legacy_keyword_groups_are_flattened_to_keyword_rules (line 54) | def test_legacy_keyword_groups_are_flattened_to_keyword_rules(): function test_generate_request_accepts_legacy_group_payload (line 79) | def test_generate_request_accepts_legacy_group_payload(): function test_generate_request_enables_image_analysis_by_default (line 90) | def test_generate_request_enables_image_analysis_by_default(): function test_generate_request_infers_fixed_account_strategy_from_state_file (line 100) | def test_generate_request_infers_fixed_account_strategy_from_state_file(): function test_generate_request_requires_state_file_for_fixed_account_strategy (line 112) | def test_generate_request_requires_state_file_for_fixed_account_strategy(): FILE: tests/unit/test_item_analysis_dispatcher.py function test_item_analysis_dispatcher_uses_bounded_concurrency (line 9) | def test_item_analysis_dispatcher_uses_bounded_concurrency(): function test_item_analysis_dispatcher_supports_keyword_mode_without_ai (line 81) | def test_item_analysis_dispatcher_supports_keyword_mode_without_ai(): FILE: tests/unit/test_keyword_rule_engine.py function _sample_record (line 4) | def _sample_record(): function test_build_search_text_contains_product_and_seller_fields (line 18) | def test_build_search_text_contains_product_and_seller_fields(): function test_keyword_rules_or_match_any_keyword (line 25) | def test_keyword_rules_or_match_any_keyword(): function test_keyword_rules_count_multiple_hits (line 34) | def test_keyword_rules_count_multiple_hits(): function test_keyword_rules_case_insensitive_contains (line 41) | def test_keyword_rules_case_insensitive_contains(): function test_keyword_rules_no_match (line 48) | def test_keyword_rules_no_match(): function test_keyword_rules_do_not_partially_match_alphanumeric_prefixes (line 55) | def test_keyword_rules_do_not_partially_match_alphanumeric_prefixes(): function test_keyword_rules_still_match_full_alphanumeric_token (line 61) | def test_keyword_rules_still_match_full_alphanumeric_token(): FILE: tests/unit/test_notification_service.py class _OkClient (line 8) | class _OkClient(NotificationClient): method send (line 12) | async def send(self, product_data, reason): class _FailClient (line 16) | class _FailClient(NotificationClient): method send (line 20) | async def send(self, product_data, reason): function test_notification_service_collects_success_and_failure_results (line 24) | def test_notification_service_collects_success_and_failure_results(): function test_webhook_client_renders_json_templates (line 37) | def test_webhook_client_renders_json_templates(monkeypatch): FILE: tests/unit/test_price_history_service.py function test_record_market_snapshots_and_build_price_history_insights (line 9) | def test_record_market_snapshots_and_build_price_history_insights(tmp_pa... FILE: tests/unit/test_process_service.py class FakeProcess (line 8) | class FakeProcess: method __init__ (line 9) | def __init__(self, pid: int): method wait (line 14) | async def wait(self): method finish (line 18) | def finish(self, returncode: int = 0): method terminate (line 22) | def terminate(self): method kill (line 25) | def kill(self): function test_process_service_marks_task_stopped_when_process_exits (line 29) | def test_process_service_marks_task_stopped_when_process_exits(monkeypat... function test_process_service_reindexes_runtime_maps_after_delete (line 77) | def test_process_service_reindexes_runtime_maps_after_delete(): function test_process_service_adds_debug_limit_arg_when_env_enabled (line 97) | def test_process_service_adds_debug_limit_arg_when_env_enabled(monkeypat... FILE: tests/unit/test_prompt_utils.py function test_generate_criteria_closes_ai_client_after_success (line 9) | def test_generate_criteria_closes_ai_client_after_success(monkeypatch, t... function test_generate_criteria_closes_ai_client_after_ai_failure (line 37) | def test_generate_criteria_closes_ai_client_after_ai_failure(monkeypatch... FILE: tests/unit/test_scraper_browser_channel.py function _load_scraper (line 4) | def _load_scraper(monkeypatch, *, login_is_edge: bool, running_in_docker... function test_resolve_browser_channel_uses_chromium_in_docker_even_when_edge_requested (line 17) | def test_resolve_browser_channel_uses_chromium_in_docker_even_when_edge_... function test_resolve_browser_channel_uses_msedge_locally_when_requested (line 24) | def test_resolve_browser_channel_uses_msedge_locally_when_requested(monk... FILE: tests/unit/test_search_pagination.py class FakeRequest (line 9) | class FakeRequest: method __init__ (line 10) | def __init__(self, method: str = "POST"): class FakeResponse (line 14) | class FakeResponse: method __init__ (line 15) | def __init__(self, url: str, ok: bool = True, method: str = "POST"): class FakeLocator (line 21) | class FakeLocator: method __init__ (line 22) | def __init__(self, count: int, click_error: Exception | None = None): method first (line 30) | def first(self): method count (line 33) | async def count(self) -> int: method scroll_into_view_if_needed (line 36) | async def scroll_into_view_if_needed(self) -> None: method click (line 39) | async def click(self, timeout: int | None = None) -> None: class FakeResponseContext (line 46) | class FakeResponseContext: method __init__ (line 47) | def __init__(self, outcome): method __aenter__ (line 50) | async def __aenter__(self): method __aexit__ (line 53) | async def __aexit__(self, exc_type, exc, tb): method value (line 57) | def value(self): method _resolve (line 60) | async def _resolve(self): class FakePage (line 66) | class FakePage: method __init__ (line 67) | def __init__( method locator (line 76) | def locator(self, _selector: str) -> FakeLocator: method expect_response (line 79) | def expect_response(self, _predicate, timeout: int): function _noop_random_sleep (line 86) | async def _noop_random_sleep(_min_seconds: float, _max_seconds: float) -... function _noop_sleep (line 90) | async def _noop_sleep(_seconds: float) -> None: function test_advance_search_page_stops_when_no_next_button (line 94) | def test_advance_search_page_stops_when_no_next_button() -> None: function test_advance_search_page_stops_after_timeout_retries (line 115) | def test_advance_search_page_stops_after_timeout_retries() -> None: function test_advance_search_page_returns_new_response_on_success (line 146) | def test_advance_search_page_returns_new_response_on_success() -> None: function test_advance_search_page_stops_when_click_times_out (line 170) | def test_advance_search_page_stops_when_click_times_out() -> None: function test_is_search_results_response_matches_exact_search_api (line 195) | def test_is_search_results_response_matches_exact_search_api() -> None: function test_is_search_results_response_rejects_search_shade_api (line 204) | def test_is_search_results_response_rejects_search_shade_api() -> None: function test_is_search_results_response_rejects_non_post_request (line 213) | def test_is_search_results_response_rejects_non_post_request() -> None: FILE: tests/unit/test_seller_profile_cache.py function test_seller_profile_cache_reuses_value_and_returns_copy (line 6) | def test_seller_profile_cache_reuses_value_and_returns_copy(): function test_seller_profile_cache_coalesces_inflight_requests (line 27) | def test_seller_profile_cache_coalesces_inflight_requests(): FILE: tests/unit/test_task_log_cleanup_service.py function _write_file (line 9) | def _write_file(path: Path, content: str = "log") -> None: function _set_mtime (line 14) | def _set_mtime(path: Path, when: datetime) -> None: function test_cleanup_task_logs_removes_only_expired_top_level_logs (line 23) | def test_cleanup_task_logs_removes_only_expired_top_level_logs(tmp_path): function test_cleanup_task_logs_skips_when_retention_is_invalid (line 50) | def test_cleanup_task_logs_skips_when_retention_is_invalid(tmp_path): FILE: tests/unit/test_utils.py function test_safe_get_nested_and_default (line 12) | def test_safe_get_nested_and_default(): function test_format_registration_days (line 18) | def test_format_registration_days(): function test_get_link_unique_key (line 23) | def test_get_link_unique_key(): function test_save_to_jsonl (line 28) | def test_save_to_jsonl(tmp_path, monkeypatch): FILE: web-ui/src/api/accounts.ts type AccountItem (line 3) | interface AccountItem { type AccountDetail (line 8) | interface AccountDetail extends AccountItem { function listAccounts (line 12) | async function listAccounts(): Promise { function getAccount (line 16) | async function getAccount(name: string): Promise { function createAccount (line 20) | async function createAccount(payload: { name: string; content: string })... function updateAccount (line 28) | async function updateAccount(name: string, content: string): Promise { FILE: web-ui/src/api/dashboard.ts function getDashboardSummary (line 4) | async function getDashboardSummary(): Promise { FILE: web-ui/src/api/logs.ts function getLogs (line 3) | async function getLogs(fromPos: number = 0, taskId?: number | null): Pro... function clearLogs (line 11) | async function clearLogs(taskId?: number | null): Promise { function getLogTail (line 19) | async function getLogTail( FILE: web-ui/src/api/prompts.ts type PromptContent (line 3) | interface PromptContent { function listPrompts (line 8) | async function listPrompts(): Promise { function getPromptContent (line 12) | async function getPromptContent(filename: string): Promise { function updatePrompt (line 16) | async function updatePrompt(filename: string, content: string): Promise<... FILE: web-ui/src/api/results.ts type GetResultContentParams (line 4) | interface GetResultContentParams { function getResultFiles (line 14) | async function getResultFiles(): Promise { function deleteResultFile (line 19) | async function deleteResultFile(filename: string): Promise<{ message: st... function getResultContent (line 23) | async function getResultContent( function getResultInsights (line 30) | async function getResultInsights(filename: string): Promise { function updateNotificationSettings (line 108) | async function updateNotificationSettings(settings: NotificationSettings... function testNotificationSettings (line 116) | async function testNotificationSettings( function getAiSettings (line 126) | async function getAiSettings(): Promise { function updateAiSettings (line 130) | async function updateAiSettings(settings: AiSettings): Promise { function getRotationSettings (line 138) | async function getRotationSettings(): Promise { function updateRotationSettings (line 142) | async function updateRotationSettings(settings: RotationSettings): Promi... function testAiSettings (line 150) | async function testAiSettings(settings: AiSettings): Promise<{ success: ... function getSystemStatus (line 158) | async function getSystemStatus(): Promise { function updateLoginState (line 162) | async function updateLoginState(content: string): Promise<{ message: str... function deleteLoginState (line 170) | async function deleteLoginState(): Promise<{ message: string }> { FILE: web-ui/src/api/tasks.ts function getAllTasks (line 10) | async function getAllTasks(): Promise { function createTaskWithAI (line 14) | async function createTaskWithAI(data: TaskGenerateRequest): Promise { function stopTask (line 44) | async function stopTask(taskId: number): Promise { function deleteTask (line 48) | async function deleteTask(taskId: number): Promise { FILE: web-ui/src/components/ui/badge/index.ts type BadgeVariants (line 26) | type BadgeVariants = VariantProps FILE: web-ui/src/components/ui/button/index.ts type ButtonVariants (line 37) | type ButtonVariants = VariantProps FILE: web-ui/src/components/ui/toast/index.ts type ToastVariants (line 33) | type ToastVariants = VariantProps type ToastProps (line 35) | interface ToastProps extends ToastRootProps { FILE: web-ui/src/components/ui/toast/use-toast.ts constant TOAST_LIMIT (line 5) | const TOAST_LIMIT = 1 constant TOAST_REMOVE_DELAY (line 6) | const TOAST_REMOVE_DELAY = 1000000 type StringOrVNode (line 8) | type StringOrVNode type ToasterToast (line 13) | type ToasterToast = ToastProps & { function genId (line 29) | function genId() { type ActionType (line 34) | type ActionType = typeof actionTypes type Action (line 36) | type Action type State (line 54) | interface State { function addToRemoveQueue (line 60) | function addToRemoveQueue(toastId: string) { function dispatch (line 79) | function dispatch(action: Action) { function useToast (line 124) | function useToast() { type Toast (line 132) | type Toast = Omit function toast (line 134) | function toast(props: Toast) { FILE: web-ui/src/composables/useAuth.ts function useAuth (line 9) | function useAuth() { FILE: web-ui/src/composables/useDashboard.ts function buildSuggestion (line 13) | function buildSuggestion( function useDashboard (line 50) | function useDashboard() { FILE: web-ui/src/composables/useLogs.ts function useLogs (line 5) | function useLogs() { FILE: web-ui/src/composables/useMobileNav.ts function useMobileNav (line 5) | function useMobileNav() { FILE: web-ui/src/composables/useResults.ts function useResults (line 10) | function useResults() { FILE: web-ui/src/composables/useSettings.ts function useSettings (line 12) | function useSettings() { FILE: web-ui/src/composables/useTaskGenerationJob.ts constant POLL_INTERVAL_MS (line 5) | const POLL_INTERVAL_MS = 800 function isTerminalStatus (line 7) | function isTerminalStatus(status: TaskGenerationJob['status']) { function useTaskGenerationJob (line 11) | function useTaskGenerationJob() { FILE: web-ui/src/composables/useTasks.ts function useTasks (line 11) | function useTasks() { FILE: web-ui/src/composables/useWebSocket.ts function useWebSocket (line 11) | function useWebSocket() { FILE: web-ui/src/i18n/index.ts type AppLocale (line 6) | type AppLocale = 'zh-CN' | 'en-US' constant LOCALE_STORAGE_KEY (line 8) | const LOCALE_STORAGE_KEY = 'app_locale' constant DEFAULT_LOCALE (line 9) | const DEFAULT_LOCALE: AppLocale = 'zh-CN' constant SUPPORTED_LOCALES (line 10) | const SUPPORTED_LOCALES: AppLocale[] = ['zh-CN', 'en-US'] function resolveInitialLocale (line 12) | function resolveInitialLocale(): AppLocale { function setLocale (line 31) | function setLocale(locale: AppLocale) { function useLocale (line 35) | function useLocale() { function t (line 50) | function t(key: string, params?: Record) { function formatNumber (line 54) | function formatNumber(value: number, options?: Intl.NumberFormatOptions) { function formatDateTime (line 58) | function formatDateTime( function formatRelativeTimeFromNow (line 66) | function formatRelativeTimeFromNow(value: string | null | undefined) { FILE: web-ui/src/lib/http.ts type FetchOptions (line 3) | interface FetchOptions extends RequestInit { function http (line 7) | async function http(url: string, options: FetchOptions = {}) { FILE: web-ui/src/lib/taskFormQuery.ts type TaskFormDefaults (line 4) | type TaskFormDefaults = Partial type QueryValue (line 5) | type QueryValue = LocationQuery[string] | undefined constant TRUE_VALUES (line 7) | const TRUE_VALUES = new Set(['1', 'true', 'yes', 'on']) constant FALSE_VALUES (line 8) | const FALSE_VALUES = new Set(['0', 'false', 'no', 'off']) function readString (line 10) | function readString(value: QueryValue): string | undefined { function readBoolean (line 16) | function readBoolean(value: QueryValue): boolean | undefined { function readNumber (line 24) | function readNumber(value: QueryValue): number | undefined { function readKeywordRules (line 31) | function readKeywordRules(value: QueryValue): string[] | undefined { function parseTaskFormDefaults (line 41) | function parseTaskFormDefaults(query: LocationQuery): TaskFormDefaults { FILE: web-ui/src/lib/taskSchedule.ts constant SECOND_MS (line 3) | const SECOND_MS = 1000 constant MINUTE_MS (line 4) | const MINUTE_MS = 60 * SECOND_MS constant HOUR_MS (line 5) | const HOUR_MS = 60 * MINUTE_MS constant DAY_MS (line 6) | const DAY_MS = 24 * HOUR_MS function parseDate (line 8) | function parseDate(value: string | null | undefined): Date | null { function padNumber (line 14) | function padNumber(value: number): string { function formatNextRunAbsolute (line 18) | function formatNextRunAbsolute(value: string | null | undefined): string { function formatCountdown (line 24) | function formatCountdown( FILE: web-ui/src/lib/utils.ts function cn (line 4) | function cn(...inputs: ClassValue[]) { FILE: web-ui/src/router/index.ts function updateDocumentTitle (line 69) | function updateDocumentTitle() { FILE: web-ui/src/services/websocket.ts type WebSocketEventHandler (line 1) | type WebSocketEventHandler = (data: any) => void; class WebSocketService (line 3) | class WebSocketService { method constructor (line 10) | constructor() { method start (line 18) | public start() { method stop (line 26) | public stop() { method connect (line 35) | private connect() { method on (line 82) | public on(event: string, handler: WebSocketEventHandler) { method off (line 89) | public off(event: string, handler: WebSocketEventHandler) { method emit (line 99) | private emit(event: string, data: any) { FILE: web-ui/src/types/dashboard.d.ts type DashboardSummary (line 3) | interface DashboardSummary { type DashboardTaskSummary (line 14) | interface DashboardTaskSummary { type DashboardActivity (line 33) | interface DashboardActivity { type DashboardSnapshot (line 45) | interface DashboardSnapshot { type DashboardSuggestion (line 52) | interface DashboardSuggestion { type DashboardState (line 60) | interface DashboardState { FILE: web-ui/src/types/result.d.ts type ProductInfo (line 3) | interface ProductInfo { type SellerInfo (line 19) | interface SellerInfo { type AiAnalysis (line 37) | interface AiAnalysis { type PriceInsight (line 51) | interface PriceInsight { type ResultInsights (line 68) | interface ResultInsights { type ResultItem (line 96) | interface ResultItem { FILE: web-ui/src/types/task.d.ts type Task (line 3) | interface Task { type TaskGenerationStatus (line 28) | type TaskGenerationStatus = 'queued' | 'running' | 'completed' | 'failed'; type TaskGenerationStepStatus (line 29) | type TaskGenerationStepStatus = 'pending' | 'running' | 'completed' | 'f... type TaskGenerationStep (line 31) | interface TaskGenerationStep { type TaskGenerationJob (line 38) | interface TaskGenerationJob { type TaskCreateResponse (line 49) | interface TaskCreateResponse { type TaskUpdate (line 56) | type TaskUpdate = Partial>; type TaskGenerateRequest (line 59) | interface TaskGenerateRequest {