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,304 @@
1
+ from google.protobuf.internal import containers as _containers
2
+ from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
3
+ from google.protobuf import descriptor as _descriptor
4
+ from google.protobuf import message as _message
5
+ from typing import (
6
+ ClassVar as _ClassVar,
7
+ Iterable as _Iterable,
8
+ Mapping as _Mapping,
9
+ Optional as _Optional,
10
+ Union as _Union,
11
+ )
12
+
13
+ DESCRIPTOR: _descriptor.FileDescriptor
14
+
15
+ class Signal(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
16
+ __slots__ = ()
17
+ SIGNAL_UNSPECIFIED: _ClassVar[Signal]
18
+ SIGNAL_SIGTERM: _ClassVar[Signal]
19
+ SIGNAL_SIGKILL: _ClassVar[Signal]
20
+
21
+ SIGNAL_UNSPECIFIED: Signal
22
+ SIGNAL_SIGTERM: Signal
23
+ SIGNAL_SIGKILL: Signal
24
+
25
+ class PTY(_message.Message):
26
+ __slots__ = ("size",)
27
+ class Size(_message.Message):
28
+ __slots__ = ("cols", "rows")
29
+ COLS_FIELD_NUMBER: _ClassVar[int]
30
+ ROWS_FIELD_NUMBER: _ClassVar[int]
31
+ cols: int
32
+ rows: int
33
+ def __init__(
34
+ self, cols: _Optional[int] = ..., rows: _Optional[int] = ...
35
+ ) -> None: ...
36
+
37
+ SIZE_FIELD_NUMBER: _ClassVar[int]
38
+ size: PTY.Size
39
+ def __init__(self, size: _Optional[_Union[PTY.Size, _Mapping]] = ...) -> None: ...
40
+
41
+ class ProcessConfig(_message.Message):
42
+ __slots__ = ("cmd", "args", "envs", "cwd")
43
+ class EnvsEntry(_message.Message):
44
+ __slots__ = ("key", "value")
45
+ KEY_FIELD_NUMBER: _ClassVar[int]
46
+ VALUE_FIELD_NUMBER: _ClassVar[int]
47
+ key: str
48
+ value: str
49
+ def __init__(
50
+ self, key: _Optional[str] = ..., value: _Optional[str] = ...
51
+ ) -> None: ...
52
+
53
+ CMD_FIELD_NUMBER: _ClassVar[int]
54
+ ARGS_FIELD_NUMBER: _ClassVar[int]
55
+ ENVS_FIELD_NUMBER: _ClassVar[int]
56
+ CWD_FIELD_NUMBER: _ClassVar[int]
57
+ cmd: str
58
+ args: _containers.RepeatedScalarFieldContainer[str]
59
+ envs: _containers.ScalarMap[str, str]
60
+ cwd: str
61
+ def __init__(
62
+ self,
63
+ cmd: _Optional[str] = ...,
64
+ args: _Optional[_Iterable[str]] = ...,
65
+ envs: _Optional[_Mapping[str, str]] = ...,
66
+ cwd: _Optional[str] = ...,
67
+ ) -> None: ...
68
+
69
+ class ListRequest(_message.Message):
70
+ __slots__ = ()
71
+ def __init__(self) -> None: ...
72
+
73
+ class ProcessInfo(_message.Message):
74
+ __slots__ = ("config", "pid", "tag")
75
+ CONFIG_FIELD_NUMBER: _ClassVar[int]
76
+ PID_FIELD_NUMBER: _ClassVar[int]
77
+ TAG_FIELD_NUMBER: _ClassVar[int]
78
+ config: ProcessConfig
79
+ pid: int
80
+ tag: str
81
+ def __init__(
82
+ self,
83
+ config: _Optional[_Union[ProcessConfig, _Mapping]] = ...,
84
+ pid: _Optional[int] = ...,
85
+ tag: _Optional[str] = ...,
86
+ ) -> None: ...
87
+
88
+ class ListResponse(_message.Message):
89
+ __slots__ = ("processes",)
90
+ PROCESSES_FIELD_NUMBER: _ClassVar[int]
91
+ processes: _containers.RepeatedCompositeFieldContainer[ProcessInfo]
92
+ def __init__(
93
+ self, processes: _Optional[_Iterable[_Union[ProcessInfo, _Mapping]]] = ...
94
+ ) -> None: ...
95
+
96
+ class StartRequest(_message.Message):
97
+ __slots__ = ("process", "pty", "tag", "stdin")
98
+ PROCESS_FIELD_NUMBER: _ClassVar[int]
99
+ PTY_FIELD_NUMBER: _ClassVar[int]
100
+ TAG_FIELD_NUMBER: _ClassVar[int]
101
+ STDIN_FIELD_NUMBER: _ClassVar[int]
102
+ process: ProcessConfig
103
+ pty: PTY
104
+ tag: str
105
+ stdin: bool
106
+ def __init__(
107
+ self,
108
+ process: _Optional[_Union[ProcessConfig, _Mapping]] = ...,
109
+ pty: _Optional[_Union[PTY, _Mapping]] = ...,
110
+ tag: _Optional[str] = ...,
111
+ stdin: bool = ...,
112
+ ) -> None: ...
113
+
114
+ class UpdateRequest(_message.Message):
115
+ __slots__ = ("process", "pty")
116
+ PROCESS_FIELD_NUMBER: _ClassVar[int]
117
+ PTY_FIELD_NUMBER: _ClassVar[int]
118
+ process: ProcessSelector
119
+ pty: PTY
120
+ def __init__(
121
+ self,
122
+ process: _Optional[_Union[ProcessSelector, _Mapping]] = ...,
123
+ pty: _Optional[_Union[PTY, _Mapping]] = ...,
124
+ ) -> None: ...
125
+
126
+ class UpdateResponse(_message.Message):
127
+ __slots__ = ()
128
+ def __init__(self) -> None: ...
129
+
130
+ class ProcessEvent(_message.Message):
131
+ __slots__ = ("start", "data", "end", "keepalive")
132
+ class StartEvent(_message.Message):
133
+ __slots__ = ("pid",)
134
+ PID_FIELD_NUMBER: _ClassVar[int]
135
+ pid: int
136
+ def __init__(self, pid: _Optional[int] = ...) -> None: ...
137
+
138
+ class DataEvent(_message.Message):
139
+ __slots__ = ("stdout", "stderr", "pty")
140
+ STDOUT_FIELD_NUMBER: _ClassVar[int]
141
+ STDERR_FIELD_NUMBER: _ClassVar[int]
142
+ PTY_FIELD_NUMBER: _ClassVar[int]
143
+ stdout: bytes
144
+ stderr: bytes
145
+ pty: bytes
146
+ def __init__(
147
+ self,
148
+ stdout: _Optional[bytes] = ...,
149
+ stderr: _Optional[bytes] = ...,
150
+ pty: _Optional[bytes] = ...,
151
+ ) -> None: ...
152
+
153
+ class EndEvent(_message.Message):
154
+ __slots__ = ("exit_code", "exited", "status", "error")
155
+ EXIT_CODE_FIELD_NUMBER: _ClassVar[int]
156
+ EXITED_FIELD_NUMBER: _ClassVar[int]
157
+ STATUS_FIELD_NUMBER: _ClassVar[int]
158
+ ERROR_FIELD_NUMBER: _ClassVar[int]
159
+ exit_code: int
160
+ exited: bool
161
+ status: str
162
+ error: str
163
+ def __init__(
164
+ self,
165
+ exit_code: _Optional[int] = ...,
166
+ exited: bool = ...,
167
+ status: _Optional[str] = ...,
168
+ error: _Optional[str] = ...,
169
+ ) -> None: ...
170
+
171
+ class KeepAlive(_message.Message):
172
+ __slots__ = ()
173
+ def __init__(self) -> None: ...
174
+
175
+ START_FIELD_NUMBER: _ClassVar[int]
176
+ DATA_FIELD_NUMBER: _ClassVar[int]
177
+ END_FIELD_NUMBER: _ClassVar[int]
178
+ KEEPALIVE_FIELD_NUMBER: _ClassVar[int]
179
+ start: ProcessEvent.StartEvent
180
+ data: ProcessEvent.DataEvent
181
+ end: ProcessEvent.EndEvent
182
+ keepalive: ProcessEvent.KeepAlive
183
+ def __init__(
184
+ self,
185
+ start: _Optional[_Union[ProcessEvent.StartEvent, _Mapping]] = ...,
186
+ data: _Optional[_Union[ProcessEvent.DataEvent, _Mapping]] = ...,
187
+ end: _Optional[_Union[ProcessEvent.EndEvent, _Mapping]] = ...,
188
+ keepalive: _Optional[_Union[ProcessEvent.KeepAlive, _Mapping]] = ...,
189
+ ) -> None: ...
190
+
191
+ class StartResponse(_message.Message):
192
+ __slots__ = ("event",)
193
+ EVENT_FIELD_NUMBER: _ClassVar[int]
194
+ event: ProcessEvent
195
+ def __init__(
196
+ self, event: _Optional[_Union[ProcessEvent, _Mapping]] = ...
197
+ ) -> None: ...
198
+
199
+ class ConnectResponse(_message.Message):
200
+ __slots__ = ("event",)
201
+ EVENT_FIELD_NUMBER: _ClassVar[int]
202
+ event: ProcessEvent
203
+ def __init__(
204
+ self, event: _Optional[_Union[ProcessEvent, _Mapping]] = ...
205
+ ) -> None: ...
206
+
207
+ class SendInputRequest(_message.Message):
208
+ __slots__ = ("process", "input")
209
+ PROCESS_FIELD_NUMBER: _ClassVar[int]
210
+ INPUT_FIELD_NUMBER: _ClassVar[int]
211
+ process: ProcessSelector
212
+ input: ProcessInput
213
+ def __init__(
214
+ self,
215
+ process: _Optional[_Union[ProcessSelector, _Mapping]] = ...,
216
+ input: _Optional[_Union[ProcessInput, _Mapping]] = ...,
217
+ ) -> None: ...
218
+
219
+ class SendInputResponse(_message.Message):
220
+ __slots__ = ()
221
+ def __init__(self) -> None: ...
222
+
223
+ class ProcessInput(_message.Message):
224
+ __slots__ = ("stdin", "pty")
225
+ STDIN_FIELD_NUMBER: _ClassVar[int]
226
+ PTY_FIELD_NUMBER: _ClassVar[int]
227
+ stdin: bytes
228
+ pty: bytes
229
+ def __init__(
230
+ self, stdin: _Optional[bytes] = ..., pty: _Optional[bytes] = ...
231
+ ) -> None: ...
232
+
233
+ class StreamInputRequest(_message.Message):
234
+ __slots__ = ("start", "data", "keepalive")
235
+ class StartEvent(_message.Message):
236
+ __slots__ = ("process",)
237
+ PROCESS_FIELD_NUMBER: _ClassVar[int]
238
+ process: ProcessSelector
239
+ def __init__(
240
+ self, process: _Optional[_Union[ProcessSelector, _Mapping]] = ...
241
+ ) -> None: ...
242
+
243
+ class DataEvent(_message.Message):
244
+ __slots__ = ("input",)
245
+ INPUT_FIELD_NUMBER: _ClassVar[int]
246
+ input: ProcessInput
247
+ def __init__(
248
+ self, input: _Optional[_Union[ProcessInput, _Mapping]] = ...
249
+ ) -> None: ...
250
+
251
+ class KeepAlive(_message.Message):
252
+ __slots__ = ()
253
+ def __init__(self) -> None: ...
254
+
255
+ START_FIELD_NUMBER: _ClassVar[int]
256
+ DATA_FIELD_NUMBER: _ClassVar[int]
257
+ KEEPALIVE_FIELD_NUMBER: _ClassVar[int]
258
+ start: StreamInputRequest.StartEvent
259
+ data: StreamInputRequest.DataEvent
260
+ keepalive: StreamInputRequest.KeepAlive
261
+ def __init__(
262
+ self,
263
+ start: _Optional[_Union[StreamInputRequest.StartEvent, _Mapping]] = ...,
264
+ data: _Optional[_Union[StreamInputRequest.DataEvent, _Mapping]] = ...,
265
+ keepalive: _Optional[_Union[StreamInputRequest.KeepAlive, _Mapping]] = ...,
266
+ ) -> None: ...
267
+
268
+ class StreamInputResponse(_message.Message):
269
+ __slots__ = ()
270
+ def __init__(self) -> None: ...
271
+
272
+ class SendSignalRequest(_message.Message):
273
+ __slots__ = ("process", "signal")
274
+ PROCESS_FIELD_NUMBER: _ClassVar[int]
275
+ SIGNAL_FIELD_NUMBER: _ClassVar[int]
276
+ process: ProcessSelector
277
+ signal: Signal
278
+ def __init__(
279
+ self,
280
+ process: _Optional[_Union[ProcessSelector, _Mapping]] = ...,
281
+ signal: _Optional[_Union[Signal, str]] = ...,
282
+ ) -> None: ...
283
+
284
+ class SendSignalResponse(_message.Message):
285
+ __slots__ = ()
286
+ def __init__(self) -> None: ...
287
+
288
+ class ConnectRequest(_message.Message):
289
+ __slots__ = ("process",)
290
+ PROCESS_FIELD_NUMBER: _ClassVar[int]
291
+ process: ProcessSelector
292
+ def __init__(
293
+ self, process: _Optional[_Union[ProcessSelector, _Mapping]] = ...
294
+ ) -> None: ...
295
+
296
+ class ProcessSelector(_message.Message):
297
+ __slots__ = ("pid", "tag")
298
+ PID_FIELD_NUMBER: _ClassVar[int]
299
+ TAG_FIELD_NUMBER: _ClassVar[int]
300
+ pid: int
301
+ tag: str
302
+ def __init__(
303
+ self, pid: _Optional[int] = ..., tag: _Optional[str] = ...
304
+ ) -> None: ...
moru/envd/rpc.py ADDED
@@ -0,0 +1,61 @@
1
+ import base64
2
+
3
+ from typing import Optional
4
+ from packaging.version import Version
5
+ from moru_connect.client import Code, ConnectException
6
+
7
+ from moru.exceptions import (
8
+ SandboxException,
9
+ InvalidArgumentException,
10
+ NotFoundException,
11
+ TimeoutException,
12
+ format_sandbox_timeout_exception,
13
+ AuthenticationException,
14
+ RateLimitException,
15
+ )
16
+ from moru.connection_config import Username, default_username
17
+ from moru.envd.versions import ENVD_DEFAULT_USER
18
+
19
+
20
+ def handle_rpc_exception(e: Exception):
21
+ if isinstance(e, ConnectException):
22
+ if e.status == Code.invalid_argument:
23
+ return InvalidArgumentException(e.message)
24
+ elif e.status == Code.unauthenticated:
25
+ return AuthenticationException(e.message)
26
+ elif e.status == Code.not_found:
27
+ return NotFoundException(e.message)
28
+ elif e.status == Code.unavailable:
29
+ return format_sandbox_timeout_exception(e.message)
30
+ elif e.status == Code.resource_exhausted:
31
+ return RateLimitException(
32
+ f"{e.message}: Rate limit exceeded, please try again later."
33
+ )
34
+ elif e.status == Code.canceled:
35
+ return TimeoutException(
36
+ f"{e.message}: This error is likely due to exceeding 'request_timeout'. You can pass the request timeout value as an option when making the request."
37
+ )
38
+ elif e.status == Code.deadline_exceeded:
39
+ return TimeoutException(
40
+ f"{e.message}: This error is likely due to exceeding 'timeout' — the total time a long running request (like process or directory watch) can be active. It can be modified by passing 'timeout' when making the request. Use '0' to disable the timeout."
41
+ )
42
+ else:
43
+ return SandboxException(f"{e.status}: {e.message}")
44
+ else:
45
+ return e
46
+
47
+
48
+ def authentication_header(
49
+ envd_version: Version, user: Optional[Username] = None
50
+ ) -> dict[str, str]:
51
+ if user is None and envd_version < ENVD_DEFAULT_USER:
52
+ user = default_username
53
+
54
+ if not user:
55
+ return {}
56
+
57
+ value = f"{user}:"
58
+
59
+ encoded = base64.b64encode(value.encode("utf-8")).decode("utf-8")
60
+
61
+ return {"Authorization": f"Basic {encoded}"}
moru/envd/versions.py ADDED
@@ -0,0 +1,6 @@
1
+ from packaging.version import Version
2
+
3
+ ENVD_VERSION_RECURSIVE_WATCH = Version("0.1.4")
4
+ ENVD_DEBUG_FALLBACK = Version("99.99.99")
5
+ ENVD_COMMANDS_STDIN = Version("0.3.0")
6
+ ENVD_DEFAULT_USER = Version("0.4.0")
moru/exceptions.py ADDED
@@ -0,0 +1,95 @@
1
+ def format_sandbox_timeout_exception(message: str):
2
+ return TimeoutException(
3
+ f"{message}: This error is likely due to sandbox timeout. You can modify the sandbox timeout by passing 'timeout' when starting the sandbox or calling '.set_timeout' on the sandbox with the desired timeout."
4
+ )
5
+
6
+
7
+ def format_request_timeout_error() -> Exception:
8
+ return TimeoutException(
9
+ "Request timed out — the 'request_timeout' option can be used to increase this timeout",
10
+ )
11
+
12
+
13
+ def format_execution_timeout_error() -> Exception:
14
+ return TimeoutException(
15
+ "Execution timed out — the 'timeout' option can be used to increase this timeout",
16
+ )
17
+
18
+
19
+ class SandboxException(Exception):
20
+ """
21
+ Base class for all sandbox errors.
22
+
23
+ Raised when a general sandbox exception occurs.
24
+ """
25
+
26
+ pass
27
+
28
+
29
+ class TimeoutException(SandboxException):
30
+ """
31
+ Raised when a timeout occurs.
32
+
33
+ The `unavailable` exception type is caused by sandbox timeout.\n
34
+ The `canceled` exception type is caused by exceeding request timeout.\n
35
+ The `deadline_exceeded` exception type is caused by exceeding the timeout for process, watch, etc.\n
36
+ The `unknown` exception type is sometimes caused by the sandbox timeout when the request is not processed correctly.\n
37
+ """
38
+
39
+ pass
40
+
41
+
42
+ class InvalidArgumentException(SandboxException):
43
+ """
44
+ Raised when an invalid argument is provided.
45
+ """
46
+
47
+ pass
48
+
49
+
50
+ class NotEnoughSpaceException(SandboxException):
51
+ """
52
+ Raised when there is not enough disk space.
53
+ """
54
+
55
+ pass
56
+
57
+
58
+ class NotFoundException(SandboxException):
59
+ """
60
+ Raised when a resource is not found.
61
+ """
62
+
63
+ pass
64
+
65
+
66
+ class AuthenticationException(Exception):
67
+ """
68
+ Raised when authentication fails.
69
+ """
70
+
71
+ pass
72
+
73
+
74
+ class TemplateException(SandboxException):
75
+ """
76
+ Exception raised when the template uses old envd version. It isn't compatible with the new SDK.
77
+ """
78
+
79
+
80
+ class RateLimitException(SandboxException):
81
+ """
82
+ Raised when the API rate limit is exceeded.
83
+ """
84
+
85
+
86
+ class BuildException(Exception):
87
+ """
88
+ Raised when the build fails.
89
+ """
90
+
91
+
92
+ class FileUploadException(BuildException):
93
+ """
94
+ Raised when the file upload fails.
95
+ """
@@ -0,0 +1,69 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ from moru.exceptions import SandboxException
5
+
6
+ Stdout = str
7
+ """
8
+ Command stdout output.
9
+ """
10
+ Stderr = str
11
+ """
12
+ Command stderr output.
13
+ """
14
+ PtyOutput = bytes
15
+ """
16
+ Pty output.
17
+ """
18
+
19
+
20
+ @dataclass
21
+ class PtySize:
22
+ """
23
+ Pseudo-terminal size.
24
+ """
25
+
26
+ rows: int
27
+ """
28
+ Number of rows.
29
+ """
30
+ cols: int
31
+ """
32
+ Number of columns.
33
+ """
34
+
35
+
36
+ @dataclass
37
+ class CommandResult:
38
+ """
39
+ Command execution result.
40
+ """
41
+
42
+ stderr: str
43
+ """
44
+ Command stderr output.
45
+ """
46
+ stdout: str
47
+ """
48
+ Command stdout output.
49
+ """
50
+ exit_code: int
51
+ """
52
+ Command exit code.
53
+
54
+ `0` if the command finished successfully.
55
+ """
56
+ error: Optional[str]
57
+ """
58
+ Error message from command execution if it failed.
59
+ """
60
+
61
+
62
+ @dataclass
63
+ class CommandExitException(SandboxException, CommandResult):
64
+ """
65
+ Exception raised when a command exits with a non-zero exit code.
66
+ """
67
+
68
+ def __str__(self):
69
+ return f"Command exited with code {self.exit_code} and error:\n{self.stderr}"
@@ -0,0 +1,39 @@
1
+ from dataclasses import dataclass
2
+ from typing import Dict, List, Optional
3
+
4
+
5
+ @dataclass
6
+ class ProcessInfo:
7
+ """
8
+ Information about a command, PTY session or start command running in the sandbox as process.
9
+ """
10
+
11
+ pid: int
12
+ """
13
+ Process ID.
14
+ """
15
+
16
+ tag: Optional[str]
17
+ """
18
+ Custom tag used for identifying special commands like start command in the custom template.
19
+ """
20
+
21
+ cmd: str
22
+ """
23
+ Command that was executed.
24
+ """
25
+
26
+ args: List[str]
27
+ """
28
+ Command arguments.
29
+ """
30
+
31
+ envs: Dict[str, str]
32
+ """
33
+ Environment variables used for the command.
34
+ """
35
+
36
+ cwd: Optional[str]
37
+ """
38
+ Executed command working directory.
39
+ """
@@ -0,0 +1,94 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+ from enum import Enum
4
+ from typing import IO, Optional, Union, TypedDict
5
+
6
+ from moru.envd.filesystem import filesystem_pb2
7
+
8
+
9
+ class FileType(Enum):
10
+ """
11
+ Enum representing the type of filesystem object.
12
+ """
13
+
14
+ FILE = "file"
15
+ """
16
+ Filesystem object is a file.
17
+ """
18
+ DIR = "dir"
19
+ """
20
+ Filesystem object is a directory.
21
+ """
22
+
23
+
24
+ def map_file_type(ft: filesystem_pb2.FileType):
25
+ if ft == filesystem_pb2.FileType.FILE_TYPE_FILE:
26
+ return FileType.FILE
27
+ elif ft == filesystem_pb2.FileType.FILE_TYPE_DIRECTORY:
28
+ return FileType.DIR
29
+
30
+
31
+ @dataclass
32
+ class WriteInfo:
33
+ """
34
+ Sandbox filesystem object information.
35
+ """
36
+
37
+ name: str
38
+ """
39
+ Name of the filesystem object.
40
+ """
41
+ type: Optional[FileType]
42
+ """
43
+ Type of the filesystem object.
44
+ """
45
+ path: str
46
+ """
47
+ Path to the filesystem object.
48
+ """
49
+
50
+
51
+ @dataclass
52
+ class EntryInfo(WriteInfo):
53
+ """
54
+ Extended sandbox filesystem object information.
55
+ """
56
+
57
+ size: int
58
+ """
59
+ Size of the filesystem object in bytes.
60
+ """
61
+ mode: int
62
+ """
63
+ File mode and permission bits.
64
+ """
65
+ permissions: str
66
+ """
67
+ String representation of file permissions (e.g. 'rwxr-xr-x').
68
+ """
69
+ owner: str
70
+ """
71
+ Owner of the filesystem object.
72
+ """
73
+ group: str
74
+ """
75
+ Group owner of the filesystem object.
76
+ """
77
+ modified_time: datetime
78
+ """
79
+ Last modification time of the filesystem object.
80
+ """
81
+ symlink_target: Optional[str] = None
82
+ """
83
+ Target of the symlink if the filesystem object is a symlink.
84
+ If the filesystem object is not a symlink, this field is None.
85
+ """
86
+
87
+
88
+ class WriteEntry(TypedDict):
89
+ """
90
+ Contains path and data of the file to be written to the filesystem.
91
+ """
92
+
93
+ path: str
94
+ data: Union[str, bytes, IO]