modal 0.5.5 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,6 +45,7 @@ We also provide a number of examples:
45
45
  - [Check the status and exit code of a Sandbox](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-poll.ts)
46
46
  - [Access Sandbox filesystem](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-filesystem.ts)
47
47
  - [Expose ports on a Sandbox using Tunnels](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-tunnels.ts)
48
+ - [Create connect tokens for a Sandbox](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-connect-token.ts)
48
49
  - [Include Secrets in Sandbox](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-secrets.ts)
49
50
  - [Mount a Volume to a Sandbox](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-volume.ts), and same but [with an ephemeral Volume](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-volume-ephemeral.ts)
50
51
  - [Mount a cloud bucket to a Sandbox](https://github.com/modal-labs/libmodal/blob/main/modal-js/examples/sandbox-cloud-bucket.ts)
package/dist/index.cjs CHANGED
@@ -45741,11 +45741,19 @@ async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45741
45741
  idleTimeout: "idleTimeoutMs"
45742
45742
  });
45743
45743
  const gpuConfig = parseGpuConfig(params.gpu);
45744
+ if (params.timeoutMs != void 0 && params.timeoutMs <= 0) {
45745
+ throw new Error(`timeoutMs must be positive, got ${params.timeoutMs}`);
45746
+ }
45744
45747
  if (params.timeoutMs && params.timeoutMs % 1e3 !== 0) {
45745
45748
  throw new Error(
45746
45749
  `timeoutMs must be a multiple of 1000ms, got ${params.timeoutMs}`
45747
45750
  );
45748
45751
  }
45752
+ if (params.idleTimeoutMs != void 0 && params.idleTimeoutMs <= 0) {
45753
+ throw new Error(
45754
+ `idleTimeoutMs must be positive, got ${params.idleTimeoutMs}`
45755
+ );
45756
+ }
45749
45757
  if (params.idleTimeoutMs && params.idleTimeoutMs % 1e3 !== 0) {
45750
45758
  throw new Error(
45751
45759
  `idleTimeoutMs must be a multiple of 1000ms, got ${params.idleTimeoutMs}`
@@ -45867,13 +45875,24 @@ async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45867
45875
  memoryMbMax = params.memoryLimitMiB;
45868
45876
  }
45869
45877
  }
45878
+ const protoExperimentalOptions = params.experimentalOptions ? Object.entries(params.experimentalOptions).reduce(
45879
+ (acc, [name, value]) => {
45880
+ if (typeof value !== "boolean") {
45881
+ throw new Error(
45882
+ `experimental option '${name}' must be a boolean, got ${value}`
45883
+ );
45884
+ }
45885
+ acc[name] = Boolean(value);
45886
+ return acc;
45887
+ },
45888
+ {}
45889
+ ) : {};
45870
45890
  return SandboxCreateRequest.create({
45871
45891
  appId,
45872
45892
  definition: {
45873
- // Sleep default is implicit in image builder version <=2024.10
45874
- entrypointArgs: params.command ?? ["sleep", "48h"],
45893
+ entrypointArgs: params.command ?? [],
45875
45894
  imageId,
45876
- timeoutSecs: params.timeoutMs != void 0 ? params.timeoutMs / 1e3 : 600,
45895
+ timeoutSecs: params.timeoutMs != void 0 ? params.timeoutMs / 1e3 : 300,
45877
45896
  idleTimeoutSecs: params.idleTimeoutMs != void 0 ? params.idleTimeoutMs / 1e3 : void 0,
45878
45897
  workdir: params.workdir ?? void 0,
45879
45898
  networkAccess,
@@ -45893,7 +45912,8 @@ async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45893
45912
  schedulerPlacement,
45894
45913
  verbose: params.verbose ?? false,
45895
45914
  proxyId: params.proxy?.proxyId,
45896
- name: params.name
45915
+ name: params.name,
45916
+ experimentalOptions: protoExperimentalOptions
45897
45917
  }
45898
45918
  });
45899
45919
  }
@@ -46062,6 +46082,14 @@ function defaultSandboxPTYInfo() {
46062
46082
  }
46063
46083
  async function buildContainerExecRequestProto(taskId, command, params) {
46064
46084
  checkForRenamedParams(params, { timeout: "timeoutMs" });
46085
+ if (params?.timeoutMs != void 0 && params.timeoutMs <= 0) {
46086
+ throw new Error(`timeoutMs must be positive, got ${params.timeoutMs}`);
46087
+ }
46088
+ if (params?.timeoutMs && params.timeoutMs % 1e3 !== 0) {
46089
+ throw new Error(
46090
+ `timeoutMs must be a multiple of 1000ms, got ${params.timeoutMs}`
46091
+ );
46092
+ }
46065
46093
  const secretIds = (params?.secrets || []).map((secret) => secret.secretId);
46066
46094
  let ptyInfo;
46067
46095
  if (params?.pty) {
@@ -46227,6 +46255,16 @@ var Sandbox2 = class _Sandbox {
46227
46255
  }
46228
46256
  return this.#taskId;
46229
46257
  }
46258
+ /**
46259
+ * Create a token for making HTTP connections to the Sandbox.
46260
+ */
46261
+ async createConnectToken(params) {
46262
+ const resp = await this.#client.cpClient.sandboxCreateConnectToken({
46263
+ sandboxId: this.sandboxId,
46264
+ userMetadata: params?.userMetadata
46265
+ });
46266
+ return { url: resp.url, token: resp.token };
46267
+ }
46230
46268
  async terminate() {
46231
46269
  await this.#client.cpClient.sandboxTerminate({ sandboxId: this.sandboxId });
46232
46270
  this.#taskId = void 0;
@@ -46672,24 +46710,27 @@ var AuthTokenManager = class {
46672
46710
  logger;
46673
46711
  currentToken = "";
46674
46712
  tokenExpiry = 0;
46675
- stopped = false;
46676
46713
  timeoutId = null;
46677
- initialTokenPromise = null;
46714
+ running = false;
46715
+ fetchPromise = null;
46678
46716
  constructor(client2, logger) {
46679
46717
  this.client = client2;
46680
46718
  this.logger = logger;
46681
46719
  }
46682
46720
  /**
46683
- * Returns the current cached token.
46684
- * If the initial token fetch is still in progress, waits for it to complete.
46721
+ * Returns a valid auth token.
46722
+ * If the current token is expired and the manager is running, triggers an on-demand refresh.
46685
46723
  */
46686
46724
  async getToken() {
46687
- if (this.initialTokenPromise) {
46688
- await this.initialTokenPromise;
46689
- }
46690
46725
  if (this.currentToken && !this.isExpired()) {
46691
46726
  return this.currentToken;
46692
46727
  }
46728
+ if (this.running) {
46729
+ await this.runFetch();
46730
+ if (this.currentToken && !this.isExpired()) {
46731
+ return this.currentToken;
46732
+ }
46733
+ }
46693
46734
  throw new Error("No valid auth token available");
46694
46735
  }
46695
46736
  /**
@@ -46726,7 +46767,7 @@ var AuthTokenManager = class {
46726
46767
  * Background loop that refreshes tokens REFRESH_WINDOW seconds before they expire.
46727
46768
  */
46728
46769
  async backgroundRefresh() {
46729
- while (!this.stopped) {
46770
+ while (this.running) {
46730
46771
  const now = Math.floor(Date.now() / 1e3);
46731
46772
  const refreshTime = this.tokenExpiry - REFRESH_WINDOW;
46732
46773
  const delay = Math.max(0, refreshTime - now) * 1e3;
@@ -46734,11 +46775,11 @@ var AuthTokenManager = class {
46734
46775
  this.timeoutId = setTimeout(resolve, delay);
46735
46776
  this.timeoutId.unref();
46736
46777
  });
46737
- if (this.stopped) {
46778
+ if (!this.running) {
46738
46779
  return;
46739
46780
  }
46740
46781
  try {
46741
- await this.fetchToken();
46782
+ await this.runFetch();
46742
46783
  } catch (error) {
46743
46784
  this.logger.error("Failed to refresh auth token", "error", error);
46744
46785
  await new Promise((resolve) => setTimeout(resolve, 5e3));
@@ -46750,20 +46791,23 @@ var AuthTokenManager = class {
46750
46791
  * Throws an error if the initial token fetch fails.
46751
46792
  */
46752
46793
  async start() {
46753
- this.initialTokenPromise = this.fetchToken();
46794
+ if (this.running) {
46795
+ return;
46796
+ }
46797
+ this.running = true;
46754
46798
  try {
46755
- await this.initialTokenPromise;
46756
- } finally {
46757
- this.initialTokenPromise = null;
46799
+ await this.runFetch();
46800
+ } catch (error) {
46801
+ this.running = false;
46802
+ throw error;
46758
46803
  }
46759
- this.stopped = false;
46760
46804
  this.backgroundRefresh();
46761
46805
  }
46762
46806
  /**
46763
46807
  * Stops the background refresh.
46764
46808
  */
46765
46809
  stop() {
46766
- this.stopped = true;
46810
+ this.running = false;
46767
46811
  if (this.timeoutId) {
46768
46812
  clearTimeout(this.timeoutId);
46769
46813
  this.timeoutId = null;
@@ -46793,6 +46837,18 @@ var AuthTokenManager = class {
46793
46837
  const now = Math.floor(Date.now() / 1e3);
46794
46838
  return now >= this.tokenExpiry;
46795
46839
  }
46840
+ runFetch() {
46841
+ if (!this.fetchPromise) {
46842
+ this.fetchPromise = (async () => {
46843
+ try {
46844
+ await this.fetchToken();
46845
+ } finally {
46846
+ this.fetchPromise = null;
46847
+ }
46848
+ })();
46849
+ }
46850
+ return this.fetchPromise;
46851
+ }
46796
46852
  getCurrentToken() {
46797
46853
  return this.currentToken;
46798
46854
  }
@@ -46804,7 +46860,7 @@ var AuthTokenManager = class {
46804
46860
 
46805
46861
  // src/version.ts
46806
46862
  function getSDKVersion() {
46807
- return true ? "0.5.5" : "0.0.0";
46863
+ return true ? "0.6.0" : "0.0.0";
46808
46864
  }
46809
46865
 
46810
46866
  // src/logger.ts
package/dist/index.d.cts CHANGED
@@ -5712,7 +5712,7 @@ type SandboxCreateParams = {
5712
5712
  memoryLimitMiB?: number;
5713
5713
  /** GPU reservation for the Sandbox (e.g. "A100", "T4:2", "A100-80GB:4"). */
5714
5714
  gpu?: string;
5715
- /** Timeout of the Sandbox container in milliseconds, defaults to 10 minutes. */
5715
+ /** Maximum lifetime of the Sandbox in milliseconds. Defaults to 5 minutes. */
5716
5716
  timeoutMs?: number;
5717
5717
  /** The amount of time in milliseconds that a Sandbox can be idle before being terminated. */
5718
5718
  idleTimeoutMs?: number;
@@ -5753,6 +5753,8 @@ type SandboxCreateParams = {
5753
5753
  proxy?: Proxy;
5754
5754
  /** Optional name for the Sandbox. Unique within an App. */
5755
5755
  name?: string;
5756
+ /** Optional experimental options. */
5757
+ experimentalOptions?: Record<string, any>;
5756
5758
  };
5757
5759
  /**
5758
5760
  * Service for managing {@link Sandbox}es.
@@ -5824,6 +5826,16 @@ type SandboxExecParams = {
5824
5826
  /** Enable a PTY for the command. */
5825
5827
  pty?: boolean;
5826
5828
  };
5829
+ /** Optional parameters for {@link Sandbox#createConnectToken Sandbox.createConnectToken()}. */
5830
+ type SandboxCreateConnectTokenParams = {
5831
+ /** Optional user-provided metadata string that will be added to the headers by the proxy when forwarding requests to the Sandbox. */
5832
+ userMetadata?: string;
5833
+ };
5834
+ /** Credentials returned by {@link Sandbox#createConnectToken Sandbox.createConnectToken()}. */
5835
+ type SandboxCreateConnectCredentials = {
5836
+ url: string;
5837
+ token: string;
5838
+ };
5827
5839
  /** A port forwarded from within a running Modal {@link Sandbox}. */
5828
5840
  declare class Tunnel {
5829
5841
  host: string;
@@ -5873,6 +5885,10 @@ declare class Sandbox {
5873
5885
  exec(command: string[], params: SandboxExecParams & {
5874
5886
  mode: "binary";
5875
5887
  }): Promise<ContainerProcess<Uint8Array>>;
5888
+ /**
5889
+ * Create a token for making HTTP connections to the Sandbox.
5890
+ */
5891
+ createConnectToken(params?: SandboxCreateConnectTokenParams): Promise<SandboxCreateConnectCredentials>;
5876
5892
  terminate(): Promise<void>;
5877
5893
  wait(): Promise<number>;
5878
5894
  /** Get {@link Tunnel} metadata for the Sandbox.
@@ -6139,4 +6155,4 @@ declare class SandboxTimeoutError extends Error {
6139
6155
 
6140
6156
  declare function checkForRenamedParams(params: any, renames: Record<string, string>): void;
6141
6157
 
6142
- export { AlreadyExistsError, App, type AppFromNameParams, AppService, type ClientOptions, CloudBucketMount, CloudBucketMountService, Cls, type ClsFromNameParams, ClsInstance, ClsService, type ClsWithBatchingParams, type ClsWithConcurrencyParams, type ClsWithOptionsParams, ContainerProcess, type DeleteOptions, type EphemeralOptions, FunctionCall, type FunctionCallCancelParams, type FunctionCallGetParams, FunctionCallService, type FunctionFromNameParams, FunctionService, type FunctionStats, FunctionTimeoutError, type FunctionUpdateAutoscalerParams, Function_, Image, type ImageDeleteParams, type ImageDockerfileCommandsParams, ImageService, InternalFailure, InvalidError, type LogLevel, type Logger, type LookupOptions, ModalClient, type ModalClientParams, type ModalReadStream, type ModalWriteStream, NotFoundError, type Profile, Proxy, type ProxyFromNameParams, ProxyService, Queue, type QueueClearParams, type QueueDeleteParams, QueueEmptyError, type QueueEphemeralParams, type QueueFromNameParams, QueueFullError, type QueueGetParams, type QueueIterateParams, type QueueLenParams, type QueuePutParams, QueueService, RemoteError, Retries, Sandbox, type SandboxCreateParams, type SandboxExecParams, SandboxFile, type SandboxFileMode, type SandboxFromNameParams, type SandboxListParams, SandboxService, SandboxTimeoutError, Secret, type SecretDeleteParams, type SecretFromNameParams, type SecretFromObjectParams, SecretService, type StdioBehavior, type StreamMode, Tunnel, Volume, type VolumeDeleteParams, type VolumeEphemeralParams, type VolumeFromNameParams, VolumeService, checkForRenamedParams, close, initializeClient };
6158
+ export { AlreadyExistsError, App, type AppFromNameParams, AppService, type ClientOptions, CloudBucketMount, CloudBucketMountService, Cls, type ClsFromNameParams, ClsInstance, ClsService, type ClsWithBatchingParams, type ClsWithConcurrencyParams, type ClsWithOptionsParams, ContainerProcess, type DeleteOptions, type EphemeralOptions, FunctionCall, type FunctionCallCancelParams, type FunctionCallGetParams, FunctionCallService, type FunctionFromNameParams, FunctionService, type FunctionStats, FunctionTimeoutError, type FunctionUpdateAutoscalerParams, Function_, Image, type ImageDeleteParams, type ImageDockerfileCommandsParams, ImageService, InternalFailure, InvalidError, type LogLevel, type Logger, type LookupOptions, ModalClient, type ModalClientParams, type ModalReadStream, type ModalWriteStream, NotFoundError, type Profile, Proxy, type ProxyFromNameParams, ProxyService, Queue, type QueueClearParams, type QueueDeleteParams, QueueEmptyError, type QueueEphemeralParams, type QueueFromNameParams, QueueFullError, type QueueGetParams, type QueueIterateParams, type QueueLenParams, type QueuePutParams, QueueService, RemoteError, Retries, Sandbox, type SandboxCreateConnectCredentials, type SandboxCreateConnectTokenParams, type SandboxCreateParams, type SandboxExecParams, SandboxFile, type SandboxFileMode, type SandboxFromNameParams, type SandboxListParams, SandboxService, SandboxTimeoutError, Secret, type SecretDeleteParams, type SecretFromNameParams, type SecretFromObjectParams, SecretService, type StdioBehavior, type StreamMode, Tunnel, Volume, type VolumeDeleteParams, type VolumeEphemeralParams, type VolumeFromNameParams, VolumeService, checkForRenamedParams, close, initializeClient };
package/dist/index.d.ts CHANGED
@@ -5712,7 +5712,7 @@ type SandboxCreateParams = {
5712
5712
  memoryLimitMiB?: number;
5713
5713
  /** GPU reservation for the Sandbox (e.g. "A100", "T4:2", "A100-80GB:4"). */
5714
5714
  gpu?: string;
5715
- /** Timeout of the Sandbox container in milliseconds, defaults to 10 minutes. */
5715
+ /** Maximum lifetime of the Sandbox in milliseconds. Defaults to 5 minutes. */
5716
5716
  timeoutMs?: number;
5717
5717
  /** The amount of time in milliseconds that a Sandbox can be idle before being terminated. */
5718
5718
  idleTimeoutMs?: number;
@@ -5753,6 +5753,8 @@ type SandboxCreateParams = {
5753
5753
  proxy?: Proxy;
5754
5754
  /** Optional name for the Sandbox. Unique within an App. */
5755
5755
  name?: string;
5756
+ /** Optional experimental options. */
5757
+ experimentalOptions?: Record<string, any>;
5756
5758
  };
5757
5759
  /**
5758
5760
  * Service for managing {@link Sandbox}es.
@@ -5824,6 +5826,16 @@ type SandboxExecParams = {
5824
5826
  /** Enable a PTY for the command. */
5825
5827
  pty?: boolean;
5826
5828
  };
5829
+ /** Optional parameters for {@link Sandbox#createConnectToken Sandbox.createConnectToken()}. */
5830
+ type SandboxCreateConnectTokenParams = {
5831
+ /** Optional user-provided metadata string that will be added to the headers by the proxy when forwarding requests to the Sandbox. */
5832
+ userMetadata?: string;
5833
+ };
5834
+ /** Credentials returned by {@link Sandbox#createConnectToken Sandbox.createConnectToken()}. */
5835
+ type SandboxCreateConnectCredentials = {
5836
+ url: string;
5837
+ token: string;
5838
+ };
5827
5839
  /** A port forwarded from within a running Modal {@link Sandbox}. */
5828
5840
  declare class Tunnel {
5829
5841
  host: string;
@@ -5873,6 +5885,10 @@ declare class Sandbox {
5873
5885
  exec(command: string[], params: SandboxExecParams & {
5874
5886
  mode: "binary";
5875
5887
  }): Promise<ContainerProcess<Uint8Array>>;
5888
+ /**
5889
+ * Create a token for making HTTP connections to the Sandbox.
5890
+ */
5891
+ createConnectToken(params?: SandboxCreateConnectTokenParams): Promise<SandboxCreateConnectCredentials>;
5876
5892
  terminate(): Promise<void>;
5877
5893
  wait(): Promise<number>;
5878
5894
  /** Get {@link Tunnel} metadata for the Sandbox.
@@ -6139,4 +6155,4 @@ declare class SandboxTimeoutError extends Error {
6139
6155
 
6140
6156
  declare function checkForRenamedParams(params: any, renames: Record<string, string>): void;
6141
6157
 
6142
- export { AlreadyExistsError, App, type AppFromNameParams, AppService, type ClientOptions, CloudBucketMount, CloudBucketMountService, Cls, type ClsFromNameParams, ClsInstance, ClsService, type ClsWithBatchingParams, type ClsWithConcurrencyParams, type ClsWithOptionsParams, ContainerProcess, type DeleteOptions, type EphemeralOptions, FunctionCall, type FunctionCallCancelParams, type FunctionCallGetParams, FunctionCallService, type FunctionFromNameParams, FunctionService, type FunctionStats, FunctionTimeoutError, type FunctionUpdateAutoscalerParams, Function_, Image, type ImageDeleteParams, type ImageDockerfileCommandsParams, ImageService, InternalFailure, InvalidError, type LogLevel, type Logger, type LookupOptions, ModalClient, type ModalClientParams, type ModalReadStream, type ModalWriteStream, NotFoundError, type Profile, Proxy, type ProxyFromNameParams, ProxyService, Queue, type QueueClearParams, type QueueDeleteParams, QueueEmptyError, type QueueEphemeralParams, type QueueFromNameParams, QueueFullError, type QueueGetParams, type QueueIterateParams, type QueueLenParams, type QueuePutParams, QueueService, RemoteError, Retries, Sandbox, type SandboxCreateParams, type SandboxExecParams, SandboxFile, type SandboxFileMode, type SandboxFromNameParams, type SandboxListParams, SandboxService, SandboxTimeoutError, Secret, type SecretDeleteParams, type SecretFromNameParams, type SecretFromObjectParams, SecretService, type StdioBehavior, type StreamMode, Tunnel, Volume, type VolumeDeleteParams, type VolumeEphemeralParams, type VolumeFromNameParams, VolumeService, checkForRenamedParams, close, initializeClient };
6158
+ export { AlreadyExistsError, App, type AppFromNameParams, AppService, type ClientOptions, CloudBucketMount, CloudBucketMountService, Cls, type ClsFromNameParams, ClsInstance, ClsService, type ClsWithBatchingParams, type ClsWithConcurrencyParams, type ClsWithOptionsParams, ContainerProcess, type DeleteOptions, type EphemeralOptions, FunctionCall, type FunctionCallCancelParams, type FunctionCallGetParams, FunctionCallService, type FunctionFromNameParams, FunctionService, type FunctionStats, FunctionTimeoutError, type FunctionUpdateAutoscalerParams, Function_, Image, type ImageDeleteParams, type ImageDockerfileCommandsParams, ImageService, InternalFailure, InvalidError, type LogLevel, type Logger, type LookupOptions, ModalClient, type ModalClientParams, type ModalReadStream, type ModalWriteStream, NotFoundError, type Profile, Proxy, type ProxyFromNameParams, ProxyService, Queue, type QueueClearParams, type QueueDeleteParams, QueueEmptyError, type QueueEphemeralParams, type QueueFromNameParams, QueueFullError, type QueueGetParams, type QueueIterateParams, type QueueLenParams, type QueuePutParams, QueueService, RemoteError, Retries, Sandbox, type SandboxCreateConnectCredentials, type SandboxCreateConnectTokenParams, type SandboxCreateParams, type SandboxExecParams, SandboxFile, type SandboxFileMode, type SandboxFromNameParams, type SandboxListParams, SandboxService, SandboxTimeoutError, Secret, type SecretDeleteParams, type SecretFromNameParams, type SecretFromObjectParams, SecretService, type StdioBehavior, type StreamMode, Tunnel, Volume, type VolumeDeleteParams, type VolumeEphemeralParams, type VolumeFromNameParams, VolumeService, checkForRenamedParams, close, initializeClient };
package/dist/index.js CHANGED
@@ -45673,11 +45673,19 @@ async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45673
45673
  idleTimeout: "idleTimeoutMs"
45674
45674
  });
45675
45675
  const gpuConfig = parseGpuConfig(params.gpu);
45676
+ if (params.timeoutMs != void 0 && params.timeoutMs <= 0) {
45677
+ throw new Error(`timeoutMs must be positive, got ${params.timeoutMs}`);
45678
+ }
45676
45679
  if (params.timeoutMs && params.timeoutMs % 1e3 !== 0) {
45677
45680
  throw new Error(
45678
45681
  `timeoutMs must be a multiple of 1000ms, got ${params.timeoutMs}`
45679
45682
  );
45680
45683
  }
45684
+ if (params.idleTimeoutMs != void 0 && params.idleTimeoutMs <= 0) {
45685
+ throw new Error(
45686
+ `idleTimeoutMs must be positive, got ${params.idleTimeoutMs}`
45687
+ );
45688
+ }
45681
45689
  if (params.idleTimeoutMs && params.idleTimeoutMs % 1e3 !== 0) {
45682
45690
  throw new Error(
45683
45691
  `idleTimeoutMs must be a multiple of 1000ms, got ${params.idleTimeoutMs}`
@@ -45799,13 +45807,24 @@ async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45799
45807
  memoryMbMax = params.memoryLimitMiB;
45800
45808
  }
45801
45809
  }
45810
+ const protoExperimentalOptions = params.experimentalOptions ? Object.entries(params.experimentalOptions).reduce(
45811
+ (acc, [name, value]) => {
45812
+ if (typeof value !== "boolean") {
45813
+ throw new Error(
45814
+ `experimental option '${name}' must be a boolean, got ${value}`
45815
+ );
45816
+ }
45817
+ acc[name] = Boolean(value);
45818
+ return acc;
45819
+ },
45820
+ {}
45821
+ ) : {};
45802
45822
  return SandboxCreateRequest.create({
45803
45823
  appId,
45804
45824
  definition: {
45805
- // Sleep default is implicit in image builder version <=2024.10
45806
- entrypointArgs: params.command ?? ["sleep", "48h"],
45825
+ entrypointArgs: params.command ?? [],
45807
45826
  imageId,
45808
- timeoutSecs: params.timeoutMs != void 0 ? params.timeoutMs / 1e3 : 600,
45827
+ timeoutSecs: params.timeoutMs != void 0 ? params.timeoutMs / 1e3 : 300,
45809
45828
  idleTimeoutSecs: params.idleTimeoutMs != void 0 ? params.idleTimeoutMs / 1e3 : void 0,
45810
45829
  workdir: params.workdir ?? void 0,
45811
45830
  networkAccess,
@@ -45825,7 +45844,8 @@ async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45825
45844
  schedulerPlacement,
45826
45845
  verbose: params.verbose ?? false,
45827
45846
  proxyId: params.proxy?.proxyId,
45828
- name: params.name
45847
+ name: params.name,
45848
+ experimentalOptions: protoExperimentalOptions
45829
45849
  }
45830
45850
  });
45831
45851
  }
@@ -45994,6 +46014,14 @@ function defaultSandboxPTYInfo() {
45994
46014
  }
45995
46015
  async function buildContainerExecRequestProto(taskId, command, params) {
45996
46016
  checkForRenamedParams(params, { timeout: "timeoutMs" });
46017
+ if (params?.timeoutMs != void 0 && params.timeoutMs <= 0) {
46018
+ throw new Error(`timeoutMs must be positive, got ${params.timeoutMs}`);
46019
+ }
46020
+ if (params?.timeoutMs && params.timeoutMs % 1e3 !== 0) {
46021
+ throw new Error(
46022
+ `timeoutMs must be a multiple of 1000ms, got ${params.timeoutMs}`
46023
+ );
46024
+ }
45997
46025
  const secretIds = (params?.secrets || []).map((secret) => secret.secretId);
45998
46026
  let ptyInfo;
45999
46027
  if (params?.pty) {
@@ -46159,6 +46187,16 @@ var Sandbox2 = class _Sandbox {
46159
46187
  }
46160
46188
  return this.#taskId;
46161
46189
  }
46190
+ /**
46191
+ * Create a token for making HTTP connections to the Sandbox.
46192
+ */
46193
+ async createConnectToken(params) {
46194
+ const resp = await this.#client.cpClient.sandboxCreateConnectToken({
46195
+ sandboxId: this.sandboxId,
46196
+ userMetadata: params?.userMetadata
46197
+ });
46198
+ return { url: resp.url, token: resp.token };
46199
+ }
46162
46200
  async terminate() {
46163
46201
  await this.#client.cpClient.sandboxTerminate({ sandboxId: this.sandboxId });
46164
46202
  this.#taskId = void 0;
@@ -46604,24 +46642,27 @@ var AuthTokenManager = class {
46604
46642
  logger;
46605
46643
  currentToken = "";
46606
46644
  tokenExpiry = 0;
46607
- stopped = false;
46608
46645
  timeoutId = null;
46609
- initialTokenPromise = null;
46646
+ running = false;
46647
+ fetchPromise = null;
46610
46648
  constructor(client2, logger) {
46611
46649
  this.client = client2;
46612
46650
  this.logger = logger;
46613
46651
  }
46614
46652
  /**
46615
- * Returns the current cached token.
46616
- * If the initial token fetch is still in progress, waits for it to complete.
46653
+ * Returns a valid auth token.
46654
+ * If the current token is expired and the manager is running, triggers an on-demand refresh.
46617
46655
  */
46618
46656
  async getToken() {
46619
- if (this.initialTokenPromise) {
46620
- await this.initialTokenPromise;
46621
- }
46622
46657
  if (this.currentToken && !this.isExpired()) {
46623
46658
  return this.currentToken;
46624
46659
  }
46660
+ if (this.running) {
46661
+ await this.runFetch();
46662
+ if (this.currentToken && !this.isExpired()) {
46663
+ return this.currentToken;
46664
+ }
46665
+ }
46625
46666
  throw new Error("No valid auth token available");
46626
46667
  }
46627
46668
  /**
@@ -46658,7 +46699,7 @@ var AuthTokenManager = class {
46658
46699
  * Background loop that refreshes tokens REFRESH_WINDOW seconds before they expire.
46659
46700
  */
46660
46701
  async backgroundRefresh() {
46661
- while (!this.stopped) {
46702
+ while (this.running) {
46662
46703
  const now = Math.floor(Date.now() / 1e3);
46663
46704
  const refreshTime = this.tokenExpiry - REFRESH_WINDOW;
46664
46705
  const delay = Math.max(0, refreshTime - now) * 1e3;
@@ -46666,11 +46707,11 @@ var AuthTokenManager = class {
46666
46707
  this.timeoutId = setTimeout(resolve, delay);
46667
46708
  this.timeoutId.unref();
46668
46709
  });
46669
- if (this.stopped) {
46710
+ if (!this.running) {
46670
46711
  return;
46671
46712
  }
46672
46713
  try {
46673
- await this.fetchToken();
46714
+ await this.runFetch();
46674
46715
  } catch (error) {
46675
46716
  this.logger.error("Failed to refresh auth token", "error", error);
46676
46717
  await new Promise((resolve) => setTimeout(resolve, 5e3));
@@ -46682,20 +46723,23 @@ var AuthTokenManager = class {
46682
46723
  * Throws an error if the initial token fetch fails.
46683
46724
  */
46684
46725
  async start() {
46685
- this.initialTokenPromise = this.fetchToken();
46726
+ if (this.running) {
46727
+ return;
46728
+ }
46729
+ this.running = true;
46686
46730
  try {
46687
- await this.initialTokenPromise;
46688
- } finally {
46689
- this.initialTokenPromise = null;
46731
+ await this.runFetch();
46732
+ } catch (error) {
46733
+ this.running = false;
46734
+ throw error;
46690
46735
  }
46691
- this.stopped = false;
46692
46736
  this.backgroundRefresh();
46693
46737
  }
46694
46738
  /**
46695
46739
  * Stops the background refresh.
46696
46740
  */
46697
46741
  stop() {
46698
- this.stopped = true;
46742
+ this.running = false;
46699
46743
  if (this.timeoutId) {
46700
46744
  clearTimeout(this.timeoutId);
46701
46745
  this.timeoutId = null;
@@ -46725,6 +46769,18 @@ var AuthTokenManager = class {
46725
46769
  const now = Math.floor(Date.now() / 1e3);
46726
46770
  return now >= this.tokenExpiry;
46727
46771
  }
46772
+ runFetch() {
46773
+ if (!this.fetchPromise) {
46774
+ this.fetchPromise = (async () => {
46775
+ try {
46776
+ await this.fetchToken();
46777
+ } finally {
46778
+ this.fetchPromise = null;
46779
+ }
46780
+ })();
46781
+ }
46782
+ return this.fetchPromise;
46783
+ }
46728
46784
  getCurrentToken() {
46729
46785
  return this.currentToken;
46730
46786
  }
@@ -46736,7 +46792,7 @@ var AuthTokenManager = class {
46736
46792
 
46737
46793
  // src/version.ts
46738
46794
  function getSDKVersion() {
46739
- return true ? "0.5.5" : "0.0.0";
46795
+ return true ? "0.6.0" : "0.0.0";
46740
46796
  }
46741
46797
 
46742
46798
  // src/logger.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modal",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
4
4
  "description": "Modal SDK for JavaScript/TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://modal.com/docs/guide/sdk-javascript-go",