@mastra/platform-workspace 1.4.1 → 1.5.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/CHANGELOG.md +37 -0
- package/LICENSE.md +6 -4
- package/README.md +40 -9
- package/dist/address-registry.d.ts.map +1 -1
- package/dist/client.d.ts +3 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/filesystem.d.ts.map +1 -1
- package/dist/index.cjs +388 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +392 -24
- package/dist/index.js.map +1 -1
- package/dist/private-net-exec.d.ts.map +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/repo-template.d.ts +69 -0
- package/dist/repo-template.d.ts.map +1 -0
- package/dist/sandbox.d.ts +60 -0
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/template.d.ts +95 -0
- package/dist/template.d.ts.map +1 -0
- package/package.json +7 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
export { PlatformClient, PlatformApiError, type PlatformClientOptions, type PlatformProxyError } from './client.js';
|
|
1
|
+
export { PlatformClient, PlatformApiError, type PlatformClientOptions, type PlatformProxyError, type SandboxProvider, } from './client.js';
|
|
2
2
|
export { PlatformFilesystem, type PlatformFilesystemOptions } from './filesystem.js';
|
|
3
|
-
export {
|
|
3
|
+
export { Template, type AptInstallOptions, type JsonPrimitive, type JsonValue, type NpmInstallOptions, type PipInstallOptions, type SandboxTemplateBuildOptions, type SandboxTemplateBuildResult, type SandboxTemplateBuilder, type SandboxTemplateMethod, type SetEnvsOptions, type SandboxTemplateOperation, type SerializedSandboxTemplate, } from './template.js';
|
|
4
|
+
export { PlatformSandbox, SandboxExecTransportError, SandboxDestroyedError, type PlatformSandboxOptions, type PlatformSandboxNetworkIsolation, type PlatformSandboxTemplate, type SandboxAddressRegistry, type SandboxTemplatePending, } from './sandbox.js';
|
|
5
|
+
export { createRepoTemplate, type PlatformRepoTemplateOptions, type PlatformRepoTemplateResolver, } from './repo-template.js';
|
|
4
6
|
export { platformFilesystemProvider, platformSandboxProvider } from './provider.js';
|
|
5
7
|
export { execViaPrivateNetwork, PrivateNetExecHttpError, type PrivateNetExecOptions, type PrivateNetExecResult, type PrivateNetFetch, } from './private-net-exec.js';
|
|
6
8
|
export { InProcessSandboxAddressRegistry } from './address-registry.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EACL,QAAQ,EACR,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,eAAe,EACf,yBAAyB,EACzB,qBAAqB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,+BAA+B,EACpC,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,EAClB,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,GAClC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACpF,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,+BAA+B,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { Buffer } from "buffer";
|
|
1
|
+
import { Buffer as Buffer$1 } from "buffer";
|
|
2
2
|
import nodePath from "path";
|
|
3
3
|
import { FileExistsError, FileNotFoundError, MastraFilesystem, MastraSandbox, ProcessHandle, SandboxNotReadyError, SandboxProcessManager, UnsupportedStdinCloseError, WorkspaceReadOnlyError } from "@mastra/core/workspace";
|
|
4
4
|
import { CommandExitError, Sandbox, TimeoutError } from "e2b";
|
|
5
|
+
import { execFile } from "child_process";
|
|
6
|
+
import { promisify } from "util";
|
|
5
7
|
//#region src/client.ts
|
|
6
8
|
const DEFAULT_PROXY_URL = "https://workspaces.mastra.ai";
|
|
7
9
|
/**
|
|
@@ -15,19 +17,19 @@ function requireOption(value, name) {
|
|
|
15
17
|
return value;
|
|
16
18
|
}
|
|
17
19
|
function resolveSandboxProvider(value) {
|
|
18
|
-
const provider = value?.trim() || "
|
|
20
|
+
const provider = value?.trim() || "e2b";
|
|
19
21
|
if (provider !== "railway" && provider !== "e2b") throw new Error("SANDBOX_PROVIDER must be either \"railway\" or \"e2b\"");
|
|
20
22
|
return provider;
|
|
21
23
|
}
|
|
22
24
|
function resolvePlatformOptions(options) {
|
|
23
|
-
const
|
|
25
|
+
const environmentSandboxProvider = process.env.SANDBOX_PROVIDER?.trim();
|
|
26
|
+
const configuredSandboxProvider = options.sandboxProvider ?? environmentSandboxProvider;
|
|
24
27
|
return {
|
|
25
28
|
accessToken: requireOption(options.accessToken ?? process.env.MASTRA_PLATFORM_ACCESS_TOKEN, "accessToken"),
|
|
26
29
|
projectId: requireOption(options.projectId ?? process.env.MASTRA_PROJECT_ID, "projectId"),
|
|
27
30
|
actingUserId: options.actingUserId?.trim() || void 0,
|
|
28
31
|
proxyUrl: (process.env.MASTRA_WORKSPACE_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\/$/, ""),
|
|
29
32
|
sandboxProvider: resolveSandboxProvider(configuredSandboxProvider),
|
|
30
|
-
useLegacyRoutes: !configuredSandboxProvider,
|
|
31
33
|
sessionId: options.sessionId,
|
|
32
34
|
threadId: options.threadId,
|
|
33
35
|
fetch: options.fetch ?? fetch
|
|
@@ -75,7 +77,6 @@ var PlatformClient = class {
|
|
|
75
77
|
actingUserId;
|
|
76
78
|
proxyUrl;
|
|
77
79
|
sandboxProvider;
|
|
78
|
-
useLegacyRoutes;
|
|
79
80
|
/** Advisory session correlation id — see {@link PlatformClientOptions.sessionId}. */
|
|
80
81
|
sessionId;
|
|
81
82
|
/** Advisory thread correlation id — see {@link PlatformClientOptions.threadId}. */
|
|
@@ -88,13 +89,17 @@ var PlatformClient = class {
|
|
|
88
89
|
this.actingUserId = resolved.actingUserId;
|
|
89
90
|
this.proxyUrl = resolved.proxyUrl;
|
|
90
91
|
this.sandboxProvider = resolved.sandboxProvider;
|
|
91
|
-
this.useLegacyRoutes = resolved.useLegacyRoutes;
|
|
92
92
|
this.sessionId = resolved.sessionId;
|
|
93
93
|
this.threadId = resolved.threadId;
|
|
94
94
|
this.fetch = resolved.fetch;
|
|
95
95
|
}
|
|
96
96
|
async request(path, options = {}) {
|
|
97
|
-
|
|
97
|
+
return this.requestAtPath(`/${this.sandboxProvider}`, path, options);
|
|
98
|
+
}
|
|
99
|
+
async requestProvider(path, options = {}) {
|
|
100
|
+
return this.requestAtPath(`/${this.sandboxProvider}`, path, options);
|
|
101
|
+
}
|
|
102
|
+
async requestAtPath(providerPath, path, options) {
|
|
98
103
|
const url = new URL(`${this.proxyUrl}/v1${providerPath}/projects/${encodeURIComponent(this.projectId)}${path}`);
|
|
99
104
|
for (const [key, value] of Object.entries(options.query ?? {})) if (value !== void 0) url.searchParams.set(key, String(value));
|
|
100
105
|
const headers = new Headers(options.headers);
|
|
@@ -141,7 +146,7 @@ function nameFromPath(path) {
|
|
|
141
146
|
}
|
|
142
147
|
function contentToBody(content) {
|
|
143
148
|
if (typeof content === "string") return content;
|
|
144
|
-
return Buffer.from(content);
|
|
149
|
+
return Buffer$1.from(content);
|
|
145
150
|
}
|
|
146
151
|
function headerDate(headers, name) {
|
|
147
152
|
const value = headers.get(name);
|
|
@@ -193,7 +198,7 @@ var PlatformFilesystem = class extends MastraFilesystem {
|
|
|
193
198
|
if (isNotFound(error)) throw new FileNotFoundError(path);
|
|
194
199
|
throw error;
|
|
195
200
|
}
|
|
196
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
201
|
+
const buffer = Buffer$1.from(await response.arrayBuffer());
|
|
197
202
|
return options?.encoding ? buffer.toString(options.encoding) : buffer;
|
|
198
203
|
}
|
|
199
204
|
async writeFile(path, content, options) {
|
|
@@ -224,8 +229,8 @@ var PlatformFilesystem = class extends MastraFilesystem {
|
|
|
224
229
|
* Use `writeFile` with distinct keys for concurrent writers.
|
|
225
230
|
*/
|
|
226
231
|
async appendFile(path, content) {
|
|
227
|
-
const existing = await this.exists(path) ? await this.readFile(path) : Buffer.alloc(0);
|
|
228
|
-
await this.writeFile(path, Buffer.concat([Buffer.isBuffer(existing) ? existing : Buffer.from(existing), Buffer.from(content)]));
|
|
232
|
+
const existing = await this.exists(path) ? await this.readFile(path) : Buffer$1.alloc(0);
|
|
233
|
+
await this.writeFile(path, Buffer$1.concat([Buffer$1.isBuffer(existing) ? existing : Buffer$1.from(existing), Buffer$1.from(content)]));
|
|
229
234
|
}
|
|
230
235
|
async deleteFile(path, options) {
|
|
231
236
|
await this.ensureReady();
|
|
@@ -362,6 +367,185 @@ function matchesExtension(name, extension) {
|
|
|
362
367
|
return (Array.isArray(extension) ? extension : [extension]).some((ext) => name.endsWith(ext));
|
|
363
368
|
}
|
|
364
369
|
//#endregion
|
|
370
|
+
//#region src/template.ts
|
|
371
|
+
const SERIALIZE_TEMPLATE = Symbol("serializeTemplate");
|
|
372
|
+
const GET_TEMPLATE_BUILD_ENVS = Symbol("getTemplateBuildEnvs");
|
|
373
|
+
const MAX_OPERATIONS = 256;
|
|
374
|
+
const MAX_SERIALIZED_BYTES = 256 * 1024;
|
|
375
|
+
const MAX_STRING_LENGTH = 32 * 1024;
|
|
376
|
+
const MAX_COLLECTION_ITEMS = 512;
|
|
377
|
+
const MAX_FAMILY_LENGTH = 200;
|
|
378
|
+
var SerializableSandboxTemplateBuilder = class SerializableSandboxTemplateBuilder {
|
|
379
|
+
#operations;
|
|
380
|
+
#family;
|
|
381
|
+
#buildEnvs;
|
|
382
|
+
constructor(operations = [], family, buildEnvs = {}) {
|
|
383
|
+
this.#operations = operations;
|
|
384
|
+
this.#family = family;
|
|
385
|
+
this.#buildEnvs = buildEnvs;
|
|
386
|
+
}
|
|
387
|
+
cpuCount(count) {
|
|
388
|
+
return this.#append("cpuCount", [validateResourceValue(count, "count")]);
|
|
389
|
+
}
|
|
390
|
+
memoryMB(memoryMB) {
|
|
391
|
+
return this.#append("memoryMB", [validateResourceValue(memoryMB, "memoryMB")]);
|
|
392
|
+
}
|
|
393
|
+
async build(options = {}) {
|
|
394
|
+
return buildSandboxTemplate(this, options);
|
|
395
|
+
}
|
|
396
|
+
runCmd(command) {
|
|
397
|
+
return this.#append("runCmd", [validateStringOrStrings(command, "command")]);
|
|
398
|
+
}
|
|
399
|
+
setWorkdir(path) {
|
|
400
|
+
return this.#append("setWorkdir", [validateString(path, "path")]);
|
|
401
|
+
}
|
|
402
|
+
setEnvs(envs, options) {
|
|
403
|
+
const copy = validateStringRecord(envs, "envs", "environment variable");
|
|
404
|
+
if ((options === void 0 ? void 0 : validateBooleanOptions(options, ["ephemeral"]))?.ephemeral === true) return new SerializableSandboxTemplateBuilder(this.#operations, this.#family, {
|
|
405
|
+
...this.#buildEnvs,
|
|
406
|
+
...copy
|
|
407
|
+
});
|
|
408
|
+
return this.#append("setEnvs", [copy]);
|
|
409
|
+
}
|
|
410
|
+
aptInstall(packages, options) {
|
|
411
|
+
const args = [validateStringOrStrings(packages, "packages")];
|
|
412
|
+
if (options !== void 0) args.push(validateBooleanOptions(options, ["noInstallRecommends", "fixMissing"]));
|
|
413
|
+
return this.#append("aptInstall", args);
|
|
414
|
+
}
|
|
415
|
+
pipInstall(packages, options) {
|
|
416
|
+
return this.#appendOptionalInstall("pipInstall", packages, options, ["g"]);
|
|
417
|
+
}
|
|
418
|
+
npmInstall(packages, options) {
|
|
419
|
+
return this.#appendOptionalInstall("npmInstall", packages, options, ["g", "dev"]);
|
|
420
|
+
}
|
|
421
|
+
withFamily(family) {
|
|
422
|
+
if (typeof family !== "string" || family.length === 0) throw new TypeError("family must be a non-empty string");
|
|
423
|
+
if (family.length > MAX_FAMILY_LENGTH) throw new RangeError(`family cannot exceed ${MAX_FAMILY_LENGTH} characters`);
|
|
424
|
+
assertSerializedSize(this.#operations, family);
|
|
425
|
+
return new SerializableSandboxTemplateBuilder(this.#operations, family, this.#buildEnvs);
|
|
426
|
+
}
|
|
427
|
+
[GET_TEMPLATE_BUILD_ENVS]() {
|
|
428
|
+
return Object.keys(this.#buildEnvs).length > 0 ? { ...this.#buildEnvs } : void 0;
|
|
429
|
+
}
|
|
430
|
+
[SERIALIZE_TEMPLATE]() {
|
|
431
|
+
return {
|
|
432
|
+
schemaVersion: 1,
|
|
433
|
+
operations: this.#operations.map((operation) => ({
|
|
434
|
+
method: operation.method,
|
|
435
|
+
args: cloneJson(operation.args)
|
|
436
|
+
})),
|
|
437
|
+
...this.#family !== void 0 && { family: this.#family }
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
#appendOptionalInstall(method, packages, options, optionKeys) {
|
|
441
|
+
const args = [];
|
|
442
|
+
if (packages !== void 0) args.push(validateStringOrStrings(packages, "packages"));
|
|
443
|
+
if (options !== void 0) {
|
|
444
|
+
if (packages === void 0) args.push(null);
|
|
445
|
+
args.push(validateBooleanOptions(options, optionKeys));
|
|
446
|
+
}
|
|
447
|
+
return this.#append(method, args);
|
|
448
|
+
}
|
|
449
|
+
#append(method, args) {
|
|
450
|
+
if (this.#operations.length >= MAX_OPERATIONS) throw new RangeError(`Sandbox template cannot contain more than ${MAX_OPERATIONS} operations`);
|
|
451
|
+
const operation = {
|
|
452
|
+
method,
|
|
453
|
+
args: cloneJson(args)
|
|
454
|
+
};
|
|
455
|
+
const operations = [...this.#operations, operation];
|
|
456
|
+
assertSerializedSize(operations, this.#family);
|
|
457
|
+
return new SerializableSandboxTemplateBuilder(operations, this.#family, this.#buildEnvs);
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
function assertSerializedSize(operations, family) {
|
|
461
|
+
const serialized = JSON.stringify({
|
|
462
|
+
schemaVersion: 1,
|
|
463
|
+
operations,
|
|
464
|
+
...family !== void 0 && { family }
|
|
465
|
+
});
|
|
466
|
+
if (new TextEncoder().encode(serialized).byteLength > MAX_SERIALIZED_BYTES) throw new RangeError(`Serialized sandbox template cannot exceed ${MAX_SERIALIZED_BYTES} bytes`);
|
|
467
|
+
}
|
|
468
|
+
function Template() {
|
|
469
|
+
return new SerializableSandboxTemplateBuilder();
|
|
470
|
+
}
|
|
471
|
+
function isSandboxTemplateBuilder(value) {
|
|
472
|
+
return value instanceof SerializableSandboxTemplateBuilder;
|
|
473
|
+
}
|
|
474
|
+
function serializeSandboxTemplate(template) {
|
|
475
|
+
if (!isSandboxTemplateBuilder(template)) throw new TypeError("template must be created with Template()");
|
|
476
|
+
return template[SERIALIZE_TEMPLATE]();
|
|
477
|
+
}
|
|
478
|
+
function getSandboxTemplateBuildEnvs(template) {
|
|
479
|
+
if (!isSandboxTemplateBuilder(template)) throw new TypeError("template must be created with Template()");
|
|
480
|
+
return template[GET_TEMPLATE_BUILD_ENVS]();
|
|
481
|
+
}
|
|
482
|
+
async function buildSandboxTemplate(template, options) {
|
|
483
|
+
const environmentId = options.environmentId ?? process.env.MASTRA_ENVIRONMENT_ID;
|
|
484
|
+
if (!environmentId) throw new Error("environmentId is required. Pass it or set MASTRA_ENVIRONMENT_ID.");
|
|
485
|
+
const client = new PlatformClient(options);
|
|
486
|
+
const templateBuildEnvs = getSandboxTemplateBuildEnvs(template);
|
|
487
|
+
return await (await client.requestProvider("/sandbox/templates/builds", {
|
|
488
|
+
method: "POST",
|
|
489
|
+
headers: { "content-type": "application/json" },
|
|
490
|
+
body: JSON.stringify({
|
|
491
|
+
environmentId,
|
|
492
|
+
templateDefinition: serializeSandboxTemplate(template),
|
|
493
|
+
...templateBuildEnvs !== void 0 && { templateBuildEnvs }
|
|
494
|
+
})
|
|
495
|
+
})).json();
|
|
496
|
+
}
|
|
497
|
+
function validateResourceValue(value, name) {
|
|
498
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) throw new TypeError(`${name} must be a positive safe integer`);
|
|
499
|
+
return value;
|
|
500
|
+
}
|
|
501
|
+
function validateString(value, name, allowEmpty = false) {
|
|
502
|
+
if (typeof value !== "string") throw new TypeError(`${name} must be a string`);
|
|
503
|
+
if (!allowEmpty && value.length === 0) throw new TypeError(`${name} must not be empty`);
|
|
504
|
+
if (value.length > MAX_STRING_LENGTH) throw new RangeError(`${name} cannot exceed ${MAX_STRING_LENGTH} characters`);
|
|
505
|
+
return value;
|
|
506
|
+
}
|
|
507
|
+
function validateStringOrStrings(value, name) {
|
|
508
|
+
if (typeof value === "string") return validateString(value, name);
|
|
509
|
+
if (!Array.isArray(value)) throw new TypeError(`${name} must be a string or an array of strings`);
|
|
510
|
+
assertCollectionSize(value.length, name);
|
|
511
|
+
if (value.length === 0) throw new TypeError(`${name} must not be empty`);
|
|
512
|
+
return Array.from(value, (item, index) => validateString(item, `${name}[${index}]`));
|
|
513
|
+
}
|
|
514
|
+
function validateStringRecord(value, name, entryName) {
|
|
515
|
+
assertPlainObject(value, name);
|
|
516
|
+
const entries = Object.entries(value);
|
|
517
|
+
assertCollectionSize(entries.length, name);
|
|
518
|
+
return Object.fromEntries(entries.map(([key, item]) => [validateString(key, `${entryName} name`), validateString(item, `${entryName} ${key}`, true)]));
|
|
519
|
+
}
|
|
520
|
+
function validateBooleanOptions(value, keys) {
|
|
521
|
+
assertPlainObject(value, "options");
|
|
522
|
+
const options = value;
|
|
523
|
+
const unknownKey = Object.keys(options).find((key) => !keys.includes(key));
|
|
524
|
+
if (unknownKey) throw new TypeError(`Unsupported option: ${unknownKey}`);
|
|
525
|
+
const copy = {};
|
|
526
|
+
for (const key of keys) {
|
|
527
|
+
const option = options[key];
|
|
528
|
+
if (option === void 0) continue;
|
|
529
|
+
if (typeof option !== "boolean") throw new TypeError(`${key} must be a boolean`);
|
|
530
|
+
copy[key] = option;
|
|
531
|
+
}
|
|
532
|
+
return copy;
|
|
533
|
+
}
|
|
534
|
+
function assertPlainObject(value, name) {
|
|
535
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError(`${name} must be a plain object`);
|
|
536
|
+
const prototype = Object.getPrototypeOf(value);
|
|
537
|
+
if (prototype !== Object.prototype && prototype !== null) throw new TypeError(`${name} must be a plain object`);
|
|
538
|
+
}
|
|
539
|
+
function assertCollectionSize(size, name) {
|
|
540
|
+
if (size > MAX_COLLECTION_ITEMS) throw new RangeError(`${name} cannot contain more than ${MAX_COLLECTION_ITEMS} items`);
|
|
541
|
+
}
|
|
542
|
+
function cloneJson(value) {
|
|
543
|
+
if (Array.isArray(value)) return value.map((item) => cloneJson(item));
|
|
544
|
+
if (typeof value === "object" && value !== null) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneJson(item)]));
|
|
545
|
+
if (typeof value === "number" && !Number.isFinite(value)) throw new TypeError("Sandbox template values must contain only finite numbers");
|
|
546
|
+
return value;
|
|
547
|
+
}
|
|
548
|
+
//#endregion
|
|
365
549
|
//#region src/direct-exec.ts
|
|
366
550
|
/**
|
|
367
551
|
* Direct exec client — opens Railway's tcp-proxy exec WebSocket directly using
|
|
@@ -902,10 +1086,26 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
902
1086
|
name = "PlatformSandbox";
|
|
903
1087
|
provider = "platform";
|
|
904
1088
|
status = "pending";
|
|
1089
|
+
/**
|
|
1090
|
+
* Populated from the platform's create/reattach response when the sandbox
|
|
1091
|
+
* booted from a prior member of the same template family or the provider
|
|
1092
|
+
* base template while the requested exact template continues to build in
|
|
1093
|
+
* the background.
|
|
1094
|
+
* `undefined` when the sandbox booted on the exact template (or when no
|
|
1095
|
+
* template was requested). Observability-only; consumers reconcile freshness
|
|
1096
|
+
* in their own runtime setup and reprovision to pick up the ready template
|
|
1097
|
+
* on a later start.
|
|
1098
|
+
*/
|
|
1099
|
+
templatePending;
|
|
905
1100
|
_client;
|
|
1101
|
+
_usesProviderRoutes;
|
|
906
1102
|
_environmentId;
|
|
907
1103
|
_sandboxId;
|
|
908
1104
|
_seedCheckpointName;
|
|
1105
|
+
_templateDefinition;
|
|
1106
|
+
_templateBuildEnvs;
|
|
1107
|
+
_template;
|
|
1108
|
+
_templateResolutionInFlight;
|
|
909
1109
|
_idleTimeoutMinutes;
|
|
910
1110
|
_networkIsolation;
|
|
911
1111
|
_env;
|
|
@@ -993,6 +1193,8 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
993
1193
|
if (!this._environmentId && !options.sandboxId) throw new Error("environmentId is required");
|
|
994
1194
|
this._sandboxId = options.sandboxId;
|
|
995
1195
|
this._seedCheckpointName = options.seedCheckpointName;
|
|
1196
|
+
this._usesProviderRoutes = options.template !== void 0;
|
|
1197
|
+
this._template = options.template;
|
|
996
1198
|
this._idleTimeoutMinutes = options.idleTimeoutMinutes;
|
|
997
1199
|
this._networkIsolation = options.networkIsolation;
|
|
998
1200
|
this._env = options.env ?? {};
|
|
@@ -1006,6 +1208,9 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1006
1208
|
generateId() {
|
|
1007
1209
|
return `platform-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1008
1210
|
}
|
|
1211
|
+
_request(path, options = {}) {
|
|
1212
|
+
return this._usesProviderRoutes ? this._client.requestProvider(path, options) : this._client.request(path, options);
|
|
1213
|
+
}
|
|
1009
1214
|
/**
|
|
1010
1215
|
* Construct a sibling {@link PlatformSandbox} that inherits this sandbox's
|
|
1011
1216
|
* credentials and defaults (access token, project, environment, network
|
|
@@ -1021,10 +1226,11 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1021
1226
|
clone(options = {}) {
|
|
1022
1227
|
const id = options.id ?? options.checkpointName;
|
|
1023
1228
|
const seedCheckpointName = options.seedCheckpointName ?? (this._client.sandboxProvider === "e2b" ? options.checkpointName : void 0) ?? this._seedCheckpointName;
|
|
1024
|
-
|
|
1229
|
+
const clone = new PlatformSandbox({
|
|
1025
1230
|
...id !== void 0 && { id },
|
|
1026
1231
|
accessToken: this._client.accessToken,
|
|
1027
1232
|
projectId: this._client.projectId,
|
|
1233
|
+
...this._usesProviderRoutes || this._client.sandboxProvider !== "railway" ? { sandboxProvider: this._client.sandboxProvider } : {},
|
|
1028
1234
|
actingUserId: options.actingUserId ?? this._client.actingUserId,
|
|
1029
1235
|
...this._client.sessionId !== void 0 && { sessionId: this._client.sessionId },
|
|
1030
1236
|
...this._client.threadId !== void 0 && { threadId: this._client.threadId },
|
|
@@ -1032,6 +1238,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1032
1238
|
environmentId: this._environmentId,
|
|
1033
1239
|
...options.sandboxId !== void 0 && { sandboxId: options.sandboxId },
|
|
1034
1240
|
...seedCheckpointName !== void 0 && { seedCheckpointName },
|
|
1241
|
+
...this._template !== void 0 && { template: this._template },
|
|
1035
1242
|
idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,
|
|
1036
1243
|
...this._networkIsolation !== void 0 && { networkIsolation: this._networkIsolation },
|
|
1037
1244
|
env: options.env ?? this._env,
|
|
@@ -1042,6 +1249,9 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1042
1249
|
...this._privateNetFetch !== void 0 && { privateNetFetch: this._privateNetFetch },
|
|
1043
1250
|
...this._addressRegistry !== void 0 && { addressRegistry: this._addressRegistry }
|
|
1044
1251
|
});
|
|
1252
|
+
clone._templateDefinition = this._templateDefinition ? structuredClone(this._templateDefinition) : void 0;
|
|
1253
|
+
clone._templateBuildEnvs = this._templateBuildEnvs ? { ...this._templateBuildEnvs } : void 0;
|
|
1254
|
+
return clone;
|
|
1045
1255
|
}
|
|
1046
1256
|
/**
|
|
1047
1257
|
* Start the sandbox: reattach to a known provider `sandboxId` when one is
|
|
@@ -1060,7 +1270,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1060
1270
|
const startedAt = Date.now();
|
|
1061
1271
|
if (this._sandboxId) try {
|
|
1062
1272
|
const requestStartedAt = Date.now();
|
|
1063
|
-
const response = await this.
|
|
1273
|
+
const response = await this._request(`/sandbox/${encodeURIComponent(this._sandboxId)}`);
|
|
1064
1274
|
const requestMs = Date.now() - requestStartedAt;
|
|
1065
1275
|
const json = await response.json();
|
|
1066
1276
|
if (!json.destroyedAt) {
|
|
@@ -1075,9 +1285,12 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1075
1285
|
this._sandboxId = void 0;
|
|
1076
1286
|
}
|
|
1077
1287
|
if (!this._environmentId) throw new Error("environmentId is required");
|
|
1078
|
-
|
|
1288
|
+
await this._prepareLazyTemplate();
|
|
1289
|
+
const createBody = () => JSON.stringify({
|
|
1079
1290
|
id: this.id,
|
|
1080
1291
|
seedCheckpointName: this._seedCheckpointName,
|
|
1292
|
+
templateDefinition: this._templateDefinition,
|
|
1293
|
+
templateBuildEnvs: this._templateBuildEnvs,
|
|
1081
1294
|
environmentId: this._environmentId,
|
|
1082
1295
|
idleTimeoutMinutes: this._idleTimeoutMinutes,
|
|
1083
1296
|
networkIsolation: this._networkIsolation,
|
|
@@ -1086,10 +1299,10 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1086
1299
|
let response;
|
|
1087
1300
|
const requestStartedAt = Date.now();
|
|
1088
1301
|
for (let attempt = 1;; attempt++) try {
|
|
1089
|
-
response = await this.
|
|
1302
|
+
response = await this._request("/sandbox", {
|
|
1090
1303
|
method: "POST",
|
|
1091
1304
|
headers: { "content-type": "application/json" },
|
|
1092
|
-
body
|
|
1305
|
+
body: createBody()
|
|
1093
1306
|
});
|
|
1094
1307
|
break;
|
|
1095
1308
|
} catch (error) {
|
|
@@ -1100,10 +1313,31 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1100
1313
|
const json = await response.json();
|
|
1101
1314
|
this._sandboxId = json.id;
|
|
1102
1315
|
this._createdAt = json.createdAt ? new Date(json.createdAt) : /* @__PURE__ */ new Date();
|
|
1316
|
+
this.templatePending = json.templatePending;
|
|
1103
1317
|
this._populateAddressFromResponse(json);
|
|
1104
1318
|
this._logStartComplete(json.id, startedAt, requestMs, "provision");
|
|
1105
1319
|
return { outcome: "created" };
|
|
1106
1320
|
}
|
|
1321
|
+
async _prepareLazyTemplate() {
|
|
1322
|
+
if (this._templateDefinition !== void 0 || this._template === void 0) return;
|
|
1323
|
+
if (!this._templateResolutionInFlight) {
|
|
1324
|
+
const attempt = this._resolveTemplate();
|
|
1325
|
+
this._templateResolutionInFlight = attempt.finally(() => {
|
|
1326
|
+
this._templateResolutionInFlight = void 0;
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
await this._templateResolutionInFlight;
|
|
1330
|
+
}
|
|
1331
|
+
async _resolveTemplate() {
|
|
1332
|
+
try {
|
|
1333
|
+
const resolved = typeof this._template === "function" ? await this._template() : this._template;
|
|
1334
|
+
if (!resolved) return;
|
|
1335
|
+
this._templateDefinition = serializeSandboxTemplate(resolved);
|
|
1336
|
+
this._templateBuildEnvs = getSandboxTemplateBuildEnvs(resolved);
|
|
1337
|
+
} catch (error) {
|
|
1338
|
+
this.logger.warn(`Platform sandbox template resolution failed; using provider default template: ${String(error)}`);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1107
1341
|
/**
|
|
1108
1342
|
* One timing summary per completed `start()` — the whole
|
|
1109
1343
|
* `PlatformSandbox`-visible boot in a single greppable line.
|
|
@@ -1268,7 +1502,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1268
1502
|
const destroyedSandboxId = this._sandboxId;
|
|
1269
1503
|
this._captureInFlight = null;
|
|
1270
1504
|
if (this._hasRecoveryKey || this._client.sandboxProvider === "e2b") try {
|
|
1271
|
-
await this.
|
|
1505
|
+
await this._request(`/sandbox/${encodeURIComponent(destroyedSandboxId)}/checkpoint`, {
|
|
1272
1506
|
method: "DELETE",
|
|
1273
1507
|
headers: { "content-type": "application/json" },
|
|
1274
1508
|
body: JSON.stringify({ id: this.id })
|
|
@@ -1295,7 +1529,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1295
1529
|
this._probeGeneration++;
|
|
1296
1530
|
this._probeTarget = null;
|
|
1297
1531
|
this._transportReadyPromise = null;
|
|
1298
|
-
await this.
|
|
1532
|
+
await this._request(`/sandbox/${encodeURIComponent(destroyedSandboxId)}`, { method: "DELETE" });
|
|
1299
1533
|
this._sandboxId = void 0;
|
|
1300
1534
|
this._createdAt = null;
|
|
1301
1535
|
this._lease = null;
|
|
@@ -1385,7 +1619,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1385
1619
|
async _doCaptureCheckpoint(sandboxId) {
|
|
1386
1620
|
let response;
|
|
1387
1621
|
try {
|
|
1388
|
-
response = await this.
|
|
1622
|
+
response = await this._request(`/sandbox/${encodeURIComponent(sandboxId)}/checkpoint`, {
|
|
1389
1623
|
method: "POST",
|
|
1390
1624
|
headers: { "content-type": "application/json" },
|
|
1391
1625
|
body: JSON.stringify({ id: this.id })
|
|
@@ -1516,8 +1750,20 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1516
1750
|
* Returns a result with a real `exitCode` OR `timedOut: true`. Never
|
|
1517
1751
|
* returns `{ exitCode: null, timedOut: false }` — that case throws.
|
|
1518
1752
|
*/
|
|
1753
|
+
/**
|
|
1754
|
+
* Drop undefined values so the result matches the Record<string, string>
|
|
1755
|
+
* shape the exec transports expect (`ExecuteCommandOptions.env` is
|
|
1756
|
+
* NodeJS.ProcessEnv). The sandbox's own env is already merged in by
|
|
1757
|
+
* `executeCommand` before a transport sees these options. Returns undefined
|
|
1758
|
+
* when there is nothing to send.
|
|
1759
|
+
*/
|
|
1760
|
+
_execEnv(options) {
|
|
1761
|
+
if (!options?.env) return void 0;
|
|
1762
|
+
const filtered = Object.fromEntries(Object.entries(options.env).filter((entry) => entry[1] !== void 0));
|
|
1763
|
+
return Object.keys(filtered).length > 0 ? filtered : void 0;
|
|
1764
|
+
}
|
|
1519
1765
|
async _runDirectExec(fullCommand, effectiveTimeout, options) {
|
|
1520
|
-
const filteredEnv =
|
|
1766
|
+
const filteredEnv = this._execEnv(options);
|
|
1521
1767
|
let lastResult;
|
|
1522
1768
|
let lastLease;
|
|
1523
1769
|
let attemptsMade = 0;
|
|
@@ -1580,7 +1826,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1580
1826
|
* sidecar bug). Only this specific exec falls back.
|
|
1581
1827
|
*/
|
|
1582
1828
|
async _tryExecViaPrivateNetwork(instanceUrl, fullCommand, effectiveTimeout, options) {
|
|
1583
|
-
const filteredEnv =
|
|
1829
|
+
const filteredEnv = this._execEnv(options);
|
|
1584
1830
|
const execOptions = {
|
|
1585
1831
|
command: fullCommand,
|
|
1586
1832
|
...options?.cwd !== void 0 && { cwd: options.cwd },
|
|
@@ -1629,7 +1875,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1629
1875
|
if (!this._sandboxId) throw new SandboxNotReadyError(this.id);
|
|
1630
1876
|
const sandboxId = this._sandboxId;
|
|
1631
1877
|
const inFlight = (async () => {
|
|
1632
|
-
const json = await (await this.
|
|
1878
|
+
const json = await (await this._request(`/sandbox/${encodeURIComponent(sandboxId)}/exec-lease`, { method: "POST" })).json();
|
|
1633
1879
|
const expiresAtMs = json.expiresAt ? Date.parse(json.expiresAt) : null;
|
|
1634
1880
|
const lease = {
|
|
1635
1881
|
provider: json.provider,
|
|
@@ -1667,7 +1913,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1667
1913
|
createdAt: this._createdAt ?? /* @__PURE__ */ new Date(),
|
|
1668
1914
|
metadata: { sandboxId: this._sandboxId }
|
|
1669
1915
|
};
|
|
1670
|
-
const json = await (await this.
|
|
1916
|
+
const json = await (await this._request(`/sandbox/${encodeURIComponent(this._sandboxId)}`)).json();
|
|
1671
1917
|
return {
|
|
1672
1918
|
id: json.id,
|
|
1673
1919
|
name: this.name,
|
|
@@ -1692,6 +1938,122 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
|
|
|
1692
1938
|
}
|
|
1693
1939
|
};
|
|
1694
1940
|
//#endregion
|
|
1941
|
+
//#region src/repo-template.ts
|
|
1942
|
+
const execFileAsync = promisify(execFile);
|
|
1943
|
+
const SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
1944
|
+
const BUILD_TOKEN_ENV = "MASTRA_REPOSITORY_ACCESS_TOKEN";
|
|
1945
|
+
/**
|
|
1946
|
+
* Clone URLs interpolate into the template's build commands, so constrain
|
|
1947
|
+
* them to https plus plain host/path characters. Every regex here is a
|
|
1948
|
+
* single anchored character class, so matching stays linear on adversarial
|
|
1949
|
+
* input; the structural checks go through WHATWG URL parsing instead of one
|
|
1950
|
+
* big backtracking pattern.
|
|
1951
|
+
*/
|
|
1952
|
+
const CLONE_URL_ALLOWED_CHARS = /^[a-z0-9:/._-]+$/i;
|
|
1953
|
+
const CLONE_URL_HOST_PATTERN = /^[a-z0-9.-]+$/i;
|
|
1954
|
+
const CLONE_URL_SEGMENT_PATTERN = /^[\w.-]+$/;
|
|
1955
|
+
/**
|
|
1956
|
+
* Create a lazy repository template definition for PlatformSandbox, mirroring
|
|
1957
|
+
* `@mastra/e2b`'s `createRepoTemplate`: pass the sandbox context through and a
|
|
1958
|
+
* repo-less session boots the provider default.
|
|
1959
|
+
*
|
|
1960
|
+
* The resolver performs no work until a fresh sandbox starts. It clones the
|
|
1961
|
+
* URL `getRepositoryAccess` resolves — the only source of the clone URL, so
|
|
1962
|
+
* what gets cloned and what the template is identified by can't drift — and
|
|
1963
|
+
* pins repositories to their current default-branch commit. Private repository
|
|
1964
|
+
* credentials are used for head resolution and sent to the provider as
|
|
1965
|
+
* transient build envs; they never enter the serialized definition. If the
|
|
1966
|
+
* head cannot be resolved, the resolver returns undefined so PlatformSandbox
|
|
1967
|
+
* boots from the provider default and the caller's runtime setup materializes
|
|
1968
|
+
* the checkout instead.
|
|
1969
|
+
*/
|
|
1970
|
+
function createRepoTemplate(options) {
|
|
1971
|
+
const getRepositoryAccess = options.getRepositoryAccess;
|
|
1972
|
+
if (!getRepositoryAccess) return void 0;
|
|
1973
|
+
const resolveHead = options.resolveHead ?? resolveDefaultBranchHead;
|
|
1974
|
+
return async () => {
|
|
1975
|
+
const access = await getRepositoryAccess().catch(() => void 0);
|
|
1976
|
+
if (!access?.cloneUrl) return void 0;
|
|
1977
|
+
const cloneUrl = normalizeCloneUrl(access.cloneUrl);
|
|
1978
|
+
if (!isValidCloneUrl(cloneUrl)) return void 0;
|
|
1979
|
+
const token = access.authorization?.token;
|
|
1980
|
+
const sha = await (token ? resolveHead(cloneUrl, token) : resolveHead(cloneUrl)).catch(() => void 0);
|
|
1981
|
+
if (!sha || !SHA_PATTERN.test(sha)) return void 0;
|
|
1982
|
+
const workdir = defaultWorkdir(cloneUrl);
|
|
1983
|
+
const auth = token ? `${gitAuthFlag()} ` : "";
|
|
1984
|
+
const steps = [
|
|
1985
|
+
`git ${auth}clone ${cloneUrl} "${workdir}"`,
|
|
1986
|
+
`git -C "${workdir}" ${auth}fetch origin ${sha}`,
|
|
1987
|
+
`git -C "${workdir}" checkout ${sha}`,
|
|
1988
|
+
...options.setupCommand ? [`cd "${workdir}" && ${options.setupCommand}`] : []
|
|
1989
|
+
];
|
|
1990
|
+
const family = `repo:${cloneUrl}:${workdir}`;
|
|
1991
|
+
let template = Template();
|
|
1992
|
+
if (token) template = template.setEnvs({ [BUILD_TOKEN_ENV]: token }, { ephemeral: true });
|
|
1993
|
+
if (options.cpuCount !== void 0) template = template.cpuCount(options.cpuCount);
|
|
1994
|
+
if (options.memoryMB !== void 0) template = template.memoryMB(options.memoryMB);
|
|
1995
|
+
return template.runCmd(steps).withFamily(family);
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
function isValidCloneUrl(cloneUrl) {
|
|
1999
|
+
if (cloneUrl.length > 2048 || !CLONE_URL_ALLOWED_CHARS.test(cloneUrl)) return false;
|
|
2000
|
+
let url;
|
|
2001
|
+
try {
|
|
2002
|
+
url = new URL(cloneUrl);
|
|
2003
|
+
} catch {
|
|
2004
|
+
return false;
|
|
2005
|
+
}
|
|
2006
|
+
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) return false;
|
|
2007
|
+
if (!CLONE_URL_HOST_PATTERN.test(url.hostname)) return false;
|
|
2008
|
+
const segments = url.pathname.split("/").slice(1);
|
|
2009
|
+
return segments.length > 0 && segments.every((segment) => CLONE_URL_SEGMENT_PATTERN.test(segment));
|
|
2010
|
+
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Canonical form used for identity, the family key, and the build's clone:
|
|
2013
|
+
* lowercase host, no trailing `.git` or slash. Two spellings of one
|
|
2014
|
+
* repository must not produce two templates.
|
|
2015
|
+
*/
|
|
2016
|
+
function normalizeCloneUrl(cloneUrl) {
|
|
2017
|
+
let end = cloneUrl.length;
|
|
2018
|
+
while (end > 0 && cloneUrl[end - 1] === "/") end--;
|
|
2019
|
+
return cloneUrl.slice(0, end).replace(/\.git$/i, "").replace(/^(https:\/\/)([^/]+)/i, (_match, scheme, host) => {
|
|
2020
|
+
return `${scheme.toLowerCase()}${host.toLowerCase()}`;
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
function defaultWorkdir(cloneUrl) {
|
|
2024
|
+
return `$HOME/${(normalizeCloneUrl(cloneUrl).split("/").at(-1) ?? "").replace(/[^\w.-]/g, "-").replace(/^\.+/, "") || "repo"}`;
|
|
2025
|
+
}
|
|
2026
|
+
function gitAuthFlag() {
|
|
2027
|
+
return `-c http.extraheader="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$${BUILD_TOKEN_ENV}" | base64 -w0)"`;
|
|
2028
|
+
}
|
|
2029
|
+
async function resolveDefaultBranchHead(cloneUrl, token, execute = execFileAsync) {
|
|
2030
|
+
try {
|
|
2031
|
+
const env = {
|
|
2032
|
+
...process.env,
|
|
2033
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
2034
|
+
};
|
|
2035
|
+
if (token) {
|
|
2036
|
+
env.GIT_CONFIG_COUNT = "1";
|
|
2037
|
+
env.GIT_CONFIG_KEY_0 = "http.extraheader";
|
|
2038
|
+
env.GIT_CONFIG_VALUE_0 = `AUTHORIZATION: basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}`;
|
|
2039
|
+
}
|
|
2040
|
+
const { stdout } = await execute("git", [
|
|
2041
|
+
"ls-remote",
|
|
2042
|
+
"--",
|
|
2043
|
+
cloneUrl,
|
|
2044
|
+
"HEAD"
|
|
2045
|
+
], {
|
|
2046
|
+
timeout: 1e4,
|
|
2047
|
+
maxBuffer: 1024 * 1024,
|
|
2048
|
+
env
|
|
2049
|
+
});
|
|
2050
|
+
const sha = stdout.trim().split(/\s+/, 1)[0];
|
|
2051
|
+
return sha && SHA_PATTERN.test(sha) ? sha : void 0;
|
|
2052
|
+
} catch {
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
//#endregion
|
|
1695
2057
|
//#region src/provider.ts
|
|
1696
2058
|
const platformSandboxProvider = {
|
|
1697
2059
|
id: "platform",
|
|
@@ -1712,6 +2074,12 @@ const platformSandboxProvider = {
|
|
|
1712
2074
|
type: "string",
|
|
1713
2075
|
description: "Opaque user subject attributed to sandbox requests"
|
|
1714
2076
|
},
|
|
2077
|
+
sandboxProvider: {
|
|
2078
|
+
type: "string",
|
|
2079
|
+
description: "Sandbox provider (falls back to SANDBOX_PROVIDER, then e2b)",
|
|
2080
|
+
enum: ["railway", "e2b"],
|
|
2081
|
+
default: "e2b"
|
|
2082
|
+
},
|
|
1715
2083
|
environmentId: {
|
|
1716
2084
|
type: "string",
|
|
1717
2085
|
description: "Platform environment ID (falls back to MASTRA_ENVIRONMENT_ID)"
|
|
@@ -1805,6 +2173,6 @@ var InProcessSandboxAddressRegistry = class {
|
|
|
1805
2173
|
}
|
|
1806
2174
|
};
|
|
1807
2175
|
//#endregion
|
|
1808
|
-
export { InProcessSandboxAddressRegistry, PlatformApiError, PlatformClient, PlatformFilesystem, PlatformSandbox, PrivateNetExecHttpError, SandboxDestroyedError, SandboxExecTransportError, execViaPrivateNetwork, platformFilesystemProvider, platformSandboxProvider };
|
|
2176
|
+
export { InProcessSandboxAddressRegistry, PlatformApiError, PlatformClient, PlatformFilesystem, PlatformSandbox, PrivateNetExecHttpError, SandboxDestroyedError, SandboxExecTransportError, Template, createRepoTemplate, execViaPrivateNetwork, platformFilesystemProvider, platformSandboxProvider };
|
|
1809
2177
|
|
|
1810
2178
|
//# sourceMappingURL=index.js.map
|