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,69 @@
1
+ import urllib.parse
2
+ from typing import Optional, List
3
+
4
+ from moru.api.client.api.sandboxes import get_v2_sandboxes
5
+ from moru.api.client.types import UNSET
6
+ from moru.exceptions import SandboxException
7
+ from moru.sandbox.sandbox_api import SandboxPaginatorBase, SandboxInfo
8
+ from moru.api import handle_api_exception
9
+ from moru.api.client.models.error import Error
10
+ from moru.api.client_async import get_api_client
11
+
12
+
13
+ class AsyncSandboxPaginator(SandboxPaginatorBase):
14
+ """
15
+ Paginator for listing sandboxes.
16
+
17
+ Example:
18
+ ```python
19
+ paginator = AsyncSandbox.list()
20
+
21
+ while paginator.has_next:
22
+ sandboxes = await paginator.next_items()
23
+ print(sandboxes)
24
+ ```
25
+ """
26
+
27
+ async def next_items(self) -> List[SandboxInfo]:
28
+ """
29
+ Returns the next page of sandboxes.
30
+
31
+ Call this method only if `has_next` is `True`, otherwise it will raise an exception.
32
+
33
+ :returns: List of sandboxes
34
+ """
35
+ if not self.has_next:
36
+ raise Exception("No more items to fetch")
37
+
38
+ # Convert filters to the format expected by the API
39
+ metadata: Optional[str] = None
40
+ if self.query and self.query.metadata:
41
+ quoted_metadata = {
42
+ urllib.parse.quote(k): urllib.parse.quote(v)
43
+ for k, v in self.query.metadata.items()
44
+ }
45
+ metadata = urllib.parse.urlencode(quoted_metadata)
46
+
47
+ api_client = get_api_client(self._config)
48
+ res = await get_v2_sandboxes.asyncio_detailed(
49
+ client=api_client,
50
+ metadata=metadata if metadata else UNSET,
51
+ state=self.query.state if self.query and self.query.state else UNSET,
52
+ limit=self.limit if self.limit else UNSET,
53
+ next_token=self._next_token if self._next_token else UNSET,
54
+ )
55
+
56
+ if res.status_code >= 300:
57
+ raise handle_api_exception(res)
58
+
59
+ self._next_token = res.headers.get("x-next-token")
60
+ self._has_next = bool(self._next_token)
61
+
62
+ if res.parsed is None:
63
+ return []
64
+
65
+ # Check if res.parse is Error
66
+ if isinstance(res.parsed, Error):
67
+ raise SandboxException(f"{res.parsed.message}: Request failed")
68
+
69
+ return [SandboxInfo._from_listed_sandbox(sandbox) for sandbox in res.parsed]
@@ -0,0 +1,325 @@
1
+ import datetime
2
+ from typing import Dict, List, Optional
3
+
4
+ from packaging.version import Version
5
+ from typing_extensions import Unpack
6
+
7
+ from moru.api import SandboxCreateResponse, handle_api_exception
8
+ from moru.api.client.api.sandboxes import (
9
+ delete_sandboxes_sandbox_id,
10
+ get_sandboxes_sandbox_id,
11
+ get_sandboxes_sandbox_id_metrics,
12
+ post_sandboxes,
13
+ post_sandboxes_sandbox_id_connect,
14
+ post_sandboxes_sandbox_id_pause,
15
+ post_sandboxes_sandbox_id_timeout,
16
+ )
17
+ from moru.api.client.models import (
18
+ ConnectSandbox,
19
+ Error,
20
+ NewSandbox,
21
+ PostSandboxesSandboxIDTimeoutBody,
22
+ Sandbox,
23
+ SandboxNetworkConfig,
24
+ )
25
+ from moru.api.client.types import UNSET
26
+ from moru.api.client_async import get_api_client
27
+ from moru.connection_config import ApiParams, ConnectionConfig
28
+ from moru.exceptions import NotFoundException, SandboxException, TemplateException
29
+ from moru.sandbox.main import SandboxBase
30
+ from moru.sandbox.sandbox_api import (
31
+ McpServer,
32
+ SandboxInfo,
33
+ SandboxMetrics,
34
+ SandboxNetworkOpts,
35
+ SandboxQuery,
36
+ )
37
+ from moru.sandbox_async.paginator import AsyncSandboxPaginator
38
+
39
+
40
+ class SandboxApi(SandboxBase):
41
+ @staticmethod
42
+ def list(
43
+ query: Optional[SandboxQuery] = None,
44
+ limit: Optional[int] = None,
45
+ next_token: Optional[str] = None,
46
+ **opts: Unpack[ApiParams],
47
+ ) -> AsyncSandboxPaginator:
48
+ """
49
+ List all running sandboxes.
50
+
51
+ :param query: Filter the list of sandboxes by metadata or state, e.g. `SandboxListQuery(metadata={"key": "value"})` or `SandboxListQuery(state=[SandboxState.RUNNING])`
52
+ :param limit: Maximum number of sandboxes to return per page
53
+ :param next_token: Token for pagination
54
+
55
+ :return: List of running sandboxes
56
+ """
57
+ return AsyncSandboxPaginator(
58
+ query=query,
59
+ limit=limit,
60
+ next_token=next_token,
61
+ **opts,
62
+ )
63
+
64
+ @classmethod
65
+ async def _cls_get_info(
66
+ cls,
67
+ sandbox_id: str,
68
+ **opts: Unpack[ApiParams],
69
+ ) -> SandboxInfo:
70
+ """
71
+ Get the sandbox info.
72
+ :param sandbox_id: Sandbox ID
73
+
74
+ :return: Sandbox info
75
+ """
76
+ config = ConnectionConfig(**opts)
77
+
78
+ api_client = get_api_client(config)
79
+ res = await get_sandboxes_sandbox_id.asyncio_detailed(
80
+ sandbox_id,
81
+ client=api_client,
82
+ )
83
+
84
+ if res.status_code == 404:
85
+ raise NotFoundException(f"Sandbox {sandbox_id} not found")
86
+
87
+ if res.status_code >= 300:
88
+ raise handle_api_exception(res)
89
+
90
+ if res.parsed is None:
91
+ raise Exception("Body of the request is None")
92
+
93
+ if isinstance(res.parsed, Error):
94
+ raise SandboxException(f"{res.parsed.message}: Request failed")
95
+
96
+ return SandboxInfo._from_sandbox_detail(res.parsed)
97
+
98
+ @classmethod
99
+ async def _cls_kill(
100
+ cls,
101
+ sandbox_id: str,
102
+ **opts: Unpack[ApiParams],
103
+ ) -> bool:
104
+ config = ConnectionConfig(**opts)
105
+
106
+ if config.debug:
107
+ # Skip killing the sandbox in debug mode
108
+ return True
109
+
110
+ api_client = get_api_client(config)
111
+ res = await delete_sandboxes_sandbox_id.asyncio_detailed(
112
+ sandbox_id,
113
+ client=api_client,
114
+ )
115
+
116
+ if res.status_code == 404:
117
+ return False
118
+
119
+ if res.status_code >= 300:
120
+ raise handle_api_exception(res)
121
+
122
+ return True
123
+
124
+ @classmethod
125
+ async def _cls_set_timeout(
126
+ cls,
127
+ sandbox_id: str,
128
+ timeout: int,
129
+ **opts: Unpack[ApiParams],
130
+ ) -> None:
131
+ config = ConnectionConfig(**opts)
132
+
133
+ if config.debug:
134
+ # Skip setting the timeout in debug mode
135
+ return
136
+
137
+ api_client = get_api_client(config)
138
+ res = await post_sandboxes_sandbox_id_timeout.asyncio_detailed(
139
+ sandbox_id,
140
+ client=api_client,
141
+ body=PostSandboxesSandboxIDTimeoutBody(timeout=timeout),
142
+ )
143
+
144
+ if res.status_code == 404:
145
+ raise NotFoundException(f"Paused sandbox {sandbox_id} not found")
146
+
147
+ if res.status_code >= 300:
148
+ raise handle_api_exception(res)
149
+
150
+ @classmethod
151
+ async def _create_sandbox(
152
+ cls,
153
+ template: str,
154
+ timeout: int,
155
+ auto_pause: bool,
156
+ allow_internet_access: bool,
157
+ metadata: Optional[Dict[str, str]],
158
+ env_vars: Optional[Dict[str, str]],
159
+ secure: bool,
160
+ mcp: Optional[McpServer] = None,
161
+ network: Optional[SandboxNetworkOpts] = None,
162
+ **opts: Unpack[ApiParams],
163
+ ) -> SandboxCreateResponse:
164
+ config = ConnectionConfig(**opts)
165
+
166
+ api_client = get_api_client(config)
167
+ res = await post_sandboxes.asyncio_detailed(
168
+ body=NewSandbox(
169
+ template_id=template,
170
+ auto_pause=auto_pause,
171
+ metadata=metadata or {},
172
+ timeout=timeout,
173
+ env_vars=env_vars or {},
174
+ mcp=mcp or UNSET,
175
+ secure=secure,
176
+ allow_internet_access=allow_internet_access,
177
+ network=SandboxNetworkConfig(**network) if network else UNSET,
178
+ ),
179
+ client=api_client,
180
+ )
181
+
182
+ if res.status_code >= 300:
183
+ raise handle_api_exception(res)
184
+
185
+ if res.parsed is None:
186
+ raise Exception("Body of the request is None")
187
+
188
+ if isinstance(res.parsed, Error):
189
+ raise SandboxException(f"{res.parsed.message}: Request failed")
190
+
191
+ if Version(res.parsed.envd_version) < Version("0.1.0"):
192
+ await SandboxApi._cls_kill(res.parsed.sandbox_id)
193
+ raise TemplateException(
194
+ "You need to update the template to use the new SDK. "
195
+ "You can do this by running `moru template build` in the directory with the template."
196
+ )
197
+
198
+ return SandboxCreateResponse(
199
+ sandbox_id=res.parsed.sandbox_id,
200
+ sandbox_domain=res.parsed.domain,
201
+ envd_version=res.parsed.envd_version,
202
+ envd_access_token=res.parsed.envd_access_token,
203
+ traffic_access_token=res.parsed.traffic_access_token,
204
+ )
205
+
206
+ @classmethod
207
+ async def _cls_get_metrics(
208
+ cls,
209
+ sandbox_id: str,
210
+ start: Optional[datetime.datetime] = None,
211
+ end: Optional[datetime.datetime] = None,
212
+ **opts: Unpack[ApiParams],
213
+ ) -> List[SandboxMetrics]:
214
+ """
215
+ Get the metrics of the sandbox specified by sandbox ID.
216
+
217
+ :param sandbox_id: Sandbox ID
218
+ :param start: Start time for the metrics, defaults to the start of the sandbox
219
+ :param end: End time for the metrics, defaults to the current time
220
+
221
+ :return: List of sandbox metrics containing CPU, memory and disk usage information
222
+ """
223
+ config = ConnectionConfig(**opts)
224
+
225
+ if config.debug:
226
+ # Skip getting the metrics in debug mode
227
+ return []
228
+
229
+ api_client = get_api_client(config)
230
+ res = await get_sandboxes_sandbox_id_metrics.asyncio_detailed(
231
+ sandbox_id,
232
+ start=int(start.timestamp() * 1000) if start else UNSET,
233
+ end=int(end.timestamp() * 1000) if end else UNSET,
234
+ client=api_client,
235
+ )
236
+
237
+ if res.status_code >= 300:
238
+ raise handle_api_exception(res)
239
+
240
+ if res.parsed is None:
241
+ return []
242
+
243
+ # Check if res.parse is Error
244
+ if isinstance(res.parsed, Error):
245
+ raise SandboxException(f"{res.parsed.message}: Request failed")
246
+
247
+ # Convert to typed SandboxMetrics objects
248
+ return [
249
+ SandboxMetrics(
250
+ cpu_count=metric.cpu_count,
251
+ cpu_used_pct=metric.cpu_used_pct,
252
+ disk_total=metric.disk_total,
253
+ disk_used=metric.disk_used,
254
+ mem_total=metric.mem_total,
255
+ mem_used=metric.mem_used,
256
+ timestamp=metric.timestamp,
257
+ )
258
+ for metric in res.parsed
259
+ ]
260
+
261
+ @classmethod
262
+ async def _cls_pause(
263
+ cls,
264
+ sandbox_id: str,
265
+ **opts: Unpack[ApiParams],
266
+ ) -> str:
267
+ config = ConnectionConfig(**opts)
268
+
269
+ api_client = get_api_client(config)
270
+ res = await post_sandboxes_sandbox_id_pause.asyncio_detailed(
271
+ sandbox_id,
272
+ client=api_client,
273
+ )
274
+
275
+ if res.status_code == 404:
276
+ raise NotFoundException(f"Sandbox {sandbox_id} not found")
277
+
278
+ if res.status_code == 409:
279
+ return sandbox_id
280
+
281
+ if res.status_code >= 300:
282
+ raise handle_api_exception(res)
283
+
284
+ # Check if res.parse is Error
285
+ if isinstance(res.parsed, Error):
286
+ raise SandboxException(f"{res.parsed.message}: Request failed")
287
+
288
+ return sandbox_id
289
+
290
+ @classmethod
291
+ async def _cls_connect(
292
+ cls,
293
+ sandbox_id: str,
294
+ timeout: Optional[int] = None,
295
+ **opts: Unpack[ApiParams],
296
+ ) -> Sandbox:
297
+ timeout = timeout or SandboxBase.default_sandbox_timeout
298
+
299
+ # Sandbox is not running, resume it
300
+ config = ConnectionConfig(**opts)
301
+
302
+ api_client = get_api_client(
303
+ config,
304
+ headers={
305
+ "Moru-Sandbox-Id": sandbox_id,
306
+ "Moru-Sandbox-Port": str(config.envd_port),
307
+ },
308
+ )
309
+ res = await post_sandboxes_sandbox_id_connect.asyncio_detailed(
310
+ sandbox_id,
311
+ client=api_client,
312
+ body=ConnectSandbox(timeout=timeout),
313
+ )
314
+
315
+ if res.status_code == 404:
316
+ raise NotFoundException(f"Paused sandbox {sandbox_id} not found")
317
+
318
+ if res.status_code >= 300:
319
+ raise handle_api_exception(res)
320
+
321
+ # Check if res.parse is Error
322
+ if isinstance(res.parsed, Error):
323
+ raise SandboxException(f"{res.parsed.message}: Request failed")
324
+
325
+ return res.parsed
@@ -0,0 +1,7 @@
1
+ from typing import TypeVar, Union, Callable, Awaitable
2
+
3
+ T = TypeVar("T")
4
+ OutputHandler = Union[
5
+ Callable[[T], None],
6
+ Callable[[T], Awaitable[None]],
7
+ ]