@alchemy.run/e2b 0.0.0 → 2.0.0-beta.87

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 (284) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +56 -4
  3. package/lib/ApiKey.d.ts +53 -0
  4. package/lib/ApiKey.d.ts.map +1 -0
  5. package/lib/ApiKey.js +107 -0
  6. package/lib/ApiKey.js.map +1 -0
  7. package/lib/AuthProvider.d.ts +60 -0
  8. package/lib/AuthProvider.d.ts.map +1 -0
  9. package/lib/AuthProvider.js +293 -0
  10. package/lib/AuthProvider.js.map +1 -0
  11. package/lib/Bootstrap.d.ts +7 -0
  12. package/lib/Bootstrap.d.ts.map +1 -0
  13. package/lib/Bootstrap.js +25 -0
  14. package/lib/Bootstrap.js.map +1 -0
  15. package/lib/ConnectSandbox.d.ts +67 -0
  16. package/lib/ConnectSandbox.d.ts.map +1 -0
  17. package/lib/ConnectSandbox.js +3 -0
  18. package/lib/ConnectSandbox.js.map +1 -0
  19. package/lib/ConnectSandboxHttp.d.ts +22 -0
  20. package/lib/ConnectSandboxHttp.d.ts.map +1 -0
  21. package/lib/ConnectSandboxHttp.js +30 -0
  22. package/lib/ConnectSandboxHttp.js.map +1 -0
  23. package/lib/Contributions.d.ts +7 -0
  24. package/lib/Contributions.d.ts.map +1 -0
  25. package/lib/Contributions.js +6 -0
  26. package/lib/Contributions.js.map +1 -0
  27. package/lib/CreateSandbox.d.ts +139 -0
  28. package/lib/CreateSandbox.d.ts.map +1 -0
  29. package/lib/CreateSandbox.js +3 -0
  30. package/lib/CreateSandbox.js.map +1 -0
  31. package/lib/CreateSandboxHttp.d.ts +22 -0
  32. package/lib/CreateSandboxHttp.d.ts.map +1 -0
  33. package/lib/CreateSandboxHttp.js +99 -0
  34. package/lib/CreateSandboxHttp.js.map +1 -0
  35. package/lib/CreateSecret.d.ts +57 -0
  36. package/lib/CreateSecret.d.ts.map +1 -0
  37. package/lib/CreateSecret.js +3 -0
  38. package/lib/CreateSecret.js.map +1 -0
  39. package/lib/CreateSecretHttp.d.ts +20 -0
  40. package/lib/CreateSecretHttp.d.ts.map +1 -0
  41. package/lib/CreateSecretHttp.js +64 -0
  42. package/lib/CreateSecretHttp.js.map +1 -0
  43. package/lib/CreateSnapshot.d.ts +53 -0
  44. package/lib/CreateSnapshot.d.ts.map +1 -0
  45. package/lib/CreateSnapshot.js +3 -0
  46. package/lib/CreateSnapshot.js.map +1 -0
  47. package/lib/CreateSnapshotHttp.d.ts +20 -0
  48. package/lib/CreateSnapshotHttp.d.ts.map +1 -0
  49. package/lib/CreateSnapshotHttp.js +41 -0
  50. package/lib/CreateSnapshotHttp.js.map +1 -0
  51. package/lib/CreateTemplate.d.ts +96 -0
  52. package/lib/CreateTemplate.d.ts.map +1 -0
  53. package/lib/CreateTemplate.js +3 -0
  54. package/lib/CreateTemplate.js.map +1 -0
  55. package/lib/CreateTemplateHttp.d.ts +20 -0
  56. package/lib/CreateTemplateHttp.d.ts.map +1 -0
  57. package/lib/CreateTemplateHttp.js +99 -0
  58. package/lib/CreateTemplateHttp.js.map +1 -0
  59. package/lib/CreateVolume.d.ts +58 -0
  60. package/lib/CreateVolume.d.ts.map +1 -0
  61. package/lib/CreateVolume.js +3 -0
  62. package/lib/CreateVolume.js.map +1 -0
  63. package/lib/CreateVolumeHttp.d.ts +20 -0
  64. package/lib/CreateVolumeHttp.d.ts.map +1 -0
  65. package/lib/CreateVolumeHttp.js +51 -0
  66. package/lib/CreateVolumeHttp.js.map +1 -0
  67. package/lib/Credentials.d.ts +18 -0
  68. package/lib/Credentials.d.ts.map +1 -0
  69. package/lib/Credentials.js +55 -0
  70. package/lib/Credentials.js.map +1 -0
  71. package/lib/E2BBindings.d.ts +35 -0
  72. package/lib/E2BBindings.d.ts.map +1 -0
  73. package/lib/E2BBindings.js +127 -0
  74. package/lib/E2BBindings.js.map +1 -0
  75. package/lib/Errors.d.ts +61 -0
  76. package/lib/Errors.d.ts.map +1 -0
  77. package/lib/Errors.js +23 -0
  78. package/lib/Errors.js.map +1 -0
  79. package/lib/InferEnv.d.ts +26 -0
  80. package/lib/InferEnv.d.ts.map +1 -0
  81. package/lib/InferEnv.js +1 -0
  82. package/lib/InferEnv.js.map +1 -0
  83. package/lib/MountVolume.d.ts +86 -0
  84. package/lib/MountVolume.d.ts.map +1 -0
  85. package/lib/MountVolume.js +3 -0
  86. package/lib/MountVolume.js.map +1 -0
  87. package/lib/MountVolumeLive.d.ts +23 -0
  88. package/lib/MountVolumeLive.d.ts.map +1 -0
  89. package/lib/MountVolumeLive.js +41 -0
  90. package/lib/MountVolumeLive.js.map +1 -0
  91. package/lib/Providers.d.ts +14 -0
  92. package/lib/Providers.d.ts.map +1 -0
  93. package/lib/Providers.js +23 -0
  94. package/lib/Providers.js.map +1 -0
  95. package/lib/ReadTeam.d.ts +41 -0
  96. package/lib/ReadTeam.d.ts.map +1 -0
  97. package/lib/ReadTeam.js +3 -0
  98. package/lib/ReadTeam.js.map +1 -0
  99. package/lib/ReadTeamHttp.d.ts +20 -0
  100. package/lib/ReadTeamHttp.d.ts.map +1 -0
  101. package/lib/ReadTeamHttp.js +40 -0
  102. package/lib/ReadTeamHttp.js.map +1 -0
  103. package/lib/ReadVolume.d.ts +25 -0
  104. package/lib/ReadVolume.d.ts.map +1 -0
  105. package/lib/ReadVolume.js +3 -0
  106. package/lib/ReadVolume.js.map +1 -0
  107. package/lib/ReadVolumeHttp.d.ts +20 -0
  108. package/lib/ReadVolumeHttp.d.ts.map +1 -0
  109. package/lib/ReadVolumeHttp.js +27 -0
  110. package/lib/ReadVolumeHttp.js.map +1 -0
  111. package/lib/ReadWebhook.d.ts +32 -0
  112. package/lib/ReadWebhook.d.ts.map +1 -0
  113. package/lib/ReadWebhook.js +3 -0
  114. package/lib/ReadWebhook.js.map +1 -0
  115. package/lib/ReadWebhookHttp.d.ts +20 -0
  116. package/lib/ReadWebhookHttp.d.ts.map +1 -0
  117. package/lib/ReadWebhookHttp.js +31 -0
  118. package/lib/ReadWebhookHttp.js.map +1 -0
  119. package/lib/ReadWriteVolume.d.ts +26 -0
  120. package/lib/ReadWriteVolume.d.ts.map +1 -0
  121. package/lib/ReadWriteVolume.js +3 -0
  122. package/lib/ReadWriteVolume.js.map +1 -0
  123. package/lib/ReadWriteVolumeHttp.d.ts +20 -0
  124. package/lib/ReadWriteVolumeHttp.d.ts.map +1 -0
  125. package/lib/ReadWriteVolumeHttp.js +27 -0
  126. package/lib/ReadWriteVolumeHttp.js.map +1 -0
  127. package/lib/Sandbox.d.ts +295 -0
  128. package/lib/Sandbox.d.ts.map +1 -0
  129. package/lib/Sandbox.js +351 -0
  130. package/lib/Sandbox.js.map +1 -0
  131. package/lib/SandboxClient.d.ts +226 -0
  132. package/lib/SandboxClient.d.ts.map +1 -0
  133. package/lib/SandboxClient.js +1 -0
  134. package/lib/SandboxClient.js.map +1 -0
  135. package/lib/SandboxEvents.d.ts +87 -0
  136. package/lib/SandboxEvents.d.ts.map +1 -0
  137. package/lib/SandboxEvents.js +39 -0
  138. package/lib/SandboxEvents.js.map +1 -0
  139. package/lib/SandboxEventsLive.d.ts +13 -0
  140. package/lib/SandboxEventsLive.d.ts.map +1 -0
  141. package/lib/SandboxEventsLive.js +80 -0
  142. package/lib/SandboxEventsLive.js.map +1 -0
  143. package/lib/Secret.d.ts +105 -0
  144. package/lib/Secret.d.ts.map +1 -0
  145. package/lib/Secret.js +161 -0
  146. package/lib/Secret.js.map +1 -0
  147. package/lib/Snapshot.d.ts +118 -0
  148. package/lib/Snapshot.d.ts.map +1 -0
  149. package/lib/Snapshot.js +158 -0
  150. package/lib/Snapshot.js.map +1 -0
  151. package/lib/Template.d.ts +313 -0
  152. package/lib/Template.d.ts.map +1 -0
  153. package/lib/Template.js +421 -0
  154. package/lib/Template.js.map +1 -0
  155. package/lib/Volume.d.ts +88 -0
  156. package/lib/Volume.d.ts.map +1 -0
  157. package/lib/Volume.js +136 -0
  158. package/lib/Volume.js.map +1 -0
  159. package/lib/VolumeFiles.d.ts +39 -0
  160. package/lib/VolumeFiles.d.ts.map +1 -0
  161. package/lib/VolumeFiles.js +1 -0
  162. package/lib/VolumeFiles.js.map +1 -0
  163. package/lib/Webhook.d.ts +83 -0
  164. package/lib/Webhook.d.ts.map +1 -0
  165. package/lib/Webhook.js +138 -0
  166. package/lib/Webhook.js.map +1 -0
  167. package/lib/WriteSecret.d.ts +34 -0
  168. package/lib/WriteSecret.d.ts.map +1 -0
  169. package/lib/WriteSecret.js +3 -0
  170. package/lib/WriteSecret.js.map +1 -0
  171. package/lib/WriteSecretHttp.d.ts +20 -0
  172. package/lib/WriteSecretHttp.d.ts.map +1 -0
  173. package/lib/WriteSecretHttp.js +37 -0
  174. package/lib/WriteSecretHttp.js.map +1 -0
  175. package/lib/WriteVolume.d.ts +25 -0
  176. package/lib/WriteVolume.d.ts.map +1 -0
  177. package/lib/WriteVolume.js +3 -0
  178. package/lib/WriteVolume.js.map +1 -0
  179. package/lib/WriteVolumeHttp.d.ts +20 -0
  180. package/lib/WriteVolumeHttp.d.ts.map +1 -0
  181. package/lib/WriteVolumeHttp.js +27 -0
  182. package/lib/WriteVolumeHttp.js.map +1 -0
  183. package/lib/index.d.ts +43 -0
  184. package/lib/index.d.ts.map +1 -0
  185. package/lib/index.js +43 -0
  186. package/lib/index.js.map +1 -0
  187. package/lib/internal/Declared.d.ts +29 -0
  188. package/lib/internal/Declared.d.ts.map +1 -0
  189. package/lib/internal/Declared.js +31 -0
  190. package/lib/internal/Declared.js.map +1 -0
  191. package/lib/internal/Login.d.ts +45 -0
  192. package/lib/internal/Login.d.ts.map +1 -0
  193. package/lib/internal/Login.js +157 -0
  194. package/lib/internal/Login.js.map +1 -0
  195. package/lib/internal/Ownership.d.ts +29 -0
  196. package/lib/internal/Ownership.d.ts.map +1 -0
  197. package/lib/internal/Ownership.js +49 -0
  198. package/lib/internal/Ownership.js.map +1 -0
  199. package/lib/internal/Program.d.ts +31 -0
  200. package/lib/internal/Program.d.ts.map +1 -0
  201. package/lib/internal/Program.js +60 -0
  202. package/lib/internal/Program.js.map +1 -0
  203. package/lib/internal/SandboxHandle.d.ts +25 -0
  204. package/lib/internal/SandboxHandle.d.ts.map +1 -0
  205. package/lib/internal/SandboxHandle.js +316 -0
  206. package/lib/internal/SandboxHandle.js.map +1 -0
  207. package/lib/internal/SnapshotBuild.d.ts +19 -0
  208. package/lib/internal/SnapshotBuild.d.ts.map +1 -0
  209. package/lib/internal/SnapshotBuild.js +49 -0
  210. package/lib/internal/SnapshotBuild.js.map +1 -0
  211. package/lib/internal/Tar.d.ts +10 -0
  212. package/lib/internal/Tar.d.ts.map +1 -0
  213. package/lib/internal/Tar.js +61 -0
  214. package/lib/internal/Tar.js.map +1 -0
  215. package/lib/internal/TemplateBuild.d.ts +142 -0
  216. package/lib/internal/TemplateBuild.d.ts.map +1 -0
  217. package/lib/internal/TemplateBuild.js +220 -0
  218. package/lib/internal/TemplateBuild.js.map +1 -0
  219. package/lib/internal/TemplatePlan.d.ts +57 -0
  220. package/lib/internal/TemplatePlan.d.ts.map +1 -0
  221. package/lib/internal/TemplatePlan.js +185 -0
  222. package/lib/internal/TemplatePlan.js.map +1 -0
  223. package/lib/internal/VolumeHttp.d.ts +5 -0
  224. package/lib/internal/VolumeHttp.d.ts.map +1 -0
  225. package/lib/internal/VolumeHttp.js +46 -0
  226. package/lib/internal/VolumeHttp.js.map +1 -0
  227. package/package.json +62 -3
  228. package/src/ApiKey.ts +148 -0
  229. package/src/AuthProvider.ts +437 -0
  230. package/src/Bootstrap.ts +51 -0
  231. package/src/ConnectSandbox.ts +71 -0
  232. package/src/ConnectSandboxHttp.ts +38 -0
  233. package/src/Contributions.ts +9 -0
  234. package/src/CreateSandbox.ts +157 -0
  235. package/src/CreateSandboxHttp.ts +131 -0
  236. package/src/CreateSecret.ts +65 -0
  237. package/src/CreateSecretHttp.ts +94 -0
  238. package/src/CreateSnapshot.ts +64 -0
  239. package/src/CreateSnapshotHttp.ts +55 -0
  240. package/src/CreateTemplate.ts +135 -0
  241. package/src/CreateTemplateHttp.ts +152 -0
  242. package/src/CreateVolume.ts +65 -0
  243. package/src/CreateVolumeHttp.ts +80 -0
  244. package/src/Credentials.ts +81 -0
  245. package/src/E2BBindings.ts +160 -0
  246. package/src/Errors.ts +47 -0
  247. package/src/InferEnv.ts +27 -0
  248. package/src/MountVolume.ts +92 -0
  249. package/src/MountVolumeLive.ts +49 -0
  250. package/src/Providers.ts +48 -0
  251. package/src/ReadTeam.ts +55 -0
  252. package/src/ReadTeamHttp.ts +44 -0
  253. package/src/ReadVolume.ts +29 -0
  254. package/src/ReadVolumeHttp.ts +32 -0
  255. package/src/ReadWebhook.ts +41 -0
  256. package/src/ReadWebhookHttp.ts +42 -0
  257. package/src/ReadWriteVolume.ts +30 -0
  258. package/src/ReadWriteVolumeHttp.ts +32 -0
  259. package/src/Sandbox.ts +581 -0
  260. package/src/SandboxClient.ts +268 -0
  261. package/src/SandboxEvents.ts +101 -0
  262. package/src/SandboxEventsLive.ts +118 -0
  263. package/src/Secret.ts +229 -0
  264. package/src/Snapshot.ts +234 -0
  265. package/src/Template.ts +636 -0
  266. package/src/Volume.ts +173 -0
  267. package/src/VolumeFiles.ts +53 -0
  268. package/src/Webhook.ts +215 -0
  269. package/src/WriteSecret.ts +39 -0
  270. package/src/WriteSecretHttp.ts +43 -0
  271. package/src/WriteVolume.ts +29 -0
  272. package/src/WriteVolumeHttp.ts +32 -0
  273. package/src/globals.d.ts +2 -0
  274. package/src/index.ts +42 -0
  275. package/src/internal/Declared.ts +80 -0
  276. package/src/internal/Login.ts +210 -0
  277. package/src/internal/Ownership.ts +67 -0
  278. package/src/internal/Program.ts +75 -0
  279. package/src/internal/SandboxHandle.ts +534 -0
  280. package/src/internal/SnapshotBuild.ts +74 -0
  281. package/src/internal/Tar.ts +74 -0
  282. package/src/internal/TemplateBuild.ts +383 -0
  283. package/src/internal/TemplatePlan.ts +230 -0
  284. package/src/internal/VolumeHttp.ts +88 -0
@@ -0,0 +1,268 @@
1
+ import type * as E2B from "@distilled.cloud/e2b";
2
+ import type * as envd from "@distilled.cloud/e2b/envd";
3
+ import type * as filesystem from "@distilled.cloud/e2b/filesystem";
4
+ import type * as process from "@distilled.cloud/e2b/process";
5
+ import type * as sandboxes from "@distilled.cloud/e2b/sandboxes";
6
+ import type * as templates from "@distilled.cloud/e2b/templates";
7
+ import type * as volumes from "@distilled.cloud/e2b/volumes";
8
+ import type { RuntimeContext } from "alchemy/RuntimeContext";
9
+ import type * as Sandbox from "alchemy/Sandbox";
10
+ import type * as Duration from "effect/Duration";
11
+ import type * as Effect from "effect/Effect";
12
+ import type * as Scope from "effect/Scope";
13
+ import type * as Stream from "effect/Stream";
14
+ import type { E2BUnexpectedResponse, ExecTimedOut, SandboxBaseMissing } from "./Errors.ts";
15
+
16
+ /** Errors an E2B call can fail with. */
17
+ export type E2BError =
18
+ | E2B.E2BOpError
19
+ | templates.TemplateInUse
20
+ | volumes.VolumesNotEnabled
21
+ | process.ConnectStreamError
22
+ | process.SandboxNotRunning
23
+ | filesystem.SandboxNotRunning
24
+ | envd.SandboxNotRunning
25
+ | SandboxBaseMissing
26
+ | ExecTimedOut
27
+ | E2BUnexpectedResponse;
28
+
29
+ /** A process inside a sandbox, by pid or by the tag it was started with. */
30
+ export type ProcessSelector = process.ConnectProcessRequest["process"];
31
+
32
+ /** An event from a watched directory. */
33
+ export type WatchEvent = filesystem.WatchDirEvent;
34
+
35
+ /** A file or directory inside a sandbox. */
36
+ export type EntryInfo = filesystem.EntryInfo;
37
+
38
+ /** A running process inside a sandbox, as `processes.list()` reports it. */
39
+ export type ProcessInfo = process.ProcessInfo;
40
+
41
+ /**
42
+ * A command: a shell command line (run with `bash -l -c`), or an argument
43
+ * vector run directly.
44
+ */
45
+ export type Command = Sandbox.Command;
46
+
47
+ /**
48
+ * `cwd` defaults to the sandbox user's home; `timeout` kills the command and
49
+ * fails with `ExecTimedOut`.
50
+ */
51
+ export type ExecOptions = Sandbox.ExecOptions;
52
+
53
+ /** What a finished command printed, and its exit code. A non-zero exit code is returned, not failed. */
54
+ export interface ExecResult extends Sandbox.ExecResult {
55
+ readonly pid: number;
56
+ /** envd's error for a process that could not run (e.g. the executable is missing). */
57
+ readonly error: string | undefined;
58
+ }
59
+
60
+ export interface StartOptions {
61
+ /** Working directory. @default the sandbox user's home */
62
+ readonly cwd?: string;
63
+ /** Environment variables for the command. */
64
+ readonly env?: Readonly<Record<string, string>>;
65
+ /** A tag to find the process by with `processes.attach({ tag })`. */
66
+ readonly tag?: string;
67
+ /** Keep stdin open for `write`. @default false */
68
+ readonly stdin?: boolean;
69
+ }
70
+
71
+ /** A chunk of a process's output. */
72
+ export type ProcessOutput = Sandbox.ProcessOutput;
73
+
74
+ /**
75
+ * A process started (or attached to) in the current scope. Closing the
76
+ * scope detaches: the process keeps running until it exits or is killed.
77
+ * `id` is its pid as a string.
78
+ */
79
+ export interface Process extends Sandbox.Process<E2BError> {
80
+ readonly pid: number;
81
+ /** stdout and stderr, from now until the process exits. Read it once. */
82
+ readonly output: Stream.Stream<ProcessOutput, E2BError>;
83
+ /** Writes to stdin (the process must have been started with `stdin: true`). */
84
+ write(data: string | Uint8Array): Effect.Effect<void, E2BError, RuntimeContext>;
85
+ /** Closes stdin. */
86
+ closeStdin(): Effect.Effect<void, E2BError, RuntimeContext>;
87
+ /** Signals the process. @default "SIGKILL" */
88
+ kill(signal?: "SIGTERM" | "SIGKILL"): Effect.Effect<void, E2BError, RuntimeContext>;
89
+ /** Waits for the process to exit, with everything it printed since it was started or attached. */
90
+ readonly wait: Effect.Effect<ExecResult, E2BError>;
91
+ }
92
+
93
+ /** The processes running in the sandbox. */
94
+ export interface Processes {
95
+ list(): Effect.Effect<ReadonlyArray<ProcessInfo>, E2BError, RuntimeContext>;
96
+ /** Re-attaches to a running process, by pid or tag; output streams from now on. */
97
+ attach(process: ProcessSelector): Effect.Effect<Process, E2BError, RuntimeContext | Scope.Scope>;
98
+ }
99
+
100
+ export interface PtyOptions extends Sandbox.PtyOptions {
101
+ /** A tag to find the terminal's process by. */
102
+ readonly tag?: string;
103
+ }
104
+
105
+ /** A pseudo-terminal opened in the current scope. Closing the scope detaches. `id` is its pid as a string. */
106
+ export interface PtyConnection extends Sandbox.PtyConnection<E2BError> {
107
+ readonly pid: number;
108
+ /** The terminal's output. Read it once. */
109
+ readonly output: Stream.Stream<Uint8Array, E2BError>;
110
+ /** Types into the terminal. */
111
+ write(data: string | Uint8Array): Effect.Effect<void, E2BError, RuntimeContext>;
112
+ resize(size: {
113
+ readonly cols: number;
114
+ readonly rows: number;
115
+ }): Effect.Effect<void, E2BError, RuntimeContext>;
116
+ /** Kills the terminal's process. */
117
+ kill(): Effect.Effect<void, E2BError, RuntimeContext>;
118
+ /** Waits for the terminal's process to exit. */
119
+ readonly wait: Effect.Effect<{ readonly exitCode: number }, E2BError>;
120
+ }
121
+
122
+ /** Pseudo-terminals inside the sandbox. */
123
+ export interface Pty extends Sandbox.Pty<E2BError, PtyOptions> {
124
+ open(options: PtyOptions): Effect.Effect<PtyConnection, E2BError, RuntimeContext | Scope.Scope>;
125
+ }
126
+
127
+ /** The sandbox's filesystem, through envd. Relative paths resolve in the user's home. */
128
+ export interface Files extends Sandbox.Files<E2BError, EntryInfo> {
129
+ read(path: string): Effect.Effect<Uint8Array, E2BError, RuntimeContext>;
130
+ readText(path: string): Effect.Effect<string, E2BError, RuntimeContext>;
131
+ /** Writes a file, creating parent directories. */
132
+ write(path: string, data: string | Uint8Array): Effect.Effect<void, E2BError, RuntimeContext>;
133
+ list(
134
+ path: string,
135
+ options?: { readonly depth?: number },
136
+ ): Effect.Effect<ReadonlyArray<EntryInfo>, E2BError, RuntimeContext>;
137
+ stat(path: string): Effect.Effect<EntryInfo, E2BError, RuntimeContext>;
138
+ remove(path: string): Effect.Effect<void, E2BError, RuntimeContext>;
139
+ /** Creates the directory and its parents. */
140
+ makeDir(path: string): Effect.Effect<void, E2BError, RuntimeContext>;
141
+ exists(path: string): Effect.Effect<boolean, E2BError, RuntimeContext>;
142
+ move(source: string, destination: string): Effect.Effect<EntryInfo, E2BError, RuntimeContext>;
143
+ /** Concatenates files into `destination`. */
144
+ compose(
145
+ sources: ReadonlyArray<string>,
146
+ destination: string,
147
+ ): Effect.Effect<void, E2BError, RuntimeContext>;
148
+ /** Streams changes under a directory until interrupted. */
149
+ watch(
150
+ path: string,
151
+ options?: { readonly recursive?: boolean; readonly includeEntry?: boolean },
152
+ ): Stream.Stream<WatchEvent, E2BError, RuntimeContext>;
153
+ }
154
+
155
+ /** One fork's outcome: the new sandbox, or why it could not start. */
156
+ export type ForkResult =
157
+ | { readonly _tag: "Forked"; readonly sandbox: E2BSandbox }
158
+ | { readonly _tag: "ForkFailed"; readonly code: number | undefined; readonly message: string };
159
+
160
+ /** Network rules for a running sandbox (`updateSandboxNetwork`), in E2B's shape. */
161
+ export type SandboxNetworkUpdate = Omit<sandboxes.UpdateSandboxNetworkRequest, "sandboxID">;
162
+
163
+ /**
164
+ * A port inside an E2B sandbox, served at `https://<port>-<sandboxId>.<domain>`.
165
+ * `fetch` sends a client request to it, or proxies a server request there.
166
+ */
167
+ export interface Port extends Sandbox.SandboxPort<E2BError> {
168
+ /** The host E2B serves the port on: `<port>-<sandboxId>.<domain>`. */
169
+ readonly host: string;
170
+ /** `https://<host>`. Public unless the sandbox requires its traffic token. */
171
+ readonly url: string;
172
+ }
173
+
174
+ /**
175
+ * What you can do with a sandbox, however you got it. Calls that reach
176
+ * inside a paused sandbox (`exec`, `start`, `files`, `pty`) fail with
177
+ * `SandboxNotRunning`: `resume()` it first. A sandbox created with
178
+ * `autoResume` is the exception: E2B resumes it on any request.
179
+ */
180
+ export interface SandboxOperations
181
+ extends
182
+ Sandbox.SandboxExec<E2BError, ExecResult, ExecOptions>,
183
+ Sandbox.SandboxProcesses<E2BError, StartOptions>,
184
+ Sandbox.SandboxFiles<E2BError, EntryInfo>,
185
+ Sandbox.SandboxPty<E2BError, PtyOptions>,
186
+ Sandbox.SandboxPorts<E2BError, Port> {
187
+ /** Runs a command to completion. */
188
+ exec(
189
+ command: Command,
190
+ options?: ExecOptions,
191
+ ): Effect.Effect<ExecResult, E2BError, RuntimeContext>;
192
+ /** Starts a command in the current scope. */
193
+ start(
194
+ command: Command,
195
+ options?: StartOptions,
196
+ ): Effect.Effect<Process, E2BError, RuntimeContext | Scope.Scope>;
197
+ readonly processes: Processes;
198
+ readonly files: Files;
199
+ readonly pty: Pty;
200
+ /**
201
+ * A port inside the sandbox. Its `fetch` adds the sandbox's traffic token
202
+ * when the sandbox's URLs are not public.
203
+ */
204
+ getTcpPort(port: number): Effect.Effect<Port, E2BError, RuntimeContext>;
205
+ /** Live details: state, resources, network, metadata. */
206
+ describe(): Effect.Effect<sandboxes.SandboxDetail, E2BError, RuntimeContext>;
207
+ /** Pauses the sandbox, keeping its memory and files. */
208
+ pause(): Effect.Effect<void, E2BError, RuntimeContext>;
209
+ /**
210
+ * Resumes a paused sandbox (a no-op on a running one). `timeout` sets how
211
+ * long it runs before it is paused or killed again.
212
+ */
213
+ resume(options?: {
214
+ readonly timeout?: Duration.Input;
215
+ }): Effect.Effect<void, E2BError, RuntimeContext>;
216
+ /** Kills the sandbox. Killing one that is gone succeeds. */
217
+ kill(): Effect.Effect<void, E2BError, RuntimeContext>;
218
+ /** Sets the time left before the sandbox is killed (or paused, with `autoPause`). */
219
+ setTimeout(timeout: Duration.Input): Effect.Effect<void, E2BError, RuntimeContext>;
220
+ /** Extends the sandbox's lifetime by `duration`. */
221
+ refresh(duration?: Duration.Input): Effect.Effect<void, E2BError, RuntimeContext>;
222
+ /** Replaces the sandbox's egress rules (E2B's `updateSandboxNetwork`). */
223
+ updateNetwork(network: SandboxNetworkUpdate): Effect.Effect<void, E2BError, RuntimeContext>;
224
+ /**
225
+ * Snapshots the sandbox into a named template (memory and disk by
226
+ * default). Sandboxes created from it start where this one is now.
227
+ */
228
+ snapshot(options?: {
229
+ readonly name?: string;
230
+ readonly memory?: boolean;
231
+ }): Effect.Effect<sandboxes.SnapshotInfo, E2BError, RuntimeContext>;
232
+ /**
233
+ * Checkpoints the sandbox (it keeps running) and starts `count` copies of
234
+ * it. Each fork succeeds or fails on its own.
235
+ */
236
+ fork(options?: {
237
+ readonly count?: number;
238
+ readonly timeout?: Duration.Input;
239
+ }): Effect.Effect<ReadonlyArray<ForkResult>, E2BError, RuntimeContext>;
240
+ /** CPU, memory and disk metrics. */
241
+ metrics(): Effect.Effect<ReadonlyArray<sandboxes.SandboxMetric>, E2BError, RuntimeContext>;
242
+ /** The sandbox's logs. */
243
+ logs(
244
+ options?: Omit<sandboxes.ListSandboxLogsRequest, "sandboxID">,
245
+ ): Effect.Effect<sandboxes.SandboxLogsV2Response, E2BError, RuntimeContext>;
246
+ }
247
+
248
+ /**
249
+ * A sandbox created or connected at runtime (`CreateSandbox`). Holds only
250
+ * ids and tokens, so it can be rebuilt from its id with `connect(id)` in
251
+ * any request.
252
+ */
253
+ export interface E2BSandbox extends SandboxOperations {
254
+ readonly sandboxId: string;
255
+ readonly templateId: string;
256
+ /** The domain the sandbox's ports are served under (`<port>-<sandboxId>.<domain>`). */
257
+ readonly domain: string;
258
+ /** What E2B returned for the sandbox when it was created or connected. */
259
+ readonly info: sandboxes.Sandbox;
260
+ }
261
+
262
+ /**
263
+ * A sandbox declared in the stack (`ConnectSandbox`). The same operations
264
+ * as {@link E2BSandbox}; its id is read at runtime.
265
+ */
266
+ export interface ConnectedSandbox extends SandboxOperations {
267
+ readonly sandboxId: Effect.Effect<string, never, RuntimeContext>;
268
+ }
@@ -0,0 +1,101 @@
1
+ import { sanitizeKey } from "alchemy/RuntimeContext";
2
+ import * as Context from "effect/Context";
3
+ import type * as Effect from "effect/Effect";
4
+ import type * as Redacted from "effect/Redacted";
5
+ import type { SandboxEventType } from "./Webhook.ts";
6
+
7
+ /** A sandbox lifecycle event, as E2B delivers it to a webhook. */
8
+ export interface SandboxEvent {
9
+ /** The event's id. A redelivered event keeps it: deduplicate on it. */
10
+ readonly id: string;
11
+ readonly version: string;
12
+ readonly type: SandboxEventType;
13
+ readonly timestamp: string;
14
+ readonly sandbox_id: string;
15
+ readonly sandbox_execution_id: string;
16
+ readonly sandbox_template_id: string;
17
+ readonly sandbox_build_id: string;
18
+ readonly sandbox_team_id: string;
19
+ readonly event_data?: {
20
+ /** The sandbox's metadata, including the ownership stamp of the host that created it. */
21
+ readonly sandbox_metadata?: Readonly<Record<string, string>>;
22
+ /** On `killed` and `paused`: what the run used. */
23
+ readonly execution?: Readonly<Record<string, unknown>>;
24
+ /** On `killed`. */
25
+ readonly kill_reason?: string;
26
+ /** On `paused`: `full` (memory and disk) or `filesystem`. */
27
+ readonly pause_mode?: "full" | "filesystem";
28
+ /** On `resumed`: `restore` (memory) or `reboot`. */
29
+ readonly resume_mode?: "restore" | "reboot";
30
+ };
31
+ }
32
+
33
+ export interface SandboxEventsProps {
34
+ /**
35
+ * The events to receive.
36
+ * @default every sandbox lifecycle event
37
+ */
38
+ readonly events?: ReadonlyArray<SandboxEventType>;
39
+ /**
40
+ * The secret E2B signs deliveries with. A random one is generated and
41
+ * bound onto the host when omitted.
42
+ */
43
+ readonly secret?: Redacted.Redacted<string>;
44
+ /**
45
+ * The path on the host E2B delivers to.
46
+ * @default "/__alchemy/e2b/events"
47
+ */
48
+ readonly path?: string;
49
+ }
50
+
51
+ /** Receives sandbox lifecycle events on a host. */
52
+ export type SandboxEventsService = <Req = never>(
53
+ props: SandboxEventsProps,
54
+ process: (event: SandboxEvent) => Effect.Effect<void, never, Req>,
55
+ ) => Effect.Effect<void, never, never>;
56
+
57
+ /**
58
+ * Receive E2B sandbox lifecycle events (created, paused, resumed,
59
+ * checkpointed, updated, killed) in a Worker or any other webhook host.
60
+ *
61
+ * At deploy, an {@link Webhook} is created pointing at the host. At runtime,
62
+ * each delivery's `e2b-signature` is verified before the handler runs; an
63
+ * unsigned or wrongly signed request is rejected with 401. E2B retries a
64
+ * failed delivery up to three times, so handlers should be idempotent on
65
+ * `event.id`.
66
+ *
67
+ * ### Handling lifecycle events
68
+ * **Example:** Record every killed sandbox
69
+ * ```typescript
70
+ * export default class Api extends Cloudflare.Worker<Api>()(
71
+ * "Api",
72
+ * { main: import.meta.url },
73
+ * Effect.gen(function* () {
74
+ * yield* E2B.consumeSandboxEvents({ events: ["sandbox.lifecycle.killed"] }, (event) =>
75
+ * Effect.log(`sandbox ${event.sandbox_id} killed: ${event.event_data?.kill_reason}`),
76
+ * );
77
+ * return { fetch: Effect.succeed(HttpServerResponse.text("ok")) };
78
+ * }).pipe(Effect.provide(E2B.SandboxEventsLive)),
79
+ * ) {}
80
+ * ```
81
+ *
82
+ * @binding
83
+ * @product Webhook
84
+ */
85
+ export class SandboxEvents extends Context.Service<SandboxEvents, SandboxEventsService>()(
86
+ "E2B.SandboxEvents",
87
+ ) {}
88
+
89
+ /** Subscribes the current host to sandbox lifecycle events. */
90
+ export const consumeSandboxEvents = <Req = never>(
91
+ props: SandboxEventsProps,
92
+ process: (event: SandboxEvent) => Effect.Effect<void, never, Req>,
93
+ ) => SandboxEvents.use((source) => source(props, process));
94
+
95
+ /** Where E2B delivers on the host. */
96
+ export const sandboxEventsPath = (props: SandboxEventsProps): string =>
97
+ props.path ?? "/__alchemy/e2b/events";
98
+
99
+ /** The env var the signing secret is bound under. */
100
+ export const sandboxEventsSecretEnvName = (path: string): string =>
101
+ `ALCHEMY_E2B_WEBHOOK_SECRET_${sanitizeKey(path)}`;
@@ -0,0 +1,118 @@
1
+ import * as Namespace from "alchemy/Namespace";
2
+ import * as Output from "alchemy/Output";
3
+ import { Random } from "alchemy/Random";
4
+ import { WebhookHost } from "alchemy/WebhookHost";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Layer from "effect/Layer";
7
+ import * as Redacted from "effect/Redacted";
8
+ import {
9
+ type SandboxEvent,
10
+ SandboxEvents,
11
+ type SandboxEventsProps,
12
+ type SandboxEventsService,
13
+ sandboxEventsPath,
14
+ sandboxEventsSecretEnvName,
15
+ } from "./SandboxEvents.ts";
16
+ import { ALL_SANDBOX_EVENTS, Webhook } from "./Webhook.ts";
17
+
18
+ /**
19
+ * {@link SandboxEvents} on any {@link WebhookHost}: every Cloudflare Worker
20
+ * is one.
21
+ *
22
+ * @layer
23
+ * @product Webhook
24
+ * @provides E2B.SandboxEvents
25
+ */
26
+ export const SandboxEventsLive = Layer.effect(
27
+ SandboxEvents,
28
+ Effect.gen(function* () {
29
+ const host = yield* WebhookHost;
30
+ // Yielding the resource class erases its `Providers` requirement; the
31
+ // stack satisfies it at plan time.
32
+ const createWebhook = yield* Webhook;
33
+
34
+ return Effect.fn(function* (
35
+ props: SandboxEventsProps,
36
+ process: (event: SandboxEvent) => Effect.Effect<void, never, any>,
37
+ ) {
38
+ const path = sandboxEventsPath(props);
39
+ const secret =
40
+ props.secret ??
41
+ (yield* Namespace.push(host.LogicalId, Random(`E2BEventsSecret${path}`))).text;
42
+
43
+ if (!globalThis.__ALCHEMY_RUNTIME__) {
44
+ yield* Namespace.push(
45
+ host.LogicalId,
46
+ Effect.asVoid(
47
+ createWebhook(`E2BEvents${path}`, {
48
+ url: Output.interpolate`${host.url}${path}`,
49
+ events: [...(props.events ?? ALL_SANDBOX_EVENTS)],
50
+ signatureSecret: secret,
51
+ }),
52
+ ),
53
+ );
54
+ }
55
+
56
+ // Registers the secret on the host at deploy, reads it back at runtime.
57
+ const boundSecret = yield* Output.named(
58
+ Output.asOutput(secret) as Output.Output<Redacted.Redacted<string>, never>,
59
+ sandboxEventsSecretEnvName(path),
60
+ ).asEffect();
61
+
62
+ yield* host.route(path, (request) => handleDelivery(request, boundSecret, process));
63
+ }) as SandboxEventsService;
64
+ }),
65
+ );
66
+
67
+ const handleDelivery = <Req>(
68
+ request: Request,
69
+ secret: Effect.Effect<Redacted.Redacted<string> | undefined>,
70
+ process: (event: SandboxEvent) => Effect.Effect<void, never, Req>,
71
+ ): Effect.Effect<Response, never, Req> =>
72
+ Effect.gen(function* () {
73
+ if (request.method !== "POST") return new Response("method not allowed", { status: 405 });
74
+ const body = yield* Effect.tryPromise(() => request.text()).pipe(Effect.option);
75
+ if (body._tag === "None") return new Response("unreadable body", { status: 400 });
76
+ const resolved = yield* secret;
77
+ const valid = yield* verifySignature(
78
+ resolved === undefined ? undefined : Redacted.value(resolved),
79
+ body.value,
80
+ request.headers.get("e2b-signature") ?? undefined,
81
+ );
82
+ if (!valid) return new Response("invalid signature", { status: 401 });
83
+ const event = yield* Effect.try(() => JSON.parse(body.value) as SandboxEvent).pipe(
84
+ Effect.orElseSucceed(() => undefined),
85
+ );
86
+ if (event === undefined) return new Response("invalid payload", { status: 400 });
87
+ yield* process(event);
88
+ return new Response(null, { status: 202 });
89
+ });
90
+
91
+ /**
92
+ * E2B's signature: the unpadded base64 SHA-256 of the secret followed by the
93
+ * raw body (a plain hash, not an HMAC).
94
+ */
95
+ const verifySignature = (
96
+ secret: string | undefined,
97
+ body: string,
98
+ signature: string | undefined,
99
+ ): Effect.Effect<boolean> =>
100
+ Effect.gen(function* () {
101
+ if (!secret || !signature) return false;
102
+ const digest = yield* Effect.tryPromise(() =>
103
+ crypto.subtle.digest("SHA-256", new TextEncoder().encode(secret + body)),
104
+ ).pipe(Effect.option);
105
+ if (digest._tag === "None") return false;
106
+ let binary = "";
107
+ for (const byte of new Uint8Array(digest.value)) binary += String.fromCharCode(byte);
108
+ const expected = btoa(binary).replace(/=+$/, "");
109
+ return timingSafeEqual(expected, signature);
110
+ });
111
+
112
+ /** Constant-time comparison; `crypto.timingSafeEqual` needs `nodejs_compat` on Workers. */
113
+ const timingSafeEqual = (a: string, b: string): boolean => {
114
+ if (a.length !== b.length) return false;
115
+ let mismatch = 0;
116
+ for (let i = 0; i < a.length; i++) mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
117
+ return mismatch === 0;
118
+ };