@mastra/railway 0.6.0-alpha.1 → 0.6.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 CHANGED
@@ -1,5 +1,50 @@
1
1
  # @mastra/railway
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added `ProcessHandle.closeStdin()` to signal end-of-file to background processes. Local and Docker sandboxes support closing stdin, while providers without an available stdin-close API return a provider-specific unsupported-operation error. Providers signal the unsupported case with the new `UnsupportedStdinCloseError`, and the base class supplies that behavior by default so existing `ProcessHandle` subclasses keep compiling. Calling `handle.writer.end()` also closes stdin, and finishes without an error when the provider cannot close stdin. ([#21606](https://github.com/mastra-ai/mastra/pull/21606))
8
+
9
+ ### Patch Changes
10
+
11
+ - Added support for booting new Railway sandboxes from a fallback checkpoint while keeping future snapshots isolated to the sandbox's own checkpoint. ([#21803](https://github.com/mastra-ai/mastra/pull/21803))
12
+
13
+ ```typescript
14
+ const sandbox = new RailwaySandbox({
15
+ checkpointName: 'session-42',
16
+ seedCheckpointName: 'repo-base',
17
+ });
18
+ ```
19
+
20
+ - Declared checkpoint support (`supportsCheckpoints`) so checkpoint-based features like warm base checkpoints and boot-from-checkpoint know snapshots are real. ([#21798](https://github.com/mastra-ai/mastra/pull/21798))
21
+
22
+ ```ts
23
+ // Gate checkpoint-dependent work on the provider's capability flag.
24
+ if (sandbox.supportsCheckpoints) {
25
+ await sandbox.snapshot(); // persists a checkpoint that can seed a later boot
26
+ }
27
+ ```
28
+
29
+ - Updated dependencies [[`587f6ef`](https://github.com/mastra-ai/mastra/commit/587f6efcfc25880b93760a8607d1cd381ec612fe), [`7e096f0`](https://github.com/mastra-ai/mastra/commit/7e096f02f0dddbf09b85d306458351245ed2f886), [`d7e6745`](https://github.com/mastra-ai/mastra/commit/d7e67456954863c55440ea9c49bc6ceb9949972d), [`6223446`](https://github.com/mastra-ai/mastra/commit/6223446ddce6166e96e0ba5e00d628b615dee8ca), [`15101bb`](https://github.com/mastra-ai/mastra/commit/15101bb53c0d934f31af6b8813b88191e382a5e5), [`4e7a421`](https://github.com/mastra-ai/mastra/commit/4e7a421dce8a48742f785d1e93ad2f43a572b282), [`c2c3deb`](https://github.com/mastra-ai/mastra/commit/c2c3debcf670c7082d0a5e553aa99818a864698c), [`d8308a2`](https://github.com/mastra-ai/mastra/commit/d8308a2be3c07e777393d1017a381dcae3890d30), [`b0a2a07`](https://github.com/mastra-ai/mastra/commit/b0a2a07800d42bd9823292e7db832374ed084c9c), [`74e5bd3`](https://github.com/mastra-ai/mastra/commit/74e5bd315b8b3a1e04cb6cf480bb0f5fc4951dc8), [`242e324`](https://github.com/mastra-ai/mastra/commit/242e3241e73cbd5c9bb86a31ebb49ca0256488d4), [`217e967`](https://github.com/mastra-ai/mastra/commit/217e9672d8b3160eb729d8e9f0044949e88da239), [`d774e89`](https://github.com/mastra-ai/mastra/commit/d774e8930c781df8c9effe3763e6b501c099b6cc), [`9c27a53`](https://github.com/mastra-ai/mastra/commit/9c27a53cd9d3de4f3f025bc387d94ce371c33f95), [`8f0a332`](https://github.com/mastra-ai/mastra/commit/8f0a3321bf180368d76fe7b36aa1a8f60f00b6de), [`0b4f108`](https://github.com/mastra-ai/mastra/commit/0b4f1089aa8d92e67c2a8e99726822c5ee410784), [`9acb50f`](https://github.com/mastra-ai/mastra/commit/9acb50f71cec9c362f06820033f90ae6b1f8282f), [`46e9e3f`](https://github.com/mastra-ai/mastra/commit/46e9e3f73babe1bc70080a596cf2ac0b9da48519), [`3f9a190`](https://github.com/mastra-ai/mastra/commit/3f9a19057c027155867b9317294ee4ca7bd0581a), [`dff25a1`](https://github.com/mastra-ai/mastra/commit/dff25a1103fa72ee082a9b6f805ebeb5ce400753), [`6db7a5d`](https://github.com/mastra-ai/mastra/commit/6db7a5dd3dd2b6f7ef75dcd804fcffef5fa83963), [`217e967`](https://github.com/mastra-ai/mastra/commit/217e9672d8b3160eb729d8e9f0044949e88da239), [`583e235`](https://github.com/mastra-ai/mastra/commit/583e23519c13af16c1746f9c49722d011216611b), [`b098de9`](https://github.com/mastra-ai/mastra/commit/b098de9d7cb9f672e0883a5c716465a3a689693d), [`e8808e3`](https://github.com/mastra-ai/mastra/commit/e8808e3d8eb585a2565be53e56a7e0e1477352a4), [`a77f8d4`](https://github.com/mastra-ai/mastra/commit/a77f8d4740d2178a74c41e4bf678b4fcd8fa0bb2), [`7f78585`](https://github.com/mastra-ai/mastra/commit/7f785857e401570e2ffb316911f126ed363aa537), [`33374ba`](https://github.com/mastra-ai/mastra/commit/33374ba359e4fb13eaa918ae925fe167a3c55414), [`940bf5c`](https://github.com/mastra-ai/mastra/commit/940bf5ccf04f2c9ebd8a1390431733222a03b1cd), [`c549e2f`](https://github.com/mastra-ai/mastra/commit/c549e2f40edc1cac5d9e74e82f90da22b48df084), [`58c43d3`](https://github.com/mastra-ai/mastra/commit/58c43d3f7cb2eeaeb8ac733ae71dde822348e588), [`ef6e295`](https://github.com/mastra-ai/mastra/commit/ef6e295b59bc25a5b61b633a89c97bcfce9fb465), [`208e1b3`](https://github.com/mastra-ai/mastra/commit/208e1b39f30f4b386e494394e9d71d96f0f90241), [`c938d34`](https://github.com/mastra-ai/mastra/commit/c938d34739936c8ecbabd67ad6a4a4396f41c4c6), [`88ddc7c`](https://github.com/mastra-ai/mastra/commit/88ddc7ce01d40175f13a3228b789a906779680bd), [`f2a4afd`](https://github.com/mastra-ai/mastra/commit/f2a4afd7e37e809669001ed17724b341a5c1f45e), [`d438148`](https://github.com/mastra-ai/mastra/commit/d438148e222c1e2fb3c652725ce75680962ebec4), [`ba05fe0`](https://github.com/mastra-ai/mastra/commit/ba05fe0738f70cb686777546e968237d09269142), [`40d358e`](https://github.com/mastra-ai/mastra/commit/40d358e29d55543803e64b49241122f598ffabc7), [`d26a8d4`](https://github.com/mastra-ai/mastra/commit/d26a8d4281f28414715b333c85bedaf70d0b2890), [`e80cd7e`](https://github.com/mastra-ai/mastra/commit/e80cd7e7683e7d732e1cc6784bcac1d2640d2ce3), [`ccbbcd9`](https://github.com/mastra-ai/mastra/commit/ccbbcd974eedff4367a54ed0e24c9ee742ab2f61), [`1d9a0ea`](https://github.com/mastra-ai/mastra/commit/1d9a0ea4a9901baee6cd56737243bd6d1f631ac0), [`677cdc6`](https://github.com/mastra-ai/mastra/commit/677cdc6af564dec29a13464d12b7ab2a4efc22e9), [`c549e2f`](https://github.com/mastra-ai/mastra/commit/c549e2f40edc1cac5d9e74e82f90da22b48df084), [`a7dd322`](https://github.com/mastra-ai/mastra/commit/a7dd32247d95afc539f483ca37f4594af0387f59), [`3f5c6f7`](https://github.com/mastra-ai/mastra/commit/3f5c6f728ea35da344248de9aa070f12849f3aa0), [`a318490`](https://github.com/mastra-ai/mastra/commit/a318490e17da32f338d50929c770d901a9b3dd72), [`b860493`](https://github.com/mastra-ai/mastra/commit/b86049391100e665d579f700c8a2034c036defc3), [`d4be8c1`](https://github.com/mastra-ai/mastra/commit/d4be8c1739d22d621e3f78790e1dd5eb5ecc3589), [`a5d2eb1`](https://github.com/mastra-ai/mastra/commit/a5d2eb10347eade1ae2816d88f466c25186c54a5), [`3667679`](https://github.com/mastra-ai/mastra/commit/3667679db057edfb086846d13369fdda4902ad65), [`49696e8`](https://github.com/mastra-ai/mastra/commit/49696e8e42f870674a0a58f5abcd22cc54dd2864), [`2ef2f23`](https://github.com/mastra-ai/mastra/commit/2ef2f230a7aed342e7dc3b2000cd42e4c43e08a7), [`763e0c6`](https://github.com/mastra-ai/mastra/commit/763e0c61e04d76ad9a9efd301aa57525ca0cbea9), [`20504b2`](https://github.com/mastra-ai/mastra/commit/20504b2ecebd0e077acda3d457ab57480a98ed3e), [`77e6b1b`](https://github.com/mastra-ai/mastra/commit/77e6b1bc4c46ce94fe501023fb4393c812ec6be3), [`c5f964d`](https://github.com/mastra-ai/mastra/commit/c5f964d3f77064e978f8066ec506eed77ba5c63c), [`23e0be2`](https://github.com/mastra-ai/mastra/commit/23e0be261381e49534b4ff3101c60ee64a946cbf), [`7fc8806`](https://github.com/mastra-ai/mastra/commit/7fc880627d3cbf995d31ea0e8b807bf15417e651), [`0e02eac`](https://github.com/mastra-ai/mastra/commit/0e02eacdb2e30e1697a41910b41163742a181dc1), [`4df174c`](https://github.com/mastra-ai/mastra/commit/4df174c32bddf093a82f273070b8380aef7c9e90), [`f7c25b5`](https://github.com/mastra-ai/mastra/commit/f7c25b5106ddfb48e591f98df7a51e0f2dd01dba), [`7aad631`](https://github.com/mastra-ai/mastra/commit/7aad631b43bc10db77d5b8c66b200d7a49d18bf2), [`512100a`](https://github.com/mastra-ai/mastra/commit/512100a7d8b7e9c920f2590c6b3612f5de0d3cff), [`e81744c`](https://github.com/mastra-ai/mastra/commit/e81744cd13c46619c142dc521dc0baac47607a84), [`f8f653f`](https://github.com/mastra-ai/mastra/commit/f8f653f10980d01a73706cc3c8689ca5e40ce808), [`dc09cc1`](https://github.com/mastra-ai/mastra/commit/dc09cc1083d861cde192c1cd235324dc75b8c731), [`9ef432b`](https://github.com/mastra-ai/mastra/commit/9ef432b6faa534b57b0d182a610e13dd9a7123ff), [`36b4649`](https://github.com/mastra-ai/mastra/commit/36b4649045a3a380cbab8ceca866db4086223aff), [`b9cf308`](https://github.com/mastra-ai/mastra/commit/b9cf30846f97f99ac1906ee8a68f4f2d117b0378), [`2e1d098`](https://github.com/mastra-ai/mastra/commit/2e1d0984e325fd319d32ea182f596b3170be3847), [`377eb81`](https://github.com/mastra-ai/mastra/commit/377eb81ce43b964e3a6b541df172da74a8ff3716), [`1794a79`](https://github.com/mastra-ai/mastra/commit/1794a79178c418004a7261b1ad9114066f7ef01d), [`0cdc5dc`](https://github.com/mastra-ai/mastra/commit/0cdc5dc69024957815da4f51acc4119eb4f447d7), [`5740ec6`](https://github.com/mastra-ai/mastra/commit/5740ec60c760ffdfbfaa59d603d03b847c864e05)]:
30
+ - @mastra/core@1.60.0
31
+
32
+ ## 0.6.0-alpha.2
33
+
34
+ ### Patch Changes
35
+
36
+ - Added support for booting new Railway sandboxes from a fallback checkpoint while keeping future snapshots isolated to the sandbox's own checkpoint. ([#21803](https://github.com/mastra-ai/mastra/pull/21803))
37
+
38
+ ```typescript
39
+ const sandbox = new RailwaySandbox({
40
+ checkpointName: 'session-42',
41
+ seedCheckpointName: 'repo-base',
42
+ });
43
+ ```
44
+
45
+ - Updated dependencies [[`58c43d3`](https://github.com/mastra-ai/mastra/commit/58c43d3f7cb2eeaeb8ac733ae71dde822348e588)]:
46
+ - @mastra/core@1.60.0-alpha.14
47
+
3
48
  ## 0.6.0-alpha.1
4
49
 
5
50
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -198,10 +198,12 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
198
198
  _checkpointRefreshTimer = null;
199
199
  _checkpointRefreshInFlight = null;
200
200
  _sandboxId;
201
+ _restoredCheckpointName;
201
202
  _startInFlight = null;
202
203
  _token;
203
204
  _environmentId;
204
205
  _checkpointName;
206
+ _seedCheckpointName;
205
207
  _idleTimeoutMinutes;
206
208
  _networkIsolation;
207
209
  _env;
@@ -219,6 +221,7 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
219
221
  this._environmentId = options.environmentId ?? process.env.RAILWAY_ENVIRONMENT_ID;
220
222
  this._sandboxId = options.sandboxId;
221
223
  this._checkpointName = options.checkpointName;
224
+ this._seedCheckpointName = options.seedCheckpointName;
222
225
  this._idleTimeoutMinutes = options.idleTimeoutMinutes;
223
226
  this._networkIsolation = options.networkIsolation;
224
227
  this._env = options.env ?? {};
@@ -251,6 +254,7 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
251
254
  if (this._sandboxId) {
252
255
  const sandboxId = this._sandboxId;
253
256
  this._startInFlight ??= (async () => {
257
+ this._restoredCheckpointName = void 0;
254
258
  try {
255
259
  this._sandbox = await this._reconnectSandbox(sandboxId, clientConfig);
256
260
  } catch (error) {
@@ -279,11 +283,14 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
279
283
  async _createNewSandbox(createOptions) {
280
284
  this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox for: ${this.id}`);
281
285
  try {
282
- let checkpoinAlreadyExists = false;
283
- if (this._checkpointName) checkpoinAlreadyExists = (await railway.Sandbox.checkpoints(this._clientConfig())).some((checkpoint) => checkpoint.key === this._checkpointName);
284
- let sandbox;
285
- if (checkpoinAlreadyExists) sandbox = await railway.Sandbox.create(this._checkpointName, createOptions);
286
- else sandbox = await railway.Sandbox.create(createOptions);
286
+ let checkpointToRestore;
287
+ if (this._checkpointName || this._seedCheckpointName) {
288
+ const checkpoints = await railway.Sandbox.checkpoints(this._clientConfig());
289
+ const checkpointNames = new Set(checkpoints.map((checkpoint) => checkpoint.key));
290
+ checkpointToRestore = checkpointNames.has(this._checkpointName ?? "") ? this._checkpointName : checkpointNames.has(this._seedCheckpointName ?? "") ? this._seedCheckpointName : void 0;
291
+ }
292
+ const sandbox = checkpointToRestore ? await railway.Sandbox.create(checkpointToRestore, createOptions) : await railway.Sandbox.create(createOptions);
293
+ this._restoredCheckpointName = checkpointToRestore;
287
294
  return sandbox;
288
295
  } catch (error) {
289
296
  throw error;
@@ -493,6 +500,7 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
493
500
  ...this._token !== void 0 && { token: this._token },
494
501
  ...this._environmentId !== void 0 && { environmentId: this._environmentId },
495
502
  ...(options.checkpointName ?? this._checkpointName) !== void 0 && { checkpointName: options.checkpointName ?? this._checkpointName },
503
+ ...options.seedCheckpointName !== void 0 && { seedCheckpointName: options.seedCheckpointName },
496
504
  idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,
497
505
  ...this._networkIsolation !== void 0 && { networkIsolation: this._networkIsolation },
498
506
  env: options.env ?? this._env,
@@ -525,7 +533,8 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
525
533
  environmentId: this._sandbox.environmentId,
526
534
  region: this._sandbox.region,
527
535
  networkIsolation: this._sandbox.networkIsolation,
528
- ...this._sandbox.idleTimeoutMinutes != null && { idleTimeoutMinutes: this._sandbox.idleTimeoutMinutes }
536
+ ...this._sandbox.idleTimeoutMinutes != null && { idleTimeoutMinutes: this._sandbox.idleTimeoutMinutes },
537
+ ...this._restoredCheckpointName && { restoredCheckpointName: this._restoredCheckpointName }
529
538
  } }
530
539
  };
531
540
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["ProcessHandle","UnsupportedStdinCloseError","SandboxProcessManager","MastraSandbox","SandboxNotReadyError","SandboxNotFoundError","Sandbox"],"sources":["../src/utils/shell-quote.ts","../src/sandbox/process-manager.ts","../src/sandbox/index.ts","../src/provider.ts"],"sourcesContent":["/**\n * Shell-quote a single argument for safe use in a command string.\n *\n * Arguments containing only safe characters are returned as-is.\n * All others are wrapped in single quotes with embedded single quotes escaped.\n */\nexport function shellQuote(arg: string): string {\n // Safe characters that don't need quoting\n if (/^[a-zA-Z0-9._\\-/@:=]+$/.test(arg)) return arg;\n // Wrap in single quotes, escaping any embedded single quotes\n return \"'\" + arg.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n","/**\n * Railway Process Manager\n *\n * Implements SandboxProcessManager for Railway sandboxes.\n * Wraps the Railway SDK's `Sandbox.exec()` API.\n *\n * Railway's `exec(command, options)` accepts per-call `cwd` and `env` options\n * (since SDK v3.3.1) and returns an `ExecHandle` that runs the command\n * server-side, independently of the client. Each spawn() starts one exec.\n * The handle streams output via `onStdout`/`onStderr` callbacks wired to\n * `emitStdout`/`emitStderr`, exposes a durable `sessionName`, and can be\n * terminated with `kill(signal)`.\n */\n\nimport { ProcessHandle, UnsupportedStdinCloseError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { CommandResult, ProcessInfo, SpawnProcessOptions } from '@mastra/core/workspace';\nimport type { ExecHandle, ExecResult } from 'railway';\nimport type { RailwaySandbox } from './index';\n\nexport const LOG_PREFIX = '[RailwaySandbox]';\n\n// =============================================================================\n// Railway Process Handle\n// =============================================================================\n\n/**\n * Wraps a Railway ExecHandle to conform to Mastra's ProcessHandle.\n * Not exported — internal to this module.\n */\nclass RailwayProcessHandle extends ProcessHandle {\n readonly pid: string;\n\n private readonly _execHandle: ExecHandle;\n private readonly _startTime: number;\n private _exitCode: number | undefined;\n private _waitPromise: Promise<CommandResult> | null = null;\n private _killed = false;\n\n constructor(pid: string, execHandle: ExecHandle, startTime: number, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this._execHandle = execHandle;\n this._startTime = startTime;\n\n // Resolve exit code once the command completes so exitCode is available\n // without an explicit wait().\n void this._execHandle.then(\n (result: ExecResult) => {\n // -1 means terminated by a signal (e.g. after kill()).\n this._exitCode = result.exitCode ?? (this._killed ? 137 : -1);\n },\n () => {\n if (this._exitCode === undefined) {\n this._exitCode = 1;\n }\n },\n );\n }\n\n get exitCode(): number | undefined {\n return this._exitCode;\n }\n\n async wait(): Promise<CommandResult> {\n // Idempotent — cache the promise so repeated calls return the same result.\n if (!this._waitPromise) {\n this._waitPromise = this._doWait();\n }\n return this._waitPromise;\n }\n\n private async _doWait(): Promise<CommandResult> {\n try {\n const result = await this._execHandle;\n const exitCode = result.exitCode ?? (this._killed ? 137 : -1);\n this._exitCode = exitCode;\n\n // Railway captures output server-side; surface it through the base buffers\n // if streaming callbacks didn't already populate them.\n if (result.stdout && !this.stdout) this.emitStdout(result.stdout);\n if (result.stderr && !this.stderr) this.emitStderr(result.stderr);\n\n return {\n success: exitCode === 0,\n exitCode,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n killed: this._killed,\n timedOut: result.timedOut,\n };\n } catch (error) {\n const exitCode = this._exitCode ?? 1;\n this._exitCode = exitCode;\n return {\n success: false,\n exitCode,\n stdout: this.stdout,\n stderr: this.stderr || (error instanceof Error ? error.message : String(error)),\n executionTimeMs: Date.now() - this._startTime,\n killed: this._killed,\n };\n }\n }\n\n async kill(): Promise<boolean> {\n if (this._exitCode !== undefined) return false;\n this._killed = true;\n try {\n return await this._execHandle.kill('TERM');\n } catch {\n // Command may already be gone.\n return false;\n }\n }\n\n async sendStdin(_data: string): Promise<void> {\n // Railway's exec API does not expose stdin streaming.\n throw new Error(`${LOG_PREFIX} sending stdin is not supported by the Railway sandbox provider`);\n }\n\n async closeStdin(): Promise<void> {\n throw new UnsupportedStdinCloseError(\n `${LOG_PREFIX} closing stdin is not supported by the Railway sandbox provider`,\n );\n }\n}\n\n// =============================================================================\n// Railway Process Manager\n// =============================================================================\n\nexport interface RailwayProcessManagerOptions {\n env?: Record<string, string | undefined>;\n}\n\n/**\n * Railway implementation of SandboxProcessManager.\n * Uses the Railway SDK's `Sandbox.exec()` with one exec per spawned process.\n */\nexport class RailwayProcessManager extends SandboxProcessManager<RailwaySandbox> {\n private _spawnCounter = 0;\n\n constructor(opts: RailwayProcessManagerOptions = {}) {\n super({ env: opts.env });\n }\n\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const railway = this.sandbox.railway;\n\n // Merge default env with per-spawn env.\n const mergedEnv = { ...this.env, ...options.env };\n const env = Object.fromEntries(\n Object.entries(mergedEnv).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n\n const pid = `railway-proc-${Date.now().toString(36)}-${(this._spawnCounter++).toString(36)}`;\n\n // Deferred reference — callbacks fire asynchronously after the handle is\n // assigned, so `handle` is always defined by the time they run.\n let handle: RailwayProcessHandle;\n\n const execHandle = railway.exec(command, {\n ...(options.timeout !== undefined && { timeoutSec: Math.ceil(options.timeout / 1000) }),\n ...(options.cwd !== undefined && { cwd: options.cwd }),\n ...(Object.keys(env).length > 0 && { env }),\n onStdout: (chunk: string) => handle.emitStdout(chunk),\n onStderr: (chunk: string) => handle.emitStderr(chunk),\n });\n\n handle = new RailwayProcessHandle(pid, execHandle, Date.now(), options);\n this._tracked.set(handle.pid, handle);\n return handle;\n }\n\n /**\n * List tracked processes.\n *\n * Railway has no API to enumerate running exec sessions by sandbox, so this\n * reports the processes this manager spawned.\n */\n async list(): Promise<ProcessInfo[]> {\n return Array.from(this._tracked.values()).map(handle => ({\n pid: handle.pid,\n command: handle.command,\n running: handle.exitCode === undefined,\n ...(handle.exitCode !== undefined && { exitCode: handle.exitCode }),\n }));\n }\n}\n","/**\n * Railway Sandbox Provider\n *\n * A Railway sandbox implementation for Mastra workspaces. Provisions an\n * ephemeral, isolated Linux VM on Railway, runs commands in it via the\n * Railway TypeScript SDK, and destroys it on teardown.\n *\n * @see https://docs.railway.com/sandboxes\n */\n\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n InstructionsOption,\n MastraSandboxOptions,\n ProviderStatus,\n SandboxCloneOptions,\n SandboxInfo,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, SandboxNotReadyError } from '@mastra/core/workspace';\nimport { Sandbox, SandboxFailedError, SandboxNotFoundError, SandboxTimeoutError } from 'railway';\nimport type { SandboxNetworkIsolation, SandboxTemplate } from 'railway';\nimport { shellQuote } from '../utils/shell-quote';\nimport { LOG_PREFIX, RailwayProcessManager } from './process-manager';\n\n/**\n * Safety margin subtracted from the sandbox's idle timeout when scheduling the\n * pre-reap checkpoint refresh. Sized to comfortably exceed Cloud Run cold-start\n * / recycle windows so a scale event during the refresh doesn't cause the timer\n * to lose the race with Railway's idle destroy. If a caller reduces the idle\n * timeout below this margin, the refresh falls back to the 1-second floor and\n * fires almost immediately after start — surfacing the misconfiguration rather\n * than silently skipping the refresh.\n */\nconst CHECKPOINT_REFRESH_MARGIN_MS = 180_000;\n\n// =============================================================================\n// Public capture result\n// =============================================================================\n\n/**\n * Outcome of a {@link RailwaySandbox.captureCheckpoint} call.\n *\n * `captured` and `coalesced` both represent a successful capture the caller can\n * persist against — they carry the checkpoint name inline so callers don't have\n * to reach back into the sandbox instance to learn what they just wrote.\n * `skipped` carries a machine-readable reason so the set stays extensible\n * without another breaking change.\n */\nexport type CaptureCheckpointResult =\n | { status: 'captured'; checkpointName: string }\n | { status: 'coalesced'; checkpointName: string }\n | { status: 'skipped'; reason: 'no-checkpoint-name-configured' | 'sandbox-not-running' };\n\n// =============================================================================\n// Railway Sandbox Options\n// =============================================================================\n\n/**\n * Railway sandbox provider configuration.\n */\nexport interface RailwaySandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** Unique identifier for this sandbox instance. */\n id?: string;\n /** Railway API token. Falls back to the RAILWAY_API_TOKEN env var. */\n token?: string;\n /** Railway environment ID. Falls back to the RAILWAY_ENVIRONMENT_ID env var. */\n environmentId?: string;\n /**\n * Reattach to an existing Railway sandbox by its Railway ID instead of\n * creating a new one. When set, `start()` calls `Sandbox.connect()`.\n */\n sandboxId?: string;\n /**\n * Reuse a saved Railway sandbox checkpoint as the baseline filesystem for\n * new sandboxes. When the named checkpoint is missing, the sandbox is created\n * normally and a checkpoint is captured after setup succeeds.\n */\n checkpointName?: string;\n /**\n * How long the sandbox can sit idle (no `exec` interaction) before Railway\n * destroys it automatically. Range depends on plan (1–120 minutes on\n * Hobby/Pro, 1–5 on Trial/Free). Defaults to the plan default when omitted.\n */\n idleTimeoutMinutes?: number;\n /**\n * Network isolation mode.\n * - `ISOLATED` (default): outbound internet only, no private network access.\n * - `PRIVATE`: joins the environment's private network.\n */\n networkIsolation?: SandboxNetworkIsolation;\n /** Environment variables baked into the sandbox, available to every command. */\n env?: Record<string, string>;\n /**\n * Provision the sandbox from a custom base image built with the Railway\n * template builder. Use this to pre-install packages or run setup steps so\n * every sandbox created from it starts ready.\n *\n * - Builder callback — receives the base `Sandbox.template()` and returns the\n * configured template. Railway builds the template when `Sandbox.create()`\n * runs during `start()`.\n * ```ts\n * template: t => t.withPackages('git', 'curl').run('npm i -g pnpm')\n * ```\n * - Pre-built `SandboxTemplate` — pass a template to reuse it across\n * sandboxes. Railway still builds the image during sandbox creation.\n *\n * Ignored when `sandboxId` is set (reattach) or when forking.\n */\n template?: SandboxTemplate | ((base: SandboxTemplate) => SandboxTemplate);\n /**\n * Default execution timeout in milliseconds applied to commands that don't\n * specify their own timeout. When omitted, commands run until they exit.\n */\n timeout?: number;\n /**\n * Custom instructions that override the default instructions returned by\n * `getInstructions()`.\n *\n * - `string` — Fully replaces the default instructions. Pass an empty string\n * to suppress instructions entirely.\n * - `(opts) => string` — Receives the default instructions and optional\n * request context so you can extend or customise per-request.\n */\n instructions?: InstructionsOption;\n}\n\n// =============================================================================\n// Railway Sandbox Implementation\n// =============================================================================\n\n/**\n * Railway sandbox provider for Mastra workspaces.\n *\n * Features:\n * - Ephemeral, isolated Linux VM via the Railway TypeScript SDK\n * - Command execution with streaming output and timeouts\n * - Configurable idle timeout and network isolation\n * - Reattach to an existing sandbox by Railway ID\n *\n * @example Basic usage\n * ```typescript\n * import { Workspace } from '@mastra/core/workspace';\n * import { RailwaySandbox } from '@mastra/railway';\n *\n * const sandbox = new RailwaySandbox({\n * // token + environmentId read from RAILWAY_API_TOKEN / RAILWAY_ENVIRONMENT_ID\n * idleTimeoutMinutes: 30,\n * });\n *\n * const workspace = new Workspace({ sandbox });\n * const result = await workspace.executeCode('console.log(\"Hello!\")');\n * ```\n *\n * @example Private networking\n * ```typescript\n * const sandbox = new RailwaySandbox({\n * networkIsolation: 'PRIVATE',\n * env: { NODE_ENV: 'production' },\n * });\n * ```\n */\nexport class RailwaySandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'RailwaySandbox';\n readonly provider = 'railway';\n status: ProviderStatus = 'pending';\n\n declare readonly processes: RailwayProcessManager;\n\n private _sandbox: Sandbox | null = null;\n private _createdAt: Date | null = null;\n private _checkpointRefreshTimer: ReturnType<typeof setTimeout> | null = null;\n private _checkpointRefreshInFlight: Promise<void> | null = null;\n private _sandboxId?: string;\n private _startInFlight: Promise<void> | null = null;\n\n private readonly _token?: string;\n private readonly _environmentId?: string;\n private readonly _checkpointName?: string;\n private readonly _idleTimeoutMinutes?: number;\n private readonly _networkIsolation?: SandboxNetworkIsolation;\n private readonly _env: Record<string, string>;\n private readonly _timeout?: number;\n private readonly _instructionsOverride?: InstructionsOption;\n private readonly _templateOption?: RailwaySandboxOptions['template'];\n\n constructor(options: RailwaySandboxOptions = {}) {\n super({\n ...options,\n name: 'RailwaySandbox',\n processes: new RailwayProcessManager({ env: options.env }),\n });\n\n this.id = options.id ?? this.generateId();\n this._token = options.token ?? process.env.RAILWAY_API_TOKEN;\n this._environmentId = options.environmentId ?? process.env.RAILWAY_ENVIRONMENT_ID;\n this._sandboxId = options.sandboxId;\n this._checkpointName = options.checkpointName;\n this._idleTimeoutMinutes = options.idleTimeoutMinutes;\n this._networkIsolation = options.networkIsolation;\n this._env = options.env ?? {};\n this._timeout = options.timeout;\n this._instructionsOverride = options.instructions;\n this._templateOption = options.template;\n }\n\n private generateId(): string {\n return `railway-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n /**\n * Get the underlying Railway Sandbox instance for direct SDK access.\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started.\n */\n get railway(): Sandbox {\n if (!this._sandbox) {\n throw new SandboxNotReadyError(this.id);\n }\n return this._sandbox;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /**\n * Start the Railway sandbox.\n *\n * Reattaches to an existing sandbox when `sandboxId` is configured,\n * otherwise provisions a new one. Resolves once the sandbox is RUNNING.\n */\n async start(): Promise<void> {\n if (this._sandbox) {\n return;\n }\n\n const clientConfig = this._clientConfig();\n const createOptions = this._createOptions(clientConfig);\n\n if (this._sandboxId) {\n const sandboxId = this._sandboxId;\n this._startInFlight ??= (async () => {\n try {\n this._sandbox = await this._reconnectSandbox(sandboxId, clientConfig);\n } catch (error) {\n if (!(error instanceof SandboxNotFoundError)) {\n throw error;\n }\n this._sandbox = await this._createNewSandbox(createOptions);\n }\n })().finally(() => {\n this._startInFlight = null;\n });\n } else {\n this._startInFlight ??= (async () => {\n this._sandbox = await this._createNewSandbox(createOptions);\n })().finally(() => {\n this._startInFlight = null;\n });\n }\n await this._startInFlight;\n\n if (!this._sandbox) {\n throw new Error('Failed to start Railway sandbox');\n }\n\n const sandbox = this._sandbox as Sandbox;\n this._sandboxId = sandbox.id;\n\n this._createdAt = sandbox.createdAt ? new Date(sandbox.createdAt) : new Date();\n this.logger.debug(`${LOG_PREFIX} Railway sandbox ${sandbox.id} ready for logical ID: ${this.id}`);\n this._scheduleCheckpointRefresh();\n }\n\n /**\n * Create a new Railway sandbox.\n */\n private async _createNewSandbox(createOptions: ReturnType<RailwaySandbox['_createOptions']>): Promise<Sandbox> {\n this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox for: ${this.id}`);\n try {\n let checkpoinAlreadyExists = false;\n if (this._checkpointName) {\n const checkpoints = await Sandbox.checkpoints(this._clientConfig());\n checkpoinAlreadyExists = checkpoints.some(checkpoint => checkpoint.key === this._checkpointName);\n }\n\n let sandbox: Sandbox | undefined;\n if (checkpoinAlreadyExists) {\n sandbox = await Sandbox.create(this._checkpointName!, createOptions);\n } else {\n sandbox = await Sandbox.create(createOptions);\n }\n\n return sandbox;\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * Reconnect to an existing Railway sandbox, creating a fresh one when\n */\n private async _reconnectSandbox(\n reconnectSandboxId: string,\n clientConfig: { token?: string; environmentId?: string },\n ): Promise<Sandbox> {\n this.logger.debug(`${LOG_PREFIX} Reconnecting to Railway sandbox ${reconnectSandboxId}...`);\n\n let connectedSandbox: Sandbox = await Sandbox.connect(reconnectSandboxId, clientConfig);\n\n if (connectedSandbox.status !== 'RUNNING') {\n throw new SandboxNotFoundError({\n id: reconnectSandboxId,\n environmentId: clientConfig.environmentId ?? '',\n });\n }\n\n return connectedSandbox;\n }\n\n private _clientConfig(): { token?: string; environmentId?: string } {\n return {\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n };\n }\n\n private _createOptions(clientConfig: { token?: string; environmentId?: string }) {\n return {\n ...clientConfig,\n ...(this._idleTimeoutMinutes !== undefined && { idleTimeoutMinutes: this._idleTimeoutMinutes }),\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n ...(Object.keys(this._env).length > 0 && { env: this._env }),\n };\n }\n\n /** Persist the configured recovery checkpoint when available. */\n async snapshot(): Promise<void> {\n await this.captureCheckpoint();\n }\n\n /** Snapshots persist real checkpoints that can seed future sandboxes. */\n readonly supportsCheckpoints: boolean = true;\n\n /**\n * Capture the sandbox's checkpoint on demand, outside the idle-timer schedule.\n *\n * Intended for callers (e.g. a factory-side scheduler) that want to refresh\n * the recovery checkpoint at semantic moments — turn end, session-idle,\n * pre-teardown — rather than only just before Railway's idle destroy.\n *\n * Coalesces with any in-flight timer-driven refresh: concurrent callers join\n * the same underlying `Sandbox.checkpoint` call and receive\n * `{ status: 'coalesced', checkpointName }`. Both `captured` and `coalesced`\n * carry the checkpoint name inline so callers can persist a session→\n * checkpoint binding without a second, non-atomic read against the sandbox.\n * Returns `{ status: 'skipped', reason }` when there's nothing to capture\n * (no `checkpointName` configured, or the sandbox isn't running yet).\n *\n * On successful capture, restarts the idle-timer countdown so the next\n * timer-driven refresh is scheduled from this capture.\n *\n * Never captures without a `checkpointName` and never mutates status — safe\n * to invoke concurrently with `executeCommand`, `restart`, or `stop`.\n */\n async captureCheckpoint(): Promise<CaptureCheckpointResult> {\n const checkpointName = this._checkpointName;\n if (!checkpointName) {\n return { status: 'skipped', reason: 'no-checkpoint-name-configured' };\n }\n\n const sandbox = this._sandbox;\n if (!sandbox) {\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n\n if (this._checkpointRefreshInFlight) {\n await this._checkpointRefreshInFlight;\n return { status: 'coalesced', checkpointName };\n }\n\n const capture = this._checkpointSandbox(sandbox).finally(() => {\n if (this._checkpointRefreshInFlight === capture) {\n this._checkpointRefreshInFlight = null;\n }\n });\n this._checkpointRefreshInFlight = capture;\n\n await capture;\n this._scheduleCheckpointRefresh();\n\n return { status: 'captured', checkpointName };\n }\n\n private async _checkpointSandbox(sandbox: Sandbox): Promise<void> {\n if (!this._checkpointName) {\n return;\n }\n\n this.logger.debug(`${LOG_PREFIX} Capturing Railway sandbox checkpoint ${this._checkpointName} for: ${this.id}`);\n\n const checkpoints = await Sandbox.checkpoints(this._clientConfig());\n const checkpointAlreadyExists = checkpoints.some(checkpoint => checkpoint.key === this._checkpointName);\n\n // Remove first so we can re-create a checkpoint with the same name.\n if (checkpointAlreadyExists) {\n await Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig());\n }\n\n await sandbox.checkpoint(this._checkpointName);\n }\n\n private _scheduleCheckpointRefresh(): void {\n if (!this._checkpointName || !this._sandbox) {\n return;\n }\n\n const idleTimeoutMinutes = this._idleTimeoutMinutes ?? this._sandbox.idleTimeoutMinutes;\n if (!idleTimeoutMinutes) {\n return;\n }\n\n this._cancelCheckpointRefresh();\n\n const delayMs = Math.max(1_000, idleTimeoutMinutes * 60_000 - CHECKPOINT_REFRESH_MARGIN_MS);\n this._checkpointRefreshTimer = setTimeout(() => {\n this._checkpointRefreshTimer = null;\n const sandbox = this._sandbox;\n if (!sandbox || this._checkpointRefreshInFlight) {\n return;\n }\n\n const refresh = this._checkpointSandbox(sandbox).finally(() => {\n if (this._checkpointRefreshInFlight === refresh) {\n this._checkpointRefreshInFlight = null;\n }\n });\n this._checkpointRefreshInFlight = refresh;\n refresh.catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to refresh Railway sandbox checkpoint ${this._checkpointName}:`, error);\n });\n }, delayMs);\n this._checkpointRefreshTimer.unref?.();\n }\n\n private _cancelCheckpointRefresh(): void {\n if (this._checkpointRefreshTimer) {\n clearTimeout(this._checkpointRefreshTimer);\n this._checkpointRefreshTimer = null;\n }\n }\n\n /**\n * Stop the Railway sandbox.\n *\n * Railway sandboxes have no separate \"stopped\" state — they're either\n * running or destroyed — so stopping destroys the sandbox but we keep a checkpoint of the filesystem.\n */\n async stop(): Promise<void> {\n if (this._checkpointName) {\n await this._flushCheckpointRefresh().catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to checkpoint Railway sandbox ${this._sandbox?.id}:`, error);\n });\n }\n\n await this._teardown();\n }\n\n /**\n * Destroy the Railway sandbox and release its resources including the checkpoint.\n */\n async destroy(): Promise<void> {\n this._cancelCheckpointRefresh();\n if (this._checkpointName) {\n await this._checkpointRefreshInFlight;\n await Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig()).catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to delete Railway checkpoint ${this._checkpointName}:`, error);\n });\n }\n\n await this._teardown();\n }\n\n private async _flushCheckpointRefresh(): Promise<void> {\n this._cancelCheckpointRefresh();\n\n if (this._checkpointRefreshInFlight) {\n await this._checkpointRefreshInFlight;\n return;\n }\n\n if (this._sandbox) {\n await this._checkpointSandbox(this._sandbox);\n }\n }\n\n private async _teardown(): Promise<void> {\n this._cancelCheckpointRefresh();\n if (!this._sandbox) {\n return;\n }\n await this._checkpointRefreshInFlight;\n\n const sandbox = this._sandbox;\n this._sandbox = null;\n try {\n await sandbox.destroy();\n } catch (error) {\n this.logger.warn(`${LOG_PREFIX} Failed to destroy Railway sandbox ${sandbox.id}:`, error);\n }\n }\n\n /**\n * Fork this running sandbox into a new, independent `RailwaySandbox`.\n *\n * Clones the filesystem (a fresh boot, not live processes) into the same\n * environment. The returned sandbox is already started and reattached to the\n * forked Railway sandbox; it inherits this sandbox's credentials and defaults\n * unless overridden via `options`.\n *\n * @throws {SandboxNotReadyError} If this sandbox has not been started.\n */\n async fork(\n options: Pick<RailwaySandboxOptions, 'id' | 'idleTimeoutMinutes' | 'networkIsolation' | 'env'> = {},\n ): Promise<RailwaySandbox> {\n const source = this.railway;\n const forked = await source.fork({\n ...(options.idleTimeoutMinutes !== undefined && { idleTimeoutMinutes: options.idleTimeoutMinutes }),\n ...(options.networkIsolation !== undefined && { networkIsolation: options.networkIsolation }),\n ...(options.env !== undefined && { env: options.env }),\n });\n\n const child = new RailwaySandbox({\n ...(options.id !== undefined && { id: options.id }),\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n networkIsolation: options.networkIsolation ?? this._networkIsolation,\n env: options.env ?? this._env,\n timeout: this._timeout,\n });\n await child._start();\n return child;\n }\n\n /**\n * Construct a sibling `RailwaySandbox` that inherits this sandbox's\n * credentials and defaults (token, environment, checkpoint, network\n * isolation, timeout, template, instructions) with per-instance overrides.\n *\n * Unlike {@link fork}, `clone` performs no I/O and does not require this\n * sandbox to be started — the returned sandbox is not started and provisions\n * (or reattaches, when `sandboxId` is set) on its own `start()`. Use it when\n * one configured sandbox acts as the template for a fleet of independent\n * sandboxes (e.g. one per project).\n */\n clone(options: SandboxCloneOptions = {}): RailwaySandbox {\n return new RailwaySandbox({\n ...(options.id !== undefined && { id: options.id }),\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n ...((options.checkpointName ?? this._checkpointName) !== undefined && {\n checkpointName: options.checkpointName ?? this._checkpointName,\n }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n env: options.env ?? this._env,\n ...(this._templateOption !== undefined && { template: this._templateOption }),\n ...(this._timeout !== undefined && { timeout: this._timeout }),\n ...(this._instructionsOverride !== undefined && { instructions: this._instructionsOverride }),\n });\n }\n\n /**\n * Whether a Railway API token was resolved at construction (explicit option\n * or the `RAILWAY_API_TOKEN` env fallback). Lets callers gate features on a\n * usable configuration without provisioning a sandbox.\n */\n get hasCredentials(): boolean {\n return this._token !== undefined && this._token !== '';\n }\n\n /** The configured idle teardown window in minutes, if any. */\n get idleTimeoutMinutes(): number | undefined {\n return this._idleTimeoutMinutes;\n }\n\n // ---------------------------------------------------------------------------\n // Info & Instructions\n // ---------------------------------------------------------------------------\n\n async getInfo(): Promise<SandboxInfo> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n ...(this._sandbox && {\n railwaySandboxId: this._sandbox.id,\n environmentId: this._sandbox.environmentId,\n region: this._sandbox.region,\n networkIsolation: this._sandbox.networkIsolation,\n ...(this._sandbox.idleTimeoutMinutes != null && {\n idleTimeoutMinutes: this._sandbox.idleTimeoutMinutes,\n }),\n }),\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions = this._buildDefaultInstructions();\n\n if (typeof this._instructionsOverride === 'string') {\n return this._instructionsOverride;\n }\n if (typeof this._instructionsOverride === 'function') {\n return this._instructionsOverride({ defaultInstructions });\n }\n return defaultInstructions;\n }\n\n private _buildDefaultInstructions(): string {\n const parts: string[] = [];\n parts.push('Railway cloud sandbox: an isolated Debian Linux VM with outbound internet access.');\n\n if (this._networkIsolation === 'PRIVATE') {\n parts.push('Joined to the environment private network.');\n }\n\n if (this._timeout !== undefined) {\n parts.push(`Default command timeout: ${Math.ceil(this._timeout / 1000)}s.`);\n } else {\n parts.push('Commands run until they exit unless a timeout is set.');\n }\n\n if (this._idleTimeoutMinutes !== undefined) {\n parts.push(`Idle timeout: ${this._idleTimeoutMinutes} minute(s).`);\n }\n\n return parts.join(' ');\n }\n\n // ---------------------------------------------------------------------------\n // Command Execution\n // ---------------------------------------------------------------------------\n\n /**\n * Execute a command in the sandbox and return the result.\n */\n async executeCommand(\n command: string,\n args: string[] = [],\n options: ExecuteCommandOptions = {},\n ): Promise<CommandResult> {\n const status = this._sandbox?.status;\n\n if (status !== 'RUNNING') {\n if (!this._checkpointName) {\n throw new SandboxNotReadyError(this.id);\n }\n\n this._sandbox = null;\n await this.start();\n }\n\n const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(' ')}` : command;\n const timeout = options.timeout ?? this._timeout;\n const env = options.env\n ? Object.fromEntries(\n Object.entries(options.env).filter((entry): entry is [string, string] => entry[1] !== undefined),\n )\n : undefined;\n const startedAt = Date.now();\n const result = await this.railway.exec(fullCommand, {\n ...(timeout !== undefined && { timeoutSec: Math.ceil(timeout / 1000) }),\n ...(options.cwd !== undefined && { cwd: options.cwd }),\n ...(env !== undefined && { env }),\n });\n const exitCode = result.exitCode ?? -1;\n return {\n success: exitCode === 0,\n exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n executionTimeMs: Date.now() - startedAt,\n command,\n args,\n timedOut: result.timedOut,\n };\n }\n}\n","/**\n * Railway sandbox provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { railwaySandboxProvider } from '@mastra/railway';\n *\n * const editor = new MastraEditor({\n * sandboxes: { [railwaySandboxProvider.id]: railwaySandboxProvider },\n * });\n * ```\n */\nimport type { SandboxProvider } from '@mastra/core/editor';\nimport { RailwaySandbox } from './sandbox';\n\n/**\n * Serializable subset of RailwaySandboxOptions for editor storage.\n */\ninterface RailwayProviderConfig {\n token?: string;\n environmentId?: string;\n sandboxId?: string;\n idleTimeoutMinutes?: number;\n networkIsolation?: 'ISOLATED' | 'PRIVATE';\n env?: Record<string, string>;\n timeout?: number;\n}\n\nexport const railwaySandboxProvider: SandboxProvider<RailwayProviderConfig> = {\n id: 'railway',\n name: 'Railway Sandbox',\n description: 'Ephemeral, isolated Linux VM powered by Railway',\n configSchema: {\n type: 'object',\n properties: {\n token: { type: 'string', description: 'Railway API token (falls back to RAILWAY_API_TOKEN)' },\n environmentId: {\n type: 'string',\n description: 'Railway environment ID (falls back to RAILWAY_ENVIRONMENT_ID)',\n },\n sandboxId: { type: 'string', description: 'Reattach to an existing Railway sandbox by ID' },\n idleTimeoutMinutes: {\n type: 'number',\n description: 'Minutes a sandbox can sit idle before Railway destroys it',\n },\n networkIsolation: {\n type: 'string',\n description: 'Network isolation mode',\n enum: ['ISOLATED', 'PRIVATE'],\n default: 'ISOLATED',\n },\n env: {\n type: 'object',\n description: 'Environment variables',\n additionalProperties: { type: 'string' },\n },\n timeout: { type: 'number', description: 'Default command timeout in ms' },\n },\n },\n createSandbox: config => new RailwaySandbox(config),\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["ProcessHandle","UnsupportedStdinCloseError","SandboxProcessManager","MastraSandbox","SandboxNotReadyError","SandboxNotFoundError","Sandbox"],"sources":["../src/utils/shell-quote.ts","../src/sandbox/process-manager.ts","../src/sandbox/index.ts","../src/provider.ts"],"sourcesContent":["/**\n * Shell-quote a single argument for safe use in a command string.\n *\n * Arguments containing only safe characters are returned as-is.\n * All others are wrapped in single quotes with embedded single quotes escaped.\n */\nexport function shellQuote(arg: string): string {\n // Safe characters that don't need quoting\n if (/^[a-zA-Z0-9._\\-/@:=]+$/.test(arg)) return arg;\n // Wrap in single quotes, escaping any embedded single quotes\n return \"'\" + arg.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n","/**\n * Railway Process Manager\n *\n * Implements SandboxProcessManager for Railway sandboxes.\n * Wraps the Railway SDK's `Sandbox.exec()` API.\n *\n * Railway's `exec(command, options)` accepts per-call `cwd` and `env` options\n * (since SDK v3.3.1) and returns an `ExecHandle` that runs the command\n * server-side, independently of the client. Each spawn() starts one exec.\n * The handle streams output via `onStdout`/`onStderr` callbacks wired to\n * `emitStdout`/`emitStderr`, exposes a durable `sessionName`, and can be\n * terminated with `kill(signal)`.\n */\n\nimport { ProcessHandle, UnsupportedStdinCloseError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { CommandResult, ProcessInfo, SpawnProcessOptions } from '@mastra/core/workspace';\nimport type { ExecHandle, ExecResult } from 'railway';\nimport type { RailwaySandbox } from './index';\n\nexport const LOG_PREFIX = '[RailwaySandbox]';\n\n// =============================================================================\n// Railway Process Handle\n// =============================================================================\n\n/**\n * Wraps a Railway ExecHandle to conform to Mastra's ProcessHandle.\n * Not exported — internal to this module.\n */\nclass RailwayProcessHandle extends ProcessHandle {\n readonly pid: string;\n\n private readonly _execHandle: ExecHandle;\n private readonly _startTime: number;\n private _exitCode: number | undefined;\n private _waitPromise: Promise<CommandResult> | null = null;\n private _killed = false;\n\n constructor(pid: string, execHandle: ExecHandle, startTime: number, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this._execHandle = execHandle;\n this._startTime = startTime;\n\n // Resolve exit code once the command completes so exitCode is available\n // without an explicit wait().\n void this._execHandle.then(\n (result: ExecResult) => {\n // -1 means terminated by a signal (e.g. after kill()).\n this._exitCode = result.exitCode ?? (this._killed ? 137 : -1);\n },\n () => {\n if (this._exitCode === undefined) {\n this._exitCode = 1;\n }\n },\n );\n }\n\n get exitCode(): number | undefined {\n return this._exitCode;\n }\n\n async wait(): Promise<CommandResult> {\n // Idempotent — cache the promise so repeated calls return the same result.\n if (!this._waitPromise) {\n this._waitPromise = this._doWait();\n }\n return this._waitPromise;\n }\n\n private async _doWait(): Promise<CommandResult> {\n try {\n const result = await this._execHandle;\n const exitCode = result.exitCode ?? (this._killed ? 137 : -1);\n this._exitCode = exitCode;\n\n // Railway captures output server-side; surface it through the base buffers\n // if streaming callbacks didn't already populate them.\n if (result.stdout && !this.stdout) this.emitStdout(result.stdout);\n if (result.stderr && !this.stderr) this.emitStderr(result.stderr);\n\n return {\n success: exitCode === 0,\n exitCode,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n killed: this._killed,\n timedOut: result.timedOut,\n };\n } catch (error) {\n const exitCode = this._exitCode ?? 1;\n this._exitCode = exitCode;\n return {\n success: false,\n exitCode,\n stdout: this.stdout,\n stderr: this.stderr || (error instanceof Error ? error.message : String(error)),\n executionTimeMs: Date.now() - this._startTime,\n killed: this._killed,\n };\n }\n }\n\n async kill(): Promise<boolean> {\n if (this._exitCode !== undefined) return false;\n this._killed = true;\n try {\n return await this._execHandle.kill('TERM');\n } catch {\n // Command may already be gone.\n return false;\n }\n }\n\n async sendStdin(_data: string): Promise<void> {\n // Railway's exec API does not expose stdin streaming.\n throw new Error(`${LOG_PREFIX} sending stdin is not supported by the Railway sandbox provider`);\n }\n\n async closeStdin(): Promise<void> {\n throw new UnsupportedStdinCloseError(\n `${LOG_PREFIX} closing stdin is not supported by the Railway sandbox provider`,\n );\n }\n}\n\n// =============================================================================\n// Railway Process Manager\n// =============================================================================\n\nexport interface RailwayProcessManagerOptions {\n env?: Record<string, string | undefined>;\n}\n\n/**\n * Railway implementation of SandboxProcessManager.\n * Uses the Railway SDK's `Sandbox.exec()` with one exec per spawned process.\n */\nexport class RailwayProcessManager extends SandboxProcessManager<RailwaySandbox> {\n private _spawnCounter = 0;\n\n constructor(opts: RailwayProcessManagerOptions = {}) {\n super({ env: opts.env });\n }\n\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const railway = this.sandbox.railway;\n\n // Merge default env with per-spawn env.\n const mergedEnv = { ...this.env, ...options.env };\n const env = Object.fromEntries(\n Object.entries(mergedEnv).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n\n const pid = `railway-proc-${Date.now().toString(36)}-${(this._spawnCounter++).toString(36)}`;\n\n // Deferred reference — callbacks fire asynchronously after the handle is\n // assigned, so `handle` is always defined by the time they run.\n let handle: RailwayProcessHandle;\n\n const execHandle = railway.exec(command, {\n ...(options.timeout !== undefined && { timeoutSec: Math.ceil(options.timeout / 1000) }),\n ...(options.cwd !== undefined && { cwd: options.cwd }),\n ...(Object.keys(env).length > 0 && { env }),\n onStdout: (chunk: string) => handle.emitStdout(chunk),\n onStderr: (chunk: string) => handle.emitStderr(chunk),\n });\n\n handle = new RailwayProcessHandle(pid, execHandle, Date.now(), options);\n this._tracked.set(handle.pid, handle);\n return handle;\n }\n\n /**\n * List tracked processes.\n *\n * Railway has no API to enumerate running exec sessions by sandbox, so this\n * reports the processes this manager spawned.\n */\n async list(): Promise<ProcessInfo[]> {\n return Array.from(this._tracked.values()).map(handle => ({\n pid: handle.pid,\n command: handle.command,\n running: handle.exitCode === undefined,\n ...(handle.exitCode !== undefined && { exitCode: handle.exitCode }),\n }));\n }\n}\n","/**\n * Railway Sandbox Provider\n *\n * A Railway sandbox implementation for Mastra workspaces. Provisions an\n * ephemeral, isolated Linux VM on Railway, runs commands in it via the\n * Railway TypeScript SDK, and destroys it on teardown.\n *\n * @see https://docs.railway.com/sandboxes\n */\n\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n InstructionsOption,\n MastraSandboxOptions,\n ProviderStatus,\n SandboxCloneOptions,\n SandboxInfo,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, SandboxNotReadyError } from '@mastra/core/workspace';\nimport { Sandbox, SandboxFailedError, SandboxNotFoundError, SandboxTimeoutError } from 'railway';\nimport type { SandboxNetworkIsolation, SandboxTemplate } from 'railway';\nimport { shellQuote } from '../utils/shell-quote';\nimport { LOG_PREFIX, RailwayProcessManager } from './process-manager';\n\n/**\n * Safety margin subtracted from the sandbox's idle timeout when scheduling the\n * pre-reap checkpoint refresh. Sized to comfortably exceed Cloud Run cold-start\n * / recycle windows so a scale event during the refresh doesn't cause the timer\n * to lose the race with Railway's idle destroy. If a caller reduces the idle\n * timeout below this margin, the refresh falls back to the 1-second floor and\n * fires almost immediately after start — surfacing the misconfiguration rather\n * than silently skipping the refresh.\n */\nconst CHECKPOINT_REFRESH_MARGIN_MS = 180_000;\n\n// =============================================================================\n// Public capture result\n// =============================================================================\n\n/**\n * Outcome of a {@link RailwaySandbox.captureCheckpoint} call.\n *\n * `captured` and `coalesced` both represent a successful capture the caller can\n * persist against — they carry the checkpoint name inline so callers don't have\n * to reach back into the sandbox instance to learn what they just wrote.\n * `skipped` carries a machine-readable reason so the set stays extensible\n * without another breaking change.\n */\nexport type CaptureCheckpointResult =\n | { status: 'captured'; checkpointName: string }\n | { status: 'coalesced'; checkpointName: string }\n | { status: 'skipped'; reason: 'no-checkpoint-name-configured' | 'sandbox-not-running' };\n\n// =============================================================================\n// Railway Sandbox Options\n// =============================================================================\n\n/**\n * Railway sandbox provider configuration.\n */\nexport interface RailwaySandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** Unique identifier for this sandbox instance. */\n id?: string;\n /** Railway API token. Falls back to the RAILWAY_API_TOKEN env var. */\n token?: string;\n /** Railway environment ID. Falls back to the RAILWAY_ENVIRONMENT_ID env var. */\n environmentId?: string;\n /**\n * Reattach to an existing Railway sandbox by its Railway ID instead of\n * creating a new one. When set, `start()` calls `Sandbox.connect()`.\n */\n sandboxId?: string;\n /**\n * Reuse a saved Railway sandbox checkpoint as the baseline filesystem for\n * new sandboxes. When the named checkpoint is missing, the sandbox is created\n * normally and a checkpoint is captured after setup succeeds.\n */\n checkpointName?: string;\n /**\n * Fallback checkpoint used to seed a new sandbox when `checkpointName` has\n * no stored state. Snapshots continue writing to `checkpointName`.\n */\n seedCheckpointName?: string;\n /**\n * How long the sandbox can sit idle (no `exec` interaction) before Railway\n * destroys it automatically. Range depends on plan (1–120 minutes on\n * Hobby/Pro, 1–5 on Trial/Free). Defaults to the plan default when omitted.\n */\n idleTimeoutMinutes?: number;\n /**\n * Network isolation mode.\n * - `ISOLATED` (default): outbound internet only, no private network access.\n * - `PRIVATE`: joins the environment's private network.\n */\n networkIsolation?: SandboxNetworkIsolation;\n /** Environment variables baked into the sandbox, available to every command. */\n env?: Record<string, string>;\n /**\n * Provision the sandbox from a custom base image built with the Railway\n * template builder. Use this to pre-install packages or run setup steps so\n * every sandbox created from it starts ready.\n *\n * - Builder callback — receives the base `Sandbox.template()` and returns the\n * configured template. Railway builds the template when `Sandbox.create()`\n * runs during `start()`.\n * ```ts\n * template: t => t.withPackages('git', 'curl').run('npm i -g pnpm')\n * ```\n * - Pre-built `SandboxTemplate` — pass a template to reuse it across\n * sandboxes. Railway still builds the image during sandbox creation.\n *\n * Ignored when `sandboxId` is set (reattach) or when forking.\n */\n template?: SandboxTemplate | ((base: SandboxTemplate) => SandboxTemplate);\n /**\n * Default execution timeout in milliseconds applied to commands that don't\n * specify their own timeout. When omitted, commands run until they exit.\n */\n timeout?: number;\n /**\n * Custom instructions that override the default instructions returned by\n * `getInstructions()`.\n *\n * - `string` — Fully replaces the default instructions. Pass an empty string\n * to suppress instructions entirely.\n * - `(opts) => string` — Receives the default instructions and optional\n * request context so you can extend or customise per-request.\n */\n instructions?: InstructionsOption;\n}\n\n// =============================================================================\n// Railway Sandbox Implementation\n// =============================================================================\n\n/**\n * Railway sandbox provider for Mastra workspaces.\n *\n * Features:\n * - Ephemeral, isolated Linux VM via the Railway TypeScript SDK\n * - Command execution with streaming output and timeouts\n * - Configurable idle timeout and network isolation\n * - Reattach to an existing sandbox by Railway ID\n *\n * @example Basic usage\n * ```typescript\n * import { Workspace } from '@mastra/core/workspace';\n * import { RailwaySandbox } from '@mastra/railway';\n *\n * const sandbox = new RailwaySandbox({\n * // token + environmentId read from RAILWAY_API_TOKEN / RAILWAY_ENVIRONMENT_ID\n * idleTimeoutMinutes: 30,\n * });\n *\n * const workspace = new Workspace({ sandbox });\n * const result = await workspace.executeCode('console.log(\"Hello!\")');\n * ```\n *\n * @example Private networking\n * ```typescript\n * const sandbox = new RailwaySandbox({\n * networkIsolation: 'PRIVATE',\n * env: { NODE_ENV: 'production' },\n * });\n * ```\n */\nexport class RailwaySandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'RailwaySandbox';\n readonly provider = 'railway';\n status: ProviderStatus = 'pending';\n\n declare readonly processes: RailwayProcessManager;\n\n private _sandbox: Sandbox | null = null;\n private _createdAt: Date | null = null;\n private _checkpointRefreshTimer: ReturnType<typeof setTimeout> | null = null;\n private _checkpointRefreshInFlight: Promise<void> | null = null;\n private _sandboxId?: string;\n private _restoredCheckpointName?: string;\n private _startInFlight: Promise<void> | null = null;\n\n private readonly _token?: string;\n private readonly _environmentId?: string;\n private readonly _checkpointName?: string;\n private readonly _seedCheckpointName?: string;\n private readonly _idleTimeoutMinutes?: number;\n private readonly _networkIsolation?: SandboxNetworkIsolation;\n private readonly _env: Record<string, string>;\n private readonly _timeout?: number;\n private readonly _instructionsOverride?: InstructionsOption;\n private readonly _templateOption?: RailwaySandboxOptions['template'];\n\n constructor(options: RailwaySandboxOptions = {}) {\n super({\n ...options,\n name: 'RailwaySandbox',\n processes: new RailwayProcessManager({ env: options.env }),\n });\n\n this.id = options.id ?? this.generateId();\n this._token = options.token ?? process.env.RAILWAY_API_TOKEN;\n this._environmentId = options.environmentId ?? process.env.RAILWAY_ENVIRONMENT_ID;\n this._sandboxId = options.sandboxId;\n this._checkpointName = options.checkpointName;\n this._seedCheckpointName = options.seedCheckpointName;\n this._idleTimeoutMinutes = options.idleTimeoutMinutes;\n this._networkIsolation = options.networkIsolation;\n this._env = options.env ?? {};\n this._timeout = options.timeout;\n this._instructionsOverride = options.instructions;\n this._templateOption = options.template;\n }\n\n private generateId(): string {\n return `railway-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n /**\n * Get the underlying Railway Sandbox instance for direct SDK access.\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started.\n */\n get railway(): Sandbox {\n if (!this._sandbox) {\n throw new SandboxNotReadyError(this.id);\n }\n return this._sandbox;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /**\n * Start the Railway sandbox.\n *\n * Reattaches to an existing sandbox when `sandboxId` is configured,\n * otherwise provisions a new one. Resolves once the sandbox is RUNNING.\n */\n async start(): Promise<void> {\n if (this._sandbox) {\n return;\n }\n\n const clientConfig = this._clientConfig();\n const createOptions = this._createOptions(clientConfig);\n\n if (this._sandboxId) {\n const sandboxId = this._sandboxId;\n this._startInFlight ??= (async () => {\n this._restoredCheckpointName = undefined;\n try {\n this._sandbox = await this._reconnectSandbox(sandboxId, clientConfig);\n } catch (error) {\n if (!(error instanceof SandboxNotFoundError)) {\n throw error;\n }\n this._sandbox = await this._createNewSandbox(createOptions);\n }\n })().finally(() => {\n this._startInFlight = null;\n });\n } else {\n this._startInFlight ??= (async () => {\n this._sandbox = await this._createNewSandbox(createOptions);\n })().finally(() => {\n this._startInFlight = null;\n });\n }\n await this._startInFlight;\n\n if (!this._sandbox) {\n throw new Error('Failed to start Railway sandbox');\n }\n\n const sandbox = this._sandbox as Sandbox;\n this._sandboxId = sandbox.id;\n\n this._createdAt = sandbox.createdAt ? new Date(sandbox.createdAt) : new Date();\n this.logger.debug(`${LOG_PREFIX} Railway sandbox ${sandbox.id} ready for logical ID: ${this.id}`);\n this._scheduleCheckpointRefresh();\n }\n\n /**\n * Create a new Railway sandbox.\n */\n private async _createNewSandbox(createOptions: ReturnType<RailwaySandbox['_createOptions']>): Promise<Sandbox> {\n this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox for: ${this.id}`);\n try {\n let checkpointToRestore: string | undefined;\n if (this._checkpointName || this._seedCheckpointName) {\n const checkpoints = await Sandbox.checkpoints(this._clientConfig());\n const checkpointNames = new Set(checkpoints.map(checkpoint => checkpoint.key));\n checkpointToRestore = checkpointNames.has(this._checkpointName ?? '')\n ? this._checkpointName\n : checkpointNames.has(this._seedCheckpointName ?? '')\n ? this._seedCheckpointName\n : undefined;\n }\n\n const sandbox = checkpointToRestore\n ? await Sandbox.create(checkpointToRestore, createOptions)\n : await Sandbox.create(createOptions);\n this._restoredCheckpointName = checkpointToRestore;\n return sandbox;\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * Reconnect to an existing Railway sandbox, creating a fresh one when\n */\n private async _reconnectSandbox(\n reconnectSandboxId: string,\n clientConfig: { token?: string; environmentId?: string },\n ): Promise<Sandbox> {\n this.logger.debug(`${LOG_PREFIX} Reconnecting to Railway sandbox ${reconnectSandboxId}...`);\n\n let connectedSandbox: Sandbox = await Sandbox.connect(reconnectSandboxId, clientConfig);\n\n if (connectedSandbox.status !== 'RUNNING') {\n throw new SandboxNotFoundError({\n id: reconnectSandboxId,\n environmentId: clientConfig.environmentId ?? '',\n });\n }\n\n return connectedSandbox;\n }\n\n private _clientConfig(): { token?: string; environmentId?: string } {\n return {\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n };\n }\n\n private _createOptions(clientConfig: { token?: string; environmentId?: string }) {\n return {\n ...clientConfig,\n ...(this._idleTimeoutMinutes !== undefined && { idleTimeoutMinutes: this._idleTimeoutMinutes }),\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n ...(Object.keys(this._env).length > 0 && { env: this._env }),\n };\n }\n\n /** Persist the configured recovery checkpoint when available. */\n async snapshot(): Promise<void> {\n await this.captureCheckpoint();\n }\n\n /** Snapshots persist real checkpoints that can seed future sandboxes. */\n readonly supportsCheckpoints: boolean = true;\n\n /**\n * Capture the sandbox's checkpoint on demand, outside the idle-timer schedule.\n *\n * Intended for callers (e.g. a factory-side scheduler) that want to refresh\n * the recovery checkpoint at semantic moments — turn end, session-idle,\n * pre-teardown — rather than only just before Railway's idle destroy.\n *\n * Coalesces with any in-flight timer-driven refresh: concurrent callers join\n * the same underlying `Sandbox.checkpoint` call and receive\n * `{ status: 'coalesced', checkpointName }`. Both `captured` and `coalesced`\n * carry the checkpoint name inline so callers can persist a session→\n * checkpoint binding without a second, non-atomic read against the sandbox.\n * Returns `{ status: 'skipped', reason }` when there's nothing to capture\n * (no `checkpointName` configured, or the sandbox isn't running yet).\n *\n * On successful capture, restarts the idle-timer countdown so the next\n * timer-driven refresh is scheduled from this capture.\n *\n * Never captures without a `checkpointName` and never mutates status — safe\n * to invoke concurrently with `executeCommand`, `restart`, or `stop`.\n */\n async captureCheckpoint(): Promise<CaptureCheckpointResult> {\n const checkpointName = this._checkpointName;\n if (!checkpointName) {\n return { status: 'skipped', reason: 'no-checkpoint-name-configured' };\n }\n\n const sandbox = this._sandbox;\n if (!sandbox) {\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n\n if (this._checkpointRefreshInFlight) {\n await this._checkpointRefreshInFlight;\n return { status: 'coalesced', checkpointName };\n }\n\n const capture = this._checkpointSandbox(sandbox).finally(() => {\n if (this._checkpointRefreshInFlight === capture) {\n this._checkpointRefreshInFlight = null;\n }\n });\n this._checkpointRefreshInFlight = capture;\n\n await capture;\n this._scheduleCheckpointRefresh();\n\n return { status: 'captured', checkpointName };\n }\n\n private async _checkpointSandbox(sandbox: Sandbox): Promise<void> {\n if (!this._checkpointName) {\n return;\n }\n\n this.logger.debug(`${LOG_PREFIX} Capturing Railway sandbox checkpoint ${this._checkpointName} for: ${this.id}`);\n\n const checkpoints = await Sandbox.checkpoints(this._clientConfig());\n const checkpointAlreadyExists = checkpoints.some(checkpoint => checkpoint.key === this._checkpointName);\n\n // Remove first so we can re-create a checkpoint with the same name.\n if (checkpointAlreadyExists) {\n await Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig());\n }\n\n await sandbox.checkpoint(this._checkpointName);\n }\n\n private _scheduleCheckpointRefresh(): void {\n if (!this._checkpointName || !this._sandbox) {\n return;\n }\n\n const idleTimeoutMinutes = this._idleTimeoutMinutes ?? this._sandbox.idleTimeoutMinutes;\n if (!idleTimeoutMinutes) {\n return;\n }\n\n this._cancelCheckpointRefresh();\n\n const delayMs = Math.max(1_000, idleTimeoutMinutes * 60_000 - CHECKPOINT_REFRESH_MARGIN_MS);\n this._checkpointRefreshTimer = setTimeout(() => {\n this._checkpointRefreshTimer = null;\n const sandbox = this._sandbox;\n if (!sandbox || this._checkpointRefreshInFlight) {\n return;\n }\n\n const refresh = this._checkpointSandbox(sandbox).finally(() => {\n if (this._checkpointRefreshInFlight === refresh) {\n this._checkpointRefreshInFlight = null;\n }\n });\n this._checkpointRefreshInFlight = refresh;\n refresh.catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to refresh Railway sandbox checkpoint ${this._checkpointName}:`, error);\n });\n }, delayMs);\n this._checkpointRefreshTimer.unref?.();\n }\n\n private _cancelCheckpointRefresh(): void {\n if (this._checkpointRefreshTimer) {\n clearTimeout(this._checkpointRefreshTimer);\n this._checkpointRefreshTimer = null;\n }\n }\n\n /**\n * Stop the Railway sandbox.\n *\n * Railway sandboxes have no separate \"stopped\" state — they're either\n * running or destroyed — so stopping destroys the sandbox but we keep a checkpoint of the filesystem.\n */\n async stop(): Promise<void> {\n if (this._checkpointName) {\n await this._flushCheckpointRefresh().catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to checkpoint Railway sandbox ${this._sandbox?.id}:`, error);\n });\n }\n\n await this._teardown();\n }\n\n /**\n * Destroy the Railway sandbox and release its resources including the checkpoint.\n */\n async destroy(): Promise<void> {\n this._cancelCheckpointRefresh();\n if (this._checkpointName) {\n await this._checkpointRefreshInFlight;\n await Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig()).catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to delete Railway checkpoint ${this._checkpointName}:`, error);\n });\n }\n\n await this._teardown();\n }\n\n private async _flushCheckpointRefresh(): Promise<void> {\n this._cancelCheckpointRefresh();\n\n if (this._checkpointRefreshInFlight) {\n await this._checkpointRefreshInFlight;\n return;\n }\n\n if (this._sandbox) {\n await this._checkpointSandbox(this._sandbox);\n }\n }\n\n private async _teardown(): Promise<void> {\n this._cancelCheckpointRefresh();\n if (!this._sandbox) {\n return;\n }\n await this._checkpointRefreshInFlight;\n\n const sandbox = this._sandbox;\n this._sandbox = null;\n try {\n await sandbox.destroy();\n } catch (error) {\n this.logger.warn(`${LOG_PREFIX} Failed to destroy Railway sandbox ${sandbox.id}:`, error);\n }\n }\n\n /**\n * Fork this running sandbox into a new, independent `RailwaySandbox`.\n *\n * Clones the filesystem (a fresh boot, not live processes) into the same\n * environment. The returned sandbox is already started and reattached to the\n * forked Railway sandbox; it inherits this sandbox's credentials and defaults\n * unless overridden via `options`.\n *\n * @throws {SandboxNotReadyError} If this sandbox has not been started.\n */\n async fork(\n options: Pick<RailwaySandboxOptions, 'id' | 'idleTimeoutMinutes' | 'networkIsolation' | 'env'> = {},\n ): Promise<RailwaySandbox> {\n const source = this.railway;\n const forked = await source.fork({\n ...(options.idleTimeoutMinutes !== undefined && { idleTimeoutMinutes: options.idleTimeoutMinutes }),\n ...(options.networkIsolation !== undefined && { networkIsolation: options.networkIsolation }),\n ...(options.env !== undefined && { env: options.env }),\n });\n\n const child = new RailwaySandbox({\n ...(options.id !== undefined && { id: options.id }),\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n networkIsolation: options.networkIsolation ?? this._networkIsolation,\n env: options.env ?? this._env,\n timeout: this._timeout,\n });\n await child._start();\n return child;\n }\n\n /**\n * Construct a sibling `RailwaySandbox` that inherits this sandbox's\n * credentials and defaults (token, environment, checkpoint, network\n * isolation, timeout, template, instructions) with per-instance overrides.\n *\n * Unlike {@link fork}, `clone` performs no I/O and does not require this\n * sandbox to be started — the returned sandbox is not started and provisions\n * (or reattaches, when `sandboxId` is set) on its own `start()`. Use it when\n * one configured sandbox acts as the template for a fleet of independent\n * sandboxes (e.g. one per project).\n */\n clone(options: SandboxCloneOptions = {}): RailwaySandbox {\n return new RailwaySandbox({\n ...(options.id !== undefined && { id: options.id }),\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n ...((options.checkpointName ?? this._checkpointName) !== undefined && {\n checkpointName: options.checkpointName ?? this._checkpointName,\n }),\n ...(options.seedCheckpointName !== undefined && { seedCheckpointName: options.seedCheckpointName }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n env: options.env ?? this._env,\n ...(this._templateOption !== undefined && { template: this._templateOption }),\n ...(this._timeout !== undefined && { timeout: this._timeout }),\n ...(this._instructionsOverride !== undefined && { instructions: this._instructionsOverride }),\n });\n }\n\n /**\n * Whether a Railway API token was resolved at construction (explicit option\n * or the `RAILWAY_API_TOKEN` env fallback). Lets callers gate features on a\n * usable configuration without provisioning a sandbox.\n */\n get hasCredentials(): boolean {\n return this._token !== undefined && this._token !== '';\n }\n\n /** The configured idle teardown window in minutes, if any. */\n get idleTimeoutMinutes(): number | undefined {\n return this._idleTimeoutMinutes;\n }\n\n // ---------------------------------------------------------------------------\n // Info & Instructions\n // ---------------------------------------------------------------------------\n\n async getInfo(): Promise<SandboxInfo> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n ...(this._sandbox && {\n railwaySandboxId: this._sandbox.id,\n environmentId: this._sandbox.environmentId,\n region: this._sandbox.region,\n networkIsolation: this._sandbox.networkIsolation,\n ...(this._sandbox.idleTimeoutMinutes != null && {\n idleTimeoutMinutes: this._sandbox.idleTimeoutMinutes,\n }),\n ...(this._restoredCheckpointName && { restoredCheckpointName: this._restoredCheckpointName }),\n }),\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions = this._buildDefaultInstructions();\n\n if (typeof this._instructionsOverride === 'string') {\n return this._instructionsOverride;\n }\n if (typeof this._instructionsOverride === 'function') {\n return this._instructionsOverride({ defaultInstructions });\n }\n return defaultInstructions;\n }\n\n private _buildDefaultInstructions(): string {\n const parts: string[] = [];\n parts.push('Railway cloud sandbox: an isolated Debian Linux VM with outbound internet access.');\n\n if (this._networkIsolation === 'PRIVATE') {\n parts.push('Joined to the environment private network.');\n }\n\n if (this._timeout !== undefined) {\n parts.push(`Default command timeout: ${Math.ceil(this._timeout / 1000)}s.`);\n } else {\n parts.push('Commands run until they exit unless a timeout is set.');\n }\n\n if (this._idleTimeoutMinutes !== undefined) {\n parts.push(`Idle timeout: ${this._idleTimeoutMinutes} minute(s).`);\n }\n\n return parts.join(' ');\n }\n\n // ---------------------------------------------------------------------------\n // Command Execution\n // ---------------------------------------------------------------------------\n\n /**\n * Execute a command in the sandbox and return the result.\n */\n async executeCommand(\n command: string,\n args: string[] = [],\n options: ExecuteCommandOptions = {},\n ): Promise<CommandResult> {\n const status = this._sandbox?.status;\n\n if (status !== 'RUNNING') {\n if (!this._checkpointName) {\n throw new SandboxNotReadyError(this.id);\n }\n\n this._sandbox = null;\n await this.start();\n }\n\n const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(' ')}` : command;\n const timeout = options.timeout ?? this._timeout;\n const env = options.env\n ? Object.fromEntries(\n Object.entries(options.env).filter((entry): entry is [string, string] => entry[1] !== undefined),\n )\n : undefined;\n const startedAt = Date.now();\n const result = await this.railway.exec(fullCommand, {\n ...(timeout !== undefined && { timeoutSec: Math.ceil(timeout / 1000) }),\n ...(options.cwd !== undefined && { cwd: options.cwd }),\n ...(env !== undefined && { env }),\n });\n const exitCode = result.exitCode ?? -1;\n return {\n success: exitCode === 0,\n exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n executionTimeMs: Date.now() - startedAt,\n command,\n args,\n timedOut: result.timedOut,\n };\n }\n}\n","/**\n * Railway sandbox provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { railwaySandboxProvider } from '@mastra/railway';\n *\n * const editor = new MastraEditor({\n * sandboxes: { [railwaySandboxProvider.id]: railwaySandboxProvider },\n * });\n * ```\n */\nimport type { SandboxProvider } from '@mastra/core/editor';\nimport { RailwaySandbox } from './sandbox';\n\n/**\n * Serializable subset of RailwaySandboxOptions for editor storage.\n */\ninterface RailwayProviderConfig {\n token?: string;\n environmentId?: string;\n sandboxId?: string;\n idleTimeoutMinutes?: number;\n networkIsolation?: 'ISOLATED' | 'PRIVATE';\n env?: Record<string, string>;\n timeout?: number;\n}\n\nexport const railwaySandboxProvider: SandboxProvider<RailwayProviderConfig> = {\n id: 'railway',\n name: 'Railway Sandbox',\n description: 'Ephemeral, isolated Linux VM powered by Railway',\n configSchema: {\n type: 'object',\n properties: {\n token: { type: 'string', description: 'Railway API token (falls back to RAILWAY_API_TOKEN)' },\n environmentId: {\n type: 'string',\n description: 'Railway environment ID (falls back to RAILWAY_ENVIRONMENT_ID)',\n },\n sandboxId: { type: 'string', description: 'Reattach to an existing Railway sandbox by ID' },\n idleTimeoutMinutes: {\n type: 'number',\n description: 'Minutes a sandbox can sit idle before Railway destroys it',\n },\n networkIsolation: {\n type: 'string',\n description: 'Network isolation mode',\n enum: ['ISOLATED', 'PRIVATE'],\n default: 'ISOLATED',\n },\n env: {\n type: 'object',\n description: 'Environment variables',\n additionalProperties: { type: 'string' },\n },\n timeout: { type: 'number', description: 'Default command timeout in ms' },\n },\n },\n createSandbox: config => new RailwaySandbox(config),\n};\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -197,10 +197,12 @@ var RailwaySandbox = class RailwaySandbox extends MastraSandbox {
197
197
  _checkpointRefreshTimer = null;
198
198
  _checkpointRefreshInFlight = null;
199
199
  _sandboxId;
200
+ _restoredCheckpointName;
200
201
  _startInFlight = null;
201
202
  _token;
202
203
  _environmentId;
203
204
  _checkpointName;
205
+ _seedCheckpointName;
204
206
  _idleTimeoutMinutes;
205
207
  _networkIsolation;
206
208
  _env;
@@ -218,6 +220,7 @@ var RailwaySandbox = class RailwaySandbox extends MastraSandbox {
218
220
  this._environmentId = options.environmentId ?? process.env.RAILWAY_ENVIRONMENT_ID;
219
221
  this._sandboxId = options.sandboxId;
220
222
  this._checkpointName = options.checkpointName;
223
+ this._seedCheckpointName = options.seedCheckpointName;
221
224
  this._idleTimeoutMinutes = options.idleTimeoutMinutes;
222
225
  this._networkIsolation = options.networkIsolation;
223
226
  this._env = options.env ?? {};
@@ -250,6 +253,7 @@ var RailwaySandbox = class RailwaySandbox extends MastraSandbox {
250
253
  if (this._sandboxId) {
251
254
  const sandboxId = this._sandboxId;
252
255
  this._startInFlight ??= (async () => {
256
+ this._restoredCheckpointName = void 0;
253
257
  try {
254
258
  this._sandbox = await this._reconnectSandbox(sandboxId, clientConfig);
255
259
  } catch (error) {
@@ -278,11 +282,14 @@ var RailwaySandbox = class RailwaySandbox extends MastraSandbox {
278
282
  async _createNewSandbox(createOptions) {
279
283
  this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox for: ${this.id}`);
280
284
  try {
281
- let checkpoinAlreadyExists = false;
282
- if (this._checkpointName) checkpoinAlreadyExists = (await Sandbox.checkpoints(this._clientConfig())).some((checkpoint) => checkpoint.key === this._checkpointName);
283
- let sandbox;
284
- if (checkpoinAlreadyExists) sandbox = await Sandbox.create(this._checkpointName, createOptions);
285
- else sandbox = await Sandbox.create(createOptions);
285
+ let checkpointToRestore;
286
+ if (this._checkpointName || this._seedCheckpointName) {
287
+ const checkpoints = await Sandbox.checkpoints(this._clientConfig());
288
+ const checkpointNames = new Set(checkpoints.map((checkpoint) => checkpoint.key));
289
+ checkpointToRestore = checkpointNames.has(this._checkpointName ?? "") ? this._checkpointName : checkpointNames.has(this._seedCheckpointName ?? "") ? this._seedCheckpointName : void 0;
290
+ }
291
+ const sandbox = checkpointToRestore ? await Sandbox.create(checkpointToRestore, createOptions) : await Sandbox.create(createOptions);
292
+ this._restoredCheckpointName = checkpointToRestore;
286
293
  return sandbox;
287
294
  } catch (error) {
288
295
  throw error;
@@ -492,6 +499,7 @@ var RailwaySandbox = class RailwaySandbox extends MastraSandbox {
492
499
  ...this._token !== void 0 && { token: this._token },
493
500
  ...this._environmentId !== void 0 && { environmentId: this._environmentId },
494
501
  ...(options.checkpointName ?? this._checkpointName) !== void 0 && { checkpointName: options.checkpointName ?? this._checkpointName },
502
+ ...options.seedCheckpointName !== void 0 && { seedCheckpointName: options.seedCheckpointName },
495
503
  idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,
496
504
  ...this._networkIsolation !== void 0 && { networkIsolation: this._networkIsolation },
497
505
  env: options.env ?? this._env,
@@ -524,7 +532,8 @@ var RailwaySandbox = class RailwaySandbox extends MastraSandbox {
524
532
  environmentId: this._sandbox.environmentId,
525
533
  region: this._sandbox.region,
526
534
  networkIsolation: this._sandbox.networkIsolation,
527
- ...this._sandbox.idleTimeoutMinutes != null && { idleTimeoutMinutes: this._sandbox.idleTimeoutMinutes }
535
+ ...this._sandbox.idleTimeoutMinutes != null && { idleTimeoutMinutes: this._sandbox.idleTimeoutMinutes },
536
+ ...this._restoredCheckpointName && { restoredCheckpointName: this._restoredCheckpointName }
528
537
  } }
529
538
  };
530
539
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/utils/shell-quote.ts","../src/sandbox/process-manager.ts","../src/sandbox/index.ts","../src/provider.ts"],"sourcesContent":["/**\n * Shell-quote a single argument for safe use in a command string.\n *\n * Arguments containing only safe characters are returned as-is.\n * All others are wrapped in single quotes with embedded single quotes escaped.\n */\nexport function shellQuote(arg: string): string {\n // Safe characters that don't need quoting\n if (/^[a-zA-Z0-9._\\-/@:=]+$/.test(arg)) return arg;\n // Wrap in single quotes, escaping any embedded single quotes\n return \"'\" + arg.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n","/**\n * Railway Process Manager\n *\n * Implements SandboxProcessManager for Railway sandboxes.\n * Wraps the Railway SDK's `Sandbox.exec()` API.\n *\n * Railway's `exec(command, options)` accepts per-call `cwd` and `env` options\n * (since SDK v3.3.1) and returns an `ExecHandle` that runs the command\n * server-side, independently of the client. Each spawn() starts one exec.\n * The handle streams output via `onStdout`/`onStderr` callbacks wired to\n * `emitStdout`/`emitStderr`, exposes a durable `sessionName`, and can be\n * terminated with `kill(signal)`.\n */\n\nimport { ProcessHandle, UnsupportedStdinCloseError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { CommandResult, ProcessInfo, SpawnProcessOptions } from '@mastra/core/workspace';\nimport type { ExecHandle, ExecResult } from 'railway';\nimport type { RailwaySandbox } from './index';\n\nexport const LOG_PREFIX = '[RailwaySandbox]';\n\n// =============================================================================\n// Railway Process Handle\n// =============================================================================\n\n/**\n * Wraps a Railway ExecHandle to conform to Mastra's ProcessHandle.\n * Not exported — internal to this module.\n */\nclass RailwayProcessHandle extends ProcessHandle {\n readonly pid: string;\n\n private readonly _execHandle: ExecHandle;\n private readonly _startTime: number;\n private _exitCode: number | undefined;\n private _waitPromise: Promise<CommandResult> | null = null;\n private _killed = false;\n\n constructor(pid: string, execHandle: ExecHandle, startTime: number, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this._execHandle = execHandle;\n this._startTime = startTime;\n\n // Resolve exit code once the command completes so exitCode is available\n // without an explicit wait().\n void this._execHandle.then(\n (result: ExecResult) => {\n // -1 means terminated by a signal (e.g. after kill()).\n this._exitCode = result.exitCode ?? (this._killed ? 137 : -1);\n },\n () => {\n if (this._exitCode === undefined) {\n this._exitCode = 1;\n }\n },\n );\n }\n\n get exitCode(): number | undefined {\n return this._exitCode;\n }\n\n async wait(): Promise<CommandResult> {\n // Idempotent — cache the promise so repeated calls return the same result.\n if (!this._waitPromise) {\n this._waitPromise = this._doWait();\n }\n return this._waitPromise;\n }\n\n private async _doWait(): Promise<CommandResult> {\n try {\n const result = await this._execHandle;\n const exitCode = result.exitCode ?? (this._killed ? 137 : -1);\n this._exitCode = exitCode;\n\n // Railway captures output server-side; surface it through the base buffers\n // if streaming callbacks didn't already populate them.\n if (result.stdout && !this.stdout) this.emitStdout(result.stdout);\n if (result.stderr && !this.stderr) this.emitStderr(result.stderr);\n\n return {\n success: exitCode === 0,\n exitCode,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n killed: this._killed,\n timedOut: result.timedOut,\n };\n } catch (error) {\n const exitCode = this._exitCode ?? 1;\n this._exitCode = exitCode;\n return {\n success: false,\n exitCode,\n stdout: this.stdout,\n stderr: this.stderr || (error instanceof Error ? error.message : String(error)),\n executionTimeMs: Date.now() - this._startTime,\n killed: this._killed,\n };\n }\n }\n\n async kill(): Promise<boolean> {\n if (this._exitCode !== undefined) return false;\n this._killed = true;\n try {\n return await this._execHandle.kill('TERM');\n } catch {\n // Command may already be gone.\n return false;\n }\n }\n\n async sendStdin(_data: string): Promise<void> {\n // Railway's exec API does not expose stdin streaming.\n throw new Error(`${LOG_PREFIX} sending stdin is not supported by the Railway sandbox provider`);\n }\n\n async closeStdin(): Promise<void> {\n throw new UnsupportedStdinCloseError(\n `${LOG_PREFIX} closing stdin is not supported by the Railway sandbox provider`,\n );\n }\n}\n\n// =============================================================================\n// Railway Process Manager\n// =============================================================================\n\nexport interface RailwayProcessManagerOptions {\n env?: Record<string, string | undefined>;\n}\n\n/**\n * Railway implementation of SandboxProcessManager.\n * Uses the Railway SDK's `Sandbox.exec()` with one exec per spawned process.\n */\nexport class RailwayProcessManager extends SandboxProcessManager<RailwaySandbox> {\n private _spawnCounter = 0;\n\n constructor(opts: RailwayProcessManagerOptions = {}) {\n super({ env: opts.env });\n }\n\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const railway = this.sandbox.railway;\n\n // Merge default env with per-spawn env.\n const mergedEnv = { ...this.env, ...options.env };\n const env = Object.fromEntries(\n Object.entries(mergedEnv).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n\n const pid = `railway-proc-${Date.now().toString(36)}-${(this._spawnCounter++).toString(36)}`;\n\n // Deferred reference — callbacks fire asynchronously after the handle is\n // assigned, so `handle` is always defined by the time they run.\n let handle: RailwayProcessHandle;\n\n const execHandle = railway.exec(command, {\n ...(options.timeout !== undefined && { timeoutSec: Math.ceil(options.timeout / 1000) }),\n ...(options.cwd !== undefined && { cwd: options.cwd }),\n ...(Object.keys(env).length > 0 && { env }),\n onStdout: (chunk: string) => handle.emitStdout(chunk),\n onStderr: (chunk: string) => handle.emitStderr(chunk),\n });\n\n handle = new RailwayProcessHandle(pid, execHandle, Date.now(), options);\n this._tracked.set(handle.pid, handle);\n return handle;\n }\n\n /**\n * List tracked processes.\n *\n * Railway has no API to enumerate running exec sessions by sandbox, so this\n * reports the processes this manager spawned.\n */\n async list(): Promise<ProcessInfo[]> {\n return Array.from(this._tracked.values()).map(handle => ({\n pid: handle.pid,\n command: handle.command,\n running: handle.exitCode === undefined,\n ...(handle.exitCode !== undefined && { exitCode: handle.exitCode }),\n }));\n }\n}\n","/**\n * Railway Sandbox Provider\n *\n * A Railway sandbox implementation for Mastra workspaces. Provisions an\n * ephemeral, isolated Linux VM on Railway, runs commands in it via the\n * Railway TypeScript SDK, and destroys it on teardown.\n *\n * @see https://docs.railway.com/sandboxes\n */\n\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n InstructionsOption,\n MastraSandboxOptions,\n ProviderStatus,\n SandboxCloneOptions,\n SandboxInfo,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, SandboxNotReadyError } from '@mastra/core/workspace';\nimport { Sandbox, SandboxFailedError, SandboxNotFoundError, SandboxTimeoutError } from 'railway';\nimport type { SandboxNetworkIsolation, SandboxTemplate } from 'railway';\nimport { shellQuote } from '../utils/shell-quote';\nimport { LOG_PREFIX, RailwayProcessManager } from './process-manager';\n\n/**\n * Safety margin subtracted from the sandbox's idle timeout when scheduling the\n * pre-reap checkpoint refresh. Sized to comfortably exceed Cloud Run cold-start\n * / recycle windows so a scale event during the refresh doesn't cause the timer\n * to lose the race with Railway's idle destroy. If a caller reduces the idle\n * timeout below this margin, the refresh falls back to the 1-second floor and\n * fires almost immediately after start — surfacing the misconfiguration rather\n * than silently skipping the refresh.\n */\nconst CHECKPOINT_REFRESH_MARGIN_MS = 180_000;\n\n// =============================================================================\n// Public capture result\n// =============================================================================\n\n/**\n * Outcome of a {@link RailwaySandbox.captureCheckpoint} call.\n *\n * `captured` and `coalesced` both represent a successful capture the caller can\n * persist against — they carry the checkpoint name inline so callers don't have\n * to reach back into the sandbox instance to learn what they just wrote.\n * `skipped` carries a machine-readable reason so the set stays extensible\n * without another breaking change.\n */\nexport type CaptureCheckpointResult =\n | { status: 'captured'; checkpointName: string }\n | { status: 'coalesced'; checkpointName: string }\n | { status: 'skipped'; reason: 'no-checkpoint-name-configured' | 'sandbox-not-running' };\n\n// =============================================================================\n// Railway Sandbox Options\n// =============================================================================\n\n/**\n * Railway sandbox provider configuration.\n */\nexport interface RailwaySandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** Unique identifier for this sandbox instance. */\n id?: string;\n /** Railway API token. Falls back to the RAILWAY_API_TOKEN env var. */\n token?: string;\n /** Railway environment ID. Falls back to the RAILWAY_ENVIRONMENT_ID env var. */\n environmentId?: string;\n /**\n * Reattach to an existing Railway sandbox by its Railway ID instead of\n * creating a new one. When set, `start()` calls `Sandbox.connect()`.\n */\n sandboxId?: string;\n /**\n * Reuse a saved Railway sandbox checkpoint as the baseline filesystem for\n * new sandboxes. When the named checkpoint is missing, the sandbox is created\n * normally and a checkpoint is captured after setup succeeds.\n */\n checkpointName?: string;\n /**\n * How long the sandbox can sit idle (no `exec` interaction) before Railway\n * destroys it automatically. Range depends on plan (1–120 minutes on\n * Hobby/Pro, 1–5 on Trial/Free). Defaults to the plan default when omitted.\n */\n idleTimeoutMinutes?: number;\n /**\n * Network isolation mode.\n * - `ISOLATED` (default): outbound internet only, no private network access.\n * - `PRIVATE`: joins the environment's private network.\n */\n networkIsolation?: SandboxNetworkIsolation;\n /** Environment variables baked into the sandbox, available to every command. */\n env?: Record<string, string>;\n /**\n * Provision the sandbox from a custom base image built with the Railway\n * template builder. Use this to pre-install packages or run setup steps so\n * every sandbox created from it starts ready.\n *\n * - Builder callback — receives the base `Sandbox.template()` and returns the\n * configured template. Railway builds the template when `Sandbox.create()`\n * runs during `start()`.\n * ```ts\n * template: t => t.withPackages('git', 'curl').run('npm i -g pnpm')\n * ```\n * - Pre-built `SandboxTemplate` — pass a template to reuse it across\n * sandboxes. Railway still builds the image during sandbox creation.\n *\n * Ignored when `sandboxId` is set (reattach) or when forking.\n */\n template?: SandboxTemplate | ((base: SandboxTemplate) => SandboxTemplate);\n /**\n * Default execution timeout in milliseconds applied to commands that don't\n * specify their own timeout. When omitted, commands run until they exit.\n */\n timeout?: number;\n /**\n * Custom instructions that override the default instructions returned by\n * `getInstructions()`.\n *\n * - `string` — Fully replaces the default instructions. Pass an empty string\n * to suppress instructions entirely.\n * - `(opts) => string` — Receives the default instructions and optional\n * request context so you can extend or customise per-request.\n */\n instructions?: InstructionsOption;\n}\n\n// =============================================================================\n// Railway Sandbox Implementation\n// =============================================================================\n\n/**\n * Railway sandbox provider for Mastra workspaces.\n *\n * Features:\n * - Ephemeral, isolated Linux VM via the Railway TypeScript SDK\n * - Command execution with streaming output and timeouts\n * - Configurable idle timeout and network isolation\n * - Reattach to an existing sandbox by Railway ID\n *\n * @example Basic usage\n * ```typescript\n * import { Workspace } from '@mastra/core/workspace';\n * import { RailwaySandbox } from '@mastra/railway';\n *\n * const sandbox = new RailwaySandbox({\n * // token + environmentId read from RAILWAY_API_TOKEN / RAILWAY_ENVIRONMENT_ID\n * idleTimeoutMinutes: 30,\n * });\n *\n * const workspace = new Workspace({ sandbox });\n * const result = await workspace.executeCode('console.log(\"Hello!\")');\n * ```\n *\n * @example Private networking\n * ```typescript\n * const sandbox = new RailwaySandbox({\n * networkIsolation: 'PRIVATE',\n * env: { NODE_ENV: 'production' },\n * });\n * ```\n */\nexport class RailwaySandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'RailwaySandbox';\n readonly provider = 'railway';\n status: ProviderStatus = 'pending';\n\n declare readonly processes: RailwayProcessManager;\n\n private _sandbox: Sandbox | null = null;\n private _createdAt: Date | null = null;\n private _checkpointRefreshTimer: ReturnType<typeof setTimeout> | null = null;\n private _checkpointRefreshInFlight: Promise<void> | null = null;\n private _sandboxId?: string;\n private _startInFlight: Promise<void> | null = null;\n\n private readonly _token?: string;\n private readonly _environmentId?: string;\n private readonly _checkpointName?: string;\n private readonly _idleTimeoutMinutes?: number;\n private readonly _networkIsolation?: SandboxNetworkIsolation;\n private readonly _env: Record<string, string>;\n private readonly _timeout?: number;\n private readonly _instructionsOverride?: InstructionsOption;\n private readonly _templateOption?: RailwaySandboxOptions['template'];\n\n constructor(options: RailwaySandboxOptions = {}) {\n super({\n ...options,\n name: 'RailwaySandbox',\n processes: new RailwayProcessManager({ env: options.env }),\n });\n\n this.id = options.id ?? this.generateId();\n this._token = options.token ?? process.env.RAILWAY_API_TOKEN;\n this._environmentId = options.environmentId ?? process.env.RAILWAY_ENVIRONMENT_ID;\n this._sandboxId = options.sandboxId;\n this._checkpointName = options.checkpointName;\n this._idleTimeoutMinutes = options.idleTimeoutMinutes;\n this._networkIsolation = options.networkIsolation;\n this._env = options.env ?? {};\n this._timeout = options.timeout;\n this._instructionsOverride = options.instructions;\n this._templateOption = options.template;\n }\n\n private generateId(): string {\n return `railway-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n /**\n * Get the underlying Railway Sandbox instance for direct SDK access.\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started.\n */\n get railway(): Sandbox {\n if (!this._sandbox) {\n throw new SandboxNotReadyError(this.id);\n }\n return this._sandbox;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /**\n * Start the Railway sandbox.\n *\n * Reattaches to an existing sandbox when `sandboxId` is configured,\n * otherwise provisions a new one. Resolves once the sandbox is RUNNING.\n */\n async start(): Promise<void> {\n if (this._sandbox) {\n return;\n }\n\n const clientConfig = this._clientConfig();\n const createOptions = this._createOptions(clientConfig);\n\n if (this._sandboxId) {\n const sandboxId = this._sandboxId;\n this._startInFlight ??= (async () => {\n try {\n this._sandbox = await this._reconnectSandbox(sandboxId, clientConfig);\n } catch (error) {\n if (!(error instanceof SandboxNotFoundError)) {\n throw error;\n }\n this._sandbox = await this._createNewSandbox(createOptions);\n }\n })().finally(() => {\n this._startInFlight = null;\n });\n } else {\n this._startInFlight ??= (async () => {\n this._sandbox = await this._createNewSandbox(createOptions);\n })().finally(() => {\n this._startInFlight = null;\n });\n }\n await this._startInFlight;\n\n if (!this._sandbox) {\n throw new Error('Failed to start Railway sandbox');\n }\n\n const sandbox = this._sandbox as Sandbox;\n this._sandboxId = sandbox.id;\n\n this._createdAt = sandbox.createdAt ? new Date(sandbox.createdAt) : new Date();\n this.logger.debug(`${LOG_PREFIX} Railway sandbox ${sandbox.id} ready for logical ID: ${this.id}`);\n this._scheduleCheckpointRefresh();\n }\n\n /**\n * Create a new Railway sandbox.\n */\n private async _createNewSandbox(createOptions: ReturnType<RailwaySandbox['_createOptions']>): Promise<Sandbox> {\n this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox for: ${this.id}`);\n try {\n let checkpoinAlreadyExists = false;\n if (this._checkpointName) {\n const checkpoints = await Sandbox.checkpoints(this._clientConfig());\n checkpoinAlreadyExists = checkpoints.some(checkpoint => checkpoint.key === this._checkpointName);\n }\n\n let sandbox: Sandbox | undefined;\n if (checkpoinAlreadyExists) {\n sandbox = await Sandbox.create(this._checkpointName!, createOptions);\n } else {\n sandbox = await Sandbox.create(createOptions);\n }\n\n return sandbox;\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * Reconnect to an existing Railway sandbox, creating a fresh one when\n */\n private async _reconnectSandbox(\n reconnectSandboxId: string,\n clientConfig: { token?: string; environmentId?: string },\n ): Promise<Sandbox> {\n this.logger.debug(`${LOG_PREFIX} Reconnecting to Railway sandbox ${reconnectSandboxId}...`);\n\n let connectedSandbox: Sandbox = await Sandbox.connect(reconnectSandboxId, clientConfig);\n\n if (connectedSandbox.status !== 'RUNNING') {\n throw new SandboxNotFoundError({\n id: reconnectSandboxId,\n environmentId: clientConfig.environmentId ?? '',\n });\n }\n\n return connectedSandbox;\n }\n\n private _clientConfig(): { token?: string; environmentId?: string } {\n return {\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n };\n }\n\n private _createOptions(clientConfig: { token?: string; environmentId?: string }) {\n return {\n ...clientConfig,\n ...(this._idleTimeoutMinutes !== undefined && { idleTimeoutMinutes: this._idleTimeoutMinutes }),\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n ...(Object.keys(this._env).length > 0 && { env: this._env }),\n };\n }\n\n /** Persist the configured recovery checkpoint when available. */\n async snapshot(): Promise<void> {\n await this.captureCheckpoint();\n }\n\n /** Snapshots persist real checkpoints that can seed future sandboxes. */\n readonly supportsCheckpoints: boolean = true;\n\n /**\n * Capture the sandbox's checkpoint on demand, outside the idle-timer schedule.\n *\n * Intended for callers (e.g. a factory-side scheduler) that want to refresh\n * the recovery checkpoint at semantic moments — turn end, session-idle,\n * pre-teardown — rather than only just before Railway's idle destroy.\n *\n * Coalesces with any in-flight timer-driven refresh: concurrent callers join\n * the same underlying `Sandbox.checkpoint` call and receive\n * `{ status: 'coalesced', checkpointName }`. Both `captured` and `coalesced`\n * carry the checkpoint name inline so callers can persist a session→\n * checkpoint binding without a second, non-atomic read against the sandbox.\n * Returns `{ status: 'skipped', reason }` when there's nothing to capture\n * (no `checkpointName` configured, or the sandbox isn't running yet).\n *\n * On successful capture, restarts the idle-timer countdown so the next\n * timer-driven refresh is scheduled from this capture.\n *\n * Never captures without a `checkpointName` and never mutates status — safe\n * to invoke concurrently with `executeCommand`, `restart`, or `stop`.\n */\n async captureCheckpoint(): Promise<CaptureCheckpointResult> {\n const checkpointName = this._checkpointName;\n if (!checkpointName) {\n return { status: 'skipped', reason: 'no-checkpoint-name-configured' };\n }\n\n const sandbox = this._sandbox;\n if (!sandbox) {\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n\n if (this._checkpointRefreshInFlight) {\n await this._checkpointRefreshInFlight;\n return { status: 'coalesced', checkpointName };\n }\n\n const capture = this._checkpointSandbox(sandbox).finally(() => {\n if (this._checkpointRefreshInFlight === capture) {\n this._checkpointRefreshInFlight = null;\n }\n });\n this._checkpointRefreshInFlight = capture;\n\n await capture;\n this._scheduleCheckpointRefresh();\n\n return { status: 'captured', checkpointName };\n }\n\n private async _checkpointSandbox(sandbox: Sandbox): Promise<void> {\n if (!this._checkpointName) {\n return;\n }\n\n this.logger.debug(`${LOG_PREFIX} Capturing Railway sandbox checkpoint ${this._checkpointName} for: ${this.id}`);\n\n const checkpoints = await Sandbox.checkpoints(this._clientConfig());\n const checkpointAlreadyExists = checkpoints.some(checkpoint => checkpoint.key === this._checkpointName);\n\n // Remove first so we can re-create a checkpoint with the same name.\n if (checkpointAlreadyExists) {\n await Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig());\n }\n\n await sandbox.checkpoint(this._checkpointName);\n }\n\n private _scheduleCheckpointRefresh(): void {\n if (!this._checkpointName || !this._sandbox) {\n return;\n }\n\n const idleTimeoutMinutes = this._idleTimeoutMinutes ?? this._sandbox.idleTimeoutMinutes;\n if (!idleTimeoutMinutes) {\n return;\n }\n\n this._cancelCheckpointRefresh();\n\n const delayMs = Math.max(1_000, idleTimeoutMinutes * 60_000 - CHECKPOINT_REFRESH_MARGIN_MS);\n this._checkpointRefreshTimer = setTimeout(() => {\n this._checkpointRefreshTimer = null;\n const sandbox = this._sandbox;\n if (!sandbox || this._checkpointRefreshInFlight) {\n return;\n }\n\n const refresh = this._checkpointSandbox(sandbox).finally(() => {\n if (this._checkpointRefreshInFlight === refresh) {\n this._checkpointRefreshInFlight = null;\n }\n });\n this._checkpointRefreshInFlight = refresh;\n refresh.catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to refresh Railway sandbox checkpoint ${this._checkpointName}:`, error);\n });\n }, delayMs);\n this._checkpointRefreshTimer.unref?.();\n }\n\n private _cancelCheckpointRefresh(): void {\n if (this._checkpointRefreshTimer) {\n clearTimeout(this._checkpointRefreshTimer);\n this._checkpointRefreshTimer = null;\n }\n }\n\n /**\n * Stop the Railway sandbox.\n *\n * Railway sandboxes have no separate \"stopped\" state — they're either\n * running or destroyed — so stopping destroys the sandbox but we keep a checkpoint of the filesystem.\n */\n async stop(): Promise<void> {\n if (this._checkpointName) {\n await this._flushCheckpointRefresh().catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to checkpoint Railway sandbox ${this._sandbox?.id}:`, error);\n });\n }\n\n await this._teardown();\n }\n\n /**\n * Destroy the Railway sandbox and release its resources including the checkpoint.\n */\n async destroy(): Promise<void> {\n this._cancelCheckpointRefresh();\n if (this._checkpointName) {\n await this._checkpointRefreshInFlight;\n await Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig()).catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to delete Railway checkpoint ${this._checkpointName}:`, error);\n });\n }\n\n await this._teardown();\n }\n\n private async _flushCheckpointRefresh(): Promise<void> {\n this._cancelCheckpointRefresh();\n\n if (this._checkpointRefreshInFlight) {\n await this._checkpointRefreshInFlight;\n return;\n }\n\n if (this._sandbox) {\n await this._checkpointSandbox(this._sandbox);\n }\n }\n\n private async _teardown(): Promise<void> {\n this._cancelCheckpointRefresh();\n if (!this._sandbox) {\n return;\n }\n await this._checkpointRefreshInFlight;\n\n const sandbox = this._sandbox;\n this._sandbox = null;\n try {\n await sandbox.destroy();\n } catch (error) {\n this.logger.warn(`${LOG_PREFIX} Failed to destroy Railway sandbox ${sandbox.id}:`, error);\n }\n }\n\n /**\n * Fork this running sandbox into a new, independent `RailwaySandbox`.\n *\n * Clones the filesystem (a fresh boot, not live processes) into the same\n * environment. The returned sandbox is already started and reattached to the\n * forked Railway sandbox; it inherits this sandbox's credentials and defaults\n * unless overridden via `options`.\n *\n * @throws {SandboxNotReadyError} If this sandbox has not been started.\n */\n async fork(\n options: Pick<RailwaySandboxOptions, 'id' | 'idleTimeoutMinutes' | 'networkIsolation' | 'env'> = {},\n ): Promise<RailwaySandbox> {\n const source = this.railway;\n const forked = await source.fork({\n ...(options.idleTimeoutMinutes !== undefined && { idleTimeoutMinutes: options.idleTimeoutMinutes }),\n ...(options.networkIsolation !== undefined && { networkIsolation: options.networkIsolation }),\n ...(options.env !== undefined && { env: options.env }),\n });\n\n const child = new RailwaySandbox({\n ...(options.id !== undefined && { id: options.id }),\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n networkIsolation: options.networkIsolation ?? this._networkIsolation,\n env: options.env ?? this._env,\n timeout: this._timeout,\n });\n await child._start();\n return child;\n }\n\n /**\n * Construct a sibling `RailwaySandbox` that inherits this sandbox's\n * credentials and defaults (token, environment, checkpoint, network\n * isolation, timeout, template, instructions) with per-instance overrides.\n *\n * Unlike {@link fork}, `clone` performs no I/O and does not require this\n * sandbox to be started — the returned sandbox is not started and provisions\n * (or reattaches, when `sandboxId` is set) on its own `start()`. Use it when\n * one configured sandbox acts as the template for a fleet of independent\n * sandboxes (e.g. one per project).\n */\n clone(options: SandboxCloneOptions = {}): RailwaySandbox {\n return new RailwaySandbox({\n ...(options.id !== undefined && { id: options.id }),\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n ...((options.checkpointName ?? this._checkpointName) !== undefined && {\n checkpointName: options.checkpointName ?? this._checkpointName,\n }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n env: options.env ?? this._env,\n ...(this._templateOption !== undefined && { template: this._templateOption }),\n ...(this._timeout !== undefined && { timeout: this._timeout }),\n ...(this._instructionsOverride !== undefined && { instructions: this._instructionsOverride }),\n });\n }\n\n /**\n * Whether a Railway API token was resolved at construction (explicit option\n * or the `RAILWAY_API_TOKEN` env fallback). Lets callers gate features on a\n * usable configuration without provisioning a sandbox.\n */\n get hasCredentials(): boolean {\n return this._token !== undefined && this._token !== '';\n }\n\n /** The configured idle teardown window in minutes, if any. */\n get idleTimeoutMinutes(): number | undefined {\n return this._idleTimeoutMinutes;\n }\n\n // ---------------------------------------------------------------------------\n // Info & Instructions\n // ---------------------------------------------------------------------------\n\n async getInfo(): Promise<SandboxInfo> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n ...(this._sandbox && {\n railwaySandboxId: this._sandbox.id,\n environmentId: this._sandbox.environmentId,\n region: this._sandbox.region,\n networkIsolation: this._sandbox.networkIsolation,\n ...(this._sandbox.idleTimeoutMinutes != null && {\n idleTimeoutMinutes: this._sandbox.idleTimeoutMinutes,\n }),\n }),\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions = this._buildDefaultInstructions();\n\n if (typeof this._instructionsOverride === 'string') {\n return this._instructionsOverride;\n }\n if (typeof this._instructionsOverride === 'function') {\n return this._instructionsOverride({ defaultInstructions });\n }\n return defaultInstructions;\n }\n\n private _buildDefaultInstructions(): string {\n const parts: string[] = [];\n parts.push('Railway cloud sandbox: an isolated Debian Linux VM with outbound internet access.');\n\n if (this._networkIsolation === 'PRIVATE') {\n parts.push('Joined to the environment private network.');\n }\n\n if (this._timeout !== undefined) {\n parts.push(`Default command timeout: ${Math.ceil(this._timeout / 1000)}s.`);\n } else {\n parts.push('Commands run until they exit unless a timeout is set.');\n }\n\n if (this._idleTimeoutMinutes !== undefined) {\n parts.push(`Idle timeout: ${this._idleTimeoutMinutes} minute(s).`);\n }\n\n return parts.join(' ');\n }\n\n // ---------------------------------------------------------------------------\n // Command Execution\n // ---------------------------------------------------------------------------\n\n /**\n * Execute a command in the sandbox and return the result.\n */\n async executeCommand(\n command: string,\n args: string[] = [],\n options: ExecuteCommandOptions = {},\n ): Promise<CommandResult> {\n const status = this._sandbox?.status;\n\n if (status !== 'RUNNING') {\n if (!this._checkpointName) {\n throw new SandboxNotReadyError(this.id);\n }\n\n this._sandbox = null;\n await this.start();\n }\n\n const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(' ')}` : command;\n const timeout = options.timeout ?? this._timeout;\n const env = options.env\n ? Object.fromEntries(\n Object.entries(options.env).filter((entry): entry is [string, string] => entry[1] !== undefined),\n )\n : undefined;\n const startedAt = Date.now();\n const result = await this.railway.exec(fullCommand, {\n ...(timeout !== undefined && { timeoutSec: Math.ceil(timeout / 1000) }),\n ...(options.cwd !== undefined && { cwd: options.cwd }),\n ...(env !== undefined && { env }),\n });\n const exitCode = result.exitCode ?? -1;\n return {\n success: exitCode === 0,\n exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n executionTimeMs: Date.now() - startedAt,\n command,\n args,\n timedOut: result.timedOut,\n };\n }\n}\n","/**\n * Railway sandbox provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { railwaySandboxProvider } from '@mastra/railway';\n *\n * const editor = new MastraEditor({\n * sandboxes: { [railwaySandboxProvider.id]: railwaySandboxProvider },\n * });\n * ```\n */\nimport type { SandboxProvider } from '@mastra/core/editor';\nimport { RailwaySandbox } from './sandbox';\n\n/**\n * Serializable subset of RailwaySandboxOptions for editor storage.\n */\ninterface RailwayProviderConfig {\n token?: string;\n environmentId?: string;\n sandboxId?: string;\n idleTimeoutMinutes?: number;\n networkIsolation?: 'ISOLATED' | 'PRIVATE';\n env?: Record<string, string>;\n timeout?: number;\n}\n\nexport const railwaySandboxProvider: SandboxProvider<RailwayProviderConfig> = {\n id: 'railway',\n name: 'Railway Sandbox',\n description: 'Ephemeral, isolated Linux VM powered by Railway',\n configSchema: {\n type: 'object',\n properties: {\n token: { type: 'string', description: 'Railway API token (falls back to RAILWAY_API_TOKEN)' },\n environmentId: {\n type: 'string',\n description: 'Railway environment ID (falls back to RAILWAY_ENVIRONMENT_ID)',\n },\n sandboxId: { type: 'string', description: 'Reattach to an existing Railway sandbox by ID' },\n idleTimeoutMinutes: {\n type: 'number',\n description: 'Minutes a sandbox can sit idle before Railway destroys it',\n },\n networkIsolation: {\n type: 'string',\n description: 'Network isolation mode',\n enum: ['ISOLATED', 'PRIVATE'],\n default: 'ISOLATED',\n },\n env: {\n type: 'object',\n description: 'Environment variables',\n additionalProperties: { type: 'string' },\n },\n timeout: { type: 'number', description: 'Default command timeout in ms' },\n },\n },\n createSandbox: config => new RailwaySandbox(config),\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/utils/shell-quote.ts","../src/sandbox/process-manager.ts","../src/sandbox/index.ts","../src/provider.ts"],"sourcesContent":["/**\n * Shell-quote a single argument for safe use in a command string.\n *\n * Arguments containing only safe characters are returned as-is.\n * All others are wrapped in single quotes with embedded single quotes escaped.\n */\nexport function shellQuote(arg: string): string {\n // Safe characters that don't need quoting\n if (/^[a-zA-Z0-9._\\-/@:=]+$/.test(arg)) return arg;\n // Wrap in single quotes, escaping any embedded single quotes\n return \"'\" + arg.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n","/**\n * Railway Process Manager\n *\n * Implements SandboxProcessManager for Railway sandboxes.\n * Wraps the Railway SDK's `Sandbox.exec()` API.\n *\n * Railway's `exec(command, options)` accepts per-call `cwd` and `env` options\n * (since SDK v3.3.1) and returns an `ExecHandle` that runs the command\n * server-side, independently of the client. Each spawn() starts one exec.\n * The handle streams output via `onStdout`/`onStderr` callbacks wired to\n * `emitStdout`/`emitStderr`, exposes a durable `sessionName`, and can be\n * terminated with `kill(signal)`.\n */\n\nimport { ProcessHandle, UnsupportedStdinCloseError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { CommandResult, ProcessInfo, SpawnProcessOptions } from '@mastra/core/workspace';\nimport type { ExecHandle, ExecResult } from 'railway';\nimport type { RailwaySandbox } from './index';\n\nexport const LOG_PREFIX = '[RailwaySandbox]';\n\n// =============================================================================\n// Railway Process Handle\n// =============================================================================\n\n/**\n * Wraps a Railway ExecHandle to conform to Mastra's ProcessHandle.\n * Not exported — internal to this module.\n */\nclass RailwayProcessHandle extends ProcessHandle {\n readonly pid: string;\n\n private readonly _execHandle: ExecHandle;\n private readonly _startTime: number;\n private _exitCode: number | undefined;\n private _waitPromise: Promise<CommandResult> | null = null;\n private _killed = false;\n\n constructor(pid: string, execHandle: ExecHandle, startTime: number, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this._execHandle = execHandle;\n this._startTime = startTime;\n\n // Resolve exit code once the command completes so exitCode is available\n // without an explicit wait().\n void this._execHandle.then(\n (result: ExecResult) => {\n // -1 means terminated by a signal (e.g. after kill()).\n this._exitCode = result.exitCode ?? (this._killed ? 137 : -1);\n },\n () => {\n if (this._exitCode === undefined) {\n this._exitCode = 1;\n }\n },\n );\n }\n\n get exitCode(): number | undefined {\n return this._exitCode;\n }\n\n async wait(): Promise<CommandResult> {\n // Idempotent — cache the promise so repeated calls return the same result.\n if (!this._waitPromise) {\n this._waitPromise = this._doWait();\n }\n return this._waitPromise;\n }\n\n private async _doWait(): Promise<CommandResult> {\n try {\n const result = await this._execHandle;\n const exitCode = result.exitCode ?? (this._killed ? 137 : -1);\n this._exitCode = exitCode;\n\n // Railway captures output server-side; surface it through the base buffers\n // if streaming callbacks didn't already populate them.\n if (result.stdout && !this.stdout) this.emitStdout(result.stdout);\n if (result.stderr && !this.stderr) this.emitStderr(result.stderr);\n\n return {\n success: exitCode === 0,\n exitCode,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n killed: this._killed,\n timedOut: result.timedOut,\n };\n } catch (error) {\n const exitCode = this._exitCode ?? 1;\n this._exitCode = exitCode;\n return {\n success: false,\n exitCode,\n stdout: this.stdout,\n stderr: this.stderr || (error instanceof Error ? error.message : String(error)),\n executionTimeMs: Date.now() - this._startTime,\n killed: this._killed,\n };\n }\n }\n\n async kill(): Promise<boolean> {\n if (this._exitCode !== undefined) return false;\n this._killed = true;\n try {\n return await this._execHandle.kill('TERM');\n } catch {\n // Command may already be gone.\n return false;\n }\n }\n\n async sendStdin(_data: string): Promise<void> {\n // Railway's exec API does not expose stdin streaming.\n throw new Error(`${LOG_PREFIX} sending stdin is not supported by the Railway sandbox provider`);\n }\n\n async closeStdin(): Promise<void> {\n throw new UnsupportedStdinCloseError(\n `${LOG_PREFIX} closing stdin is not supported by the Railway sandbox provider`,\n );\n }\n}\n\n// =============================================================================\n// Railway Process Manager\n// =============================================================================\n\nexport interface RailwayProcessManagerOptions {\n env?: Record<string, string | undefined>;\n}\n\n/**\n * Railway implementation of SandboxProcessManager.\n * Uses the Railway SDK's `Sandbox.exec()` with one exec per spawned process.\n */\nexport class RailwayProcessManager extends SandboxProcessManager<RailwaySandbox> {\n private _spawnCounter = 0;\n\n constructor(opts: RailwayProcessManagerOptions = {}) {\n super({ env: opts.env });\n }\n\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const railway = this.sandbox.railway;\n\n // Merge default env with per-spawn env.\n const mergedEnv = { ...this.env, ...options.env };\n const env = Object.fromEntries(\n Object.entries(mergedEnv).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n\n const pid = `railway-proc-${Date.now().toString(36)}-${(this._spawnCounter++).toString(36)}`;\n\n // Deferred reference — callbacks fire asynchronously after the handle is\n // assigned, so `handle` is always defined by the time they run.\n let handle: RailwayProcessHandle;\n\n const execHandle = railway.exec(command, {\n ...(options.timeout !== undefined && { timeoutSec: Math.ceil(options.timeout / 1000) }),\n ...(options.cwd !== undefined && { cwd: options.cwd }),\n ...(Object.keys(env).length > 0 && { env }),\n onStdout: (chunk: string) => handle.emitStdout(chunk),\n onStderr: (chunk: string) => handle.emitStderr(chunk),\n });\n\n handle = new RailwayProcessHandle(pid, execHandle, Date.now(), options);\n this._tracked.set(handle.pid, handle);\n return handle;\n }\n\n /**\n * List tracked processes.\n *\n * Railway has no API to enumerate running exec sessions by sandbox, so this\n * reports the processes this manager spawned.\n */\n async list(): Promise<ProcessInfo[]> {\n return Array.from(this._tracked.values()).map(handle => ({\n pid: handle.pid,\n command: handle.command,\n running: handle.exitCode === undefined,\n ...(handle.exitCode !== undefined && { exitCode: handle.exitCode }),\n }));\n }\n}\n","/**\n * Railway Sandbox Provider\n *\n * A Railway sandbox implementation for Mastra workspaces. Provisions an\n * ephemeral, isolated Linux VM on Railway, runs commands in it via the\n * Railway TypeScript SDK, and destroys it on teardown.\n *\n * @see https://docs.railway.com/sandboxes\n */\n\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n InstructionsOption,\n MastraSandboxOptions,\n ProviderStatus,\n SandboxCloneOptions,\n SandboxInfo,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, SandboxNotReadyError } from '@mastra/core/workspace';\nimport { Sandbox, SandboxFailedError, SandboxNotFoundError, SandboxTimeoutError } from 'railway';\nimport type { SandboxNetworkIsolation, SandboxTemplate } from 'railway';\nimport { shellQuote } from '../utils/shell-quote';\nimport { LOG_PREFIX, RailwayProcessManager } from './process-manager';\n\n/**\n * Safety margin subtracted from the sandbox's idle timeout when scheduling the\n * pre-reap checkpoint refresh. Sized to comfortably exceed Cloud Run cold-start\n * / recycle windows so a scale event during the refresh doesn't cause the timer\n * to lose the race with Railway's idle destroy. If a caller reduces the idle\n * timeout below this margin, the refresh falls back to the 1-second floor and\n * fires almost immediately after start — surfacing the misconfiguration rather\n * than silently skipping the refresh.\n */\nconst CHECKPOINT_REFRESH_MARGIN_MS = 180_000;\n\n// =============================================================================\n// Public capture result\n// =============================================================================\n\n/**\n * Outcome of a {@link RailwaySandbox.captureCheckpoint} call.\n *\n * `captured` and `coalesced` both represent a successful capture the caller can\n * persist against — they carry the checkpoint name inline so callers don't have\n * to reach back into the sandbox instance to learn what they just wrote.\n * `skipped` carries a machine-readable reason so the set stays extensible\n * without another breaking change.\n */\nexport type CaptureCheckpointResult =\n | { status: 'captured'; checkpointName: string }\n | { status: 'coalesced'; checkpointName: string }\n | { status: 'skipped'; reason: 'no-checkpoint-name-configured' | 'sandbox-not-running' };\n\n// =============================================================================\n// Railway Sandbox Options\n// =============================================================================\n\n/**\n * Railway sandbox provider configuration.\n */\nexport interface RailwaySandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** Unique identifier for this sandbox instance. */\n id?: string;\n /** Railway API token. Falls back to the RAILWAY_API_TOKEN env var. */\n token?: string;\n /** Railway environment ID. Falls back to the RAILWAY_ENVIRONMENT_ID env var. */\n environmentId?: string;\n /**\n * Reattach to an existing Railway sandbox by its Railway ID instead of\n * creating a new one. When set, `start()` calls `Sandbox.connect()`.\n */\n sandboxId?: string;\n /**\n * Reuse a saved Railway sandbox checkpoint as the baseline filesystem for\n * new sandboxes. When the named checkpoint is missing, the sandbox is created\n * normally and a checkpoint is captured after setup succeeds.\n */\n checkpointName?: string;\n /**\n * Fallback checkpoint used to seed a new sandbox when `checkpointName` has\n * no stored state. Snapshots continue writing to `checkpointName`.\n */\n seedCheckpointName?: string;\n /**\n * How long the sandbox can sit idle (no `exec` interaction) before Railway\n * destroys it automatically. Range depends on plan (1–120 minutes on\n * Hobby/Pro, 1–5 on Trial/Free). Defaults to the plan default when omitted.\n */\n idleTimeoutMinutes?: number;\n /**\n * Network isolation mode.\n * - `ISOLATED` (default): outbound internet only, no private network access.\n * - `PRIVATE`: joins the environment's private network.\n */\n networkIsolation?: SandboxNetworkIsolation;\n /** Environment variables baked into the sandbox, available to every command. */\n env?: Record<string, string>;\n /**\n * Provision the sandbox from a custom base image built with the Railway\n * template builder. Use this to pre-install packages or run setup steps so\n * every sandbox created from it starts ready.\n *\n * - Builder callback — receives the base `Sandbox.template()` and returns the\n * configured template. Railway builds the template when `Sandbox.create()`\n * runs during `start()`.\n * ```ts\n * template: t => t.withPackages('git', 'curl').run('npm i -g pnpm')\n * ```\n * - Pre-built `SandboxTemplate` — pass a template to reuse it across\n * sandboxes. Railway still builds the image during sandbox creation.\n *\n * Ignored when `sandboxId` is set (reattach) or when forking.\n */\n template?: SandboxTemplate | ((base: SandboxTemplate) => SandboxTemplate);\n /**\n * Default execution timeout in milliseconds applied to commands that don't\n * specify their own timeout. When omitted, commands run until they exit.\n */\n timeout?: number;\n /**\n * Custom instructions that override the default instructions returned by\n * `getInstructions()`.\n *\n * - `string` — Fully replaces the default instructions. Pass an empty string\n * to suppress instructions entirely.\n * - `(opts) => string` — Receives the default instructions and optional\n * request context so you can extend or customise per-request.\n */\n instructions?: InstructionsOption;\n}\n\n// =============================================================================\n// Railway Sandbox Implementation\n// =============================================================================\n\n/**\n * Railway sandbox provider for Mastra workspaces.\n *\n * Features:\n * - Ephemeral, isolated Linux VM via the Railway TypeScript SDK\n * - Command execution with streaming output and timeouts\n * - Configurable idle timeout and network isolation\n * - Reattach to an existing sandbox by Railway ID\n *\n * @example Basic usage\n * ```typescript\n * import { Workspace } from '@mastra/core/workspace';\n * import { RailwaySandbox } from '@mastra/railway';\n *\n * const sandbox = new RailwaySandbox({\n * // token + environmentId read from RAILWAY_API_TOKEN / RAILWAY_ENVIRONMENT_ID\n * idleTimeoutMinutes: 30,\n * });\n *\n * const workspace = new Workspace({ sandbox });\n * const result = await workspace.executeCode('console.log(\"Hello!\")');\n * ```\n *\n * @example Private networking\n * ```typescript\n * const sandbox = new RailwaySandbox({\n * networkIsolation: 'PRIVATE',\n * env: { NODE_ENV: 'production' },\n * });\n * ```\n */\nexport class RailwaySandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'RailwaySandbox';\n readonly provider = 'railway';\n status: ProviderStatus = 'pending';\n\n declare readonly processes: RailwayProcessManager;\n\n private _sandbox: Sandbox | null = null;\n private _createdAt: Date | null = null;\n private _checkpointRefreshTimer: ReturnType<typeof setTimeout> | null = null;\n private _checkpointRefreshInFlight: Promise<void> | null = null;\n private _sandboxId?: string;\n private _restoredCheckpointName?: string;\n private _startInFlight: Promise<void> | null = null;\n\n private readonly _token?: string;\n private readonly _environmentId?: string;\n private readonly _checkpointName?: string;\n private readonly _seedCheckpointName?: string;\n private readonly _idleTimeoutMinutes?: number;\n private readonly _networkIsolation?: SandboxNetworkIsolation;\n private readonly _env: Record<string, string>;\n private readonly _timeout?: number;\n private readonly _instructionsOverride?: InstructionsOption;\n private readonly _templateOption?: RailwaySandboxOptions['template'];\n\n constructor(options: RailwaySandboxOptions = {}) {\n super({\n ...options,\n name: 'RailwaySandbox',\n processes: new RailwayProcessManager({ env: options.env }),\n });\n\n this.id = options.id ?? this.generateId();\n this._token = options.token ?? process.env.RAILWAY_API_TOKEN;\n this._environmentId = options.environmentId ?? process.env.RAILWAY_ENVIRONMENT_ID;\n this._sandboxId = options.sandboxId;\n this._checkpointName = options.checkpointName;\n this._seedCheckpointName = options.seedCheckpointName;\n this._idleTimeoutMinutes = options.idleTimeoutMinutes;\n this._networkIsolation = options.networkIsolation;\n this._env = options.env ?? {};\n this._timeout = options.timeout;\n this._instructionsOverride = options.instructions;\n this._templateOption = options.template;\n }\n\n private generateId(): string {\n return `railway-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n /**\n * Get the underlying Railway Sandbox instance for direct SDK access.\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started.\n */\n get railway(): Sandbox {\n if (!this._sandbox) {\n throw new SandboxNotReadyError(this.id);\n }\n return this._sandbox;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /**\n * Start the Railway sandbox.\n *\n * Reattaches to an existing sandbox when `sandboxId` is configured,\n * otherwise provisions a new one. Resolves once the sandbox is RUNNING.\n */\n async start(): Promise<void> {\n if (this._sandbox) {\n return;\n }\n\n const clientConfig = this._clientConfig();\n const createOptions = this._createOptions(clientConfig);\n\n if (this._sandboxId) {\n const sandboxId = this._sandboxId;\n this._startInFlight ??= (async () => {\n this._restoredCheckpointName = undefined;\n try {\n this._sandbox = await this._reconnectSandbox(sandboxId, clientConfig);\n } catch (error) {\n if (!(error instanceof SandboxNotFoundError)) {\n throw error;\n }\n this._sandbox = await this._createNewSandbox(createOptions);\n }\n })().finally(() => {\n this._startInFlight = null;\n });\n } else {\n this._startInFlight ??= (async () => {\n this._sandbox = await this._createNewSandbox(createOptions);\n })().finally(() => {\n this._startInFlight = null;\n });\n }\n await this._startInFlight;\n\n if (!this._sandbox) {\n throw new Error('Failed to start Railway sandbox');\n }\n\n const sandbox = this._sandbox as Sandbox;\n this._sandboxId = sandbox.id;\n\n this._createdAt = sandbox.createdAt ? new Date(sandbox.createdAt) : new Date();\n this.logger.debug(`${LOG_PREFIX} Railway sandbox ${sandbox.id} ready for logical ID: ${this.id}`);\n this._scheduleCheckpointRefresh();\n }\n\n /**\n * Create a new Railway sandbox.\n */\n private async _createNewSandbox(createOptions: ReturnType<RailwaySandbox['_createOptions']>): Promise<Sandbox> {\n this.logger.debug(`${LOG_PREFIX} Creating Railway sandbox for: ${this.id}`);\n try {\n let checkpointToRestore: string | undefined;\n if (this._checkpointName || this._seedCheckpointName) {\n const checkpoints = await Sandbox.checkpoints(this._clientConfig());\n const checkpointNames = new Set(checkpoints.map(checkpoint => checkpoint.key));\n checkpointToRestore = checkpointNames.has(this._checkpointName ?? '')\n ? this._checkpointName\n : checkpointNames.has(this._seedCheckpointName ?? '')\n ? this._seedCheckpointName\n : undefined;\n }\n\n const sandbox = checkpointToRestore\n ? await Sandbox.create(checkpointToRestore, createOptions)\n : await Sandbox.create(createOptions);\n this._restoredCheckpointName = checkpointToRestore;\n return sandbox;\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * Reconnect to an existing Railway sandbox, creating a fresh one when\n */\n private async _reconnectSandbox(\n reconnectSandboxId: string,\n clientConfig: { token?: string; environmentId?: string },\n ): Promise<Sandbox> {\n this.logger.debug(`${LOG_PREFIX} Reconnecting to Railway sandbox ${reconnectSandboxId}...`);\n\n let connectedSandbox: Sandbox = await Sandbox.connect(reconnectSandboxId, clientConfig);\n\n if (connectedSandbox.status !== 'RUNNING') {\n throw new SandboxNotFoundError({\n id: reconnectSandboxId,\n environmentId: clientConfig.environmentId ?? '',\n });\n }\n\n return connectedSandbox;\n }\n\n private _clientConfig(): { token?: string; environmentId?: string } {\n return {\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n };\n }\n\n private _createOptions(clientConfig: { token?: string; environmentId?: string }) {\n return {\n ...clientConfig,\n ...(this._idleTimeoutMinutes !== undefined && { idleTimeoutMinutes: this._idleTimeoutMinutes }),\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n ...(Object.keys(this._env).length > 0 && { env: this._env }),\n };\n }\n\n /** Persist the configured recovery checkpoint when available. */\n async snapshot(): Promise<void> {\n await this.captureCheckpoint();\n }\n\n /** Snapshots persist real checkpoints that can seed future sandboxes. */\n readonly supportsCheckpoints: boolean = true;\n\n /**\n * Capture the sandbox's checkpoint on demand, outside the idle-timer schedule.\n *\n * Intended for callers (e.g. a factory-side scheduler) that want to refresh\n * the recovery checkpoint at semantic moments — turn end, session-idle,\n * pre-teardown — rather than only just before Railway's idle destroy.\n *\n * Coalesces with any in-flight timer-driven refresh: concurrent callers join\n * the same underlying `Sandbox.checkpoint` call and receive\n * `{ status: 'coalesced', checkpointName }`. Both `captured` and `coalesced`\n * carry the checkpoint name inline so callers can persist a session→\n * checkpoint binding without a second, non-atomic read against the sandbox.\n * Returns `{ status: 'skipped', reason }` when there's nothing to capture\n * (no `checkpointName` configured, or the sandbox isn't running yet).\n *\n * On successful capture, restarts the idle-timer countdown so the next\n * timer-driven refresh is scheduled from this capture.\n *\n * Never captures without a `checkpointName` and never mutates status — safe\n * to invoke concurrently with `executeCommand`, `restart`, or `stop`.\n */\n async captureCheckpoint(): Promise<CaptureCheckpointResult> {\n const checkpointName = this._checkpointName;\n if (!checkpointName) {\n return { status: 'skipped', reason: 'no-checkpoint-name-configured' };\n }\n\n const sandbox = this._sandbox;\n if (!sandbox) {\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n\n if (this._checkpointRefreshInFlight) {\n await this._checkpointRefreshInFlight;\n return { status: 'coalesced', checkpointName };\n }\n\n const capture = this._checkpointSandbox(sandbox).finally(() => {\n if (this._checkpointRefreshInFlight === capture) {\n this._checkpointRefreshInFlight = null;\n }\n });\n this._checkpointRefreshInFlight = capture;\n\n await capture;\n this._scheduleCheckpointRefresh();\n\n return { status: 'captured', checkpointName };\n }\n\n private async _checkpointSandbox(sandbox: Sandbox): Promise<void> {\n if (!this._checkpointName) {\n return;\n }\n\n this.logger.debug(`${LOG_PREFIX} Capturing Railway sandbox checkpoint ${this._checkpointName} for: ${this.id}`);\n\n const checkpoints = await Sandbox.checkpoints(this._clientConfig());\n const checkpointAlreadyExists = checkpoints.some(checkpoint => checkpoint.key === this._checkpointName);\n\n // Remove first so we can re-create a checkpoint with the same name.\n if (checkpointAlreadyExists) {\n await Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig());\n }\n\n await sandbox.checkpoint(this._checkpointName);\n }\n\n private _scheduleCheckpointRefresh(): void {\n if (!this._checkpointName || !this._sandbox) {\n return;\n }\n\n const idleTimeoutMinutes = this._idleTimeoutMinutes ?? this._sandbox.idleTimeoutMinutes;\n if (!idleTimeoutMinutes) {\n return;\n }\n\n this._cancelCheckpointRefresh();\n\n const delayMs = Math.max(1_000, idleTimeoutMinutes * 60_000 - CHECKPOINT_REFRESH_MARGIN_MS);\n this._checkpointRefreshTimer = setTimeout(() => {\n this._checkpointRefreshTimer = null;\n const sandbox = this._sandbox;\n if (!sandbox || this._checkpointRefreshInFlight) {\n return;\n }\n\n const refresh = this._checkpointSandbox(sandbox).finally(() => {\n if (this._checkpointRefreshInFlight === refresh) {\n this._checkpointRefreshInFlight = null;\n }\n });\n this._checkpointRefreshInFlight = refresh;\n refresh.catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to refresh Railway sandbox checkpoint ${this._checkpointName}:`, error);\n });\n }, delayMs);\n this._checkpointRefreshTimer.unref?.();\n }\n\n private _cancelCheckpointRefresh(): void {\n if (this._checkpointRefreshTimer) {\n clearTimeout(this._checkpointRefreshTimer);\n this._checkpointRefreshTimer = null;\n }\n }\n\n /**\n * Stop the Railway sandbox.\n *\n * Railway sandboxes have no separate \"stopped\" state — they're either\n * running or destroyed — so stopping destroys the sandbox but we keep a checkpoint of the filesystem.\n */\n async stop(): Promise<void> {\n if (this._checkpointName) {\n await this._flushCheckpointRefresh().catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to checkpoint Railway sandbox ${this._sandbox?.id}:`, error);\n });\n }\n\n await this._teardown();\n }\n\n /**\n * Destroy the Railway sandbox and release its resources including the checkpoint.\n */\n async destroy(): Promise<void> {\n this._cancelCheckpointRefresh();\n if (this._checkpointName) {\n await this._checkpointRefreshInFlight;\n await Sandbox.deleteCheckpoint(this._checkpointName, this._clientConfig()).catch(error => {\n this.logger.warn(`${LOG_PREFIX} Failed to delete Railway checkpoint ${this._checkpointName}:`, error);\n });\n }\n\n await this._teardown();\n }\n\n private async _flushCheckpointRefresh(): Promise<void> {\n this._cancelCheckpointRefresh();\n\n if (this._checkpointRefreshInFlight) {\n await this._checkpointRefreshInFlight;\n return;\n }\n\n if (this._sandbox) {\n await this._checkpointSandbox(this._sandbox);\n }\n }\n\n private async _teardown(): Promise<void> {\n this._cancelCheckpointRefresh();\n if (!this._sandbox) {\n return;\n }\n await this._checkpointRefreshInFlight;\n\n const sandbox = this._sandbox;\n this._sandbox = null;\n try {\n await sandbox.destroy();\n } catch (error) {\n this.logger.warn(`${LOG_PREFIX} Failed to destroy Railway sandbox ${sandbox.id}:`, error);\n }\n }\n\n /**\n * Fork this running sandbox into a new, independent `RailwaySandbox`.\n *\n * Clones the filesystem (a fresh boot, not live processes) into the same\n * environment. The returned sandbox is already started and reattached to the\n * forked Railway sandbox; it inherits this sandbox's credentials and defaults\n * unless overridden via `options`.\n *\n * @throws {SandboxNotReadyError} If this sandbox has not been started.\n */\n async fork(\n options: Pick<RailwaySandboxOptions, 'id' | 'idleTimeoutMinutes' | 'networkIsolation' | 'env'> = {},\n ): Promise<RailwaySandbox> {\n const source = this.railway;\n const forked = await source.fork({\n ...(options.idleTimeoutMinutes !== undefined && { idleTimeoutMinutes: options.idleTimeoutMinutes }),\n ...(options.networkIsolation !== undefined && { networkIsolation: options.networkIsolation }),\n ...(options.env !== undefined && { env: options.env }),\n });\n\n const child = new RailwaySandbox({\n ...(options.id !== undefined && { id: options.id }),\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n networkIsolation: options.networkIsolation ?? this._networkIsolation,\n env: options.env ?? this._env,\n timeout: this._timeout,\n });\n await child._start();\n return child;\n }\n\n /**\n * Construct a sibling `RailwaySandbox` that inherits this sandbox's\n * credentials and defaults (token, environment, checkpoint, network\n * isolation, timeout, template, instructions) with per-instance overrides.\n *\n * Unlike {@link fork}, `clone` performs no I/O and does not require this\n * sandbox to be started — the returned sandbox is not started and provisions\n * (or reattaches, when `sandboxId` is set) on its own `start()`. Use it when\n * one configured sandbox acts as the template for a fleet of independent\n * sandboxes (e.g. one per project).\n */\n clone(options: SandboxCloneOptions = {}): RailwaySandbox {\n return new RailwaySandbox({\n ...(options.id !== undefined && { id: options.id }),\n ...(this._token !== undefined && { token: this._token }),\n ...(this._environmentId !== undefined && { environmentId: this._environmentId }),\n ...((options.checkpointName ?? this._checkpointName) !== undefined && {\n checkpointName: options.checkpointName ?? this._checkpointName,\n }),\n ...(options.seedCheckpointName !== undefined && { seedCheckpointName: options.seedCheckpointName }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n env: options.env ?? this._env,\n ...(this._templateOption !== undefined && { template: this._templateOption }),\n ...(this._timeout !== undefined && { timeout: this._timeout }),\n ...(this._instructionsOverride !== undefined && { instructions: this._instructionsOverride }),\n });\n }\n\n /**\n * Whether a Railway API token was resolved at construction (explicit option\n * or the `RAILWAY_API_TOKEN` env fallback). Lets callers gate features on a\n * usable configuration without provisioning a sandbox.\n */\n get hasCredentials(): boolean {\n return this._token !== undefined && this._token !== '';\n }\n\n /** The configured idle teardown window in minutes, if any. */\n get idleTimeoutMinutes(): number | undefined {\n return this._idleTimeoutMinutes;\n }\n\n // ---------------------------------------------------------------------------\n // Info & Instructions\n // ---------------------------------------------------------------------------\n\n async getInfo(): Promise<SandboxInfo> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n ...(this._sandbox && {\n railwaySandboxId: this._sandbox.id,\n environmentId: this._sandbox.environmentId,\n region: this._sandbox.region,\n networkIsolation: this._sandbox.networkIsolation,\n ...(this._sandbox.idleTimeoutMinutes != null && {\n idleTimeoutMinutes: this._sandbox.idleTimeoutMinutes,\n }),\n ...(this._restoredCheckpointName && { restoredCheckpointName: this._restoredCheckpointName }),\n }),\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions = this._buildDefaultInstructions();\n\n if (typeof this._instructionsOverride === 'string') {\n return this._instructionsOverride;\n }\n if (typeof this._instructionsOverride === 'function') {\n return this._instructionsOverride({ defaultInstructions });\n }\n return defaultInstructions;\n }\n\n private _buildDefaultInstructions(): string {\n const parts: string[] = [];\n parts.push('Railway cloud sandbox: an isolated Debian Linux VM with outbound internet access.');\n\n if (this._networkIsolation === 'PRIVATE') {\n parts.push('Joined to the environment private network.');\n }\n\n if (this._timeout !== undefined) {\n parts.push(`Default command timeout: ${Math.ceil(this._timeout / 1000)}s.`);\n } else {\n parts.push('Commands run until they exit unless a timeout is set.');\n }\n\n if (this._idleTimeoutMinutes !== undefined) {\n parts.push(`Idle timeout: ${this._idleTimeoutMinutes} minute(s).`);\n }\n\n return parts.join(' ');\n }\n\n // ---------------------------------------------------------------------------\n // Command Execution\n // ---------------------------------------------------------------------------\n\n /**\n * Execute a command in the sandbox and return the result.\n */\n async executeCommand(\n command: string,\n args: string[] = [],\n options: ExecuteCommandOptions = {},\n ): Promise<CommandResult> {\n const status = this._sandbox?.status;\n\n if (status !== 'RUNNING') {\n if (!this._checkpointName) {\n throw new SandboxNotReadyError(this.id);\n }\n\n this._sandbox = null;\n await this.start();\n }\n\n const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(' ')}` : command;\n const timeout = options.timeout ?? this._timeout;\n const env = options.env\n ? Object.fromEntries(\n Object.entries(options.env).filter((entry): entry is [string, string] => entry[1] !== undefined),\n )\n : undefined;\n const startedAt = Date.now();\n const result = await this.railway.exec(fullCommand, {\n ...(timeout !== undefined && { timeoutSec: Math.ceil(timeout / 1000) }),\n ...(options.cwd !== undefined && { cwd: options.cwd }),\n ...(env !== undefined && { env }),\n });\n const exitCode = result.exitCode ?? -1;\n return {\n success: exitCode === 0,\n exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n executionTimeMs: Date.now() - startedAt,\n command,\n args,\n timedOut: result.timedOut,\n };\n }\n}\n","/**\n * Railway sandbox provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { railwaySandboxProvider } from '@mastra/railway';\n *\n * const editor = new MastraEditor({\n * sandboxes: { [railwaySandboxProvider.id]: railwaySandboxProvider },\n * });\n * ```\n */\nimport type { SandboxProvider } from '@mastra/core/editor';\nimport { RailwaySandbox } from './sandbox';\n\n/**\n * Serializable subset of RailwaySandboxOptions for editor storage.\n */\ninterface RailwayProviderConfig {\n token?: string;\n environmentId?: string;\n sandboxId?: string;\n idleTimeoutMinutes?: number;\n networkIsolation?: 'ISOLATED' | 'PRIVATE';\n env?: Record<string, string>;\n timeout?: number;\n}\n\nexport const railwaySandboxProvider: SandboxProvider<RailwayProviderConfig> = {\n id: 'railway',\n name: 'Railway Sandbox',\n description: 'Ephemeral, isolated Linux VM powered by Railway',\n configSchema: {\n type: 'object',\n properties: {\n token: { type: 'string', description: 'Railway API token (falls back to RAILWAY_API_TOKEN)' },\n environmentId: {\n type: 'string',\n description: 'Railway environment ID (falls back to RAILWAY_ENVIRONMENT_ID)',\n },\n sandboxId: { type: 'string', description: 'Reattach to an existing Railway sandbox by ID' },\n idleTimeoutMinutes: {\n type: 'number',\n description: 'Minutes a sandbox can sit idle before Railway destroys it',\n },\n networkIsolation: {\n type: 'string',\n description: 'Network isolation mode',\n enum: ['ISOLATED', 'PRIVATE'],\n default: 'ISOLATED',\n },\n env: {\n type: 'object',\n description: 'Environment variables',\n additionalProperties: { type: 'string' },\n },\n timeout: { type: 'number', description: 'Default command timeout in ms' },\n },\n },\n createSandbox: config => new RailwaySandbox(config),\n};\n"],"mappings":"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"}
@@ -52,6 +52,11 @@ export interface RailwaySandboxOptions extends Omit<MastraSandboxOptions, 'proce
52
52
  * normally and a checkpoint is captured after setup succeeds.
53
53
  */
54
54
  checkpointName?: string;
55
+ /**
56
+ * Fallback checkpoint used to seed a new sandbox when `checkpointName` has
57
+ * no stored state. Snapshots continue writing to `checkpointName`.
58
+ */
59
+ seedCheckpointName?: string;
55
60
  /**
56
61
  * How long the sandbox can sit idle (no `exec` interaction) before Railway
57
62
  * destroys it automatically. Range depends on plan (1–120 minutes on
@@ -141,10 +146,12 @@ export declare class RailwaySandbox extends MastraSandbox {
141
146
  private _checkpointRefreshTimer;
142
147
  private _checkpointRefreshInFlight;
143
148
  private _sandboxId?;
149
+ private _restoredCheckpointName?;
144
150
  private _startInFlight;
145
151
  private readonly _token?;
146
152
  private readonly _environmentId?;
147
153
  private readonly _checkpointName?;
154
+ private readonly _seedCheckpointName?;
148
155
  private readonly _idleTimeoutMinutes?;
149
156
  private readonly _networkIsolation?;
150
157
  private readonly _env;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/railway",
3
- "version": "0.6.0-alpha.1",
3
+ "version": "0.6.0",
4
4
  "description": "Railway cloud sandbox provider for Mastra workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,10 +31,10 @@
31
31
  "tsdown": "0.22.9",
32
32
  "typescript": "^6.0.3",
33
33
  "vitest": "4.1.10",
34
- "@internal/workspace-test-utils": "0.0.67",
35
- "@internal/types-builder": "0.0.98",
36
- "@internal/lint": "0.0.123",
37
- "@mastra/core": "1.60.0-alpha.13"
34
+ "@internal/lint": "0.0.124",
35
+ "@internal/types-builder": "0.0.99",
36
+ "@internal/workspace-test-utils": "0.0.68",
37
+ "@mastra/core": "1.60.0"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@mastra/core": ">=1.12.0-0 <2.0.0-0"