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