intuned-runtime 1.0.0__py3-none-any.whl → 1.1.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 (78) hide show
  1. intuned_cli/__init__.py +40 -0
  2. intuned_cli/commands/__init__.py +18 -0
  3. intuned_cli/commands/attempt_api_command.py +51 -0
  4. intuned_cli/commands/attempt_authsession_check_command.py +38 -0
  5. intuned_cli/commands/attempt_authsession_command.py +12 -0
  6. intuned_cli/commands/attempt_authsession_create_command.py +44 -0
  7. intuned_cli/commands/attempt_command.py +12 -0
  8. intuned_cli/commands/command.py +26 -0
  9. intuned_cli/commands/deploy_command.py +47 -0
  10. intuned_cli/commands/init_command.py +21 -0
  11. intuned_cli/commands/run_api_command.py +69 -0
  12. intuned_cli/commands/run_authsession_command.py +12 -0
  13. intuned_cli/commands/run_authsession_create_command.py +50 -0
  14. intuned_cli/commands/run_authsession_update_command.py +52 -0
  15. intuned_cli/commands/run_authsession_validate_command.py +49 -0
  16. intuned_cli/commands/run_command.py +12 -0
  17. intuned_cli/constants/__init__.py +1 -0
  18. intuned_cli/constants/readme.py +134 -0
  19. intuned_cli/controller/__test__/__init__.py +0 -0
  20. intuned_cli/controller/__test__/test_api.py +529 -0
  21. intuned_cli/controller/__test__/test_authsession.py +907 -0
  22. intuned_cli/controller/api.py +212 -0
  23. intuned_cli/controller/authsession.py +458 -0
  24. intuned_cli/controller/deploy.py +352 -0
  25. intuned_cli/controller/init.py +97 -0
  26. intuned_cli/types.py +33 -0
  27. intuned_cli/utils/api_helpers.py +32 -0
  28. intuned_cli/utils/auth_session_helpers.py +57 -0
  29. intuned_cli/utils/backend.py +5 -0
  30. intuned_cli/utils/confirmation.py +0 -0
  31. intuned_cli/utils/console.py +6 -0
  32. intuned_cli/utils/error.py +27 -0
  33. intuned_cli/utils/exclusions.py +40 -0
  34. intuned_cli/utils/get_auth_parameters.py +18 -0
  35. intuned_cli/utils/import_function.py +15 -0
  36. intuned_cli/utils/timeout.py +25 -0
  37. {cli → intuned_internal_cli}/__init__.py +1 -1
  38. {cli → intuned_internal_cli}/commands/__init__.py +2 -0
  39. {cli → intuned_internal_cli}/commands/ai_source/deploy.py +1 -1
  40. {cli → intuned_internal_cli}/commands/project/type_check.py +39 -32
  41. intuned_internal_cli/commands/root.py +15 -0
  42. {intuned_runtime-1.0.0.dist-info → intuned_runtime-1.1.0.dist-info}/METADATA +3 -1
  43. intuned_runtime-1.1.0.dist-info/RECORD +96 -0
  44. intuned_runtime-1.1.0.dist-info/entry_points.txt +4 -0
  45. runtime/__init__.py +2 -1
  46. runtime/backend_functions/_call_backend_function.py +0 -5
  47. runtime/browser/__init__.py +2 -1
  48. runtime/browser/launch_chromium.py +68 -49
  49. runtime/browser/storage_state.py +11 -12
  50. runtime/errors/run_api_errors.py +14 -10
  51. runtime/run/playwright_constructs.py +4 -2
  52. runtime/run/pydantic_encoder.py +15 -0
  53. runtime/run/run_api.py +5 -4
  54. runtime/types/run_types.py +16 -0
  55. intuned_runtime-1.0.0.dist-info/RECORD +0 -58
  56. intuned_runtime-1.0.0.dist-info/entry_points.txt +0 -3
  57. {cli → intuned_internal_cli}/commands/ai_source/__init__.py +0 -0
  58. {cli → intuned_internal_cli}/commands/ai_source/ai_source.py +0 -0
  59. {cli → intuned_internal_cli}/commands/browser/__init__.py +0 -0
  60. {cli → intuned_internal_cli}/commands/browser/save_state.py +0 -0
  61. {cli → intuned_internal_cli}/commands/init.py +0 -0
  62. {cli → intuned_internal_cli}/commands/project/__init__.py +0 -0
  63. {cli → intuned_internal_cli}/commands/project/auth_session/__init__.py +0 -0
  64. {cli → intuned_internal_cli}/commands/project/auth_session/check.py +0 -0
  65. {cli → intuned_internal_cli}/commands/project/auth_session/create.py +0 -0
  66. {cli → intuned_internal_cli}/commands/project/auth_session/load.py +0 -0
  67. {cli → intuned_internal_cli}/commands/project/project.py +0 -0
  68. {cli → intuned_internal_cli}/commands/project/run.py +0 -0
  69. {cli → intuned_internal_cli}/commands/project/run_interface.py +0 -0
  70. {cli → intuned_internal_cli}/commands/project/upgrade.py +0 -0
  71. {cli → intuned_internal_cli}/commands/publish_packages.py +0 -0
  72. {cli → intuned_internal_cli}/logger.py +0 -0
  73. {cli → intuned_internal_cli}/utils/ai_source_project.py +0 -0
  74. {cli → intuned_internal_cli}/utils/code_tree.py +0 -0
  75. {cli → intuned_internal_cli}/utils/run_apis.py +0 -0
  76. {cli → intuned_internal_cli}/utils/unix_socket.py +0 -0
  77. {intuned_runtime-1.0.0.dist-info → intuned_runtime-1.1.0.dist-info}/LICENSE +0 -0
  78. {intuned_runtime-1.0.0.dist-info → intuned_runtime-1.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,212 @@
1
+ import json
2
+ import os
3
+ from typing import Any
4
+
5
+ from anyio import Path
6
+ from pydantic import BaseModel
7
+
8
+ from intuned_cli.controller.authsession import execute_run_validate_auth_session_cli
9
+ from intuned_cli.controller.authsession import load_auth_session_instance
10
+ from intuned_cli.utils.api_helpers import assert_api_file_exists
11
+ from intuned_cli.utils.console import console
12
+ from intuned_cli.utils.error import CLIError
13
+ from intuned_cli.utils.error import log_automation_error
14
+ from intuned_cli.utils.get_auth_parameters import register_get_auth_session_parameters
15
+ from intuned_cli.utils.timeout import extendable_timeout
16
+ from runtime.errors.run_api_errors import AutomationError
17
+ from runtime.run.run_api import import_function_from_api_dir
18
+ from runtime.run.run_api import run_api
19
+ from runtime.types.run_types import Auth
20
+ from runtime.types.run_types import AutomationFunction
21
+ from runtime.types.run_types import PayloadToAppend
22
+ from runtime.types.run_types import ProxyConfig
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
+
28
+
29
+ class AuthSessionInput(BaseModel):
30
+ id: str
31
+ auto_recreate: bool
32
+ check_retries: int
33
+ create_retries: int
34
+
35
+
36
+ async def execute_run_api_cli(
37
+ *,
38
+ api_name: str,
39
+ input_data: Any | None,
40
+ retries: int,
41
+ auth_session: AuthSessionInput | None = None,
42
+ output_file: str | None = None,
43
+ headless: bool,
44
+ timeout: float,
45
+ proxy: str | None = None,
46
+ ):
47
+ """
48
+ Execute API with retries and optional auth session validation.
49
+ """
50
+ await assert_api_file_exists("api", api_name)
51
+
52
+ register_get_auth_session_parameters(auth_session.id if auth_session else None)
53
+
54
+ console.print(f"[bold]Running API [cyan]{api_name}[/cyan][/bold]")
55
+
56
+ for i in range(retries):
57
+ console.print(f"\n[bold]Executing [cyan]{api_name}[/cyan] [italic](Attempt {i + 1})[/italic]...\n")
58
+
59
+ auth_session_instance: StorageState | None = None
60
+ if auth_session:
61
+ auth_session_instance = await execute_run_validate_auth_session_cli(
62
+ id=auth_session.id,
63
+ auto_recreate=auth_session.auto_recreate,
64
+ check_retries=auth_session.check_retries,
65
+ create_retries=auth_session.create_retries,
66
+ headless=headless,
67
+ timeout=timeout,
68
+ proxy=proxy,
69
+ )
70
+
71
+ try:
72
+ result, extended_payloads = await attempt_api(
73
+ api_name=api_name,
74
+ parameters=input_data,
75
+ auth=auth_session_instance,
76
+ headless=headless,
77
+ timeout=timeout,
78
+ proxy=proxy,
79
+ )
80
+
81
+ return await handle_api_result(
82
+ result=result,
83
+ extended_payloads=extended_payloads,
84
+ file_path=Path(output_file) if output_file else None,
85
+ )
86
+
87
+ except AutomationError as error:
88
+ log_automation_error(error)
89
+ console.print(f"[bold red]Attempt {i + 1} failed[/bold red]")
90
+ continue
91
+
92
+ raise CLIError(
93
+ f"[red][bold]Failed to run API [/bold][bold]{api_name}[/bold]: [bright_red]Exceeded maximum retries of [bold]{retries}[/bold][/bright_red][/red]",
94
+ auto_color=False,
95
+ )
96
+
97
+
98
+ async def execute_attempt_api_cli(
99
+ *,
100
+ api_name: str,
101
+ input_data: Any | None,
102
+ auth_session_id: str | None = None,
103
+ output_file: str | None = None,
104
+ headless: bool,
105
+ timeout: float,
106
+ proxy: str | None = None,
107
+ ):
108
+ """
109
+ Execute a single API attempt with optional auth session.
110
+ """
111
+ console.print(f"[bold]Execute API attempt for [cyan]{api_name}[/cyan][/bold]")
112
+
113
+ await assert_api_file_exists("api", api_name)
114
+
115
+ register_get_auth_session_parameters(auth_session_id)
116
+
117
+ auth_session_instance: StorageState | None = None
118
+ if auth_session_id:
119
+ auth_session_instance, _ = await load_auth_session_instance(auth_session_id)
120
+
121
+ result, extended_payloads = await attempt_api(
122
+ api_name=api_name,
123
+ parameters=input_data,
124
+ auth=auth_session_instance,
125
+ headless=headless,
126
+ timeout=timeout,
127
+ proxy=proxy,
128
+ )
129
+
130
+ return await handle_api_result(
131
+ result=result,
132
+ extended_payloads=extended_payloads,
133
+ file_path=Path(output_file) if output_file else None,
134
+ )
135
+
136
+
137
+ async def handle_api_result(
138
+ *,
139
+ result: Any,
140
+ extended_payloads: list[PayloadToAppend] | None = None,
141
+ file_path: Path | None = None,
142
+ ):
143
+ console.print("[bold][green]API executed successfully[/green][/bold]")
144
+
145
+ if not file_path:
146
+ if result is None:
147
+ console.print("[bold][yellow]No result returned from the API[/yellow][/bold]")
148
+ else:
149
+ console.print("[bold][green]Result:[/green][/bold]")
150
+ console.print(f"{json.dumps(result, indent=2)}")
151
+
152
+ if extended_payloads and len(extended_payloads) > 0:
153
+ console.print(
154
+ "[bold green]Extended payloads:[/bold green] [italic][bright_green](This will only take effect if this API run was part of a job.)[/bright_green][/italic]"
155
+ )
156
+ console.print(f"{json.dumps([p.model_dump(by_alias=True) for p in extended_payloads], indent=2)}")
157
+
158
+ return
159
+
160
+ await write_results_to_file(
161
+ file_path=file_path,
162
+ result=result,
163
+ extended_payloads=extended_payloads,
164
+ )
165
+
166
+
167
+ async def write_results_to_file(
168
+ *,
169
+ file_path: Path,
170
+ result: Any,
171
+ extended_payloads: list[PayloadToAppend] | None = None,
172
+ ):
173
+ output_json = {"result": result}
174
+ if extended_payloads:
175
+ output_json["extendedPayloads"] = [m.model_dump(by_alias=True) for m in extended_payloads]
176
+
177
+ try:
178
+ await file_path.write_text(json.dumps(output_json, indent=2), encoding="utf-8")
179
+ console.print(f"[bold green]Results written to[/bold green] [underline]{file_path}[/underline]")
180
+ except Exception as e:
181
+ raise CLIError(f"Failed to write results to file '{file_path}': {e}") from e
182
+
183
+
184
+ async def attempt_api(
185
+ *,
186
+ api_name: str,
187
+ parameters: Any | None,
188
+ auth: StorageState | None = None,
189
+ headless: bool,
190
+ timeout: float,
191
+ proxy: str | None = None,
192
+ ):
193
+ cwd = await Path().resolve()
194
+ async with extendable_timeout(timeout):
195
+ result = await run_api(
196
+ RunApiParameters(
197
+ automation_function=AutomationFunction(name=f"api/{api_name}", params=parameters),
198
+ auth=Auth(
199
+ session=StateSession(state=auth),
200
+ run_check=False,
201
+ )
202
+ if auth
203
+ else None,
204
+ run_options=StandaloneRunOptions(
205
+ headless=headless, proxy=ProxyConfig.parse_from_str(proxy) if proxy else None
206
+ ),
207
+ ),
208
+ import_function=lambda file_path, name=None: import_function_from_api_dir(
209
+ file_path=file_path, automation_function_name=name, base_dir=os.fspath(cwd)
210
+ ),
211
+ )
212
+ return result.result, result.payload_to_append
@@ -0,0 +1,458 @@
1
+ import datetime
2
+ import json
3
+ import time
4
+ from typing import Any
5
+ from typing import Literal
6
+
7
+ from anyio import Path
8
+ from pydantic import BaseModel
9
+ from pydantic import Field
10
+ from pydantic import ValidationError
11
+
12
+ from intuned_cli.utils.api_helpers import assert_api_file_exists
13
+ from intuned_cli.utils.console import console
14
+ from intuned_cli.utils.error import CLIError
15
+ from intuned_cli.utils.error import log_automation_error
16
+ from intuned_cli.utils.get_auth_parameters import register_get_auth_session_parameters
17
+ from intuned_cli.utils.import_function import get_cli_import_function
18
+ from intuned_cli.utils.timeout import extendable_timeout
19
+ from runtime.errors.run_api_errors import AutomationError
20
+ from runtime.run.run_api import run_api
21
+ from runtime.types.run_types import Auth
22
+ from runtime.types.run_types import AutomationFunction
23
+ from runtime.types.run_types import ProxyConfig
24
+ from runtime.types.run_types import RunApiParameters
25
+ from runtime.types.run_types import StandaloneRunOptions
26
+ from runtime.types.run_types import StateSession
27
+ from runtime.types.run_types import StorageState
28
+
29
+ auth_session_instances_dirname = "auth-session-instances"
30
+
31
+
32
+ class AuthSessionMetadata(BaseModel):
33
+ created_at: str = Field(alias="createdAt")
34
+ updated_at: str = Field(alias="updatedAt")
35
+ auth_session_id: str = Field(alias="authSessionId")
36
+ auth_session_type: Literal["MANUAL", "API"] = Field(alias="authSessionType")
37
+ auth_session_input: dict[str, Any] | None = Field(alias="authSessionInput", default=None)
38
+ recorder_start_url: str | None = Field(alias="recorderStartUrl", default=None)
39
+ recorder_end_url: str | None = Field(alias="recorderEndUrl", default=None)
40
+
41
+
42
+ async def execute_run_validate_auth_session_cli(
43
+ *,
44
+ id: str,
45
+ auto_recreate: bool,
46
+ check_retries: int,
47
+ create_retries: int,
48
+ headless: bool,
49
+ timeout: float,
50
+ proxy: str | None = None,
51
+ ) -> StorageState:
52
+ """
53
+ Validate auth session with optional auto-recreation.
54
+ """
55
+ console.print(f"[bold]Validating auth session with id [cyan]{id}[/cyan][/bold]")
56
+
57
+ register_get_auth_session_parameters(id)
58
+
59
+ # Get auth session instance and path
60
+ instance, metadata = await load_auth_session_instance(id)
61
+
62
+ await assert_api_file_exists("auth-sessions", "check")
63
+
64
+ check_result = await run_check_with_retries(
65
+ auth=instance,
66
+ retries=check_retries,
67
+ headless=headless,
68
+ timeout=timeout,
69
+ proxy=proxy,
70
+ )
71
+
72
+ if not check_result:
73
+ if not auto_recreate:
74
+ raise CLIError("Auto recreate is disabled, please provide a new auth session or update it manually")
75
+
76
+ if metadata and metadata.auth_session_type == "MANUAL":
77
+ raise CLIError("Auth session is recorder-based, please provide a new one or update it manually")
78
+
79
+ console.print("[bold]Auto recreate is enabled - trying to re-create it[/bold]")
80
+
81
+ await assert_api_file_exists("auth-sessions", "create")
82
+
83
+ auth_session_input = (metadata.auth_session_input or {}) if metadata else {}
84
+
85
+ instance = await run_create_with_retries(
86
+ auth_session_id=id,
87
+ auth_session_input=auth_session_input,
88
+ retries=create_retries,
89
+ headless=headless,
90
+ timeout=timeout,
91
+ proxy=proxy,
92
+ metadata=metadata,
93
+ )
94
+
95
+ # Rerun check after refresh
96
+ check_result = await run_check_with_retries(
97
+ auth=instance,
98
+ retries=check_retries,
99
+ headless=headless,
100
+ timeout=timeout,
101
+ proxy=proxy,
102
+ )
103
+ if not check_result:
104
+ raise CLIError("Failed to re-create auth session")
105
+
106
+ console.print("[bold][green]Auth session validated successfully[/green][/bold]")
107
+ return instance
108
+
109
+
110
+ async def execute_run_create_auth_session_cli(
111
+ *,
112
+ id: str | None = None,
113
+ input_data: Any,
114
+ check_retries: int,
115
+ create_retries: int,
116
+ log: bool = True,
117
+ headless: bool,
118
+ timeout: float,
119
+ proxy: str | None = None,
120
+ metadata: AuthSessionMetadata | None = None,
121
+ ):
122
+ """
123
+ Create a new auth session.
124
+ """
125
+ if id is None:
126
+ id = f"auth-session-{int(time.time() * 1000)}"
127
+
128
+ if log:
129
+ console.print(f"[bold]Creating auth session with id [cyan]{id}[/cyan][/bold]")
130
+
131
+ await assert_api_file_exists("auth-sessions", "create")
132
+ await assert_api_file_exists("auth-sessions", "check")
133
+
134
+ instance = await run_create_with_retries(
135
+ auth_session_id=id,
136
+ auth_session_input=input_data,
137
+ retries=create_retries,
138
+ headless=headless,
139
+ timeout=timeout,
140
+ proxy=proxy,
141
+ metadata=metadata,
142
+ )
143
+
144
+ check_result = await run_check_with_retries(
145
+ auth=instance,
146
+ retries=check_retries,
147
+ headless=headless,
148
+ timeout=timeout,
149
+ proxy=proxy,
150
+ )
151
+ if not check_result:
152
+ raise CLIError("Failed to create auth session")
153
+
154
+ if log:
155
+ console.print("[bold][green]Auth session created successfully[/green][/bold]")
156
+
157
+
158
+ async def execute_run_update_auth_session_cli(
159
+ *,
160
+ id: str,
161
+ input_data: Any | None = None,
162
+ check_retries: int,
163
+ create_retries: int,
164
+ headless: bool,
165
+ timeout: float,
166
+ proxy: str | None = None,
167
+ ):
168
+ """
169
+ Update an existing auth session.
170
+ """
171
+ console.print(f"[bold]Updating auth session with id [cyan]{id}[/cyan][/bold]")
172
+
173
+ _, metadata = await load_auth_session_instance(id)
174
+ if metadata and metadata.auth_session_type == "MANUAL":
175
+ raise CLIError("Auth session is recorder-based, it cannot be updated.")
176
+
177
+ if input_data is None:
178
+ input_data = metadata.auth_session_input if metadata else {}
179
+
180
+ await assert_api_file_exists("auth-sessions", "create")
181
+ await assert_api_file_exists("auth-sessions", "check")
182
+
183
+ await execute_run_create_auth_session_cli(
184
+ id=id,
185
+ input_data=input_data,
186
+ check_retries=check_retries,
187
+ create_retries=create_retries,
188
+ log=False,
189
+ headless=headless,
190
+ timeout=timeout,
191
+ proxy=proxy,
192
+ metadata=metadata,
193
+ )
194
+
195
+ console.print("[bold][green]Auth session updated successfully[/green][/bold]")
196
+
197
+
198
+ async def execute_attempt_create_auth_session_cli(
199
+ *,
200
+ id: str | None = None,
201
+ input_data: Any,
202
+ headless: bool,
203
+ timeout: float,
204
+ proxy: str | None = None,
205
+ ):
206
+ """
207
+ Execute a single attempt to create auth session.
208
+ """
209
+ if id is None:
210
+ id = f"auth-session-attempt-{int(time.time() * 1000)}"
211
+
212
+ console.print(f"[bold]Executing create auth session attempt with id [cyan]{id}[/cyan][/bold]")
213
+ await assert_api_file_exists("auth-sessions", "create")
214
+ await run_create_with_retries(
215
+ auth_session_id=id,
216
+ auth_session_input=input_data,
217
+ retries=1,
218
+ headless=headless,
219
+ timeout=timeout,
220
+ proxy=proxy,
221
+ )
222
+
223
+
224
+ async def execute_attempt_check_auth_session_cli(
225
+ *,
226
+ id: str,
227
+ headless: bool,
228
+ timeout: float,
229
+ proxy: str | None = None,
230
+ ):
231
+ """
232
+ Execute a single attempt to check auth session.
233
+ """
234
+ console.print(f"[bold]Executing check auth session attempt with id [cyan]{id}[/cyan][/bold]")
235
+ await assert_api_file_exists("auth-sessions", "check")
236
+
237
+ register_get_auth_session_parameters(id)
238
+
239
+ instance, _ = await load_auth_session_instance(id)
240
+
241
+ check_result = await run_check_with_retries(
242
+ auth=instance,
243
+ retries=1,
244
+ headless=headless,
245
+ timeout=timeout,
246
+ proxy=proxy,
247
+ )
248
+
249
+ if not check_result:
250
+ raise CLIError("Check failed")
251
+
252
+ console.print("[bold green]Auth session check successful[/bold green]")
253
+
254
+
255
+ async def run_check(
256
+ *,
257
+ auth: StorageState,
258
+ headless: bool,
259
+ timeout: float,
260
+ proxy: str | None = None,
261
+ ) -> bool:
262
+ """
263
+ Run auth session check.
264
+ """
265
+ async with extendable_timeout(timeout):
266
+ result = await run_api(
267
+ RunApiParameters(
268
+ automation_function=AutomationFunction(
269
+ name="auth-sessions/check",
270
+ params=None,
271
+ ),
272
+ run_options=StandaloneRunOptions(
273
+ headless=headless, proxy=ProxyConfig.parse_from_str(proxy) if proxy else None
274
+ ),
275
+ auth=Auth(
276
+ session=StateSession(
277
+ state=auth,
278
+ ),
279
+ run_check=False,
280
+ ),
281
+ ),
282
+ import_function=await get_cli_import_function(),
283
+ )
284
+
285
+ if not result.result:
286
+ return False
287
+
288
+ return bool(result.result)
289
+
290
+
291
+ async def run_create(
292
+ *,
293
+ auth_session_input: dict[str, Any],
294
+ headless: bool,
295
+ timeout: float,
296
+ proxy: str | None = None,
297
+ ) -> StorageState:
298
+ """
299
+ Run auth session create.
300
+ """
301
+ async with extendable_timeout(timeout):
302
+ result = await run_api(
303
+ RunApiParameters(
304
+ automation_function=AutomationFunction(
305
+ name="auth-sessions/create",
306
+ params=auth_session_input,
307
+ ),
308
+ run_options=StandaloneRunOptions(
309
+ headless=headless, proxy=ProxyConfig.parse_from_str(proxy) if proxy else None
310
+ ),
311
+ retrieve_session=True,
312
+ ),
313
+ import_function=await get_cli_import_function(),
314
+ )
315
+ if not result.session:
316
+ raise Exception("Auth session create did not return a session")
317
+ return result.session
318
+
319
+
320
+ async def run_check_with_retries(
321
+ *,
322
+ auth: StorageState,
323
+ retries: int,
324
+ headless: bool,
325
+ timeout: float,
326
+ proxy: str | None = None,
327
+ ) -> bool:
328
+ """
329
+ Run auth session check with retries.
330
+ """
331
+ for i in range(retries):
332
+ attempt_text = "" if i == 0 else f" [italic](Attempt {i + 1})[/italic]"
333
+ console.print(f"\n[bold]Running [cyan]auth session check[/cyan]{attempt_text}...[/bold]\n")
334
+
335
+ try:
336
+ check_result = await run_check(
337
+ auth=auth,
338
+ headless=headless,
339
+ timeout=timeout,
340
+ proxy=proxy,
341
+ )
342
+
343
+ if check_result:
344
+ console.print("[bold][green]Auth session check passed[/green][/bold]")
345
+ return True
346
+ except AutomationError as e:
347
+ log_automation_error(e)
348
+ continue
349
+
350
+ console.print(f"[bold][red]Auth session check failed after {retries} attempt(s)[/red][/bold]")
351
+ return False
352
+
353
+
354
+ async def run_create_with_retries(
355
+ *,
356
+ auth_session_id: str,
357
+ auth_session_input: dict[str, Any],
358
+ retries: int,
359
+ headless: bool,
360
+ timeout: float,
361
+ proxy: str | None = None,
362
+ metadata: AuthSessionMetadata | None = None,
363
+ ):
364
+ """
365
+ Run auth session create with retries.
366
+ """
367
+ new_auth_session_instance: StorageState | None = None
368
+
369
+ for i in range(retries):
370
+ attempt_text = "" if i == 0 else f" [italic](Attempt {i + 1})[/italic]"
371
+ console.print(f"\n[bold]Running [cyan]auth session create[/cyan]{attempt_text}...[/bold]\n")
372
+
373
+ try:
374
+ new_auth_session_instance = await run_create(
375
+ auth_session_input=auth_session_input,
376
+ headless=headless,
377
+ timeout=timeout,
378
+ proxy=proxy,
379
+ )
380
+ console.print("[bold][green]Auth session create succeeded[/green][/bold]")
381
+ break
382
+ except AutomationError as e:
383
+ log_automation_error(e)
384
+ continue
385
+
386
+ if not new_auth_session_instance:
387
+ raise CLIError(f"Failed to create auth session after {retries} attempt(s)")
388
+
389
+ await store_auth_session_instance(new_auth_session_instance, auth_session_id, auth_session_input, metadata=metadata)
390
+
391
+ return new_auth_session_instance
392
+
393
+
394
+ async def load_auth_session_instance(auth_session_id: str) -> tuple[StorageState, AuthSessionMetadata]:
395
+ """
396
+ Retrieve auth session instance storage path by ID.
397
+ """
398
+ # Placeholder implementation - will be replaced with actual retrieval logic
399
+ auth_session_instances_path = get_auth_session_path(auth_session_id)
400
+
401
+ instance_path = auth_session_instances_path / "auth-session.json"
402
+
403
+ if not await instance_path.exists():
404
+ raise CLIError(f"Auth session instance with ID {auth_session_id} not found")
405
+
406
+ metadata_path = auth_session_instances_path / "metadata.json"
407
+
408
+ if not await metadata_path.exists():
409
+ raise CLIError(f"Metadata for auth session instance with ID {auth_session_id} not found")
410
+
411
+ try:
412
+ instance_text_content = await instance_path.read_text()
413
+
414
+ instance = StorageState.model_validate_json(instance_text_content)
415
+
416
+ metadata_text_content = await metadata_path.read_text()
417
+
418
+ metadata = AuthSessionMetadata.model_validate_json(metadata_text_content)
419
+ except ValidationError as e:
420
+ raise CLIError(f"Failed to parse auth session instance or metadata: {e}") from e
421
+
422
+ return instance, metadata
423
+
424
+
425
+ async def store_auth_session_instance(
426
+ auth_session_instance: StorageState,
427
+ auth_session_id: str,
428
+ auth_session_input: dict[str, Any],
429
+ metadata: AuthSessionMetadata | None = None,
430
+ ):
431
+ """
432
+ Store auth session instance with metadata.
433
+ """
434
+
435
+ # Create directory path
436
+ auth_session_path = get_auth_session_path(auth_session_id)
437
+ await auth_session_path.mkdir(parents=True, exist_ok=True)
438
+
439
+ # Store the session data
440
+ instance_file_path = auth_session_path / "auth-session.json"
441
+ instance_json = json.dumps(auth_session_instance.model_dump(by_alias=True), indent=2)
442
+ await instance_file_path.write_text(instance_json)
443
+ # Store metadata
444
+ metadata = AuthSessionMetadata(
445
+ createdAt=metadata.created_at if metadata else datetime.datetime.now().isoformat(),
446
+ updatedAt=datetime.datetime.now().isoformat(),
447
+ authSessionId=auth_session_id,
448
+ authSessionInput=auth_session_input,
449
+ authSessionType="API",
450
+ )
451
+ metadata_file_path = auth_session_path / "metadata.json"
452
+
453
+ metadata_json = json.dumps(metadata.model_dump(by_alias=True), indent=2)
454
+ await metadata_file_path.write_text(metadata_json)
455
+
456
+
457
+ def get_auth_session_path(auth_session_id: str):
458
+ return Path(auth_session_instances_dirname) / auth_session_id