@evolvingmachines/e2b 0.0.55 → 0.0.57
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/README.md +2 -2
- package/dist/index.cjs +8 -2
- package/dist/index.d.cts +313 -3
- package/dist/index.d.ts +313 -3
- package/dist/index.js +8 -2
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
# Evolving Machines — Evolve E2B Provider
|
|
10
10
|
|
|
11
|
-
E2B sandbox provider for [Evolve SDK](https://www.npmjs.com/package/@evolvingmachines/
|
|
11
|
+
E2B sandbox provider for [Evolve SDK](https://www.npmjs.com/package/@evolvingmachines/evolve).
|
|
12
12
|
|
|
13
|
-
**Note:** This package is automatically installed as a dependency of `@evolvingmachines/
|
|
13
|
+
**Note:** This package is automatically installed as a dependency of `@evolvingmachines/evolve`. You don't need to install it separately.
|
|
14
14
|
|
|
15
15
|
Check out the [official documentation](https://github.com/evolving-machines-lab/evolve/tree/main/docs) and [cookbooks](https://github.com/evolving-machines-lab/evolve/tree/main/cookbooks).
|
|
16
16
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
'use strict';var codeInterpreter=require('@e2b/code-interpreter');
|
|
2
|
-
|
|
1
|
+
'use strict';var codeInterpreter=require('@e2b/code-interpreter');function b(s){if(!s||typeof s!="object")return false;if(s.name==="TimeoutError")return true;let e=s.message;return typeof e=="string"&&/timed?\s*out/i.test(e)}var k=100,I=100;function u(s){return s instanceof Date?s.toISOString():String(s)}function U(s){if(typeof s=="string"||s instanceof ArrayBuffer)return s;if(s instanceof Buffer)return new Uint8Array(s).buffer;if(s instanceof Uint8Array)return new Uint8Array(s).buffer;throw new Error(`Unsupported data type for file upload: ${typeof s}. Expected string, Buffer, ArrayBuffer, or Uint8Array.`)}var l=class extends Error{constructor(e){super(e),this.name="E2BResourcesError";}},B="2.25.0",g=class extends Error{constructor(e){super(e),this.name="E2BNetworkUpdateUnsupportedError";}};function x(s){if(s?.outbound==="open"&&s.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");let e=s?.allowedDestinations??[],t=s?.outbound==="blocked"&&e.length>0;return {allowInternetAccess:s?.outbound!=="blocked"||t,network:t?{denyOut:["0.0.0.0/0"],allowOut:e}:void 0}}var h=class extends Error{constructor(){super("E2B takes no create-time boot command: the sandbox boots its template's own init, and a start command is a template-BUILD field (Template startCmd) this provider never sets. The boot is already inert \u2014 run the process by exec after create, or bake it into the template the `image` names."),this.name="E2BBootCommandError";}},y=class extends Error{constructor(){super("E2B has no idle timeout: its sandbox timeout is an ABSOLUTE lifetime that in-sandbox activity never extends. Use `timeoutMs` for the lifetime, and call setTimeout() to extend a sandbox you know is still working."),this.name="E2BIdleTimeoutError";}},w=class{constructor(e,t){this.sandbox=e;this.defaultUser=t;}async run(e,t){try{let r=await this.sandbox.commands.run(e,{timeoutMs:t?.timeoutMs,envs:t?.envs,cwd:t?.cwd,user:this.defaultUser,onStdout:t?.onStdout,onStderr:t?.onStderr});return {exitCode:r.exitCode,stdout:r.stdout,stderr:r.stderr}}catch(r){if(r&&typeof r=="object"&&"exitCode"in r){let n=r;return {exitCode:n.exitCode,stdout:n.stdout??"",stderr:n.stderr??""}}if(b(r))return {exitCode:124,stdout:"",stderr:r instanceof Error?r.message:"command timed out"};throw r}}async spawn(e,t){let r=await this.sandbox.commands.run(e,{background:true,stdin:t?.stdin??true,timeoutMs:t?.timeoutMs,envs:t?.envs,cwd:t?.cwd,user:this.defaultUser,onStdout:t?.onStdout,onStderr:t?.onStderr});return {processId:String(r.pid),wait:async()=>{try{let n=await r.wait();return {exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr}}catch(n){if(n&&typeof n=="object"&&"exitCode"in n){let i=n;return {exitCode:i.exitCode,stdout:i.stdout??"",stderr:i.stderr??""}}if(b(n))return {exitCode:124,stdout:"",stderr:n instanceof Error?n.message:"command timed out"};throw n}},kill:()=>this.sandbox.commands.kill(r.pid)}}async list(){return (await this.sandbox.commands.list()).map(t=>({...t,processId:String(t.pid)}))}async connect(e,t){let r=await this.sandbox.commands.connect(Number(e),{onStdout:t?.onStdout,onStderr:t?.onStderr,timeoutMs:t?.timeoutMs});return {processId:String(r.pid),wait:async()=>{try{let n=await r.wait();return {exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr}}catch(n){if(n&&typeof n=="object"&&"exitCode"in n){let i=n;return {exitCode:i.exitCode,stdout:i.stdout??"",stderr:i.stderr??""}}if(b(n))return {exitCode:124,stdout:"",stderr:n instanceof Error?n.message:"command timed out"};throw n}},kill:()=>this.sandbox.commands.kill(r.pid)}}async sendStdin(e,t){await this.sandbox.commands.sendStdin(Number(e),t);}async kill(e){return this.sandbox.commands.kill(Number(e))}},S=class{constructor(e,t){this.sandbox=e;this.defaultUser=t;}async read(e){let t=await this.sandbox.files.read(e,{format:"bytes",requestTimeoutMs:3e5,user:this.defaultUser});if(!t.includes(0))try{return new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}).decode(t)}catch{}return t}async write(e,t){await this.sandbox.files.write(e,U(t),{requestTimeoutMs:3e5,user:this.defaultUser});}async writeBatch(e){let t=e.map(r=>({path:r.path,data:U(r.data)}));await this.sandbox.files.write(t,{requestTimeoutMs:3e5,user:this.defaultUser});}async makeDir(e){await this.sandbox.files.makeDir(e,{user:this.defaultUser});}async uploadUrl(e,t){return this.sandbox.uploadUrl(e,{user:this.defaultUser,...t?{useSignatureExpiration:t}:{}})}async downloadUrl(e,t){return this.sandbox.downloadUrl(e,{user:this.defaultUser,...t?{useSignatureExpiration:t}:{}})}async exists(e){return this.sandbox.files.exists(e,{user:this.defaultUser})}async list(e){return (await this.sandbox.files.list(e,{user:this.defaultUser})).map(r=>({name:r.name,path:r.path,type:r.type==="dir"?"dir":"file"}))}async remove(e){await this.sandbox.files.remove(e,{user:this.defaultUser});}async rename(e,t){await this.sandbox.files.rename(e,t,{user:this.defaultUser});}async readStream(e){return this.sandbox.files.read(e,{format:"stream",user:this.defaultUser})}async writeStream(e,t){await this.sandbox.files.write(e,t,{user:this.defaultUser});}async writeFromPath(e,t){let{createReadStream:r,statSync:n}=await import('fs'),{Readable:i}=await import('stream'),{randomBytes:d}=await import('crypto'),a=n(t).size,p=`----evolve${d(16).toString("hex")}`,v=Buffer.from(`--${p}\r
|
|
2
|
+
Content-Disposition: form-data; name="file"; filename="blob"\r
|
|
3
|
+
Content-Type: application/octet-stream\r
|
|
4
|
+
\r
|
|
5
|
+
`),A=Buffer.from(`\r
|
|
6
|
+
--${p}--\r
|
|
7
|
+
`);async function*C(){yield v;for await(let o of r(t))yield o;yield A;}try{let o=await this.uploadUrl(e),m=await fetch(o,{method:"POST",body:i.toWeb(i.from(C())),headers:{"Content-Type":`multipart/form-data; boundary=${p}`,"Content-Length":String(v.length+a+A.length)},duplex:"half"});if(!m.ok)throw new Error(`streamed upload to ${e} failed (HTTP ${m.status})`)}catch(o){console.warn(`[e2b] streamed upload of ${t} failed, falling back to a buffered write:`,o instanceof Error?o.message:o);let m=i.toWeb(r(t));await this.writeStream(e,m);}}async watchDir(e,t,r){let n=await this.sandbox.files.watchDir(e,t,{recursive:r?.recursive,timeoutMs:r?.timeoutMs,user:this.defaultUser,onExit:r?.onExit});return {stop:()=>n.stop()}}},c=class{constructor(e,t,r,n){this.sandbox=e;this.apiKey=t;this.apiUrl=r;this.commands=new w(e,n),this.files=new S(e,n);}commands;files;get sandboxId(){return this.sandbox.sandboxId}getHost(e){return Promise.resolve(this.sandbox.getHost(e))}async isRunning(){return this.sandbox.isRunning()}async getInfo(){let e=await this.sandbox.getInfo();return {sandboxId:e.sandboxId,image:e.templateId,name:e.name,metadata:e.metadata??{},startedAt:u(e.startedAt),endAt:e.endAt?u(e.endAt):void 0}}async updateNetwork(e){let t=x(e),r=this.sandbox;if(typeof r.updateNetwork!="function")throw new g(`The installed e2b client cannot change a running sandbox's network policy: Sandbox.updateNetwork requires e2b >= ${B}. Upgrade the e2b dependency (it is pulled in by @e2b/code-interpreter), or create the sandbox with the policy it needs for its whole lifetime.`);await r.updateNetwork({allowInternetAccess:t.allowInternetAccess,...t.network??{}});}async kill(){try{await this.sandbox.kill();}catch{await new Promise(e=>setTimeout(e,500)),await this.sandbox.kill();}}async pause(){await this.sandbox.betaPause({apiKey:this.apiKey,apiUrl:this.apiUrl});}},P=class{providerType="e2b";name="E2B";apiKey;apiUrl;defaultTimeoutMs;templateId;sandboxUsers=new Map;constructor(e){this.apiKey=e.apiKey,this.apiUrl=e.apiUrl,this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.templateId=e.templateId;}async create(e){if(e.bootCommand!==void 0)throw new h;if(e.idleTimeoutMs!==void 0)throw new y;if(e.resources?.gpu!==void 0||e.resources?.gpuTypes!==void 0)throw new l("E2B offers no GPU allocation at any tier \u2014 a `resources.gpu` request cannot be enforced. Run GPU workloads on a provider that reserves GPUs (modal).");if(e.resources&&(e.resources.cpu!==void 0||e.resources.memory!==void 0||e.resources.disk!==void 0))throw new l("E2B cannot size a sandbox at create time: sizing is fixed by the template (Template.build cpuCount/memoryMB; disk is plan-fixed). Build a template with the desired resources and pass it as `image` instead of `resources`.");let t=e.timeoutMs??this.defaultTimeoutMs,r=e.image??this.templateId??"evolve-all";for(let d of e.phaseNetworkPolicies??[])x(d);let n=x(e.network),i=await codeInterpreter.Sandbox.create(r,{apiKey:this.apiKey,apiUrl:this.apiUrl,envs:e.envs,metadata:e.metadata,timeoutMs:t,...n});return e.workingDirectory&&await i.files.makeDir(e.workingDirectory,{user:e.user}),e.user&&this.sandboxUsers.set(i.sandboxId,e.user),new c(i,this.apiKey,this.apiUrl,e.user)}async connect(e,t){let r=await codeInterpreter.Sandbox.connect(e,{apiKey:this.apiKey,apiUrl:this.apiUrl,timeoutMs:t??this.defaultTimeoutMs});return new c(r,this.apiKey,this.apiUrl,this.sandboxUsers.get(e))}async list(e){let t=await this.paginate(e);if(t.error&&!t.stoppedAtLimit)throw new Error(t.error);return t.sandboxes}async listAll(e){let{stoppedAtLimit:t,...r}=await this.paginate(e);return r}async paginate(e){let t=e?.limit;return E(codeInterpreter.Sandbox.list({apiKey:this.apiKey,apiUrl:this.apiUrl,query:{state:e?.state,metadata:e?.metadata},limit:t===void 0?k:Math.min(t,k)}),t)}};async function E(s,e){let t=[],r=0,n=new Set;for(;s.hasNext;){if(r>=I)return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:false,error:`sandbox list exceeded ${I} pages`};let i=s.nextToken;if(i!==void 0){if(n.has(i))return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:false,error:"pagination token repeated"};n.add(i);}let d;try{d=await s.nextItems();}catch(a){return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:false,error:`sandbox list failed: ${a instanceof Error?a.message:String(a)}`}}r+=1;for(let a of d){if(e!==void 0&&t.length>=e)return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:true,error:`stopped at the requested limit of ${e} with more sandboxes available`};t.push({sandboxId:a.sandboxId,image:a.templateId,name:a.name,metadata:a.metadata??{},startedAt:u(a.startedAt),endAt:a.endAt?u(a.endAt):void 0});}if(e!==void 0&&t.length>=e&&s.hasNext)return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:true,error:`stopped at the requested limit of ${e} with more sandboxes available`};if(e!==void 0&&t.length>=e)break}return {sandboxes:t,complete:true,pagesFetched:r,stoppedAtLimit:false}}var T=E;function M(s={}){let e=s.apiKey??process.env.E2B_API_KEY;if(!e)throw new Error("E2B API key required. Set E2B_API_KEY environment variable or pass apiKey in config. Get your key at https://e2b.dev/sign-in");return new P({...s,apiKey:e})}
|
|
8
|
+
exports.E2BBootCommandError=h;exports.E2BCommands=w;exports.E2BFiles=S;exports.E2BIdleTimeoutError=y;exports.E2BNetworkUpdateUnsupportedError=g;exports.E2BProvider=P;exports.E2BResourcesError=l;exports.E2B_MAX_LIST_PAGES=I;exports.E2B_MAX_PAGE_SIZE=k;exports.E2B_MIN_UPDATE_NETWORK_VERSION=B;exports._testCollectSandboxPages=T;exports.createE2BProvider=M;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Sandbox } from '@e2b/code-interpreter';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* E2B Sandbox Provider - Clean Architecture
|
|
3
5
|
*
|
|
@@ -11,6 +13,72 @@
|
|
|
11
13
|
* - Configuration externalized (no hardcoded mappings)
|
|
12
14
|
* - Clear naming (run = blocking, spawn = background)
|
|
13
15
|
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Largest page E2B will serve. Not a taste choice — a larger `limit` is refused
|
|
19
|
+
* outright: `400 validation error: parameter "limit" in query has an error:
|
|
20
|
+
* number must be at most 100`.
|
|
21
|
+
*/
|
|
22
|
+
declare const E2B_MAX_PAGE_SIZE = 100;
|
|
23
|
+
/**
|
|
24
|
+
* Page ceiling for one enumeration — 10,000 sandboxes at the maximum page size.
|
|
25
|
+
* A walk that reaches it stops and reports itself INCOMPLETE, because the one
|
|
26
|
+
* thing a fleet enumeration may never do is return a short list that reads like
|
|
27
|
+
* a whole one.
|
|
28
|
+
*/
|
|
29
|
+
declare const E2B_MAX_LIST_PAGES = 100;
|
|
30
|
+
/**
|
|
31
|
+
* Typed error for create-time sizing requests E2B cannot enforce.
|
|
32
|
+
* E2B sandboxes inherit cpu/memory from their TEMPLATE (Template.build
|
|
33
|
+
* cpuCount/memoryMB; disk is plan-fixed) — Sandbox.create has no sizing
|
|
34
|
+
* parameters, so a `resources` request would be silently ignored. Per the
|
|
35
|
+
* provider law (reject what you cannot enforce) it is refused loudly here.
|
|
36
|
+
*/
|
|
37
|
+
declare class E2BResourcesError extends Error {
|
|
38
|
+
constructor(message: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Minimum `e2b` client that can switch a running sandbox's egress.
|
|
42
|
+
*
|
|
43
|
+
* 2.25.0, per e2b's own release notes: "Add `Sandbox.updateNetwork` /
|
|
44
|
+
* `update_network` to replace a running sandbox's egress configuration"
|
|
45
|
+
* (e2b@2.25.0). Confirmed against the published type declarations — absent in
|
|
46
|
+
* 2.24.0, present in 2.25.0.
|
|
47
|
+
*
|
|
48
|
+
* IT SAID 2.26.0 UNTIL THE RELEASE NOTES WERE READ. That number came from a
|
|
49
|
+
* binary search over published tarballs that happened to sample only even
|
|
50
|
+
* minors, so 2.25.0 was never tested and the first "present" result looked
|
|
51
|
+
* like the boundary. The search was sound and the conclusion was still wrong
|
|
52
|
+
* by one release — which is the argument for reading the vendor's own record
|
|
53
|
+
* rather than inferring version history from artifacts.
|
|
54
|
+
*/
|
|
55
|
+
declare const E2B_MIN_UPDATE_NETWORK_VERSION = "2.25.0";
|
|
56
|
+
/**
|
|
57
|
+
* Typed error for a runtime egress switch the INSTALLED client cannot make.
|
|
58
|
+
*
|
|
59
|
+
* E2B the service supports it; an older `e2b` package simply has no method to
|
|
60
|
+
* call. That distinction matters to whoever reads this: the fix is a
|
|
61
|
+
* dependency bump, not a plan change or a different provider. Refused loudly
|
|
62
|
+
* rather than skipped, because a policy switch that quietly does nothing
|
|
63
|
+
* leaves the sandbox running under the previous policy — which, when the
|
|
64
|
+
* switch was meant to TIGHTEN egress, is the failure nobody notices.
|
|
65
|
+
*/
|
|
66
|
+
declare class E2BNetworkUpdateUnsupportedError extends Error {
|
|
67
|
+
constructor(message: string);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Typed error for an idle timeout E2B cannot enforce. E2B has exactly one
|
|
71
|
+
* clock and it is absolute: the sandbox is killed when its timeout expires,
|
|
72
|
+
* and only explicit client calls (create, connect, setTimeout) move that
|
|
73
|
+
* deadline — never what the sandbox is or is not doing. An `idleTimeoutMs`
|
|
74
|
+
* would therefore be silently ignored, so it is refused instead.
|
|
75
|
+
*/
|
|
76
|
+
declare class E2BBootCommandError extends Error {
|
|
77
|
+
constructor();
|
|
78
|
+
}
|
|
79
|
+
declare class E2BIdleTimeoutError extends Error {
|
|
80
|
+
constructor();
|
|
81
|
+
}
|
|
14
82
|
/** Result of a completed sandbox command */
|
|
15
83
|
interface SandboxCommandResult {
|
|
16
84
|
exitCode: number;
|
|
@@ -85,11 +153,71 @@ interface SandboxConnectOptions {
|
|
|
85
153
|
}
|
|
86
154
|
/** Options for creating a sandbox */
|
|
87
155
|
interface SandboxCreateOptions {
|
|
88
|
-
|
|
156
|
+
/**
|
|
157
|
+
* E2B template ID. OPTIONAL: create() falls back to the provider's
|
|
158
|
+
* configured `templateId`, then to "evolve-all" — declaring it required
|
|
159
|
+
* contradicted that implementation and only went unnoticed because TS
|
|
160
|
+
* method parameters are bivariant, so the SDK could already call
|
|
161
|
+
* create({}) through the shared contract.
|
|
162
|
+
*/
|
|
163
|
+
image?: string;
|
|
89
164
|
envs?: Record<string, string>;
|
|
90
165
|
metadata?: Record<string, string>;
|
|
91
166
|
timeoutMs?: number;
|
|
167
|
+
/**
|
|
168
|
+
* REJECTED with E2BIdleTimeoutError. E2B's timeout is an ABSOLUTE lifetime
|
|
169
|
+
* that in-sandbox activity never extends, so there is no inactivity clock to
|
|
170
|
+
* map this onto and honouring it would be a lie.
|
|
171
|
+
*/
|
|
172
|
+
idleTimeoutMs?: number;
|
|
173
|
+
/**
|
|
174
|
+
* REJECTED with E2BBootCommandError. The sandbox boots the template's own
|
|
175
|
+
* init — a start command is a template-BUILD field (startCmd) this provider
|
|
176
|
+
* never sets, so a create-time argv cannot be honored. The boot is already
|
|
177
|
+
* inert; exec the process after create instead.
|
|
178
|
+
*/
|
|
179
|
+
bootCommand?: string[];
|
|
92
180
|
workingDirectory?: string;
|
|
181
|
+
/**
|
|
182
|
+
* Per-sandbox compute sizing (cpu cores, memory GiB, disk GiB). E2B sizes
|
|
183
|
+
* sandboxes at TEMPLATE BUILD time only (Template.build cpuCount/memoryMB;
|
|
184
|
+
* disk is plan-fixed) — create-time sizing cannot be enforced, so any value
|
|
185
|
+
* here is REJECTED with E2BResourcesError rather than silently ignored.
|
|
186
|
+
* Bake sizing into the template the `image` names instead. `gpu`/`gpuTypes`
|
|
187
|
+
* are rejected the same way: e2b offers no GPU allocation at any tier.
|
|
188
|
+
*/
|
|
189
|
+
resources?: {
|
|
190
|
+
cpu?: number;
|
|
191
|
+
memory?: number;
|
|
192
|
+
disk?: number;
|
|
193
|
+
gpu?: number;
|
|
194
|
+
gpuTypes?: string[];
|
|
195
|
+
};
|
|
196
|
+
network?: {
|
|
197
|
+
outbound: "open" | "blocked";
|
|
198
|
+
allowedDestinations?: string[];
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Every policy `updateNetwork()` may later be asked for on this box.
|
|
202
|
+
*
|
|
203
|
+
* ACCEPTED AND IGNORED HERE, deliberately. It exists because on modal the
|
|
204
|
+
* create call decides whether switching is possible at all, and a box built
|
|
205
|
+
* the blunt way can never be widened. E2B has no such constraint — it
|
|
206
|
+
* switches freely at any time — so this changes nothing about the sandbox
|
|
207
|
+
* it creates.
|
|
208
|
+
*
|
|
209
|
+
* Declared anyway so the option means the SAME thing in every provider
|
|
210
|
+
* package: a caller reading these types must not conclude that E2B
|
|
211
|
+
* cannot do phase switching because the option is missing here.
|
|
212
|
+
*/
|
|
213
|
+
phaseNetworkPolicies?: Array<{
|
|
214
|
+
outbound: "open" | "blocked";
|
|
215
|
+
allowedDestinations?: string[];
|
|
216
|
+
}>;
|
|
217
|
+
/** Run all commands and file operations as this user (passed on every E2B operation that supports it). */
|
|
218
|
+
user?: string;
|
|
219
|
+
/** Home directory used by the SDK for agent config paths; not consumed by the provider. */
|
|
220
|
+
homeDir?: string;
|
|
93
221
|
}
|
|
94
222
|
/** Options for listing sandboxes */
|
|
95
223
|
interface SandboxListOptions {
|
|
@@ -97,6 +225,20 @@ interface SandboxListOptions {
|
|
|
97
225
|
metadata?: Record<string, string>;
|
|
98
226
|
limit?: number;
|
|
99
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* A COMPLETE (or admittedly incomplete) enumeration of the project's fleet.
|
|
230
|
+
*
|
|
231
|
+
* `complete` is the load-bearing field. Callers that need a whole fleet —
|
|
232
|
+
* orphan sweeps, lifecycle reconciliation — read a sandbox's ABSENCE from the
|
|
233
|
+
* list as evidence it is gone, so a truncated page and a small fleet must never
|
|
234
|
+
* be the same answer. `complete: false` means leave every row alone.
|
|
235
|
+
*/
|
|
236
|
+
interface SandboxListPage {
|
|
237
|
+
sandboxes: SandboxInfo[];
|
|
238
|
+
complete: boolean;
|
|
239
|
+
pagesFetched: number;
|
|
240
|
+
error?: string;
|
|
241
|
+
}
|
|
100
242
|
/** Command execution capabilities */
|
|
101
243
|
interface SandboxCommands {
|
|
102
244
|
/** Run command and wait for completion */
|
|
@@ -127,6 +269,8 @@ interface SandboxFiles {
|
|
|
127
269
|
readStream(path: string): Promise<ReadableStream<Uint8Array>>;
|
|
128
270
|
/** Write from stream */
|
|
129
271
|
writeStream(path: string, stream: ReadableStream<Uint8Array>): Promise<void>;
|
|
272
|
+
/** Upload a local file by path, streamed off disk (never buffered whole) */
|
|
273
|
+
writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
|
|
130
274
|
/** Get pre-signed upload URL for large files (expiration in seconds) */
|
|
131
275
|
uploadUrl(path: string, expiresInSeconds?: number): Promise<string>;
|
|
132
276
|
/** Get pre-signed download URL for large files (expiration in seconds) */
|
|
@@ -159,17 +303,23 @@ interface SandboxInstance {
|
|
|
159
303
|
kill(): Promise<void>;
|
|
160
304
|
/** Pause sandbox (preserves state) */
|
|
161
305
|
pause(): Promise<void>;
|
|
306
|
+
/** Replace the outbound network policy of the running sandbox. */
|
|
307
|
+
updateNetwork(network: SandboxCreateOptions["network"]): Promise<void>;
|
|
162
308
|
}
|
|
163
309
|
/** Sandbox lifecycle management */
|
|
164
310
|
interface SandboxProvider {
|
|
165
311
|
/** Provider type identifier */
|
|
166
312
|
readonly providerType: string;
|
|
313
|
+
/** Human-readable provider name for logging */
|
|
314
|
+
readonly name?: string;
|
|
167
315
|
/** Create new sandbox */
|
|
168
316
|
create(options: SandboxCreateOptions): Promise<SandboxInstance>;
|
|
169
317
|
/** Connect to existing sandbox */
|
|
170
318
|
connect(sandboxId: string, timeoutMs?: number): Promise<SandboxInstance>;
|
|
171
|
-
/** List sandboxes
|
|
319
|
+
/** List sandboxes, paginating to exhaustion. `limit` bounds items returned. */
|
|
172
320
|
list(options?: SandboxListOptions): Promise<SandboxInfo[]>;
|
|
321
|
+
/** The same enumeration for fleet bookkeeping: never throws, reports completeness. */
|
|
322
|
+
listAll(options?: SandboxListOptions): Promise<SandboxListPage>;
|
|
173
323
|
}
|
|
174
324
|
interface E2BConfig {
|
|
175
325
|
/** E2B API key. Default: reads from E2B_API_KEY env var */
|
|
@@ -187,17 +337,169 @@ interface ResolvedE2BConfig {
|
|
|
187
337
|
defaultTimeoutMs?: number;
|
|
188
338
|
templateId?: string;
|
|
189
339
|
}
|
|
340
|
+
declare class E2BCommands implements SandboxCommands {
|
|
341
|
+
private sandbox;
|
|
342
|
+
private defaultUser?;
|
|
343
|
+
constructor(sandbox: Sandbox, defaultUser?: string | undefined);
|
|
344
|
+
run(command: string, options?: SandboxRunOptions): Promise<SandboxCommandResult>;
|
|
345
|
+
spawn(command: string, options?: SandboxSpawnOptions): Promise<SandboxCommandHandle>;
|
|
346
|
+
list(): Promise<ProcessInfo[]>;
|
|
347
|
+
connect(processId: string, options?: SandboxConnectOptions): Promise<SandboxCommandHandle>;
|
|
348
|
+
sendStdin(processId: string, data: string): Promise<void>;
|
|
349
|
+
kill(processId: string): Promise<boolean>;
|
|
350
|
+
}
|
|
351
|
+
declare class E2BFiles implements SandboxFiles {
|
|
352
|
+
private sandbox;
|
|
353
|
+
private defaultUser?;
|
|
354
|
+
constructor(sandbox: Sandbox, defaultUser?: string | undefined);
|
|
355
|
+
read(path: string): Promise<string | Uint8Array>;
|
|
356
|
+
write(path: string, content: string | Buffer | ArrayBuffer | Uint8Array): Promise<void>;
|
|
357
|
+
writeBatch(files: Array<{
|
|
358
|
+
path: string;
|
|
359
|
+
data: string | Buffer | ArrayBuffer | Uint8Array;
|
|
360
|
+
}>): Promise<void>;
|
|
361
|
+
makeDir(path: string): Promise<void>;
|
|
362
|
+
uploadUrl(path: string, expiresInSeconds?: number): Promise<string>;
|
|
363
|
+
downloadUrl(path: string, expiresInSeconds?: number): Promise<string>;
|
|
364
|
+
exists(path: string): Promise<boolean>;
|
|
365
|
+
list(path: string): Promise<FileInfo[]>;
|
|
366
|
+
remove(path: string): Promise<void>;
|
|
367
|
+
rename(oldPath: string, newPath: string): Promise<void>;
|
|
368
|
+
readStream(path: string): Promise<ReadableStream<Uint8Array>>;
|
|
369
|
+
writeStream(path: string, stream: ReadableStream<Uint8Array>): Promise<void>;
|
|
370
|
+
/**
|
|
371
|
+
* Upload a local file by PATH, streamed off disk with real backpressure.
|
|
372
|
+
*
|
|
373
|
+
* Why this does NOT go through files.write(): that method calls
|
|
374
|
+
* `new Response(data).blob()` on whatever it is given, which MATERIALIZES the
|
|
375
|
+
* body — measured at +463 MB RSS for a 200 MB source, whether the input is a
|
|
376
|
+
* stream, a Buffer, or even a file-backed Blob. So handing it a stream looks
|
|
377
|
+
* like streaming and is not, which is precisely the trap this method exists to
|
|
378
|
+
* avoid: a caller uploading a large artifact once per concurrent task would
|
|
379
|
+
* still pay the full size every time.
|
|
380
|
+
*
|
|
381
|
+
* Instead the multipart body is assembled as a ReadableStream and POSTed to
|
|
382
|
+
* the signed upload URL with `duplex: "half"` — the same shape E2B's own
|
|
383
|
+
* template-context upload uses. Verified to hold backpressure: against a
|
|
384
|
+
* deliberately slow sink the producer had read 7 MB of a 200 MB file after
|
|
385
|
+
* four seconds, rather than racing to the end.
|
|
386
|
+
*
|
|
387
|
+
* The wire format mirrors files.write()'s single-file case exactly: the
|
|
388
|
+
* destination is the `path` query parameter of the signed URL, and the body is
|
|
389
|
+
* one `file` part. If E2B ever changes that, the POST fails and we fall back to
|
|
390
|
+
* the SDK's own (buffering) path — a correct upload beats a cheap one.
|
|
391
|
+
*/
|
|
392
|
+
writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
|
|
393
|
+
watchDir(path: string, onEvent: (event: FilesystemEvent) => void | Promise<void>, options?: WatchOptions): Promise<WatchHandle>;
|
|
394
|
+
}
|
|
395
|
+
declare class E2BSandboxImpl implements SandboxInstance {
|
|
396
|
+
private sandbox;
|
|
397
|
+
private apiKey;
|
|
398
|
+
private apiUrl?;
|
|
399
|
+
readonly commands: SandboxCommands;
|
|
400
|
+
readonly files: SandboxFiles;
|
|
401
|
+
constructor(sandbox: Sandbox, apiKey: string, apiUrl?: string | undefined, defaultUser?: string);
|
|
402
|
+
get sandboxId(): string;
|
|
403
|
+
getHost(port: number): Promise<string>;
|
|
404
|
+
isRunning(): Promise<boolean>;
|
|
405
|
+
getInfo(): Promise<SandboxInfo>;
|
|
406
|
+
/**
|
|
407
|
+
* Replace the running sandbox's outbound policy — E2B's
|
|
408
|
+
* `Sandbox.updateNetwork`: "Update the network configuration of the sandbox.
|
|
409
|
+
* Replaces the current egress configuration atomically — fields that are
|
|
410
|
+
* omitted are cleared on the server." (from the published e2b 2.25.0+ type
|
|
411
|
+
* declarations for Sandbox.updateNetwork).
|
|
412
|
+
*
|
|
413
|
+
* That "omitted fields are cleared" rule is why the whole policy is sent
|
|
414
|
+
* every time and never a delta: mapNetworkPolicy always states
|
|
415
|
+
* allowInternetAccess, and states `network` only when an allowlist is in
|
|
416
|
+
* force — so switching from an allowlist to a sealed box clears the
|
|
417
|
+
* allowlist by the server's own rule rather than by a second call that could
|
|
418
|
+
* fail in between and leave the box half-switched.
|
|
419
|
+
*
|
|
420
|
+
* VERSION GATE. `updateNetwork` landed in e2b 2.25.0; the client resolved in
|
|
421
|
+
* this workspace today is older and has no such method. Feature-detected
|
|
422
|
+
* rather than assumed, and refused loudly when absent — a `catch` around a
|
|
423
|
+
* missing method, or an early return, would leave the sandbox on its
|
|
424
|
+
* previous policy while the caller believes the switch happened.
|
|
425
|
+
*/
|
|
426
|
+
updateNetwork(network: SandboxCreateOptions["network"]): Promise<void>;
|
|
427
|
+
kill(): Promise<void>;
|
|
428
|
+
pause(): Promise<void>;
|
|
429
|
+
}
|
|
190
430
|
declare class E2BProvider implements SandboxProvider {
|
|
191
431
|
readonly providerType: "e2b";
|
|
432
|
+
readonly name = "E2B";
|
|
192
433
|
private readonly apiKey;
|
|
193
434
|
private readonly apiUrl?;
|
|
194
435
|
private readonly defaultTimeoutMs;
|
|
195
436
|
private readonly templateId?;
|
|
437
|
+
/**
|
|
438
|
+
* Sandbox user configured at create time, reapplied on connect() (e.g., resume).
|
|
439
|
+
* In-memory only: a connect() from a fresh process runs as the template default
|
|
440
|
+
* user — callers reconnecting across processes must recreate the provider config
|
|
441
|
+
* with the same user, or operations on user-owned files fail loudly.
|
|
442
|
+
*/
|
|
443
|
+
private readonly sandboxUsers;
|
|
196
444
|
constructor(config: ResolvedE2BConfig);
|
|
197
445
|
create(options: SandboxCreateOptions): Promise<SandboxInstance>;
|
|
198
446
|
connect(sandboxId: string, timeoutMs?: number): Promise<SandboxInstance>;
|
|
447
|
+
/**
|
|
448
|
+
* List sandboxes, walking every page.
|
|
449
|
+
*
|
|
450
|
+
* This used to call `nextItems()` exactly once and return whatever the first
|
|
451
|
+
* page held — an account with more than E2B_MAX_PAGE_SIZE sandboxes was
|
|
452
|
+
* silently truncated, and nothing in the return value said so. That is a
|
|
453
|
+
* correctness bug rather than a performance one for any caller that reads
|
|
454
|
+
* absence from the list as "this sandbox is gone".
|
|
455
|
+
*
|
|
456
|
+
* `limit` still means what a caller expects: stop after this many ITEMS, so
|
|
457
|
+
* an explicit limit remains a cost bound. The per-request page size is capped
|
|
458
|
+
* separately at E2B_MAX_PAGE_SIZE because E2B rejects anything larger
|
|
459
|
+
* outright (`400 validation error: parameter "limit" ... must be at most
|
|
460
|
+
* 100`), so a caller asking for 500 gets five requests, not one refusal.
|
|
461
|
+
*/
|
|
199
462
|
list(options?: SandboxListOptions): Promise<SandboxInfo[]>;
|
|
463
|
+
/**
|
|
464
|
+
* The fleet-bookkeeping enumeration: same walk, never throws.
|
|
465
|
+
*
|
|
466
|
+
* The distinction from `list()` is what a failure MEANS to the caller. An
|
|
467
|
+
* orphan sweep treats a missing sandbox as a terminated one, so it must be
|
|
468
|
+
* able to tell "the project has no sandboxes" from "the enumeration stopped
|
|
469
|
+
* early" — and an exception it catches and turns into an empty array erases
|
|
470
|
+
* exactly that difference.
|
|
471
|
+
*/
|
|
472
|
+
listAll(options?: SandboxListOptions): Promise<SandboxListPage>;
|
|
473
|
+
private paginate;
|
|
474
|
+
}
|
|
475
|
+
/** The paginator surface this walk needs — E2B's SandboxPaginator satisfies it. */
|
|
476
|
+
interface E2BSandboxPaginator {
|
|
477
|
+
readonly hasNext: boolean;
|
|
478
|
+
readonly nextToken?: string;
|
|
479
|
+
nextItems(): Promise<Array<{
|
|
480
|
+
sandboxId: string;
|
|
481
|
+
templateId: string;
|
|
482
|
+
name?: string;
|
|
483
|
+
metadata?: Record<string, string>;
|
|
484
|
+
startedAt: unknown;
|
|
485
|
+
endAt?: unknown;
|
|
486
|
+
}>>;
|
|
200
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* Drain a paginator into one answer, with an honest completeness verdict.
|
|
490
|
+
*
|
|
491
|
+
* Separate from the provider because everything worth getting wrong lives here
|
|
492
|
+
* and none of it needs a network: the two ways a walk can fail to terminate,
|
|
493
|
+
* the difference between "the caller asked for ten" and "the provider ran out",
|
|
494
|
+
* and the rule that a failure mid-walk yields the sandboxes seen so far marked
|
|
495
|
+
* INCOMPLETE rather than either an exception or a short complete list.
|
|
496
|
+
*
|
|
497
|
+
* Exported for its test (`_testCollectSandboxPages`).
|
|
498
|
+
*/
|
|
499
|
+
declare function collectSandboxPages(paginator: E2BSandboxPaginator, wanted?: number): Promise<SandboxListPage & {
|
|
500
|
+
stoppedAtLimit: boolean;
|
|
501
|
+
}>;
|
|
502
|
+
declare const _testCollectSandboxPages: typeof collectSandboxPages;
|
|
201
503
|
/**
|
|
202
504
|
* Create E2B sandbox provider.
|
|
203
505
|
*
|
|
@@ -205,5 +507,13 @@ declare class E2BProvider implements SandboxProvider {
|
|
|
205
507
|
* @throws Error if apiKey cannot be resolved
|
|
206
508
|
*/
|
|
207
509
|
declare function createE2BProvider(config?: E2BConfig): SandboxProvider;
|
|
510
|
+
/**
|
|
511
|
+
* TYPE-ONLY handle on the concrete sandbox class, for the contract-conformance
|
|
512
|
+
* seam. create() is declared to return the local SandboxInstance INTERFACE, so
|
|
513
|
+
* a seam reading create()'s return type checks the interface and never the
|
|
514
|
+
* class — which let a narrowed method on the class pass unnoticed. Exporting
|
|
515
|
+
* the type (never the constructor) gives the seam the real methods to pin.
|
|
516
|
+
*/
|
|
517
|
+
type _testE2BSandboxImpl = E2BSandboxImpl;
|
|
208
518
|
|
|
209
|
-
export { type E2BConfig, E2BProvider, type FileInfo, type FilesystemEvent, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxConnectOptions, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxProvider, type SandboxRunOptions, type SandboxSpawnOptions, type WatchHandle, type WatchOptions, createE2BProvider };
|
|
519
|
+
export { E2BBootCommandError, E2BCommands, type E2BConfig, E2BFiles, E2BIdleTimeoutError, E2BNetworkUpdateUnsupportedError, E2BProvider, E2BResourcesError, type E2BSandboxPaginator, E2B_MAX_LIST_PAGES, E2B_MAX_PAGE_SIZE, E2B_MIN_UPDATE_NETWORK_VERSION, type FileInfo, type FilesystemEvent, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxConnectOptions, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxListPage, type SandboxProvider, type SandboxRunOptions, type SandboxSpawnOptions, type WatchHandle, type WatchOptions, _testCollectSandboxPages, type _testE2BSandboxImpl, createE2BProvider };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Sandbox } from '@e2b/code-interpreter';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* E2B Sandbox Provider - Clean Architecture
|
|
3
5
|
*
|
|
@@ -11,6 +13,72 @@
|
|
|
11
13
|
* - Configuration externalized (no hardcoded mappings)
|
|
12
14
|
* - Clear naming (run = blocking, spawn = background)
|
|
13
15
|
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Largest page E2B will serve. Not a taste choice — a larger `limit` is refused
|
|
19
|
+
* outright: `400 validation error: parameter "limit" in query has an error:
|
|
20
|
+
* number must be at most 100`.
|
|
21
|
+
*/
|
|
22
|
+
declare const E2B_MAX_PAGE_SIZE = 100;
|
|
23
|
+
/**
|
|
24
|
+
* Page ceiling for one enumeration — 10,000 sandboxes at the maximum page size.
|
|
25
|
+
* A walk that reaches it stops and reports itself INCOMPLETE, because the one
|
|
26
|
+
* thing a fleet enumeration may never do is return a short list that reads like
|
|
27
|
+
* a whole one.
|
|
28
|
+
*/
|
|
29
|
+
declare const E2B_MAX_LIST_PAGES = 100;
|
|
30
|
+
/**
|
|
31
|
+
* Typed error for create-time sizing requests E2B cannot enforce.
|
|
32
|
+
* E2B sandboxes inherit cpu/memory from their TEMPLATE (Template.build
|
|
33
|
+
* cpuCount/memoryMB; disk is plan-fixed) — Sandbox.create has no sizing
|
|
34
|
+
* parameters, so a `resources` request would be silently ignored. Per the
|
|
35
|
+
* provider law (reject what you cannot enforce) it is refused loudly here.
|
|
36
|
+
*/
|
|
37
|
+
declare class E2BResourcesError extends Error {
|
|
38
|
+
constructor(message: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Minimum `e2b` client that can switch a running sandbox's egress.
|
|
42
|
+
*
|
|
43
|
+
* 2.25.0, per e2b's own release notes: "Add `Sandbox.updateNetwork` /
|
|
44
|
+
* `update_network` to replace a running sandbox's egress configuration"
|
|
45
|
+
* (e2b@2.25.0). Confirmed against the published type declarations — absent in
|
|
46
|
+
* 2.24.0, present in 2.25.0.
|
|
47
|
+
*
|
|
48
|
+
* IT SAID 2.26.0 UNTIL THE RELEASE NOTES WERE READ. That number came from a
|
|
49
|
+
* binary search over published tarballs that happened to sample only even
|
|
50
|
+
* minors, so 2.25.0 was never tested and the first "present" result looked
|
|
51
|
+
* like the boundary. The search was sound and the conclusion was still wrong
|
|
52
|
+
* by one release — which is the argument for reading the vendor's own record
|
|
53
|
+
* rather than inferring version history from artifacts.
|
|
54
|
+
*/
|
|
55
|
+
declare const E2B_MIN_UPDATE_NETWORK_VERSION = "2.25.0";
|
|
56
|
+
/**
|
|
57
|
+
* Typed error for a runtime egress switch the INSTALLED client cannot make.
|
|
58
|
+
*
|
|
59
|
+
* E2B the service supports it; an older `e2b` package simply has no method to
|
|
60
|
+
* call. That distinction matters to whoever reads this: the fix is a
|
|
61
|
+
* dependency bump, not a plan change or a different provider. Refused loudly
|
|
62
|
+
* rather than skipped, because a policy switch that quietly does nothing
|
|
63
|
+
* leaves the sandbox running under the previous policy — which, when the
|
|
64
|
+
* switch was meant to TIGHTEN egress, is the failure nobody notices.
|
|
65
|
+
*/
|
|
66
|
+
declare class E2BNetworkUpdateUnsupportedError extends Error {
|
|
67
|
+
constructor(message: string);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Typed error for an idle timeout E2B cannot enforce. E2B has exactly one
|
|
71
|
+
* clock and it is absolute: the sandbox is killed when its timeout expires,
|
|
72
|
+
* and only explicit client calls (create, connect, setTimeout) move that
|
|
73
|
+
* deadline — never what the sandbox is or is not doing. An `idleTimeoutMs`
|
|
74
|
+
* would therefore be silently ignored, so it is refused instead.
|
|
75
|
+
*/
|
|
76
|
+
declare class E2BBootCommandError extends Error {
|
|
77
|
+
constructor();
|
|
78
|
+
}
|
|
79
|
+
declare class E2BIdleTimeoutError extends Error {
|
|
80
|
+
constructor();
|
|
81
|
+
}
|
|
14
82
|
/** Result of a completed sandbox command */
|
|
15
83
|
interface SandboxCommandResult {
|
|
16
84
|
exitCode: number;
|
|
@@ -85,11 +153,71 @@ interface SandboxConnectOptions {
|
|
|
85
153
|
}
|
|
86
154
|
/** Options for creating a sandbox */
|
|
87
155
|
interface SandboxCreateOptions {
|
|
88
|
-
|
|
156
|
+
/**
|
|
157
|
+
* E2B template ID. OPTIONAL: create() falls back to the provider's
|
|
158
|
+
* configured `templateId`, then to "evolve-all" — declaring it required
|
|
159
|
+
* contradicted that implementation and only went unnoticed because TS
|
|
160
|
+
* method parameters are bivariant, so the SDK could already call
|
|
161
|
+
* create({}) through the shared contract.
|
|
162
|
+
*/
|
|
163
|
+
image?: string;
|
|
89
164
|
envs?: Record<string, string>;
|
|
90
165
|
metadata?: Record<string, string>;
|
|
91
166
|
timeoutMs?: number;
|
|
167
|
+
/**
|
|
168
|
+
* REJECTED with E2BIdleTimeoutError. E2B's timeout is an ABSOLUTE lifetime
|
|
169
|
+
* that in-sandbox activity never extends, so there is no inactivity clock to
|
|
170
|
+
* map this onto and honouring it would be a lie.
|
|
171
|
+
*/
|
|
172
|
+
idleTimeoutMs?: number;
|
|
173
|
+
/**
|
|
174
|
+
* REJECTED with E2BBootCommandError. The sandbox boots the template's own
|
|
175
|
+
* init — a start command is a template-BUILD field (startCmd) this provider
|
|
176
|
+
* never sets, so a create-time argv cannot be honored. The boot is already
|
|
177
|
+
* inert; exec the process after create instead.
|
|
178
|
+
*/
|
|
179
|
+
bootCommand?: string[];
|
|
92
180
|
workingDirectory?: string;
|
|
181
|
+
/**
|
|
182
|
+
* Per-sandbox compute sizing (cpu cores, memory GiB, disk GiB). E2B sizes
|
|
183
|
+
* sandboxes at TEMPLATE BUILD time only (Template.build cpuCount/memoryMB;
|
|
184
|
+
* disk is plan-fixed) — create-time sizing cannot be enforced, so any value
|
|
185
|
+
* here is REJECTED with E2BResourcesError rather than silently ignored.
|
|
186
|
+
* Bake sizing into the template the `image` names instead. `gpu`/`gpuTypes`
|
|
187
|
+
* are rejected the same way: e2b offers no GPU allocation at any tier.
|
|
188
|
+
*/
|
|
189
|
+
resources?: {
|
|
190
|
+
cpu?: number;
|
|
191
|
+
memory?: number;
|
|
192
|
+
disk?: number;
|
|
193
|
+
gpu?: number;
|
|
194
|
+
gpuTypes?: string[];
|
|
195
|
+
};
|
|
196
|
+
network?: {
|
|
197
|
+
outbound: "open" | "blocked";
|
|
198
|
+
allowedDestinations?: string[];
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Every policy `updateNetwork()` may later be asked for on this box.
|
|
202
|
+
*
|
|
203
|
+
* ACCEPTED AND IGNORED HERE, deliberately. It exists because on modal the
|
|
204
|
+
* create call decides whether switching is possible at all, and a box built
|
|
205
|
+
* the blunt way can never be widened. E2B has no such constraint — it
|
|
206
|
+
* switches freely at any time — so this changes nothing about the sandbox
|
|
207
|
+
* it creates.
|
|
208
|
+
*
|
|
209
|
+
* Declared anyway so the option means the SAME thing in every provider
|
|
210
|
+
* package: a caller reading these types must not conclude that E2B
|
|
211
|
+
* cannot do phase switching because the option is missing here.
|
|
212
|
+
*/
|
|
213
|
+
phaseNetworkPolicies?: Array<{
|
|
214
|
+
outbound: "open" | "blocked";
|
|
215
|
+
allowedDestinations?: string[];
|
|
216
|
+
}>;
|
|
217
|
+
/** Run all commands and file operations as this user (passed on every E2B operation that supports it). */
|
|
218
|
+
user?: string;
|
|
219
|
+
/** Home directory used by the SDK for agent config paths; not consumed by the provider. */
|
|
220
|
+
homeDir?: string;
|
|
93
221
|
}
|
|
94
222
|
/** Options for listing sandboxes */
|
|
95
223
|
interface SandboxListOptions {
|
|
@@ -97,6 +225,20 @@ interface SandboxListOptions {
|
|
|
97
225
|
metadata?: Record<string, string>;
|
|
98
226
|
limit?: number;
|
|
99
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* A COMPLETE (or admittedly incomplete) enumeration of the project's fleet.
|
|
230
|
+
*
|
|
231
|
+
* `complete` is the load-bearing field. Callers that need a whole fleet —
|
|
232
|
+
* orphan sweeps, lifecycle reconciliation — read a sandbox's ABSENCE from the
|
|
233
|
+
* list as evidence it is gone, so a truncated page and a small fleet must never
|
|
234
|
+
* be the same answer. `complete: false` means leave every row alone.
|
|
235
|
+
*/
|
|
236
|
+
interface SandboxListPage {
|
|
237
|
+
sandboxes: SandboxInfo[];
|
|
238
|
+
complete: boolean;
|
|
239
|
+
pagesFetched: number;
|
|
240
|
+
error?: string;
|
|
241
|
+
}
|
|
100
242
|
/** Command execution capabilities */
|
|
101
243
|
interface SandboxCommands {
|
|
102
244
|
/** Run command and wait for completion */
|
|
@@ -127,6 +269,8 @@ interface SandboxFiles {
|
|
|
127
269
|
readStream(path: string): Promise<ReadableStream<Uint8Array>>;
|
|
128
270
|
/** Write from stream */
|
|
129
271
|
writeStream(path: string, stream: ReadableStream<Uint8Array>): Promise<void>;
|
|
272
|
+
/** Upload a local file by path, streamed off disk (never buffered whole) */
|
|
273
|
+
writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
|
|
130
274
|
/** Get pre-signed upload URL for large files (expiration in seconds) */
|
|
131
275
|
uploadUrl(path: string, expiresInSeconds?: number): Promise<string>;
|
|
132
276
|
/** Get pre-signed download URL for large files (expiration in seconds) */
|
|
@@ -159,17 +303,23 @@ interface SandboxInstance {
|
|
|
159
303
|
kill(): Promise<void>;
|
|
160
304
|
/** Pause sandbox (preserves state) */
|
|
161
305
|
pause(): Promise<void>;
|
|
306
|
+
/** Replace the outbound network policy of the running sandbox. */
|
|
307
|
+
updateNetwork(network: SandboxCreateOptions["network"]): Promise<void>;
|
|
162
308
|
}
|
|
163
309
|
/** Sandbox lifecycle management */
|
|
164
310
|
interface SandboxProvider {
|
|
165
311
|
/** Provider type identifier */
|
|
166
312
|
readonly providerType: string;
|
|
313
|
+
/** Human-readable provider name for logging */
|
|
314
|
+
readonly name?: string;
|
|
167
315
|
/** Create new sandbox */
|
|
168
316
|
create(options: SandboxCreateOptions): Promise<SandboxInstance>;
|
|
169
317
|
/** Connect to existing sandbox */
|
|
170
318
|
connect(sandboxId: string, timeoutMs?: number): Promise<SandboxInstance>;
|
|
171
|
-
/** List sandboxes
|
|
319
|
+
/** List sandboxes, paginating to exhaustion. `limit` bounds items returned. */
|
|
172
320
|
list(options?: SandboxListOptions): Promise<SandboxInfo[]>;
|
|
321
|
+
/** The same enumeration for fleet bookkeeping: never throws, reports completeness. */
|
|
322
|
+
listAll(options?: SandboxListOptions): Promise<SandboxListPage>;
|
|
173
323
|
}
|
|
174
324
|
interface E2BConfig {
|
|
175
325
|
/** E2B API key. Default: reads from E2B_API_KEY env var */
|
|
@@ -187,17 +337,169 @@ interface ResolvedE2BConfig {
|
|
|
187
337
|
defaultTimeoutMs?: number;
|
|
188
338
|
templateId?: string;
|
|
189
339
|
}
|
|
340
|
+
declare class E2BCommands implements SandboxCommands {
|
|
341
|
+
private sandbox;
|
|
342
|
+
private defaultUser?;
|
|
343
|
+
constructor(sandbox: Sandbox, defaultUser?: string | undefined);
|
|
344
|
+
run(command: string, options?: SandboxRunOptions): Promise<SandboxCommandResult>;
|
|
345
|
+
spawn(command: string, options?: SandboxSpawnOptions): Promise<SandboxCommandHandle>;
|
|
346
|
+
list(): Promise<ProcessInfo[]>;
|
|
347
|
+
connect(processId: string, options?: SandboxConnectOptions): Promise<SandboxCommandHandle>;
|
|
348
|
+
sendStdin(processId: string, data: string): Promise<void>;
|
|
349
|
+
kill(processId: string): Promise<boolean>;
|
|
350
|
+
}
|
|
351
|
+
declare class E2BFiles implements SandboxFiles {
|
|
352
|
+
private sandbox;
|
|
353
|
+
private defaultUser?;
|
|
354
|
+
constructor(sandbox: Sandbox, defaultUser?: string | undefined);
|
|
355
|
+
read(path: string): Promise<string | Uint8Array>;
|
|
356
|
+
write(path: string, content: string | Buffer | ArrayBuffer | Uint8Array): Promise<void>;
|
|
357
|
+
writeBatch(files: Array<{
|
|
358
|
+
path: string;
|
|
359
|
+
data: string | Buffer | ArrayBuffer | Uint8Array;
|
|
360
|
+
}>): Promise<void>;
|
|
361
|
+
makeDir(path: string): Promise<void>;
|
|
362
|
+
uploadUrl(path: string, expiresInSeconds?: number): Promise<string>;
|
|
363
|
+
downloadUrl(path: string, expiresInSeconds?: number): Promise<string>;
|
|
364
|
+
exists(path: string): Promise<boolean>;
|
|
365
|
+
list(path: string): Promise<FileInfo[]>;
|
|
366
|
+
remove(path: string): Promise<void>;
|
|
367
|
+
rename(oldPath: string, newPath: string): Promise<void>;
|
|
368
|
+
readStream(path: string): Promise<ReadableStream<Uint8Array>>;
|
|
369
|
+
writeStream(path: string, stream: ReadableStream<Uint8Array>): Promise<void>;
|
|
370
|
+
/**
|
|
371
|
+
* Upload a local file by PATH, streamed off disk with real backpressure.
|
|
372
|
+
*
|
|
373
|
+
* Why this does NOT go through files.write(): that method calls
|
|
374
|
+
* `new Response(data).blob()` on whatever it is given, which MATERIALIZES the
|
|
375
|
+
* body — measured at +463 MB RSS for a 200 MB source, whether the input is a
|
|
376
|
+
* stream, a Buffer, or even a file-backed Blob. So handing it a stream looks
|
|
377
|
+
* like streaming and is not, which is precisely the trap this method exists to
|
|
378
|
+
* avoid: a caller uploading a large artifact once per concurrent task would
|
|
379
|
+
* still pay the full size every time.
|
|
380
|
+
*
|
|
381
|
+
* Instead the multipart body is assembled as a ReadableStream and POSTed to
|
|
382
|
+
* the signed upload URL with `duplex: "half"` — the same shape E2B's own
|
|
383
|
+
* template-context upload uses. Verified to hold backpressure: against a
|
|
384
|
+
* deliberately slow sink the producer had read 7 MB of a 200 MB file after
|
|
385
|
+
* four seconds, rather than racing to the end.
|
|
386
|
+
*
|
|
387
|
+
* The wire format mirrors files.write()'s single-file case exactly: the
|
|
388
|
+
* destination is the `path` query parameter of the signed URL, and the body is
|
|
389
|
+
* one `file` part. If E2B ever changes that, the POST fails and we fall back to
|
|
390
|
+
* the SDK's own (buffering) path — a correct upload beats a cheap one.
|
|
391
|
+
*/
|
|
392
|
+
writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
|
|
393
|
+
watchDir(path: string, onEvent: (event: FilesystemEvent) => void | Promise<void>, options?: WatchOptions): Promise<WatchHandle>;
|
|
394
|
+
}
|
|
395
|
+
declare class E2BSandboxImpl implements SandboxInstance {
|
|
396
|
+
private sandbox;
|
|
397
|
+
private apiKey;
|
|
398
|
+
private apiUrl?;
|
|
399
|
+
readonly commands: SandboxCommands;
|
|
400
|
+
readonly files: SandboxFiles;
|
|
401
|
+
constructor(sandbox: Sandbox, apiKey: string, apiUrl?: string | undefined, defaultUser?: string);
|
|
402
|
+
get sandboxId(): string;
|
|
403
|
+
getHost(port: number): Promise<string>;
|
|
404
|
+
isRunning(): Promise<boolean>;
|
|
405
|
+
getInfo(): Promise<SandboxInfo>;
|
|
406
|
+
/**
|
|
407
|
+
* Replace the running sandbox's outbound policy — E2B's
|
|
408
|
+
* `Sandbox.updateNetwork`: "Update the network configuration of the sandbox.
|
|
409
|
+
* Replaces the current egress configuration atomically — fields that are
|
|
410
|
+
* omitted are cleared on the server." (from the published e2b 2.25.0+ type
|
|
411
|
+
* declarations for Sandbox.updateNetwork).
|
|
412
|
+
*
|
|
413
|
+
* That "omitted fields are cleared" rule is why the whole policy is sent
|
|
414
|
+
* every time and never a delta: mapNetworkPolicy always states
|
|
415
|
+
* allowInternetAccess, and states `network` only when an allowlist is in
|
|
416
|
+
* force — so switching from an allowlist to a sealed box clears the
|
|
417
|
+
* allowlist by the server's own rule rather than by a second call that could
|
|
418
|
+
* fail in between and leave the box half-switched.
|
|
419
|
+
*
|
|
420
|
+
* VERSION GATE. `updateNetwork` landed in e2b 2.25.0; the client resolved in
|
|
421
|
+
* this workspace today is older and has no such method. Feature-detected
|
|
422
|
+
* rather than assumed, and refused loudly when absent — a `catch` around a
|
|
423
|
+
* missing method, or an early return, would leave the sandbox on its
|
|
424
|
+
* previous policy while the caller believes the switch happened.
|
|
425
|
+
*/
|
|
426
|
+
updateNetwork(network: SandboxCreateOptions["network"]): Promise<void>;
|
|
427
|
+
kill(): Promise<void>;
|
|
428
|
+
pause(): Promise<void>;
|
|
429
|
+
}
|
|
190
430
|
declare class E2BProvider implements SandboxProvider {
|
|
191
431
|
readonly providerType: "e2b";
|
|
432
|
+
readonly name = "E2B";
|
|
192
433
|
private readonly apiKey;
|
|
193
434
|
private readonly apiUrl?;
|
|
194
435
|
private readonly defaultTimeoutMs;
|
|
195
436
|
private readonly templateId?;
|
|
437
|
+
/**
|
|
438
|
+
* Sandbox user configured at create time, reapplied on connect() (e.g., resume).
|
|
439
|
+
* In-memory only: a connect() from a fresh process runs as the template default
|
|
440
|
+
* user — callers reconnecting across processes must recreate the provider config
|
|
441
|
+
* with the same user, or operations on user-owned files fail loudly.
|
|
442
|
+
*/
|
|
443
|
+
private readonly sandboxUsers;
|
|
196
444
|
constructor(config: ResolvedE2BConfig);
|
|
197
445
|
create(options: SandboxCreateOptions): Promise<SandboxInstance>;
|
|
198
446
|
connect(sandboxId: string, timeoutMs?: number): Promise<SandboxInstance>;
|
|
447
|
+
/**
|
|
448
|
+
* List sandboxes, walking every page.
|
|
449
|
+
*
|
|
450
|
+
* This used to call `nextItems()` exactly once and return whatever the first
|
|
451
|
+
* page held — an account with more than E2B_MAX_PAGE_SIZE sandboxes was
|
|
452
|
+
* silently truncated, and nothing in the return value said so. That is a
|
|
453
|
+
* correctness bug rather than a performance one for any caller that reads
|
|
454
|
+
* absence from the list as "this sandbox is gone".
|
|
455
|
+
*
|
|
456
|
+
* `limit` still means what a caller expects: stop after this many ITEMS, so
|
|
457
|
+
* an explicit limit remains a cost bound. The per-request page size is capped
|
|
458
|
+
* separately at E2B_MAX_PAGE_SIZE because E2B rejects anything larger
|
|
459
|
+
* outright (`400 validation error: parameter "limit" ... must be at most
|
|
460
|
+
* 100`), so a caller asking for 500 gets five requests, not one refusal.
|
|
461
|
+
*/
|
|
199
462
|
list(options?: SandboxListOptions): Promise<SandboxInfo[]>;
|
|
463
|
+
/**
|
|
464
|
+
* The fleet-bookkeeping enumeration: same walk, never throws.
|
|
465
|
+
*
|
|
466
|
+
* The distinction from `list()` is what a failure MEANS to the caller. An
|
|
467
|
+
* orphan sweep treats a missing sandbox as a terminated one, so it must be
|
|
468
|
+
* able to tell "the project has no sandboxes" from "the enumeration stopped
|
|
469
|
+
* early" — and an exception it catches and turns into an empty array erases
|
|
470
|
+
* exactly that difference.
|
|
471
|
+
*/
|
|
472
|
+
listAll(options?: SandboxListOptions): Promise<SandboxListPage>;
|
|
473
|
+
private paginate;
|
|
474
|
+
}
|
|
475
|
+
/** The paginator surface this walk needs — E2B's SandboxPaginator satisfies it. */
|
|
476
|
+
interface E2BSandboxPaginator {
|
|
477
|
+
readonly hasNext: boolean;
|
|
478
|
+
readonly nextToken?: string;
|
|
479
|
+
nextItems(): Promise<Array<{
|
|
480
|
+
sandboxId: string;
|
|
481
|
+
templateId: string;
|
|
482
|
+
name?: string;
|
|
483
|
+
metadata?: Record<string, string>;
|
|
484
|
+
startedAt: unknown;
|
|
485
|
+
endAt?: unknown;
|
|
486
|
+
}>>;
|
|
200
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* Drain a paginator into one answer, with an honest completeness verdict.
|
|
490
|
+
*
|
|
491
|
+
* Separate from the provider because everything worth getting wrong lives here
|
|
492
|
+
* and none of it needs a network: the two ways a walk can fail to terminate,
|
|
493
|
+
* the difference between "the caller asked for ten" and "the provider ran out",
|
|
494
|
+
* and the rule that a failure mid-walk yields the sandboxes seen so far marked
|
|
495
|
+
* INCOMPLETE rather than either an exception or a short complete list.
|
|
496
|
+
*
|
|
497
|
+
* Exported for its test (`_testCollectSandboxPages`).
|
|
498
|
+
*/
|
|
499
|
+
declare function collectSandboxPages(paginator: E2BSandboxPaginator, wanted?: number): Promise<SandboxListPage & {
|
|
500
|
+
stoppedAtLimit: boolean;
|
|
501
|
+
}>;
|
|
502
|
+
declare const _testCollectSandboxPages: typeof collectSandboxPages;
|
|
201
503
|
/**
|
|
202
504
|
* Create E2B sandbox provider.
|
|
203
505
|
*
|
|
@@ -205,5 +507,13 @@ declare class E2BProvider implements SandboxProvider {
|
|
|
205
507
|
* @throws Error if apiKey cannot be resolved
|
|
206
508
|
*/
|
|
207
509
|
declare function createE2BProvider(config?: E2BConfig): SandboxProvider;
|
|
510
|
+
/**
|
|
511
|
+
* TYPE-ONLY handle on the concrete sandbox class, for the contract-conformance
|
|
512
|
+
* seam. create() is declared to return the local SandboxInstance INTERFACE, so
|
|
513
|
+
* a seam reading create()'s return type checks the interface and never the
|
|
514
|
+
* class — which let a narrowed method on the class pass unnoticed. Exporting
|
|
515
|
+
* the type (never the constructor) gives the seam the real methods to pin.
|
|
516
|
+
*/
|
|
517
|
+
type _testE2BSandboxImpl = E2BSandboxImpl;
|
|
208
518
|
|
|
209
|
-
export { type E2BConfig, E2BProvider, type FileInfo, type FilesystemEvent, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxConnectOptions, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxProvider, type SandboxRunOptions, type SandboxSpawnOptions, type WatchHandle, type WatchOptions, createE2BProvider };
|
|
519
|
+
export { E2BBootCommandError, E2BCommands, type E2BConfig, E2BFiles, E2BIdleTimeoutError, E2BNetworkUpdateUnsupportedError, E2BProvider, E2BResourcesError, type E2BSandboxPaginator, E2B_MAX_LIST_PAGES, E2B_MAX_PAGE_SIZE, E2B_MIN_UPDATE_NETWORK_VERSION, type FileInfo, type FilesystemEvent, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxConnectOptions, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxListPage, type SandboxProvider, type SandboxRunOptions, type SandboxSpawnOptions, type WatchHandle, type WatchOptions, _testCollectSandboxPages, type _testE2BSandboxImpl, createE2BProvider };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
import {Sandbox}from'@e2b/code-interpreter';
|
|
2
|
-
|
|
1
|
+
import {Sandbox}from'@e2b/code-interpreter';function b(s){if(!s||typeof s!="object")return false;if(s.name==="TimeoutError")return true;let e=s.message;return typeof e=="string"&&/timed?\s*out/i.test(e)}var k=100,I=100;function u(s){return s instanceof Date?s.toISOString():String(s)}function U(s){if(typeof s=="string"||s instanceof ArrayBuffer)return s;if(s instanceof Buffer)return new Uint8Array(s).buffer;if(s instanceof Uint8Array)return new Uint8Array(s).buffer;throw new Error(`Unsupported data type for file upload: ${typeof s}. Expected string, Buffer, ArrayBuffer, or Uint8Array.`)}var l=class extends Error{constructor(e){super(e),this.name="E2BResourcesError";}},B="2.25.0",g=class extends Error{constructor(e){super(e),this.name="E2BNetworkUpdateUnsupportedError";}};function x(s){if(s?.outbound==="open"&&s.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");let e=s?.allowedDestinations??[],t=s?.outbound==="blocked"&&e.length>0;return {allowInternetAccess:s?.outbound!=="blocked"||t,network:t?{denyOut:["0.0.0.0/0"],allowOut:e}:void 0}}var h=class extends Error{constructor(){super("E2B takes no create-time boot command: the sandbox boots its template's own init, and a start command is a template-BUILD field (Template startCmd) this provider never sets. The boot is already inert \u2014 run the process by exec after create, or bake it into the template the `image` names."),this.name="E2BBootCommandError";}},y=class extends Error{constructor(){super("E2B has no idle timeout: its sandbox timeout is an ABSOLUTE lifetime that in-sandbox activity never extends. Use `timeoutMs` for the lifetime, and call setTimeout() to extend a sandbox you know is still working."),this.name="E2BIdleTimeoutError";}},w=class{constructor(e,t){this.sandbox=e;this.defaultUser=t;}async run(e,t){try{let r=await this.sandbox.commands.run(e,{timeoutMs:t?.timeoutMs,envs:t?.envs,cwd:t?.cwd,user:this.defaultUser,onStdout:t?.onStdout,onStderr:t?.onStderr});return {exitCode:r.exitCode,stdout:r.stdout,stderr:r.stderr}}catch(r){if(r&&typeof r=="object"&&"exitCode"in r){let n=r;return {exitCode:n.exitCode,stdout:n.stdout??"",stderr:n.stderr??""}}if(b(r))return {exitCode:124,stdout:"",stderr:r instanceof Error?r.message:"command timed out"};throw r}}async spawn(e,t){let r=await this.sandbox.commands.run(e,{background:true,stdin:t?.stdin??true,timeoutMs:t?.timeoutMs,envs:t?.envs,cwd:t?.cwd,user:this.defaultUser,onStdout:t?.onStdout,onStderr:t?.onStderr});return {processId:String(r.pid),wait:async()=>{try{let n=await r.wait();return {exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr}}catch(n){if(n&&typeof n=="object"&&"exitCode"in n){let i=n;return {exitCode:i.exitCode,stdout:i.stdout??"",stderr:i.stderr??""}}if(b(n))return {exitCode:124,stdout:"",stderr:n instanceof Error?n.message:"command timed out"};throw n}},kill:()=>this.sandbox.commands.kill(r.pid)}}async list(){return (await this.sandbox.commands.list()).map(t=>({...t,processId:String(t.pid)}))}async connect(e,t){let r=await this.sandbox.commands.connect(Number(e),{onStdout:t?.onStdout,onStderr:t?.onStderr,timeoutMs:t?.timeoutMs});return {processId:String(r.pid),wait:async()=>{try{let n=await r.wait();return {exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr}}catch(n){if(n&&typeof n=="object"&&"exitCode"in n){let i=n;return {exitCode:i.exitCode,stdout:i.stdout??"",stderr:i.stderr??""}}if(b(n))return {exitCode:124,stdout:"",stderr:n instanceof Error?n.message:"command timed out"};throw n}},kill:()=>this.sandbox.commands.kill(r.pid)}}async sendStdin(e,t){await this.sandbox.commands.sendStdin(Number(e),t);}async kill(e){return this.sandbox.commands.kill(Number(e))}},S=class{constructor(e,t){this.sandbox=e;this.defaultUser=t;}async read(e){let t=await this.sandbox.files.read(e,{format:"bytes",requestTimeoutMs:3e5,user:this.defaultUser});if(!t.includes(0))try{return new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}).decode(t)}catch{}return t}async write(e,t){await this.sandbox.files.write(e,U(t),{requestTimeoutMs:3e5,user:this.defaultUser});}async writeBatch(e){let t=e.map(r=>({path:r.path,data:U(r.data)}));await this.sandbox.files.write(t,{requestTimeoutMs:3e5,user:this.defaultUser});}async makeDir(e){await this.sandbox.files.makeDir(e,{user:this.defaultUser});}async uploadUrl(e,t){return this.sandbox.uploadUrl(e,{user:this.defaultUser,...t?{useSignatureExpiration:t}:{}})}async downloadUrl(e,t){return this.sandbox.downloadUrl(e,{user:this.defaultUser,...t?{useSignatureExpiration:t}:{}})}async exists(e){return this.sandbox.files.exists(e,{user:this.defaultUser})}async list(e){return (await this.sandbox.files.list(e,{user:this.defaultUser})).map(r=>({name:r.name,path:r.path,type:r.type==="dir"?"dir":"file"}))}async remove(e){await this.sandbox.files.remove(e,{user:this.defaultUser});}async rename(e,t){await this.sandbox.files.rename(e,t,{user:this.defaultUser});}async readStream(e){return this.sandbox.files.read(e,{format:"stream",user:this.defaultUser})}async writeStream(e,t){await this.sandbox.files.write(e,t,{user:this.defaultUser});}async writeFromPath(e,t){let{createReadStream:r,statSync:n}=await import('fs'),{Readable:i}=await import('stream'),{randomBytes:d}=await import('crypto'),a=n(t).size,p=`----evolve${d(16).toString("hex")}`,v=Buffer.from(`--${p}\r
|
|
2
|
+
Content-Disposition: form-data; name="file"; filename="blob"\r
|
|
3
|
+
Content-Type: application/octet-stream\r
|
|
4
|
+
\r
|
|
5
|
+
`),A=Buffer.from(`\r
|
|
6
|
+
--${p}--\r
|
|
7
|
+
`);async function*C(){yield v;for await(let o of r(t))yield o;yield A;}try{let o=await this.uploadUrl(e),m=await fetch(o,{method:"POST",body:i.toWeb(i.from(C())),headers:{"Content-Type":`multipart/form-data; boundary=${p}`,"Content-Length":String(v.length+a+A.length)},duplex:"half"});if(!m.ok)throw new Error(`streamed upload to ${e} failed (HTTP ${m.status})`)}catch(o){console.warn(`[e2b] streamed upload of ${t} failed, falling back to a buffered write:`,o instanceof Error?o.message:o);let m=i.toWeb(r(t));await this.writeStream(e,m);}}async watchDir(e,t,r){let n=await this.sandbox.files.watchDir(e,t,{recursive:r?.recursive,timeoutMs:r?.timeoutMs,user:this.defaultUser,onExit:r?.onExit});return {stop:()=>n.stop()}}},c=class{constructor(e,t,r,n){this.sandbox=e;this.apiKey=t;this.apiUrl=r;this.commands=new w(e,n),this.files=new S(e,n);}commands;files;get sandboxId(){return this.sandbox.sandboxId}getHost(e){return Promise.resolve(this.sandbox.getHost(e))}async isRunning(){return this.sandbox.isRunning()}async getInfo(){let e=await this.sandbox.getInfo();return {sandboxId:e.sandboxId,image:e.templateId,name:e.name,metadata:e.metadata??{},startedAt:u(e.startedAt),endAt:e.endAt?u(e.endAt):void 0}}async updateNetwork(e){let t=x(e),r=this.sandbox;if(typeof r.updateNetwork!="function")throw new g(`The installed e2b client cannot change a running sandbox's network policy: Sandbox.updateNetwork requires e2b >= ${B}. Upgrade the e2b dependency (it is pulled in by @e2b/code-interpreter), or create the sandbox with the policy it needs for its whole lifetime.`);await r.updateNetwork({allowInternetAccess:t.allowInternetAccess,...t.network??{}});}async kill(){try{await this.sandbox.kill();}catch{await new Promise(e=>setTimeout(e,500)),await this.sandbox.kill();}}async pause(){await this.sandbox.betaPause({apiKey:this.apiKey,apiUrl:this.apiUrl});}},P=class{providerType="e2b";name="E2B";apiKey;apiUrl;defaultTimeoutMs;templateId;sandboxUsers=new Map;constructor(e){this.apiKey=e.apiKey,this.apiUrl=e.apiUrl,this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.templateId=e.templateId;}async create(e){if(e.bootCommand!==void 0)throw new h;if(e.idleTimeoutMs!==void 0)throw new y;if(e.resources?.gpu!==void 0||e.resources?.gpuTypes!==void 0)throw new l("E2B offers no GPU allocation at any tier \u2014 a `resources.gpu` request cannot be enforced. Run GPU workloads on a provider that reserves GPUs (modal).");if(e.resources&&(e.resources.cpu!==void 0||e.resources.memory!==void 0||e.resources.disk!==void 0))throw new l("E2B cannot size a sandbox at create time: sizing is fixed by the template (Template.build cpuCount/memoryMB; disk is plan-fixed). Build a template with the desired resources and pass it as `image` instead of `resources`.");let t=e.timeoutMs??this.defaultTimeoutMs,r=e.image??this.templateId??"evolve-all";for(let d of e.phaseNetworkPolicies??[])x(d);let n=x(e.network),i=await Sandbox.create(r,{apiKey:this.apiKey,apiUrl:this.apiUrl,envs:e.envs,metadata:e.metadata,timeoutMs:t,...n});return e.workingDirectory&&await i.files.makeDir(e.workingDirectory,{user:e.user}),e.user&&this.sandboxUsers.set(i.sandboxId,e.user),new c(i,this.apiKey,this.apiUrl,e.user)}async connect(e,t){let r=await Sandbox.connect(e,{apiKey:this.apiKey,apiUrl:this.apiUrl,timeoutMs:t??this.defaultTimeoutMs});return new c(r,this.apiKey,this.apiUrl,this.sandboxUsers.get(e))}async list(e){let t=await this.paginate(e);if(t.error&&!t.stoppedAtLimit)throw new Error(t.error);return t.sandboxes}async listAll(e){let{stoppedAtLimit:t,...r}=await this.paginate(e);return r}async paginate(e){let t=e?.limit;return E(Sandbox.list({apiKey:this.apiKey,apiUrl:this.apiUrl,query:{state:e?.state,metadata:e?.metadata},limit:t===void 0?k:Math.min(t,k)}),t)}};async function E(s,e){let t=[],r=0,n=new Set;for(;s.hasNext;){if(r>=I)return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:false,error:`sandbox list exceeded ${I} pages`};let i=s.nextToken;if(i!==void 0){if(n.has(i))return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:false,error:"pagination token repeated"};n.add(i);}let d;try{d=await s.nextItems();}catch(a){return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:false,error:`sandbox list failed: ${a instanceof Error?a.message:String(a)}`}}r+=1;for(let a of d){if(e!==void 0&&t.length>=e)return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:true,error:`stopped at the requested limit of ${e} with more sandboxes available`};t.push({sandboxId:a.sandboxId,image:a.templateId,name:a.name,metadata:a.metadata??{},startedAt:u(a.startedAt),endAt:a.endAt?u(a.endAt):void 0});}if(e!==void 0&&t.length>=e&&s.hasNext)return {sandboxes:t,complete:false,pagesFetched:r,stoppedAtLimit:true,error:`stopped at the requested limit of ${e} with more sandboxes available`};if(e!==void 0&&t.length>=e)break}return {sandboxes:t,complete:true,pagesFetched:r,stoppedAtLimit:false}}var T=E;function M(s={}){let e=s.apiKey??process.env.E2B_API_KEY;if(!e)throw new Error("E2B API key required. Set E2B_API_KEY environment variable or pass apiKey in config. Get your key at https://e2b.dev/sign-in");return new P({...s,apiKey:e})}
|
|
8
|
+
export{h as E2BBootCommandError,w as E2BCommands,S as E2BFiles,y as E2BIdleTimeoutError,g as E2BNetworkUpdateUnsupportedError,P as E2BProvider,l as E2BResourcesError,I as E2B_MAX_LIST_PAGES,k as E2B_MAX_PAGE_SIZE,B as E2B_MIN_UPDATE_NETWORK_VERSION,T as _testCollectSandboxPages,M as createE2BProvider};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolvingmachines/e2b",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.57",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"ai",
|
|
6
6
|
"agents",
|
|
7
7
|
"sandbox",
|
|
8
8
|
"e2b",
|
|
9
|
-
"evolve
|
|
9
|
+
"evolve",
|
|
10
10
|
"orchestration"
|
|
11
11
|
],
|
|
12
12
|
"homepage": "https://github.com/evolving-machines-lab/evolve",
|
|
@@ -32,10 +32,11 @@
|
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsup --minify",
|
|
34
34
|
"dev": "tsup --watch",
|
|
35
|
-
"type-check": "tsc --noEmit"
|
|
35
|
+
"type-check": "tsc --noEmit",
|
|
36
|
+
"test:unit": "tsx tests/unit/e2b-list-pagination.test.ts && tsx tests/unit/e2b-files-read.test.ts && tsx tests/unit/e2b-gpu-rejection.test.ts && tsx tests/unit/e2b-boot-command-rejection.test.ts && tsx tests/unit/e2b-template-refresh.test.ts"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
|
-
"@e2b/code-interpreter": "^2.
|
|
39
|
+
"@e2b/code-interpreter": "^2.7.1"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@types/node": "^22.15.18",
|