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.
Files changed (137) hide show
  1. {unrealon-2.0.4/unrealon.egg-info → unrealon-2.0.6}/PKG-INFO +1 -1
  2. {unrealon-2.0.4 → unrealon-2.0.6}/pyproject.toml +1 -1
  3. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/browser_cli.py +1 -1
  4. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/interactive_mode.py +1 -1
  5. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/config.py +1 -1
  6. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/manager.pyc +0 -0
  7. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/scanner_tester.pyc +0 -0
  8. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/__init__.py +27 -6
  9. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/config/environment.py +54 -1
  10. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/config/urls.py +9 -8
  11. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/enums/types.py +1 -0
  12. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/__init__.py +0 -19
  13. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/__init__.py +13 -1
  14. unrealon-2.0.6/unrealon-core/src/unrealon_core/models/websocket/broadcast.py +137 -0
  15. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/communication/session.py +12 -3
  16. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/communication/websocket_client.py +26 -13
  17. unrealon-2.0.6/unrealon-driver/src/unrealon_driver/driver/core/config.py +175 -0
  18. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/lifecycle/daemon.py +1 -0
  19. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/lifecycle/initialization.py +14 -1
  20. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/logger.py +5 -0
  21. {unrealon-2.0.4 → unrealon-2.0.6/unrealon.egg-info}/PKG-INFO +1 -1
  22. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/SOURCES.txt +1 -1
  23. unrealon-2.0.4/unrealon-core/src/unrealon_core/models/config.py +0 -47
  24. unrealon-2.0.4/unrealon-driver/src/unrealon_driver/driver/core/config.py +0 -85
  25. {unrealon-2.0.4 → unrealon-2.0.6}/LICENSE +0 -0
  26. {unrealon-2.0.4 → unrealon-2.0.6}/MANIFEST.in +0 -0
  27. {unrealon-2.0.4 → unrealon-2.0.6}/README.md +0 -0
  28. {unrealon-2.0.4 → unrealon-2.0.6}/setup.cfg +0 -0
  29. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/README.md +0 -0
  30. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/__init__.py +0 -0
  31. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/__init__.py +0 -0
  32. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/cookies_cli.py +0 -0
  33. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/cli/main.py +0 -0
  34. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/core/__init__.py +0 -0
  35. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/core/browser_manager.py +0 -0
  36. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/__init__.py +0 -0
  37. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/bot_detection.py +0 -0
  38. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/core.py +0 -0
  39. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/dataclasses.py +0 -0
  40. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/detection.py +0 -0
  41. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/enums.py +0 -0
  42. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/dto/models/statistics.py +0 -0
  43. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/__init__.py +0 -0
  44. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/captcha.py +0 -0
  45. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/cookies.py +0 -0
  46. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/logger_bridge.py +0 -0
  47. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/page_wait_manager.py +0 -0
  48. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/managers/profile.py +0 -0
  49. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/__init__.py +0 -0
  50. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/bypass_techniques.pyc +0 -0
  51. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
  52. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/playwright_stealth.pyc +0 -0
  53. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-browser/src/unrealon_browser/stealth/undetected_chrome.pyc +0 -0
  54. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/config/__init__.py +0 -0
  55. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/enums/__init__.py +0 -0
  56. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/enums/status.py +0 -0
  57. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/__init__.py +0 -0
  58. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/circuit_breaker.py +0 -0
  59. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/error_context.py +0 -0
  60. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/recovery.py +0 -0
  61. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/error_handling/retry.py +0 -0
  62. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/__init__.py +0 -0
  63. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/base.py +0 -0
  64. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/communication.py +0 -0
  65. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/driver.py +0 -0
  66. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/proxy.py +0 -0
  67. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/task.py +0 -0
  68. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/exceptions/validation.py +0 -0
  69. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/arq_context.py +0 -0
  70. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/arq_responses.py +0 -0
  71. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/base.py +0 -0
  72. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/bridge_stats.py +0 -0
  73. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/communication.py +0 -0
  74. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/connection_stats.py +0 -0
  75. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/driver.py +0 -0
  76. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/driver_details.py +0 -0
  77. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/logging.py +0 -0
  78. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/task.py +0 -0
  79. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/typed_responses.py +0 -0
  80. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/base.py +0 -0
  81. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/config.py +0 -0
  82. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/driver.py +0 -0
  83. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/errors.py +0 -0
  84. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/heartbeat.py +0 -0
  85. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/logging.py +0 -0
  86. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/proxy.py +0 -0
  87. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/tasks.py +0 -0
  88. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket/utils.py +0 -0
  89. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/models/websocket_session.py +0 -0
  90. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/__init__.py +0 -0
  91. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/alerts.py +0 -0
  92. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/dashboard.py +0 -0
  93. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/health_check.py +0 -0
  94. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/monitoring/metrics.py +0 -0
  95. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/utils/__init__.py +0 -0
  96. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/utils/time.py +0 -0
  97. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-core/src/unrealon_core/version.py +0 -0
  98. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/__init__.py +0 -0
  99. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/__init__.py +0 -0
  100. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/base.py +0 -0
  101. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/config.py +0 -0
  102. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/event_manager.py +0 -0
  103. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/protocols.py +0 -0
  104. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/core_module/registry.py +0 -0
  105. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/__init__.py +0 -0
  106. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/retry.py +0 -0
  107. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/schedule.py +0 -0
  108. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/task.py +0 -0
  109. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/decorators/timing.py +0 -0
  110. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/__init__.py +0 -0
  111. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/communication/__init__.py +0 -0
  112. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/core/__init__.py +0 -0
  113. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/core/driver.py +0 -0
  114. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/factory/__init__.py +0 -0
  115. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/factory/manager_factory.py +0 -0
  116. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/lifecycle/__init__.py +0 -0
  117. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/lifecycle/shutdown.py +0 -0
  118. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/monitoring/__init__.py +0 -0
  119. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/monitoring/health.py +0 -0
  120. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/utilities/__init__.py +0 -0
  121. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/utilities/logging.py +0 -0
  122. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/driver/utilities/serialization.py +0 -0
  123. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/__init__.py +0 -0
  124. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/base.py +0 -0
  125. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/browser.py +0 -0
  126. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/cache.py +0 -0
  127. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/http.py +0 -0
  128. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/proxy.py +0 -0
  129. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/registry.py +0 -0
  130. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/threading.py +0 -0
  131. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/managers/update.py +0 -0
  132. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/utils/__init__.py +0 -0
  133. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon-driver/src/unrealon_driver/utils/time.py +0 -0
  134. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/dependency_links.txt +0 -0
  135. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/entry_points.txt +0 -0
  136. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/requires.txt +0 -0
  137. {unrealon-2.0.4 → unrealon-2.0.6}/unrealon.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unrealon
3
- Version: 2.0.4
3
+ Version: 2.0.6
4
4
  Summary: Enterprise-grade web scraping platform with AI-powered automation and real-time orchestration capabilities
5
5
  Author-email: UnrealOn Team <team@unrealon.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "unrealon"
7
- version = "2.0.4"
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
 
@@ -11,7 +11,7 @@ from rich.panel import Panel
11
11
  from rich.table import Table
12
12
  from typing import Dict, Any
13
13
 
14
- from unrealon_core.config import get_url_config
14
+ from unrealon_core.config.urls import get_url_config
15
15
 
16
16
  console = Console()
17
17
 
@@ -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:
@@ -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, URLConfig,
81
- get_environment_config, get_url_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
- return cls(
48
- scanner_url="https://staging.unrealon.com/scanner",
49
- cloud_base_url="https://staging.unrealon.com",
50
- api_base_url="https://api-staging.unrealon.com"
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
  )
@@ -53,6 +53,7 @@ class MessageType(str, Enum):
53
53
  LOG_BATCH = "log_batch" # Batch of log messages
54
54
 
55
55
  # System messages
56
+ WELCOME = "welcome" # Welcome message from server
56
57
  PING = "ping" # Ping message
57
58
  PONG = "pong" # Pong response
58
59
  ERROR = "error" # Error message
@@ -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
  ]
@@ -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
- # Validate task data
143
- task_data = TaskAssignmentData.model_validate(message.data)
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
- from websockets.asyncio.client import ClientConnection
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
- logger.info("WebSocket client stopped")
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
- logger.info(f"Connecting to WebSocket: {self.websocket_url}")
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
- logger.info("WebSocket connected successfully")
109
+ self._logger.info("WebSocket connected successfully")
100
110
  return True
101
111
  except Exception as e:
102
- logger.error(f"WebSocket connection failed: {e}")
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
- logger.info(f"Attempting reconnection (attempt {self.reconnect_attempts + 1})")
120
+ self._logger.info(f"Attempting reconnection (attempt {self.reconnect_attempts + 1})")
111
121
 
112
122
  if await self._establish_connection():
113
- logger.info("Reconnection successful")
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
- logger.error(f"Error sending message: {e}")
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
- logger.warning("WebSocket connection lost")
171
+ self._logger.warning("WebSocket connection lost")
159
172
  except Exception as e:
160
- logger.error(f"Error receiving message: {e}")
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
- logger.info(f"Driver registration queued: {self.driver_id}")
205
+ self._logger.info(f"Driver registration queued: {self.driver_id}")
193
206
  return True
194
207
 
195
208
  except Exception as e:
196
- logger.error(f"Driver registration failed: {e}")
209
+ self._logger.error(f"Driver registration failed: {e}")
197
210
  return False