@mastra/platform-workspace 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @mastra/platform
2
2
 
3
+ ## 0.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix direct-exec fallback loop: when the WebSocket transport fails on a sandbox (Railway rejects the handshake, mid-stream drop, etc.), disable direct-exec permanently for that sandbox instead of re-minting a fresh lease on every subsequent exec. Also surface WebSocket close code, reason, and `opened` state in `DirectExecResult` and emit a diagnostic warning on transport failure so we can see why Railway is refusing the upgrade in production. ([#20412](https://github.com/mastra-ai/mastra/pull/20412))
8
+
9
+ - Updated dependencies [[`3f472b4`](https://github.com/mastra-ai/mastra/commit/3f472b468892a1ff14ccb43cc0343b86f7d8fd7d), [`ba369f2`](https://github.com/mastra-ai/mastra/commit/ba369f2a0aaf998da0d6aa033d26f64f96bef8ac), [`35b929b`](https://github.com/mastra-ai/mastra/commit/35b929b7abc3d20d85c7985880960ac2d04a6c86), [`55c9e24`](https://github.com/mastra-ai/mastra/commit/55c9e248c27c1d72b5bb7e94ea6b8a3999eee49f), [`dcfed93`](https://github.com/mastra-ai/mastra/commit/dcfed93e1e256c6abfa792cbb7ca836f5d0e8638), [`2876e15`](https://github.com/mastra-ai/mastra/commit/2876e15b4d2f616a3bc1ed3af57d546c268384ce), [`9b3626a`](https://github.com/mastra-ai/mastra/commit/9b3626aeb1d16fcd34b0a8e94c114ddb80a3b240), [`4696963`](https://github.com/mastra-ai/mastra/commit/469696312ac4c618bc8475b0c5ed7949b8a3455e), [`723aa54`](https://github.com/mastra-ai/mastra/commit/723aa5437106bdb708ae03c0ef6b77aa11291e73), [`07f5b4b`](https://github.com/mastra-ai/mastra/commit/07f5b4ba9d608d88865030732e580298296adf99), [`723aa54`](https://github.com/mastra-ai/mastra/commit/723aa5437106bdb708ae03c0ef6b77aa11291e73), [`723aa54`](https://github.com/mastra-ai/mastra/commit/723aa5437106bdb708ae03c0ef6b77aa11291e73), [`598080f`](https://github.com/mastra-ai/mastra/commit/598080f224edb3f0f5b801035b067fac50a56a03)]:
10
+ - @mastra/core@1.55.0
11
+
12
+ ## 0.2.3-alpha.0
13
+
14
+ ### Patch Changes
15
+
16
+ - Fix direct-exec fallback loop: when the WebSocket transport fails on a sandbox (Railway rejects the handshake, mid-stream drop, etc.), disable direct-exec permanently for that sandbox instead of re-minting a fresh lease on every subsequent exec. Also surface WebSocket close code, reason, and `opened` state in `DirectExecResult` and emit a diagnostic warning on transport failure so we can see why Railway is refusing the upgrade in production. ([#20412](https://github.com/mastra-ai/mastra/pull/20412))
17
+
18
+ - Updated dependencies [[`55c9e24`](https://github.com/mastra-ai/mastra/commit/55c9e248c27c1d72b5bb7e94ea6b8a3999eee49f), [`07f5b4b`](https://github.com/mastra-ai/mastra/commit/07f5b4ba9d608d88865030732e580298296adf99)]:
19
+ - @mastra/core@1.55.0-alpha.2
20
+
3
21
  ## 0.2.2
4
22
 
5
23
  ### Patch Changes
@@ -76,6 +76,15 @@ export interface DirectExecResult {
76
76
  stderr: string;
77
77
  truncated: boolean;
78
78
  timedOut: boolean;
79
+ /**
80
+ * WebSocket close metadata. Populated on any close (normal or transport
81
+ * failure). `opened` distinguishes handshake failures (never opened) from
82
+ * mid-stream drops. Callers use this for diagnostic logging; not part of
83
+ * the CommandResult contract.
84
+ */
85
+ closeCode?: number;
86
+ closeReason?: string;
87
+ opened?: boolean;
79
88
  }
80
89
  /**
81
90
  * Open the provider exec WebSocket using `lease`, run `command`, and resolve
@@ -1 +1 @@
1
- {"version":3,"file":"direct-exec.d.ts","sourceRoot":"","sources":["../src/direct-exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACvD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACpE,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED;;;;GAIG;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,mBAAmB,CAAC;AAE3G,8FAA8F;AAC9F,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAcD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA6IpG"}
1
+ {"version":3,"file":"direct-exec.d.ts","sourceRoot":"","sources":["../src/direct-exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACvD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACpE,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED;;;;GAIG;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,mBAAmB,CAAC;AAE3G,8FAA8F;AAC9F,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAcD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyJpG"}
package/dist/index.cjs CHANGED
@@ -408,6 +408,8 @@ function execViaLease(lease, options) {
408
408
  let timedOut = false;
409
409
  let settled = false;
410
410
  let opened = false;
411
+ let closeCode;
412
+ let closeReason;
411
413
  let timer;
412
414
  let handshakeTimer;
413
415
  const settle = () => {
@@ -433,7 +435,10 @@ function execViaLease(lease, options) {
433
435
  stdout,
434
436
  stderr,
435
437
  truncated: false,
436
- timedOut
438
+ timedOut,
439
+ ...closeCode !== void 0 && { closeCode },
440
+ ...closeReason !== void 0 && { closeReason },
441
+ opened
437
442
  });
438
443
  };
439
444
  if (options.timeoutMs !== void 0 && options.timeoutMs > 0) timer = setTimeout(() => {
@@ -467,6 +472,8 @@ function execViaLease(lease, options) {
467
472
  else if (typeof data === "string") handleTextFrame(data);
468
473
  };
469
474
  socket.onclose = (event) => {
475
+ closeCode = event.code;
476
+ closeReason = event.reason;
470
477
  if (!opened) {
471
478
  settle();
472
479
  return;
@@ -620,7 +627,8 @@ var PlatformSandbox = class PlatformSandbox extends _mastra_core_workspace.Mastr
620
627
  * Tri-state feature detection for the platform's exec-lease endpoint:
621
628
  * undefined — not yet tried (default; try direct on first exec)
622
629
  * true — endpoint present, use direct exec
623
- * false — endpoint returned 404 or 501, fall back permanently to /exec
630
+ * false — endpoint absent (404/501) OR the WebSocket transport failed
631
+ * once; fall back permanently to /exec for this sandbox
624
632
  * Sticky per instance so we make the fallback decision once per sandbox
625
633
  * lifetime instead of paying an extra round-trip on every exec.
626
634
  */
@@ -773,7 +781,15 @@ var PlatformSandbox = class PlatformSandbox extends _mastra_core_workspace.Mastr
773
781
  ...this._webSocketFactory && { webSocketFactory: this._webSocketFactory }
774
782
  });
775
783
  if (result.exitCode === null && !result.timedOut) {
784
+ console.warn("[platform-workspace] direct-exec transport failed; falling back to /exec permanently for this sandbox", {
785
+ sandboxId: this._sandboxId,
786
+ opened: result.opened,
787
+ closeCode: result.closeCode,
788
+ closeReason: result.closeReason,
789
+ wsEndpoint: lease.wsEndpoint
790
+ });
776
791
  this._lease = null;
792
+ this._directExecAvailable = false;
777
793
  return null;
778
794
  }
779
795
  const exitCode = result.exitCode ?? 124;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["nodePath","path","Buffer","MastraFilesystem","FileNotFoundError","buffer","WorkspaceReadOnlyError","FileExistsError","ProcessHandle","SandboxProcessManager","MastraSandbox","SandboxNotReadyError"],"sources":["../src/client.ts","../src/filesystem.ts","../src/direct-exec.ts","../src/sandbox.ts","../src/provider.ts"],"sourcesContent":["export interface PlatformClientOptions {\n accessToken?: string;\n projectId?: 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(\n options.accessToken ??\n process.env.MASTRA_PLATFORM_SECRET_KEY ??\n // Deprecated alias — prefer MASTRA_PLATFORM_SECRET_KEY.\n process.env.MASTRA_PLATFORM_ACCESS_TOKEN,\n 'accessToken',\n ),\n projectId: requireOption(options.projectId ?? process.env.MASTRA_PROJECT_ID, 'projectId'),\n proxyUrl: (process.env.MASTRA_WORKSPACE_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\\/$/, ''),\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 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.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\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\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 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({ exitCode, stdout, stderr, truncated: false, timedOut });\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 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 void event; // eslint: intentionally observed for future logging\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","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';\n\nexport type PlatformSandboxNetworkIsolation = 'ISOLATED' | 'PRIVATE';\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\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\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\ninterface ExecResponse {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n truncated?: boolean;\n timedOut?: boolean;\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 /**\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 * Tri-state feature detection for the platform's exec-lease endpoint:\n * undefined — not yet tried (default; try direct on first exec)\n * true — endpoint present, use direct exec\n * false — endpoint returned 404 or 501, fall back permanently to /exec\n * Sticky per instance so we make the fallback decision once per sandbox\n * lifetime instead of paying an extra round-trip on every exec.\n */\n private _directExecAvailable: boolean | undefined = undefined;\n\n constructor(options: PlatformSandboxOptions = {}) {\n super({ ...options, name: 'PlatformSandbox', processes: new PlatformProcessManager() });\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 }\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 return new PlatformSandbox({\n ...(options.id !== undefined && { id: options.id }),\n accessToken: this._client.accessToken,\n projectId: this._client.projectId,\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 });\n }\n\n async start(): Promise<void> {\n if (this._sandboxId) {\n try {\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`);\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 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 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 json = (await response.json()) as CreateSandboxResponse;\n this._sandboxId = json.id;\n this._createdAt = json.createdAt ? new Date(json.createdAt) : new Date();\n }\n\n async stop(): Promise<void> {\n await this.destroy();\n }\n\n async destroy(): Promise<void> {\n if (!this._sandboxId) return;\n await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`, { 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 }\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` is sent as `0` (interpreted as\n // \"no timeout\" by the proxy) instead of being dropped by a truthy check.\n const effectiveTimeout = options?.timeout ?? this._timeout;\n\n // Prefer the direct-exec data plane (WebSocket straight to Railway's\n // tcp-proxy) when the platform exposes the exec-lease endpoint. Falls\n // back to the proxy /exec route on older platform deployments. See\n // ./direct-exec.ts and `docs/factory/direct-sandbox-connection.md` in\n // the Platform repo.\n if (this._directExecAvailable !== false) {\n const leaseResult = await this._tryDirectExec(fullCommand, effectiveTimeout, options);\n if (leaseResult) return { ...leaseResult, executionTimeMs: Date.now() - started };\n }\n return this._execViaProxy(fullCommand, effectiveTimeout, options, started);\n }\n\n private async _tryDirectExec(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n ): Promise<Omit<CommandResult, 'executionTimeMs'> | null> {\n let lease: (ExecLease & { expiresAtMs: number | null }) | null;\n try {\n lease = await this._ensureLease();\n } catch (error) {\n // 404/501 → endpoint not present on this proxy deployment. Flip the\n // feature bit and take the fallback path for this call AND every\n // subsequent one on this sandbox.\n if (error instanceof PlatformApiError && (error.status === 404 || error.status === 501)) {\n this._directExecAvailable = false;\n return null;\n }\n throw error;\n }\n this._directExecAvailable = true;\n\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); the proxy `/exec` path ships\n // it through JSON.stringify which drops undefined implicitly, so this is\n // just the explicit version of the same filter.\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 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 // `null` exitCode without `timedOut` means the socket closed without an\n // exit frame — i.e. a transport failure (handshake stalled, mid-stream\n // drop, expired token). Drop the cached lease so the next call re-mints,\n // and return `null` to let the caller fall through to the proxy /exec\n // path. Timed-out and normal-exit results still return synthesised /\n // real exit codes as before.\n if (result.exitCode === null && !result.timedOut) {\n this._lease = null;\n return null;\n }\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 };\n }\n\n private async _execViaProxy(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n started: number,\n ): Promise<CommandResult> {\n if (!this._sandboxId) throw new SandboxNotReadyError(this.id);\n const timeoutSec = effectiveTimeout != null ? Math.ceil(effectiveTimeout / 1000) : undefined;\n // Pass our own signal for exec so the client's default per-request\n // timeout (60s) doesn't cut off commands that expect to run longer.\n // Give the proxy a generous buffer over the requested command timeout.\n const clientSignal =\n effectiveTimeout != null && effectiveTimeout > 0 ? AbortSignal.timeout(effectiveTimeout + 30_000) : undefined;\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}/exec`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n command: fullCommand,\n timeoutSec,\n cwd: options?.cwd,\n env: options?.env,\n }),\n signal: clientSignal,\n });\n const json = (await response.json()) as ExecResponse;\n const exitCode = json.exitCode ?? (json.timedOut ? 124 : 1);\n return {\n success: exitCode === 0,\n exitCode,\n stdout: json.stdout,\n stderr: json.stderr,\n executionTimeMs: Date.now() - started,\n timedOut: json.timedOut,\n command: fullCommand,\n };\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 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 secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)',\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 secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)',\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,MAAM,oBAAoB;;;;;;AAO1B,MAAM,6BAA6B;AAEnC,SAAgB,cAAc,OAA2B,MAAsB;CAC7E,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,GAAG,KAAK,aAAa;CACjD,OAAO;AACT;AAEA,SAAgB,uBAAuB,SAAgC;CACrE,OAAO;EACL,aAAa,cACX,QAAQ,eACN,QAAQ,IAAI,8BAEZ,QAAQ,IAAI,8BACd,aACF;EACA,WAAW,cAAc,QAAQ,aAAa,QAAQ,IAAI,mBAAmB,WAAW;EACxF,WAAW,QAAQ,IAAI,8BAA8B,kBAAA,CAAmB,QAAQ,OAAO,EAAE;EACzF,OAAO,QAAQ,SAAS;CAC1B;AACF;AAcA,SAAS,gBAAgB,MAA8C;CACrE,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,QAAQ;EACN;CACF;CACA,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM,OAAO,KAAA;CAC1D,MAAM,MAAO,OAA+B;CAC5C,IAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM,OAAO,KAAA;CACpD,MAAM,EAAE,SAAS,SAAS;CAC1B,IAAI,OAAO,YAAY,YAAY,OAAO,SAAS,UAAU,OAAO,KAAA;CACpE,OAAO;EAAE;EAAS;CAAK;AACzB;AAEA,IAAa,mBAAb,cAAsC,MAAM;CAC1C;CACA;;CAEA;;CAEA;CAEA,YAAY,QAAgB,MAAc;EACxC,MAAM,SAAS,gBAAgB,IAAI;EACnC,MAAM,UAAU,SAAS,GAAG,OAAO,KAAK,IAAI,OAAO,YAAY;EAC/D,MAAM,sCAAsC,SAAS,UAAU,KAAK,YAAY,IAAI;EACpF,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,eAAe,QAAQ;CAC9B;AACF;AAEA,IAAa,iBAAb,MAA4B;CAC1B;CACA;CACA;CACA;CAEA,YAAY,SAAgC;EAC1C,MAAM,WAAW,uBAAuB,OAAO;EAC/C,KAAK,cAAc,SAAS;EAC5B,KAAK,YAAY,SAAS;EAC1B,KAAK,WAAW,SAAS;EACzB,KAAK,QAAQ,SAAS;CACxB;CAEA,MAAM,QAAQ,MAAc,UAAkC,CAAC,GAAsB;EACnF,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,SAAS,eAAe,mBAAmB,KAAK,SAAS,IAAI,MAAM;EAC/F,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,SAAS,CAAC,CAAC,GAC3D,IAAI,UAAU,KAAA,GAAW,IAAI,aAAa,IAAI,KAAK,OAAO,KAAK,CAAC;EAGlE,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;EAC3C,QAAQ,IAAI,iBAAiB,UAAU,KAAK,aAAa;EAGzD,MAAM,EAAE,OAAO,QAAQ,GAAG,iBAAiB;EAG3C,MAAM,SAAS,aAAa,UAAU,YAAY,QAAQ,0BAA0B;EACpF,MAAM,WAAW,MAAM,KAAK,MAAM,KAAK;GAAE,GAAG;GAAc;GAAS;EAAO,CAAC;EAC3E,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,iBAAiB,SAAS,QAAQ,MAAM,SAAS,KAAK,CAAC;EAEnE,OAAO;CACT;AACF;;;ACpFA,SAAS,cAAc,OAAuB;CAC5C,IAAI,CAAC,SAAS,UAAU,KAAK,OAAO;CACpC,IAAI,aAAa,MAAM,WAAW,GAAG,IAAI,QAAQ,IAAI;CACrD,aAAaA,KAAAA,QAAS,MAAM,UAAU,UAAU;CAChD,OAAO,eAAe,MAAM,MAAM;AACpC;AAEA,SAAS,YAAY,QAAsB;CACzC,MAAM,aAAa,cAAcC,MAAI;CACrC,OAAO,eAAe,MAAM,KAAK,WAAW,MAAM,CAAC;AACrD;;;;;;;AAQA,SAAS,cAAc,KAAqB;CAC1C,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;AACxD;AAEA,SAAS,aAAa,QAAsB;CAC1C,MAAM,aAAa,cAAcA,MAAI;CACrC,IAAI,eAAe,KAAK,OAAO;CAC/B,OAAO,WAAW,MAAM,WAAW,YAAY,GAAG,IAAI,CAAC;AACzD;AAEA,SAAS,cAAc,SAAuC;CAC5D,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,OAAOC,OAAAA,OAAO,KAAK,OAAO;AAC5B;AAEA,SAAS,WAAW,SAAkB,MAAoB;CACxD,MAAM,QAAQ,QAAQ,IAAI,IAAI;CAC9B,OAAO,QAAQ,IAAI,KAAK,KAAK,oBAAI,IAAI,KAAK,CAAC;AAC7C;AAEA,SAAS,WAAW,SAA0B;CAC5C,MAAM,QAAQ,QAAQ,IAAI,gBAAgB;CAC1C,OAAO,QAAQ,OAAO,KAAK,IAAI;AACjC;AAEA,SAAS,WAAW,OAAyB;CAC3C,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,SAAS,MAAM,WAAW;AAC9F;AAEA,IAAa,qBAAb,cAAwCC,uBAAAA,iBAAiB;CACvD;CACA,OAAgB;CAChB,WAAoB;CACpB;CACA;CACA;CACA;CACA,SAAyB;CAEzB;CACA;CACA;CAEA,YAAY,UAAqC,CAAC,GAAG;EACnD,MAAM;GAAE,GAAG;GAAS,MAAM;EAAqB,CAAC;EAChD,KAAK,KAAK,QAAQ,MAAM,KAAK,WAAW;EACxC,KAAK,cAAc,QAAQ,cAAc,QAAQ,IAAI,+BAA+B;EACpF,IAAI,CAAC,KAAK,aAAa,MAAM,IAAI,MAAM,wBAAwB;EAC/D,KAAK,WAAW,QAAQ;EACxB,KAAK,cAAc,QAAQ;EAC3B,KAAK,OAAO,QAAQ,QAAQ;EAC5B,KAAK,cAAc,QAAQ;EAC3B,KAAK,wBAAwB,QAAQ;EACrC,KAAK,UAAU,IAAI,eAAe,OAAO;CAC3C;CAEA,aAA6B;EAC3B,OAAO,eAAe,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;CACxF;CAEA,MAAM,SAAS,QAAc,SAAiD;EAC5E,MAAM,KAAK,YAAY;EACvB,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,KAAK,QAAQ,QAC5B,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAYF,MAAI,CAAC,GAChF;EACF,SAAS,OAAO;GACd,IAAI,WAAW,KAAK,GAAG,MAAM,IAAIG,uBAAAA,kBAAkBH,MAAI;GACvD,MAAM;EACR;EACA,MAAMI,WAASH,OAAAA,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;EACvD,OAAO,SAAS,WAAWG,SAAO,SAAS,QAAQ,QAAQ,IAAIA;CACjE;CAEA,MAAM,UAAU,QAAc,SAAsB,SAAuC;EACzF,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAIC,uBAAAA,uBAAuB,WAAW;EAC/D,MAAM,UAAkC,CAAC;EACzC,IAAI,SAAS,UAAU,QAAQ,kBAAkB,QAAQ;EACzD,IAAI,SAAS,cAAc,OAAO,QAAQ,mBAAmB;EAC7D,IAAI;GACF,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAYL,MAAI,CAAC,KAAK;IAC5G,QAAQ;IACR;IACA,MAAM,cAAc,OAAO;GAC7B,CAAC;EACH,SAAS,OAAO;GACd,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,SAAS,MAAM,WAAW,KACvF,MAAM,IAAIM,uBAAAA,gBAAgBN,MAAI;GAEhC,MAAM;EACR;CACF;;;;;;;;;;;CAYA,MAAM,WAAW,QAAc,SAAqC;EAClE,MAAM,WAAY,MAAM,KAAK,OAAOA,MAAI,IAAK,MAAM,KAAK,SAASA,MAAI,IAAIC,OAAAA,OAAO,MAAM,CAAC;EACvF,MAAM,KAAK,UACTD,QACAC,OAAAA,OAAO,OAAO,CAACA,OAAAA,OAAO,SAAS,QAAQ,IAAI,WAAWA,OAAAA,OAAO,KAAK,QAAQ,GAAGA,OAAAA,OAAO,KAAK,OAAO,CAAC,CAAC,CACpG;CACF;CAEA,MAAM,WAAW,QAAc,SAAwC;EACrE,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAII,uBAAAA,uBAAuB,YAAY;EAChE,IAAI;GACF,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAYL,MAAI,CAAC,KAAK;IAC5G,QAAQ;IACR,OAAO,EAAE,WAAW,SAAS,UAAU;GACzC,CAAC;EACH,SAAS,OAAO;GACd,IAAI,WAAW,KAAK,KAAK,SAAS,OAAO;GACzC,IAAI,WAAW,KAAK,GAAG,MAAM,IAAIG,uBAAAA,kBAAkBH,MAAI;GACvD,MAAM;EACR;CACF;CAEA,MAAM,SAAS,KAAa,MAAc,SAAsC;EAC9E,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAIK,uBAAAA,uBAAuB,UAAU;EAI9D,IAAI,SAAS,cAAc,OACzB,MAAM,IAAI,MAAM,8FAA8F;EAEhH,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAY,GAAG,CAAC,KAAK;GAC3G,QAAQ;GACR,OAAO,EAAE,IAAI,OAAO;GACpB,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU,EAAE,aAAa,YAAY,IAAI,EAAE,CAAC;EACzD,CAAC;CACH;CAEA,MAAM,SAAS,KAAa,MAAc,SAAsC;EAC9E,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAIA,uBAAAA,uBAAuB,UAAU;EAE9D,IAAI,SAAS,cAAc,OACzB,MAAM,IAAI,MAAM,8FAA8F;EAEhH,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAY,GAAG,CAAC,KAAK;GAC3G,QAAQ;GACR,OAAO,EAAE,IAAI,SAAS;GACtB,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU,EAAE,aAAa,YAAY,IAAI,EAAE,CAAC;EACzD,CAAC;CACH;CAEA,MAAM,MAAM,QAAc,UAAmD;EAC3E,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAIA,uBAAAA,uBAAuB,OAAO;EAC3D,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAYL,MAAI,CAAC,KAAK;GAC5G,QAAQ;GACR,OAAO,EAAE,IAAI,QAAQ;EACvB,CAAC;CACH;CAEA,MAAM,MAAM,QAAc,SAAwC;EAChE,MAAM,KAAK,WAAWA,OAAK,SAAS,GAAG,IAAIA,SAAO,GAAGA,OAAK,IAAI;GAAE,WAAW;GAAM,OAAO,SAAS;EAAM,CAAC;CAC1G;CAEA,MAAM,QAAQ,QAAc,SAA6C;EACvE,MAAM,KAAK,YAAY;EACvB,MAAM,SAAS,YAAYA,MAAI;EAU/B,MAAM,OAAQ,OAAM,MATG,KAAK,QAAQ,QAClC,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,MAAM,KACnE,EACE,OAAO;GACL,WAAW,SAAS,YAAY,KAAA,IAAY;GAC5C,QAAQ,SAAS,GAAG,OAAO,QAAQ,OAAO,EAAE,EAAE,KAAK,KAAA;EACrD,EACF,CACF,EAAA,CAC6B,KAAK;EAClC,OAAO,CACL,IAAI,KAAK,kBAAkB,CAAC,EAAA,CAAG,KAAI,YAAW;GAC5C,MAAM,aAAa,OAAO,QAAQ,OAAO,EAAE,CAAC;GAC5C,MAAM;EACR,EAAE,GACF,IAAI,KAAK,YAAY,CAAC,EAAA,CACnB,QAAO,WAAU,OAAO,OAAO,CAAC,OAAO,IAAI,SAAS,GAAG,CAAC,CAAC,CACzD,KAAI,YAAW;GACd,MAAM,aAAa,OAAO,GAAI;GAC9B,MAAM;GACN,MAAM,OAAO;EACf,EAAE,CACN,CAAC,CAAC,QACA,UAAS,CAAC,SAAS,aAAa,MAAM,SAAS,eAAe,iBAAiB,MAAM,MAAM,QAAQ,SAAS,CAC9G;CACF;CAEA,MAAM,OAAO,SAAgC;EAC3C,IAAI;GACF,MAAM,KAAK,KAAKA,OAAI;GACpB,OAAO;EACT,SAAS,OAAO;GACd,IAAI,WAAW,KAAK,KAAK,iBAAiBG,uBAAAA,mBAAmB,OAAO;GACpE,MAAM;EACR;CACF;CAEA,MAAM,KAAK,SAAiC;EAC1C,MAAM,KAAK,YAAY;EACvB,MAAM,aAAa,cAAcH,OAAI;EACrC,IAAI,eAAe,KACjB,OAAO;GAAE,MAAM;GAAI,MAAM;GAAK,MAAM;GAAa,MAAM;GAAG,2BAAW,IAAI,KAAK,CAAC;GAAG,4BAAY,IAAI,KAAK,CAAC;EAAE;EAE5G,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,KAAK,QAAQ,QAC5B,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAYA,OAAI,CAAC,KAC9E,EACE,QAAQ,OACV,CACF;EACF,SAAS,OAAO;GACd,IAAI,WAAW,KAAK,GAAG,MAAM,IAAIG,uBAAAA,kBAAkBH,OAAI;GACvD,MAAM;EACR;EACA,OAAO;GACL,MAAM,aAAaA,OAAI;GACvB,MAAM;GACN,MAAM,WAAW,SAAS,GAAG,IAAI,cAAc;GAC/C,MAAM,WAAW,SAAS,OAAO;GACjC,WAAW,WAAW,SAAS,SAAS,eAAe;GACvD,YAAY,WAAW,SAAS,SAAS,eAAe;GACxD,UAAU,SAAS,QAAQ,IAAI,cAAc,KAAK,KAAA;EACpD;CACF;CAEA,SAAS,SAA+B;EACtC,OAAO,QAAQ,QAAQ,cAAcA,OAAI,CAAC;CAC5C;CAEA,gBAAgB,MAAoD;EAClE,MAAM,sBAAsB,wDAAwD,KAAK,YAAY;EACrG,IAAI,OAAO,KAAK,0BAA0B,YACxC,OAAO,KAAK,sBAAsB;GAAE;GAAqB,gBAAgB,MAAM;EAAe,CAAC;EAEjG,IAAI,OAAO,KAAK,0BAA0B,UAAU,OAAO,KAAK;EAChE,OAAO;CACT;CAEA,UAA8F;EAC5F,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,UAAU,KAAK;GACf,QAAQ,KAAK;GACb,UAAU,KAAK;GACf,MAAM,KAAK;GACX,UAAU;IACR,YAAY,KAAK;IACjB,GAAI,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;IACxD,GAAI,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;GAC1D;EACF;CACF;AACF;AAEA,SAAS,iBAAiB,MAAc,WAAuC;CAE7E,QADmB,MAAM,QAAQ,SAAS,IAAI,YAAY,CAAC,SAAS,EAAA,CAClD,MAAK,QAAO,KAAK,SAAS,GAAG,CAAC;AAClD;;;;;;;;;;;;;;;;;AC3TA,MAAM,eAAe;;AAErB,MAAM,eAAe;;;;;;;;AAQrB,MAAM,wBAAwB;AAoE9B,MAAM,sBAAkD,UAAU,iBAAiB;CACjF,MAAM,KAAM,WAAuC;CAGnD,IAAI,CAAC,IACH,MAAM,IAAI,MACR,uIACF;CAEF,OAAO,IAAI,GAAG,UAAU,YAAY;AACtC;;;;;;;;;AAUA,SAAgB,aAAa,OAAkB,SAAuD;CACpG,MAAM,UAAU,QAAQ,oBAAoB;CAC5C,MAAM,gBAAgB,IAAI,YAAY;CACtC,MAAM,gBAAgB,IAAI,YAAY;CAEtC,OAAO,IAAI,SAA0B,YAAW;EAC9C,IAAI,SAAS;EACb,IAAI,SAAS;EACb,IAAI,WAA0B;EAC9B,IAAI,WAAW;EACf,IAAI,UAAU;EACd,IAAI,SAAS;EACb,IAAI;EACJ,IAAI;EAEJ,MAAM,eAAe;GACnB,IAAI,SAAS;GACb,UAAU;GACV,IAAI,OAAO,aAAa,KAAK;GAC7B,IAAI,gBAAgB,aAAa,cAAc;GAI/C,MAAM,aAAa,cAAc,OAAO;GACxC,IAAI,YAAY;IACd,UAAU;IACV,QAAQ,WAAW,UAAU;GAC/B;GACA,MAAM,aAAa,cAAc,OAAO;GACxC,IAAI,YAAY;IACd,UAAU;IACV,QAAQ,WAAW,UAAU;GAC/B;GACA,IAAI;IACF,OAAO,MAAM,KAAM,EAAE;GACvB,QAAQ,CAER;GACA,QAAQ;IAAE;IAAU;IAAQ;IAAQ,WAAW;IAAO;GAAS,CAAC;EAClE;EAMA,IAAI,QAAQ,cAAc,KAAA,KAAa,QAAQ,YAAY,GACzD,QAAQ,iBAAiB;GACvB,WAAW;GAGX,IAAI,aAAa,MAAM,WAAW;GAClC,OAAO;EACT,GAAG,QAAQ,SAAS;OAEpB,iBAAiB,iBAAiB;GAIhC,IAAI,CAAC,QAAQ,OAAO;EACtB,GAAG,qBAAqB;EAG1B,MAAM,SAAS,QAAQ,MAAM,YAAY,CAAC,MAAM,aAAa,MAAM,GAAG,CAAC;EACvE,OAAO,aAAa;EAEpB,OAAO,eAAe;GACpB,SAAS;GACT,IAAI,gBAAgB;IAClB,aAAa,cAAc;IAC3B,iBAAiB,KAAA;GACnB;GACA,MAAM,OAAgC,EAAE,SAAS,QAAQ,QAAQ;GACjE,IAAI,QAAQ,KAAK,KAAK,MAAM,QAAQ;GACpC,IAAI,QAAQ,OAAO,OAAO,KAAK,QAAQ,GAAG,CAAC,CAAC,SAAS,GAAG,KAAK,MAAM,QAAQ;GAC3E,OAAO,KAAK,KAAK,UAAU;IAAE,MAAM;IAAa;GAAK,CAAC,CAAC;GAGvD,OAAO,KAAK,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC,CAAC;EACrD;EAEA,OAAO,aAAY,UAAS;GAC1B,MAAM,EAAE,SAAS;GACjB,IAAI,gBAAgB,aAClB,kBAAkB,IAAI;QACjB,IAAI,OAAO,SAAS,UACzB,gBAAgB,IAAI;EAExB;EAEA,OAAO,WAAU,UAAS;GACxB,IAAI,CAAC,QAAQ;IAIX,OAAO;IACP;GACF;GAGA,OAAO;EAET;EAEA,OAAO,gBAAgB;GACrB,IAAI,SAAS;GACb,IAAI,CAAC,QACH,OAAO;EAIX;EAEA,SAAS,kBAAkB,QAAqB;GAC9C,MAAM,OAAO,IAAI,WAAW,MAAM;GAClC,IAAI,KAAK,UAAU,GAAG;GACtB,IAAI,KAAK,OAAO,cAAc;IAC5B,MAAM,QAAQ,cAAc,OAAO,KAAK,SAAS,CAAC,GAAG,EAAE,QAAQ,KAAK,CAAC;IACrE,UAAU;IACV,QAAQ,WAAW,KAAK;GAC1B,OAAO,IAAI,KAAK,OAAO,cAAc;IACnC,MAAM,QAAQ,cAAc,OAAO,KAAK,SAAS,CAAC,GAAG,EAAE,QAAQ,KAAK,CAAC;IACrE,UAAU;IACV,QAAQ,WAAW,KAAK;GAC1B;EACF;EAEA,SAAS,gBAAgB,MAAc;GACrC,IAAI;GACJ,IAAI;IACF,QAAQ,KAAK,MAAM,IAAI;GACzB,QAAQ;IACN;GACF;GACA,IAAI,MAAM,SAAS,QAAQ;IACzB,WAAW,MAAM,MAAM,aAAa;IACpC,OAAO;GACT;EAGF;CACF,CAAC;AACH;;;;;;;;ACzMA,MAAM,0BAA0B;;AAWhC,MAAM,sBAAsB;;AAE5B,MAAM,6BAA6B;;;;;;;;;;;;;;;AAwBnC,SAAS,aAAa,SAAiB,MAAyB;CAC9D,OAAO,MAAM,SAAS,GAAG,QAAQ,GAAG,KAAK,IAAI,UAAU,CAAC,CAAC,KAAK,GAAG,MAAM;AACzE;AAEA,SAAS,WAAW,KAAqB;CACvC,IAAI,yBAAyB,KAAK,GAAG,GAAG,OAAO;CAC/C,OAAO,IAAI,IAAI,QAAQ,MAAM,OAAO,EAAE;AACxC;AAEA,IAAM,wBAAN,cAAoCO,uBAAAA,cAAc;CAChD;CACA;CACA;CAEA,YAAY,KAAa,eAAuC,SAA+B;EAC7F,MAAM,OAAO;EACb,KAAK,MAAM;EACX,KAAK,gBAAgB,cAAc,MAAK,WAAU;GAChD,KAAK,gBAAgB,OAAO;GAC5B,IAAI,OAAO,QAAQ,KAAK,WAAW,OAAO,MAAM;GAChD,IAAI,OAAO,QAAQ,KAAK,WAAW,OAAO,MAAM;GAChD,OAAO;EACT,CAAC;CACH;CAEA,IAAI,WAA+B;EACjC,OAAO,KAAK;CACd;CAEA,MAAM,OAA+B;EACnC,OAAO,KAAK;CACd;CAEA,MAAM,OAAyB;EAK7B,MAAM,IAAI,MAAM,kFAAkF;CACpG;CAEA,MAAM,YAA2B;EAC/B,MAAM,IAAI,MAAM,2DAA2D;CAC7E;AACF;AAEA,IAAM,yBAAN,cAAqCC,uBAAAA,sBAAuC;CAC1E,eAAuB;;;;;;;;CASvB,MAAM,MAAM,SAAiB,UAA+B,CAAC,GAA2B;EAGtF,MAAM,SAAS,IAAI,sBAAsB,iBAFZ,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,KAAK,eAAA,CAAgB,SAAS,EAAE,KACnE,KAAK,QAAQ,eAAe,SAAS,KAAA,GAAW,OACxB,GAAe,OAAO;EACpE,KAAK,SAAS,IAAI,OAAO,KAAK,MAAM;EACpC,OAAO;CACT;CAEA,MAAM,OAA+B;EACnC,OAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC,KAAI,YAAW;GACvD,KAAK,OAAO;GACZ,SAAS,OAAO;GAChB,SAAS,OAAO,aAAa,KAAA;GAC7B,GAAI,OAAO,aAAa,KAAA,KAAa,EAAE,UAAU,OAAO,SAAS;EACnE,EAAE;CACJ;AACF;AAEA,IAAa,kBAAb,MAAa,wBAAwBC,uBAAAA,cAAc;CACjD;CACA,OAAgB;CAChB,WAAoB;CACpB,SAAyB;CAGzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,aAAkC;CAClC;;;;;;;CAOA,SAAsE;;;;;;;CAOtE,iBAAqF;;;;;;;;;CASrF,uBAAoD,KAAA;CAEpD,YAAY,UAAkC,CAAC,GAAG;EAChD,MAAM;GAAE,GAAG;GAAS,MAAM;GAAmB,WAAW,IAAI,uBAAuB;EAAE,CAAC;EACtF,KAAK,KAAK,QAAQ,MAAM,KAAK,WAAW;EACxC,KAAK,UAAU,IAAI,eAAe,OAAO;EACzC,KAAK,iBAAiB,QAAQ,iBAAiB,QAAQ,IAAI,yBAAyB;EACpF,IAAI,CAAC,KAAK,kBAAkB,CAAC,QAAQ,WAAW,MAAM,IAAI,MAAM,2BAA2B;EAC3F,KAAK,aAAa,QAAQ;EAC1B,KAAK,sBAAsB,QAAQ;EACnC,KAAK,oBAAoB,QAAQ;EACjC,KAAK,OAAO,QAAQ,OAAO,CAAC;EAC5B,KAAK,WAAW,QAAQ;EACxB,KAAK,wBAAwB,QAAQ;EACrC,KAAK,oBAAoB,QAAQ;CACnC;CAEA,aAA6B;EAC3B,OAAO,oBAAoB,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;CAC7F;;;;;;;;;;;;;CAcA,MAAM,UAA+B,CAAC,GAAoB;EACxD,OAAO,IAAI,gBAAgB;GACzB,GAAI,QAAQ,OAAO,KAAA,KAAa,EAAE,IAAI,QAAQ,GAAG;GACjD,aAAa,KAAK,QAAQ;GAC1B,WAAW,KAAK,QAAQ;GACxB,OAAO,KAAK,QAAQ;GACpB,eAAe,KAAK;GACpB,GAAI,QAAQ,cAAc,KAAA,KAAa,EAAE,WAAW,QAAQ,UAAU;GACtE,oBAAoB,QAAQ,sBAAsB,KAAK;GACvD,GAAI,KAAK,sBAAsB,KAAA,KAAa,EAAE,kBAAkB,KAAK,kBAAkB;GACvF,KAAK,QAAQ,OAAO,KAAK;GACzB,GAAI,KAAK,aAAa,KAAA,KAAa,EAAE,SAAS,KAAK,SAAS;GAC5D,GAAI,KAAK,0BAA0B,KAAA,KAAa,EAAE,cAAc,KAAK,sBAAsB;GAC3F,GAAI,KAAK,sBAAsB,KAAA,KAAa,EAAE,kBAAkB,KAAK,kBAAkB;EACzF,CAAC;CACH;CAEA,MAAM,QAAuB;EAC3B,IAAI,KAAK,YACP,IAAI;GAEF,MAAM,OAAQ,OAAM,MADG,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,KAAK,UAAU,GAAG,EAAA,CAChE,KAAK;GAIlC,IAAI,CAAC,KAAK,aAAa;IACrB,KAAK,aAAa,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,oBAAI,IAAI,KAAK;IACvE;GACF;GACA,KAAK,aAAa,KAAA;EACpB,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,qBAAqB,MAAM,WAAW,KAAK,MAAM;GACxE,KAAK,aAAa,KAAA;EACpB;EAGF,IAAI,CAAC,KAAK,gBAAgB,MAAM,IAAI,MAAM,2BAA2B;EAErE,MAAM,OAAO,KAAK,UAAU;GAK1B,IAAI,KAAK;GACT,eAAe,KAAK;GACpB,oBAAoB,KAAK;GACzB,kBAAkB,KAAK;GACvB,KAAK,KAAK;EACZ,CAAC;EAMD,IAAI;EACJ,KAAK,IAAI,UAAU,IAAK,WACtB,IAAI;GACF,WAAW,MAAM,KAAK,QAAQ,QAAQ,YAAY;IAChD,QAAQ;IACR,SAAS,EAAE,gBAAgB,mBAAmB;IAC9C;GACF,CAAC;GACD;EACF,SAAS,OAAO;GAEd,IAAI,EADc,iBAAiB,oBAAoB,MAAM,UAAU,QACrD,WAAW,qBAAqB,MAAM;GACxD,MAAM,IAAI,SAAQ,YAAW,WAAW,SAAS,6BAA6B,OAAO,CAAC;EACxF;EAEF,MAAM,OAAQ,MAAM,SAAS,KAAK;EAClC,KAAK,aAAa,KAAK;EACvB,KAAK,aAAa,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,oBAAI,IAAI,KAAK;CACzE;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,QAAQ;CACrB;CAEA,MAAM,UAAyB;EAC7B,IAAI,CAAC,KAAK,YAAY;EACtB,MAAM,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,KAAK,UAAU,KAAK,EAAE,QAAQ,SAAS,CAAC;EAGlG,KAAK,aAAa,KAAA;EAClB,KAAK,aAAa;EAGlB,KAAK,SAAS;CAChB;;;;;;;;;;;;;;;;;;CAmBA,MAAM,eAAe,SAAiB,MAAiB,SAAyD;EAC9G,MAAM,KAAK,cAAc;EACzB,IAAI,CAAC,KAAK,YAAY,MAAM,IAAIC,uBAAAA,qBAAqB,KAAK,EAAE;EAE5D,MAAM,UAAU,KAAK,IAAI;EACzB,MAAM,cAAc,aAAa,SAAS,IAAI;EAG9C,MAAM,mBAAmB,SAAS,WAAW,KAAK;EAOlD,IAAI,KAAK,yBAAyB,OAAO;GACvC,MAAM,cAAc,MAAM,KAAK,eAAe,aAAa,kBAAkB,OAAO;GACpF,IAAI,aAAa,OAAO;IAAE,GAAG;IAAa,iBAAiB,KAAK,IAAI,IAAI;GAAQ;EAClF;EACA,OAAO,KAAK,cAAc,aAAa,kBAAkB,SAAS,OAAO;CAC3E;CAEA,MAAc,eACZ,aACA,kBACA,SACwD;EACxD,IAAI;EACJ,IAAI;GACF,QAAQ,MAAM,KAAK,aAAa;EAClC,SAAS,OAAO;GAId,IAAI,iBAAiB,qBAAqB,MAAM,WAAW,OAAO,MAAM,WAAW,MAAM;IACvF,KAAK,uBAAuB;IAC5B,OAAO;GACT;GACA,MAAM;EACR;EACA,KAAK,uBAAuB;EAO5B,MAAM,cAAc,SAAS,MACzB,OAAO,YACL,OAAO,QAAQ,QAAQ,GAAG,CAAC,CAAC,QAAQ,UAAqC,MAAM,OAAO,KAAA,CAAS,CACjG,IACA,KAAA;EACJ,MAAM,SAAS,MAAM,aAAa,OAAO;GACvC,SAAS;GACT,GAAI,SAAS,QAAQ,KAAA,KAAa,EAAE,KAAK,QAAQ,IAAI;GACrD,GAAI,gBAAgB,KAAA,KAAa,EAAE,KAAK,YAAY;GACpD,GAAI,oBAAoB,QAAQ,mBAAmB,KAAK,EAAE,WAAW,iBAAiB;GACtF,GAAI,KAAK,qBAAqB,EAAE,kBAAkB,KAAK,kBAAkB;EAC3E,CAAC;EAOD,IAAI,OAAO,aAAa,QAAQ,CAAC,OAAO,UAAU;GAChD,KAAK,SAAS;GACd,OAAO;EACT;EACA,MAAM,WAAW,OAAO,YAAY;EACpC,OAAO;GACL,SAAS,aAAa;GACtB;GACA,QAAQ,OAAO;GACf,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,SAAS;EACX;CACF;CAEA,MAAc,cACZ,aACA,kBACA,SACA,SACwB;EACxB,IAAI,CAAC,KAAK,YAAY,MAAM,IAAIA,uBAAAA,qBAAqB,KAAK,EAAE;EAC5D,MAAM,aAAa,oBAAoB,OAAO,KAAK,KAAK,mBAAmB,GAAI,IAAI,KAAA;EAInF,MAAM,eACJ,oBAAoB,QAAQ,mBAAmB,IAAI,YAAY,QAAQ,mBAAmB,GAAM,IAAI,KAAA;EAYtG,MAAM,OAAQ,OAAM,MAXG,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,KAAK,UAAU,EAAE,QAAQ;GAClG,QAAQ;GACR,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU;IACnB,SAAS;IACT;IACA,KAAK,SAAS;IACd,KAAK,SAAS;GAChB,CAAC;GACD,QAAQ;EACV,CAAC,EAAA,CAC4B,KAAK;EAClC,MAAM,WAAW,KAAK,aAAa,KAAK,WAAW,MAAM;EACzD,OAAO;GACL,SAAS,aAAa;GACtB;GACA,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,iBAAiB,KAAK,IAAI,IAAI;GAC9B,UAAU,KAAK;GACf,SAAS;EACX;CACF;;;;;;;;CASA,MAAc,eAAoE;EAChF,MAAM,MAAM,KAAK,IAAI;EAKrB,IAAI,KAAK,UAAU,KAAK,OAAO,gBAAgB,QAAQ,KAAK,OAAO,cAAc,0BAA0B,KACzG,OAAO,KAAK;EAGd,IAAI,KAAK,gBAAgB,OAAO,KAAK;EACrC,IAAI,CAAC,KAAK,YAAY,MAAM,IAAIA,uBAAAA,qBAAqB,KAAK,EAAE;EAC5D,MAAM,YAAY,KAAK;EACvB,MAAM,YAAY,YAAY;GAI5B,MAAM,OAAQ,OAAM,MAHG,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,SAAS,EAAE,cAAc,EAClG,QAAQ,OACV,CAAC,EAAA,CAC4B,KAAK;GAClC,MAAM,cAAc,KAAK,YAAY,KAAK,MAAM,KAAK,SAAS,IAAI;GAClE,MAAM,QAAQ;IACZ,KAAK,KAAK;IACV,YAAY,KAAK;IACjB,aAAa,KAAK;IAClB,WAAW,KAAK;IAIhB,aAAa,gBAAgB,QAAQ,CAAC,OAAO,MAAM,WAAW,IAAI,cAAc;GAClF;GACA,KAAK,SAAS;GACd,OAAO;EACT,EAAA,CAAG;EACH,KAAK,iBAAiB;EACtB,IAAI;GACF,OAAO,MAAM;EACf,UAAU;GAGR,IAAI,KAAK,mBAAmB,UAAU,KAAK,iBAAiB;EAC9D;CACF;CAEA,MAAM,UAAgC;EACpC,IAAI,CAAC,KAAK,YACR,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,UAAU,KAAK;GACf,QAAQ,KAAK;GACb,WAAW,KAAK,8BAAc,IAAI,KAAK;EACzC;EAGF,MAAM,OAAQ,OAAM,MADG,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,KAAK,UAAU,GAAG,EAAA,CAChE,KAAK;EAClC,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,UAAU,KAAK;GACf,QAAQ,KAAK;GACb,WAAW,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,IAAK,KAAK,8BAAc,IAAI,KAAK;GACpF,UAAU;IAMR,WAAW,KAAK;IAChB,oBAAoB,KAAK,sBAAsB,KAAA;IAC/C,gBAAgB,KAAK;GACvB;EACF;CACF;CAEA,gBAAgB,MAAoD;EAClE,MAAM,sBAAsB,mBAAmB,KAAK,aAAa,IAAI,KAAK,eAAe,GAAG;EAC5F,IAAI,OAAO,KAAK,0BAA0B,YACxC,OAAO,KAAK,sBAAsB;GAAE;GAAqB,gBAAgB,MAAM;EAAe,CAAC;EAEjG,IAAI,OAAO,KAAK,0BAA0B,UAAU,OAAO,KAAK;EAChE,OAAO;CACT;AACF;;;AC9hBA,MAAa,0BAAmE;CAC9E,IAAI;CACJ,MAAM;CACN,aAAa;CACb,cAAc;EACZ,MAAM;EACN,YAAY;GACV,aAAa;IACX,MAAM;IACN,aAAa;GACf;GACA,WAAW;IAAE,MAAM;IAAU,aAAa;GAAwD;GAClG,eAAe;IAAE,MAAM;IAAU,aAAa;GAAgE;GAC9G,WAAW;IAAE,MAAM;IAAU,aAAa;GAAiD;GAC3F,oBAAoB;IAAE,MAAM;IAAU,aAAa;GAAyD;GAC5G,kBAAkB;IAChB,MAAM;IACN,aAAa;IACb,MAAM,CAAC,YAAY,SAAS;IAC5B,SAAS;GACX;GACA,KAAK;IAAE,MAAM;IAAU,aAAa;IAAyB,sBAAsB,EAAE,MAAM,SAAS;GAAE;GACtG,SAAS;IAAE,MAAM;IAAU,aAAa;GAAgC;EAC1E;CACF;CACA,gBAAe,WAAU,IAAI,gBAAgB,MAAM;AACrD;AAEA,MAAa,6BAA4E;CACvF,IAAI;CACJ,MAAM;CACN,aAAa;CACb,cAAc;EACZ,MAAM;EACN,YAAY;GACV,aAAa;IACX,MAAM;IACN,aAAa;GACf;GACA,WAAW;IAAE,MAAM;IAAU,aAAa;GAAwD;GAClG,YAAY;IACV,MAAM;IACN,aAAa;GACf;GACA,UAAU;IAAE,MAAM;IAAW,aAAa;IAAsB,SAAS;GAAM;EACjF;CACF;CACA,mBAAkB,WAAU,IAAI,mBAAmB,MAAM;AAC3D"}
1
+ {"version":3,"file":"index.cjs","names":["nodePath","path","Buffer","MastraFilesystem","FileNotFoundError","buffer","WorkspaceReadOnlyError","FileExistsError","ProcessHandle","SandboxProcessManager","MastraSandbox","SandboxNotReadyError"],"sources":["../src/client.ts","../src/filesystem.ts","../src/direct-exec.ts","../src/sandbox.ts","../src/provider.ts"],"sourcesContent":["export interface PlatformClientOptions {\n accessToken?: string;\n projectId?: 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(\n options.accessToken ??\n process.env.MASTRA_PLATFORM_SECRET_KEY ??\n // Deprecated alias — prefer MASTRA_PLATFORM_SECRET_KEY.\n process.env.MASTRA_PLATFORM_ACCESS_TOKEN,\n 'accessToken',\n ),\n projectId: requireOption(options.projectId ?? process.env.MASTRA_PROJECT_ID, 'projectId'),\n proxyUrl: (process.env.MASTRA_WORKSPACE_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\\/$/, ''),\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 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.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\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","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';\n\nexport type PlatformSandboxNetworkIsolation = 'ISOLATED' | 'PRIVATE';\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\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\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\ninterface ExecResponse {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n truncated?: boolean;\n timedOut?: boolean;\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 /**\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 * Tri-state feature detection for the platform's exec-lease endpoint:\n * undefined — not yet tried (default; try direct on first exec)\n * true — endpoint present, use direct exec\n * false — endpoint absent (404/501) OR the WebSocket transport failed\n * once; fall back permanently to /exec for this sandbox\n * Sticky per instance so we make the fallback decision once per sandbox\n * lifetime instead of paying an extra round-trip on every exec.\n */\n private _directExecAvailable: boolean | undefined = undefined;\n\n constructor(options: PlatformSandboxOptions = {}) {\n super({ ...options, name: 'PlatformSandbox', processes: new PlatformProcessManager() });\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 }\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 return new PlatformSandbox({\n ...(options.id !== undefined && { id: options.id }),\n accessToken: this._client.accessToken,\n projectId: this._client.projectId,\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 });\n }\n\n async start(): Promise<void> {\n if (this._sandboxId) {\n try {\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`);\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 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 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 json = (await response.json()) as CreateSandboxResponse;\n this._sandboxId = json.id;\n this._createdAt = json.createdAt ? new Date(json.createdAt) : new Date();\n }\n\n async stop(): Promise<void> {\n await this.destroy();\n }\n\n async destroy(): Promise<void> {\n if (!this._sandboxId) return;\n await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`, { 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 }\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` is sent as `0` (interpreted as\n // \"no timeout\" by the proxy) instead of being dropped by a truthy check.\n const effectiveTimeout = options?.timeout ?? this._timeout;\n\n // Prefer the direct-exec data plane (WebSocket straight to Railway's\n // tcp-proxy) when the platform exposes the exec-lease endpoint. Falls\n // back to the proxy /exec route on older platform deployments. See\n // ./direct-exec.ts and `docs/factory/direct-sandbox-connection.md` in\n // the Platform repo.\n if (this._directExecAvailable !== false) {\n const leaseResult = await this._tryDirectExec(fullCommand, effectiveTimeout, options);\n if (leaseResult) return { ...leaseResult, executionTimeMs: Date.now() - started };\n }\n return this._execViaProxy(fullCommand, effectiveTimeout, options, started);\n }\n\n private async _tryDirectExec(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n ): Promise<Omit<CommandResult, 'executionTimeMs'> | null> {\n let lease: (ExecLease & { expiresAtMs: number | null }) | null;\n try {\n lease = await this._ensureLease();\n } catch (error) {\n // 404/501 → endpoint not present on this proxy deployment. Flip the\n // feature bit and take the fallback path for this call AND every\n // subsequent one on this sandbox.\n if (error instanceof PlatformApiError && (error.status === 404 || error.status === 501)) {\n this._directExecAvailable = false;\n return null;\n }\n throw error;\n }\n this._directExecAvailable = true;\n\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); the proxy `/exec` path ships\n // it through JSON.stringify which drops undefined implicitly, so this is\n // just the explicit version of the same filter.\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 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 // `null` exitCode without `timedOut` means the socket closed without an\n // exit frame — i.e. a transport failure (handshake stalled, mid-stream\n // drop, expired token). Drop the cached lease AND flip the feature bit\n // so we don't burn a fresh mint + failed WS handshake on every subsequent\n // exec; fall back permanently to /exec for this sandbox. Log the close\n // metadata so we can diagnose why Railway refused the WebSocket. Timed-out\n // and normal-exit results still return synthesised / real exit codes as\n // before.\n if (result.exitCode === null && !result.timedOut) {\n // eslint-disable-next-line no-console\n console.warn(\n '[platform-workspace] direct-exec transport failed; falling back to /exec permanently for this sandbox',\n {\n sandboxId: this._sandboxId,\n opened: result.opened,\n closeCode: result.closeCode,\n closeReason: result.closeReason,\n wsEndpoint: lease.wsEndpoint,\n },\n );\n this._lease = null;\n this._directExecAvailable = false;\n return null;\n }\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 };\n }\n\n private async _execViaProxy(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n started: number,\n ): Promise<CommandResult> {\n if (!this._sandboxId) throw new SandboxNotReadyError(this.id);\n const timeoutSec = effectiveTimeout != null ? Math.ceil(effectiveTimeout / 1000) : undefined;\n // Pass our own signal for exec so the client's default per-request\n // timeout (60s) doesn't cut off commands that expect to run longer.\n // Give the proxy a generous buffer over the requested command timeout.\n const clientSignal =\n effectiveTimeout != null && effectiveTimeout > 0 ? AbortSignal.timeout(effectiveTimeout + 30_000) : undefined;\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}/exec`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n command: fullCommand,\n timeoutSec,\n cwd: options?.cwd,\n env: options?.env,\n }),\n signal: clientSignal,\n });\n const json = (await response.json()) as ExecResponse;\n const exitCode = json.exitCode ?? (json.timedOut ? 124 : 1);\n return {\n success: exitCode === 0,\n exitCode,\n stdout: json.stdout,\n stderr: json.stderr,\n executionTimeMs: Date.now() - started,\n timedOut: json.timedOut,\n command: fullCommand,\n };\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 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 secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)',\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 secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)',\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"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -384,6 +384,8 @@ function execViaLease(lease, options) {
384
384
  let timedOut = false;
385
385
  let settled = false;
386
386
  let opened = false;
387
+ let closeCode;
388
+ let closeReason;
387
389
  let timer;
388
390
  let handshakeTimer;
389
391
  const settle = () => {
@@ -409,7 +411,10 @@ function execViaLease(lease, options) {
409
411
  stdout,
410
412
  stderr,
411
413
  truncated: false,
412
- timedOut
414
+ timedOut,
415
+ ...closeCode !== void 0 && { closeCode },
416
+ ...closeReason !== void 0 && { closeReason },
417
+ opened
413
418
  });
414
419
  };
415
420
  if (options.timeoutMs !== void 0 && options.timeoutMs > 0) timer = setTimeout(() => {
@@ -443,6 +448,8 @@ function execViaLease(lease, options) {
443
448
  else if (typeof data === "string") handleTextFrame(data);
444
449
  };
445
450
  socket.onclose = (event) => {
451
+ closeCode = event.code;
452
+ closeReason = event.reason;
446
453
  if (!opened) {
447
454
  settle();
448
455
  return;
@@ -596,7 +603,8 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
596
603
  * Tri-state feature detection for the platform's exec-lease endpoint:
597
604
  * undefined — not yet tried (default; try direct on first exec)
598
605
  * true — endpoint present, use direct exec
599
- * false — endpoint returned 404 or 501, fall back permanently to /exec
606
+ * false — endpoint absent (404/501) OR the WebSocket transport failed
607
+ * once; fall back permanently to /exec for this sandbox
600
608
  * Sticky per instance so we make the fallback decision once per sandbox
601
609
  * lifetime instead of paying an extra round-trip on every exec.
602
610
  */
@@ -749,7 +757,15 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
749
757
  ...this._webSocketFactory && { webSocketFactory: this._webSocketFactory }
750
758
  });
751
759
  if (result.exitCode === null && !result.timedOut) {
760
+ console.warn("[platform-workspace] direct-exec transport failed; falling back to /exec permanently for this sandbox", {
761
+ sandboxId: this._sandboxId,
762
+ opened: result.opened,
763
+ closeCode: result.closeCode,
764
+ closeReason: result.closeReason,
765
+ wsEndpoint: lease.wsEndpoint
766
+ });
752
767
  this._lease = null;
768
+ this._directExecAvailable = false;
753
769
  return null;
754
770
  }
755
771
  const exitCode = result.exitCode ?? 124;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/client.ts","../src/filesystem.ts","../src/direct-exec.ts","../src/sandbox.ts","../src/provider.ts"],"sourcesContent":["export interface PlatformClientOptions {\n accessToken?: string;\n projectId?: 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(\n options.accessToken ??\n process.env.MASTRA_PLATFORM_SECRET_KEY ??\n // Deprecated alias — prefer MASTRA_PLATFORM_SECRET_KEY.\n process.env.MASTRA_PLATFORM_ACCESS_TOKEN,\n 'accessToken',\n ),\n projectId: requireOption(options.projectId ?? process.env.MASTRA_PROJECT_ID, 'projectId'),\n proxyUrl: (process.env.MASTRA_WORKSPACE_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\\/$/, ''),\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 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.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\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\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 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({ exitCode, stdout, stderr, truncated: false, timedOut });\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 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 void event; // eslint: intentionally observed for future logging\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","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';\n\nexport type PlatformSandboxNetworkIsolation = 'ISOLATED' | 'PRIVATE';\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\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\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\ninterface ExecResponse {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n truncated?: boolean;\n timedOut?: boolean;\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 /**\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 * Tri-state feature detection for the platform's exec-lease endpoint:\n * undefined — not yet tried (default; try direct on first exec)\n * true — endpoint present, use direct exec\n * false — endpoint returned 404 or 501, fall back permanently to /exec\n * Sticky per instance so we make the fallback decision once per sandbox\n * lifetime instead of paying an extra round-trip on every exec.\n */\n private _directExecAvailable: boolean | undefined = undefined;\n\n constructor(options: PlatformSandboxOptions = {}) {\n super({ ...options, name: 'PlatformSandbox', processes: new PlatformProcessManager() });\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 }\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 return new PlatformSandbox({\n ...(options.id !== undefined && { id: options.id }),\n accessToken: this._client.accessToken,\n projectId: this._client.projectId,\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 });\n }\n\n async start(): Promise<void> {\n if (this._sandboxId) {\n try {\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`);\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 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 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 json = (await response.json()) as CreateSandboxResponse;\n this._sandboxId = json.id;\n this._createdAt = json.createdAt ? new Date(json.createdAt) : new Date();\n }\n\n async stop(): Promise<void> {\n await this.destroy();\n }\n\n async destroy(): Promise<void> {\n if (!this._sandboxId) return;\n await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`, { 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 }\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` is sent as `0` (interpreted as\n // \"no timeout\" by the proxy) instead of being dropped by a truthy check.\n const effectiveTimeout = options?.timeout ?? this._timeout;\n\n // Prefer the direct-exec data plane (WebSocket straight to Railway's\n // tcp-proxy) when the platform exposes the exec-lease endpoint. Falls\n // back to the proxy /exec route on older platform deployments. See\n // ./direct-exec.ts and `docs/factory/direct-sandbox-connection.md` in\n // the Platform repo.\n if (this._directExecAvailable !== false) {\n const leaseResult = await this._tryDirectExec(fullCommand, effectiveTimeout, options);\n if (leaseResult) return { ...leaseResult, executionTimeMs: Date.now() - started };\n }\n return this._execViaProxy(fullCommand, effectiveTimeout, options, started);\n }\n\n private async _tryDirectExec(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n ): Promise<Omit<CommandResult, 'executionTimeMs'> | null> {\n let lease: (ExecLease & { expiresAtMs: number | null }) | null;\n try {\n lease = await this._ensureLease();\n } catch (error) {\n // 404/501 → endpoint not present on this proxy deployment. Flip the\n // feature bit and take the fallback path for this call AND every\n // subsequent one on this sandbox.\n if (error instanceof PlatformApiError && (error.status === 404 || error.status === 501)) {\n this._directExecAvailable = false;\n return null;\n }\n throw error;\n }\n this._directExecAvailable = true;\n\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); the proxy `/exec` path ships\n // it through JSON.stringify which drops undefined implicitly, so this is\n // just the explicit version of the same filter.\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 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 // `null` exitCode without `timedOut` means the socket closed without an\n // exit frame — i.e. a transport failure (handshake stalled, mid-stream\n // drop, expired token). Drop the cached lease so the next call re-mints,\n // and return `null` to let the caller fall through to the proxy /exec\n // path. Timed-out and normal-exit results still return synthesised /\n // real exit codes as before.\n if (result.exitCode === null && !result.timedOut) {\n this._lease = null;\n return null;\n }\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 };\n }\n\n private async _execViaProxy(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n started: number,\n ): Promise<CommandResult> {\n if (!this._sandboxId) throw new SandboxNotReadyError(this.id);\n const timeoutSec = effectiveTimeout != null ? Math.ceil(effectiveTimeout / 1000) : undefined;\n // Pass our own signal for exec so the client's default per-request\n // timeout (60s) doesn't cut off commands that expect to run longer.\n // Give the proxy a generous buffer over the requested command timeout.\n const clientSignal =\n effectiveTimeout != null && effectiveTimeout > 0 ? AbortSignal.timeout(effectiveTimeout + 30_000) : undefined;\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}/exec`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n command: fullCommand,\n timeoutSec,\n cwd: options?.cwd,\n env: options?.env,\n }),\n signal: clientSignal,\n });\n const json = (await response.json()) as ExecResponse;\n const exitCode = json.exitCode ?? (json.timedOut ? 124 : 1);\n return {\n success: exitCode === 0,\n exitCode,\n stdout: json.stdout,\n stderr: json.stderr,\n executionTimeMs: Date.now() - started,\n timedOut: json.timedOut,\n command: fullCommand,\n };\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 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 secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)',\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 secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)',\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"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/client.ts","../src/filesystem.ts","../src/direct-exec.ts","../src/sandbox.ts","../src/provider.ts"],"sourcesContent":["export interface PlatformClientOptions {\n accessToken?: string;\n projectId?: 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(\n options.accessToken ??\n process.env.MASTRA_PLATFORM_SECRET_KEY ??\n // Deprecated alias — prefer MASTRA_PLATFORM_SECRET_KEY.\n process.env.MASTRA_PLATFORM_ACCESS_TOKEN,\n 'accessToken',\n ),\n projectId: requireOption(options.projectId ?? process.env.MASTRA_PROJECT_ID, 'projectId'),\n proxyUrl: (process.env.MASTRA_WORKSPACE_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\\/$/, ''),\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 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.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\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","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';\n\nexport type PlatformSandboxNetworkIsolation = 'ISOLATED' | 'PRIVATE';\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\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\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\ninterface ExecResponse {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n truncated?: boolean;\n timedOut?: boolean;\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 /**\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 * Tri-state feature detection for the platform's exec-lease endpoint:\n * undefined — not yet tried (default; try direct on first exec)\n * true — endpoint present, use direct exec\n * false — endpoint absent (404/501) OR the WebSocket transport failed\n * once; fall back permanently to /exec for this sandbox\n * Sticky per instance so we make the fallback decision once per sandbox\n * lifetime instead of paying an extra round-trip on every exec.\n */\n private _directExecAvailable: boolean | undefined = undefined;\n\n constructor(options: PlatformSandboxOptions = {}) {\n super({ ...options, name: 'PlatformSandbox', processes: new PlatformProcessManager() });\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 }\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 return new PlatformSandbox({\n ...(options.id !== undefined && { id: options.id }),\n accessToken: this._client.accessToken,\n projectId: this._client.projectId,\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 });\n }\n\n async start(): Promise<void> {\n if (this._sandboxId) {\n try {\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`);\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 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 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 json = (await response.json()) as CreateSandboxResponse;\n this._sandboxId = json.id;\n this._createdAt = json.createdAt ? new Date(json.createdAt) : new Date();\n }\n\n async stop(): Promise<void> {\n await this.destroy();\n }\n\n async destroy(): Promise<void> {\n if (!this._sandboxId) return;\n await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`, { 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 }\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` is sent as `0` (interpreted as\n // \"no timeout\" by the proxy) instead of being dropped by a truthy check.\n const effectiveTimeout = options?.timeout ?? this._timeout;\n\n // Prefer the direct-exec data plane (WebSocket straight to Railway's\n // tcp-proxy) when the platform exposes the exec-lease endpoint. Falls\n // back to the proxy /exec route on older platform deployments. See\n // ./direct-exec.ts and `docs/factory/direct-sandbox-connection.md` in\n // the Platform repo.\n if (this._directExecAvailable !== false) {\n const leaseResult = await this._tryDirectExec(fullCommand, effectiveTimeout, options);\n if (leaseResult) return { ...leaseResult, executionTimeMs: Date.now() - started };\n }\n return this._execViaProxy(fullCommand, effectiveTimeout, options, started);\n }\n\n private async _tryDirectExec(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n ): Promise<Omit<CommandResult, 'executionTimeMs'> | null> {\n let lease: (ExecLease & { expiresAtMs: number | null }) | null;\n try {\n lease = await this._ensureLease();\n } catch (error) {\n // 404/501 → endpoint not present on this proxy deployment. Flip the\n // feature bit and take the fallback path for this call AND every\n // subsequent one on this sandbox.\n if (error instanceof PlatformApiError && (error.status === 404 || error.status === 501)) {\n this._directExecAvailable = false;\n return null;\n }\n throw error;\n }\n this._directExecAvailable = true;\n\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); the proxy `/exec` path ships\n // it through JSON.stringify which drops undefined implicitly, so this is\n // just the explicit version of the same filter.\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 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 // `null` exitCode without `timedOut` means the socket closed without an\n // exit frame — i.e. a transport failure (handshake stalled, mid-stream\n // drop, expired token). Drop the cached lease AND flip the feature bit\n // so we don't burn a fresh mint + failed WS handshake on every subsequent\n // exec; fall back permanently to /exec for this sandbox. Log the close\n // metadata so we can diagnose why Railway refused the WebSocket. Timed-out\n // and normal-exit results still return synthesised / real exit codes as\n // before.\n if (result.exitCode === null && !result.timedOut) {\n // eslint-disable-next-line no-console\n console.warn(\n '[platform-workspace] direct-exec transport failed; falling back to /exec permanently for this sandbox',\n {\n sandboxId: this._sandboxId,\n opened: result.opened,\n closeCode: result.closeCode,\n closeReason: result.closeReason,\n wsEndpoint: lease.wsEndpoint,\n },\n );\n this._lease = null;\n this._directExecAvailable = false;\n return null;\n }\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 };\n }\n\n private async _execViaProxy(\n fullCommand: string,\n effectiveTimeout: number | undefined,\n options: ExecuteCommandOptions | undefined,\n started: number,\n ): Promise<CommandResult> {\n if (!this._sandboxId) throw new SandboxNotReadyError(this.id);\n const timeoutSec = effectiveTimeout != null ? Math.ceil(effectiveTimeout / 1000) : undefined;\n // Pass our own signal for exec so the client's default per-request\n // timeout (60s) doesn't cut off commands that expect to run longer.\n // Give the proxy a generous buffer over the requested command timeout.\n const clientSignal =\n effectiveTimeout != null && effectiveTimeout > 0 ? AbortSignal.timeout(effectiveTimeout + 30_000) : undefined;\n const response = await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}/exec`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n command: fullCommand,\n timeoutSec,\n cwd: options?.cwd,\n env: options?.env,\n }),\n signal: clientSignal,\n });\n const json = (await response.json()) as ExecResponse;\n const exitCode = json.exitCode ?? (json.timedOut ? 124 : 1);\n return {\n success: exitCode === 0,\n exitCode,\n stdout: json.stdout,\n stderr: json.stderr,\n executionTimeMs: Date.now() - started,\n timedOut: json.timedOut,\n command: fullCommand,\n };\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 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 secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)',\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 secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)',\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"],"mappings":";;;;AAUA,MAAM,oBAAoB;;;;;;AAO1B,MAAM,6BAA6B;AAEnC,SAAgB,cAAc,OAA2B,MAAsB;CAC7E,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,GAAG,KAAK,aAAa;CACjD,OAAO;AACT;AAEA,SAAgB,uBAAuB,SAAgC;CACrE,OAAO;EACL,aAAa,cACX,QAAQ,eACN,QAAQ,IAAI,8BAEZ,QAAQ,IAAI,8BACd,aACF;EACA,WAAW,cAAc,QAAQ,aAAa,QAAQ,IAAI,mBAAmB,WAAW;EACxF,WAAW,QAAQ,IAAI,8BAA8B,kBAAA,CAAmB,QAAQ,OAAO,EAAE;EACzF,OAAO,QAAQ,SAAS;CAC1B;AACF;AAcA,SAAS,gBAAgB,MAA8C;CACrE,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,QAAQ;EACN;CACF;CACA,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM,OAAO,KAAA;CAC1D,MAAM,MAAO,OAA+B;CAC5C,IAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM,OAAO,KAAA;CACpD,MAAM,EAAE,SAAS,SAAS;CAC1B,IAAI,OAAO,YAAY,YAAY,OAAO,SAAS,UAAU,OAAO,KAAA;CACpE,OAAO;EAAE;EAAS;CAAK;AACzB;AAEA,IAAa,mBAAb,cAAsC,MAAM;CAC1C;CACA;;CAEA;;CAEA;CAEA,YAAY,QAAgB,MAAc;EACxC,MAAM,SAAS,gBAAgB,IAAI;EACnC,MAAM,UAAU,SAAS,GAAG,OAAO,KAAK,IAAI,OAAO,YAAY;EAC/D,MAAM,sCAAsC,SAAS,UAAU,KAAK,YAAY,IAAI;EACpF,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,eAAe,QAAQ;CAC9B;AACF;AAEA,IAAa,iBAAb,MAA4B;CAC1B;CACA;CACA;CACA;CAEA,YAAY,SAAgC;EAC1C,MAAM,WAAW,uBAAuB,OAAO;EAC/C,KAAK,cAAc,SAAS;EAC5B,KAAK,YAAY,SAAS;EAC1B,KAAK,WAAW,SAAS;EACzB,KAAK,QAAQ,SAAS;CACxB;CAEA,MAAM,QAAQ,MAAc,UAAkC,CAAC,GAAsB;EACnF,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,SAAS,eAAe,mBAAmB,KAAK,SAAS,IAAI,MAAM;EAC/F,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,SAAS,CAAC,CAAC,GAC3D,IAAI,UAAU,KAAA,GAAW,IAAI,aAAa,IAAI,KAAK,OAAO,KAAK,CAAC;EAGlE,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;EAC3C,QAAQ,IAAI,iBAAiB,UAAU,KAAK,aAAa;EAGzD,MAAM,EAAE,OAAO,QAAQ,GAAG,iBAAiB;EAG3C,MAAM,SAAS,aAAa,UAAU,YAAY,QAAQ,0BAA0B;EACpF,MAAM,WAAW,MAAM,KAAK,MAAM,KAAK;GAAE,GAAG;GAAc;GAAS;EAAO,CAAC;EAC3E,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,iBAAiB,SAAS,QAAQ,MAAM,SAAS,KAAK,CAAC;EAEnE,OAAO;CACT;AACF;;;ACpFA,SAAS,cAAc,OAAuB;CAC5C,IAAI,CAAC,SAAS,UAAU,KAAK,OAAO;CACpC,IAAI,aAAa,MAAM,WAAW,GAAG,IAAI,QAAQ,IAAI;CACrD,aAAa,SAAS,MAAM,UAAU,UAAU;CAChD,OAAO,eAAe,MAAM,MAAM;AACpC;AAEA,SAAS,YAAY,MAAsB;CACzC,MAAM,aAAa,cAAc,IAAI;CACrC,OAAO,eAAe,MAAM,KAAK,WAAW,MAAM,CAAC;AACrD;;;;;;;AAQA,SAAS,cAAc,KAAqB;CAC1C,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;AACxD;AAEA,SAAS,aAAa,MAAsB;CAC1C,MAAM,aAAa,cAAc,IAAI;CACrC,IAAI,eAAe,KAAK,OAAO;CAC/B,OAAO,WAAW,MAAM,WAAW,YAAY,GAAG,IAAI,CAAC;AACzD;AAEA,SAAS,cAAc,SAAuC;CAC5D,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,OAAO,OAAO,KAAK,OAAO;AAC5B;AAEA,SAAS,WAAW,SAAkB,MAAoB;CACxD,MAAM,QAAQ,QAAQ,IAAI,IAAI;CAC9B,OAAO,QAAQ,IAAI,KAAK,KAAK,oBAAI,IAAI,KAAK,CAAC;AAC7C;AAEA,SAAS,WAAW,SAA0B;CAC5C,MAAM,QAAQ,QAAQ,IAAI,gBAAgB;CAC1C,OAAO,QAAQ,OAAO,KAAK,IAAI;AACjC;AAEA,SAAS,WAAW,OAAyB;CAC3C,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,SAAS,MAAM,WAAW;AAC9F;AAEA,IAAa,qBAAb,cAAwC,iBAAiB;CACvD;CACA,OAAgB;CAChB,WAAoB;CACpB;CACA;CACA;CACA;CACA,SAAyB;CAEzB;CACA;CACA;CAEA,YAAY,UAAqC,CAAC,GAAG;EACnD,MAAM;GAAE,GAAG;GAAS,MAAM;EAAqB,CAAC;EAChD,KAAK,KAAK,QAAQ,MAAM,KAAK,WAAW;EACxC,KAAK,cAAc,QAAQ,cAAc,QAAQ,IAAI,+BAA+B;EACpF,IAAI,CAAC,KAAK,aAAa,MAAM,IAAI,MAAM,wBAAwB;EAC/D,KAAK,WAAW,QAAQ;EACxB,KAAK,cAAc,QAAQ;EAC3B,KAAK,OAAO,QAAQ,QAAQ;EAC5B,KAAK,cAAc,QAAQ;EAC3B,KAAK,wBAAwB,QAAQ;EACrC,KAAK,UAAU,IAAI,eAAe,OAAO;CAC3C;CAEA,aAA6B;EAC3B,OAAO,eAAe,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;CACxF;CAEA,MAAM,SAAS,MAAc,SAAiD;EAC5E,MAAM,KAAK,YAAY;EACvB,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,KAAK,QAAQ,QAC5B,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAY,IAAI,CAAC,GAChF;EACF,SAAS,OAAO;GACd,IAAI,WAAW,KAAK,GAAG,MAAM,IAAI,kBAAkB,IAAI;GACvD,MAAM;EACR;EACA,MAAM,SAAS,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;EACvD,OAAO,SAAS,WAAW,OAAO,SAAS,QAAQ,QAAQ,IAAI;CACjE;CAEA,MAAM,UAAU,MAAc,SAAsB,SAAuC;EACzF,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAI,uBAAuB,WAAW;EAC/D,MAAM,UAAkC,CAAC;EACzC,IAAI,SAAS,UAAU,QAAQ,kBAAkB,QAAQ;EACzD,IAAI,SAAS,cAAc,OAAO,QAAQ,mBAAmB;EAC7D,IAAI;GACF,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAY,IAAI,CAAC,KAAK;IAC5G,QAAQ;IACR;IACA,MAAM,cAAc,OAAO;GAC7B,CAAC;EACH,SAAS,OAAO;GACd,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,SAAS,MAAM,WAAW,KACvF,MAAM,IAAI,gBAAgB,IAAI;GAEhC,MAAM;EACR;CACF;;;;;;;;;;;CAYA,MAAM,WAAW,MAAc,SAAqC;EAClE,MAAM,WAAY,MAAM,KAAK,OAAO,IAAI,IAAK,MAAM,KAAK,SAAS,IAAI,IAAI,OAAO,MAAM,CAAC;EACvF,MAAM,KAAK,UACT,MACA,OAAO,OAAO,CAAC,OAAO,SAAS,QAAQ,IAAI,WAAW,OAAO,KAAK,QAAQ,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CACpG;CACF;CAEA,MAAM,WAAW,MAAc,SAAwC;EACrE,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAI,uBAAuB,YAAY;EAChE,IAAI;GACF,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAY,IAAI,CAAC,KAAK;IAC5G,QAAQ;IACR,OAAO,EAAE,WAAW,SAAS,UAAU;GACzC,CAAC;EACH,SAAS,OAAO;GACd,IAAI,WAAW,KAAK,KAAK,SAAS,OAAO;GACzC,IAAI,WAAW,KAAK,GAAG,MAAM,IAAI,kBAAkB,IAAI;GACvD,MAAM;EACR;CACF;CAEA,MAAM,SAAS,KAAa,MAAc,SAAsC;EAC9E,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAI,uBAAuB,UAAU;EAI9D,IAAI,SAAS,cAAc,OACzB,MAAM,IAAI,MAAM,8FAA8F;EAEhH,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAY,GAAG,CAAC,KAAK;GAC3G,QAAQ;GACR,OAAO,EAAE,IAAI,OAAO;GACpB,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU,EAAE,aAAa,YAAY,IAAI,EAAE,CAAC;EACzD,CAAC;CACH;CAEA,MAAM,SAAS,KAAa,MAAc,SAAsC;EAC9E,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAI,uBAAuB,UAAU;EAE9D,IAAI,SAAS,cAAc,OACzB,MAAM,IAAI,MAAM,8FAA8F;EAEhH,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAY,GAAG,CAAC,KAAK;GAC3G,QAAQ;GACR,OAAO,EAAE,IAAI,SAAS;GACtB,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU,EAAE,aAAa,YAAY,IAAI,EAAE,CAAC;EACzD,CAAC;CACH;CAEA,MAAM,MAAM,MAAc,UAAmD;EAC3E,MAAM,KAAK,YAAY;EACvB,IAAI,KAAK,UAAU,MAAM,IAAI,uBAAuB,OAAO;EAC3D,MAAM,KAAK,QAAQ,QAAQ,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAY,IAAI,CAAC,KAAK;GAC5G,QAAQ;GACR,OAAO,EAAE,IAAI,QAAQ;EACvB,CAAC;CACH;CAEA,MAAM,MAAM,MAAc,SAAwC;EAChE,MAAM,KAAK,WAAW,KAAK,SAAS,GAAG,IAAI,OAAO,GAAG,KAAK,IAAI;GAAE,WAAW;GAAM,OAAO,SAAS;EAAM,CAAC;CAC1G;CAEA,MAAM,QAAQ,MAAc,SAA6C;EACvE,MAAM,KAAK,YAAY;EACvB,MAAM,SAAS,YAAY,IAAI;EAU/B,MAAM,OAAQ,OAAM,MATG,KAAK,QAAQ,QAClC,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,MAAM,KACnE,EACE,OAAO;GACL,WAAW,SAAS,YAAY,KAAA,IAAY;GAC5C,QAAQ,SAAS,GAAG,OAAO,QAAQ,OAAO,EAAE,EAAE,KAAK,KAAA;EACrD,EACF,CACF,EAAA,CAC6B,KAAK;EAClC,OAAO,CACL,IAAI,KAAK,kBAAkB,CAAC,EAAA,CAAG,KAAI,YAAW;GAC5C,MAAM,aAAa,OAAO,QAAQ,OAAO,EAAE,CAAC;GAC5C,MAAM;EACR,EAAE,GACF,IAAI,KAAK,YAAY,CAAC,EAAA,CACnB,QAAO,WAAU,OAAO,OAAO,CAAC,OAAO,IAAI,SAAS,GAAG,CAAC,CAAC,CACzD,KAAI,YAAW;GACd,MAAM,aAAa,OAAO,GAAI;GAC9B,MAAM;GACN,MAAM,OAAO;EACf,EAAE,CACN,CAAC,CAAC,QACA,UAAS,CAAC,SAAS,aAAa,MAAM,SAAS,eAAe,iBAAiB,MAAM,MAAM,QAAQ,SAAS,CAC9G;CACF;CAEA,MAAM,OAAO,MAAgC;EAC3C,IAAI;GACF,MAAM,KAAK,KAAK,IAAI;GACpB,OAAO;EACT,SAAS,OAAO;GACd,IAAI,WAAW,KAAK,KAAK,iBAAiB,mBAAmB,OAAO;GACpE,MAAM;EACR;CACF;CAEA,MAAM,KAAK,MAAiC;EAC1C,MAAM,KAAK,YAAY;EACvB,MAAM,aAAa,cAAc,IAAI;EACrC,IAAI,eAAe,KACjB,OAAO;GAAE,MAAM;GAAI,MAAM;GAAK,MAAM;GAAa,MAAM;GAAG,2BAAW,IAAI,KAAK,CAAC;GAAG,4BAAY,IAAI,KAAK,CAAC;EAAE;EAE5G,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,KAAK,QAAQ,QAC5B,OAAO,mBAAmB,KAAK,WAAW,EAAE,GAAG,cAAc,YAAY,IAAI,CAAC,KAC9E,EACE,QAAQ,OACV,CACF;EACF,SAAS,OAAO;GACd,IAAI,WAAW,KAAK,GAAG,MAAM,IAAI,kBAAkB,IAAI;GACvD,MAAM;EACR;EACA,OAAO;GACL,MAAM,aAAa,IAAI;GACvB,MAAM;GACN,MAAM,WAAW,SAAS,GAAG,IAAI,cAAc;GAC/C,MAAM,WAAW,SAAS,OAAO;GACjC,WAAW,WAAW,SAAS,SAAS,eAAe;GACvD,YAAY,WAAW,SAAS,SAAS,eAAe;GACxD,UAAU,SAAS,QAAQ,IAAI,cAAc,KAAK,KAAA;EACpD;CACF;CAEA,SAAS,MAA+B;EACtC,OAAO,QAAQ,QAAQ,cAAc,IAAI,CAAC;CAC5C;CAEA,gBAAgB,MAAoD;EAClE,MAAM,sBAAsB,wDAAwD,KAAK,YAAY;EACrG,IAAI,OAAO,KAAK,0BAA0B,YACxC,OAAO,KAAK,sBAAsB;GAAE;GAAqB,gBAAgB,MAAM;EAAe,CAAC;EAEjG,IAAI,OAAO,KAAK,0BAA0B,UAAU,OAAO,KAAK;EAChE,OAAO;CACT;CAEA,UAA8F;EAC5F,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,UAAU,KAAK;GACf,QAAQ,KAAK;GACb,UAAU,KAAK;GACf,MAAM,KAAK;GACX,UAAU;IACR,YAAY,KAAK;IACjB,GAAI,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;IACxD,GAAI,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;GAC1D;EACF;CACF;AACF;AAEA,SAAS,iBAAiB,MAAc,WAAuC;CAE7E,QADmB,MAAM,QAAQ,SAAS,IAAI,YAAY,CAAC,SAAS,EAAA,CAClD,MAAK,QAAO,KAAK,SAAS,GAAG,CAAC;AAClD;;;;;;;;;;;;;;;;;AC3TA,MAAM,eAAe;;AAErB,MAAM,eAAe;;;;;;;;AAQrB,MAAM,wBAAwB;AA6E9B,MAAM,sBAAkD,UAAU,iBAAiB;CACjF,MAAM,KAAM,WAAuC;CAGnD,IAAI,CAAC,IACH,MAAM,IAAI,MACR,uIACF;CAEF,OAAO,IAAI,GAAG,UAAU,YAAY;AACtC;;;;;;;;;AAUA,SAAgB,aAAa,OAAkB,SAAuD;CACpG,MAAM,UAAU,QAAQ,oBAAoB;CAC5C,MAAM,gBAAgB,IAAI,YAAY;CACtC,MAAM,gBAAgB,IAAI,YAAY;CAEtC,OAAO,IAAI,SAA0B,YAAW;EAC9C,IAAI,SAAS;EACb,IAAI,SAAS;EACb,IAAI,WAA0B;EAC9B,IAAI,WAAW;EACf,IAAI,UAAU;EACd,IAAI,SAAS;EACb,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EAEJ,MAAM,eAAe;GACnB,IAAI,SAAS;GACb,UAAU;GACV,IAAI,OAAO,aAAa,KAAK;GAC7B,IAAI,gBAAgB,aAAa,cAAc;GAI/C,MAAM,aAAa,cAAc,OAAO;GACxC,IAAI,YAAY;IACd,UAAU;IACV,QAAQ,WAAW,UAAU;GAC/B;GACA,MAAM,aAAa,cAAc,OAAO;GACxC,IAAI,YAAY;IACd,UAAU;IACV,QAAQ,WAAW,UAAU;GAC/B;GACA,IAAI;IACF,OAAO,MAAM,KAAM,EAAE;GACvB,QAAQ,CAER;GACA,QAAQ;IACN;IACA;IACA;IACA,WAAW;IACX;IACA,GAAI,cAAc,KAAA,KAAa,EAAE,UAAU;IAC3C,GAAI,gBAAgB,KAAA,KAAa,EAAE,YAAY;IAC/C;GACF,CAAC;EACH;EAMA,IAAI,QAAQ,cAAc,KAAA,KAAa,QAAQ,YAAY,GACzD,QAAQ,iBAAiB;GACvB,WAAW;GAGX,IAAI,aAAa,MAAM,WAAW;GAClC,OAAO;EACT,GAAG,QAAQ,SAAS;OAEpB,iBAAiB,iBAAiB;GAIhC,IAAI,CAAC,QAAQ,OAAO;EACtB,GAAG,qBAAqB;EAG1B,MAAM,SAAS,QAAQ,MAAM,YAAY,CAAC,MAAM,aAAa,MAAM,GAAG,CAAC;EACvE,OAAO,aAAa;EAEpB,OAAO,eAAe;GACpB,SAAS;GACT,IAAI,gBAAgB;IAClB,aAAa,cAAc;IAC3B,iBAAiB,KAAA;GACnB;GACA,MAAM,OAAgC,EAAE,SAAS,QAAQ,QAAQ;GACjE,IAAI,QAAQ,KAAK,KAAK,MAAM,QAAQ;GACpC,IAAI,QAAQ,OAAO,OAAO,KAAK,QAAQ,GAAG,CAAC,CAAC,SAAS,GAAG,KAAK,MAAM,QAAQ;GAC3E,OAAO,KAAK,KAAK,UAAU;IAAE,MAAM;IAAa;GAAK,CAAC,CAAC;GAGvD,OAAO,KAAK,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC,CAAC;EACrD;EAEA,OAAO,aAAY,UAAS;GAC1B,MAAM,EAAE,SAAS;GACjB,IAAI,gBAAgB,aAClB,kBAAkB,IAAI;QACjB,IAAI,OAAO,SAAS,UACzB,gBAAgB,IAAI;EAExB;EAEA,OAAO,WAAU,UAAS;GACxB,YAAY,MAAM;GAClB,cAAc,MAAM;GACpB,IAAI,CAAC,QAAQ;IAIX,OAAO;IACP;GACF;GAGA,OAAO;EACT;EAEA,OAAO,gBAAgB;GACrB,IAAI,SAAS;GACb,IAAI,CAAC,QACH,OAAO;EAIX;EAEA,SAAS,kBAAkB,QAAqB;GAC9C,MAAM,OAAO,IAAI,WAAW,MAAM;GAClC,IAAI,KAAK,UAAU,GAAG;GACtB,IAAI,KAAK,OAAO,cAAc;IAC5B,MAAM,QAAQ,cAAc,OAAO,KAAK,SAAS,CAAC,GAAG,EAAE,QAAQ,KAAK,CAAC;IACrE,UAAU;IACV,QAAQ,WAAW,KAAK;GAC1B,OAAO,IAAI,KAAK,OAAO,cAAc;IACnC,MAAM,QAAQ,cAAc,OAAO,KAAK,SAAS,CAAC,GAAG,EAAE,QAAQ,KAAK,CAAC;IACrE,UAAU;IACV,QAAQ,WAAW,KAAK;GAC1B;EACF;EAEA,SAAS,gBAAgB,MAAc;GACrC,IAAI;GACJ,IAAI;IACF,QAAQ,KAAK,MAAM,IAAI;GACzB,QAAQ;IACN;GACF;GACA,IAAI,MAAM,SAAS,QAAQ;IACzB,WAAW,MAAM,MAAM,aAAa;IACpC,OAAO;GACT;EAGF;CACF,CAAC;AACH;;;;;;;;AC9NA,MAAM,0BAA0B;;AAWhC,MAAM,sBAAsB;;AAE5B,MAAM,6BAA6B;;;;;;;;;;;;;;;AAwBnC,SAAS,aAAa,SAAiB,MAAyB;CAC9D,OAAO,MAAM,SAAS,GAAG,QAAQ,GAAG,KAAK,IAAI,UAAU,CAAC,CAAC,KAAK,GAAG,MAAM;AACzE;AAEA,SAAS,WAAW,KAAqB;CACvC,IAAI,yBAAyB,KAAK,GAAG,GAAG,OAAO;CAC/C,OAAO,IAAI,IAAI,QAAQ,MAAM,OAAO,EAAE;AACxC;AAEA,IAAM,wBAAN,cAAoC,cAAc;CAChD;CACA;CACA;CAEA,YAAY,KAAa,eAAuC,SAA+B;EAC7F,MAAM,OAAO;EACb,KAAK,MAAM;EACX,KAAK,gBAAgB,cAAc,MAAK,WAAU;GAChD,KAAK,gBAAgB,OAAO;GAC5B,IAAI,OAAO,QAAQ,KAAK,WAAW,OAAO,MAAM;GAChD,IAAI,OAAO,QAAQ,KAAK,WAAW,OAAO,MAAM;GAChD,OAAO;EACT,CAAC;CACH;CAEA,IAAI,WAA+B;EACjC,OAAO,KAAK;CACd;CAEA,MAAM,OAA+B;EACnC,OAAO,KAAK;CACd;CAEA,MAAM,OAAyB;EAK7B,MAAM,IAAI,MAAM,kFAAkF;CACpG;CAEA,MAAM,YAA2B;EAC/B,MAAM,IAAI,MAAM,2DAA2D;CAC7E;AACF;AAEA,IAAM,yBAAN,cAAqC,sBAAuC;CAC1E,eAAuB;;;;;;;;CASvB,MAAM,MAAM,SAAiB,UAA+B,CAAC,GAA2B;EAGtF,MAAM,SAAS,IAAI,sBAAsB,iBAFZ,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,KAAK,eAAA,CAAgB,SAAS,EAAE,KACnE,KAAK,QAAQ,eAAe,SAAS,KAAA,GAAW,OACxB,GAAe,OAAO;EACpE,KAAK,SAAS,IAAI,OAAO,KAAK,MAAM;EACpC,OAAO;CACT;CAEA,MAAM,OAA+B;EACnC,OAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC,KAAI,YAAW;GACvD,KAAK,OAAO;GACZ,SAAS,OAAO;GAChB,SAAS,OAAO,aAAa,KAAA;GAC7B,GAAI,OAAO,aAAa,KAAA,KAAa,EAAE,UAAU,OAAO,SAAS;EACnE,EAAE;CACJ;AACF;AAEA,IAAa,kBAAb,MAAa,wBAAwB,cAAc;CACjD;CACA,OAAgB;CAChB,WAAoB;CACpB,SAAyB;CAGzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,aAAkC;CAClC;;;;;;;CAOA,SAAsE;;;;;;;CAOtE,iBAAqF;;;;;;;;;;CAUrF,uBAAoD,KAAA;CAEpD,YAAY,UAAkC,CAAC,GAAG;EAChD,MAAM;GAAE,GAAG;GAAS,MAAM;GAAmB,WAAW,IAAI,uBAAuB;EAAE,CAAC;EACtF,KAAK,KAAK,QAAQ,MAAM,KAAK,WAAW;EACxC,KAAK,UAAU,IAAI,eAAe,OAAO;EACzC,KAAK,iBAAiB,QAAQ,iBAAiB,QAAQ,IAAI,yBAAyB;EACpF,IAAI,CAAC,KAAK,kBAAkB,CAAC,QAAQ,WAAW,MAAM,IAAI,MAAM,2BAA2B;EAC3F,KAAK,aAAa,QAAQ;EAC1B,KAAK,sBAAsB,QAAQ;EACnC,KAAK,oBAAoB,QAAQ;EACjC,KAAK,OAAO,QAAQ,OAAO,CAAC;EAC5B,KAAK,WAAW,QAAQ;EACxB,KAAK,wBAAwB,QAAQ;EACrC,KAAK,oBAAoB,QAAQ;CACnC;CAEA,aAA6B;EAC3B,OAAO,oBAAoB,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;CAC7F;;;;;;;;;;;;;CAcA,MAAM,UAA+B,CAAC,GAAoB;EACxD,OAAO,IAAI,gBAAgB;GACzB,GAAI,QAAQ,OAAO,KAAA,KAAa,EAAE,IAAI,QAAQ,GAAG;GACjD,aAAa,KAAK,QAAQ;GAC1B,WAAW,KAAK,QAAQ;GACxB,OAAO,KAAK,QAAQ;GACpB,eAAe,KAAK;GACpB,GAAI,QAAQ,cAAc,KAAA,KAAa,EAAE,WAAW,QAAQ,UAAU;GACtE,oBAAoB,QAAQ,sBAAsB,KAAK;GACvD,GAAI,KAAK,sBAAsB,KAAA,KAAa,EAAE,kBAAkB,KAAK,kBAAkB;GACvF,KAAK,QAAQ,OAAO,KAAK;GACzB,GAAI,KAAK,aAAa,KAAA,KAAa,EAAE,SAAS,KAAK,SAAS;GAC5D,GAAI,KAAK,0BAA0B,KAAA,KAAa,EAAE,cAAc,KAAK,sBAAsB;GAC3F,GAAI,KAAK,sBAAsB,KAAA,KAAa,EAAE,kBAAkB,KAAK,kBAAkB;EACzF,CAAC;CACH;CAEA,MAAM,QAAuB;EAC3B,IAAI,KAAK,YACP,IAAI;GAEF,MAAM,OAAQ,OAAM,MADG,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,KAAK,UAAU,GAAG,EAAA,CAChE,KAAK;GAIlC,IAAI,CAAC,KAAK,aAAa;IACrB,KAAK,aAAa,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,oBAAI,IAAI,KAAK;IACvE;GACF;GACA,KAAK,aAAa,KAAA;EACpB,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,qBAAqB,MAAM,WAAW,KAAK,MAAM;GACxE,KAAK,aAAa,KAAA;EACpB;EAGF,IAAI,CAAC,KAAK,gBAAgB,MAAM,IAAI,MAAM,2BAA2B;EAErE,MAAM,OAAO,KAAK,UAAU;GAK1B,IAAI,KAAK;GACT,eAAe,KAAK;GACpB,oBAAoB,KAAK;GACzB,kBAAkB,KAAK;GACvB,KAAK,KAAK;EACZ,CAAC;EAMD,IAAI;EACJ,KAAK,IAAI,UAAU,IAAK,WACtB,IAAI;GACF,WAAW,MAAM,KAAK,QAAQ,QAAQ,YAAY;IAChD,QAAQ;IACR,SAAS,EAAE,gBAAgB,mBAAmB;IAC9C;GACF,CAAC;GACD;EACF,SAAS,OAAO;GAEd,IAAI,EADc,iBAAiB,oBAAoB,MAAM,UAAU,QACrD,WAAW,qBAAqB,MAAM;GACxD,MAAM,IAAI,SAAQ,YAAW,WAAW,SAAS,6BAA6B,OAAO,CAAC;EACxF;EAEF,MAAM,OAAQ,MAAM,SAAS,KAAK;EAClC,KAAK,aAAa,KAAK;EACvB,KAAK,aAAa,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,oBAAI,IAAI,KAAK;CACzE;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,QAAQ;CACrB;CAEA,MAAM,UAAyB;EAC7B,IAAI,CAAC,KAAK,YAAY;EACtB,MAAM,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,KAAK,UAAU,KAAK,EAAE,QAAQ,SAAS,CAAC;EAGlG,KAAK,aAAa,KAAA;EAClB,KAAK,aAAa;EAGlB,KAAK,SAAS;CAChB;;;;;;;;;;;;;;;;;;CAmBA,MAAM,eAAe,SAAiB,MAAiB,SAAyD;EAC9G,MAAM,KAAK,cAAc;EACzB,IAAI,CAAC,KAAK,YAAY,MAAM,IAAI,qBAAqB,KAAK,EAAE;EAE5D,MAAM,UAAU,KAAK,IAAI;EACzB,MAAM,cAAc,aAAa,SAAS,IAAI;EAG9C,MAAM,mBAAmB,SAAS,WAAW,KAAK;EAOlD,IAAI,KAAK,yBAAyB,OAAO;GACvC,MAAM,cAAc,MAAM,KAAK,eAAe,aAAa,kBAAkB,OAAO;GACpF,IAAI,aAAa,OAAO;IAAE,GAAG;IAAa,iBAAiB,KAAK,IAAI,IAAI;GAAQ;EAClF;EACA,OAAO,KAAK,cAAc,aAAa,kBAAkB,SAAS,OAAO;CAC3E;CAEA,MAAc,eACZ,aACA,kBACA,SACwD;EACxD,IAAI;EACJ,IAAI;GACF,QAAQ,MAAM,KAAK,aAAa;EAClC,SAAS,OAAO;GAId,IAAI,iBAAiB,qBAAqB,MAAM,WAAW,OAAO,MAAM,WAAW,MAAM;IACvF,KAAK,uBAAuB;IAC5B,OAAO;GACT;GACA,MAAM;EACR;EACA,KAAK,uBAAuB;EAO5B,MAAM,cAAc,SAAS,MACzB,OAAO,YACL,OAAO,QAAQ,QAAQ,GAAG,CAAC,CAAC,QAAQ,UAAqC,MAAM,OAAO,KAAA,CAAS,CACjG,IACA,KAAA;EACJ,MAAM,SAAS,MAAM,aAAa,OAAO;GACvC,SAAS;GACT,GAAI,SAAS,QAAQ,KAAA,KAAa,EAAE,KAAK,QAAQ,IAAI;GACrD,GAAI,gBAAgB,KAAA,KAAa,EAAE,KAAK,YAAY;GACpD,GAAI,oBAAoB,QAAQ,mBAAmB,KAAK,EAAE,WAAW,iBAAiB;GACtF,GAAI,KAAK,qBAAqB,EAAE,kBAAkB,KAAK,kBAAkB;EAC3E,CAAC;EASD,IAAI,OAAO,aAAa,QAAQ,CAAC,OAAO,UAAU;GAEhD,QAAQ,KACN,yGACA;IACE,WAAW,KAAK;IAChB,QAAQ,OAAO;IACf,WAAW,OAAO;IAClB,aAAa,OAAO;IACpB,YAAY,MAAM;GACpB,CACF;GACA,KAAK,SAAS;GACd,KAAK,uBAAuB;GAC5B,OAAO;EACT;EACA,MAAM,WAAW,OAAO,YAAY;EACpC,OAAO;GACL,SAAS,aAAa;GACtB;GACA,QAAQ,OAAO;GACf,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,SAAS;EACX;CACF;CAEA,MAAc,cACZ,aACA,kBACA,SACA,SACwB;EACxB,IAAI,CAAC,KAAK,YAAY,MAAM,IAAI,qBAAqB,KAAK,EAAE;EAC5D,MAAM,aAAa,oBAAoB,OAAO,KAAK,KAAK,mBAAmB,GAAI,IAAI,KAAA;EAInF,MAAM,eACJ,oBAAoB,QAAQ,mBAAmB,IAAI,YAAY,QAAQ,mBAAmB,GAAM,IAAI,KAAA;EAYtG,MAAM,OAAQ,OAAM,MAXG,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,KAAK,UAAU,EAAE,QAAQ;GAClG,QAAQ;GACR,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU;IACnB,SAAS;IACT;IACA,KAAK,SAAS;IACd,KAAK,SAAS;GAChB,CAAC;GACD,QAAQ;EACV,CAAC,EAAA,CAC4B,KAAK;EAClC,MAAM,WAAW,KAAK,aAAa,KAAK,WAAW,MAAM;EACzD,OAAO;GACL,SAAS,aAAa;GACtB;GACA,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,iBAAiB,KAAK,IAAI,IAAI;GAC9B,UAAU,KAAK;GACf,SAAS;EACX;CACF;;;;;;;;CASA,MAAc,eAAoE;EAChF,MAAM,MAAM,KAAK,IAAI;EAKrB,IAAI,KAAK,UAAU,KAAK,OAAO,gBAAgB,QAAQ,KAAK,OAAO,cAAc,0BAA0B,KACzG,OAAO,KAAK;EAGd,IAAI,KAAK,gBAAgB,OAAO,KAAK;EACrC,IAAI,CAAC,KAAK,YAAY,MAAM,IAAI,qBAAqB,KAAK,EAAE;EAC5D,MAAM,YAAY,KAAK;EACvB,MAAM,YAAY,YAAY;GAI5B,MAAM,OAAQ,OAAM,MAHG,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,SAAS,EAAE,cAAc,EAClG,QAAQ,OACV,CAAC,EAAA,CAC4B,KAAK;GAClC,MAAM,cAAc,KAAK,YAAY,KAAK,MAAM,KAAK,SAAS,IAAI;GAClE,MAAM,QAAQ;IACZ,KAAK,KAAK;IACV,YAAY,KAAK;IACjB,aAAa,KAAK;IAClB,WAAW,KAAK;IAIhB,aAAa,gBAAgB,QAAQ,CAAC,OAAO,MAAM,WAAW,IAAI,cAAc;GAClF;GACA,KAAK,SAAS;GACd,OAAO;EACT,EAAA,CAAG;EACH,KAAK,iBAAiB;EACtB,IAAI;GACF,OAAO,MAAM;EACf,UAAU;GAGR,IAAI,KAAK,mBAAmB,UAAU,KAAK,iBAAiB;EAC9D;CACF;CAEA,MAAM,UAAgC;EACpC,IAAI,CAAC,KAAK,YACR,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,UAAU,KAAK;GACf,QAAQ,KAAK;GACb,WAAW,KAAK,8BAAc,IAAI,KAAK;EACzC;EAGF,MAAM,OAAQ,OAAM,MADG,KAAK,QAAQ,QAAQ,YAAY,mBAAmB,KAAK,UAAU,GAAG,EAAA,CAChE,KAAK;EAClC,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,UAAU,KAAK;GACf,QAAQ,KAAK;GACb,WAAW,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,IAAK,KAAK,8BAAc,IAAI,KAAK;GACpF,UAAU;IAMR,WAAW,KAAK;IAChB,oBAAoB,KAAK,sBAAsB,KAAA;IAC/C,gBAAgB,KAAK;GACvB;EACF;CACF;CAEA,gBAAgB,MAAoD;EAClE,MAAM,sBAAsB,mBAAmB,KAAK,aAAa,IAAI,KAAK,eAAe,GAAG;EAC5F,IAAI,OAAO,KAAK,0BAA0B,YACxC,OAAO,KAAK,sBAAsB;GAAE;GAAqB,gBAAgB,MAAM;EAAe,CAAC;EAEjG,IAAI,OAAO,KAAK,0BAA0B,UAAU,OAAO,KAAK;EAChE,OAAO;CACT;AACF;;;AC7iBA,MAAa,0BAAmE;CAC9E,IAAI;CACJ,MAAM;CACN,aAAa;CACb,cAAc;EACZ,MAAM;EACN,YAAY;GACV,aAAa;IACX,MAAM;IACN,aAAa;GACf;GACA,WAAW;IAAE,MAAM;IAAU,aAAa;GAAwD;GAClG,eAAe;IAAE,MAAM;IAAU,aAAa;GAAgE;GAC9G,WAAW;IAAE,MAAM;IAAU,aAAa;GAAiD;GAC3F,oBAAoB;IAAE,MAAM;IAAU,aAAa;GAAyD;GAC5G,kBAAkB;IAChB,MAAM;IACN,aAAa;IACb,MAAM,CAAC,YAAY,SAAS;IAC5B,SAAS;GACX;GACA,KAAK;IAAE,MAAM;IAAU,aAAa;IAAyB,sBAAsB,EAAE,MAAM,SAAS;GAAE;GACtG,SAAS;IAAE,MAAM;IAAU,aAAa;GAAgC;EAC1E;CACF;CACA,gBAAe,WAAU,IAAI,gBAAgB,MAAM;AACrD;AAEA,MAAa,6BAA4E;CACvF,IAAI;CACJ,MAAM;CACN,aAAa;CACb,cAAc;EACZ,MAAM;EACN,YAAY;GACV,aAAa;IACX,MAAM;IACN,aAAa;GACf;GACA,WAAW;IAAE,MAAM;IAAU,aAAa;GAAwD;GAClG,YAAY;IACV,MAAM;IACN,aAAa;GACf;GACA,UAAU;IAAE,MAAM;IAAW,aAAa;IAAsB,SAAS;GAAM;EACjF;CACF;CACA,mBAAkB,WAAU,IAAI,mBAAmB,MAAM;AAC3D"}
package/dist/sandbox.d.ts CHANGED
@@ -67,7 +67,8 @@ export declare class PlatformSandbox extends MastraSandbox {
67
67
  * Tri-state feature detection for the platform's exec-lease endpoint:
68
68
  * undefined — not yet tried (default; try direct on first exec)
69
69
  * true — endpoint present, use direct exec
70
- * false — endpoint returned 404 or 501, fall back permanently to /exec
70
+ * false — endpoint absent (404/501) OR the WebSocket transport failed
71
+ * once; fall back permanently to /exec for this sandbox
71
72
  * Sticky per instance so we make the fallback decision once per sandbox
72
73
  * lifetime instead of paying an extra round-trip on every exec.
73
74
  */
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/platform-workspace",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Mastra Platform workspace sandbox and filesystem providers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -27,9 +27,9 @@
27
27
  "tsdown": "0.22.9",
28
28
  "typescript": "^6.0.3",
29
29
  "vitest": "4.1.10",
30
- "@internal/lint": "0.0.118",
31
- "@internal/types-builder": "0.0.93",
32
- "@mastra/core": "1.54.0"
30
+ "@internal/lint": "0.0.119",
31
+ "@internal/types-builder": "0.0.94",
32
+ "@mastra/core": "1.55.0"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "@mastra/core": ">=1.4.0-0 <2.0.0-0"