@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,437 @@
|
|
|
1
|
+
import * as auth from "@distilled.cloud/e2b/auth";
|
|
2
|
+
import { credentials as e2bCredentials, DEFAULT_DOMAIN } from "@distilled.cloud/e2b/Credentials";
|
|
3
|
+
import {
|
|
4
|
+
AuthError,
|
|
5
|
+
AuthProviderLayer,
|
|
6
|
+
NeedsReauth,
|
|
7
|
+
refreshHint,
|
|
8
|
+
type ConfigureField,
|
|
9
|
+
type ConfigureMethod,
|
|
10
|
+
type ProviderDetails,
|
|
11
|
+
} from "alchemy/Auth/AuthProvider";
|
|
12
|
+
import { browserOAuth } from "alchemy/Auth/BrowserOAuth";
|
|
13
|
+
import { displayRedacted } from "alchemy/Auth/Credentials";
|
|
14
|
+
import { getEnv, getEnvRedacted, mapPromptCancellation } from "alchemy/Auth/Env";
|
|
15
|
+
import { withProfileCredentialsLock } from "alchemy/Auth/Lock";
|
|
16
|
+
import {
|
|
17
|
+
collectFieldValues,
|
|
18
|
+
storedSecret,
|
|
19
|
+
storedValueText,
|
|
20
|
+
validateFieldValues,
|
|
21
|
+
type StoredValues,
|
|
22
|
+
} from "alchemy/Auth/StoredAuthProvider";
|
|
23
|
+
import * as Interaction from "alchemy/Interaction";
|
|
24
|
+
import * as Clock from "effect/Clock";
|
|
25
|
+
import * as Duration from "effect/Duration";
|
|
26
|
+
import * as Effect from "effect/Effect";
|
|
27
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
28
|
+
import * as Layer from "effect/Layer";
|
|
29
|
+
import * as Match from "effect/Match";
|
|
30
|
+
import * as Redacted from "effect/Redacted";
|
|
31
|
+
import * as Schema from "effect/Schema";
|
|
32
|
+
import {
|
|
33
|
+
grantFromUrl,
|
|
34
|
+
refreshGrant,
|
|
35
|
+
revokeGrant,
|
|
36
|
+
startLogin,
|
|
37
|
+
type E2BGrant,
|
|
38
|
+
} from "./internal/Login.ts";
|
|
39
|
+
|
|
40
|
+
export const E2B_AUTH_PROVIDER_NAME = "E2B";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A profile's E2B values: an OAuth grant from the browser login (`oauth`),
|
|
44
|
+
* or a pasted access token or API key (`stored`).
|
|
45
|
+
*/
|
|
46
|
+
export const E2BAuthConfigSchema = Schema.Union([
|
|
47
|
+
Schema.Struct({
|
|
48
|
+
method: Schema.Literal("oauth"),
|
|
49
|
+
email: Schema.String,
|
|
50
|
+
teamId: Schema.String,
|
|
51
|
+
teamName: Schema.String,
|
|
52
|
+
clientId: Schema.String,
|
|
53
|
+
tokenEndpoint: Schema.String,
|
|
54
|
+
revokeEndpoint: Schema.String,
|
|
55
|
+
access: Schema.String,
|
|
56
|
+
refresh: Schema.String,
|
|
57
|
+
expires: Schema.Number,
|
|
58
|
+
domain: Schema.optional(Schema.String),
|
|
59
|
+
}),
|
|
60
|
+
Schema.Struct({
|
|
61
|
+
method: Schema.Literal("stored"),
|
|
62
|
+
accessToken: Schema.optional(Schema.String),
|
|
63
|
+
teamId: Schema.optional(Schema.String),
|
|
64
|
+
apiKey: Schema.optional(Schema.String),
|
|
65
|
+
domain: Schema.optional(Schema.String),
|
|
66
|
+
}),
|
|
67
|
+
]);
|
|
68
|
+
export type E2BAuthConfig = typeof E2BAuthConfigSchema.Type;
|
|
69
|
+
type OAuthConfig = Extract<E2BAuthConfig, { method: "oauth" }>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolved E2B credentials.
|
|
73
|
+
*
|
|
74
|
+
* `accessToken` reaches every control-plane endpoint, and it is the only
|
|
75
|
+
* credential that can mint API keys, which bindings and `E2B.ApiKey` need.
|
|
76
|
+
* `teamId` is looked up from the token when unset. `apiKey` is a team API
|
|
77
|
+
* key: enough for resources, not for bindings. `expires` is set for an
|
|
78
|
+
* OAuth access token, so it is refreshed before it lapses.
|
|
79
|
+
*/
|
|
80
|
+
export interface E2BResolvedCredentials {
|
|
81
|
+
readonly apiKey: Redacted.Redacted<string> | undefined;
|
|
82
|
+
readonly accessToken: Redacted.Redacted<string> | undefined;
|
|
83
|
+
readonly teamId: string | undefined;
|
|
84
|
+
readonly domain: string;
|
|
85
|
+
readonly expires?: number;
|
|
86
|
+
readonly source: { readonly type: E2BAuthConfig["method"] | "env"; readonly details?: string };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const storedFields: ReadonlyArray<ConfigureField> = [
|
|
90
|
+
{
|
|
91
|
+
name: "accessToken",
|
|
92
|
+
label: "E2B access token",
|
|
93
|
+
description: "Reaches every endpoint and mints the API keys bindings need.",
|
|
94
|
+
secret: true,
|
|
95
|
+
optional: true,
|
|
96
|
+
},
|
|
97
|
+
{ name: "teamId", label: "E2B team id, if not your default team", optional: true },
|
|
98
|
+
{
|
|
99
|
+
name: "apiKey",
|
|
100
|
+
label: "E2B API key (e2b_…)",
|
|
101
|
+
description: "Only without an access token: enough for resources, not for bindings.",
|
|
102
|
+
secret: true,
|
|
103
|
+
optional: true,
|
|
104
|
+
},
|
|
105
|
+
{ name: "domain", label: "E2B domain", optional: true, placeholder: DEFAULT_DOMAIN },
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
const storedConfig = (values: StoredValues): E2BAuthConfig => ({
|
|
109
|
+
method: "stored",
|
|
110
|
+
...Object.fromEntries(
|
|
111
|
+
Object.entries(values).flatMap(([key, value]) => {
|
|
112
|
+
const text = storedValueText(value);
|
|
113
|
+
return text === undefined ? [] : [[key, text]];
|
|
114
|
+
}),
|
|
115
|
+
),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const grantOf = (config: OAuthConfig): E2BGrant => ({
|
|
119
|
+
email: config.email,
|
|
120
|
+
access: Redacted.make(config.access),
|
|
121
|
+
refresh: Redacted.make(config.refresh),
|
|
122
|
+
tokenEndpoint: config.tokenEndpoint,
|
|
123
|
+
revokeEndpoint: config.revokeEndpoint,
|
|
124
|
+
clientId: config.clientId,
|
|
125
|
+
expires: config.expires,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const withGrant = (config: OAuthConfig, grant: E2BGrant): OAuthConfig => ({
|
|
129
|
+
...config,
|
|
130
|
+
access: Redacted.value(grant.access),
|
|
131
|
+
refresh: Redacted.value(grant.refresh),
|
|
132
|
+
expires: grant.expires,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
/** The team the token acts on: its default team, as `e2b auth login` picks it. */
|
|
136
|
+
const defaultTeam = (access: Redacted.Redacted<string>, domain: string) =>
|
|
137
|
+
auth.listTeams({}).pipe(
|
|
138
|
+
Effect.provide(
|
|
139
|
+
Layer.mergeAll(e2bCredentials({ accessToken: access, domain }), FetchHttpClient.layer),
|
|
140
|
+
),
|
|
141
|
+
Effect.flatMap((teams) => {
|
|
142
|
+
const team = teams.find((t) => t.isDefault) ?? (teams.length === 1 ? teams[0] : undefined);
|
|
143
|
+
return team === undefined
|
|
144
|
+
? Effect.fail(AuthError.make({ message: "E2B: the account has no default team" }))
|
|
145
|
+
: Effect.succeed(team);
|
|
146
|
+
}),
|
|
147
|
+
Effect.mapError((cause) =>
|
|
148
|
+
cause._tag === "AuthError"
|
|
149
|
+
? cause
|
|
150
|
+
: AuthError.make({ message: "E2B: could not list the account's teams", cause }),
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const readEnvironment = Effect.gen(function* () {
|
|
155
|
+
const apiKey = yield* getEnvRedacted("E2B_API_KEY");
|
|
156
|
+
const accessToken = yield* getEnvRedacted("E2B_ACCESS_TOKEN");
|
|
157
|
+
if (!apiKey && !accessToken) {
|
|
158
|
+
return yield* AuthError.make({
|
|
159
|
+
message:
|
|
160
|
+
"E2B CI credentials not found. Set E2B_ACCESS_TOKEN (or E2B_API_KEY for resources only).",
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
apiKey,
|
|
165
|
+
accessToken,
|
|
166
|
+
teamId: yield* getEnv("E2B_TEAM_ID"),
|
|
167
|
+
domain: (yield* getEnv("E2B_DOMAIN")) ?? DEFAULT_DOMAIN,
|
|
168
|
+
source: { type: "env" as const },
|
|
169
|
+
} satisfies E2BResolvedCredentials;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Layer that registers the E2B {@link AuthProvider} into the
|
|
174
|
+
* {@link AuthProviders} registry when built. Included in `E2B.providers()`
|
|
175
|
+
* so `alchemy profile edit --add E2B` can configure it.
|
|
176
|
+
*
|
|
177
|
+
* Methods:
|
|
178
|
+
* - `oauth`: E2B's browser login, the one `e2b auth login` runs; the access
|
|
179
|
+
* token refreshes itself.
|
|
180
|
+
* - `stored`: a pasted access token or API key.
|
|
181
|
+
*/
|
|
182
|
+
export const E2BAuth = AuthProviderLayer<E2BAuthConfig, E2BResolvedCredentials>()(
|
|
183
|
+
E2B_AUTH_PROVIDER_NAME,
|
|
184
|
+
Effect.gen(function* () {
|
|
185
|
+
const browserLogin = Effect.fn(function* (domain: string) {
|
|
186
|
+
const grant = yield* Effect.scoped(
|
|
187
|
+
Effect.gen(function* () {
|
|
188
|
+
const login = yield* startLogin(domain);
|
|
189
|
+
return yield* browserOAuth({
|
|
190
|
+
provider: "E2B",
|
|
191
|
+
url: login.url,
|
|
192
|
+
callback: login.grant,
|
|
193
|
+
exchange: grantFromUrl,
|
|
194
|
+
});
|
|
195
|
+
}),
|
|
196
|
+
);
|
|
197
|
+
const team = yield* defaultTeam(grant.access, domain);
|
|
198
|
+
yield* Interaction.success(`E2B: logged in as ${grant.email} (team ${team.name}).`);
|
|
199
|
+
return {
|
|
200
|
+
method: "oauth" as const,
|
|
201
|
+
email: grant.email,
|
|
202
|
+
teamId: team.teamID,
|
|
203
|
+
teamName: team.name,
|
|
204
|
+
clientId: grant.clientId,
|
|
205
|
+
tokenEndpoint: grant.tokenEndpoint,
|
|
206
|
+
revokeEndpoint: grant.revokeEndpoint,
|
|
207
|
+
access: Redacted.value(grant.access),
|
|
208
|
+
refresh: Redacted.value(grant.refresh),
|
|
209
|
+
expires: grant.expires,
|
|
210
|
+
...(domain === DEFAULT_DOMAIN ? {} : { domain }),
|
|
211
|
+
} satisfies OAuthConfig;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const configure = (_profileName: string) =>
|
|
215
|
+
Interaction.select({
|
|
216
|
+
message: "E2B authentication method",
|
|
217
|
+
options: [
|
|
218
|
+
{
|
|
219
|
+
value: "oauth" as const,
|
|
220
|
+
label: "Browser login",
|
|
221
|
+
description: "recommended — sign in to E2B; the token refreshes itself",
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
value: "stored" as const,
|
|
225
|
+
label: "Access token or API key",
|
|
226
|
+
description: "paste a token and store it in the profile",
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
}).pipe(
|
|
230
|
+
mapPromptCancellation,
|
|
231
|
+
Effect.flatMap((method) =>
|
|
232
|
+
method === "oauth"
|
|
233
|
+
? browserLogin(DEFAULT_DOMAIN)
|
|
234
|
+
: collectFieldValues(storedFields).pipe(
|
|
235
|
+
Effect.map(storedConfig),
|
|
236
|
+
Effect.tap(() => Interaction.success("E2B: credentials saved.")),
|
|
237
|
+
),
|
|
238
|
+
),
|
|
239
|
+
Effect.mapError((cause) =>
|
|
240
|
+
cause._tag === "AuthError"
|
|
241
|
+
? cause
|
|
242
|
+
: AuthError.make({
|
|
243
|
+
message: `E2B: failed to configure credentials: ${cause.message}`,
|
|
244
|
+
cause,
|
|
245
|
+
}),
|
|
246
|
+
),
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const configureMethods: ReadonlyArray<ConfigureMethod> = [
|
|
250
|
+
{ method: "oauth", fields: [] },
|
|
251
|
+
{ method: "stored", fields: storedFields },
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
const configureWith = (
|
|
255
|
+
_profileName: string,
|
|
256
|
+
input: { readonly method: string; readonly values: Record<string, string> },
|
|
257
|
+
) =>
|
|
258
|
+
input.method === "oauth"
|
|
259
|
+
? browserLogin(DEFAULT_DOMAIN).pipe(
|
|
260
|
+
Effect.mapError((cause) =>
|
|
261
|
+
cause._tag === "AuthError"
|
|
262
|
+
? cause
|
|
263
|
+
: AuthError.make({ message: `E2B: browser login failed: ${cause.message}`, cause }),
|
|
264
|
+
),
|
|
265
|
+
)
|
|
266
|
+
: input.method === "stored"
|
|
267
|
+
? validateFieldValues(E2B_AUTH_PROVIDER_NAME, storedFields, input.values).pipe(
|
|
268
|
+
Effect.map(storedConfig),
|
|
269
|
+
Effect.tap(() => Interaction.success("E2B: credentials saved.")),
|
|
270
|
+
)
|
|
271
|
+
: Effect.fail(
|
|
272
|
+
AuthError.make({
|
|
273
|
+
message: `E2B: unknown method '${input.method}'. Valid methods: oauth, stored.`,
|
|
274
|
+
}),
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
/** Refreshes an OAuth grant that is within a minute of expiring. */
|
|
278
|
+
const freshGrant = Effect.fn(function* (profileName: string, config: OAuthConfig) {
|
|
279
|
+
const now = yield* Clock.currentTimeMillis;
|
|
280
|
+
if (config.expires > now + 60_000) return config;
|
|
281
|
+
return withGrant(
|
|
282
|
+
config,
|
|
283
|
+
yield* refreshGrant(grantOf(config)).pipe(
|
|
284
|
+
Effect.mapError((cause) =>
|
|
285
|
+
NeedsReauth.make({
|
|
286
|
+
provider: E2B_AUTH_PROVIDER_NAME,
|
|
287
|
+
profile: profileName,
|
|
288
|
+
message: `E2B login expired. ${refreshHint(E2B_AUTH_PROVIDER_NAME, profileName)}`,
|
|
289
|
+
cause,
|
|
290
|
+
}),
|
|
291
|
+
),
|
|
292
|
+
),
|
|
293
|
+
);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const read = (
|
|
297
|
+
profileName: string,
|
|
298
|
+
config: E2BAuthConfig,
|
|
299
|
+
updateConfig?: (config: E2BAuthConfig) => Effect.Effect<void, AuthError>,
|
|
300
|
+
): Effect.Effect<E2BResolvedCredentials, AuthError | NeedsReauth> =>
|
|
301
|
+
Match.value(config).pipe(
|
|
302
|
+
Match.when({ method: "stored" }, (stored) =>
|
|
303
|
+
Effect.succeed({
|
|
304
|
+
apiKey: storedSecret(stored.apiKey),
|
|
305
|
+
accessToken: storedSecret(stored.accessToken),
|
|
306
|
+
teamId: stored.teamId,
|
|
307
|
+
domain: stored.domain ?? DEFAULT_DOMAIN,
|
|
308
|
+
source: { type: "stored" as const },
|
|
309
|
+
} satisfies E2BResolvedCredentials),
|
|
310
|
+
),
|
|
311
|
+
Match.when({ method: "oauth" }, (oauth) =>
|
|
312
|
+
Effect.gen(function* () {
|
|
313
|
+
const fresh = yield* freshGrant(profileName, oauth);
|
|
314
|
+
if (fresh !== oauth) yield* updateConfig?.(fresh) ?? Effect.void;
|
|
315
|
+
return {
|
|
316
|
+
apiKey: undefined,
|
|
317
|
+
accessToken: Redacted.make(fresh.access),
|
|
318
|
+
teamId: fresh.teamId,
|
|
319
|
+
domain: fresh.domain ?? DEFAULT_DOMAIN,
|
|
320
|
+
expires: fresh.expires,
|
|
321
|
+
source: { type: "oauth" as const, details: fresh.email },
|
|
322
|
+
} satisfies E2BResolvedCredentials;
|
|
323
|
+
}),
|
|
324
|
+
),
|
|
325
|
+
Match.exhaustive,
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
const login = (
|
|
329
|
+
profileName: string,
|
|
330
|
+
config: E2BAuthConfig,
|
|
331
|
+
updateConfig?: (config: E2BAuthConfig) => Effect.Effect<void, AuthError>,
|
|
332
|
+
reload?: Effect.Effect<E2BAuthConfig, AuthError>,
|
|
333
|
+
) =>
|
|
334
|
+
Match.value(config).pipe(
|
|
335
|
+
Match.when({ method: "stored" }, (stored) => Effect.succeed(stored)),
|
|
336
|
+
Match.when({ method: "oauth" }, (oauth) =>
|
|
337
|
+
// Refresh tokens can be single-use: refresh the one stored now,
|
|
338
|
+
// under the lock; a refused refresh falls back to the browser.
|
|
339
|
+
withProfileCredentialsLock(
|
|
340
|
+
profileName,
|
|
341
|
+
Effect.gen(function* () {
|
|
342
|
+
const current = yield* reload ?? Effect.succeed(oauth);
|
|
343
|
+
if (current.method !== "oauth") return current;
|
|
344
|
+
const refreshed = withGrant(current, yield* refreshGrant(grantOf(current)));
|
|
345
|
+
if (updateConfig === undefined) return refreshed;
|
|
346
|
+
yield* updateConfig(refreshed);
|
|
347
|
+
return undefined;
|
|
348
|
+
}),
|
|
349
|
+
).pipe(
|
|
350
|
+
Effect.catchTag("E2BLoginError", () => browserLogin(oauth.domain ?? DEFAULT_DOMAIN)),
|
|
351
|
+
),
|
|
352
|
+
),
|
|
353
|
+
Match.exhaustive,
|
|
354
|
+
Effect.mapError((cause) =>
|
|
355
|
+
cause._tag === "AuthError"
|
|
356
|
+
? cause
|
|
357
|
+
: AuthError.make({ message: `E2B: login failed: ${cause.message}`, cause }),
|
|
358
|
+
),
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
const logout = (_profileName: string, config: E2BAuthConfig) =>
|
|
362
|
+
config.method === "oauth" ? revokeGrant(grantOf(config)) : Effect.void;
|
|
363
|
+
|
|
364
|
+
const details = (
|
|
365
|
+
profileName: string,
|
|
366
|
+
config: E2BAuthConfig,
|
|
367
|
+
updateConfig?: (config: E2BAuthConfig) => Effect.Effect<void, AuthError>,
|
|
368
|
+
) =>
|
|
369
|
+
Effect.all([read(profileName, config, updateConfig), Clock.currentTimeMillis]).pipe(
|
|
370
|
+
Effect.map(([creds, now]): ProviderDetails => {
|
|
371
|
+
const lines: Array<{ key: string; value: string }> = [];
|
|
372
|
+
if (config.method === "oauth") lines.push({ key: "email", value: config.email });
|
|
373
|
+
if (creds.accessToken) {
|
|
374
|
+
lines.push({ key: "accessToken", value: displayRedacted(creds.accessToken) });
|
|
375
|
+
}
|
|
376
|
+
if (creds.expires !== undefined) {
|
|
377
|
+
const remaining = creds.expires - now;
|
|
378
|
+
lines.push({
|
|
379
|
+
key: "expires",
|
|
380
|
+
value:
|
|
381
|
+
remaining <= 0
|
|
382
|
+
? "expired"
|
|
383
|
+
: `in ${Duration.format(Duration.millis(remaining))} (refreshes itself)`,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
if (creds.apiKey) lines.push({ key: "apiKey", value: displayRedacted(creds.apiKey) });
|
|
387
|
+
lines.push({
|
|
388
|
+
key: "team",
|
|
389
|
+
value:
|
|
390
|
+
config.method === "oauth"
|
|
391
|
+
? `${config.teamName} (${config.teamId})`
|
|
392
|
+
: (creds.teamId ?? "default"),
|
|
393
|
+
});
|
|
394
|
+
lines.push({ key: "source", value: creds.source.type });
|
|
395
|
+
return { lines };
|
|
396
|
+
}),
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
configSchema: E2BAuthConfigSchema,
|
|
401
|
+
configure,
|
|
402
|
+
configureWith,
|
|
403
|
+
configureMethods,
|
|
404
|
+
login,
|
|
405
|
+
logout,
|
|
406
|
+
details,
|
|
407
|
+
read,
|
|
408
|
+
readEnvironment,
|
|
409
|
+
environment: [
|
|
410
|
+
{
|
|
411
|
+
name: "E2B_ACCESS_TOKEN",
|
|
412
|
+
required: false,
|
|
413
|
+
secret: true,
|
|
414
|
+
description:
|
|
415
|
+
"Access token. Reaches every endpoint and mints the API keys bindings and E2B.ApiKey need.",
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
name: "E2B_TEAM_ID",
|
|
419
|
+
required: false,
|
|
420
|
+
description: "Team id sent with the access token. Defaults to the token's default team.",
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
name: "E2B_API_KEY",
|
|
424
|
+
required: false,
|
|
425
|
+
secret: true,
|
|
426
|
+
description:
|
|
427
|
+
"Team API key, sent as X-API-Key. Enough for resources; bindings need the access token.",
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: "E2B_DOMAIN",
|
|
431
|
+
required: false,
|
|
432
|
+
description: "E2B domain for self-hosted deployments.",
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
};
|
|
436
|
+
}),
|
|
437
|
+
);
|
package/src/Bootstrap.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process bootstrap for `E2B.Template` programs (a Bun process serving the
|
|
3
|
+
* bundled program). The generated entry imports this module and the user's
|
|
4
|
+
* `main`, nothing else — see `alchemy/Runtime/Bootstrap` for why.
|
|
5
|
+
*/
|
|
6
|
+
import { BunServices } from "@effect/platform-bun";
|
|
7
|
+
import { BunHttpServer } from "alchemy/Http";
|
|
8
|
+
import { reifyBoundConfigProvider } from "alchemy/Runtime";
|
|
9
|
+
import {
|
|
10
|
+
entrypointLayer,
|
|
11
|
+
resolveProgram,
|
|
12
|
+
runProcess,
|
|
13
|
+
stackFromEnv,
|
|
14
|
+
} from "alchemy/Runtime/Bootstrap";
|
|
15
|
+
import * as ConfigProvider from "effect/ConfigProvider";
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
18
|
+
import * as Layer from "effect/Layer";
|
|
19
|
+
import * as Logger from "effect/Logger";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the bundled program (the runners registered via `host.run` /
|
|
23
|
+
* serve) and run it with a Bun HTTP server bound to `PORT`, so the returned
|
|
24
|
+
* `{ fetch }` handler is actually served and `host.run` loops stay alive.
|
|
25
|
+
*/
|
|
26
|
+
export const bootstrap = (entrypoint: unknown): Promise<void> => {
|
|
27
|
+
const platform = Layer.mergeAll(
|
|
28
|
+
BunServices.layer,
|
|
29
|
+
FetchHttpClient.layer,
|
|
30
|
+
Logger.layer([Logger.consolePretty()]),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const program = resolveProgram("program").pipe(
|
|
34
|
+
Effect.provide(
|
|
35
|
+
entrypointLayer(entrypoint).pipe(
|
|
36
|
+
Layer.provideMerge(stackFromEnv),
|
|
37
|
+
Layer.provideMerge(BunHttpServer()),
|
|
38
|
+
Layer.provideMerge(platform),
|
|
39
|
+
Layer.provideMerge(
|
|
40
|
+
Layer.succeed(
|
|
41
|
+
ConfigProvider.ConfigProvider,
|
|
42
|
+
reifyBoundConfigProvider(ConfigProvider.fromEnv(), process.env),
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
),
|
|
47
|
+
Effect.scoped,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return runProcess("Docker service", program);
|
|
51
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as Binding from "alchemy/Binding";
|
|
2
|
+
import type * as Effect from "effect/Effect";
|
|
3
|
+
import type { Sandbox } from "./Sandbox.ts";
|
|
4
|
+
import type { ConnectedSandbox } from "./SandboxClient.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Work with one {@link Sandbox} declared in the stack, from a Worker,
|
|
8
|
+
* Function or any other host: run commands, read and write files, open
|
|
9
|
+
* terminals, call its ports, snapshot or fork it. The handle has the same
|
|
10
|
+
* operations as the sandboxes `CreateSandbox` returns; its `sandboxId` and
|
|
11
|
+
* `url(port)` are Effects, read at runtime.
|
|
12
|
+
*
|
|
13
|
+
* A declared sandbox pauses when it is idle. With `autoResume` (the
|
|
14
|
+
* default), E2B resumes it on the next request, to one of its ports or from
|
|
15
|
+
* any call here. With `autoResume: false`, calls inside it (`exec`,
|
|
16
|
+
* `start`, `files`, `pty`) fail with `SandboxNotRunning` until you call
|
|
17
|
+
* `resume()`.
|
|
18
|
+
*
|
|
19
|
+
* The host gets its own E2B API key, minted at deploy and revoked when the
|
|
20
|
+
* host is destroyed. E2B keys are not scoped, so the key reaches the whole
|
|
21
|
+
* team; it is never the deployer's own.
|
|
22
|
+
*
|
|
23
|
+
* ### Running commands in a dev box
|
|
24
|
+
* **Example:** Pull and test on request
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const devBox = yield* E2B.ConnectSandbox(DevBox);
|
|
27
|
+
* return {
|
|
28
|
+
* fetch: Effect.gen(function* () {
|
|
29
|
+
* yield* devBox.resume({ timeout: "10 minutes" });
|
|
30
|
+
* const test = yield* devBox.exec("git pull && npm test", { cwd: "/home/user/repo" });
|
|
31
|
+
* return yield* HttpServerResponse.json(test);
|
|
32
|
+
* }),
|
|
33
|
+
* };
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* ### Streaming a process
|
|
37
|
+
* **Example:** Stream a build's output
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const build = yield* devBox.start("npm run build", { cwd: "/home/user/repo" });
|
|
40
|
+
* const output = build.output.pipe(Stream.map((chunk) => chunk.data));
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* ### Calling a program in the sandbox
|
|
44
|
+
* **Example:** Call port 3000
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const port = yield* api.getTcpPort(3000);
|
|
47
|
+
* const response = yield* port.fetch(HttpClientRequest.get("/health"));
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* **Example:** Proxy the Worker's request to port 3000
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const port = yield* api.getTcpPort(3000);
|
|
53
|
+
* return yield* port.fetch(yield* HttpServerRequest);
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* ### Pausing it early
|
|
57
|
+
* **Example:** Pause when the work is done
|
|
58
|
+
* ```typescript
|
|
59
|
+
* yield* devBox.pause();
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @binding
|
|
63
|
+
* @product Sandbox
|
|
64
|
+
*/
|
|
65
|
+
export interface ConnectSandbox extends Binding.Service<
|
|
66
|
+
ConnectSandbox,
|
|
67
|
+
"E2B.ConnectSandbox",
|
|
68
|
+
(sandbox: Sandbox) => Effect.Effect<ConnectedSandbox>
|
|
69
|
+
> {}
|
|
70
|
+
|
|
71
|
+
export const ConnectSandbox = Binding.Service<ConnectSandbox>("E2B.ConnectSandbox");
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type * as Output from "alchemy/Output";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import type * as Redacted from "effect/Redacted";
|
|
5
|
+
import { ConnectSandbox } from "./ConnectSandbox.ts";
|
|
6
|
+
import { bindE2B } from "./E2BBindings.ts";
|
|
7
|
+
import { makeConnectedSandbox } from "./internal/SandboxHandle.ts";
|
|
8
|
+
import type { Sandbox } from "./Sandbox.ts";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* HTTP implementation of {@link ConnectSandbox}: E2B's control plane with
|
|
12
|
+
* the host's key, and envd inside the sandbox with the sandbox's own token.
|
|
13
|
+
* Works on any host.
|
|
14
|
+
*
|
|
15
|
+
* ### Provide the layer
|
|
16
|
+
* **Example:** On a Cloudflare Worker
|
|
17
|
+
* ```typescript
|
|
18
|
+
* Effect.gen(function* () {
|
|
19
|
+
* const devBox = yield* E2B.ConnectSandbox(DevBox);
|
|
20
|
+
* // ...
|
|
21
|
+
* }).pipe(Effect.provide(E2B.ConnectSandboxHttp))
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @layer
|
|
25
|
+
* @product Sandbox
|
|
26
|
+
* @provides E2B.ConnectSandbox
|
|
27
|
+
*/
|
|
28
|
+
export const ConnectSandboxHttp = Layer.succeed(
|
|
29
|
+
ConnectSandbox,
|
|
30
|
+
Effect.fn("E2B.ConnectSandbox")(function* (sandbox: Sandbox) {
|
|
31
|
+
const auth = yield* bindE2B;
|
|
32
|
+
const sandboxId = yield* (sandbox.sandboxId as Output.Output<string, never>).asEffect();
|
|
33
|
+
const trafficToken = yield* (
|
|
34
|
+
sandbox.trafficAccessToken as Output.Output<Redacted.Redacted<string> | undefined, never>
|
|
35
|
+
).asEffect();
|
|
36
|
+
return makeConnectedSandbox(auth, sandboxId, trafficToken);
|
|
37
|
+
}),
|
|
38
|
+
);
|