@mastra/e2b 0.13.0 → 0.14.0-alpha.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/factory-sandbox.d.ts +36 -0
- package/dist/factory-sandbox.d.ts.map +1 -0
- package/dist/index.cjs +217 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +219 -3
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/utils/repo-template.d.ts +13 -1
- package/dist/utils/repo-template.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PublicSchema } from '@mastra/core/schema';
|
|
2
|
+
import { FactorySandbox } from '@mastra/core/workspace';
|
|
3
|
+
import type { FactorySandboxBuilds, FactorySandboxContext } from '@mastra/core/workspace';
|
|
4
|
+
import { E2BSandbox } from './sandbox/index.js';
|
|
5
|
+
import type { E2BSandboxOptions } from './sandbox/index.js';
|
|
6
|
+
import type { DeferredNamedTemplateSpec } from './utils/template.js';
|
|
7
|
+
/** User-tunable settings of an E2B factory sandbox. Every field is optional; absent means the provider default. */
|
|
8
|
+
export interface E2BFactorySandboxSettings extends Record<string, unknown> {
|
|
9
|
+
/** vCPUs for the sandbox template. Part of the template identity. */
|
|
10
|
+
cpuCount?: number;
|
|
11
|
+
/** Memory in MB for the sandbox template. Part of the template identity. */
|
|
12
|
+
memoryMb?: number;
|
|
13
|
+
/** Minutes a sandbox stays alive before E2B stops it. Not part of the template identity. */
|
|
14
|
+
idleTimeoutMinutes?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface E2BFactorySandboxOptions extends Omit<E2BSandboxOptions, 'id' | 'sandboxId' | 'template'> {
|
|
17
|
+
/** Provider-side defaults used when a setting is unset. Never stored by factory. */
|
|
18
|
+
defaults?: Partial<E2BFactorySandboxSettings>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The E2B host contract for factory: every session sandbox is an
|
|
22
|
+
* {@link E2BSandbox} whose template is {@link createRepoTemplate} over the
|
|
23
|
+
* session's repositories, sized by the factory's environment settings. The
|
|
24
|
+
* `builds` capability starts the same sha-tagged template ahead of any
|
|
25
|
+
* session and reads its status, logs and history back from E2B.
|
|
26
|
+
*/
|
|
27
|
+
export declare class E2BFactorySandbox extends FactorySandbox<E2BFactorySandboxSettings> {
|
|
28
|
+
#private;
|
|
29
|
+
readonly provider = "e2b";
|
|
30
|
+
readonly settings: PublicSchema<E2BFactorySandboxSettings>;
|
|
31
|
+
readonly builds: FactorySandboxBuilds<E2BFactorySandboxSettings>;
|
|
32
|
+
constructor(options?: E2BFactorySandboxOptions);
|
|
33
|
+
template(ctx: FactorySandboxContext, settings: E2BFactorySandboxSettings): DeferredNamedTemplateSpec | undefined;
|
|
34
|
+
create(ctx: FactorySandboxContext, settings: E2BFactorySandboxSettings): E2BSandbox;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=factory-sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory-sandbox.d.ts","sourceRoot":"","sources":["../src/factory-sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAIV,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAGnD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAElE,mHAAmH;AACnH,MAAM,WAAW,yBAA0B,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxE,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA2DD,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,WAAW,GAAG,UAAU,CAAC;IACxG,oFAAoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAC,yBAAyB,CAAC,CAAC;CAC/C;AAQD;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,cAAc,CAAC,yBAAyB,CAAC;;IAC9E,QAAQ,CAAC,QAAQ,SAAS;IAC1B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,yBAAyB,CAAC,CAAC;IAC3D,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC,yBAAyB,CAAC,CAAC;IAKjE,YAAY,OAAO,GAAE,wBAA6B,EAejD;IAED,QAAQ,CAAC,GAAG,EAAE,qBAAqB,EAAE,QAAQ,EAAE,yBAAyB,GAAG,yBAAyB,GAAG,SAAS,CAE/G;IAED,MAAM,CAAC,GAAG,EAAE,qBAAqB,EAAE,QAAQ,EAAE,yBAAyB,GAAG,UAAU,CAUlF;CA6FF"}
|
package/dist/index.cjs
CHANGED
|
@@ -2146,6 +2146,222 @@ const e2bSandboxProvider = {
|
|
|
2146
2146
|
createSandbox: (config) => new E2BSandbox(config)
|
|
2147
2147
|
};
|
|
2148
2148
|
//#endregion
|
|
2149
|
+
//#region src/factory-sandbox.ts
|
|
2150
|
+
/** Applied when neither the environment setting nor a host default is set. */
|
|
2151
|
+
const DEFAULT_SETTINGS = {
|
|
2152
|
+
cpuCount: 2,
|
|
2153
|
+
memoryMb: 1024,
|
|
2154
|
+
idleTimeoutMinutes: 5
|
|
2155
|
+
};
|
|
2156
|
+
const SETTINGS_SCHEMA = {
|
|
2157
|
+
type: "object",
|
|
2158
|
+
properties: {
|
|
2159
|
+
cpuCount: {
|
|
2160
|
+
type: "integer",
|
|
2161
|
+
title: "CPU",
|
|
2162
|
+
description: "vCPUs for the sandbox template",
|
|
2163
|
+
default: DEFAULT_SETTINGS.cpuCount,
|
|
2164
|
+
minimum: 1,
|
|
2165
|
+
maximum: 8
|
|
2166
|
+
},
|
|
2167
|
+
memoryMb: {
|
|
2168
|
+
type: "integer",
|
|
2169
|
+
title: "Memory (MB)",
|
|
2170
|
+
description: "Memory in MB for the sandbox template",
|
|
2171
|
+
default: DEFAULT_SETTINGS.memoryMb,
|
|
2172
|
+
minimum: 512,
|
|
2173
|
+
maximum: 65536
|
|
2174
|
+
},
|
|
2175
|
+
idleTimeoutMinutes: {
|
|
2176
|
+
type: "integer",
|
|
2177
|
+
title: "Idle timeout (minutes)",
|
|
2178
|
+
description: "Minutes a sandbox stays alive before E2B stops it",
|
|
2179
|
+
default: DEFAULT_SETTINGS.idleTimeoutMinutes,
|
|
2180
|
+
minimum: 1,
|
|
2181
|
+
maximum: 1440
|
|
2182
|
+
}
|
|
2183
|
+
},
|
|
2184
|
+
additionalProperties: false
|
|
2185
|
+
};
|
|
2186
|
+
/**
|
|
2187
|
+
* The settings schema with the provider's effective defaults as `default`:
|
|
2188
|
+
* the built-in values, overridden by the host's `defaults` option. The schema
|
|
2189
|
+
* is what clients show as placeholders, so it must match what the provider
|
|
2190
|
+
* applies when a setting is unset.
|
|
2191
|
+
*/
|
|
2192
|
+
function settingsSchema(options) {
|
|
2193
|
+
const { defaults, timeout } = options;
|
|
2194
|
+
const effective = {
|
|
2195
|
+
...DEFAULT_SETTINGS,
|
|
2196
|
+
...timeout !== void 0 ? { idleTimeoutMinutes: Math.ceil(timeout / 6e4) } : {},
|
|
2197
|
+
...defaults
|
|
2198
|
+
};
|
|
2199
|
+
const properties = Object.fromEntries(Object.entries(SETTINGS_SCHEMA.properties).map(([key, property]) => [key, {
|
|
2200
|
+
...property,
|
|
2201
|
+
default: effective[key]
|
|
2202
|
+
}]));
|
|
2203
|
+
return {
|
|
2204
|
+
...SETTINGS_SCHEMA,
|
|
2205
|
+
properties
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
/** Log entries E2B returns per build-status call. */
|
|
2209
|
+
const BUILD_LOG_PAGE = 100;
|
|
2210
|
+
/** The id of a build that needed no build: the sha-tagged template already existed. */
|
|
2211
|
+
const EXISTING_BUILD = "existing";
|
|
2212
|
+
/**
|
|
2213
|
+
* The E2B host contract for factory: every session sandbox is an
|
|
2214
|
+
* {@link E2BSandbox} whose template is {@link createRepoTemplate} over the
|
|
2215
|
+
* session's repositories, sized by the factory's environment settings. The
|
|
2216
|
+
* `builds` capability starts the same sha-tagged template ahead of any
|
|
2217
|
+
* session and reads its status, logs and history back from E2B.
|
|
2218
|
+
*/
|
|
2219
|
+
var E2BFactorySandbox = class extends _mastra_core_workspace.FactorySandbox {
|
|
2220
|
+
provider = "e2b";
|
|
2221
|
+
settings;
|
|
2222
|
+
builds;
|
|
2223
|
+
#options;
|
|
2224
|
+
#connection;
|
|
2225
|
+
constructor(options = {}) {
|
|
2226
|
+
super();
|
|
2227
|
+
this.#options = options;
|
|
2228
|
+
this.settings = settingsSchema(options);
|
|
2229
|
+
this.#connection = {
|
|
2230
|
+
...options.domain && { domain: options.domain },
|
|
2231
|
+
...options.apiUrl && { apiUrl: options.apiUrl },
|
|
2232
|
+
...options.apiKey && { apiKey: options.apiKey },
|
|
2233
|
+
...options.accessToken && { accessToken: options.accessToken }
|
|
2234
|
+
};
|
|
2235
|
+
this.builds = {
|
|
2236
|
+
start: (ctx, settings) => this.#startBuild(ctx, settings),
|
|
2237
|
+
get: (_ctx, _settings, buildId) => this.#getBuild(buildId),
|
|
2238
|
+
list: (ctx, settings) => this.#listBuilds(ctx, settings)
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2241
|
+
template(ctx, settings) {
|
|
2242
|
+
return createRepoTemplate(this.#templateOptions(ctx, settings));
|
|
2243
|
+
}
|
|
2244
|
+
create(ctx, settings) {
|
|
2245
|
+
const { defaults, ...options } = this.#options;
|
|
2246
|
+
return new E2BSandbox({
|
|
2247
|
+
...options,
|
|
2248
|
+
id: ctx.sessionId,
|
|
2249
|
+
sandboxId: ctx.sandboxId,
|
|
2250
|
+
template: this.template(ctx, settings),
|
|
2251
|
+
...ctx.workingDirectory ? { workingDirectory: ctx.workingDirectory } : {},
|
|
2252
|
+
timeout: this.#timeout(settings)
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
/** The setting, then the host default, then the host's own `timeout` option, then 5 minutes. */
|
|
2256
|
+
#timeout(settings) {
|
|
2257
|
+
const minutes = settings.idleTimeoutMinutes ?? this.#options.defaults?.idleTimeoutMinutes;
|
|
2258
|
+
if (minutes !== void 0) return minutes * 6e4;
|
|
2259
|
+
return this.#options.timeout ?? DEFAULT_SETTINGS.idleTimeoutMinutes * 6e4;
|
|
2260
|
+
}
|
|
2261
|
+
#templateOptions(ctx, settings) {
|
|
2262
|
+
const { defaults } = this.#options;
|
|
2263
|
+
const { resolveHead: _resolveHead, ...rest } = ctx;
|
|
2264
|
+
return {
|
|
2265
|
+
...rest,
|
|
2266
|
+
cpuCount: settings.cpuCount ?? defaults?.cpuCount ?? DEFAULT_SETTINGS.cpuCount,
|
|
2267
|
+
memoryMB: settings.memoryMb ?? defaults?.memoryMb ?? DEFAULT_SETTINGS.memoryMb
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
async #startBuild(ctx, settings) {
|
|
2271
|
+
const { spec } = await resolveSpecAtHead(this.#templateOptions(ctx, settings));
|
|
2272
|
+
if (await e2b.Template.exists(spec.ref, this.#connection)) return {
|
|
2273
|
+
buildId: `${spec.ref}:${EXISTING_BUILD}`,
|
|
2274
|
+
templateId: spec.ref,
|
|
2275
|
+
status: "ready"
|
|
2276
|
+
};
|
|
2277
|
+
const info = await e2b.Template.buildInBackground(spec.template, spec.ref, {
|
|
2278
|
+
...this.#connection,
|
|
2279
|
+
...spec.buildTags?.length ? { tags: spec.buildTags } : {},
|
|
2280
|
+
...spec.buildResources
|
|
2281
|
+
});
|
|
2282
|
+
return {
|
|
2283
|
+
buildId: `${info.templateId}:${info.buildId}`,
|
|
2284
|
+
templateId: info.templateId,
|
|
2285
|
+
status: "pending"
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
async #getBuild(compositeId) {
|
|
2289
|
+
const { templateId, buildId } = splitBuildId(compositeId);
|
|
2290
|
+
if (buildId === EXISTING_BUILD) return {
|
|
2291
|
+
buildId: compositeId,
|
|
2292
|
+
templateId,
|
|
2293
|
+
status: "ready"
|
|
2294
|
+
};
|
|
2295
|
+
let response = await e2b.Template.getBuildStatus({
|
|
2296
|
+
templateId,
|
|
2297
|
+
buildId
|
|
2298
|
+
}, this.#connection);
|
|
2299
|
+
const entries = [...response.logEntries ?? []];
|
|
2300
|
+
while (response.logEntries && response.logEntries.length >= BUILD_LOG_PAGE) {
|
|
2301
|
+
response = await e2b.Template.getBuildStatus({
|
|
2302
|
+
templateId,
|
|
2303
|
+
buildId
|
|
2304
|
+
}, {
|
|
2305
|
+
...this.#connection,
|
|
2306
|
+
logsOffset: entries.length
|
|
2307
|
+
});
|
|
2308
|
+
entries.push(...response.logEntries ?? []);
|
|
2309
|
+
}
|
|
2310
|
+
const logs = entries.length ? entries.map((entry) => entry.toString()) : response.logs?.length ? response.logs : void 0;
|
|
2311
|
+
return {
|
|
2312
|
+
buildId: compositeId,
|
|
2313
|
+
templateId: response.templateID,
|
|
2314
|
+
status: mapStatus(response.status),
|
|
2315
|
+
...logs ? { logs } : {},
|
|
2316
|
+
...response.reason?.message ? { error: response.reason.message } : {}
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
async #listBuilds(ctx, settings) {
|
|
2320
|
+
const { spec } = await resolveSpecAtHead(this.#templateOptions(ctx, settings));
|
|
2321
|
+
const name = templateNameOf(spec.ref);
|
|
2322
|
+
const client = new e2b.ApiClient(new e2b.ConnectionConfig(this.#connection));
|
|
2323
|
+
const templates = await client.api.GET("/templates");
|
|
2324
|
+
if (templates.error || !templates.data) throw new Error(`E2B template listing failed: ${describeApiError(templates.error)}`);
|
|
2325
|
+
const template = templates.data.find((t) => t.names.includes(name) || t.aliases.includes(name));
|
|
2326
|
+
if (!template) return [];
|
|
2327
|
+
const detail = await client.api.GET("/templates/{templateID}", { params: { path: { templateID: template.templateID } } });
|
|
2328
|
+
if (detail.error || !detail.data) throw new Error(`E2B template build listing failed: ${describeApiError(detail.error)}`);
|
|
2329
|
+
return detail.data.builds.slice().sort((a, b) => b.createdAt.localeCompare(a.createdAt)).map((build) => ({
|
|
2330
|
+
buildId: `${template.templateID}:${build.buildID}`,
|
|
2331
|
+
templateId: template.templateID,
|
|
2332
|
+
status: mapStatus(build.status),
|
|
2333
|
+
startedAt: build.createdAt,
|
|
2334
|
+
...build.finishedAt ? { finishedAt: build.finishedAt } : {}
|
|
2335
|
+
}));
|
|
2336
|
+
}
|
|
2337
|
+
};
|
|
2338
|
+
function describeApiError(error) {
|
|
2339
|
+
return error ? JSON.stringify(error) : "empty response";
|
|
2340
|
+
}
|
|
2341
|
+
/** `<templateId>:<buildId>`, split on the last colon because a template ref itself carries one (`name:sha-<head>`). */
|
|
2342
|
+
function splitBuildId(compositeId) {
|
|
2343
|
+
const at = compositeId.lastIndexOf(":");
|
|
2344
|
+
if (at <= 0 || at === compositeId.length - 1) throw new Error(`Invalid E2B build id "${compositeId}": expected "<templateId>:<buildId>"`);
|
|
2345
|
+
return {
|
|
2346
|
+
templateId: compositeId.slice(0, at),
|
|
2347
|
+
buildId: compositeId.slice(at + 1)
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
/** The template name of a `name:tag` ref. */
|
|
2351
|
+
function templateNameOf(ref) {
|
|
2352
|
+
const at = ref.lastIndexOf(":");
|
|
2353
|
+
return at > 0 ? ref.slice(0, at) : ref;
|
|
2354
|
+
}
|
|
2355
|
+
function mapStatus(status) {
|
|
2356
|
+
switch (status) {
|
|
2357
|
+
case "waiting": return "pending";
|
|
2358
|
+
case "building": return "building";
|
|
2359
|
+
case "ready": return "ready";
|
|
2360
|
+
case "error": return "failed";
|
|
2361
|
+
default: return "unknown";
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
//#endregion
|
|
2149
2365
|
//#region src/code-mode/transport.ts
|
|
2150
2366
|
/**
|
|
2151
2367
|
* Code Mode — E2B transport
|
|
@@ -2353,6 +2569,7 @@ var E2BCodeModeTransport = class {
|
|
|
2353
2569
|
//#endregion
|
|
2354
2570
|
exports.DEFAULT_NODE_VERSION = DEFAULT_NODE_VERSION;
|
|
2355
2571
|
exports.E2BCodeModeTransport = E2BCodeModeTransport;
|
|
2572
|
+
exports.E2BFactorySandbox = E2BFactorySandbox;
|
|
2356
2573
|
exports.E2BProcessManager = E2BProcessManager;
|
|
2357
2574
|
exports.E2BSandbox = E2BSandbox;
|
|
2358
2575
|
exports.createDefaultMountableTemplate = createDefaultMountableTemplate;
|