scalebox-sdk 0.1.25__py3-none-any.whl → 1.0.2__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. scalebox/__init__.py +2 -2
  2. scalebox/api/__init__.py +3 -1
  3. scalebox/api/client/api/sandboxes/get_sandboxes.py +1 -1
  4. scalebox/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
  5. scalebox/api/client/models/connect_sandbox.py +59 -0
  6. scalebox/api/client/models/error.py +2 -2
  7. scalebox/api/client/models/listed_sandbox.py +24 -3
  8. scalebox/api/client/models/new_sandbox.py +10 -0
  9. scalebox/api/client/models/sandbox.py +13 -0
  10. scalebox/api/client/models/sandbox_detail.py +24 -0
  11. scalebox/cli.py +125 -125
  12. scalebox/client/aclient.py +57 -57
  13. scalebox/client/client.py +102 -102
  14. scalebox/code_interpreter/__init__.py +12 -12
  15. scalebox/code_interpreter/charts.py +230 -230
  16. scalebox/code_interpreter/code_interpreter_async.py +3 -1
  17. scalebox/code_interpreter/code_interpreter_sync.py +3 -1
  18. scalebox/code_interpreter/constants.py +3 -3
  19. scalebox/code_interpreter/exceptions.py +13 -13
  20. scalebox/code_interpreter/models.py +485 -485
  21. scalebox/connection_config.py +36 -1
  22. scalebox/csx_connect/__init__.py +1 -1
  23. scalebox/csx_connect/client.py +485 -485
  24. scalebox/csx_desktop/main.py +651 -651
  25. scalebox/exceptions.py +83 -83
  26. scalebox/generated/api.py +61 -61
  27. scalebox/generated/api_pb2.py +203 -203
  28. scalebox/generated/api_pb2.pyi +956 -956
  29. scalebox/generated/api_pb2_connect.py +1407 -1407
  30. scalebox/generated/rpc.py +50 -50
  31. scalebox/sandbox/main.py +146 -139
  32. scalebox/sandbox/sandbox_api.py +105 -91
  33. scalebox/sandbox/signature.py +40 -40
  34. scalebox/sandbox/utils.py +34 -34
  35. scalebox/sandbox_async/main.py +226 -44
  36. scalebox/sandbox_async/sandbox_api.py +124 -3
  37. scalebox/sandbox_sync/main.py +205 -130
  38. scalebox/sandbox_sync/sandbox_api.py +119 -3
  39. scalebox/test/CODE_INTERPRETER_TESTS_READY.md +323 -323
  40. scalebox/test/README.md +329 -329
  41. scalebox/test/bedrock_openai_adapter.py +73 -0
  42. scalebox/test/code_interpreter_test.py +34 -34
  43. scalebox/test/code_interpreter_test_sync.py +34 -34
  44. scalebox/test/run_stress_code_interpreter_sync.py +178 -0
  45. scalebox/test/simple_upload_example.py +131 -0
  46. scalebox/test/stabitiy_test.py +323 -0
  47. scalebox/test/test_browser_use.py +27 -0
  48. scalebox/test/test_browser_use_scalebox.py +62 -0
  49. scalebox/test/test_code_interpreter_execcode.py +289 -211
  50. scalebox/test/test_code_interpreter_sync_comprehensive.py +116 -69
  51. scalebox/test/test_connect_pause_async.py +300 -0
  52. scalebox/test/test_connect_pause_sync.py +300 -0
  53. scalebox/test/test_csx_desktop_examples.py +3 -3
  54. scalebox/test/test_desktop_sandbox_sf.py +112 -0
  55. scalebox/test/test_download_url.py +41 -0
  56. scalebox/test/test_existing_sandbox.py +1037 -0
  57. scalebox/test/test_sandbox_async_comprehensive.py +5 -3
  58. scalebox/test/test_sandbox_object_storage_example.py +151 -0
  59. scalebox/test/test_sandbox_object_storage_example_async.py +159 -0
  60. scalebox/test/test_sandbox_sync_comprehensive.py +1 -1
  61. scalebox/test/test_sf.py +141 -0
  62. scalebox/test/test_watch_dir_async.py +58 -0
  63. scalebox/test/testacreate.py +1 -1
  64. scalebox/test/testagetinfo.py +1 -3
  65. scalebox/test/testcomputeuse.py +243 -243
  66. scalebox/test/testsandbox_api.py +5 -5
  67. scalebox/test/testsandbox_async.py +17 -47
  68. scalebox/test/testsandbox_sync.py +19 -15
  69. scalebox/test/upload_100mb_example.py +377 -0
  70. scalebox/utils/httpcoreclient.py +297 -297
  71. scalebox/utils/httpxclient.py +403 -403
  72. scalebox/version.py +2 -2
  73. {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/METADATA +1 -1
  74. {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/RECORD +78 -60
  75. {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/WHEEL +1 -1
  76. {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/entry_points.txt +0 -0
  77. {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/licenses/LICENSE +0 -0
  78. {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/top_level.txt +0 -0
@@ -1,40 +1,40 @@
1
- import base64
2
- import hashlib
3
- import time
4
- from typing import Literal, Optional, TypedDict
5
-
6
- Operation = Literal["read", "write"]
7
-
8
-
9
- class Signature(TypedDict):
10
- signature: str
11
- expiration: Optional[int] # Unix timestamp or None
12
-
13
-
14
- def get_signature(
15
- path: str,
16
- operation: Operation,
17
- user: str,
18
- envd_access_token: Optional[str],
19
- expiration_in_seconds: Optional[int] = None,
20
- ) -> Signature:
21
- """
22
- Generate a v1 signature for sandbox file URLs.
23
- """
24
- if not envd_access_token:
25
- raise ValueError("Access token is not set and signature cannot be generated!")
26
-
27
- expiration = (
28
- int(time.time()) + expiration_in_seconds if expiration_in_seconds else None
29
- )
30
-
31
- raw = (
32
- f"{path}:{operation}:{user}:{envd_access_token}"
33
- if expiration is None
34
- else f"{path}:{operation}:{user}:{envd_access_token}:{expiration}"
35
- )
36
-
37
- digest = hashlib.sha256(raw.encode("utf-8")).digest()
38
- encoded = base64.b64encode(digest).rstrip(b"=").decode("ascii")
39
-
40
- return {"signature": f"v1_{encoded}", "expiration": expiration}
1
+ import base64
2
+ import hashlib
3
+ import time
4
+ from typing import Literal, Optional, TypedDict
5
+
6
+ Operation = Literal["read", "write"]
7
+
8
+
9
+ class Signature(TypedDict):
10
+ signature: str
11
+ expiration: Optional[int] # Unix timestamp or None
12
+
13
+
14
+ def get_signature(
15
+ path: str,
16
+ operation: Operation,
17
+ user: str,
18
+ envd_access_token: Optional[str],
19
+ expiration_in_seconds: Optional[int] = None,
20
+ ) -> Signature:
21
+ """
22
+ Generate a v1 signature for sandbox file URLs.
23
+ """
24
+ if not envd_access_token:
25
+ raise ValueError("Access token is not set and signature cannot be generated!")
26
+
27
+ expiration = (
28
+ int(time.time()) + expiration_in_seconds if expiration_in_seconds else None
29
+ )
30
+
31
+ raw = (
32
+ f"{path}:{operation}:{user}:{envd_access_token}"
33
+ if expiration is None
34
+ else f"{path}:{operation}:{user}:{envd_access_token}:{expiration}"
35
+ )
36
+ print(raw)
37
+ digest = hashlib.sha256(raw.encode("utf-8")).digest()
38
+ encoded = base64.b64encode(digest).rstrip(b"=").decode("ascii")
39
+
40
+ return {"signature": f"v1_{encoded}", "expiration": expiration}
scalebox/sandbox/utils.py CHANGED
@@ -1,34 +1,34 @@
1
- import functools
2
- from typing import Any, Optional, Type, TypeVar, cast
3
-
4
- T = TypeVar("T")
5
-
6
-
7
- class class_method_variant(object):
8
- def __init__(self, class_method_name):
9
- self.class_method_name = class_method_name
10
-
11
- method: Any
12
-
13
- def __call__(self, method: T) -> T:
14
- self.method = method
15
- return cast(T, self)
16
-
17
- def __get__(self, obj, objtype: Optional[Type[Any]] = None):
18
- @functools.wraps(self.method)
19
- def _wrapper(*args, **kwargs):
20
- if obj is not None:
21
- # Method was called as an instance method, e.g.
22
- # instance.method(...)
23
- return self.method(obj, *args, **kwargs)
24
- elif len(args) > 0 and objtype is not None and isinstance(args[0], objtype):
25
- # Method was called as a class method with the instance as the
26
- # first argument, e.g. Class.method(instance, ...) which in
27
- # Python is the same thing as calling an instance method
28
- return self.method(args[0], *args[1:], **kwargs)
29
- else:
30
- # Method was called as a class method, e.g. Class.method(...)
31
- class_method = getattr(objtype, self.class_method_name)
32
- return class_method(*args, **kwargs)
33
-
34
- return _wrapper
1
+ import functools
2
+ from typing import Any, Optional, Type, TypeVar, cast
3
+
4
+ T = TypeVar("T")
5
+
6
+
7
+ class class_method_variant(object):
8
+ def __init__(self, class_method_name):
9
+ self.class_method_name = class_method_name
10
+
11
+ method: Any
12
+
13
+ def __call__(self, method: T) -> T:
14
+ self.method = method
15
+ return cast(T, self)
16
+
17
+ def __get__(self, obj, objtype: Optional[Type[Any]] = None):
18
+ @functools.wraps(self.method)
19
+ def _wrapper(*args, **kwargs):
20
+ if obj is not None:
21
+ # Method was called as an instance method, e.g.
22
+ # instance.method(...)
23
+ return self.method(obj, *args, **kwargs)
24
+ elif len(args) > 0 and objtype is not None and isinstance(args[0], objtype):
25
+ # Method was called as a class method with the instance as the
26
+ # first argument, e.g. Class.method(instance, ...) which in
27
+ # Python is the same thing as calling an instance method
28
+ return self.method(args[0], *args[1:], **kwargs)
29
+ else:
30
+ # Method was called as a class method, e.g. Class.method(...)
31
+ class_method = getattr(objtype, self.class_method_name)
32
+ return class_method(*args, **kwargs)
33
+
34
+ return _wrapper
@@ -6,10 +6,10 @@ from typing import Dict, List, Optional, TypedDict, overload
6
6
  import aiohttp
7
7
  import httpx
8
8
  from aiohttp import TCPConnector
9
- from typing_extensions import Unpack
9
+ from typing_extensions import Unpack, Self
10
10
 
11
11
  from ..api.client.types import Unset
12
- from ..connection_config import ConnectionConfig, ProxyTypes
12
+ from ..connection_config import ConnectionConfig, ProxyTypes, ApiParams
13
13
  from ..exceptions import SandboxException, request_timeout_error
14
14
  from ..generated.api import ENVD_API_HEALTH_ROUTE, ahandle_envd_api_exception
15
15
  from ..sandbox.main import SandboxSetup
@@ -41,6 +41,8 @@ class AsyncSandboxOpts(TypedDict):
41
41
  envd_version: Optional[str]
42
42
  envd_access_token: Optional[str]
43
43
  connection_config: ConnectionConfig
44
+ object_storage: Optional[Dict[str, str]]
45
+ network_proxy: Optional[Dict[str, any]]
44
46
 
45
47
 
46
48
  class AsyncSandbox(SandboxSetup, SandboxApi):
@@ -101,6 +103,17 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
101
103
  """
102
104
  return self._sandbox_domain
103
105
 
106
+ @property
107
+ def object_storage(self) -> Optional[Dict[str, str]]:
108
+ """
109
+ Object storage configuration returned during sandbox creation (if any).
110
+ Only synchronous sandboxes currently expose this field.
111
+ """
112
+ return self._object_storage
113
+
114
+ def network_proxy(self) -> Optional[Dict[str, object]]:
115
+ return self._network_proxy
116
+
104
117
  @property
105
118
  def envd_api_url(self) -> str:
106
119
  return self._envd_api_url
@@ -126,7 +139,8 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
126
139
  super().__init__()
127
140
 
128
141
  self._connection_config = opts["connection_config"]
129
-
142
+ self._object_storage = opts["object_storage"]
143
+ self._network_proxy = opts["network_proxy"]
130
144
  self._sandbox_id = opts["sandbox_id"]
131
145
  self._sandbox_domain = opts["sandbox_domain"] or self.connection_config.domain
132
146
  debug=self._connection_config.debug
@@ -222,13 +236,16 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
222
236
  timeout: Optional[int] = None,
223
237
  metadata: Optional[Dict[str, str]] = None,
224
238
  envs: Optional[Dict[str, str]] = None,
239
+ object_storage: Optional[Dict[str, str]] = None,
225
240
  api_key: Optional[str] = None,
226
241
  domain: Optional[str] = None,
227
242
  debug: Optional[bool] = None,
243
+ sandbox_id: Optional[str] = None,
228
244
  request_timeout: Optional[float] = None,
229
245
  proxy: Optional[ProxyTypes] = None,
230
246
  secure: Optional[bool] = None,
231
247
  allow_internet_access: Optional[bool] = True,
248
+ net_proxy_country: Optional[str] = None,
232
249
  ):
233
250
  """
234
251
  Create a new sandbox.
@@ -251,11 +268,32 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
251
268
  """
252
269
 
253
270
  connection_headers = {"Authorization": "Bearer root", }
271
+ network_proxy = {}
254
272
  if debug:
255
273
  sandbox_id = "debug_sandbox_id"
256
274
  sandbox_domain = None
257
275
  envd_version = None
258
276
  envd_access_token = None
277
+ elif sandbox_id is not None:
278
+ response = await SandboxApi._cls_get_info(
279
+ sandbox_id,
280
+ api_key=api_key,
281
+ domain=domain,
282
+ debug=debug,
283
+ request_timeout=request_timeout,
284
+ proxy=proxy,
285
+ )
286
+
287
+ sandbox_domain = response.sandbox_domain
288
+ envd_version = response.envd_version
289
+ envd_access_token = response._envd_access_token
290
+ object_storage = response.object_storage
291
+ network_proxy = response.network_proxy
292
+
293
+ if response._envd_access_token is not None and not isinstance(
294
+ response._envd_access_token, Unset
295
+ ):
296
+ connection_headers["X-Access-Token"] = response._envd_access_token
259
297
  else:
260
298
  response = await SandboxApi._create_sandbox(
261
299
  template=template or cls.default_template,
@@ -269,12 +307,16 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
269
307
  secure=secure,
270
308
  proxy=proxy,
271
309
  allow_internet_access=allow_internet_access,
310
+ object_storage=object_storage,
311
+ net_proxy_country=net_proxy_country,
272
312
  )
273
313
 
274
314
  sandbox_id = response.sandbox_id
275
315
  sandbox_domain = response.sandbox_domain
276
316
  envd_version = response.envd_version
277
317
  envd_access_token = response.envd_access_token
318
+ object_storage = response.object_storage
319
+ network_proxy = response.network_proxy
278
320
 
279
321
  if envd_access_token is not None and not isinstance(
280
322
  envd_access_token, Unset
@@ -297,6 +339,8 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
297
339
  envd_version=envd_version,
298
340
  envd_access_token=envd_access_token,
299
341
  connection_config=connection_config,
342
+ object_storage=object_storage,
343
+ network_proxy=network_proxy
300
344
  )
301
345
  timeout = 10.0
302
346
  interval = 0.3
@@ -312,69 +356,158 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
312
356
  time.sleep(interval)
313
357
  elapsed += interval
314
358
  else:
315
- print("connect "+sandbox_domain+ENVD_API_HEALTH_ROUTE +" timeout 5s")
359
+ print("connect "+sandbox_domain+ENVD_API_HEALTH_ROUTE +" timeout 10s")
316
360
  return sanbox
317
361
 
362
+ @overload
363
+ async def connect(
364
+ self,
365
+ timeout: Optional[int] = None,
366
+ **opts: Unpack[ApiParams],
367
+ ) -> Self:
368
+ """
369
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
370
+ Sandbox must be either running or be paused.
371
+
372
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
373
+
374
+ :param timeout: Timeout for the sandbox in **seconds**
375
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
376
+ :return: A running sandbox instance
377
+
378
+ @example
379
+ ```python
380
+ sandbox = await AsyncSandbox.create()
381
+ await sandbox.beta_pause()
382
+
383
+ # Another code block
384
+ same_sandbox = await sandbox.connect()
385
+ ```
386
+ """
387
+ ...
388
+
389
+ @overload
318
390
  @classmethod
319
391
  async def connect(
320
- cls,
321
- sandbox_id: str,
322
- api_key: Optional[str] = None,
323
- domain: Optional[str] = None,
324
- debug: Optional[bool] = None,
325
- proxy: Optional[ProxyTypes] = None,
326
- ):
392
+ cls,
393
+ sandbox_id: str,
394
+ timeout: Optional[int] = None,
395
+ **opts: Unpack[ApiParams],
396
+ ) -> Self:
327
397
  """
328
- Connect to an existing sandbox.
398
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
399
+ Sandbox must be either running or be paused.
400
+
329
401
  With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
330
402
 
331
403
  :param sandbox_id: Sandbox ID
332
- :param api_key: SBX API Key to use for authentication, defaults to `SBX_API_KEY` environment variable
333
- :param proxy: Proxy to use for the request and for the **requests made to the returned sandbox**
334
-
335
- :return: sandbox instance for the existing sandbox
404
+ :param timeout: Timeout for the sandbox in **seconds**
405
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
406
+ :return: A running sandbox instance
336
407
 
337
408
  @example
338
409
  ```python
339
410
  sandbox = await AsyncSandbox.create()
340
- sandbox_id = sandbox.sandbox_id
411
+ await AsyncSandbox.beta_pause(sandbox.sandbox_id)
341
412
 
342
413
  # Another code block
343
- same_sandbox = await AsyncSandbox.connect(sandbox_id)
414
+ same_sandbox = await AsyncSandbox.connect(sandbox.sandbox_id))
415
+ ```
344
416
  """
417
+ ...
345
418
 
346
- connection_headers = {"Authorization": "Bearer root"}
419
+ @class_method_variant("_cls_connect")
420
+ async def connect(
421
+ self,
422
+ timeout: Optional[int] = None,
423
+ **opts: Unpack[ApiParams],
424
+ ) -> Self:
425
+ """
426
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
427
+ Sandbox must be either running or be paused.
347
428
 
348
- response = await SandboxApi._cls_get_info(
349
- sandbox_id,
350
- api_key=api_key,
351
- domain=domain,
352
- debug=debug,
353
- proxy=proxy,
354
- )
429
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
355
430
 
356
- if response._envd_access_token is not None and not isinstance(
357
- response._envd_access_token, Unset
358
- ):
359
- connection_headers["X-Access-Token"] = response._envd_access_token
360
- print("connection_headers:"+str(connection_headers))
431
+ :param timeout: Timeout for the sandbox in **seconds**
432
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
433
+ :return: A running sandbox instance
361
434
 
362
- connection_config = ConnectionConfig(
363
- api_key=api_key,
364
- domain=domain,
365
- debug=debug,
366
- headers=connection_headers,
367
- proxy=proxy,
368
- )
435
+ @example
436
+ ```python
437
+ sandbox = await AsyncSandbox.create()
438
+ await sandbox.beta_pause()
369
439
 
370
- return cls(
371
- sandbox_id=sandbox_id,
372
- sandbox_domain=response.sandbox_domain,
373
- connection_config=connection_config,
374
- envd_version=response.envd_version,
375
- envd_access_token=response._envd_access_token,
440
+ # Another code block
441
+ same_sandbox = await sandbox.connect()
442
+ ```
443
+ """
444
+ await SandboxApi._cls_connect(
445
+ sandbox_id=self.sandbox_id,
446
+ timeout=timeout,
447
+ **opts,
376
448
  )
377
449
 
450
+ return self
451
+ # @classmethod
452
+ # async def connect(
453
+ # cls,
454
+ # sandbox_id: str,
455
+ # api_key: Optional[str] = None,
456
+ # domain: Optional[str] = None,
457
+ # debug: Optional[bool] = None,
458
+ # proxy: Optional[ProxyTypes] = None,
459
+ # ):
460
+ # """
461
+ # Connect to an existing sandbox.
462
+ # With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
463
+ #
464
+ # :param sandbox_id: Sandbox ID
465
+ # :param api_key: SBX API Key to use for authentication, defaults to `SBX_API_KEY` environment variable
466
+ # :param proxy: Proxy to use for the request and for the **requests made to the returned sandbox**
467
+ #
468
+ # :return: sandbox instance for the existing sandbox
469
+ #
470
+ # @example
471
+ # ```python
472
+ # sandbox = await AsyncSandbox.create()
473
+ # sandbox_id = sandbox.sandbox_id
474
+ #
475
+ # # Another code block
476
+ # same_sandbox = await AsyncSandbox.connect(sandbox_id)
477
+ # """
478
+ #
479
+ # connection_headers = {"Authorization": "Bearer root"}
480
+ #
481
+ # response = await SandboxApi._cls_get_info(
482
+ # sandbox_id,
483
+ # api_key=api_key,
484
+ # domain=domain,
485
+ # debug=debug,
486
+ # proxy=proxy,
487
+ # )
488
+ #
489
+ # if response._envd_access_token is not None and not isinstance(
490
+ # response._envd_access_token, Unset
491
+ # ):
492
+ # connection_headers["X-Access-Token"] = response._envd_access_token
493
+ # print("connection_headers:"+str(connection_headers))
494
+ #
495
+ # connection_config = ConnectionConfig(
496
+ # api_key=api_key,
497
+ # domain=domain,
498
+ # debug=debug,
499
+ # headers=connection_headers,
500
+ # proxy=proxy,
501
+ # )
502
+ #
503
+ # return cls(
504
+ # sandbox_id=sandbox_id,
505
+ # sandbox_domain=response.sandbox_domain,
506
+ # connection_config=connection_config,
507
+ # envd_version=response.envd_version,
508
+ # envd_access_token=response._envd_access_token,
509
+ # )
510
+
378
511
  async def __aenter__(self):
379
512
  if self._session.closed:
380
513
  connector = TCPConnector(
@@ -645,3 +778,52 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
645
778
  end=end,
646
779
  **config_dict,
647
780
  )
781
+
782
+ @overload
783
+ async def beta_pause(
784
+ self,
785
+ **opts: Unpack[ApiParams],
786
+ ) -> None:
787
+ """
788
+ [BETA] This feature is in beta and may change in the future.
789
+
790
+ Pause the sandbox.
791
+
792
+ :return: Sandbox ID that can be used to resume the sandbox
793
+ """
794
+ ...
795
+
796
+ @overload
797
+ @staticmethod
798
+ async def beta_pause(
799
+ sandbox_id: str,
800
+ **opts: Unpack[ApiParams],
801
+ ) -> None:
802
+ """
803
+ [BETA] This feature is in beta and may change in the future.
804
+
805
+ Pause the sandbox specified by sandbox ID.
806
+
807
+ :param sandbox_id: Sandbox ID
808
+
809
+ :return: Sandbox ID that can be used to resume the sandbox
810
+ """
811
+ ...
812
+
813
+ @class_method_variant("_cls_pause")
814
+ async def beta_pause(
815
+ self,
816
+ **opts: Unpack[ApiParams],
817
+ ) -> None:
818
+ """
819
+ [BETA] This feature is in beta and may change in the future.
820
+
821
+ Pause the sandbox.
822
+
823
+ :return: Sandbox ID that can be used to resume the sandbox
824
+ """
825
+
826
+ await SandboxApi._cls_pause(
827
+ sandbox_id=self.sandbox_id,
828
+ **opts,
829
+ )