unrealon 2.0.4__tar.gz → 2.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unrealon-2.0.4/unrealon.egg-info → unrealon-2.0.6}/PKG-INFO +1 -1
- {unrealon-2.0.4 → unrealon-2.0.6}/pyproject.toml +1 -1
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/browser_cli.py +1 -1
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/interactive_mode.py +1 -1
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/config.py +1 -1
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/manager.pyc +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/scanner_tester.pyc +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/__init__.py +27 -6
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/config/environment.py +54 -1
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/config/urls.py +9 -8
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/enums/types.py +1 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/__init__.py +0 -19
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/__init__.py +13 -1
- unrealon-2.0.6/unrealon-core/src/unrealon_core/models/websocket/broadcast.py +137 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/communication/session.py +12 -3
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/communication/websocket_client.py +26 -13
- unrealon-2.0.6/unrealon-driver/src/unrealon_driver/driver/core/config.py +175 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/lifecycle/daemon.py +1 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/lifecycle/initialization.py +14 -1
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/logger.py +5 -0
- {unrealon-2.0.4 → unrealon-2.0.6/unrealon.egg-info}/PKG-INFO +1 -1
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/SOURCES.txt +1 -1
- unrealon-2.0.4/unrealon-core/src/unrealon_core/models/config.py +0 -47
- unrealon-2.0.4/unrealon-driver/src/unrealon_driver/driver/core/config.py +0 -85
- {unrealon-2.0.4 → unrealon-2.0.6}/LICENSE +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/MANIFEST.in +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/README.md +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/setup.cfg +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/README.md +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/cookies_cli.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/main.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/core/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/core/browser_manager.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/bot_detection.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/core.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/dataclasses.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/detection.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/enums.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/statistics.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/captcha.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/cookies.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/logger_bridge.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/page_wait_manager.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/profile.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/bypass_techniques.pyc +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/playwright_stealth.pyc +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/undetected_chrome.pyc +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/config/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/enums/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/enums/status.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/circuit_breaker.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/error_context.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/recovery.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/retry.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/base.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/communication.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/driver.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/proxy.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/task.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/validation.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/arq_context.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/arq_responses.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/base.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/bridge_stats.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/communication.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/connection_stats.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/driver.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/driver_details.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/logging.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/task.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/typed_responses.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/base.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/config.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/driver.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/errors.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/heartbeat.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/logging.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/proxy.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/tasks.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/utils.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket_session.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/alerts.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/dashboard.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/health_check.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/metrics.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/utils/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/utils/time.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/version.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/base.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/config.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/event_manager.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/protocols.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/registry.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/retry.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/schedule.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/task.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/timing.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/communication/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/core/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/core/driver.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/factory/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/factory/manager_factory.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/lifecycle/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/lifecycle/shutdown.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/monitoring/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/monitoring/health.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/utilities/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/utilities/logging.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/utilities/serialization.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/base.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/browser.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/cache.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/http.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/proxy.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/registry.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/threading.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/update.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/utils/__init__.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/utils/time.py +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/dependency_links.txt +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/entry_points.txt +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/requires.txt +0 -0
- {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "unrealon"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.6"
|
|
8
8
|
description = "Enterprise-grade web scraping platform with AI-powered automation and real-time orchestration capabilities"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "UnrealOn Team", email = "team@unrealon.com"}
|
|
@@ -16,7 +16,7 @@ from rich.table import Table
|
|
|
16
16
|
from unrealon_browser.core.browser_manager import BrowserManager
|
|
17
17
|
from unrealon_browser.dto.models.config import BrowserConfig, BrowserType, BrowserMode
|
|
18
18
|
|
|
19
|
-
from unrealon_core.config import get_url_config
|
|
19
|
+
from unrealon_core.config.urls import get_url_config
|
|
20
20
|
|
|
21
21
|
console = Console()
|
|
22
22
|
|
{unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/config.py
RENAMED
|
@@ -8,7 +8,7 @@ from typing import Optional
|
|
|
8
8
|
from pydantic import BaseModel, Field, ConfigDict
|
|
9
9
|
from .enums import BrowserType, BrowserMode
|
|
10
10
|
|
|
11
|
-
from unrealon_core.config import get_url_config
|
|
11
|
+
from unrealon_core.config.urls import get_url_config
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def _get_default_stealth_url() -> str:
|
|
Binary file
|
{unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/scanner_tester.pyc
RENAMED
|
Binary file
|
|
@@ -42,7 +42,10 @@ from .models.communication import (
|
|
|
42
42
|
create_error_message, create_ack_message,
|
|
43
43
|
# Data models for strict typing
|
|
44
44
|
TaskAssignmentData, TaskResultData,
|
|
45
|
-
DriverRegistrationData, HeartbeatData, LogEntryData, LogContext
|
|
45
|
+
DriverRegistrationData, HeartbeatData, LogEntryData, LogContext,
|
|
46
|
+
# Broadcast models
|
|
47
|
+
DriverBroadcastData, DriverRegisterBroadcast,
|
|
48
|
+
DriverHeartbeatBroadcast, DriverDisconnectBroadcast
|
|
46
49
|
)
|
|
47
50
|
from .enums.status import DriverStatus, TaskStatus, ProxyStatus, LogLevel
|
|
48
51
|
from .enums.types import MessageType, ProxyType, TaskPriority
|
|
@@ -76,9 +79,19 @@ from .monitoring import (
|
|
|
76
79
|
)
|
|
77
80
|
|
|
78
81
|
# Configuration system
|
|
79
|
-
from .config import (
|
|
80
|
-
EnvironmentConfig,
|
|
81
|
-
get_environment_config,
|
|
82
|
+
from .config.environment import (
|
|
83
|
+
EnvironmentConfig, Environment,
|
|
84
|
+
get_environment_config, set_environment_config
|
|
85
|
+
)
|
|
86
|
+
from .config.urls import (
|
|
87
|
+
URLConfig,
|
|
88
|
+
get_url_config
|
|
89
|
+
)
|
|
90
|
+
from .models.websocket.config import (
|
|
91
|
+
DriverConfiguration,
|
|
92
|
+
LoggingConfiguration,
|
|
93
|
+
TaskConfiguration,
|
|
94
|
+
ProxyConfiguration
|
|
82
95
|
)
|
|
83
96
|
|
|
84
97
|
__all__ = [
|
|
@@ -117,6 +130,12 @@ __all__ = [
|
|
|
117
130
|
"LogEntryData",
|
|
118
131
|
"LogContext",
|
|
119
132
|
|
|
133
|
+
# Broadcast models
|
|
134
|
+
"DriverBroadcastData",
|
|
135
|
+
"DriverRegisterBroadcast",
|
|
136
|
+
"DriverHeartbeatBroadcast",
|
|
137
|
+
"DriverDisconnectBroadcast",
|
|
138
|
+
|
|
120
139
|
# Status enums
|
|
121
140
|
"DriverStatus",
|
|
122
141
|
"TaskStatus",
|
|
@@ -155,6 +174,8 @@ __all__ = [
|
|
|
155
174
|
"MonitoringDashboard", "get_system_overview",
|
|
156
175
|
|
|
157
176
|
# Configuration system
|
|
158
|
-
"EnvironmentConfig", "URLConfig",
|
|
159
|
-
"get_environment_config", "get_url_config",
|
|
177
|
+
"EnvironmentConfig", "Environment", "URLConfig",
|
|
178
|
+
"get_environment_config", "set_environment_config", "get_url_config",
|
|
179
|
+
"DriverConfiguration", "LoggingConfiguration",
|
|
180
|
+
"TaskConfiguration", "ProxyConfiguration",
|
|
160
181
|
]
|
|
@@ -18,7 +18,7 @@ class Environment(str, Enum):
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class EnvironmentConfig(BaseModel):
|
|
21
|
-
"""Environment configuration settings."""
|
|
21
|
+
"""Environment configuration settings with all system URLs and settings."""
|
|
22
22
|
|
|
23
23
|
model_config = ConfigDict(
|
|
24
24
|
# Add any specific config here if needed
|
|
@@ -39,6 +39,17 @@ class EnvironmentConfig(BaseModel):
|
|
|
39
39
|
description="Logging level"
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
+
# System URLs - environment-aware
|
|
43
|
+
redis_url: str = Field(
|
|
44
|
+
default_factory=lambda: os.getenv("REDIS_URL", "redis://localhost:6379/0"),
|
|
45
|
+
description="Redis connection URL"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
max_workers: int = Field(
|
|
49
|
+
default_factory=lambda: int(os.getenv("MAX_WORKERS", "10")),
|
|
50
|
+
description="Maximum number of workers"
|
|
51
|
+
)
|
|
52
|
+
|
|
42
53
|
@classmethod
|
|
43
54
|
def from_env(cls) -> "EnvironmentConfig":
|
|
44
55
|
"""Create config from environment variables."""
|
|
@@ -76,6 +87,48 @@ class EnvironmentConfig(BaseModel):
|
|
|
76
87
|
def is_testing(self) -> bool:
|
|
77
88
|
"""Check if running in testing mode."""
|
|
78
89
|
return self.environment == Environment.TESTING
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def websocket_url(self) -> str:
|
|
93
|
+
"""Get WebSocket URL based on environment."""
|
|
94
|
+
# Check explicit env var first
|
|
95
|
+
if ws_url := os.getenv("WEBSOCKET_URL"):
|
|
96
|
+
return ws_url
|
|
97
|
+
|
|
98
|
+
if self.is_production:
|
|
99
|
+
return "wss://ws.unrealon.com/ws"
|
|
100
|
+
elif self.is_development:
|
|
101
|
+
return "ws://localhost:8001/ws" # RPC server port
|
|
102
|
+
else: # testing
|
|
103
|
+
return "ws://localhost:8001/ws"
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def api_url(self) -> str:
|
|
107
|
+
"""Get API URL based on environment."""
|
|
108
|
+
# Check explicit env var first
|
|
109
|
+
if api_url := os.getenv("API_URL"):
|
|
110
|
+
return api_url
|
|
111
|
+
|
|
112
|
+
if self.is_production:
|
|
113
|
+
return "https://api-m.unrealon.com"
|
|
114
|
+
elif self.is_development:
|
|
115
|
+
return "http://localhost:8002" # Backend server port
|
|
116
|
+
else: # testing
|
|
117
|
+
return "http://localhost:8002"
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def django_api_url(self) -> str:
|
|
121
|
+
"""Get Django API URL based on environment."""
|
|
122
|
+
# Check explicit env var first
|
|
123
|
+
if api_url := os.getenv("DJANGO_API_URL"):
|
|
124
|
+
return api_url
|
|
125
|
+
|
|
126
|
+
if self.is_production:
|
|
127
|
+
return "https://api.unrealon.com"
|
|
128
|
+
elif self.is_development:
|
|
129
|
+
return "http://localhost:8000" # Django server port
|
|
130
|
+
else: # testing
|
|
131
|
+
return "http://localhost:8000"
|
|
79
132
|
|
|
80
133
|
|
|
81
134
|
# Global config instance
|
|
@@ -18,6 +18,7 @@ class URLConfig(BaseModel):
|
|
|
18
18
|
|
|
19
19
|
# Scanner/Detection URLs
|
|
20
20
|
scanner_url: str = Field(
|
|
21
|
+
default="https://cloud.unrealon.com/scanner",
|
|
21
22
|
description="URL for browser detection and stealth testing"
|
|
22
23
|
)
|
|
23
24
|
|
|
@@ -39,19 +40,19 @@ class URLConfig(BaseModel):
|
|
|
39
40
|
|
|
40
41
|
if environment == Environment.PRODUCTION:
|
|
41
42
|
return cls(
|
|
42
|
-
scanner_url="https://cloud.unrealon.com/scanner",
|
|
43
|
+
# scanner_url="https://cloud.unrealon.com/scanner",
|
|
43
44
|
cloud_base_url="https://cloud.unrealon.com",
|
|
44
45
|
api_base_url="https://api.unrealon.com"
|
|
45
46
|
)
|
|
46
|
-
elif environment == Environment.TESTING:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
# elif environment == Environment.TESTING:
|
|
48
|
+
# return cls(
|
|
49
|
+
# scanner_url="https://staging.unrealon.com/scanner",
|
|
50
|
+
# cloud_base_url="https://staging.unrealon.com",
|
|
51
|
+
# api_base_url="https://api-staging.unrealon.com"
|
|
52
|
+
# )
|
|
52
53
|
else: # Development
|
|
53
54
|
return cls(
|
|
54
|
-
scanner_url="http://localhost:3000/scanner",
|
|
55
|
+
# scanner_url="http://localhost:3000/scanner",
|
|
55
56
|
cloud_base_url="http://localhost:3000",
|
|
56
57
|
api_base_url="http://localhost:8000"
|
|
57
58
|
)
|
|
@@ -46,16 +46,6 @@ from .logging import (
|
|
|
46
46
|
LogMetrics
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
-
from .config import (
|
|
50
|
-
SystemConfig,
|
|
51
|
-
HttpConfig,
|
|
52
|
-
ProxyConfig,
|
|
53
|
-
BrowserConfig,
|
|
54
|
-
LoggingConfig,
|
|
55
|
-
CacheConfig,
|
|
56
|
-
ThreadConfig
|
|
57
|
-
)
|
|
58
|
-
|
|
59
49
|
__all__ = [
|
|
60
50
|
# Base models
|
|
61
51
|
"UnrealOnBaseModel",
|
|
@@ -86,13 +76,4 @@ __all__ = [
|
|
|
86
76
|
"LogEntry",
|
|
87
77
|
"LogQuery",
|
|
88
78
|
"LogMetrics",
|
|
89
|
-
|
|
90
|
-
# Configuration models
|
|
91
|
-
"SystemConfig",
|
|
92
|
-
"HttpConfig",
|
|
93
|
-
"ProxyConfig",
|
|
94
|
-
"BrowserConfig",
|
|
95
|
-
"LoggingConfig",
|
|
96
|
-
"CacheConfig",
|
|
97
|
-
"ThreadConfig",
|
|
98
79
|
]
|
{unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/__init__.py
RENAMED
|
@@ -38,6 +38,12 @@ from .proxy import (
|
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
from .utils import create_error_message, create_ack_message
|
|
41
|
+
from .broadcast import (
|
|
42
|
+
DriverBroadcastData,
|
|
43
|
+
DriverRegisterBroadcast,
|
|
44
|
+
DriverHeartbeatBroadcast,
|
|
45
|
+
DriverDisconnectBroadcast
|
|
46
|
+
)
|
|
41
47
|
|
|
42
48
|
__all__ = [
|
|
43
49
|
# Base
|
|
@@ -87,5 +93,11 @@ __all__ = [
|
|
|
87
93
|
|
|
88
94
|
# Utilities
|
|
89
95
|
'create_error_message',
|
|
90
|
-
'create_ack_message'
|
|
96
|
+
'create_ack_message',
|
|
97
|
+
|
|
98
|
+
# Broadcast messages
|
|
99
|
+
'DriverBroadcastData',
|
|
100
|
+
'DriverRegisterBroadcast',
|
|
101
|
+
'DriverHeartbeatBroadcast',
|
|
102
|
+
'DriverDisconnectBroadcast'
|
|
91
103
|
]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Broadcast WebSocket Models.
|
|
3
|
+
|
|
4
|
+
Strictly typed models for broadcasting driver events to monitoring clients.
|
|
5
|
+
These are Server → Client messages for real-time monitoring.
|
|
6
|
+
|
|
7
|
+
Phase 2: Core Systems - WebSocket Bridge
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import List, Optional
|
|
11
|
+
from pydantic import Field, ConfigDict
|
|
12
|
+
|
|
13
|
+
from ..base import UnrealOnBaseModel
|
|
14
|
+
from .base import WebSocketMessage, MessageType
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DriverBroadcastData(UnrealOnBaseModel):
|
|
18
|
+
"""Driver broadcast data payload - strictly typed."""
|
|
19
|
+
|
|
20
|
+
model_config = ConfigDict(
|
|
21
|
+
validate_assignment=True,
|
|
22
|
+
extra="forbid"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
driver_id: str = Field(
|
|
26
|
+
description="Unique driver identifier",
|
|
27
|
+
min_length=1
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
driver_type: str = Field(
|
|
31
|
+
description="Type of driver (e.g., 'universal', 'ecommerce')",
|
|
32
|
+
min_length=1
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
status: str = Field(
|
|
36
|
+
default="active",
|
|
37
|
+
pattern=r"^(active|idle|busy|offline|error)$",
|
|
38
|
+
description="Current driver status"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
capabilities: List[str] = Field(
|
|
42
|
+
default_factory=list,
|
|
43
|
+
description="List of supported task types"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
active_tasks: int = Field(
|
|
47
|
+
default=0,
|
|
48
|
+
ge=0,
|
|
49
|
+
description="Number of currently active tasks"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
completed_tasks: int = Field(
|
|
53
|
+
default=0,
|
|
54
|
+
ge=0,
|
|
55
|
+
description="Total completed tasks since startup"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
failed_tasks: int = Field(
|
|
59
|
+
default=0,
|
|
60
|
+
ge=0,
|
|
61
|
+
description="Total failed tasks since startup"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
uptime_seconds: float = Field(
|
|
65
|
+
default=0.0,
|
|
66
|
+
ge=0.0,
|
|
67
|
+
description="Driver uptime in seconds"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
last_seen: Optional[str] = Field(
|
|
71
|
+
default=None,
|
|
72
|
+
description="ISO timestamp of last heartbeat"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
connected_at: Optional[str] = Field(
|
|
76
|
+
default=None,
|
|
77
|
+
description="ISO timestamp when driver connected"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
disconnected_at: Optional[str] = Field(
|
|
81
|
+
default=None,
|
|
82
|
+
description="ISO timestamp when driver disconnected"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class DriverRegisterBroadcast(WebSocketMessage):
|
|
87
|
+
"""Driver registration broadcast message (Server → Monitoring Clients)."""
|
|
88
|
+
|
|
89
|
+
model_config = ConfigDict(
|
|
90
|
+
validate_assignment=True,
|
|
91
|
+
extra="forbid"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
type: MessageType = Field(
|
|
95
|
+
default=MessageType.DRIVER_REGISTER,
|
|
96
|
+
frozen=True
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
data: DriverBroadcastData = Field(
|
|
100
|
+
description="Driver registration broadcast data"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class DriverHeartbeatBroadcast(WebSocketMessage):
|
|
105
|
+
"""Driver heartbeat broadcast message (Server → Monitoring Clients)."""
|
|
106
|
+
|
|
107
|
+
model_config = ConfigDict(
|
|
108
|
+
validate_assignment=True,
|
|
109
|
+
extra="forbid"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
type: MessageType = Field(
|
|
113
|
+
default=MessageType.DRIVER_HEARTBEAT,
|
|
114
|
+
frozen=True
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
data: DriverBroadcastData = Field(
|
|
118
|
+
description="Driver heartbeat broadcast data"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class DriverDisconnectBroadcast(WebSocketMessage):
|
|
123
|
+
"""Driver disconnect broadcast message (Server → Monitoring Clients)."""
|
|
124
|
+
|
|
125
|
+
model_config = ConfigDict(
|
|
126
|
+
validate_assignment=True,
|
|
127
|
+
extra="forbid"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
type: MessageType = Field(
|
|
131
|
+
default=MessageType.DRIVER_DISCONNECT,
|
|
132
|
+
frozen=True
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
data: DriverBroadcastData = Field(
|
|
136
|
+
description="Driver disconnect broadcast data"
|
|
137
|
+
)
|
|
@@ -12,6 +12,7 @@ from pydantic import BaseModel, Field
|
|
|
12
12
|
|
|
13
13
|
from unrealon_core.models.websocket import (
|
|
14
14
|
TaskAssignmentData,
|
|
15
|
+
TaskAssignmentMessage,
|
|
15
16
|
TaskResultData,
|
|
16
17
|
WebSocketMessage
|
|
17
18
|
)
|
|
@@ -69,21 +70,26 @@ class DriverSession:
|
|
|
69
70
|
|
|
70
71
|
try:
|
|
71
72
|
# Connect WebSocket
|
|
73
|
+
logger.info(f"🔌 Connecting WebSocket for driver: {self.driver_id}")
|
|
72
74
|
self.status = SessionStatus.CONNECTING
|
|
73
75
|
if not await self.websocket_client.connect():
|
|
76
|
+
logger.error(f"❌ WebSocket connection failed for driver: {self.driver_id}")
|
|
74
77
|
self.status = SessionStatus.ERROR
|
|
75
78
|
return False
|
|
76
79
|
|
|
80
|
+
logger.info(f"✅ WebSocket connected for driver: {self.driver_id}")
|
|
77
81
|
self.status = SessionStatus.CONNECTED
|
|
78
82
|
self.stats.connected_at = utc_now()
|
|
79
83
|
|
|
80
84
|
# Register driver
|
|
85
|
+
logger.info(f"📝 Registering driver: {self.driver_id} with capabilities: {capabilities}")
|
|
81
86
|
if await self.register(capabilities or []):
|
|
82
87
|
self.status = SessionStatus.REGISTERED
|
|
83
88
|
self.stats.registered_at = utc_now()
|
|
84
|
-
logger.info(f"Session started for driver: {self.driver_id}")
|
|
89
|
+
logger.info(f"✅ Session started for driver: {self.driver_id}")
|
|
85
90
|
return True
|
|
86
91
|
else:
|
|
92
|
+
logger.error(f"❌ Driver registration failed for: {self.driver_id}")
|
|
87
93
|
self.status = SessionStatus.ERROR
|
|
88
94
|
return False
|
|
89
95
|
|
|
@@ -139,8 +145,11 @@ class DriverSession:
|
|
|
139
145
|
async def _handle_task_assignment(self, message: WebSocketMessage):
|
|
140
146
|
"""Handle task assignment."""
|
|
141
147
|
try:
|
|
142
|
-
#
|
|
143
|
-
|
|
148
|
+
# Parse as TaskAssignmentMessage
|
|
149
|
+
|
|
150
|
+
# Convert to proper task assignment message
|
|
151
|
+
task_message = TaskAssignmentMessage.model_validate(message.model_dump())
|
|
152
|
+
task_data = task_message.data
|
|
144
153
|
|
|
145
154
|
# Find handler
|
|
146
155
|
handler = self.task_handlers.get(task_data.task_type)
|
|
@@ -9,7 +9,15 @@ from typing import Optional, Callable, List
|
|
|
9
9
|
from collections import deque
|
|
10
10
|
|
|
11
11
|
import websockets
|
|
12
|
-
|
|
12
|
+
try:
|
|
13
|
+
from websockets.asyncio.client import ClientConnection
|
|
14
|
+
except ImportError:
|
|
15
|
+
# Fallback for older websockets versions
|
|
16
|
+
try:
|
|
17
|
+
from websockets.client import WebSocketClientProtocol as ClientConnection
|
|
18
|
+
except ImportError:
|
|
19
|
+
# Ultimate fallback
|
|
20
|
+
ClientConnection = None
|
|
13
21
|
|
|
14
22
|
from unrealon_core.models.websocket import (
|
|
15
23
|
DriverRegistrationMessage,
|
|
@@ -32,9 +40,11 @@ class WebSocketClient:
|
|
|
32
40
|
- Clean error handling
|
|
33
41
|
"""
|
|
34
42
|
|
|
35
|
-
def __init__(self, websocket_url: str, driver_id: str):
|
|
43
|
+
def __init__(self, websocket_url: str, driver_id: str, custom_logger=None):
|
|
36
44
|
self.websocket_url = websocket_url
|
|
37
45
|
self.driver_id = driver_id
|
|
46
|
+
# Use custom logger if provided, otherwise use default
|
|
47
|
+
self._logger = custom_logger if custom_logger else logger
|
|
38
48
|
self.websocket: Optional[ClientConnection] = None
|
|
39
49
|
self.connected = False
|
|
40
50
|
self.running = False
|
|
@@ -86,20 +96,20 @@ class WebSocketClient:
|
|
|
86
96
|
self.websocket = None
|
|
87
97
|
|
|
88
98
|
self.connected = False
|
|
89
|
-
|
|
99
|
+
self._logger.info("WebSocket client stopped")
|
|
90
100
|
|
|
91
101
|
async def _establish_connection(self) -> bool:
|
|
92
102
|
"""Establish WebSocket connection."""
|
|
93
103
|
try:
|
|
94
|
-
|
|
104
|
+
self._logger.info(f"Connecting to WebSocket: {self.websocket_url}")
|
|
95
105
|
self.websocket = await websockets.connect(self.websocket_url)
|
|
96
106
|
self.connected = True
|
|
97
107
|
self.reconnect_attempts = 0
|
|
98
108
|
self.reconnect_delay = 1.0
|
|
99
|
-
|
|
109
|
+
self._logger.info("WebSocket connected successfully")
|
|
100
110
|
return True
|
|
101
111
|
except Exception as e:
|
|
102
|
-
|
|
112
|
+
self._logger.error(f"WebSocket connection failed: {e}")
|
|
103
113
|
self.connected = False
|
|
104
114
|
return False
|
|
105
115
|
|
|
@@ -107,10 +117,10 @@ class WebSocketClient:
|
|
|
107
117
|
"""Monitor connection and handle reconnection."""
|
|
108
118
|
while self.running:
|
|
109
119
|
if not self.connected and self.running:
|
|
110
|
-
|
|
120
|
+
self._logger.info(f"Attempting reconnection (attempt {self.reconnect_attempts + 1})")
|
|
111
121
|
|
|
112
122
|
if await self._establish_connection():
|
|
113
|
-
|
|
123
|
+
self._logger.info("Reconnection successful")
|
|
114
124
|
else:
|
|
115
125
|
self.reconnect_attempts += 1
|
|
116
126
|
# Exponential backoff
|
|
@@ -128,13 +138,16 @@ class WebSocketClient:
|
|
|
128
138
|
if self.connected and self.websocket and self.message_queue:
|
|
129
139
|
try:
|
|
130
140
|
message = self.message_queue.popleft()
|
|
141
|
+
self._logger.info(f"📤 Sending WebSocket message: {message[:200]}...") # Log first 200 chars
|
|
131
142
|
await self.websocket.send(message)
|
|
143
|
+
self._logger.info(f"✅ Message sent successfully")
|
|
132
144
|
except (websockets.exceptions.ConnectionClosed, ConnectionResetError):
|
|
133
145
|
self.connected = False
|
|
134
146
|
# Put message back in queue
|
|
135
147
|
self.message_queue.appendleft(message)
|
|
148
|
+
self._logger.warning("🔌 Connection lost, message queued for retry")
|
|
136
149
|
except Exception as e:
|
|
137
|
-
|
|
150
|
+
self._logger.error(f"❌ Error sending message: {e}")
|
|
138
151
|
else:
|
|
139
152
|
await asyncio.sleep(0.1)
|
|
140
153
|
|
|
@@ -155,9 +168,9 @@ class WebSocketClient:
|
|
|
155
168
|
|
|
156
169
|
except (websockets.exceptions.ConnectionClosed, ConnectionResetError):
|
|
157
170
|
self.connected = False
|
|
158
|
-
|
|
171
|
+
self._logger.warning("WebSocket connection lost")
|
|
159
172
|
except Exception as e:
|
|
160
|
-
|
|
173
|
+
self._logger.error(f"Error receiving message: {e}")
|
|
161
174
|
else:
|
|
162
175
|
await asyncio.sleep(0.1)
|
|
163
176
|
|
|
@@ -189,9 +202,9 @@ class WebSocketClient:
|
|
|
189
202
|
# Queue for sending
|
|
190
203
|
self.send(registration_message)
|
|
191
204
|
|
|
192
|
-
|
|
205
|
+
self._logger.info(f"Driver registration queued: {self.driver_id}")
|
|
193
206
|
return True
|
|
194
207
|
|
|
195
208
|
except Exception as e:
|
|
196
|
-
|
|
209
|
+
self._logger.error(f"Driver registration failed: {e}")
|
|
197
210
|
return False
|