intuned-runtime 1.3.1__py3-none-any.whl → 1.3.3__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.

Potentially problematic release.


This version of intuned-runtime might be problematic. Click here for more details.

Files changed (69) hide show
  1. intuned_cli/__init__.py +15 -24
  2. intuned_cli/commands/__init__.py +5 -0
  3. intuned_cli/commands/attempt_api_command.py +8 -2
  4. intuned_cli/commands/attempt_authsession_check_command.py +8 -2
  5. intuned_cli/commands/attempt_authsession_command.py +4 -7
  6. intuned_cli/commands/attempt_authsession_create_command.py +9 -3
  7. intuned_cli/commands/attempt_command.py +4 -7
  8. intuned_cli/commands/authsession_command.py +9 -0
  9. intuned_cli/commands/authsession_record_command.py +52 -0
  10. intuned_cli/commands/command.py +6 -7
  11. intuned_cli/commands/deploy_command.py +2 -3
  12. intuned_cli/commands/init_command.py +2 -3
  13. intuned_cli/commands/run_api_command.py +9 -3
  14. intuned_cli/commands/run_authsession_command.py +4 -7
  15. intuned_cli/commands/run_authsession_create_command.py +32 -5
  16. intuned_cli/commands/run_authsession_update_command.py +31 -5
  17. intuned_cli/commands/run_authsession_validate_command.py +30 -4
  18. intuned_cli/commands/run_command.py +4 -7
  19. intuned_cli/commands/save_command.py +2 -3
  20. intuned_cli/controller/__test__/test_api.py +159 -18
  21. intuned_cli/controller/__test__/test_authsession.py +497 -6
  22. intuned_cli/controller/api.py +40 -39
  23. intuned_cli/controller/authsession.py +213 -110
  24. intuned_cli/controller/deploy.py +3 -5
  25. intuned_cli/controller/save.py +47 -66
  26. intuned_cli/types.py +14 -0
  27. intuned_cli/utils/__test__/test_browser.py +132 -0
  28. intuned_cli/utils/__test__/test_traces.py +27 -0
  29. intuned_cli/utils/api_helpers.py +54 -5
  30. intuned_cli/utils/auth_session_helpers.py +42 -7
  31. intuned_cli/utils/backend.py +4 -1
  32. intuned_cli/utils/browser.py +63 -0
  33. intuned_cli/utils/error.py +14 -0
  34. intuned_cli/utils/exclusions.py +1 -0
  35. intuned_cli/utils/help.py +9 -0
  36. intuned_cli/utils/traces.py +31 -0
  37. intuned_cli/utils/wrapper.py +68 -0
  38. intuned_internal_cli/__init__.py +7 -0
  39. intuned_internal_cli/commands/__init__.py +8 -16
  40. intuned_internal_cli/commands/browser/__init__.py +1 -1
  41. intuned_internal_cli/commands/browser/save_state.py +2 -3
  42. intuned_internal_cli/commands/project/__init__.py +7 -9
  43. intuned_internal_cli/commands/project/auth_session/__init__.py +3 -3
  44. intuned_internal_cli/commands/project/auth_session/check.py +2 -2
  45. intuned_internal_cli/commands/project/auth_session/create.py +2 -3
  46. intuned_internal_cli/commands/project/auth_session/load.py +2 -3
  47. intuned_internal_cli/commands/project/project.py +2 -2
  48. intuned_internal_cli/commands/project/run.py +2 -2
  49. intuned_internal_cli/commands/project/run_interface.py +75 -8
  50. intuned_internal_cli/commands/project/type_check.py +5 -5
  51. intuned_internal_cli/commands/root.py +2 -1
  52. intuned_internal_cli/utils/wrapper.py +15 -0
  53. {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.3.dist-info}/METADATA +4 -2
  54. intuned_runtime-1.3.3.dist-info/RECORD +115 -0
  55. runtime/browser/launch_browser.py +15 -0
  56. runtime/browser/launch_chromium.py +14 -1
  57. runtime/run/types.py +0 -5
  58. runtime/types/settings_types.py +13 -4
  59. runtime/utils/anyio.py +26 -0
  60. intuned_internal_cli/commands/ai_source/__init__.py +0 -4
  61. intuned_internal_cli/commands/ai_source/ai_source.py +0 -10
  62. intuned_internal_cli/commands/ai_source/deploy.py +0 -64
  63. intuned_internal_cli/commands/init.py +0 -127
  64. intuned_internal_cli/commands/project/upgrade.py +0 -92
  65. intuned_internal_cli/commands/publish_packages.py +0 -264
  66. intuned_runtime-1.3.1.dist-info/RECORD +0 -111
  67. {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.3.dist-info}/WHEEL +0 -0
  68. {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.3.dist-info}/entry_points.txt +0 -0
  69. {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,31 @@
1
+ from contextlib import contextmanager
2
+ from datetime import datetime
3
+ from pathlib import Path
4
+
5
+ from intuned_cli.utils.console import console
6
+ from runtime.types.run_types import TracingDisabled
7
+ from runtime.types.run_types import TracingEnabled
8
+
9
+ _trace_dir_name = datetime.now().isoformat()
10
+
11
+ _count = 0
12
+
13
+
14
+ def get_trace_path(id: str):
15
+ global _count
16
+ _count += 1
17
+ return Path() / "traces" / _trace_dir_name / f"{_count}-{id}.zip"
18
+
19
+
20
+ @contextmanager
21
+ def cli_trace(id: str | None):
22
+ if not id:
23
+ yield TracingDisabled()
24
+ return
25
+
26
+ trace_path = get_trace_path(id)
27
+ try:
28
+ yield TracingEnabled(file_path=str(trace_path))
29
+ finally:
30
+ if trace_path.exists():
31
+ console.print(f"[bold]Trace saved to [/bold][underline]{str(trace_path)}[/underline]")
@@ -0,0 +1,68 @@
1
+ import asyncio
2
+ from collections.abc import Awaitable
3
+ from collections.abc import Callable
4
+ from functools import wraps
5
+ from typing import Any
6
+ from typing import cast
7
+ from typing import ParamSpec
8
+ from typing import TypeVar
9
+
10
+ import arguably
11
+
12
+ from intuned_cli.utils.browser import close_cli_browser
13
+ from intuned_cli.utils.browser import is_cli_browser_launched
14
+ from intuned_cli.utils.console import console
15
+ from intuned_cli.utils.error import CLIError
16
+ from intuned_cli.utils.error import CLIExit
17
+ from intuned_cli.utils.error import log_automation_error
18
+ from runtime.errors.run_api_errors import RunApiError
19
+ from runtime.utils.anyio import run_sync
20
+
21
+ P = ParamSpec("P")
22
+ R = TypeVar("R")
23
+
24
+
25
+ def cli_command(fn: Callable[P, Awaitable[R]]) -> Callable[P, R]:
26
+ @arguably.command # type: ignore
27
+ @wraps(fn)
28
+ @run_sync
29
+ async def wrapper(*args: Any, **kwargs: Any) -> R:
30
+ keep_open = kwargs.get("keep_browser_open", False)
31
+ if keep_open:
32
+ console.print(
33
+ "[bold]--keep-browser-open is set, the CLI will not close the last browser after the command completes.[/bold]"
34
+ )
35
+ try:
36
+ result = await fn(*args, **kwargs)
37
+ return result
38
+ except CLIError as e:
39
+ if e.auto_color:
40
+ console.print(f"[bold red]{e.message}[/bold red]")
41
+ else:
42
+ console.print(e.message)
43
+ raise CLIExit(1) from e
44
+ except RunApiError as e:
45
+ log_automation_error(e)
46
+ raise CLIExit(1) from e
47
+ except KeyboardInterrupt:
48
+ console.print("[bold red]Aborted[/bold red]")
49
+ raise CLIExit(1) from None
50
+ except Exception as e:
51
+ console.print(
52
+ f"[red][bold]An error occurred: [/bold]{e}\n[bold]Please report this issue to the Intuned team.[/bold]"
53
+ )
54
+ raise CLIExit(1) from e
55
+ finally:
56
+ if keep_open:
57
+ await _wait_for_user_input()
58
+ await close_cli_browser()
59
+
60
+ return cast(Callable[P, R], wrapper)
61
+
62
+
63
+ async def _wait_for_user_input():
64
+ if not is_cli_browser_launched():
65
+ return
66
+ if not console.is_terminal:
67
+ return
68
+ await asyncio.to_thread(console.input, "Press Enter to continue...")
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import sys
2
3
  import traceback
3
4
 
@@ -12,6 +13,12 @@ from runtime.context.context import IntunedContext
12
13
 
13
14
  from . import commands
14
15
 
16
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
17
+
18
+ logging.getLogger("runtime").setLevel(logging.INFO)
19
+ logging.getLogger("intuned_runtime").setLevel(logging.INFO)
20
+ logging.getLogger("intuned_browser").setLevel(logging.INFO)
21
+
15
22
 
16
23
  def run():
17
24
  dotenv = find_dotenv(usecwd=True)
@@ -1,23 +1,15 @@
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
- from .root import __root__ # type: ignore
1
+ from .browser import browser__save_state
2
+ from .project import project
3
+ from .project import project__run
4
+ from .project import project__type_check
5
+ from .project.auth_session import project__auth_session__check
6
+ from .project.auth_session import project__auth_session__create
7
+ from .project.auth_session import project__auth_session__load
8
+ from .root import __root__
13
9
 
14
10
  __all__ = [
15
11
  "project__run",
16
- "publish_packages",
17
- "init",
18
12
  "project",
19
- "ai_source__deploy",
20
- "ai_source",
21
13
  "project__auth_session__load",
22
14
  "project__auth_session__create",
23
15
  "project__auth_session__check",
@@ -1,3 +1,3 @@
1
- from .save_state import browser__save_state # type: ignore
1
+ from .save_state import browser__save_state
2
2
 
3
3
  __all__ = ["browser__save_state"]
@@ -1,13 +1,12 @@
1
1
  import json
2
2
  import os
3
3
 
4
- import arguably
5
-
4
+ from intuned_internal_cli.utils.wrapper import internal_cli_command
6
5
  from runtime.browser import launch_browser
7
6
  from runtime.browser.storage_state import get_storage_state
8
7
 
9
8
 
10
- @arguably.command # type: ignore
9
+ @internal_cli_command
11
10
  async def browser__save_state(
12
11
  *,
13
12
  cdp_address: str,
@@ -1,11 +1,10 @@
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
1
+ from .auth_session import project__auth_session__check
2
+ from .auth_session import project__auth_session__create
3
+ from .auth_session import project__auth_session__load
4
+ from .project import project
5
+ from .run import project__run
6
+ from .run_interface import project__run_interface
7
+ from .type_check import project__type_check
9
8
 
10
9
  __all__ = [
11
10
  "run",
@@ -15,6 +14,5 @@ __all__ = [
15
14
  "project__auth_session__load",
16
15
  "project__auth_session__create",
17
16
  "project__auth_session__check",
18
- "project__upgrade",
19
17
  "project__run_interface",
20
18
  ]
@@ -1,5 +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
1
+ from .check import project__auth_session__check
2
+ from .create import project__auth_session__create
3
+ from .load import project__auth_session__load
4
4
 
5
5
  __all__ = ["project__auth_session__check", "project__auth_session__create", "project__auth_session__load"]
@@ -2,7 +2,6 @@ import json
2
2
  import os
3
3
  from typing import Any
4
4
 
5
- import arguably
6
5
  import pydantic # type: ignore
7
6
  from more_termcolor import bold # type: ignore
8
7
  from more_termcolor import green # type: ignore
@@ -12,6 +11,7 @@ from tenacity import retry_if_not_result
12
11
  from tenacity import RetryError
13
12
  from tenacity import stop_after_attempt
14
13
 
14
+ from intuned_internal_cli.utils.wrapper import internal_cli_command
15
15
  from runtime.context.context import IntunedContext
16
16
  from runtime.errors.run_api_errors import RunApiError
17
17
  from runtime.run.intuned_settings import load_intuned_settings
@@ -27,7 +27,7 @@ from runtime.types.run_types import StorageState
27
27
  from runtime.types.run_types import TracingDisabled
28
28
 
29
29
 
30
- @arguably.command # type: ignore
30
+ @internal_cli_command
31
31
  async def project__auth_session__check(
32
32
  *,
33
33
  no_headless: bool = False,
@@ -2,8 +2,7 @@ import json
2
2
  import os
3
3
  from typing import Any
4
4
 
5
- import arguably
6
-
5
+ from intuned_internal_cli.utils.wrapper import internal_cli_command
7
6
  from runtime.context.context import IntunedContext
8
7
  from runtime.errors.run_api_errors import RunApiError
9
8
  from runtime.run.intuned_settings import load_intuned_settings
@@ -16,7 +15,7 @@ from runtime.types.run_types import StandaloneRunOptions
16
15
  from runtime.types.run_types import TracingDisabled
17
16
 
18
17
 
19
- @arguably.command # type: ignore
18
+ @internal_cli_command
20
19
  async def project__auth_session__create(
21
20
  *,
22
21
  no_headless: bool = False,
@@ -1,15 +1,14 @@
1
1
  import json
2
2
  import os
3
3
 
4
- import arguably
5
-
4
+ from intuned_internal_cli.utils.wrapper import internal_cli_command
6
5
  from runtime.browser import launch_browser
7
6
  from runtime.browser.storage_state import set_storage_state
8
7
  from runtime.run.intuned_settings import load_intuned_settings
9
8
  from runtime.types.run_types import StorageState
10
9
 
11
10
 
12
- @arguably.command # type: ignore
11
+ @internal_cli_command
13
12
  async def project__auth_session__load(
14
13
  *,
15
14
  cdp_address: str,
@@ -1,7 +1,7 @@
1
- import arguably
1
+ from intuned_internal_cli.utils.wrapper import internal_cli_command
2
2
 
3
3
 
4
- @arguably.command # type: ignore
4
+ @internal_cli_command
5
5
  def project():
6
6
  """
7
7
  Commands to run on automation projects.
@@ -10,7 +10,6 @@ from importlib import import_module
10
10
  from typing import Any
11
11
  from typing import cast
12
12
 
13
- import arguably
14
13
  from dotenv import find_dotenv
15
14
  from dotenv import load_dotenv
16
15
  from more_termcolor import bold # type: ignore
@@ -21,6 +20,7 @@ from more_termcolor import on_blue # type: ignore
21
20
  from more_termcolor import underline # type: ignore
22
21
  from more_termcolor import yellow # type: ignore
23
22
 
23
+ from intuned_internal_cli.utils.wrapper import internal_cli_command
24
24
  from runtime.run.intuned_settings import load_intuned_settings
25
25
  from runtime.types import Payload
26
26
  from runtime.types.run_types import Auth
@@ -38,7 +38,7 @@ class Mode(StrEnum):
38
38
  ide = "ide"
39
39
 
40
40
 
41
- @arguably.command # type: ignore
41
+ @internal_cli_command
42
42
  async def project__run(
43
43
  *,
44
44
  api_name: str = "default",
@@ -2,18 +2,21 @@ import asyncio
2
2
  import os
3
3
  import signal
4
4
  import socket
5
+ import sys
5
6
  import time
6
7
  from collections.abc import AsyncGenerator
8
+ from pathlib import Path
7
9
  from typing import Any
8
10
  from typing import cast
9
11
  from typing import Literal
10
12
 
11
- import arguably
12
13
  from pydantic import Field
13
14
  from pydantic import ValidationError
14
15
 
16
+ from intuned_internal_cli.utils.wrapper import internal_cli_command
15
17
  from runtime.backend_functions import get_auth_session_parameters
16
18
  from runtime.context import IntunedContext
19
+ from runtime.errors.run_api_errors import ApiNotFoundError
17
20
  from runtime.errors.run_api_errors import InternalInvalidInputError
18
21
  from runtime.errors.run_api_errors import RunApiError
19
22
  from runtime.run.run_api import import_function_from_api_dir
@@ -69,15 +72,18 @@ class MessageWrapper(CamelBaseModel):
69
72
  )
70
73
 
71
74
 
72
- @arguably.command # type: ignore
75
+ @internal_cli_command
73
76
  async def project__run_interface(
74
77
  socket_path: str,
78
+ *,
79
+ jsonl: bool = False,
75
80
  ):
76
81
  """
77
82
  Runs the current project. Project must contain an "api" directory with API functions.
78
83
 
79
84
  Args:
80
85
  socket_path (str): Path to the socket file.
86
+ jsonl (bool, optional): Use a JSONL client instead of socket. Defaults to False.
81
87
 
82
88
  """
83
89
 
@@ -86,7 +92,7 @@ async def project__run_interface(
86
92
  raise Exception("socket_path is required")
87
93
 
88
94
  timeout_timestamp = time.time()
89
- client = UDASClient(socket_path)
95
+ client = SocketClient(socket_path) if not jsonl else JSONLFileClient(socket_path)
90
96
  connected = await client.connect()
91
97
  if not connected:
92
98
  raise Exception("Failed to connect to UDAS")
@@ -97,7 +103,9 @@ async def project__run_interface(
97
103
 
98
104
  def done(exitCode: int = 0):
99
105
  client.close()
100
- exit(exitCode)
106
+ loop.remove_signal_handler(signal.SIGTERM)
107
+ loop.remove_signal_handler(signal.SIGINT)
108
+ sys.exit(exitCode)
101
109
 
102
110
  def interrupt_signal_handler():
103
111
  async def _impl():
@@ -133,6 +141,11 @@ async def project__run_interface(
133
141
  asyncio.Task[RunAutomationSuccessResult] | None, None
134
142
  )
135
143
 
144
+ def import_function(file_path: str, automation_name: str | None = None):
145
+ return import_function_from_api_dir(
146
+ automation_function_name=automation_name, file_path=file_path, base_dir=os.getcwd()
147
+ )
148
+
136
149
  async def handle_message(message: Message):
137
150
  nonlocal run_api_task
138
151
  if message.type == "start":
@@ -151,9 +164,7 @@ async def project__run_interface(
151
164
  run_api_task = asyncio.create_task(
152
165
  run_api(
153
166
  message.parameters,
154
- import_function=lambda file_path, automation_name=None: import_function_from_api_dir(
155
- automation_function_name=automation_name, file_path=file_path, base_dir=os.getcwd()
156
- ),
167
+ import_function=import_function,
157
168
  ),
158
169
  )
159
170
  return
@@ -191,6 +202,14 @@ async def project__run_interface(
191
202
  await client.send_message({"type": "done", "success": False, "result": message.json})
192
203
  break
193
204
  if message.type == "ping":
205
+ api_files = await get_python_files_from_dir(Path() / "api")
206
+ apis = [f'api/{str(p.with_suffix("").as_posix())}' for p in api_files]
207
+
208
+ for api in [*apis, "auth-sessions/create", "auth-sessions/check"]:
209
+ try:
210
+ import_function(api)
211
+ except ApiNotFoundError:
212
+ pass
194
213
  await client.send_message({"type": "pong"})
195
214
  break
196
215
  await handle_message(message)
@@ -231,7 +250,7 @@ async def project__run_interface(
231
250
  done()
232
251
 
233
252
 
234
- class UDASClient:
253
+ class SocketClient:
235
254
  def __init__(self, socket_path: str):
236
255
  self.socket_path = socket_path
237
256
  self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
@@ -263,3 +282,51 @@ class UDASClient:
263
282
  return
264
283
  self.sock.shutdown(socket.SHUT_RDWR)
265
284
  self.sock.close()
285
+
286
+
287
+ class JSONLFileClient(SocketClient):
288
+ def __init__(self, socket_path: str):
289
+ self.socket_path = socket_path
290
+ self.fp = None
291
+
292
+ async def connect(self):
293
+ if not os.path.exists(self.socket_path):
294
+ return False
295
+ self.fp = open(self.socket_path, "r+b", buffering=0)
296
+ return True
297
+
298
+ def receive_messages(self) -> AsyncGenerator[dict[str, Any], None]:
299
+ import json
300
+
301
+ async def generator():
302
+ if self.fp is None:
303
+ raise Exception("Socket not connected")
304
+ while True:
305
+ line = self.fp.readline()
306
+ if not line:
307
+ break
308
+ yield json.loads(line.decode("utf-8"))
309
+
310
+ return generator()
311
+
312
+ async def send_message(self, message: dict[str, Any]):
313
+ print("Sending message", message)
314
+
315
+ def close(self):
316
+ if not self.fp:
317
+ return
318
+ self.fp.close()
319
+
320
+
321
+ async def get_python_files_from_dir(dir: Path) -> list[Path]:
322
+ """Get all Python files under a directory, returning relative paths."""
323
+ python_files: list[Path] = []
324
+
325
+ file_tree = await asyncio.to_thread(os.walk, dir)
326
+ for root, _, files in file_tree:
327
+ for file in files:
328
+ if file.endswith(".py"):
329
+ full_path = Path(root) / file
330
+ relative_path = full_path.relative_to(dir)
331
+ python_files.append(relative_path)
332
+ return python_files
@@ -2,15 +2,15 @@ import json
2
2
  import os
3
3
  import subprocess
4
4
  import sys
5
+ from typing import Any
5
6
 
6
- import arguably
7
+ from intuned_internal_cli.utils.wrapper import internal_cli_command
7
8
 
8
9
  current_dir = os.path.dirname(os.path.abspath(__file__))
9
- PYRIGHT_CONFIG_PATH = os.path.join(current_dir, "..", "..", "pyright_type_check.json")
10
- PYRIGHT_CONFIG_PATH = os.path.abspath(PYRIGHT_CONFIG_PATH)
10
+ PYRIGHT_CONFIG_PATH = os.path.abspath(os.path.join(current_dir, "..", "..", "pyright_type_check.json"))
11
11
 
12
12
 
13
- @arguably.command # type: ignore
13
+ @internal_cli_command
14
14
  async def project__type_check():
15
15
  """
16
16
  Run type checking on the API directory using pyright.
@@ -39,7 +39,7 @@ async def project__type_check():
39
39
  print("📦 Checking Types...")
40
40
 
41
41
  try:
42
- pyright_issues = []
42
+ pyright_issues: list[dict[str, Any]] = []
43
43
  pyright_result = subprocess.run(
44
44
  ["pyright", "--outputjson", project_dir, "--project", PYRIGHT_CONFIG_PATH],
45
45
  capture_output=True,
@@ -1,7 +1,8 @@
1
1
  import arguably
2
2
 
3
3
 
4
- @arguably.command # type: ignore
4
+ # @arguably.command # type: ignore
5
+ # @run_sync
5
6
  async def __root__():
6
7
  """Internal Intuned CLI.
7
8
 
@@ -0,0 +1,15 @@
1
+ from collections.abc import Awaitable
2
+ from collections.abc import Callable
3
+ from typing import ParamSpec
4
+ from typing import TypeVar
5
+
6
+ import arguably
7
+
8
+ from runtime.utils.anyio import run_sync
9
+
10
+ P = ParamSpec("P")
11
+ R = TypeVar("R")
12
+
13
+
14
+ def internal_cli_command(fn: Callable[P, R | Awaitable[R]]) -> Callable[P, R]:
15
+ return arguably.command(run_sync(fn)) # type: ignore
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: intuned-runtime
3
- Version: 1.3.1
4
- Summary: Runtime commands for Intuned platform Python scrapers
3
+ Version: 1.3.3
4
+ Summary: Runtime SDK that powers browser automation projects running on Intuned
5
5
  License: Elastic-2.0
6
6
  License-File: LICENSE
7
7
  Keywords: runtime,intuned
@@ -23,12 +23,14 @@ Requires-Dist: browserforge[all]
23
23
  Requires-Dist: camoufox[geoip] (>=0.4.11,<0.5.0)
24
24
  Requires-Dist: gitpython (>=3.1.43,<4.0.0)
25
25
  Requires-Dist: httpx (>=0.23.0,<1)
26
+ Requires-Dist: jsonc-parser (>=1.1.5,<2.0.0)
26
27
  Requires-Dist: more-termcolor (>=1.1.3,<2.0.0)
27
28
  Requires-Dist: pathspec (>=0.12.1,<0.13.0)
28
29
  Requires-Dist: pydantic (>=2.10.6,<3.0.0)
29
30
  Requires-Dist: pyright (>=1.1.387,<2.0.0)
30
31
  Requires-Dist: python-dotenv (==1.0.1)
31
32
  Requires-Dist: pytimeparse (>=1.1.8,<2.0.0)
33
+ Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
32
34
  Requires-Dist: requests (>=2.32.3,<3.0.0)
33
35
  Requires-Dist: rich (>=14.1.0,<15.0.0)
34
36
  Requires-Dist: ruff (>=0.7.2,<0.8.0)