@mastra/railway 0.7.0-alpha.0 → 0.7.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,52 @@
1
1
  # @mastra/railway
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Honor the sandbox runtime environment (`setEnv()`/`getEnv()` from `@mastra/core`) in every workspace sandbox provider. Environment variables set after construction now reach subsequent commands on all providers: ([#22250](https://github.com/mastra-ai/mastra/pull/22250))
8
+
9
+ - Process-manager-routed providers (E2B, Blaxel, Cloudflare, Daytona, Docker, Modal, Vercel microVM spawns) inherit the merge from the core spawn wrapper; their duplicated per-manager env plumbing is removed.
10
+ - Providers with their own exec transports (AgentCore, Apple Container, Railway, Vercel microVM and serverless `executeCommand`, Platform's private-network, WebSocket lease, and E2B lease paths) now merge `getEnv()` under per-call env.
11
+
12
+ Constructor `env` continues to behave as before: it seeds the sandbox runtime environment, and providers that bake env into the VM or container at creation time (Docker, Modal, Railway, Apple Container, Vercel, Platform) still do so. Per-call `env` on `executeCommand` still takes precedence for that command only.
13
+
14
+ Removed exported types (minor bump for these two packages): `BlaxelProcessManagerOptions` from `@mastra/blaxel` and `RailwayProcessManagerOptions` from `@mastra/railway`. Both existed only to pass `env` into a process manager constructed by hand; the core spawn wrapper now owns that merge, so the option and its type are gone.
15
+
16
+ ### Patch Changes
17
+
18
+ - Starting a sandbox now reports whether it created a fresh sandbox or reconnected to an existing one, so an `onStart` handler can run first-time setup only when it's actually needed: ([#21984](https://github.com/mastra-ai/mastra/pull/21984))
19
+
20
+ ```typescript
21
+ new E2BSandbox({
22
+ id: 'session-1',
23
+ onStart: async ({ outcome }) => {
24
+ if (outcome === 'created') await cloneRepo();
25
+ },
26
+ });
27
+ ```
28
+
29
+ - Updated dependencies [[`79f04a7`](https://github.com/mastra-ai/mastra/commit/79f04a7f6c6829da541139f638f2f1d267916e08), [`65edab1`](https://github.com/mastra-ai/mastra/commit/65edab1c233d17b8f163bad12fca410d0e6f16b1), [`1e47b75`](https://github.com/mastra-ai/mastra/commit/1e47b7520cab4cfaa8daed52f17e2e6d14ff7539), [`ab20a38`](https://github.com/mastra-ai/mastra/commit/ab20a38d0275f8d85e0f3833bd87ef487bcc609f), [`fd4d5fe`](https://github.com/mastra-ai/mastra/commit/fd4d5fe4f943699b85db5e74404f190d5a6b8c2a), [`ae8790c`](https://github.com/mastra-ai/mastra/commit/ae8790c4bfaa088d2ab279d1dcc06f326b9fd109), [`2c85f42`](https://github.com/mastra-ai/mastra/commit/2c85f428e04ccd63ea31a7ec80b5b327afdad555), [`11bbeb9`](https://github.com/mastra-ai/mastra/commit/11bbeb9b108ef2264e05acefc6dafb9cbb342921), [`48ef1f1`](https://github.com/mastra-ai/mastra/commit/48ef1f1d24eedafbb07f64e659a81b52b67b8bf6), [`aa3a85d`](https://github.com/mastra-ai/mastra/commit/aa3a85daf094c683bb97efdf4b6a696d2e474af5), [`d29d06f`](https://github.com/mastra-ai/mastra/commit/d29d06fe00bbd35b4571150ea04c59d2ed783c71), [`e6516df`](https://github.com/mastra-ai/mastra/commit/e6516dfcdae4f4ac0e7971d84359a81385ee602f), [`1a485f3`](https://github.com/mastra-ai/mastra/commit/1a485f3538f5ec64d58bd8b5e1e99de0c695c87b), [`0d37487`](https://github.com/mastra-ai/mastra/commit/0d37487d9f349388a3f1cef6a536cf9dcc4b6273), [`8661d7d`](https://github.com/mastra-ai/mastra/commit/8661d7d7179f0a024456aabdd8679bcecd09ac28), [`dbbfeb8`](https://github.com/mastra-ai/mastra/commit/dbbfeb85ec949dc9ebc0755e1ad262e4f5eba8db), [`575e343`](https://github.com/mastra-ai/mastra/commit/575e343900451021d96110916497d334af7bc252), [`0b2a3d1`](https://github.com/mastra-ai/mastra/commit/0b2a3d1783875c5b97b7b36ab3d03d7360e0dde7), [`6bb5d71`](https://github.com/mastra-ai/mastra/commit/6bb5d7193fe9166b219f0fccae17db7a5ae86e65), [`3cc9d00`](https://github.com/mastra-ai/mastra/commit/3cc9d00b2b4333e0377a5e9df5eff92c17ce7630), [`cacb839`](https://github.com/mastra-ai/mastra/commit/cacb8392d9e74189b56d857290b0615f98a2683d), [`57de7d6`](https://github.com/mastra-ai/mastra/commit/57de7d644ba7146edb4e9e6111ec4fa98c3a59e9), [`c8e4cea`](https://github.com/mastra-ai/mastra/commit/c8e4ceac9a390d78c8327dff3cdb2861dd71957f), [`ed01e9a`](https://github.com/mastra-ai/mastra/commit/ed01e9a807514a904374bf687a7b8f18750f6f78), [`b47b26e`](https://github.com/mastra-ai/mastra/commit/b47b26e6fe95cb8a3482be2c5e52de157fe59d0b), [`0d37487`](https://github.com/mastra-ai/mastra/commit/0d37487d9f349388a3f1cef6a536cf9dcc4b6273), [`733a537`](https://github.com/mastra-ai/mastra/commit/733a537489a858b5880b2e98809334fba895a221), [`e8e299c`](https://github.com/mastra-ai/mastra/commit/e8e299cc6abdfc39947e2fec25803493015d3882), [`edfc548`](https://github.com/mastra-ai/mastra/commit/edfc548886bc7bae17b681f8b6b41a47eb32bcd2), [`b05f486`](https://github.com/mastra-ai/mastra/commit/b05f48612984d5fe2447ea2d6cdd5c604d285b97), [`a8a4871`](https://github.com/mastra-ai/mastra/commit/a8a4871215f51da95c47129602157ce5372f634a), [`eb9ecaa`](https://github.com/mastra-ai/mastra/commit/eb9ecaa89c36e889749e3b825cfc507ce7f7980b), [`4ff3ee2`](https://github.com/mastra-ai/mastra/commit/4ff3ee2bff7ed07528b4817f8f49639031c72a4d), [`9207dfa`](https://github.com/mastra-ai/mastra/commit/9207dfab8062e5fc68b751684797ff86fe0b4e70), [`5165cdc`](https://github.com/mastra-ai/mastra/commit/5165cdcdcf50e144bb8113278535196cc9b07065), [`e737014`](https://github.com/mastra-ai/mastra/commit/e737014e0fc7035759762bb5b48baef1d6c0f6a7), [`6bb5d71`](https://github.com/mastra-ai/mastra/commit/6bb5d7193fe9166b219f0fccae17db7a5ae86e65), [`f591643`](https://github.com/mastra-ai/mastra/commit/f591643becdf0be9bddce6ba1748e64bc30d77f1), [`63796ba`](https://github.com/mastra-ai/mastra/commit/63796ba0fda60253be17535e68f6bbbf1e6ffa09), [`b1ad324`](https://github.com/mastra-ai/mastra/commit/b1ad324d657f3544b0701332aef7eb10e9a36258), [`61c566d`](https://github.com/mastra-ai/mastra/commit/61c566dd2f2cde2b23ed8f139924e530d4202214), [`c24754c`](https://github.com/mastra-ai/mastra/commit/c24754c1fb6fe144e5051e536e98c8a18b0214ac), [`12c61d2`](https://github.com/mastra-ai/mastra/commit/12c61d280c8cb208bc3c8dbcbe5dcc60cf9d1cd0), [`c46eb09`](https://github.com/mastra-ai/mastra/commit/c46eb09ce4987509af57a0ac582c61241a6dd2f1), [`9ee8120`](https://github.com/mastra-ai/mastra/commit/9ee8120ce17f76b9f617489e05a283353742690a), [`d975e92`](https://github.com/mastra-ai/mastra/commit/d975e924d4936f46c386bd3dee39c671720289f6), [`45dd6ee`](https://github.com/mastra-ai/mastra/commit/45dd6ee089bd7df0d0c98a10098e483fd388e04a), [`4e9a228`](https://github.com/mastra-ai/mastra/commit/4e9a2283d5fd6ed1b70a2751eb3dc2cbf82ada20), [`d6ce34a`](https://github.com/mastra-ai/mastra/commit/d6ce34aeceb06ddf3d595a1eed5cc74f481a46a1), [`f95f468`](https://github.com/mastra-ai/mastra/commit/f95f468cf1e7c2b924a13826494f98b8f2ccd581), [`30ed33e`](https://github.com/mastra-ai/mastra/commit/30ed33ee14084a26019aba15fceadda6d6ddefaf), [`04a815f`](https://github.com/mastra-ai/mastra/commit/04a815fc8971d29e97fcdcc5008a1eb472fc00ff), [`1cfa878`](https://github.com/mastra-ai/mastra/commit/1cfa8784d8da0dfaa0317e5048bc48b6084a5ea5), [`9a12ef3`](https://github.com/mastra-ai/mastra/commit/9a12ef3fccf3f4186db0f294f4ee1f02cf4d8db2), [`32d3583`](https://github.com/mastra-ai/mastra/commit/32d358332cb8ac2306b83b73cf3536e74dbd435e), [`7960688`](https://github.com/mastra-ai/mastra/commit/7960688828e04eaf3106e34f7758fa580257eef6), [`91ad69d`](https://github.com/mastra-ai/mastra/commit/91ad69d64994c89199b0c55399e64ed91c61df2f), [`8dc408d`](https://github.com/mastra-ai/mastra/commit/8dc408d34438f9e13297f792c11a5cfd6cf952e1), [`c92def1`](https://github.com/mastra-ai/mastra/commit/c92def10a13c822972c96f0a4ca6ffc1f4258aed), [`63041eb`](https://github.com/mastra-ai/mastra/commit/63041eb4c50b520a0a80e03d4cd6ea99f67715a0), [`c118318`](https://github.com/mastra-ai/mastra/commit/c1183181c9804303db4b511c2e2648f8b714712b), [`c5eaec5`](https://github.com/mastra-ai/mastra/commit/c5eaec5a860d80d0e3805e67db0414b87ac8cbed), [`fc07c64`](https://github.com/mastra-ai/mastra/commit/fc07c6465043e08e99193a6751a01c56ffc2e7a1), [`cced745`](https://github.com/mastra-ai/mastra/commit/cced745a056ec2225c5bc702e32d848847aa8b65), [`542dee2`](https://github.com/mastra-ai/mastra/commit/542dee254167f974ff8cbbbfc0ce10f9a2616a7b), [`3c19dce`](https://github.com/mastra-ai/mastra/commit/3c19dcef8e73062a80627a4927eae3ec11145afd), [`aca2869`](https://github.com/mastra-ai/mastra/commit/aca2869b2031982f3c4a2f52525c9be7cf123ef8), [`a58483c`](https://github.com/mastra-ai/mastra/commit/a58483cff1a9d41fce7c931843f48cb0ac450f64), [`a58483c`](https://github.com/mastra-ai/mastra/commit/a58483cff1a9d41fce7c931843f48cb0ac450f64), [`e6f8450`](https://github.com/mastra-ai/mastra/commit/e6f845074d478527026b18d85031b23353e1d0a4), [`895e9df`](https://github.com/mastra-ai/mastra/commit/895e9dfc17d6f34299eca64e317ded9e5f5e5ef8), [`e66b2ba`](https://github.com/mastra-ai/mastra/commit/e66b2ba100db63eaeab6e21e1ea34b113f2ec781), [`3e8727e`](https://github.com/mastra-ai/mastra/commit/3e8727e11ec1a5d733acedb5c872896394be18c1)]:
30
+ - @mastra/core@1.62.0
31
+
32
+ ## 0.7.0-alpha.1
33
+
34
+ ### Patch Changes
35
+
36
+ - Starting a sandbox now reports whether it created a fresh sandbox or reconnected to an existing one, so an `onStart` handler can run first-time setup only when it's actually needed: ([#21984](https://github.com/mastra-ai/mastra/pull/21984))
37
+
38
+ ```typescript
39
+ new E2BSandbox({
40
+ id: 'session-1',
41
+ onStart: async ({ outcome }) => {
42
+ if (outcome === 'created') await cloneRepo();
43
+ },
44
+ });
45
+ ```
46
+
47
+ - Updated dependencies [[`4ff3ee2`](https://github.com/mastra-ai/mastra/commit/4ff3ee2bff7ed07528b4817f8f49639031c72a4d), [`c24754c`](https://github.com/mastra-ai/mastra/commit/c24754c1fb6fe144e5051e536e98c8a18b0214ac), [`45dd6ee`](https://github.com/mastra-ai/mastra/commit/45dd6ee089bd7df0d0c98a10098e483fd388e04a), [`32d3583`](https://github.com/mastra-ai/mastra/commit/32d358332cb8ac2306b83b73cf3536e74dbd435e), [`aca2869`](https://github.com/mastra-ai/mastra/commit/aca2869b2031982f3c4a2f52525c9be7cf123ef8)]:
48
+ - @mastra/core@1.62.0-alpha.11
49
+
3
50
  ## 0.7.0-alpha.0
4
51
 
5
52
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -193,7 +193,6 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
193
193
  _checkpointRefreshInFlight = null;
194
194
  _sandboxId;
195
195
  _restoredCheckpointName;
196
- _startInFlight = null;
197
196
  _token;
198
197
  _environmentId;
199
198
  _checkpointName;
@@ -240,36 +239,37 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
240
239
  *
241
240
  * Reattaches to an existing sandbox when `sandboxId` is configured,
242
241
  * otherwise provisions a new one. Resolves once the sandbox is RUNNING.
242
+ *
243
+ * Concurrent-caller coalescing lives in the `MastraSandbox` base class
244
+ * (constructor-wrapped `start()`); a failed attempt is never latched.
245
+ *
246
+ * Reports `outcome: 'connected'` on reattach and `outcome: 'created'` when a new
247
+ * sandbox was provisioned (including checkpoint-seeded fresh VMs).
243
248
  */
244
249
  async start() {
245
- if (this._sandbox) return;
250
+ if (this._sandbox) return { outcome: "connected" };
246
251
  const clientConfig = this._clientConfig();
247
252
  const createOptions = this._createOptions(clientConfig);
253
+ let outcome = "connected";
248
254
  if (this._sandboxId) {
249
- const sandboxId = this._sandboxId;
250
- this._startInFlight ??= (async () => {
251
- this._restoredCheckpointName = void 0;
252
- try {
253
- this._sandbox = await this._reconnectSandbox(sandboxId, clientConfig);
254
- } catch (error) {
255
- if (!(error instanceof railway.SandboxNotFoundError)) throw error;
256
- this._sandbox = await this._createNewSandbox(createOptions);
257
- }
258
- })().finally(() => {
259
- this._startInFlight = null;
260
- });
261
- } else this._startInFlight ??= (async () => {
255
+ this._restoredCheckpointName = void 0;
256
+ try {
257
+ this._sandbox = await this._reconnectSandbox(this._sandboxId, clientConfig);
258
+ } catch (error) {
259
+ if (!(error instanceof railway.SandboxNotFoundError)) throw error;
260
+ this._sandbox = await this._createNewSandbox(createOptions);
261
+ outcome = "created";
262
+ }
263
+ } else {
262
264
  this._sandbox = await this._createNewSandbox(createOptions);
263
- })().finally(() => {
264
- this._startInFlight = null;
265
- });
266
- await this._startInFlight;
267
- if (!this._sandbox) throw new Error("Failed to start Railway sandbox");
265
+ outcome = "created";
266
+ }
268
267
  const sandbox = this._sandbox;
269
268
  this._sandboxId = sandbox.id;
270
269
  this._createdAt = sandbox.createdAt ? new Date(sandbox.createdAt) : /* @__PURE__ */ new Date();
271
270
  this.logger.debug(`${LOG_PREFIX} Railway sandbox ${sandbox.id} ready for logical ID: ${this.id}`);
272
271
  this._scheduleCheckpointRefresh();
272
+ return { outcome };
273
273
  }
274
274
  /**
275
275
  * Create a new Railway sandbox.
@@ -553,8 +553,14 @@ var RailwaySandbox = class RailwaySandbox extends _mastra_core_workspace.MastraS
553
553
  async executeCommand(command, args = [], options = {}) {
554
554
  if (this._sandbox?.status !== "RUNNING") {
555
555
  if (!this._checkpointName) throw new _mastra_core_workspace.SandboxNotReadyError(this.id);
556
- this._sandbox = null;
557
- await this.start();
556
+ if (this._startPromise) {
557
+ if (this.status === "running") throw new _mastra_core_workspace.SandboxNotReadyError(this.id);
558
+ await this.start();
559
+ } else {
560
+ this._sandbox = null;
561
+ this.status = "stopped";
562
+ await this.start();
563
+ }
558
564
  }
559
565
  const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(" ")}` : command;
560
566
  const timeout = options.timeout ?? this._timeout;
@@ -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\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 async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const railway = this.sandbox.railway;\n\n // The base spawn wrapper already merged the sandbox env into options.env\n const mergedEnv = { ...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(),\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 mergedEnv = { ...this.getEnv(), ...options.env };\n const env = Object.keys(mergedEnv).length\n ? Object.fromEntries(\n Object.entries(mergedEnv).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\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 async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const railway = this.sandbox.railway;\n\n // The base spawn wrapper already merged the sandbox env into options.env\n const mergedEnv = { ...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 SandboxStartOutcome,\n SandboxStartResult,\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\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(),\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 * Concurrent-caller coalescing lives in the `MastraSandbox` base class\n * (constructor-wrapped `start()`); a failed attempt is never latched.\n *\n * Reports `outcome: 'connected'` on reattach and `outcome: 'created'` when a new\n * sandbox was provisioned (including checkpoint-seeded fresh VMs).\n */\n async start(): Promise<SandboxStartResult> {\n if (this._sandbox) {\n return { outcome: 'connected' };\n }\n\n const clientConfig = this._clientConfig();\n const createOptions = this._createOptions(clientConfig);\n\n let outcome: SandboxStartOutcome = 'connected';\n if (this._sandboxId) {\n this._restoredCheckpointName = undefined;\n try {\n this._sandbox = await this._reconnectSandbox(this._sandboxId, clientConfig);\n } catch (error) {\n if (!(error instanceof SandboxNotFoundError)) {\n throw error;\n }\n this._sandbox = await this._createNewSandbox(createOptions);\n outcome = 'created';\n }\n } else {\n this._sandbox = await this._createNewSandbox(createOptions);\n outcome = 'created';\n }\n\n const sandbox = this._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 return { outcome };\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 // TODO: this reconstructs \"am I inside the current start?\" from base\n // internals because status flips to 'running' before the onStart hook.\n // If the base ever gates commands on start re-entrancy directly, delete\n // this branch and let it decide.\n if (this._startPromise) {\n if (this.status === 'running') {\n // status flips to 'running' before the base class runs the\n // bootstrap/onStart phase of the CURRENT start attempt — an\n // executeCommand arriving here is running inside that attempt, and\n // joining the in-flight promise would await itself forever. Fail\n // loudly instead of hanging.\n throw new SandboxNotReadyError(this.id);\n }\n // Concurrent with someone else's in-flight start — join it.\n await this.start();\n } else {\n // The VM is provably down — reset local state so the base-class start\n // wrapper (which early-returns while status is 'running') actually\n // re-runs the reconnect/provision logic.\n this._sandbox = null;\n this.status = 'stopped';\n await this.start();\n }\n }\n\n const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(' ')}` : command;\n const timeout = options.timeout ?? this._timeout;\n const mergedEnv = { ...this.getEnv(), ...options.env };\n const env = Object.keys(mergedEnv).length\n ? Object.fromEntries(\n Object.entries(mergedEnv).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
@@ -192,7 +192,6 @@ var RailwaySandbox = class RailwaySandbox extends MastraSandbox {
192
192
  _checkpointRefreshInFlight = null;
193
193
  _sandboxId;
194
194
  _restoredCheckpointName;
195
- _startInFlight = null;
196
195
  _token;
197
196
  _environmentId;
198
197
  _checkpointName;
@@ -239,36 +238,37 @@ var RailwaySandbox = class RailwaySandbox extends MastraSandbox {
239
238
  *
240
239
  * Reattaches to an existing sandbox when `sandboxId` is configured,
241
240
  * otherwise provisions a new one. Resolves once the sandbox is RUNNING.
241
+ *
242
+ * Concurrent-caller coalescing lives in the `MastraSandbox` base class
243
+ * (constructor-wrapped `start()`); a failed attempt is never latched.
244
+ *
245
+ * Reports `outcome: 'connected'` on reattach and `outcome: 'created'` when a new
246
+ * sandbox was provisioned (including checkpoint-seeded fresh VMs).
242
247
  */
243
248
  async start() {
244
- if (this._sandbox) return;
249
+ if (this._sandbox) return { outcome: "connected" };
245
250
  const clientConfig = this._clientConfig();
246
251
  const createOptions = this._createOptions(clientConfig);
252
+ let outcome = "connected";
247
253
  if (this._sandboxId) {
248
- const sandboxId = this._sandboxId;
249
- this._startInFlight ??= (async () => {
250
- this._restoredCheckpointName = void 0;
251
- try {
252
- this._sandbox = await this._reconnectSandbox(sandboxId, clientConfig);
253
- } catch (error) {
254
- if (!(error instanceof SandboxNotFoundError)) throw error;
255
- this._sandbox = await this._createNewSandbox(createOptions);
256
- }
257
- })().finally(() => {
258
- this._startInFlight = null;
259
- });
260
- } else this._startInFlight ??= (async () => {
254
+ this._restoredCheckpointName = void 0;
255
+ try {
256
+ this._sandbox = await this._reconnectSandbox(this._sandboxId, clientConfig);
257
+ } catch (error) {
258
+ if (!(error instanceof SandboxNotFoundError)) throw error;
259
+ this._sandbox = await this._createNewSandbox(createOptions);
260
+ outcome = "created";
261
+ }
262
+ } else {
261
263
  this._sandbox = await this._createNewSandbox(createOptions);
262
- })().finally(() => {
263
- this._startInFlight = null;
264
- });
265
- await this._startInFlight;
266
- if (!this._sandbox) throw new Error("Failed to start Railway sandbox");
264
+ outcome = "created";
265
+ }
267
266
  const sandbox = this._sandbox;
268
267
  this._sandboxId = sandbox.id;
269
268
  this._createdAt = sandbox.createdAt ? new Date(sandbox.createdAt) : /* @__PURE__ */ new Date();
270
269
  this.logger.debug(`${LOG_PREFIX} Railway sandbox ${sandbox.id} ready for logical ID: ${this.id}`);
271
270
  this._scheduleCheckpointRefresh();
271
+ return { outcome };
272
272
  }
273
273
  /**
274
274
  * Create a new Railway sandbox.
@@ -552,8 +552,14 @@ var RailwaySandbox = class RailwaySandbox extends MastraSandbox {
552
552
  async executeCommand(command, args = [], options = {}) {
553
553
  if (this._sandbox?.status !== "RUNNING") {
554
554
  if (!this._checkpointName) throw new SandboxNotReadyError(this.id);
555
- this._sandbox = null;
556
- await this.start();
555
+ if (this._startPromise) {
556
+ if (this.status === "running") throw new SandboxNotReadyError(this.id);
557
+ await this.start();
558
+ } else {
559
+ this._sandbox = null;
560
+ this.status = "stopped";
561
+ await this.start();
562
+ }
557
563
  }
558
564
  const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(" ")}` : command;
559
565
  const timeout = options.timeout ?? this._timeout;
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\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 async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const railway = this.sandbox.railway;\n\n // The base spawn wrapper already merged the sandbox env into options.env\n const mergedEnv = { ...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(),\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 mergedEnv = { ...this.getEnv(), ...options.env };\n const env = Object.keys(mergedEnv).length\n ? Object.fromEntries(\n Object.entries(mergedEnv).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\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 async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const railway = this.sandbox.railway;\n\n // The base spawn wrapper already merged the sandbox env into options.env\n const mergedEnv = { ...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 SandboxStartOutcome,\n SandboxStartResult,\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\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(),\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 * Concurrent-caller coalescing lives in the `MastraSandbox` base class\n * (constructor-wrapped `start()`); a failed attempt is never latched.\n *\n * Reports `outcome: 'connected'` on reattach and `outcome: 'created'` when a new\n * sandbox was provisioned (including checkpoint-seeded fresh VMs).\n */\n async start(): Promise<SandboxStartResult> {\n if (this._sandbox) {\n return { outcome: 'connected' };\n }\n\n const clientConfig = this._clientConfig();\n const createOptions = this._createOptions(clientConfig);\n\n let outcome: SandboxStartOutcome = 'connected';\n if (this._sandboxId) {\n this._restoredCheckpointName = undefined;\n try {\n this._sandbox = await this._reconnectSandbox(this._sandboxId, clientConfig);\n } catch (error) {\n if (!(error instanceof SandboxNotFoundError)) {\n throw error;\n }\n this._sandbox = await this._createNewSandbox(createOptions);\n outcome = 'created';\n }\n } else {\n this._sandbox = await this._createNewSandbox(createOptions);\n outcome = 'created';\n }\n\n const sandbox = this._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 return { outcome };\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 // TODO: this reconstructs \"am I inside the current start?\" from base\n // internals because status flips to 'running' before the onStart hook.\n // If the base ever gates commands on start re-entrancy directly, delete\n // this branch and let it decide.\n if (this._startPromise) {\n if (this.status === 'running') {\n // status flips to 'running' before the base class runs the\n // bootstrap/onStart phase of the CURRENT start attempt — an\n // executeCommand arriving here is running inside that attempt, and\n // joining the in-flight promise would await itself forever. Fail\n // loudly instead of hanging.\n throw new SandboxNotReadyError(this.id);\n }\n // Concurrent with someone else's in-flight start — join it.\n await this.start();\n } else {\n // The VM is provably down — reset local state so the base-class start\n // wrapper (which early-returns while status is 'running') actually\n // re-runs the reconnect/provision logic.\n this._sandbox = null;\n this.status = 'stopped';\n await this.start();\n }\n }\n\n const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(' ')}` : command;\n const timeout = options.timeout ?? this._timeout;\n const mergedEnv = { ...this.getEnv(), ...options.env };\n const env = Object.keys(mergedEnv).length\n ? Object.fromEntries(\n Object.entries(mergedEnv).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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"}
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @see https://docs.railway.com/sandboxes
9
9
  */
10
- import type { CommandResult, ExecuteCommandOptions, InstructionsOption, MastraSandboxOptions, ProviderStatus, SandboxCloneOptions, SandboxInfo } from '@mastra/core/workspace';
10
+ import type { CommandResult, ExecuteCommandOptions, InstructionsOption, MastraSandboxOptions, ProviderStatus, SandboxCloneOptions, SandboxInfo, SandboxStartResult } from '@mastra/core/workspace';
11
11
  import { MastraSandbox } from '@mastra/core/workspace';
12
12
  import { Sandbox } from 'railway';
13
13
  import type { SandboxNetworkIsolation, SandboxTemplate } from 'railway';
@@ -147,7 +147,6 @@ export declare class RailwaySandbox extends MastraSandbox {
147
147
  private _checkpointRefreshInFlight;
148
148
  private _sandboxId?;
149
149
  private _restoredCheckpointName?;
150
- private _startInFlight;
151
150
  private readonly _token?;
152
151
  private readonly _environmentId?;
153
152
  private readonly _checkpointName?;
@@ -171,8 +170,14 @@ export declare class RailwaySandbox extends MastraSandbox {
171
170
  *
172
171
  * Reattaches to an existing sandbox when `sandboxId` is configured,
173
172
  * otherwise provisions a new one. Resolves once the sandbox is RUNNING.
173
+ *
174
+ * Concurrent-caller coalescing lives in the `MastraSandbox` base class
175
+ * (constructor-wrapped `start()`); a failed attempt is never latched.
176
+ *
177
+ * Reports `outcome: 'connected'` on reattach and `outcome: 'created'` when a new
178
+ * sandbox was provisioned (including checkpoint-seeded fresh VMs).
174
179
  */
175
- start(): Promise<void>;
180
+ start(): Promise<SandboxStartResult>;
176
181
  /**
177
182
  * Create a new Railway sandbox.
178
183
  */
@@ -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.7.0-alpha.0",
3
+ "version": "0.7.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/types-builder": "0.0.100",
35
- "@internal/lint": "0.0.125",
36
- "@internal/workspace-test-utils": "0.0.69",
37
- "@mastra/core": "1.62.0-alpha.8"
34
+ "@internal/lint": "0.0.126",
35
+ "@internal/types-builder": "0.0.101",
36
+ "@internal/workspace-test-utils": "0.0.70",
37
+ "@mastra/core": "1.62.0"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@mastra/core": ">=1.12.0-0 <2.0.0-0"