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.
Files changed (180) hide show
  1. moru-0.2.0/PKG-INFO +122 -0
  2. moru-0.2.0/README.md +91 -0
  3. {moru-0.1.0 → moru-0.2.0}/moru/__init__.py +8 -0
  4. {moru-0.1.0 → moru-0.2.0}/moru/api/__init__.py +4 -0
  5. {moru-0.1.0 → moru-0.2.0}/moru/api/client/__init__.py +1 -1
  6. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +4 -0
  7. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes.py +4 -0
  8. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes_metrics.py +5 -1
  9. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +4 -0
  10. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +67 -23
  11. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +5 -0
  12. moru-0.2.0/moru/api/client/api/sandboxes/get_v2_sandbox_runs.py +218 -0
  13. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/get_v2_sandboxes.py +5 -2
  14. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes.py +4 -0
  15. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +6 -0
  16. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +5 -0
  17. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +3 -0
  18. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +5 -0
  19. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +4 -0
  20. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/delete_templates_template_id.py +3 -0
  21. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/get_templates.py +3 -0
  22. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/get_templates_template_id.py +3 -0
  23. moru-0.2.0/moru/api/client/api/templates/get_templates_template_id_builds_build_id_logs.py +276 -0
  24. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +23 -4
  25. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/get_templates_template_id_files_hash.py +5 -0
  26. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/patch_templates_template_id.py +4 -0
  27. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_templates.py +4 -0
  28. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_templates_template_id.py +3 -0
  29. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_templates_template_id_builds_build_id.py +3 -0
  30. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_v2_templates.py +4 -0
  31. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/post_v3_templates.py +4 -0
  32. {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
  33. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/__init__.py +30 -0
  34. moru-0.2.0/moru/api/client/models/admin_sandbox_kill_result.py +67 -0
  35. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/build_log_entry.py +1 -1
  36. moru-0.2.0/moru/api/client/models/create_volume_request.py +59 -0
  37. moru-0.2.0/moru/api/client/models/file_info.py +105 -0
  38. moru-0.2.0/moru/api/client/models/file_info_type.py +9 -0
  39. moru-0.2.0/moru/api/client/models/file_list_response.py +84 -0
  40. moru-0.2.0/moru/api/client/models/logs_direction.py +9 -0
  41. moru-0.2.0/moru/api/client/models/logs_source.py +9 -0
  42. moru-0.2.0/moru/api/client/models/machine_info.py +83 -0
  43. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/new_sandbox.py +19 -0
  44. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node.py +10 -0
  45. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node_detail.py +10 -0
  46. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_log_entry.py +9 -9
  47. moru-0.2.0/moru/api/client/models/sandbox_log_event_type.py +11 -0
  48. moru-0.2.0/moru/api/client/models/sandbox_run.py +130 -0
  49. moru-0.2.0/moru/api/client/models/sandbox_run_end_reason.py +11 -0
  50. moru-0.2.0/moru/api/client/models/sandbox_run_status.py +10 -0
  51. moru-0.2.0/moru/api/client/models/template_build_logs_response.py +73 -0
  52. moru-0.2.0/moru/api/client/models/upload_response.py +67 -0
  53. moru-0.2.0/moru/api/client/models/volume.py +105 -0
  54. moru-0.2.0/moru/sandbox/mcp.py +1949 -0
  55. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/commands/command.py +5 -1
  56. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/filesystem/filesystem.py +5 -1
  57. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/main.py +21 -0
  58. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/sandbox_api.py +17 -11
  59. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/filesystem/filesystem.py +5 -1
  60. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/main.py +21 -0
  61. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/sandbox_api.py +17 -11
  62. moru-0.2.0/moru/volume/__init__.py +11 -0
  63. moru-0.2.0/moru/volume/types.py +83 -0
  64. moru-0.2.0/moru/volume/volume_api.py +330 -0
  65. moru-0.2.0/moru/volume_async/__init__.py +5 -0
  66. moru-0.2.0/moru/volume_async/main.py +327 -0
  67. moru-0.2.0/moru/volume_async/volume_api.py +290 -0
  68. moru-0.2.0/moru/volume_sync/__init__.py +5 -0
  69. moru-0.2.0/moru/volume_sync/main.py +325 -0
  70. {moru-0.1.0 → moru-0.2.0}/pyproject.toml +4 -4
  71. moru-0.1.0/PKG-INFO +0 -63
  72. moru-0.1.0/README.md +0 -30
  73. moru-0.1.0/moru/sandbox/mcp.py +0 -1120
  74. {moru-0.1.0 → moru-0.2.0}/LICENSE +0 -0
  75. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/__init__.py +0 -0
  76. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/sandboxes/__init__.py +0 -0
  77. {moru-0.1.0 → moru-0.2.0}/moru/api/client/api/templates/__init__.py +0 -0
  78. {moru-0.1.0 → moru-0.2.0}/moru/api/client/client.py +0 -0
  79. {moru-0.1.0 → moru-0.2.0}/moru/api/client/errors.py +0 -0
  80. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/aws_registry.py +0 -0
  81. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/aws_registry_type.py +0 -0
  82. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/build_status_reason.py +0 -0
  83. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/connect_sandbox.py +0 -0
  84. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/created_access_token.py +0 -0
  85. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/created_team_api_key.py +0 -0
  86. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/disk_metrics.py +0 -0
  87. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/error.py +0 -0
  88. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/gcp_registry.py +0 -0
  89. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/gcp_registry_type.py +0 -0
  90. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/general_registry.py +0 -0
  91. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/general_registry_type.py +0 -0
  92. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/identifier_masking_details.py +0 -0
  93. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/listed_sandbox.py +0 -0
  94. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/log_level.py +0 -0
  95. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/max_team_metric.py +0 -0
  96. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/mcp_type_0.py +0 -0
  97. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/new_access_token.py +0 -0
  98. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/new_team_api_key.py +0 -0
  99. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node_metrics.py +0 -0
  100. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node_status.py +0 -0
  101. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/node_status_change.py +0 -0
  102. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +0 -0
  103. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +0 -0
  104. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/resumed_sandbox.py +0 -0
  105. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox.py +0 -0
  106. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_detail.py +0 -0
  107. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_log.py +0 -0
  108. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_log_entry_fields.py +0 -0
  109. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_logs.py +0 -0
  110. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_metric.py +0 -0
  111. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_network_config.py +0 -0
  112. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandbox_state.py +0 -0
  113. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/sandboxes_with_metrics.py +0 -0
  114. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/team.py +0 -0
  115. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/team_api_key.py +0 -0
  116. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/team_metric.py +0 -0
  117. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/team_user.py +0 -0
  118. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template.py +0 -0
  119. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build.py +0 -0
  120. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_file_upload.py +0 -0
  121. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_info.py +0 -0
  122. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_request.py +0 -0
  123. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_request_v2.py +0 -0
  124. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_request_v3.py +0 -0
  125. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_start_v2.py +0 -0
  126. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_build_status.py +0 -0
  127. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_legacy.py +0 -0
  128. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_request_response_v3.py +0 -0
  129. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_step.py +0 -0
  130. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_update_request.py +0 -0
  131. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/template_with_builds.py +0 -0
  132. {moru-0.1.0 → moru-0.2.0}/moru/api/client/models/update_team_api_key.py +0 -0
  133. {moru-0.1.0 → moru-0.2.0}/moru/api/client/py.typed +0 -0
  134. {moru-0.1.0 → moru-0.2.0}/moru/api/client/types.py +0 -0
  135. {moru-0.1.0 → moru-0.2.0}/moru/api/client_async/__init__.py +0 -0
  136. {moru-0.1.0 → moru-0.2.0}/moru/api/client_sync/__init__.py +0 -0
  137. {moru-0.1.0 → moru-0.2.0}/moru/api/metadata.py +0 -0
  138. {moru-0.1.0 → moru-0.2.0}/moru/connection_config.py +0 -0
  139. {moru-0.1.0 → moru-0.2.0}/moru/envd/api.py +0 -0
  140. {moru-0.1.0 → moru-0.2.0}/moru/envd/filesystem/filesystem_connect.py +0 -0
  141. {moru-0.1.0 → moru-0.2.0}/moru/envd/filesystem/filesystem_pb2.py +0 -0
  142. {moru-0.1.0 → moru-0.2.0}/moru/envd/filesystem/filesystem_pb2.pyi +0 -0
  143. {moru-0.1.0 → moru-0.2.0}/moru/envd/process/process_connect.py +0 -0
  144. {moru-0.1.0 → moru-0.2.0}/moru/envd/process/process_pb2.py +0 -0
  145. {moru-0.1.0 → moru-0.2.0}/moru/envd/process/process_pb2.pyi +0 -0
  146. {moru-0.1.0 → moru-0.2.0}/moru/envd/rpc.py +0 -0
  147. {moru-0.1.0 → moru-0.2.0}/moru/envd/versions.py +0 -0
  148. {moru-0.1.0 → moru-0.2.0}/moru/exceptions.py +0 -0
  149. {moru-0.1.0 → moru-0.2.0}/moru/sandbox/commands/command_handle.py +0 -0
  150. {moru-0.1.0 → moru-0.2.0}/moru/sandbox/commands/main.py +0 -0
  151. {moru-0.1.0 → moru-0.2.0}/moru/sandbox/filesystem/filesystem.py +0 -0
  152. {moru-0.1.0 → moru-0.2.0}/moru/sandbox/filesystem/watch_handle.py +0 -0
  153. {moru-0.1.0 → moru-0.2.0}/moru/sandbox/main.py +0 -0
  154. {moru-0.1.0 → moru-0.2.0}/moru/sandbox/network.py +0 -0
  155. {moru-0.1.0 → moru-0.2.0}/moru/sandbox/sandbox_api.py +0 -0
  156. {moru-0.1.0 → moru-0.2.0}/moru/sandbox/signature.py +0 -0
  157. {moru-0.1.0 → moru-0.2.0}/moru/sandbox/utils.py +0 -0
  158. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/commands/command_handle.py +0 -0
  159. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/commands/pty.py +0 -0
  160. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/filesystem/watch_handle.py +0 -0
  161. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/paginator.py +0 -0
  162. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_async/utils.py +0 -0
  163. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/commands/command.py +0 -0
  164. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/commands/command_handle.py +0 -0
  165. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/commands/pty.py +0 -0
  166. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/filesystem/watch_handle.py +0 -0
  167. {moru-0.1.0 → moru-0.2.0}/moru/sandbox_sync/paginator.py +0 -0
  168. {moru-0.1.0 → moru-0.2.0}/moru/template/consts.py +0 -0
  169. {moru-0.1.0 → moru-0.2.0}/moru/template/dockerfile_parser.py +0 -0
  170. {moru-0.1.0 → moru-0.2.0}/moru/template/logger.py +0 -0
  171. {moru-0.1.0 → moru-0.2.0}/moru/template/main.py +0 -0
  172. {moru-0.1.0 → moru-0.2.0}/moru/template/readycmd.py +0 -0
  173. {moru-0.1.0 → moru-0.2.0}/moru/template/types.py +0 -0
  174. {moru-0.1.0 → moru-0.2.0}/moru/template/utils.py +0 -0
  175. {moru-0.1.0 → moru-0.2.0}/moru/template_async/build_api.py +0 -0
  176. {moru-0.1.0 → moru-0.2.0}/moru/template_async/main.py +0 -0
  177. {moru-0.1.0 → moru-0.2.0}/moru/template_sync/build_api.py +0 -0
  178. {moru-0.1.0 → moru-0.2.0}/moru/template_sync/main.py +0 -0
  179. {moru-0.1.0 → moru-0.2.0}/moru_connect/__init__.py +0 -0
  180. {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:
@@ -1,4 +1,4 @@
1
- """A client library for accessing Moru API"""
1
+ """A client library for accessing Moru Sandbox API"""
2
2
 
3
3
  from .client import AuthenticatedClient, Client
4
4
 
@@ -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"] = ",".join(str(item) for item in json_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
- start: Union[Unset, int] = UNSET,
17
- limit: Union[Unset, int] = 1000,
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["start"] = start
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
- start: Union[Unset, int] = UNSET,
77
- limit: Union[Unset, int] = 1000,
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
- start (Union[Unset, int]):
84
- limit (Union[Unset, int]): Default: 1000.
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
- start=start,
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
- start: Union[Unset, int] = UNSET,
112
- limit: Union[Unset, int] = 1000,
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
- start (Union[Unset, int]):
119
- limit (Union[Unset, int]): Default: 1000.
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
- start=start,
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
- start: Union[Unset, int] = UNSET,
142
- limit: Union[Unset, int] = 1000,
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
- start (Union[Unset, int]):
149
- limit (Union[Unset, int]): Default: 1000.
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
- start=start,
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
- start: Union[Unset, int] = UNSET,
175
- limit: Union[Unset, int] = 1000,
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
- start (Union[Unset, int]):
182
- limit (Union[Unset, int]): Default: 1000.
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
- start=start,
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: