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.
- moru/__init__.py +174 -0
- moru/api/__init__.py +164 -0
- moru/api/client/__init__.py +8 -0
- moru/api/client/api/__init__.py +1 -0
- moru/api/client/api/sandboxes/__init__.py +1 -0
- moru/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +161 -0
- moru/api/client/api/sandboxes/get_sandboxes.py +176 -0
- moru/api/client/api/sandboxes/get_sandboxes_metrics.py +173 -0
- moru/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +163 -0
- moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +199 -0
- moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +212 -0
- moru/api/client/api/sandboxes/get_v2_sandboxes.py +230 -0
- moru/api/client/api/sandboxes/post_sandboxes.py +172 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +165 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +181 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +189 -0
- moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +193 -0
- moru/api/client/api/templates/__init__.py +1 -0
- moru/api/client/api/templates/delete_templates_template_id.py +157 -0
- moru/api/client/api/templates/get_templates.py +172 -0
- moru/api/client/api/templates/get_templates_template_id.py +195 -0
- moru/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +217 -0
- moru/api/client/api/templates/get_templates_template_id_files_hash.py +180 -0
- moru/api/client/api/templates/patch_templates_template_id.py +183 -0
- moru/api/client/api/templates/post_templates.py +172 -0
- moru/api/client/api/templates/post_templates_template_id.py +181 -0
- moru/api/client/api/templates/post_templates_template_id_builds_build_id.py +170 -0
- moru/api/client/api/templates/post_v2_templates.py +172 -0
- moru/api/client/api/templates/post_v3_templates.py +172 -0
- moru/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +192 -0
- moru/api/client/client.py +286 -0
- moru/api/client/errors.py +16 -0
- moru/api/client/models/__init__.py +123 -0
- moru/api/client/models/aws_registry.py +85 -0
- moru/api/client/models/aws_registry_type.py +8 -0
- moru/api/client/models/build_log_entry.py +89 -0
- moru/api/client/models/build_status_reason.py +95 -0
- moru/api/client/models/connect_sandbox.py +59 -0
- moru/api/client/models/created_access_token.py +100 -0
- moru/api/client/models/created_team_api_key.py +166 -0
- moru/api/client/models/disk_metrics.py +91 -0
- moru/api/client/models/error.py +67 -0
- moru/api/client/models/gcp_registry.py +69 -0
- moru/api/client/models/gcp_registry_type.py +8 -0
- moru/api/client/models/general_registry.py +77 -0
- moru/api/client/models/general_registry_type.py +8 -0
- moru/api/client/models/identifier_masking_details.py +83 -0
- moru/api/client/models/listed_sandbox.py +154 -0
- moru/api/client/models/log_level.py +11 -0
- moru/api/client/models/max_team_metric.py +78 -0
- moru/api/client/models/mcp_type_0.py +44 -0
- moru/api/client/models/new_access_token.py +59 -0
- moru/api/client/models/new_sandbox.py +172 -0
- moru/api/client/models/new_team_api_key.py +59 -0
- moru/api/client/models/node.py +155 -0
- moru/api/client/models/node_detail.py +165 -0
- moru/api/client/models/node_metrics.py +122 -0
- moru/api/client/models/node_status.py +11 -0
- moru/api/client/models/node_status_change.py +79 -0
- moru/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +59 -0
- moru/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +59 -0
- moru/api/client/models/resumed_sandbox.py +68 -0
- moru/api/client/models/sandbox.py +145 -0
- moru/api/client/models/sandbox_detail.py +183 -0
- moru/api/client/models/sandbox_log.py +70 -0
- moru/api/client/models/sandbox_log_entry.py +93 -0
- moru/api/client/models/sandbox_log_entry_fields.py +44 -0
- moru/api/client/models/sandbox_logs.py +91 -0
- moru/api/client/models/sandbox_metric.py +118 -0
- moru/api/client/models/sandbox_network_config.py +92 -0
- moru/api/client/models/sandbox_state.py +9 -0
- moru/api/client/models/sandboxes_with_metrics.py +59 -0
- moru/api/client/models/team.py +83 -0
- moru/api/client/models/team_api_key.py +158 -0
- moru/api/client/models/team_metric.py +86 -0
- moru/api/client/models/team_user.py +68 -0
- moru/api/client/models/template.py +217 -0
- moru/api/client/models/template_build.py +139 -0
- moru/api/client/models/template_build_file_upload.py +70 -0
- moru/api/client/models/template_build_info.py +126 -0
- moru/api/client/models/template_build_request.py +115 -0
- moru/api/client/models/template_build_request_v2.py +88 -0
- moru/api/client/models/template_build_request_v3.py +88 -0
- moru/api/client/models/template_build_start_v2.py +184 -0
- moru/api/client/models/template_build_status.py +11 -0
- moru/api/client/models/template_legacy.py +207 -0
- moru/api/client/models/template_request_response_v3.py +83 -0
- moru/api/client/models/template_step.py +91 -0
- moru/api/client/models/template_update_request.py +59 -0
- moru/api/client/models/template_with_builds.py +148 -0
- moru/api/client/models/update_team_api_key.py +59 -0
- moru/api/client/py.typed +1 -0
- moru/api/client/types.py +54 -0
- moru/api/client_async/__init__.py +50 -0
- moru/api/client_sync/__init__.py +52 -0
- moru/api/metadata.py +14 -0
- moru/connection_config.py +217 -0
- moru/envd/api.py +59 -0
- moru/envd/filesystem/filesystem_connect.py +193 -0
- moru/envd/filesystem/filesystem_pb2.py +76 -0
- moru/envd/filesystem/filesystem_pb2.pyi +233 -0
- moru/envd/process/process_connect.py +155 -0
- moru/envd/process/process_pb2.py +92 -0
- moru/envd/process/process_pb2.pyi +304 -0
- moru/envd/rpc.py +61 -0
- moru/envd/versions.py +6 -0
- moru/exceptions.py +95 -0
- moru/sandbox/commands/command_handle.py +69 -0
- moru/sandbox/commands/main.py +39 -0
- moru/sandbox/filesystem/filesystem.py +94 -0
- moru/sandbox/filesystem/watch_handle.py +60 -0
- moru/sandbox/main.py +210 -0
- moru/sandbox/mcp.py +1120 -0
- moru/sandbox/network.py +8 -0
- moru/sandbox/sandbox_api.py +210 -0
- moru/sandbox/signature.py +45 -0
- moru/sandbox/utils.py +34 -0
- moru/sandbox_async/commands/command.py +336 -0
- moru/sandbox_async/commands/command_handle.py +196 -0
- moru/sandbox_async/commands/pty.py +240 -0
- moru/sandbox_async/filesystem/filesystem.py +531 -0
- moru/sandbox_async/filesystem/watch_handle.py +62 -0
- moru/sandbox_async/main.py +734 -0
- moru/sandbox_async/paginator.py +69 -0
- moru/sandbox_async/sandbox_api.py +325 -0
- moru/sandbox_async/utils.py +7 -0
- moru/sandbox_sync/commands/command.py +328 -0
- moru/sandbox_sync/commands/command_handle.py +150 -0
- moru/sandbox_sync/commands/pty.py +230 -0
- moru/sandbox_sync/filesystem/filesystem.py +518 -0
- moru/sandbox_sync/filesystem/watch_handle.py +69 -0
- moru/sandbox_sync/main.py +726 -0
- moru/sandbox_sync/paginator.py +69 -0
- moru/sandbox_sync/sandbox_api.py +308 -0
- moru/template/consts.py +30 -0
- moru/template/dockerfile_parser.py +275 -0
- moru/template/logger.py +232 -0
- moru/template/main.py +1360 -0
- moru/template/readycmd.py +138 -0
- moru/template/types.py +105 -0
- moru/template/utils.py +320 -0
- moru/template_async/build_api.py +202 -0
- moru/template_async/main.py +366 -0
- moru/template_sync/build_api.py +199 -0
- moru/template_sync/main.py +371 -0
- moru-0.1.0.dist-info/METADATA +63 -0
- moru-0.1.0.dist-info/RECORD +152 -0
- moru-0.1.0.dist-info/WHEEL +4 -0
- moru-0.1.0.dist-info/licenses/LICENSE +9 -0
- moru_connect/__init__.py +1 -0
- 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
|