@openai/agents-extensions 0.8.5 → 0.9.0
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/dist/metadata.js +2 -2
- package/dist/metadata.mjs +2 -2
- package/dist/sandbox/blaxel/index.d.ts +2 -0
- package/dist/sandbox/blaxel/index.js +19 -0
- package/dist/sandbox/blaxel/index.js.map +1 -0
- package/dist/sandbox/blaxel/index.mjs +3 -0
- package/dist/sandbox/blaxel/index.mjs.map +1 -0
- package/dist/sandbox/blaxel/mounts.d.ts +101 -0
- package/dist/sandbox/blaxel/mounts.js +542 -0
- package/dist/sandbox/blaxel/mounts.js.map +1 -0
- package/dist/sandbox/blaxel/mounts.mjs +529 -0
- package/dist/sandbox/blaxel/mounts.mjs.map +1 -0
- package/dist/sandbox/blaxel/sandbox.d.ts +155 -0
- package/dist/sandbox/blaxel/sandbox.js +1110 -0
- package/dist/sandbox/blaxel/sandbox.js.map +1 -0
- package/dist/sandbox/blaxel/sandbox.mjs +1072 -0
- package/dist/sandbox/blaxel/sandbox.mjs.map +1 -0
- package/dist/sandbox/cloudflare/index.d.ts +2 -0
- package/dist/sandbox/cloudflare/index.js +19 -0
- package/dist/sandbox/cloudflare/index.js.map +1 -0
- package/dist/sandbox/cloudflare/index.mjs +3 -0
- package/dist/sandbox/cloudflare/index.mjs.map +1 -0
- package/dist/sandbox/cloudflare/mounts.d.ts +28 -0
- package/dist/sandbox/cloudflare/mounts.js +150 -0
- package/dist/sandbox/cloudflare/mounts.js.map +1 -0
- package/dist/sandbox/cloudflare/mounts.mjs +143 -0
- package/dist/sandbox/cloudflare/mounts.mjs.map +1 -0
- package/dist/sandbox/cloudflare/sandbox.d.ts +91 -0
- package/dist/sandbox/cloudflare/sandbox.js +1163 -0
- package/dist/sandbox/cloudflare/sandbox.js.map +1 -0
- package/dist/sandbox/cloudflare/sandbox.mjs +1125 -0
- package/dist/sandbox/cloudflare/sandbox.mjs.map +1 -0
- package/dist/sandbox/daytona/index.d.ts +2 -0
- package/dist/sandbox/daytona/index.js +19 -0
- package/dist/sandbox/daytona/index.js.map +1 -0
- package/dist/sandbox/daytona/index.mjs +3 -0
- package/dist/sandbox/daytona/index.mjs.map +1 -0
- package/dist/sandbox/daytona/mounts.d.ts +11 -0
- package/dist/sandbox/daytona/mounts.js +17 -0
- package/dist/sandbox/daytona/mounts.js.map +1 -0
- package/dist/sandbox/daytona/mounts.mjs +12 -0
- package/dist/sandbox/daytona/mounts.mjs.map +1 -0
- package/dist/sandbox/daytona/sandbox.d.ts +160 -0
- package/dist/sandbox/daytona/sandbox.js +938 -0
- package/dist/sandbox/daytona/sandbox.js.map +1 -0
- package/dist/sandbox/daytona/sandbox.mjs +900 -0
- package/dist/sandbox/daytona/sandbox.mjs.map +1 -0
- package/dist/sandbox/e2b/index.d.ts +2 -0
- package/dist/sandbox/e2b/index.js +19 -0
- package/dist/sandbox/e2b/index.js.map +1 -0
- package/dist/sandbox/e2b/index.mjs +3 -0
- package/dist/sandbox/e2b/index.mjs.map +1 -0
- package/dist/sandbox/e2b/mounts.d.ts +11 -0
- package/dist/sandbox/e2b/mounts.js +17 -0
- package/dist/sandbox/e2b/mounts.js.map +1 -0
- package/dist/sandbox/e2b/mounts.mjs +12 -0
- package/dist/sandbox/e2b/mounts.mjs.map +1 -0
- package/dist/sandbox/e2b/sandbox.d.ts +169 -0
- package/dist/sandbox/e2b/sandbox.js +829 -0
- package/dist/sandbox/e2b/sandbox.js.map +1 -0
- package/dist/sandbox/e2b/sandbox.mjs +791 -0
- package/dist/sandbox/e2b/sandbox.mjs.map +1 -0
- package/dist/sandbox/modal/index.d.ts +2 -0
- package/dist/sandbox/modal/index.js +19 -0
- package/dist/sandbox/modal/index.js.map +1 -0
- package/dist/sandbox/modal/index.mjs +3 -0
- package/dist/sandbox/modal/index.mjs.map +1 -0
- package/dist/sandbox/modal/mounts.d.ts +24 -0
- package/dist/sandbox/modal/mounts.js +194 -0
- package/dist/sandbox/modal/mounts.js.map +1 -0
- package/dist/sandbox/modal/mounts.mjs +188 -0
- package/dist/sandbox/modal/mounts.mjs.map +1 -0
- package/dist/sandbox/modal/sandbox.d.ts +220 -0
- package/dist/sandbox/modal/sandbox.js +1621 -0
- package/dist/sandbox/modal/sandbox.js.map +1 -0
- package/dist/sandbox/modal/sandbox.mjs +1581 -0
- package/dist/sandbox/modal/sandbox.mjs.map +1 -0
- package/dist/sandbox/runloop/index.d.ts +2 -0
- package/dist/sandbox/runloop/index.js +19 -0
- package/dist/sandbox/runloop/index.js.map +1 -0
- package/dist/sandbox/runloop/index.mjs +3 -0
- package/dist/sandbox/runloop/index.mjs.map +1 -0
- package/dist/sandbox/runloop/mounts.d.ts +11 -0
- package/dist/sandbox/runloop/mounts.js +17 -0
- package/dist/sandbox/runloop/mounts.js.map +1 -0
- package/dist/sandbox/runloop/mounts.mjs +12 -0
- package/dist/sandbox/runloop/mounts.mjs.map +1 -0
- package/dist/sandbox/runloop/sandbox.d.ts +266 -0
- package/dist/sandbox/runloop/sandbox.js +1390 -0
- package/dist/sandbox/runloop/sandbox.js.map +1 -0
- package/dist/sandbox/runloop/sandbox.mjs +1346 -0
- package/dist/sandbox/runloop/sandbox.mjs.map +1 -0
- package/dist/sandbox/shared/archive.d.ts +34 -0
- package/dist/sandbox/shared/archive.js +397 -0
- package/dist/sandbox/shared/archive.js.map +1 -0
- package/dist/sandbox/shared/archive.mjs +389 -0
- package/dist/sandbox/shared/archive.mjs.map +1 -0
- package/dist/sandbox/shared/editor.d.ts +17 -0
- package/dist/sandbox/shared/editor.js +65 -0
- package/dist/sandbox/shared/editor.js.map +1 -0
- package/dist/sandbox/shared/editor.mjs +61 -0
- package/dist/sandbox/shared/editor.mjs.map +1 -0
- package/dist/sandbox/shared/environment.d.ts +1 -0
- package/dist/sandbox/shared/environment.js +13 -0
- package/dist/sandbox/shared/environment.js.map +1 -0
- package/dist/sandbox/shared/environment.mjs +2 -0
- package/dist/sandbox/shared/environment.mjs.map +1 -0
- package/dist/sandbox/shared/git.d.ts +4 -0
- package/dist/sandbox/shared/git.js +20 -0
- package/dist/sandbox/shared/git.js.map +1 -0
- package/dist/sandbox/shared/git.mjs +17 -0
- package/dist/sandbox/shared/git.mjs.map +1 -0
- package/dist/sandbox/shared/inContainerMounts.d.ts +40 -0
- package/dist/sandbox/shared/inContainerMounts.js +670 -0
- package/dist/sandbox/shared/inContainerMounts.js.map +1 -0
- package/dist/sandbox/shared/inContainerMounts.mjs +662 -0
- package/dist/sandbox/shared/inContainerMounts.mjs.map +1 -0
- package/dist/sandbox/shared/index.d.ts +23 -0
- package/dist/sandbox/shared/index.js +112 -0
- package/dist/sandbox/shared/index.js.map +1 -0
- package/dist/sandbox/shared/index.mjs +18 -0
- package/dist/sandbox/shared/index.mjs.map +1 -0
- package/dist/sandbox/shared/localSources.d.ts +8 -0
- package/dist/sandbox/shared/localSources.js +378 -0
- package/dist/sandbox/shared/localSources.js.map +1 -0
- package/dist/sandbox/shared/localSources.mjs +372 -0
- package/dist/sandbox/shared/localSources.mjs.map +1 -0
- package/dist/sandbox/shared/manifest.d.ts +38 -0
- package/dist/sandbox/shared/manifest.js +254 -0
- package/dist/sandbox/shared/manifest.js.map +1 -0
- package/dist/sandbox/shared/manifest.mjs +233 -0
- package/dist/sandbox/shared/manifest.mjs.map +1 -0
- package/dist/sandbox/shared/media.d.ts +1 -0
- package/dist/sandbox/shared/media.js +10 -0
- package/dist/sandbox/shared/media.js.map +1 -0
- package/dist/sandbox/shared/media.mjs +2 -0
- package/dist/sandbox/shared/media.mjs.map +1 -0
- package/dist/sandbox/shared/metadata.d.ts +7 -0
- package/dist/sandbox/shared/metadata.js +87 -0
- package/dist/sandbox/shared/metadata.js.map +1 -0
- package/dist/sandbox/shared/metadata.mjs +81 -0
- package/dist/sandbox/shared/metadata.mjs.map +1 -0
- package/dist/sandbox/shared/output.d.ts +1 -0
- package/dist/sandbox/shared/output.js +8 -0
- package/dist/sandbox/shared/output.js.map +1 -0
- package/dist/sandbox/shared/output.mjs +2 -0
- package/dist/sandbox/shared/output.mjs.map +1 -0
- package/dist/sandbox/shared/paths.d.ts +27 -0
- package/dist/sandbox/shared/paths.js +245 -0
- package/dist/sandbox/shared/paths.js.map +1 -0
- package/dist/sandbox/shared/paths.mjs +236 -0
- package/dist/sandbox/shared/paths.mjs.map +1 -0
- package/dist/sandbox/shared/ports.d.ts +14 -0
- package/dist/sandbox/shared/ports.js +75 -0
- package/dist/sandbox/shared/ports.js.map +1 -0
- package/dist/sandbox/shared/ports.mjs +69 -0
- package/dist/sandbox/shared/ports.mjs.map +1 -0
- package/dist/sandbox/shared/process.d.ts +16 -0
- package/dist/sandbox/shared/process.js +90 -0
- package/dist/sandbox/shared/process.js.map +1 -0
- package/dist/sandbox/shared/process.mjs +86 -0
- package/dist/sandbox/shared/process.mjs.map +1 -0
- package/dist/sandbox/shared/pty.d.ts +81 -0
- package/dist/sandbox/shared/pty.js +382 -0
- package/dist/sandbox/shared/pty.js.map +1 -0
- package/dist/sandbox/shared/pty.mjs +335 -0
- package/dist/sandbox/shared/pty.mjs.map +1 -0
- package/dist/sandbox/shared/runAs.d.ts +46 -0
- package/dist/sandbox/shared/runAs.js +149 -0
- package/dist/sandbox/shared/runAs.js.map +1 -0
- package/dist/sandbox/shared/runAs.mjs +141 -0
- package/dist/sandbox/shared/runAs.mjs.map +1 -0
- package/dist/sandbox/shared/session.d.ts +16 -0
- package/dist/sandbox/shared/session.js +138 -0
- package/dist/sandbox/shared/session.js.map +1 -0
- package/dist/sandbox/shared/session.mjs +128 -0
- package/dist/sandbox/shared/session.mjs.map +1 -0
- package/dist/sandbox/shared/sessionBase.d.ts +90 -0
- package/dist/sandbox/shared/sessionBase.js +446 -0
- package/dist/sandbox/shared/sessionBase.js.map +1 -0
- package/dist/sandbox/shared/sessionBase.mjs +442 -0
- package/dist/sandbox/shared/sessionBase.mjs.map +1 -0
- package/dist/sandbox/shared/sessionState.d.ts +7 -0
- package/dist/sandbox/shared/sessionState.js +35 -0
- package/dist/sandbox/shared/sessionState.js.map +1 -0
- package/dist/sandbox/shared/sessionState.mjs +31 -0
- package/dist/sandbox/shared/sessionState.mjs.map +1 -0
- package/dist/sandbox/shared/snapshots.d.ts +9 -0
- package/dist/sandbox/shared/snapshots.js +62 -0
- package/dist/sandbox/shared/snapshots.js.map +1 -0
- package/dist/sandbox/shared/snapshots.mjs +57 -0
- package/dist/sandbox/shared/snapshots.mjs.map +1 -0
- package/dist/sandbox/shared/typeGuards.d.ts +10 -0
- package/dist/sandbox/shared/typeGuards.js +50 -0
- package/dist/sandbox/shared/typeGuards.js.map +1 -0
- package/dist/sandbox/shared/typeGuards.mjs +38 -0
- package/dist/sandbox/shared/typeGuards.mjs.map +1 -0
- package/dist/sandbox/shared/types.d.ts +24 -0
- package/dist/sandbox/shared/types.js +3 -0
- package/dist/sandbox/shared/types.js.map +1 -0
- package/dist/sandbox/shared/types.mjs +2 -0
- package/dist/sandbox/shared/types.mjs.map +1 -0
- package/dist/sandbox/vercel/index.d.ts +1 -0
- package/dist/sandbox/vercel/index.js +18 -0
- package/dist/sandbox/vercel/index.js.map +1 -0
- package/dist/sandbox/vercel/index.mjs +2 -0
- package/dist/sandbox/vercel/index.mjs.map +1 -0
- package/dist/sandbox/vercel/sandbox.d.ts +135 -0
- package/dist/sandbox/vercel/sandbox.js +1001 -0
- package/dist/sandbox/vercel/sandbox.js.map +1 -0
- package/dist/sandbox/vercel/sandbox.mjs +963 -0
- package/dist/sandbox/vercel/sandbox.mjs.map +1 -0
- package/package.json +108 -4
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
import { UserError } from '@openai/agents-core';
|
|
2
|
+
import { SandboxProviderError, SandboxUnsupportedFeatureError, normalizeSandboxClientCreateArgs, } from '@openai/agents-core/sandbox';
|
|
3
|
+
import { appendPtyOutput, assertCoreSnapshotUnsupported, assertResumeRecreateAllowed, assertTarWorkspacePersistence, createPtyProcessEntry, deserializeRemoteSandboxSessionStateValues, formatPtyExecUpdate, assertSandboxManifestMetadataSupported, SANDBOX_MANIFEST_METADATA_SUPPORT, closeRemoteSessionOnManifestError, decodeNativeSnapshotRef, encodeNativeSnapshotRef, materializeEnvironment, parseExposedPortEndpoint, shellQuote, shellCommandForPty, serializeRemoteSandboxSessionState, watchPtyProcess, isRecord, readOptionalBoolean, readOptionalNumber, readOptionalNumberArray, readOptionalRecord, readOptionalString, readOptionalStringRecord, readString, withProviderError, withSandboxSpan, writePtyStdin, PtyProcessRegistry, RemoteSandboxSessionBase, } from "../shared/index.mjs";
|
|
4
|
+
import { mountRcloneCloudBucket, rclonePatternFromMountStrategy, unmountRcloneMount, } from "../shared/inContainerMounts.mjs";
|
|
5
|
+
import { isE2BCloudBucketMountEntry } from "./mounts.mjs";
|
|
6
|
+
export class E2BSandboxSession extends RemoteSandboxSessionBase {
|
|
7
|
+
sandbox;
|
|
8
|
+
ptyProcesses = new PtyProcessRegistry();
|
|
9
|
+
activeMountPaths = new Set();
|
|
10
|
+
constructor(args) {
|
|
11
|
+
super({
|
|
12
|
+
state: args.state,
|
|
13
|
+
options: {
|
|
14
|
+
providerName: 'E2BSandboxClient',
|
|
15
|
+
providerId: 'e2b',
|
|
16
|
+
concurrencyLimits: args.concurrencyLimits,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
this.sandbox = args.sandbox;
|
|
20
|
+
this.state.pauseOnExitSupported = canPauseE2BSandbox(args.sandbox);
|
|
21
|
+
}
|
|
22
|
+
supportsPty() {
|
|
23
|
+
return typeof this.sandbox.pty?.create === 'function';
|
|
24
|
+
}
|
|
25
|
+
async writeStdin(args) {
|
|
26
|
+
return await writePtyStdin({
|
|
27
|
+
providerName: 'E2BSandboxClient',
|
|
28
|
+
registry: this.ptyProcesses,
|
|
29
|
+
sessionId: args.sessionId,
|
|
30
|
+
chars: args.chars,
|
|
31
|
+
yieldTimeMs: args.yieldTimeMs,
|
|
32
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async execPtyCommand(args) {
|
|
36
|
+
if (args.runAs) {
|
|
37
|
+
throw new SandboxUnsupportedFeatureError('E2BSandboxClient tty=true does not support runAs because the E2B SDK PTY API does not expose a user option.', {
|
|
38
|
+
provider: 'e2b',
|
|
39
|
+
feature: 'tty.runAs',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const pty = this.sandbox.pty;
|
|
43
|
+
if (!pty?.create) {
|
|
44
|
+
throw new SandboxUnsupportedFeatureError('E2BSandboxClient tty=true requires E2B SDK PTY support.', {
|
|
45
|
+
provider: 'e2b',
|
|
46
|
+
feature: 'tty',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const start = Date.now();
|
|
50
|
+
const command = shellCommandForPty(args);
|
|
51
|
+
const entry = createPtyProcessEntry({ tty: true });
|
|
52
|
+
const timeoutMs = typeof this.state.commandTimeoutMs === 'number'
|
|
53
|
+
? this.state.commandTimeoutMs
|
|
54
|
+
: typeof this.state.timeout === 'number'
|
|
55
|
+
? Math.max(1, Math.trunc(this.state.timeout * 1000))
|
|
56
|
+
: undefined;
|
|
57
|
+
const handle = await pty.create({
|
|
58
|
+
cols: 80,
|
|
59
|
+
rows: 24,
|
|
60
|
+
size: { cols: 80, rows: 24 },
|
|
61
|
+
cwd: this.resolveWorkdir(args.workdir),
|
|
62
|
+
envs: this.state.environment,
|
|
63
|
+
timeoutMs,
|
|
64
|
+
onData: (data) => appendPtyOutput(entry, data),
|
|
65
|
+
});
|
|
66
|
+
entry.terminate = async () => {
|
|
67
|
+
if (handle.kill) {
|
|
68
|
+
await handle.kill();
|
|
69
|
+
}
|
|
70
|
+
else if (pty.kill) {
|
|
71
|
+
await pty.kill(handle.pid, {
|
|
72
|
+
requestTimeoutMs: this.state.requestTimeoutMs,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
if (!pty.sendInput) {
|
|
77
|
+
await entry.terminate().catch(() => { });
|
|
78
|
+
throw new SandboxUnsupportedFeatureError('E2BSandboxClient tty=true requires E2B SDK PTY stdin support.', {
|
|
79
|
+
provider: 'e2b',
|
|
80
|
+
feature: 'tty.stdin',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
entry.sendInput = async (chars) => {
|
|
84
|
+
await pty.sendInput(handle.pid, new TextEncoder().encode(chars), {
|
|
85
|
+
requestTimeoutMs: this.state.requestTimeoutMs,
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
watchPtyProcess(entry, async () => (handle.wait ? await handle.wait() : undefined), (result, error) => exitCodeFromE2BResult(result) ??
|
|
89
|
+
exitCodeFromE2BResult(error) ??
|
|
90
|
+
exitCodeFromE2BResult(handle));
|
|
91
|
+
try {
|
|
92
|
+
await entry.sendInput(`${command}\n`);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
await entry.terminate().catch(() => { });
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
const { sessionId, pruned } = this.ptyProcesses.register(entry);
|
|
99
|
+
if (pruned) {
|
|
100
|
+
await pruned.terminate?.().catch(() => { });
|
|
101
|
+
}
|
|
102
|
+
return await formatPtyExecUpdate({
|
|
103
|
+
registry: this.ptyProcesses,
|
|
104
|
+
sessionId,
|
|
105
|
+
entry,
|
|
106
|
+
startTime: start,
|
|
107
|
+
yieldTimeMs: args.yieldTimeMs,
|
|
108
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
exposedPortSource() {
|
|
112
|
+
return 'host';
|
|
113
|
+
}
|
|
114
|
+
async resolveRemoteExposedPort(requestedPort) {
|
|
115
|
+
if (!this.sandbox.getHost) {
|
|
116
|
+
throw new SandboxProviderError('E2BSandboxClient exposed port resolution requires an E2B SDK getHost(port) API.', {
|
|
117
|
+
provider: 'e2b',
|
|
118
|
+
port: requestedPort,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
let host;
|
|
122
|
+
try {
|
|
123
|
+
host = await this.sandbox.getHost(requestedPort);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
throw new SandboxProviderError(`E2BSandboxClient failed to resolve exposed port ${requestedPort}.`, {
|
|
127
|
+
provider: 'e2b',
|
|
128
|
+
port: requestedPort,
|
|
129
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (typeof host !== 'string') {
|
|
133
|
+
throw new SandboxProviderError('E2BSandboxClient exposed port resolution returned a non-string host.', {
|
|
134
|
+
provider: 'e2b',
|
|
135
|
+
port: requestedPort,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return parseExposedPortEndpoint(host, {
|
|
139
|
+
providerName: 'E2BSandboxClient',
|
|
140
|
+
source: 'host',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async prepareWorkspaceRoot() {
|
|
144
|
+
const root = this.state.manifest.root;
|
|
145
|
+
await this.ensureDirectory(root);
|
|
146
|
+
const result = await this.sandbox.commands.run(`mkdir -p -- ${shellQuote(root)}`, {
|
|
147
|
+
cwd: '/',
|
|
148
|
+
envs: this.state.environment,
|
|
149
|
+
});
|
|
150
|
+
if ((result.exitCode ?? 1) !== 0) {
|
|
151
|
+
throw new SandboxProviderError('E2BSandboxClient failed to prepare the workspace root.', {
|
|
152
|
+
provider: 'e2b',
|
|
153
|
+
operation: 'prepare workspace root',
|
|
154
|
+
sandboxId: this.state.sandboxId,
|
|
155
|
+
root,
|
|
156
|
+
stderr: result.stderr ?? '',
|
|
157
|
+
stdout: result.stdout ?? '',
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async persistWorkspace() {
|
|
162
|
+
if (this.state.workspacePersistence === 'snapshot') {
|
|
163
|
+
const archive = await this.persistWorkspaceViaNativeSnapshot();
|
|
164
|
+
if (archive) {
|
|
165
|
+
return archive;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
assertTarWorkspacePersistence('E2BSandboxClient', this.state.workspacePersistence);
|
|
170
|
+
}
|
|
171
|
+
return await this.persistWorkspaceTar();
|
|
172
|
+
}
|
|
173
|
+
async hydrateWorkspace(data) {
|
|
174
|
+
const snapshotRef = decodeNativeSnapshotRef(data);
|
|
175
|
+
if (snapshotRef?.provider === 'e2b') {
|
|
176
|
+
await this.replaceSandboxFromSnapshot(snapshotRef.snapshotId);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (this.state.workspacePersistence !== 'snapshot') {
|
|
180
|
+
assertTarWorkspacePersistence('E2BSandboxClient', this.state.workspacePersistence);
|
|
181
|
+
}
|
|
182
|
+
await this.hydrateWorkspaceTar(data);
|
|
183
|
+
}
|
|
184
|
+
async persistWorkspaceViaNativeSnapshot() {
|
|
185
|
+
if (this.nativeSnapshotRequiresTarFallback()) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
if (!this.sandbox.createSnapshot) {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
let snapshot;
|
|
192
|
+
try {
|
|
193
|
+
snapshot = await this.sandbox.createSnapshot();
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
throw new SandboxProviderError('E2BSandboxClient failed to create a native workspace snapshot.', {
|
|
197
|
+
provider: 'e2b',
|
|
198
|
+
sandboxId: this.state.sandboxId,
|
|
199
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (!snapshot.snapshotId) {
|
|
203
|
+
throw new SandboxProviderError('E2BSandboxClient native snapshot persistence did not return a snapshot id.', {
|
|
204
|
+
provider: 'e2b',
|
|
205
|
+
sandboxId: this.state.sandboxId,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return encodeNativeSnapshotRef({
|
|
209
|
+
provider: 'e2b',
|
|
210
|
+
snapshotId: snapshot.snapshotId,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
nativeSnapshotRequiresTarFallback() {
|
|
214
|
+
return this.state.manifest.ephemeralPersistencePaths().size > 0;
|
|
215
|
+
}
|
|
216
|
+
async replaceSandboxFromSnapshot(snapshotId) {
|
|
217
|
+
const Sandbox = await loadE2BSandboxClass(this.state.sandboxType);
|
|
218
|
+
const previousSandbox = this.sandbox;
|
|
219
|
+
let sandbox;
|
|
220
|
+
try {
|
|
221
|
+
sandbox = await createSandboxInstance(Sandbox, {
|
|
222
|
+
...stateToCreateOptions(this.state),
|
|
223
|
+
template: snapshotId,
|
|
224
|
+
}, this.state.environment);
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
throw new SandboxProviderError('E2BSandboxClient failed to restore a native workspace snapshot.', {
|
|
228
|
+
provider: 'e2b',
|
|
229
|
+
snapshotId,
|
|
230
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
await previousSandbox.kill();
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
await sandbox.kill().catch(() => { });
|
|
238
|
+
throw new SandboxProviderError('E2BSandboxClient failed to terminate the previous sandbox while restoring a native workspace snapshot.', {
|
|
239
|
+
provider: 'e2b',
|
|
240
|
+
operation: 'restore snapshot',
|
|
241
|
+
sandboxId: previousSandbox.sandboxId,
|
|
242
|
+
replacementSandboxId: sandbox.sandboxId,
|
|
243
|
+
snapshotId,
|
|
244
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
this.sandbox = sandbox;
|
|
248
|
+
this.state.sandboxId = sandbox.sandboxId;
|
|
249
|
+
delete this.state.exposedPorts;
|
|
250
|
+
}
|
|
251
|
+
async close() {
|
|
252
|
+
await withSandboxSpan('sandbox.stop', {
|
|
253
|
+
backend_id: 'e2b',
|
|
254
|
+
sandbox_id: this.state.sandboxId,
|
|
255
|
+
}, async () => {
|
|
256
|
+
await this.ptyProcesses.terminateAll();
|
|
257
|
+
if (this.state.pauseOnExit && canPauseE2BSandbox(this.sandbox)) {
|
|
258
|
+
await this.pauseOrKillAfterPauseFailure('close');
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
await this.unmountActiveMounts();
|
|
262
|
+
await this.sandbox.kill();
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
async shutdown(options) {
|
|
266
|
+
await withSandboxSpan('sandbox.shutdown', {
|
|
267
|
+
backend_id: 'e2b',
|
|
268
|
+
sandbox_id: this.state.sandboxId,
|
|
269
|
+
}, async () => {
|
|
270
|
+
await this.ptyProcesses.terminateAll();
|
|
271
|
+
if (this.shouldPauseOnCleanup(options)) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
await this.unmountActiveMounts();
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
async delete(options) {
|
|
278
|
+
await withSandboxSpan('sandbox.shutdown', {
|
|
279
|
+
backend_id: 'e2b',
|
|
280
|
+
sandbox_id: this.state.sandboxId,
|
|
281
|
+
}, async () => {
|
|
282
|
+
await this.ptyProcesses.terminateAll();
|
|
283
|
+
if (this.shouldPauseOnCleanup(options)) {
|
|
284
|
+
await this.pauseOrKillAfterPauseFailure('cleanup');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
await this.unmountActiveMounts();
|
|
288
|
+
await this.sandbox.kill();
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
shouldPauseOnCleanup(options) {
|
|
292
|
+
return (options?.reason === 'cleanup' &&
|
|
293
|
+
options.preserveOwnedSessions === true &&
|
|
294
|
+
this.state.pauseOnExit &&
|
|
295
|
+
canPauseE2BSandbox(this.sandbox));
|
|
296
|
+
}
|
|
297
|
+
async pauseOrKillAfterPauseFailure(operation) {
|
|
298
|
+
try {
|
|
299
|
+
await this.sandbox.pause();
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
catch (pauseError) {
|
|
303
|
+
try {
|
|
304
|
+
await this.unmountActiveMounts();
|
|
305
|
+
await this.sandbox.kill();
|
|
306
|
+
}
|
|
307
|
+
catch (killError) {
|
|
308
|
+
throw new SandboxProviderError('E2BSandboxClient failed to pause and then terminate the sandbox.', {
|
|
309
|
+
provider: 'e2b',
|
|
310
|
+
operation,
|
|
311
|
+
sandboxId: this.state.sandboxId,
|
|
312
|
+
pauseCause: pauseError instanceof Error
|
|
313
|
+
? pauseError.message
|
|
314
|
+
: String(pauseError),
|
|
315
|
+
killCause: killError instanceof Error
|
|
316
|
+
? killError.message
|
|
317
|
+
: String(killError),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
this.state.pauseOnExit = false;
|
|
321
|
+
this.state.pauseOnExitSupported = false;
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
assertExecRunAs(_runAs) {
|
|
326
|
+
void _runAs;
|
|
327
|
+
}
|
|
328
|
+
assertFilesystemRunAs(_runAs) {
|
|
329
|
+
void _runAs;
|
|
330
|
+
}
|
|
331
|
+
manifestMetadataSupport() {
|
|
332
|
+
return SANDBOX_MANIFEST_METADATA_SUPPORT;
|
|
333
|
+
}
|
|
334
|
+
manifestMaterializationOptions() {
|
|
335
|
+
return {
|
|
336
|
+
materializeMount: this.materializeMountEntry.bind(this),
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
async runRemoteCommand(command, options) {
|
|
340
|
+
const result = await this.runCommandForStatus(command, {
|
|
341
|
+
cwd: options.workdir,
|
|
342
|
+
envs: this.state.environment,
|
|
343
|
+
timeoutMs: options.timeoutMs ??
|
|
344
|
+
(options.kind === 'exec' ? this.commandTimeoutMs() : undefined),
|
|
345
|
+
user: options.runAs,
|
|
346
|
+
});
|
|
347
|
+
return {
|
|
348
|
+
status: result.exitCode ?? 1,
|
|
349
|
+
stdout: result.stdout ?? '',
|
|
350
|
+
stderr: [result.stderr ?? '', result.error ?? '']
|
|
351
|
+
.filter((value) => value.trim().length > 0)
|
|
352
|
+
.join('\n'),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
async mkdirRemote(path) {
|
|
356
|
+
await this.ensureDirectory(path);
|
|
357
|
+
}
|
|
358
|
+
async readRemoteText(path) {
|
|
359
|
+
if (this.sandbox.files.read) {
|
|
360
|
+
const result = await this.sandbox.files.read(path);
|
|
361
|
+
return typeof result === 'string'
|
|
362
|
+
? result
|
|
363
|
+
: new TextDecoder().decode(result);
|
|
364
|
+
}
|
|
365
|
+
const result = await this.runRemoteCommand(`cat ${shellQuote(path)}`, {
|
|
366
|
+
kind: 'path',
|
|
367
|
+
workdir: this.state.manifest.root,
|
|
368
|
+
});
|
|
369
|
+
if (result.status !== 0) {
|
|
370
|
+
throw new UserError(`Sandbox path not found: ${path}`);
|
|
371
|
+
}
|
|
372
|
+
return result.stdout ?? '';
|
|
373
|
+
}
|
|
374
|
+
async readRemoteFile(path) {
|
|
375
|
+
if (this.sandbox.files.read) {
|
|
376
|
+
const result = await this.sandbox.files.read(path, { format: 'bytes' });
|
|
377
|
+
return typeof result === 'string'
|
|
378
|
+
? new TextEncoder().encode(result)
|
|
379
|
+
: Uint8Array.from(result);
|
|
380
|
+
}
|
|
381
|
+
const encoded = await this.sandbox.commands.run(`base64 ${shellQuote(path)}`, {
|
|
382
|
+
cwd: this.state.manifest.root,
|
|
383
|
+
envs: this.state.environment,
|
|
384
|
+
});
|
|
385
|
+
if ((encoded.exitCode ?? 1) !== 0 || !encoded.stdout) {
|
|
386
|
+
throw new UserError(`Sandbox path not found: ${path}`);
|
|
387
|
+
}
|
|
388
|
+
return Uint8Array.from(Buffer.from(encoded.stdout.replace(/\s+/g, ''), 'base64'));
|
|
389
|
+
}
|
|
390
|
+
async writeRemoteFile(path, content) {
|
|
391
|
+
await this.sandbox.files.write(path, content);
|
|
392
|
+
}
|
|
393
|
+
async deleteRemotePath(path) {
|
|
394
|
+
await this.removeSandboxPath(path);
|
|
395
|
+
}
|
|
396
|
+
async ensureDirectory(path) {
|
|
397
|
+
if (this.sandbox.files.makeDir) {
|
|
398
|
+
await this.sandbox.files.makeDir(path);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
await this.sandbox.commands.run(`mkdir -p -- ${shellQuote(path)}`, {
|
|
402
|
+
cwd: '/',
|
|
403
|
+
envs: this.state.environment,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
async removeSandboxPath(path) {
|
|
407
|
+
if (this.sandbox.files.remove) {
|
|
408
|
+
await this.sandbox.files.remove(path);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
await this.sandbox.commands.run(`rm -f -- ${shellQuote(path)}`, {
|
|
412
|
+
cwd: this.state.manifest.root,
|
|
413
|
+
envs: this.state.environment,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
async materializeMountEntry(absolutePath, entry) {
|
|
417
|
+
if (!isE2BCloudBucketMountEntry(entry)) {
|
|
418
|
+
throw new SandboxUnsupportedFeatureError('E2BSandboxClient only supports E2BCloudBucketMountStrategy mount entries.', {
|
|
419
|
+
provider: 'e2b',
|
|
420
|
+
feature: 'entry.mountStrategy',
|
|
421
|
+
path: absolutePath,
|
|
422
|
+
mountType: entry.type,
|
|
423
|
+
strategyType: entry.mountStrategy?.type,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
const mountPath = await this.resolveRemotePath(entry.mountPath ?? absolutePath, { forWrite: true });
|
|
427
|
+
await mountRcloneCloudBucket({
|
|
428
|
+
providerName: 'E2BSandboxClient',
|
|
429
|
+
providerId: 'e2b',
|
|
430
|
+
strategyType: 'e2b_cloud_bucket',
|
|
431
|
+
entry,
|
|
432
|
+
mountPath,
|
|
433
|
+
pattern: rclonePatternFromMountStrategy(entry.mountStrategy),
|
|
434
|
+
runCommand: this.mountCommandRunner(),
|
|
435
|
+
writeFile: this.writeRemoteFile.bind(this),
|
|
436
|
+
packageManagers: ['apt'],
|
|
437
|
+
installRcloneViaScript: true,
|
|
438
|
+
});
|
|
439
|
+
this.activeMountPaths.add(mountPath);
|
|
440
|
+
}
|
|
441
|
+
async unmountActiveMounts() {
|
|
442
|
+
for (const mountPath of [...this.activeMountPaths].reverse()) {
|
|
443
|
+
await unmountRcloneMount({
|
|
444
|
+
providerName: 'E2BSandboxClient',
|
|
445
|
+
providerId: 'e2b',
|
|
446
|
+
mountPath,
|
|
447
|
+
runCommand: this.mountCommandRunner(),
|
|
448
|
+
}).catch(() => { });
|
|
449
|
+
this.activeMountPaths.delete(mountPath);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
mountCommandRunner() {
|
|
453
|
+
return async (command, options = {}) => {
|
|
454
|
+
const result = await this.runCommandForStatus(command, {
|
|
455
|
+
cwd: this.state.manifest.root,
|
|
456
|
+
envs: this.state.environment,
|
|
457
|
+
timeoutMs: options.timeoutMs,
|
|
458
|
+
user: options.user,
|
|
459
|
+
});
|
|
460
|
+
return {
|
|
461
|
+
status: result.exitCode ?? 1,
|
|
462
|
+
stdout: result.stdout ?? '',
|
|
463
|
+
stderr: result.stderr ?? result.error ?? '',
|
|
464
|
+
};
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
async runCommandForStatus(command, options) {
|
|
468
|
+
try {
|
|
469
|
+
return await this.sandbox.commands.run(command, options);
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
const result = commandResultFromE2BError(error);
|
|
473
|
+
if (!result) {
|
|
474
|
+
throw error;
|
|
475
|
+
}
|
|
476
|
+
return result;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
commandTimeoutMs() {
|
|
480
|
+
return typeof this.state.commandTimeoutMs === 'number'
|
|
481
|
+
? this.state.commandTimeoutMs
|
|
482
|
+
: typeof this.state.timeout === 'number'
|
|
483
|
+
? Math.max(1, Math.trunc(this.state.timeout * 1000))
|
|
484
|
+
: undefined;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* @see {@link https://e2b.dev/docs | E2B docs}.
|
|
489
|
+
* @see {@link https://e2b.dev/docs/sdk-reference/js-sdk/v2.8.0/sandbox | JavaScript SDK Sandbox reference}.
|
|
490
|
+
* @see {@link https://e2b.dev/docs/sdk-reference/code-interpreter-js-sdk/v2.3.2/sandbox | Code Interpreter JavaScript SDK Sandbox reference}.
|
|
491
|
+
*/
|
|
492
|
+
export class E2BSandboxClient {
|
|
493
|
+
backendId = 'e2b';
|
|
494
|
+
options;
|
|
495
|
+
constructor(options = {}) {
|
|
496
|
+
this.options = options;
|
|
497
|
+
}
|
|
498
|
+
async create(args, manifestOptions) {
|
|
499
|
+
const createArgs = normalizeSandboxClientCreateArgs(args, manifestOptions);
|
|
500
|
+
assertCoreSnapshotUnsupported('E2BSandboxClient', createArgs.snapshot);
|
|
501
|
+
const manifest = createArgs.manifest;
|
|
502
|
+
return await withSandboxSpan('sandbox.start', {
|
|
503
|
+
backend_id: this.backendId,
|
|
504
|
+
}, async () => {
|
|
505
|
+
const resolvedOptions = {
|
|
506
|
+
...this.options,
|
|
507
|
+
...createArgs.options,
|
|
508
|
+
};
|
|
509
|
+
validateOptions(resolvedOptions);
|
|
510
|
+
assertSandboxManifestMetadataSupported('E2BSandboxClient', manifest, SANDBOX_MANIFEST_METADATA_SUPPORT);
|
|
511
|
+
const Sandbox = await loadE2BSandboxClass(resolvedOptions.sandboxType ?? 'e2b');
|
|
512
|
+
const environment = await materializeEnvironment(manifest, resolvedOptions.env);
|
|
513
|
+
const sandbox = await createSandboxInstance(Sandbox, resolvedOptions, environment);
|
|
514
|
+
const session = new E2BSandboxSession({
|
|
515
|
+
sandbox,
|
|
516
|
+
concurrencyLimits: createArgs.concurrencyLimits,
|
|
517
|
+
state: {
|
|
518
|
+
manifest,
|
|
519
|
+
sandboxId: sandbox.sandboxId,
|
|
520
|
+
template: resolvedOptions.template,
|
|
521
|
+
sandboxType: resolvedOptions.sandboxType ?? 'e2b',
|
|
522
|
+
timeout: resolvedOptions.timeout,
|
|
523
|
+
commandTimeoutMs: resolvedOptions.commandTimeoutMs,
|
|
524
|
+
requestTimeoutMs: resolvedOptions.requestTimeoutMs,
|
|
525
|
+
connectionTimeoutMs: resolvedOptions.connectionTimeoutMs,
|
|
526
|
+
onTimeout: resolveE2BOnTimeout(resolvedOptions),
|
|
527
|
+
timeoutAction: resolvedOptions.timeoutAction,
|
|
528
|
+
autoResume: resolvedOptions.autoResume,
|
|
529
|
+
metadata: resolvedOptions.metadata,
|
|
530
|
+
secure: resolvedOptions.secure,
|
|
531
|
+
allowInternetAccess: resolvedOptions.allowInternetAccess,
|
|
532
|
+
configuredExposedPorts: resolvedOptions.exposedPorts,
|
|
533
|
+
workspacePersistence: resolvedOptions.workspacePersistence,
|
|
534
|
+
mcp: resolvedOptions.mcp,
|
|
535
|
+
pauseOnExit: resolvedOptions.pauseOnExit ?? false,
|
|
536
|
+
environment,
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
try {
|
|
540
|
+
await session.prepareWorkspaceRoot();
|
|
541
|
+
await session.applyManifest(manifest);
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
session.state.pauseOnExit = false;
|
|
545
|
+
await closeRemoteSessionOnManifestError('E2B', session, error);
|
|
546
|
+
}
|
|
547
|
+
return session;
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
async serializeSessionState(state) {
|
|
551
|
+
return serializeRemoteSandboxSessionState(state);
|
|
552
|
+
}
|
|
553
|
+
canPersistOwnedSessionState(state) {
|
|
554
|
+
return state.pauseOnExit && state.pauseOnExitSupported === true;
|
|
555
|
+
}
|
|
556
|
+
async deserializeSessionState(state) {
|
|
557
|
+
const baseState = deserializeRemoteSandboxSessionStateValues(state, this.options.env);
|
|
558
|
+
return {
|
|
559
|
+
...state,
|
|
560
|
+
...baseState,
|
|
561
|
+
sandboxId: readString(state, 'sandboxId'),
|
|
562
|
+
template: readOptionalString(state, 'template'),
|
|
563
|
+
sandboxType: state.sandboxType === 'e2b_code_interpreter'
|
|
564
|
+
? 'e2b_code_interpreter'
|
|
565
|
+
: 'e2b',
|
|
566
|
+
timeout: readOptionalNumber(state, 'timeout'),
|
|
567
|
+
commandTimeoutMs: readOptionalNumber(state, 'commandTimeoutMs'),
|
|
568
|
+
requestTimeoutMs: readOptionalNumber(state, 'requestTimeoutMs'),
|
|
569
|
+
connectionTimeoutMs: readOptionalNumber(state, 'connectionTimeoutMs'),
|
|
570
|
+
onTimeout: readOptionalE2BTimeoutAction(readOptionalString(state, 'onTimeout') ??
|
|
571
|
+
readOptionalString(state, 'timeoutAction')),
|
|
572
|
+
timeoutAction: readOptionalE2BTimeoutAction(readOptionalString(state, 'timeoutAction')),
|
|
573
|
+
autoResume: readOptionalBoolean(state, 'autoResume'),
|
|
574
|
+
metadata: readOptionalStringRecord(state.metadata),
|
|
575
|
+
secure: readOptionalBoolean(state, 'secure'),
|
|
576
|
+
allowInternetAccess: readOptionalBoolean(state, 'allowInternetAccess'),
|
|
577
|
+
configuredExposedPorts: readOptionalNumberArray(state.configuredExposedPorts),
|
|
578
|
+
workspacePersistence: readE2BWorkspacePersistence(state.workspacePersistence),
|
|
579
|
+
mcp: readOptionalRecord(state.mcp),
|
|
580
|
+
pauseOnExit: Boolean(state.pauseOnExit),
|
|
581
|
+
pauseOnExitSupported: readOptionalBoolean(state, 'pauseOnExitSupported') ?? false,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
async resume(state) {
|
|
585
|
+
const Sandbox = await loadE2BSandboxClass(state.sandboxType);
|
|
586
|
+
const connect = Sandbox.connect ?? Sandbox.resume;
|
|
587
|
+
if (connect) {
|
|
588
|
+
try {
|
|
589
|
+
const sandbox = await connect.call(Sandbox, state.sandboxId, e2bReconnectOptions(state));
|
|
590
|
+
return new E2BSandboxSession({ state, sandbox });
|
|
591
|
+
}
|
|
592
|
+
catch (error) {
|
|
593
|
+
assertResumeRecreateAllowed(error, {
|
|
594
|
+
providerName: 'E2BSandboxClient',
|
|
595
|
+
provider: 'e2b',
|
|
596
|
+
details: { sandboxId: state.sandboxId },
|
|
597
|
+
});
|
|
598
|
+
// Fall through to recreate from serialized state.
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return await this.create(state.manifest, {
|
|
602
|
+
...stateToCreateOptions(state),
|
|
603
|
+
env: state.environment,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function validateOptions(options) {
|
|
608
|
+
readE2BWorkspacePersistence(options.workspacePersistence);
|
|
609
|
+
resolveE2BOnTimeout(options);
|
|
610
|
+
}
|
|
611
|
+
function resolveE2BOnTimeout(options) {
|
|
612
|
+
return readOptionalE2BTimeoutAction(options.onTimeout ?? options.timeoutAction);
|
|
613
|
+
}
|
|
614
|
+
function readOptionalE2BTimeoutAction(value) {
|
|
615
|
+
if (value === undefined || value === 'pause' || value === 'kill') {
|
|
616
|
+
return value;
|
|
617
|
+
}
|
|
618
|
+
throw new SandboxUnsupportedFeatureError('E2BSandboxClient onTimeout must be "pause" or "kill".', {
|
|
619
|
+
provider: 'E2BSandboxClient',
|
|
620
|
+
feature: 'onTimeout',
|
|
621
|
+
onTimeout: value,
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
function readE2BWorkspacePersistence(value) {
|
|
625
|
+
if (value === undefined ||
|
|
626
|
+
value === true ||
|
|
627
|
+
value === 'tar' ||
|
|
628
|
+
value === 'snapshot') {
|
|
629
|
+
return value;
|
|
630
|
+
}
|
|
631
|
+
throw new SandboxUnsupportedFeatureError('E2BSandboxClient workspacePersistence must be true, "tar", or "snapshot".', {
|
|
632
|
+
provider: 'E2BSandboxClient',
|
|
633
|
+
feature: 'workspacePersistence',
|
|
634
|
+
workspacePersistence: value,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
function stateToCreateOptions(state) {
|
|
638
|
+
return {
|
|
639
|
+
sandboxType: state.sandboxType,
|
|
640
|
+
template: state.template,
|
|
641
|
+
timeout: state.timeout,
|
|
642
|
+
commandTimeoutMs: state.commandTimeoutMs,
|
|
643
|
+
requestTimeoutMs: state.requestTimeoutMs,
|
|
644
|
+
connectionTimeoutMs: state.connectionTimeoutMs,
|
|
645
|
+
onTimeout: state.onTimeout,
|
|
646
|
+
timeoutAction: state.timeoutAction,
|
|
647
|
+
autoResume: state.autoResume,
|
|
648
|
+
metadata: state.metadata,
|
|
649
|
+
secure: state.secure,
|
|
650
|
+
allowInternetAccess: state.allowInternetAccess,
|
|
651
|
+
exposedPorts: state.configuredExposedPorts,
|
|
652
|
+
workspacePersistence: state.workspacePersistence,
|
|
653
|
+
mcp: state.mcp,
|
|
654
|
+
pauseOnExit: state.pauseOnExit,
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
function e2bReconnectOptions(state) {
|
|
658
|
+
const options = {};
|
|
659
|
+
if (state.timeout !== undefined) {
|
|
660
|
+
options.timeoutMs = Math.max(1, Math.trunc(state.timeout * 1000));
|
|
661
|
+
}
|
|
662
|
+
if (state.requestTimeoutMs !== undefined) {
|
|
663
|
+
options.requestTimeoutMs = state.requestTimeoutMs;
|
|
664
|
+
}
|
|
665
|
+
if (state.connectionTimeoutMs !== undefined) {
|
|
666
|
+
options.connectionTimeoutMs = state.connectionTimeoutMs;
|
|
667
|
+
}
|
|
668
|
+
return Object.keys(options).length > 0 ? options : undefined;
|
|
669
|
+
}
|
|
670
|
+
function canPauseE2BSandbox(sandbox) {
|
|
671
|
+
return typeof sandbox.pause === 'function';
|
|
672
|
+
}
|
|
673
|
+
async function loadE2BSandboxClass(sandboxType) {
|
|
674
|
+
const moduleName = sandboxType === 'e2b_code_interpreter' ? '@e2b/code-interpreter' : 'e2b';
|
|
675
|
+
try {
|
|
676
|
+
const Sandbox = sandboxType === 'e2b_code_interpreter'
|
|
677
|
+
? (await import('@e2b/code-interpreter')).Sandbox
|
|
678
|
+
: (await import('e2b')).Sandbox;
|
|
679
|
+
if (!Sandbox) {
|
|
680
|
+
throw new Error(`Missing Sandbox export from ${moduleName}.`);
|
|
681
|
+
}
|
|
682
|
+
return adaptE2BSandboxClass(Sandbox);
|
|
683
|
+
}
|
|
684
|
+
catch (error) {
|
|
685
|
+
throw new UserError(`E2B sandbox support requires the optional \`${moduleName}\` package. Install it before using E2B-backed sandbox examples. ${error.message}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
function adaptE2BSandboxClass(Sandbox) {
|
|
689
|
+
const sdkSandbox = Sandbox;
|
|
690
|
+
const adapted = {
|
|
691
|
+
create: Sandbox.create.bind(Sandbox),
|
|
692
|
+
};
|
|
693
|
+
if (typeof sdkSandbox.connect === 'function') {
|
|
694
|
+
adapted.connect = sdkSandbox.connect.bind(Sandbox);
|
|
695
|
+
}
|
|
696
|
+
if (typeof sdkSandbox.resume === 'function') {
|
|
697
|
+
adapted.resume = sdkSandbox.resume.bind(Sandbox);
|
|
698
|
+
}
|
|
699
|
+
return adapted;
|
|
700
|
+
}
|
|
701
|
+
async function createSandboxInstance(Sandbox, options, environment) {
|
|
702
|
+
const createOptions = {};
|
|
703
|
+
if (typeof options.timeout === 'number') {
|
|
704
|
+
createOptions.timeoutMs = Math.max(1, Math.trunc(options.timeout * 1000));
|
|
705
|
+
}
|
|
706
|
+
const onTimeout = resolveE2BOnTimeout(options);
|
|
707
|
+
const lifecycle = {};
|
|
708
|
+
if (onTimeout !== undefined) {
|
|
709
|
+
lifecycle.onTimeout = onTimeout;
|
|
710
|
+
}
|
|
711
|
+
if (options.autoResume !== undefined && onTimeout === 'pause') {
|
|
712
|
+
lifecycle.autoResume = options.autoResume;
|
|
713
|
+
}
|
|
714
|
+
if (Object.keys(lifecycle).length > 0) {
|
|
715
|
+
createOptions.lifecycle = lifecycle;
|
|
716
|
+
}
|
|
717
|
+
for (const key of [
|
|
718
|
+
'commandTimeoutMs',
|
|
719
|
+
'requestTimeoutMs',
|
|
720
|
+
'connectionTimeoutMs',
|
|
721
|
+
'metadata',
|
|
722
|
+
'secure',
|
|
723
|
+
'allowInternetAccess',
|
|
724
|
+
'mcp',
|
|
725
|
+
]) {
|
|
726
|
+
if (options[key] !== undefined) {
|
|
727
|
+
createOptions[key] = options[key];
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (options.exposedPorts?.length) {
|
|
731
|
+
createOptions.network = {
|
|
732
|
+
allowPublicTraffic: true,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
if (Object.keys(environment).length > 0) {
|
|
736
|
+
createOptions.envs = environment;
|
|
737
|
+
}
|
|
738
|
+
if (options.template) {
|
|
739
|
+
return await withProviderError('E2BSandboxClient', 'e2b', 'create sandbox', async () => await Sandbox.create(options.template, createOptions), { template: options.template });
|
|
740
|
+
}
|
|
741
|
+
if (Object.keys(createOptions).length > 0) {
|
|
742
|
+
return await withProviderError('E2BSandboxClient', 'e2b', 'create sandbox', async () => await Sandbox.create(createOptions));
|
|
743
|
+
}
|
|
744
|
+
return await withProviderError('E2BSandboxClient', 'e2b', 'create sandbox', async () => await Sandbox.create());
|
|
745
|
+
}
|
|
746
|
+
function commandResultFromE2BError(error) {
|
|
747
|
+
return commandResultFromE2BErrorValue(error, new Set());
|
|
748
|
+
}
|
|
749
|
+
function commandResultFromE2BErrorValue(value, seen) {
|
|
750
|
+
if (!isRecord(value) || seen.has(value)) {
|
|
751
|
+
return undefined;
|
|
752
|
+
}
|
|
753
|
+
seen.add(value);
|
|
754
|
+
const directResult = commandResultFromRecord(value);
|
|
755
|
+
if (directResult) {
|
|
756
|
+
return directResult;
|
|
757
|
+
}
|
|
758
|
+
for (const key of ['result', 'context', 'data', 'details', 'cause']) {
|
|
759
|
+
const nestedResult = commandResultFromE2BErrorValue(value[key], seen);
|
|
760
|
+
if (nestedResult) {
|
|
761
|
+
return nestedResult;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return undefined;
|
|
765
|
+
}
|
|
766
|
+
function commandResultFromRecord(value) {
|
|
767
|
+
const exitCode = typeof value.exitCode === 'number' ? value.exitCode : undefined;
|
|
768
|
+
const stdout = typeof value.stdout === 'string' ? value.stdout : undefined;
|
|
769
|
+
const stderr = typeof value.stderr === 'string'
|
|
770
|
+
? value.stderr
|
|
771
|
+
: typeof value.error === 'string'
|
|
772
|
+
? value.error
|
|
773
|
+
: exitCode !== undefined && typeof value.message === 'string'
|
|
774
|
+
? value.message
|
|
775
|
+
: undefined;
|
|
776
|
+
if (exitCode === undefined && stdout === undefined && stderr === undefined) {
|
|
777
|
+
return undefined;
|
|
778
|
+
}
|
|
779
|
+
return {
|
|
780
|
+
exitCode: exitCode ?? 1,
|
|
781
|
+
stdout,
|
|
782
|
+
stderr,
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
function exitCodeFromE2BResult(value) {
|
|
786
|
+
if (!isRecord(value)) {
|
|
787
|
+
return undefined;
|
|
788
|
+
}
|
|
789
|
+
return typeof value.exitCode === 'number' ? value.exitCode : undefined;
|
|
790
|
+
}
|
|
791
|
+
//# sourceMappingURL=sandbox.mjs.map
|