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,9 +1,14 @@
1
1
  import datetime
2
2
  import urllib.parse
3
- from typing import Dict, List, Optional
3
+ from typing import Dict, List, Optional, Unpack, TYPE_CHECKING
4
4
 
5
5
  from packaging.version import Version
6
6
 
7
+ if TYPE_CHECKING:
8
+ from . import AsyncSandbox
9
+
10
+ from ..api.client.api.sandboxes import post_sandboxes_sandbox_id_pause, post_sandboxes_sandbox_id_connect
11
+ from ..api.client.models.connect_sandbox import ConnectSandbox
7
12
  from ..api import AsyncApiClient, SandboxCreateResponse, handle_api_exception
8
13
  from ..api.client.api.sandboxes import (
9
14
  delete_sandboxes_sandbox_id,
@@ -14,8 +19,8 @@ from ..api.client.api.sandboxes import (
14
19
  post_sandboxes_sandbox_id_timeout,
15
20
  )
16
21
  from ..api.client.models import Error, NewSandbox, PostSandboxesSandboxIDTimeoutBody
17
- from ..connection_config import ConnectionConfig, ProxyTypes
18
- from ..exceptions import SandboxException, TemplateException
22
+ from ..connection_config import ConnectionConfig, ProxyTypes, ApiParams
23
+ from ..exceptions import SandboxException, TemplateException, NotFoundException
19
24
  from ..sandbox.sandbox_api import (
20
25
  ListedSandbox,
21
26
  SandboxApiBase,
@@ -97,6 +102,9 @@ class SandboxApi(SandboxApiBase):
97
102
  memory_mb=sandbox.memory_mb,
98
103
  started_at=sandbox.started_at,
99
104
  end_at=sandbox.end_at,
105
+ object_storage=sandbox.object_storage,
106
+ uptime=sandbox.uptime,
107
+ timeout=sandbox.timeout,
100
108
  )
101
109
  for sandbox in res.parsed
102
110
  ]
@@ -160,6 +168,9 @@ class SandboxApi(SandboxApiBase):
160
168
  end_at=res.parsed.end_at,
161
169
  envd_version=res.parsed.envd_version,
162
170
  _envd_access_token=res.parsed.envd_access_token,
171
+ object_storage=res.parsed.object_storage,
172
+ uptime=res.parsed.uptime,
173
+ timeout=res.parsed.timeout,
163
174
  )
164
175
 
165
176
  @classmethod
@@ -256,6 +267,8 @@ class SandboxApi(SandboxApiBase):
256
267
  headers: Optional[Dict[str, str]] = None,
257
268
  proxy: Optional[ProxyTypes] = None,
258
269
  allow_internet_access: Optional[bool] = True,
270
+ object_storage: Optional[Dict[str, str]] = None,
271
+ net_proxy_country:Optional[str] = None,
259
272
  ) -> SandboxCreateResponse:
260
273
  config = ConnectionConfig(
261
274
  api_key=api_key,
@@ -279,6 +292,8 @@ class SandboxApi(SandboxApiBase):
279
292
  secure=secure or False,
280
293
  allow_internet_access=allow_internet_access,
281
294
  is_async=False,
295
+ object_storage=object_storage,
296
+ net_proxy_country=net_proxy_country,
282
297
  ),
283
298
  client=api_client,
284
299
  )
@@ -301,6 +316,8 @@ class SandboxApi(SandboxApiBase):
301
316
  sandbox_domain=res.parsed.domain,
302
317
  envd_version=res.parsed.envd_version,
303
318
  envd_access_token=res.parsed.envd_access_token,
319
+ object_storage=res.parsed.object_storage,
320
+ network_proxy=res.parsed.network_proxy,
304
321
  )
305
322
 
306
323
  @classmethod
@@ -363,3 +380,107 @@ class SandboxApi(SandboxApiBase):
363
380
  )
364
381
  for metric in res.parsed
365
382
  ]
383
+
384
+ @classmethod
385
+ async def _cls_pause(
386
+ cls,
387
+ sandbox_id: str,
388
+ **opts: Unpack[ApiParams],
389
+ ) -> str:
390
+ config = ConnectionConfig(**opts)
391
+
392
+ async with AsyncApiClient(
393
+ config,
394
+ limits=SandboxApiBase._limits,
395
+ ) as api_client:
396
+ res = await post_sandboxes_sandbox_id_pause.asyncio_detailed(
397
+ sandbox_id,
398
+ client=api_client,
399
+ )
400
+
401
+ if res.status_code == 404:
402
+ raise NotFoundException(f"Sandbox {sandbox_id} not found")
403
+
404
+ if res.status_code == 409:
405
+ return sandbox_id
406
+
407
+ if res.status_code >= 300:
408
+ raise handle_api_exception(res)
409
+
410
+ # Check if res.parse is Error
411
+ if isinstance(res.parsed, Error):
412
+ raise SandboxException(f"{res.parsed.message}: Request failed")
413
+
414
+ return sandbox_id
415
+
416
+ @classmethod
417
+ async def _cls_connect(
418
+ cls,
419
+ sandbox_id: str,
420
+ timeout: Optional[int] = None,
421
+ **opts: Unpack[ApiParams],
422
+ ) -> "AsyncSandbox":
423
+ timeout = timeout or SandboxApiBase.default_sandbox_timeout
424
+
425
+ # Sandbox is not running, resume it
426
+ config = ConnectionConfig(**opts)
427
+
428
+ async with AsyncApiClient(
429
+ config,
430
+ limits=SandboxApiBase._limits,
431
+ ) as api_client:
432
+ res = await post_sandboxes_sandbox_id_connect.asyncio_detailed(
433
+ sandbox_id,
434
+ client=api_client,
435
+ body=ConnectSandbox(timeout=timeout),
436
+ )
437
+
438
+ if res.status_code == 404:
439
+ raise NotFoundException(f"Paused sandbox {sandbox_id} not found")
440
+
441
+ if res.status_code >= 300:
442
+ raise handle_api_exception(res)
443
+
444
+ # Check if res.parse is Error
445
+ if isinstance(res.parsed, Error):
446
+ raise SandboxException(f"{res.parsed.message}: Request failed")
447
+
448
+ # Extract information from API response and create a full AsyncSandbox instance
449
+ # Use delayed import to avoid circular dependency
450
+ from . import AsyncSandbox
451
+
452
+ response = res.parsed
453
+ if response is None:
454
+ raise SandboxException("Connect response is None")
455
+
456
+ connection_headers = {"Authorization": "Bearer root"}
457
+
458
+ # Extract fields from API response
459
+ sandbox_domain = response.domain if hasattr(response, 'domain') and response.domain is not None else None
460
+ envd_version = response.envd_version if hasattr(response, 'envd_version') else None
461
+ envd_access_token = None
462
+ if hasattr(response, 'envd_access_token'):
463
+ from ..api.client.types import Unset
464
+ if response.envd_access_token is not None and not isinstance(response.envd_access_token, Unset):
465
+ envd_access_token = response.envd_access_token
466
+ connection_headers["X-Access-Token"] = envd_access_token
467
+
468
+ connection_config = ConnectionConfig(
469
+ api_key=config.api_key,
470
+ domain=config.domain,
471
+ debug=config.debug,
472
+ request_timeout=config.request_timeout,
473
+ headers=connection_headers,
474
+ proxy=config.proxy,
475
+ )
476
+
477
+ # Create and return a full AsyncSandbox instance
478
+ sandbox = AsyncSandbox(
479
+ sandbox_id=sandbox_id,
480
+ sandbox_domain=sandbox_domain,
481
+ envd_version=envd_version,
482
+ envd_access_token=envd_access_token,
483
+ connection_config=connection_config,
484
+ )
485
+
486
+ return sandbox
@@ -8,10 +8,11 @@ import httpx
8
8
  import urllib3
9
9
  from httpx import Timeout
10
10
  from packaging.version import Version
11
+ from typing_extensions import Self, Unpack
11
12
  from urllib3 import Retry
12
13
 
13
14
  from ..api.client.types import Unset
14
- from ..connection_config import ConnectionConfig, ProxyTypes
15
+ from ..connection_config import ConnectionConfig, ProxyTypes,ApiParams
15
16
  from ..exceptions import SandboxException, request_timeout_error
16
17
  from ..generated.api import ENVD_API_HEALTH_ROUTE, handle_envd_api_exception
17
18
  from ..sandbox.main import SandboxSetup
@@ -101,6 +102,17 @@ class Sandbox(SandboxSetup, SandboxApi):
101
102
  """
102
103
  return self._sandbox_domain
103
104
 
105
+ @property
106
+ def object_storage(self) -> Optional[Dict[str, str]]:
107
+ """
108
+ Object storage configuration returned during sandbox creation (if any).
109
+ Only synchronous sandboxes currently expose this field.
110
+ """
111
+ return self._object_storage
112
+
113
+ def network_proxy(self) -> Optional[Dict[str, any]]:
114
+ return self._network_proxy
115
+
104
116
  @property
105
117
  def envd_api_url(self) -> str:
106
118
  return self._envd_api_url
@@ -153,104 +165,19 @@ class Sandbox(SandboxSetup, SandboxApi):
153
165
  """
154
166
  super().__init__()
155
167
  self._connection_config = opts["connection_config"]
156
-
168
+ self._object_storage = opts["object_storage"]
157
169
  self._sandbox_id = opts["sandbox_id"]
170
+ self._network_proxy = opts["network_proxy"]
158
171
  self._sandbox_domain = opts["sandbox_domain"] or self.connection_config.domain
159
172
  debug = self._connection_config.debug
160
- # connection_headers = {"Authorization": "Bearer root", }
161
-
162
- # if debug:
163
- # self._sandbox_id = "debug_sandbox_id"
164
- # self._sandbox_domain = None
165
- # self._envd_version = None
166
- # self._envd_access_token = None
167
- # elif sandbox_id is not None:
168
- # response = SandboxApi._cls_get_info(
169
- # sandbox_id,
170
- # api_key=api_key,
171
- # domain=domain,
172
- # debug=debug,
173
- # request_timeout=request_timeout,
174
- # proxy=proxy,
175
- # )
176
- #
177
- # self._sandbox_id = sandbox_id
178
- # self._sandbox_domain = response.sandbox_domain
179
- # self._envd_version = response.envd_version
180
- # self._envd_access_token = response._envd_access_token
181
- #
182
- # if response._envd_access_token is not None and not isinstance(
183
- # response._envd_access_token, Unset
184
- # ):
185
- # connection_headers["X-Access-Token"] = response._envd_access_token
186
- # else:
187
- # template = template or self.default_template
188
- # timeout = timeout or self.default_sandbox_timeout
189
- # response = SandboxApi._create_sandbox(
190
- # template=template,
191
- # api_key=api_key,
192
- # timeout=timeout,
193
- # metadata=metadata,
194
- # env_vars=envs,
195
- # domain=domain,
196
- # debug=debug,
197
- # request_timeout=request_timeout,
198
- # secure=secure or False,
199
- # proxy=proxy,
200
- # allow_internet_access=allow_internet_access,
201
- # )
202
- #
203
- # self._sandbox_id = response.sandbox_id
204
- # self._sandbox_domain = response.sandbox_domain
205
- # self._envd_version = response.envd_version
206
- #
207
- # if response.envd_access_token is not None and not isinstance(
208
- # response.envd_access_token, Unset
209
- # ):
210
- # self._envd_access_token = response.envd_access_token
211
- # connection_headers["X-Access-Token"] = response.envd_access_token
212
- # else:
213
- # self._envd_access_token = None
214
- # self._transport = TransportWithLogger(limits=self._limits, proxy=proxy)
215
- # self._connection_config = ConnectionConfig(
216
- # api_key=api_key,
217
- # domain=domain,
218
- # debug=debug,
219
- # request_timeout=request_timeout,
220
- # headers=connection_headers,
221
- # proxy=proxy,
222
- # )
173
+ self.__envd_access_token=opts["envd_access_token"]
223
174
 
224
175
  self._sandbox_domain = self._sandbox_domain or self._connection_config.domain
225
- # self._envd_api_url = f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(self.envd_port)}"
226
176
  if debug:
227
177
  self._envd_api_url = f"http://{self.get_host(8888)}"
228
- # elif self._sandbox_id is not None:
229
- # response = SandboxApi._cls_get_info(
230
- # self._sandbox_id,
231
- # api_key=self._api_key(),
232
- # domain=self._sandbox_domain,
233
- # debug=debug,
234
- # request_timeout=self.request_timeout,
235
- # proxy=self.proxy,
236
- # )
237
- #
238
- # self._sandbox_id = self._sandbox_id
239
- # self._sandbox_domain = response.sandbox_domain
240
- # self._envd_version = response.envd_version
241
- # self._envd_access_token = response._envd_access_token
242
- #
243
- # if response._envd_access_token is not None and not isinstance(
244
- # response._envd_access_token, Unset
245
- # ):
246
- # self._connection_config["X-Access-Token"] = response._envd_access_token
247
- # self._envd_api_url = f"http://{self.get_host(self.envd_port)}"
248
178
  else:
249
179
  self._envd_api_url = f"https://{self.get_host(self.envd_port)}"
250
180
  self._transport = TransportWithLogger(limits=self._limits, proxy=self._connection_config.proxy)
251
- # self._envd_api_url = f"http://localhost:8088"
252
- # self._envd_api_url = f"http://{self.get_host(self.envd_port)}"
253
- # self._envd_api_url = f"http://localhost:31000"
254
181
  self._envd_api = httpx.Client(
255
182
  base_url=self._envd_api_url,
256
183
  transport=self._transport,
@@ -317,6 +244,7 @@ class Sandbox(SandboxSetup, SandboxApi):
317
244
  timeout: Optional[int] = None,
318
245
  metadata: Optional[Dict[str, str]] = None,
319
246
  envs: Optional[Dict[str, str]] = None,
247
+ object_storage:Optional[Dict[str, str]] = None,
320
248
  api_key: Optional[str] = None,
321
249
  domain: Optional[str] = None,
322
250
  debug: Optional[bool] = None,
@@ -325,6 +253,7 @@ class Sandbox(SandboxSetup, SandboxApi):
325
253
  proxy: Optional[ProxyTypes] = None,
326
254
  secure: Optional[bool] = None,
327
255
  allow_internet_access: Optional[bool] = True,
256
+ net_proxy_country: Optional[str] = None
328
257
  ):
329
258
  """
330
259
  Create a new sandbox.
@@ -350,6 +279,7 @@ class Sandbox(SandboxSetup, SandboxApi):
350
279
  "Cannot set metadata or timeout when connecting to an existing sandbox. "
351
280
  "Use Sandbox.connect method instead.",
352
281
  )
282
+ network_proxy = {}
353
283
  connection_headers = {"Authorization": "Bearer root", }
354
284
  if debug:
355
285
  sandbox_id = "debug_sandbox_id"
@@ -369,6 +299,8 @@ class Sandbox(SandboxSetup, SandboxApi):
369
299
  sandbox_domain = response.sandbox_domain
370
300
  envd_version = response.envd_version
371
301
  envd_access_token = response._envd_access_token
302
+ object_storage = response.object_storage
303
+ network_proxy = response.network_proxy
372
304
 
373
305
  if response._envd_access_token is not None and not isinstance(
374
306
  response._envd_access_token, Unset
@@ -387,12 +319,16 @@ class Sandbox(SandboxSetup, SandboxApi):
387
319
  secure=secure,
388
320
  proxy=proxy,
389
321
  allow_internet_access=allow_internet_access,
322
+ object_storage=object_storage,
323
+ net_proxy_country=net_proxy_country
390
324
  )
391
325
 
392
326
  sandbox_id = response.sandbox_id
393
327
  sandbox_domain = response.sandbox_domain
394
328
  envd_version = response.envd_version
395
329
  envd_access_token = response.envd_access_token
330
+ object_storage = response.object_storage
331
+ network_proxy = response.network_proxy
396
332
 
397
333
  if envd_access_token is not None and not isinstance(
398
334
  envd_access_token, Unset
@@ -414,6 +350,8 @@ class Sandbox(SandboxSetup, SandboxApi):
414
350
  envd_version=envd_version,
415
351
  envd_access_token=envd_access_token,
416
352
  connection_config=connection_config,
353
+ object_storage=object_storage,
354
+ network_proxy=network_proxy
417
355
  )
418
356
 
419
357
  timeout = 10.0
@@ -429,7 +367,7 @@ class Sandbox(SandboxSetup, SandboxApi):
429
367
  time.sleep(interval)
430
368
  elapsed += interval
431
369
  else:
432
- print("connect "+sandbox_domain+ENVD_API_HEALTH_ROUTE +" timeout 5s")
370
+ print("connect "+sandbox_domain+ENVD_API_HEALTH_ROUTE +" timeout 10s")
433
371
  return sanbox
434
372
 
435
373
  def is_running(self, request_timeout: Optional[float] = None) -> bool:
@@ -468,64 +406,155 @@ class Sandbox(SandboxSetup, SandboxApi):
468
406
 
469
407
  return True
470
408
 
409
+ @overload
410
+ def connect(
411
+ self,
412
+ timeout: Optional[int] = None,
413
+ **opts: Unpack[ApiParams],
414
+ ) -> Self:
415
+ """
416
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
417
+ Sandbox must be either running or be paused.
418
+
419
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
420
+
421
+ :param timeout: Timeout for the sandbox in **seconds**
422
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
423
+ :return: A running sandbox instance
424
+
425
+ @example
426
+ ```python
427
+ sandbox = Sandbox.create()
428
+ sandbox.beta_pause()
429
+
430
+ # Another code block
431
+ same_sandbox = sandbox.connect()
432
+
433
+ :return: A running sandbox instance
434
+ """
435
+ ...
436
+
437
+ @overload
471
438
  @classmethod
472
439
  def connect(
473
- cls,
474
- sandbox_id: str,
475
- api_key: Optional[str] = None,
476
- domain: Optional[str] = None,
477
- debug: Optional[bool] = None,
478
- proxy: Optional[ProxyTypes] = None,
479
- ):
440
+ cls,
441
+ sandbox_id: str,
442
+ timeout: Optional[int] = None,
443
+ **opts: Unpack[ApiParams],
444
+ ) -> Self:
480
445
  """
481
- Connects to an existing Sandbox.
446
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
447
+ Sandbox must be either running or be paused.
448
+
482
449
  With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
483
450
 
484
451
  :param sandbox_id: Sandbox ID
485
- :param api_key: Scalebox API Key to use for authentication, defaults to `SBX_API_KEY` environment variable
486
- :param proxy: Proxy to use for the request and for the **requests made to the returned sandbox**
487
-
488
- :return: sandbox instance for the existing sandbox
452
+ :param timeout: Timeout for the sandbox in **seconds**.
453
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
454
+ :return: A running sandbox instance
489
455
 
490
456
  @example
491
457
  ```python
492
- sandbox = Sandbox()
493
- sandbox_id = sandbox.sandbox_id
458
+ sandbox = Sandbox.create()
459
+ Sandbox.beta_pause(sandbox.sandbox_id)
494
460
 
495
461
  # Another code block
496
- same_sandbox = Sandbox.connect(sandbox_id)
462
+ same_sandbox = Sandbox.connect(sandbox.sandbox_id)
497
463
  ```
498
464
  """
499
- connection_headers = {"Authorization": "Bearer root"}
465
+ ...
500
466
 
501
- response = SandboxApi._cls_get_info(
502
- sandbox_id,
503
- api_key=api_key,
504
- domain=domain,
505
- debug=debug,
506
- proxy=proxy,
507
- )
467
+ @class_method_variant("_cls_connect")
468
+ def connect(
469
+ self,
470
+ timeout: Optional[int] = None,
471
+ **opts: Unpack[ApiParams],
472
+ ) -> Self:
473
+ """
474
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
475
+ Sandbox must be either running or be paused.
508
476
 
509
- if response._envd_access_token is not None and not isinstance(
510
- response._envd_access_token, Unset
511
- ):
512
- connection_headers["X-Access-Token"] = response._envd_access_token
513
- connection_config = ConnectionConfig(
514
- api_key=api_key,
515
- domain=domain,
516
- debug=debug,
517
- headers=connection_headers,
518
- proxy=proxy,
519
- )
477
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
520
478
 
521
- return cls(
522
- sandbox_id=sandbox_id,
523
- sandbox_domain=response.sandbox_domain,
524
- envd_version=response.envd_version,
525
- envd_access_token=response._envd_access_token,
526
- connection_config=connection_config,
479
+ :param timeout: Timeout for the sandbox in **seconds**.
480
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
481
+ :return: A running sandbox instance
482
+
483
+ @example
484
+ ```python
485
+ sandbox = Sandbox.create()
486
+ sandbox.beta_pause()
487
+
488
+ # Another code block
489
+ same_sandbox = sandbox.connect()
490
+ ```
491
+ """
492
+ SandboxApi._cls_connect(
493
+ sandbox_id=self.sandbox_id,
494
+ timeout=timeout,
495
+ **opts,
527
496
  )
528
497
 
498
+ return self
499
+
500
+ # @classmethod
501
+ # def connect(
502
+ # cls,
503
+ # sandbox_id: str,
504
+ # api_key: Optional[str] = None,
505
+ # domain: Optional[str] = None,
506
+ # debug: Optional[bool] = None,
507
+ # proxy: Optional[ProxyTypes] = None,
508
+ # ):
509
+ # """
510
+ # Connects to an existing Sandbox.
511
+ # With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
512
+ #
513
+ # :param sandbox_id: Sandbox ID
514
+ # :param api_key: Scalebox API Key to use for authentication, defaults to `SBX_API_KEY` environment variable
515
+ # :param proxy: Proxy to use for the request and for the **requests made to the returned sandbox**
516
+ #
517
+ # :return: sandbox instance for the existing sandbox
518
+ #
519
+ # @example
520
+ # ```python
521
+ # sandbox = Sandbox()
522
+ # sandbox_id = sandbox.sandbox_id
523
+ #
524
+ # # Another code block
525
+ # same_sandbox = Sandbox.connect(sandbox_id)
526
+ # ```
527
+ # """
528
+ # connection_headers = {"Authorization": "Bearer root"}
529
+ #
530
+ # response = SandboxApi._cls_get_info(
531
+ # sandbox_id,
532
+ # api_key=api_key,
533
+ # domain=domain,
534
+ # debug=debug,
535
+ # proxy=proxy,
536
+ # )
537
+ #
538
+ # if response._envd_access_token is not None and not isinstance(
539
+ # response._envd_access_token, Unset
540
+ # ):
541
+ # connection_headers["X-Access-Token"] = response._envd_access_token
542
+ # connection_config = ConnectionConfig(
543
+ # api_key=api_key,
544
+ # domain=domain,
545
+ # debug=debug,
546
+ # headers=connection_headers,
547
+ # proxy=proxy,
548
+ # )
549
+ #
550
+ # return cls(
551
+ # sandbox_id=sandbox_id,
552
+ # sandbox_domain=response.sandbox_domain,
553
+ # envd_version=response.envd_version,
554
+ # envd_access_token=response._envd_access_token,
555
+ # connection_config=connection_config,
556
+ # )
557
+
529
558
  def __enter__(self):
530
559
  return self
531
560
 
@@ -787,3 +816,49 @@ class Sandbox(SandboxSetup, SandboxApi):
787
816
  end=end,
788
817
  **config_dict,
789
818
  )
819
+
820
+ @overload
821
+ def beta_pause(
822
+ self,
823
+ **opts: Unpack[ApiParams],
824
+ ) -> None:
825
+ """
826
+ [BETA] This feature is in beta and may change in the future.
827
+
828
+ Pause the sandbox.
829
+ """
830
+ ...
831
+
832
+ @overload
833
+ @classmethod
834
+ def beta_pause(
835
+ cls,
836
+ sandbox_id: str,
837
+ **opts: Unpack[ApiParams],
838
+ ) -> None:
839
+ """
840
+ [BETA] This feature is in beta and may change in the future.
841
+
842
+ Pause the sandbox specified by sandbox ID.
843
+
844
+ :param sandbox_id: Sandbox ID
845
+ """
846
+ ...
847
+
848
+ @class_method_variant("_cls_pause")
849
+ def beta_pause(
850
+ self,
851
+ **opts: Unpack[ApiParams],
852
+ ) -> None:
853
+ """
854
+ [BETA] This feature is in beta and may change in the future.
855
+
856
+ Pause the sandbox.
857
+
858
+ :return: Sandbox ID that can be used to resume the sandbox
859
+ """
860
+
861
+ SandboxApi._cls_pause(
862
+ sandbox_id=self.sandbox_id,
863
+ **opts,
864
+ )