loopix-sdk 2.30.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 (238) hide show
  1. loopix/__init__.py +260 -0
  2. loopix/api/__init__.py +287 -0
  3. loopix/api/client/__init__.py +8 -0
  4. loopix/api/client/api/__init__.py +1 -0
  5. loopix/api/client/api/sandboxes/__init__.py +1 -0
  6. loopix/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +161 -0
  7. loopix/api/client/api/sandboxes/get_sandboxes.py +176 -0
  8. loopix/api/client/api/sandboxes/get_sandboxes_metrics.py +173 -0
  9. loopix/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +163 -0
  10. loopix/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +199 -0
  11. loopix/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +212 -0
  12. loopix/api/client/api/sandboxes/get_v2_sandboxes.py +230 -0
  13. loopix/api/client/api/sandboxes/get_v_2_sandboxes_sandbox_id_logs.py +254 -0
  14. loopix/api/client/api/sandboxes/post_sandboxes.py +172 -0
  15. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
  16. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +187 -0
  17. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +181 -0
  18. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +189 -0
  19. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_snapshots.py +195 -0
  20. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +193 -0
  21. loopix/api/client/api/sandboxes/put_sandboxes_sandbox_id_network.py +199 -0
  22. loopix/api/client/api/snapshots/__init__.py +1 -0
  23. loopix/api/client/api/snapshots/get_snapshots.py +202 -0
  24. loopix/api/client/api/tags/__init__.py +1 -0
  25. loopix/api/client/api/tags/delete_templates_tags.py +174 -0
  26. loopix/api/client/api/tags/get_templates_template_id_tags.py +172 -0
  27. loopix/api/client/api/tags/post_templates_tags.py +176 -0
  28. loopix/api/client/api/templates/__init__.py +1 -0
  29. loopix/api/client/api/templates/delete_templates_template_id.py +157 -0
  30. loopix/api/client/api/templates/get_templates.py +172 -0
  31. loopix/api/client/api/templates/get_templates_aliases_alias.py +167 -0
  32. loopix/api/client/api/templates/get_templates_template_id.py +195 -0
  33. loopix/api/client/api/templates/get_templates_template_id_builds_build_id_logs.py +272 -0
  34. loopix/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +232 -0
  35. loopix/api/client/api/templates/get_templates_template_id_files_hash.py +180 -0
  36. loopix/api/client/api/templates/patch_templates_template_id.py +183 -0
  37. loopix/api/client/api/templates/patch_v_2_templates_template_id.py +185 -0
  38. loopix/api/client/api/templates/post_templates.py +172 -0
  39. loopix/api/client/api/templates/post_templates_template_id.py +181 -0
  40. loopix/api/client/api/templates/post_templates_template_id_builds_build_id.py +170 -0
  41. loopix/api/client/api/templates/post_v2_templates.py +172 -0
  42. loopix/api/client/api/templates/post_v3_templates.py +176 -0
  43. loopix/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +192 -0
  44. loopix/api/client/api/volumes/__init__.py +1 -0
  45. loopix/api/client/api/volumes/delete_volumes_volume_id.py +161 -0
  46. loopix/api/client/api/volumes/get_volumes.py +140 -0
  47. loopix/api/client/api/volumes/get_volumes_volume_id.py +163 -0
  48. loopix/api/client/api/volumes/post_volumes.py +172 -0
  49. loopix/api/client/client.py +286 -0
  50. loopix/api/client/errors.py +16 -0
  51. loopix/api/client/models/__init__.py +185 -0
  52. loopix/api/client/models/admin_build_cancel_result.py +67 -0
  53. loopix/api/client/models/admin_sandbox_kill_result.py +67 -0
  54. loopix/api/client/models/assign_template_tags_request.py +67 -0
  55. loopix/api/client/models/assigned_template_tags.py +68 -0
  56. loopix/api/client/models/aws_registry.py +85 -0
  57. loopix/api/client/models/aws_registry_type.py +8 -0
  58. loopix/api/client/models/build_log_entry.py +89 -0
  59. loopix/api/client/models/build_status_reason.py +95 -0
  60. loopix/api/client/models/connect_sandbox.py +59 -0
  61. loopix/api/client/models/created_access_token.py +100 -0
  62. loopix/api/client/models/created_team_api_key.py +166 -0
  63. loopix/api/client/models/delete_template_tags_request.py +67 -0
  64. loopix/api/client/models/disk_metrics.py +91 -0
  65. loopix/api/client/models/error.py +67 -0
  66. loopix/api/client/models/gcp_registry.py +69 -0
  67. loopix/api/client/models/gcp_registry_type.py +8 -0
  68. loopix/api/client/models/general_registry.py +77 -0
  69. loopix/api/client/models/general_registry_type.py +8 -0
  70. loopix/api/client/models/identifier_masking_details.py +83 -0
  71. loopix/api/client/models/listed_sandbox.py +179 -0
  72. loopix/api/client/models/log_level.py +11 -0
  73. loopix/api/client/models/logs_direction.py +9 -0
  74. loopix/api/client/models/logs_source.py +9 -0
  75. loopix/api/client/models/machine_info.py +83 -0
  76. loopix/api/client/models/max_team_metric.py +78 -0
  77. loopix/api/client/models/mcp_type_0.py +44 -0
  78. loopix/api/client/models/new_access_token.py +59 -0
  79. loopix/api/client/models/new_sandbox.py +224 -0
  80. loopix/api/client/models/new_team_api_key.py +59 -0
  81. loopix/api/client/models/new_volume.py +59 -0
  82. loopix/api/client/models/node.py +160 -0
  83. loopix/api/client/models/node_detail.py +160 -0
  84. loopix/api/client/models/node_metrics.py +122 -0
  85. loopix/api/client/models/node_status.py +12 -0
  86. loopix/api/client/models/node_status_change.py +82 -0
  87. loopix/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +59 -0
  88. loopix/api/client/models/post_sandboxes_sandbox_id_snapshots_body.py +60 -0
  89. loopix/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +59 -0
  90. loopix/api/client/models/resumed_sandbox.py +68 -0
  91. loopix/api/client/models/sandbox.py +145 -0
  92. loopix/api/client/models/sandbox_auto_resume_config.py +60 -0
  93. loopix/api/client/models/sandbox_detail.py +267 -0
  94. loopix/api/client/models/sandbox_lifecycle.py +70 -0
  95. loopix/api/client/models/sandbox_log.py +70 -0
  96. loopix/api/client/models/sandbox_log_entry.py +93 -0
  97. loopix/api/client/models/sandbox_log_entry_fields.py +44 -0
  98. loopix/api/client/models/sandbox_logs.py +91 -0
  99. loopix/api/client/models/sandbox_logs_v2_response.py +73 -0
  100. loopix/api/client/models/sandbox_metric.py +126 -0
  101. loopix/api/client/models/sandbox_network_config.py +118 -0
  102. loopix/api/client/models/sandbox_network_config_rules.py +72 -0
  103. loopix/api/client/models/sandbox_network_rule.py +74 -0
  104. loopix/api/client/models/sandbox_network_transform.py +79 -0
  105. loopix/api/client/models/sandbox_network_transform_headers.py +47 -0
  106. loopix/api/client/models/sandbox_network_update_config.py +114 -0
  107. loopix/api/client/models/sandbox_network_update_config_rules.py +71 -0
  108. loopix/api/client/models/sandbox_on_timeout.py +9 -0
  109. loopix/api/client/models/sandbox_pause_request.py +62 -0
  110. loopix/api/client/models/sandbox_state.py +9 -0
  111. loopix/api/client/models/sandbox_volume_mount.py +67 -0
  112. loopix/api/client/models/sandboxes_with_metrics.py +59 -0
  113. loopix/api/client/models/snapshot_info.py +70 -0
  114. loopix/api/client/models/team.py +83 -0
  115. loopix/api/client/models/team_api_key.py +158 -0
  116. loopix/api/client/models/team_metric.py +86 -0
  117. loopix/api/client/models/team_user.py +75 -0
  118. loopix/api/client/models/template.py +225 -0
  119. loopix/api/client/models/template_alias_response.py +67 -0
  120. loopix/api/client/models/template_build.py +139 -0
  121. loopix/api/client/models/template_build_file_upload.py +70 -0
  122. loopix/api/client/models/template_build_info.py +126 -0
  123. loopix/api/client/models/template_build_logs_response.py +73 -0
  124. loopix/api/client/models/template_build_request.py +115 -0
  125. loopix/api/client/models/template_build_request_v2.py +88 -0
  126. loopix/api/client/models/template_build_request_v3.py +107 -0
  127. loopix/api/client/models/template_build_start_v2.py +184 -0
  128. loopix/api/client/models/template_build_status.py +11 -0
  129. loopix/api/client/models/template_legacy.py +207 -0
  130. loopix/api/client/models/template_request_response_v3.py +99 -0
  131. loopix/api/client/models/template_step.py +91 -0
  132. loopix/api/client/models/template_tag.py +78 -0
  133. loopix/api/client/models/template_update_request.py +59 -0
  134. loopix/api/client/models/template_update_response.py +59 -0
  135. loopix/api/client/models/template_with_builds.py +156 -0
  136. loopix/api/client/models/update_team_api_key.py +59 -0
  137. loopix/api/client/models/volume.py +67 -0
  138. loopix/api/client/models/volume_and_token.py +75 -0
  139. loopix/api/client/models/volume_token.py +59 -0
  140. loopix/api/client/py.typed +1 -0
  141. loopix/api/client/types.py +54 -0
  142. loopix/api/client_async/__init__.py +74 -0
  143. loopix/api/client_sync/__init__.py +73 -0
  144. loopix/api/metadata.py +14 -0
  145. loopix/connection_config.py +309 -0
  146. loopix/envd/api.py +170 -0
  147. loopix/envd/filesystem/filesystem_connect.py +193 -0
  148. loopix/envd/filesystem/filesystem_pb2.py +80 -0
  149. loopix/envd/filesystem/filesystem_pb2.pyi +272 -0
  150. loopix/envd/process/process_connect.py +174 -0
  151. loopix/envd/process/process_pb2.py +96 -0
  152. loopix/envd/process/process_pb2.pyi +316 -0
  153. loopix/envd/rpc.py +139 -0
  154. loopix/envd/versions.py +11 -0
  155. loopix/exceptions.py +133 -0
  156. loopix/io_utils.py +57 -0
  157. loopix/paginator.py +52 -0
  158. loopix/py.typed +0 -0
  159. loopix/sandbox/_git/__init__.py +85 -0
  160. loopix/sandbox/_git/args.py +363 -0
  161. loopix/sandbox/_git/auth.py +132 -0
  162. loopix/sandbox/_git/config.py +32 -0
  163. loopix/sandbox/_git/parse.py +222 -0
  164. loopix/sandbox/_git/types.py +149 -0
  165. loopix/sandbox/commands/command_handle.py +69 -0
  166. loopix/sandbox/commands/main.py +39 -0
  167. loopix/sandbox/filesystem/filesystem.py +337 -0
  168. loopix/sandbox/filesystem/watch_handle.py +70 -0
  169. loopix/sandbox/main.py +227 -0
  170. loopix/sandbox/mcp.py +1949 -0
  171. loopix/sandbox/network.py +8 -0
  172. loopix/sandbox/sandbox_api.py +624 -0
  173. loopix/sandbox/signature.py +47 -0
  174. loopix/sandbox/utils.py +34 -0
  175. loopix/sandbox_async/commands/command.py +396 -0
  176. loopix/sandbox_async/commands/command_handle.py +298 -0
  177. loopix/sandbox_async/commands/pty.py +257 -0
  178. loopix/sandbox_async/filesystem/filesystem.py +720 -0
  179. loopix/sandbox_async/filesystem/watch_handle.py +97 -0
  180. loopix/sandbox_async/git.py +1100 -0
  181. loopix/sandbox_async/main.py +987 -0
  182. loopix/sandbox_async/paginator.py +140 -0
  183. loopix/sandbox_async/sandbox_api.py +504 -0
  184. loopix/sandbox_async/utils.py +7 -0
  185. loopix/sandbox_domains.py +5 -0
  186. loopix/sandbox_sync/commands/command.py +420 -0
  187. loopix/sandbox_sync/commands/command_handle.py +239 -0
  188. loopix/sandbox_sync/commands/pty.py +279 -0
  189. loopix/sandbox_sync/filesystem/filesystem.py +710 -0
  190. loopix/sandbox_sync/filesystem/watch_handle.py +102 -0
  191. loopix/sandbox_sync/git.py +1077 -0
  192. loopix/sandbox_sync/main.py +975 -0
  193. loopix/sandbox_sync/paginator.py +140 -0
  194. loopix/sandbox_sync/sandbox_api.py +491 -0
  195. loopix/template/consts.py +45 -0
  196. loopix/template/dockerfile_parser.py +286 -0
  197. loopix/template/logger.py +232 -0
  198. loopix/template/main.py +1368 -0
  199. loopix/template/readycmd.py +144 -0
  200. loopix/template/types.py +194 -0
  201. loopix/template/utils.py +426 -0
  202. loopix/template_async/build_api.py +419 -0
  203. loopix/template_async/main.py +528 -0
  204. loopix/template_sync/build_api.py +409 -0
  205. loopix/template_sync/main.py +529 -0
  206. loopix/volume/client/__init__.py +8 -0
  207. loopix/volume/client/api/__init__.py +1 -0
  208. loopix/volume/client/api/volumes/__init__.py +1 -0
  209. loopix/volume/client/api/volumes/delete_volumecontent_volume_id_path.py +174 -0
  210. loopix/volume/client/api/volumes/get_volumecontent_volume_id_dir.py +204 -0
  211. loopix/volume/client/api/volumes/get_volumecontent_volume_id_file.py +179 -0
  212. loopix/volume/client/api/volumes/get_volumecontent_volume_id_path.py +176 -0
  213. loopix/volume/client/api/volumes/patch_volumecontent_volume_id_path.py +203 -0
  214. loopix/volume/client/api/volumes/post_volumecontent_volume_id_dir.py +239 -0
  215. loopix/volume/client/api/volumes/put_volumecontent_volume_id_file.py +259 -0
  216. loopix/volume/client/client.py +286 -0
  217. loopix/volume/client/errors.py +16 -0
  218. loopix/volume/client/models/__init__.py +13 -0
  219. loopix/volume/client/models/error.py +67 -0
  220. loopix/volume/client/models/patch_volumecontent_volume_id_path_body.py +77 -0
  221. loopix/volume/client/models/volume_entry_stat.py +145 -0
  222. loopix/volume/client/models/volume_entry_stat_type.py +11 -0
  223. loopix/volume/client/py.typed +1 -0
  224. loopix/volume/client/types.py +54 -0
  225. loopix/volume/client_async/__init__.py +88 -0
  226. loopix/volume/client_sync/__init__.py +80 -0
  227. loopix/volume/connection_config.py +145 -0
  228. loopix/volume/types.py +62 -0
  229. loopix/volume/utils.py +52 -0
  230. loopix/volume/volume_async.py +639 -0
  231. loopix/volume/volume_sync.py +639 -0
  232. loopix_connect/__init__.py +1 -0
  233. loopix_connect/client.py +534 -0
  234. loopix_connect/py.typed +0 -0
  235. loopix_sdk-2.30.0.dist-info/METADATA +98 -0
  236. loopix_sdk-2.30.0.dist-info/RECORD +238 -0
  237. loopix_sdk-2.30.0.dist-info/WHEEL +4 -0
  238. loopix_sdk-2.30.0.dist-info/licenses/LICENSE +9 -0
@@ -0,0 +1,987 @@
1
+ import datetime
2
+ import json
3
+ import logging
4
+ import shlex
5
+ import uuid
6
+ from typing import Dict, List, Optional, Union, overload
7
+
8
+ import httpx
9
+ from packaging.version import Version
10
+ from typing_extensions import Self, Unpack
11
+
12
+ from loopix.api import make_async_logging_event_hooks
13
+ from loopix.api.client.types import Unset
14
+ from loopix.api.client_async import get_envd_transport as get_transport
15
+ from loopix.connection_config import ApiParams, ConnectionConfig
16
+ from loopix.envd.api import ENVD_API_HEALTH_ROUTE, ahandle_envd_api_exception
17
+ from loopix.envd.versions import ENVD_DEBUG_FALLBACK
18
+ from loopix.exceptions import (
19
+ TemplateException,
20
+ format_request_timeout_error,
21
+ )
22
+ from loopix.sandbox.main import SandboxOpts
23
+ from loopix.sandbox.sandbox_api import (
24
+ McpServer,
25
+ SandboxLifecycle,
26
+ SandboxMetrics,
27
+ SandboxNetworkOpts,
28
+ SandboxNetworkUpdate,
29
+ SnapshotInfo,
30
+ )
31
+ from loopix.sandbox.utils import class_method_variant
32
+ from loopix.sandbox_async.commands.command import Commands
33
+ from loopix.sandbox_async.commands.pty import Pty
34
+ from loopix.sandbox_async.filesystem.filesystem import Filesystem
35
+ from loopix.sandbox_async.git import Git
36
+ from loopix.sandbox_async.sandbox_api import SandboxApi, SandboxInfo
37
+ from loopix.sandbox_async.paginator import AsyncSnapshotPaginator
38
+ from loopix.volume.volume_async import AsyncVolume
39
+ from loopix.api.client.models import SandboxVolumeMount as SandboxVolumeMountAPI
40
+
41
+ logger = logging.getLogger(__name__)
42
+
43
+ SandboxAsyncVolumeMount = Dict[str, Union[AsyncVolume, str]]
44
+
45
+
46
+ class AsyncSandbox(SandboxApi):
47
+ """
48
+ Loopix cloud sandbox is a secure and isolated cloud environment.
49
+
50
+ The sandbox allows you to:
51
+ - Access Linux OS
52
+ - Create, list, and delete files and directories
53
+ - Run commands
54
+ - Run isolated code
55
+ - Access the internet
56
+
57
+ Check docs [here](https://vm.betmandu.net/docs).
58
+
59
+ Use the `AsyncSandbox.create()` to create a new sandbox.
60
+
61
+ Example:
62
+ ```python
63
+ from loopix import AsyncSandbox
64
+
65
+ sandbox = await AsyncSandbox.create()
66
+ ```
67
+ """
68
+
69
+ @property
70
+ def files(self) -> Filesystem:
71
+ """
72
+ Module for interacting with the sandbox filesystem.
73
+ """
74
+ return self._filesystem
75
+
76
+ @property
77
+ def commands(self) -> Commands:
78
+ """
79
+ Module for running commands in the sandbox.
80
+ """
81
+ return self._commands
82
+
83
+ @property
84
+ def pty(self) -> Pty:
85
+ """
86
+ Module for interacting with the sandbox pseudo-terminal.
87
+ """
88
+ return self._pty
89
+
90
+ @property
91
+ def git(self) -> Git:
92
+ """
93
+ Module for running git operations in the sandbox.
94
+ """
95
+ return self._git
96
+
97
+ def __init__(
98
+ self,
99
+ **opts: Unpack[SandboxOpts],
100
+ ):
101
+ """
102
+ :deprecated: This constructor is deprecated
103
+
104
+ Use `AsyncSandbox.create()` to create a new sandbox instead.
105
+ """
106
+ super().__init__(**opts)
107
+
108
+ self._transport = get_transport(self.connection_config)
109
+ self._envd_api = httpx.AsyncClient(
110
+ base_url=self.envd_api_url,
111
+ transport=self._transport,
112
+ headers=self.connection_config.sandbox_headers,
113
+ event_hooks=make_async_logging_event_hooks(self.connection_config.logger),
114
+ )
115
+ self._filesystem = Filesystem(
116
+ self.envd_api_url,
117
+ self._envd_version,
118
+ self.connection_config,
119
+ self._transport.pool,
120
+ self._envd_api,
121
+ )
122
+ self._commands = Commands(
123
+ self.envd_api_url,
124
+ self.connection_config,
125
+ self._transport.pool,
126
+ self._envd_version,
127
+ self._envd_api,
128
+ )
129
+ self._pty = Pty(
130
+ self.envd_api_url,
131
+ self.connection_config,
132
+ self._transport.pool,
133
+ self._envd_version,
134
+ self._envd_api,
135
+ )
136
+ self._git = Git(self._commands)
137
+
138
+ async def is_running(self, request_timeout: Optional[float] = None) -> bool:
139
+ """
140
+ Check if the sandbox is running.
141
+
142
+ :param request_timeout: Timeout for the request in **seconds**
143
+
144
+ :return: `True` if the sandbox is running, `False` otherwise
145
+
146
+ Example
147
+ ```python
148
+ sandbox = await AsyncSandbox.create()
149
+ await sandbox.is_running() # Returns True
150
+
151
+ await sandbox.kill()
152
+ await sandbox.is_running() # Returns False
153
+ ```
154
+ """
155
+ try:
156
+ r = await self._envd_api.get(
157
+ ENVD_API_HEALTH_ROUTE,
158
+ timeout=self.connection_config.get_request_timeout(request_timeout),
159
+ )
160
+
161
+ if r.status_code == 502:
162
+ return False
163
+
164
+ err = await ahandle_envd_api_exception(r)
165
+
166
+ if err:
167
+ raise err
168
+
169
+ except httpx.TimeoutException:
170
+ raise format_request_timeout_error()
171
+
172
+ return True
173
+
174
+ @classmethod
175
+ async def create(
176
+ cls,
177
+ template: Optional[str] = None,
178
+ timeout: Optional[int] = None,
179
+ metadata: Optional[Dict[str, str]] = None,
180
+ envs: Optional[Dict[str, str]] = None,
181
+ secure: bool = True,
182
+ allow_internet_access: bool = True,
183
+ mcp: Optional[McpServer] = None,
184
+ network: Optional[SandboxNetworkOpts] = None,
185
+ lifecycle: Optional[SandboxLifecycle] = None,
186
+ volume_mounts: Optional[SandboxAsyncVolumeMount] = None,
187
+ logger: Optional[logging.Logger] = None,
188
+ **opts: Unpack[ApiParams],
189
+ ) -> Self:
190
+ """
191
+ Create a new sandbox.
192
+
193
+ By default, the sandbox is created from the default `base` sandbox template.
194
+
195
+ :param template: Sandbox template name or ID
196
+ :param timeout: Timeout for the sandbox in **seconds**, default to 300 seconds. The maximum time a sandbox can be kept alive is 24 hours (86_400 seconds) for Pro users and 1 hour (3_600 seconds) for Hobby users.
197
+ :param metadata: Custom metadata for the sandbox
198
+ :param envs: Custom environment variables for the sandbox
199
+ :param secure: Envd is secured with access token and cannot be used without it, defaults to `True`.
200
+ :param allow_internet_access: Allow sandbox to access the internet, defaults to `True`. If set to `False`, it works the same as setting network `deny_out` to `[0.0.0.0/0]`.
201
+ :param mcp: MCP server to enable in the sandbox
202
+ :param network: Sandbox network configuration. ``allow_out``/``deny_out`` may also be a callable receiving a :class:`SandboxNetworkSelectorContext` (``ctx.all_traffic``, ``ctx.rules``) and returning a list of strings. Per-host transform rules are nested under ``network.rules``.
203
+ :param lifecycle: Sandbox lifecycle configuration — ``on_timeout``: ``"kill"`` (default) or ``"pause"``, or an object ``{"action": "pause"|"kill", "keep_memory": bool}`` where ``keep_memory`` (default ``True``) set to ``False`` makes a timeout auto-pause filesystem-only (cold-boots on resume; cannot be combined with ``auto_resume``); ``auto_resume``: ``False`` (default) or ``True`` (only when ``on_timeout`` action is ``"pause"``). Example: ``{"on_timeout": {"action": "pause", "keep_memory": False}}``
204
+ :param volume_mounts: Dictionary mapping mount paths to AsyncVolume instances or volume names
205
+ :param logger: Logger used for request and response logging for this sandbox. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.
206
+
207
+ :return: A Sandbox instance for the new sandbox
208
+
209
+ Use this method instead of using the constructor to create a new sandbox.
210
+ """
211
+ if not template and mcp is not None:
212
+ template = cls.default_mcp_template
213
+ elif not template:
214
+ template = cls.default_template
215
+
216
+ transformed_mounts: Optional[List[SandboxVolumeMountAPI]] = None
217
+ if volume_mounts:
218
+ transformed_mounts = [
219
+ SandboxVolumeMountAPI(
220
+ name=vol.name if isinstance(vol, AsyncVolume) else vol,
221
+ path=path,
222
+ )
223
+ for path, vol in volume_mounts.items()
224
+ ]
225
+
226
+ sandbox = await cls._create(
227
+ template=template,
228
+ timeout=timeout,
229
+ metadata=metadata,
230
+ envs=envs,
231
+ secure=secure,
232
+ allow_internet_access=allow_internet_access,
233
+ mcp=mcp,
234
+ network=network,
235
+ lifecycle=lifecycle,
236
+ volume_mounts=transformed_mounts,
237
+ logger=logger,
238
+ **opts,
239
+ )
240
+
241
+ if mcp is not None:
242
+ token = str(uuid.uuid4())
243
+ sandbox._mcp_token = token
244
+
245
+ res = await sandbox.commands.run(
246
+ f"mcp-gateway --config {shlex.quote(json.dumps(mcp))}",
247
+ user="root",
248
+ envs={"GATEWAY_ACCESS_TOKEN": token},
249
+ )
250
+ if res.exit_code != 0:
251
+ raise Exception(f"Failed to start MCP gateway: {res.stderr}")
252
+
253
+ return sandbox
254
+
255
+ @overload
256
+ async def connect(
257
+ self,
258
+ timeout: Optional[int] = None,
259
+ **opts: Unpack[ApiParams],
260
+ ) -> Self:
261
+ """
262
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
263
+ Sandbox must be either running or be paused.
264
+
265
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
266
+
267
+ :param timeout: Timeout for the sandbox in **seconds**
268
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
269
+ :return: A running sandbox instance
270
+
271
+ @example
272
+ ```python
273
+ sandbox = await AsyncSandbox.create()
274
+ await sandbox.pause()
275
+
276
+ # Another code block
277
+ same_sandbox = await sandbox.connect()
278
+ ```
279
+ """
280
+ ...
281
+
282
+ @overload
283
+ @staticmethod
284
+ async def connect(
285
+ sandbox_id: str,
286
+ timeout: Optional[int] = None,
287
+ logger: Optional[logging.Logger] = None,
288
+ **opts: Unpack[ApiParams],
289
+ ) -> "AsyncSandbox":
290
+ """
291
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
292
+ Sandbox must be either running or be paused.
293
+
294
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
295
+
296
+ :param sandbox_id: Sandbox ID
297
+ :param timeout: Timeout for the sandbox in **seconds**
298
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
299
+ :param logger: Logger used for request and response logging for this sandbox. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.
300
+ :return: A running sandbox instance
301
+
302
+ @example
303
+ ```python
304
+ sandbox = await AsyncSandbox.create()
305
+ await AsyncSandbox.pause(sandbox.sandbox_id)
306
+
307
+ # Another code block
308
+ same_sandbox = await AsyncSandbox.connect(sandbox.sandbox_id)
309
+ ```
310
+ """
311
+ ...
312
+
313
+ @class_method_variant("_cls_connect_sandbox")
314
+ async def connect(
315
+ self,
316
+ timeout: Optional[int] = None,
317
+ **opts: Unpack[ApiParams],
318
+ ) -> Self:
319
+ """
320
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
321
+ Sandbox must be either running or be paused.
322
+
323
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
324
+
325
+ :param timeout: Timeout for the sandbox in **seconds**
326
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
327
+ :return: A running sandbox instance
328
+
329
+ @example
330
+ ```python
331
+ sandbox = await AsyncSandbox.create()
332
+ await sandbox.pause()
333
+
334
+ # Another code block
335
+ same_sandbox = await sandbox.connect()
336
+ ```
337
+ """
338
+ if self.connection_config.debug:
339
+ # Skip connecting to the sandbox in debug mode
340
+ return self
341
+
342
+ await SandboxApi._cls_connect(
343
+ sandbox_id=self.sandbox_id,
344
+ timeout=timeout,
345
+ **self.connection_config.get_api_params(**opts),
346
+ )
347
+
348
+ return self
349
+
350
+ async def __aenter__(self):
351
+ return self
352
+
353
+ async def __aexit__(self, exc_type, exc_value, traceback):
354
+ await self.kill()
355
+
356
+ @overload
357
+ async def kill(
358
+ self,
359
+ **opts: Unpack[ApiParams],
360
+ ) -> bool:
361
+ """
362
+ Kill the sandbox.
363
+
364
+ :return: `True` if the sandbox was killed, `False` if the sandbox was not found
365
+ """
366
+ ...
367
+
368
+ @overload
369
+ @staticmethod
370
+ async def kill(
371
+ sandbox_id: str,
372
+ **opts: Unpack[ApiParams],
373
+ ) -> bool:
374
+ """
375
+ Kill the sandbox specified by sandbox ID.
376
+
377
+ :param sandbox_id: Sandbox ID
378
+
379
+ :return: `True` if the sandbox was killed, `False` if the sandbox was not found
380
+ """
381
+ ...
382
+
383
+ @class_method_variant("_cls_kill")
384
+ async def kill(
385
+ self,
386
+ **opts: Unpack[ApiParams],
387
+ ) -> bool:
388
+ """
389
+ Kill the sandbox specified by sandbox ID.
390
+
391
+ :return: `True` if the sandbox was killed, `False` if the sandbox was not found
392
+ """
393
+ if self.connection_config.debug:
394
+ # Skip killing the sandbox in debug mode
395
+ return True
396
+
397
+ return await SandboxApi._cls_kill(
398
+ sandbox_id=self.sandbox_id,
399
+ **self.connection_config.get_api_params(**opts),
400
+ )
401
+
402
+ @overload
403
+ async def set_timeout(
404
+ self,
405
+ timeout: int,
406
+ **opts: Unpack[ApiParams],
407
+ ) -> None:
408
+ """
409
+ Set the timeout of the sandbox.
410
+ This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.set_timeout`.
411
+
412
+ The maximum time a sandbox can be kept alive is 24 hours (86_400 seconds) for Pro users and 1 hour (3_600 seconds) for Hobby users.
413
+
414
+ :param timeout: Timeout for the sandbox in **seconds**
415
+ """
416
+ ...
417
+
418
+ @overload
419
+ @staticmethod
420
+ async def set_timeout(
421
+ sandbox_id: str,
422
+ timeout: int,
423
+ **opts: Unpack[ApiParams],
424
+ ) -> None:
425
+ """
426
+ Set the timeout of the specified sandbox.
427
+ This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.set_timeout`.
428
+
429
+ The maximum time a sandbox can be kept alive is 24 hours (86_400 seconds) for Pro users and 1 hour (3_600 seconds) for Hobby users.
430
+
431
+ :param sandbox_id: Sandbox ID
432
+ :param timeout: Timeout for the sandbox in **seconds**
433
+ """
434
+ ...
435
+
436
+ @class_method_variant("_cls_set_timeout")
437
+ async def set_timeout(
438
+ self,
439
+ timeout: int,
440
+ **opts: Unpack[ApiParams],
441
+ ) -> None:
442
+ """
443
+ Set the timeout of the specified sandbox.
444
+ This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.set_timeout`.
445
+
446
+ The maximum time a sandbox can be kept alive is 24 hours (86_400 seconds) for Pro users and 1 hour (3_600 seconds) for Hobby users.
447
+
448
+ :param timeout: Timeout for the sandbox in **seconds**
449
+ """
450
+ await SandboxApi._cls_set_timeout(
451
+ sandbox_id=self.sandbox_id,
452
+ timeout=timeout,
453
+ **self.connection_config.get_api_params(**opts),
454
+ )
455
+
456
+ @overload
457
+ async def update_network(
458
+ self,
459
+ network: SandboxNetworkUpdate,
460
+ **opts: Unpack[ApiParams],
461
+ ) -> None:
462
+ """
463
+ Update the network configuration of the sandbox.
464
+
465
+ Replaces the current egress configuration atomically — fields that are
466
+ omitted are cleared on the server.
467
+
468
+ :param network: New network configuration.
469
+ """
470
+ ...
471
+
472
+ @overload
473
+ @staticmethod
474
+ async def update_network(
475
+ sandbox_id: str,
476
+ network: SandboxNetworkUpdate,
477
+ **opts: Unpack[ApiParams],
478
+ ) -> None:
479
+ """
480
+ Update the network configuration of the sandbox specified by sandbox ID.
481
+
482
+ Replaces the current egress configuration atomically — fields that are
483
+ omitted are cleared on the server.
484
+
485
+ :param sandbox_id: Sandbox ID.
486
+ :param network: New network configuration.
487
+ """
488
+ ...
489
+
490
+ @class_method_variant("_cls_update_network")
491
+ async def update_network(
492
+ self,
493
+ network: SandboxNetworkUpdate,
494
+ **opts: Unpack[ApiParams],
495
+ ) -> None:
496
+ """
497
+ Update the network configuration of the sandbox.
498
+
499
+ Replaces the current egress configuration atomically — fields that are
500
+ omitted are cleared on the server.
501
+
502
+ :param network: New network configuration.
503
+ """
504
+ await SandboxApi._cls_update_network(
505
+ sandbox_id=self.sandbox_id,
506
+ network=network,
507
+ **self.connection_config.get_api_params(**opts),
508
+ )
509
+
510
+ @overload
511
+ async def get_info(
512
+ self,
513
+ **opts: Unpack[ApiParams],
514
+ ) -> SandboxInfo:
515
+ """
516
+ Get sandbox information like sandbox ID, template, metadata, started at/end at date.
517
+
518
+ :return: Sandbox info
519
+ """
520
+ ...
521
+
522
+ @overload
523
+ @staticmethod
524
+ async def get_info(
525
+ sandbox_id: str,
526
+ **opts: Unpack[ApiParams],
527
+ ) -> SandboxInfo:
528
+ """
529
+ Get sandbox information like sandbox ID, template, metadata, started at/end at date.
530
+ :param sandbox_id: Sandbox ID
531
+
532
+ :return: Sandbox info
533
+ """
534
+ ...
535
+
536
+ @class_method_variant("_cls_get_info")
537
+ async def get_info(
538
+ self,
539
+ **opts: Unpack[ApiParams],
540
+ ) -> SandboxInfo:
541
+ """
542
+ Get sandbox information like sandbox ID, template, metadata, started at/end at date.
543
+
544
+ :return: Sandbox info
545
+ """
546
+
547
+ return await SandboxApi._cls_get_info(
548
+ sandbox_id=self.sandbox_id,
549
+ **self.connection_config.get_api_params(**opts),
550
+ )
551
+
552
+ @overload
553
+ async def get_metrics(
554
+ self,
555
+ start: Optional[datetime.datetime] = None,
556
+ end: Optional[datetime.datetime] = None,
557
+ **opts: Unpack[ApiParams],
558
+ ) -> List[SandboxMetrics]:
559
+ """
560
+ Get the metrics of the current sandbox.
561
+
562
+ :param start: Start time for the metrics, defaults to the start of the sandbox
563
+ :param end: End time for the metrics, defaults to the current time
564
+
565
+ :return: List of sandbox metrics containing CPU, memory and disk usage information
566
+ """
567
+ ...
568
+
569
+ @overload
570
+ @staticmethod
571
+ async def get_metrics(
572
+ sandbox_id: str,
573
+ start: Optional[datetime.datetime] = None,
574
+ end: Optional[datetime.datetime] = None,
575
+ **opts: Unpack[ApiParams],
576
+ ) -> List[SandboxMetrics]:
577
+ """
578
+ Get the metrics of the sandbox specified by sandbox ID.
579
+
580
+ :param sandbox_id: Sandbox ID
581
+ :param start: Start time for the metrics, defaults to the start of the sandbox
582
+ :param end: End time for the metrics, defaults to the current time
583
+
584
+ :return: List of sandbox metrics containing CPU, memory and disk usage information
585
+ """
586
+ ...
587
+
588
+ @class_method_variant("_cls_get_metrics")
589
+ async def get_metrics(
590
+ self,
591
+ start: Optional[datetime.datetime] = None,
592
+ end: Optional[datetime.datetime] = None,
593
+ **opts: Unpack[ApiParams],
594
+ ) -> List[SandboxMetrics]:
595
+ """
596
+ Get the metrics of the current sandbox.
597
+
598
+ :param start: Start time for the metrics, defaults to the start of the sandbox
599
+ :param end: End time for the metrics, defaults to the current time
600
+
601
+ :return: List of sandbox metrics containing CPU, memory and disk usage information
602
+ """
603
+ if self.connection_config.debug:
604
+ # Skip getting the metrics in debug mode
605
+ return []
606
+
607
+ if self._envd_version < Version("0.1.5"):
608
+ raise TemplateException(
609
+ "You need to update the template to use the new SDK."
610
+ )
611
+
612
+ if self._envd_version < Version("0.2.4"):
613
+ logger.warning(
614
+ "Disk metrics are not supported in this version of the sandbox, please rebuild the template to get disk metrics."
615
+ )
616
+
617
+ return await SandboxApi._cls_get_metrics(
618
+ sandbox_id=self.sandbox_id,
619
+ start=start,
620
+ end=end,
621
+ **self.connection_config.get_api_params(**opts),
622
+ )
623
+
624
+ @overload
625
+ async def pause(
626
+ self,
627
+ keep_memory: bool = True,
628
+ **opts: Unpack[ApiParams],
629
+ ) -> bool:
630
+ """
631
+ Pause the sandbox.
632
+
633
+ :param keep_memory: When `False`, the in-memory state is dropped and only the filesystem is persisted (no memory snapshot); resuming such a sandbox cold-boots (reboots) it from disk. Defaults to `True`.
634
+
635
+ :return: `True` if the sandbox got paused, `False` if the sandbox was already paused
636
+ """
637
+ ...
638
+
639
+ @overload
640
+ @staticmethod
641
+ async def pause(
642
+ sandbox_id: str,
643
+ keep_memory: bool = True,
644
+ **opts: Unpack[ApiParams],
645
+ ) -> bool:
646
+ """
647
+ Pause the sandbox specified by sandbox ID.
648
+
649
+ :param sandbox_id: Sandbox ID
650
+ :param keep_memory: When `False`, the in-memory state is dropped and only the filesystem is persisted (no memory snapshot); resuming such a sandbox cold-boots (reboots) it from disk. Defaults to `True`.
651
+
652
+ :return: `True` if the sandbox got paused, `False` if the sandbox was already paused
653
+ """
654
+ ...
655
+
656
+ @class_method_variant("_cls_pause")
657
+ async def pause(
658
+ self,
659
+ keep_memory: bool = True,
660
+ **opts: Unpack[ApiParams],
661
+ ) -> bool:
662
+ """
663
+ Pause the sandbox.
664
+
665
+ :param keep_memory: When `False`, the in-memory state is dropped and only the filesystem is persisted (no memory snapshot); resuming such a sandbox cold-boots (reboots) it from disk, losing running processes and open connections. Defaults to `True` (full memory snapshot).
666
+
667
+ :return: `True` if the sandbox got paused, `False` if the sandbox was already paused
668
+ """
669
+
670
+ return await SandboxApi._cls_pause(
671
+ sandbox_id=self.sandbox_id,
672
+ keep_memory=keep_memory,
673
+ **self.connection_config.get_api_params(**opts),
674
+ )
675
+
676
+ @overload
677
+ async def beta_pause(
678
+ self,
679
+ keep_memory: bool = True,
680
+ **opts: Unpack[ApiParams],
681
+ ) -> bool: ...
682
+
683
+ @overload
684
+ @staticmethod
685
+ async def beta_pause(
686
+ sandbox_id: str,
687
+ keep_memory: bool = True,
688
+ **opts: Unpack[ApiParams],
689
+ ) -> bool: ...
690
+
691
+ @class_method_variant("_cls_pause")
692
+ async def beta_pause(
693
+ self,
694
+ keep_memory: bool = True,
695
+ **opts: Unpack[ApiParams],
696
+ ) -> bool:
697
+ """
698
+ :deprecated: Use `pause()` instead.
699
+
700
+ :return: `True` if the sandbox got paused, `False` if the sandbox was already paused
701
+ """
702
+ return await self.pause(keep_memory=keep_memory, **opts)
703
+
704
+ @overload
705
+ async def create_snapshot(
706
+ self,
707
+ name: Optional[str] = None,
708
+ **opts: Unpack[ApiParams],
709
+ ) -> SnapshotInfo:
710
+ """
711
+ Create a snapshot of the sandbox's current state.
712
+
713
+ The sandbox will be paused while the snapshot is being created.
714
+ The snapshot can be used to create new sandboxes with the same filesystem and state.
715
+ Snapshots are persistent and survive sandbox deletion.
716
+
717
+ Use the returned `snapshot_id` with `AsyncSandbox.create(snapshot_id)` to create a new sandbox from the snapshot.
718
+
719
+ :param name: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one.
720
+
721
+ :return: Snapshot information including the snapshot ID and names
722
+ """
723
+ ...
724
+
725
+ @overload
726
+ @staticmethod
727
+ async def create_snapshot(
728
+ sandbox_id: str,
729
+ name: Optional[str] = None,
730
+ **opts: Unpack[ApiParams],
731
+ ) -> SnapshotInfo:
732
+ """
733
+ Create a snapshot from the sandbox specified by sandbox ID.
734
+
735
+ The sandbox will be paused while the snapshot is being created.
736
+
737
+ :param sandbox_id: Sandbox ID
738
+ :param name: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one.
739
+
740
+ :return: Snapshot information including the snapshot ID and names
741
+ """
742
+ ...
743
+
744
+ @class_method_variant("_cls_create_snapshot")
745
+ async def create_snapshot(
746
+ self,
747
+ name: Optional[str] = None,
748
+ **opts: Unpack[ApiParams],
749
+ ) -> SnapshotInfo:
750
+ """
751
+ Create a snapshot of the sandbox's current state.
752
+
753
+ The sandbox will be paused while the snapshot is being created.
754
+ The snapshot can be used to create new sandboxes with the same filesystem and state.
755
+ Snapshots are persistent and survive sandbox deletion.
756
+
757
+ Use the returned `snapshot_id` with `AsyncSandbox.create(snapshot_id)` to create a new sandbox from the snapshot.
758
+
759
+ :param name: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one.
760
+
761
+ :return: Snapshot information including the snapshot ID and names
762
+ """
763
+ return await SandboxApi._cls_create_snapshot(
764
+ sandbox_id=self.sandbox_id,
765
+ name=name,
766
+ **self.connection_config.get_api_params(**opts),
767
+ )
768
+
769
+ @overload
770
+ def list_snapshots(
771
+ self,
772
+ limit: Optional[int] = None,
773
+ next_token: Optional[str] = None,
774
+ **opts: Unpack[ApiParams],
775
+ ) -> AsyncSnapshotPaginator:
776
+ """
777
+ List snapshots for this sandbox.
778
+
779
+ :param limit: Maximum number of snapshots to return per page
780
+ :param next_token: Token for pagination
781
+
782
+ :return: Paginator for listing snapshots
783
+ """
784
+ ...
785
+
786
+ @overload
787
+ @staticmethod
788
+ def list_snapshots(
789
+ sandbox_id: Optional[str] = None,
790
+ limit: Optional[int] = None,
791
+ next_token: Optional[str] = None,
792
+ **opts: Unpack[ApiParams],
793
+ ) -> AsyncSnapshotPaginator:
794
+ """
795
+ List all snapshots.
796
+
797
+ :param sandbox_id: Filter snapshots by source sandbox ID
798
+ :param limit: Maximum number of snapshots to return per page
799
+ :param next_token: Token for pagination
800
+
801
+ :return: Paginator for listing snapshots
802
+ """
803
+ ...
804
+
805
+ @class_method_variant("_cls_list_snapshots")
806
+ def list_snapshots(
807
+ self,
808
+ limit: Optional[int] = None,
809
+ next_token: Optional[str] = None,
810
+ **opts: Unpack[ApiParams],
811
+ ) -> AsyncSnapshotPaginator:
812
+ """
813
+ List snapshots for this sandbox.
814
+
815
+ :param limit: Maximum number of snapshots to return per page
816
+ :param next_token: Token for pagination
817
+
818
+ :return: Paginator for listing snapshots
819
+ """
820
+ return AsyncSnapshotPaginator(
821
+ sandbox_id=self.sandbox_id,
822
+ limit=limit,
823
+ next_token=next_token,
824
+ **self.connection_config.get_api_params(**opts),
825
+ )
826
+
827
+ @staticmethod
828
+ def _cls_list_snapshots(
829
+ sandbox_id: Optional[str] = None,
830
+ limit: Optional[int] = None,
831
+ next_token: Optional[str] = None,
832
+ **opts: Unpack[ApiParams],
833
+ ) -> AsyncSnapshotPaginator:
834
+ return AsyncSnapshotPaginator(
835
+ sandbox_id=sandbox_id,
836
+ limit=limit,
837
+ next_token=next_token,
838
+ **opts,
839
+ )
840
+
841
+ @staticmethod
842
+ async def delete_snapshot(
843
+ snapshot_id: str,
844
+ **opts: Unpack[ApiParams],
845
+ ) -> bool:
846
+ """
847
+ Delete a snapshot.
848
+
849
+ :param snapshot_id: Snapshot ID
850
+ :return: `True` if the snapshot was deleted, `False` if it was not found
851
+ """
852
+ return await SandboxApi._cls_delete_snapshot(
853
+ snapshot_id=snapshot_id,
854
+ **opts,
855
+ )
856
+
857
+ async def get_mcp_token(self) -> Optional[str]:
858
+ """
859
+ Get the MCP token for the sandbox.
860
+
861
+ :return: MCP token for the sandbox, or None if MCP is not enabled.
862
+ """
863
+ if not self._mcp_token:
864
+ self._mcp_token = await self.files.read(
865
+ "/etc/mcp-gateway/.token", user="root"
866
+ )
867
+ return self._mcp_token
868
+
869
+ @classmethod
870
+ async def _cls_connect_sandbox(
871
+ cls,
872
+ sandbox_id: str,
873
+ timeout: Optional[int] = None,
874
+ logger: Optional[logging.Logger] = None,
875
+ **opts: Unpack[ApiParams],
876
+ ) -> Self:
877
+ debug = ConnectionConfig(**opts).debug
878
+ if debug:
879
+ sandbox_domain = None
880
+ envd_version = ENVD_DEBUG_FALLBACK
881
+ envd_access_token = None
882
+ traffic_access_token = None
883
+ else:
884
+ sandbox = await SandboxApi._cls_connect(
885
+ sandbox_id=sandbox_id,
886
+ timeout=timeout,
887
+ logger=logger,
888
+ **opts,
889
+ )
890
+
891
+ sandbox_id = sandbox.sandbox_id
892
+ sandbox_domain = sandbox.sandbox_domain
893
+ envd_version = Version(sandbox.envd_version)
894
+ envd_access_token = sandbox.envd_access_token
895
+ traffic_access_token = sandbox.traffic_access_token
896
+
897
+ sandbox_headers = {
898
+ "Lpx-Sandbox-Id": sandbox_id,
899
+ "Lpx-Sandbox-Port": str(ConnectionConfig.envd_port),
900
+ }
901
+ if envd_access_token is not None and not isinstance(envd_access_token, Unset):
902
+ sandbox_headers["X-Access-Token"] = envd_access_token
903
+
904
+ connection_config = ConnectionConfig(
905
+ extra_sandbox_headers=sandbox_headers,
906
+ logger=logger,
907
+ **opts,
908
+ )
909
+
910
+ return cls(
911
+ sandbox_id=sandbox_id,
912
+ sandbox_domain=sandbox_domain,
913
+ envd_version=envd_version,
914
+ envd_access_token=envd_access_token,
915
+ traffic_access_token=traffic_access_token,
916
+ connection_config=connection_config,
917
+ )
918
+
919
+ @classmethod
920
+ async def _create(
921
+ cls,
922
+ template: Optional[str],
923
+ timeout: Optional[int],
924
+ metadata: Optional[Dict[str, str]],
925
+ envs: Optional[Dict[str, str]],
926
+ secure: bool,
927
+ allow_internet_access: bool,
928
+ mcp: Optional[McpServer] = None,
929
+ network: Optional[SandboxNetworkOpts] = None,
930
+ lifecycle: Optional[SandboxLifecycle] = None,
931
+ volume_mounts: Optional[list] = None,
932
+ logger: Optional[logging.Logger] = None,
933
+ **opts: Unpack[ApiParams],
934
+ ) -> Self:
935
+ extra_sandbox_headers = {}
936
+
937
+ debug = ConnectionConfig(**opts).debug
938
+ if debug:
939
+ sandbox_id = "debug_sandbox_id"
940
+ sandbox_domain = None
941
+ envd_version = ENVD_DEBUG_FALLBACK
942
+ envd_access_token = None
943
+ traffic_access_token = None
944
+ else:
945
+ response = await SandboxApi._create_sandbox(
946
+ template=template or cls.default_template,
947
+ timeout=timeout or cls.default_sandbox_timeout,
948
+ metadata=metadata,
949
+ env_vars=envs,
950
+ secure=secure,
951
+ allow_internet_access=allow_internet_access,
952
+ mcp=mcp,
953
+ network=network,
954
+ lifecycle=lifecycle,
955
+ volume_mounts=volume_mounts,
956
+ logger=logger,
957
+ **opts,
958
+ )
959
+
960
+ sandbox_id = response.sandbox_id
961
+ sandbox_domain = response.sandbox_domain
962
+ envd_version = Version(response.envd_version)
963
+ envd_access_token = response.envd_access_token
964
+ traffic_access_token = response.traffic_access_token
965
+
966
+ if envd_access_token is not None and not isinstance(
967
+ envd_access_token, Unset
968
+ ):
969
+ extra_sandbox_headers["X-Access-Token"] = envd_access_token
970
+
971
+ extra_sandbox_headers["Lpx-Sandbox-Id"] = sandbox_id
972
+ extra_sandbox_headers["Lpx-Sandbox-Port"] = str(ConnectionConfig.envd_port)
973
+
974
+ connection_config = ConnectionConfig(
975
+ extra_sandbox_headers=extra_sandbox_headers,
976
+ logger=logger,
977
+ **opts,
978
+ )
979
+
980
+ return cls(
981
+ sandbox_id=sandbox_id,
982
+ sandbox_domain=sandbox_domain,
983
+ envd_version=envd_version,
984
+ envd_access_token=envd_access_token,
985
+ traffic_access_token=traffic_access_token,
986
+ connection_config=connection_config,
987
+ )