@mastra/platform-workspace 1.7.0-alpha.0 → 1.7.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Buffer as Buffer$1 } from "buffer";
2
2
  import nodePath from "path";
3
- import { FileExistsError, FileNotFoundError, MastraFilesystem, MastraSandbox, ProcessHandle, SandboxNotReadyError, SandboxProcessManager, UnsupportedStdinCloseError, WorkspaceReadOnlyError } from "@mastra/core/workspace";
3
+ import { FileExistsError, FileNotFoundError, MastraFilesystem, MastraSandbox, ProcessHandle, SandboxError, SandboxNotReadyError, SandboxProcessManager, UnsupportedStdinCloseError, WorkspaceReadOnlyError } from "@mastra/core/workspace";
4
4
  import { CommandExitError, Sandbox, TimeoutError } from "e2b";
5
5
  import { execFile } from "child_process";
6
6
  import { createHash } from "crypto";
@@ -1127,10 +1127,21 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1127
1127
  * on a later start.
1128
1128
  */
1129
1129
  templatePending;
1130
+ /**
1131
+ * The platform's id for the provisioned sandbox, as returned by the create
1132
+ * or reattach response. Persist this to reattach later via the `sandboxId`
1133
+ * option. Undefined until the sandbox has been started in this process; a
1134
+ * `sandboxId` passed to the constructor is only a hint for `start()` and is
1135
+ * not reported here until the proxy confirms it.
1136
+ */
1137
+ get sandboxId() {
1138
+ return this.status === "running" ? this._sandboxId : void 0;
1139
+ }
1130
1140
  _client;
1131
1141
  _usesProviderRoutes;
1132
1142
  _environmentId;
1133
1143
  _sandboxId;
1144
+ _reattach;
1134
1145
  _seedCheckpointName;
1135
1146
  _templateDefinition;
1136
1147
  _templateBuildEnvs;
@@ -1222,6 +1233,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1222
1233
  this._environmentId = options.environmentId ?? process.env.MASTRA_ENVIRONMENT_ID ?? "";
1223
1234
  if (!this._environmentId && !options.sandboxId) throw new Error("environmentId is required");
1224
1235
  this._sandboxId = options.sandboxId;
1236
+ this._reattach = options.reattach ?? "best-effort";
1225
1237
  this._seedCheckpointName = options.seedCheckpointName;
1226
1238
  this._usesProviderRoutes = options.template !== void 0;
1227
1239
  this._template = options.template;
@@ -1238,6 +1250,16 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1238
1250
  generateId() {
1239
1251
  return `platform-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
1240
1252
  }
1253
+ _failIfReattachRequired(reason, cause) {
1254
+ if (this._reattach !== "required") return;
1255
+ throw new SandboxError(`Platform sandbox ${this._sandboxId} could not be reattached (${reason}); refusing to provision a replacement because reattach is 'required'. Clear the persisted sandboxId to start a fresh sandbox.`, "SANDBOX_REATTACH_FAILED", {
1256
+ provider: "platform",
1257
+ sandboxId: this._sandboxId,
1258
+ logicalId: this.id,
1259
+ reason,
1260
+ ...cause !== void 0 && { cause }
1261
+ });
1262
+ }
1241
1263
  _request(path, options = {}) {
1242
1264
  return this._usesProviderRoutes ? this._client.requestProvider(path, options) : this._client.request(path, options);
1243
1265
  }
@@ -1267,6 +1289,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1267
1289
  fetch: this._client.fetch,
1268
1290
  environmentId: this._environmentId,
1269
1291
  ...options.sandboxId !== void 0 && { sandboxId: options.sandboxId },
1292
+ reattach: this._reattach,
1270
1293
  ...seedCheckpointName !== void 0 && { seedCheckpointName },
1271
1294
  ...this._template !== void 0 && { template: this._template },
1272
1295
  idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,
@@ -1309,9 +1332,11 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1309
1332
  this._logStartComplete(json.id, startedAt, requestMs, "reattach");
1310
1333
  return { outcome: "connected" };
1311
1334
  }
1335
+ this._failIfReattachRequired("destroyed");
1312
1336
  this._sandboxId = void 0;
1313
1337
  } catch (error) {
1314
- if (!(error instanceof PlatformApiError) || error.status !== 404) throw error;
1338
+ if (!(error instanceof PlatformApiError) || error.status !== 404 && error.status !== 410) throw error;
1339
+ this._failIfReattachRequired(String(error.status), error);
1315
1340
  this._sandboxId = void 0;
1316
1341
  }
1317
1342
  if (!this._environmentId) throw new Error("environmentId is required");
@@ -1494,23 +1519,28 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1494
1519
  await Promise.race([this._transportReadyPromise, new Promise((r) => setTimeout(r, TRANSPORT_READY_WAIT_MS))]);
1495
1520
  }
1496
1521
  /**
1497
- * Stop the sandbox while **preserving its recovery checkpoint**.
1522
+ * Stop this process's use of the sandbox without touching the remote one.
1498
1523
  *
1499
- * Semantic parity with `@mastra/railway` `RailwaySandbox.stop()`: the VM
1500
- * is released but the on-provider checkpoint survives, so a subsequent
1501
- * `start()` on a sandbox constructed with the same `id` can restore from
1502
- * it. Any in-flight capture is awaited first so the preserved checkpoint
1503
- * reflects the latest disk state we asked for.
1524
+ * Nothing is sent to workspace-proxy. The VM keeps running and idles out
1525
+ * under the environment's own idle policy, the recovery checkpoint stays,
1526
+ * and `_sandboxId` is kept so a later `start()` on this instance, or on a
1527
+ * new instance constructed with the same `id` or `sandboxId`, reattaches
1528
+ * instead of provisioning. `Mastra.shutdown()` stops every registered
1529
+ * workspace on SIGINT; releasing the VM here would destroy every live
1530
+ * session on a host restart. Use {@link destroy} to release the VM and
1531
+ * its checkpoint.
1504
1532
  *
1505
- * Corresponds to `DELETE /v1/:provider/projects/:pid/sandbox/:sandboxId` on
1506
- * workspace-proxy, which by contract does not touch the checkpoint. Use
1507
- * {@link destroy} when you want the checkpoint released too.
1533
+ * Any in-flight capture is awaited first so the checkpoint reflects the
1534
+ * latest disk state the caller asked for.
1508
1535
  */
1509
1536
  async stop() {
1510
1537
  if (this._captureInFlight) await this._captureInFlight.catch((error) => {
1511
- this.logger.warn(`stop(): failed to flush in-flight capture before teardown:`, error);
1538
+ this.logger.warn(`stop(): failed to flush in-flight capture before stop:`, error);
1512
1539
  });
1513
- await this._teardownSandbox();
1540
+ this._probeGeneration++;
1541
+ this._probeTarget = null;
1542
+ this._transportReadyPromise = null;
1543
+ this._lease = null;
1514
1544
  }
1515
1545
  /**
1516
1546
  * Destroy the sandbox **and release its recovery checkpoint**.
@@ -1547,12 +1577,9 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1547
1577
  /**
1548
1578
  * Release the remote sandbox VM and clear the local state pointing at it.
1549
1579
  *
1550
- * Shared body of {@link stop} and {@link destroy} — both funnel through
1551
- * here after they've dealt with the checkpoint (preserve vs release).
1552
- * The VM DELETE is safe to issue in either mode: the proxy's DELETE
1553
- * route does not touch the checkpoint on its own, so `stop()` correctly
1554
- * leaves the checkpoint intact and `destroy()` has already removed it
1555
- * before this call.
1580
+ * Body of {@link destroy}, called after the checkpoint has been released.
1581
+ * The proxy's DELETE route releases the VM (on E2B that kills it), so
1582
+ * only destroy() may reach here; stop() never does.
1556
1583
  */
1557
1584
  async _teardownSandbox() {
1558
1585
  if (!this._sandboxId) return;
@@ -1931,7 +1958,7 @@ var PlatformSandbox = class PlatformSandbox extends MastraSandbox {
1931
1958
  }
1932
1959
  }
1933
1960
  async getInfo() {
1934
- if (!this._sandboxId) return {
1961
+ if (!this._sandboxId || this.status !== "running") return {
1935
1962
  id: this.id,
1936
1963
  name: this.name,
1937
1964
  provider: this.provider,