@alchemy.run/e2b 0.0.0 → 2.0.0-beta.86
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.
- package/LICENSE +201 -0
- package/README.md +56 -4
- package/lib/ApiKey.d.ts +53 -0
- package/lib/ApiKey.d.ts.map +1 -0
- package/lib/ApiKey.js +107 -0
- package/lib/ApiKey.js.map +1 -0
- package/lib/AuthProvider.d.ts +60 -0
- package/lib/AuthProvider.d.ts.map +1 -0
- package/lib/AuthProvider.js +293 -0
- package/lib/AuthProvider.js.map +1 -0
- package/lib/Bootstrap.d.ts +7 -0
- package/lib/Bootstrap.d.ts.map +1 -0
- package/lib/Bootstrap.js +25 -0
- package/lib/Bootstrap.js.map +1 -0
- package/lib/ConnectSandbox.d.ts +67 -0
- package/lib/ConnectSandbox.d.ts.map +1 -0
- package/lib/ConnectSandbox.js +3 -0
- package/lib/ConnectSandbox.js.map +1 -0
- package/lib/ConnectSandboxHttp.d.ts +22 -0
- package/lib/ConnectSandboxHttp.d.ts.map +1 -0
- package/lib/ConnectSandboxHttp.js +30 -0
- package/lib/ConnectSandboxHttp.js.map +1 -0
- package/lib/Contributions.d.ts +7 -0
- package/lib/Contributions.d.ts.map +1 -0
- package/lib/Contributions.js +6 -0
- package/lib/Contributions.js.map +1 -0
- package/lib/CreateSandbox.d.ts +139 -0
- package/lib/CreateSandbox.d.ts.map +1 -0
- package/lib/CreateSandbox.js +3 -0
- package/lib/CreateSandbox.js.map +1 -0
- package/lib/CreateSandboxHttp.d.ts +22 -0
- package/lib/CreateSandboxHttp.d.ts.map +1 -0
- package/lib/CreateSandboxHttp.js +99 -0
- package/lib/CreateSandboxHttp.js.map +1 -0
- package/lib/CreateSecret.d.ts +57 -0
- package/lib/CreateSecret.d.ts.map +1 -0
- package/lib/CreateSecret.js +3 -0
- package/lib/CreateSecret.js.map +1 -0
- package/lib/CreateSecretHttp.d.ts +20 -0
- package/lib/CreateSecretHttp.d.ts.map +1 -0
- package/lib/CreateSecretHttp.js +64 -0
- package/lib/CreateSecretHttp.js.map +1 -0
- package/lib/CreateSnapshot.d.ts +53 -0
- package/lib/CreateSnapshot.d.ts.map +1 -0
- package/lib/CreateSnapshot.js +3 -0
- package/lib/CreateSnapshot.js.map +1 -0
- package/lib/CreateSnapshotHttp.d.ts +20 -0
- package/lib/CreateSnapshotHttp.d.ts.map +1 -0
- package/lib/CreateSnapshotHttp.js +41 -0
- package/lib/CreateSnapshotHttp.js.map +1 -0
- package/lib/CreateTemplate.d.ts +96 -0
- package/lib/CreateTemplate.d.ts.map +1 -0
- package/lib/CreateTemplate.js +3 -0
- package/lib/CreateTemplate.js.map +1 -0
- package/lib/CreateTemplateHttp.d.ts +20 -0
- package/lib/CreateTemplateHttp.d.ts.map +1 -0
- package/lib/CreateTemplateHttp.js +99 -0
- package/lib/CreateTemplateHttp.js.map +1 -0
- package/lib/CreateVolume.d.ts +58 -0
- package/lib/CreateVolume.d.ts.map +1 -0
- package/lib/CreateVolume.js +3 -0
- package/lib/CreateVolume.js.map +1 -0
- package/lib/CreateVolumeHttp.d.ts +20 -0
- package/lib/CreateVolumeHttp.d.ts.map +1 -0
- package/lib/CreateVolumeHttp.js +51 -0
- package/lib/CreateVolumeHttp.js.map +1 -0
- package/lib/Credentials.d.ts +18 -0
- package/lib/Credentials.d.ts.map +1 -0
- package/lib/Credentials.js +55 -0
- package/lib/Credentials.js.map +1 -0
- package/lib/E2BBindings.d.ts +35 -0
- package/lib/E2BBindings.d.ts.map +1 -0
- package/lib/E2BBindings.js +127 -0
- package/lib/E2BBindings.js.map +1 -0
- package/lib/Errors.d.ts +61 -0
- package/lib/Errors.d.ts.map +1 -0
- package/lib/Errors.js +23 -0
- package/lib/Errors.js.map +1 -0
- package/lib/InferEnv.d.ts +26 -0
- package/lib/InferEnv.d.ts.map +1 -0
- package/lib/InferEnv.js +1 -0
- package/lib/InferEnv.js.map +1 -0
- package/lib/MountVolume.d.ts +86 -0
- package/lib/MountVolume.d.ts.map +1 -0
- package/lib/MountVolume.js +3 -0
- package/lib/MountVolume.js.map +1 -0
- package/lib/MountVolumeLive.d.ts +23 -0
- package/lib/MountVolumeLive.d.ts.map +1 -0
- package/lib/MountVolumeLive.js +41 -0
- package/lib/MountVolumeLive.js.map +1 -0
- package/lib/Providers.d.ts +14 -0
- package/lib/Providers.d.ts.map +1 -0
- package/lib/Providers.js +23 -0
- package/lib/Providers.js.map +1 -0
- package/lib/ReadTeam.d.ts +41 -0
- package/lib/ReadTeam.d.ts.map +1 -0
- package/lib/ReadTeam.js +3 -0
- package/lib/ReadTeam.js.map +1 -0
- package/lib/ReadTeamHttp.d.ts +20 -0
- package/lib/ReadTeamHttp.d.ts.map +1 -0
- package/lib/ReadTeamHttp.js +40 -0
- package/lib/ReadTeamHttp.js.map +1 -0
- package/lib/ReadVolume.d.ts +25 -0
- package/lib/ReadVolume.d.ts.map +1 -0
- package/lib/ReadVolume.js +3 -0
- package/lib/ReadVolume.js.map +1 -0
- package/lib/ReadVolumeHttp.d.ts +20 -0
- package/lib/ReadVolumeHttp.d.ts.map +1 -0
- package/lib/ReadVolumeHttp.js +27 -0
- package/lib/ReadVolumeHttp.js.map +1 -0
- package/lib/ReadWebhook.d.ts +32 -0
- package/lib/ReadWebhook.d.ts.map +1 -0
- package/lib/ReadWebhook.js +3 -0
- package/lib/ReadWebhook.js.map +1 -0
- package/lib/ReadWebhookHttp.d.ts +20 -0
- package/lib/ReadWebhookHttp.d.ts.map +1 -0
- package/lib/ReadWebhookHttp.js +31 -0
- package/lib/ReadWebhookHttp.js.map +1 -0
- package/lib/ReadWriteVolume.d.ts +26 -0
- package/lib/ReadWriteVolume.d.ts.map +1 -0
- package/lib/ReadWriteVolume.js +3 -0
- package/lib/ReadWriteVolume.js.map +1 -0
- package/lib/ReadWriteVolumeHttp.d.ts +20 -0
- package/lib/ReadWriteVolumeHttp.d.ts.map +1 -0
- package/lib/ReadWriteVolumeHttp.js +27 -0
- package/lib/ReadWriteVolumeHttp.js.map +1 -0
- package/lib/Sandbox.d.ts +295 -0
- package/lib/Sandbox.d.ts.map +1 -0
- package/lib/Sandbox.js +351 -0
- package/lib/Sandbox.js.map +1 -0
- package/lib/SandboxClient.d.ts +226 -0
- package/lib/SandboxClient.d.ts.map +1 -0
- package/lib/SandboxClient.js +1 -0
- package/lib/SandboxClient.js.map +1 -0
- package/lib/SandboxEvents.d.ts +87 -0
- package/lib/SandboxEvents.d.ts.map +1 -0
- package/lib/SandboxEvents.js +39 -0
- package/lib/SandboxEvents.js.map +1 -0
- package/lib/SandboxEventsLive.d.ts +13 -0
- package/lib/SandboxEventsLive.d.ts.map +1 -0
- package/lib/SandboxEventsLive.js +80 -0
- package/lib/SandboxEventsLive.js.map +1 -0
- package/lib/Secret.d.ts +105 -0
- package/lib/Secret.d.ts.map +1 -0
- package/lib/Secret.js +161 -0
- package/lib/Secret.js.map +1 -0
- package/lib/Snapshot.d.ts +118 -0
- package/lib/Snapshot.d.ts.map +1 -0
- package/lib/Snapshot.js +158 -0
- package/lib/Snapshot.js.map +1 -0
- package/lib/Template.d.ts +313 -0
- package/lib/Template.d.ts.map +1 -0
- package/lib/Template.js +421 -0
- package/lib/Template.js.map +1 -0
- package/lib/Volume.d.ts +88 -0
- package/lib/Volume.d.ts.map +1 -0
- package/lib/Volume.js +136 -0
- package/lib/Volume.js.map +1 -0
- package/lib/VolumeFiles.d.ts +39 -0
- package/lib/VolumeFiles.d.ts.map +1 -0
- package/lib/VolumeFiles.js +1 -0
- package/lib/VolumeFiles.js.map +1 -0
- package/lib/Webhook.d.ts +83 -0
- package/lib/Webhook.d.ts.map +1 -0
- package/lib/Webhook.js +138 -0
- package/lib/Webhook.js.map +1 -0
- package/lib/WriteSecret.d.ts +34 -0
- package/lib/WriteSecret.d.ts.map +1 -0
- package/lib/WriteSecret.js +3 -0
- package/lib/WriteSecret.js.map +1 -0
- package/lib/WriteSecretHttp.d.ts +20 -0
- package/lib/WriteSecretHttp.d.ts.map +1 -0
- package/lib/WriteSecretHttp.js +37 -0
- package/lib/WriteSecretHttp.js.map +1 -0
- package/lib/WriteVolume.d.ts +25 -0
- package/lib/WriteVolume.d.ts.map +1 -0
- package/lib/WriteVolume.js +3 -0
- package/lib/WriteVolume.js.map +1 -0
- package/lib/WriteVolumeHttp.d.ts +20 -0
- package/lib/WriteVolumeHttp.d.ts.map +1 -0
- package/lib/WriteVolumeHttp.js +27 -0
- package/lib/WriteVolumeHttp.js.map +1 -0
- package/lib/index.d.ts +43 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +43 -0
- package/lib/index.js.map +1 -0
- package/lib/internal/Declared.d.ts +29 -0
- package/lib/internal/Declared.d.ts.map +1 -0
- package/lib/internal/Declared.js +31 -0
- package/lib/internal/Declared.js.map +1 -0
- package/lib/internal/Login.d.ts +45 -0
- package/lib/internal/Login.d.ts.map +1 -0
- package/lib/internal/Login.js +157 -0
- package/lib/internal/Login.js.map +1 -0
- package/lib/internal/Ownership.d.ts +29 -0
- package/lib/internal/Ownership.d.ts.map +1 -0
- package/lib/internal/Ownership.js +49 -0
- package/lib/internal/Ownership.js.map +1 -0
- package/lib/internal/Program.d.ts +31 -0
- package/lib/internal/Program.d.ts.map +1 -0
- package/lib/internal/Program.js +60 -0
- package/lib/internal/Program.js.map +1 -0
- package/lib/internal/SandboxHandle.d.ts +25 -0
- package/lib/internal/SandboxHandle.d.ts.map +1 -0
- package/lib/internal/SandboxHandle.js +316 -0
- package/lib/internal/SandboxHandle.js.map +1 -0
- package/lib/internal/SnapshotBuild.d.ts +19 -0
- package/lib/internal/SnapshotBuild.d.ts.map +1 -0
- package/lib/internal/SnapshotBuild.js +49 -0
- package/lib/internal/SnapshotBuild.js.map +1 -0
- package/lib/internal/Tar.d.ts +10 -0
- package/lib/internal/Tar.d.ts.map +1 -0
- package/lib/internal/Tar.js +61 -0
- package/lib/internal/Tar.js.map +1 -0
- package/lib/internal/TemplateBuild.d.ts +142 -0
- package/lib/internal/TemplateBuild.d.ts.map +1 -0
- package/lib/internal/TemplateBuild.js +220 -0
- package/lib/internal/TemplateBuild.js.map +1 -0
- package/lib/internal/TemplatePlan.d.ts +57 -0
- package/lib/internal/TemplatePlan.d.ts.map +1 -0
- package/lib/internal/TemplatePlan.js +185 -0
- package/lib/internal/TemplatePlan.js.map +1 -0
- package/lib/internal/VolumeHttp.d.ts +5 -0
- package/lib/internal/VolumeHttp.d.ts.map +1 -0
- package/lib/internal/VolumeHttp.js +46 -0
- package/lib/internal/VolumeHttp.js.map +1 -0
- package/package.json +62 -3
- package/src/ApiKey.ts +148 -0
- package/src/AuthProvider.ts +437 -0
- package/src/Bootstrap.ts +51 -0
- package/src/ConnectSandbox.ts +71 -0
- package/src/ConnectSandboxHttp.ts +38 -0
- package/src/Contributions.ts +9 -0
- package/src/CreateSandbox.ts +157 -0
- package/src/CreateSandboxHttp.ts +131 -0
- package/src/CreateSecret.ts +65 -0
- package/src/CreateSecretHttp.ts +94 -0
- package/src/CreateSnapshot.ts +64 -0
- package/src/CreateSnapshotHttp.ts +55 -0
- package/src/CreateTemplate.ts +135 -0
- package/src/CreateTemplateHttp.ts +152 -0
- package/src/CreateVolume.ts +65 -0
- package/src/CreateVolumeHttp.ts +80 -0
- package/src/Credentials.ts +81 -0
- package/src/E2BBindings.ts +160 -0
- package/src/Errors.ts +47 -0
- package/src/InferEnv.ts +27 -0
- package/src/MountVolume.ts +92 -0
- package/src/MountVolumeLive.ts +49 -0
- package/src/Providers.ts +48 -0
- package/src/ReadTeam.ts +55 -0
- package/src/ReadTeamHttp.ts +44 -0
- package/src/ReadVolume.ts +29 -0
- package/src/ReadVolumeHttp.ts +32 -0
- package/src/ReadWebhook.ts +41 -0
- package/src/ReadWebhookHttp.ts +42 -0
- package/src/ReadWriteVolume.ts +30 -0
- package/src/ReadWriteVolumeHttp.ts +32 -0
- package/src/Sandbox.ts +581 -0
- package/src/SandboxClient.ts +268 -0
- package/src/SandboxEvents.ts +101 -0
- package/src/SandboxEventsLive.ts +118 -0
- package/src/Secret.ts +229 -0
- package/src/Snapshot.ts +234 -0
- package/src/Template.ts +636 -0
- package/src/Volume.ts +173 -0
- package/src/VolumeFiles.ts +53 -0
- package/src/Webhook.ts +215 -0
- package/src/WriteSecret.ts +39 -0
- package/src/WriteSecretHttp.ts +43 -0
- package/src/WriteVolume.ts +29 -0
- package/src/WriteVolumeHttp.ts +32 -0
- package/src/globals.d.ts +2 -0
- package/src/index.ts +42 -0
- package/src/internal/Declared.ts +80 -0
- package/src/internal/Login.ts +210 -0
- package/src/internal/Ownership.ts +67 -0
- package/src/internal/Program.ts +75 -0
- package/src/internal/SandboxHandle.ts +534 -0
- package/src/internal/SnapshotBuild.ts +74 -0
- package/src/internal/Tar.ts +74 -0
- package/src/internal/TemplateBuild.ts +383 -0
- package/src/internal/TemplatePlan.ts +230 -0
- 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
|
+
};
|