intuned-runtime 1.2.1__tar.gz → 1.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/PKG-INFO +4 -2
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/__init__.py +12 -2
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/__init__.py +1 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/attempt_api_command.py +1 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/attempt_authsession_check_command.py +1 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/attempt_authsession_create_command.py +1 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/deploy_command.py +4 -4
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/run_api_command.py +1 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/run_authsession_create_command.py +1 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/run_authsession_update_command.py +1 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/run_authsession_validate_command.py +1 -1
- intuned_runtime-1.2.2/intuned_cli/commands/save_command.py +47 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/controller/__test__/test_api.py +0 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/controller/api.py +0 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/controller/authsession.py +0 -1
- intuned_runtime-1.2.2/intuned_cli/controller/deploy.py +150 -0
- intuned_runtime-1.2.1/intuned_cli/controller/deploy.py → intuned_runtime-1.2.2/intuned_cli/controller/save.py +46 -138
- intuned_runtime-1.2.2/intuned_cli/utils/backend.py +31 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/utils/error.py +7 -3
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/auth_session/check.py +0 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/run.py +0 -2
- {intuned_runtime-1.2.1/runtime_helpers → intuned_runtime-1.2.2/intuned_runtime}/__init__.py +2 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/pyproject.toml +4 -1
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/backend_functions/_call_backend_function.py +18 -2
- intuned_runtime-1.2.2/runtime/browser/helpers.py +43 -0
- intuned_runtime-1.2.2/runtime/browser/launch_browser.py +59 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/browser/launch_chromium.py +26 -22
- intuned_runtime-1.2.2/runtime/constants.py +1 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/context/context.py +1 -0
- intuned_runtime-1.2.2/runtime/env.py +31 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/errors/run_api_errors.py +8 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/helpers/__init__.py +2 -1
- intuned_runtime-1.2.2/runtime/helpers/attempt_store.py +14 -0
- intuned_runtime-1.2.2/runtime/run/playwright_context.py +147 -0
- intuned_runtime-1.2.2/runtime/run/playwright_tracing.py +27 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/run/run_api.py +71 -91
- intuned_runtime-1.2.2/runtime/run/setup_context_hook.py +40 -0
- intuned_runtime-1.2.2/runtime/run/types.py +14 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/types/run_types.py +0 -1
- {intuned_runtime-1.2.1/intuned_runtime → intuned_runtime-1.2.2/runtime_helpers}/__init__.py +2 -1
- intuned_runtime-1.2.1/intuned_cli/utils/backend.py +0 -5
- intuned_runtime-1.2.1/runtime/browser/helpers.py +0 -21
- intuned_runtime-1.2.1/runtime/browser/launch_browser.py +0 -31
- intuned_runtime-1.2.1/runtime/env.py +0 -17
- intuned_runtime-1.2.1/runtime/run/playwright_constructs.py +0 -20
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/LICENSE +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/README.md +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/attempt_authsession_command.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/attempt_command.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/command.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/init_command.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/run_authsession_command.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/commands/run_command.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/controller/__test__/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/controller/__test__/test_authsession.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/types.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/utils/api_helpers.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/utils/auth_session_helpers.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/utils/confirmation.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/utils/console.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/utils/exclusions.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/utils/get_auth_parameters.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/utils/import_function.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_cli/utils/timeout.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/ai_source/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/ai_source/ai_source.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/ai_source/deploy.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/browser/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/browser/save_state.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/init.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/auth_session/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/auth_session/create.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/auth_session/load.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/project.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/run_interface.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/type_check.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/project/upgrade.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/publish_packages.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/commands/root.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/logger.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/utils/ai_source_project.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/utils/code_tree.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/utils/run_apis.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/utils/setup_ide_functions_token.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/intuned_internal_cli/utils/unix_socket.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/backend_functions/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/backend_functions/get_auth_session_parameters.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/browser/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/browser/launch_camoufox.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/browser/storage_state.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/context/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/errors/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/errors/auth_session_errors.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/errors/trace_errors.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/helpers/extend_payload.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/helpers/extend_timeout.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/helpers/get_auth_session_parameters.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/py.typed +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/run/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/run/intuned_settings.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/run/pydantic_encoder.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/run/traces.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/types/__init__.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime/types/payload.py +0 -0
- {intuned_runtime-1.2.1 → intuned_runtime-1.2.2}/runtime_helpers/py.typed +0 -0
@@ -1,8 +1,9 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: intuned-runtime
|
3
|
-
Version: 1.2.
|
3
|
+
Version: 1.2.2
|
4
4
|
Summary: Runtime commands for Intuned platform Python scrapers
|
5
5
|
License: Elastic-2.0
|
6
|
+
License-File: LICENSE
|
6
7
|
Keywords: runtime,intuned
|
7
8
|
Author: Intuned Developers
|
8
9
|
Author-email: engineering@intunedhq.com
|
@@ -15,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.11
|
16
17
|
Classifier: Programming Language :: Python :: 3.12
|
17
18
|
Classifier: Programming Language :: Python :: 3.13
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
18
20
|
Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
|
19
21
|
Requires-Dist: arguably (>=1.3.0,<2.0.0)
|
20
22
|
Requires-Dist: browserforge[all]
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import os
|
1
2
|
import sys
|
2
3
|
|
3
4
|
import arguably
|
@@ -8,7 +9,7 @@ from intuned_cli.utils.console import console
|
|
8
9
|
from intuned_cli.utils.error import CLIError
|
9
10
|
from intuned_cli.utils.error import log_automation_error
|
10
11
|
from runtime.context.context import IntunedContext
|
11
|
-
from runtime.errors.run_api_errors import
|
12
|
+
from runtime.errors.run_api_errors import RunApiError
|
12
13
|
|
13
14
|
from . import commands
|
14
15
|
|
@@ -17,6 +18,15 @@ def run():
|
|
17
18
|
dotenv = find_dotenv(usecwd=True)
|
18
19
|
if dotenv:
|
19
20
|
load_dotenv(dotenv, override=True)
|
21
|
+
from runtime.env import cli_env_var_key
|
22
|
+
|
23
|
+
os.environ[cli_env_var_key] = "true"
|
24
|
+
os.environ["RUN_ENVIRONMENT"] = "AUTHORING"
|
25
|
+
|
26
|
+
if not os.environ.get("FUNCTIONS_DOMAIN"):
|
27
|
+
from intuned_cli.utils.backend import get_base_url
|
28
|
+
|
29
|
+
os.environ["FUNCTIONS_DOMAIN"] = get_base_url().replace("/$", "")
|
20
30
|
try:
|
21
31
|
with IntunedContext():
|
22
32
|
arguably.run(name="intuned")
|
@@ -26,7 +36,7 @@ def run():
|
|
26
36
|
console.print(f"[bold red]{e.message}[/bold red]")
|
27
37
|
else:
|
28
38
|
console.print(e.message)
|
29
|
-
except
|
39
|
+
except RunApiError as e:
|
30
40
|
log_automation_error(e)
|
31
41
|
except KeyboardInterrupt:
|
32
42
|
console.print("[bold red]Aborted[/bold red]")
|
@@ -16,3 +16,4 @@ from .run_authsession_create_command import run__authsession__create as run__aut
|
|
16
16
|
from .run_authsession_update_command import run__authsession__update as run__authsession__update # type: ignore
|
17
17
|
from .run_authsession_validate_command import run__authsession__validate as run__authsession__validate # type: ignore
|
18
18
|
from .run_command import run as run # type: ignore
|
19
|
+
from .save_command import save as save # type: ignore
|
@@ -25,7 +25,7 @@ async def attempt__api(
|
|
25
25
|
parameters (str): Path to JSON file containing API parameters or the parameters as a JSON string.
|
26
26
|
auth_session (str | None, optional): [-a/--auth-session]. ID of the auth session to use for the API. This is expected to be in ./auth-session-instances/<id>
|
27
27
|
proxy (str | None, optional): [--proxy]. Proxy URL to use. Defaults to None.
|
28
|
-
timeout (str, optional): [--timeout]. Timeout -
|
28
|
+
timeout (str, optional): [--timeout]. Timeout - seconds or pytimeparse-formatted string. Defaults to "10 min".
|
29
29
|
headless (bool, optional): [--headless]. Run the API in headless mode (default: False). This will not open a browser window.
|
30
30
|
output_file (str | None, optional): [-o/--output-file]. Output file path. Defaults to None.
|
31
31
|
"""
|
@@ -19,7 +19,7 @@ async def attempt__authsession__check(
|
|
19
19
|
Args:
|
20
20
|
id (str): ID of the auth session to check
|
21
21
|
proxy (str | None, optional): [--proxy]. Proxy URL to use for the auth session command. Defaults to None.
|
22
|
-
timeout (str, optional): [--timeout]. Timeout for the auth session command -
|
22
|
+
timeout (str, optional): [--timeout]. Timeout for the auth session command - seconds or pytimeparse-formatted string. Defaults to "10 min".
|
23
23
|
headless (bool, optional): [--headless]. Run the API in headless mode (default: False). This will not open a browser window.
|
24
24
|
"""
|
25
25
|
await assert_auth_enabled()
|
@@ -22,7 +22,7 @@ async def attempt__authsession__create(
|
|
22
22
|
parameters (str): Parameters for the auth session command
|
23
23
|
id (str | None, optional): [--id]. ID of the auth session to use for the command. Defaults to ./auth-session-instances/[current timestamp].json.
|
24
24
|
proxy (str | None, optional): [--proxy]. Proxy URL to use for the auth session command. Defaults to None.
|
25
|
-
timeout (str, optional): [--timeout]. Timeout for the auth session command -
|
25
|
+
timeout (str, optional): [--timeout]. Timeout for the auth session command - seconds or pytimeparse-formatted string. Defaults to "10 min".
|
26
26
|
headless (bool, optional): [--headless]. Run the API in headless mode (default: False). This will not open a browser window.
|
27
27
|
"""
|
28
28
|
await assert_auth_enabled()
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import arguably
|
2
2
|
|
3
3
|
from intuned_cli.controller.deploy import deploy_project
|
4
|
-
from intuned_cli.controller.
|
5
|
-
from intuned_cli.controller.
|
6
|
-
from intuned_cli.
|
4
|
+
from intuned_cli.controller.save import validate_intuned_project
|
5
|
+
from intuned_cli.controller.save import validate_project_name
|
6
|
+
from intuned_cli.utils.backend import get_intuned_api_auth_credentials
|
7
7
|
from intuned_cli.utils.error import CLIError
|
8
8
|
|
9
9
|
|
@@ -15,7 +15,7 @@ async def deploy(
|
|
15
15
|
workspace_id: str | None = None,
|
16
16
|
api_key: str | None = None,
|
17
17
|
):
|
18
|
-
"""
|
18
|
+
"""Saves and deploys the project to Intuned.
|
19
19
|
|
20
20
|
Args:
|
21
21
|
project_name (str | None, optional): The name of the project to deploy.
|
@@ -34,7 +34,7 @@ async def run__api(
|
|
34
34
|
no_auth_session_auto_recreate (bool, optional): [--no-auth-session-auto-recreate]. Disable auto recreate for auth session. Defaults to False.
|
35
35
|
auth_session_check_attempts (int, optional): [--auth-session-check-attempts]. Auth session check attempts. Defaults to 1.
|
36
36
|
auth_session_create_attempts (int, optional): [--auth-session-create-attempts]. Auth session create attempts. Defaults to 1.
|
37
|
-
timeout (str, optional): [--timeout]. Timeout -
|
37
|
+
timeout (str, optional): [--timeout]. Timeout - seconds or pytimeparse-formatted string. Defaults to "10 min".
|
38
38
|
headless (bool, optional): [--headless]. Run the API in headless mode (default: False). This will not open a browser window.
|
39
39
|
output_file (str | None, optional): [-o/--output-file]. Output file path. Defaults to None.
|
40
40
|
"""
|
@@ -26,7 +26,7 @@ async def run__authsession__create(
|
|
26
26
|
check_attempts (int, optional): [--check-attempts]. Number of attempts to check the auth session validity. Defaults to 1.
|
27
27
|
create_attempts (int, optional): [--create-attempts]. Number of attempts to create a new auth session if it is invalid. Defaults to 1.
|
28
28
|
proxy (str | None, optional): [--proxy]. Proxy URL to use for the auth session command. Defaults to None.
|
29
|
-
timeout (str, optional): [--timeout]. Timeout for the auth session command -
|
29
|
+
timeout (str, optional): [--timeout]. Timeout for the auth session command - seconds or pytimeparse-formatted string. Defaults to "10 min".
|
30
30
|
headless (bool, optional): [--headless]. Run the API in headless mode (default: False). This will not open a browser window.
|
31
31
|
"""
|
32
32
|
await assert_auth_enabled()
|
@@ -26,7 +26,7 @@ async def run__authsession__update(
|
|
26
26
|
check_attempts (int, optional): [--check-attempts]. Number of attempts to check the auth session validity. Defaults to 1.
|
27
27
|
create_attempts (int, optional): [--create-attempts]. Number of attempts to create a new auth session if it is invalid. Defaults to 1.
|
28
28
|
proxy (str | None, optional): [--proxy]. Proxy URL to use for the auth session command. Defaults to None.
|
29
|
-
timeout (str, optional): [--timeout]. Timeout for the auth session command -
|
29
|
+
timeout (str, optional): [--timeout]. Timeout for the auth session command - seconds or pytimeparse-formatted string. Defaults to "10 min".
|
30
30
|
headless (bool, optional): [--headless]. Run the API in headless mode (default: False). This will not open a browser window.
|
31
31
|
"""
|
32
32
|
await assert_auth_enabled()
|
@@ -24,7 +24,7 @@ async def run__authsession__validate(
|
|
24
24
|
check_attempts (int, optional): [--check-attempts]. Number of attempts to check the auth session validity. Defaults to 1.
|
25
25
|
create_attempts (int, optional): [--create-attempts]. Number of attempts to create a new auth session if it is invalid. Defaults to 1.
|
26
26
|
proxy (str | None, optional): [--proxy]. Proxy URL to use for the auth session command. Defaults to None.
|
27
|
-
timeout (str, optional): [--timeout]. Timeout for the auth session command -
|
27
|
+
timeout (str, optional): [--timeout]. Timeout for the auth session command - seconds or pytimeparse-formatted string. Defaults to "10 min".
|
28
28
|
no_auto_recreate (bool, optional): [--no-auto-recreate]. Disable auto recreation of the auth session if it is invalid. Defaults to False.
|
29
29
|
headless (bool, optional): [--headless]. Run the API in headless mode (default: False). This will not open a browser window.
|
30
30
|
"""
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import arguably
|
2
|
+
|
3
|
+
from intuned_cli.controller.save import save_project
|
4
|
+
from intuned_cli.controller.save import validate_intuned_project
|
5
|
+
from intuned_cli.controller.save import validate_project_name
|
6
|
+
from intuned_cli.utils.backend import get_intuned_api_auth_credentials
|
7
|
+
from intuned_cli.utils.error import CLIError
|
8
|
+
|
9
|
+
|
10
|
+
@arguably.command # type: ignore
|
11
|
+
async def save(
|
12
|
+
project_name: str | None = None,
|
13
|
+
/,
|
14
|
+
*,
|
15
|
+
workspace_id: str | None = None,
|
16
|
+
api_key: str | None = None,
|
17
|
+
):
|
18
|
+
"""Saves the project to Intuned without deploying it.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
project_name (str | None, optional): The name of the project to save.
|
22
|
+
workspace_id (str | None, optional): The ID of the workspace to save to.
|
23
|
+
api_key (str | None, optional): The API key to use for authentication.
|
24
|
+
"""
|
25
|
+
try:
|
26
|
+
intuned_json = await validate_intuned_project()
|
27
|
+
except CLIError as e:
|
28
|
+
raise CLIError(
|
29
|
+
f"[bold red]Project to be saved is not a valid Intuned project:[/bold red][bright_red] {e}[/bright_red]\n",
|
30
|
+
auto_color=False,
|
31
|
+
) from e
|
32
|
+
|
33
|
+
project_name = project_name or intuned_json.project_name
|
34
|
+
if not project_name:
|
35
|
+
raise CLIError("Project name is required")
|
36
|
+
|
37
|
+
validate_project_name(project_name)
|
38
|
+
|
39
|
+
workspace_id, api_key = await get_intuned_api_auth_credentials(
|
40
|
+
intuned_json=intuned_json, workspace_id=workspace_id, api_key=api_key
|
41
|
+
)
|
42
|
+
|
43
|
+
await save_project(
|
44
|
+
project_name=project_name,
|
45
|
+
workspace_id=workspace_id,
|
46
|
+
api_key=api_key,
|
47
|
+
)
|
@@ -187,7 +187,6 @@ class TestAttemptApi:
|
|
187
187
|
assert call_args.run_options.headless is False
|
188
188
|
assert call_args.run_options.proxy == proxy_config
|
189
189
|
assert call_args.auth.session.state is auth
|
190
|
-
assert call_args.auth.run_check is False
|
191
190
|
|
192
191
|
@pytest.mark.asyncio
|
193
192
|
async def test_attempt_api_returns_result_and_extended_payloads_if_run_api_succeeds(
|
@@ -0,0 +1,150 @@
|
|
1
|
+
import asyncio
|
2
|
+
import time
|
3
|
+
from itertools import cycle
|
4
|
+
from typing import Literal
|
5
|
+
|
6
|
+
import httpx
|
7
|
+
from pydantic import BaseModel
|
8
|
+
|
9
|
+
from intuned_cli.controller.save import save_project
|
10
|
+
from intuned_cli.utils.backend import get_base_url
|
11
|
+
from intuned_cli.utils.console import console
|
12
|
+
from intuned_cli.utils.error import CLIError
|
13
|
+
|
14
|
+
supported_playwright_versions = ["1.46.0", "1.52.0"]
|
15
|
+
|
16
|
+
project_deploy_timeout = 10 * 60
|
17
|
+
project_deploy_check_period = 5
|
18
|
+
|
19
|
+
|
20
|
+
class DeployStatus(BaseModel):
|
21
|
+
status: Literal["completed", "failed", "pending"]
|
22
|
+
message: str | None = None
|
23
|
+
reason: str | None = None
|
24
|
+
|
25
|
+
|
26
|
+
async def check_deploy_status(
|
27
|
+
*,
|
28
|
+
project_name: str,
|
29
|
+
workspace_id: str,
|
30
|
+
api_key: str,
|
31
|
+
):
|
32
|
+
base_url = get_base_url()
|
33
|
+
url = f"{base_url}/api/v1/workspace/{workspace_id}/projects/create/{project_name}/result"
|
34
|
+
|
35
|
+
headers = {
|
36
|
+
"x-api-key": api_key,
|
37
|
+
"Content-Type": "application/json",
|
38
|
+
}
|
39
|
+
async with httpx.AsyncClient() as client:
|
40
|
+
response = await client.get(url, headers=headers)
|
41
|
+
if response.status_code < 200 or response.status_code >= 300:
|
42
|
+
if response.status_code == 401:
|
43
|
+
raise CLIError("Invalid API key. Please check your API key and try again.")
|
44
|
+
if response.status_code == 404:
|
45
|
+
raise CLIError(f"Project '{project_name}' not found in workspace '{workspace_id}'.")
|
46
|
+
raise CLIError(f"Failed to check deploy status for project '{project_name}': {response.text}")
|
47
|
+
|
48
|
+
data = response.json()
|
49
|
+
try:
|
50
|
+
deploy_status = DeployStatus.model_validate(data)
|
51
|
+
except Exception as e:
|
52
|
+
raise CLIError(f"Failed to parse deploy status response: {e}") from e
|
53
|
+
|
54
|
+
return deploy_status
|
55
|
+
|
56
|
+
|
57
|
+
async def deploy_project(
|
58
|
+
*,
|
59
|
+
project_name: str,
|
60
|
+
workspace_id: str,
|
61
|
+
api_key: str,
|
62
|
+
):
|
63
|
+
await save_project(
|
64
|
+
project_name=project_name,
|
65
|
+
workspace_id=workspace_id,
|
66
|
+
api_key=api_key,
|
67
|
+
)
|
68
|
+
base_url = get_base_url()
|
69
|
+
url = f"{base_url}/api/v1/workspace/{workspace_id}/projects/{project_name}/deploy"
|
70
|
+
headers = {
|
71
|
+
"x-api-key": api_key,
|
72
|
+
"Content-Type": "application/json",
|
73
|
+
}
|
74
|
+
|
75
|
+
async with httpx.AsyncClient() as client:
|
76
|
+
response = await client.post(url, headers=headers)
|
77
|
+
if response.status_code < 200 or response.status_code >= 300:
|
78
|
+
if response.status_code == 401:
|
79
|
+
raise CLIError("Invalid API key. Please check your API key and try again.")
|
80
|
+
|
81
|
+
raise CLIError(
|
82
|
+
f"[red bold]Invalid response from server:[/red bold]\n [bright_red]{response.status_code} {response.text}[/bright_red][red bold]\nProject deployment failed.[/red bold]"
|
83
|
+
)
|
84
|
+
|
85
|
+
start_time = time.time()
|
86
|
+
|
87
|
+
async def update_console():
|
88
|
+
for spinner in cycle("⠙⠹⠸⠼⠴⠦⠧⠇"):
|
89
|
+
await asyncio.sleep(0.05)
|
90
|
+
|
91
|
+
time_elapsed_text = f"{time.time() - start_time:.1f}"
|
92
|
+
print("\r", end="", flush=True)
|
93
|
+
console.print(
|
94
|
+
f"{spinner} [cyan]Deploying[/cyan] [bright_black]({time_elapsed_text}s)[/bright_black] ", end=""
|
95
|
+
)
|
96
|
+
|
97
|
+
if console.is_terminal:
|
98
|
+
update_console_task = asyncio.create_task(update_console())
|
99
|
+
else:
|
100
|
+
update_console_task = None
|
101
|
+
console.print("[cyan]Deploying[/cyan]")
|
102
|
+
|
103
|
+
try:
|
104
|
+
while True:
|
105
|
+
await asyncio.sleep(project_deploy_check_period)
|
106
|
+
if not console.is_terminal:
|
107
|
+
time_elapsed_text = f"{time.time() - start_time:.1f}"
|
108
|
+
console.print(f"[cyan]Deploying[/cyan] [bright_black]({time_elapsed_text}s)[/bright_black]")
|
109
|
+
|
110
|
+
try:
|
111
|
+
deploy_status = await check_deploy_status(
|
112
|
+
project_name=project_name,
|
113
|
+
workspace_id=workspace_id,
|
114
|
+
api_key=api_key,
|
115
|
+
)
|
116
|
+
|
117
|
+
if deploy_status.status == "pending":
|
118
|
+
elapsed_time = time.time() - start_time
|
119
|
+
if elapsed_time > project_deploy_timeout:
|
120
|
+
raise CLIError(f"Deployment timed out after {project_deploy_timeout//60} minutes.")
|
121
|
+
continue
|
122
|
+
|
123
|
+
if deploy_status.status == "completed":
|
124
|
+
if update_console_task:
|
125
|
+
update_console_task.cancel()
|
126
|
+
if console.is_terminal:
|
127
|
+
print("\r", " " * 100)
|
128
|
+
console.print("[green][bold]Project deployed successfully![/bold][/green]")
|
129
|
+
console.print(
|
130
|
+
f"[bold]You can check your project on the platform:[/bold] [cyan underline]{get_base_url()}/projects/{project_name}/details[/cyan underline]"
|
131
|
+
)
|
132
|
+
return
|
133
|
+
|
134
|
+
error_message = (
|
135
|
+
f"[red bold]Project deployment failed:[/bold red]\n{deploy_status.message or 'Unknown error'}\n"
|
136
|
+
)
|
137
|
+
if deploy_status.reason:
|
138
|
+
error_message += f"Reason: {deploy_status.reason}\n"
|
139
|
+
error_message += "[red bold]Project deployment failed[/red bold]"
|
140
|
+
raise CLIError(
|
141
|
+
error_message,
|
142
|
+
auto_color=False,
|
143
|
+
)
|
144
|
+
except Exception:
|
145
|
+
if console.is_terminal:
|
146
|
+
print("\r", " " * 100)
|
147
|
+
raise
|
148
|
+
finally:
|
149
|
+
if update_console_task:
|
150
|
+
update_console_task.cancel()
|
@@ -1,35 +1,33 @@
|
|
1
|
-
import
|
1
|
+
import io
|
2
2
|
import json
|
3
|
-
import os
|
4
3
|
import re
|
5
|
-
import time
|
6
4
|
import uuid
|
7
|
-
from itertools import cycle
|
8
5
|
from typing import Any
|
9
|
-
from typing import Literal
|
10
6
|
|
11
7
|
import httpx
|
12
8
|
import pathspec
|
13
9
|
import toml
|
14
10
|
from anyio import Path
|
11
|
+
from dotenv.main import DotEnv
|
15
12
|
from pydantic import BaseModel
|
13
|
+
from pydantic import ValidationError
|
16
14
|
|
17
15
|
from intuned_cli.types import DirectoryNode
|
18
16
|
from intuned_cli.types import FileNode
|
19
17
|
from intuned_cli.types import FileNodeContent
|
20
18
|
from intuned_cli.types import FileSystemTree
|
21
|
-
from intuned_cli.types import IntunedJson
|
22
19
|
from intuned_cli.utils.api_helpers import load_intuned_json
|
23
20
|
from intuned_cli.utils.backend import get_base_url
|
24
21
|
from intuned_cli.utils.console import console
|
25
22
|
from intuned_cli.utils.error import CLIError
|
26
23
|
from intuned_cli.utils.exclusions import exclusions
|
24
|
+
from runtime.constants import api_key_header_name
|
25
|
+
from runtime.env import api_key_env_var_key
|
26
|
+
from runtime.env import project_env_var_key
|
27
|
+
from runtime.env import workspace_env_var_key
|
27
28
|
|
28
29
|
supported_playwright_versions = ["1.46.0", "1.52.0"]
|
29
30
|
|
30
|
-
project_deploy_timeout = 10 * 60
|
31
|
-
project_deploy_check_period = 5
|
32
|
-
|
33
31
|
|
34
32
|
class IntunedPyprojectToml(BaseModel):
|
35
33
|
class _Tool(BaseModel):
|
@@ -96,29 +94,6 @@ def validate_project_name(project_name: str):
|
|
96
94
|
pass
|
97
95
|
|
98
96
|
|
99
|
-
async def get_intuned_api_auth_credentials(
|
100
|
-
*, intuned_json: IntunedJson, workspace_id: str | None, api_key: str | None
|
101
|
-
) -> tuple[str, str]:
|
102
|
-
"""
|
103
|
-
Retrieves the Intuned API authentication credentials from environment variables.
|
104
|
-
|
105
|
-
Returns:
|
106
|
-
tuple: A tuple containing the workspace ID and API key.
|
107
|
-
"""
|
108
|
-
workspace_id = workspace_id or intuned_json.workspace_id
|
109
|
-
api_key = api_key or os.environ.get("INTUNED_API_KEY")
|
110
|
-
|
111
|
-
if not workspace_id:
|
112
|
-
raise CLIError("Workspace ID is required. Please provide it via command line options or Intuned.json")
|
113
|
-
|
114
|
-
if not api_key:
|
115
|
-
raise CLIError(
|
116
|
-
"API key is required. Please provide it via command line options or INTUNED_API_KEY environment variable."
|
117
|
-
)
|
118
|
-
|
119
|
-
return workspace_id, api_key
|
120
|
-
|
121
|
-
|
122
97
|
async def get_file_tree_from_project(path: Path, *, exclude: list[str] | None = None):
|
123
98
|
# Create pathspec object for gitignore-style pattern matching
|
124
99
|
spec = None
|
@@ -214,53 +189,21 @@ def mapFileTreeToIdeFileTree(file_tree: FileSystemTree):
|
|
214
189
|
file_tree.root["____testParameters"] = test_parameters
|
215
190
|
|
216
191
|
|
217
|
-
class
|
218
|
-
|
219
|
-
message: str | None = None
|
220
|
-
reason: str | None = None
|
221
|
-
|
222
|
-
|
223
|
-
async def check_deploy_status(
|
224
|
-
*,
|
225
|
-
project_name: str,
|
226
|
-
workspace_id: str,
|
227
|
-
api_key: str,
|
228
|
-
):
|
229
|
-
base_url = get_base_url()
|
230
|
-
url = f"{base_url}/api/v1/workspace/{workspace_id}/projects/create/{project_name}/result"
|
231
|
-
|
232
|
-
headers = {
|
233
|
-
"x-api-key": api_key,
|
234
|
-
"Content-Type": "application/json",
|
235
|
-
}
|
236
|
-
async with httpx.AsyncClient() as client:
|
237
|
-
response = await client.get(url, headers=headers)
|
238
|
-
if response.status_code < 200 or response.status_code >= 300:
|
239
|
-
if response.status_code == 401:
|
240
|
-
raise CLIError("Invalid API key. Please check your API key and try again.")
|
241
|
-
if response.status_code == 404:
|
242
|
-
raise CLIError(f"Project '{project_name}' not found in workspace '{workspace_id}'.")
|
243
|
-
raise CLIError(f"Failed to check deploy status for project '{project_name}': {response.text}")
|
244
|
-
|
245
|
-
data = response.json()
|
246
|
-
try:
|
247
|
-
deploy_status = DeployStatus.model_validate(data)
|
248
|
-
except Exception as e:
|
249
|
-
raise CLIError(f"Failed to parse deploy status response: {e}") from e
|
250
|
-
|
251
|
-
return deploy_status
|
192
|
+
class SaveProjectResponse(BaseModel):
|
193
|
+
id: str
|
252
194
|
|
253
195
|
|
254
|
-
async def
|
196
|
+
async def save_project(
|
255
197
|
*,
|
256
198
|
project_name: str,
|
257
199
|
workspace_id: str,
|
258
200
|
api_key: str,
|
259
201
|
):
|
260
202
|
base_url = get_base_url()
|
261
|
-
url = f"{base_url}/api/v1/workspace/{workspace_id}/projects/
|
203
|
+
url = f"{base_url}/api/v1/workspace/{workspace_id}/projects/{project_name}"
|
204
|
+
print(f"calling {url}")
|
262
205
|
headers = {
|
263
|
-
|
206
|
+
api_key_header_name: api_key,
|
264
207
|
"Content-Type": "application/json",
|
265
208
|
}
|
266
209
|
cwd = await Path().resolve()
|
@@ -268,85 +211,50 @@ async def deploy_project(
|
|
268
211
|
mapFileTreeToIdeFileTree(file_tree)
|
269
212
|
|
270
213
|
payload: dict[str, Any] = {
|
271
|
-
"name": project_name,
|
272
214
|
"codeTree": file_tree.model_dump(mode="json"),
|
273
|
-
"
|
215
|
+
"platformType": "CLI",
|
274
216
|
"language": "python",
|
275
217
|
}
|
276
218
|
|
277
219
|
async with httpx.AsyncClient() as client:
|
278
|
-
response = await client.
|
220
|
+
response = await client.put(url, headers=headers, json=payload)
|
279
221
|
if response.status_code < 200 or response.status_code >= 300:
|
280
222
|
if response.status_code == 401:
|
281
223
|
raise CLIError("Invalid API key. Please check your API key and try again.")
|
282
224
|
|
283
225
|
raise CLIError(
|
284
|
-
f"[red bold]Invalid response from server:[/red bold]\n [bright_red]{response.status_code} {response.text}[/bright_red][red bold]\nProject
|
226
|
+
f"[red bold]Invalid response from server:[/red bold]\n [bright_red]{response.status_code} {response.text}[/bright_red][red bold]\nProject save failed.[/red bold]"
|
285
227
|
)
|
286
228
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
time_elapsed_text = f"{time.time() - start_time:.1f}"
|
294
|
-
print("\r", end="", flush=True)
|
295
|
-
console.print(
|
296
|
-
f"{spinner} [cyan]Deploying[/cyan] [bright_black]({time_elapsed_text}s)[/bright_black] ", end=""
|
297
|
-
)
|
229
|
+
console.print("[green]Project saved successfully.[/green]")
|
230
|
+
try:
|
231
|
+
response = SaveProjectResponse.model_validate(response.json())
|
232
|
+
except ValidationError:
|
233
|
+
console.print(f"[yellow]Could not parse response:[/yellow]\n {response.text}")
|
234
|
+
return
|
298
235
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
236
|
+
dotenv_path = cwd / ".env"
|
237
|
+
if not await dotenv_path.exists():
|
238
|
+
content_to_write = f"""{workspace_env_var_key}={workspace_id}
|
239
|
+
{project_env_var_key}={response.id}
|
240
|
+
{api_key_env_var_key}={api_key}
|
241
|
+
"""
|
242
|
+
await dotenv_path.write_text(content_to_write)
|
243
|
+
console.print("[green]Created .env with project credentials.[/green]")
|
244
|
+
return
|
304
245
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
elapsed_time = time.time() - start_time
|
321
|
-
if elapsed_time > project_deploy_timeout:
|
322
|
-
raise CLIError(f"Deployment timed out after {project_deploy_timeout//60} minutes.")
|
323
|
-
continue
|
324
|
-
|
325
|
-
if deploy_status.status == "completed":
|
326
|
-
if update_console_task:
|
327
|
-
update_console_task.cancel()
|
328
|
-
if console.is_terminal:
|
329
|
-
print("\r", " " * 100)
|
330
|
-
console.print("[green][bold]Project deployed successfully![/bold][/green]")
|
331
|
-
console.print(
|
332
|
-
f"[bold]You can check your project on the platform:[/bold] [cyan underline]{get_base_url()}/projects/{project_name}/details[/cyan underline]"
|
333
|
-
)
|
334
|
-
return
|
335
|
-
|
336
|
-
error_message = (
|
337
|
-
f"[red bold]Project deployment failed:[/bold red]\n{deploy_status.message or 'Unknown error'}\n"
|
338
|
-
)
|
339
|
-
if deploy_status.reason:
|
340
|
-
error_message += f"Reason: {deploy_status.reason}\n"
|
341
|
-
error_message += "[red bold]Project deployment failed[/red bold]"
|
342
|
-
raise CLIError(
|
343
|
-
error_message,
|
344
|
-
auto_color=False,
|
345
|
-
)
|
346
|
-
except Exception:
|
347
|
-
if console.is_terminal:
|
348
|
-
print("\r", " " * 100)
|
349
|
-
raise
|
350
|
-
finally:
|
351
|
-
if update_console_task:
|
352
|
-
update_console_task.cancel()
|
246
|
+
dotenv_content = await dotenv_path.read_text()
|
247
|
+
dotenv = DotEnv(
|
248
|
+
dotenv_path=None,
|
249
|
+
stream=io.StringIO(dotenv_content),
|
250
|
+
).dict()
|
251
|
+
content_to_append = ""
|
252
|
+
if dotenv.get(project_env_var_key) is None or dotenv.get(project_env_var_key) != response.id:
|
253
|
+
content_to_append += f"{project_env_var_key}={response.id}"
|
254
|
+
if dotenv.get(workspace_env_var_key) is None:
|
255
|
+
content_to_append += f"\n{workspace_env_var_key}={workspace_id}"
|
256
|
+
if dotenv.get(api_key_env_var_key) is None:
|
257
|
+
content_to_append += f"\n{api_key_env_var_key}={api_key}"
|
258
|
+
|
259
|
+
await dotenv_path.write_text(f"{dotenv_content}\n{content_to_append}\n")
|
260
|
+
console.print("[green]Updated .env with project credentials.[/green]")
|