@alchemy.run/e2b 0.0.0-stage → 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 +60 -2
- 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 +78 -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,87 @@
|
|
|
1
|
+
import * as Context from "effect/Context";
|
|
2
|
+
import type * as Effect from "effect/Effect";
|
|
3
|
+
import type * as Redacted from "effect/Redacted";
|
|
4
|
+
import type { SandboxEventType } from "./Webhook.ts";
|
|
5
|
+
/** A sandbox lifecycle event, as E2B delivers it to a webhook. */
|
|
6
|
+
export interface SandboxEvent {
|
|
7
|
+
/** The event's id. A redelivered event keeps it: deduplicate on it. */
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly version: string;
|
|
10
|
+
readonly type: SandboxEventType;
|
|
11
|
+
readonly timestamp: string;
|
|
12
|
+
readonly sandbox_id: string;
|
|
13
|
+
readonly sandbox_execution_id: string;
|
|
14
|
+
readonly sandbox_template_id: string;
|
|
15
|
+
readonly sandbox_build_id: string;
|
|
16
|
+
readonly sandbox_team_id: string;
|
|
17
|
+
readonly event_data?: {
|
|
18
|
+
/** The sandbox's metadata, including the ownership stamp of the host that created it. */
|
|
19
|
+
readonly sandbox_metadata?: Readonly<Record<string, string>>;
|
|
20
|
+
/** On `killed` and `paused`: what the run used. */
|
|
21
|
+
readonly execution?: Readonly<Record<string, unknown>>;
|
|
22
|
+
/** On `killed`. */
|
|
23
|
+
readonly kill_reason?: string;
|
|
24
|
+
/** On `paused`: `full` (memory and disk) or `filesystem`. */
|
|
25
|
+
readonly pause_mode?: "full" | "filesystem";
|
|
26
|
+
/** On `resumed`: `restore` (memory) or `reboot`. */
|
|
27
|
+
readonly resume_mode?: "restore" | "reboot";
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface SandboxEventsProps {
|
|
31
|
+
/**
|
|
32
|
+
* The events to receive.
|
|
33
|
+
* @default every sandbox lifecycle event
|
|
34
|
+
*/
|
|
35
|
+
readonly events?: ReadonlyArray<SandboxEventType>;
|
|
36
|
+
/**
|
|
37
|
+
* The secret E2B signs deliveries with. A random one is generated and
|
|
38
|
+
* bound onto the host when omitted.
|
|
39
|
+
*/
|
|
40
|
+
readonly secret?: Redacted.Redacted<string>;
|
|
41
|
+
/**
|
|
42
|
+
* The path on the host E2B delivers to.
|
|
43
|
+
* @default "/__alchemy/e2b/events"
|
|
44
|
+
*/
|
|
45
|
+
readonly path?: string;
|
|
46
|
+
}
|
|
47
|
+
/** Receives sandbox lifecycle events on a host. */
|
|
48
|
+
export type SandboxEventsService = <Req = never>(props: SandboxEventsProps, process: (event: SandboxEvent) => Effect.Effect<void, never, Req>) => Effect.Effect<void, never, never>;
|
|
49
|
+
declare const SandboxEvents_base: Context.ServiceClass<SandboxEvents, "E2B.SandboxEvents", SandboxEventsService>;
|
|
50
|
+
/**
|
|
51
|
+
* Receive E2B sandbox lifecycle events (created, paused, resumed,
|
|
52
|
+
* checkpointed, updated, killed) in a Worker or any other webhook host.
|
|
53
|
+
*
|
|
54
|
+
* At deploy, an {@link Webhook} is created pointing at the host. At runtime,
|
|
55
|
+
* each delivery's `e2b-signature` is verified before the handler runs; an
|
|
56
|
+
* unsigned or wrongly signed request is rejected with 401. E2B retries a
|
|
57
|
+
* failed delivery up to three times, so handlers should be idempotent on
|
|
58
|
+
* `event.id`.
|
|
59
|
+
*
|
|
60
|
+
* ### Handling lifecycle events
|
|
61
|
+
* **Example:** Record every killed sandbox
|
|
62
|
+
* ```typescript
|
|
63
|
+
* export default class Api extends Cloudflare.Worker<Api>()(
|
|
64
|
+
* "Api",
|
|
65
|
+
* { main: import.meta.url },
|
|
66
|
+
* Effect.gen(function* () {
|
|
67
|
+
* yield* E2B.consumeSandboxEvents({ events: ["sandbox.lifecycle.killed"] }, (event) =>
|
|
68
|
+
* Effect.log(`sandbox ${event.sandbox_id} killed: ${event.event_data?.kill_reason}`),
|
|
69
|
+
* );
|
|
70
|
+
* return { fetch: Effect.succeed(HttpServerResponse.text("ok")) };
|
|
71
|
+
* }).pipe(Effect.provide(E2B.SandboxEventsLive)),
|
|
72
|
+
* ) {}
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* @binding
|
|
76
|
+
* @product Webhook
|
|
77
|
+
*/
|
|
78
|
+
export declare class SandboxEvents extends SandboxEvents_base {
|
|
79
|
+
}
|
|
80
|
+
/** Subscribes the current host to sandbox lifecycle events. */
|
|
81
|
+
export declare const consumeSandboxEvents: <Req = never>(props: SandboxEventsProps, process: (event: SandboxEvent) => Effect.Effect<void, never, Req>) => Effect.Effect<void, never, SandboxEvents>;
|
|
82
|
+
/** Where E2B delivers on the host. */
|
|
83
|
+
export declare const sandboxEventsPath: (props: SandboxEventsProps) => string;
|
|
84
|
+
/** The env var the signing secret is bound under. */
|
|
85
|
+
export declare const sandboxEventsSecretEnvName: (path: string) => string;
|
|
86
|
+
export {};
|
|
87
|
+
//# sourceMappingURL=SandboxEvents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SandboxEvents.d.ts","sources":["../src/SandboxEvents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE;QACpB,yFAAyF;QACzF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7D,mDAAmD;QACnD,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACvD,mBAAmB;QACnB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAC9B,6DAA6D;QAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;QAC5C,oDAAoD;QACpD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;KAC7C,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAClD;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5C;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,mDAAmD;AACnD,MAAM,MAAM,oBAAoB,GAAG,CAAC,GAAG,GAAG,KAAK,EAC7C,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAC9D,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,aAAc,SAAQ,kBAElC;CAAG;AAEJ,+DAA+D;AAC/D,eAAO,MAAM,oBAAoB,GAAI,GAAG,GAAG,KAAK,SACvC,kBAAkB,WAChB,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,8CACT,CAAC;AAE3D,sCAAsC;AACtC,eAAO,MAAM,iBAAiB,UAAW,kBAAkB,KAAG,MACvB,CAAC;AAExC,qDAAqD;AACrD,eAAO,MAAM,0BAA0B,SAAU,MAAM,KAAG,MACP,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { sanitizeKey } from "alchemy/RuntimeContext";
|
|
2
|
+
import * as Context from "effect/Context";
|
|
3
|
+
/**
|
|
4
|
+
* Receive E2B sandbox lifecycle events (created, paused, resumed,
|
|
5
|
+
* checkpointed, updated, killed) in a Worker or any other webhook host.
|
|
6
|
+
*
|
|
7
|
+
* At deploy, an {@link Webhook} is created pointing at the host. At runtime,
|
|
8
|
+
* each delivery's `e2b-signature` is verified before the handler runs; an
|
|
9
|
+
* unsigned or wrongly signed request is rejected with 401. E2B retries a
|
|
10
|
+
* failed delivery up to three times, so handlers should be idempotent on
|
|
11
|
+
* `event.id`.
|
|
12
|
+
*
|
|
13
|
+
* ### Handling lifecycle events
|
|
14
|
+
* **Example:** Record every killed sandbox
|
|
15
|
+
* ```typescript
|
|
16
|
+
* export default class Api extends Cloudflare.Worker<Api>()(
|
|
17
|
+
* "Api",
|
|
18
|
+
* { main: import.meta.url },
|
|
19
|
+
* Effect.gen(function* () {
|
|
20
|
+
* yield* E2B.consumeSandboxEvents({ events: ["sandbox.lifecycle.killed"] }, (event) =>
|
|
21
|
+
* Effect.log(`sandbox ${event.sandbox_id} killed: ${event.event_data?.kill_reason}`),
|
|
22
|
+
* );
|
|
23
|
+
* return { fetch: Effect.succeed(HttpServerResponse.text("ok")) };
|
|
24
|
+
* }).pipe(Effect.provide(E2B.SandboxEventsLive)),
|
|
25
|
+
* ) {}
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @binding
|
|
29
|
+
* @product Webhook
|
|
30
|
+
*/
|
|
31
|
+
export class SandboxEvents extends Context.Service()("E2B.SandboxEvents") {
|
|
32
|
+
}
|
|
33
|
+
/** Subscribes the current host to sandbox lifecycle events. */
|
|
34
|
+
export const consumeSandboxEvents = (props, process) => SandboxEvents.use((source) => source(props, process));
|
|
35
|
+
/** Where E2B delivers on the host. */
|
|
36
|
+
export const sandboxEventsPath = (props) => props.path ?? "/__alchemy/e2b/events";
|
|
37
|
+
/** The env var the signing secret is bound under. */
|
|
38
|
+
export const sandboxEventsSecretEnvName = (path) => `ALCHEMY_E2B_WEBHOOK_SECRET_${sanitizeKey(path)}`;
|
|
39
|
+
//# sourceMappingURL=SandboxEvents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SandboxEvents.js","sources":["../src/SandboxEvents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAuD1C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,aAAc,SAAQ,OAAO,CAAC,OAAO,EAAuC,CACvF,mBAAmB,CACpB;CAAG;AAEJ,+DAA+D;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,KAAyB,EACzB,OAAiE,EACjE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAE3D,sCAAsC;AACtC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAyB,EAAU,EAAE,CACrE,KAAK,CAAC,IAAI,IAAI,uBAAuB,CAAC;AAExC,qDAAqD;AACrD,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,IAAY,EAAU,EAAE,CACjE,8BAA8B,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { WebhookHost } from "alchemy/WebhookHost";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import { SandboxEvents } from "./SandboxEvents.ts";
|
|
4
|
+
/**
|
|
5
|
+
* {@link SandboxEvents} on any {@link WebhookHost}: every Cloudflare Worker
|
|
6
|
+
* is one.
|
|
7
|
+
*
|
|
8
|
+
* @layer
|
|
9
|
+
* @product Webhook
|
|
10
|
+
* @provides E2B.SandboxEvents
|
|
11
|
+
*/
|
|
12
|
+
export declare const SandboxEventsLive: Layer.Layer<SandboxEvents, never, WebhookHost>;
|
|
13
|
+
//# sourceMappingURL=SandboxEventsLive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SandboxEventsLive.d.ts","sources":["../src/SandboxEventsLive.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,EAEL,aAAa,EAKd,MAAM,oBAAoB,CAAC;AAG5B;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,gDAuC7B,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
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 { SandboxEvents, sandboxEventsPath, sandboxEventsSecretEnvName, } from "./SandboxEvents.js";
|
|
9
|
+
import { ALL_SANDBOX_EVENTS, Webhook } from "./Webhook.js";
|
|
10
|
+
/**
|
|
11
|
+
* {@link SandboxEvents} on any {@link WebhookHost}: every Cloudflare Worker
|
|
12
|
+
* is one.
|
|
13
|
+
*
|
|
14
|
+
* @layer
|
|
15
|
+
* @product Webhook
|
|
16
|
+
* @provides E2B.SandboxEvents
|
|
17
|
+
*/
|
|
18
|
+
export const SandboxEventsLive = Layer.effect(SandboxEvents, Effect.gen(function* () {
|
|
19
|
+
const host = yield* WebhookHost;
|
|
20
|
+
// Yielding the resource class erases its `Providers` requirement; the
|
|
21
|
+
// stack satisfies it at plan time.
|
|
22
|
+
const createWebhook = yield* Webhook;
|
|
23
|
+
return Effect.fn(function* (props, process) {
|
|
24
|
+
const path = sandboxEventsPath(props);
|
|
25
|
+
const secret = props.secret ??
|
|
26
|
+
(yield* Namespace.push(host.LogicalId, Random(`E2BEventsSecret${path}`))).text;
|
|
27
|
+
if (!globalThis.__ALCHEMY_RUNTIME__) {
|
|
28
|
+
yield* Namespace.push(host.LogicalId, Effect.asVoid(createWebhook(`E2BEvents${path}`, {
|
|
29
|
+
url: Output.interpolate `${host.url}${path}`,
|
|
30
|
+
events: [...(props.events ?? ALL_SANDBOX_EVENTS)],
|
|
31
|
+
signatureSecret: secret,
|
|
32
|
+
})));
|
|
33
|
+
}
|
|
34
|
+
// Registers the secret on the host at deploy, reads it back at runtime.
|
|
35
|
+
const boundSecret = yield* Output.named(Output.asOutput(secret), sandboxEventsSecretEnvName(path)).asEffect();
|
|
36
|
+
yield* host.route(path, (request) => handleDelivery(request, boundSecret, process));
|
|
37
|
+
});
|
|
38
|
+
}));
|
|
39
|
+
const handleDelivery = (request, secret, process) => Effect.gen(function* () {
|
|
40
|
+
if (request.method !== "POST")
|
|
41
|
+
return new Response("method not allowed", { status: 405 });
|
|
42
|
+
const body = yield* Effect.tryPromise(() => request.text()).pipe(Effect.option);
|
|
43
|
+
if (body._tag === "None")
|
|
44
|
+
return new Response("unreadable body", { status: 400 });
|
|
45
|
+
const resolved = yield* secret;
|
|
46
|
+
const valid = yield* verifySignature(resolved === undefined ? undefined : Redacted.value(resolved), body.value, request.headers.get("e2b-signature") ?? undefined);
|
|
47
|
+
if (!valid)
|
|
48
|
+
return new Response("invalid signature", { status: 401 });
|
|
49
|
+
const event = yield* Effect.try(() => JSON.parse(body.value)).pipe(Effect.orElseSucceed(() => undefined));
|
|
50
|
+
if (event === undefined)
|
|
51
|
+
return new Response("invalid payload", { status: 400 });
|
|
52
|
+
yield* process(event);
|
|
53
|
+
return new Response(null, { status: 202 });
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* E2B's signature: the unpadded base64 SHA-256 of the secret followed by the
|
|
57
|
+
* raw body (a plain hash, not an HMAC).
|
|
58
|
+
*/
|
|
59
|
+
const verifySignature = (secret, body, signature) => Effect.gen(function* () {
|
|
60
|
+
if (!secret || !signature)
|
|
61
|
+
return false;
|
|
62
|
+
const digest = yield* Effect.tryPromise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(secret + body))).pipe(Effect.option);
|
|
63
|
+
if (digest._tag === "None")
|
|
64
|
+
return false;
|
|
65
|
+
let binary = "";
|
|
66
|
+
for (const byte of new Uint8Array(digest.value))
|
|
67
|
+
binary += String.fromCharCode(byte);
|
|
68
|
+
const expected = btoa(binary).replace(/=+$/, "");
|
|
69
|
+
return timingSafeEqual(expected, signature);
|
|
70
|
+
});
|
|
71
|
+
/** Constant-time comparison; `crypto.timingSafeEqual` needs `nodejs_compat` on Workers. */
|
|
72
|
+
const timingSafeEqual = (a, b) => {
|
|
73
|
+
if (a.length !== b.length)
|
|
74
|
+
return false;
|
|
75
|
+
let mismatch = 0;
|
|
76
|
+
for (let i = 0; i < a.length; i++)
|
|
77
|
+
mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
78
|
+
return mismatch === 0;
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=SandboxEventsLive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SandboxEventsLive.js","sources":["../src/SandboxEventsLive.ts"],"names":[],"mappings":"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"}
|
package/lib/Secret.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as secrets from "@distilled.cloud/e2b/secrets";
|
|
2
|
+
import * as Provider from "alchemy/Provider";
|
|
3
|
+
import { Resource } from "alchemy/Resource";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Redacted from "effect/Redacted";
|
|
6
|
+
import type { Providers } from "./Providers.ts";
|
|
7
|
+
export interface SecretProps {
|
|
8
|
+
/**
|
|
9
|
+
* Secret name, unique within the team. E2B lowercases it. If omitted, a
|
|
10
|
+
* unique name is generated from the stack, stage and logical ID. Changing
|
|
11
|
+
* it replaces the secret.
|
|
12
|
+
*/
|
|
13
|
+
name?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The secret value. Write-only: E2B never returns it. Changing it stores a
|
|
16
|
+
* new version, which every sandbox resolves on its next request.
|
|
17
|
+
*/
|
|
18
|
+
value: Redacted.Redacted<string>;
|
|
19
|
+
/** Your own metadata (at most 32 entries). */
|
|
20
|
+
metadata?: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
export interface SecretAttributes {
|
|
23
|
+
/** E2B's secret id (`sec_…`). */
|
|
24
|
+
secretId: string;
|
|
25
|
+
/** The secret's canonical (lowercased) name. */
|
|
26
|
+
name: string;
|
|
27
|
+
/** The version sandboxes resolve. */
|
|
28
|
+
version: number;
|
|
29
|
+
/** Your metadata, without Alchemy's ownership stamp. */
|
|
30
|
+
metadata: Record<string, string>;
|
|
31
|
+
/**
|
|
32
|
+
* The placeholder E2B resolves to the secret's value at sandbox egress:
|
|
33
|
+
* `${e2b.secrets.<name>}`. Put it in a sandbox's network header rules; the
|
|
34
|
+
* value itself never enters the sandbox.
|
|
35
|
+
*/
|
|
36
|
+
ref: string;
|
|
37
|
+
}
|
|
38
|
+
export interface Secret extends Resource<"E2B.Secret", SecretProps, SecretAttributes, never, Providers, {
|
|
39
|
+
name: string;
|
|
40
|
+
}> {
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* An E2B secret: a write-only value that E2B injects into a sandbox's
|
|
44
|
+
* outbound requests, so code inside the sandbox never sees it.
|
|
45
|
+
*
|
|
46
|
+
* Reference a secret in a sandbox's network rules with its `ref`
|
|
47
|
+
* placeholder (`${e2b.secrets.<name>}`). E2B resolves the placeholder at
|
|
48
|
+
* egress, on the host, for requests to the domains you list.
|
|
49
|
+
*
|
|
50
|
+
* Alchemy stamps its stack, stage and logical ID into the secret's
|
|
51
|
+
* metadata to recognise the secrets it owns.
|
|
52
|
+
*
|
|
53
|
+
* ### Creating a Secret
|
|
54
|
+
* **Example:** A GitHub token from the stage's config
|
|
55
|
+
* ```typescript
|
|
56
|
+
* export const GitHubToken = E2B.Secret("GitHubToken", {
|
|
57
|
+
* value: Config.Redacted("GITHUB_TOKEN"),
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* ### Injecting a Secret at egress
|
|
62
|
+
* **Example:** Authenticate GitHub API calls from inside a sandbox
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const github = yield* GitHubToken;
|
|
65
|
+
* const sandbox = yield* sandboxes.create({
|
|
66
|
+
* network: {
|
|
67
|
+
* allowOut: ["api.github.com"],
|
|
68
|
+
* rules: {
|
|
69
|
+
* "api.github.com": [{ transform: { headers: { Authorization: `Bearer ${github.ref}` } } }],
|
|
70
|
+
* },
|
|
71
|
+
* },
|
|
72
|
+
* });
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* ### Rotating a Secret
|
|
76
|
+
* Changing `value` stores a new version in place. Sandboxes resolve the new
|
|
77
|
+
* version on their next request.
|
|
78
|
+
*
|
|
79
|
+
* **Example:** Rotate at runtime with `WriteSecret`
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const token = yield* E2B.WriteSecret(GitHubToken);
|
|
82
|
+
* yield* token.put(Redacted.make(freshToken));
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* ### Referencing a secret the stack doesn't manage
|
|
86
|
+
* `E2B.Secret.lookup` finds an existing secret by name. Nothing is created,
|
|
87
|
+
* adopted or deleted.
|
|
88
|
+
*
|
|
89
|
+
* **Example:** Look one up by name
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const github = E2B.Secret.lookup({ name: "github-token" });
|
|
92
|
+
* const token = yield* E2B.WriteSecret(github);
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* @resource
|
|
96
|
+
* @product Secret
|
|
97
|
+
*/
|
|
98
|
+
export declare const Secret: import("alchemy/Resource").ResourceClass<Secret>;
|
|
99
|
+
/** E2B's egress placeholder for a secret name. */
|
|
100
|
+
export declare const secretRef: (name: string) => string;
|
|
101
|
+
export declare const toSecretAttributes: (secret: secrets.Secret) => SecretAttributes;
|
|
102
|
+
/** A secret by id or canonical name (E2B accepts either). */
|
|
103
|
+
export declare const observeSecret: (idOrName: string) => Effect.Effect<secrets.Secret | undefined, import("@distilled.cloud/e2b").BadRequest | import("@distilled.cloud/e2b").ConfigError | import("@distilled.cloud/e2b").Conflict | import("@distilled.cloud/e2b").Forbidden | import("effect/http/HttpClientError").HttpClientError | import("@distilled.cloud/core/protocol-http").InvalidInput | import("@distilled.cloud/e2b").Locked | import("@distilled.cloud/core/sdk-error").SdkError | import("@distilled.cloud/e2b").UnprocessableEntity | import("@distilled.cloud/e2b").ClientErrors | import("@distilled.cloud/core/errors").DefaultErrors, secrets.E2BOpContext>;
|
|
104
|
+
export declare const SecretProvider: () => import("effect/Layer").Layer<Provider.Provider<Secret>, never, import("alchemy/Stack").Stack | import("alchemy/Stage").Stage | secrets.E2BOpContext>;
|
|
105
|
+
//# sourceMappingURL=Secret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Secret.d.ts","sources":["../src/Secret.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,8BAA8B,CAAC;AAGxD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAU5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjC,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,MAAO,SAAQ,QAAQ,CACtC,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,KAAK,EACL,SAAS,EACT;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CACjB;CAAG;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,eAAO,MAAM,MAAM,kDAAiC,CAAC;AAErD,kDAAkD;AAClD,eAAO,MAAM,SAAS,SAAU,MAAM,KAAG,MAAmC,CAAC;AAE7E,eAAO,MAAM,kBAAkB,WAAY,OAAO,CAAC,MAAM,KAAG,gBAM1D,CAAC;AAEH,6DAA6D;AAC7D,eAAO,MAAM,aAAa,aAAc,MAAM,6lBAI3C,CAAC;AAQJ,eAAO,MAAM,cAAc,4JAsFxB,CAAC"}
|
package/lib/Secret.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as secrets from "@distilled.cloud/e2b/secrets";
|
|
2
|
+
import { Unowned } from "alchemy/AdoptPolicy";
|
|
3
|
+
import { isResolved } from "alchemy/Diff";
|
|
4
|
+
import * as Provider from "alchemy/Provider";
|
|
5
|
+
import { Resource } from "alchemy/Resource";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Redacted from "effect/Redacted";
|
|
8
|
+
import * as Stream from "effect/Stream";
|
|
9
|
+
import { E2BUnexpectedResponse } from "./Errors.js";
|
|
10
|
+
import { e2bName, hasAnyAlchemyMetadata, hasOwnershipMetadata, ownershipMetadata, userMetadata, } from "./internal/Ownership.js";
|
|
11
|
+
/**
|
|
12
|
+
* An E2B secret: a write-only value that E2B injects into a sandbox's
|
|
13
|
+
* outbound requests, so code inside the sandbox never sees it.
|
|
14
|
+
*
|
|
15
|
+
* Reference a secret in a sandbox's network rules with its `ref`
|
|
16
|
+
* placeholder (`${e2b.secrets.<name>}`). E2B resolves the placeholder at
|
|
17
|
+
* egress, on the host, for requests to the domains you list.
|
|
18
|
+
*
|
|
19
|
+
* Alchemy stamps its stack, stage and logical ID into the secret's
|
|
20
|
+
* metadata to recognise the secrets it owns.
|
|
21
|
+
*
|
|
22
|
+
* ### Creating a Secret
|
|
23
|
+
* **Example:** A GitHub token from the stage's config
|
|
24
|
+
* ```typescript
|
|
25
|
+
* export const GitHubToken = E2B.Secret("GitHubToken", {
|
|
26
|
+
* value: Config.Redacted("GITHUB_TOKEN"),
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* ### Injecting a Secret at egress
|
|
31
|
+
* **Example:** Authenticate GitHub API calls from inside a sandbox
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const github = yield* GitHubToken;
|
|
34
|
+
* const sandbox = yield* sandboxes.create({
|
|
35
|
+
* network: {
|
|
36
|
+
* allowOut: ["api.github.com"],
|
|
37
|
+
* rules: {
|
|
38
|
+
* "api.github.com": [{ transform: { headers: { Authorization: `Bearer ${github.ref}` } } }],
|
|
39
|
+
* },
|
|
40
|
+
* },
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* ### Rotating a Secret
|
|
45
|
+
* Changing `value` stores a new version in place. Sandboxes resolve the new
|
|
46
|
+
* version on their next request.
|
|
47
|
+
*
|
|
48
|
+
* **Example:** Rotate at runtime with `WriteSecret`
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const token = yield* E2B.WriteSecret(GitHubToken);
|
|
51
|
+
* yield* token.put(Redacted.make(freshToken));
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* ### Referencing a secret the stack doesn't manage
|
|
55
|
+
* `E2B.Secret.lookup` finds an existing secret by name. Nothing is created,
|
|
56
|
+
* adopted or deleted.
|
|
57
|
+
*
|
|
58
|
+
* **Example:** Look one up by name
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const github = E2B.Secret.lookup({ name: "github-token" });
|
|
61
|
+
* const token = yield* E2B.WriteSecret(github);
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @resource
|
|
65
|
+
* @product Secret
|
|
66
|
+
*/
|
|
67
|
+
export const Secret = Resource("E2B.Secret");
|
|
68
|
+
/** E2B's egress placeholder for a secret name. */
|
|
69
|
+
export const secretRef = (name) => `\${e2b.secrets.${name}}`;
|
|
70
|
+
export const toSecretAttributes = (secret) => ({
|
|
71
|
+
secretId: secret.secretID,
|
|
72
|
+
name: secret.name,
|
|
73
|
+
version: secret.currentVersion,
|
|
74
|
+
metadata: userMetadata(secret.metadata),
|
|
75
|
+
ref: secretRef(secret.name),
|
|
76
|
+
});
|
|
77
|
+
/** A secret by id or canonical name (E2B accepts either). */
|
|
78
|
+
export const observeSecret = (idOrName) => secrets.getSecret({ secretID: idOrName }).pipe(Effect.map((secret) => secret), Effect.catchTag("NotFound", () => Effect.undefined));
|
|
79
|
+
const sameMetadata = (a, b) => {
|
|
80
|
+
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
81
|
+
for (const key of keys)
|
|
82
|
+
if (a[key] !== b[key])
|
|
83
|
+
return false;
|
|
84
|
+
return true;
|
|
85
|
+
};
|
|
86
|
+
export const SecretProvider = () => Provider.effect(Secret, Effect.gen(function* () {
|
|
87
|
+
return {
|
|
88
|
+
stables: ["secretId", "name", "ref"],
|
|
89
|
+
diff: Effect.fn(function* ({ id, olds, news, output }) {
|
|
90
|
+
if (!isResolved(news))
|
|
91
|
+
return undefined;
|
|
92
|
+
if (news.name === undefined && olds?.name === undefined)
|
|
93
|
+
return undefined;
|
|
94
|
+
const name = output?.name ?? (yield* e2bName(id, olds?.name));
|
|
95
|
+
if (news.name?.toLowerCase() !== name)
|
|
96
|
+
return { action: "replace" };
|
|
97
|
+
return undefined;
|
|
98
|
+
}),
|
|
99
|
+
lookup: Effect.fn(function* ({ name }) {
|
|
100
|
+
const secret = yield* observeSecret(name.toLowerCase());
|
|
101
|
+
return secret === undefined ? undefined : toSecretAttributes(secret);
|
|
102
|
+
}),
|
|
103
|
+
read: Effect.fn(function* ({ id, olds, output }) {
|
|
104
|
+
const name = output?.name ?? (yield* e2bName(id, olds?.name));
|
|
105
|
+
const secret = yield* observeSecret(output?.secretId ?? name);
|
|
106
|
+
if (secret === undefined)
|
|
107
|
+
return undefined;
|
|
108
|
+
const attrs = toSecretAttributes(secret);
|
|
109
|
+
const ours = output !== undefined ||
|
|
110
|
+
(yield* hasOwnershipMetadata(id, secret.metadata));
|
|
111
|
+
return ours ? attrs : Unowned(attrs);
|
|
112
|
+
}),
|
|
113
|
+
reconcile: Effect.fn(function* ({ id, news, olds, output, session }) {
|
|
114
|
+
const name = output?.name ?? (yield* e2bName(id, news.name)).toLowerCase();
|
|
115
|
+
const metadata = { ...news.metadata, ...(yield* ownershipMetadata(id)) };
|
|
116
|
+
// Observe
|
|
117
|
+
let secret = yield* observeSecret(output?.secretId ?? name);
|
|
118
|
+
// Ensure — a Conflict is a peer creating the same name: read it back
|
|
119
|
+
// and fall through to the sync, which writes our value.
|
|
120
|
+
if (secret === undefined) {
|
|
121
|
+
yield* session.note(`Creating secret ${name}...`);
|
|
122
|
+
const created = yield* secrets.createSecret({ name, value: news.value, metadata }).pipe(Effect.map((s) => s), Effect.catchTag("Conflict", () => Effect.undefined));
|
|
123
|
+
if (created !== undefined)
|
|
124
|
+
return toSecretAttributes(created);
|
|
125
|
+
secret = yield* observeSecret(name);
|
|
126
|
+
if (secret === undefined) {
|
|
127
|
+
return yield* new E2BUnexpectedResponse({
|
|
128
|
+
message: `E2B secret ${name} vanished after create`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// A secret someone else created under this name: write our value.
|
|
132
|
+
olds = undefined;
|
|
133
|
+
}
|
|
134
|
+
// Sync — values are write-only, so `olds` is the only signal that the
|
|
135
|
+
// stored value already matches. Without it (adoption, a lost write),
|
|
136
|
+
// store ours. Metadata is observed.
|
|
137
|
+
const valueChanged = olds === undefined || Redacted.value(olds.value) !== Redacted.value(news.value);
|
|
138
|
+
const metadataChanged = !sameMetadata(metadata, secret.metadata);
|
|
139
|
+
if (valueChanged || metadataChanged) {
|
|
140
|
+
secret = yield* secrets.updateSecret({
|
|
141
|
+
secretID: secret.secretID,
|
|
142
|
+
value: news.value,
|
|
143
|
+
metadata,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return toSecretAttributes(secret);
|
|
147
|
+
}),
|
|
148
|
+
delete: Effect.fn(function* ({ output }) {
|
|
149
|
+
yield* secrets
|
|
150
|
+
.deleteSecret({ secretID: output.secretId })
|
|
151
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.void));
|
|
152
|
+
}),
|
|
153
|
+
list: Effect.fn(function* () {
|
|
154
|
+
const all = Array.from(yield* Stream.runCollect(secrets.listSecrets.items({ limit: 100 })));
|
|
155
|
+
return all
|
|
156
|
+
.filter((secret) => hasAnyAlchemyMetadata(secret.metadata))
|
|
157
|
+
.map(toSecretAttributes);
|
|
158
|
+
}),
|
|
159
|
+
};
|
|
160
|
+
}));
|
|
161
|
+
//# sourceMappingURL=Secret.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Secret.js","sources":["../src/Secret.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import * as templates from "@distilled.cloud/e2b/templates";
|
|
2
|
+
import * as Provider from "alchemy/Provider";
|
|
3
|
+
import { Resource } from "alchemy/Resource";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import type { Providers } from "./Providers.ts";
|
|
6
|
+
import type { SandboxTemplate } from "./Sandbox.ts";
|
|
7
|
+
export interface SnapshotProps {
|
|
8
|
+
/**
|
|
9
|
+
* The `E2B.Template` (or `E2B.Snapshot`) the snapshot starts from.
|
|
10
|
+
* Rebuilding it takes the snapshot again.
|
|
11
|
+
*/
|
|
12
|
+
template: SandboxTemplate;
|
|
13
|
+
/**
|
|
14
|
+
* Shell commands run in order (`bash -l -c`) before the snapshot is taken:
|
|
15
|
+
* clone a repository, install dependencies, warm a cache. A non-zero exit
|
|
16
|
+
* fails the deploy with `SnapshotSetupFailed`.
|
|
17
|
+
*/
|
|
18
|
+
run: string[];
|
|
19
|
+
/** Environment variables for the setup commands. */
|
|
20
|
+
envs?: Record<string, string>;
|
|
21
|
+
/** Working directory for the setup commands. @default the sandbox user's home */
|
|
22
|
+
cwd?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Capture memory as well as the filesystem, so sandboxes resume with the
|
|
25
|
+
* setup's processes still running. `false` captures the filesystem only
|
|
26
|
+
* and sandboxes cold-boot from it.
|
|
27
|
+
* @default true
|
|
28
|
+
*/
|
|
29
|
+
memory?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Snapshot name. If omitted, a unique name is generated from the stack,
|
|
32
|
+
* stage and logical ID. Changing it replaces the snapshot.
|
|
33
|
+
*/
|
|
34
|
+
name?: string;
|
|
35
|
+
/**
|
|
36
|
+
* How long the setup sandbox may run, in seconds.
|
|
37
|
+
* @default 900
|
|
38
|
+
*/
|
|
39
|
+
timeout?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface SnapshotAttributes {
|
|
42
|
+
/** The id sandboxes are created from, e.g. `my-team/my-snapshot:default`. */
|
|
43
|
+
snapshotId: string;
|
|
44
|
+
/** The snapshot's name. */
|
|
45
|
+
name: string;
|
|
46
|
+
/** E2B's template id for the snapshot (a snapshot is stored as a template). */
|
|
47
|
+
templateId: string;
|
|
48
|
+
/** Every name the snapshot answers to. */
|
|
49
|
+
names: string[];
|
|
50
|
+
/** Content hash of the base and setup. A deploy re-snapshots only when it changes. */
|
|
51
|
+
setupHash: string;
|
|
52
|
+
}
|
|
53
|
+
export interface Snapshot extends Resource<"E2B.Snapshot", SnapshotProps, SnapshotAttributes, never, Providers, {
|
|
54
|
+
name: string;
|
|
55
|
+
}> {
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A warm base to fork sandboxes from: a template booted, set up with your
|
|
59
|
+
* commands, and snapshotted — memory included, so sandboxes created from it
|
|
60
|
+
* start exactly where the setup left off.
|
|
61
|
+
*
|
|
62
|
+
* Use a snapshot for work that is too slow to repeat per sandbox and does
|
|
63
|
+
* not belong in an image: cloning a repository, installing its
|
|
64
|
+
* dependencies, starting a language server. A deploy re-runs the setup only
|
|
65
|
+
* when the template or the commands change. E2B stores a snapshot as a
|
|
66
|
+
* template, so it is created from with `E2B.CreateSandbox(snapshot)` and
|
|
67
|
+
* deleted with its template.
|
|
68
|
+
*
|
|
69
|
+
* ### Creating a Snapshot
|
|
70
|
+
* **Example:** A repository with its dependencies installed
|
|
71
|
+
* ```typescript
|
|
72
|
+
* export const Agent = E2B.Template("Agent", { image: "node:22-bookworm" });
|
|
73
|
+
*
|
|
74
|
+
* export const Warm = E2B.Snapshot("Warm", {
|
|
75
|
+
* template: Agent,
|
|
76
|
+
* run: [
|
|
77
|
+
* "git clone https://github.com/alchemy-run/alchemy /home/user/alchemy",
|
|
78
|
+
* "cd /home/user/alchemy && pnpm install",
|
|
79
|
+
* ],
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* ### Forking sandboxes from a Snapshot
|
|
84
|
+
* **Example:** Every sandbox starts from the warm base
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const sandboxes = yield* E2B.CreateSandbox(Warm);
|
|
87
|
+
* const sandbox = yield* sandboxes.create({ timeout: 600 });
|
|
88
|
+
* yield* sandbox.exec("cd alchemy && pnpm test");
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* ### Referencing a snapshot the stack doesn't manage
|
|
92
|
+
* `E2B.Snapshot.lookup` finds an existing snapshot by name. Nothing is created,
|
|
93
|
+
* adopted or deleted.
|
|
94
|
+
*
|
|
95
|
+
* **Example:** Look one up by name
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const warm = E2B.Snapshot.lookup({ name: "warm-repo" });
|
|
98
|
+
* const sandboxes = yield* E2B.CreateSandbox(warm);
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @resource
|
|
102
|
+
* @product Snapshot
|
|
103
|
+
*/
|
|
104
|
+
export declare const Snapshot: import("alchemy/Resource").ResourceConstructor<Snapshot, Providers> & Effect.Effect<import("alchemy/Resource").ResourceConstructor<Snapshot, never>, never, never> & {
|
|
105
|
+
Self: import("alchemy/Self").Self<Snapshot>;
|
|
106
|
+
Provider: Provider.Provider<Snapshot>;
|
|
107
|
+
Aliases: readonly string[] | undefined;
|
|
108
|
+
ref(id: string, options?: {
|
|
109
|
+
stage?: string;
|
|
110
|
+
stack?: string;
|
|
111
|
+
}): Effect.Effect<Snapshot, never, never>;
|
|
112
|
+
} & {
|
|
113
|
+
lookup(props: {
|
|
114
|
+
name: import("alchemy/Input").Input<string>;
|
|
115
|
+
}): Snapshot;
|
|
116
|
+
} & ((id: string, props: import("./internal/Declared.ts").DeclaredProps<SnapshotProps>) => Effect.Effect<Snapshot, never, Providers>);
|
|
117
|
+
export declare const SnapshotProvider: () => import("effect/Layer").Layer<Provider.Provider<Snapshot>, never, import("alchemy/Stack").Stack | import("alchemy/Stage").Stage | templates.E2BOpContext>;
|
|
118
|
+
//# sourceMappingURL=Snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Snapshot.d.ts","sources":["../src/Snapshot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,SAAS,MAAM,gCAAgC,CAAC;AAG5D,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAKxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;;OAIG;IACH,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAS,SAAQ,QAAQ,CACxC,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CACjB;CAAG;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;qIAA6D,CAAC;AAyBnF,eAAO,MAAM,gBAAgB,gKA0F1B,CAAC"}
|