intuned-runtime 1.0.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.
- cli/__init__.py +45 -0
- cli/commands/__init__.py +25 -0
- cli/commands/ai_source/__init__.py +4 -0
- cli/commands/ai_source/ai_source.py +10 -0
- cli/commands/ai_source/deploy.py +64 -0
- cli/commands/browser/__init__.py +3 -0
- cli/commands/browser/save_state.py +32 -0
- cli/commands/init.py +127 -0
- cli/commands/project/__init__.py +20 -0
- cli/commands/project/auth_session/__init__.py +5 -0
- cli/commands/project/auth_session/check.py +118 -0
- cli/commands/project/auth_session/create.py +96 -0
- cli/commands/project/auth_session/load.py +39 -0
- cli/commands/project/project.py +10 -0
- cli/commands/project/run.py +340 -0
- cli/commands/project/run_interface.py +265 -0
- cli/commands/project/type_check.py +86 -0
- cli/commands/project/upgrade.py +92 -0
- cli/commands/publish_packages.py +264 -0
- cli/logger.py +19 -0
- cli/utils/ai_source_project.py +31 -0
- cli/utils/code_tree.py +83 -0
- cli/utils/run_apis.py +147 -0
- cli/utils/unix_socket.py +55 -0
- intuned_runtime-1.0.0.dist-info/LICENSE +42 -0
- intuned_runtime-1.0.0.dist-info/METADATA +113 -0
- intuned_runtime-1.0.0.dist-info/RECORD +58 -0
- intuned_runtime-1.0.0.dist-info/WHEEL +4 -0
- intuned_runtime-1.0.0.dist-info/entry_points.txt +3 -0
- runtime/__init__.py +3 -0
- runtime/backend_functions/__init__.py +5 -0
- runtime/backend_functions/_call_backend_function.py +86 -0
- runtime/backend_functions/get_auth_session_parameters.py +30 -0
- runtime/browser/__init__.py +3 -0
- runtime/browser/launch_chromium.py +212 -0
- runtime/browser/storage_state.py +106 -0
- runtime/context/__init__.py +5 -0
- runtime/context/context.py +51 -0
- runtime/env.py +13 -0
- runtime/errors/__init__.py +21 -0
- runtime/errors/auth_session_errors.py +9 -0
- runtime/errors/run_api_errors.py +120 -0
- runtime/errors/trace_errors.py +3 -0
- runtime/helpers/__init__.py +5 -0
- runtime/helpers/extend_payload.py +9 -0
- runtime/helpers/extend_timeout.py +13 -0
- runtime/helpers/get_auth_session_parameters.py +14 -0
- runtime/py.typed +0 -0
- runtime/run/__init__.py +3 -0
- runtime/run/intuned_settings.py +38 -0
- runtime/run/playwright_constructs.py +19 -0
- runtime/run/run_api.py +233 -0
- runtime/run/traces.py +36 -0
- runtime/types/__init__.py +15 -0
- runtime/types/payload.py +7 -0
- runtime/types/run_types.py +177 -0
- runtime_helpers/__init__.py +5 -0
- runtime_helpers/py.typed +0 -0
cli/__init__.py
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
import sys
|
2
|
+
import traceback
|
3
|
+
|
4
|
+
import arguably
|
5
|
+
from dotenv import find_dotenv
|
6
|
+
from dotenv import load_dotenv
|
7
|
+
from more_termcolor import bold # type: ignore
|
8
|
+
from more_termcolor import red # type: ignore
|
9
|
+
|
10
|
+
from runtime.context.context import IntunedContext
|
11
|
+
|
12
|
+
from . import commands
|
13
|
+
|
14
|
+
|
15
|
+
def run():
|
16
|
+
dotenv = find_dotenv(usecwd=True)
|
17
|
+
if dotenv:
|
18
|
+
load_dotenv(dotenv, override=True)
|
19
|
+
try:
|
20
|
+
with IntunedContext():
|
21
|
+
arguably.run()
|
22
|
+
except ValueError as e:
|
23
|
+
print(bold(red(str(e))))
|
24
|
+
sys.exit(1)
|
25
|
+
except KeyboardInterrupt:
|
26
|
+
print(bold(red("\nš Aborted")))
|
27
|
+
sys.exit(1)
|
28
|
+
except Exception as e:
|
29
|
+
tb_list = traceback.extract_tb(e.__traceback__)
|
30
|
+
|
31
|
+
DEPTH_THRESHOLD = 4
|
32
|
+
|
33
|
+
if len(tb_list) > DEPTH_THRESHOLD:
|
34
|
+
relevant_frames = tb_list[DEPTH_THRESHOLD:] # Show last 2 frames - adjust as needed
|
35
|
+
formatted_tb = "".join(traceback.format_list(relevant_frames))
|
36
|
+
print(f"Traceback (most recent call last):\n{formatted_tb}{type(e).__name__}: {str(e)}")
|
37
|
+
else:
|
38
|
+
# For shallow traces, show everything
|
39
|
+
print("".join(traceback.format_exception(type(e), e, e.__traceback__)))
|
40
|
+
|
41
|
+
print(red(bold(f"ā An error occurred: {e}")))
|
42
|
+
sys.exit(1)
|
43
|
+
|
44
|
+
|
45
|
+
__all__ = ["commands", "run"]
|
cli/commands/__init__.py
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
from .ai_source import ai_source # type: ignore
|
2
|
+
from .ai_source import ai_source__deploy # type: ignore
|
3
|
+
from .browser import browser__save_state # type: ignore
|
4
|
+
from .init import init # type: ignore
|
5
|
+
from .project import project # type: ignore
|
6
|
+
from .project import project__run # type: ignore
|
7
|
+
from .project import project__type_check # type: ignore
|
8
|
+
from .project.auth_session import project__auth_session__check # type: ignore
|
9
|
+
from .project.auth_session import project__auth_session__create # type: ignore
|
10
|
+
from .project.auth_session import project__auth_session__load # type: ignore
|
11
|
+
from .publish_packages import publish_packages # type: ignore
|
12
|
+
|
13
|
+
__all__ = [
|
14
|
+
"project__run",
|
15
|
+
"publish_packages",
|
16
|
+
"init",
|
17
|
+
"project",
|
18
|
+
"ai_source__deploy",
|
19
|
+
"ai_source",
|
20
|
+
"project__auth_session__load",
|
21
|
+
"project__auth_session__create",
|
22
|
+
"project__auth_session__check",
|
23
|
+
"project__type_check",
|
24
|
+
"browser__save_state",
|
25
|
+
]
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import json
|
2
|
+
|
3
|
+
import arguably
|
4
|
+
from more_termcolor import bold # type: ignore
|
5
|
+
from more_termcolor import cyan # type: ignore
|
6
|
+
from more_termcolor import green # type: ignore
|
7
|
+
from more_termcolor import red # type: ignore
|
8
|
+
|
9
|
+
from cli.utils.code_tree import convert_project_to_code_tree
|
10
|
+
|
11
|
+
from ...utils.ai_source_project import AiSourceInfo
|
12
|
+
from ...utils.ai_source_project import deploy_ai_source
|
13
|
+
|
14
|
+
|
15
|
+
@arguably.command # type: ignore
|
16
|
+
def ai_source__deploy(
|
17
|
+
*,
|
18
|
+
ai_source_info_str: str,
|
19
|
+
ai_source_info_path: str,
|
20
|
+
yes_to_all: bool = False,
|
21
|
+
):
|
22
|
+
"""
|
23
|
+
Commands to run on AI source projects.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
ai_source_info_str (str): [--ai-source-info] JSON string containing the AI source project information.
|
27
|
+
ai_source_info_path (str): Path to the JSON file containing the AI source project information. Defaults to <current directory>/ai_source.json.
|
28
|
+
yes_to_all (bool): [-y/--yes] Skip confirmation prompts.
|
29
|
+
"""
|
30
|
+
|
31
|
+
if ai_source_info_str and ai_source_info_path:
|
32
|
+
raise ValueError("Only one of ai_source_info or ai_source_info_path should be provided.")
|
33
|
+
|
34
|
+
if not (ai_source_info_str or ai_source_info_path):
|
35
|
+
ai_source_info_path = "ai_source.json"
|
36
|
+
|
37
|
+
try:
|
38
|
+
if ai_source_info_str:
|
39
|
+
ai_source_info_json = json.loads(ai_source_info_str)
|
40
|
+
else:
|
41
|
+
with open(ai_source_info_path) as f:
|
42
|
+
ai_source_info_json = json.load(f)
|
43
|
+
ai_source_info = AiSourceInfo(**ai_source_info_json)
|
44
|
+
except json.JSONDecodeError as e:
|
45
|
+
raise ValueError(f"Invalid JSON in ai_source_info: {e}") from e
|
46
|
+
except FileNotFoundError as e:
|
47
|
+
raise ValueError("AI source info file not found") from e
|
48
|
+
except OSError as e:
|
49
|
+
raise ValueError("Error reading AI source info file") from e
|
50
|
+
except TypeError as e:
|
51
|
+
raise ValueError("AI source info is invalid:", str(e)) from e
|
52
|
+
|
53
|
+
wait_for_confirm = not yes_to_all
|
54
|
+
|
55
|
+
code_tree = convert_project_to_code_tree(".", wait_for_confirm=wait_for_confirm)
|
56
|
+
|
57
|
+
success = deploy_ai_source(code_tree, ai_source_info)
|
58
|
+
|
59
|
+
if success:
|
60
|
+
print(
|
61
|
+
f"š AI source deployment triggered for {bold(green(ai_source_info.id))} ({bold(green(ai_source_info.version_id))}). Check progress at {cyan(f"{ai_source_info.environment_url}/__internal-ai-sources/{ai_source_info.id}?version_id={ai_source_info.version_id}")}"
|
62
|
+
)
|
63
|
+
else:
|
64
|
+
print(red(bold("Deployment failed")))
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
|
4
|
+
import arguably
|
5
|
+
|
6
|
+
from runtime.browser import launch_chromium
|
7
|
+
from runtime.browser.storage_state import get_storage_state
|
8
|
+
|
9
|
+
|
10
|
+
@arguably.command # type: ignore
|
11
|
+
async def browser__save_state(
|
12
|
+
*,
|
13
|
+
cdp_address: str,
|
14
|
+
output_path: str,
|
15
|
+
):
|
16
|
+
"""
|
17
|
+
Load an auth session to a browser.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
cdp_address (str): The CDP address of the browser to save the state from.
|
21
|
+
output_path (str): Path to save browser state to.
|
22
|
+
"""
|
23
|
+
|
24
|
+
async with launch_chromium(
|
25
|
+
cdp_address=cdp_address,
|
26
|
+
) as (context, _):
|
27
|
+
storage_state = await get_storage_state(context)
|
28
|
+
|
29
|
+
path = os.path.join(os.getcwd(), output_path)
|
30
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
31
|
+
with open(path, "w") as f:
|
32
|
+
json.dump(storage_state.model_dump(by_alias=True), f, indent=2)
|
cli/commands/init.py
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
import arguably
|
6
|
+
import toml
|
7
|
+
from more_termcolor import bold # type: ignore
|
8
|
+
from more_termcolor import green # type: ignore
|
9
|
+
|
10
|
+
from ..utils.code_tree import get_project_name
|
11
|
+
|
12
|
+
|
13
|
+
@arguably.command # type: ignore
|
14
|
+
def init(
|
15
|
+
*,
|
16
|
+
yes_to_all: bool = False,
|
17
|
+
no_to_all: bool = False,
|
18
|
+
project_name: str | None,
|
19
|
+
):
|
20
|
+
"""
|
21
|
+
Initializes current app, creating pyproject.toml and Intuned.json files. Will ask for confirmation before overwriting existing files.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
yes_to_all (bool): [-y/--yes] Answer yes to all confirmation prompts
|
25
|
+
no_to_all (bool): [-n/--no] Answer no to any confirmation prompts
|
26
|
+
project_name (str | None): Name of the project. Will automatically resolve if not provided.
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
None
|
30
|
+
"""
|
31
|
+
|
32
|
+
if yes_to_all and no_to_all:
|
33
|
+
raise ValueError("Cannot specify both --yes and --no")
|
34
|
+
|
35
|
+
def should_write_file(file: str) -> bool:
|
36
|
+
if not os.path.exists(file):
|
37
|
+
return True
|
38
|
+
if no_to_all:
|
39
|
+
return False
|
40
|
+
elif yes_to_all:
|
41
|
+
print(bold(f"Overwriting {green(file)}"))
|
42
|
+
return True
|
43
|
+
return input(f"Overwrite {green(file)}? (y/N) ").lower().strip() == "y"
|
44
|
+
|
45
|
+
project_name = project_name or get_project_name(".")
|
46
|
+
print(bold("Initializing"), green(project_name))
|
47
|
+
|
48
|
+
def print_created(file: str) -> None:
|
49
|
+
print(bold("š¦ Created"), green(file))
|
50
|
+
|
51
|
+
pyproject_name = "pyproject.toml"
|
52
|
+
if should_write_file(pyproject_name):
|
53
|
+
with open(pyproject_name, "w") as f:
|
54
|
+
toml.dump(_get_pyproject(project_name), f)
|
55
|
+
print_created(pyproject_name)
|
56
|
+
|
57
|
+
intuned_json_name = "Intuned.json"
|
58
|
+
if should_write_file(intuned_json_name):
|
59
|
+
with open(intuned_json_name, "w") as f:
|
60
|
+
json.dump(_get_intuned_json(project_name), f, indent=2)
|
61
|
+
print_created(intuned_json_name)
|
62
|
+
|
63
|
+
readme_name = "README.md"
|
64
|
+
if should_write_file(readme_name):
|
65
|
+
with open(readme_name, "w") as f:
|
66
|
+
f.write(_get_readme(project_name))
|
67
|
+
print_created(readme_name)
|
68
|
+
|
69
|
+
print(bold("⨠Done!"))
|
70
|
+
|
71
|
+
|
72
|
+
def _get_pyproject(project_name: str) -> dict[str, Any]:
|
73
|
+
return {
|
74
|
+
"build-system": {"requires": ["poetry-core>=1.2.0"], "build-backend": "poetry.core.masonry.api"},
|
75
|
+
"tool": {
|
76
|
+
"poetry": {
|
77
|
+
"package-mode": False,
|
78
|
+
"name": project_name,
|
79
|
+
"version": "0.0.1",
|
80
|
+
"description": f"Project {project_name}",
|
81
|
+
"authors": ["Intuned <service@intunedhq.com>"],
|
82
|
+
"readme": "README.md",
|
83
|
+
"dependencies": {
|
84
|
+
"python": ">=3.12,<3.13",
|
85
|
+
"intuned-runtime": {
|
86
|
+
"git": "ssh://git@github.com/Intuned/python-packages.git",
|
87
|
+
"tag": "runtime-latest",
|
88
|
+
"subdirectory": "runtime",
|
89
|
+
},
|
90
|
+
"intuned-sdk": {
|
91
|
+
"git": "ssh://git@github.com/Intuned/python-packages.git",
|
92
|
+
"tag": "sdk-latest",
|
93
|
+
"subdirectory": "sdk",
|
94
|
+
},
|
95
|
+
},
|
96
|
+
}
|
97
|
+
},
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
def _get_intuned_json(_project_name: str) -> dict[str, Any]:
|
102
|
+
return {
|
103
|
+
"authSessions": {"enabled": False},
|
104
|
+
"scale": {"machineCount": 1, "softLimit": 1, "hardLimit": 5, "memory": 2048, "cpus": 6},
|
105
|
+
"proxy": {"enabled": False},
|
106
|
+
}
|
107
|
+
|
108
|
+
|
109
|
+
def _get_readme(project_name: str) -> str:
|
110
|
+
return (
|
111
|
+
f"# `{project_name}` Intuned Automation Project\n"
|
112
|
+
f"\n"
|
113
|
+
f"\n"
|
114
|
+
f"## Getting started\n"
|
115
|
+
f"- Install dependencies: `poetry install`\n"
|
116
|
+
f"- Activate virtual environment: `poetry shell`\n"
|
117
|
+
f"- Project commands: `intuned project --help`\n"
|
118
|
+
f" - Run the project:\n"
|
119
|
+
f" - Sample mode: `intuned project run`\n"
|
120
|
+
f" - Full mode: `intuned project run --mode full`\n"
|
121
|
+
f" - Single mode: `intuned project run --mode single`\n"
|
122
|
+
f" - Deploy the project: `intuned project deploy`\n"
|
123
|
+
f" - Use `--help` for full details on each command.\n"
|
124
|
+
f"\n"
|
125
|
+
f"## SDK\n"
|
126
|
+
f"- If you want to use a specific version of the SDK, make sure to change the tag from `sdk-latest` to `sdk-<version>` in **pyproject.toml**.\n"
|
127
|
+
)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from .auth_session import project__auth_session__check # type: ignore
|
2
|
+
from .auth_session import project__auth_session__create # type: ignore
|
3
|
+
from .auth_session import project__auth_session__load # type: ignore
|
4
|
+
from .project import project # type: ignore
|
5
|
+
from .run import project__run # type: ignore
|
6
|
+
from .run_interface import project__run_interface # type: ignore
|
7
|
+
from .type_check import project__type_check # type: ignore
|
8
|
+
from .upgrade import project__upgrade # type: ignore
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"run",
|
12
|
+
"project__run",
|
13
|
+
"project__type_check",
|
14
|
+
"project",
|
15
|
+
"project__auth_session__load",
|
16
|
+
"project__auth_session__create",
|
17
|
+
"project__auth_session__check",
|
18
|
+
"project__upgrade",
|
19
|
+
"project__run_interface",
|
20
|
+
]
|
@@ -0,0 +1,5 @@
|
|
1
|
+
from .check import project__auth_session__check # type: ignore
|
2
|
+
from .create import project__auth_session__create # type: ignore
|
3
|
+
from .load import project__auth_session__load # type: ignore
|
4
|
+
|
5
|
+
__all__ = ["project__auth_session__check", "project__auth_session__create", "project__auth_session__load"]
|
@@ -0,0 +1,118 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
import arguably
|
6
|
+
import pydantic # type: ignore
|
7
|
+
from more_termcolor import bold # type: ignore
|
8
|
+
from more_termcolor import green # type: ignore
|
9
|
+
from more_termcolor import red # type: ignore
|
10
|
+
from tenacity import retry
|
11
|
+
from tenacity import retry_if_not_result
|
12
|
+
from tenacity import RetryError
|
13
|
+
from tenacity import stop_after_attempt
|
14
|
+
|
15
|
+
from runtime.context.context import IntunedContext
|
16
|
+
from runtime.errors.run_api_errors import RunApiError
|
17
|
+
from runtime.run.intuned_settings import load_intuned_settings
|
18
|
+
from runtime.run.run_api import import_function_from_api_dir
|
19
|
+
from runtime.run.run_api import run_api
|
20
|
+
from runtime.types.run_types import Auth
|
21
|
+
from runtime.types.run_types import AutomationFunction
|
22
|
+
from runtime.types.run_types import CDPRunOptions
|
23
|
+
from runtime.types.run_types import RunApiParameters
|
24
|
+
from runtime.types.run_types import StandaloneRunOptions
|
25
|
+
from runtime.types.run_types import StateSession
|
26
|
+
from runtime.types.run_types import StorageState
|
27
|
+
from runtime.types.run_types import TracingDisabled
|
28
|
+
|
29
|
+
|
30
|
+
@arguably.command # type: ignore
|
31
|
+
async def project__auth_session__check(
|
32
|
+
*,
|
33
|
+
no_headless: bool = False,
|
34
|
+
cdp_address: str | None = None,
|
35
|
+
auth_session_path: str,
|
36
|
+
auth_session_parameters: str | None = None,
|
37
|
+
):
|
38
|
+
"""
|
39
|
+
Check the auth session.
|
40
|
+
|
41
|
+
Args:
|
42
|
+
cdp_address (str): The CDP address of the browser to load the auth session to.
|
43
|
+
auth_session_path (str): Path to the auth session file.
|
44
|
+
no_headless (bool): Whether to run the browser in headless mode.
|
45
|
+
auth_session_parameters (str | None): JSON string with auth session parameters.
|
46
|
+
"""
|
47
|
+
intuned_settings = await load_intuned_settings()
|
48
|
+
if not intuned_settings.auth_sessions.enabled:
|
49
|
+
raise Exception("Auth sessions are not enabled")
|
50
|
+
|
51
|
+
if not os.path.exists(auth_session_path):
|
52
|
+
raise Exception("Auth session file does not exist")
|
53
|
+
|
54
|
+
with open(auth_session_path) as f:
|
55
|
+
try:
|
56
|
+
auth_session = StorageState(**json.load(f))
|
57
|
+
except (json.JSONDecodeError, TypeError) as e:
|
58
|
+
raise Exception("Auth session file is not a valid JSON file") from e
|
59
|
+
except pydantic.ValidationError as e:
|
60
|
+
raise Exception(f"Auth session file is not valid: {e}") from e
|
61
|
+
|
62
|
+
retry_configs = retry(stop=stop_after_attempt(2), retry=retry_if_not_result(lambda result: result is True))
|
63
|
+
|
64
|
+
def import_function(file_path: str, function_name: str | None = None):
|
65
|
+
return import_function_from_api_dir(
|
66
|
+
file_path=file_path,
|
67
|
+
automation_function_name=function_name,
|
68
|
+
base_dir=os.path.join(os.getcwd()),
|
69
|
+
)
|
70
|
+
|
71
|
+
async def get_auth_session_parameters() -> dict[str, Any]:
|
72
|
+
assert auth_session_parameters is not None
|
73
|
+
try:
|
74
|
+
return json.loads(auth_session_parameters)
|
75
|
+
except json.JSONDecodeError as e:
|
76
|
+
raise Exception("Auth session parameters are not a valid JSON string") from e
|
77
|
+
|
78
|
+
if auth_session_parameters is not None:
|
79
|
+
IntunedContext.current().get_auth_session_parameters = get_auth_session_parameters
|
80
|
+
|
81
|
+
try:
|
82
|
+
|
83
|
+
async def check_fn():
|
84
|
+
result = await run_api(
|
85
|
+
RunApiParameters(
|
86
|
+
automation_function=AutomationFunction(
|
87
|
+
name="auth-sessions/check",
|
88
|
+
params=None,
|
89
|
+
),
|
90
|
+
tracing=TracingDisabled(),
|
91
|
+
run_options=CDPRunOptions(
|
92
|
+
cdp_address=cdp_address,
|
93
|
+
)
|
94
|
+
if cdp_address is not None
|
95
|
+
else StandaloneRunOptions(headless=not no_headless),
|
96
|
+
auth=Auth(
|
97
|
+
run_check=False,
|
98
|
+
session=StateSession(
|
99
|
+
state=auth_session,
|
100
|
+
),
|
101
|
+
),
|
102
|
+
),
|
103
|
+
import_function=import_function,
|
104
|
+
)
|
105
|
+
check_result = result.result
|
106
|
+
return check_result is True
|
107
|
+
|
108
|
+
check_fn_with_retries = retry_configs(check_fn)
|
109
|
+
try:
|
110
|
+
result = await check_fn_with_retries()
|
111
|
+
except RetryError:
|
112
|
+
result = False
|
113
|
+
success = type(result) is bool and result
|
114
|
+
print(bold("Check result is"), bold(red(result)) if not success else bold(green(result)))
|
115
|
+
if not success:
|
116
|
+
raise Exception("Auth session check failed")
|
117
|
+
except RunApiError as e:
|
118
|
+
raise Exception(f"Error running auth session check: {e}") from e
|
@@ -0,0 +1,96 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
import arguably
|
6
|
+
|
7
|
+
from runtime.context.context import IntunedContext
|
8
|
+
from runtime.errors.run_api_errors import RunApiError
|
9
|
+
from runtime.run.intuned_settings import load_intuned_settings
|
10
|
+
from runtime.run.run_api import import_function_from_api_dir
|
11
|
+
from runtime.run.run_api import run_api
|
12
|
+
from runtime.types.run_types import AutomationFunction
|
13
|
+
from runtime.types.run_types import CDPRunOptions
|
14
|
+
from runtime.types.run_types import RunApiParameters
|
15
|
+
from runtime.types.run_types import StandaloneRunOptions
|
16
|
+
from runtime.types.run_types import TracingDisabled
|
17
|
+
|
18
|
+
|
19
|
+
@arguably.command # type: ignore
|
20
|
+
async def project__auth_session__create(
|
21
|
+
*,
|
22
|
+
no_headless: bool = False,
|
23
|
+
cdp_address: str | None = None,
|
24
|
+
input_file: str | None = None,
|
25
|
+
input_json: str | None = None,
|
26
|
+
output_path: str | None = None,
|
27
|
+
):
|
28
|
+
"""
|
29
|
+
Run auth session create
|
30
|
+
|
31
|
+
Args:
|
32
|
+
cdp_address (str): Browser CDP address
|
33
|
+
input_file (str | None, optional): Auth session create input file path.
|
34
|
+
input_json (str | None, optional): Auth session create input JSON string.
|
35
|
+
output_path (str | None, optional): Path to save the auth session. If not provided, the auth session will not be saved.
|
36
|
+
"""
|
37
|
+
|
38
|
+
input_data = None
|
39
|
+
if input_file:
|
40
|
+
with open(input_file) as f:
|
41
|
+
input_data = json.load(f)
|
42
|
+
elif input_json:
|
43
|
+
input_data = json.loads(input_json)
|
44
|
+
|
45
|
+
# Load the intuned settings
|
46
|
+
intuned_settings = await load_intuned_settings()
|
47
|
+
if not intuned_settings.auth_sessions.enabled:
|
48
|
+
raise Exception("Auth sessions are not enabled")
|
49
|
+
|
50
|
+
def import_function(file_path: str, function_name: str | None = None):
|
51
|
+
return import_function_from_api_dir(
|
52
|
+
file_path=file_path,
|
53
|
+
automation_function_name=function_name,
|
54
|
+
base_dir=os.path.join(os.getcwd()),
|
55
|
+
)
|
56
|
+
|
57
|
+
async def get_auth_session_parameters() -> dict[str, Any]:
|
58
|
+
return input_data or dict[str, Any]()
|
59
|
+
|
60
|
+
IntunedContext.current().get_auth_session_parameters = get_auth_session_parameters
|
61
|
+
try:
|
62
|
+
result = await run_api(
|
63
|
+
RunApiParameters(
|
64
|
+
automation_function=AutomationFunction(
|
65
|
+
name="auth-sessions/create",
|
66
|
+
params=input_data,
|
67
|
+
),
|
68
|
+
tracing=TracingDisabled(),
|
69
|
+
run_options=CDPRunOptions(
|
70
|
+
cdp_address=cdp_address,
|
71
|
+
)
|
72
|
+
if cdp_address is not None
|
73
|
+
else StandaloneRunOptions(
|
74
|
+
headless=not no_headless,
|
75
|
+
),
|
76
|
+
retrieve_session=True,
|
77
|
+
),
|
78
|
+
import_function=import_function,
|
79
|
+
)
|
80
|
+
session = result.session
|
81
|
+
if not session:
|
82
|
+
raise Exception("Could not capture auth session")
|
83
|
+
except RunApiError as e:
|
84
|
+
raise Exception(f"Error running auth session create: {e}") from e
|
85
|
+
|
86
|
+
if not output_path:
|
87
|
+
print("Output path not set, discarding auth session")
|
88
|
+
return
|
89
|
+
|
90
|
+
full_output_path = (
|
91
|
+
os.path.abspath(output_path) if os.path.isabs(output_path) else os.path.join(os.getcwd(), output_path)
|
92
|
+
)
|
93
|
+
|
94
|
+
os.makedirs(os.path.dirname(full_output_path), exist_ok=True)
|
95
|
+
with open(full_output_path, "w") as f:
|
96
|
+
json.dump(session.model_dump(by_alias=True), f, indent=2)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
|
4
|
+
import arguably
|
5
|
+
|
6
|
+
from runtime.browser import launch_chromium
|
7
|
+
from runtime.browser.storage_state import set_storage_state
|
8
|
+
from runtime.run.intuned_settings import load_intuned_settings
|
9
|
+
from runtime.types.run_types import StorageState
|
10
|
+
|
11
|
+
|
12
|
+
@arguably.command # type: ignore
|
13
|
+
async def project__auth_session__load(
|
14
|
+
*,
|
15
|
+
cdp_address: str,
|
16
|
+
auth_session_path: str,
|
17
|
+
):
|
18
|
+
"""
|
19
|
+
Load an auth session to a browser.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
cdp_address (str): The CDP address of the browser to load the auth session to.
|
23
|
+
auth_session_path (str): Path to the auth session file.
|
24
|
+
"""
|
25
|
+
intuned_settings = await load_intuned_settings()
|
26
|
+
if not intuned_settings.auth_sessions.enabled:
|
27
|
+
raise Exception("Auth sessions are not enabled")
|
28
|
+
|
29
|
+
async with launch_chromium(
|
30
|
+
cdp_address=cdp_address,
|
31
|
+
) as (context, _):
|
32
|
+
auth_session_path = os.path.join(os.getcwd(), auth_session_path)
|
33
|
+
if not os.path.exists(auth_session_path):
|
34
|
+
raise Exception("Auth session file does not exist")
|
35
|
+
|
36
|
+
with open(auth_session_path) as f:
|
37
|
+
auth_session = StorageState(**json.load(f))
|
38
|
+
|
39
|
+
await set_storage_state(context, auth_session)
|