boxfab 0.1.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. boxfab/__init__.py +331 -0
  2. boxfab/api/__init__.py +301 -0
  3. boxfab/api/client/__init__.py +8 -0
  4. boxfab/api/client/api/__init__.py +1 -0
  5. boxfab/api/client/api/sandboxes/__init__.py +1 -0
  6. boxfab/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +173 -0
  7. boxfab/api/client/api/sandboxes/get_sandboxes.py +188 -0
  8. boxfab/api/client/api/sandboxes/get_sandboxes_metrics.py +185 -0
  9. boxfab/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +175 -0
  10. boxfab/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +211 -0
  11. boxfab/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +224 -0
  12. boxfab/api/client/api/sandboxes/get_v2_sandboxes.py +296 -0
  13. boxfab/api/client/api/sandboxes/get_v_2_sandboxes_sandbox_id_logs.py +266 -0
  14. boxfab/api/client/api/sandboxes/post_sandboxes.py +196 -0
  15. boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +221 -0
  16. boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_fork.py +226 -0
  17. boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +203 -0
  18. boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +191 -0
  19. boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +213 -0
  20. boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_snapshots.py +205 -0
  21. boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +203 -0
  22. boxfab/api/client/api/sandboxes/post_v2_sandboxes.py +227 -0
  23. boxfab/api/client/api/sandboxes/post_v_2_sandboxes_sandbox_id_connect.py +221 -0
  24. boxfab/api/client/api/sandboxes/put_sandboxes_sandbox_id_network.py +211 -0
  25. boxfab/api/client/api/secrets/__init__.py +1 -0
  26. boxfab/api/client/api/secrets/delete_secrets_secret_id.py +197 -0
  27. boxfab/api/client/api/secrets/get_secrets.py +223 -0
  28. boxfab/api/client/api/secrets/get_secrets_secret_id.py +199 -0
  29. boxfab/api/client/api/secrets/post_secrets.py +208 -0
  30. boxfab/api/client/api/secrets/post_secrets_secret_id.py +221 -0
  31. boxfab/api/client/api/snapshots/__init__.py +1 -0
  32. boxfab/api/client/api/snapshots/get_snapshots.py +229 -0
  33. boxfab/api/client/api/tags/__init__.py +1 -0
  34. boxfab/api/client/api/tags/delete_templates_tags.py +186 -0
  35. boxfab/api/client/api/tags/get_templates_template_id_tags.py +184 -0
  36. boxfab/api/client/api/tags/get_templates_template_id_tags_count.py +183 -0
  37. boxfab/api/client/api/tags/get_templates_template_id_tags_exists.py +208 -0
  38. boxfab/api/client/api/tags/get_templates_template_id_tags_groups.py +293 -0
  39. boxfab/api/client/api/tags/get_templates_template_id_tags_tag_assignments.py +236 -0
  40. boxfab/api/client/api/tags/post_templates_tags.py +188 -0
  41. boxfab/api/client/api/templates/__init__.py +1 -0
  42. boxfab/api/client/api/templates/delete_templates_template_id.py +169 -0
  43. boxfab/api/client/api/templates/get_default_templates.py +172 -0
  44. boxfab/api/client/api/templates/get_templates.py +188 -0
  45. boxfab/api/client/api/templates/get_templates_aliases_alias.py +179 -0
  46. boxfab/api/client/api/templates/get_templates_template_id.py +207 -0
  47. boxfab/api/client/api/templates/get_templates_template_id_builds_build_id_logs.py +284 -0
  48. boxfab/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +244 -0
  49. boxfab/api/client/api/templates/get_templates_template_id_files_hash.py +192 -0
  50. boxfab/api/client/api/templates/get_v2_templates.py +226 -0
  51. boxfab/api/client/api/templates/patch_templates_template_id.py +195 -0
  52. boxfab/api/client/api/templates/patch_v_2_templates_template_id.py +197 -0
  53. boxfab/api/client/api/templates/post_v3_templates.py +192 -0
  54. boxfab/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +212 -0
  55. boxfab/api/client/api/volumes/__init__.py +1 -0
  56. boxfab/api/client/api/volumes/delete_volumes_volume_id.py +173 -0
  57. boxfab/api/client/api/volumes/get_volumes.py +152 -0
  58. boxfab/api/client/api/volumes/get_volumes_volume_id.py +175 -0
  59. boxfab/api/client/api/volumes/post_volumes.py +184 -0
  60. boxfab/api/client/client.py +286 -0
  61. boxfab/api/client/errors.py +16 -0
  62. boxfab/api/client/models/__init__.py +183 -0
  63. boxfab/api/client/models/assign_template_tags_request.py +67 -0
  64. boxfab/api/client/models/assigned_template_tags.py +68 -0
  65. boxfab/api/client/models/aws_registry.py +85 -0
  66. boxfab/api/client/models/aws_registry_type.py +8 -0
  67. boxfab/api/client/models/build_log_entry.py +89 -0
  68. boxfab/api/client/models/build_status_reason.py +95 -0
  69. boxfab/api/client/models/connect_sandbox.py +74 -0
  70. boxfab/api/client/models/connect_sandbox_v2.py +73 -0
  71. boxfab/api/client/models/delete_template_tags_request.py +67 -0
  72. boxfab/api/client/models/error.py +83 -0
  73. boxfab/api/client/models/gcp_registry.py +69 -0
  74. boxfab/api/client/models/gcp_registry_type.py +8 -0
  75. boxfab/api/client/models/general_registry.py +77 -0
  76. boxfab/api/client/models/general_registry_type.py +8 -0
  77. boxfab/api/client/models/get_templates_template_id_tags_groups_sort.py +11 -0
  78. boxfab/api/client/models/listed_sandbox.py +179 -0
  79. boxfab/api/client/models/log_level.py +11 -0
  80. boxfab/api/client/models/logs_direction.py +9 -0
  81. boxfab/api/client/models/logs_source.py +9 -0
  82. boxfab/api/client/models/mcp_type_0.py +44 -0
  83. boxfab/api/client/models/new_sandbox.py +244 -0
  84. boxfab/api/client/models/new_sandbox_v2.py +236 -0
  85. boxfab/api/client/models/new_secret.py +95 -0
  86. boxfab/api/client/models/new_volume.py +59 -0
  87. boxfab/api/client/models/order_direction.py +9 -0
  88. boxfab/api/client/models/resumed_sandbox.py +81 -0
  89. boxfab/api/client/models/sandbox.py +145 -0
  90. boxfab/api/client/models/sandbox_auto_resume_config.py +60 -0
  91. boxfab/api/client/models/sandbox_detail.py +287 -0
  92. boxfab/api/client/models/sandbox_egress_proxy_config_type_0.py +133 -0
  93. boxfab/api/client/models/sandbox_egress_proxy_tls_config_type_0.py +87 -0
  94. boxfab/api/client/models/sandbox_fork_request.py +70 -0
  95. boxfab/api/client/models/sandbox_fork_result.py +92 -0
  96. boxfab/api/client/models/sandbox_iam.py +73 -0
  97. boxfab/api/client/models/sandbox_iam_token.py +67 -0
  98. boxfab/api/client/models/sandbox_iam_tokens.py +59 -0
  99. boxfab/api/client/models/sandbox_lifecycle.py +70 -0
  100. boxfab/api/client/models/sandbox_log.py +70 -0
  101. boxfab/api/client/models/sandbox_log_entry.py +93 -0
  102. boxfab/api/client/models/sandbox_log_entry_fields.py +44 -0
  103. boxfab/api/client/models/sandbox_logs.py +91 -0
  104. boxfab/api/client/models/sandbox_logs_v2_response.py +73 -0
  105. boxfab/api/client/models/sandbox_metric.py +126 -0
  106. boxfab/api/client/models/sandbox_network_config.py +210 -0
  107. boxfab/api/client/models/sandbox_network_config_rules.py +76 -0
  108. boxfab/api/client/models/sandbox_network_rule.py +74 -0
  109. boxfab/api/client/models/sandbox_network_transform.py +79 -0
  110. boxfab/api/client/models/sandbox_network_transform_headers.py +47 -0
  111. boxfab/api/client/models/sandbox_network_update_config.py +179 -0
  112. boxfab/api/client/models/sandbox_network_update_config_rules.py +78 -0
  113. boxfab/api/client/models/sandbox_on_timeout.py +9 -0
  114. boxfab/api/client/models/sandbox_pause_mode.py +9 -0
  115. boxfab/api/client/models/sandbox_pause_request.py +62 -0
  116. boxfab/api/client/models/sandbox_refresh_request.py +59 -0
  117. boxfab/api/client/models/sandbox_snapshot_request.py +72 -0
  118. boxfab/api/client/models/sandbox_state.py +9 -0
  119. boxfab/api/client/models/sandbox_timeout_request.py +59 -0
  120. boxfab/api/client/models/sandbox_volume_mount.py +67 -0
  121. boxfab/api/client/models/sandboxes_with_metrics.py +59 -0
  122. boxfab/api/client/models/secret.py +109 -0
  123. boxfab/api/client/models/secret_metadata.py +47 -0
  124. boxfab/api/client/models/secret_update.py +86 -0
  125. boxfab/api/client/models/snapshot_info.py +70 -0
  126. boxfab/api/client/models/team_user.py +74 -0
  127. boxfab/api/client/models/template.py +246 -0
  128. boxfab/api/client/models/template_alias_response.py +67 -0
  129. boxfab/api/client/models/template_build.py +139 -0
  130. boxfab/api/client/models/template_build_file_upload.py +97 -0
  131. boxfab/api/client/models/template_build_file_upload_headers.py +44 -0
  132. boxfab/api/client/models/template_build_info.py +126 -0
  133. boxfab/api/client/models/template_build_logs_response.py +73 -0
  134. boxfab/api/client/models/template_build_request_v3.py +119 -0
  135. boxfab/api/client/models/template_build_start_v2.py +185 -0
  136. boxfab/api/client/models/template_build_status.py +11 -0
  137. boxfab/api/client/models/template_request_response_v3.py +99 -0
  138. boxfab/api/client/models/template_step.py +91 -0
  139. boxfab/api/client/models/template_tag.py +78 -0
  140. boxfab/api/client/models/template_tag_assignment.py +111 -0
  141. boxfab/api/client/models/template_tag_assignments_response.py +87 -0
  142. boxfab/api/client/models/template_tag_exists_response.py +67 -0
  143. boxfab/api/client/models/template_tag_group.py +89 -0
  144. boxfab/api/client/models/template_tag_groups_response.py +88 -0
  145. boxfab/api/client/models/template_tags_count_response.py +59 -0
  146. boxfab/api/client/models/template_update_request.py +59 -0
  147. boxfab/api/client/models/template_update_response.py +59 -0
  148. boxfab/api/client/models/template_with_builds.py +264 -0
  149. boxfab/api/client/models/volume.py +67 -0
  150. boxfab/api/client/models/volume_and_token.py +89 -0
  151. boxfab/api/client/py.typed +1 -0
  152. boxfab/api/client/types.py +54 -0
  153. boxfab/api/client_async/__init__.py +232 -0
  154. boxfab/api/client_sync/__init__.py +237 -0
  155. boxfab/api/metadata.py +14 -0
  156. boxfab/client.py +92 -0
  157. boxfab/connection_config.py +473 -0
  158. boxfab/envd/__init__.py +1 -0
  159. boxfab/envd/api.py +204 -0
  160. boxfab/envd/client_async/__init__.py +149 -0
  161. boxfab/envd/client_shared.py +143 -0
  162. boxfab/envd/client_sync/__init__.py +97 -0
  163. boxfab/envd/filesystem/__init__.py +1 -0
  164. boxfab/envd/filesystem/filesystem_connect.py +690 -0
  165. boxfab/envd/filesystem/filesystem_pb.py +851 -0
  166. boxfab/envd/interceptors.py +275 -0
  167. boxfab/envd/process/__init__.py +1 -0
  168. boxfab/envd/process/process_connect.py +641 -0
  169. boxfab/envd/process/process_pb.py +938 -0
  170. boxfab/envd/rpc.py +247 -0
  171. boxfab/envd/utils.py +57 -0
  172. boxfab/envd/versions.py +11 -0
  173. boxfab/exceptions.py +269 -0
  174. boxfab/io_utils.py +57 -0
  175. boxfab/paginator.py +50 -0
  176. boxfab/py.typed +0 -0
  177. boxfab/retry.py +229 -0
  178. boxfab/sandbox/_git/__init__.py +85 -0
  179. boxfab/sandbox/_git/args.py +363 -0
  180. boxfab/sandbox/_git/auth.py +132 -0
  181. boxfab/sandbox/_git/config.py +32 -0
  182. boxfab/sandbox/_git/parse.py +222 -0
  183. boxfab/sandbox/_git/types.py +156 -0
  184. boxfab/sandbox/commands/command_handle.py +89 -0
  185. boxfab/sandbox/commands/main.py +39 -0
  186. boxfab/sandbox/commands/stream_input.py +37 -0
  187. boxfab/sandbox/filesystem/filesystem.py +585 -0
  188. boxfab/sandbox/filesystem/watch_handle.py +70 -0
  189. boxfab/sandbox/iam.py +93 -0
  190. boxfab/sandbox/main.py +232 -0
  191. boxfab/sandbox/mcp.py +2538 -0
  192. boxfab/sandbox/network.py +8 -0
  193. boxfab/sandbox/sandbox_api.py +1398 -0
  194. boxfab/sandbox/signature.py +47 -0
  195. boxfab/sandbox/utils.py +34 -0
  196. boxfab/sandbox_async/commands/command.py +582 -0
  197. boxfab/sandbox_async/commands/command_handle.py +324 -0
  198. boxfab/sandbox_async/commands/command_input_handle.py +131 -0
  199. boxfab/sandbox_async/commands/pty.py +283 -0
  200. boxfab/sandbox_async/filesystem/filesystem.py +785 -0
  201. boxfab/sandbox_async/filesystem/watch_handle.py +100 -0
  202. boxfab/sandbox_async/git.py +1102 -0
  203. boxfab/sandbox_async/main.py +1250 -0
  204. boxfab/sandbox_async/paginator.py +159 -0
  205. boxfab/sandbox_async/sandbox_api.py +533 -0
  206. boxfab/sandbox_async/utils.py +7 -0
  207. boxfab/sandbox_domains.py +13 -0
  208. boxfab/sandbox_sync/commands/command.py +576 -0
  209. boxfab/sandbox_sync/commands/command_handle.py +260 -0
  210. boxfab/sandbox_sync/commands/pty.py +286 -0
  211. boxfab/sandbox_sync/filesystem/filesystem.py +731 -0
  212. boxfab/sandbox_sync/filesystem/watch_handle.py +103 -0
  213. boxfab/sandbox_sync/git.py +1079 -0
  214. boxfab/sandbox_sync/main.py +1246 -0
  215. boxfab/sandbox_sync/paginator.py +159 -0
  216. boxfab/sandbox_sync/sandbox_api.py +520 -0
  217. boxfab/secret/__init__.py +11 -0
  218. boxfab/secret/base.py +84 -0
  219. boxfab/secret/secret_async.py +270 -0
  220. boxfab/secret/secret_sync.py +270 -0
  221. boxfab/secret/types.py +34 -0
  222. boxfab/template/consts.py +33 -0
  223. boxfab/template/dockerfile_parser.py +634 -0
  224. boxfab/template/logger.py +232 -0
  225. boxfab/template/main.py +1315 -0
  226. boxfab/template/readycmd.py +144 -0
  227. boxfab/template/types.py +194 -0
  228. boxfab/template/utils.py +535 -0
  229. boxfab/template_async/build_api.py +449 -0
  230. boxfab/template_async/main.py +551 -0
  231. boxfab/template_sync/build_api.py +445 -0
  232. boxfab/template_sync/main.py +553 -0
  233. boxfab/volume/client/__init__.py +8 -0
  234. boxfab/volume/client/api/__init__.py +1 -0
  235. boxfab/volume/client/api/volumes/__init__.py +1 -0
  236. boxfab/volume/client/api/volumes/delete_volumecontent_volume_id_path.py +174 -0
  237. boxfab/volume/client/api/volumes/get_volumecontent_volume_id_dir.py +204 -0
  238. boxfab/volume/client/api/volumes/get_volumecontent_volume_id_file.py +179 -0
  239. boxfab/volume/client/api/volumes/get_volumecontent_volume_id_path.py +176 -0
  240. boxfab/volume/client/api/volumes/patch_volumecontent_volume_id_path.py +203 -0
  241. boxfab/volume/client/api/volumes/post_volumecontent_volume_id_dir.py +239 -0
  242. boxfab/volume/client/api/volumes/put_volumecontent_volume_id_file.py +259 -0
  243. boxfab/volume/client/client.py +286 -0
  244. boxfab/volume/client/errors.py +16 -0
  245. boxfab/volume/client/models/__init__.py +13 -0
  246. boxfab/volume/client/models/error.py +67 -0
  247. boxfab/volume/client/models/patch_volumecontent_volume_id_path_body.py +77 -0
  248. boxfab/volume/client/models/volume_entry_stat.py +145 -0
  249. boxfab/volume/client/models/volume_entry_stat_type.py +11 -0
  250. boxfab/volume/client/py.typed +1 -0
  251. boxfab/volume/client/types.py +54 -0
  252. boxfab/volume/client_async/__init__.py +94 -0
  253. boxfab/volume/client_sync/__init__.py +92 -0
  254. boxfab/volume/connection_config.py +166 -0
  255. boxfab/volume/types.py +66 -0
  256. boxfab/volume/utils.py +56 -0
  257. boxfab/volume/volume_async.py +719 -0
  258. boxfab/volume/volume_sync.py +691 -0
  259. boxfab-0.1.0a1.dist-info/METADATA +186 -0
  260. boxfab-0.1.0a1.dist-info/RECORD +263 -0
  261. boxfab-0.1.0a1.dist-info/WHEEL +4 -0
  262. boxfab-0.1.0a1.dist-info/licenses/LICENSE +13 -0
  263. boxfab-0.1.0a1.dist-info/licenses/LICENSE-E2B +9 -0
boxfab/__init__.py ADDED
@@ -0,0 +1,331 @@
1
+ """
2
+ Secure sandboxed cloud environments made for AI agents and AI apps.
3
+
4
+ Check docs [here](https://e2b.dev/docs).
5
+
6
+ E2B 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
+ E2B Python SDK supports both sync and async API:
12
+
13
+ ```py
14
+ from boxfab import Sandbox
15
+
16
+ # Create sandbox
17
+ sandbox = Sandbox.create()
18
+ ```
19
+
20
+ ```py
21
+ from boxfab 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
+ ApiParams,
34
+ ConnectionConfig,
35
+ HttpVersion,
36
+ ProxyTypes,
37
+ Username,
38
+ )
39
+ from .volume.connection_config import VolumeApiParams, VolumeConnectionConfig
40
+ from .exceptions import (
41
+ AuthenticationException,
42
+ FileNotFoundException,
43
+ GitAuthException,
44
+ GitUpstreamException,
45
+ BuildException,
46
+ FileUploadException,
47
+ InvalidArgumentException,
48
+ NotEnoughSpaceException,
49
+ NotFoundException,
50
+ RateLimitException,
51
+ ServiceBusyException,
52
+ SandboxException,
53
+ SandboxNotFoundException,
54
+ SandboxNotRunningException,
55
+ SandboxUnreachableException,
56
+ TemplateException,
57
+ TimeoutException,
58
+ VolumeException,
59
+ VolumeNotFoundException,
60
+ VolumePathNotFoundException,
61
+ SecretException,
62
+ SecretNotFoundException,
63
+ )
64
+ from .sandbox.commands.command_handle import (
65
+ CommandExitException,
66
+ CommandResult,
67
+ PtyOutput,
68
+ PtySize,
69
+ Stderr,
70
+ Stdout,
71
+ )
72
+ from .sandbox.commands.main import ProcessInfo
73
+ from .sandbox.filesystem.filesystem import (
74
+ AsyncWriteData,
75
+ EntryInfo,
76
+ FileType,
77
+ WriteData,
78
+ WriteInfo,
79
+ )
80
+ from .sandbox.filesystem.watch_handle import (
81
+ FilesystemEvent,
82
+ FilesystemEventType,
83
+ )
84
+ from .sandbox._git import GitBranches, GitFileStatus, GitResetMode, GitStatus
85
+ from .sandbox_sync.git import Git
86
+ from .sandbox.network import ALL_TRAFFIC
87
+ from .sandbox.signature import get_signature
88
+ from .sandbox.sandbox_api import (
89
+ GitHubMcpServer,
90
+ GitHubMcpServerConfig,
91
+ McpServer,
92
+ SandboxEgressProxyInfo,
93
+ SandboxEgressProxyOpts,
94
+ SandboxEgressProxyTLSInfo,
95
+ SandboxEgressProxyTLSOpts,
96
+ SandboxIamOpts,
97
+ SandboxIamToken,
98
+ SandboxIamTokenType,
99
+ SandboxInfo,
100
+ SandboxInfoLifecycle,
101
+ SandboxMetrics,
102
+ SandboxLifecycle,
103
+ SandboxOnTimeout,
104
+ SandboxNetworkInfo,
105
+ SandboxNetworkOpts,
106
+ SandboxNetworkRule,
107
+ SandboxNetworkRuleInfo,
108
+ SandboxNetworkRules,
109
+ SandboxNetworkSelector,
110
+ SandboxNetworkSelectorContext,
111
+ SandboxNetworkTransform,
112
+ SandboxNetworkTransformContext,
113
+ SandboxNetworkTransformResolver,
114
+ SandboxNetworkUpdate,
115
+ SandboxOnResume,
116
+ SandboxListOrder,
117
+ SandboxQuery,
118
+ SandboxState,
119
+ SnapshotInfo,
120
+ SnapshotMode,
121
+ )
122
+ from .sandbox_async.commands.command_handle import AsyncCommandHandle
123
+ from .sandbox_async.commands.command_input_handle import AsyncCommandInputHandle
124
+ from .sandbox_async.filesystem.watch_handle import AsyncWatchHandle
125
+ from .sandbox_async.main import AsyncSandbox
126
+ from .sandbox_async.paginator import AsyncSandboxPaginator, AsyncSnapshotPaginator
127
+ from .sandbox_async.utils import OutputHandler
128
+ from .secret import (
129
+ AsyncSecret,
130
+ AsyncSecretPaginator,
131
+ Secret,
132
+ SecretInfo,
133
+ SecretPaginator,
134
+ )
135
+ from .sandbox_sync.commands.command_handle import CommandHandle
136
+ from .sandbox_sync.filesystem.watch_handle import WatchHandle
137
+ from .sandbox_sync.main import Sandbox
138
+ from .sandbox_sync.paginator import SandboxPaginator, SnapshotPaginator
139
+ from .template.logger import (
140
+ LogEntry,
141
+ LogEntryEnd,
142
+ LogEntryLevel,
143
+ LogEntryStart,
144
+ default_build_logger,
145
+ )
146
+ from .template.dockerfile_parser import DockerfileSyntaxError
147
+ from .template.main import TemplateBase, TemplateClass
148
+ from .template.readycmd import (
149
+ ReadyCmd,
150
+ wait_for_file,
151
+ wait_for_port,
152
+ wait_for_process,
153
+ wait_for_timeout,
154
+ wait_for_url,
155
+ )
156
+ from .template.types import (
157
+ BuildInfo,
158
+ BuildStatusReason,
159
+ CopyItem,
160
+ TemplateBuildStatus,
161
+ TemplateBuildStatusResponse,
162
+ TemplateTag,
163
+ TemplateTagInfo,
164
+ )
165
+ from .template_async.main import AsyncTemplate
166
+ from .template_sync.main import Template
167
+
168
+ from .volume.volume_sync import Volume
169
+ from .volume.volume_async import AsyncVolume
170
+ from .client import BoxFab, BoxFabClientParams, E2B, E2BClientParams
171
+ from .volume.types import (
172
+ VolumeInfo,
173
+ VolumeAndToken,
174
+ VolumeEntryStat,
175
+ VolumeFileType,
176
+ )
177
+
178
+ __all__ = [
179
+ # Client
180
+ "BoxFab",
181
+ "BoxFabClientParams",
182
+ "E2B",
183
+ "E2BClientParams",
184
+ # API
185
+ "ApiClient",
186
+ "client",
187
+ # Connection config
188
+ "ConnectionConfig",
189
+ "VolumeConnectionConfig",
190
+ "HttpVersion",
191
+ "ProxyTypes",
192
+ "ApiParams",
193
+ "VolumeApiParams",
194
+ "Username",
195
+ # Exceptions
196
+ "SandboxException",
197
+ "ServiceBusyException",
198
+ "TimeoutException",
199
+ "NotFoundException",
200
+ "FileNotFoundException",
201
+ "SandboxNotFoundException",
202
+ "SandboxNotRunningException",
203
+ "SandboxUnreachableException",
204
+ "AuthenticationException",
205
+ "GitAuthException",
206
+ "GitUpstreamException",
207
+ "InvalidArgumentException",
208
+ "NotEnoughSpaceException",
209
+ "TemplateException",
210
+ "DockerfileSyntaxError",
211
+ "BuildException",
212
+ "FileUploadException",
213
+ "RateLimitException",
214
+ "VolumeException",
215
+ "VolumeNotFoundException",
216
+ "VolumePathNotFoundException",
217
+ # Sandbox API
218
+ "SandboxInfo",
219
+ "SandboxInfoLifecycle",
220
+ "SandboxMetrics",
221
+ "ProcessInfo",
222
+ "SandboxListOrder",
223
+ "SandboxQuery",
224
+ "SandboxState",
225
+ "SandboxMetrics",
226
+ "GitStatus",
227
+ "GitBranches",
228
+ "GitFileStatus",
229
+ "GitResetMode",
230
+ # Command handle
231
+ "CommandResult",
232
+ "Stderr",
233
+ "Stdout",
234
+ "CommandExitException",
235
+ "PtyOutput",
236
+ "PtySize",
237
+ # Filesystem
238
+ "FilesystemEvent",
239
+ "FilesystemEventType",
240
+ "EntryInfo",
241
+ "WriteInfo",
242
+ "WriteData",
243
+ "AsyncWriteData",
244
+ "FileType",
245
+ # Network
246
+ "SandboxEgressProxyOpts",
247
+ "SandboxEgressProxyInfo",
248
+ "SandboxEgressProxyTLSOpts",
249
+ "SandboxEgressProxyTLSInfo",
250
+ "SandboxNetworkOpts",
251
+ "SandboxNetworkInfo",
252
+ "SandboxNetworkSelector",
253
+ "SandboxNetworkSelectorContext",
254
+ "SandboxNetworkRule",
255
+ "SandboxNetworkRuleInfo",
256
+ "SandboxNetworkRules",
257
+ "SandboxNetworkTransform",
258
+ "SandboxNetworkTransformContext",
259
+ "SandboxNetworkTransformResolver",
260
+ "SandboxNetworkUpdate",
261
+ "SandboxLifecycle",
262
+ "SandboxOnTimeout",
263
+ "SandboxOnResume",
264
+ "ALL_TRAFFIC",
265
+ # IAM
266
+ "SandboxIamOpts",
267
+ "SandboxIamToken",
268
+ "SandboxIamTokenType",
269
+ "Secret",
270
+ "AsyncSecret",
271
+ "SecretInfo",
272
+ "SecretPaginator",
273
+ "AsyncSecretPaginator",
274
+ "SecretException",
275
+ "SecretNotFoundException",
276
+ # Snapshot
277
+ "SnapshotInfo",
278
+ "SnapshotMode",
279
+ "SnapshotPaginator",
280
+ "AsyncSnapshotPaginator",
281
+ # Signature
282
+ "get_signature",
283
+ # Sync sandbox
284
+ "Sandbox",
285
+ "SandboxPaginator",
286
+ "WatchHandle",
287
+ "CommandHandle",
288
+ # Async sandbox
289
+ "OutputHandler",
290
+ "AsyncSandboxPaginator",
291
+ "AsyncSandbox",
292
+ "AsyncWatchHandle",
293
+ "AsyncCommandHandle",
294
+ "AsyncCommandInputHandle",
295
+ # Template
296
+ "Template",
297
+ "AsyncTemplate",
298
+ "TemplateBase",
299
+ "TemplateClass",
300
+ "CopyItem",
301
+ "BuildInfo",
302
+ "BuildStatusReason",
303
+ "TemplateBuildStatus",
304
+ "TemplateBuildStatusResponse",
305
+ "TemplateTag",
306
+ "TemplateTagInfo",
307
+ "ReadyCmd",
308
+ "wait_for_file",
309
+ "wait_for_url",
310
+ "wait_for_port",
311
+ "wait_for_process",
312
+ "wait_for_timeout",
313
+ "LogEntry",
314
+ "LogEntryStart",
315
+ "LogEntryEnd",
316
+ "LogEntryLevel",
317
+ "default_build_logger",
318
+ # MCP
319
+ "McpServer",
320
+ "GitHubMcpServer",
321
+ "GitHubMcpServerConfig",
322
+ # Git
323
+ "Git",
324
+ # Volume
325
+ "Volume",
326
+ "AsyncVolume",
327
+ "VolumeInfo",
328
+ "VolumeAndToken",
329
+ "VolumeEntryStat",
330
+ "VolumeFileType",
331
+ ]
boxfab/api/__init__.py ADDED
@@ -0,0 +1,301 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ from dataclasses import dataclass
5
+ from types import TracebackType
6
+ from typing import (
7
+ NamedTuple,
8
+ Optional,
9
+ Protocol,
10
+ Tuple,
11
+ Union,
12
+ )
13
+ from urllib.parse import quote
14
+
15
+ import httpx
16
+ from httpx import AsyncBaseTransport, BaseTransport, Timeout
17
+ from pyqwest import Proxy
18
+
19
+ from boxfab.api.client.client import AuthenticatedClient
20
+ from boxfab.api.client.types import Response
21
+ from boxfab.api.metadata import default_headers
22
+ from boxfab.connection_config import ConnectionConfig, ProxyTypes
23
+ from boxfab.exceptions import (
24
+ AuthenticationException,
25
+ InvalidArgumentException,
26
+ RateLimitException,
27
+ ServiceBusyException,
28
+ SandboxException,
29
+ )
30
+
31
+
32
+ def encode_path_param(value: str) -> str:
33
+ """
34
+ Percent-encode a template ID, name, or alias for use as one URL path segment.
35
+
36
+ Endpoints that take a template ID also accept a name, which may be
37
+ namespaced (``namespace/name``) and carry a ``:tag``. Escaping those
38
+ separators keeps the value inside a single segment instead of splitting the
39
+ route, matching the JS SDK's ``encodeURIComponent``.
40
+ """
41
+ return quote(value, safe="")
42
+
43
+
44
+ def make_logging_event_hooks(log: Optional[logging.Logger]) -> dict:
45
+ """Build synchronous httpx ``event_hooks`` that log requests and responses
46
+ to the given ``logging.Logger``. Requests log at ``INFO``, successful
47
+ responses at ``INFO`` and responses with status >= 400 at ``ERROR``.
48
+
49
+ Returns no hooks when ``log`` is ``None`` so that nothing is logged unless a
50
+ logger was explicitly supplied."""
51
+ if log is None:
52
+ return {}
53
+
54
+ def on_request(request) -> None:
55
+ log.info(f"Request {request.method} {request.url}")
56
+
57
+ def on_response(response: Response) -> None:
58
+ if response.status_code >= 400:
59
+ log.error(f"Response {response.status_code}")
60
+ else:
61
+ log.info(f"Response {response.status_code}")
62
+
63
+ return {"request": [on_request], "response": [on_response]}
64
+
65
+
66
+ def make_async_logging_event_hooks(log: Optional[logging.Logger]) -> dict:
67
+ """Asynchronous counterpart of :func:`make_logging_event_hooks`."""
68
+ if log is None:
69
+ return {}
70
+
71
+ async def on_request(request) -> None:
72
+ log.info(f"Request {request.method} {request.url}")
73
+
74
+ async def on_response(response: Response) -> None:
75
+ if response.status_code >= 400:
76
+ log.error(f"Response {response.status_code}")
77
+ else:
78
+ log.info(f"Response {response.status_code}")
79
+
80
+ return {"request": [on_request], "response": [on_response]}
81
+
82
+
83
+ connection_retries = int(os.getenv("BOXFAB_CONNECTION_RETRIES") or "3")
84
+
85
+ # Pool tuning for the pyqwest transports, shared by the REST API, envd RPC,
86
+ # and envd HTTP API stacks. `pool_max_idle_per_host` is per host rather than
87
+ # the global idle cap the httpx transports took, which suits both: API traffic
88
+ # goes to a single host and each sandbox is its own host.
89
+ pool_idle_timeout = float(os.getenv("BOXFAB_KEEPALIVE_EXPIRY") or "300")
90
+ pool_max_idle_per_host = int(os.getenv("BOXFAB_MAX_KEEPALIVE_CONNECTIONS") or "20")
91
+
92
+ # pyqwest spreads an origin's HTTP/2 streams over as many connections as the
93
+ # server's concurrent-stream limit requires (envd's edge, which all sandboxes
94
+ # share, advertises 100), dialing another once every connection is full, so
95
+ # long-running commands never queue behind one connection. `max_connections`
96
+ # caps the connections to each address an origin resolves to; past it, a
97
+ # request waits on the least loaded connection for a stream to free up.
98
+ max_connections = max(1, int(os.getenv("BOXFAB_MAX_CONNECTIONS") or "200"))
99
+
100
+
101
+ class ProxyConfig(NamedTuple):
102
+ """The ``proxy`` connection option in the shape pyqwest transports take.
103
+
104
+ A tuple so it can key the transport caches directly: it is hashable and
105
+ compares by value, where a ``pyqwest.Proxy`` compares by identity and
106
+ would hand every call its own connection pool."""
107
+
108
+ url: str
109
+ auth: Optional[Tuple[str, str]] = None
110
+ headers: Tuple[Tuple[str, str], ...] = ()
111
+
112
+ def to_pyqwest(self) -> Proxy:
113
+ """The ``pyqwest.Proxy`` to hand a transport."""
114
+ return Proxy(self.url, auth=self.auth, headers=self.headers or None)
115
+
116
+
117
+ def proxy_to_config(proxy: Optional[ProxyTypes]) -> Optional[ProxyConfig]:
118
+ """Convert the ``proxy`` connection option — a URL string, an
119
+ ``httpx.URL``, or an ``httpx.Proxy`` — to the proxy configuration pyqwest
120
+ transports take: a proxy URL (scheme http, https, socks5, or socks5h,
121
+ credentials allowed in the userinfo), basic-auth credentials, and headers
122
+ to send to the proxy. An ``httpx.Proxy`` ``ssl_context`` has no pyqwest
123
+ counterpart and is rejected rather than silently dropped."""
124
+ if proxy is None:
125
+ return None
126
+ if isinstance(proxy, str):
127
+ return ProxyConfig(proxy)
128
+ if isinstance(proxy, httpx.URL):
129
+ return ProxyConfig(str(proxy))
130
+ if isinstance(proxy, httpx.Proxy):
131
+ if proxy.ssl_context is not None:
132
+ raise InvalidArgumentException("httpx.Proxy ssl_context is not supported")
133
+ # httpx.Proxy splits userinfo out of the URL into `.auth`; pyqwest
134
+ # takes the credentials the same way, so they pass straight through.
135
+ return ProxyConfig(
136
+ str(proxy.url),
137
+ auth=proxy.auth,
138
+ headers=tuple(proxy.headers.items()),
139
+ )
140
+ raise InvalidArgumentException(
141
+ "Only URL-string, httpx.URL, and httpx.Proxy proxies are supported, "
142
+ 'e.g. proxy="http://user:pass@localhost:8030"'
143
+ )
144
+
145
+
146
+ @dataclass
147
+ class SandboxCreateResponse:
148
+ sandbox_id: str
149
+ sandbox_domain: Optional[str]
150
+ envd_version: str
151
+ envd_access_token: Optional[str]
152
+ traffic_access_token: Optional[str]
153
+
154
+
155
+ def api_exception_from_code(
156
+ status_code: int,
157
+ message: Optional[str] = None,
158
+ default_exception_class: type[Exception] = SandboxException,
159
+ stack_trace: Optional[TracebackType] = None,
160
+ ) -> Exception:
161
+ """Map an API error code and message to the matching exception class — the
162
+ same mapping :func:`handle_api_exception` applies to HTTP responses, usable
163
+ for error objects embedded in response bodies (e.g. per-fork results)."""
164
+ if status_code == 401:
165
+ text = f"{status_code}: Unauthorized, please check your credentials."
166
+ if message:
167
+ text += f" - {message}"
168
+ return AuthenticationException(text)
169
+
170
+ if status_code == 429:
171
+ text = f"{status_code}: Rate limit exceeded, please try again later."
172
+ if message:
173
+ text += f" - {message}"
174
+ return RateLimitException(text)
175
+
176
+ if status_code == 503:
177
+ text = f"{status_code}: Service temporarily unavailable, please retry."
178
+ if message:
179
+ text += f" - {message}"
180
+ return ServiceBusyException(text)
181
+
182
+ err = default_exception_class(f"{status_code}: {message}").with_traceback(
183
+ stack_trace
184
+ )
185
+ if isinstance(err, SandboxException):
186
+ err.status_code = status_code
187
+
188
+ return err
189
+
190
+
191
+ def handle_api_exception(
192
+ e: "SupportsApiErrorResponse",
193
+ default_exception_class: type[Exception] = SandboxException,
194
+ stack_trace: Optional[TracebackType] = None,
195
+ ):
196
+ try:
197
+ body = json.loads(e.content) if e.content else {}
198
+ except json.JSONDecodeError:
199
+ body = {}
200
+
201
+ message = body["message"] if "message" in body else None
202
+ if message is None and e.status_code not in (401, 429, 503):
203
+ err = default_exception_class(f"{e.status_code}: {e.content}").with_traceback(
204
+ stack_trace
205
+ )
206
+ if isinstance(err, SandboxException):
207
+ err.status_code = e.status_code
208
+
209
+ return err
210
+
211
+ return api_exception_from_code(
212
+ e.status_code, message, default_exception_class, stack_trace
213
+ )
214
+
215
+
216
+ class SupportsApiErrorResponse(Protocol):
217
+ @property
218
+ def status_code(self) -> int: ...
219
+
220
+ @property
221
+ def content(self) -> Union[str, bytes]: ...
222
+
223
+
224
+ class ApiClient(AuthenticatedClient):
225
+ """
226
+ The client for interacting with the E2B API.
227
+
228
+ A single lazily-created httpx client (see the generated
229
+ ``AuthenticatedClient``) serves all threads and event loops: the pyqwest
230
+ transports it delegates to are thread-safe and loop-independent, and
231
+ ``httpx.Client`` is documented thread-safe.
232
+ """
233
+
234
+ def __init__(
235
+ self,
236
+ config: ConnectionConfig,
237
+ transport: Optional[Union[BaseTransport, AsyncBaseTransport]] = None,
238
+ *args,
239
+ **kwargs,
240
+ ):
241
+ self._proxy = config.proxy
242
+
243
+ if config.api_key is None:
244
+ raise AuthenticationException(
245
+ "API key is required, get one from your BoxFab administrator (keys are shown at https://dashboard.sandd.contextfab.io). "
246
+ "You can either set the environment variable `BOXFAB_API_KEY` "
247
+ 'or you can pass it directly to the method like api_key="e2b_..."',
248
+ )
249
+
250
+ token = config.api_key
251
+ auth_header_name = "X-API-KEY"
252
+ prefix = ""
253
+
254
+ self._logger = config.logger
255
+
256
+ headers = {
257
+ **default_headers,
258
+ **(config.headers or {}),
259
+ }
260
+
261
+ # Prevent passing these parameters twice
262
+ more_headers: Optional[dict] = kwargs.pop("headers", None)
263
+ if more_headers:
264
+ headers.update(more_headers)
265
+ kwargs.pop("token", None)
266
+ kwargs.pop("auth_header_name", None)
267
+ kwargs.pop("prefix", None)
268
+
269
+ httpx_args = {
270
+ "event_hooks": self._logging_event_hooks(),
271
+ }
272
+ if transport is not None:
273
+ # The proxy lives in the transport; passing `proxy` here too
274
+ # would mount a fresh, never-closed proxy transport per client.
275
+ httpx_args["transport"] = transport
276
+ else:
277
+ httpx_args["proxy"] = config.proxy
278
+
279
+ # config.request_timeout is None when the timeout is explicitly
280
+ # disabled (request_timeout=0), which httpx.Timeout(None) preserves.
281
+ kwargs.setdefault("timeout", Timeout(config.request_timeout))
282
+
283
+ super().__init__(
284
+ base_url=config.api_url,
285
+ httpx_args=httpx_args,
286
+ headers=headers,
287
+ token=token or "",
288
+ auth_header_name=auth_header_name,
289
+ prefix=prefix,
290
+ *args,
291
+ **kwargs,
292
+ )
293
+
294
+ def _logging_event_hooks(self) -> dict:
295
+ return make_logging_event_hooks(self._logger)
296
+
297
+
298
+ # We need to override the logging hooks for the async usage
299
+ class AsyncApiClient(ApiClient):
300
+ def _logging_event_hooks(self) -> dict:
301
+ return make_async_logging_event_hooks(self._logger)
@@ -0,0 +1,8 @@
1
+ """A client library for accessing E2B 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"""