intuned-runtime 1.2.4__py3-none-any.whl → 1.3.0__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.
- intuned_cli/types.py +0 -12
- intuned_cli/utils/api_helpers.py +1 -1
- intuned_cli/utils/backend.py +1 -1
- {intuned_runtime-1.2.4.dist-info → intuned_runtime-1.3.0.dist-info}/METADATA +1 -1
- {intuned_runtime-1.2.4.dist-info → intuned_runtime-1.3.0.dist-info}/RECORD +16 -10
- runtime/browser/extensions/__init__.py +3 -0
- runtime/browser/extensions/helpers.py +11 -0
- runtime/browser/extensions/intuned_extension.py +87 -0
- runtime/browser/launch_chromium.py +26 -0
- runtime/types/__init__.py +4 -0
- runtime/types/settings_types.py +63 -0
- runtime/utils/__init__.py +3 -0
- runtime/utils/config_loader.py +17 -0
- {intuned_runtime-1.2.4.dist-info → intuned_runtime-1.3.0.dist-info}/WHEEL +0 -0
- {intuned_runtime-1.2.4.dist-info → intuned_runtime-1.3.0.dist-info}/entry_points.txt +0 -0
- {intuned_runtime-1.2.4.dist-info → intuned_runtime-1.3.0.dist-info}/licenses/LICENSE +0 -0
intuned_cli/types.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
from pydantic import BaseModel
|
2
|
-
from pydantic import Field
|
3
2
|
from pydantic import RootModel
|
4
3
|
|
5
4
|
|
@@ -20,14 +19,3 @@ class FileNode(BaseModel):
|
|
20
19
|
|
21
20
|
|
22
21
|
FileSystemTree.model_rebuild()
|
23
|
-
|
24
|
-
|
25
|
-
class IntunedJson(BaseModel):
|
26
|
-
model_config = {"populate_by_name": True}
|
27
|
-
|
28
|
-
class _AuthSessions(BaseModel):
|
29
|
-
enabled: bool
|
30
|
-
|
31
|
-
auth_sessions: _AuthSessions = Field(alias="authSessions")
|
32
|
-
project_name: str | None = Field(alias="projectName", default=None)
|
33
|
-
workspace_id: str | None = Field(alias="workspaceId", default=None)
|
intuned_cli/utils/api_helpers.py
CHANGED
intuned_cli/utils/backend.py
CHANGED
@@ -22,10 +22,10 @@ intuned_cli/controller/api.py,sha256=cIxfuKsMibn7XRbTQCELy1QVtWH-UMWO8KGyOCvKx2A
|
|
22
22
|
intuned_cli/controller/authsession.py,sha256=KGpRYXO9W6DFbuM1ofdH5aba0Zs4A_A3nhbNj-KaCE8,14272
|
23
23
|
intuned_cli/controller/deploy.py,sha256=krbVm0-c1XDIsiOPIf0lesFmFuVF_VfGYNWCOQ70Mmo,5452
|
24
24
|
intuned_cli/controller/save.py,sha256=_ujsh4C9cgFGW4GTkj43JDSpIq0TuBLhZRJsH_1wgX8,9025
|
25
|
-
intuned_cli/types.py,sha256=
|
26
|
-
intuned_cli/utils/api_helpers.py,sha256=
|
25
|
+
intuned_cli/types.py,sha256=A053bHBqgVUG5wdHygwlJ2F-XeJIhFRidSOij5veBOQ,394
|
26
|
+
intuned_cli/utils/api_helpers.py,sha256=q_xJMcl-RMccdTNTyDFu27T4OrXihhZtshYdZDiSDGI,1118
|
27
27
|
intuned_cli/utils/auth_session_helpers.py,sha256=acKAPUjOfdybstLur4Lk3huaLFP2Ipl4AjPSqQPQLzY,1899
|
28
|
-
intuned_cli/utils/backend.py,sha256=
|
28
|
+
intuned_cli/utils/backend.py,sha256=RvaDClFDf_Tur1E4Z_HoxM1hsHYpm_CWG0ju77ZyKZo,1026
|
29
29
|
intuned_cli/utils/confirmation.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
30
|
intuned_cli/utils/console.py,sha256=jgin2xB-0kpANPMoiDBOnWhrc8Ey5zT4IHRxQa8f5CQ,93
|
31
31
|
intuned_cli/utils/error.py,sha256=KzqWG0e8PLerCY-AL534zygCLbIBD6uReo2WEuej-EA,990
|
@@ -65,10 +65,13 @@ runtime/backend_functions/__init__.py,sha256=j2EaK4FK8bmdFtqc5FxtFwx1KhIn_7qKPCh
|
|
65
65
|
runtime/backend_functions/_call_backend_function.py,sha256=NpbQmEieuRan0fgbJQ0L_skU6MgjMmR99bWsQQYuFtI,3402
|
66
66
|
runtime/backend_functions/get_auth_session_parameters.py,sha256=pOvB7XiWpphEuBpazdKALw9EWgBU1PeY3gkzBfVLpkc,869
|
67
67
|
runtime/browser/__init__.py,sha256=EPWfa4ZmdR8GJqh2qcsx1ZvHmCYiUYrQ-zeHYVapH9s,285
|
68
|
+
runtime/browser/extensions/__init__.py,sha256=VGUFvkYUwuYc9UlYy1TD79veXuDhnLDcmbyHiVnf6dQ,80
|
69
|
+
runtime/browser/extensions/helpers.py,sha256=W3XiOf66J_xWpa4-U7hQjW0CfAucCYz12pBcIRnzLTQ,387
|
70
|
+
runtime/browser/extensions/intuned_extension.py,sha256=_La0ikQX2isdiBgZXkZt2iQ6eyW7Fe-9yjePFzpj0rk,2804
|
68
71
|
runtime/browser/helpers.py,sha256=CwgiBToawPgwAY9nIGkGHW544N7Db_OgKmS-SHkN2pU,1255
|
69
72
|
runtime/browser/launch_browser.py,sha256=ym97J4RffOGUwhi9iNjAR5Ek2f8pKiAtAcDQFSqMJw0,1899
|
70
73
|
runtime/browser/launch_camoufox.py,sha256=TBOAwwipNGlbtMdFYnGkVM0ppLU44vWNkMGZA5uPZCE,1787
|
71
|
-
runtime/browser/launch_chromium.py,sha256=
|
74
|
+
runtime/browser/launch_chromium.py,sha256=uKX12nclua-tcW6_dgJvihq4MrcC0tnsgcndKSLWm_M,8002
|
72
75
|
runtime/browser/storage_state.py,sha256=fwLg8sP-H-vgt_6AJKNl03CpgyMVCQWWcN2cqswTQMs,3603
|
73
76
|
runtime/constants.py,sha256=YMYQgCWZdJXUpxz_IN2TvZO5rFye9k_Lk9CS8m-shLg,34
|
74
77
|
runtime/context/__init__.py,sha256=hg8ejm4bJy4tNkwmZ9lKgYJx6bU7OgOdBS684Uv5XGg,73
|
@@ -93,13 +96,16 @@ runtime/run/run_api.py,sha256=AFhZerLTyHGznGCJl0fLbiTqhZ_WRmphaJK-RqrupTQ,8981
|
|
93
96
|
runtime/run/setup_context_hook.py,sha256=KTX4mRmeUEmYS9zrmobR1V09GakOk6uz81Uo_xXTJZk,1156
|
94
97
|
runtime/run/traces.py,sha256=fKzh11LqV47ujgq_9I2tdp-dgld566wffWaHwU_4gis,1123
|
95
98
|
runtime/run/types.py,sha256=RR4RGiYVBIK6f2qXvzfLhQMZ8kmrziu265k5eqoIh98,346
|
96
|
-
runtime/types/__init__.py,sha256=
|
99
|
+
runtime/types/__init__.py,sha256=LWf5iOMgbve_BrpVP-LWWzDD3v2K4Y2sLxthOnVEqyY,539
|
97
100
|
runtime/types/payload.py,sha256=sty8HgDEn3nJbZrwEOMCXyuG7_ICGDwlBIIWSON5ABY,124
|
98
101
|
runtime/types/run_types.py,sha256=GcYLkL2BHxOjT4O3KvBP6xjBKsmJbjltMt_5bCVnfCI,4554
|
102
|
+
runtime/types/settings_types.py,sha256=KWCxrQBlvJ8cmOJ2w1qd0-R5ypKlJLFJ5aiRh3BAluU,2486
|
103
|
+
runtime/utils/__init__.py,sha256=v0qHjnc54YCkY1yPbXuihgymVZau_15xaEVyaFQj9ts,78
|
104
|
+
runtime/utils/config_loader.py,sha256=yqk2eDGbgyw0Xslgd3dJbB28NjUe02L9LyCxzCmH9r4,482
|
99
105
|
runtime_helpers/__init__.py,sha256=1BPzEc-qC2WAYiCWDOJChpgnFyO3ZNYRKHEZqdHUGwM,322
|
100
106
|
runtime_helpers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
101
|
-
intuned_runtime-1.
|
102
|
-
intuned_runtime-1.
|
103
|
-
intuned_runtime-1.
|
104
|
-
intuned_runtime-1.
|
105
|
-
intuned_runtime-1.
|
107
|
+
intuned_runtime-1.3.0.dist-info/METADATA,sha256=urXlJpaADhNBiM22PAO1OUOn0xhbXEh8HyDsJSVZO6w,5372
|
108
|
+
intuned_runtime-1.3.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
109
|
+
intuned_runtime-1.3.0.dist-info/entry_points.txt,sha256=ToMS2cqDeRmF1FGkflwoeD-Xz6jJV5p1zIbw9G7IxMg,85
|
110
|
+
intuned_runtime-1.3.0.dist-info/licenses/LICENSE,sha256=9LIjQdgyU_ptzNIfItNCR7VmEHqYnrY1f1XwOreKFI0,3714
|
111
|
+
intuned_runtime-1.3.0.dist-info/RECORD,,
|
@@ -0,0 +1,11 @@
|
|
1
|
+
from .intuned_extension import get_intuned_extension_path
|
2
|
+
from .intuned_extension import is_intuned_extension_enabled
|
3
|
+
|
4
|
+
|
5
|
+
def build_extensions_list() -> list[str]:
|
6
|
+
extensions_list: list[str] = []
|
7
|
+
|
8
|
+
if is_intuned_extension_enabled():
|
9
|
+
intuned_extension_path = get_intuned_extension_path()
|
10
|
+
extensions_list.append(str(intuned_extension_path))
|
11
|
+
return extensions_list
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from playwright.async_api import BrowserContext
|
7
|
+
|
8
|
+
from runtime.context.context import IntunedContext
|
9
|
+
from runtime.env import get_functions_domain
|
10
|
+
from runtime.env import get_project_id
|
11
|
+
from runtime.env import get_workspace_id
|
12
|
+
from runtime.types import CaptchaSolverSettings
|
13
|
+
from runtime.utils.config_loader import load_intuned_json
|
14
|
+
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
def get_intuned_extension_path() -> Path | None:
|
19
|
+
if "INTUNED_EXTENSION_PATH" not in os.environ:
|
20
|
+
return None
|
21
|
+
intuned_extension_path = Path(os.environ["INTUNED_EXTENSION_PATH"])
|
22
|
+
if not intuned_extension_path.exists():
|
23
|
+
return None
|
24
|
+
return intuned_extension_path
|
25
|
+
|
26
|
+
|
27
|
+
def is_intuned_extension_enabled() -> bool:
|
28
|
+
intuned_extension_path = get_intuned_extension_path()
|
29
|
+
if intuned_extension_path is None:
|
30
|
+
return False
|
31
|
+
else:
|
32
|
+
return True
|
33
|
+
|
34
|
+
|
35
|
+
async def get_intuned_worker(context: BrowserContext):
|
36
|
+
if not is_intuned_extension_enabled():
|
37
|
+
return None
|
38
|
+
|
39
|
+
for attempt in range(5):
|
40
|
+
for service_worker in context.service_workers:
|
41
|
+
if "intunedWorker.js" in service_worker.url:
|
42
|
+
return service_worker
|
43
|
+
try:
|
44
|
+
if attempt < 4:
|
45
|
+
await context.wait_for_event("serviceworker", timeout=3000)
|
46
|
+
except Exception as e:
|
47
|
+
logger.warning(f"Error accessing service workers (attempt {attempt + 1}): {e}")
|
48
|
+
|
49
|
+
logger.warning("Failed to get intuned worker after 5 attempts")
|
50
|
+
return None
|
51
|
+
|
52
|
+
|
53
|
+
async def get_intuned_extension_settings() -> dict[str, Any]:
|
54
|
+
intuned_json = await load_intuned_json()
|
55
|
+
captcha_settings: CaptchaSolverSettings = (
|
56
|
+
intuned_json.captcha_solver
|
57
|
+
if intuned_json and intuned_json.captcha_solver is not None
|
58
|
+
else CaptchaSolverSettings()
|
59
|
+
)
|
60
|
+
context = IntunedContext.current()
|
61
|
+
return {
|
62
|
+
**captcha_settings.model_dump(mode="json"),
|
63
|
+
"workspaceId": get_workspace_id(),
|
64
|
+
"projectId": get_project_id(),
|
65
|
+
"token": context.functions_token,
|
66
|
+
"baseUrl": get_functions_domain(),
|
67
|
+
}
|
68
|
+
|
69
|
+
|
70
|
+
async def setup_intuned_extension():
|
71
|
+
if not is_intuned_extension_enabled():
|
72
|
+
return
|
73
|
+
intuned_extension_path = get_intuned_extension_path()
|
74
|
+
if intuned_extension_path is None:
|
75
|
+
logger.warning("Intuned extension path not found, intuned extension might not work properly")
|
76
|
+
return
|
77
|
+
|
78
|
+
settings_path = intuned_extension_path / "intunedSettings.json"
|
79
|
+
settings_data = await get_intuned_extension_settings()
|
80
|
+
|
81
|
+
try:
|
82
|
+
with open(settings_path, "w") as f:
|
83
|
+
import json
|
84
|
+
|
85
|
+
json.dump(settings_data, f)
|
86
|
+
except Exception as e:
|
87
|
+
logger.warning(f"Failed to write intuned settings to {settings_path}: {e}")
|
@@ -8,6 +8,11 @@ from typing import TYPE_CHECKING
|
|
8
8
|
|
9
9
|
import anyio
|
10
10
|
|
11
|
+
from runtime.browser.extensions import build_extensions_list
|
12
|
+
from runtime.browser.extensions.intuned_extension import get_intuned_worker
|
13
|
+
from runtime.browser.extensions.intuned_extension import is_intuned_extension_enabled
|
14
|
+
from runtime.browser.extensions.intuned_extension import setup_intuned_extension
|
15
|
+
|
11
16
|
from .helpers import get_local_cdp_address
|
12
17
|
from .helpers import get_proxy_env
|
13
18
|
from .helpers import wait_on_cdp_address
|
@@ -80,15 +85,36 @@ async def launch_chromium(
|
|
80
85
|
if cdp_port:
|
81
86
|
extra_args.append(f"--remote-debugging-port={cdp_port}")
|
82
87
|
|
88
|
+
args_to_ignore = [
|
89
|
+
"--disable-extensions",
|
90
|
+
"--disable-component-extensions-with-background-pages",
|
91
|
+
"--disable-background-networking",
|
92
|
+
"--disable-backgrounding-occluded-windows",
|
93
|
+
"--disable-background-timer-throttling",
|
94
|
+
]
|
95
|
+
if is_intuned_extension_enabled():
|
96
|
+
extensions_list = build_extensions_list()
|
97
|
+
extensions = ",".join(extensions_list)
|
98
|
+
extra_args.append("--disable-extensions-except=" + extensions)
|
99
|
+
extra_args.append(f"--load-extension={extensions}")
|
100
|
+
await setup_intuned_extension()
|
101
|
+
|
102
|
+
if headless:
|
103
|
+
args_to_ignore.append("--headless=old")
|
104
|
+
extra_args.append("--headless=new")
|
83
105
|
context = await playwright.chromium.launch_persistent_context(
|
84
106
|
os.fspath(user_preferences_dir),
|
85
107
|
headless=headless,
|
86
108
|
viewport=viewport,
|
87
109
|
proxy=proxy,
|
110
|
+
ignore_default_args=args_to_ignore,
|
88
111
|
user_agent=os.environ.get("USER_AGENT", default_user_agent),
|
89
112
|
args=extra_args,
|
90
113
|
**kwargs,
|
91
114
|
)
|
115
|
+
if is_intuned_extension_enabled():
|
116
|
+
# wait for intuned extension to be ready
|
117
|
+
await get_intuned_worker(context)
|
92
118
|
if cdp_port:
|
93
119
|
await wait_on_cdp_address(get_local_cdp_address(cdp_port))
|
94
120
|
|
runtime/types/__init__.py
CHANGED
@@ -4,6 +4,8 @@ from .run_types import RunAutomationErrorResult
|
|
4
4
|
from .run_types import RunAutomationResult
|
5
5
|
from .run_types import RunAutomationSuccessResult
|
6
6
|
from .run_types import RunBody
|
7
|
+
from .settings_types import CaptchaSolverSettings
|
8
|
+
from .settings_types import IntunedJson
|
7
9
|
|
8
10
|
__all__ = [
|
9
11
|
"Payload",
|
@@ -12,4 +14,6 @@ __all__ = [
|
|
12
14
|
"RunAutomationErrorResult",
|
13
15
|
"RunAutomationSuccessResult",
|
14
16
|
"PayloadToAppend",
|
17
|
+
"IntunedJson",
|
18
|
+
"CaptchaSolverSettings",
|
15
19
|
]
|
@@ -0,0 +1,63 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
3
|
+
from pydantic import BaseModel
|
4
|
+
from pydantic import Field
|
5
|
+
|
6
|
+
|
7
|
+
class CaptchaSettings(BaseModel):
|
8
|
+
enabled: bool = Field(default=False)
|
9
|
+
|
10
|
+
|
11
|
+
class CustomCaptchaSettings(CaptchaSettings):
|
12
|
+
model_config = {
|
13
|
+
"populate_by_name": True,
|
14
|
+
"serialize_by_alias": True,
|
15
|
+
}
|
16
|
+
|
17
|
+
image_locators: List[str] = Field(alias="imageLocators", default=[])
|
18
|
+
submit_locators: List[str] = Field(alias="submitLocators", default=[])
|
19
|
+
input_locators: List[str] = Field(alias="inputLocators", default=[])
|
20
|
+
|
21
|
+
|
22
|
+
class TextCaptchaSettings(CaptchaSettings):
|
23
|
+
model_config = {
|
24
|
+
"populate_by_name": True,
|
25
|
+
"serialize_by_alias": True,
|
26
|
+
}
|
27
|
+
label_locators: List[str] = Field(alias="labelLocators", default=[])
|
28
|
+
submit_locators: List[str] = Field(alias="submitLocators", default=[])
|
29
|
+
input_locators: List[str] = Field(alias="inputLocators", default=[])
|
30
|
+
|
31
|
+
|
32
|
+
class CaptchaSolverSettings(BaseModel):
|
33
|
+
model_config = {
|
34
|
+
"populate_by_name": True,
|
35
|
+
"serialize_by_alias": True,
|
36
|
+
}
|
37
|
+
|
38
|
+
enabled: bool = Field(default=False)
|
39
|
+
cloudflare: CaptchaSettings = Field(default_factory=CaptchaSettings)
|
40
|
+
google_recaptcha_v2: CaptchaSettings = Field(alias="googleRecaptchaV2", default_factory=CaptchaSettings)
|
41
|
+
google_recaptcha_v3: CaptchaSettings = Field(alias="googleRecaptchaV3", default_factory=CaptchaSettings)
|
42
|
+
awscaptcha: CaptchaSettings = Field(default_factory=CaptchaSettings)
|
43
|
+
hcaptcha: CaptchaSettings = Field(default_factory=CaptchaSettings)
|
44
|
+
funcaptcha: CaptchaSettings = Field(default_factory=CaptchaSettings)
|
45
|
+
geetest: CaptchaSettings = Field(default_factory=CaptchaSettings)
|
46
|
+
lemin: CaptchaSettings = Field(default_factory=CaptchaSettings)
|
47
|
+
custom_captcha: CustomCaptchaSettings = Field(alias="customCaptcha", default_factory=CustomCaptchaSettings)
|
48
|
+
text: TextCaptchaSettings = Field(default_factory=TextCaptchaSettings)
|
49
|
+
settings: dict[str, int | bool] = Field(
|
50
|
+
default={"autoSolve": True, "solveDelay": 2000, "maxRetries": 3, "timeout": 30000}
|
51
|
+
)
|
52
|
+
|
53
|
+
|
54
|
+
class IntunedJson(BaseModel):
|
55
|
+
model_config = {"populate_by_name": True}
|
56
|
+
|
57
|
+
class _AuthSessions(BaseModel):
|
58
|
+
enabled: bool
|
59
|
+
|
60
|
+
auth_sessions: _AuthSessions = Field(alias="authSessions")
|
61
|
+
project_name: str | None = Field(alias="projectName", default=None)
|
62
|
+
workspace_id: str | None = Field(alias="workspaceId", default=None)
|
63
|
+
captcha_solver: CaptchaSolverSettings | None = Field(alias="captchaSolver", default=None)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from anyio import Path
|
2
|
+
|
3
|
+
from runtime.types import IntunedJson
|
4
|
+
|
5
|
+
|
6
|
+
async def load_intuned_json() -> IntunedJson | None:
|
7
|
+
"""
|
8
|
+
Load the Intuned.json configuration file.
|
9
|
+
Returns None if file doesn't exist or fails to parse.
|
10
|
+
"""
|
11
|
+
intuned_json_path = Path("Intuned.json")
|
12
|
+
if not await intuned_json_path.exists():
|
13
|
+
return None
|
14
|
+
try:
|
15
|
+
return IntunedJson.model_validate_json(await intuned_json_path.read_text())
|
16
|
+
except Exception:
|
17
|
+
return None
|
File without changes
|
File without changes
|
File without changes
|