smooth-py 0.2.6.dev20250922__tar.gz → 0.2.8.dev20251003__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.
Potentially problematic release.
This version of smooth-py might be problematic. Click here for more details.
- {smooth_py-0.2.6.dev20250922 → smooth_py-0.2.8.dev20251003}/PKG-INFO +2 -1
- {smooth_py-0.2.6.dev20250922 → smooth_py-0.2.8.dev20251003}/pyproject.toml +3 -2
- {smooth_py-0.2.6.dev20250922 → smooth_py-0.2.8.dev20251003}/src/smooth/__init__.py +179 -41
- {smooth_py-0.2.6.dev20250922 → smooth_py-0.2.8.dev20251003}/README.md +0 -0
- {smooth_py-0.2.6.dev20250922 → smooth_py-0.2.8.dev20251003}/src/smooth/mcp/__init__.py +0 -0
- {smooth_py-0.2.6.dev20250922 → smooth_py-0.2.8.dev20251003}/src/smooth/mcp/server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: smooth-py
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8.dev20251003
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Luca Pinchetti
|
|
6
6
|
Author-email: luca@circlemind.co
|
|
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
13
|
Provides-Extra: mcp
|
|
14
|
+
Requires-Dist: deprecated (>=1.2.18,<2.0.0)
|
|
14
15
|
Requires-Dist: fastmcp (>=2.12.0,<3.0.0) ; extra == "mcp"
|
|
15
16
|
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
16
17
|
Requires-Dist: pydantic (>=2.11.7,<3.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "smooth-py"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.8.dev20251003"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Luca Pinchetti",email = "luca@circlemind.co"}
|
|
@@ -10,7 +10,8 @@ requires-python = ">=3.10,<4.0"
|
|
|
10
10
|
dependencies = [
|
|
11
11
|
"pydantic (>=2.11.7,<3.0.0)",
|
|
12
12
|
"httpx (>=0.28.1,<0.29.0)",
|
|
13
|
-
"requests (>=2.32.5,<3.0.0)"
|
|
13
|
+
"requests (>=2.32.5,<3.0.0)",
|
|
14
|
+
"deprecated (>=1.2.18,<2.0.0)"
|
|
14
15
|
]
|
|
15
16
|
|
|
16
17
|
[project.optional-dependencies]
|
|
@@ -6,12 +6,14 @@ import logging
|
|
|
6
6
|
import os
|
|
7
7
|
import time
|
|
8
8
|
import urllib.parse
|
|
9
|
+
import warnings
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
from typing import Any, Literal, Type
|
|
11
12
|
|
|
12
13
|
import httpx
|
|
13
14
|
import requests
|
|
14
|
-
from
|
|
15
|
+
from deprecated import deprecated
|
|
16
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
15
17
|
|
|
16
18
|
# Configure logging
|
|
17
19
|
logger = logging.getLogger("smooth")
|
|
@@ -36,6 +38,7 @@ def _encode_url(url: str, interactive: bool = True, embed: bool = False) -> str:
|
|
|
36
38
|
|
|
37
39
|
class TaskResponse(BaseModel):
|
|
38
40
|
"""Task response model."""
|
|
41
|
+
model_config = ConfigDict(extra='allow')
|
|
39
42
|
|
|
40
43
|
id: str = Field(description="The ID of the task.")
|
|
41
44
|
status: Literal["waiting", "running", "done", "failed"] = Field(description="The status of the task.")
|
|
@@ -67,13 +70,16 @@ class TaskRequest(BaseModel):
|
|
|
67
70
|
allowed_urls: list[str] | None = Field(
|
|
68
71
|
default=None,
|
|
69
72
|
description=(
|
|
70
|
-
"List of allowed URL patterns using wildcard syntax (e.g., https
|
|
73
|
+
"List of allowed URL patterns using wildcard syntax (e.g., https://*example.com/*). If None, all URLs are allowed."
|
|
71
74
|
),
|
|
72
75
|
)
|
|
73
76
|
enable_recording: bool = Field(default=True, description="Enable video recording of the task execution. Default is True")
|
|
74
|
-
|
|
77
|
+
profile_id: str | None = Field(
|
|
75
78
|
default=None,
|
|
76
|
-
description="Browser
|
|
79
|
+
description=("Browser profile ID to use. Each profile maintains its own state, such as cookies and login credentials."),
|
|
80
|
+
)
|
|
81
|
+
profile_read_only: bool = Field(
|
|
82
|
+
default=False, description="If true, the profile specified by `profile_id` will be loaded in read-only mode."
|
|
77
83
|
)
|
|
78
84
|
stealth_mode: bool = Field(default=False, description="Run the browser in stealth mode.")
|
|
79
85
|
proxy_server: str | None = Field(
|
|
@@ -84,29 +90,116 @@ class TaskRequest(BaseModel):
|
|
|
84
90
|
)
|
|
85
91
|
proxy_username: str | None = Field(default=None, description="Proxy server username.")
|
|
86
92
|
proxy_password: str | None = Field(default=None, description="Proxy server password.")
|
|
93
|
+
experimental_features: dict[str, Any] | None = Field(
|
|
94
|
+
default=None, description="Experimental features to enable for the task."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
@model_validator(mode="before")
|
|
98
|
+
@classmethod
|
|
99
|
+
def _handle_deprecated_session_id(cls, data: Any) -> Any:
|
|
100
|
+
if isinstance(data, dict) and "session_id" in data:
|
|
101
|
+
warnings.warn("'session_id' is deprecated, use 'profile_id' instead", DeprecationWarning, stacklevel=2)
|
|
102
|
+
data["profile_id"] = data.pop("session_id")
|
|
103
|
+
return data
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def session_id(self):
|
|
107
|
+
"""(Deprecated) Returns the session ID."""
|
|
108
|
+
warnings.warn("'session_id' is deprecated, use 'profile_id' instead", DeprecationWarning, stacklevel=2)
|
|
109
|
+
return self.profile_id
|
|
110
|
+
|
|
111
|
+
@session_id.setter
|
|
112
|
+
def session_id(self, value):
|
|
113
|
+
"""(Deprecated) Sets the session ID."""
|
|
114
|
+
warnings.warn("'session_id' is deprecated, use 'profile_id' instead", DeprecationWarning, stacklevel=2)
|
|
115
|
+
self.profile_id = value
|
|
87
116
|
|
|
88
117
|
|
|
89
118
|
class BrowserSessionRequest(BaseModel):
|
|
90
119
|
"""Request model for creating a browser session."""
|
|
91
120
|
|
|
92
|
-
|
|
93
|
-
default=None,
|
|
94
|
-
description=("The session ID to open in the browser. If None, a new session will be created with a random name."),
|
|
121
|
+
profile_id: str | None = Field(
|
|
122
|
+
default=None, description=("The profile ID to use for the browser session. If None, a new profile will be created.")
|
|
95
123
|
)
|
|
96
124
|
live_view: bool | None = Field(default=True, description="Request a live URL to interact with the browser session.")
|
|
97
125
|
|
|
126
|
+
@model_validator(mode="before")
|
|
127
|
+
@classmethod
|
|
128
|
+
def _handle_deprecated_session_id(cls, data: Any) -> Any:
|
|
129
|
+
if isinstance(data, dict) and "session_id" in data:
|
|
130
|
+
warnings.warn("'session_id' is deprecated, use 'profile_id' instead", DeprecationWarning, stacklevel=2)
|
|
131
|
+
data["profile_id"] = data.pop("session_id")
|
|
132
|
+
return data
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def session_id(self):
|
|
136
|
+
"""(Deprecated) Returns the session ID."""
|
|
137
|
+
warnings.warn("'session_id' is deprecated, use 'profile_id' instead", DeprecationWarning, stacklevel=2)
|
|
138
|
+
return self.profile_id
|
|
139
|
+
|
|
140
|
+
@session_id.setter
|
|
141
|
+
def session_id(self, value):
|
|
142
|
+
"""(Deprecated) Sets the session ID."""
|
|
143
|
+
warnings.warn("'session_id' is deprecated, use 'profile_id' instead", DeprecationWarning, stacklevel=2)
|
|
144
|
+
self.profile_id = value
|
|
145
|
+
|
|
98
146
|
|
|
99
147
|
class BrowserSessionResponse(BaseModel):
|
|
100
148
|
"""Browser session response model."""
|
|
101
149
|
|
|
102
|
-
|
|
150
|
+
profile_id: str = Field(description="The ID of the browser profile associated with the opened browser instance.")
|
|
103
151
|
live_url: str | None = Field(default=None, description="The live URL to interact with the browser session.")
|
|
104
152
|
|
|
153
|
+
@model_validator(mode="before")
|
|
154
|
+
@classmethod
|
|
155
|
+
def _handle_deprecated_session_id(cls, data: Any) -> Any:
|
|
156
|
+
if isinstance(data, dict) and "session_id" in data:
|
|
157
|
+
warnings.warn("'session_id' is deprecated, use 'profile_id' instead", DeprecationWarning, stacklevel=2)
|
|
158
|
+
data["profile_id"] = data.pop("session_id")
|
|
159
|
+
return data
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def session_id(self):
|
|
163
|
+
"""(Deprecated) Returns the session ID."""
|
|
164
|
+
warnings.warn("'session_id' is deprecated, use 'profile_id' instead", DeprecationWarning, stacklevel=2)
|
|
165
|
+
return self.profile_id
|
|
166
|
+
|
|
167
|
+
@session_id.setter
|
|
168
|
+
def session_id(self, value):
|
|
169
|
+
"""(Deprecated) Sets the session ID."""
|
|
170
|
+
warnings.warn("'session_id' is deprecated, use 'profile_id' instead", DeprecationWarning, stacklevel=2)
|
|
171
|
+
self.profile_id = value
|
|
172
|
+
|
|
105
173
|
|
|
106
|
-
class
|
|
107
|
-
"""Response model for listing browser
|
|
174
|
+
class BrowserProfilesResponse(BaseModel):
|
|
175
|
+
"""Response model for listing browser profiles."""
|
|
108
176
|
|
|
109
|
-
|
|
177
|
+
profile_ids: list[str] = Field(description="The IDs of the browser profiles.")
|
|
178
|
+
|
|
179
|
+
@model_validator(mode="before")
|
|
180
|
+
@classmethod
|
|
181
|
+
def _handle_deprecated_session_ids(cls, data: Any) -> Any:
|
|
182
|
+
if isinstance(data, dict) and "session_ids" in data:
|
|
183
|
+
warnings.warn("'session_ids' is deprecated, use 'profile_ids' instead", DeprecationWarning, stacklevel=2)
|
|
184
|
+
data["profile_ids"] = data.pop("session_ids")
|
|
185
|
+
return data
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def session_ids(self):
|
|
189
|
+
"""(Deprecated) Returns the session IDs."""
|
|
190
|
+
warnings.warn("'session_ids' is deprecated, use 'profile_ids' instead", DeprecationWarning, stacklevel=2)
|
|
191
|
+
return self.profile_ids
|
|
192
|
+
|
|
193
|
+
@session_ids.setter
|
|
194
|
+
def session_ids(self, value):
|
|
195
|
+
"""(Deprecated) Sets the session IDs."""
|
|
196
|
+
warnings.warn("'session_ids' is deprecated, use 'profile_ids' instead", DeprecationWarning, stacklevel=2)
|
|
197
|
+
self.profile_ids = value
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class BrowserSessionsResponse(BrowserProfilesResponse):
|
|
201
|
+
"""Response model for listing browser profiles."""
|
|
202
|
+
pass
|
|
110
203
|
|
|
111
204
|
|
|
112
205
|
class UploadFileResponse(BaseModel):
|
|
@@ -189,9 +282,14 @@ class BrowserSessionHandle(BaseModel):
|
|
|
189
282
|
|
|
190
283
|
browser_session: BrowserSessionResponse = Field(description="The browser session associated with this handle.")
|
|
191
284
|
|
|
285
|
+
@deprecated("session_id is deprecated, use profile_id instead")
|
|
192
286
|
def session_id(self):
|
|
193
287
|
"""Returns the session ID for the browser session."""
|
|
194
|
-
return self.
|
|
288
|
+
return self.profile_id()
|
|
289
|
+
|
|
290
|
+
def profile_id(self):
|
|
291
|
+
"""Returns the profile ID for the browser session."""
|
|
292
|
+
return self.browser_session.profile_id
|
|
195
293
|
|
|
196
294
|
def live_url(self, interactive: bool = True, embed: bool = False):
|
|
197
295
|
"""Returns the live URL for the browser session."""
|
|
@@ -321,10 +419,13 @@ class SmoothClient(BaseClient):
|
|
|
321
419
|
allowed_urls: list[str] | None = None,
|
|
322
420
|
enable_recording: bool = False,
|
|
323
421
|
session_id: str | None = None,
|
|
422
|
+
profile_id: str | None = None,
|
|
423
|
+
profile_read_only: bool = False,
|
|
324
424
|
stealth_mode: bool = False,
|
|
325
425
|
proxy_server: str | None = None,
|
|
326
426
|
proxy_username: str | None = None,
|
|
327
427
|
proxy_password: str | None = None,
|
|
428
|
+
experimental_features: dict[str, Any] | None = None,
|
|
328
429
|
) -> TaskHandle:
|
|
329
430
|
"""Runs a task and returns a handle to the task.
|
|
330
431
|
|
|
@@ -340,14 +441,17 @@ class SmoothClient(BaseClient):
|
|
|
340
441
|
agent: The agent to use for the task.
|
|
341
442
|
max_steps: Maximum number of steps the agent can take (max 64).
|
|
342
443
|
device: Device type for the task. Default is mobile.
|
|
343
|
-
allowed_urls: List of allowed URL patterns using wildcard syntax (e.g., https
|
|
444
|
+
allowed_urls: List of allowed URL patterns using wildcard syntax (e.g., https://*example.com/*).
|
|
344
445
|
If None, all URLs are allowed.
|
|
345
446
|
enable_recording: Enable video recording of the task execution.
|
|
346
|
-
session_id: Browser session ID to use.
|
|
447
|
+
session_id: (Deprecated, now `profile_id`) Browser session ID to use.
|
|
448
|
+
profile_id: Browser profile ID to use. Each profile maintains its own state, such as cookies and login credentials.
|
|
449
|
+
profile_read_only: If true, the profile specified by `profile_id` will be loaded in read-only mode.
|
|
347
450
|
stealth_mode: Run the browser in stealth mode.
|
|
348
451
|
proxy_server: Proxy server url to route browser traffic through.
|
|
349
452
|
proxy_username: Proxy server username.
|
|
350
453
|
proxy_password: Proxy server password.
|
|
454
|
+
experimental_features: Experimental features to enable for the task.
|
|
351
455
|
|
|
352
456
|
Returns:
|
|
353
457
|
A handle to the running task.
|
|
@@ -366,21 +470,26 @@ class SmoothClient(BaseClient):
|
|
|
366
470
|
device=device,
|
|
367
471
|
allowed_urls=allowed_urls,
|
|
368
472
|
enable_recording=enable_recording,
|
|
369
|
-
|
|
473
|
+
profile_id=profile_id or session_id,
|
|
474
|
+
profile_read_only=profile_read_only,
|
|
370
475
|
stealth_mode=stealth_mode,
|
|
371
476
|
proxy_server=proxy_server,
|
|
372
477
|
proxy_username=proxy_username,
|
|
373
478
|
proxy_password=proxy_password,
|
|
479
|
+
experimental_features=experimental_features,
|
|
374
480
|
)
|
|
375
481
|
initial_response = self._submit_task(payload)
|
|
376
482
|
|
|
377
483
|
return TaskHandle(initial_response.id, self)
|
|
378
484
|
|
|
379
|
-
def open_session(
|
|
380
|
-
|
|
485
|
+
def open_session(
|
|
486
|
+
self, profile_id: str | None = None, session_id: str | None = None, live_view: bool = True
|
|
487
|
+
) -> BrowserSessionHandle:
|
|
488
|
+
"""Opens an interactive browser instance to interact with a specific browser profile.
|
|
381
489
|
|
|
382
490
|
Args:
|
|
383
|
-
|
|
491
|
+
profile_id: The profile ID to use for the session. If None, a new profile will be created.
|
|
492
|
+
session_id: (Deprecated, now `profile_id`) The session ID to associate with the browser.
|
|
384
493
|
live_view: Whether to enable live view for the session.
|
|
385
494
|
|
|
386
495
|
Returns:
|
|
@@ -392,7 +501,7 @@ class SmoothClient(BaseClient):
|
|
|
392
501
|
try:
|
|
393
502
|
response = self._session.post(
|
|
394
503
|
f"{self.base_url}/browser/session",
|
|
395
|
-
json=BrowserSessionRequest(
|
|
504
|
+
json=BrowserSessionRequest(profile_id=profile_id or session_id, live_view=live_view).model_dump(exclude_none=True),
|
|
396
505
|
)
|
|
397
506
|
data = self._handle_response(response)
|
|
398
507
|
return BrowserSessionHandle(browser_session=BrowserSessionResponse(**data["r"]))
|
|
@@ -400,11 +509,11 @@ class SmoothClient(BaseClient):
|
|
|
400
509
|
logger.error(f"Request failed: {e}")
|
|
401
510
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
402
511
|
|
|
403
|
-
def
|
|
404
|
-
"""Lists all browser
|
|
512
|
+
def list_profiles(self):
|
|
513
|
+
"""Lists all browser profiles for the user.
|
|
405
514
|
|
|
406
515
|
Returns:
|
|
407
|
-
A list of existing browser
|
|
516
|
+
A list of existing browser profiles.
|
|
408
517
|
|
|
409
518
|
Raises:
|
|
410
519
|
ApiException: If the API request fails.
|
|
@@ -412,20 +521,30 @@ class SmoothClient(BaseClient):
|
|
|
412
521
|
try:
|
|
413
522
|
response = self._session.get(f"{self.base_url}/browser/session")
|
|
414
523
|
data = self._handle_response(response)
|
|
415
|
-
return
|
|
524
|
+
return BrowserProfilesResponse(**data["r"])
|
|
416
525
|
except requests.exceptions.RequestException as e:
|
|
417
526
|
logger.error(f"Request failed: {e}")
|
|
418
527
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
419
528
|
|
|
420
|
-
|
|
421
|
-
|
|
529
|
+
@deprecated("list_sessions is deprecated, use list_profiles instead")
|
|
530
|
+
def list_sessions(self):
|
|
531
|
+
"""Lists all browser profiles for the user."""
|
|
532
|
+
return self.list_profiles()
|
|
533
|
+
|
|
534
|
+
def delete_profile(self, profile_id: str):
|
|
535
|
+
"""Delete a browser profile."""
|
|
422
536
|
try:
|
|
423
|
-
response = self._session.delete(f"{self.base_url}/browser/session/{
|
|
537
|
+
response = self._session.delete(f"{self.base_url}/browser/session/{profile_id}")
|
|
424
538
|
self._handle_response(response)
|
|
425
539
|
except requests.exceptions.RequestException as e:
|
|
426
540
|
logger.error(f"Request failed: {e}")
|
|
427
541
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
428
542
|
|
|
543
|
+
@deprecated("delete_session is deprecated, use delete_profile instead")
|
|
544
|
+
def delete_session(self, session_id: str):
|
|
545
|
+
"""Delete a browser profile."""
|
|
546
|
+
self.delete_profile(session_id)
|
|
547
|
+
|
|
429
548
|
def upload_file(self, file: io.IOBase, name: str | None = None, purpose: str | None = None) -> UploadFileResponse:
|
|
430
549
|
"""Upload a file and return the file ID.
|
|
431
550
|
|
|
@@ -588,10 +707,13 @@ class SmoothAsyncClient(BaseClient):
|
|
|
588
707
|
allowed_urls: list[str] | None = None,
|
|
589
708
|
enable_recording: bool = False,
|
|
590
709
|
session_id: str | None = None,
|
|
710
|
+
profile_id: str | None = None,
|
|
711
|
+
profile_read_only: bool = False,
|
|
591
712
|
stealth_mode: bool = False,
|
|
592
713
|
proxy_server: str | None = None,
|
|
593
714
|
proxy_username: str | None = None,
|
|
594
715
|
proxy_password: str | None = None,
|
|
716
|
+
experimental_features: dict[str, Any] | None = None,
|
|
595
717
|
) -> AsyncTaskHandle:
|
|
596
718
|
"""Runs a task and returns a handle to the task asynchronously.
|
|
597
719
|
|
|
@@ -607,16 +729,17 @@ class SmoothAsyncClient(BaseClient):
|
|
|
607
729
|
agent: The agent to use for the task.
|
|
608
730
|
max_steps: Maximum number of steps the agent can take (max 64).
|
|
609
731
|
device: Device type for the task. Default is mobile.
|
|
610
|
-
allowed_urls: List of allowed URL patterns using wildcard syntax (e.g., https
|
|
732
|
+
allowed_urls: List of allowed URL patterns using wildcard syntax (e.g., https://*example.com/*).
|
|
611
733
|
If None, all URLs are allowed.
|
|
612
734
|
enable_recording: Enable video recording of the task execution.
|
|
613
|
-
session_id: Browser session ID to use.
|
|
735
|
+
session_id: (Deprecated, now `profile_id`) Browser session ID to use.
|
|
736
|
+
profile_id: Browser profile ID to use. Each profile maintains its own state, such as cookies and login credentials.
|
|
737
|
+
profile_read_only: If true, the profile specified by `profile_id` will be loaded in read-only mode.
|
|
614
738
|
stealth_mode: Run the browser in stealth mode.
|
|
615
739
|
proxy_server: Proxy server url to route browser traffic through.
|
|
616
740
|
proxy_username: Proxy server username.
|
|
617
741
|
proxy_password: Proxy server password.
|
|
618
|
-
|
|
619
|
-
timeout: The maximum time in seconds to wait for the task to complete.
|
|
742
|
+
experimental_features: Experimental features to enable for the task.
|
|
620
743
|
|
|
621
744
|
Returns:
|
|
622
745
|
A handle to the running task.
|
|
@@ -635,21 +758,26 @@ class SmoothAsyncClient(BaseClient):
|
|
|
635
758
|
device=device,
|
|
636
759
|
allowed_urls=allowed_urls,
|
|
637
760
|
enable_recording=enable_recording,
|
|
638
|
-
|
|
761
|
+
profile_id=profile_id or session_id,
|
|
762
|
+
profile_read_only=profile_read_only,
|
|
639
763
|
stealth_mode=stealth_mode,
|
|
640
764
|
proxy_server=proxy_server,
|
|
641
765
|
proxy_username=proxy_username,
|
|
642
766
|
proxy_password=proxy_password,
|
|
767
|
+
experimental_features=experimental_features,
|
|
643
768
|
)
|
|
644
769
|
|
|
645
770
|
initial_response = await self._submit_task(payload)
|
|
646
771
|
return AsyncTaskHandle(initial_response.id, self)
|
|
647
772
|
|
|
648
|
-
async def open_session(
|
|
773
|
+
async def open_session(
|
|
774
|
+
self, profile_id: str | None = None, session_id: str | None = None, live_view: bool = True
|
|
775
|
+
) -> BrowserSessionHandle:
|
|
649
776
|
"""Opens an interactive browser instance asynchronously.
|
|
650
777
|
|
|
651
778
|
Args:
|
|
652
|
-
session_id: The session ID to associate with the browser.
|
|
779
|
+
session_id: (Deprecated, now `profile_id`) The session ID to associate with the browser.
|
|
780
|
+
profile_id: The profile ID to associate with the browser.
|
|
653
781
|
live_view: Whether to enable live view for the session.
|
|
654
782
|
|
|
655
783
|
Returns:
|
|
@@ -661,7 +789,7 @@ class SmoothAsyncClient(BaseClient):
|
|
|
661
789
|
try:
|
|
662
790
|
response = await self._client.post(
|
|
663
791
|
f"{self.base_url}/browser/session",
|
|
664
|
-
json=BrowserSessionRequest(
|
|
792
|
+
json=BrowserSessionRequest(profile_id=profile_id or session_id, live_view=live_view).model_dump(exclude_none=True),
|
|
665
793
|
)
|
|
666
794
|
data = self._handle_response(response)
|
|
667
795
|
return BrowserSessionHandle(browser_session=BrowserSessionResponse(**data["r"]))
|
|
@@ -669,11 +797,11 @@ class SmoothAsyncClient(BaseClient):
|
|
|
669
797
|
logger.error(f"Request failed: {e}")
|
|
670
798
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
671
799
|
|
|
672
|
-
async def
|
|
673
|
-
"""Lists all browser
|
|
800
|
+
async def list_profiles(self):
|
|
801
|
+
"""Lists all browser profiles for the user.
|
|
674
802
|
|
|
675
803
|
Returns:
|
|
676
|
-
A list of existing browser
|
|
804
|
+
A list of existing browser profiles.
|
|
677
805
|
|
|
678
806
|
Raises:
|
|
679
807
|
ApiException: If the API request fails.
|
|
@@ -681,20 +809,30 @@ class SmoothAsyncClient(BaseClient):
|
|
|
681
809
|
try:
|
|
682
810
|
response = await self._client.get(f"{self.base_url}/browser/session")
|
|
683
811
|
data = self._handle_response(response)
|
|
684
|
-
return
|
|
812
|
+
return BrowserProfilesResponse(**data["r"])
|
|
685
813
|
except httpx.RequestError as e:
|
|
686
814
|
logger.error(f"Request failed: {e}")
|
|
687
815
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
688
816
|
|
|
689
|
-
|
|
690
|
-
|
|
817
|
+
@deprecated("list_sessions is deprecated, use list_profiles instead")
|
|
818
|
+
async def list_sessions(self):
|
|
819
|
+
"""Lists all browser profiles for the user."""
|
|
820
|
+
return await self.list_profiles()
|
|
821
|
+
|
|
822
|
+
async def delete_profile(self, profile_id: str):
|
|
823
|
+
"""Delete a browser profile."""
|
|
691
824
|
try:
|
|
692
|
-
response = await self._client.delete(f"{self.base_url}/browser/session/{
|
|
825
|
+
response = await self._client.delete(f"{self.base_url}/browser/session/{profile_id}")
|
|
693
826
|
self._handle_response(response)
|
|
694
827
|
except httpx.RequestError as e:
|
|
695
828
|
logger.error(f"Request failed: {e}")
|
|
696
829
|
raise ApiError(status_code=0, detail=f"Request failed: {str(e)}") from None
|
|
697
830
|
|
|
831
|
+
@deprecated("delete_session is deprecated, use delete_profile instead")
|
|
832
|
+
async def delete_session(self, session_id: str):
|
|
833
|
+
"""Delete a browser profile."""
|
|
834
|
+
await self.delete_profile(session_id)
|
|
835
|
+
|
|
698
836
|
async def upload_file(self, file: io.IOBase, name: str | None = None, purpose: str | None = None) -> UploadFileResponse:
|
|
699
837
|
"""Upload a file and return the file ID.
|
|
700
838
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|