@mastra/platform-workspace 1.2.0 → 1.2.1
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 +36 -0
- package/README.md +2 -1
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/sandbox.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#map"],"sources":["../src/client.ts","../src/filesystem.ts","../src/direct-exec.ts","../src/private-net-exec.ts","../src/sandbox.ts","../src/provider.ts","../src/address-registry.ts"],"sourcesContent":["export interface PlatformClientOptions {\n accessToken?: string;\n projectId?: string;\n /**\n * Advisory correlation id for the factory session driving this client.\n * Sent as `x-mastra-session-id` on every proxy request so proxy-side logs\n * can be joined back to the calling session without a multi-store hand-join\n * (`threadId → sessionId → sandboxId → providerResourceId`). Never used for\n * authorization — the Bearer token remains the only credential.\n */\n sessionId?: string;\n /**\n * Advisory correlation id for the factory thread, sent as\n * `x-mastra-thread-id` when present. See {@link PlatformClientOptions.sessionId}.\n */\n threadId?: string;\n fetch?: typeof fetch;\n}\n\nexport interface PlatformRequestOptions extends RequestInit {\n query?: Record<string, string | number | boolean | undefined>;\n}\n\nconst DEFAULT_PROXY_URL = 'https://workspaces.mastra.ai';\n\n/**\n * Default per-request timeout for calls to the workspace proxy. Applied only\n * when the caller doesn't already pass an `AbortSignal`. Long-running routes\n * (e.g. `POST /sandbox/:id/exec`) pass their own longer signal.\n */\nconst DEFAULT_REQUEST_TIMEOUT_MS = 60_000;\n\nexport function requireOption(value: string | undefined, name: string): string {\n if (!value) throw new Error(`${name} is required`);\n return value;\n}\n\nexport function resolvePlatformOptions(options: PlatformClientOptions) {\n return {\n accessToken: requireOption(options.accessToken ?? process.env.MASTRA_PLATFORM_ACCESS_TOKEN, 'accessToken'),\n projectId: requireOption(options.projectId ?? process.env.MASTRA_PROJECT_ID, 'projectId'),\n proxyUrl: (process.env.MASTRA_WORKSPACE_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\\/$/, ''),\n sessionId: options.sessionId,\n threadId: options.threadId,\n fetch: options.fetch ?? fetch,\n };\n}\n\n/**\n * Structured error shape returned by the workspace proxy. All routes emit\n * `{ error: { message, type } }` on failure — see servers/workspace-proxy in\n * the Platform repo. Kept as a wire-level type so callers can switch on\n * `error.code` without re-parsing `error.body`.\n */\nexport interface PlatformProxyError {\n message: string;\n /** Machine-readable error kind, e.g. `not_found`, `invalid_request`, `authentication_error`. */\n type: string;\n}\n\nfunction parseProxyError(body: string): PlatformProxyError | undefined {\n if (!body) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return undefined;\n }\n if (typeof parsed !== 'object' || parsed === null) return undefined;\n const err = (parsed as { error?: unknown }).error;\n if (typeof err !== 'object' || err === null) return undefined;\n const { message, type } = err as { message?: unknown; type?: unknown };\n if (typeof message !== 'string' || typeof type !== 'string') return undefined;\n return { message, type };\n}\n\nexport class PlatformApiError extends Error {\n readonly status: number;\n readonly body: string;\n /** Machine-readable proxy error kind (e.g. `not_found`), when the response body matches `{ error: { message, type } }`. */\n readonly code: string | undefined;\n /** Human-readable proxy error message, when the response body matches `{ error: { message, type } }`. */\n readonly proxyMessage: string | undefined;\n\n constructor(status: number, body: string) {\n const parsed = parseProxyError(body);\n const summary = parsed ? `${parsed.type}: ${parsed.message}` : body;\n super(`Platform proxy request failed with ${status}${summary ? `: ${summary}` : ''}`);\n this.name = 'PlatformApiError';\n this.status = status;\n this.body = body;\n this.code = parsed?.type;\n this.proxyMessage = parsed?.message;\n }\n}\n\nexport class PlatformClient {\n readonly accessToken: string;\n readonly projectId: string;\n readonly proxyUrl: string;\n /** Advisory session correlation id — see {@link PlatformClientOptions.sessionId}. */\n readonly sessionId: string | undefined;\n /** Advisory thread correlation id — see {@link PlatformClientOptions.threadId}. */\n readonly threadId: string | undefined;\n readonly fetch: typeof fetch;\n\n constructor(options: PlatformClientOptions) {\n const resolved = resolvePlatformOptions(options);\n this.accessToken = resolved.accessToken;\n this.projectId = resolved.projectId;\n this.proxyUrl = resolved.proxyUrl;\n this.sessionId = resolved.sessionId;\n this.threadId = resolved.threadId;\n this.fetch = resolved.fetch;\n }\n\n async request(path: string, options: PlatformRequestOptions = {}): Promise<Response> {\n const url = new URL(`${this.proxyUrl}/v1/projects/${encodeURIComponent(this.projectId)}${path}`);\n for (const [key, value] of Object.entries(options.query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n\n const headers = new Headers(options.headers);\n headers.set('authorization', `Bearer ${this.accessToken}`);\n // Advisory correlation headers — the proxy folds them into its log lines\n // so proxy-side events can be joined back to the calling factory session\n // without a cross-store hand-join. Unknown headers are passthrough for\n // older proxies; these are never used for authorization.\n if (this.sessionId) headers.set('x-mastra-session-id', this.sessionId);\n if (this.threadId) headers.set('x-mastra-thread-id', this.threadId);\n\n // Strip our helper-only field so the underlying fetch sees a valid RequestInit.\n const { query: _query, ...fetchOptions } = options;\n // Apply a default timeout only when the caller didn't already supply an\n // AbortSignal — long-running routes (exec) provide their own longer signal.\n const signal = fetchOptions.signal ?? AbortSignal.timeout(DEFAULT_REQUEST_TIMEOUT_MS);\n const response = await this.fetch(url, { ...fetchOptions, headers, signal });\n if (!response.ok) {\n throw new PlatformApiError(response.status, await response.text());\n }\n return response;\n }\n}\n","import { Buffer } from 'node:buffer';\nimport nodePath from 'node:path';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type {\n CopyOptions,\n FileContent,\n FileEntry,\n FileStat,\n FilesystemIcon,\n FilesystemInfo,\n InstructionsOption,\n ListOptions,\n MastraFilesystemOptions,\n ProviderStatus,\n ReadOptions,\n RemoveOptions,\n WriteOptions,\n} from '@mastra/core/workspace';\nimport { FileExistsError, FileNotFoundError, MastraFilesystem, WorkspaceReadOnlyError } from '@mastra/core/workspace';\nimport type { PlatformClientOptions } from './client.js';\nimport { PlatformClient } from './client.js';\n\ninterface ProxyListResponse {\n contents?: Array<{ key?: string; size?: number; lastModified?: string }>;\n commonPrefixes?: string[];\n}\n\nexport interface PlatformFilesystemOptions extends PlatformClientOptions, MastraFilesystemOptions {\n id?: string;\n bucketName?: string;\n readOnly?: boolean;\n displayName?: string;\n icon?: FilesystemIcon;\n description?: string;\n instructions?: InstructionsOption;\n}\n\nfunction normalizePath(input: string): string {\n if (!input || input === '.') return '/';\n let normalized = input.startsWith('/') ? input : `/${input}`;\n normalized = nodePath.posix.normalize(normalized);\n return normalized === '.' ? '/' : normalized;\n}\n\nfunction keyFromPath(path: string): string {\n const normalized = normalizePath(path);\n return normalized === '/' ? '' : normalized.slice(1);\n}\n\n/**\n * Encode each `/`-delimited segment of an object key with `encodeURIComponent`\n * so reserved URL characters (`?`, `#`, `%`, `&`, `+`, spaces, etc.) are\n * treated as part of the key instead of URL syntax. Kept segment-aware so\n * `/` continues to act as a path separator on the wire.\n */\nfunction encodeKeyPath(key: string): string {\n return key.split('/').map(encodeURIComponent).join('/');\n}\n\nfunction nameFromPath(path: string): string {\n const normalized = normalizePath(path);\n if (normalized === '/') return '';\n return normalized.slice(normalized.lastIndexOf('/') + 1);\n}\n\nfunction contentToBody(content: FileContent): string | Buffer {\n if (typeof content === 'string') return content;\n return Buffer.from(content);\n}\n\nfunction headerDate(headers: Headers, name: string): Date {\n const value = headers.get(name);\n return value ? new Date(value) : new Date(0);\n}\n\nfunction headerSize(headers: Headers): number {\n const value = headers.get('content-length');\n return value ? Number(value) : 0;\n}\n\nfunction isNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && 'status' in error && error.status === 404;\n}\n\nexport class PlatformFilesystem extends MastraFilesystem {\n readonly id: string;\n readonly name = 'PlatformFilesystem';\n readonly provider = 'platform';\n readonly readOnly?: boolean;\n readonly displayName?: string;\n readonly icon: FilesystemIcon;\n readonly description?: string;\n status: ProviderStatus = 'pending';\n\n private readonly _client: PlatformClient;\n private readonly _bucketName: string;\n private readonly _instructionsOverride?: InstructionsOption;\n\n constructor(options: PlatformFilesystemOptions = {}) {\n super({ ...options, name: 'PlatformFilesystem' });\n this.id = options.id ?? this.generateId();\n this._bucketName = options.bucketName ?? process.env.MASTRA_PLATFORM_BUCKET_NAME ?? '';\n if (!this._bucketName) throw new Error('bucketName is required');\n this.readOnly = options.readOnly;\n this.displayName = options.displayName;\n this.icon = options.icon ?? 'cloud';\n this.description = options.description;\n this._instructionsOverride = options.instructions;\n this._client = new PlatformClient(options);\n }\n\n private generateId(): string {\n return `platform-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n async readFile(path: string, options?: ReadOptions): Promise<string | Buffer> {\n await this.ensureReady();\n let response: Response;\n try {\n response = await this._client.request(\n `/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`,\n );\n } catch (error) {\n if (isNotFound(error)) throw new FileNotFoundError(path);\n throw error;\n }\n const buffer = Buffer.from(await response.arrayBuffer());\n return options?.encoding ? buffer.toString(options.encoding) : buffer;\n }\n\n async writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('writeFile');\n const headers: Record<string, string> = {};\n if (options?.mimeType) headers['content-type'] = options.mimeType;\n if (options?.overwrite === false) headers['if-none-match'] = '*';\n try {\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`, {\n method: 'PUT',\n headers,\n body: contentToBody(content),\n });\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'status' in error && error.status === 412) {\n throw new FileExistsError(path);\n }\n throw error;\n }\n }\n\n /**\n * Append bytes to a file.\n *\n * **Not atomic.** Object storage behind the workspace proxy has no native\n * append or compare-and-swap primitive, so this implementation is a\n * read-modify-write: it reads the current contents, concatenates the new\n * bytes, and PUTs the whole object back. Concurrent `appendFile` calls to\n * the same path can overwrite each other's writes (\"last write wins\").\n * Use `writeFile` with distinct keys for concurrent writers.\n */\n async appendFile(path: string, content: FileContent): Promise<void> {\n const existing = (await this.exists(path)) ? await this.readFile(path) : Buffer.alloc(0);\n await this.writeFile(\n path,\n Buffer.concat([Buffer.isBuffer(existing) ? existing : Buffer.from(existing), Buffer.from(content)]),\n );\n }\n\n async deleteFile(path: string, options?: RemoveOptions): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('deleteFile');\n try {\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`, {\n method: 'DELETE',\n query: { recursive: options?.recursive },\n });\n } catch (error) {\n if (isNotFound(error) && options?.force) return;\n if (isNotFound(error)) throw new FileNotFoundError(path);\n throw error;\n }\n }\n\n async copyFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('copyFile');\n // The workspace proxy's `?op=copy` route always overwrites the destination;\n // there's no conditional wire field to prevent it. Reject the option\n // explicitly instead of silently overwriting when the caller asked us not to.\n if (options?.overwrite === false) {\n throw new Error('PlatformFilesystem.copyFile does not support overwrite: false — the proxy always overwrites.');\n }\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(src))}`, {\n method: 'POST',\n query: { op: 'copy' },\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ destination: keyFromPath(dest) }),\n });\n }\n\n async moveFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('moveFile');\n // Same rationale as copyFile: `?op=rename` always overwrites.\n if (options?.overwrite === false) {\n throw new Error('PlatformFilesystem.moveFile does not support overwrite: false — the proxy always overwrites.');\n }\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(src))}`, {\n method: 'POST',\n query: { op: 'rename' },\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ destination: keyFromPath(dest) }),\n });\n }\n\n async mkdir(path: string, _options?: { recursive?: boolean }): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('mkdir');\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`, {\n method: 'POST',\n query: { op: 'mkdir' },\n });\n }\n\n async rmdir(path: string, options?: RemoveOptions): Promise<void> {\n await this.deleteFile(path.endsWith('/') ? path : `${path}/`, { recursive: true, force: options?.force });\n }\n\n async readdir(path: string, options?: ListOptions): Promise<FileEntry[]> {\n await this.ensureReady();\n const prefix = keyFromPath(path);\n const response = await this._client.request(\n `/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(prefix)}`,\n {\n query: {\n delimiter: options?.recursive ? undefined : '/',\n prefix: prefix ? `${prefix.replace(/\\/$/, '')}/` : undefined,\n },\n },\n );\n const json = (await response.json()) as ProxyListResponse;\n return [\n ...(json.commonPrefixes ?? []).map(prefix => ({\n name: nameFromPath(prefix.replace(/\\/$/, '')),\n type: 'directory' as const,\n })),\n ...(json.contents ?? [])\n .filter(object => object.key && !object.key.endsWith('/'))\n .map(object => ({\n name: nameFromPath(object.key!),\n type: 'file' as const,\n size: object.size,\n })),\n ].filter(\n entry => !options?.extension || entry.type === 'directory' || matchesExtension(entry.name, options.extension),\n );\n }\n\n async exists(path: string): Promise<boolean> {\n try {\n await this.stat(path);\n return true;\n } catch (error) {\n if (isNotFound(error) || error instanceof FileNotFoundError) return false;\n throw error;\n }\n }\n\n async stat(path: string): Promise<FileStat> {\n await this.ensureReady();\n const normalized = normalizePath(path);\n if (normalized === '/') {\n return { name: '', path: '/', type: 'directory', size: 0, createdAt: new Date(0), modifiedAt: new Date(0) };\n }\n let response: Response;\n try {\n response = await this._client.request(\n `/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`,\n {\n method: 'HEAD',\n },\n );\n } catch (error) {\n if (isNotFound(error)) throw new FileNotFoundError(path);\n throw error;\n }\n return {\n name: nameFromPath(path),\n path: normalized,\n type: normalized.endsWith('/') ? 'directory' : 'file',\n size: headerSize(response.headers),\n createdAt: headerDate(response.headers, 'last-modified'),\n modifiedAt: headerDate(response.headers, 'last-modified'),\n mimeType: response.headers.get('content-type') ?? undefined,\n };\n }\n\n realpath(path: string): Promise<string> {\n return Promise.resolve(normalizePath(path));\n }\n\n getInstructions(opts?: { requestContext?: RequestContext }): string {\n const defaultInstructions = `Platform filesystem backed by Mastra Platform bucket ${this._bucketName}. Use absolute workspace paths.`;\n if (typeof this._instructionsOverride === 'function') {\n return this._instructionsOverride({ defaultInstructions, requestContext: opts?.requestContext });\n }\n if (typeof this._instructionsOverride === 'string') return this._instructionsOverride;\n return defaultInstructions;\n }\n\n getInfo(): FilesystemInfo<{ bucketName: string; displayName?: string; description?: string }> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n readOnly: this.readOnly,\n icon: this.icon,\n metadata: {\n bucketName: this._bucketName,\n ...(this.displayName && { displayName: this.displayName }),\n ...(this.description && { description: this.description }),\n },\n };\n }\n}\n\nfunction matchesExtension(name: string, extension: string | string[]): boolean {\n const extensions = Array.isArray(extension) ? extension : [extension];\n return extensions.some(ext => name.endsWith(ext));\n}\n","/**\n * Direct exec client — opens Railway's tcp-proxy exec WebSocket directly using\n * a short-lived JWT minted by the workspace proxy's exec-lease endpoint. This\n * removes the platform data plane from the exec stdout/stderr path entirely\n * (see `docs/factory/direct-sandbox-connection.md` in the Platform repo),\n * cutting payload-scaled Cloud Run egress and RTT for commands like\n * `pnpm install` that stream tens of MB of output.\n *\n * The frame protocol below mirrors `connectExecWs()` in `railway@3.5.5`\n * (`workspaces/railway/node_modules/railway/dist/index.js`). The `railway`\n * SDK's version is pinned on both sides (platform + here); a version bump\n * signals the protocol may have drifted and this module must be revisited.\n */\n\n/** Byte-0 tag on binary WS frames for stdout output. */\nconst STDOUT_FRAME = 1;\n/** Byte-0 tag on binary WS frames for stderr output. */\nconst STDERR_FRAME = 3;\n/**\n * Upper bound on how long we'll wait for the WebSocket to open when the\n * caller didn't supply a `timeoutMs`. Guards against a stalled TLS/WS\n * handshake leaving the promise unresolved forever. Not applied once the\n * socket has opened — a caller with no timeout has opted in to unbounded\n * command runtime, just not to unbounded connection setup.\n */\nconst HANDSHAKE_DEADLINE_MS = 30_000;\n\n/**\n * Minimal WebSocket surface this module depends on. Matches both the browser\n * `WebSocket` global and Node 22+'s built-in `WebSocket`. Extracted so tests\n * can inject a fake without pulling in `ws` or jsdom.\n */\nexport interface DirectExecWebSocket {\n binaryType: 'blob' | 'arraybuffer';\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code: number; reason: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\n\n/**\n * Factory that opens a WebSocket to `endpoint` with the given subprotocols.\n * Defaults to the global `WebSocket` when omitted, which works on Node 22+\n * (the package's minimum) and in the browser. Tests inject a fake here.\n */\nexport type DirectExecWebSocketFactory = (endpoint: string, subprotocols: string[]) => DirectExecWebSocket;\n\n/** Lease payload returned by `POST /v1/projects/:projectId/sandbox/:sandboxId/exec-lease`. */\nexport interface ExecLease {\n jwt: string;\n wsEndpoint: string;\n subprotocol: string;\n /** ISO-8601 UTC. Null when the provider issues a JWT without an `exp` claim. */\n expiresAt: string | null;\n}\n\n/** Inputs to a direct exec invocation. Mirrors the shape of the `/exec` route body. */\nexport interface DirectExecOptions {\n command: string;\n cwd?: string;\n env?: Record<string, string>;\n /**\n * Wall-clock cap for the exec. When elapsed, we close the socket and\n * return `{timedOut: true, exitCode: 124}` matching the semantics of the\n * proxy's `/exec` route.\n */\n timeoutMs?: number;\n onStdout?: (chunk: string) => void;\n onStderr?: (chunk: string) => void;\n /** Injected for tests. Defaults to `globalThis.WebSocket`. */\n webSocketFactory?: DirectExecWebSocketFactory;\n}\n\n/**\n * Result of a direct exec. Shape matches the workspace-proxy `/exec` response\n * so the caller (`PlatformSandbox.executeCommand`) can hand it back with no\n * translation.\n *\n * `exitCode` is `null` when the socket closed without an `exit` frame AND the\n * exec did not time out (rare — usually a mid-stream network drop). Callers\n * currently coerce `null` to `1` upstream; kept nullable here to preserve the\n * distinction for future observability.\n */\nexport interface DirectExecResult {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n truncated: boolean;\n timedOut: boolean;\n /**\n * WebSocket close metadata. Populated on any close (normal or transport\n * failure). `opened` distinguishes handshake failures (never opened) from\n * mid-stream drops. Callers use this for diagnostic logging; not part of\n * the CommandResult contract.\n */\n closeCode?: number;\n closeReason?: string;\n opened?: boolean;\n}\n\nconst DEFAULT_WS_FACTORY: DirectExecWebSocketFactory = (endpoint, subprotocols) => {\n const WS = (globalThis as { WebSocket?: unknown }).WebSocket as\n | (new (url: string, protocols: string[]) => DirectExecWebSocket)\n | undefined;\n if (!WS) {\n throw new Error(\n 'Direct exec requires a WebSocket implementation. Node 22+ provides one globally; on older runtimes, pass webSocketFactory explicitly.',\n );\n }\n return new WS(endpoint, subprotocols);\n};\n\n/**\n * Open the provider exec WebSocket using `lease`, run `command`, and resolve\n * with the accumulated stdout/stderr + exit code. See the module docstring\n * for the wire protocol reference.\n *\n * The client sends `stdin_close` immediately after `init_exec`, matching the\n * SDK's own one-shot exec behavior — we never stream stdin from the caller.\n */\nexport function execViaLease(lease: ExecLease, options: DirectExecOptions): Promise<DirectExecResult> {\n const factory = options.webSocketFactory ?? DEFAULT_WS_FACTORY;\n const stdoutDecoder = new TextDecoder();\n const stderrDecoder = new TextDecoder();\n\n return new Promise<DirectExecResult>(resolve => {\n let stdout = '';\n let stderr = '';\n let exitCode: number | null = null;\n let timedOut = false;\n let settled = false;\n let opened = false;\n let closeCode: number | undefined;\n let closeReason: string | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let handshakeTimer: ReturnType<typeof setTimeout> | undefined;\n\n const settle = () => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n if (handshakeTimer) clearTimeout(handshakeTimer);\n // Flush any bytes still buffered in the decoders. A stream:true decode\n // holds trailing partial multi-byte sequences until the next chunk, so\n // without a flush the final char(s) of a UTF-8 stream can be dropped.\n const stdoutTail = stdoutDecoder.decode();\n if (stdoutTail) {\n stdout += stdoutTail;\n options.onStdout?.(stdoutTail);\n }\n const stderrTail = stderrDecoder.decode();\n if (stderrTail) {\n stderr += stderrTail;\n options.onStderr?.(stderrTail);\n }\n try {\n socket.close(1000, '');\n } catch {\n /* already closed */\n }\n resolve({\n exitCode,\n stdout,\n stderr,\n truncated: false,\n timedOut,\n ...(closeCode !== undefined && { closeCode }),\n ...(closeReason !== undefined && { closeReason }),\n opened,\n });\n };\n\n // Arm the timeout BEFORE we open the socket so a stalled handshake can't\n // leave the promise pending. Callers with a positive `timeoutMs` get the\n // wall-clock cap they asked for; callers without one still get a\n // connect-only deadline that clears once the socket opens.\n if (options.timeoutMs !== undefined && options.timeoutMs > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n // 124 matches the proxy's `/exec` semantics (coreutils `timeout`\n // exit code) so callers that switch on exitCode see the same value.\n if (exitCode === null) exitCode = 124;\n settle();\n }, options.timeoutMs);\n } else {\n handshakeTimer = setTimeout(() => {\n // Never opened → treat as a transport failure. Leave exitCode=null\n // so the caller can distinguish this from a normal exit; do not\n // set timedOut (that flag is reserved for the wall-clock case).\n if (!opened) settle();\n }, HANDSHAKE_DEADLINE_MS);\n }\n\n const socket = factory(lease.wsEndpoint, [lease.subprotocol, lease.jwt]);\n socket.binaryType = 'arraybuffer';\n\n socket.onopen = () => {\n opened = true;\n if (handshakeTimer) {\n clearTimeout(handshakeTimer);\n handshakeTimer = undefined;\n }\n const data: Record<string, unknown> = { command: options.command };\n if (options.cwd) data.cwd = options.cwd;\n if (options.env && Object.keys(options.env).length > 0) data.env = options.env;\n socket.send(JSON.stringify({ type: 'init_exec', data }));\n // We never stream stdin for one-shot exec; the SDK does this too, and\n // omitting it can leave the exec hanging waiting on EOF.\n socket.send(JSON.stringify({ type: 'stdin_close' }));\n };\n\n socket.onmessage = event => {\n const { data } = event;\n if (data instanceof ArrayBuffer) {\n handleBinaryFrame(data);\n } else if (typeof data === 'string') {\n handleTextFrame(data);\n }\n };\n\n socket.onclose = event => {\n closeCode = event.code;\n closeReason = event.reason;\n if (!opened) {\n // Never opened — surface as a failure via exitCode=null,\n // truncated=false, timedOut=false so the caller can distinguish\n // it from a normal exit-0 by inspecting `exitCode === null`.\n settle();\n return;\n }\n // Preserve any info captured before close; if the server sent an\n // `exit` frame this is a no-op because settle() already ran.\n settle();\n };\n\n socket.onerror = () => {\n if (settled) return;\n if (!opened) {\n settle();\n }\n // If we're mid-stream and the socket errors, wait for onclose to fire\n // so we settle with whatever output we did receive.\n };\n\n function handleBinaryFrame(buffer: ArrayBuffer) {\n const view = new Uint8Array(buffer);\n if (view.length <= 1) return;\n if (view[0] === STDOUT_FRAME) {\n const chunk = stdoutDecoder.decode(view.subarray(1), { stream: true });\n stdout += chunk;\n options.onStdout?.(chunk);\n } else if (view[0] === STDERR_FRAME) {\n const chunk = stderrDecoder.decode(view.subarray(1), { stream: true });\n stderr += chunk;\n options.onStderr?.(chunk);\n }\n }\n\n function handleTextFrame(text: string) {\n let frame: { type?: string; data?: { exit_code?: number } };\n try {\n frame = JSON.parse(text) as { type?: string; data?: { exit_code?: number } };\n } catch {\n return;\n }\n if (frame.type === 'exit') {\n exitCode = frame.data?.exit_code ?? 0;\n settle();\n }\n // `durable_session` frames are intentionally ignored — we don't reattach\n // or expose session names on the one-shot exec path.\n }\n });\n}\n","/**\n * Private-network exec client — dials the in-sandbox sidecar HTTP server\n * directly over Railway's private IPv6 network, bypassing the workspace-proxy\n * exec lease and the public tcp-proxy WebSocket entirely.\n *\n * Wire spec (matches the sidecar's `POST /exec` route, see\n * `.scratch/factory-deploy/issue-sandbox-sidecar-agent-in-base-image.md` in\n * the Platform repo):\n *\n * - Request: `POST ${instanceUrl}/exec` with JSON body\n * `{command, cwd?, env?, timeoutMs?}`.\n * - Response: `text/plain` NDJSON, one JSON object per line, terminated by an\n * `exit` frame:\n * `{\"type\":\"stdout\",\"data\":\"…\"}`\n * `{\"type\":\"stderr\",\"data\":\"…\"}`\n * `{\"type\":\"exit\",\"code\":<number>}`\n *\n * Auth is *network position* — the private ULA IPv6 space is only reachable\n * from workloads inside this Railway environment, and every workload there is\n * our own code. A bearer secret adds no boundary today so we do not send one;\n * `bearerToken` is accepted here as a defense-in-depth hook for when\n * untrusted-tenant sandboxes eventually share a private network.\n *\n * Return shape mirrors {@link ../direct-exec.ts}'s `DirectExecResult` so the\n * caller in `sandbox.ts` can hand results back with no translation regardless\n * of which transport served the exec.\n */\n\n/**\n * Minimal fetch surface this module depends on. Matches the global `fetch`\n * exposed by Node 22+ (undici) and the browser. Extracted so tests can inject\n * a fake without spinning up a real HTTP server.\n */\nexport type PrivateNetFetch = typeof fetch;\n\n/** Inputs to a private-network exec invocation. Mirrors {@link DirectExecOptions}. */\nexport interface PrivateNetExecOptions {\n command: string;\n cwd?: string;\n env?: Record<string, string>;\n /**\n * Wall-clock cap for the exec. When elapsed, we abort the request and\n * return `{timedOut: true, exitCode: 124}` matching the semantics of\n * `direct-exec.ts` (and the proxy's `/exec` route before it).\n */\n timeoutMs?: number;\n onStdout?: (chunk: string) => void;\n onStderr?: (chunk: string) => void;\n /** Injected for tests. Defaults to `globalThis.fetch`. */\n fetch?: PrivateNetFetch;\n /**\n * Optional bearer secret forwarded as `Authorization: Bearer <token>`.\n * Not required today (network position is the boundary). Present so we can\n * flip on per-sandbox secrets in a follow-up without a client-side reshape.\n */\n bearerToken?: string;\n}\n\n/**\n * Result of a private-network exec. Same shape as `DirectExecResult` — the\n * caller does not care which transport produced it.\n *\n * `exitCode` is `null` when the response body ended without an `exit` frame\n * AND the exec did not time out. That is a transport-level failure (the\n * sidecar died mid-stream or the socket was cut) and callers should treat it\n * as such (invalidate the cached `instanceUrl`, fall back to the lease path).\n */\nexport interface PrivateNetExecResult {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n timedOut: boolean;\n /**\n * True when we got a full HTTP response (status + at least the response\n * headers), even if the stream later dropped. False when we could not\n * establish the connection or the sidecar refused the request outright.\n * Used by the caller to distinguish \"sidecar unreachable\" (invalidate\n * cache) from \"sidecar answered but the stream broke\" (still invalidate,\n * but log differently).\n */\n opened: boolean;\n /** HTTP status when we got a response. Undefined on connection failure. */\n status?: number;\n /** Populated when we could not even open the request (DNS/connect/TLS). */\n transportErrorMessage?: string;\n}\n\n/**\n * Thrown by {@link execViaPrivateNetwork} when the sidecar returns a non-2xx\n * HTTP response. This is an *application* error, not a transport error — the\n * sidecar is reachable and answered, it just refused the exec. Callers should\n * fall back to the lease path for this one call but MUST NOT invalidate the\n * cached `instanceUrl` (the address is still good).\n */\nexport class PrivateNetExecHttpError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`Sidecar /exec returned ${status}${body ? `: ${body.slice(0, 200)}` : ''}`);\n this.name = 'PrivateNetExecHttpError';\n this.status = status;\n this.body = body;\n }\n}\n\nconst DEFAULT_FETCH: PrivateNetFetch = (input, init) => {\n const f = (globalThis as { fetch?: typeof fetch }).fetch;\n if (!f) {\n throw new Error(\n 'Private-network exec requires a fetch implementation. Node 22+ provides one globally; on older runtimes, pass fetch explicitly.',\n );\n }\n return f(input, init);\n};\n\n/**\n * Wire frame shapes accepted from the sidecar. Anything else on the stream\n * is ignored so a future sidecar can add new frame types without breaking\n * older clients.\n */\ntype SidecarFrame =\n | { type: 'stdout'; data: string }\n | { type: 'stderr'; data: string }\n | { type: 'exit'; code: number };\n\n/**\n * Dial `${instanceUrl}/exec` and stream the response, resolving with the\n * accumulated stdout/stderr + exit code.\n *\n * Errors:\n * - Connection failure (DNS, refused, reset) → resolves with\n * `{opened:false, exitCode:null, transportErrorMessage}`. Never throws for\n * transport failures — the shape matches the lease-path result so the\n * caller can treat both transports uniformly.\n * - Non-2xx HTTP response from the sidecar → throws {@link PrivateNetExecHttpError}.\n * Application-level; caller decides whether to fall back.\n * - Stream ends without an `exit` frame → resolves with\n * `{opened:true, exitCode:null}`, matching the lease-path semantics for a\n * mid-stream drop.\n * - `timeoutMs` elapsed → aborts the request, resolves with\n * `{timedOut:true, exitCode:124}`.\n */\nexport async function execViaPrivateNetwork(\n instanceUrl: string,\n options: PrivateNetExecOptions,\n): Promise<PrivateNetExecResult> {\n const fetchImpl = options.fetch ?? DEFAULT_FETCH;\n const url = `${instanceUrl.replace(/\\/$/, '')}/exec`;\n\n const controller = new AbortController();\n let timedOut = false;\n let timeoutTimer: ReturnType<typeof setTimeout> | undefined;\n if (options.timeoutMs !== undefined && options.timeoutMs > 0) {\n timeoutTimer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, options.timeoutMs);\n }\n\n const body: Record<string, unknown> = { command: options.command };\n if (options.cwd) body.cwd = options.cwd;\n if (options.env && Object.keys(options.env).length > 0) body.env = options.env;\n if (options.timeoutMs !== undefined && options.timeoutMs > 0) body.timeoutMs = options.timeoutMs;\n\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n if (options.bearerToken) headers.authorization = `Bearer ${options.bearerToken}`;\n\n let response: Response;\n try {\n response = await fetchImpl(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (error) {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n // AbortError from our own timeout → treat as timeout, not transport error.\n if (timedOut) {\n return {\n exitCode: 124,\n stdout: '',\n stderr: '',\n timedOut: true,\n opened: false,\n };\n }\n return {\n exitCode: null,\n stdout: '',\n stderr: '',\n timedOut: false,\n opened: false,\n transportErrorMessage: error instanceof Error ? error.message : String(error),\n };\n }\n\n if (!response.ok) {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n const text = await response.text().catch(() => '');\n throw new PrivateNetExecHttpError(response.status, text);\n }\n\n if (!response.body) {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n // A 200 without a body is a broken sidecar. Treat as mid-stream drop\n // (opened=true, no exit frame) so the caller invalidates cache.\n return {\n exitCode: null,\n stdout: '',\n stderr: '',\n timedOut: false,\n opened: true,\n status: response.status,\n };\n }\n\n let stdout = '';\n let stderr = '';\n let exitCode: number | null = null;\n const decoder = new TextDecoder();\n let buffer = '';\n\n const handleLine = (line: string): void => {\n if (!line) return;\n let frame: SidecarFrame | undefined;\n try {\n frame = JSON.parse(line) as SidecarFrame;\n } catch {\n // Unknown / malformed frame — ignore. A well-behaved sidecar only emits\n // JSON objects, but tolerating garbage protects against a partial write.\n return;\n }\n if (!frame || typeof frame !== 'object') return;\n if (frame.type === 'stdout' && typeof frame.data === 'string') {\n stdout += frame.data;\n options.onStdout?.(frame.data);\n } else if (frame.type === 'stderr' && typeof frame.data === 'string') {\n stderr += frame.data;\n options.onStderr?.(frame.data);\n } else if (frame.type === 'exit' && typeof frame.code === 'number') {\n exitCode = frame.code;\n }\n // Any other frame type is intentionally ignored (see SidecarFrame jsdoc).\n };\n\n try {\n const reader = response.body.getReader();\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n // NDJSON: split on newline; last piece stays in buffer for the next chunk.\n let newlineIdx = buffer.indexOf('\\n');\n while (newlineIdx !== -1) {\n const line = buffer.slice(0, newlineIdx).trim();\n buffer = buffer.slice(newlineIdx + 1);\n handleLine(line);\n newlineIdx = buffer.indexOf('\\n');\n }\n }\n // Flush the decoder + any trailing (no-newline) line at EOF.\n buffer += decoder.decode();\n const trailing = buffer.trim();\n if (trailing) handleLine(trailing);\n } catch (error) {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n if (timedOut) {\n return {\n exitCode: 124,\n stdout,\n stderr,\n timedOut: true,\n opened: true,\n status: response.status,\n };\n }\n // Mid-stream failure. Return whatever we accumulated with exitCode=null\n // so the caller sees this as a transport failure and can invalidate.\n return {\n exitCode,\n stdout,\n stderr,\n timedOut: false,\n opened: true,\n status: response.status,\n transportErrorMessage: error instanceof Error ? error.message : String(error),\n };\n } finally {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n }\n\n return {\n exitCode,\n stdout,\n stderr,\n timedOut: false,\n opened: true,\n status: response.status,\n };\n}\n","import type { RequestContext } from '@mastra/core/di';\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n InstructionsOption,\n MastraSandboxOptions,\n ProcessInfo,\n ProviderStatus,\n SandboxCloneOptions,\n SandboxInfo,\n SpawnProcessOptions,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, ProcessHandle, SandboxNotReadyError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { PlatformClientOptions } from './client.js';\nimport { PlatformApiError, PlatformClient } from './client.js';\nimport type { DirectExecWebSocketFactory, ExecLease } from './direct-exec.js';\nimport { execViaLease } from './direct-exec.js';\nimport type { PrivateNetExecOptions, PrivateNetExecResult, PrivateNetFetch } from './private-net-exec.js';\nimport { execViaPrivateNetwork, PrivateNetExecHttpError } from './private-net-exec.js';\n\nexport type PlatformSandboxNetworkIsolation = 'ISOLATED' | 'PRIVATE';\n\n/**\n * In-process `sandboxId → instanceUrl` map that lets\n * {@link PlatformSandbox.executeCommand} dial the in-sandbox sidecar over\n * Railway's private network instead of paying for a lease + WebSocket\n * round-trip through Railway's public control plane.\n *\n * The workspace proxy discovers each sandbox's IPv6 during\n * `POST /v1/projects/:pid/sandbox` and returns it as `instanceUrl` on the\n * create + get responses (see the platform inline-discovery issue). The\n * `PlatformSandbox` client copies that field into this registry from both\n * {@link PlatformSandbox.start} branches (fresh provision + reattach), evicts\n * on {@link PlatformSandbox.destroy}, and evicts again on any observed\n * transport failure so the next exec falls back to the lease path cleanly.\n *\n * See:\n * - `.scratch/factory-deploy/issue-runtime-sandbox-address-discovery.md`\n * - `.scratch/factory-deploy/issue-platform-sandbox-exec-via-private-network.md`\n */\nexport interface SandboxAddressRegistry {\n set(sandboxId: string, instanceUrl: string): void;\n get(sandboxId: string): string | undefined;\n delete(sandboxId: string): void;\n}\n\nexport interface PlatformSandboxOptions extends Omit<MastraSandboxOptions, 'processes'>, PlatformClientOptions {\n id?: string;\n environmentId?: string;\n sandboxId?: string;\n idleTimeoutMinutes?: number;\n networkIsolation?: PlatformSandboxNetworkIsolation;\n env?: Record<string, string>;\n timeout?: number;\n instructions?: InstructionsOption;\n /**\n * Injected WebSocket factory used by the direct-exec code path. Defaults to\n * the global `WebSocket` (available on Node 22+, this package's minimum) and\n * only exists so tests can drive the exec state machine deterministically\n * without a real network socket.\n */\n webSocketFactory?: DirectExecWebSocketFactory;\n /**\n * Injected fetch implementation used by the private-network exec code path\n * to dial the in-sandbox sidecar. Defaults to `globalThis.fetch` and only\n * exists so tests can drive that transport without a real HTTP server.\n * Note: this is separate from the `fetch` on {@link PlatformClientOptions},\n * which is used for calls to the workspace proxy.\n */\n privateNetFetch?: PrivateNetFetch;\n /**\n * Registry that maps `sandboxId → instanceUrl` for the private-network\n * exec path. When set, {@link PlatformSandbox.start} populates it from the\n * `instanceUrl` field workspace-proxy returns on create + get responses,\n * and {@link PlatformSandbox.executeCommand} looks it up before every exec\n * and tries the private-network transport first, falling back to the lease\n * path on any transport failure (and invalidating the registry entry so\n * the next call goes to the lease path cleanly). When absent, all execs\n * go straight to the lease path — this is the pre-existing behavior and\n * the expected mode outside the shipyard runtime. See\n * {@link SandboxAddressRegistry}.\n */\n addressRegistry?: SandboxAddressRegistry;\n}\n\ninterface ExecLeaseResponse {\n provider: string;\n sandboxId: string;\n providerResourceId: string;\n jwt: string;\n wsEndpoint: string;\n subprotocol: string;\n expiresAt: string | null;\n}\n\n/**\n * How long before a lease's stated `expiresAt` we should treat it as\n * expired. Avoids a race where the JWT is valid at cache-hit time but the\n * server rejects it by the time the WebSocket handshake completes.\n */\nconst LEASE_REFRESH_MARGIN_MS = 60_000;\n\ninterface CreateSandboxResponse {\n id: string;\n providerResourceId?: string | null;\n status?: string;\n createdAt?: string;\n destroyedAt?: string | null;\n /**\n * Full sidecar URL (`http://[<ipv6>]:<port>`) the runtime can dial over\n * Railway's private network to reach the in-sandbox exec sidecar. The\n * workspace proxy discovers the sandbox's IPv6 during `Sandbox.create()`\n * via one `sandbox.exec(\"awk … /proc/net/if_inet6\")` and stores it in\n * `environment_sandboxes.instance_url`; the same field is echoed on\n * `GET /sandbox/:id`. `null` when discovery failed (returned by the proxy\n * so the runtime knows to skip private-net dial for this sandbox rather\n * than fall back on a missing-field ambiguity).\n */\n instanceUrl?: string | null;\n}\n\n/** Max attempts for `POST /sandbox` when the proxy returns transient 5xx errors. */\nconst CREATE_MAX_ATTEMPTS = 3;\n/** Base delay between create retries; multiplied by the attempt number. */\nconst CREATE_RETRY_BASE_DELAY_MS = 2_000;\n\n/**\n * How long to wait for the in-sandbox sidecar's `/health` endpoint to respond\n * before giving up and leaving the address registry unpopulated (execs fall\n * back to the lease path). This bounds the fire-and-forget probe that runs\n * after `start()` resolves; the sandbox is usable immediately — the probe\n * only controls whether early execs go via private-net or lease.\n */\nconst SIDECAR_PROBE_TIMEOUT_MS = 30_000;\n/** Delay between sidecar probe attempts. */\nconst SIDECAR_PROBE_INTERVAL_MS = 250;\n/**\n * How long `executeCommand` waits for the transport to become ready before\n * falling back to the lease path. This is much shorter than\n * `SIDECAR_PROBE_TIMEOUT_MS` because we want execs to proceed quickly if\n * the sidecar is slow to boot — the probe continues in the background and\n * later execs will use private-net once it succeeds.\n */\nconst TRANSPORT_READY_WAIT_MS = 5_000;\n\n/**\n * Diagnostic error thrown when the direct-exec WebSocket transport fails\n * twice in a row (opening handshake refused or socket closed mid-stream\n * without an `exit` frame). Distinguishes \"the sandbox transport is broken\"\n * from \"your command failed\" so callers can decide whether to retry at a\n * higher level (e.g. reprovision the sandbox) or surface the error.\n *\n * `opened` is `true` when the WebSocket completed its handshake at least\n * once before closing; `false` when Railway refused the upgrade outright.\n */\nexport class SandboxExecTransportError extends Error {\n readonly sandboxId: string | undefined;\n readonly command: string;\n readonly attempts: number;\n readonly opened: boolean;\n readonly closeCode: number | undefined;\n readonly closeReason: string | undefined;\n readonly wsEndpoint: string;\n\n constructor(\n message: string,\n diagnostics: {\n sandboxId?: string;\n command: string;\n attempts: number;\n opened: boolean;\n closeCode?: number;\n closeReason?: string;\n wsEndpoint: string;\n },\n ) {\n super(message);\n this.name = 'SandboxExecTransportError';\n this.sandboxId = diagnostics.sandboxId;\n this.command = diagnostics.command;\n this.attempts = diagnostics.attempts;\n this.opened = diagnostics.opened;\n this.closeCode = diagnostics.closeCode;\n this.closeReason = diagnostics.closeReason;\n this.wsEndpoint = diagnostics.wsEndpoint;\n }\n}\n\n/**\n * Outcome of {@link PlatformSandbox.captureCheckpoint}. Mirrors the shape of\n * the OSS `@mastra/railway` `RailwaySandbox.captureCheckpoint()` return so a\n * caller (e.g. the factory fleet) can branch on `status`/`reason` uniformly\n * across providers without knowing which one is underneath.\n *\n * `captured` and `coalesced` both represent a successful capture the caller\n * can persist against — they carry the checkpoint name inline so callers\n * don't have to reach back into the sandbox instance to learn what was\n * written. `skipped` carries a machine-readable `reason` so the discriminant\n * set stays extensible.\n *\n * Note: the platform proxy's own `skipped` (returned when the upstream\n * sandbox is already destroyed) is mapped to `sandbox-not-running` here to\n * keep the discriminant identical to the OSS provider. The diagnostic\n * distinction (pre-flight vs post-hoc discovery) is preserved in log lines,\n * not the return type — see {@link PlatformSandbox.captureCheckpoint}.\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 * Thrown when `/exec-lease` returns 410 Gone — the sandbox has been destroyed\n * (Railway destroy, quota reclamation, etc.). The client cannot recover from\n * this on its own because it does not own the binding store; only the fleet\n * layer can clear the stale sandbox id and provision a fresh one. Callers\n * (typically `SandboxFleet`) must catch this and reprovision-and-replay.\n *\n * When this is thrown the cached `_lease` and `_sandboxId` on the sandbox\n * instance are cleared, so the next `ensureRunning()` on a reused instance\n * will re-provision cleanly.\n */\nexport class SandboxDestroyedError extends Error {\n readonly sandboxId: string | undefined;\n readonly command: string;\n readonly attempts: number;\n\n constructor(message: string, diagnostics: { sandboxId?: string; command: string; attempts: number }) {\n super(message);\n this.name = 'SandboxDestroyedError';\n this.sandboxId = diagnostics.sandboxId;\n this.command = diagnostics.command;\n this.attempts = diagnostics.attempts;\n }\n}\n\n/**\n * Compose a shell command line from a `command` string and optional `args`.\n *\n * IMPORTANT: `command` is treated as a **shell string** and passed to the\n * remote shell verbatim so callers can use pipes, redirects, and chaining\n * (`ls -la | grep foo`). This matches the contract of {@link MastraSandbox}\n * and the local sandbox implementation. `args` are always shell-quoted so\n * they cannot inject syntax.\n *\n * Callers MUST NOT pass untrusted input as `command`. Untrusted values must\n * be passed via `args`, where they are safely quoted. Passing untrusted\n * input as `command` allows arbitrary shell syntax execution on the remote\n * sandbox.\n */\nfunction buildCommand(command: string, args?: string[]): string {\n return args?.length ? `${command} ${args.map(shellQuote).join(' ')}` : command;\n}\n\nfunction shellQuote(arg: string): string {\n if (/^[a-zA-Z0-9._\\-/=:@]+$/.test(arg)) return arg;\n return `'${arg.replace(/'/g, `'\\\\''`)}'`;\n}\n\nclass PlatformProcessHandle extends ProcessHandle {\n readonly pid: string;\n private readonly resultPromise: Promise<CommandResult>;\n private exitCodeValue: number | undefined;\n\n constructor(pid: string, resultPromise: Promise<CommandResult>, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this.resultPromise = resultPromise.then(result => {\n this.exitCodeValue = result.exitCode;\n if (result.stdout) this.emitStdout(result.stdout);\n if (result.stderr) this.emitStderr(result.stderr);\n return result;\n });\n }\n\n get exitCode(): number | undefined {\n return this.exitCodeValue;\n }\n\n async wait(): Promise<CommandResult> {\n return this.resultPromise;\n }\n\n async kill(): Promise<boolean> {\n // The workspace proxy has no cancel-exec endpoint; each `executeCommand`\n // is a synchronous round-trip that has already completed (or timed out)\n // by the time a handle exists to kill. Making this explicit avoids\n // callers silently believing they cancelled a still-running process.\n throw new Error('Platform sandbox command execution does not support killing individual processes');\n }\n\n async sendStdin(): Promise<void> {\n throw new Error('Platform sandbox command execution does not support stdin');\n }\n}\n\nclass PlatformProcessManager extends SandboxProcessManager<PlatformSandbox> {\n private spawnCounter = 0;\n\n /**\n * Spawn a process on the remote sandbox.\n *\n * `command` is interpreted as a shell string by the remote shell, matching\n * the {@link MastraSandbox} contract. See {@link PlatformSandbox.executeCommand}\n * for the untrusted-input caveat: never pass untrusted values as `command`.\n */\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const pid = `platform-proc-${Date.now().toString(36)}-${(this.spawnCounter++).toString(36)}`;\n const resultPromise = this.sandbox.executeCommand(command, undefined, options);\n const handle = new PlatformProcessHandle(pid, resultPromise, options);\n this._tracked.set(handle.pid, handle);\n return handle;\n }\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\nexport class PlatformSandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'PlatformSandbox';\n readonly provider = 'platform';\n status: ProviderStatus = 'pending';\n declare readonly processes: PlatformProcessManager;\n\n private readonly _client: PlatformClient;\n private readonly _environmentId: string;\n private _sandboxId?: string;\n private readonly _idleTimeoutMinutes?: number;\n private readonly _networkIsolation?: PlatformSandboxNetworkIsolation;\n private readonly _env: Record<string, string>;\n private readonly _timeout?: number;\n private readonly _instructionsOverride?: InstructionsOption;\n private _createdAt: Date | null = null;\n private readonly _webSocketFactory?: DirectExecWebSocketFactory;\n private readonly _privateNetFetch?: PrivateNetFetch;\n /**\n * Registry that maps `sandboxId → instanceUrl` for the private-network\n * exec path. Injected by the composition site via\n * {@link PlatformSandboxOptions.addressRegistry} and populated by this\n * class itself in `start()` when the workspace-proxy's create/reattach\n * response includes an `instanceUrl` field. The registry IS the cache —\n * there is no per-instance mirror on `PlatformSandbox`, so every exec is\n * a `Map.get()` (in the default in-process impl) against the live view.\n * When absent, executes go straight to the lease path with no extra\n * round-trip.\n */\n private readonly _addressRegistry?: SandboxAddressRegistry;\n /**\n * Cached exec lease for this sandbox. `null` before the first exec and\n * after {@link destroy}. Refreshed when `expiresAt - LEASE_REFRESH_MARGIN_MS < now`\n * (see {@link _ensureLease}); a lease without a disclosed `expiresAt`\n * is refreshed on every call.\n */\n private _lease: (ExecLease & { expiresAtMs: number | null }) | null = null;\n /**\n * In-flight mint request; concurrent `_ensureLease` callers on a cold or\n * near-expiry cache all await this single promise so we don't burn N\n * `POST /exec-lease` round-trips when the sandbox is doing N parallel execs.\n * Cleared (regardless of success or failure) when the request settles.\n */\n private _leaseInFlight: Promise<ExecLease & { expiresAtMs: number | null }> | null = null;\n /**\n * True when this sandbox was constructed with a caller-supplied `id` (the\n * recovery key the proxy hashes into an on-provider checkpoint name).\n * `captureCheckpoint()` needs this to distinguish \"no checkpoint intent\"\n * (auto-generated random id — capture would land under a name no future\n * boot would look for) from \"capture on demand\". Cloned sandboxes route\n * `checkpointName` through `id`, so both entry points set this the same\n * way.\n */\n private readonly _hasRecoveryKey: boolean;\n /**\n * In-flight `captureCheckpoint()` request. Concurrent callers on the same\n * instance coalesce onto this single promise so we don't burn N `POST\n * /checkpoint` round-trips when the fleet fires several turn-end captures\n * before the first one resolves. Cleared when the request settles.\n */\n private _captureInFlight: Promise<CaptureCheckpointResult> | null = null;\n /**\n * In-flight `start()` attempt. Concurrent callers on a fresh instance\n * coalesce onto this single promise so a `POST /sandbox` is not fired\n * N times when N fleet callers race to bring the same logical sandbox\n * up. Published **synchronously** with `??=` before the first `await`\n * so a later caller cannot slip through the null check while the\n * originator is mid-round-trip. Cleared when the shared attempt\n * settles (success or failure) so the next call sees a clean slot.\n *\n * Mirrors OSS `@mastra/railway` `RailwaySandbox._startInFlight`.\n */\n private _startInFlight: Promise<void> | null = null;\n /**\n * Generation token for the sidecar probe. Incremented on every `start()`\n * and on teardown. The probe captures this value when it begins; if the\n * generation has changed by the time the probe succeeds, the probe skips\n * the `set()` to avoid re-populating a deleted or superseded sandbox entry.\n */\n private _probeGeneration = 0;\n /**\n * In-flight sidecar probe promise. Concurrent `executeCommand` callers that\n * arrive before the registry is populated all await this single promise so\n * we don't fire N independent lease requests during the sidecar boot window.\n * Once the probe resolves (success or timeout), callers check the registry\n * and proceed — either via private-net (probe succeeded) or via lease (probe\n * failed/timed out, but now coalesced via `_leaseInFlight`).\n */\n private _transportReadyPromise: Promise<void> | null = null;\n\n constructor(options: PlatformSandboxOptions = {}) {\n super({ ...options, name: 'PlatformSandbox', processes: new PlatformProcessManager() });\n this._hasRecoveryKey = options.id !== undefined;\n this.id = options.id ?? this.generateId();\n this._client = new PlatformClient(options);\n this._environmentId = options.environmentId ?? process.env.MASTRA_ENVIRONMENT_ID ?? '';\n if (!this._environmentId && !options.sandboxId) throw new Error('environmentId is required');\n this._sandboxId = options.sandboxId;\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._webSocketFactory = options.webSocketFactory;\n this._privateNetFetch = options.privateNetFetch;\n this._addressRegistry = options.addressRegistry;\n }\n\n private generateId(): string {\n return `platform-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n /**\n * Construct a sibling {@link PlatformSandbox} that inherits this sandbox's\n * credentials and defaults (access token, project, environment, network\n * isolation, timeout, instructions, env, idle timeout) with per-instance\n * overrides from `options`.\n *\n * Performs no I/O and does not require this sandbox to be started — the\n * returned sandbox is not started and provisions (or reattaches, when\n * `sandboxId` is set) on its own `start()`. Use it when one configured\n * sandbox acts as the template for a fleet of independent sandboxes\n * (e.g. one per project).\n */\n clone(options: SandboxCloneOptions = {}): PlatformSandbox {\n // The proxy hashes `body.id` on POST /sandbox to look up a prior\n // checkpoint. A stable `checkpointName` is only useful if it round-trips\n // to `body.id`, so route it through the sandbox id when the caller\n // didn't pick one explicitly. Without this, every clone gets a random\n // id and no boot ever hits its captured checkpoint (see\n // issue-platform-sandbox-clone-drops-checkpoint-name.md).\n const id = options.id ?? options.checkpointName;\n return new PlatformSandbox({\n ...(id !== undefined && { id }),\n accessToken: this._client.accessToken,\n projectId: this._client.projectId,\n ...(this._client.sessionId !== undefined && { sessionId: this._client.sessionId }),\n ...(this._client.threadId !== undefined && { threadId: this._client.threadId }),\n fetch: this._client.fetch,\n environmentId: this._environmentId,\n ...(options.sandboxId !== undefined && { sandboxId: options.sandboxId }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n env: options.env ?? this._env,\n ...(this._timeout !== undefined && { timeout: this._timeout }),\n ...(this._instructionsOverride !== undefined && { instructions: this._instructionsOverride }),\n ...(this._webSocketFactory !== undefined && { webSocketFactory: this._webSocketFactory }),\n ...(this._privateNetFetch !== undefined && { privateNetFetch: this._privateNetFetch }),\n // Propagate the registry — the clone is a different sandbox with a\n // different id, so it will `get()` its OWN address (or nothing) out\n // of the shared registry, not the parent's. See\n // `.scratch/factory-deploy/issue-platform-sandbox-exec-via-private-network.md`.\n ...(this._addressRegistry !== undefined && { addressRegistry: this._addressRegistry }),\n });\n }\n\n async start(): Promise<void> {\n // Coalesce concurrent callers onto a single in-flight attempt. `??=`\n // publishes the promise **synchronously** before the first `await`\n // below, so a second caller entering `start()` while the first is\n // mid-round-trip sees a populated `_startInFlight` and joins it\n // instead of racing to `POST /sandbox` alongside the originator. On\n // settle (success or failure) the slot is cleared so the next call\n // starts fresh — a failed attempt is not a permanent latch.\n // Mirrors OSS @mastra/railway RailwaySandbox._startInFlight.\n this._startInFlight ??= this._doStart().finally(() => {\n this._startInFlight = null;\n });\n return this._startInFlight;\n }\n\n /**\n * The single `start` attempt behind {@link start}'s coalescing wrapper.\n *\n * Split out so the wrapper can install a shared in-flight promise\n * synchronously (before the first `await`) without inlining the reattach\n * / retry logic. Joined callers observe whatever outcome this method\n * produces — success returns normally, failures propagate to every\n * awaiter.\n */\n private async _doStart(): Promise<void> {\n const startedAt = Date.now();\n if (this._sandboxId) {\n try {\n const requestStartedAt = Date.now();\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`);\n const requestMs = Date.now() - requestStartedAt;\n const json = (await response.json()) as CreateSandboxResponse;\n // A destroyed record (idle GC, manual delete) is not reattachable —\n // treat it like a missing sandbox so we fall through to a fresh\n // provision instead of pointing exec at a dead resource.\n if (!json.destroyedAt) {\n this._createdAt = json.createdAt ? new Date(json.createdAt) : new Date();\n this._populateAddressFromResponse(json);\n this._logStartComplete(json.id, startedAt, requestMs, 'reattach');\n return;\n }\n this._sandboxId = undefined;\n } catch (error) {\n if (!(error instanceof PlatformApiError) || error.status !== 404) throw error;\n this._sandboxId = undefined;\n }\n }\n\n if (!this._environmentId) throw new Error('environmentId is required');\n\n const body = JSON.stringify({\n // Sent so the platform can associate the provisioned resource with a\n // caller-stable identifier (used for opt-in checkpoint recovery). The\n // platform treats it as an advisory key: unknown values fall through\n // to a fresh sandbox, matching pre-existing behavior.\n id: this.id,\n environmentId: this._environmentId,\n idleTimeoutMinutes: this._idleTimeoutMinutes,\n networkIsolation: this._networkIsolation,\n env: this._env,\n });\n // Provisioning is observed to fail intermittently with proxy 500s while\n // the provider is under load. A create either succeeds (201) or fails\n // without allocating a caller-visible resource, so retrying transient\n // 5xx responses with a short backoff is safe and keeps a single flaky\n // window from killing the caller's whole workflow.\n let response: Response | undefined;\n const requestStartedAt = Date.now();\n for (let attempt = 1; ; attempt++) {\n try {\n response = await this._client.request('/sandbox', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body,\n });\n break;\n } catch (error) {\n const transient = error instanceof PlatformApiError && error.status >= 500;\n if (!transient || attempt >= CREATE_MAX_ATTEMPTS) throw error;\n await new Promise(resolve => setTimeout(resolve, CREATE_RETRY_BASE_DELAY_MS * attempt));\n }\n }\n const requestMs = Date.now() - requestStartedAt;\n const json = (await response.json()) as CreateSandboxResponse;\n this._sandboxId = json.id;\n this._createdAt = json.createdAt ? new Date(json.createdAt) : new Date();\n this._populateAddressFromResponse(json);\n this._logStartComplete(json.id, startedAt, requestMs, 'provision');\n }\n\n /**\n * One timing summary per completed `start()` — the whole\n * `PlatformSandbox`-visible boot in a single greppable line.\n *\n * `requestMs` is the proxy round-trip (`GET /sandbox/:id` on reattach,\n * `POST /sandbox` including transient-5xx retries on provision) — a black\n * box from this side that rolls up Railway RPC, sidecar launch, and the\n * proxy's discovery exec. Sidecar probe cost is intentionally NOT here: the\n * probe is fire-and-forget and outlives `start()` by design, so its\n * duration lands on the `platform-workspace probe ok` line instead.\n */\n private _logStartComplete(sandboxId: string, startedAt: number, requestMs: number, mode: string): void {\n this.logger.info('platform-workspace start complete', {\n sandboxId,\n sessionId: this._client.sessionId,\n mode,\n totalMs: Date.now() - startedAt,\n requestMs,\n });\n }\n\n /**\n * Copy `response.instanceUrl` into the injected {@link SandboxAddressRegistry}\n * when both are present. Called from both {@link start} branches (fresh\n * provision + reattach) with the workspace-proxy response for this sandbox.\n *\n * The proxy discovers the IPv6 during `Sandbox.create()` and stores it in\n * `environment_sandboxes.instance_url`; both the create response and\n * `GET /sandbox/:id` echo the same field. The runtime does not do any\n * discovery of its own — it only mirrors the field into an in-process map\n * so {@link executeCommand} can `Map.get()` before every exec without an\n * HTTP round-trip.\n *\n * `null`/absent `instanceUrl` (proxy discovery failed, or an older proxy\n * that predates the field) leaves the registry untouched — executes fall\n * through to the lease path with no branch here.\n */\n private _populateAddressFromResponse(json: CreateSandboxResponse): void {\n if (!this._addressRegistry) return;\n if (!json.instanceUrl) return;\n // Clear any stale entry before probing. On reattach, the registry may have\n // the old sandbox's address; execs should fall back to lease until the new\n // probe succeeds rather than dialing the stale address.\n this._addressRegistry.delete(json.id);\n // Start the sidecar probe and expose it so executeCommand can await it.\n // Early execs wait for the probe (up to TRANSPORT_READY_WAIT_MS) rather\n // than all racing to the lease path independently.\n const generation = ++this._probeGeneration;\n this._transportReadyPromise = this._probeSidecarThenRegister(json.id, json.instanceUrl, generation);\n }\n\n /**\n * Fire-and-forget probe that polls the sidecar's `/health` endpoint until\n * it responds, then populates the address registry. Runs detached from\n * `start()` so sandbox provision latency is unchanged; early execs simply\n * fall back to the lease path until the probe succeeds.\n *\n * If the sidecar never comes up within {@link SIDECAR_PROBE_TIMEOUT_MS},\n * the registry stays unpopulated and all execs go via lease for this\n * sandbox's lifetime (or until a future `start()` re-runs the probe).\n *\n * @param generation - The probe generation captured at call time. If this\n * no longer matches `_probeGeneration` when the probe succeeds, the probe\n * was superseded by a teardown or a new `start()`, so we skip the `set()`.\n */\n private async _probeSidecarThenRegister(sandboxId: string, instanceUrl: string, generation: number): Promise<void> {\n const probeStartedAt = Date.now();\n const deadline = probeStartedAt + SIDECAR_PROBE_TIMEOUT_MS;\n const fetchFn = this._privateNetFetch ?? fetch;\n let attempts = 0;\n while (Date.now() < deadline) {\n // Teardown or new start() superseded this probe — bail out early.\n if (generation !== this._probeGeneration) return;\n attempts++;\n try {\n const res = await fetchFn(`${instanceUrl}/health`, {\n method: 'GET',\n signal: AbortSignal.timeout(1_000),\n });\n const ok = res.ok;\n // Release the response body so the connection returns to the pool.\n await res.body?.cancel().catch(() => {});\n if (ok) {\n // A ~1-attempt probe means the sidecar was ready when the proxy\n // returned; hundreds of ms means it was still booting — the exact\n // window that used to silently fall back to the lease path.\n this.logger.info('platform-workspace probe ok', {\n sandboxId,\n sessionId: this._client.sessionId,\n probeDurationMs: Date.now() - probeStartedAt,\n attempts,\n });\n // Sidecar is listening. Only populate if this probe is still current.\n if (generation === this._probeGeneration && this._sandboxId === sandboxId) {\n this._addressRegistry?.set(sandboxId, instanceUrl);\n }\n return;\n }\n } catch {\n // Connection refused / timeout — keep polling.\n }\n await new Promise(r => setTimeout(r, SIDECAR_PROBE_INTERVAL_MS));\n }\n // Sidecar never came up. Leave registry entry unset — every exec goes lease.\n this.logger.warn('platform-workspace probe timed out', {\n sandboxId,\n sessionId: this._client.sessionId,\n timeoutMs: SIDECAR_PROBE_TIMEOUT_MS,\n attempts,\n });\n }\n\n /**\n * Wait for the transport to become ready (sidecar probe succeeds) or time\n * out. Concurrent callers all await the same probe promise, coalescing the\n * cold-start storm into a single warmup attempt.\n *\n * If no probe is in flight (no registry, or registry already populated),\n * this returns immediately. After the wait (success or timeout), callers\n * check the registry and proceed — either via private-net or lease. The\n * lease path is still coalesced via `_leaseInFlight`, so even if the probe\n * times out, we only mint one lease for all concurrent execs.\n */\n private async _awaitTransportReady(): Promise<void> {\n // Fast path: registry already has an entry, transport is warm.\n if (this._sandboxId && this._addressRegistry?.get(this._sandboxId)) {\n return;\n }\n // No probe in flight — nothing to wait for, proceed to lease path.\n if (!this._transportReadyPromise) {\n return;\n }\n // Race the probe against a timeout. We don't want to block execs forever\n // if the sidecar is slow to boot — they can proceed via lease after a\n // short wait, and later execs will use private-net once the probe succeeds.\n await Promise.race([this._transportReadyPromise, new Promise<void>(r => setTimeout(r, TRANSPORT_READY_WAIT_MS))]);\n }\n\n /**\n * Stop the sandbox while **preserving its recovery checkpoint**.\n *\n * Semantic parity with `@mastra/railway` `RailwaySandbox.stop()`: the VM\n * is released but the on-provider checkpoint survives, so a subsequent\n * `start()` on a sandbox constructed with the same `id` can restore from\n * it. Any in-flight capture is awaited first so the preserved checkpoint\n * reflects the latest disk state we asked for.\n *\n * Corresponds to `DELETE /v1/projects/:pid/sandbox/:sandboxId` on\n * workspace-proxy, which by contract does not touch the checkpoint. Use\n * {@link destroy} when you want the checkpoint released too.\n */\n async stop(): Promise<void> {\n // Await any in-flight capture so the preserved checkpoint reflects the\n // latest capture the caller triggered. Never rethrow — a failing capture\n // must not block teardown; the proxy's safety-net refresh timer is a\n // fallback for the checkpoint state.\n if (this._captureInFlight) {\n await this._captureInFlight.catch(error => {\n this.logger.warn(`stop(): failed to flush in-flight capture before teardown:`, error);\n });\n }\n await this._teardownSandbox();\n }\n\n /**\n * Destroy the sandbox **and release its recovery checkpoint**.\n *\n * Semantic parity with `@mastra/railway` `RailwaySandbox.destroy()`:\n * cancels any in-flight capture (the checkpoint is about to be deleted\n * — no reason to burn a capture on state we're releasing), asks the\n * proxy to delete the checkpoint, then releases the VM. Both remote\n * operations are best-effort logged failures — a stray checkpoint or a\n * transient proxy error must not leave the caller with a half-torn-down\n * sandbox they can't safely retry.\n *\n * Requires the caller to have constructed with a recovery `id` (there is\n * no checkpoint to delete otherwise); callers without one skip the\n * checkpoint DELETE and behave identically to {@link stop}.\n */\n async destroy(): Promise<void> {\n if (!this._sandboxId) return;\n const destroyedSandboxId = this._sandboxId;\n\n // Drop the in-flight capture promise — we're about to delete the\n // checkpoint, so completing an in-flight capture is at best a wasted\n // round-trip and at worst races with the delete. Callers get whatever\n // resolution the pending capture already had; we don't rethrow.\n this._captureInFlight = null;\n\n if (this._hasRecoveryKey) {\n // Body mirrors the POST /checkpoint shape (`{ id }`) so the proxy\n // can hash the same recovery key into the same checkpoint name.\n // Best-effort: a proxy 404/410 means the checkpoint is already\n // absent (idle GC, prior delete) and we can proceed with the VM\n // teardown; other failures are surfaced in logs but do not abort\n // — the VM DELETE below is the operation the caller most needs.\n try {\n await this._client.request(`/sandbox/${encodeURIComponent(destroyedSandboxId)}/checkpoint`, {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: this.id }),\n });\n } catch (error) {\n if (error instanceof PlatformApiError && (error.status === 404 || error.status === 410)) {\n this.logger.debug(`destroy(): checkpoint already absent upstream (status=${error.status})`);\n } else {\n this.logger.warn(`destroy(): failed to delete checkpoint upstream:`, error);\n }\n }\n }\n\n await this._teardownSandbox();\n }\n\n /**\n * Release the remote sandbox VM and clear the local state pointing at it.\n *\n * Shared body of {@link stop} and {@link destroy} — both funnel through\n * here after they've dealt with the checkpoint (preserve vs release).\n * The VM DELETE is safe to issue in either mode: the proxy's DELETE\n * route does not touch the checkpoint on its own, so `stop()` correctly\n * leaves the checkpoint intact and `destroy()` has already removed it\n * before this call.\n */\n private async _teardownSandbox(): Promise<void> {\n if (!this._sandboxId) return;\n const destroyedSandboxId = this._sandboxId;\n // Invalidate any in-flight probe so it doesn't re-populate the registry\n // after we've deleted the entry below. The probe checks this generation\n // before calling set().\n this._probeGeneration++;\n await this._client.request(`/sandbox/${encodeURIComponent(destroyedSandboxId)}`, { method: 'DELETE' });\n // Clear local state so a subsequent start() creates a fresh remote sandbox\n // instead of taking the reattach branch and pointing exec at a deleted resource.\n this._sandboxId = undefined;\n this._createdAt = null;\n // Drop the exec lease with the sandbox — the JWT is tied to the provider\n // instance id and would be rejected against a fresh one.\n this._lease = null;\n // Evict the sidecar address from the registry explicitly. Destroy\n // deallocates the IPv6, so any stale entry would be a dial-to-nowhere;\n // clearing here prevents a transport-failure round-trip on the next\n // exec against a reused instance. A subsequent start() (fresh provision\n // or reattach) will re-populate the entry from the workspace-proxy's\n // response.\n this._addressRegistry?.delete(destroyedSandboxId);\n }\n\n /** Persist the configured recovery checkpoint when available. */\n async snapshot(): Promise<void> {\n await this.captureCheckpoint();\n }\n\n /**\n * Capture the sandbox's checkpoint on demand, outside any refresh timer the\n * workspace-proxy owns internally.\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 the upstream's idle destroy.\n *\n * Mirrors the OSS `@mastra/railway` `RailwaySandbox.captureCheckpoint()`\n * shape so factory can call `sandbox.captureCheckpoint()` uniformly and\n * branch on `status`/`reason` without knowing which provider is underneath.\n * Both `captured` and `coalesced` carry the checkpoint name inline so the\n * caller can persist a session→checkpoint binding atomically with the\n * awaited capture.\n *\n * Skip semantics:\n * - No caller-supplied `id`: returns `{ status: 'skipped', reason:\n * 'no-checkpoint-name-configured' }`. An auto-generated random id is\n * never a meaningful recovery key (no future boot would look for a\n * checkpoint under it), so capturing would silently produce dead data.\n * - Not started (no `_sandboxId`): returns `{ status: 'skipped', reason:\n * 'sandbox-not-running' }` without a round-trip.\n * - Upstream 410 (workspace-proxy or Railway reports the sandbox is\n * already destroyed): returns the same `sandbox-not-running` skip so\n * the discriminant matches the pre-flight case. Local state\n * (`_sandboxId`, `_lease`, sidecar address) is cleared as a side\n * effect so the next `start()` provisions fresh instead of reattaching\n * to a dead id. The diagnostic distinction (pre-flight vs post-hoc)\n * is preserved in log level: debug for the expected pre-flight skip,\n * warn for the surprise upstream destroy.\n *\n * Concurrent callers on the same instance coalesce onto a single in-flight\n * `POST /checkpoint` so N simultaneous turn-end fires (e.g. several tabs)\n * do not each round-trip the proxy. Both the originator and joiners\n * receive `{ status: 'coalesced', ... }` for the joined result — the\n * outer contract does not distinguish who started the request, only that\n * one upstream capture was made.\n *\n * Never throws for expected outcomes. Transport failures (5xx, 4xx other\n * than 410) propagate as {@link PlatformApiError}; a 410 is normalized\n * to a skip as described above.\n */\n async captureCheckpoint(): Promise<CaptureCheckpointResult> {\n if (!this._hasRecoveryKey) {\n this.logger.debug(\n `captureCheckpoint skipped: no recovery key configured for sandbox ${this._sandboxId ?? '(unstarted)'}`,\n );\n return { status: 'skipped', reason: 'no-checkpoint-name-configured' };\n }\n\n if (!this._sandboxId) {\n this.logger.debug(`captureCheckpoint skipped: sandbox not running (local pre-flight, id=${this.id})`);\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n\n if (this._captureInFlight) {\n return this._captureInFlight;\n }\n\n const sandboxId = this._sandboxId;\n const capture = this._doCaptureCheckpoint(sandboxId).finally(() => {\n if (this._captureInFlight === capture) {\n this._captureInFlight = null;\n }\n });\n this._captureInFlight = capture;\n return capture;\n }\n\n /**\n * The single `POST /checkpoint` attempt behind {@link captureCheckpoint}.\n *\n * Split out so the coalescing wrapper can install a shared in-flight\n * promise without inlining the transport + response-mapping logic.\n * Joined callers observe `{ status: 'coalesced', ... }` — the initiator\n * sees the underlying `captured` / `coalesced` / `skipped` result the\n * proxy returned. Both are legitimate: the OSS mirror uses the same\n * \"initiator sees the truth, joiners see coalesced\" split.\n */\n private async _doCaptureCheckpoint(sandboxId: string): Promise<CaptureCheckpointResult> {\n let response: Response;\n try {\n response = await this._client.request(`/sandbox/${encodeURIComponent(sandboxId)}/checkpoint`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: this.id }),\n });\n } catch (error) {\n if (error instanceof PlatformApiError && error.status === 410) {\n this.logger.warn(`captureCheckpoint skipped: sandbox destroyed upstream (proxy 410, sandboxId=${sandboxId})`);\n this._clearDestroyedState(sandboxId);\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n throw error;\n }\n const json = (await response.json()) as { checkpointName: string; status: 'captured' | 'coalesced' | 'skipped' };\n if (json.status === 'skipped') {\n // Proxy's own `skipped` means the upstream sandbox is already\n // destroyed — same actionable outcome as a 410, so normalize to\n // the same discriminant + clear local state.\n this.logger.warn(\n `captureCheckpoint skipped: sandbox destroyed upstream (proxy reported skipped, sandboxId=${sandboxId})`,\n );\n this._clearDestroyedState(sandboxId);\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n return { status: json.status, checkpointName: json.checkpointName };\n }\n\n /**\n * Clear local state that would otherwise let the caller keep exec'ing\n * against a sandbox the upstream has already destroyed. Mirrors what\n * `destroy()` does minus the outbound DELETE — the sandbox is already\n * gone, so all that remains is to stop pointing at it.\n *\n * Also resets `status` to `'pending'` so a subsequent `_start()` on this\n * reused instance re-runs provisioning instead of short-circuiting on\n * the cached `'running'` state (see `MastraSandbox._start`).\n */\n private _clearDestroyedState(destroyedSandboxId: string): void {\n this._sandboxId = undefined;\n this._createdAt = null;\n this._lease = null;\n this._addressRegistry?.delete(destroyedSandboxId);\n this.status = 'pending';\n }\n\n /**\n * Execute a command on the remote sandbox.\n *\n * `command` is a **shell string**: it is concatenated verbatim into the\n * command line sent to the remote shell, which lets callers use pipes,\n * redirects, and chaining (`ls -la | grep foo`). This matches the contract\n * of {@link MastraSandbox} and the local sandbox implementation.\n *\n * `args`, when provided, are always shell-quoted so they cannot inject\n * additional shell syntax.\n *\n * Security: callers MUST NOT pass untrusted input as `command`. If any part\n * of the invocation is derived from an untrusted source, pass it through\n * `args` (which is safely quoted) or shell-quote it yourself before\n * inclusion. Untrusted `command` values allow arbitrary shell syntax\n * execution on the remote sandbox.\n */\n async executeCommand(command: string, args?: string[], options?: ExecuteCommandOptions): Promise<CommandResult> {\n await this.ensureRunning();\n if (!this._sandboxId) throw new SandboxNotReadyError(this.id);\n\n const started = Date.now();\n const fullCommand = buildCommand(command, args);\n // Nullish check so an explicit `timeout: 0` still overrides the instance\n // default. `_runDirectExec` omits `timeoutMs` from the exec payload when\n // the value is 0, which disables the client-side timer entirely.\n const effectiveTimeout = options?.timeout ?? this._timeout;\n\n // Wait for the transport to become ready before proceeding. During the\n // sidecar boot window (immediately after start()), concurrent execs all\n // await the same probe promise rather than each independently racing to\n // the lease path — this coalesces the cold-start storm into a single\n // warmup attempt. Once the probe resolves (or times out after\n // TRANSPORT_READY_WAIT_MS), we check the registry and proceed.\n await this._awaitTransportReady();\n\n // Preferred path: dial the in-sandbox sidecar over Railway's private\n // network (~16 ms p50). No GraphQL, no lease mint, no public tcp-proxy.\n // Available only when the in-process address registry has an entry for\n // this sandboxId — populated by start() from the workspace-proxy's\n // create/reattach response when the proxy discloses `instanceUrl`. On\n // any transport failure (connection refused, mid-stream drop, no exit\n // frame) we invalidate the registry entry and fall through to the lease\n // path — application-level failures (sidecar returns 5xx) still fall\n // back for this call but do NOT invalidate the entry. See\n // `.scratch/factory-deploy/issue-platform-sandbox-exec-via-private-network.md`.\n const instanceUrl = this._addressRegistry?.get(this._sandboxId);\n if (instanceUrl) {\n const privateNet = await this._tryExecViaPrivateNetwork(instanceUrl, fullCommand, effectiveTimeout, options);\n if (privateNet) {\n const privateExit = privateNet.exitCode ?? 124;\n return {\n success: privateExit === 0,\n exitCode: privateExit,\n stdout: privateNet.stdout,\n stderr: privateNet.stderr,\n timedOut: privateNet.timedOut,\n command: fullCommand,\n executionTimeMs: Date.now() - started,\n };\n }\n // Fall through — `_tryExecViaPrivateNetwork` already evicted the\n // registry entry if this was a transport failure.\n }\n\n // Fallback: WebSocket direct-exec via `/exec-lease`. `_runDirectExec`\n // handles single-shot transport retry and throws typed errors on\n // unrecoverable failure: `SandboxDestroyedError` when `/exec-lease`\n // returns 410 (fleet must reprovision), `SandboxExecTransportError`\n // when the WebSocket transport fails twice against a live sandbox,\n // `PlatformApiError` for other `/exec-lease` errors (404/500/501).\n // See ./direct-exec.ts and `docs/factory/direct-sandbox-connection.md`\n // in the Platform repo.\n const result = await this._runDirectExec(fullCommand, effectiveTimeout, options);\n // `_runDirectExec` throws on transport failure (see its jsdoc), so a\n // `null` exitCode here can only mean `timedOut: true` — the sandbox\n // never got to send an exit frame because we cut the command short.\n // Use 124 for that (the conventional timeout exit code). We are NOT\n // coercing transport-failure nulls to fake exit codes — those throw.\n const exitCode = result.exitCode ?? 124;\n return {\n success: exitCode === 0,\n exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n timedOut: result.timedOut,\n command: fullCommand,\n executionTimeMs: Date.now() - started,\n };\n }\n\n /**\n * Run a single exec against the direct-exec transport, with one in-flight\n * retry on WebSocket transport failure (socket closed without an `exit`\n * frame and the exec did not time out). The retry mints a fresh lease\n * — the failure could be a stale JWT — and reopens a new WebSocket.\n *\n * Error taxonomy:\n * - **410 on `/exec-lease`** (either attempt) → the sandbox is gone.\n * Nulls the cached `_lease` and `_sandboxId` and throws\n * {@link SandboxDestroyedError}. Callers (typically `SandboxFleet`) must\n * catch this, clear the stale binding, and reprovision + replay.\n * - **Persistent transport failure** (both WS attempts close without an\n * `exit` frame against a live sandbox) → {@link SandboxExecTransportError}\n * with WebSocket close diagnostics.\n * - **Other `PlatformApiError`s** (404/500/501) propagate directly.\n * - **Real command result** (exit code from Railway's exit frame, or\n * `timedOut: true`) returns normally.\n *\n * Returns a result with a real `exitCode` OR `timedOut: true`. Never\n * returns `{ exitCode: null, timedOut: false }` — that case throws.\n */\n private async _runDirectExec(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n ): Promise<{ exitCode: number | null; stdout: string; stderr: string; timedOut: boolean }> {\n // Filter undefined values out of the env overlay so we match the\n // Record<string, string> shape execViaLease expects. `ExecuteCommandOptions.env`\n // is NodeJS.ProcessEnv (string | undefined).\n const filteredEnv = options?.env\n ? Object.fromEntries(\n Object.entries(options.env).filter((entry): entry is [string, string] => entry[1] !== undefined),\n )\n : undefined;\n\n let lastResult: Awaited<ReturnType<typeof execViaLease>> | undefined;\n let lastLease: (ExecLease & { expiresAtMs: number | null }) | undefined;\n let attemptsMade = 0;\n // Two attempts: initial + one retry. On the second attempt we drop the\n // cached lease so we don't reuse a JWT that may itself be the cause of\n // the transport failure — but only if the cache still holds the same\n // lease we just failed against. A concurrent exec sharing this instance\n // may have already cached a fresh, unrelated lease in between, and we\n // must not discard that.\n for (let attempt = 0; attempt < 2; attempt++) {\n if (attempt > 0 && lastLease && this._lease === lastLease) this._lease = null;\n let lease: ExecLease & { expiresAtMs: number | null };\n try {\n lease = await this._ensureLease();\n } catch (error) {\n // 410 → sandbox has been destroyed. Clear all cached state so a\n // reused instance re-provisions cleanly, then hand off to the fleet\n // layer via a typed error. Other PlatformApiErrors (404/500/501)\n // propagate as-is — those are configuration or platform errors, not\n // a \"reprovision me\" signal.\n if (error instanceof PlatformApiError && error.status === 410) {\n this._lease = null;\n const priorSandboxId = this._sandboxId;\n this._sandboxId = undefined;\n throw new SandboxDestroyedError(\n `Sandbox ${priorSandboxId ?? '(unknown)'} was destroyed; /exec-lease returned 410`,\n {\n ...(priorSandboxId && { sandboxId: priorSandboxId }),\n command: fullCommand,\n attempts: attempt + 1,\n },\n );\n }\n throw error;\n }\n lastLease = lease;\n attemptsMade = attempt + 1;\n const result = await execViaLease(lease, {\n command: fullCommand,\n ...(options?.cwd !== undefined && { cwd: options.cwd }),\n ...(filteredEnv !== undefined && { env: filteredEnv }),\n ...(effectiveTimeout != null && effectiveTimeout > 0 && { timeoutMs: effectiveTimeout }),\n ...(this._webSocketFactory && { webSocketFactory: this._webSocketFactory }),\n });\n lastResult = result;\n // `null` exitCode with `timedOut: false` means the socket closed\n // without an exit frame — a transport failure (handshake stalled,\n // mid-stream drop, expired token). Any other outcome (real exit code\n // or timed-out) is a valid result and we return it.\n if (result.exitCode !== null || result.timedOut) return result;\n }\n\n // Both attempts failed at the transport layer against a live sandbox.\n // Surface a loud, typed error with close diagnostics so callers can\n // distinguish \"your command failed\" from \"the sandbox transport is\n // broken.\"\n const result = lastResult!;\n const lease = lastLease!;\n // The lease from the failed second attempt is still cached; drop it so\n // the next `executeCommand` doesn't waste its first attempt on the same\n // implicated JWT before minting fresh. Identity-check first so a\n // concurrent exec that has already cached a fresh, unrelated lease\n // isn't collateral-damaged.\n if (this._lease === lease) this._lease = null;\n throw new SandboxExecTransportError(\n `Direct-exec transport failed for sandbox ${this._sandboxId ?? '(unknown)'} after ${attemptsMade} attempt(s)` +\n (result.closeCode !== undefined\n ? ` (close ${result.closeCode}${result.closeReason ? ` ${result.closeReason}` : ''})`\n : ''),\n {\n ...(this._sandboxId && { sandboxId: this._sandboxId }),\n command: fullCommand,\n attempts: attemptsMade,\n opened: result.opened ?? false,\n ...(result.closeCode !== undefined && { closeCode: result.closeCode }),\n ...(result.closeReason !== undefined && { closeReason: result.closeReason }),\n wsEndpoint: lease.wsEndpoint,\n },\n );\n }\n\n /**\n * Try to run the exec against the in-sandbox sidecar over Railway's private\n * network. Returns the result on success (including non-zero exit codes and\n * timeouts — those are real command results, not failures). Returns\n * `undefined` when the caller should fall back to the lease path:\n *\n * - Transport failure (connection refused, mid-stream drop, no `exit`\n * frame). The registry entry is evicted so subsequent execs skip the\n * private-net dial until the sidecar re-registers.\n * - Sidecar answered with a non-2xx HTTP status. Registry is left intact —\n * the address is still valid; something else is wrong (bad request,\n * sidecar bug). Only this specific exec falls back.\n */\n private async _tryExecViaPrivateNetwork(\n instanceUrl: string,\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n ): Promise<PrivateNetExecResult | undefined> {\n // Match the env-filter dance in `_runDirectExec`: ExecuteCommandOptions.env\n // is NodeJS.ProcessEnv (string | undefined) but the wire body wants a\n // Record<string, string>.\n const filteredEnv = options?.env\n ? Object.fromEntries(\n Object.entries(options.env).filter((entry): entry is [string, string] => entry[1] !== undefined),\n )\n : undefined;\n\n const execOptions: PrivateNetExecOptions = {\n command: fullCommand,\n ...(options?.cwd !== undefined && { cwd: options.cwd }),\n ...(filteredEnv !== undefined && { env: filteredEnv }),\n ...(effectiveTimeout != null && effectiveTimeout > 0 && { timeoutMs: effectiveTimeout }),\n ...(this._privateNetFetch && { fetch: this._privateNetFetch }),\n };\n\n let result: PrivateNetExecResult;\n try {\n result = await execViaPrivateNetwork(instanceUrl, execOptions);\n } catch (error) {\n if (error instanceof PrivateNetExecHttpError) {\n // Application-level failure from a reachable sidecar. Fall back for\n // this call, but do NOT evict the registry entry — future calls\n // should still prefer the private-network path.\n return undefined;\n }\n // Anything else escaping is unexpected (e.g. options validation). Treat\n // it like a transport failure so we don't wedge the caller.\n this._invalidateAddress();\n return undefined;\n }\n\n // A timeout is always the caller's answer, never a lease-fallback trigger:\n // the sidecar may already be running the command, so re-executing on the\n // lease path would double-run non-idempotent work (rm, git push, DB\n // migrations) and return the second result instead of `timedOut: true`.\n // If we never opened the response (pre-headers abort), the address is\n // still evicted so subsequent execs skip a dial we already know is slow —\n // but the timed-out result itself flows back to the caller unchanged.\n if (result.timedOut) {\n if (!result.opened) this._invalidateAddress();\n return result;\n }\n\n // A completed exec (real exit code) is a valid result — hand it back even\n // if exitCode is non-zero. Only evict when the transport itself failed:\n // never opened, or opened without an exit frame. `opened=false` here\n // means connection refused (no timeout to disambiguate).\n const transportFailed = !result.opened || result.exitCode === null;\n if (transportFailed) {\n this._invalidateAddress();\n return undefined;\n }\n\n return result;\n }\n\n /**\n * Evict this sandbox's entry from the address registry after an observed\n * transport failure. The entry stays gone until the next start() re-reads\n * `instanceUrl` from a workspace-proxy response — until then, execs skip\n * the private-net dial and go straight to the lease path.\n */\n private _invalidateAddress(): void {\n if (this._sandboxId) this._addressRegistry?.delete(this._sandboxId);\n }\n\n /**\n * Return a cached exec lease, minting a fresh one when the cache is empty\n * or the JWT is within {@link LEASE_REFRESH_MARGIN_MS} of `expiresAt`.\n *\n * Callers are expected to be on the \"sandbox is running\" path; we don't\n * re-check `_sandboxId` here because `executeCommand` already gated on it.\n */\n private async _ensureLease(): Promise<ExecLease & { expiresAtMs: number | null }> {\n const now = Date.now();\n // Cache hit only when we know the expiry AND we're comfortably before it.\n // A null `expiresAtMs` means the provider didn't disclose a TTL — treat\n // that as \"refresh every call\" rather than \"cache forever\", so a token\n // that turns out to be short-lived can't wedge the sandbox until restart.\n if (this._lease && this._lease.expiresAtMs !== null && this._lease.expiresAtMs - LEASE_REFRESH_MARGIN_MS > now) {\n return this._lease;\n }\n // Coalesce concurrent mints on a cold/expired cache.\n if (this._leaseInFlight) return this._leaseInFlight;\n if (!this._sandboxId) throw new SandboxNotReadyError(this.id);\n const sandboxId = this._sandboxId;\n const inFlight = (async () => {\n const response = await this._client.request(`/sandbox/${encodeURIComponent(sandboxId)}/exec-lease`, {\n method: 'POST',\n });\n const json = (await response.json()) as ExecLeaseResponse;\n const expiresAtMs = json.expiresAt ? Date.parse(json.expiresAt) : null;\n const lease = {\n jwt: json.jwt,\n wsEndpoint: json.wsEndpoint,\n subprotocol: json.subprotocol,\n expiresAt: json.expiresAt,\n // Guard against `Date.parse` returning NaN for malformed values by\n // treating them as \"no expiry known\", which forces a mint every call\n // rather than silently caching a broken lease forever.\n expiresAtMs: expiresAtMs !== null && !Number.isNaN(expiresAtMs) ? expiresAtMs : null,\n };\n this._lease = lease;\n return lease;\n })();\n this._leaseInFlight = inFlight;\n try {\n return await inFlight;\n } finally {\n // Clear on both success and failure so a failed mint doesn't wedge\n // future callers into awaiting the same rejected promise forever.\n if (this._leaseInFlight === inFlight) this._leaseInFlight = null;\n }\n }\n\n async getInfo(): Promise<SandboxInfo> {\n if (!this._sandboxId) {\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 };\n }\n // Skip the workspace-proxy round-trip when the address registry has an\n // entry for this sandbox — a live entry is proof that start() saw an\n // `instanceUrl` on the create/reattach response and that no exec since\n // has evicted it via a transport failure. Serving `getInfo()` from\n // local state here removes the per-poll `GET /sandbox/:id` hit that\n // otherwise triggers a Railway GraphQL call + `sandboxExec` awk on\n // `/proc/net/if_inet6` inside the proxy.\n //\n // Note: this does NOT probe the sidecar. If the sandbox has been\n // destroyed out-of-band the next exec will fail transport, evict the\n // registry entry, and a subsequent getInfo() will fall through to the\n // proxy below and observe the true status.\n if (this._addressRegistry?.get(this._sandboxId)) {\n return {\n id: this._sandboxId,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n sandboxId: this._sandboxId,\n },\n };\n }\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`);\n const json = (await response.json()) as CreateSandboxResponse;\n return {\n id: json.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: json.createdAt ? new Date(json.createdAt) : (this._createdAt ?? new Date()),\n metadata: {\n // The platform assigns its own sandbox id on create (the advisory id\n // sent in the POST body is not honored). Expose it so callers that\n // persist a reattach id (e.g. the Factory sandbox fleet, which reads\n // `metadata.sandboxId`) store the id the proxy actually recognizes\n // instead of the locally generated construction id.\n sandboxId: json.id,\n providerResourceId: json.providerResourceId ?? undefined,\n platformStatus: json.status,\n },\n };\n }\n\n getInstructions(opts?: { requestContext?: RequestContext }): string {\n const defaultInstructions = `Platform sandbox${this._sandboxId ? ` ${this._sandboxId}` : ''}. Execute commands with the sandbox command APIs.`;\n if (typeof this._instructionsOverride === 'function') {\n return this._instructionsOverride({ defaultInstructions, requestContext: opts?.requestContext });\n }\n if (typeof this._instructionsOverride === 'string') return this._instructionsOverride;\n return defaultInstructions;\n }\n}\n","import type { FilesystemProvider, SandboxProvider } from '@mastra/core/editor';\nimport type { PlatformFilesystemOptions } from './filesystem.js';\nimport { PlatformFilesystem } from './filesystem.js';\nimport type { PlatformSandboxOptions } from './sandbox.js';\nimport { PlatformSandbox } from './sandbox.js';\n\nexport const platformSandboxProvider: SandboxProvider<PlatformSandboxOptions> = {\n id: 'platform',\n name: 'Mastra Platform Sandbox',\n description: 'Environment-scoped sandbox execution through Mastra Platform workspace proxy',\n configSchema: {\n type: 'object',\n properties: {\n accessToken: {\n type: 'string',\n description: 'Mastra Platform access token (falls back to MASTRA_PLATFORM_ACCESS_TOKEN)',\n },\n projectId: { type: 'string', description: 'Platform project ID (falls back to MASTRA_PROJECT_ID)' },\n environmentId: { type: 'string', description: 'Platform environment ID (falls back to MASTRA_ENVIRONMENT_ID)' },\n sandboxId: { type: 'string', description: 'Reattach to an existing Platform sandbox by ID' },\n idleTimeoutMinutes: { type: 'number', description: 'Minutes before the sandbox can be destroyed while idle' },\n networkIsolation: {\n type: 'string',\n description: 'Network isolation mode',\n enum: ['ISOLATED', 'PRIVATE'],\n default: 'ISOLATED',\n },\n env: { type: 'object', description: 'Environment variables', additionalProperties: { type: 'string' } },\n timeout: { type: 'number', description: 'Default command timeout in ms' },\n },\n },\n createSandbox: config => new PlatformSandbox(config),\n};\n\nexport const platformFilesystemProvider: FilesystemProvider<PlatformFilesystemOptions> = {\n id: 'platform',\n name: 'Mastra Platform Filesystem',\n description: 'Bucket-backed filesystem access through Mastra Platform workspace proxy',\n configSchema: {\n type: 'object',\n properties: {\n accessToken: {\n type: 'string',\n description: 'Mastra Platform access token (falls back to MASTRA_PLATFORM_ACCESS_TOKEN)',\n },\n projectId: { type: 'string', description: 'Platform project ID (falls back to MASTRA_PROJECT_ID)' },\n bucketName: {\n type: 'string',\n description: 'Platform workspace bucket name (falls back to MASTRA_PLATFORM_BUCKET_NAME)',\n },\n readOnly: { type: 'boolean', description: 'Mount as read-only', default: false },\n },\n },\n createFilesystem: config => new PlatformFilesystem(config),\n};\n","/**\n * In-process `sandboxId → instanceUrl` registry populated by\n * {@link PlatformSandbox.start} from the `instanceUrl` field workspace-proxy\n * includes on create + get responses, and consumed by\n * {@link PlatformSandbox.executeCommand} on the private-network fast path.\n *\n * The registry lives in the same Node process as the `PlatformSandbox`\n * consumer — for shipyard, that is the Mastra runtime deployed from\n * `mastracode/web`. There is no receiver route and no cross-service dance;\n * the address is just a field copied from a response the runtime already\n * receives.\n *\n * State intentionally does not persist:\n *\n * - The IPv6 rotates on every sandbox recreate.\n * - The URL has no meaning after the sandbox is destroyed.\n * - The live sandbox binding, session context, and lease all die with the\n * runtime process; the address dying with them is correct.\n * - The proxy's `environment_sandboxes.instance_url` column is the durable\n * source of truth — a runtime restart re-populates the registry on the\n * next `start()` / reattach from the proxy's response.\n */\n\nimport type { SandboxAddressRegistry } from './sandbox.js';\n\n/**\n * Concrete in-process {@link SandboxAddressRegistry}. Backed by a `Map`; no\n * eviction policy, no TTL — entries live until an observed transport failure\n * calls `delete`, until the sandbox is explicitly destroyed, or until the\n * process exits.\n */\nexport class InProcessSandboxAddressRegistry implements SandboxAddressRegistry {\n readonly #map = new Map<string, string>();\n\n get(sandboxId: string): string | undefined {\n return this.#map.get(sandboxId);\n }\n\n /**\n * Populate or overwrite the address for a sandbox. Called by\n * {@link PlatformSandbox.start} on every fresh provision and every reattach;\n * overwriting is intentional so a re-provision with a fresh IPv6 heals the\n * map without a branch.\n */\n set(sandboxId: string, instanceUrl: string): void {\n this.#map.set(sandboxId, instanceUrl);\n }\n\n delete(sandboxId: string): void {\n this.#map.delete(sandboxId);\n }\n\n /**\n * Test-only introspection. Not part of {@link SandboxAddressRegistry} —\n * production callers must not read the registry as a whole.\n */\n get size(): number {\n return this.#map.size;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["#map"],"sources":["../src/client.ts","../src/filesystem.ts","../src/direct-exec.ts","../src/private-net-exec.ts","../src/sandbox.ts","../src/provider.ts","../src/address-registry.ts"],"sourcesContent":["export interface PlatformClientOptions {\n accessToken?: string;\n projectId?: string;\n actingUserId?: string;\n /**\n * Advisory correlation id for the factory session driving this client.\n * Sent as `x-mastra-session-id` on every proxy request so proxy-side logs\n * can be joined back to the calling session without a multi-store hand-join\n * (`threadId → sessionId → sandboxId → providerResourceId`). Never used for\n * authorization — the Bearer token remains the only credential.\n */\n sessionId?: string;\n /**\n * Advisory correlation id for the factory thread, sent as\n * `x-mastra-thread-id` when present. See {@link PlatformClientOptions.sessionId}.\n */\n threadId?: string;\n fetch?: typeof fetch;\n}\n\nexport interface PlatformRequestOptions extends RequestInit {\n query?: Record<string, string | number | boolean | undefined>;\n}\n\nconst DEFAULT_PROXY_URL = 'https://workspaces.mastra.ai';\n\n/**\n * Default per-request timeout for calls to the workspace proxy. Applied only\n * when the caller doesn't already pass an `AbortSignal`. Long-running routes\n * (e.g. `POST /sandbox/:id/exec`) pass their own longer signal.\n */\nconst DEFAULT_REQUEST_TIMEOUT_MS = 60_000;\n\nexport function requireOption(value: string | undefined, name: string): string {\n if (!value) throw new Error(`${name} is required`);\n return value;\n}\n\nexport function resolvePlatformOptions(options: PlatformClientOptions) {\n return {\n accessToken: requireOption(options.accessToken ?? process.env.MASTRA_PLATFORM_ACCESS_TOKEN, 'accessToken'),\n projectId: requireOption(options.projectId ?? process.env.MASTRA_PROJECT_ID, 'projectId'),\n actingUserId: options.actingUserId?.trim() || undefined,\n proxyUrl: (process.env.MASTRA_WORKSPACE_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\\/$/, ''),\n sessionId: options.sessionId,\n threadId: options.threadId,\n fetch: options.fetch ?? fetch,\n };\n}\n\n/**\n * Structured error shape returned by the workspace proxy. All routes emit\n * `{ error: { message, type } }` on failure — see servers/workspace-proxy in\n * the Platform repo. Kept as a wire-level type so callers can switch on\n * `error.code` without re-parsing `error.body`.\n */\nexport interface PlatformProxyError {\n message: string;\n /** Machine-readable error kind, e.g. `not_found`, `invalid_request`, `authentication_error`. */\n type: string;\n}\n\nfunction parseProxyError(body: string): PlatformProxyError | undefined {\n if (!body) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return undefined;\n }\n if (typeof parsed !== 'object' || parsed === null) return undefined;\n const err = (parsed as { error?: unknown }).error;\n if (typeof err !== 'object' || err === null) return undefined;\n const { message, type } = err as { message?: unknown; type?: unknown };\n if (typeof message !== 'string' || typeof type !== 'string') return undefined;\n return { message, type };\n}\n\nexport class PlatformApiError extends Error {\n readonly status: number;\n readonly body: string;\n /** Machine-readable proxy error kind (e.g. `not_found`), when the response body matches `{ error: { message, type } }`. */\n readonly code: string | undefined;\n /** Human-readable proxy error message, when the response body matches `{ error: { message, type } }`. */\n readonly proxyMessage: string | undefined;\n\n constructor(status: number, body: string) {\n const parsed = parseProxyError(body);\n const summary = parsed ? `${parsed.type}: ${parsed.message}` : body;\n super(`Platform proxy request failed with ${status}${summary ? `: ${summary}` : ''}`);\n this.name = 'PlatformApiError';\n this.status = status;\n this.body = body;\n this.code = parsed?.type;\n this.proxyMessage = parsed?.message;\n }\n}\n\nexport class PlatformClient {\n readonly accessToken: string;\n readonly projectId: string;\n readonly actingUserId: string | undefined;\n readonly proxyUrl: string;\n /** Advisory session correlation id — see {@link PlatformClientOptions.sessionId}. */\n readonly sessionId: string | undefined;\n /** Advisory thread correlation id — see {@link PlatformClientOptions.threadId}. */\n readonly threadId: string | undefined;\n readonly fetch: typeof fetch;\n\n constructor(options: PlatformClientOptions) {\n const resolved = resolvePlatformOptions(options);\n this.accessToken = resolved.accessToken;\n this.projectId = resolved.projectId;\n this.actingUserId = resolved.actingUserId;\n this.proxyUrl = resolved.proxyUrl;\n this.sessionId = resolved.sessionId;\n this.threadId = resolved.threadId;\n this.fetch = resolved.fetch;\n }\n\n async request(path: string, options: PlatformRequestOptions = {}): Promise<Response> {\n const url = new URL(`${this.proxyUrl}/v1/projects/${encodeURIComponent(this.projectId)}${path}`);\n for (const [key, value] of Object.entries(options.query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n\n const headers = new Headers(options.headers);\n headers.set('authorization', `Bearer ${this.accessToken}`);\n if (this.actingUserId) headers.set('x-acting-user-id', this.actingUserId);\n // Advisory correlation headers — the proxy folds them into its log lines\n // so proxy-side events can be joined back to the calling factory session\n // without a cross-store hand-join. Unknown headers are passthrough for\n // older proxies; these are never used for authorization.\n if (this.sessionId) headers.set('x-mastra-session-id', this.sessionId);\n if (this.threadId) headers.set('x-mastra-thread-id', this.threadId);\n\n // Strip our helper-only field so the underlying fetch sees a valid RequestInit.\n const { query: _query, ...fetchOptions } = options;\n // Apply a default timeout only when the caller didn't already supply an\n // AbortSignal — long-running routes (exec) provide their own longer signal.\n const signal = fetchOptions.signal ?? AbortSignal.timeout(DEFAULT_REQUEST_TIMEOUT_MS);\n const response = await this.fetch(url, { ...fetchOptions, headers, signal });\n if (!response.ok) {\n throw new PlatformApiError(response.status, await response.text());\n }\n return response;\n }\n}\n","import { Buffer } from 'node:buffer';\nimport nodePath from 'node:path';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type {\n CopyOptions,\n FileContent,\n FileEntry,\n FileStat,\n FilesystemIcon,\n FilesystemInfo,\n InstructionsOption,\n ListOptions,\n MastraFilesystemOptions,\n ProviderStatus,\n ReadOptions,\n RemoveOptions,\n WriteOptions,\n} from '@mastra/core/workspace';\nimport { FileExistsError, FileNotFoundError, MastraFilesystem, WorkspaceReadOnlyError } from '@mastra/core/workspace';\nimport type { PlatformClientOptions } from './client.js';\nimport { PlatformClient } from './client.js';\n\ninterface ProxyListResponse {\n contents?: Array<{ key?: string; size?: number; lastModified?: string }>;\n commonPrefixes?: string[];\n}\n\nexport interface PlatformFilesystemOptions extends PlatformClientOptions, MastraFilesystemOptions {\n id?: string;\n bucketName?: string;\n readOnly?: boolean;\n displayName?: string;\n icon?: FilesystemIcon;\n description?: string;\n instructions?: InstructionsOption;\n}\n\nfunction normalizePath(input: string): string {\n if (!input || input === '.') return '/';\n let normalized = input.startsWith('/') ? input : `/${input}`;\n normalized = nodePath.posix.normalize(normalized);\n return normalized === '.' ? '/' : normalized;\n}\n\nfunction keyFromPath(path: string): string {\n const normalized = normalizePath(path);\n return normalized === '/' ? '' : normalized.slice(1);\n}\n\n/**\n * Encode each `/`-delimited segment of an object key with `encodeURIComponent`\n * so reserved URL characters (`?`, `#`, `%`, `&`, `+`, spaces, etc.) are\n * treated as part of the key instead of URL syntax. Kept segment-aware so\n * `/` continues to act as a path separator on the wire.\n */\nfunction encodeKeyPath(key: string): string {\n return key.split('/').map(encodeURIComponent).join('/');\n}\n\nfunction nameFromPath(path: string): string {\n const normalized = normalizePath(path);\n if (normalized === '/') return '';\n return normalized.slice(normalized.lastIndexOf('/') + 1);\n}\n\nfunction contentToBody(content: FileContent): string | Buffer {\n if (typeof content === 'string') return content;\n return Buffer.from(content);\n}\n\nfunction headerDate(headers: Headers, name: string): Date {\n const value = headers.get(name);\n return value ? new Date(value) : new Date(0);\n}\n\nfunction headerSize(headers: Headers): number {\n const value = headers.get('content-length');\n return value ? Number(value) : 0;\n}\n\nfunction isNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && 'status' in error && error.status === 404;\n}\n\nexport class PlatformFilesystem extends MastraFilesystem {\n readonly id: string;\n readonly name = 'PlatformFilesystem';\n readonly provider = 'platform';\n readonly readOnly?: boolean;\n readonly displayName?: string;\n readonly icon: FilesystemIcon;\n readonly description?: string;\n status: ProviderStatus = 'pending';\n\n private readonly _client: PlatformClient;\n private readonly _bucketName: string;\n private readonly _instructionsOverride?: InstructionsOption;\n\n constructor(options: PlatformFilesystemOptions = {}) {\n super({ ...options, name: 'PlatformFilesystem' });\n this.id = options.id ?? this.generateId();\n this._bucketName = options.bucketName ?? process.env.MASTRA_PLATFORM_BUCKET_NAME ?? '';\n if (!this._bucketName) throw new Error('bucketName is required');\n this.readOnly = options.readOnly;\n this.displayName = options.displayName;\n this.icon = options.icon ?? 'cloud';\n this.description = options.description;\n this._instructionsOverride = options.instructions;\n this._client = new PlatformClient(options);\n }\n\n private generateId(): string {\n return `platform-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n async readFile(path: string, options?: ReadOptions): Promise<string | Buffer> {\n await this.ensureReady();\n let response: Response;\n try {\n response = await this._client.request(\n `/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`,\n );\n } catch (error) {\n if (isNotFound(error)) throw new FileNotFoundError(path);\n throw error;\n }\n const buffer = Buffer.from(await response.arrayBuffer());\n return options?.encoding ? buffer.toString(options.encoding) : buffer;\n }\n\n async writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('writeFile');\n const headers: Record<string, string> = {};\n if (options?.mimeType) headers['content-type'] = options.mimeType;\n if (options?.overwrite === false) headers['if-none-match'] = '*';\n try {\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`, {\n method: 'PUT',\n headers,\n body: contentToBody(content),\n });\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'status' in error && error.status === 412) {\n throw new FileExistsError(path);\n }\n throw error;\n }\n }\n\n /**\n * Append bytes to a file.\n *\n * **Not atomic.** Object storage behind the workspace proxy has no native\n * append or compare-and-swap primitive, so this implementation is a\n * read-modify-write: it reads the current contents, concatenates the new\n * bytes, and PUTs the whole object back. Concurrent `appendFile` calls to\n * the same path can overwrite each other's writes (\"last write wins\").\n * Use `writeFile` with distinct keys for concurrent writers.\n */\n async appendFile(path: string, content: FileContent): Promise<void> {\n const existing = (await this.exists(path)) ? await this.readFile(path) : Buffer.alloc(0);\n await this.writeFile(\n path,\n Buffer.concat([Buffer.isBuffer(existing) ? existing : Buffer.from(existing), Buffer.from(content)]),\n );\n }\n\n async deleteFile(path: string, options?: RemoveOptions): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('deleteFile');\n try {\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`, {\n method: 'DELETE',\n query: { recursive: options?.recursive },\n });\n } catch (error) {\n if (isNotFound(error) && options?.force) return;\n if (isNotFound(error)) throw new FileNotFoundError(path);\n throw error;\n }\n }\n\n async copyFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('copyFile');\n // The workspace proxy's `?op=copy` route always overwrites the destination;\n // there's no conditional wire field to prevent it. Reject the option\n // explicitly instead of silently overwriting when the caller asked us not to.\n if (options?.overwrite === false) {\n throw new Error('PlatformFilesystem.copyFile does not support overwrite: false — the proxy always overwrites.');\n }\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(src))}`, {\n method: 'POST',\n query: { op: 'copy' },\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ destination: keyFromPath(dest) }),\n });\n }\n\n async moveFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('moveFile');\n // Same rationale as copyFile: `?op=rename` always overwrites.\n if (options?.overwrite === false) {\n throw new Error('PlatformFilesystem.moveFile does not support overwrite: false — the proxy always overwrites.');\n }\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(src))}`, {\n method: 'POST',\n query: { op: 'rename' },\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ destination: keyFromPath(dest) }),\n });\n }\n\n async mkdir(path: string, _options?: { recursive?: boolean }): Promise<void> {\n await this.ensureReady();\n if (this.readOnly) throw new WorkspaceReadOnlyError('mkdir');\n await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`, {\n method: 'POST',\n query: { op: 'mkdir' },\n });\n }\n\n async rmdir(path: string, options?: RemoveOptions): Promise<void> {\n await this.deleteFile(path.endsWith('/') ? path : `${path}/`, { recursive: true, force: options?.force });\n }\n\n async readdir(path: string, options?: ListOptions): Promise<FileEntry[]> {\n await this.ensureReady();\n const prefix = keyFromPath(path);\n const response = await this._client.request(\n `/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(prefix)}`,\n {\n query: {\n delimiter: options?.recursive ? undefined : '/',\n prefix: prefix ? `${prefix.replace(/\\/$/, '')}/` : undefined,\n },\n },\n );\n const json = (await response.json()) as ProxyListResponse;\n return [\n ...(json.commonPrefixes ?? []).map(prefix => ({\n name: nameFromPath(prefix.replace(/\\/$/, '')),\n type: 'directory' as const,\n })),\n ...(json.contents ?? [])\n .filter(object => object.key && !object.key.endsWith('/'))\n .map(object => ({\n name: nameFromPath(object.key!),\n type: 'file' as const,\n size: object.size,\n })),\n ].filter(\n entry => !options?.extension || entry.type === 'directory' || matchesExtension(entry.name, options.extension),\n );\n }\n\n async exists(path: string): Promise<boolean> {\n try {\n await this.stat(path);\n return true;\n } catch (error) {\n if (isNotFound(error) || error instanceof FileNotFoundError) return false;\n throw error;\n }\n }\n\n async stat(path: string): Promise<FileStat> {\n await this.ensureReady();\n const normalized = normalizePath(path);\n if (normalized === '/') {\n return { name: '', path: '/', type: 'directory', size: 0, createdAt: new Date(0), modifiedAt: new Date(0) };\n }\n let response: Response;\n try {\n response = await this._client.request(\n `/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path))}`,\n {\n method: 'HEAD',\n },\n );\n } catch (error) {\n if (isNotFound(error)) throw new FileNotFoundError(path);\n throw error;\n }\n return {\n name: nameFromPath(path),\n path: normalized,\n type: normalized.endsWith('/') ? 'directory' : 'file',\n size: headerSize(response.headers),\n createdAt: headerDate(response.headers, 'last-modified'),\n modifiedAt: headerDate(response.headers, 'last-modified'),\n mimeType: response.headers.get('content-type') ?? undefined,\n };\n }\n\n realpath(path: string): Promise<string> {\n return Promise.resolve(normalizePath(path));\n }\n\n getInstructions(opts?: { requestContext?: RequestContext }): string {\n const defaultInstructions = `Platform filesystem backed by Mastra Platform bucket ${this._bucketName}. Use absolute workspace paths.`;\n if (typeof this._instructionsOverride === 'function') {\n return this._instructionsOverride({ defaultInstructions, requestContext: opts?.requestContext });\n }\n if (typeof this._instructionsOverride === 'string') return this._instructionsOverride;\n return defaultInstructions;\n }\n\n getInfo(): FilesystemInfo<{ bucketName: string; displayName?: string; description?: string }> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n readOnly: this.readOnly,\n icon: this.icon,\n metadata: {\n bucketName: this._bucketName,\n ...(this.displayName && { displayName: this.displayName }),\n ...(this.description && { description: this.description }),\n },\n };\n }\n}\n\nfunction matchesExtension(name: string, extension: string | string[]): boolean {\n const extensions = Array.isArray(extension) ? extension : [extension];\n return extensions.some(ext => name.endsWith(ext));\n}\n","/**\n * Direct exec client — opens Railway's tcp-proxy exec WebSocket directly using\n * a short-lived JWT minted by the workspace proxy's exec-lease endpoint. This\n * removes the platform data plane from the exec stdout/stderr path entirely\n * (see `docs/factory/direct-sandbox-connection.md` in the Platform repo),\n * cutting payload-scaled Cloud Run egress and RTT for commands like\n * `pnpm install` that stream tens of MB of output.\n *\n * The frame protocol below mirrors `connectExecWs()` in `railway@3.5.5`\n * (`workspaces/railway/node_modules/railway/dist/index.js`). The `railway`\n * SDK's version is pinned on both sides (platform + here); a version bump\n * signals the protocol may have drifted and this module must be revisited.\n */\n\n/** Byte-0 tag on binary WS frames for stdout output. */\nconst STDOUT_FRAME = 1;\n/** Byte-0 tag on binary WS frames for stderr output. */\nconst STDERR_FRAME = 3;\n/**\n * Upper bound on how long we'll wait for the WebSocket to open when the\n * caller didn't supply a `timeoutMs`. Guards against a stalled TLS/WS\n * handshake leaving the promise unresolved forever. Not applied once the\n * socket has opened — a caller with no timeout has opted in to unbounded\n * command runtime, just not to unbounded connection setup.\n */\nconst HANDSHAKE_DEADLINE_MS = 30_000;\n\n/**\n * Minimal WebSocket surface this module depends on. Matches both the browser\n * `WebSocket` global and Node 22+'s built-in `WebSocket`. Extracted so tests\n * can inject a fake without pulling in `ws` or jsdom.\n */\nexport interface DirectExecWebSocket {\n binaryType: 'blob' | 'arraybuffer';\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code: number; reason: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\n\n/**\n * Factory that opens a WebSocket to `endpoint` with the given subprotocols.\n * Defaults to the global `WebSocket` when omitted, which works on Node 22+\n * (the package's minimum) and in the browser. Tests inject a fake here.\n */\nexport type DirectExecWebSocketFactory = (endpoint: string, subprotocols: string[]) => DirectExecWebSocket;\n\n/** Lease payload returned by `POST /v1/projects/:projectId/sandbox/:sandboxId/exec-lease`. */\nexport interface ExecLease {\n jwt: string;\n wsEndpoint: string;\n subprotocol: string;\n /** ISO-8601 UTC. Null when the provider issues a JWT without an `exp` claim. */\n expiresAt: string | null;\n}\n\n/** Inputs to a direct exec invocation. Mirrors the shape of the `/exec` route body. */\nexport interface DirectExecOptions {\n command: string;\n cwd?: string;\n env?: Record<string, string>;\n /**\n * Wall-clock cap for the exec. When elapsed, we close the socket and\n * return `{timedOut: true, exitCode: 124}` matching the semantics of the\n * proxy's `/exec` route.\n */\n timeoutMs?: number;\n onStdout?: (chunk: string) => void;\n onStderr?: (chunk: string) => void;\n /** Injected for tests. Defaults to `globalThis.WebSocket`. */\n webSocketFactory?: DirectExecWebSocketFactory;\n}\n\n/**\n * Result of a direct exec. Shape matches the workspace-proxy `/exec` response\n * so the caller (`PlatformSandbox.executeCommand`) can hand it back with no\n * translation.\n *\n * `exitCode` is `null` when the socket closed without an `exit` frame AND the\n * exec did not time out (rare — usually a mid-stream network drop). Callers\n * currently coerce `null` to `1` upstream; kept nullable here to preserve the\n * distinction for future observability.\n */\nexport interface DirectExecResult {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n truncated: boolean;\n timedOut: boolean;\n /**\n * WebSocket close metadata. Populated on any close (normal or transport\n * failure). `opened` distinguishes handshake failures (never opened) from\n * mid-stream drops. Callers use this for diagnostic logging; not part of\n * the CommandResult contract.\n */\n closeCode?: number;\n closeReason?: string;\n opened?: boolean;\n}\n\nconst DEFAULT_WS_FACTORY: DirectExecWebSocketFactory = (endpoint, subprotocols) => {\n const WS = (globalThis as { WebSocket?: unknown }).WebSocket as\n | (new (url: string, protocols: string[]) => DirectExecWebSocket)\n | undefined;\n if (!WS) {\n throw new Error(\n 'Direct exec requires a WebSocket implementation. Node 22+ provides one globally; on older runtimes, pass webSocketFactory explicitly.',\n );\n }\n return new WS(endpoint, subprotocols);\n};\n\n/**\n * Open the provider exec WebSocket using `lease`, run `command`, and resolve\n * with the accumulated stdout/stderr + exit code. See the module docstring\n * for the wire protocol reference.\n *\n * The client sends `stdin_close` immediately after `init_exec`, matching the\n * SDK's own one-shot exec behavior — we never stream stdin from the caller.\n */\nexport function execViaLease(lease: ExecLease, options: DirectExecOptions): Promise<DirectExecResult> {\n const factory = options.webSocketFactory ?? DEFAULT_WS_FACTORY;\n const stdoutDecoder = new TextDecoder();\n const stderrDecoder = new TextDecoder();\n\n return new Promise<DirectExecResult>(resolve => {\n let stdout = '';\n let stderr = '';\n let exitCode: number | null = null;\n let timedOut = false;\n let settled = false;\n let opened = false;\n let closeCode: number | undefined;\n let closeReason: string | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let handshakeTimer: ReturnType<typeof setTimeout> | undefined;\n\n const settle = () => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n if (handshakeTimer) clearTimeout(handshakeTimer);\n // Flush any bytes still buffered in the decoders. A stream:true decode\n // holds trailing partial multi-byte sequences until the next chunk, so\n // without a flush the final char(s) of a UTF-8 stream can be dropped.\n const stdoutTail = stdoutDecoder.decode();\n if (stdoutTail) {\n stdout += stdoutTail;\n options.onStdout?.(stdoutTail);\n }\n const stderrTail = stderrDecoder.decode();\n if (stderrTail) {\n stderr += stderrTail;\n options.onStderr?.(stderrTail);\n }\n try {\n socket.close(1000, '');\n } catch {\n /* already closed */\n }\n resolve({\n exitCode,\n stdout,\n stderr,\n truncated: false,\n timedOut,\n ...(closeCode !== undefined && { closeCode }),\n ...(closeReason !== undefined && { closeReason }),\n opened,\n });\n };\n\n // Arm the timeout BEFORE we open the socket so a stalled handshake can't\n // leave the promise pending. Callers with a positive `timeoutMs` get the\n // wall-clock cap they asked for; callers without one still get a\n // connect-only deadline that clears once the socket opens.\n if (options.timeoutMs !== undefined && options.timeoutMs > 0) {\n timer = setTimeout(() => {\n timedOut = true;\n // 124 matches the proxy's `/exec` semantics (coreutils `timeout`\n // exit code) so callers that switch on exitCode see the same value.\n if (exitCode === null) exitCode = 124;\n settle();\n }, options.timeoutMs);\n } else {\n handshakeTimer = setTimeout(() => {\n // Never opened → treat as a transport failure. Leave exitCode=null\n // so the caller can distinguish this from a normal exit; do not\n // set timedOut (that flag is reserved for the wall-clock case).\n if (!opened) settle();\n }, HANDSHAKE_DEADLINE_MS);\n }\n\n const socket = factory(lease.wsEndpoint, [lease.subprotocol, lease.jwt]);\n socket.binaryType = 'arraybuffer';\n\n socket.onopen = () => {\n opened = true;\n if (handshakeTimer) {\n clearTimeout(handshakeTimer);\n handshakeTimer = undefined;\n }\n const data: Record<string, unknown> = { command: options.command };\n if (options.cwd) data.cwd = options.cwd;\n if (options.env && Object.keys(options.env).length > 0) data.env = options.env;\n socket.send(JSON.stringify({ type: 'init_exec', data }));\n // We never stream stdin for one-shot exec; the SDK does this too, and\n // omitting it can leave the exec hanging waiting on EOF.\n socket.send(JSON.stringify({ type: 'stdin_close' }));\n };\n\n socket.onmessage = event => {\n const { data } = event;\n if (data instanceof ArrayBuffer) {\n handleBinaryFrame(data);\n } else if (typeof data === 'string') {\n handleTextFrame(data);\n }\n };\n\n socket.onclose = event => {\n closeCode = event.code;\n closeReason = event.reason;\n if (!opened) {\n // Never opened — surface as a failure via exitCode=null,\n // truncated=false, timedOut=false so the caller can distinguish\n // it from a normal exit-0 by inspecting `exitCode === null`.\n settle();\n return;\n }\n // Preserve any info captured before close; if the server sent an\n // `exit` frame this is a no-op because settle() already ran.\n settle();\n };\n\n socket.onerror = () => {\n if (settled) return;\n if (!opened) {\n settle();\n }\n // If we're mid-stream and the socket errors, wait for onclose to fire\n // so we settle with whatever output we did receive.\n };\n\n function handleBinaryFrame(buffer: ArrayBuffer) {\n const view = new Uint8Array(buffer);\n if (view.length <= 1) return;\n if (view[0] === STDOUT_FRAME) {\n const chunk = stdoutDecoder.decode(view.subarray(1), { stream: true });\n stdout += chunk;\n options.onStdout?.(chunk);\n } else if (view[0] === STDERR_FRAME) {\n const chunk = stderrDecoder.decode(view.subarray(1), { stream: true });\n stderr += chunk;\n options.onStderr?.(chunk);\n }\n }\n\n function handleTextFrame(text: string) {\n let frame: { type?: string; data?: { exit_code?: number } };\n try {\n frame = JSON.parse(text) as { type?: string; data?: { exit_code?: number } };\n } catch {\n return;\n }\n if (frame.type === 'exit') {\n exitCode = frame.data?.exit_code ?? 0;\n settle();\n }\n // `durable_session` frames are intentionally ignored — we don't reattach\n // or expose session names on the one-shot exec path.\n }\n });\n}\n","/**\n * Private-network exec client — dials the in-sandbox sidecar HTTP server\n * directly over Railway's private IPv6 network, bypassing the workspace-proxy\n * exec lease and the public tcp-proxy WebSocket entirely.\n *\n * Wire spec (matches the sidecar's `POST /exec` route, see\n * `.scratch/factory-deploy/issue-sandbox-sidecar-agent-in-base-image.md` in\n * the Platform repo):\n *\n * - Request: `POST ${instanceUrl}/exec` with JSON body\n * `{command, cwd?, env?, timeoutMs?}`.\n * - Response: `text/plain` NDJSON, one JSON object per line, terminated by an\n * `exit` frame:\n * `{\"type\":\"stdout\",\"data\":\"…\"}`\n * `{\"type\":\"stderr\",\"data\":\"…\"}`\n * `{\"type\":\"exit\",\"code\":<number>}`\n *\n * Auth is *network position* — the private ULA IPv6 space is only reachable\n * from workloads inside this Railway environment, and every workload there is\n * our own code. A bearer secret adds no boundary today so we do not send one;\n * `bearerToken` is accepted here as a defense-in-depth hook for when\n * untrusted-tenant sandboxes eventually share a private network.\n *\n * Return shape mirrors {@link ../direct-exec.ts}'s `DirectExecResult` so the\n * caller in `sandbox.ts` can hand results back with no translation regardless\n * of which transport served the exec.\n */\n\n/**\n * Minimal fetch surface this module depends on. Matches the global `fetch`\n * exposed by Node 22+ (undici) and the browser. Extracted so tests can inject\n * a fake without spinning up a real HTTP server.\n */\nexport type PrivateNetFetch = typeof fetch;\n\n/** Inputs to a private-network exec invocation. Mirrors {@link DirectExecOptions}. */\nexport interface PrivateNetExecOptions {\n command: string;\n cwd?: string;\n env?: Record<string, string>;\n /**\n * Wall-clock cap for the exec. When elapsed, we abort the request and\n * return `{timedOut: true, exitCode: 124}` matching the semantics of\n * `direct-exec.ts` (and the proxy's `/exec` route before it).\n */\n timeoutMs?: number;\n onStdout?: (chunk: string) => void;\n onStderr?: (chunk: string) => void;\n /** Injected for tests. Defaults to `globalThis.fetch`. */\n fetch?: PrivateNetFetch;\n /**\n * Optional bearer secret forwarded as `Authorization: Bearer <token>`.\n * Not required today (network position is the boundary). Present so we can\n * flip on per-sandbox secrets in a follow-up without a client-side reshape.\n */\n bearerToken?: string;\n}\n\n/**\n * Result of a private-network exec. Same shape as `DirectExecResult` — the\n * caller does not care which transport produced it.\n *\n * `exitCode` is `null` when the response body ended without an `exit` frame\n * AND the exec did not time out. That is a transport-level failure (the\n * sidecar died mid-stream or the socket was cut) and callers should treat it\n * as such (invalidate the cached `instanceUrl`, fall back to the lease path).\n */\nexport interface PrivateNetExecResult {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n timedOut: boolean;\n /**\n * True when we got a full HTTP response (status + at least the response\n * headers), even if the stream later dropped. False when we could not\n * establish the connection or the sidecar refused the request outright.\n * Used by the caller to distinguish \"sidecar unreachable\" (invalidate\n * cache) from \"sidecar answered but the stream broke\" (still invalidate,\n * but log differently).\n */\n opened: boolean;\n /** HTTP status when we got a response. Undefined on connection failure. */\n status?: number;\n /** Populated when we could not even open the request (DNS/connect/TLS). */\n transportErrorMessage?: string;\n}\n\n/**\n * Thrown by {@link execViaPrivateNetwork} when the sidecar returns a non-2xx\n * HTTP response. This is an *application* error, not a transport error — the\n * sidecar is reachable and answered, it just refused the exec. Callers should\n * fall back to the lease path for this one call but MUST NOT invalidate the\n * cached `instanceUrl` (the address is still good).\n */\nexport class PrivateNetExecHttpError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`Sidecar /exec returned ${status}${body ? `: ${body.slice(0, 200)}` : ''}`);\n this.name = 'PrivateNetExecHttpError';\n this.status = status;\n this.body = body;\n }\n}\n\nconst DEFAULT_FETCH: PrivateNetFetch = (input, init) => {\n const f = (globalThis as { fetch?: typeof fetch }).fetch;\n if (!f) {\n throw new Error(\n 'Private-network exec requires a fetch implementation. Node 22+ provides one globally; on older runtimes, pass fetch explicitly.',\n );\n }\n return f(input, init);\n};\n\n/**\n * Wire frame shapes accepted from the sidecar. Anything else on the stream\n * is ignored so a future sidecar can add new frame types without breaking\n * older clients.\n */\ntype SidecarFrame =\n | { type: 'stdout'; data: string }\n | { type: 'stderr'; data: string }\n | { type: 'exit'; code: number };\n\n/**\n * Dial `${instanceUrl}/exec` and stream the response, resolving with the\n * accumulated stdout/stderr + exit code.\n *\n * Errors:\n * - Connection failure (DNS, refused, reset) → resolves with\n * `{opened:false, exitCode:null, transportErrorMessage}`. Never throws for\n * transport failures — the shape matches the lease-path result so the\n * caller can treat both transports uniformly.\n * - Non-2xx HTTP response from the sidecar → throws {@link PrivateNetExecHttpError}.\n * Application-level; caller decides whether to fall back.\n * - Stream ends without an `exit` frame → resolves with\n * `{opened:true, exitCode:null}`, matching the lease-path semantics for a\n * mid-stream drop.\n * - `timeoutMs` elapsed → aborts the request, resolves with\n * `{timedOut:true, exitCode:124}`.\n */\nexport async function execViaPrivateNetwork(\n instanceUrl: string,\n options: PrivateNetExecOptions,\n): Promise<PrivateNetExecResult> {\n const fetchImpl = options.fetch ?? DEFAULT_FETCH;\n const url = `${instanceUrl.replace(/\\/$/, '')}/exec`;\n\n const controller = new AbortController();\n let timedOut = false;\n let timeoutTimer: ReturnType<typeof setTimeout> | undefined;\n if (options.timeoutMs !== undefined && options.timeoutMs > 0) {\n timeoutTimer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, options.timeoutMs);\n }\n\n const body: Record<string, unknown> = { command: options.command };\n if (options.cwd) body.cwd = options.cwd;\n if (options.env && Object.keys(options.env).length > 0) body.env = options.env;\n if (options.timeoutMs !== undefined && options.timeoutMs > 0) body.timeoutMs = options.timeoutMs;\n\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n if (options.bearerToken) headers.authorization = `Bearer ${options.bearerToken}`;\n\n let response: Response;\n try {\n response = await fetchImpl(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (error) {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n // AbortError from our own timeout → treat as timeout, not transport error.\n if (timedOut) {\n return {\n exitCode: 124,\n stdout: '',\n stderr: '',\n timedOut: true,\n opened: false,\n };\n }\n return {\n exitCode: null,\n stdout: '',\n stderr: '',\n timedOut: false,\n opened: false,\n transportErrorMessage: error instanceof Error ? error.message : String(error),\n };\n }\n\n if (!response.ok) {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n const text = await response.text().catch(() => '');\n throw new PrivateNetExecHttpError(response.status, text);\n }\n\n if (!response.body) {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n // A 200 without a body is a broken sidecar. Treat as mid-stream drop\n // (opened=true, no exit frame) so the caller invalidates cache.\n return {\n exitCode: null,\n stdout: '',\n stderr: '',\n timedOut: false,\n opened: true,\n status: response.status,\n };\n }\n\n let stdout = '';\n let stderr = '';\n let exitCode: number | null = null;\n const decoder = new TextDecoder();\n let buffer = '';\n\n const handleLine = (line: string): void => {\n if (!line) return;\n let frame: SidecarFrame | undefined;\n try {\n frame = JSON.parse(line) as SidecarFrame;\n } catch {\n // Unknown / malformed frame — ignore. A well-behaved sidecar only emits\n // JSON objects, but tolerating garbage protects against a partial write.\n return;\n }\n if (!frame || typeof frame !== 'object') return;\n if (frame.type === 'stdout' && typeof frame.data === 'string') {\n stdout += frame.data;\n options.onStdout?.(frame.data);\n } else if (frame.type === 'stderr' && typeof frame.data === 'string') {\n stderr += frame.data;\n options.onStderr?.(frame.data);\n } else if (frame.type === 'exit' && typeof frame.code === 'number') {\n exitCode = frame.code;\n }\n // Any other frame type is intentionally ignored (see SidecarFrame jsdoc).\n };\n\n try {\n const reader = response.body.getReader();\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n // NDJSON: split on newline; last piece stays in buffer for the next chunk.\n let newlineIdx = buffer.indexOf('\\n');\n while (newlineIdx !== -1) {\n const line = buffer.slice(0, newlineIdx).trim();\n buffer = buffer.slice(newlineIdx + 1);\n handleLine(line);\n newlineIdx = buffer.indexOf('\\n');\n }\n }\n // Flush the decoder + any trailing (no-newline) line at EOF.\n buffer += decoder.decode();\n const trailing = buffer.trim();\n if (trailing) handleLine(trailing);\n } catch (error) {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n if (timedOut) {\n return {\n exitCode: 124,\n stdout,\n stderr,\n timedOut: true,\n opened: true,\n status: response.status,\n };\n }\n // Mid-stream failure. Return whatever we accumulated with exitCode=null\n // so the caller sees this as a transport failure and can invalidate.\n return {\n exitCode,\n stdout,\n stderr,\n timedOut: false,\n opened: true,\n status: response.status,\n transportErrorMessage: error instanceof Error ? error.message : String(error),\n };\n } finally {\n if (timeoutTimer) clearTimeout(timeoutTimer);\n }\n\n return {\n exitCode,\n stdout,\n stderr,\n timedOut: false,\n opened: true,\n status: response.status,\n };\n}\n","import type { RequestContext } from '@mastra/core/di';\nimport type {\n CommandResult,\n ExecuteCommandOptions,\n InstructionsOption,\n MastraSandboxOptions,\n ProcessInfo,\n ProviderStatus,\n SandboxCloneOptions,\n SandboxInfo,\n SpawnProcessOptions,\n} from '@mastra/core/workspace';\nimport { MastraSandbox, ProcessHandle, SandboxNotReadyError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { PlatformClientOptions } from './client.js';\nimport { PlatformApiError, PlatformClient } from './client.js';\nimport type { DirectExecWebSocketFactory, ExecLease } from './direct-exec.js';\nimport { execViaLease } from './direct-exec.js';\nimport type { PrivateNetExecOptions, PrivateNetExecResult, PrivateNetFetch } from './private-net-exec.js';\nimport { execViaPrivateNetwork, PrivateNetExecHttpError } from './private-net-exec.js';\n\nexport type PlatformSandboxNetworkIsolation = 'ISOLATED' | 'PRIVATE';\n\n/**\n * In-process `sandboxId → instanceUrl` map that lets\n * {@link PlatformSandbox.executeCommand} dial the in-sandbox sidecar over\n * Railway's private network instead of paying for a lease + WebSocket\n * round-trip through Railway's public control plane.\n *\n * The workspace proxy discovers each sandbox's IPv6 during\n * `POST /v1/projects/:pid/sandbox` and returns it as `instanceUrl` on the\n * create + get responses (see the platform inline-discovery issue). The\n * `PlatformSandbox` client copies that field into this registry from both\n * {@link PlatformSandbox.start} branches (fresh provision + reattach), evicts\n * on {@link PlatformSandbox.destroy}, and evicts again on any observed\n * transport failure so the next exec falls back to the lease path cleanly.\n *\n * See:\n * - `.scratch/factory-deploy/issue-runtime-sandbox-address-discovery.md`\n * - `.scratch/factory-deploy/issue-platform-sandbox-exec-via-private-network.md`\n */\nexport interface SandboxAddressRegistry {\n set(sandboxId: string, instanceUrl: string): void;\n get(sandboxId: string): string | undefined;\n delete(sandboxId: string): void;\n}\n\nexport interface PlatformSandboxOptions extends Omit<MastraSandboxOptions, 'processes'>, PlatformClientOptions {\n id?: string;\n environmentId?: string;\n sandboxId?: string;\n idleTimeoutMinutes?: number;\n networkIsolation?: PlatformSandboxNetworkIsolation;\n env?: Record<string, string>;\n timeout?: number;\n instructions?: InstructionsOption;\n /**\n * Injected WebSocket factory used by the direct-exec code path. Defaults to\n * the global `WebSocket` (available on Node 22+, this package's minimum) and\n * only exists so tests can drive the exec state machine deterministically\n * without a real network socket.\n */\n webSocketFactory?: DirectExecWebSocketFactory;\n /**\n * Injected fetch implementation used by the private-network exec code path\n * to dial the in-sandbox sidecar. Defaults to `globalThis.fetch` and only\n * exists so tests can drive that transport without a real HTTP server.\n * Note: this is separate from the `fetch` on {@link PlatformClientOptions},\n * which is used for calls to the workspace proxy.\n */\n privateNetFetch?: PrivateNetFetch;\n /**\n * Registry that maps `sandboxId → instanceUrl` for the private-network\n * exec path. When set, {@link PlatformSandbox.start} populates it from the\n * `instanceUrl` field workspace-proxy returns on create + get responses,\n * and {@link PlatformSandbox.executeCommand} looks it up before every exec\n * and tries the private-network transport first, falling back to the lease\n * path on any transport failure (and invalidating the registry entry so\n * the next call goes to the lease path cleanly). When absent, all execs\n * go straight to the lease path — this is the pre-existing behavior and\n * the expected mode outside the shipyard runtime. See\n * {@link SandboxAddressRegistry}.\n */\n addressRegistry?: SandboxAddressRegistry;\n}\n\ninterface ExecLeaseResponse {\n provider: string;\n sandboxId: string;\n providerResourceId: string;\n jwt: string;\n wsEndpoint: string;\n subprotocol: string;\n expiresAt: string | null;\n}\n\n/**\n * How long before a lease's stated `expiresAt` we should treat it as\n * expired. Avoids a race where the JWT is valid at cache-hit time but the\n * server rejects it by the time the WebSocket handshake completes.\n */\nconst LEASE_REFRESH_MARGIN_MS = 60_000;\n\ninterface CreateSandboxResponse {\n id: string;\n providerResourceId?: string | null;\n status?: string;\n createdAt?: string;\n destroyedAt?: string | null;\n /**\n * Full sidecar URL (`http://[<ipv6>]:<port>`) the runtime can dial over\n * Railway's private network to reach the in-sandbox exec sidecar. The\n * workspace proxy discovers the sandbox's IPv6 during `Sandbox.create()`\n * via one `sandbox.exec(\"awk … /proc/net/if_inet6\")` and stores it in\n * `environment_sandboxes.instance_url`; the same field is echoed on\n * `GET /sandbox/:id`. `null` when discovery failed (returned by the proxy\n * so the runtime knows to skip private-net dial for this sandbox rather\n * than fall back on a missing-field ambiguity).\n */\n instanceUrl?: string | null;\n}\n\n/** Max attempts for `POST /sandbox` when the proxy returns transient 5xx errors. */\nconst CREATE_MAX_ATTEMPTS = 3;\n/** Base delay between create retries; multiplied by the attempt number. */\nconst CREATE_RETRY_BASE_DELAY_MS = 2_000;\n\n/**\n * How long to wait for the in-sandbox sidecar's `/health` endpoint to respond\n * before giving up and leaving the address registry unpopulated (execs fall\n * back to the lease path). This bounds the fire-and-forget probe that runs\n * after `start()` resolves; the sandbox is usable immediately — the probe\n * only controls whether early execs go via private-net or lease.\n */\nconst SIDECAR_PROBE_TIMEOUT_MS = 30_000;\n/** Delay between sidecar probe attempts. */\nconst SIDECAR_PROBE_INTERVAL_MS = 250;\n/**\n * How long `executeCommand` waits for the transport to become ready before\n * falling back to the lease path. This is much shorter than\n * `SIDECAR_PROBE_TIMEOUT_MS` because we want execs to proceed quickly if\n * the sidecar is slow to boot — the probe continues in the background and\n * later execs will use private-net once it succeeds.\n */\nconst TRANSPORT_READY_WAIT_MS = 5_000;\n\n/**\n * Diagnostic error thrown when the direct-exec WebSocket transport fails\n * twice in a row (opening handshake refused or socket closed mid-stream\n * without an `exit` frame). Distinguishes \"the sandbox transport is broken\"\n * from \"your command failed\" so callers can decide whether to retry at a\n * higher level (e.g. reprovision the sandbox) or surface the error.\n *\n * `opened` is `true` when the WebSocket completed its handshake at least\n * once before closing; `false` when Railway refused the upgrade outright.\n */\nexport class SandboxExecTransportError extends Error {\n readonly sandboxId: string | undefined;\n readonly command: string;\n readonly attempts: number;\n readonly opened: boolean;\n readonly closeCode: number | undefined;\n readonly closeReason: string | undefined;\n readonly wsEndpoint: string;\n\n constructor(\n message: string,\n diagnostics: {\n sandboxId?: string;\n command: string;\n attempts: number;\n opened: boolean;\n closeCode?: number;\n closeReason?: string;\n wsEndpoint: string;\n },\n ) {\n super(message);\n this.name = 'SandboxExecTransportError';\n this.sandboxId = diagnostics.sandboxId;\n this.command = diagnostics.command;\n this.attempts = diagnostics.attempts;\n this.opened = diagnostics.opened;\n this.closeCode = diagnostics.closeCode;\n this.closeReason = diagnostics.closeReason;\n this.wsEndpoint = diagnostics.wsEndpoint;\n }\n}\n\n/**\n * Outcome of {@link PlatformSandbox.captureCheckpoint}. Mirrors the shape of\n * the OSS `@mastra/railway` `RailwaySandbox.captureCheckpoint()` return so a\n * caller (e.g. the factory fleet) can branch on `status`/`reason` uniformly\n * across providers without knowing which one is underneath.\n *\n * `captured` and `coalesced` both represent a successful capture the caller\n * can persist against — they carry the checkpoint name inline so callers\n * don't have to reach back into the sandbox instance to learn what was\n * written. `skipped` carries a machine-readable `reason` so the discriminant\n * set stays extensible.\n *\n * Note: the platform proxy's own `skipped` (returned when the upstream\n * sandbox is already destroyed) is mapped to `sandbox-not-running` here to\n * keep the discriminant identical to the OSS provider. The diagnostic\n * distinction (pre-flight vs post-hoc discovery) is preserved in log lines,\n * not the return type — see {@link PlatformSandbox.captureCheckpoint}.\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 * Thrown when `/exec-lease` returns 410 Gone — the sandbox has been destroyed\n * (Railway destroy, quota reclamation, etc.). The client cannot recover from\n * this on its own because it does not own the binding store; only the fleet\n * layer can clear the stale sandbox id and provision a fresh one. Callers\n * (typically `SandboxFleet`) must catch this and reprovision-and-replay.\n *\n * When this is thrown the cached `_lease` and `_sandboxId` on the sandbox\n * instance are cleared, so the next `ensureRunning()` on a reused instance\n * will re-provision cleanly.\n */\nexport class SandboxDestroyedError extends Error {\n readonly sandboxId: string | undefined;\n readonly command: string;\n readonly attempts: number;\n\n constructor(message: string, diagnostics: { sandboxId?: string; command: string; attempts: number }) {\n super(message);\n this.name = 'SandboxDestroyedError';\n this.sandboxId = diagnostics.sandboxId;\n this.command = diagnostics.command;\n this.attempts = diagnostics.attempts;\n }\n}\n\n/**\n * Compose a shell command line from a `command` string and optional `args`.\n *\n * IMPORTANT: `command` is treated as a **shell string** and passed to the\n * remote shell verbatim so callers can use pipes, redirects, and chaining\n * (`ls -la | grep foo`). This matches the contract of {@link MastraSandbox}\n * and the local sandbox implementation. `args` are always shell-quoted so\n * they cannot inject syntax.\n *\n * Callers MUST NOT pass untrusted input as `command`. Untrusted values must\n * be passed via `args`, where they are safely quoted. Passing untrusted\n * input as `command` allows arbitrary shell syntax execution on the remote\n * sandbox.\n */\nfunction buildCommand(command: string, args?: string[]): string {\n return args?.length ? `${command} ${args.map(shellQuote).join(' ')}` : command;\n}\n\nfunction shellQuote(arg: string): string {\n if (/^[a-zA-Z0-9._\\-/=:@]+$/.test(arg)) return arg;\n return `'${arg.replace(/'/g, `'\\\\''`)}'`;\n}\n\nclass PlatformProcessHandle extends ProcessHandle {\n readonly pid: string;\n private readonly resultPromise: Promise<CommandResult>;\n private exitCodeValue: number | undefined;\n\n constructor(pid: string, resultPromise: Promise<CommandResult>, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this.resultPromise = resultPromise.then(result => {\n this.exitCodeValue = result.exitCode;\n if (result.stdout) this.emitStdout(result.stdout);\n if (result.stderr) this.emitStderr(result.stderr);\n return result;\n });\n }\n\n get exitCode(): number | undefined {\n return this.exitCodeValue;\n }\n\n async wait(): Promise<CommandResult> {\n return this.resultPromise;\n }\n\n async kill(): Promise<boolean> {\n // The workspace proxy has no cancel-exec endpoint; each `executeCommand`\n // is a synchronous round-trip that has already completed (or timed out)\n // by the time a handle exists to kill. Making this explicit avoids\n // callers silently believing they cancelled a still-running process.\n throw new Error('Platform sandbox command execution does not support killing individual processes');\n }\n\n async sendStdin(): Promise<void> {\n throw new Error('Platform sandbox command execution does not support stdin');\n }\n}\n\nclass PlatformProcessManager extends SandboxProcessManager<PlatformSandbox> {\n private spawnCounter = 0;\n\n /**\n * Spawn a process on the remote sandbox.\n *\n * `command` is interpreted as a shell string by the remote shell, matching\n * the {@link MastraSandbox} contract. See {@link PlatformSandbox.executeCommand}\n * for the untrusted-input caveat: never pass untrusted values as `command`.\n */\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n const pid = `platform-proc-${Date.now().toString(36)}-${(this.spawnCounter++).toString(36)}`;\n const resultPromise = this.sandbox.executeCommand(command, undefined, options);\n const handle = new PlatformProcessHandle(pid, resultPromise, options);\n this._tracked.set(handle.pid, handle);\n return handle;\n }\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\nexport class PlatformSandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'PlatformSandbox';\n readonly provider = 'platform';\n status: ProviderStatus = 'pending';\n declare readonly processes: PlatformProcessManager;\n\n private readonly _client: PlatformClient;\n private readonly _environmentId: string;\n private _sandboxId?: string;\n private readonly _idleTimeoutMinutes?: number;\n private readonly _networkIsolation?: PlatformSandboxNetworkIsolation;\n private readonly _env: Record<string, string>;\n private readonly _timeout?: number;\n private readonly _instructionsOverride?: InstructionsOption;\n private _createdAt: Date | null = null;\n private readonly _webSocketFactory?: DirectExecWebSocketFactory;\n private readonly _privateNetFetch?: PrivateNetFetch;\n /**\n * Registry that maps `sandboxId → instanceUrl` for the private-network\n * exec path. Injected by the composition site via\n * {@link PlatformSandboxOptions.addressRegistry} and populated by this\n * class itself in `start()` when the workspace-proxy's create/reattach\n * response includes an `instanceUrl` field. The registry IS the cache —\n * there is no per-instance mirror on `PlatformSandbox`, so every exec is\n * a `Map.get()` (in the default in-process impl) against the live view.\n * When absent, executes go straight to the lease path with no extra\n * round-trip.\n */\n private readonly _addressRegistry?: SandboxAddressRegistry;\n /**\n * Cached exec lease for this sandbox. `null` before the first exec and\n * after {@link destroy}. Refreshed when `expiresAt - LEASE_REFRESH_MARGIN_MS < now`\n * (see {@link _ensureLease}); a lease without a disclosed `expiresAt`\n * is refreshed on every call.\n */\n private _lease: (ExecLease & { expiresAtMs: number | null }) | null = null;\n /**\n * In-flight mint request; concurrent `_ensureLease` callers on a cold or\n * near-expiry cache all await this single promise so we don't burn N\n * `POST /exec-lease` round-trips when the sandbox is doing N parallel execs.\n * Cleared (regardless of success or failure) when the request settles.\n */\n private _leaseInFlight: Promise<ExecLease & { expiresAtMs: number | null }> | null = null;\n /**\n * True when this sandbox was constructed with a caller-supplied `id` (the\n * recovery key the proxy hashes into an on-provider checkpoint name).\n * `captureCheckpoint()` needs this to distinguish \"no checkpoint intent\"\n * (auto-generated random id — capture would land under a name no future\n * boot would look for) from \"capture on demand\". Cloned sandboxes route\n * `checkpointName` through `id`, so both entry points set this the same\n * way.\n */\n private readonly _hasRecoveryKey: boolean;\n /**\n * In-flight `captureCheckpoint()` request. Concurrent callers on the same\n * instance coalesce onto this single promise so we don't burn N `POST\n * /checkpoint` round-trips when the fleet fires several turn-end captures\n * before the first one resolves. Cleared when the request settles.\n */\n private _captureInFlight: Promise<CaptureCheckpointResult> | null = null;\n /**\n * In-flight `start()` attempt. Concurrent callers on a fresh instance\n * coalesce onto this single promise so a `POST /sandbox` is not fired\n * N times when N fleet callers race to bring the same logical sandbox\n * up. Published **synchronously** with `??=` before the first `await`\n * so a later caller cannot slip through the null check while the\n * originator is mid-round-trip. Cleared when the shared attempt\n * settles (success or failure) so the next call sees a clean slot.\n *\n * Mirrors OSS `@mastra/railway` `RailwaySandbox._startInFlight`.\n */\n private _startInFlight: Promise<void> | null = null;\n /**\n * Generation token for the sidecar probe. Incremented on every `start()`\n * and on teardown. The probe captures this value when it begins; if the\n * generation has changed by the time the probe succeeds, the probe skips\n * the `set()` to avoid re-populating a deleted or superseded sandbox entry.\n */\n private _probeGeneration = 0;\n /**\n * In-flight sidecar probe promise. Concurrent `executeCommand` callers that\n * arrive before the registry is populated all await this single promise so\n * we don't fire N independent lease requests during the sidecar boot window.\n * Once the probe resolves (success or timeout), callers check the registry\n * and proceed — either via private-net (probe succeeded) or via lease (probe\n * failed/timed out, but now coalesced via `_leaseInFlight`).\n */\n private _transportReadyPromise: Promise<void> | null = null;\n\n constructor(options: PlatformSandboxOptions = {}) {\n super({ ...options, name: 'PlatformSandbox', processes: new PlatformProcessManager() });\n this._hasRecoveryKey = options.id !== undefined;\n this.id = options.id ?? this.generateId();\n this._client = new PlatformClient(options);\n this._environmentId = options.environmentId ?? process.env.MASTRA_ENVIRONMENT_ID ?? '';\n if (!this._environmentId && !options.sandboxId) throw new Error('environmentId is required');\n this._sandboxId = options.sandboxId;\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._webSocketFactory = options.webSocketFactory;\n this._privateNetFetch = options.privateNetFetch;\n this._addressRegistry = options.addressRegistry;\n }\n\n private generateId(): string {\n return `platform-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n /**\n * Construct a sibling {@link PlatformSandbox} that inherits this sandbox's\n * credentials and defaults (access token, project, environment, network\n * isolation, timeout, instructions, env, idle timeout) with per-instance\n * overrides from `options`.\n *\n * Performs no I/O and does not require this sandbox to be started — the\n * returned sandbox is not started and provisions (or reattaches, when\n * `sandboxId` is set) on its own `start()`. Use it when one configured\n * sandbox acts as the template for a fleet of independent sandboxes\n * (e.g. one per project).\n */\n clone(options: SandboxCloneOptions = {}): PlatformSandbox {\n // The proxy hashes `body.id` on POST /sandbox to look up a prior\n // checkpoint. A stable `checkpointName` is only useful if it round-trips\n // to `body.id`, so route it through the sandbox id when the caller\n // didn't pick one explicitly. Without this, every clone gets a random\n // id and no boot ever hits its captured checkpoint (see\n // issue-platform-sandbox-clone-drops-checkpoint-name.md).\n const id = options.id ?? options.checkpointName;\n return new PlatformSandbox({\n ...(id !== undefined && { id }),\n accessToken: this._client.accessToken,\n projectId: this._client.projectId,\n actingUserId: options.actingUserId ?? this._client.actingUserId,\n ...(this._client.sessionId !== undefined && { sessionId: this._client.sessionId }),\n ...(this._client.threadId !== undefined && { threadId: this._client.threadId }),\n fetch: this._client.fetch,\n environmentId: this._environmentId,\n ...(options.sandboxId !== undefined && { sandboxId: options.sandboxId }),\n idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,\n ...(this._networkIsolation !== undefined && { networkIsolation: this._networkIsolation }),\n env: options.env ?? this._env,\n ...(this._timeout !== undefined && { timeout: this._timeout }),\n ...(this._instructionsOverride !== undefined && { instructions: this._instructionsOverride }),\n ...(this._webSocketFactory !== undefined && { webSocketFactory: this._webSocketFactory }),\n ...(this._privateNetFetch !== undefined && { privateNetFetch: this._privateNetFetch }),\n // Propagate the registry — the clone is a different sandbox with a\n // different id, so it will `get()` its OWN address (or nothing) out\n // of the shared registry, not the parent's. See\n // `.scratch/factory-deploy/issue-platform-sandbox-exec-via-private-network.md`.\n ...(this._addressRegistry !== undefined && { addressRegistry: this._addressRegistry }),\n });\n }\n\n async start(): Promise<void> {\n // Coalesce concurrent callers onto a single in-flight attempt. `??=`\n // publishes the promise **synchronously** before the first `await`\n // below, so a second caller entering `start()` while the first is\n // mid-round-trip sees a populated `_startInFlight` and joins it\n // instead of racing to `POST /sandbox` alongside the originator. On\n // settle (success or failure) the slot is cleared so the next call\n // starts fresh — a failed attempt is not a permanent latch.\n // Mirrors OSS @mastra/railway RailwaySandbox._startInFlight.\n this._startInFlight ??= this._doStart().finally(() => {\n this._startInFlight = null;\n });\n return this._startInFlight;\n }\n\n /**\n * The single `start` attempt behind {@link start}'s coalescing wrapper.\n *\n * Split out so the wrapper can install a shared in-flight promise\n * synchronously (before the first `await`) without inlining the reattach\n * / retry logic. Joined callers observe whatever outcome this method\n * produces — success returns normally, failures propagate to every\n * awaiter.\n */\n private async _doStart(): Promise<void> {\n const startedAt = Date.now();\n if (this._sandboxId) {\n try {\n const requestStartedAt = Date.now();\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`);\n const requestMs = Date.now() - requestStartedAt;\n const json = (await response.json()) as CreateSandboxResponse;\n // A destroyed record (idle GC, manual delete) is not reattachable —\n // treat it like a missing sandbox so we fall through to a fresh\n // provision instead of pointing exec at a dead resource.\n if (!json.destroyedAt) {\n this._createdAt = json.createdAt ? new Date(json.createdAt) : new Date();\n this._populateAddressFromResponse(json);\n this._logStartComplete(json.id, startedAt, requestMs, 'reattach');\n return;\n }\n this._sandboxId = undefined;\n } catch (error) {\n if (!(error instanceof PlatformApiError) || error.status !== 404) throw error;\n this._sandboxId = undefined;\n }\n }\n\n if (!this._environmentId) throw new Error('environmentId is required');\n\n const body = JSON.stringify({\n // Sent so the platform can associate the provisioned resource with a\n // caller-stable identifier (used for opt-in checkpoint recovery). The\n // platform treats it as an advisory key: unknown values fall through\n // to a fresh sandbox, matching pre-existing behavior.\n id: this.id,\n environmentId: this._environmentId,\n idleTimeoutMinutes: this._idleTimeoutMinutes,\n networkIsolation: this._networkIsolation,\n env: this._env,\n });\n // Provisioning is observed to fail intermittently with proxy 500s while\n // the provider is under load. A create either succeeds (201) or fails\n // without allocating a caller-visible resource, so retrying transient\n // 5xx responses with a short backoff is safe and keeps a single flaky\n // window from killing the caller's whole workflow.\n let response: Response | undefined;\n const requestStartedAt = Date.now();\n for (let attempt = 1; ; attempt++) {\n try {\n response = await this._client.request('/sandbox', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body,\n });\n break;\n } catch (error) {\n const transient = error instanceof PlatformApiError && error.status >= 500;\n if (!transient || attempt >= CREATE_MAX_ATTEMPTS) throw error;\n await new Promise(resolve => setTimeout(resolve, CREATE_RETRY_BASE_DELAY_MS * attempt));\n }\n }\n const requestMs = Date.now() - requestStartedAt;\n const json = (await response.json()) as CreateSandboxResponse;\n this._sandboxId = json.id;\n this._createdAt = json.createdAt ? new Date(json.createdAt) : new Date();\n this._populateAddressFromResponse(json);\n this._logStartComplete(json.id, startedAt, requestMs, 'provision');\n }\n\n /**\n * One timing summary per completed `start()` — the whole\n * `PlatformSandbox`-visible boot in a single greppable line.\n *\n * `requestMs` is the proxy round-trip (`GET /sandbox/:id` on reattach,\n * `POST /sandbox` including transient-5xx retries on provision) — a black\n * box from this side that rolls up Railway RPC, sidecar launch, and the\n * proxy's discovery exec. Sidecar probe cost is intentionally NOT here: the\n * probe is fire-and-forget and outlives `start()` by design, so its\n * duration lands on the `platform-workspace probe ok` line instead.\n */\n private _logStartComplete(sandboxId: string, startedAt: number, requestMs: number, mode: string): void {\n this.logger.info('platform-workspace start complete', {\n sandboxId,\n sessionId: this._client.sessionId,\n mode,\n totalMs: Date.now() - startedAt,\n requestMs,\n });\n }\n\n /**\n * Copy `response.instanceUrl` into the injected {@link SandboxAddressRegistry}\n * when both are present. Called from both {@link start} branches (fresh\n * provision + reattach) with the workspace-proxy response for this sandbox.\n *\n * The proxy discovers the IPv6 during `Sandbox.create()` and stores it in\n * `environment_sandboxes.instance_url`; both the create response and\n * `GET /sandbox/:id` echo the same field. The runtime does not do any\n * discovery of its own — it only mirrors the field into an in-process map\n * so {@link executeCommand} can `Map.get()` before every exec without an\n * HTTP round-trip.\n *\n * `null`/absent `instanceUrl` (proxy discovery failed, or an older proxy\n * that predates the field) leaves the registry untouched — executes fall\n * through to the lease path with no branch here.\n */\n private _populateAddressFromResponse(json: CreateSandboxResponse): void {\n if (!this._addressRegistry) return;\n if (!json.instanceUrl) return;\n // Clear any stale entry before probing. On reattach, the registry may have\n // the old sandbox's address; execs should fall back to lease until the new\n // probe succeeds rather than dialing the stale address.\n this._addressRegistry.delete(json.id);\n // Start the sidecar probe and expose it so executeCommand can await it.\n // Early execs wait for the probe (up to TRANSPORT_READY_WAIT_MS) rather\n // than all racing to the lease path independently.\n const generation = ++this._probeGeneration;\n this._transportReadyPromise = this._probeSidecarThenRegister(json.id, json.instanceUrl, generation);\n }\n\n /**\n * Fire-and-forget probe that polls the sidecar's `/health` endpoint until\n * it responds, then populates the address registry. Runs detached from\n * `start()` so sandbox provision latency is unchanged; early execs simply\n * fall back to the lease path until the probe succeeds.\n *\n * If the sidecar never comes up within {@link SIDECAR_PROBE_TIMEOUT_MS},\n * the registry stays unpopulated and all execs go via lease for this\n * sandbox's lifetime (or until a future `start()` re-runs the probe).\n *\n * @param generation - The probe generation captured at call time. If this\n * no longer matches `_probeGeneration` when the probe succeeds, the probe\n * was superseded by a teardown or a new `start()`, so we skip the `set()`.\n */\n private async _probeSidecarThenRegister(sandboxId: string, instanceUrl: string, generation: number): Promise<void> {\n const probeStartedAt = Date.now();\n const deadline = probeStartedAt + SIDECAR_PROBE_TIMEOUT_MS;\n const fetchFn = this._privateNetFetch ?? fetch;\n let attempts = 0;\n while (Date.now() < deadline) {\n // Teardown or new start() superseded this probe — bail out early.\n if (generation !== this._probeGeneration) return;\n attempts++;\n try {\n const res = await fetchFn(`${instanceUrl}/health`, {\n method: 'GET',\n signal: AbortSignal.timeout(1_000),\n });\n const ok = res.ok;\n // Release the response body so the connection returns to the pool.\n await res.body?.cancel().catch(() => {});\n if (ok) {\n // A ~1-attempt probe means the sidecar was ready when the proxy\n // returned; hundreds of ms means it was still booting — the exact\n // window that used to silently fall back to the lease path.\n this.logger.info('platform-workspace probe ok', {\n sandboxId,\n sessionId: this._client.sessionId,\n probeDurationMs: Date.now() - probeStartedAt,\n attempts,\n });\n // Sidecar is listening. Only populate if this probe is still current.\n if (generation === this._probeGeneration && this._sandboxId === sandboxId) {\n this._addressRegistry?.set(sandboxId, instanceUrl);\n }\n return;\n }\n } catch {\n // Connection refused / timeout — keep polling.\n }\n await new Promise(r => setTimeout(r, SIDECAR_PROBE_INTERVAL_MS));\n }\n // Sidecar never came up. Leave registry entry unset — every exec goes lease.\n this.logger.warn('platform-workspace probe timed out', {\n sandboxId,\n sessionId: this._client.sessionId,\n timeoutMs: SIDECAR_PROBE_TIMEOUT_MS,\n attempts,\n });\n }\n\n /**\n * Wait for the transport to become ready (sidecar probe succeeds) or time\n * out. Concurrent callers all await the same probe promise, coalescing the\n * cold-start storm into a single warmup attempt.\n *\n * If no probe is in flight (no registry, or registry already populated),\n * this returns immediately. After the wait (success or timeout), callers\n * check the registry and proceed — either via private-net or lease. The\n * lease path is still coalesced via `_leaseInFlight`, so even if the probe\n * times out, we only mint one lease for all concurrent execs.\n */\n private async _awaitTransportReady(): Promise<void> {\n // Fast path: registry already has an entry, transport is warm.\n if (this._sandboxId && this._addressRegistry?.get(this._sandboxId)) {\n return;\n }\n // No probe in flight — nothing to wait for, proceed to lease path.\n if (!this._transportReadyPromise) {\n return;\n }\n // Race the probe against a timeout. We don't want to block execs forever\n // if the sidecar is slow to boot — they can proceed via lease after a\n // short wait, and later execs will use private-net once the probe succeeds.\n await Promise.race([this._transportReadyPromise, new Promise<void>(r => setTimeout(r, TRANSPORT_READY_WAIT_MS))]);\n }\n\n /**\n * Stop the sandbox while **preserving its recovery checkpoint**.\n *\n * Semantic parity with `@mastra/railway` `RailwaySandbox.stop()`: the VM\n * is released but the on-provider checkpoint survives, so a subsequent\n * `start()` on a sandbox constructed with the same `id` can restore from\n * it. Any in-flight capture is awaited first so the preserved checkpoint\n * reflects the latest disk state we asked for.\n *\n * Corresponds to `DELETE /v1/projects/:pid/sandbox/:sandboxId` on\n * workspace-proxy, which by contract does not touch the checkpoint. Use\n * {@link destroy} when you want the checkpoint released too.\n */\n async stop(): Promise<void> {\n // Await any in-flight capture so the preserved checkpoint reflects the\n // latest capture the caller triggered. Never rethrow — a failing capture\n // must not block teardown; the proxy's safety-net refresh timer is a\n // fallback for the checkpoint state.\n if (this._captureInFlight) {\n await this._captureInFlight.catch(error => {\n this.logger.warn(`stop(): failed to flush in-flight capture before teardown:`, error);\n });\n }\n await this._teardownSandbox();\n }\n\n /**\n * Destroy the sandbox **and release its recovery checkpoint**.\n *\n * Semantic parity with `@mastra/railway` `RailwaySandbox.destroy()`:\n * cancels any in-flight capture (the checkpoint is about to be deleted\n * — no reason to burn a capture on state we're releasing), asks the\n * proxy to delete the checkpoint, then releases the VM. Both remote\n * operations are best-effort logged failures — a stray checkpoint or a\n * transient proxy error must not leave the caller with a half-torn-down\n * sandbox they can't safely retry.\n *\n * Requires the caller to have constructed with a recovery `id` (there is\n * no checkpoint to delete otherwise); callers without one skip the\n * checkpoint DELETE and behave identically to {@link stop}.\n */\n async destroy(): Promise<void> {\n if (!this._sandboxId) return;\n const destroyedSandboxId = this._sandboxId;\n\n // Drop the in-flight capture promise — we're about to delete the\n // checkpoint, so completing an in-flight capture is at best a wasted\n // round-trip and at worst races with the delete. Callers get whatever\n // resolution the pending capture already had; we don't rethrow.\n this._captureInFlight = null;\n\n if (this._hasRecoveryKey) {\n // Body mirrors the POST /checkpoint shape (`{ id }`) so the proxy\n // can hash the same recovery key into the same checkpoint name.\n // Best-effort: a proxy 404/410 means the checkpoint is already\n // absent (idle GC, prior delete) and we can proceed with the VM\n // teardown; other failures are surfaced in logs but do not abort\n // — the VM DELETE below is the operation the caller most needs.\n try {\n await this._client.request(`/sandbox/${encodeURIComponent(destroyedSandboxId)}/checkpoint`, {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: this.id }),\n });\n } catch (error) {\n if (error instanceof PlatformApiError && (error.status === 404 || error.status === 410)) {\n this.logger.debug(`destroy(): checkpoint already absent upstream (status=${error.status})`);\n } else {\n this.logger.warn(`destroy(): failed to delete checkpoint upstream:`, error);\n }\n }\n }\n\n await this._teardownSandbox();\n }\n\n /**\n * Release the remote sandbox VM and clear the local state pointing at it.\n *\n * Shared body of {@link stop} and {@link destroy} — both funnel through\n * here after they've dealt with the checkpoint (preserve vs release).\n * The VM DELETE is safe to issue in either mode: the proxy's DELETE\n * route does not touch the checkpoint on its own, so `stop()` correctly\n * leaves the checkpoint intact and `destroy()` has already removed it\n * before this call.\n */\n private async _teardownSandbox(): Promise<void> {\n if (!this._sandboxId) return;\n const destroyedSandboxId = this._sandboxId;\n // Invalidate any in-flight probe so it doesn't re-populate the registry\n // after we've deleted the entry below. The probe checks this generation\n // before calling set().\n this._probeGeneration++;\n await this._client.request(`/sandbox/${encodeURIComponent(destroyedSandboxId)}`, { method: 'DELETE' });\n // Clear local state so a subsequent start() creates a fresh remote sandbox\n // instead of taking the reattach branch and pointing exec at a deleted resource.\n this._sandboxId = undefined;\n this._createdAt = null;\n // Drop the exec lease with the sandbox — the JWT is tied to the provider\n // instance id and would be rejected against a fresh one.\n this._lease = null;\n // Evict the sidecar address from the registry explicitly. Destroy\n // deallocates the IPv6, so any stale entry would be a dial-to-nowhere;\n // clearing here prevents a transport-failure round-trip on the next\n // exec against a reused instance. A subsequent start() (fresh provision\n // or reattach) will re-populate the entry from the workspace-proxy's\n // response.\n this._addressRegistry?.delete(destroyedSandboxId);\n }\n\n /** Persist the configured recovery checkpoint when available. */\n async snapshot(): Promise<void> {\n await this.captureCheckpoint();\n }\n\n /**\n * Capture the sandbox's checkpoint on demand, outside any refresh timer the\n * workspace-proxy owns internally.\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 the upstream's idle destroy.\n *\n * Mirrors the OSS `@mastra/railway` `RailwaySandbox.captureCheckpoint()`\n * shape so factory can call `sandbox.captureCheckpoint()` uniformly and\n * branch on `status`/`reason` without knowing which provider is underneath.\n * Both `captured` and `coalesced` carry the checkpoint name inline so the\n * caller can persist a session→checkpoint binding atomically with the\n * awaited capture.\n *\n * Skip semantics:\n * - No caller-supplied `id`: returns `{ status: 'skipped', reason:\n * 'no-checkpoint-name-configured' }`. An auto-generated random id is\n * never a meaningful recovery key (no future boot would look for a\n * checkpoint under it), so capturing would silently produce dead data.\n * - Not started (no `_sandboxId`): returns `{ status: 'skipped', reason:\n * 'sandbox-not-running' }` without a round-trip.\n * - Upstream 410 (workspace-proxy or Railway reports the sandbox is\n * already destroyed): returns the same `sandbox-not-running` skip so\n * the discriminant matches the pre-flight case. Local state\n * (`_sandboxId`, `_lease`, sidecar address) is cleared as a side\n * effect so the next `start()` provisions fresh instead of reattaching\n * to a dead id. The diagnostic distinction (pre-flight vs post-hoc)\n * is preserved in log level: debug for the expected pre-flight skip,\n * warn for the surprise upstream destroy.\n *\n * Concurrent callers on the same instance coalesce onto a single in-flight\n * `POST /checkpoint` so N simultaneous turn-end fires (e.g. several tabs)\n * do not each round-trip the proxy. Both the originator and joiners\n * receive `{ status: 'coalesced', ... }` for the joined result — the\n * outer contract does not distinguish who started the request, only that\n * one upstream capture was made.\n *\n * Never throws for expected outcomes. Transport failures (5xx, 4xx other\n * than 410) propagate as {@link PlatformApiError}; a 410 is normalized\n * to a skip as described above.\n */\n async captureCheckpoint(): Promise<CaptureCheckpointResult> {\n if (!this._hasRecoveryKey) {\n this.logger.debug(\n `captureCheckpoint skipped: no recovery key configured for sandbox ${this._sandboxId ?? '(unstarted)'}`,\n );\n return { status: 'skipped', reason: 'no-checkpoint-name-configured' };\n }\n\n if (!this._sandboxId) {\n this.logger.debug(`captureCheckpoint skipped: sandbox not running (local pre-flight, id=${this.id})`);\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n\n if (this._captureInFlight) {\n return this._captureInFlight;\n }\n\n const sandboxId = this._sandboxId;\n const capture = this._doCaptureCheckpoint(sandboxId).finally(() => {\n if (this._captureInFlight === capture) {\n this._captureInFlight = null;\n }\n });\n this._captureInFlight = capture;\n return capture;\n }\n\n /**\n * The single `POST /checkpoint` attempt behind {@link captureCheckpoint}.\n *\n * Split out so the coalescing wrapper can install a shared in-flight\n * promise without inlining the transport + response-mapping logic.\n * Joined callers observe `{ status: 'coalesced', ... }` — the initiator\n * sees the underlying `captured` / `coalesced` / `skipped` result the\n * proxy returned. Both are legitimate: the OSS mirror uses the same\n * \"initiator sees the truth, joiners see coalesced\" split.\n */\n private async _doCaptureCheckpoint(sandboxId: string): Promise<CaptureCheckpointResult> {\n let response: Response;\n try {\n response = await this._client.request(`/sandbox/${encodeURIComponent(sandboxId)}/checkpoint`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: this.id }),\n });\n } catch (error) {\n if (error instanceof PlatformApiError && error.status === 410) {\n this.logger.warn(`captureCheckpoint skipped: sandbox destroyed upstream (proxy 410, sandboxId=${sandboxId})`);\n this._clearDestroyedState(sandboxId);\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n throw error;\n }\n const json = (await response.json()) as { checkpointName: string; status: 'captured' | 'coalesced' | 'skipped' };\n if (json.status === 'skipped') {\n // Proxy's own `skipped` means the upstream sandbox is already\n // destroyed — same actionable outcome as a 410, so normalize to\n // the same discriminant + clear local state.\n this.logger.warn(\n `captureCheckpoint skipped: sandbox destroyed upstream (proxy reported skipped, sandboxId=${sandboxId})`,\n );\n this._clearDestroyedState(sandboxId);\n return { status: 'skipped', reason: 'sandbox-not-running' };\n }\n return { status: json.status, checkpointName: json.checkpointName };\n }\n\n /**\n * Clear local state that would otherwise let the caller keep exec'ing\n * against a sandbox the upstream has already destroyed. Mirrors what\n * `destroy()` does minus the outbound DELETE — the sandbox is already\n * gone, so all that remains is to stop pointing at it.\n *\n * Also resets `status` to `'pending'` so a subsequent `_start()` on this\n * reused instance re-runs provisioning instead of short-circuiting on\n * the cached `'running'` state (see `MastraSandbox._start`).\n */\n private _clearDestroyedState(destroyedSandboxId: string): void {\n this._sandboxId = undefined;\n this._createdAt = null;\n this._lease = null;\n this._addressRegistry?.delete(destroyedSandboxId);\n this.status = 'pending';\n }\n\n /**\n * Execute a command on the remote sandbox.\n *\n * `command` is a **shell string**: it is concatenated verbatim into the\n * command line sent to the remote shell, which lets callers use pipes,\n * redirects, and chaining (`ls -la | grep foo`). This matches the contract\n * of {@link MastraSandbox} and the local sandbox implementation.\n *\n * `args`, when provided, are always shell-quoted so they cannot inject\n * additional shell syntax.\n *\n * Security: callers MUST NOT pass untrusted input as `command`. If any part\n * of the invocation is derived from an untrusted source, pass it through\n * `args` (which is safely quoted) or shell-quote it yourself before\n * inclusion. Untrusted `command` values allow arbitrary shell syntax\n * execution on the remote sandbox.\n */\n async executeCommand(command: string, args?: string[], options?: ExecuteCommandOptions): Promise<CommandResult> {\n await this.ensureRunning();\n if (!this._sandboxId) throw new SandboxNotReadyError(this.id);\n\n const started = Date.now();\n const fullCommand = buildCommand(command, args);\n // Nullish check so an explicit `timeout: 0` still overrides the instance\n // default. `_runDirectExec` omits `timeoutMs` from the exec payload when\n // the value is 0, which disables the client-side timer entirely.\n const effectiveTimeout = options?.timeout ?? this._timeout;\n\n // Wait for the transport to become ready before proceeding. During the\n // sidecar boot window (immediately after start()), concurrent execs all\n // await the same probe promise rather than each independently racing to\n // the lease path — this coalesces the cold-start storm into a single\n // warmup attempt. Once the probe resolves (or times out after\n // TRANSPORT_READY_WAIT_MS), we check the registry and proceed.\n await this._awaitTransportReady();\n\n // Preferred path: dial the in-sandbox sidecar over Railway's private\n // network (~16 ms p50). No GraphQL, no lease mint, no public tcp-proxy.\n // Available only when the in-process address registry has an entry for\n // this sandboxId — populated by start() from the workspace-proxy's\n // create/reattach response when the proxy discloses `instanceUrl`. On\n // any transport failure (connection refused, mid-stream drop, no exit\n // frame) we invalidate the registry entry and fall through to the lease\n // path — application-level failures (sidecar returns 5xx) still fall\n // back for this call but do NOT invalidate the entry. See\n // `.scratch/factory-deploy/issue-platform-sandbox-exec-via-private-network.md`.\n const instanceUrl = this._addressRegistry?.get(this._sandboxId);\n if (instanceUrl) {\n const privateNet = await this._tryExecViaPrivateNetwork(instanceUrl, fullCommand, effectiveTimeout, options);\n if (privateNet) {\n const privateExit = privateNet.exitCode ?? 124;\n return {\n success: privateExit === 0,\n exitCode: privateExit,\n stdout: privateNet.stdout,\n stderr: privateNet.stderr,\n timedOut: privateNet.timedOut,\n command: fullCommand,\n executionTimeMs: Date.now() - started,\n };\n }\n // Fall through — `_tryExecViaPrivateNetwork` already evicted the\n // registry entry if this was a transport failure.\n }\n\n // Fallback: WebSocket direct-exec via `/exec-lease`. `_runDirectExec`\n // handles single-shot transport retry and throws typed errors on\n // unrecoverable failure: `SandboxDestroyedError` when `/exec-lease`\n // returns 410 (fleet must reprovision), `SandboxExecTransportError`\n // when the WebSocket transport fails twice against a live sandbox,\n // `PlatformApiError` for other `/exec-lease` errors (404/500/501).\n // See ./direct-exec.ts and `docs/factory/direct-sandbox-connection.md`\n // in the Platform repo.\n const result = await this._runDirectExec(fullCommand, effectiveTimeout, options);\n // `_runDirectExec` throws on transport failure (see its jsdoc), so a\n // `null` exitCode here can only mean `timedOut: true` — the sandbox\n // never got to send an exit frame because we cut the command short.\n // Use 124 for that (the conventional timeout exit code). We are NOT\n // coercing transport-failure nulls to fake exit codes — those throw.\n const exitCode = result.exitCode ?? 124;\n return {\n success: exitCode === 0,\n exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n timedOut: result.timedOut,\n command: fullCommand,\n executionTimeMs: Date.now() - started,\n };\n }\n\n /**\n * Run a single exec against the direct-exec transport, with one in-flight\n * retry on WebSocket transport failure (socket closed without an `exit`\n * frame and the exec did not time out). The retry mints a fresh lease\n * — the failure could be a stale JWT — and reopens a new WebSocket.\n *\n * Error taxonomy:\n * - **410 on `/exec-lease`** (either attempt) → the sandbox is gone.\n * Nulls the cached `_lease` and `_sandboxId` and throws\n * {@link SandboxDestroyedError}. Callers (typically `SandboxFleet`) must\n * catch this, clear the stale binding, and reprovision + replay.\n * - **Persistent transport failure** (both WS attempts close without an\n * `exit` frame against a live sandbox) → {@link SandboxExecTransportError}\n * with WebSocket close diagnostics.\n * - **Other `PlatformApiError`s** (404/500/501) propagate directly.\n * - **Real command result** (exit code from Railway's exit frame, or\n * `timedOut: true`) returns normally.\n *\n * Returns a result with a real `exitCode` OR `timedOut: true`. Never\n * returns `{ exitCode: null, timedOut: false }` — that case throws.\n */\n private async _runDirectExec(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n ): Promise<{ exitCode: number | null; stdout: string; stderr: string; timedOut: boolean }> {\n // Filter undefined values out of the env overlay so we match the\n // Record<string, string> shape execViaLease expects. `ExecuteCommandOptions.env`\n // is NodeJS.ProcessEnv (string | undefined).\n const filteredEnv = options?.env\n ? Object.fromEntries(\n Object.entries(options.env).filter((entry): entry is [string, string] => entry[1] !== undefined),\n )\n : undefined;\n\n let lastResult: Awaited<ReturnType<typeof execViaLease>> | undefined;\n let lastLease: (ExecLease & { expiresAtMs: number | null }) | undefined;\n let attemptsMade = 0;\n // Two attempts: initial + one retry. On the second attempt we drop the\n // cached lease so we don't reuse a JWT that may itself be the cause of\n // the transport failure — but only if the cache still holds the same\n // lease we just failed against. A concurrent exec sharing this instance\n // may have already cached a fresh, unrelated lease in between, and we\n // must not discard that.\n for (let attempt = 0; attempt < 2; attempt++) {\n if (attempt > 0 && lastLease && this._lease === lastLease) this._lease = null;\n let lease: ExecLease & { expiresAtMs: number | null };\n try {\n lease = await this._ensureLease();\n } catch (error) {\n // 410 → sandbox has been destroyed. Clear all cached state so a\n // reused instance re-provisions cleanly, then hand off to the fleet\n // layer via a typed error. Other PlatformApiErrors (404/500/501)\n // propagate as-is — those are configuration or platform errors, not\n // a \"reprovision me\" signal.\n if (error instanceof PlatformApiError && error.status === 410) {\n this._lease = null;\n const priorSandboxId = this._sandboxId;\n this._sandboxId = undefined;\n throw new SandboxDestroyedError(\n `Sandbox ${priorSandboxId ?? '(unknown)'} was destroyed; /exec-lease returned 410`,\n {\n ...(priorSandboxId && { sandboxId: priorSandboxId }),\n command: fullCommand,\n attempts: attempt + 1,\n },\n );\n }\n throw error;\n }\n lastLease = lease;\n attemptsMade = attempt + 1;\n const result = await execViaLease(lease, {\n command: fullCommand,\n ...(options?.cwd !== undefined && { cwd: options.cwd }),\n ...(filteredEnv !== undefined && { env: filteredEnv }),\n ...(effectiveTimeout != null && effectiveTimeout > 0 && { timeoutMs: effectiveTimeout }),\n ...(this._webSocketFactory && { webSocketFactory: this._webSocketFactory }),\n });\n lastResult = result;\n // `null` exitCode with `timedOut: false` means the socket closed\n // without an exit frame — a transport failure (handshake stalled,\n // mid-stream drop, expired token). Any other outcome (real exit code\n // or timed-out) is a valid result and we return it.\n if (result.exitCode !== null || result.timedOut) return result;\n }\n\n // Both attempts failed at the transport layer against a live sandbox.\n // Surface a loud, typed error with close diagnostics so callers can\n // distinguish \"your command failed\" from \"the sandbox transport is\n // broken.\"\n const result = lastResult!;\n const lease = lastLease!;\n // The lease from the failed second attempt is still cached; drop it so\n // the next `executeCommand` doesn't waste its first attempt on the same\n // implicated JWT before minting fresh. Identity-check first so a\n // concurrent exec that has already cached a fresh, unrelated lease\n // isn't collateral-damaged.\n if (this._lease === lease) this._lease = null;\n throw new SandboxExecTransportError(\n `Direct-exec transport failed for sandbox ${this._sandboxId ?? '(unknown)'} after ${attemptsMade} attempt(s)` +\n (result.closeCode !== undefined\n ? ` (close ${result.closeCode}${result.closeReason ? ` ${result.closeReason}` : ''})`\n : ''),\n {\n ...(this._sandboxId && { sandboxId: this._sandboxId }),\n command: fullCommand,\n attempts: attemptsMade,\n opened: result.opened ?? false,\n ...(result.closeCode !== undefined && { closeCode: result.closeCode }),\n ...(result.closeReason !== undefined && { closeReason: result.closeReason }),\n wsEndpoint: lease.wsEndpoint,\n },\n );\n }\n\n /**\n * Try to run the exec against the in-sandbox sidecar over Railway's private\n * network. Returns the result on success (including non-zero exit codes and\n * timeouts — those are real command results, not failures). Returns\n * `undefined` when the caller should fall back to the lease path:\n *\n * - Transport failure (connection refused, mid-stream drop, no `exit`\n * frame). The registry entry is evicted so subsequent execs skip the\n * private-net dial until the sidecar re-registers.\n * - Sidecar answered with a non-2xx HTTP status. Registry is left intact —\n * the address is still valid; something else is wrong (bad request,\n * sidecar bug). Only this specific exec falls back.\n */\n private async _tryExecViaPrivateNetwork(\n instanceUrl: string,\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n ): Promise<PrivateNetExecResult | undefined> {\n // Match the env-filter dance in `_runDirectExec`: ExecuteCommandOptions.env\n // is NodeJS.ProcessEnv (string | undefined) but the wire body wants a\n // Record<string, string>.\n const filteredEnv = options?.env\n ? Object.fromEntries(\n Object.entries(options.env).filter((entry): entry is [string, string] => entry[1] !== undefined),\n )\n : undefined;\n\n const execOptions: PrivateNetExecOptions = {\n command: fullCommand,\n ...(options?.cwd !== undefined && { cwd: options.cwd }),\n ...(filteredEnv !== undefined && { env: filteredEnv }),\n ...(effectiveTimeout != null && effectiveTimeout > 0 && { timeoutMs: effectiveTimeout }),\n ...(this._privateNetFetch && { fetch: this._privateNetFetch }),\n };\n\n let result: PrivateNetExecResult;\n try {\n result = await execViaPrivateNetwork(instanceUrl, execOptions);\n } catch (error) {\n if (error instanceof PrivateNetExecHttpError) {\n // Application-level failure from a reachable sidecar. Fall back for\n // this call, but do NOT evict the registry entry — future calls\n // should still prefer the private-network path.\n return undefined;\n }\n // Anything else escaping is unexpected (e.g. options validation). Treat\n // it like a transport failure so we don't wedge the caller.\n this._invalidateAddress();\n return undefined;\n }\n\n // A timeout is always the caller's answer, never a lease-fallback trigger:\n // the sidecar may already be running the command, so re-executing on the\n // lease path would double-run non-idempotent work (rm, git push, DB\n // migrations) and return the second result instead of `timedOut: true`.\n // If we never opened the response (pre-headers abort), the address is\n // still evicted so subsequent execs skip a dial we already know is slow —\n // but the timed-out result itself flows back to the caller unchanged.\n if (result.timedOut) {\n if (!result.opened) this._invalidateAddress();\n return result;\n }\n\n // A completed exec (real exit code) is a valid result — hand it back even\n // if exitCode is non-zero. Only evict when the transport itself failed:\n // never opened, or opened without an exit frame. `opened=false` here\n // means connection refused (no timeout to disambiguate).\n const transportFailed = !result.opened || result.exitCode === null;\n if (transportFailed) {\n this._invalidateAddress();\n return undefined;\n }\n\n return result;\n }\n\n /**\n * Evict this sandbox's entry from the address registry after an observed\n * transport failure. The entry stays gone until the next start() re-reads\n * `instanceUrl` from a workspace-proxy response — until then, execs skip\n * the private-net dial and go straight to the lease path.\n */\n private _invalidateAddress(): void {\n if (this._sandboxId) this._addressRegistry?.delete(this._sandboxId);\n }\n\n /**\n * Return a cached exec lease, minting a fresh one when the cache is empty\n * or the JWT is within {@link LEASE_REFRESH_MARGIN_MS} of `expiresAt`.\n *\n * Callers are expected to be on the \"sandbox is running\" path; we don't\n * re-check `_sandboxId` here because `executeCommand` already gated on it.\n */\n private async _ensureLease(): Promise<ExecLease & { expiresAtMs: number | null }> {\n const now = Date.now();\n // Cache hit only when we know the expiry AND we're comfortably before it.\n // A null `expiresAtMs` means the provider didn't disclose a TTL — treat\n // that as \"refresh every call\" rather than \"cache forever\", so a token\n // that turns out to be short-lived can't wedge the sandbox until restart.\n if (this._lease && this._lease.expiresAtMs !== null && this._lease.expiresAtMs - LEASE_REFRESH_MARGIN_MS > now) {\n return this._lease;\n }\n // Coalesce concurrent mints on a cold/expired cache.\n if (this._leaseInFlight) return this._leaseInFlight;\n if (!this._sandboxId) throw new SandboxNotReadyError(this.id);\n const sandboxId = this._sandboxId;\n const inFlight = (async () => {\n const response = await this._client.request(`/sandbox/${encodeURIComponent(sandboxId)}/exec-lease`, {\n method: 'POST',\n });\n const json = (await response.json()) as ExecLeaseResponse;\n const expiresAtMs = json.expiresAt ? Date.parse(json.expiresAt) : null;\n const lease = {\n jwt: json.jwt,\n wsEndpoint: json.wsEndpoint,\n subprotocol: json.subprotocol,\n expiresAt: json.expiresAt,\n // Guard against `Date.parse` returning NaN for malformed values by\n // treating them as \"no expiry known\", which forces a mint every call\n // rather than silently caching a broken lease forever.\n expiresAtMs: expiresAtMs !== null && !Number.isNaN(expiresAtMs) ? expiresAtMs : null,\n };\n this._lease = lease;\n return lease;\n })();\n this._leaseInFlight = inFlight;\n try {\n return await inFlight;\n } finally {\n // Clear on both success and failure so a failed mint doesn't wedge\n // future callers into awaiting the same rejected promise forever.\n if (this._leaseInFlight === inFlight) this._leaseInFlight = null;\n }\n }\n\n async getInfo(): Promise<SandboxInfo> {\n if (!this._sandboxId) {\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 };\n }\n // Skip the workspace-proxy round-trip when the address registry has an\n // entry for this sandbox — a live entry is proof that start() saw an\n // `instanceUrl` on the create/reattach response and that no exec since\n // has evicted it via a transport failure. Serving `getInfo()` from\n // local state here removes the per-poll `GET /sandbox/:id` hit that\n // otherwise triggers a Railway GraphQL call + `sandboxExec` awk on\n // `/proc/net/if_inet6` inside the proxy.\n //\n // Note: this does NOT probe the sidecar. If the sandbox has been\n // destroyed out-of-band the next exec will fail transport, evict the\n // registry entry, and a subsequent getInfo() will fall through to the\n // proxy below and observe the true status.\n if (this._addressRegistry?.get(this._sandboxId)) {\n return {\n id: this._sandboxId,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n sandboxId: this._sandboxId,\n },\n };\n }\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`);\n const json = (await response.json()) as CreateSandboxResponse;\n return {\n id: json.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: json.createdAt ? new Date(json.createdAt) : (this._createdAt ?? new Date()),\n metadata: {\n // The platform assigns its own sandbox id on create (the advisory id\n // sent in the POST body is not honored). Expose it so callers that\n // persist a reattach id (e.g. the Factory sandbox fleet, which reads\n // `metadata.sandboxId`) store the id the proxy actually recognizes\n // instead of the locally generated construction id.\n sandboxId: json.id,\n providerResourceId: json.providerResourceId ?? undefined,\n platformStatus: json.status,\n },\n };\n }\n\n getInstructions(opts?: { requestContext?: RequestContext }): string {\n const defaultInstructions = `Platform sandbox${this._sandboxId ? ` ${this._sandboxId}` : ''}. Execute commands with the sandbox command APIs.`;\n if (typeof this._instructionsOverride === 'function') {\n return this._instructionsOverride({ defaultInstructions, requestContext: opts?.requestContext });\n }\n if (typeof this._instructionsOverride === 'string') return this._instructionsOverride;\n return defaultInstructions;\n }\n}\n","import type { FilesystemProvider, SandboxProvider } from '@mastra/core/editor';\nimport type { PlatformFilesystemOptions } from './filesystem.js';\nimport { PlatformFilesystem } from './filesystem.js';\nimport type { PlatformSandboxOptions } from './sandbox.js';\nimport { PlatformSandbox } from './sandbox.js';\n\nexport const platformSandboxProvider: SandboxProvider<PlatformSandboxOptions> = {\n id: 'platform',\n name: 'Mastra Platform Sandbox',\n description: 'Environment-scoped sandbox execution through Mastra Platform workspace proxy',\n configSchema: {\n type: 'object',\n properties: {\n accessToken: {\n type: 'string',\n description: 'Mastra Platform access token (falls back to MASTRA_PLATFORM_ACCESS_TOKEN)',\n },\n projectId: { type: 'string', description: 'Platform project ID (falls back to MASTRA_PROJECT_ID)' },\n actingUserId: { type: 'string', description: 'Opaque user subject attributed to sandbox requests' },\n environmentId: { type: 'string', description: 'Platform environment ID (falls back to MASTRA_ENVIRONMENT_ID)' },\n sandboxId: { type: 'string', description: 'Reattach to an existing Platform sandbox by ID' },\n idleTimeoutMinutes: { type: 'number', description: 'Minutes before the sandbox can be destroyed while idle' },\n networkIsolation: {\n type: 'string',\n description: 'Network isolation mode',\n enum: ['ISOLATED', 'PRIVATE'],\n default: 'ISOLATED',\n },\n env: { type: 'object', description: 'Environment variables', additionalProperties: { type: 'string' } },\n timeout: { type: 'number', description: 'Default command timeout in ms' },\n },\n },\n createSandbox: config => new PlatformSandbox(config),\n};\n\nexport const platformFilesystemProvider: FilesystemProvider<PlatformFilesystemOptions> = {\n id: 'platform',\n name: 'Mastra Platform Filesystem',\n description: 'Bucket-backed filesystem access through Mastra Platform workspace proxy',\n configSchema: {\n type: 'object',\n properties: {\n accessToken: {\n type: 'string',\n description: 'Mastra Platform access token (falls back to MASTRA_PLATFORM_ACCESS_TOKEN)',\n },\n projectId: { type: 'string', description: 'Platform project ID (falls back to MASTRA_PROJECT_ID)' },\n bucketName: {\n type: 'string',\n description: 'Platform workspace bucket name (falls back to MASTRA_PLATFORM_BUCKET_NAME)',\n },\n readOnly: { type: 'boolean', description: 'Mount as read-only', default: false },\n },\n },\n createFilesystem: config => new PlatformFilesystem(config),\n};\n","/**\n * In-process `sandboxId → instanceUrl` registry populated by\n * {@link PlatformSandbox.start} from the `instanceUrl` field workspace-proxy\n * includes on create + get responses, and consumed by\n * {@link PlatformSandbox.executeCommand} on the private-network fast path.\n *\n * The registry lives in the same Node process as the `PlatformSandbox`\n * consumer — for shipyard, that is the Mastra runtime deployed from\n * `mastracode/web`. There is no receiver route and no cross-service dance;\n * the address is just a field copied from a response the runtime already\n * receives.\n *\n * State intentionally does not persist:\n *\n * - The IPv6 rotates on every sandbox recreate.\n * - The URL has no meaning after the sandbox is destroyed.\n * - The live sandbox binding, session context, and lease all die with the\n * runtime process; the address dying with them is correct.\n * - The proxy's `environment_sandboxes.instance_url` column is the durable\n * source of truth — a runtime restart re-populates the registry on the\n * next `start()` / reattach from the proxy's response.\n */\n\nimport type { SandboxAddressRegistry } from './sandbox.js';\n\n/**\n * Concrete in-process {@link SandboxAddressRegistry}. Backed by a `Map`; no\n * eviction policy, no TTL — entries live until an observed transport failure\n * calls `delete`, until the sandbox is explicitly destroyed, or until the\n * process exits.\n */\nexport class InProcessSandboxAddressRegistry implements SandboxAddressRegistry {\n readonly #map = new Map<string, string>();\n\n get(sandboxId: string): string | undefined {\n return this.#map.get(sandboxId);\n }\n\n /**\n * Populate or overwrite the address for a sandbox. Called by\n * {@link PlatformSandbox.start} on every fresh provision and every reattach;\n * overwriting is intentional so a re-provision with a fresh IPv6 heals the\n * map without a branch.\n */\n set(sandboxId: string, instanceUrl: string): void {\n this.#map.set(sandboxId, instanceUrl);\n }\n\n delete(sandboxId: string): void {\n this.#map.delete(sandboxId);\n }\n\n /**\n * Test-only introspection. Not part of {@link SandboxAddressRegistry} —\n * production callers must not read the registry as a whole.\n */\n get size(): number {\n return this.#map.size;\n }\n}\n"],"mappings":"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"}
|