@cloudflare/sandbox 0.10.2 → 0.10.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.
@@ -2765,6 +2765,10 @@ function normalizeBackupExcludePattern(pattern) {
2765
2765
  return normalized;
2766
2766
  }
2767
2767
 
2768
+ //#endregion
2769
+ //#region ../shared/src/internal.ts
2770
+ const DISABLE_SESSION_TOKEN = "__DISABLE_SESSION__";
2771
+
2768
2772
  //#endregion
2769
2773
  //#region src/container-control/connection.ts
2770
2774
  const DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
@@ -3057,12 +3061,33 @@ const IDLE_EXPORT_THRESHOLD = 1;
3057
3061
  /**
3058
3062
  * Translate a capnweb-propagated error into a typed SandboxError.
3059
3063
  *
3060
- * capnweb only preserves `error.name` and `error.message` across the wire.
3061
- * The container encodes the full error as a JSON object in the message
3062
- * string: `{"code":"...","message":"...","context":{...}}`.
3064
+ * Two wire formats are supported for backward compatibility with older
3065
+ * container images:
3066
+ *
3067
+ * 1. Propagated error properties (capnweb >= 0.8.0). The container throws a
3068
+ * `ServiceError`-shaped object with own enumerable `code` and `details`
3069
+ * properties. capnweb walks `Object.keys()` and reconstructs those fields
3070
+ * on the SDK side.
3071
+ * 2. Legacy JSON-encoded message. Older containers encoded the structured
3072
+ * payload as a JSON string in `error.message`.
3073
+ *
3074
+ * The JSON-fallback branch can be removed once all older container images are
3075
+ * no longer in service.
3063
3076
  */
3064
3077
  function translateRPCError(error) {
3065
3078
  if (error instanceof Error) {
3079
+ const propagated = error;
3080
+ if (typeof propagated.code === "string" && Object.hasOwn(ErrorCode, propagated.code)) {
3081
+ const code = propagated.code;
3082
+ const context = propagated.details && typeof propagated.details === "object" ? propagated.details : {};
3083
+ throw createErrorFromResponse({
3084
+ code,
3085
+ message: error.message,
3086
+ context,
3087
+ httpStatus: getHttpStatus(code),
3088
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3089
+ });
3090
+ }
3066
3091
  let payload;
3067
3092
  try {
3068
3093
  payload = JSON.parse(error.message);
@@ -4838,7 +4863,7 @@ function createTunnelsHandler(host) {
4838
4863
  * This file is auto-updated by .github/changeset-version.ts during releases
4839
4864
  * DO NOT EDIT MANUALLY - Changes will be overwritten on the next version bump
4840
4865
  */
4841
- const SDK_VERSION = "0.10.2";
4866
+ const SDK_VERSION = "0.10.3";
4842
4867
 
4843
4868
  //#endregion
4844
4869
  //#region src/sandbox.ts
@@ -4987,14 +5012,63 @@ function getSandbox(ns, id, options) {
4987
5012
  });
4988
5013
  }
4989
5014
  const defaultSessionId = `sandbox-${effectiveId}`;
5015
+ const useDefaultSession = options?.enableDefaultSession !== false;
4990
5016
  const enhancedMethods = {
4991
5017
  fetch: (request) => stub.fetch(request),
5018
+ exec: (command, execOptions) => useDefaultSession ? stub.exec(command, execOptions) : stub.execWithSessionToken(command, DISABLE_SESSION_TOKEN, execOptions),
5019
+ startProcess: (command, processOptions) => useDefaultSession || processOptions?.sessionId !== void 0 ? stub.startProcess(command, processOptions) : stub.startProcess(command, {
5020
+ ...processOptions,
5021
+ sessionId: DISABLE_SESSION_TOKEN
5022
+ }),
5023
+ listProcesses: (sessionId) => useDefaultSession || sessionId !== void 0 ? stub.listProcesses(sessionId) : stub.listProcesses(DISABLE_SESSION_TOKEN),
5024
+ getProcess: (id$1, sessionId) => useDefaultSession || sessionId !== void 0 ? stub.getProcess(id$1, sessionId) : stub.getProcess(id$1, DISABLE_SESSION_TOKEN),
5025
+ execStream: (command, streamOptions) => {
5026
+ if (useDefaultSession || streamOptions?.sessionId !== void 0) return stub.execStream(command, streamOptions);
5027
+ return stub.execStreamWithSessionToken(command, DISABLE_SESSION_TOKEN, streamOptions);
5028
+ },
5029
+ writeFile: (path$1, content, fileOptions = {}) => useDefaultSession || fileOptions.sessionId !== void 0 ? stub.writeFile(path$1, content, fileOptions) : stub.writeFile(path$1, content, {
5030
+ ...fileOptions,
5031
+ sessionId: DISABLE_SESSION_TOKEN
5032
+ }),
5033
+ readFile: (path$1, fileOptions = {}) => {
5034
+ const options$1 = useDefaultSession || fileOptions.sessionId !== void 0 ? fileOptions : {
5035
+ ...fileOptions,
5036
+ sessionId: DISABLE_SESSION_TOKEN
5037
+ };
5038
+ if (options$1.encoding === "none") return stub.readFile(path$1, options$1);
5039
+ return stub.readFile(path$1, options$1);
5040
+ },
5041
+ readFileStream: (path$1, fileOptions = {}) => useDefaultSession || fileOptions.sessionId !== void 0 ? stub.readFileStream(path$1, fileOptions) : stub.readFileStream(path$1, { sessionId: DISABLE_SESSION_TOKEN }),
5042
+ mkdir: (path$1, mkdirOptions = {}) => useDefaultSession || mkdirOptions.sessionId !== void 0 ? stub.mkdir(path$1, mkdirOptions) : stub.mkdir(path$1, {
5043
+ ...mkdirOptions,
5044
+ sessionId: DISABLE_SESSION_TOKEN
5045
+ }),
5046
+ deleteFile: (path$1) => useDefaultSession ? stub.deleteFile(path$1) : stub.deleteFile(path$1, DISABLE_SESSION_TOKEN),
5047
+ renameFile: (oldPath, newPath) => useDefaultSession ? stub.renameFile(oldPath, newPath) : stub.renameFile(oldPath, newPath, DISABLE_SESSION_TOKEN),
5048
+ moveFile: (sourcePath, destinationPath) => useDefaultSession ? stub.moveFile(sourcePath, destinationPath) : stub.moveFile(sourcePath, destinationPath, DISABLE_SESSION_TOKEN),
5049
+ listFiles: (path$1, listOptions) => useDefaultSession || listOptions?.sessionId !== void 0 ? stub.listFiles(path$1, listOptions) : stub.listFiles(path$1, {
5050
+ ...listOptions,
5051
+ sessionId: DISABLE_SESSION_TOKEN
5052
+ }),
5053
+ exists: (path$1, sessionId) => useDefaultSession || sessionId !== void 0 ? stub.exists(path$1, sessionId) : stub.exists(path$1, DISABLE_SESSION_TOKEN),
5054
+ gitCheckout: (repoUrl, gitOptions) => useDefaultSession || gitOptions?.sessionId !== void 0 ? stub.gitCheckout(repoUrl, gitOptions) : stub.gitCheckout(repoUrl, {
5055
+ ...gitOptions,
5056
+ sessionId: DISABLE_SESSION_TOKEN
5057
+ }),
4992
5058
  createSession: async (opts) => {
4993
5059
  return enhanceSession(stub, await stub.createSession(opts));
4994
5060
  },
4995
5061
  getSession: async (sessionId) => {
4996
5062
  return enhanceSession(stub, await stub.getSession(sessionId));
4997
5063
  },
5064
+ watch: (path$1, options$1 = {}) => useDefaultSession || options$1.sessionId !== void 0 ? stub.watch(path$1, options$1) : stub.watch(path$1, {
5065
+ ...options$1,
5066
+ sessionId: DISABLE_SESSION_TOKEN
5067
+ }),
5068
+ checkChanges: (path$1, options$1 = {}) => useDefaultSession || options$1.sessionId !== void 0 ? stub.checkChanges(path$1, options$1) : stub.checkChanges(path$1, {
5069
+ ...options$1,
5070
+ sessionId: DISABLE_SESSION_TOKEN
5071
+ }),
4998
5072
  terminal: (request, opts) => proxyTerminal(stub, defaultSessionId, request, opts),
4999
5073
  wsConnect: connect(stub),
5000
5074
  desktop: new Proxy({}, { get(_, method) {
@@ -5325,13 +5399,6 @@ var Sandbox = class Sandbox extends Container {
5325
5399
  }
5326
5400
  }
5327
5401
  }
5328
- /**
5329
- * RPC method to configure container startup timeouts. Idempotent once
5330
- * the values have been persisted: re-applying the same timeout set is a
5331
- * no-op. The transport retry budget is recomputed only when at least
5332
- * one timeout actually changes. Storage is written before the in-memory
5333
- * mirror and derived state are updated.
5334
- */
5335
5402
  async setContainerTimeouts(timeouts) {
5336
5403
  const validated = { ...this.containerTimeouts };
5337
5404
  if (timeouts.instanceGetTimeoutMS !== void 0) validated.instanceGetTimeoutMS = this.validateTimeout(timeouts.instanceGetTimeoutMS, "instanceGetTimeoutMS", 5e3, 3e5);
@@ -5344,11 +5411,6 @@ var Sandbox = class Sandbox extends Container {
5344
5411
  this.client.setRetryTimeoutMs(this.computeRetryTimeoutMs());
5345
5412
  this.logger.debug("Container timeouts updated", this.containerTimeouts);
5346
5413
  }
5347
- /**
5348
- * RPC method to set the transport protocol. Idempotent once the value
5349
- * has been persisted: re-applying the same transport is a no-op.
5350
- * Storage is written before the in-memory state and client are updated.
5351
- */
5352
5414
  async setTransport(transport) {
5353
5415
  if (transport !== "http" && transport !== "websocket" && transport !== "rpc") {
5354
5416
  this.logger.warn(`Invalid transport value: "${transport}". Must be "http", "websocket", or "rpc". Ignoring.`);
@@ -5367,19 +5429,11 @@ var Sandbox = class Sandbox extends Container {
5367
5429
  this.renewActivityTimeout();
5368
5430
  this.logger.debug("Transport updated", { transport });
5369
5431
  }
5370
- /**
5371
- * Validate a timeout value is within acceptable range
5372
- * Throws error if invalid - used for user-provided values
5373
- */
5374
5432
  validateTimeout(value, name, min, max) {
5375
5433
  if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) throw new Error(`${name} must be a valid finite number, got ${value}`);
5376
5434
  if (value < min || value > max) throw new Error(`${name} must be between ${min}-${max}ms, got ${value}ms`);
5377
5435
  return value;
5378
5436
  }
5379
- /**
5380
- * Get default timeouts with env var fallbacks and validation
5381
- * Precedence: SDK defaults < Env vars < User config
5382
- */
5383
5437
  getDefaultTimeouts(env$1) {
5384
5438
  const parseAndValidate = (envVar, name, min, max) => {
5385
5439
  const defaultValue = this.DEFAULT_CONTAINER_TIMEOUTS[name];
@@ -5742,7 +5796,7 @@ var Sandbox = class Sandbox extends Container {
5742
5796
  */
5743
5797
  async createPasswordFile(passwordFilePath, bucket, credentials) {
5744
5798
  const content = `${bucket}:${credentials.accessKeyId}:${credentials.secretAccessKey}`;
5745
- await this.writeFile(passwordFilePath, content);
5799
+ await this.client.files.writeFile(passwordFilePath, content, DISABLE_SESSION_TOKEN);
5746
5800
  await this.execInternal(`chmod 0600 ${shellEscape(passwordFilePath)}`);
5747
5801
  }
5748
5802
  /**
@@ -5920,8 +5974,7 @@ var Sandbox = class Sandbox extends Container {
5920
5974
  let restored = 0;
5921
5975
  let skipped = 0;
5922
5976
  let failed = 0;
5923
- const sessionId = await this.ensureDefaultSession();
5924
- const exposedSet = await this.client.ports.getExposedPorts(sessionId).then((response) => new Set(response.ports.map((p) => p.port))).catch((error) => {
5977
+ const exposedSet = await this.client.ports.getExposedPorts(DISABLE_SESSION_TOKEN).then((response) => new Set(response.ports.map((p) => p.port))).catch((error) => {
5925
5978
  this.logger.warn("Failed to fetch exposed ports for restore; assuming none exposed", { error: error instanceof Error ? error.message : String(error) });
5926
5979
  return /* @__PURE__ */ new Set();
5927
5980
  });
@@ -5937,7 +5990,7 @@ var Sandbox = class Sandbox extends Container {
5937
5990
  continue;
5938
5991
  }
5939
5992
  try {
5940
- await this.client.ports.exposePort(port, sessionId, entry.name);
5993
+ await this.client.ports.exposePort(port, DISABLE_SESSION_TOKEN, entry.name);
5941
5994
  restored++;
5942
5995
  } catch (error) {
5943
5996
  failed++;
@@ -6320,10 +6373,78 @@ var Sandbox = class Sandbox extends Container {
6320
6373
  if (containerPlacementId === void 0) return;
6321
6374
  await this.ctx.storage.put("containerPlacementId", containerPlacementId);
6322
6375
  }
6376
+ async resolveExecution(explicitSessionId) {
6377
+ if (explicitSessionId !== void 0) {
6378
+ this.validateExplicitSessionId(explicitSessionId);
6379
+ if (explicitSessionId === DISABLE_SESSION_TOKEN) return { kind: "sessionless" };
6380
+ return {
6381
+ kind: "session",
6382
+ sessionId: explicitSessionId
6383
+ };
6384
+ }
6385
+ return {
6386
+ kind: "session",
6387
+ sessionId: await this.ensureDefaultSession()
6388
+ };
6389
+ }
6390
+ validateExplicitSessionId(sessionId) {
6391
+ if (sessionId.trim().length === 0) throw new Error("sessionId must not be empty or whitespace");
6392
+ }
6393
+ serializeExecutionContext(context) {
6394
+ if (context.kind === "sessionless") return DISABLE_SESSION_TOKEN;
6395
+ return context.sessionId;
6396
+ }
6397
+ getPublicExecutionSessionId(sessionId) {
6398
+ return sessionId === DISABLE_SESSION_TOKEN ? void 0 : sessionId;
6399
+ }
6400
+ /**
6401
+ * Resolves the session ID to annotate returned Process objects.
6402
+ *
6403
+ * Unlike `resolveExecution`, this is synchronous and never creates a
6404
+ * session. When the default session hasn't been established yet, it returns
6405
+ * `undefined` rather than triggering session creation. The resolved value is
6406
+ * only used to populate `Process.sessionId` on the returned object — it is
6407
+ * never sent to the container API.
6408
+ */
6409
+ getProcessSessionBinding(explicitSessionId) {
6410
+ if (explicitSessionId !== void 0) {
6411
+ this.validateExplicitSessionId(explicitSessionId);
6412
+ if (explicitSessionId === DISABLE_SESSION_TOKEN) return;
6413
+ return explicitSessionId;
6414
+ }
6415
+ return this.defaultSession ?? void 0;
6416
+ }
6417
+ resolveExecutionEnv(sessionId, env$1) {
6418
+ if (sessionId === DISABLE_SESSION_TOKEN) {
6419
+ const mergedEnv = filterEnvVars({
6420
+ ...this.envVars,
6421
+ ...env$1 ?? {}
6422
+ });
6423
+ return Object.keys(mergedEnv).length > 0 ? mergedEnv : void 0;
6424
+ }
6425
+ if (env$1 === void 0) return;
6426
+ const filteredEnv = filterEnvVars(env$1);
6427
+ return Object.keys(filteredEnv).length > 0 ? filteredEnv : void 0;
6428
+ }
6429
+ buildExecutionRequestOptions(sessionId, options) {
6430
+ const env$1 = this.resolveExecutionEnv(sessionId, options?.env);
6431
+ if (options?.timeout === void 0 && env$1 === void 0 && options?.cwd === void 0 && options?.origin === void 0) return;
6432
+ return {
6433
+ ...options?.timeout !== void 0 && { timeoutMs: options.timeout },
6434
+ ...env$1 !== void 0 && { env: env$1 },
6435
+ ...options?.cwd !== void 0 && { cwd: options.cwd },
6436
+ ...options?.origin !== void 0 && { origin: options.origin }
6437
+ };
6438
+ }
6323
6439
  async exec(command, options) {
6324
- const session = await this.ensureDefaultSession();
6440
+ const context = await this.resolveExecution();
6441
+ const session = this.serializeExecutionContext(context);
6325
6442
  return this.execWithSession(command, session, options);
6326
6443
  }
6444
+ async execWithSessionToken(command, sessionId, options) {
6445
+ this.validateExplicitSessionId(sessionId);
6446
+ return this.execWithSession(command, sessionId, options);
6447
+ }
6327
6448
  /**
6328
6449
  * Execute an infrastructure command (backup, mount, env setup, etc.)
6329
6450
  * tagged with origin: 'internal' so logging demotes it to debug level.
@@ -6346,15 +6467,11 @@ var Sandbox = class Sandbox extends Container {
6346
6467
  let result;
6347
6468
  if (options?.stream && options?.onOutput) result = await this.executeWithStreaming(command, sessionId, options, startTime, timestamp);
6348
6469
  else {
6349
- const commandOptions = options && (options.timeout !== void 0 || options.env !== void 0 || options.cwd !== void 0 || options.origin !== void 0) ? {
6350
- timeoutMs: options.timeout,
6351
- env: options.env,
6352
- cwd: options.cwd,
6353
- origin: options.origin
6354
- } : void 0;
6470
+ const commandOptions = this.buildExecutionRequestOptions(sessionId, options);
6355
6471
  const response = await this.client.commands.execute(command, sessionId, commandOptions);
6356
6472
  const duration = Date.now() - startTime;
6357
- result = this.mapExecuteResponseToExecResult(response, duration, sessionId);
6473
+ const publicSessionId = this.getPublicExecutionSessionId(sessionId);
6474
+ result = this.mapExecuteResponseToExecResult(response, duration, publicSessionId);
6358
6475
  }
6359
6476
  execOutcome = {
6360
6477
  exitCode: result.exitCode,
@@ -6373,7 +6490,7 @@ var Sandbox = class Sandbox extends Container {
6373
6490
  command,
6374
6491
  exitCode: execOutcome?.exitCode,
6375
6492
  durationMs: Date.now() - startTime,
6376
- sessionId,
6493
+ sessionId: this.getPublicExecutionSessionId(sessionId),
6377
6494
  origin: options?.origin ?? "user",
6378
6495
  error: execError ?? void 0,
6379
6496
  errorMessage: execError?.message
@@ -6384,12 +6501,8 @@ var Sandbox = class Sandbox extends Container {
6384
6501
  let stdout = "";
6385
6502
  let stderr = "";
6386
6503
  try {
6387
- const stream = await this.client.commands.executeStream(command, sessionId, {
6388
- timeoutMs: options.timeout,
6389
- env: options.env,
6390
- cwd: options.cwd,
6391
- origin: options.origin
6392
- });
6504
+ const commandOptions = this.buildExecutionRequestOptions(sessionId, options);
6505
+ const stream = await this.client.commands.executeStream(command, sessionId, commandOptions);
6393
6506
  for await (const event of parseSSEStream(stream)) {
6394
6507
  if (options.signal?.aborted) throw new Error("Operation was aborted");
6395
6508
  switch (event.type) {
@@ -6411,7 +6524,7 @@ var Sandbox = class Sandbox extends Container {
6411
6524
  command,
6412
6525
  duration,
6413
6526
  timestamp,
6414
- sessionId
6527
+ sessionId: this.getPublicExecutionSessionId(sessionId)
6415
6528
  };
6416
6529
  }
6417
6530
  case "error": throw new Error(event.data || "Command execution failed");
@@ -6687,12 +6800,16 @@ var Sandbox = class Sandbox extends Container {
6687
6800
  }
6688
6801
  async startProcess(command, options, sessionId) {
6689
6802
  try {
6690
- const session = sessionId ?? await this.ensureDefaultSession();
6803
+ const execution = await this.resolveExecution(sessionId);
6804
+ const session = this.serializeExecutionContext(execution);
6805
+ const processSession = this.getProcessSessionBinding(session);
6691
6806
  const requestOptions = {
6807
+ ...this.buildExecutionRequestOptions(session, {
6808
+ timeout: options?.timeout,
6809
+ env: options?.env,
6810
+ cwd: options?.cwd
6811
+ }),
6692
6812
  ...options?.processId !== void 0 && { processId: options.processId },
6693
- ...options?.timeout !== void 0 && { timeoutMs: options.timeout },
6694
- ...options?.env !== void 0 && { env: filterEnvVars(options.env) },
6695
- ...options?.cwd !== void 0 && { cwd: options.cwd },
6696
6813
  ...options?.encoding !== void 0 && { encoding: options.encoding },
6697
6814
  ...options?.autoCleanup !== void 0 && { autoCleanup: options.autoCleanup }
6698
6815
  };
@@ -6705,7 +6822,7 @@ var Sandbox = class Sandbox extends Container {
6705
6822
  startTime: /* @__PURE__ */ new Date(),
6706
6823
  endTime: void 0,
6707
6824
  exitCode: void 0
6708
- }, session);
6825
+ }, processSession);
6709
6826
  if (options?.onStart) options.onStart(processObj);
6710
6827
  if (options?.onOutput || options?.onExit) this.startProcessCallbackStream(response.processId, options).catch(() => {});
6711
6828
  return processObj;
@@ -6733,13 +6850,14 @@ var Sandbox = class Sandbox extends Container {
6733
6850
  if (options.onExit) options.onExit(event.exitCode ?? null);
6734
6851
  return;
6735
6852
  }
6853
+ throw new Error("Stream ended without completion event");
6736
6854
  } catch (error) {
6737
6855
  if (options.onError && error instanceof Error) options.onError(error);
6738
6856
  this.logger.error("Background process streaming failed", error instanceof Error ? error : new Error(String(error)), { processId });
6739
6857
  }
6740
6858
  }
6741
6859
  async listProcesses(sessionId) {
6742
- const session = sessionId ?? await this.ensureDefaultSession();
6860
+ const session = this.getProcessSessionBinding(sessionId);
6743
6861
  return (await this.client.processes.listProcesses()).processes.map((processData) => this.createProcessFromDTO({
6744
6862
  id: processData.id,
6745
6863
  pid: processData.pid,
@@ -6751,22 +6869,24 @@ var Sandbox = class Sandbox extends Container {
6751
6869
  }, session));
6752
6870
  }
6753
6871
  async getProcess(id, sessionId) {
6754
- const session = sessionId ?? await this.ensureDefaultSession();
6755
- const response = await this.client.processes.getProcess(id).catch((e) => {
6756
- if (e instanceof ProcessNotFoundError) return null;
6757
- throw e;
6758
- });
6759
- if (!response?.process) return null;
6760
- const processData = response.process;
6761
- return this.createProcessFromDTO({
6762
- id: processData.id,
6763
- pid: processData.pid,
6764
- command: processData.command,
6765
- status: processData.status,
6766
- startTime: processData.startTime,
6767
- endTime: processData.endTime,
6768
- exitCode: processData.exitCode
6769
- }, session);
6872
+ const session = this.getProcessSessionBinding(sessionId);
6873
+ try {
6874
+ const response = await this.client.processes.getProcess(id);
6875
+ if (!response.process) return null;
6876
+ const processData = response.process;
6877
+ return this.createProcessFromDTO({
6878
+ id: processData.id,
6879
+ pid: processData.pid,
6880
+ command: processData.command,
6881
+ status: processData.status,
6882
+ startTime: processData.startTime,
6883
+ endTime: processData.endTime,
6884
+ exitCode: processData.exitCode
6885
+ }, session);
6886
+ } catch (error) {
6887
+ if (error instanceof ProcessNotFoundError) return null;
6888
+ throw error;
6889
+ }
6770
6890
  }
6771
6891
  async killProcess(id, signal, sessionId) {
6772
6892
  await this.client.processes.killProcess(id);
@@ -6787,23 +6907,29 @@ var Sandbox = class Sandbox extends Container {
6787
6907
  }
6788
6908
  async execStream(command, options) {
6789
6909
  if (options?.signal?.aborted) throw new Error("Operation was aborted");
6790
- const session = await this.ensureDefaultSession();
6791
- return this.client.commands.executeStream(command, session, {
6792
- timeoutMs: options?.timeout,
6910
+ const context = await this.resolveExecution(options?.sessionId);
6911
+ const session = this.serializeExecutionContext(context);
6912
+ const executionOptions = this.buildExecutionRequestOptions(session, {
6913
+ timeout: options?.timeout,
6793
6914
  env: options?.env,
6794
6915
  cwd: options?.cwd
6795
6916
  });
6917
+ return this.client.commands.executeStream(command, session, executionOptions);
6918
+ }
6919
+ async execStreamWithSessionToken(command, sessionId, options) {
6920
+ this.validateExplicitSessionId(sessionId);
6921
+ return this.execStreamWithSession(command, sessionId, options);
6796
6922
  }
6797
6923
  /**
6798
6924
  * Internal session-aware execStream implementation
6799
6925
  */
6800
6926
  async execStreamWithSession(command, sessionId, options) {
6801
6927
  if (options?.signal?.aborted) throw new Error("Operation was aborted");
6802
- return this.client.commands.executeStream(command, sessionId, {
6803
- timeoutMs: options?.timeout,
6928
+ return this.client.commands.executeStream(command, sessionId, this.buildExecutionRequestOptions(sessionId, {
6929
+ timeout: options?.timeout,
6804
6930
  env: options?.env,
6805
6931
  cwd: options?.cwd
6806
- });
6932
+ }));
6807
6933
  }
6808
6934
  /**
6809
6935
  * Stream logs from a background process as a ReadableStream.
@@ -6813,7 +6939,8 @@ var Sandbox = class Sandbox extends Container {
6813
6939
  return this.client.processes.streamProcessLogs(processId);
6814
6940
  }
6815
6941
  async gitCheckout(repoUrl, options) {
6816
- const session = options?.sessionId ?? await this.ensureDefaultSession();
6942
+ const execution = await this.resolveExecution(options?.sessionId);
6943
+ const session = this.serializeExecutionContext(execution);
6817
6944
  return this.client.git.checkout(repoUrl, session, {
6818
6945
  branch: options?.branch,
6819
6946
  targetDir: options?.targetDir,
@@ -6822,28 +6949,34 @@ var Sandbox = class Sandbox extends Container {
6822
6949
  });
6823
6950
  }
6824
6951
  async mkdir(path$1, options = {}) {
6825
- const session = options.sessionId ?? await this.ensureDefaultSession();
6952
+ const execution = await this.resolveExecution(options.sessionId);
6953
+ const session = this.serializeExecutionContext(execution);
6826
6954
  return this.client.files.mkdir(path$1, session, { recursive: options.recursive });
6827
6955
  }
6828
6956
  async writeFile(path$1, content, options = {}) {
6829
- const session = options.sessionId ?? await this.ensureDefaultSession();
6957
+ const execution = await this.resolveExecution(options.sessionId);
6958
+ const session = this.serializeExecutionContext(execution);
6830
6959
  if (content instanceof ReadableStream) return this.client.files.writeFileStream(path$1, content, session);
6831
6960
  return this.client.files.writeFile(path$1, content, session, { encoding: options.encoding });
6832
6961
  }
6833
6962
  async deleteFile(path$1, sessionId) {
6834
- const session = sessionId ?? await this.ensureDefaultSession();
6963
+ const execution = await this.resolveExecution(sessionId);
6964
+ const session = this.serializeExecutionContext(execution);
6835
6965
  return this.client.files.deleteFile(path$1, session);
6836
6966
  }
6837
6967
  async renameFile(oldPath, newPath, sessionId) {
6838
- const session = sessionId ?? await this.ensureDefaultSession();
6968
+ const execution = await this.resolveExecution(sessionId);
6969
+ const session = this.serializeExecutionContext(execution);
6839
6970
  return this.client.files.renameFile(oldPath, newPath, session);
6840
6971
  }
6841
6972
  async moveFile(sourcePath, destinationPath, sessionId) {
6842
- const session = sessionId ?? await this.ensureDefaultSession();
6973
+ const execution = await this.resolveExecution(sessionId);
6974
+ const session = this.serializeExecutionContext(execution);
6843
6975
  return this.client.files.moveFile(sourcePath, destinationPath, session);
6844
6976
  }
6845
6977
  async readFile(path$1, options = {}) {
6846
- const session = options.sessionId ?? await this.ensureDefaultSession();
6978
+ const execution = await this.resolveExecution(options.sessionId);
6979
+ const session = this.serializeExecutionContext(execution);
6847
6980
  if (options.encoding === "none") return this.client.files.readFile(path$1, session, { encoding: "none" });
6848
6981
  return this.client.files.readFile(path$1, session, { encoding: options.encoding });
6849
6982
  }
@@ -6854,15 +6987,18 @@ var Sandbox = class Sandbox extends Container {
6854
6987
  * @param options - Optional session ID
6855
6988
  */
6856
6989
  async readFileStream(path$1, options = {}) {
6857
- const session = options.sessionId ?? await this.ensureDefaultSession();
6990
+ const execution = await this.resolveExecution(options.sessionId);
6991
+ const session = this.serializeExecutionContext(execution);
6858
6992
  return this.client.files.readFileStream(path$1, session);
6859
6993
  }
6860
6994
  async listFiles(path$1, options) {
6861
- const session = await this.ensureDefaultSession();
6995
+ const context = await this.resolveExecution(options?.sessionId);
6996
+ const session = this.serializeExecutionContext(context);
6862
6997
  return this.client.files.listFiles(path$1, session, options);
6863
6998
  }
6864
6999
  async exists(path$1, sessionId) {
6865
- const session = sessionId ?? await this.ensureDefaultSession();
7000
+ const execution = await this.resolveExecution(sessionId);
7001
+ const session = this.serializeExecutionContext(execution);
6866
7002
  return this.client.files.exists(path$1, session);
6867
7003
  }
6868
7004
  /**
@@ -6913,7 +7049,8 @@ var Sandbox = class Sandbox extends Container {
6913
7049
  * @param options - Watch options
6914
7050
  */
6915
7051
  async watch(path$1, options = {}) {
6916
- const sessionId = options.sessionId ?? await this.ensureDefaultSession();
7052
+ const execution = await this.resolveExecution(options.sessionId);
7053
+ const sessionId = this.serializeExecutionContext(execution);
6917
7054
  return this.client.watch.watch({
6918
7055
  path: path$1,
6919
7056
  recursive: options.recursive,
@@ -6933,7 +7070,8 @@ var Sandbox = class Sandbox extends Container {
6933
7070
  * @param options - Change-check options
6934
7071
  */
6935
7072
  async checkChanges(path$1, options = {}) {
6936
- const sessionId = options.sessionId ?? await this.ensureDefaultSession();
7073
+ const execution = await this.resolveExecution(options.sessionId);
7074
+ const sessionId = this.serializeExecutionContext(execution);
6937
7075
  return this.client.watch.checkChanges({
6938
7076
  path: path$1,
6939
7077
  recursive: options.recursive,
@@ -6995,7 +7133,7 @@ var Sandbox = class Sandbox extends Container {
6995
7133
  const tokens = await this.readPortTokens();
6996
7134
  const existingPort = Object.entries(tokens).find(([p, entry]) => entry.token === token && p !== port.toString());
6997
7135
  if (existingPort) throw new SandboxSecurityError(`Token '${token}' is already in use by port ${existingPort[0]}. Please use a different token.`);
6998
- const sessionId = await this.ensureDefaultSession();
7136
+ const sessionId = this.serializeExecutionContext(await this.resolveExecution());
6999
7137
  await this.client.ports.exposePort(port, sessionId, options?.name);
7000
7138
  tokens[port.toString()] = {
7001
7139
  token,
@@ -7035,7 +7173,7 @@ var Sandbox = class Sandbox extends Container {
7035
7173
  delete tokens[port.toString()];
7036
7174
  await this.ctx.storage.put("portTokens", tokens);
7037
7175
  }
7038
- const sessionId = await this.ensureDefaultSession();
7176
+ const sessionId = this.serializeExecutionContext(await this.resolveExecution());
7039
7177
  try {
7040
7178
  await this.client.ports.unexposePort(port, sessionId);
7041
7179
  } catch (error) {
@@ -7056,7 +7194,7 @@ var Sandbox = class Sandbox extends Container {
7056
7194
  }
7057
7195
  }
7058
7196
  async getExposedPorts(hostname) {
7059
- const sessionId = await this.ensureDefaultSession();
7197
+ const sessionId = this.serializeExecutionContext(await this.resolveExecution());
7060
7198
  const response = await this.client.ports.getExposedPorts(sessionId);
7061
7199
  if (!this.sandboxName) throw new Error("Sandbox name not available. Ensure sandbox is accessed through getSandbox()");
7062
7200
  const tokens = await this.readPortTokens();
@@ -7114,7 +7252,7 @@ var Sandbox = class Sandbox extends Container {
7114
7252
  }
7115
7253
  async isPortExposed(port) {
7116
7254
  try {
7117
- const sessionId = await this.ensureDefaultSession();
7255
+ const sessionId = this.serializeExecutionContext(await this.resolveExecution());
7118
7256
  return (await this.client.ports.getExposedPorts(sessionId)).ports.some((exposedPort) => exposedPort.port === port);
7119
7257
  } catch (error) {
7120
7258
  this.logger.error("Error checking if port is exposed", error instanceof Error ? error : new Error(String(error)), { port });
@@ -7174,6 +7312,7 @@ var Sandbox = class Sandbox extends Container {
7174
7312
  */
7175
7313
  async createSession(options) {
7176
7314
  const sessionId = options?.id || `session-${Date.now()}`;
7315
+ if (sessionId === DISABLE_SESSION_TOKEN) throw new Error(`Session ID '${DISABLE_SESSION_TOKEN}' is reserved for internal use`);
7177
7316
  const filteredEnv = filterEnvVars({
7178
7317
  ...this.envVars,
7179
7318
  ...options?.env ?? {}
@@ -8401,4 +8540,4 @@ var Sandbox = class Sandbox extends Container {
8401
8540
 
8402
8541
  //#endregion
8403
8542
  export { DesktopInvalidOptionsError as A, CommandClient as C, BackupNotFoundError as D, BackupExpiredError as E, InvalidBackupConfigError as F, ProcessExitedBeforeReadyError as I, ProcessReadyTimeoutError as L, DesktopProcessCrashedError as M, DesktopStartFailedError as N, BackupRestoreError as O, DesktopUnavailableError as P, RPCTransportError as R, DesktopClient as S, BackupCreateError as T, UtilityClient as _, BucketMountError as a, GitClient as b, MissingCredentialsError as c, parseSSEStream as d, responseToAsyncIterable as f, SandboxClient as g, streamFile as h, proxyTerminal as i, DesktopNotStartedError as j, DesktopInvalidCoordinatesError as k, S3FSMountError as l, collectFile as m, getSandbox as n, BucketUnmountError as o, CodeInterpreter as p, proxyToSandbox as r, InvalidMountConfigError as s, Sandbox as t, asyncIterableToSSEStream as u, ProcessClient as v, BackupClient as w, FileClient as x, PortClient as y, SessionTerminatedError as z };
8404
- //# sourceMappingURL=sandbox-BcEq4aUF.js.map
8543
+ //# sourceMappingURL=sandbox-B-MUmsli.js.map