@mastra/railway 0.4.1-alpha.0 → 0.5.0-alpha.2
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 +28 -0
- package/dist/index.cjs +125 -212
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +126 -213
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +14 -23
- package/dist/sandbox/index.d.ts.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @mastra/railway
|
|
2
2
|
|
|
3
|
+
## 0.5.0-alpha.2
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added `RailwaySandbox.snapshot()` to capture the configured recovery checkpoint. ([#21221](https://github.com/mastra-ai/mastra/pull/21221))
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
await sandbox.snapshot();
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [[`9571e3a`](https://github.com/mastra-ai/mastra/commit/9571e3a06ed2c5220196460bf82a2129255c3a8b), [`d6c56f9`](https://github.com/mastra-ai/mastra/commit/d6c56f951db3213330b98b0abafa9778c8770e58), [`9571e3a`](https://github.com/mastra-ai/mastra/commit/9571e3a06ed2c5220196460bf82a2129255c3a8b), [`acc3513`](https://github.com/mastra-ai/mastra/commit/acc3513b19f79bf0a7ec2998694580edca54086c), [`94e7ae9`](https://github.com/mastra-ai/mastra/commit/94e7ae970b37c888cd1244ef013292639a2fe6d1), [`6a667b4`](https://github.com/mastra-ai/mastra/commit/6a667b4b7cd6a93fe41fcdd357b08c5a8c09b9ab), [`2440e09`](https://github.com/mastra-ai/mastra/commit/2440e096ea6c2def1ccc1eb2d0f3f5b88c4af940), [`a59049b`](https://github.com/mastra-ai/mastra/commit/a59049b1652a13efff66ac826326b5ed9a550342)]:
|
|
16
|
+
- @mastra/core@1.58.0-alpha.13
|
|
17
|
+
|
|
18
|
+
## 0.4.1-alpha.1
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Improved Railway sandbox recovery and checkpoint management: ([#20739](https://github.com/mastra-ai/mastra/pull/20739))
|
|
23
|
+
|
|
24
|
+
- A configured `sandboxId` reconnects when the sandbox is running, or creates a replacement when it is missing or stopped.
|
|
25
|
+
- Use `captureCheckpoint()` to save a recovery point on demand. Saved checkpoints provide the baseline filesystem for new sandboxes; `stop()` captures one before teardown and `destroy()` removes it.
|
|
26
|
+
- `start()` no longer resolves `template`. The option is still accepted and copied by `clone()`, but has no effect: callers receive neither a custom base image nor an error.
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [[`b4c89b4`](https://github.com/mastra-ai/mastra/commit/b4c89b4371b0c86da57403ad1a3b3ef0681f3128), [`e44e8f3`](https://github.com/mastra-ai/mastra/commit/e44e8f370b66c339ddcaba946d33da6d3c3f06cd), [`c967a5e`](https://github.com/mastra-ai/mastra/commit/c967a5eec150c5dc5418c4a4388982d1fb7ad27c), [`f53d5bd`](https://github.com/mastra-ai/mastra/commit/f53d5bd4885b29e4ac29a428a6044088ea8d6aa3), [`bda2235`](https://github.com/mastra-ai/mastra/commit/bda22353ee28f2df0eaea555f7cae1549f979c0b), [`a7eb4a1`](https://github.com/mastra-ai/mastra/commit/a7eb4a11450f6170274ed5141bffe821d4fdd5a6), [`2f9ef3f`](https://github.com/mastra-ai/mastra/commit/2f9ef3f4ca06fc2dcdd5088c26b7f4da6a016791), [`e7eefcb`](https://github.com/mastra-ai/mastra/commit/e7eefcb162cda7c493e8c3bf43050ead0efbcb2c), [`4d7aca2`](https://github.com/mastra-ai/mastra/commit/4d7aca2fe75f225c83d1502d63079568e6ec163f), [`c4ec889`](https://github.com/mastra-ai/mastra/commit/c4ec889561c0264c43f66d04d587bee4ce35e792), [`9be8878`](https://github.com/mastra-ai/mastra/commit/9be8878dcf0388e84fc4873e0eec27bd49b881a4)]:
|
|
29
|
+
- @mastra/core@1.58.0-alpha.2
|
|
30
|
+
|
|
3
31
|
## 0.4.1-alpha.0
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -194,9 +194,10 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
|
|
|
194
194
|
_createdAt = null;
|
|
195
195
|
_checkpointRefreshTimer = null;
|
|
196
196
|
_checkpointRefreshInFlight = null;
|
|
197
|
+
_sandboxId;
|
|
198
|
+
_startInFlight = null;
|
|
197
199
|
_token;
|
|
198
200
|
_environmentId;
|
|
199
|
-
_sandboxId;
|
|
200
201
|
_checkpointName;
|
|
201
202
|
_idleTimeoutMinutes;
|
|
202
203
|
_networkIsolation;
|
|
@@ -242,67 +243,60 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
|
|
|
242
243
|
*/
|
|
243
244
|
async start() {
|
|
244
245
|
if (this._sandbox) return;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
const clientConfig = this._clientConfig();
|
|
247
|
+
const createOptions = this._createOptions(clientConfig);
|
|
248
|
+
if (this._sandboxId) {
|
|
249
|
+
const sandboxId = this._sandboxId;
|
|
250
|
+
this._startInFlight ??= (async () => {
|
|
251
|
+
try {
|
|
252
|
+
this._sandbox = await this._reconnectSandbox(sandboxId, clientConfig);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
if (!(error instanceof railway.SandboxNotFoundError)) throw error;
|
|
255
|
+
this._sandbox = await this._createNewSandbox(createOptions);
|
|
256
|
+
}
|
|
257
|
+
})().finally(() => {
|
|
258
|
+
this._startInFlight = null;
|
|
259
|
+
});
|
|
260
|
+
} else this._startInFlight ??= (async () => {
|
|
261
|
+
this._sandbox = await this._createNewSandbox(createOptions);
|
|
262
|
+
})().finally(() => {
|
|
263
|
+
this._startInFlight = null;
|
|
248
264
|
});
|
|
265
|
+
await this._startInFlight;
|
|
266
|
+
if (!this._sandbox) throw new Error("Failed to start Railway sandbox");
|
|
267
|
+
const sandbox = this._sandbox;
|
|
268
|
+
this._sandboxId = sandbox.id;
|
|
269
|
+
this._createdAt = sandbox.createdAt ? new Date(sandbox.createdAt) : /* @__PURE__ */ new Date();
|
|
270
|
+
this.logger.debug(`${LOG_PREFIX} Railway sandbox ${sandbox.id} ready for logical ID: ${this.id}`);
|
|
271
|
+
this._scheduleCheckpointRefresh();
|
|
249
272
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
});
|
|
256
|
-
this._sandbox = null;
|
|
257
|
-
this._createdAt = null;
|
|
258
|
-
this.status = "starting";
|
|
273
|
+
/**
|
|
274
|
+
* Create a new Railway sandbox.
|
|
275
|
+
*/
|
|
276
|
+
async _createNewSandbox(createOptions) {
|
|
277
|
+
this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox for: ${this.id}`);
|
|
259
278
|
try {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
279
|
+
let checkpoinAlreadyExists = false;
|
|
280
|
+
if (this._checkpointName) checkpoinAlreadyExists = (await railway.Sandbox.checkpoints(this._clientConfig())).some((checkpoint) => checkpoint.key === this._checkpointName);
|
|
281
|
+
let sandbox;
|
|
282
|
+
if (checkpoinAlreadyExists) sandbox = await railway.Sandbox.create(this._checkpointName, createOptions);
|
|
283
|
+
else sandbox = await railway.Sandbox.create(createOptions);
|
|
284
|
+
return sandbox;
|
|
265
285
|
} catch (error) {
|
|
266
|
-
this.status = "error";
|
|
267
286
|
throw error;
|
|
268
287
|
}
|
|
269
288
|
}
|
|
270
|
-
async withRestartRetry(operation) {
|
|
271
|
-
await this.ensureRunning();
|
|
272
|
-
try {
|
|
273
|
-
return await operation();
|
|
274
|
-
} catch (error) {
|
|
275
|
-
if (!this.isSandboxUnavailableError(error)) throw error;
|
|
276
|
-
await this.restart();
|
|
277
|
-
return await operation();
|
|
278
|
-
} finally {
|
|
279
|
-
this._scheduleCheckpointRefresh();
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
async _startRailwaySandbox({ reconnectSandboxId, fallbackToCreate }) {
|
|
283
|
-
const clientConfig = this._clientConfig();
|
|
284
|
-
const createOptions = this._createOptions(clientConfig);
|
|
285
|
-
this._sandbox = reconnectSandboxId ? await this._reconnectSandbox(reconnectSandboxId, fallbackToCreate, clientConfig, createOptions) : await this._createNewSandbox(createOptions);
|
|
286
|
-
this._createdAt = this._sandbox.createdAt ? new Date(this._sandbox.createdAt) : /* @__PURE__ */ new Date();
|
|
287
|
-
this.logger.debug(`${LOG_PREFIX} Railway sandbox ${this._sandbox.id} ready for logical ID: ${this.id}`);
|
|
288
|
-
this._scheduleCheckpointRefresh();
|
|
289
|
-
}
|
|
290
289
|
/**
|
|
291
290
|
* Reconnect to an existing Railway sandbox, creating a fresh one when
|
|
292
|
-
* `fallbackToCreate` is set and the sandbox is unavailable or not running.
|
|
293
291
|
*/
|
|
294
|
-
async _reconnectSandbox(reconnectSandboxId,
|
|
292
|
+
async _reconnectSandbox(reconnectSandboxId, clientConfig) {
|
|
295
293
|
this.logger.debug(`${LOG_PREFIX} Reconnecting to Railway sandbox ${reconnectSandboxId}...`);
|
|
296
|
-
let connectedSandbox;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
if (connectedSandbox.status === "RUNNING") return connectedSandbox;
|
|
304
|
-
if (!fallbackToCreate) throw new Error(`Railway sandbox ${reconnectSandboxId} is not running (status: ${connectedSandbox.status})`);
|
|
305
|
-
return this._createNewSandbox(createOptions);
|
|
294
|
+
let connectedSandbox = await railway.Sandbox.connect(reconnectSandboxId, clientConfig);
|
|
295
|
+
if (connectedSandbox.status !== "RUNNING") throw new railway.SandboxNotFoundError({
|
|
296
|
+
id: reconnectSandboxId,
|
|
297
|
+
environmentId: clientConfig.environmentId ?? ""
|
|
298
|
+
});
|
|
299
|
+
return connectedSandbox;
|
|
306
300
|
}
|
|
307
301
|
_clientConfig() {
|
|
308
302
|
return {
|
|
@@ -318,84 +312,9 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
|
|
|
318
312
|
...Object.keys(this._env).length > 0 && { env: this._env }
|
|
319
313
|
};
|
|
320
314
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (this._templateOption) {
|
|
325
|
-
const template = this._resolveTemplate();
|
|
326
|
-
this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox from template for: ${this.id}`);
|
|
327
|
-
const sandbox = await railway.Sandbox.create(template, createOptions);
|
|
328
|
-
await this._checkpointSandbox(sandbox);
|
|
329
|
-
return sandbox;
|
|
330
|
-
}
|
|
331
|
-
this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox for: ${this.id}`);
|
|
332
|
-
const sandbox = await railway.Sandbox.create(createOptions);
|
|
333
|
-
await this._checkpointSandbox(sandbox);
|
|
334
|
-
return sandbox;
|
|
335
|
-
}
|
|
336
|
-
async _tryCreateFromCheckpoint(createOptions) {
|
|
337
|
-
if (!this._checkpointName) return;
|
|
338
|
-
this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox from checkpoint ${this._checkpointName} for: ${this.id}`);
|
|
339
|
-
try {
|
|
340
|
-
return await railway.Sandbox.create(this._checkpointName, createOptions);
|
|
341
|
-
} catch (error) {
|
|
342
|
-
if (!this.isCheckpointUnavailableError(error)) throw error;
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
async _checkpointSandbox(sandbox) {
|
|
347
|
-
if (!this._checkpointName) return;
|
|
348
|
-
try {
|
|
349
|
-
this.logger.debug(`${LOG_PREFIX} Capturing Railway sandbox checkpoint ${this._checkpointName} for: ${this.id}`);
|
|
350
|
-
await sandbox.checkpoint(this._checkpointName);
|
|
351
|
-
} catch (error) {
|
|
352
|
-
if (!this.isCheckpointAlreadyExistsError(error)) throw error;
|
|
353
|
-
await this._deleteCheckpointByName(this._checkpointName);
|
|
354
|
-
await sandbox.checkpoint(this._checkpointName);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
async _deleteCheckpointByName(name) {
|
|
358
|
-
try {
|
|
359
|
-
const checkpoint = (await railway.Sandbox.checkpoints(this._clientConfig())).find((checkpoint) => checkpoint.key === name);
|
|
360
|
-
if (!checkpoint) return;
|
|
361
|
-
await railway.Sandbox.deleteCheckpoint(checkpoint.id, this._clientConfig());
|
|
362
|
-
} catch (error) {
|
|
363
|
-
if (!this.isCheckpointUnavailableError(error)) throw error;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
_scheduleCheckpointRefresh() {
|
|
367
|
-
if (!this._checkpointName || !this._sandbox) return;
|
|
368
|
-
const idleTimeoutMinutes = this._idleTimeoutMinutes ?? this._sandbox.idleTimeoutMinutes;
|
|
369
|
-
if (!idleTimeoutMinutes) return;
|
|
370
|
-
if (this._checkpointRefreshTimer) clearTimeout(this._checkpointRefreshTimer);
|
|
371
|
-
const delayMs = Math.max(1e3, idleTimeoutMinutes * 6e4 - CHECKPOINT_REFRESH_MARGIN_MS);
|
|
372
|
-
this._checkpointRefreshTimer = setTimeout(() => {
|
|
373
|
-
this._checkpointRefreshTimer = null;
|
|
374
|
-
const sandbox = this._sandbox;
|
|
375
|
-
if (!sandbox) return;
|
|
376
|
-
const refresh = this._checkpointSandbox(sandbox).finally(() => {
|
|
377
|
-
if (this._checkpointRefreshInFlight === refresh) this._checkpointRefreshInFlight = null;
|
|
378
|
-
});
|
|
379
|
-
this._checkpointRefreshInFlight = refresh;
|
|
380
|
-
this._checkpointRefreshInFlight.catch((error) => {
|
|
381
|
-
this.logger.warn(`${LOG_PREFIX} Failed to refresh Railway sandbox checkpoint ${this._checkpointName}:`, error);
|
|
382
|
-
});
|
|
383
|
-
}, delayMs);
|
|
384
|
-
this._checkpointRefreshTimer.unref?.();
|
|
385
|
-
}
|
|
386
|
-
_cancelCheckpointRefresh() {
|
|
387
|
-
if (this._checkpointRefreshTimer) {
|
|
388
|
-
clearTimeout(this._checkpointRefreshTimer);
|
|
389
|
-
this._checkpointRefreshTimer = null;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
async _flushCheckpointRefresh() {
|
|
393
|
-
this._cancelCheckpointRefresh();
|
|
394
|
-
if (this._checkpointRefreshInFlight) {
|
|
395
|
-
await this._checkpointRefreshInFlight;
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
if (this._sandbox) await this._checkpointSandbox(this._sandbox);
|
|
315
|
+
/** Persist the configured recovery checkpoint when available. */
|
|
316
|
+
async snapshot() {
|
|
317
|
+
await this.captureCheckpoint();
|
|
399
318
|
}
|
|
400
319
|
/**
|
|
401
320
|
* Capture the sandbox's checkpoint on demand, outside the idle-timer schedule.
|
|
@@ -447,74 +366,76 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
|
|
|
447
366
|
checkpointName
|
|
448
367
|
};
|
|
449
368
|
}
|
|
450
|
-
|
|
451
|
-
if (!
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if (name === "SandboxNotFoundError" || name === "SandboxFailedError" || name === "SandboxTimeoutError" && errorLike.resource === "sandbox") return true;
|
|
481
|
-
if (message.includes("sandbox") && [
|
|
482
|
-
"not found",
|
|
483
|
-
"destroyed",
|
|
484
|
-
"failed",
|
|
485
|
-
"not running",
|
|
486
|
-
"unavailable"
|
|
487
|
-
].some((phrase) => message.includes(phrase))) return true;
|
|
488
|
-
if (errorLike.cause !== void 0) return this.isSandboxUnavailableError(errorLike.cause, seen);
|
|
369
|
+
async _checkpointSandbox(sandbox) {
|
|
370
|
+
if (!this._checkpointName) return;
|
|
371
|
+
this.logger.debug(`${LOG_PREFIX} Capturing Railway sandbox checkpoint ${this._checkpointName} for: ${this.id}`);
|
|
372
|
+
if ((await railway.Sandbox.checkpoints(this._clientConfig())).some((checkpoint) => checkpoint.key === this._checkpointName)) await railway.Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig());
|
|
373
|
+
await sandbox.checkpoint(this._checkpointName);
|
|
374
|
+
}
|
|
375
|
+
_scheduleCheckpointRefresh() {
|
|
376
|
+
if (!this._checkpointName || !this._sandbox) return;
|
|
377
|
+
const idleTimeoutMinutes = this._idleTimeoutMinutes ?? this._sandbox.idleTimeoutMinutes;
|
|
378
|
+
if (!idleTimeoutMinutes) return;
|
|
379
|
+
this._cancelCheckpointRefresh();
|
|
380
|
+
const delayMs = Math.max(1e3, idleTimeoutMinutes * 6e4 - CHECKPOINT_REFRESH_MARGIN_MS);
|
|
381
|
+
this._checkpointRefreshTimer = setTimeout(() => {
|
|
382
|
+
this._checkpointRefreshTimer = null;
|
|
383
|
+
const sandbox = this._sandbox;
|
|
384
|
+
if (!sandbox || this._checkpointRefreshInFlight) return;
|
|
385
|
+
const refresh = this._checkpointSandbox(sandbox).finally(() => {
|
|
386
|
+
if (this._checkpointRefreshInFlight === refresh) this._checkpointRefreshInFlight = null;
|
|
387
|
+
});
|
|
388
|
+
this._checkpointRefreshInFlight = refresh;
|
|
389
|
+
refresh.catch((error) => {
|
|
390
|
+
this.logger.warn(`${LOG_PREFIX} Failed to refresh Railway sandbox checkpoint ${this._checkpointName}:`, error);
|
|
391
|
+
});
|
|
392
|
+
}, delayMs);
|
|
393
|
+
this._checkpointRefreshTimer.unref?.();
|
|
394
|
+
}
|
|
395
|
+
_cancelCheckpointRefresh() {
|
|
396
|
+
if (this._checkpointRefreshTimer) {
|
|
397
|
+
clearTimeout(this._checkpointRefreshTimer);
|
|
398
|
+
this._checkpointRefreshTimer = null;
|
|
489
399
|
}
|
|
490
|
-
return false;
|
|
491
400
|
}
|
|
492
401
|
/**
|
|
493
402
|
* Stop the Railway sandbox.
|
|
494
403
|
*
|
|
495
404
|
* Railway sandboxes have no separate "stopped" state — they're either
|
|
496
|
-
* running or destroyed — so stopping destroys the sandbox.
|
|
405
|
+
* running or destroyed — so stopping destroys the sandbox but we keep a checkpoint of the filesystem.
|
|
497
406
|
*/
|
|
498
407
|
async stop() {
|
|
408
|
+
if (this._checkpointName) await this._flushCheckpointRefresh().catch((error) => {
|
|
409
|
+
this.logger.warn(`${LOG_PREFIX} Failed to checkpoint Railway sandbox ${this._sandbox?.id}:`, error);
|
|
410
|
+
});
|
|
499
411
|
await this._teardown();
|
|
500
412
|
}
|
|
501
413
|
/**
|
|
502
|
-
* Destroy the Railway sandbox and release its resources.
|
|
414
|
+
* Destroy the Railway sandbox and release its resources including the checkpoint.
|
|
503
415
|
*/
|
|
504
416
|
async destroy() {
|
|
417
|
+
this._cancelCheckpointRefresh();
|
|
418
|
+
if (this._checkpointName) {
|
|
419
|
+
await this._checkpointRefreshInFlight;
|
|
420
|
+
await railway.Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig()).catch((error) => {
|
|
421
|
+
this.logger.warn(`${LOG_PREFIX} Failed to delete Railway checkpoint ${this._checkpointName}:`, error);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
505
424
|
await this._teardown();
|
|
506
425
|
}
|
|
507
|
-
async
|
|
508
|
-
|
|
509
|
-
|
|
426
|
+
async _flushCheckpointRefresh() {
|
|
427
|
+
this._cancelCheckpointRefresh();
|
|
428
|
+
if (this._checkpointRefreshInFlight) {
|
|
429
|
+
await this._checkpointRefreshInFlight;
|
|
510
430
|
return;
|
|
511
431
|
}
|
|
432
|
+
if (this._sandbox) await this._checkpointSandbox(this._sandbox);
|
|
433
|
+
}
|
|
434
|
+
async _teardown() {
|
|
435
|
+
this._cancelCheckpointRefresh();
|
|
436
|
+
if (!this._sandbox) return;
|
|
437
|
+
await this._checkpointRefreshInFlight;
|
|
512
438
|
const sandbox = this._sandbox;
|
|
513
|
-
try {
|
|
514
|
-
await this._flushCheckpointRefresh();
|
|
515
|
-
} catch (error) {
|
|
516
|
-
this.logger.warn(`${LOG_PREFIX} Failed to flush checkpoint before teardown:`, error);
|
|
517
|
-
}
|
|
518
439
|
this._sandbox = null;
|
|
519
440
|
try {
|
|
520
441
|
await sandbox.destroy();
|
|
@@ -523,15 +444,6 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
|
|
|
523
444
|
}
|
|
524
445
|
}
|
|
525
446
|
/**
|
|
526
|
-
* Resolve the configured template into a `SandboxTemplate` that Railway
|
|
527
|
-
* builds during `Sandbox.create()`. Accepts either a pre-built
|
|
528
|
-
* `SandboxTemplate` or a builder callback over `Sandbox.template()`.
|
|
529
|
-
*/
|
|
530
|
-
_resolveTemplate() {
|
|
531
|
-
const option = this._templateOption;
|
|
532
|
-
return typeof option === "function" ? option(railway.Sandbox.template()) : option;
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
447
|
* Fork this running sandbox into a new, independent `RailwaySandbox`.
|
|
536
448
|
*
|
|
537
449
|
* Clones the filesystem (a fresh boot, not live processes) into the same
|
|
@@ -542,7 +454,7 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
|
|
|
542
454
|
* @throws {SandboxNotReadyError} If this sandbox has not been started.
|
|
543
455
|
*/
|
|
544
456
|
async fork(options = {}) {
|
|
545
|
-
|
|
457
|
+
await this.railway.fork({
|
|
546
458
|
...options.idleTimeoutMinutes !== void 0 && { idleTimeoutMinutes: options.idleTimeoutMinutes },
|
|
547
459
|
...options.networkIsolation !== void 0 && { networkIsolation: options.networkIsolation },
|
|
548
460
|
...options.env !== void 0 && { env: options.env }
|
|
@@ -551,7 +463,6 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
|
|
|
551
463
|
...options.id !== void 0 && { id: options.id },
|
|
552
464
|
...this._token !== void 0 && { token: this._token },
|
|
553
465
|
...this._environmentId !== void 0 && { environmentId: this._environmentId },
|
|
554
|
-
sandboxId: forked.id,
|
|
555
466
|
idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,
|
|
556
467
|
networkIsolation: options.networkIsolation ?? this._networkIsolation,
|
|
557
468
|
env: options.env ?? this._env,
|
|
@@ -576,7 +487,6 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
|
|
|
576
487
|
...options.id !== void 0 && { id: options.id },
|
|
577
488
|
...this._token !== void 0 && { token: this._token },
|
|
578
489
|
...this._environmentId !== void 0 && { environmentId: this._environmentId },
|
|
579
|
-
...options.sandboxId !== void 0 && { sandboxId: options.sandboxId },
|
|
580
490
|
...(options.checkpointName ?? this._checkpointName) !== void 0 && { checkpointName: options.checkpointName ?? this._checkpointName },
|
|
581
491
|
idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,
|
|
582
492
|
...this._networkIsolation !== void 0 && { networkIsolation: this._networkIsolation },
|
|
@@ -633,28 +543,31 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
|
|
|
633
543
|
* Execute a command in the sandbox and return the result.
|
|
634
544
|
*/
|
|
635
545
|
async executeCommand(command, args = [], options = {}) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
exitCode,
|
|
650
|
-
stdout: result.stdout,
|
|
651
|
-
stderr: result.stderr,
|
|
652
|
-
executionTimeMs: Date.now() - startedAt,
|
|
653
|
-
command,
|
|
654
|
-
args,
|
|
655
|
-
timedOut: result.timedOut
|
|
656
|
-
};
|
|
546
|
+
if (this._sandbox?.status !== "RUNNING") {
|
|
547
|
+
if (!this._checkpointName) throw new _mastra_core_workspace.SandboxNotReadyError(this.id);
|
|
548
|
+
this._sandbox = null;
|
|
549
|
+
await this.start();
|
|
550
|
+
}
|
|
551
|
+
const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(" ")}` : command;
|
|
552
|
+
const timeout = options.timeout ?? this._timeout;
|
|
553
|
+
const env = options.env ? Object.fromEntries(Object.entries(options.env).filter((entry) => entry[1] !== void 0)) : void 0;
|
|
554
|
+
const startedAt = Date.now();
|
|
555
|
+
const result = await this.railway.exec(fullCommand, {
|
|
556
|
+
...timeout !== void 0 && { timeoutSec: Math.ceil(timeout / 1e3) },
|
|
557
|
+
...options.cwd !== void 0 && { cwd: options.cwd },
|
|
558
|
+
...env !== void 0 && { env }
|
|
657
559
|
});
|
|
560
|
+
const exitCode = result.exitCode ?? -1;
|
|
561
|
+
return {
|
|
562
|
+
success: exitCode === 0,
|
|
563
|
+
exitCode,
|
|
564
|
+
stdout: result.stdout,
|
|
565
|
+
stderr: result.stderr,
|
|
566
|
+
executionTimeMs: Date.now() - startedAt,
|
|
567
|
+
command,
|
|
568
|
+
args,
|
|
569
|
+
timedOut: result.timedOut
|
|
570
|
+
};
|
|
658
571
|
}
|
|
659
572
|
};
|
|
660
573
|
//#endregion
|