@mastra/e2b 0.13.0-alpha.3 → 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.
@@ -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;