moru 0.1.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 (152) hide show
  1. moru/__init__.py +174 -0
  2. moru/api/__init__.py +164 -0
  3. moru/api/client/__init__.py +8 -0
  4. moru/api/client/api/__init__.py +1 -0
  5. moru/api/client/api/sandboxes/__init__.py +1 -0
  6. moru/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +161 -0
  7. moru/api/client/api/sandboxes/get_sandboxes.py +176 -0
  8. moru/api/client/api/sandboxes/get_sandboxes_metrics.py +173 -0
  9. moru/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +163 -0
  10. moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +199 -0
  11. moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +212 -0
  12. moru/api/client/api/sandboxes/get_v2_sandboxes.py +230 -0
  13. moru/api/client/api/sandboxes/post_sandboxes.py +172 -0
  14. moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
  15. moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +165 -0
  16. moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +181 -0
  17. moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +189 -0
  18. moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +193 -0
  19. moru/api/client/api/templates/__init__.py +1 -0
  20. moru/api/client/api/templates/delete_templates_template_id.py +157 -0
  21. moru/api/client/api/templates/get_templates.py +172 -0
  22. moru/api/client/api/templates/get_templates_template_id.py +195 -0
  23. moru/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +217 -0
  24. moru/api/client/api/templates/get_templates_template_id_files_hash.py +180 -0
  25. moru/api/client/api/templates/patch_templates_template_id.py +183 -0
  26. moru/api/client/api/templates/post_templates.py +172 -0
  27. moru/api/client/api/templates/post_templates_template_id.py +181 -0
  28. moru/api/client/api/templates/post_templates_template_id_builds_build_id.py +170 -0
  29. moru/api/client/api/templates/post_v2_templates.py +172 -0
  30. moru/api/client/api/templates/post_v3_templates.py +172 -0
  31. moru/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +192 -0
  32. moru/api/client/client.py +286 -0
  33. moru/api/client/errors.py +16 -0
  34. moru/api/client/models/__init__.py +123 -0
  35. moru/api/client/models/aws_registry.py +85 -0
  36. moru/api/client/models/aws_registry_type.py +8 -0
  37. moru/api/client/models/build_log_entry.py +89 -0
  38. moru/api/client/models/build_status_reason.py +95 -0
  39. moru/api/client/models/connect_sandbox.py +59 -0
  40. moru/api/client/models/created_access_token.py +100 -0
  41. moru/api/client/models/created_team_api_key.py +166 -0
  42. moru/api/client/models/disk_metrics.py +91 -0
  43. moru/api/client/models/error.py +67 -0
  44. moru/api/client/models/gcp_registry.py +69 -0
  45. moru/api/client/models/gcp_registry_type.py +8 -0
  46. moru/api/client/models/general_registry.py +77 -0
  47. moru/api/client/models/general_registry_type.py +8 -0
  48. moru/api/client/models/identifier_masking_details.py +83 -0
  49. moru/api/client/models/listed_sandbox.py +154 -0
  50. moru/api/client/models/log_level.py +11 -0
  51. moru/api/client/models/max_team_metric.py +78 -0
  52. moru/api/client/models/mcp_type_0.py +44 -0
  53. moru/api/client/models/new_access_token.py +59 -0
  54. moru/api/client/models/new_sandbox.py +172 -0
  55. moru/api/client/models/new_team_api_key.py +59 -0
  56. moru/api/client/models/node.py +155 -0
  57. moru/api/client/models/node_detail.py +165 -0
  58. moru/api/client/models/node_metrics.py +122 -0
  59. moru/api/client/models/node_status.py +11 -0
  60. moru/api/client/models/node_status_change.py +79 -0
  61. moru/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +59 -0
  62. moru/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +59 -0
  63. moru/api/client/models/resumed_sandbox.py +68 -0
  64. moru/api/client/models/sandbox.py +145 -0
  65. moru/api/client/models/sandbox_detail.py +183 -0
  66. moru/api/client/models/sandbox_log.py +70 -0
  67. moru/api/client/models/sandbox_log_entry.py +93 -0
  68. moru/api/client/models/sandbox_log_entry_fields.py +44 -0
  69. moru/api/client/models/sandbox_logs.py +91 -0
  70. moru/api/client/models/sandbox_metric.py +118 -0
  71. moru/api/client/models/sandbox_network_config.py +92 -0
  72. moru/api/client/models/sandbox_state.py +9 -0
  73. moru/api/client/models/sandboxes_with_metrics.py +59 -0
  74. moru/api/client/models/team.py +83 -0
  75. moru/api/client/models/team_api_key.py +158 -0
  76. moru/api/client/models/team_metric.py +86 -0
  77. moru/api/client/models/team_user.py +68 -0
  78. moru/api/client/models/template.py +217 -0
  79. moru/api/client/models/template_build.py +139 -0
  80. moru/api/client/models/template_build_file_upload.py +70 -0
  81. moru/api/client/models/template_build_info.py +126 -0
  82. moru/api/client/models/template_build_request.py +115 -0
  83. moru/api/client/models/template_build_request_v2.py +88 -0
  84. moru/api/client/models/template_build_request_v3.py +88 -0
  85. moru/api/client/models/template_build_start_v2.py +184 -0
  86. moru/api/client/models/template_build_status.py +11 -0
  87. moru/api/client/models/template_legacy.py +207 -0
  88. moru/api/client/models/template_request_response_v3.py +83 -0
  89. moru/api/client/models/template_step.py +91 -0
  90. moru/api/client/models/template_update_request.py +59 -0
  91. moru/api/client/models/template_with_builds.py +148 -0
  92. moru/api/client/models/update_team_api_key.py +59 -0
  93. moru/api/client/py.typed +1 -0
  94. moru/api/client/types.py +54 -0
  95. moru/api/client_async/__init__.py +50 -0
  96. moru/api/client_sync/__init__.py +52 -0
  97. moru/api/metadata.py +14 -0
  98. moru/connection_config.py +217 -0
  99. moru/envd/api.py +59 -0
  100. moru/envd/filesystem/filesystem_connect.py +193 -0
  101. moru/envd/filesystem/filesystem_pb2.py +76 -0
  102. moru/envd/filesystem/filesystem_pb2.pyi +233 -0
  103. moru/envd/process/process_connect.py +155 -0
  104. moru/envd/process/process_pb2.py +92 -0
  105. moru/envd/process/process_pb2.pyi +304 -0
  106. moru/envd/rpc.py +61 -0
  107. moru/envd/versions.py +6 -0
  108. moru/exceptions.py +95 -0
  109. moru/sandbox/commands/command_handle.py +69 -0
  110. moru/sandbox/commands/main.py +39 -0
  111. moru/sandbox/filesystem/filesystem.py +94 -0
  112. moru/sandbox/filesystem/watch_handle.py +60 -0
  113. moru/sandbox/main.py +210 -0
  114. moru/sandbox/mcp.py +1120 -0
  115. moru/sandbox/network.py +8 -0
  116. moru/sandbox/sandbox_api.py +210 -0
  117. moru/sandbox/signature.py +45 -0
  118. moru/sandbox/utils.py +34 -0
  119. moru/sandbox_async/commands/command.py +336 -0
  120. moru/sandbox_async/commands/command_handle.py +196 -0
  121. moru/sandbox_async/commands/pty.py +240 -0
  122. moru/sandbox_async/filesystem/filesystem.py +531 -0
  123. moru/sandbox_async/filesystem/watch_handle.py +62 -0
  124. moru/sandbox_async/main.py +734 -0
  125. moru/sandbox_async/paginator.py +69 -0
  126. moru/sandbox_async/sandbox_api.py +325 -0
  127. moru/sandbox_async/utils.py +7 -0
  128. moru/sandbox_sync/commands/command.py +328 -0
  129. moru/sandbox_sync/commands/command_handle.py +150 -0
  130. moru/sandbox_sync/commands/pty.py +230 -0
  131. moru/sandbox_sync/filesystem/filesystem.py +518 -0
  132. moru/sandbox_sync/filesystem/watch_handle.py +69 -0
  133. moru/sandbox_sync/main.py +726 -0
  134. moru/sandbox_sync/paginator.py +69 -0
  135. moru/sandbox_sync/sandbox_api.py +308 -0
  136. moru/template/consts.py +30 -0
  137. moru/template/dockerfile_parser.py +275 -0
  138. moru/template/logger.py +232 -0
  139. moru/template/main.py +1360 -0
  140. moru/template/readycmd.py +138 -0
  141. moru/template/types.py +105 -0
  142. moru/template/utils.py +320 -0
  143. moru/template_async/build_api.py +202 -0
  144. moru/template_async/main.py +366 -0
  145. moru/template_sync/build_api.py +199 -0
  146. moru/template_sync/main.py +371 -0
  147. moru-0.1.0.dist-info/METADATA +63 -0
  148. moru-0.1.0.dist-info/RECORD +152 -0
  149. moru-0.1.0.dist-info/WHEEL +4 -0
  150. moru-0.1.0.dist-info/licenses/LICENSE +9 -0
  151. moru_connect/__init__.py +1 -0
  152. moru_connect/client.py +493 -0
@@ -0,0 +1,726 @@
1
+ import datetime
2
+ import json
3
+ import logging
4
+ import uuid
5
+ from typing import Dict, List, Optional, overload
6
+
7
+ import httpx
8
+ from packaging.version import Version
9
+ from typing_extensions import Self, Unpack
10
+
11
+ from moru.api.client.types import Unset
12
+ from moru.connection_config import ApiParams, ConnectionConfig
13
+ from moru.envd.api import ENVD_API_HEALTH_ROUTE, handle_envd_api_exception
14
+ from moru.envd.versions import ENVD_DEBUG_FALLBACK
15
+ from moru.exceptions import SandboxException, format_request_timeout_error
16
+ from moru.sandbox.main import SandboxOpts
17
+ from moru.sandbox.sandbox_api import McpServer, SandboxMetrics, SandboxNetworkOpts
18
+ from moru.sandbox.utils import class_method_variant
19
+ from moru.sandbox_sync.commands.command import Commands
20
+ from moru.sandbox_sync.commands.pty import Pty
21
+ from moru.sandbox_sync.filesystem.filesystem import Filesystem
22
+ from moru.sandbox_sync.sandbox_api import SandboxApi, SandboxInfo
23
+ from moru.api.client_sync import get_transport
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class Sandbox(SandboxApi):
29
+ """
30
+ Moru cloud sandbox is a secure and isolated cloud environment.
31
+
32
+ The sandbox allows you to:
33
+ - Access Linux OS
34
+ - Create, list, and delete files and directories
35
+ - Run commands
36
+ - Run isolated code
37
+ - Access the internet
38
+
39
+ Check docs [here](https://moru.io/docs).
40
+
41
+ Use the `Sandbox.create()` to create a new sandbox.
42
+
43
+ Example:
44
+ ```python
45
+ from moru import Sandbox
46
+
47
+ sandbox = Sandbox.create()
48
+ ```
49
+ """
50
+
51
+ @property
52
+ def files(self) -> Filesystem:
53
+ """
54
+ Module for interacting with the sandbox filesystem.
55
+ """
56
+ return self._filesystem
57
+
58
+ @property
59
+ def commands(self) -> Commands:
60
+ """
61
+ Module for running commands in the sandbox.
62
+ """
63
+ return self._commands
64
+
65
+ @property
66
+ def pty(self) -> Pty:
67
+ """
68
+ Module for interacting with the sandbox pseudo-terminal.
69
+ """
70
+ return self._pty
71
+
72
+ def __init__(self, **opts: Unpack[SandboxOpts]):
73
+ """
74
+ :deprecated: This constructor is deprecated
75
+
76
+ Use `Sandbox.create()` to create a new sandbox instead.
77
+ """
78
+ super().__init__(**opts)
79
+
80
+ self._transport = get_transport(self.connection_config)
81
+
82
+ self._envd_api = httpx.Client(
83
+ base_url=self.envd_api_url,
84
+ transport=self._transport,
85
+ headers=self.connection_config.sandbox_headers,
86
+ )
87
+ self._filesystem = Filesystem(
88
+ self.envd_api_url,
89
+ self._envd_version,
90
+ self.connection_config,
91
+ self._transport.pool,
92
+ self._envd_api,
93
+ )
94
+ self._commands = Commands(
95
+ self.envd_api_url,
96
+ self.connection_config,
97
+ self._transport.pool,
98
+ self._envd_version,
99
+ )
100
+ self._pty = Pty(
101
+ self.envd_api_url,
102
+ self.connection_config,
103
+ self._transport.pool,
104
+ self._envd_version,
105
+ )
106
+
107
+ def is_running(self, request_timeout: Optional[float] = None) -> bool:
108
+ """
109
+ Check if the sandbox is running.
110
+
111
+ :param request_timeout: Timeout for the request in **seconds**
112
+
113
+ :return: `True` if the sandbox is running, `False` otherwise
114
+
115
+ Example
116
+ ```python
117
+ sandbox = Sandbox.create()
118
+ sandbox.is_running() # Returns True
119
+
120
+ sandbox.kill()
121
+ sandbox.is_running() # Returns False
122
+ ```
123
+ """
124
+ try:
125
+ r = self._envd_api.get(
126
+ ENVD_API_HEALTH_ROUTE,
127
+ timeout=self.connection_config.get_request_timeout(request_timeout),
128
+ )
129
+
130
+ if r.status_code == 502:
131
+ return False
132
+
133
+ err = handle_envd_api_exception(r)
134
+
135
+ if err:
136
+ raise err
137
+
138
+ except httpx.TimeoutException:
139
+ raise format_request_timeout_error()
140
+
141
+ return True
142
+
143
+ @classmethod
144
+ def create(
145
+ cls,
146
+ template: Optional[str] = None,
147
+ timeout: Optional[int] = None,
148
+ metadata: Optional[Dict[str, str]] = None,
149
+ envs: Optional[Dict[str, str]] = None,
150
+ secure: bool = True,
151
+ allow_internet_access: bool = True,
152
+ mcp: Optional[McpServer] = None,
153
+ network: Optional[SandboxNetworkOpts] = None,
154
+ **opts: Unpack[ApiParams],
155
+ ) -> Self:
156
+ """
157
+ Create a new sandbox.
158
+
159
+ By default, the sandbox is created from the default `base` sandbox template.
160
+
161
+ :param template: Sandbox template name or ID
162
+ :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.
163
+ :param metadata: Custom metadata for the sandbox
164
+ :param envs: Custom environment variables for the sandbox
165
+ :param secure: Envd is secured with access token and cannot be used without it, defaults to `True`.
166
+ :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]`.
167
+ :param mcp: MCP server to enable in the sandbox
168
+ :param network: Sandbox network configuration
169
+
170
+ :return: A Sandbox instance for the new sandbox
171
+
172
+ Use this method instead of using the constructor to create a new sandbox.
173
+ """
174
+ if not template and mcp is not None:
175
+ template = cls.default_mcp_template
176
+ elif not template:
177
+ template = cls.default_template
178
+
179
+ sandbox = cls._create(
180
+ template=template,
181
+ auto_pause=False,
182
+ timeout=timeout,
183
+ metadata=metadata,
184
+ envs=envs,
185
+ secure=secure,
186
+ allow_internet_access=allow_internet_access,
187
+ mcp=mcp,
188
+ network=network,
189
+ **opts,
190
+ )
191
+
192
+ if mcp is not None:
193
+ token = str(uuid.uuid4())
194
+ sandbox._mcp_token = token
195
+
196
+ res = sandbox.commands.run(
197
+ f"mcp-gateway --config '{json.dumps(mcp)}'",
198
+ user="root",
199
+ envs={"GATEWAY_ACCESS_TOKEN": token},
200
+ )
201
+ if res.exit_code != 0:
202
+ raise Exception(f"Failed to start MCP gateway: {res.stderr}")
203
+
204
+ return sandbox
205
+
206
+ @overload
207
+ def connect(
208
+ self,
209
+ timeout: Optional[int] = None,
210
+ **opts: Unpack[ApiParams],
211
+ ) -> Self:
212
+ """
213
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
214
+ Sandbox must be either running or be paused.
215
+
216
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
217
+
218
+ :param timeout: Timeout for the sandbox in **seconds**
219
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
220
+ :return: A running sandbox instance
221
+
222
+ @example
223
+ ```python
224
+ sandbox = Sandbox.create()
225
+ sandbox.beta_pause()
226
+
227
+ # Another code block
228
+ same_sandbox = sandbox.connect()
229
+
230
+ :return: A running sandbox instance
231
+ """
232
+ ...
233
+
234
+ @overload
235
+ @classmethod
236
+ def connect(
237
+ cls,
238
+ sandbox_id: str,
239
+ timeout: Optional[int] = None,
240
+ **opts: Unpack[ApiParams],
241
+ ) -> Self:
242
+ """
243
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
244
+ Sandbox must be either running or be paused.
245
+
246
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
247
+
248
+ :param sandbox_id: Sandbox ID
249
+ :param timeout: Timeout for the sandbox in **seconds**.
250
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
251
+ :return: A running sandbox instance
252
+
253
+ @example
254
+ ```python
255
+ sandbox = Sandbox.create()
256
+ Sandbox.beta_pause(sandbox.sandbox_id)
257
+
258
+ # Another code block
259
+ same_sandbox = Sandbox.connect(sandbox.sandbox_id)
260
+ ```
261
+ """
262
+ ...
263
+
264
+ @class_method_variant("_cls_connect")
265
+ def connect(
266
+ self,
267
+ timeout: Optional[int] = None,
268
+ **opts: Unpack[ApiParams],
269
+ ) -> Self:
270
+ """
271
+ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
272
+ Sandbox must be either running or be paused.
273
+
274
+ With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
275
+
276
+ :param timeout: Timeout for the sandbox in **seconds**.
277
+ For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
278
+ :return: A running sandbox instance
279
+
280
+ @example
281
+ ```python
282
+ sandbox = Sandbox.create()
283
+ sandbox.beta_pause()
284
+
285
+ # Another code block
286
+ same_sandbox = sandbox.connect()
287
+ ```
288
+ """
289
+ SandboxApi._cls_connect(
290
+ sandbox_id=self.sandbox_id,
291
+ timeout=timeout,
292
+ **opts,
293
+ )
294
+
295
+ return self
296
+
297
+ def __enter__(self):
298
+ return self
299
+
300
+ def __exit__(self, exc_type, exc_value, traceback):
301
+ self.kill()
302
+
303
+ @overload
304
+ def kill(
305
+ self,
306
+ **opts: Unpack[ApiParams],
307
+ ) -> bool:
308
+ """
309
+ Kill the sandbox.
310
+
311
+ :return: `True` if the sandbox was killed, `False` if the sandbox was not found
312
+ """
313
+ ...
314
+
315
+ @overload
316
+ @staticmethod
317
+ def kill(
318
+ sandbox_id: str,
319
+ **opts: Unpack[ApiParams],
320
+ ) -> bool:
321
+ """
322
+ Kill the sandbox specified by sandbox ID.
323
+
324
+ :param sandbox_id: Sandbox ID
325
+
326
+ :return: `True` if the sandbox was killed, `False` if the sandbox was not found
327
+ """
328
+ ...
329
+
330
+ @class_method_variant("_cls_kill")
331
+ def kill(
332
+ self,
333
+ **opts: Unpack[ApiParams],
334
+ ) -> bool:
335
+ """
336
+ Kill the sandbox specified by sandbox ID.
337
+
338
+ :return: `True` if the sandbox was killed, `False` if the sandbox was not found
339
+ """
340
+ return SandboxApi._cls_kill(
341
+ sandbox_id=self.sandbox_id,
342
+ **self.connection_config.get_api_params(**opts),
343
+ )
344
+
345
+ @overload
346
+ def set_timeout(
347
+ self,
348
+ timeout: int,
349
+ **opts: Unpack[ApiParams],
350
+ ) -> None:
351
+ """
352
+ Set the timeout of the sandbox.
353
+ After the timeout expires, the sandbox will be automatically killed.
354
+ This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.set_timeout`.
355
+
356
+ 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.
357
+
358
+ :param timeout: Timeout for the sandbox in **seconds**
359
+ """
360
+ ...
361
+
362
+ @overload
363
+ @staticmethod
364
+ def set_timeout(
365
+ sandbox_id: str,
366
+ timeout: int,
367
+ **opts: Unpack[ApiParams],
368
+ ) -> None:
369
+ """
370
+ Set the timeout of the sandbox specified by sandbox ID.
371
+ After the timeout expires, the sandbox will be automatically killed.
372
+ This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.set_timeout`.
373
+
374
+ 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.
375
+
376
+ :param sandbox_id: Sandbox ID
377
+ :param timeout: Timeout for the sandbox in **seconds**
378
+ """
379
+ ...
380
+
381
+ @class_method_variant("_cls_set_timeout")
382
+ def set_timeout(
383
+ self,
384
+ timeout: int,
385
+ **opts: Unpack[ApiParams],
386
+ ) -> None:
387
+ """
388
+ Set the timeout of the sandbox.
389
+ After the timeout expires, the sandbox will be automatically killed.
390
+ This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.set_timeout`.
391
+
392
+ 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.
393
+
394
+ :param timeout: Timeout for the sandbox in **seconds**
395
+
396
+ """
397
+
398
+ SandboxApi._cls_set_timeout(
399
+ sandbox_id=self.sandbox_id,
400
+ timeout=timeout,
401
+ **self.connection_config.get_api_params(**opts),
402
+ )
403
+
404
+ @overload
405
+ def get_info(
406
+ self,
407
+ **opts: Unpack[ApiParams],
408
+ ) -> SandboxInfo:
409
+ """
410
+ Get sandbox information like sandbox ID, template, metadata, started at/end at date.
411
+
412
+ :return: Sandbox info
413
+ """
414
+ ...
415
+
416
+ @overload
417
+ @staticmethod
418
+ def get_info(
419
+ sandbox_id: str,
420
+ **opts: Unpack[ApiParams],
421
+ ) -> SandboxInfo:
422
+ """
423
+ Get sandbox information like sandbox ID, template, metadata, started at/end at date.
424
+
425
+ :param sandbox_id: Sandbox ID
426
+
427
+ :return: Sandbox info
428
+ """
429
+ ...
430
+
431
+ @class_method_variant("_cls_get_info")
432
+ def get_info(
433
+ self,
434
+ **opts: Unpack[ApiParams],
435
+ ) -> SandboxInfo:
436
+ """
437
+ Get sandbox information like sandbox ID, template, metadata, started at/end at date.
438
+
439
+ :return: Sandbox info
440
+ """
441
+ return SandboxApi._cls_get_info(
442
+ sandbox_id=self.sandbox_id,
443
+ **self.connection_config.get_api_params(**opts),
444
+ )
445
+
446
+ @overload
447
+ def get_metrics(
448
+ self,
449
+ start: Optional[datetime.datetime] = None,
450
+ end: Optional[datetime.datetime] = None,
451
+ **opts: Unpack[ApiParams],
452
+ ) -> List[SandboxMetrics]:
453
+ """
454
+ Get the metrics of the current sandbox.
455
+
456
+ :param start: Start time for the metrics, defaults to the start of the sandbox
457
+ :param end: End time for the metrics, defaults to the current time
458
+
459
+ :return: List of sandbox metrics containing CPU, memory and disk usage information
460
+ """
461
+ ...
462
+
463
+ @overload
464
+ @staticmethod
465
+ def get_metrics(
466
+ sandbox_id: str,
467
+ start: Optional[datetime.datetime] = None,
468
+ end: Optional[datetime.datetime] = None,
469
+ **opts: Unpack[ApiParams],
470
+ ) -> List[SandboxMetrics]:
471
+ """
472
+ Get the metrics of the sandbox specified by sandbox ID.
473
+
474
+ :param sandbox_id: Sandbox ID
475
+ :param start: Start time for the metrics, defaults to the start of the sandbox
476
+ :param end: End time for the metrics, defaults to the current time
477
+
478
+ :return: List of sandbox metrics containing CPU, memory and disk usage information
479
+ """
480
+ ...
481
+
482
+ @class_method_variant("_cls_get_metrics")
483
+ def get_metrics(
484
+ self,
485
+ start: Optional[datetime.datetime] = None,
486
+ end: Optional[datetime.datetime] = None,
487
+ **opts: Unpack[ApiParams],
488
+ ) -> List[SandboxMetrics]:
489
+ """
490
+ Get the metrics of the sandbox specified by sandbox ID.
491
+
492
+ :param start: Start time for the metrics, defaults to the start of the sandbox
493
+ :param end: End time for the metrics, defaults to the current time
494
+
495
+ :return: List of sandbox metrics containing CPU, memory and disk usage information
496
+ """
497
+ if self._envd_version < Version("0.1.5"):
498
+ raise SandboxException(
499
+ "Metrics are not supported in this version of the sandbox, please rebuild your template."
500
+ )
501
+
502
+ if self._envd_version < Version("0.2.4"):
503
+ logger.warning(
504
+ "Disk metrics are not supported in this version of the sandbox, please rebuild the template to get disk metrics."
505
+ )
506
+
507
+ return SandboxApi._cls_get_metrics(
508
+ sandbox_id=self.sandbox_id,
509
+ start=start,
510
+ end=end,
511
+ **self.connection_config.get_api_params(**opts),
512
+ )
513
+
514
+ @classmethod
515
+ def beta_create(
516
+ cls,
517
+ template: Optional[str] = None,
518
+ timeout: Optional[int] = None,
519
+ auto_pause: bool = False,
520
+ metadata: Optional[Dict[str, str]] = None,
521
+ envs: Optional[Dict[str, str]] = None,
522
+ secure: bool = True,
523
+ allow_internet_access: bool = True,
524
+ mcp: Optional[McpServer] = None,
525
+ **opts: Unpack[ApiParams],
526
+ ) -> Self:
527
+ """
528
+ [BETA] This feature is in beta and may change in the future.
529
+
530
+ Create a new sandbox.
531
+
532
+ By default, the sandbox is created from the default `base` sandbox template.
533
+
534
+ :param template: Sandbox template name or ID
535
+ :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.
536
+ :param auto_pause: Automatically pause the sandbox after the timeout expires. Defaults to `False`.
537
+ :param metadata: Custom metadata for the sandbox
538
+ :param envs: Custom environment variables for the sandbox
539
+ :param secure: Envd is secured with access token and cannot be used without it, defaults to `True`.
540
+ :param allow_internet_access: Allow sandbox to access the internet, defaults to `True`.
541
+ :param mcp: MCP server to enable in the sandbox
542
+
543
+ :return: A Sandbox instance for the new sandbox
544
+
545
+ Use this method instead of using the constructor to create a new sandbox.
546
+ """
547
+
548
+ if not template and mcp is not None:
549
+ template = cls.default_mcp_template
550
+ elif not template:
551
+ template = cls.default_template
552
+
553
+ sandbox = cls._create(
554
+ template=template,
555
+ auto_pause=auto_pause,
556
+ timeout=timeout,
557
+ metadata=metadata,
558
+ envs=envs,
559
+ secure=secure,
560
+ allow_internet_access=allow_internet_access,
561
+ mcp=mcp,
562
+ **opts,
563
+ )
564
+
565
+ if mcp is not None:
566
+ token = str(uuid.uuid4())
567
+ sandbox._mcp_token = token
568
+
569
+ res = sandbox.commands.run(
570
+ f"mcp-gateway --config '{json.dumps(mcp)}'",
571
+ user="root",
572
+ envs={"GATEWAY_ACCESS_TOKEN": token},
573
+ )
574
+ if res.exit_code != 0:
575
+ raise Exception(f"Failed to start MCP gateway: {res.stderr}")
576
+
577
+ return sandbox
578
+
579
+ @overload
580
+ def beta_pause(
581
+ self,
582
+ **opts: Unpack[ApiParams],
583
+ ) -> None:
584
+ """
585
+ [BETA] This feature is in beta and may change in the future.
586
+
587
+ Pause the sandbox.
588
+ """
589
+ ...
590
+
591
+ @overload
592
+ @classmethod
593
+ def beta_pause(
594
+ cls,
595
+ sandbox_id: str,
596
+ **opts: Unpack[ApiParams],
597
+ ) -> None:
598
+ """
599
+ [BETA] This feature is in beta and may change in the future.
600
+
601
+ Pause the sandbox specified by sandbox ID.
602
+
603
+ :param sandbox_id: Sandbox ID
604
+ """
605
+ ...
606
+
607
+ @class_method_variant("_cls_pause")
608
+ def beta_pause(
609
+ self,
610
+ **opts: Unpack[ApiParams],
611
+ ) -> None:
612
+ """
613
+ [BETA] This feature is in beta and may change in the future.
614
+
615
+ Pause the sandbox.
616
+
617
+ :return: Sandbox ID that can be used to resume the sandbox
618
+ """
619
+
620
+ SandboxApi._cls_pause(
621
+ sandbox_id=self.sandbox_id,
622
+ **opts,
623
+ )
624
+
625
+ def get_mcp_token(self) -> Optional[str]:
626
+ """
627
+ Get the MCP token for the sandbox.
628
+
629
+ :return: MCP token for the sandbox, or None if MCP is not enabled.
630
+ """
631
+ if not self._mcp_token:
632
+ self._mcp_token = self.files.read("/etc/mcp-gateway/.token", user="root")
633
+ return self._mcp_token
634
+
635
+ @classmethod
636
+ def _cls_connect(
637
+ cls,
638
+ sandbox_id: str,
639
+ timeout: Optional[int] = None,
640
+ **opts: Unpack[ApiParams],
641
+ ) -> Self:
642
+ sandbox = SandboxApi._cls_connect(sandbox_id, timeout, **opts)
643
+
644
+ sandbox_headers = {}
645
+ envd_access_token = sandbox.envd_access_token
646
+ if envd_access_token is not None and not isinstance(envd_access_token, Unset):
647
+ sandbox_headers["X-Access-Token"] = envd_access_token
648
+
649
+ connection_config = ConnectionConfig(
650
+ extra_sandbox_headers=sandbox_headers,
651
+ **opts,
652
+ )
653
+
654
+ return cls(
655
+ sandbox_id=sandbox_id,
656
+ sandbox_domain=sandbox.domain,
657
+ connection_config=connection_config,
658
+ envd_version=Version(sandbox.envd_version),
659
+ envd_access_token=envd_access_token,
660
+ traffic_access_token=sandbox.traffic_access_token,
661
+ )
662
+
663
+ @classmethod
664
+ def _create(
665
+ cls,
666
+ template: Optional[str],
667
+ timeout: Optional[int],
668
+ auto_pause: bool,
669
+ metadata: Optional[Dict[str, str]],
670
+ envs: Optional[Dict[str, str]],
671
+ secure: bool,
672
+ allow_internet_access: bool,
673
+ mcp: Optional[McpServer] = None,
674
+ network: Optional[SandboxNetworkOpts] = None,
675
+ **opts: Unpack[ApiParams],
676
+ ) -> Self:
677
+ extra_sandbox_headers = {}
678
+
679
+ debug = opts.get("debug")
680
+ if debug:
681
+ sandbox_id = "debug_sandbox_id"
682
+ sandbox_domain = None
683
+ envd_version = ENVD_DEBUG_FALLBACK
684
+ envd_access_token = None
685
+ traffic_access_token = None
686
+ else:
687
+ response = SandboxApi._create_sandbox(
688
+ template=template or cls.default_template,
689
+ timeout=timeout or cls.default_sandbox_timeout,
690
+ auto_pause=auto_pause,
691
+ metadata=metadata,
692
+ env_vars=envs,
693
+ secure=secure,
694
+ allow_internet_access=allow_internet_access,
695
+ mcp=mcp,
696
+ network=network,
697
+ **opts,
698
+ )
699
+
700
+ sandbox_id = response.sandbox_id
701
+ sandbox_domain = response.sandbox_domain
702
+ envd_version = Version(response.envd_version)
703
+ envd_access_token = response.envd_access_token
704
+ traffic_access_token = response.traffic_access_token
705
+
706
+ if envd_access_token is not None and not isinstance(
707
+ envd_access_token, Unset
708
+ ):
709
+ extra_sandbox_headers["X-Access-Token"] = envd_access_token
710
+
711
+ extra_sandbox_headers["Moru-Sandbox-Id"] = sandbox_id
712
+ extra_sandbox_headers["Moru-Sandbox-Port"] = str(ConnectionConfig.envd_port)
713
+
714
+ connection_config = ConnectionConfig(
715
+ extra_sandbox_headers=extra_sandbox_headers,
716
+ **opts,
717
+ )
718
+
719
+ return cls(
720
+ sandbox_id=sandbox_id,
721
+ sandbox_domain=sandbox_domain,
722
+ envd_version=envd_version,
723
+ envd_access_token=envd_access_token,
724
+ traffic_access_token=traffic_access_token,
725
+ connection_config=connection_config,
726
+ )