unrealon 2.0.4__py3-none-any.whl → 2.0.6__py3-none-any.whl
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.dist-info → unrealon-2.0.6.dist-info}/METADATA +1 -1
- {unrealon-2.0.4.dist-info → unrealon-2.0.6.dist-info}/RECORD +24 -24
- unrealon_browser/cli/browser_cli.py +1 -1
- unrealon_browser/cli/interactive_mode.py +1 -1
- unrealon_browser/dto/models/config.py +1 -1
- unrealon_browser/stealth/manager.pyc +0 -0
- unrealon_browser/stealth/scanner_tester.pyc +0 -0
- unrealon_core/__init__.py +27 -6
- unrealon_core/config/environment.py +54 -1
- unrealon_core/config/urls.py +9 -8
- unrealon_core/enums/types.py +1 -0
- unrealon_core/models/__init__.py +0 -19
- unrealon_core/models/websocket/__init__.py +13 -1
- unrealon_core/models/websocket/broadcast.py +137 -0
- unrealon_driver/driver/communication/session.py +12 -3
- unrealon_driver/driver/communication/websocket_client.py +26 -13
- unrealon_driver/driver/core/config.py +94 -4
- unrealon_driver/driver/lifecycle/daemon.py +1 -0
- unrealon_driver/driver/lifecycle/initialization.py +14 -1
- unrealon_driver/managers/logger.py +5 -0
- unrealon_core/models/config.py +0 -47
- {unrealon-2.0.4.dist-info → unrealon-2.0.6.dist-info}/LICENSE +0 -0
- {unrealon-2.0.4.dist-info → unrealon-2.0.6.dist-info}/WHEEL +0 -0
- {unrealon-2.0.4.dist-info → unrealon-2.0.6.dist-info}/entry_points.txt +0 -0
- {unrealon-2.0.4.dist-info → unrealon-2.0.6.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
unrealon_browser/README.md,sha256=9pP6RrfMGHtdT5uDLFAUB1e4nNGzZudXViEo1940gKw,396
|
|
2
2
|
unrealon_browser/__init__.py,sha256=aYEmGQyhGEwcr-XPKqWJb21uQPB9XvqwZgKSSwkbb_I,1521
|
|
3
3
|
unrealon_browser/cli/__init__.py,sha256=b3r88oeCYsqZF8EU8EZXP9v54Q8cIimN7UmxJsXcB84,264
|
|
4
|
-
unrealon_browser/cli/browser_cli.py,sha256=
|
|
4
|
+
unrealon_browser/cli/browser_cli.py,sha256=KobEb9nhPEHDmB5K67LQNpMvuA_fDNrVfq9w9JWpgHI,8510
|
|
5
5
|
unrealon_browser/cli/cookies_cli.py,sha256=yhZvGrg8bknlH4zlySdi8ue-25Ue-1rI_u1G06OIMg4,13304
|
|
6
|
-
unrealon_browser/cli/interactive_mode.py,sha256=
|
|
6
|
+
unrealon_browser/cli/interactive_mode.py,sha256=gLn9bMH0h0tPX3dP4i4QQxQK4Htkyg5r4KcqdMBaP6Q,12125
|
|
7
7
|
unrealon_browser/cli/main.py,sha256=XCYcTxJUqaz320KCU_JPKizYMk6bdljb8Boyok3uO-4,1353
|
|
8
8
|
unrealon_browser/core/__init__.py,sha256=uVL_t4sZelUzflWPdgrwoXGnAkSV1WNQ98-eu0QB2eM,151
|
|
9
9
|
unrealon_browser/core/browser_manager.py,sha256=9xxo0kqbUcssFBNpvIXavWBya2E3TmgHKplQQ2kkZeU,29598
|
|
10
10
|
unrealon_browser/dto/__init__.py,sha256=bApqcLz-KanEi0_MCiFPrQmGBoX3VBijP7XtBUyIfjo,1636
|
|
11
11
|
unrealon_browser/dto/bot_detection.py,sha256=qXfC0HghV7m4L6qA87t3STi-166jM-QgoP6OYbCb4o4,6884
|
|
12
|
-
unrealon_browser/dto/models/config.py,sha256=
|
|
12
|
+
unrealon_browser/dto/models/config.py,sha256=Why5H3rtFclmwbdczuDfhlgf-LDz72Aa8LhDX4_ayfw,1752
|
|
13
13
|
unrealon_browser/dto/models/core.py,sha256=HvbwYG27rmmWtp401uws7lfalN_9QPad0M6ceCiN5iQ,2741
|
|
14
14
|
unrealon_browser/dto/models/dataclasses.py,sha256=zqhJVyzp4CvtuTBsZwm6n6TodVWrZf9gkdDG-0_tgeA,2571
|
|
15
15
|
unrealon_browser/dto/models/detection.py,sha256=ma9ZNIjPR7HnjqZaAj6ZoskiewPFiSn_FgFXSkgiQc8,2715
|
|
@@ -23,19 +23,19 @@ unrealon_browser/managers/page_wait_manager.py,sha256=UyZqiSfkjzahrxp9x1odXFIT_s
|
|
|
23
23
|
unrealon_browser/managers/profile.py,sha256=HjddlSeUry_65WPtF8CMkT7cfJ6X3Jap9kJaaZpwtAA,18956
|
|
24
24
|
unrealon_browser/stealth/__init__.py,sha256=zUfkPPafYlPANLVQIy-Se11R_UjcJakUb3krCxxUK5Q,842
|
|
25
25
|
unrealon_browser/stealth/bypass_techniques.pyc,sha256=Tys_I4tnJmL9aQLB1k1mL-4OtzedhpYTmW4XPEIb3cI,25790
|
|
26
|
-
unrealon_browser/stealth/manager.pyc,sha256=
|
|
26
|
+
unrealon_browser/stealth/manager.pyc,sha256=prGC5fWE-T4ROGSVrc65CJ_qFpb8dScuN83FotZ1oj4,21183
|
|
27
27
|
unrealon_browser/stealth/nodriver_stealth.pyc,sha256=SnDMdeG_W2LDK_3UfrjDeolMpqxTMDNjRbId_YC_cKA,15286
|
|
28
28
|
unrealon_browser/stealth/playwright_stealth.pyc,sha256=Y-IiRTu136R6LIstGdKbns1j-CODX3sTGVT0IGx_MF4,6529
|
|
29
|
-
unrealon_browser/stealth/scanner_tester.pyc,sha256=
|
|
29
|
+
unrealon_browser/stealth/scanner_tester.pyc,sha256=ki_Lp7zBp12iWgj3LGPnXTwemwgaGG1UrWeM1IXB6Gs,21711
|
|
30
30
|
unrealon_browser/stealth/undetected_chrome.pyc,sha256=HNp2_84zwp-JG-Y3jqQwCNldnG6P5bBnbf9dBV_pOuw,12765
|
|
31
|
-
unrealon_core/__init__.py,sha256=
|
|
31
|
+
unrealon_core/__init__.py,sha256=ZAinyQDsAS63xqnl5uqUKTna9M-xFDs6fLwy8-Hh-X0,5599
|
|
32
32
|
unrealon_core/version.py,sha256=qZOlKA_Hsz7_KXsCLO_0l9Mf0u_iNTxvHZPV21DwCqs,5803
|
|
33
33
|
unrealon_core/config/__init__.py,sha256=57-KZaTDya0oyfOCfZ3pU1xLLbnZPBgqwfP9VrfhcKE,395
|
|
34
|
-
unrealon_core/config/environment.py,sha256=
|
|
35
|
-
unrealon_core/config/urls.py,sha256=
|
|
34
|
+
unrealon_core/config/environment.py,sha256=TACbyjr3lxrA9R_Ve0LLqyLnRk3FShm2imQjhiP9i98,4589
|
|
35
|
+
unrealon_core/config/urls.py,sha256=7ScpWEhSu1kewhMaKhuqgiMHIv_fyI1TyRiS-f2wrLo,2821
|
|
36
36
|
unrealon_core/enums/__init__.py,sha256=C-fqNVSWofnaq2Ed6GbGtck8WkN8gpHYA_NiIlaEbTI,520
|
|
37
37
|
unrealon_core/enums/status.py,sha256=DT6rth7Ve7chsYhJtccY6plsgQQ8jEMhfhcZTpK308c,6861
|
|
38
|
-
unrealon_core/enums/types.py,sha256=
|
|
38
|
+
unrealon_core/enums/types.py,sha256=o4df_zPVE5ByF4P5t_dnwmr1GVQytl5ppKNQBCu0i2A,8784
|
|
39
39
|
unrealon_core/error_handling/__init__.py,sha256=s7VXUJG7nvD6Od76-ZfFmn5e6r29mxNusP8YPba73GM,1248
|
|
40
40
|
unrealon_core/error_handling/circuit_breaker.py,sha256=IPT7I8nrDm4eDojrjMgM3-vLIaz-0raS-9W3LUpFnhY,10066
|
|
41
41
|
unrealon_core/error_handling/error_context.py,sha256=jfluYy1FVyyU_8dwjBN1w2vPq9oLRYLg2fgndlc_8As,9798
|
|
@@ -48,13 +48,12 @@ unrealon_core/exceptions/driver.py,sha256=YB3_oRgOMzD4cCSEhqpqpP5iD6ChbZmaOtnw4B
|
|
|
48
48
|
unrealon_core/exceptions/proxy.py,sha256=oqy3gV8jha1a2JrOtbJU8zTpWq_XA8Ooou2cl4eHF0o,246
|
|
49
49
|
unrealon_core/exceptions/task.py,sha256=uNSyJgZvBSHtimQ-ZLPjjDZrVzq6sXscJUQDkCmQ0No,285
|
|
50
50
|
unrealon_core/exceptions/validation.py,sha256=f7nWbi5ag1nqo7kwN0QaXYNfRrIwHhYVNlKHGiyhuQ8,351
|
|
51
|
-
unrealon_core/models/__init__.py,sha256=
|
|
51
|
+
unrealon_core/models/__init__.py,sha256=msWqpIVlqWb0mX3jAwX83i78hFIoqZj8XgE4TgL_uu4,1379
|
|
52
52
|
unrealon_core/models/arq_context.py,sha256=9a6yJgdQ3ZifoCgEEiN90q5qRzQ9pk0ivHnowhzLF7c,8389
|
|
53
53
|
unrealon_core/models/arq_responses.py,sha256=OYEIjLZ1iEoWEKAOqAuudYWT4I_ZVeftDFOOmWjgEaM,4471
|
|
54
54
|
unrealon_core/models/base.py,sha256=5x5Ie77wH_qRrezd8kSQ23DgOffSmCmICmVqEeXX4Hc,8964
|
|
55
55
|
unrealon_core/models/bridge_stats.py,sha256=trVoAQkFG5fc8TSzUmH133Xwf8htKYM5Xyl_3-5ApnE,1878
|
|
56
56
|
unrealon_core/models/communication.py,sha256=YFdiR-J0ncjmvg4yudU1bTfR61BEmV6sQ1zCXWN_xR0,1152
|
|
57
|
-
unrealon_core/models/config.py,sha256=VFOFyjAdDVqNMVUruIeqdbOvo-_FvXhCtt9LQvPOijE,1355
|
|
58
57
|
unrealon_core/models/connection_stats.py,sha256=KngZoi78WXsLAvl9ZYq6xRoVUaTBUVmnI8hrxZ8y038,1522
|
|
59
58
|
unrealon_core/models/driver.py,sha256=RVl69fPFWW-E9yINUi2yXEWcw3sycc-bgVOw00VfkdU,690
|
|
60
59
|
unrealon_core/models/driver_details.py,sha256=0Bc1J16Zika7p1SflsqssiF4IjpQbwNFij17H1pQR44,3737
|
|
@@ -62,8 +61,9 @@ unrealon_core/models/logging.py,sha256=MKGjMmhJhediGNVKmX0wfm1GO2kfvEqOp2cCfq42V
|
|
|
62
61
|
unrealon_core/models/task.py,sha256=QSOQMXNbvD7vfP6ddYUBPwD7jj0N9_zQxjwDMrznXo4,435
|
|
63
62
|
unrealon_core/models/typed_responses.py,sha256=shEvV4A7mYUKIzG3zD4zoRO2PmVLL3iLtGjzRGSW8Ks,7034
|
|
64
63
|
unrealon_core/models/websocket_session.py,sha256=q3RXQP2CwRTgUTYMRnb4xzzdGWUOeL8maYxVFPmF92w,5119
|
|
65
|
-
unrealon_core/models/websocket/__init__.py,sha256=
|
|
64
|
+
unrealon_core/models/websocket/__init__.py,sha256=wBgv1ZI_0_SiC2AX-UOA5fivi4rf3GEFhLDG159-x_s,2845
|
|
66
65
|
unrealon_core/models/websocket/base.py,sha256=gmkJ-vLpsajc4aGlPBeMURIFNg3AgX4yYSnJQAqqkf0,1388
|
|
66
|
+
unrealon_core/models/websocket/broadcast.py,sha256=M4MycZvF88klW2RbZeeQD5xh2xKt7B7C59pqULC28oE,3366
|
|
67
67
|
unrealon_core/models/websocket/config.py,sha256=AZz9wK8j0UIkRkDE17Nsxdocimh-41MJQkS-_VzTnM0,4665
|
|
68
68
|
unrealon_core/models/websocket/driver.py,sha256=Bi1B7nSSWkLFCwfIOLJSkPUxGS5r9x4Mw9XWvHuSxa0,5206
|
|
69
69
|
unrealon_core/models/websocket/errors.py,sha256=l5w1rCx_RrzdISPwLOoKtoPCuSNjHL5XExyFaHgayS4,3095
|
|
@@ -93,16 +93,16 @@ unrealon_driver/decorators/task.py,sha256=nnG-S99BSdtKYXZ97XBkQLDc-jpc4a6SKv-6b7
|
|
|
93
93
|
unrealon_driver/decorators/timing.py,sha256=Bmtsp2tWxY4EOq3onF1OTSk0rqf9lQNlX4RL-hyr4ZM,3927
|
|
94
94
|
unrealon_driver/driver/__init__.py,sha256=P_NJXcsigIjROwErqUXgS0VneUfGggFL4bWDbdB73iw,419
|
|
95
95
|
unrealon_driver/driver/communication/__init__.py,sha256=SalrzX_55WXlkbS9P6nm6PhddoI8pquURqx3103Q708,205
|
|
96
|
-
unrealon_driver/driver/communication/session.py,sha256=
|
|
97
|
-
unrealon_driver/driver/communication/websocket_client.py,sha256=
|
|
96
|
+
unrealon_driver/driver/communication/session.py,sha256=DYN_Q3Qm3XuOi-dM8aNihJfQJl7n8cgB1GHP621ZosQ,7552
|
|
97
|
+
unrealon_driver/driver/communication/websocket_client.py,sha256=VPsICBvGHunuCGZvorvPCF01Qdvp7QWyTDX0hkYXRwo,7910
|
|
98
98
|
unrealon_driver/driver/core/__init__.py,sha256=ZvJQp1zO7pj6tBNYTJk2fj-0ZMiQTQEk-I9hXalNsfg,235
|
|
99
|
-
unrealon_driver/driver/core/config.py,sha256=
|
|
99
|
+
unrealon_driver/driver/core/config.py,sha256=jWJjRll19VlL4iM5Q-J3o9qwYeH89Iuj1_3KayM6fCk,5914
|
|
100
100
|
unrealon_driver/driver/core/driver.py,sha256=NI-pdhnduRyHLsfFr8HmP2gp7pR1pWB4vBIJkMJ2cls,7886
|
|
101
101
|
unrealon_driver/driver/factory/__init__.py,sha256=XrjBhOaLvC3MIG5PAFIYS_xYXFDz5JizpFvmQcwA7mU,189
|
|
102
102
|
unrealon_driver/driver/factory/manager_factory.py,sha256=zJt63N8oWNJS0aNbLy7WjY4GCeUwLjvtWGdqdpucufU,4792
|
|
103
103
|
unrealon_driver/driver/lifecycle/__init__.py,sha256=KnkXklezAOIbXcCzEU_XSOt32z7tz1zIGclXYXTkO8k,286
|
|
104
|
-
unrealon_driver/driver/lifecycle/daemon.py,sha256=
|
|
105
|
-
unrealon_driver/driver/lifecycle/initialization.py,sha256=
|
|
104
|
+
unrealon_driver/driver/lifecycle/daemon.py,sha256=KHAzpiWFu3HRElRtzSEStmI74bMivFjfCAFlXha87KU,2609
|
|
105
|
+
unrealon_driver/driver/lifecycle/initialization.py,sha256=R4MgfkSNnfAdMO0Kp1Cx42cfNqq8VIxj_mGX7ECXad4,4406
|
|
106
106
|
unrealon_driver/driver/lifecycle/shutdown.py,sha256=h-6hKjDP30OjKiSgLe3UIfpS5wDbQFRaYWnEPosyTe0,1426
|
|
107
107
|
unrealon_driver/driver/monitoring/__init__.py,sha256=MJjLm4b5pzSpXGOq50e-MKUkyToiUbARzkGqNqN-iJg,162
|
|
108
108
|
unrealon_driver/driver/monitoring/health.py,sha256=rOH0wChDBTnDbV7MstB21JsJJUMGMeq3crb47RLL2ek,2164
|
|
@@ -114,16 +114,16 @@ unrealon_driver/managers/base.py,sha256=GkuXillg9uqqnx6RL682fmKgK-7JyqYlH6DFUgyN
|
|
|
114
114
|
unrealon_driver/managers/browser.py,sha256=9kwdmWPxWFpNQ1KejtpzdN615V8PBJu8Y2nUOewQaeQ,3489
|
|
115
115
|
unrealon_driver/managers/cache.py,sha256=c0tPKQ5KFd_Un1U8mw3j1WPuycxg863MMWNMveVF_2I,3506
|
|
116
116
|
unrealon_driver/managers/http.py,sha256=EjlpoTRuhpsgzzrEARxRlbGczzua7hnKFVq06bvCgTM,3624
|
|
117
|
-
unrealon_driver/managers/logger.py,sha256=
|
|
117
|
+
unrealon_driver/managers/logger.py,sha256=PL3rA9ZQl12jJU0EiPAkLwJ6eDHQfIzr8-nc8bVivKQ,10526
|
|
118
118
|
unrealon_driver/managers/proxy.py,sha256=Y_3ok2iGefeYHcTtBQIEK3Rnr8qRnPsP90hOOYPUMz0,3449
|
|
119
119
|
unrealon_driver/managers/registry.py,sha256=--oNPU-65e8J21ubJufyEOc1TirnzJIvpvuY_j7rH7Q,2666
|
|
120
120
|
unrealon_driver/managers/threading.py,sha256=djw5cSC99dfBKmep3IJ_8IgxQceMXtNvCp5fIxHM0TY,1702
|
|
121
121
|
unrealon_driver/managers/update.py,sha256=-hohVxGXpj5bZ6ZTQN6NH1RK9Pd6GVzCMtu3GS2SdcQ,3582
|
|
122
122
|
unrealon_driver/utils/__init__.py,sha256=qxXVoQJVdLJhaLBXk_LZV_062AhrvBrMPXWAKfEc3C4,104
|
|
123
123
|
unrealon_driver/utils/time.py,sha256=Oxk1eicKeZl8ZWbf7gu1Ll716k6CpXmVj67FHSnPIsA,184
|
|
124
|
-
unrealon-2.0.
|
|
125
|
-
unrealon-2.0.
|
|
126
|
-
unrealon-2.0.
|
|
127
|
-
unrealon-2.0.
|
|
128
|
-
unrealon-2.0.
|
|
129
|
-
unrealon-2.0.
|
|
124
|
+
unrealon-2.0.6.dist-info/LICENSE,sha256=eEH8mWZW49YMpl4Sh5MtKqkZ8aVTzKQXiNPEnvL14ns,1070
|
|
125
|
+
unrealon-2.0.6.dist-info/METADATA,sha256=i0-XIxaXmnkBInCUQVKkTLxp4Wh9fEGdL-4RvhIb12w,15688
|
|
126
|
+
unrealon-2.0.6.dist-info/WHEEL,sha256=pL8R0wFFS65tNSRnaOVrsw9EOkOqxLrlUPenUYnJKNo,91
|
|
127
|
+
unrealon-2.0.6.dist-info/entry_points.txt,sha256=k0qM-eotpajkKUq-almJmxj9afhXprZ6IkvQkSdcKhI,104
|
|
128
|
+
unrealon-2.0.6.dist-info/top_level.txt,sha256=Gu8IeIfIVfUxdi-h-F0nKMQxo15pjhHZ0aTadXTpRE8,47
|
|
129
|
+
unrealon-2.0.6.dist-info/RECORD,,
|
|
@@ -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
|
|
|
@@ -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
|
|
Binary file
|
unrealon_core/__init__.py
CHANGED
|
@@ -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
|
unrealon_core/config/urls.py
CHANGED
|
@@ -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
|
)
|
unrealon_core/enums/types.py
CHANGED
unrealon_core/models/__init__.py
CHANGED
|
@@ -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
|
-
#
|
|
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
|
|
@@ -7,6 +7,12 @@ from enum import Enum
|
|
|
7
7
|
from typing import Optional
|
|
8
8
|
from pydantic import BaseModel, Field, computed_field
|
|
9
9
|
|
|
10
|
+
try:
|
|
11
|
+
from unrealon_core.config.environment import get_environment_config
|
|
12
|
+
UNREALON_CORE_AVAILABLE = True
|
|
13
|
+
except ImportError:
|
|
14
|
+
UNREALON_CORE_AVAILABLE = False
|
|
15
|
+
|
|
10
16
|
|
|
11
17
|
class DriverMode(str, Enum):
|
|
12
18
|
"""Driver operation modes."""
|
|
@@ -36,9 +42,9 @@ class DriverConfig(BaseModel):
|
|
|
36
42
|
|
|
37
43
|
Priority:
|
|
38
44
|
1. Explicit websocket_url field
|
|
39
|
-
2. Environment
|
|
40
|
-
3.
|
|
41
|
-
4.
|
|
45
|
+
2. Environment variables (UNREALON_WEBSOCKET_URL, UNREALON_WS_URL, WS_URL)
|
|
46
|
+
3. UnrealOn core environment config (if available)
|
|
47
|
+
4. No default - return None if nothing configured
|
|
42
48
|
"""
|
|
43
49
|
# 1. Explicit override
|
|
44
50
|
if self.websocket_url:
|
|
@@ -53,7 +59,15 @@ class DriverConfig(BaseModel):
|
|
|
53
59
|
if env_url:
|
|
54
60
|
return env_url
|
|
55
61
|
|
|
56
|
-
# 3.
|
|
62
|
+
# 3. Try unrealon_core environment config
|
|
63
|
+
if UNREALON_CORE_AVAILABLE:
|
|
64
|
+
try:
|
|
65
|
+
env_config = get_environment_config()
|
|
66
|
+
return env_config.websocket_url
|
|
67
|
+
except Exception:
|
|
68
|
+
pass # Fallback gracefully if core config fails
|
|
69
|
+
|
|
70
|
+
# 4. No default - return None if nothing is configured
|
|
57
71
|
return None
|
|
58
72
|
|
|
59
73
|
# Logging
|
|
@@ -83,3 +97,79 @@ class DriverConfig(BaseModel):
|
|
|
83
97
|
batch_size: int = Field(default=10, description="Batch processing size")
|
|
84
98
|
|
|
85
99
|
model_config = {"extra": "forbid"}
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def for_development(cls, name: str, **kwargs) -> "DriverConfig":
|
|
103
|
+
"""
|
|
104
|
+
Create development configuration with sensible defaults.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
name: Driver name
|
|
108
|
+
**kwargs: Additional configuration overrides
|
|
109
|
+
"""
|
|
110
|
+
defaults = {
|
|
111
|
+
"name": name,
|
|
112
|
+
"mode": DriverMode.STANDALONE,
|
|
113
|
+
"log_level": "DEBUG",
|
|
114
|
+
"browser_headless": False,
|
|
115
|
+
"proxy_enabled": False,
|
|
116
|
+
"cache_enabled": True,
|
|
117
|
+
"max_workers": 2,
|
|
118
|
+
}
|
|
119
|
+
defaults.update(kwargs)
|
|
120
|
+
return cls(**defaults)
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
def for_production(cls, name: str, **kwargs) -> "DriverConfig":
|
|
124
|
+
"""
|
|
125
|
+
Create production configuration with performance and reliability defaults.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
name: Driver name
|
|
129
|
+
**kwargs: Additional configuration overrides
|
|
130
|
+
"""
|
|
131
|
+
defaults = {
|
|
132
|
+
"name": name,
|
|
133
|
+
"mode": DriverMode.DAEMON,
|
|
134
|
+
"log_level": "INFO",
|
|
135
|
+
"browser_headless": True,
|
|
136
|
+
"proxy_enabled": True,
|
|
137
|
+
"cache_enabled": True,
|
|
138
|
+
"max_workers": 4,
|
|
139
|
+
"max_retries": 5,
|
|
140
|
+
"http_timeout": 60,
|
|
141
|
+
"browser_timeout": 60,
|
|
142
|
+
}
|
|
143
|
+
defaults.update(kwargs)
|
|
144
|
+
return cls(**defaults)
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def auto_detect(cls, name: str, **kwargs) -> "DriverConfig":
|
|
148
|
+
"""
|
|
149
|
+
Auto-detect environment and create appropriate configuration.
|
|
150
|
+
|
|
151
|
+
Uses UNREALON_ENV environment variable or unrealon_core config to determine environment.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
name: Driver name
|
|
155
|
+
**kwargs: Additional configuration overrides
|
|
156
|
+
"""
|
|
157
|
+
# Try to detect environment
|
|
158
|
+
env_name = os.getenv("UNREALON_ENV", "development").lower()
|
|
159
|
+
|
|
160
|
+
# Try unrealon_core if available
|
|
161
|
+
if UNREALON_CORE_AVAILABLE:
|
|
162
|
+
try:
|
|
163
|
+
env_config = get_environment_config()
|
|
164
|
+
if env_config.is_production:
|
|
165
|
+
return cls.for_production(name, **kwargs)
|
|
166
|
+
elif env_config.is_development:
|
|
167
|
+
return cls.for_development(name, **kwargs)
|
|
168
|
+
except Exception:
|
|
169
|
+
pass # Fallback to env variable
|
|
170
|
+
|
|
171
|
+
# Fallback to environment variable
|
|
172
|
+
if env_name in ("prod", "production"):
|
|
173
|
+
return cls.for_production(name, **kwargs)
|
|
174
|
+
else:
|
|
175
|
+
return cls.for_development(name, **kwargs)
|
|
@@ -47,6 +47,7 @@ class DaemonManager:
|
|
|
47
47
|
|
|
48
48
|
try:
|
|
49
49
|
# Initialize driver with capabilities
|
|
50
|
+
logger.info(f"🎯 Daemon initializing driver with capabilities: {driver.capabilities}")
|
|
50
51
|
success = await DriverInitializer.initialize_driver(driver, driver.capabilities)
|
|
51
52
|
if not success:
|
|
52
53
|
raise RuntimeError("Driver initialization failed")
|
|
@@ -10,6 +10,8 @@ from typing import List, TYPE_CHECKING
|
|
|
10
10
|
from ..core.config import DriverMode
|
|
11
11
|
from ..communication.websocket_client import WebSocketClient
|
|
12
12
|
from ..communication.session import DriverSession
|
|
13
|
+
from unrealon_core.config.environment import get_environment_config
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
if TYPE_CHECKING:
|
|
15
17
|
from ..core.driver import UniversalDriver
|
|
@@ -37,6 +39,14 @@ class DriverInitializer:
|
|
|
37
39
|
|
|
38
40
|
try:
|
|
39
41
|
logger.info(f"Initializing UniversalDriver: {driver.driver_id}")
|
|
42
|
+
logger.info(f"🔧 Driver mode: {driver.config.mode}")
|
|
43
|
+
logger.info(f"📡 WebSocket URL: {driver.config.effective_websocket_url}")
|
|
44
|
+
logger.info(f"🎯 Capabilities: {capabilities}")
|
|
45
|
+
|
|
46
|
+
# Log environment info if available
|
|
47
|
+
env_config = get_environment_config()
|
|
48
|
+
logger.info(f"🌐 Environment: {env_config.environment.value}")
|
|
49
|
+
print(f"🌐 Environment: {env_config.environment}")
|
|
40
50
|
|
|
41
51
|
# Initialize manager system
|
|
42
52
|
if not await driver.manager_registry.initialize_all():
|
|
@@ -65,9 +75,12 @@ class DriverInitializer:
|
|
|
65
75
|
|
|
66
76
|
# Setup WebSocket client if URL available
|
|
67
77
|
if websocket_url:
|
|
78
|
+
# Pass driver's logger to WebSocket client for unified logging
|
|
79
|
+
custom_logger = driver.logger_manager.local_logger if driver.logger_manager else None
|
|
68
80
|
driver.websocket_client = WebSocketClient(
|
|
69
81
|
websocket_url,
|
|
70
|
-
driver.driver_id
|
|
82
|
+
driver.driver_id,
|
|
83
|
+
custom_logger=custom_logger
|
|
71
84
|
)
|
|
72
85
|
|
|
73
86
|
# Set WebSocket client for logger (always for logging)
|
|
@@ -89,6 +89,11 @@ class LoggerManager(BaseManager):
|
|
|
89
89
|
self.local_logger.addHandler(handler)
|
|
90
90
|
self.local_logger.setLevel(getattr(logging, self.config.log_level))
|
|
91
91
|
|
|
92
|
+
# Also configure root unrealon_driver logger to use same handler
|
|
93
|
+
unrealon_logger = logging.getLogger('unrealon_driver')
|
|
94
|
+
unrealon_logger.addHandler(handler)
|
|
95
|
+
unrealon_logger.setLevel(getattr(logging, self.config.log_level))
|
|
96
|
+
|
|
92
97
|
except Exception as e:
|
|
93
98
|
logger.error(f"Failed to setup local logging: {e}")
|
|
94
99
|
|
unrealon_core/models/config.py
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Config models - Phase 2 update.
|
|
3
|
-
|
|
4
|
-
Import from strictly typed websocket models to avoid duplication.
|
|
5
|
-
Following critical requirements - no raw Dict[str, Any].
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
# Import strictly typed models from websocket package
|
|
9
|
-
from .websocket.config import (
|
|
10
|
-
DriverConfiguration,
|
|
11
|
-
LoggingConfiguration,
|
|
12
|
-
TaskConfiguration,
|
|
13
|
-
ProxyConfiguration
|
|
14
|
-
)
|
|
15
|
-
from .base import ConfigBaseModel
|
|
16
|
-
|
|
17
|
-
# System config that doesn't exist in websocket models yet
|
|
18
|
-
class SystemConfig(ConfigBaseModel):
|
|
19
|
-
"""System-wide configuration."""
|
|
20
|
-
|
|
21
|
-
redis_url: str = "redis://localhost:6379/0"
|
|
22
|
-
websocket_url: str = "ws://localhost:8000/ws"
|
|
23
|
-
max_workers: int = 10
|
|
24
|
-
debug: bool = False
|
|
25
|
-
|
|
26
|
-
# Legacy compatibility - map to new typed models
|
|
27
|
-
HttpConfig = TaskConfiguration # HTTP settings are part of task config
|
|
28
|
-
ProxyConfig = ProxyConfiguration
|
|
29
|
-
LoggingConfig = LoggingConfiguration
|
|
30
|
-
BrowserConfig = TaskConfiguration # Browser settings are part of task config
|
|
31
|
-
CacheConfig = SystemConfig # Cache settings are system-wide
|
|
32
|
-
ThreadConfig = TaskConfiguration # Thread settings are part of task config
|
|
33
|
-
|
|
34
|
-
__all__ = [
|
|
35
|
-
'DriverConfiguration',
|
|
36
|
-
'LoggingConfiguration',
|
|
37
|
-
'TaskConfiguration',
|
|
38
|
-
'ProxyConfiguration',
|
|
39
|
-
'SystemConfig',
|
|
40
|
-
# Legacy names
|
|
41
|
-
'HttpConfig',
|
|
42
|
-
'ProxyConfig',
|
|
43
|
-
'LoggingConfig',
|
|
44
|
-
'BrowserConfig',
|
|
45
|
-
'CacheConfig',
|
|
46
|
-
'ThreadConfig'
|
|
47
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|