moru 0.1.0__tar.gz
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-0.1.0/LICENSE +9 -0
- moru-0.1.0/PKG-INFO +63 -0
- moru-0.1.0/README.md +30 -0
- moru-0.1.0/moru/__init__.py +174 -0
- moru-0.1.0/moru/api/__init__.py +164 -0
- moru-0.1.0/moru/api/client/__init__.py +8 -0
- moru-0.1.0/moru/api/client/api/__init__.py +1 -0
- moru-0.1.0/moru/api/client/api/sandboxes/__init__.py +1 -0
- moru-0.1.0/moru/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +161 -0
- moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes.py +176 -0
- moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes_metrics.py +173 -0
- moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +163 -0
- moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +199 -0
- moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +212 -0
- moru-0.1.0/moru/api/client/api/sandboxes/get_v2_sandboxes.py +230 -0
- moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes.py +172 -0
- moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
- moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +165 -0
- moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +181 -0
- moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +189 -0
- moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +193 -0
- moru-0.1.0/moru/api/client/api/templates/__init__.py +1 -0
- moru-0.1.0/moru/api/client/api/templates/delete_templates_template_id.py +157 -0
- moru-0.1.0/moru/api/client/api/templates/get_templates.py +172 -0
- moru-0.1.0/moru/api/client/api/templates/get_templates_template_id.py +195 -0
- moru-0.1.0/moru/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +217 -0
- moru-0.1.0/moru/api/client/api/templates/get_templates_template_id_files_hash.py +180 -0
- moru-0.1.0/moru/api/client/api/templates/patch_templates_template_id.py +183 -0
- moru-0.1.0/moru/api/client/api/templates/post_templates.py +172 -0
- moru-0.1.0/moru/api/client/api/templates/post_templates_template_id.py +181 -0
- moru-0.1.0/moru/api/client/api/templates/post_templates_template_id_builds_build_id.py +170 -0
- moru-0.1.0/moru/api/client/api/templates/post_v2_templates.py +172 -0
- moru-0.1.0/moru/api/client/api/templates/post_v3_templates.py +172 -0
- moru-0.1.0/moru/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +192 -0
- moru-0.1.0/moru/api/client/client.py +286 -0
- moru-0.1.0/moru/api/client/errors.py +16 -0
- moru-0.1.0/moru/api/client/models/__init__.py +123 -0
- moru-0.1.0/moru/api/client/models/aws_registry.py +85 -0
- moru-0.1.0/moru/api/client/models/aws_registry_type.py +8 -0
- moru-0.1.0/moru/api/client/models/build_log_entry.py +89 -0
- moru-0.1.0/moru/api/client/models/build_status_reason.py +95 -0
- moru-0.1.0/moru/api/client/models/connect_sandbox.py +59 -0
- moru-0.1.0/moru/api/client/models/created_access_token.py +100 -0
- moru-0.1.0/moru/api/client/models/created_team_api_key.py +166 -0
- moru-0.1.0/moru/api/client/models/disk_metrics.py +91 -0
- moru-0.1.0/moru/api/client/models/error.py +67 -0
- moru-0.1.0/moru/api/client/models/gcp_registry.py +69 -0
- moru-0.1.0/moru/api/client/models/gcp_registry_type.py +8 -0
- moru-0.1.0/moru/api/client/models/general_registry.py +77 -0
- moru-0.1.0/moru/api/client/models/general_registry_type.py +8 -0
- moru-0.1.0/moru/api/client/models/identifier_masking_details.py +83 -0
- moru-0.1.0/moru/api/client/models/listed_sandbox.py +154 -0
- moru-0.1.0/moru/api/client/models/log_level.py +11 -0
- moru-0.1.0/moru/api/client/models/max_team_metric.py +78 -0
- moru-0.1.0/moru/api/client/models/mcp_type_0.py +44 -0
- moru-0.1.0/moru/api/client/models/new_access_token.py +59 -0
- moru-0.1.0/moru/api/client/models/new_sandbox.py +172 -0
- moru-0.1.0/moru/api/client/models/new_team_api_key.py +59 -0
- moru-0.1.0/moru/api/client/models/node.py +155 -0
- moru-0.1.0/moru/api/client/models/node_detail.py +165 -0
- moru-0.1.0/moru/api/client/models/node_metrics.py +122 -0
- moru-0.1.0/moru/api/client/models/node_status.py +11 -0
- moru-0.1.0/moru/api/client/models/node_status_change.py +79 -0
- moru-0.1.0/moru/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +59 -0
- moru-0.1.0/moru/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +59 -0
- moru-0.1.0/moru/api/client/models/resumed_sandbox.py +68 -0
- moru-0.1.0/moru/api/client/models/sandbox.py +145 -0
- moru-0.1.0/moru/api/client/models/sandbox_detail.py +183 -0
- moru-0.1.0/moru/api/client/models/sandbox_log.py +70 -0
- moru-0.1.0/moru/api/client/models/sandbox_log_entry.py +93 -0
- moru-0.1.0/moru/api/client/models/sandbox_log_entry_fields.py +44 -0
- moru-0.1.0/moru/api/client/models/sandbox_logs.py +91 -0
- moru-0.1.0/moru/api/client/models/sandbox_metric.py +118 -0
- moru-0.1.0/moru/api/client/models/sandbox_network_config.py +92 -0
- moru-0.1.0/moru/api/client/models/sandbox_state.py +9 -0
- moru-0.1.0/moru/api/client/models/sandboxes_with_metrics.py +59 -0
- moru-0.1.0/moru/api/client/models/team.py +83 -0
- moru-0.1.0/moru/api/client/models/team_api_key.py +158 -0
- moru-0.1.0/moru/api/client/models/team_metric.py +86 -0
- moru-0.1.0/moru/api/client/models/team_user.py +68 -0
- moru-0.1.0/moru/api/client/models/template.py +217 -0
- moru-0.1.0/moru/api/client/models/template_build.py +139 -0
- moru-0.1.0/moru/api/client/models/template_build_file_upload.py +70 -0
- moru-0.1.0/moru/api/client/models/template_build_info.py +126 -0
- moru-0.1.0/moru/api/client/models/template_build_request.py +115 -0
- moru-0.1.0/moru/api/client/models/template_build_request_v2.py +88 -0
- moru-0.1.0/moru/api/client/models/template_build_request_v3.py +88 -0
- moru-0.1.0/moru/api/client/models/template_build_start_v2.py +184 -0
- moru-0.1.0/moru/api/client/models/template_build_status.py +11 -0
- moru-0.1.0/moru/api/client/models/template_legacy.py +207 -0
- moru-0.1.0/moru/api/client/models/template_request_response_v3.py +83 -0
- moru-0.1.0/moru/api/client/models/template_step.py +91 -0
- moru-0.1.0/moru/api/client/models/template_update_request.py +59 -0
- moru-0.1.0/moru/api/client/models/template_with_builds.py +148 -0
- moru-0.1.0/moru/api/client/models/update_team_api_key.py +59 -0
- moru-0.1.0/moru/api/client/py.typed +1 -0
- moru-0.1.0/moru/api/client/types.py +54 -0
- moru-0.1.0/moru/api/client_async/__init__.py +50 -0
- moru-0.1.0/moru/api/client_sync/__init__.py +52 -0
- moru-0.1.0/moru/api/metadata.py +14 -0
- moru-0.1.0/moru/connection_config.py +217 -0
- moru-0.1.0/moru/envd/api.py +59 -0
- moru-0.1.0/moru/envd/filesystem/filesystem_connect.py +193 -0
- moru-0.1.0/moru/envd/filesystem/filesystem_pb2.py +76 -0
- moru-0.1.0/moru/envd/filesystem/filesystem_pb2.pyi +233 -0
- moru-0.1.0/moru/envd/process/process_connect.py +155 -0
- moru-0.1.0/moru/envd/process/process_pb2.py +92 -0
- moru-0.1.0/moru/envd/process/process_pb2.pyi +304 -0
- moru-0.1.0/moru/envd/rpc.py +61 -0
- moru-0.1.0/moru/envd/versions.py +6 -0
- moru-0.1.0/moru/exceptions.py +95 -0
- moru-0.1.0/moru/sandbox/commands/command_handle.py +69 -0
- moru-0.1.0/moru/sandbox/commands/main.py +39 -0
- moru-0.1.0/moru/sandbox/filesystem/filesystem.py +94 -0
- moru-0.1.0/moru/sandbox/filesystem/watch_handle.py +60 -0
- moru-0.1.0/moru/sandbox/main.py +210 -0
- moru-0.1.0/moru/sandbox/mcp.py +1120 -0
- moru-0.1.0/moru/sandbox/network.py +8 -0
- moru-0.1.0/moru/sandbox/sandbox_api.py +210 -0
- moru-0.1.0/moru/sandbox/signature.py +45 -0
- moru-0.1.0/moru/sandbox/utils.py +34 -0
- moru-0.1.0/moru/sandbox_async/commands/command.py +336 -0
- moru-0.1.0/moru/sandbox_async/commands/command_handle.py +196 -0
- moru-0.1.0/moru/sandbox_async/commands/pty.py +240 -0
- moru-0.1.0/moru/sandbox_async/filesystem/filesystem.py +531 -0
- moru-0.1.0/moru/sandbox_async/filesystem/watch_handle.py +62 -0
- moru-0.1.0/moru/sandbox_async/main.py +734 -0
- moru-0.1.0/moru/sandbox_async/paginator.py +69 -0
- moru-0.1.0/moru/sandbox_async/sandbox_api.py +325 -0
- moru-0.1.0/moru/sandbox_async/utils.py +7 -0
- moru-0.1.0/moru/sandbox_sync/commands/command.py +328 -0
- moru-0.1.0/moru/sandbox_sync/commands/command_handle.py +150 -0
- moru-0.1.0/moru/sandbox_sync/commands/pty.py +230 -0
- moru-0.1.0/moru/sandbox_sync/filesystem/filesystem.py +518 -0
- moru-0.1.0/moru/sandbox_sync/filesystem/watch_handle.py +69 -0
- moru-0.1.0/moru/sandbox_sync/main.py +726 -0
- moru-0.1.0/moru/sandbox_sync/paginator.py +69 -0
- moru-0.1.0/moru/sandbox_sync/sandbox_api.py +308 -0
- moru-0.1.0/moru/template/consts.py +30 -0
- moru-0.1.0/moru/template/dockerfile_parser.py +275 -0
- moru-0.1.0/moru/template/logger.py +232 -0
- moru-0.1.0/moru/template/main.py +1360 -0
- moru-0.1.0/moru/template/readycmd.py +138 -0
- moru-0.1.0/moru/template/types.py +105 -0
- moru-0.1.0/moru/template/utils.py +320 -0
- moru-0.1.0/moru/template_async/build_api.py +202 -0
- moru-0.1.0/moru/template_async/main.py +366 -0
- moru-0.1.0/moru/template_sync/build_api.py +199 -0
- moru-0.1.0/moru/template_sync/main.py +371 -0
- moru-0.1.0/moru_connect/__init__.py +1 -0
- moru-0.1.0/moru_connect/client.py +493 -0
- moru-0.1.0/pyproject.toml +46 -0
moru-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 FOUNDRYLABS, INC.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
moru-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: moru
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Moru SDK that gives agents cloud environments
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Moru AI
|
|
8
|
+
Author-email: hello@moru.ai
|
|
9
|
+
Requires-Python: >=3.9,<4.0
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Requires-Dist: attrs (>=23.2.0)
|
|
19
|
+
Requires-Dist: dockerfile-parse (>=2.0.1,<3.0.0)
|
|
20
|
+
Requires-Dist: httpcore (>=1.0.5,<2.0.0)
|
|
21
|
+
Requires-Dist: httpx (>=0.27.0,<1.0.0)
|
|
22
|
+
Requires-Dist: packaging (>=24.1)
|
|
23
|
+
Requires-Dist: protobuf (>=4.21.0)
|
|
24
|
+
Requires-Dist: python-dateutil (>=2.8.2)
|
|
25
|
+
Requires-Dist: rich (>=14.0.0)
|
|
26
|
+
Requires-Dist: typing-extensions (>=4.1.0)
|
|
27
|
+
Requires-Dist: wcmatch (>=10.1,<11.0)
|
|
28
|
+
Project-URL: Bug Tracker, https://github.com/moru-ai/sdks/issues
|
|
29
|
+
Project-URL: Homepage, https://github.com/moru-ai/sdks
|
|
30
|
+
Project-URL: Repository, https://github.com/moru-ai/sdks/tree/main/packages/python-sdk
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# Moru Python SDK
|
|
34
|
+
|
|
35
|
+
Moru SDK for Python provides cloud environments for AI agents.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install moru
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
### 1. Set your API key
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
export MORU_API_KEY=your_api_key
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. Create a sandbox
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from moru import Sandbox
|
|
55
|
+
|
|
56
|
+
with Sandbox() as sandbox:
|
|
57
|
+
sandbox.run_code('print("Hello from Moru!")')
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Acknowledgement
|
|
61
|
+
|
|
62
|
+
This project is a fork of [E2B](https://github.com/e2b-dev/E2B).
|
|
63
|
+
|
moru-0.1.0/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Moru Python SDK
|
|
2
|
+
|
|
3
|
+
Moru SDK for Python provides cloud environments for AI agents.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install moru
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### 1. Set your API key
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
export MORU_API_KEY=your_api_key
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 2. Create a sandbox
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from moru import Sandbox
|
|
23
|
+
|
|
24
|
+
with Sandbox() as sandbox:
|
|
25
|
+
sandbox.run_code('print("Hello from Moru!")')
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Acknowledgement
|
|
29
|
+
|
|
30
|
+
This project is a fork of [E2B](https://github.com/e2b-dev/E2B).
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Secure sandboxed cloud environments made for AI agents and AI apps.
|
|
3
|
+
|
|
4
|
+
Check docs [here](https://moru.io/docs).
|
|
5
|
+
|
|
6
|
+
Moru Sandbox is a secure cloud sandbox environment made for AI agents and AI
|
|
7
|
+
apps.
|
|
8
|
+
Sandboxes allow AI agents and apps to have long running cloud secure environments.
|
|
9
|
+
In these environments, large language models can use the same tools as humans do.
|
|
10
|
+
|
|
11
|
+
Moru Python SDK supports both sync and async API:
|
|
12
|
+
|
|
13
|
+
```py
|
|
14
|
+
from moru import Sandbox
|
|
15
|
+
|
|
16
|
+
# Create sandbox
|
|
17
|
+
sandbox = Sandbox.create()
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```py
|
|
21
|
+
from moru import AsyncSandbox
|
|
22
|
+
|
|
23
|
+
# Create sandbox
|
|
24
|
+
sandbox = await AsyncSandbox.create()
|
|
25
|
+
```
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from .api import (
|
|
29
|
+
ApiClient,
|
|
30
|
+
client,
|
|
31
|
+
)
|
|
32
|
+
from .connection_config import (
|
|
33
|
+
ConnectionConfig,
|
|
34
|
+
ProxyTypes,
|
|
35
|
+
)
|
|
36
|
+
from .exceptions import (
|
|
37
|
+
AuthenticationException,
|
|
38
|
+
BuildException,
|
|
39
|
+
FileUploadException,
|
|
40
|
+
InvalidArgumentException,
|
|
41
|
+
NotEnoughSpaceException,
|
|
42
|
+
NotFoundException,
|
|
43
|
+
SandboxException,
|
|
44
|
+
TemplateException,
|
|
45
|
+
TimeoutException,
|
|
46
|
+
)
|
|
47
|
+
from .sandbox.commands.command_handle import (
|
|
48
|
+
CommandExitException,
|
|
49
|
+
CommandResult,
|
|
50
|
+
PtyOutput,
|
|
51
|
+
PtySize,
|
|
52
|
+
Stderr,
|
|
53
|
+
Stdout,
|
|
54
|
+
)
|
|
55
|
+
from .sandbox.commands.main import ProcessInfo
|
|
56
|
+
from .sandbox.filesystem.filesystem import EntryInfo, FileType, WriteInfo
|
|
57
|
+
from .sandbox.filesystem.watch_handle import (
|
|
58
|
+
FilesystemEvent,
|
|
59
|
+
FilesystemEventType,
|
|
60
|
+
)
|
|
61
|
+
from .sandbox.network import ALL_TRAFFIC
|
|
62
|
+
from .sandbox.sandbox_api import (
|
|
63
|
+
GitHubMcpServer,
|
|
64
|
+
GitHubMcpServerConfig,
|
|
65
|
+
McpServer,
|
|
66
|
+
SandboxInfo,
|
|
67
|
+
SandboxMetrics,
|
|
68
|
+
SandboxNetworkOpts,
|
|
69
|
+
SandboxQuery,
|
|
70
|
+
SandboxState,
|
|
71
|
+
)
|
|
72
|
+
from .sandbox_async.commands.command_handle import AsyncCommandHandle
|
|
73
|
+
from .sandbox_async.filesystem.watch_handle import AsyncWatchHandle
|
|
74
|
+
from .sandbox_async.main import AsyncSandbox
|
|
75
|
+
from .sandbox_async.paginator import AsyncSandboxPaginator
|
|
76
|
+
from .sandbox_async.utils import OutputHandler
|
|
77
|
+
from .sandbox_sync.commands.command_handle import CommandHandle
|
|
78
|
+
from .sandbox_sync.filesystem.watch_handle import WatchHandle
|
|
79
|
+
from .sandbox_sync.main import Sandbox
|
|
80
|
+
from .sandbox_sync.paginator import SandboxPaginator
|
|
81
|
+
from .template.logger import (
|
|
82
|
+
LogEntry,
|
|
83
|
+
LogEntryEnd,
|
|
84
|
+
LogEntryLevel,
|
|
85
|
+
LogEntryStart,
|
|
86
|
+
default_build_logger,
|
|
87
|
+
)
|
|
88
|
+
from .template.main import TemplateBase, TemplateClass
|
|
89
|
+
from .template.readycmd import (
|
|
90
|
+
ReadyCmd,
|
|
91
|
+
wait_for_file,
|
|
92
|
+
wait_for_port,
|
|
93
|
+
wait_for_process,
|
|
94
|
+
wait_for_timeout,
|
|
95
|
+
wait_for_url,
|
|
96
|
+
)
|
|
97
|
+
from .template.types import BuildInfo, CopyItem
|
|
98
|
+
from .template_async.main import AsyncTemplate
|
|
99
|
+
from .template_sync.main import Template
|
|
100
|
+
|
|
101
|
+
__all__ = [
|
|
102
|
+
# API
|
|
103
|
+
"ApiClient",
|
|
104
|
+
"client",
|
|
105
|
+
# Connection config
|
|
106
|
+
"ConnectionConfig",
|
|
107
|
+
"ProxyTypes",
|
|
108
|
+
# Exceptions
|
|
109
|
+
"SandboxException",
|
|
110
|
+
"TimeoutException",
|
|
111
|
+
"NotFoundException",
|
|
112
|
+
"AuthenticationException",
|
|
113
|
+
"InvalidArgumentException",
|
|
114
|
+
"NotEnoughSpaceException",
|
|
115
|
+
"TemplateException",
|
|
116
|
+
"BuildException",
|
|
117
|
+
"FileUploadException",
|
|
118
|
+
# Sandbox API
|
|
119
|
+
"SandboxInfo",
|
|
120
|
+
"SandboxMetrics",
|
|
121
|
+
"ProcessInfo",
|
|
122
|
+
"SandboxQuery",
|
|
123
|
+
"SandboxState",
|
|
124
|
+
"SandboxMetrics",
|
|
125
|
+
# Command handle
|
|
126
|
+
"CommandResult",
|
|
127
|
+
"Stderr",
|
|
128
|
+
"Stdout",
|
|
129
|
+
"CommandExitException",
|
|
130
|
+
"PtyOutput",
|
|
131
|
+
"PtySize",
|
|
132
|
+
# Filesystem
|
|
133
|
+
"FilesystemEvent",
|
|
134
|
+
"FilesystemEventType",
|
|
135
|
+
"EntryInfo",
|
|
136
|
+
"WriteInfo",
|
|
137
|
+
"FileType",
|
|
138
|
+
# Network
|
|
139
|
+
"SandboxNetworkOpts",
|
|
140
|
+
"ALL_TRAFFIC",
|
|
141
|
+
# Sync sandbox
|
|
142
|
+
"Sandbox",
|
|
143
|
+
"SandboxPaginator",
|
|
144
|
+
"WatchHandle",
|
|
145
|
+
"CommandHandle",
|
|
146
|
+
# Async sandbox
|
|
147
|
+
"OutputHandler",
|
|
148
|
+
"AsyncSandboxPaginator",
|
|
149
|
+
"AsyncSandbox",
|
|
150
|
+
"AsyncWatchHandle",
|
|
151
|
+
"AsyncCommandHandle",
|
|
152
|
+
# Template
|
|
153
|
+
"Template",
|
|
154
|
+
"AsyncTemplate",
|
|
155
|
+
"TemplateBase",
|
|
156
|
+
"TemplateClass",
|
|
157
|
+
"CopyItem",
|
|
158
|
+
"BuildInfo",
|
|
159
|
+
"ReadyCmd",
|
|
160
|
+
"wait_for_file",
|
|
161
|
+
"wait_for_url",
|
|
162
|
+
"wait_for_port",
|
|
163
|
+
"wait_for_process",
|
|
164
|
+
"wait_for_timeout",
|
|
165
|
+
"LogEntry",
|
|
166
|
+
"LogEntryStart",
|
|
167
|
+
"LogEntryEnd",
|
|
168
|
+
"LogEntryLevel",
|
|
169
|
+
"default_build_logger",
|
|
170
|
+
# MCP
|
|
171
|
+
"McpServer",
|
|
172
|
+
"GitHubMcpServer",
|
|
173
|
+
"GitHubMcpServerConfig",
|
|
174
|
+
]
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from types import TracebackType
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
|
|
8
|
+
from httpx import AsyncBaseTransport, BaseTransport, Limits
|
|
9
|
+
|
|
10
|
+
from moru.api.client.client import AuthenticatedClient
|
|
11
|
+
from moru.api.client.types import Response
|
|
12
|
+
from moru.api.metadata import default_headers
|
|
13
|
+
from moru.connection_config import ConnectionConfig
|
|
14
|
+
from moru.exceptions import (
|
|
15
|
+
AuthenticationException,
|
|
16
|
+
RateLimitException,
|
|
17
|
+
SandboxException,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
limits = Limits(
|
|
23
|
+
max_keepalive_connections=int(os.getenv("MORU_MAX_KEEPALIVE_CONNECTIONS", "20")),
|
|
24
|
+
max_connections=int(os.getenv("MORU_MAX_CONNECTIONS", "2000")),
|
|
25
|
+
keepalive_expiry=int(os.getenv("MORU_KEEPALIVE_EXPIRY", "300")),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class SandboxCreateResponse:
|
|
31
|
+
sandbox_id: str
|
|
32
|
+
sandbox_domain: Optional[str]
|
|
33
|
+
envd_version: str
|
|
34
|
+
envd_access_token: str
|
|
35
|
+
traffic_access_token: Optional[str]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def handle_api_exception(
|
|
39
|
+
e: Response,
|
|
40
|
+
default_exception_class: type[Exception] = SandboxException,
|
|
41
|
+
stack_trace: Optional[TracebackType] = None,
|
|
42
|
+
):
|
|
43
|
+
try:
|
|
44
|
+
body = json.loads(e.content) if e.content else {}
|
|
45
|
+
except json.JSONDecodeError:
|
|
46
|
+
body = {}
|
|
47
|
+
|
|
48
|
+
if e.status_code == 401:
|
|
49
|
+
message = f"{e.status_code}: Unauthorized, please check your credentials."
|
|
50
|
+
if body.get("message"):
|
|
51
|
+
message += f" - {body['message']}"
|
|
52
|
+
return AuthenticationException(message)
|
|
53
|
+
|
|
54
|
+
if e.status_code == 429:
|
|
55
|
+
message = f"{e.status_code}: Rate limit exceeded, please try again later."
|
|
56
|
+
if body.get("message"):
|
|
57
|
+
message += f" - {body['message']}"
|
|
58
|
+
return RateLimitException(message)
|
|
59
|
+
|
|
60
|
+
if "message" in body:
|
|
61
|
+
return default_exception_class(
|
|
62
|
+
f"{e.status_code}: {body['message']}"
|
|
63
|
+
).with_traceback(stack_trace)
|
|
64
|
+
return default_exception_class(f"{e.status_code}: {e.content}").with_traceback(
|
|
65
|
+
stack_trace
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ApiClient(AuthenticatedClient):
|
|
70
|
+
"""
|
|
71
|
+
The client for interacting with the Moru API.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
config: ConnectionConfig,
|
|
77
|
+
require_api_key: bool = True,
|
|
78
|
+
require_access_token: bool = False,
|
|
79
|
+
transport: Optional[Union[BaseTransport, AsyncBaseTransport]] = None,
|
|
80
|
+
*args,
|
|
81
|
+
**kwargs,
|
|
82
|
+
):
|
|
83
|
+
if require_api_key and require_access_token:
|
|
84
|
+
raise AuthenticationException(
|
|
85
|
+
"Only one of api_key or access_token can be required, not both",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not require_api_key and not require_access_token:
|
|
89
|
+
raise AuthenticationException(
|
|
90
|
+
"Either api_key or access_token is required",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
token = None
|
|
94
|
+
if require_api_key:
|
|
95
|
+
if config.api_key is None:
|
|
96
|
+
raise AuthenticationException(
|
|
97
|
+
"API key is required. "
|
|
98
|
+
"You can either set the environment variable `MORU_API_KEY` "
|
|
99
|
+
'or you can pass it directly to the method like api_key="..."',
|
|
100
|
+
)
|
|
101
|
+
token = config.api_key
|
|
102
|
+
|
|
103
|
+
if require_access_token:
|
|
104
|
+
if config.access_token is None:
|
|
105
|
+
raise AuthenticationException(
|
|
106
|
+
"Access token is required. "
|
|
107
|
+
"You can set the environment variable `MORU_ACCESS_TOKEN` or pass the `access_token` in options.",
|
|
108
|
+
)
|
|
109
|
+
token = config.access_token
|
|
110
|
+
|
|
111
|
+
auth_header_name = "X-API-KEY" if require_api_key else "Authorization"
|
|
112
|
+
prefix = "" if require_api_key else "Bearer"
|
|
113
|
+
|
|
114
|
+
headers = {
|
|
115
|
+
**default_headers,
|
|
116
|
+
**(config.headers or {}),
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Prevent passing these parameters twice
|
|
120
|
+
more_headers: Optional[dict] = kwargs.pop("headers", None)
|
|
121
|
+
if more_headers:
|
|
122
|
+
headers.update(more_headers)
|
|
123
|
+
kwargs.pop("token", None)
|
|
124
|
+
kwargs.pop("auth_header_name", None)
|
|
125
|
+
kwargs.pop("prefix", None)
|
|
126
|
+
|
|
127
|
+
super().__init__(
|
|
128
|
+
base_url=config.api_url,
|
|
129
|
+
httpx_args={
|
|
130
|
+
"event_hooks": {
|
|
131
|
+
"request": [self._log_request],
|
|
132
|
+
"response": [self._log_response],
|
|
133
|
+
},
|
|
134
|
+
"proxy": config.proxy,
|
|
135
|
+
"transport": transport,
|
|
136
|
+
},
|
|
137
|
+
headers=headers,
|
|
138
|
+
token=token,
|
|
139
|
+
auth_header_name=auth_header_name,
|
|
140
|
+
prefix=prefix,
|
|
141
|
+
*args,
|
|
142
|
+
**kwargs,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def _log_request(self, request):
|
|
146
|
+
logger.info(f"Request {request.method} {request.url}")
|
|
147
|
+
|
|
148
|
+
def _log_response(self, response: Response):
|
|
149
|
+
if response.status_code >= 400:
|
|
150
|
+
logger.error(f"Response {response.status_code}")
|
|
151
|
+
else:
|
|
152
|
+
logger.info(f"Response {response.status_code}")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# We need to override the logging hooks for the async usage
|
|
156
|
+
class AsyncApiClient(ApiClient):
|
|
157
|
+
async def _log_request(self, request):
|
|
158
|
+
logger.info(f"Request {request.method} {request.url}")
|
|
159
|
+
|
|
160
|
+
async def _log_response(self, response: Response):
|
|
161
|
+
if response.status_code >= 400:
|
|
162
|
+
logger.error(f"Response {response.status_code}")
|
|
163
|
+
else:
|
|
164
|
+
logger.info(f"Response {response.status_code}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains methods for accessing the API"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains endpoint functions for accessing the API"""
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Any, Optional, Union, cast
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from ... import errors
|
|
7
|
+
from ...client import AuthenticatedClient, Client
|
|
8
|
+
from ...models.error import Error
|
|
9
|
+
from ...types import Response
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_kwargs(
|
|
13
|
+
sandbox_id: str,
|
|
14
|
+
) -> dict[str, Any]:
|
|
15
|
+
_kwargs: dict[str, Any] = {
|
|
16
|
+
"method": "delete",
|
|
17
|
+
"url": f"/sandboxes/{sandbox_id}",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return _kwargs
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _parse_response(
|
|
24
|
+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
|
25
|
+
) -> Optional[Union[Any, Error]]:
|
|
26
|
+
if response.status_code == 204:
|
|
27
|
+
response_204 = cast(Any, None)
|
|
28
|
+
return response_204
|
|
29
|
+
if response.status_code == 401:
|
|
30
|
+
response_401 = Error.from_dict(response.json())
|
|
31
|
+
|
|
32
|
+
return response_401
|
|
33
|
+
if response.status_code == 404:
|
|
34
|
+
response_404 = Error.from_dict(response.json())
|
|
35
|
+
|
|
36
|
+
return response_404
|
|
37
|
+
if response.status_code == 500:
|
|
38
|
+
response_500 = Error.from_dict(response.json())
|
|
39
|
+
|
|
40
|
+
return response_500
|
|
41
|
+
if client.raise_on_unexpected_status:
|
|
42
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
43
|
+
else:
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _build_response(
|
|
48
|
+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
|
49
|
+
) -> Response[Union[Any, Error]]:
|
|
50
|
+
return Response(
|
|
51
|
+
status_code=HTTPStatus(response.status_code),
|
|
52
|
+
content=response.content,
|
|
53
|
+
headers=response.headers,
|
|
54
|
+
parsed=_parse_response(client=client, response=response),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def sync_detailed(
|
|
59
|
+
sandbox_id: str,
|
|
60
|
+
*,
|
|
61
|
+
client: AuthenticatedClient,
|
|
62
|
+
) -> Response[Union[Any, Error]]:
|
|
63
|
+
"""Kill a sandbox
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
sandbox_id (str):
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
70
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Response[Union[Any, Error]]
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
kwargs = _get_kwargs(
|
|
77
|
+
sandbox_id=sandbox_id,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
response = client.get_httpx_client().request(
|
|
81
|
+
**kwargs,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return _build_response(client=client, response=response)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def sync(
|
|
88
|
+
sandbox_id: str,
|
|
89
|
+
*,
|
|
90
|
+
client: AuthenticatedClient,
|
|
91
|
+
) -> Optional[Union[Any, Error]]:
|
|
92
|
+
"""Kill a sandbox
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
sandbox_id (str):
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
99
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Union[Any, Error]
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
return sync_detailed(
|
|
106
|
+
sandbox_id=sandbox_id,
|
|
107
|
+
client=client,
|
|
108
|
+
).parsed
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
async def asyncio_detailed(
|
|
112
|
+
sandbox_id: str,
|
|
113
|
+
*,
|
|
114
|
+
client: AuthenticatedClient,
|
|
115
|
+
) -> Response[Union[Any, Error]]:
|
|
116
|
+
"""Kill a sandbox
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
sandbox_id (str):
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
123
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Response[Union[Any, Error]]
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
kwargs = _get_kwargs(
|
|
130
|
+
sandbox_id=sandbox_id,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
response = await client.get_async_httpx_client().request(**kwargs)
|
|
134
|
+
|
|
135
|
+
return _build_response(client=client, response=response)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
async def asyncio(
|
|
139
|
+
sandbox_id: str,
|
|
140
|
+
*,
|
|
141
|
+
client: AuthenticatedClient,
|
|
142
|
+
) -> Optional[Union[Any, Error]]:
|
|
143
|
+
"""Kill a sandbox
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
sandbox_id (str):
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
150
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Union[Any, Error]
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
await asyncio_detailed(
|
|
158
|
+
sandbox_id=sandbox_id,
|
|
159
|
+
client=client,
|
|
160
|
+
)
|
|
161
|
+
).parsed
|