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.
Files changed (152) hide show
  1. moru-0.1.0/LICENSE +9 -0
  2. moru-0.1.0/PKG-INFO +63 -0
  3. moru-0.1.0/README.md +30 -0
  4. moru-0.1.0/moru/__init__.py +174 -0
  5. moru-0.1.0/moru/api/__init__.py +164 -0
  6. moru-0.1.0/moru/api/client/__init__.py +8 -0
  7. moru-0.1.0/moru/api/client/api/__init__.py +1 -0
  8. moru-0.1.0/moru/api/client/api/sandboxes/__init__.py +1 -0
  9. moru-0.1.0/moru/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +161 -0
  10. moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes.py +176 -0
  11. moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes_metrics.py +173 -0
  12. moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +163 -0
  13. moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +199 -0
  14. moru-0.1.0/moru/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +212 -0
  15. moru-0.1.0/moru/api/client/api/sandboxes/get_v2_sandboxes.py +230 -0
  16. moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes.py +172 -0
  17. moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
  18. moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +165 -0
  19. moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +181 -0
  20. moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +189 -0
  21. moru-0.1.0/moru/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +193 -0
  22. moru-0.1.0/moru/api/client/api/templates/__init__.py +1 -0
  23. moru-0.1.0/moru/api/client/api/templates/delete_templates_template_id.py +157 -0
  24. moru-0.1.0/moru/api/client/api/templates/get_templates.py +172 -0
  25. moru-0.1.0/moru/api/client/api/templates/get_templates_template_id.py +195 -0
  26. moru-0.1.0/moru/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +217 -0
  27. moru-0.1.0/moru/api/client/api/templates/get_templates_template_id_files_hash.py +180 -0
  28. moru-0.1.0/moru/api/client/api/templates/patch_templates_template_id.py +183 -0
  29. moru-0.1.0/moru/api/client/api/templates/post_templates.py +172 -0
  30. moru-0.1.0/moru/api/client/api/templates/post_templates_template_id.py +181 -0
  31. moru-0.1.0/moru/api/client/api/templates/post_templates_template_id_builds_build_id.py +170 -0
  32. moru-0.1.0/moru/api/client/api/templates/post_v2_templates.py +172 -0
  33. moru-0.1.0/moru/api/client/api/templates/post_v3_templates.py +172 -0
  34. moru-0.1.0/moru/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +192 -0
  35. moru-0.1.0/moru/api/client/client.py +286 -0
  36. moru-0.1.0/moru/api/client/errors.py +16 -0
  37. moru-0.1.0/moru/api/client/models/__init__.py +123 -0
  38. moru-0.1.0/moru/api/client/models/aws_registry.py +85 -0
  39. moru-0.1.0/moru/api/client/models/aws_registry_type.py +8 -0
  40. moru-0.1.0/moru/api/client/models/build_log_entry.py +89 -0
  41. moru-0.1.0/moru/api/client/models/build_status_reason.py +95 -0
  42. moru-0.1.0/moru/api/client/models/connect_sandbox.py +59 -0
  43. moru-0.1.0/moru/api/client/models/created_access_token.py +100 -0
  44. moru-0.1.0/moru/api/client/models/created_team_api_key.py +166 -0
  45. moru-0.1.0/moru/api/client/models/disk_metrics.py +91 -0
  46. moru-0.1.0/moru/api/client/models/error.py +67 -0
  47. moru-0.1.0/moru/api/client/models/gcp_registry.py +69 -0
  48. moru-0.1.0/moru/api/client/models/gcp_registry_type.py +8 -0
  49. moru-0.1.0/moru/api/client/models/general_registry.py +77 -0
  50. moru-0.1.0/moru/api/client/models/general_registry_type.py +8 -0
  51. moru-0.1.0/moru/api/client/models/identifier_masking_details.py +83 -0
  52. moru-0.1.0/moru/api/client/models/listed_sandbox.py +154 -0
  53. moru-0.1.0/moru/api/client/models/log_level.py +11 -0
  54. moru-0.1.0/moru/api/client/models/max_team_metric.py +78 -0
  55. moru-0.1.0/moru/api/client/models/mcp_type_0.py +44 -0
  56. moru-0.1.0/moru/api/client/models/new_access_token.py +59 -0
  57. moru-0.1.0/moru/api/client/models/new_sandbox.py +172 -0
  58. moru-0.1.0/moru/api/client/models/new_team_api_key.py +59 -0
  59. moru-0.1.0/moru/api/client/models/node.py +155 -0
  60. moru-0.1.0/moru/api/client/models/node_detail.py +165 -0
  61. moru-0.1.0/moru/api/client/models/node_metrics.py +122 -0
  62. moru-0.1.0/moru/api/client/models/node_status.py +11 -0
  63. moru-0.1.0/moru/api/client/models/node_status_change.py +79 -0
  64. moru-0.1.0/moru/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +59 -0
  65. moru-0.1.0/moru/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +59 -0
  66. moru-0.1.0/moru/api/client/models/resumed_sandbox.py +68 -0
  67. moru-0.1.0/moru/api/client/models/sandbox.py +145 -0
  68. moru-0.1.0/moru/api/client/models/sandbox_detail.py +183 -0
  69. moru-0.1.0/moru/api/client/models/sandbox_log.py +70 -0
  70. moru-0.1.0/moru/api/client/models/sandbox_log_entry.py +93 -0
  71. moru-0.1.0/moru/api/client/models/sandbox_log_entry_fields.py +44 -0
  72. moru-0.1.0/moru/api/client/models/sandbox_logs.py +91 -0
  73. moru-0.1.0/moru/api/client/models/sandbox_metric.py +118 -0
  74. moru-0.1.0/moru/api/client/models/sandbox_network_config.py +92 -0
  75. moru-0.1.0/moru/api/client/models/sandbox_state.py +9 -0
  76. moru-0.1.0/moru/api/client/models/sandboxes_with_metrics.py +59 -0
  77. moru-0.1.0/moru/api/client/models/team.py +83 -0
  78. moru-0.1.0/moru/api/client/models/team_api_key.py +158 -0
  79. moru-0.1.0/moru/api/client/models/team_metric.py +86 -0
  80. moru-0.1.0/moru/api/client/models/team_user.py +68 -0
  81. moru-0.1.0/moru/api/client/models/template.py +217 -0
  82. moru-0.1.0/moru/api/client/models/template_build.py +139 -0
  83. moru-0.1.0/moru/api/client/models/template_build_file_upload.py +70 -0
  84. moru-0.1.0/moru/api/client/models/template_build_info.py +126 -0
  85. moru-0.1.0/moru/api/client/models/template_build_request.py +115 -0
  86. moru-0.1.0/moru/api/client/models/template_build_request_v2.py +88 -0
  87. moru-0.1.0/moru/api/client/models/template_build_request_v3.py +88 -0
  88. moru-0.1.0/moru/api/client/models/template_build_start_v2.py +184 -0
  89. moru-0.1.0/moru/api/client/models/template_build_status.py +11 -0
  90. moru-0.1.0/moru/api/client/models/template_legacy.py +207 -0
  91. moru-0.1.0/moru/api/client/models/template_request_response_v3.py +83 -0
  92. moru-0.1.0/moru/api/client/models/template_step.py +91 -0
  93. moru-0.1.0/moru/api/client/models/template_update_request.py +59 -0
  94. moru-0.1.0/moru/api/client/models/template_with_builds.py +148 -0
  95. moru-0.1.0/moru/api/client/models/update_team_api_key.py +59 -0
  96. moru-0.1.0/moru/api/client/py.typed +1 -0
  97. moru-0.1.0/moru/api/client/types.py +54 -0
  98. moru-0.1.0/moru/api/client_async/__init__.py +50 -0
  99. moru-0.1.0/moru/api/client_sync/__init__.py +52 -0
  100. moru-0.1.0/moru/api/metadata.py +14 -0
  101. moru-0.1.0/moru/connection_config.py +217 -0
  102. moru-0.1.0/moru/envd/api.py +59 -0
  103. moru-0.1.0/moru/envd/filesystem/filesystem_connect.py +193 -0
  104. moru-0.1.0/moru/envd/filesystem/filesystem_pb2.py +76 -0
  105. moru-0.1.0/moru/envd/filesystem/filesystem_pb2.pyi +233 -0
  106. moru-0.1.0/moru/envd/process/process_connect.py +155 -0
  107. moru-0.1.0/moru/envd/process/process_pb2.py +92 -0
  108. moru-0.1.0/moru/envd/process/process_pb2.pyi +304 -0
  109. moru-0.1.0/moru/envd/rpc.py +61 -0
  110. moru-0.1.0/moru/envd/versions.py +6 -0
  111. moru-0.1.0/moru/exceptions.py +95 -0
  112. moru-0.1.0/moru/sandbox/commands/command_handle.py +69 -0
  113. moru-0.1.0/moru/sandbox/commands/main.py +39 -0
  114. moru-0.1.0/moru/sandbox/filesystem/filesystem.py +94 -0
  115. moru-0.1.0/moru/sandbox/filesystem/watch_handle.py +60 -0
  116. moru-0.1.0/moru/sandbox/main.py +210 -0
  117. moru-0.1.0/moru/sandbox/mcp.py +1120 -0
  118. moru-0.1.0/moru/sandbox/network.py +8 -0
  119. moru-0.1.0/moru/sandbox/sandbox_api.py +210 -0
  120. moru-0.1.0/moru/sandbox/signature.py +45 -0
  121. moru-0.1.0/moru/sandbox/utils.py +34 -0
  122. moru-0.1.0/moru/sandbox_async/commands/command.py +336 -0
  123. moru-0.1.0/moru/sandbox_async/commands/command_handle.py +196 -0
  124. moru-0.1.0/moru/sandbox_async/commands/pty.py +240 -0
  125. moru-0.1.0/moru/sandbox_async/filesystem/filesystem.py +531 -0
  126. moru-0.1.0/moru/sandbox_async/filesystem/watch_handle.py +62 -0
  127. moru-0.1.0/moru/sandbox_async/main.py +734 -0
  128. moru-0.1.0/moru/sandbox_async/paginator.py +69 -0
  129. moru-0.1.0/moru/sandbox_async/sandbox_api.py +325 -0
  130. moru-0.1.0/moru/sandbox_async/utils.py +7 -0
  131. moru-0.1.0/moru/sandbox_sync/commands/command.py +328 -0
  132. moru-0.1.0/moru/sandbox_sync/commands/command_handle.py +150 -0
  133. moru-0.1.0/moru/sandbox_sync/commands/pty.py +230 -0
  134. moru-0.1.0/moru/sandbox_sync/filesystem/filesystem.py +518 -0
  135. moru-0.1.0/moru/sandbox_sync/filesystem/watch_handle.py +69 -0
  136. moru-0.1.0/moru/sandbox_sync/main.py +726 -0
  137. moru-0.1.0/moru/sandbox_sync/paginator.py +69 -0
  138. moru-0.1.0/moru/sandbox_sync/sandbox_api.py +308 -0
  139. moru-0.1.0/moru/template/consts.py +30 -0
  140. moru-0.1.0/moru/template/dockerfile_parser.py +275 -0
  141. moru-0.1.0/moru/template/logger.py +232 -0
  142. moru-0.1.0/moru/template/main.py +1360 -0
  143. moru-0.1.0/moru/template/readycmd.py +138 -0
  144. moru-0.1.0/moru/template/types.py +105 -0
  145. moru-0.1.0/moru/template/utils.py +320 -0
  146. moru-0.1.0/moru/template_async/build_api.py +202 -0
  147. moru-0.1.0/moru/template_async/main.py +366 -0
  148. moru-0.1.0/moru/template_sync/build_api.py +199 -0
  149. moru-0.1.0/moru/template_sync/main.py +371 -0
  150. moru-0.1.0/moru_connect/__init__.py +1 -0
  151. moru-0.1.0/moru_connect/client.py +493 -0
  152. 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,8 @@
1
+ """A client library for accessing Moru API"""
2
+
3
+ from .client import AuthenticatedClient, Client
4
+
5
+ __all__ = (
6
+ "AuthenticatedClient",
7
+ "Client",
8
+ )
@@ -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