moru 0.1.0__tar.gz → 0.2.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.2.0/PKG-INFO +122 -0
- moru-0.2.0/README.md +91 -0
- {moru-0.1.0 → moru-0.2.0}/moru/__init__.py +8 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/__init__.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/__init__.py +1 -1
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes_metrics.py +5 -1
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +67 -23
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +5 -0
- moru-0.2.0/moru/api/client/api/sandboxes/get_v2_sandbox_runs.py +218 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_v2_sandboxes.py +5 -2
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +6 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +5 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +3 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +5 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/delete_templates_template_id.py +3 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/get_templates.py +3 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/get_templates_template_id.py +3 -0
- moru-0.2.0/moru/api/client/api/templates/get_templates_template_id_builds_build_id_logs.py +276 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +23 -4
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/get_templates_template_id_files_hash.py +5 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/patch_templates_template_id.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_templates.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_templates_template_id.py +3 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_templates_template_id_builds_build_id.py +3 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_v2_templates.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_v3_templates.py +4 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +3 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/__init__.py +30 -0
- moru-0.2.0/moru/api/client/models/admin_sandbox_kill_result.py +67 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/build_log_entry.py +1 -1
- moru-0.2.0/moru/api/client/models/create_volume_request.py +59 -0
- moru-0.2.0/moru/api/client/models/file_info.py +105 -0
- moru-0.2.0/moru/api/client/models/file_info_type.py +9 -0
- moru-0.2.0/moru/api/client/models/file_list_response.py +84 -0
- moru-0.2.0/moru/api/client/models/logs_direction.py +9 -0
- moru-0.2.0/moru/api/client/models/logs_source.py +9 -0
- moru-0.2.0/moru/api/client/models/machine_info.py +83 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/new_sandbox.py +19 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node.py +10 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node_detail.py +10 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_log_entry.py +9 -9
- moru-0.2.0/moru/api/client/models/sandbox_log_event_type.py +11 -0
- moru-0.2.0/moru/api/client/models/sandbox_run.py +130 -0
- moru-0.2.0/moru/api/client/models/sandbox_run_end_reason.py +11 -0
- moru-0.2.0/moru/api/client/models/sandbox_run_status.py +10 -0
- moru-0.2.0/moru/api/client/models/template_build_logs_response.py +73 -0
- moru-0.2.0/moru/api/client/models/upload_response.py +67 -0
- moru-0.2.0/moru/api/client/models/volume.py +105 -0
- moru-0.2.0/moru/sandbox/mcp.py +1949 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/commands/command.py +5 -1
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/filesystem/filesystem.py +5 -1
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/main.py +21 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/sandbox_api.py +17 -11
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/filesystem/filesystem.py +5 -1
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/main.py +21 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/sandbox_api.py +17 -11
- moru-0.2.0/moru/volume/__init__.py +11 -0
- moru-0.2.0/moru/volume/types.py +83 -0
- moru-0.2.0/moru/volume/volume_api.py +330 -0
- moru-0.2.0/moru/volume_async/__init__.py +5 -0
- moru-0.2.0/moru/volume_async/main.py +327 -0
- moru-0.2.0/moru/volume_async/volume_api.py +290 -0
- moru-0.2.0/moru/volume_sync/__init__.py +5 -0
- moru-0.2.0/moru/volume_sync/main.py +325 -0
- {moru-0.1.0 → moru-0.2.0}/pyproject.toml +4 -4
- moru-0.1.0/PKG-INFO +0 -63
- moru-0.1.0/README.md +0 -30
- moru-0.1.0/moru/sandbox/mcp.py +0 -1120
- {moru-0.1.0 → moru-0.2.0}/LICENSE +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/__init__.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/__init__.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/__init__.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/client.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/errors.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/aws_registry.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/aws_registry_type.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/build_status_reason.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/connect_sandbox.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/created_access_token.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/created_team_api_key.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/disk_metrics.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/error.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/gcp_registry.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/gcp_registry_type.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/general_registry.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/general_registry_type.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/identifier_masking_details.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/listed_sandbox.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/log_level.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/max_team_metric.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/mcp_type_0.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/new_access_token.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/new_team_api_key.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node_metrics.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node_status.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node_status_change.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/resumed_sandbox.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_detail.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_log.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_log_entry_fields.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_logs.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_metric.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_network_config.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_state.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandboxes_with_metrics.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/team.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/team_api_key.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/team_metric.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/team_user.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_file_upload.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_info.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_request.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_request_v2.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_request_v3.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_start_v2.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_status.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_legacy.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_request_response_v3.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_step.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_update_request.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_with_builds.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/update_team_api_key.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/py.typed +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client/types.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client_async/__init__.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/client_sync/__init__.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/api/metadata.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/connection_config.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/envd/api.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/envd/filesystem/filesystem_connect.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/envd/filesystem/filesystem_pb2.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/envd/filesystem/filesystem_pb2.pyi +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/envd/process/process_connect.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/envd/process/process_pb2.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/envd/process/process_pb2.pyi +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/envd/rpc.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/envd/versions.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/exceptions.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox/commands/command_handle.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox/commands/main.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox/filesystem/filesystem.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox/filesystem/watch_handle.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox/main.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox/network.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox/sandbox_api.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox/signature.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox/utils.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/commands/command_handle.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/commands/pty.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/filesystem/watch_handle.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/paginator.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/utils.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/commands/command.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/commands/command_handle.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/commands/pty.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/filesystem/watch_handle.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/paginator.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template/consts.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template/dockerfile_parser.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template/logger.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template/main.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template/readycmd.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template/types.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template/utils.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template_async/build_api.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template_async/main.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template_sync/build_api.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru/template_sync/main.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru_connect/__init__.py +0 -0
- {moru-0.1.0 → moru-0.2.0}/moru_connect/client.py +0 -0
moru-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: moru
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Moru SDK that gives agents cloud environments
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Moru AI
|
|
7
|
+
Author-email: hello@moru.ai
|
|
8
|
+
Requires-Python: >=3.9,<4.0
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Requires-Dist: attrs (>=23.2.0)
|
|
17
|
+
Requires-Dist: dockerfile-parse (>=2.0.1,<3.0.0)
|
|
18
|
+
Requires-Dist: httpcore (>=1.0.5,<2.0.0)
|
|
19
|
+
Requires-Dist: httpx (>=0.27.0,<1.0.0)
|
|
20
|
+
Requires-Dist: packaging (>=24.1)
|
|
21
|
+
Requires-Dist: protobuf (>=4.21.0)
|
|
22
|
+
Requires-Dist: python-dateutil (>=2.8.2)
|
|
23
|
+
Requires-Dist: rich (>=14.0.0)
|
|
24
|
+
Requires-Dist: typing-extensions (>=4.1.0)
|
|
25
|
+
Requires-Dist: wcmatch (>=10.1,<11.0)
|
|
26
|
+
Project-URL: Bug Tracker, https://github.com/moru-ai/moru/issues
|
|
27
|
+
Project-URL: Homepage, https://github.com/moru-ai/moru
|
|
28
|
+
Project-URL: Repository, https://github.com/moru-ai/moru/tree/main/packages/python-sdk
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Moru Python SDK
|
|
32
|
+
|
|
33
|
+
Moru SDK for Python provides cloud sandbox environments for AI agents.
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### 1. Create an Account
|
|
38
|
+
|
|
39
|
+
Sign up for a free account at [moru.io/dashboard](https://moru.io/dashboard).
|
|
40
|
+
|
|
41
|
+
### 2. Get Your API Key
|
|
42
|
+
|
|
43
|
+
1. Go to the [API Keys tab](https://moru.io/dashboard?tab=keys) in your dashboard
|
|
44
|
+
2. Click **Create API Key**
|
|
45
|
+
3. Copy your new API key
|
|
46
|
+
|
|
47
|
+
### 3. Install the SDK
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install moru
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 4. Set Your API Key
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
export MORU_API_KEY=your_api_key
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 5. Create a Sandbox and Run Commands
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from moru import Sandbox
|
|
63
|
+
|
|
64
|
+
# Create a sandbox using the 'base' template (default)
|
|
65
|
+
sandbox = Sandbox.create()
|
|
66
|
+
print(f"Sandbox created: {sandbox.sandbox_id}")
|
|
67
|
+
|
|
68
|
+
# Run a command
|
|
69
|
+
result = sandbox.commands.run("echo 'Hello from Moru!'")
|
|
70
|
+
print(f"Output: {result.stdout}")
|
|
71
|
+
print(f"Exit code: {result.exit_code}")
|
|
72
|
+
|
|
73
|
+
# Write and read files
|
|
74
|
+
sandbox.files.write("/tmp/hello.txt", "Hello from Moru!")
|
|
75
|
+
content = sandbox.files.read("/tmp/hello.txt")
|
|
76
|
+
print(f"File content: {content}")
|
|
77
|
+
|
|
78
|
+
# Clean up
|
|
79
|
+
sandbox.kill()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Using Async
|
|
83
|
+
|
|
84
|
+
For async applications, use `AsyncSandbox`:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import asyncio
|
|
88
|
+
from moru import AsyncSandbox
|
|
89
|
+
|
|
90
|
+
async def main():
|
|
91
|
+
sandbox = await AsyncSandbox.create()
|
|
92
|
+
|
|
93
|
+
result = await sandbox.commands.run("python3 --version")
|
|
94
|
+
print(result.stdout)
|
|
95
|
+
|
|
96
|
+
await sandbox.kill()
|
|
97
|
+
|
|
98
|
+
asyncio.run(main())
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Using a Custom Template
|
|
102
|
+
|
|
103
|
+
Create templates via the [dashboard](https://moru.io/dashboard) or CLI.
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
# Use a custom template
|
|
107
|
+
sandbox = Sandbox.create("my-template")
|
|
108
|
+
|
|
109
|
+
result = sandbox.commands.run("echo 'Running in custom template'")
|
|
110
|
+
print(result.stdout)
|
|
111
|
+
|
|
112
|
+
sandbox.kill()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Documentation
|
|
116
|
+
|
|
117
|
+
For full documentation, visit [docs.moru.io](https://docs.moru.io).
|
|
118
|
+
|
|
119
|
+
## Acknowledgement
|
|
120
|
+
|
|
121
|
+
This project is a fork of [E2B](https://github.com/e2b-dev/E2B).
|
|
122
|
+
|
moru-0.2.0/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Moru Python SDK
|
|
2
|
+
|
|
3
|
+
Moru SDK for Python provides cloud sandbox environments for AI agents.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### 1. Create an Account
|
|
8
|
+
|
|
9
|
+
Sign up for a free account at [moru.io/dashboard](https://moru.io/dashboard).
|
|
10
|
+
|
|
11
|
+
### 2. Get Your API Key
|
|
12
|
+
|
|
13
|
+
1. Go to the [API Keys tab](https://moru.io/dashboard?tab=keys) in your dashboard
|
|
14
|
+
2. Click **Create API Key**
|
|
15
|
+
3. Copy your new API key
|
|
16
|
+
|
|
17
|
+
### 3. Install the SDK
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install moru
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 4. Set Your API Key
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
export MORU_API_KEY=your_api_key
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 5. Create a Sandbox and Run Commands
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from moru import Sandbox
|
|
33
|
+
|
|
34
|
+
# Create a sandbox using the 'base' template (default)
|
|
35
|
+
sandbox = Sandbox.create()
|
|
36
|
+
print(f"Sandbox created: {sandbox.sandbox_id}")
|
|
37
|
+
|
|
38
|
+
# Run a command
|
|
39
|
+
result = sandbox.commands.run("echo 'Hello from Moru!'")
|
|
40
|
+
print(f"Output: {result.stdout}")
|
|
41
|
+
print(f"Exit code: {result.exit_code}")
|
|
42
|
+
|
|
43
|
+
# Write and read files
|
|
44
|
+
sandbox.files.write("/tmp/hello.txt", "Hello from Moru!")
|
|
45
|
+
content = sandbox.files.read("/tmp/hello.txt")
|
|
46
|
+
print(f"File content: {content}")
|
|
47
|
+
|
|
48
|
+
# Clean up
|
|
49
|
+
sandbox.kill()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Using Async
|
|
53
|
+
|
|
54
|
+
For async applications, use `AsyncSandbox`:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import asyncio
|
|
58
|
+
from moru import AsyncSandbox
|
|
59
|
+
|
|
60
|
+
async def main():
|
|
61
|
+
sandbox = await AsyncSandbox.create()
|
|
62
|
+
|
|
63
|
+
result = await sandbox.commands.run("python3 --version")
|
|
64
|
+
print(result.stdout)
|
|
65
|
+
|
|
66
|
+
await sandbox.kill()
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Using a Custom Template
|
|
72
|
+
|
|
73
|
+
Create templates via the [dashboard](https://moru.io/dashboard) or CLI.
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
# Use a custom template
|
|
77
|
+
sandbox = Sandbox.create("my-template")
|
|
78
|
+
|
|
79
|
+
result = sandbox.commands.run("echo 'Running in custom template'")
|
|
80
|
+
print(result.stdout)
|
|
81
|
+
|
|
82
|
+
sandbox.kill()
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Documentation
|
|
86
|
+
|
|
87
|
+
For full documentation, visit [docs.moru.io](https://docs.moru.io).
|
|
88
|
+
|
|
89
|
+
## Acknowledgement
|
|
90
|
+
|
|
91
|
+
This project is a fork of [E2B](https://github.com/e2b-dev/E2B).
|
|
@@ -97,6 +97,9 @@ from .template.readycmd import (
|
|
|
97
97
|
from .template.types import BuildInfo, CopyItem
|
|
98
98
|
from .template_async.main import AsyncTemplate
|
|
99
99
|
from .template_sync.main import Template
|
|
100
|
+
from .volume.types import FileInfo as VolumeFileInfo, VolumeInfo
|
|
101
|
+
from .volume_async.main import AsyncVolume
|
|
102
|
+
from .volume_sync.main import Volume
|
|
100
103
|
|
|
101
104
|
__all__ = [
|
|
102
105
|
# API
|
|
@@ -171,4 +174,9 @@ __all__ = [
|
|
|
171
174
|
"McpServer",
|
|
172
175
|
"GitHubMcpServer",
|
|
173
176
|
"GitHubMcpServerConfig",
|
|
177
|
+
# Volumes
|
|
178
|
+
"Volume",
|
|
179
|
+
"AsyncVolume",
|
|
180
|
+
"VolumeInfo",
|
|
181
|
+
"VolumeFileInfo",
|
|
174
182
|
]
|
|
@@ -40,6 +40,10 @@ def handle_api_exception(
|
|
|
40
40
|
default_exception_class: type[Exception] = SandboxException,
|
|
41
41
|
stack_trace: Optional[TracebackType] = None,
|
|
42
42
|
):
|
|
43
|
+
# Success codes - no exception to return
|
|
44
|
+
if 200 <= e.status_code < 300:
|
|
45
|
+
return None
|
|
46
|
+
|
|
43
47
|
try:
|
|
44
48
|
body = json.loads(e.content) if e.content else {}
|
|
45
49
|
except json.JSONDecodeError:
|
|
@@ -26,18 +26,22 @@ def _parse_response(
|
|
|
26
26
|
if response.status_code == 204:
|
|
27
27
|
response_204 = cast(Any, None)
|
|
28
28
|
return response_204
|
|
29
|
+
|
|
29
30
|
if response.status_code == 401:
|
|
30
31
|
response_401 = Error.from_dict(response.json())
|
|
31
32
|
|
|
32
33
|
return response_401
|
|
34
|
+
|
|
33
35
|
if response.status_code == 404:
|
|
34
36
|
response_404 = Error.from_dict(response.json())
|
|
35
37
|
|
|
36
38
|
return response_404
|
|
39
|
+
|
|
37
40
|
if response.status_code == 500:
|
|
38
41
|
response_500 = Error.from_dict(response.json())
|
|
39
42
|
|
|
40
43
|
return response_500
|
|
44
|
+
|
|
41
45
|
if client.raise_on_unexpected_status:
|
|
42
46
|
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
43
47
|
else:
|
|
@@ -41,18 +41,22 @@ def _parse_response(
|
|
|
41
41
|
response_200.append(response_200_item)
|
|
42
42
|
|
|
43
43
|
return response_200
|
|
44
|
+
|
|
44
45
|
if response.status_code == 400:
|
|
45
46
|
response_400 = Error.from_dict(response.json())
|
|
46
47
|
|
|
47
48
|
return response_400
|
|
49
|
+
|
|
48
50
|
if response.status_code == 401:
|
|
49
51
|
response_401 = Error.from_dict(response.json())
|
|
50
52
|
|
|
51
53
|
return response_401
|
|
54
|
+
|
|
52
55
|
if response.status_code == 500:
|
|
53
56
|
response_500 = Error.from_dict(response.json())
|
|
54
57
|
|
|
55
58
|
return response_500
|
|
59
|
+
|
|
56
60
|
if client.raise_on_unexpected_status:
|
|
57
61
|
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
58
62
|
else:
|
|
@@ -18,7 +18,7 @@ def _get_kwargs(
|
|
|
18
18
|
|
|
19
19
|
json_sandbox_ids = sandbox_ids
|
|
20
20
|
|
|
21
|
-
params["sandbox_ids"] =
|
|
21
|
+
params["sandbox_ids"] = json_sandbox_ids
|
|
22
22
|
|
|
23
23
|
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
|
|
24
24
|
|
|
@@ -38,18 +38,22 @@ def _parse_response(
|
|
|
38
38
|
response_200 = SandboxesWithMetrics.from_dict(response.json())
|
|
39
39
|
|
|
40
40
|
return response_200
|
|
41
|
+
|
|
41
42
|
if response.status_code == 400:
|
|
42
43
|
response_400 = Error.from_dict(response.json())
|
|
43
44
|
|
|
44
45
|
return response_400
|
|
46
|
+
|
|
45
47
|
if response.status_code == 401:
|
|
46
48
|
response_401 = Error.from_dict(response.json())
|
|
47
49
|
|
|
48
50
|
return response_401
|
|
51
|
+
|
|
49
52
|
if response.status_code == 500:
|
|
50
53
|
response_500 = Error.from_dict(response.json())
|
|
51
54
|
|
|
52
55
|
return response_500
|
|
56
|
+
|
|
53
57
|
if client.raise_on_unexpected_status:
|
|
54
58
|
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
55
59
|
else:
|
|
@@ -28,18 +28,22 @@ def _parse_response(
|
|
|
28
28
|
response_200 = SandboxDetail.from_dict(response.json())
|
|
29
29
|
|
|
30
30
|
return response_200
|
|
31
|
+
|
|
31
32
|
if response.status_code == 401:
|
|
32
33
|
response_401 = Error.from_dict(response.json())
|
|
33
34
|
|
|
34
35
|
return response_401
|
|
36
|
+
|
|
35
37
|
if response.status_code == 404:
|
|
36
38
|
response_404 = Error.from_dict(response.json())
|
|
37
39
|
|
|
38
40
|
return response_404
|
|
41
|
+
|
|
39
42
|
if response.status_code == 500:
|
|
40
43
|
response_500 = Error.from_dict(response.json())
|
|
41
44
|
|
|
42
45
|
return response_500
|
|
46
|
+
|
|
43
47
|
if client.raise_on_unexpected_status:
|
|
44
48
|
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
45
49
|
else:
|
|
@@ -6,6 +6,8 @@ import httpx
|
|
|
6
6
|
from ... import errors
|
|
7
7
|
from ...client import AuthenticatedClient, Client
|
|
8
8
|
from ...models.error import Error
|
|
9
|
+
from ...models.logs_direction import LogsDirection
|
|
10
|
+
from ...models.sandbox_log_event_type import SandboxLogEventType
|
|
9
11
|
from ...models.sandbox_logs import SandboxLogs
|
|
10
12
|
from ...types import UNSET, Response, Unset
|
|
11
13
|
|
|
@@ -13,15 +15,29 @@ from ...types import UNSET, Response, Unset
|
|
|
13
15
|
def _get_kwargs(
|
|
14
16
|
sandbox_id: str,
|
|
15
17
|
*,
|
|
16
|
-
|
|
17
|
-
limit: Union[Unset, int] =
|
|
18
|
+
cursor: Union[Unset, int] = UNSET,
|
|
19
|
+
limit: Union[Unset, int] = 100,
|
|
20
|
+
direction: Union[Unset, LogsDirection] = UNSET,
|
|
21
|
+
event_type: Union[Unset, SandboxLogEventType] = UNSET,
|
|
18
22
|
) -> dict[str, Any]:
|
|
19
23
|
params: dict[str, Any] = {}
|
|
20
24
|
|
|
21
|
-
params["
|
|
25
|
+
params["cursor"] = cursor
|
|
22
26
|
|
|
23
27
|
params["limit"] = limit
|
|
24
28
|
|
|
29
|
+
json_direction: Union[Unset, str] = UNSET
|
|
30
|
+
if not isinstance(direction, Unset):
|
|
31
|
+
json_direction = direction.value
|
|
32
|
+
|
|
33
|
+
params["direction"] = json_direction
|
|
34
|
+
|
|
35
|
+
json_event_type: Union[Unset, str] = UNSET
|
|
36
|
+
if not isinstance(event_type, Unset):
|
|
37
|
+
json_event_type = event_type.value
|
|
38
|
+
|
|
39
|
+
params["eventType"] = json_event_type
|
|
40
|
+
|
|
25
41
|
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
|
|
26
42
|
|
|
27
43
|
_kwargs: dict[str, Any] = {
|
|
@@ -40,18 +56,22 @@ def _parse_response(
|
|
|
40
56
|
response_200 = SandboxLogs.from_dict(response.json())
|
|
41
57
|
|
|
42
58
|
return response_200
|
|
59
|
+
|
|
43
60
|
if response.status_code == 401:
|
|
44
61
|
response_401 = Error.from_dict(response.json())
|
|
45
62
|
|
|
46
63
|
return response_401
|
|
64
|
+
|
|
47
65
|
if response.status_code == 404:
|
|
48
66
|
response_404 = Error.from_dict(response.json())
|
|
49
67
|
|
|
50
68
|
return response_404
|
|
69
|
+
|
|
51
70
|
if response.status_code == 500:
|
|
52
71
|
response_500 = Error.from_dict(response.json())
|
|
53
72
|
|
|
54
73
|
return response_500
|
|
74
|
+
|
|
55
75
|
if client.raise_on_unexpected_status:
|
|
56
76
|
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
57
77
|
else:
|
|
@@ -73,15 +93,19 @@ def sync_detailed(
|
|
|
73
93
|
sandbox_id: str,
|
|
74
94
|
*,
|
|
75
95
|
client: AuthenticatedClient,
|
|
76
|
-
|
|
77
|
-
limit: Union[Unset, int] =
|
|
96
|
+
cursor: Union[Unset, int] = UNSET,
|
|
97
|
+
limit: Union[Unset, int] = 100,
|
|
98
|
+
direction: Union[Unset, LogsDirection] = UNSET,
|
|
99
|
+
event_type: Union[Unset, SandboxLogEventType] = UNSET,
|
|
78
100
|
) -> Response[Union[Error, SandboxLogs]]:
|
|
79
101
|
"""Get sandbox logs
|
|
80
102
|
|
|
81
103
|
Args:
|
|
82
104
|
sandbox_id (str):
|
|
83
|
-
|
|
84
|
-
limit (Union[Unset, int]): Default:
|
|
105
|
+
cursor (Union[Unset, int]):
|
|
106
|
+
limit (Union[Unset, int]): Default: 100.
|
|
107
|
+
direction (Union[Unset, LogsDirection]): Direction of the logs that should be returned
|
|
108
|
+
event_type (Union[Unset, SandboxLogEventType]): Type of sandbox log event
|
|
85
109
|
|
|
86
110
|
Raises:
|
|
87
111
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
@@ -93,8 +117,10 @@ def sync_detailed(
|
|
|
93
117
|
|
|
94
118
|
kwargs = _get_kwargs(
|
|
95
119
|
sandbox_id=sandbox_id,
|
|
96
|
-
|
|
120
|
+
cursor=cursor,
|
|
97
121
|
limit=limit,
|
|
122
|
+
direction=direction,
|
|
123
|
+
event_type=event_type,
|
|
98
124
|
)
|
|
99
125
|
|
|
100
126
|
response = client.get_httpx_client().request(
|
|
@@ -108,15 +134,19 @@ def sync(
|
|
|
108
134
|
sandbox_id: str,
|
|
109
135
|
*,
|
|
110
136
|
client: AuthenticatedClient,
|
|
111
|
-
|
|
112
|
-
limit: Union[Unset, int] =
|
|
137
|
+
cursor: Union[Unset, int] = UNSET,
|
|
138
|
+
limit: Union[Unset, int] = 100,
|
|
139
|
+
direction: Union[Unset, LogsDirection] = UNSET,
|
|
140
|
+
event_type: Union[Unset, SandboxLogEventType] = UNSET,
|
|
113
141
|
) -> Optional[Union[Error, SandboxLogs]]:
|
|
114
142
|
"""Get sandbox logs
|
|
115
143
|
|
|
116
144
|
Args:
|
|
117
145
|
sandbox_id (str):
|
|
118
|
-
|
|
119
|
-
limit (Union[Unset, int]): Default:
|
|
146
|
+
cursor (Union[Unset, int]):
|
|
147
|
+
limit (Union[Unset, int]): Default: 100.
|
|
148
|
+
direction (Union[Unset, LogsDirection]): Direction of the logs that should be returned
|
|
149
|
+
event_type (Union[Unset, SandboxLogEventType]): Type of sandbox log event
|
|
120
150
|
|
|
121
151
|
Raises:
|
|
122
152
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
@@ -129,8 +159,10 @@ def sync(
|
|
|
129
159
|
return sync_detailed(
|
|
130
160
|
sandbox_id=sandbox_id,
|
|
131
161
|
client=client,
|
|
132
|
-
|
|
162
|
+
cursor=cursor,
|
|
133
163
|
limit=limit,
|
|
164
|
+
direction=direction,
|
|
165
|
+
event_type=event_type,
|
|
134
166
|
).parsed
|
|
135
167
|
|
|
136
168
|
|
|
@@ -138,15 +170,19 @@ async def asyncio_detailed(
|
|
|
138
170
|
sandbox_id: str,
|
|
139
171
|
*,
|
|
140
172
|
client: AuthenticatedClient,
|
|
141
|
-
|
|
142
|
-
limit: Union[Unset, int] =
|
|
173
|
+
cursor: Union[Unset, int] = UNSET,
|
|
174
|
+
limit: Union[Unset, int] = 100,
|
|
175
|
+
direction: Union[Unset, LogsDirection] = UNSET,
|
|
176
|
+
event_type: Union[Unset, SandboxLogEventType] = UNSET,
|
|
143
177
|
) -> Response[Union[Error, SandboxLogs]]:
|
|
144
178
|
"""Get sandbox logs
|
|
145
179
|
|
|
146
180
|
Args:
|
|
147
181
|
sandbox_id (str):
|
|
148
|
-
|
|
149
|
-
limit (Union[Unset, int]): Default:
|
|
182
|
+
cursor (Union[Unset, int]):
|
|
183
|
+
limit (Union[Unset, int]): Default: 100.
|
|
184
|
+
direction (Union[Unset, LogsDirection]): Direction of the logs that should be returned
|
|
185
|
+
event_type (Union[Unset, SandboxLogEventType]): Type of sandbox log event
|
|
150
186
|
|
|
151
187
|
Raises:
|
|
152
188
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
@@ -158,8 +194,10 @@ async def asyncio_detailed(
|
|
|
158
194
|
|
|
159
195
|
kwargs = _get_kwargs(
|
|
160
196
|
sandbox_id=sandbox_id,
|
|
161
|
-
|
|
197
|
+
cursor=cursor,
|
|
162
198
|
limit=limit,
|
|
199
|
+
direction=direction,
|
|
200
|
+
event_type=event_type,
|
|
163
201
|
)
|
|
164
202
|
|
|
165
203
|
response = await client.get_async_httpx_client().request(**kwargs)
|
|
@@ -171,15 +209,19 @@ async def asyncio(
|
|
|
171
209
|
sandbox_id: str,
|
|
172
210
|
*,
|
|
173
211
|
client: AuthenticatedClient,
|
|
174
|
-
|
|
175
|
-
limit: Union[Unset, int] =
|
|
212
|
+
cursor: Union[Unset, int] = UNSET,
|
|
213
|
+
limit: Union[Unset, int] = 100,
|
|
214
|
+
direction: Union[Unset, LogsDirection] = UNSET,
|
|
215
|
+
event_type: Union[Unset, SandboxLogEventType] = UNSET,
|
|
176
216
|
) -> Optional[Union[Error, SandboxLogs]]:
|
|
177
217
|
"""Get sandbox logs
|
|
178
218
|
|
|
179
219
|
Args:
|
|
180
220
|
sandbox_id (str):
|
|
181
|
-
|
|
182
|
-
limit (Union[Unset, int]): Default:
|
|
221
|
+
cursor (Union[Unset, int]):
|
|
222
|
+
limit (Union[Unset, int]): Default: 100.
|
|
223
|
+
direction (Union[Unset, LogsDirection]): Direction of the logs that should be returned
|
|
224
|
+
event_type (Union[Unset, SandboxLogEventType]): Type of sandbox log event
|
|
183
225
|
|
|
184
226
|
Raises:
|
|
185
227
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
@@ -193,7 +235,9 @@ async def asyncio(
|
|
|
193
235
|
await asyncio_detailed(
|
|
194
236
|
sandbox_id=sandbox_id,
|
|
195
237
|
client=client,
|
|
196
|
-
|
|
238
|
+
cursor=cursor,
|
|
197
239
|
limit=limit,
|
|
240
|
+
direction=direction,
|
|
241
|
+
event_type=event_type,
|
|
198
242
|
)
|
|
199
243
|
).parsed
|
|
@@ -45,22 +45,27 @@ def _parse_response(
|
|
|
45
45
|
response_200.append(response_200_item)
|
|
46
46
|
|
|
47
47
|
return response_200
|
|
48
|
+
|
|
48
49
|
if response.status_code == 400:
|
|
49
50
|
response_400 = Error.from_dict(response.json())
|
|
50
51
|
|
|
51
52
|
return response_400
|
|
53
|
+
|
|
52
54
|
if response.status_code == 401:
|
|
53
55
|
response_401 = Error.from_dict(response.json())
|
|
54
56
|
|
|
55
57
|
return response_401
|
|
58
|
+
|
|
56
59
|
if response.status_code == 404:
|
|
57
60
|
response_404 = Error.from_dict(response.json())
|
|
58
61
|
|
|
59
62
|
return response_404
|
|
63
|
+
|
|
60
64
|
if response.status_code == 500:
|
|
61
65
|
response_500 = Error.from_dict(response.json())
|
|
62
66
|
|
|
63
67
|
return response_500
|
|
68
|
+
|
|
64
69
|
if client.raise_on_unexpected_status:
|
|
65
70
|
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
66
71
|
else:
|