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.
Files changed (58) hide show
  1. cli/__init__.py +45 -0
  2. cli/commands/__init__.py +25 -0
  3. cli/commands/ai_source/__init__.py +4 -0
  4. cli/commands/ai_source/ai_source.py +10 -0
  5. cli/commands/ai_source/deploy.py +64 -0
  6. cli/commands/browser/__init__.py +3 -0
  7. cli/commands/browser/save_state.py +32 -0
  8. cli/commands/init.py +127 -0
  9. cli/commands/project/__init__.py +20 -0
  10. cli/commands/project/auth_session/__init__.py +5 -0
  11. cli/commands/project/auth_session/check.py +118 -0
  12. cli/commands/project/auth_session/create.py +96 -0
  13. cli/commands/project/auth_session/load.py +39 -0
  14. cli/commands/project/project.py +10 -0
  15. cli/commands/project/run.py +340 -0
  16. cli/commands/project/run_interface.py +265 -0
  17. cli/commands/project/type_check.py +86 -0
  18. cli/commands/project/upgrade.py +92 -0
  19. cli/commands/publish_packages.py +264 -0
  20. cli/logger.py +19 -0
  21. cli/utils/ai_source_project.py +31 -0
  22. cli/utils/code_tree.py +83 -0
  23. cli/utils/run_apis.py +147 -0
  24. cli/utils/unix_socket.py +55 -0
  25. intuned_runtime-1.0.0.dist-info/LICENSE +42 -0
  26. intuned_runtime-1.0.0.dist-info/METADATA +113 -0
  27. intuned_runtime-1.0.0.dist-info/RECORD +58 -0
  28. intuned_runtime-1.0.0.dist-info/WHEEL +4 -0
  29. intuned_runtime-1.0.0.dist-info/entry_points.txt +3 -0
  30. runtime/__init__.py +3 -0
  31. runtime/backend_functions/__init__.py +5 -0
  32. runtime/backend_functions/_call_backend_function.py +86 -0
  33. runtime/backend_functions/get_auth_session_parameters.py +30 -0
  34. runtime/browser/__init__.py +3 -0
  35. runtime/browser/launch_chromium.py +212 -0
  36. runtime/browser/storage_state.py +106 -0
  37. runtime/context/__init__.py +5 -0
  38. runtime/context/context.py +51 -0
  39. runtime/env.py +13 -0
  40. runtime/errors/__init__.py +21 -0
  41. runtime/errors/auth_session_errors.py +9 -0
  42. runtime/errors/run_api_errors.py +120 -0
  43. runtime/errors/trace_errors.py +3 -0
  44. runtime/helpers/__init__.py +5 -0
  45. runtime/helpers/extend_payload.py +9 -0
  46. runtime/helpers/extend_timeout.py +13 -0
  47. runtime/helpers/get_auth_session_parameters.py +14 -0
  48. runtime/py.typed +0 -0
  49. runtime/run/__init__.py +3 -0
  50. runtime/run/intuned_settings.py +38 -0
  51. runtime/run/playwright_constructs.py +19 -0
  52. runtime/run/run_api.py +233 -0
  53. runtime/run/traces.py +36 -0
  54. runtime/types/__init__.py +15 -0
  55. runtime/types/payload.py +7 -0
  56. runtime/types/run_types.py +177 -0
  57. runtime_helpers/__init__.py +5 -0
  58. 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"]
@@ -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,4 @@
1
+ from .ai_source import ai_source # type: ignore
2
+ from .deploy import ai_source__deploy # type: ignore
3
+
4
+ __all__ = ["ai_source", "ai_source__deploy"]
@@ -0,0 +1,10 @@
1
+ import arguably
2
+
3
+
4
+ @arguably.command # type: ignore
5
+ def ai_source():
6
+ """
7
+ Commands to run on AI source projects.
8
+ """
9
+
10
+ pass
@@ -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,3 @@
1
+ from .save_state import browser__save_state # type: ignore
2
+
3
+ __all__ = ["browser__save_state"]
@@ -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)
@@ -0,0 +1,10 @@
1
+ import arguably
2
+
3
+
4
+ @arguably.command # type: ignore
5
+ def project():
6
+ """
7
+ Commands to run on automation projects.
8
+ """
9
+
10
+ pass