modal 0.5.4 → 0.5.6

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
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  App: () => App2,
35
35
  AppService: () => AppService,
36
36
  CloudBucketMount: () => CloudBucketMount2,
37
+ CloudBucketMountService: () => CloudBucketMountService,
37
38
  Cls: () => Cls,
38
39
  ClsInstance: () => ClsInstance,
39
40
  ClsService: () => ClsService,
@@ -47,7 +48,7 @@ __export(index_exports, {
47
48
  ImageService: () => ImageService,
48
49
  InternalFailure: () => InternalFailure,
49
50
  InvalidError: () => InvalidError,
50
- ModalClient: () => ModalClient,
51
+ ModalClient: () => ModalClient2,
51
52
  NotFoundError: () => NotFoundError,
52
53
  Proxy: () => Proxy3,
53
54
  ProxyService: () => ProxyService,
@@ -43324,6 +43325,89 @@ function isSet4(value) {
43324
43325
  var import_uuid = require("uuid");
43325
43326
  var import_nice_grpc10 = require("nice-grpc");
43326
43327
 
43328
+ // src/cloud_bucket_mount.ts
43329
+ var CloudBucketMountService = class {
43330
+ #client;
43331
+ constructor(client2) {
43332
+ this.#client = client2;
43333
+ }
43334
+ create(bucketName, params = {}) {
43335
+ let bucketType = 1 /* S3 */;
43336
+ if (params.bucketEndpointUrl) {
43337
+ const url = new URL(params.bucketEndpointUrl);
43338
+ if (url.hostname.endsWith("r2.cloudflarestorage.com")) {
43339
+ bucketType = 2 /* R2 */;
43340
+ } else if (url.hostname.endsWith("storage.googleapis.com")) {
43341
+ bucketType = 3 /* GCP */;
43342
+ } else {
43343
+ bucketType = 1 /* S3 */;
43344
+ this.#client.logger.debug(
43345
+ "CloudBucketMount received unrecognized bucket endpoint URL. Assuming AWS S3 configuration as fallback.",
43346
+ "bucketEndpointUrl",
43347
+ params.bucketEndpointUrl
43348
+ );
43349
+ }
43350
+ }
43351
+ if (params.requesterPays && !params.secret) {
43352
+ throw new Error("Credentials required in order to use Requester Pays.");
43353
+ }
43354
+ if (params.keyPrefix && !params.keyPrefix.endsWith("/")) {
43355
+ throw new Error(
43356
+ "keyPrefix will be prefixed to all object paths, so it must end in a '/'"
43357
+ );
43358
+ }
43359
+ return new CloudBucketMount2(
43360
+ bucketName,
43361
+ params.secret,
43362
+ params.readOnly ?? false,
43363
+ params.requesterPays ?? false,
43364
+ params.bucketEndpointUrl,
43365
+ params.keyPrefix,
43366
+ params.oidcAuthRoleArn,
43367
+ bucketType
43368
+ );
43369
+ }
43370
+ };
43371
+ var CloudBucketMount2 = class {
43372
+ bucketName;
43373
+ secret;
43374
+ readOnly;
43375
+ requesterPays;
43376
+ bucketEndpointUrl;
43377
+ keyPrefix;
43378
+ oidcAuthRoleArn;
43379
+ #bucketType;
43380
+ constructor(bucketName, secretOrParams, readOnly, requesterPays, bucketEndpointUrl, keyPrefix, oidcAuthRoleArn, bucketType) {
43381
+ if (bucketType !== void 0) {
43382
+ this.bucketName = bucketName;
43383
+ this.secret = secretOrParams;
43384
+ this.readOnly = readOnly;
43385
+ this.requesterPays = requesterPays;
43386
+ this.bucketEndpointUrl = bucketEndpointUrl;
43387
+ this.keyPrefix = keyPrefix;
43388
+ this.oidcAuthRoleArn = oidcAuthRoleArn;
43389
+ this.#bucketType = bucketType;
43390
+ } else {
43391
+ const params = secretOrParams === void 0 ? {} : secretOrParams;
43392
+ return getDefaultClient().cloudBucketMounts.create(bucketName, params);
43393
+ }
43394
+ }
43395
+ /** @ignore */
43396
+ toProto(mountPath) {
43397
+ return {
43398
+ bucketName: this.bucketName,
43399
+ mountPath,
43400
+ credentialsSecretId: this.secret?.secretId ?? "",
43401
+ readOnly: this.readOnly,
43402
+ bucketType: this.#bucketType,
43403
+ requesterPays: this.requesterPays,
43404
+ bucketEndpointUrl: this.bucketEndpointUrl,
43405
+ keyPrefix: this.keyPrefix,
43406
+ oidcAuthRoleArn: this.oidcAuthRoleArn
43407
+ };
43408
+ }
43409
+ };
43410
+
43327
43411
  // src/cls.ts
43328
43412
  var import_nice_grpc3 = require("nice-grpc");
43329
43413
 
@@ -44224,7 +44308,8 @@ var Cls = class _Cls {
44224
44308
  const bindResp = await this.#client.cpClient.functionBindParams({
44225
44309
  functionId: this.#serviceFunctionId,
44226
44310
  serializedParams,
44227
- functionOptions
44311
+ functionOptions,
44312
+ environmentName: this.#client.environmentName()
44228
44313
  });
44229
44314
  return bindResp.boundFunctionId;
44230
44315
  }
@@ -45647,68 +45732,6 @@ async function consumeIterator(iter) {
45647
45732
  }
45648
45733
  }
45649
45734
 
45650
- // src/cloud_bucket_mount.ts
45651
- var CloudBucketMount2 = class {
45652
- bucketName;
45653
- secret;
45654
- readOnly;
45655
- requesterPays;
45656
- bucketEndpointUrl;
45657
- keyPrefix;
45658
- oidcAuthRoleArn;
45659
- constructor(bucketName, params = {}) {
45660
- this.bucketName = bucketName;
45661
- this.secret = params.secret;
45662
- this.readOnly = params.readOnly ?? false;
45663
- this.requesterPays = params.requesterPays ?? false;
45664
- this.bucketEndpointUrl = params.bucketEndpointUrl;
45665
- this.keyPrefix = params.keyPrefix;
45666
- this.oidcAuthRoleArn = params.oidcAuthRoleArn;
45667
- if (this.bucketEndpointUrl) {
45668
- const url = new URL(this.bucketEndpointUrl);
45669
- if (!url.hostname.endsWith("r2.cloudflarestorage.com") && !url.hostname.endsWith("storage.googleapis.com")) {
45670
- console.warn(
45671
- "CloudBucketMount received unrecognized bucket endpoint URL. Assuming AWS S3 configuration as fallback."
45672
- );
45673
- }
45674
- }
45675
- if (this.requesterPays && !this.secret) {
45676
- throw new Error("Credentials required in order to use Requester Pays.");
45677
- }
45678
- if (this.keyPrefix && !this.keyPrefix.endsWith("/")) {
45679
- throw new Error(
45680
- "keyPrefix will be prefixed to all object paths, so it must end in a '/'"
45681
- );
45682
- }
45683
- }
45684
- };
45685
- function endpointUrlToBucketType(bucketEndpointUrl) {
45686
- if (!bucketEndpointUrl) {
45687
- return 1 /* S3 */;
45688
- }
45689
- const url = new URL(bucketEndpointUrl);
45690
- if (url.hostname.endsWith("r2.cloudflarestorage.com")) {
45691
- return 2 /* R2 */;
45692
- } else if (url.hostname.endsWith("storage.googleapis.com")) {
45693
- return 3 /* GCP */;
45694
- } else {
45695
- return 1 /* S3 */;
45696
- }
45697
- }
45698
- function cloudBucketMountToProto(mount, mountPath) {
45699
- return {
45700
- bucketName: mount.bucketName,
45701
- mountPath,
45702
- credentialsSecretId: mount.secret?.secretId ?? "",
45703
- readOnly: mount.readOnly,
45704
- bucketType: endpointUrlToBucketType(mount.bucketEndpointUrl),
45705
- requesterPays: mount.requesterPays,
45706
- bucketEndpointUrl: mount.bucketEndpointUrl,
45707
- keyPrefix: mount.keyPrefix,
45708
- oidcAuthRoleArn: mount.oidcAuthRoleArn
45709
- };
45710
- }
45711
-
45712
45735
  // src/sandbox.ts
45713
45736
  async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45714
45737
  checkForRenamedParams(params, {
@@ -45738,32 +45761,38 @@ async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45738
45761
  readOnly: volume.isReadOnly
45739
45762
  })) : [];
45740
45763
  const cloudBucketMounts = params.cloudBucketMounts ? Object.entries(params.cloudBucketMounts).map(
45741
- ([mountPath, mount]) => cloudBucketMountToProto(mount, mountPath)
45764
+ ([mountPath, mount]) => mount.toProto(mountPath)
45742
45765
  ) : [];
45743
45766
  const openPorts = [];
45744
45767
  if (params.encryptedPorts) {
45745
45768
  openPorts.push(
45746
- ...params.encryptedPorts.map((port) => ({
45747
- port,
45748
- unencrypted: false
45749
- }))
45769
+ ...params.encryptedPorts.map(
45770
+ (port) => PortSpec.create({
45771
+ port,
45772
+ unencrypted: false
45773
+ })
45774
+ )
45750
45775
  );
45751
45776
  }
45752
45777
  if (params.h2Ports) {
45753
45778
  openPorts.push(
45754
- ...params.h2Ports.map((port) => ({
45755
- port,
45756
- unencrypted: false,
45757
- tunnelType: 1 /* TUNNEL_TYPE_H2 */
45758
- }))
45779
+ ...params.h2Ports.map(
45780
+ (port) => PortSpec.create({
45781
+ port,
45782
+ unencrypted: false,
45783
+ tunnelType: 1 /* TUNNEL_TYPE_H2 */
45784
+ })
45785
+ )
45759
45786
  );
45760
45787
  }
45761
45788
  if (params.unencryptedPorts) {
45762
45789
  openPorts.push(
45763
- ...params.unencryptedPorts.map((port) => ({
45764
- port,
45765
- unencrypted: true
45766
- }))
45790
+ ...params.unencryptedPorts.map(
45791
+ (port) => PortSpec.create({
45792
+ port,
45793
+ unencrypted: true
45794
+ })
45795
+ )
45767
45796
  );
45768
45797
  }
45769
45798
  const secretIds = (params.secrets || []).map((secret) => secret.secretId);
@@ -45789,9 +45818,9 @@ async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45789
45818
  allowedCidrs: []
45790
45819
  };
45791
45820
  }
45792
- const schedulerPlacement = SchedulerPlacement.create({
45793
- regions: params.regions ?? []
45794
- });
45821
+ const schedulerPlacement = params.regions?.length ? SchedulerPlacement.create({
45822
+ regions: params.regions
45823
+ }) : void 0;
45795
45824
  let ptyInfo;
45796
45825
  if (params.pty) {
45797
45826
  ptyInfo = defaultSandboxPTYInfo();
@@ -45848,18 +45877,18 @@ async function buildSandboxCreateRequestProto(appId, imageId, params = {}) {
45848
45877
  idleTimeoutSecs: params.idleTimeoutMs != void 0 ? params.idleTimeoutMs / 1e3 : void 0,
45849
45878
  workdir: params.workdir ?? void 0,
45850
45879
  networkAccess,
45851
- resources: {
45880
+ resources: Resources.create({
45852
45881
  milliCpu,
45853
45882
  milliCpuMax,
45854
45883
  memoryMb,
45855
45884
  memoryMbMax,
45856
45885
  gpuConfig
45857
- },
45886
+ }),
45858
45887
  volumeMounts,
45859
45888
  cloudBucketMounts,
45860
45889
  ptyInfo,
45861
45890
  secretIds,
45862
- openPorts: openPorts.length > 0 ? { ports: openPorts } : void 0,
45891
+ openPorts: PortSpecs.create({ ports: openPorts }),
45863
45892
  cloudProviderStr: params.cloud ?? "",
45864
45893
  schedulerPlacement,
45865
45894
  verbose: params.verbose ?? false,
@@ -46198,6 +46227,16 @@ var Sandbox2 = class _Sandbox {
46198
46227
  }
46199
46228
  return this.#taskId;
46200
46229
  }
46230
+ /**
46231
+ * Create a token for making HTTP connections to the Sandbox.
46232
+ */
46233
+ async createConnectToken(params) {
46234
+ const resp = await this.#client.cpClient.sandboxCreateConnectToken({
46235
+ sandboxId: this.sandboxId,
46236
+ userMetadata: params?.userMetadata
46237
+ });
46238
+ return { url: resp.url, token: resp.token };
46239
+ }
46201
46240
  async terminate() {
46202
46241
  await this.#client.cpClient.sandboxTerminate({ sandboxId: this.sandboxId });
46203
46242
  this.#taskId = void 0;
@@ -46643,24 +46682,27 @@ var AuthTokenManager = class {
46643
46682
  logger;
46644
46683
  currentToken = "";
46645
46684
  tokenExpiry = 0;
46646
- stopped = false;
46647
46685
  timeoutId = null;
46648
- initialTokenPromise = null;
46686
+ running = false;
46687
+ fetchPromise = null;
46649
46688
  constructor(client2, logger) {
46650
46689
  this.client = client2;
46651
46690
  this.logger = logger;
46652
46691
  }
46653
46692
  /**
46654
- * Returns the current cached token.
46655
- * If the initial token fetch is still in progress, waits for it to complete.
46693
+ * Returns a valid auth token.
46694
+ * If the current token is expired and the manager is running, triggers an on-demand refresh.
46656
46695
  */
46657
46696
  async getToken() {
46658
- if (this.initialTokenPromise) {
46659
- await this.initialTokenPromise;
46660
- }
46661
46697
  if (this.currentToken && !this.isExpired()) {
46662
46698
  return this.currentToken;
46663
46699
  }
46700
+ if (this.running) {
46701
+ await this.runFetch();
46702
+ if (this.currentToken && !this.isExpired()) {
46703
+ return this.currentToken;
46704
+ }
46705
+ }
46664
46706
  throw new Error("No valid auth token available");
46665
46707
  }
46666
46708
  /**
@@ -46697,7 +46739,7 @@ var AuthTokenManager = class {
46697
46739
  * Background loop that refreshes tokens REFRESH_WINDOW seconds before they expire.
46698
46740
  */
46699
46741
  async backgroundRefresh() {
46700
- while (!this.stopped) {
46742
+ while (this.running) {
46701
46743
  const now = Math.floor(Date.now() / 1e3);
46702
46744
  const refreshTime = this.tokenExpiry - REFRESH_WINDOW;
46703
46745
  const delay = Math.max(0, refreshTime - now) * 1e3;
@@ -46705,11 +46747,11 @@ var AuthTokenManager = class {
46705
46747
  this.timeoutId = setTimeout(resolve, delay);
46706
46748
  this.timeoutId.unref();
46707
46749
  });
46708
- if (this.stopped) {
46750
+ if (!this.running) {
46709
46751
  return;
46710
46752
  }
46711
46753
  try {
46712
- await this.fetchToken();
46754
+ await this.runFetch();
46713
46755
  } catch (error) {
46714
46756
  this.logger.error("Failed to refresh auth token", "error", error);
46715
46757
  await new Promise((resolve) => setTimeout(resolve, 5e3));
@@ -46721,20 +46763,23 @@ var AuthTokenManager = class {
46721
46763
  * Throws an error if the initial token fetch fails.
46722
46764
  */
46723
46765
  async start() {
46724
- this.initialTokenPromise = this.fetchToken();
46766
+ if (this.running) {
46767
+ return;
46768
+ }
46769
+ this.running = true;
46725
46770
  try {
46726
- await this.initialTokenPromise;
46727
- } finally {
46728
- this.initialTokenPromise = null;
46771
+ await this.runFetch();
46772
+ } catch (error) {
46773
+ this.running = false;
46774
+ throw error;
46729
46775
  }
46730
- this.stopped = false;
46731
46776
  this.backgroundRefresh();
46732
46777
  }
46733
46778
  /**
46734
46779
  * Stops the background refresh.
46735
46780
  */
46736
46781
  stop() {
46737
- this.stopped = true;
46782
+ this.running = false;
46738
46783
  if (this.timeoutId) {
46739
46784
  clearTimeout(this.timeoutId);
46740
46785
  this.timeoutId = null;
@@ -46764,6 +46809,18 @@ var AuthTokenManager = class {
46764
46809
  const now = Math.floor(Date.now() / 1e3);
46765
46810
  return now >= this.tokenExpiry;
46766
46811
  }
46812
+ runFetch() {
46813
+ if (!this.fetchPromise) {
46814
+ this.fetchPromise = (async () => {
46815
+ try {
46816
+ await this.fetchToken();
46817
+ } finally {
46818
+ this.fetchPromise = null;
46819
+ }
46820
+ })();
46821
+ }
46822
+ return this.fetchPromise;
46823
+ }
46767
46824
  getCurrentToken() {
46768
46825
  return this.currentToken;
46769
46826
  }
@@ -46775,7 +46832,7 @@ var AuthTokenManager = class {
46775
46832
 
46776
46833
  // src/version.ts
46777
46834
  function getSDKVersion() {
46778
- return true ? "0.5.4" : "0.0.0";
46835
+ return true ? "0.5.6" : "0.0.0";
46779
46836
  }
46780
46837
 
46781
46838
  // src/logger.ts
@@ -46885,8 +46942,9 @@ function createLogger(logger, logLevel = "") {
46885
46942
  }
46886
46943
 
46887
46944
  // src/client.ts
46888
- var ModalClient = class {
46945
+ var ModalClient2 = class {
46889
46946
  apps;
46947
+ cloudBucketMounts;
46890
46948
  cls;
46891
46949
  functions;
46892
46950
  functionCalls;
@@ -46926,6 +46984,7 @@ var ModalClient = class {
46926
46984
  this.cpClient = params?.cpClient ?? this.createClient(this.profile);
46927
46985
  this.logger.debug("Modal client initialized successfully");
46928
46986
  this.apps = new AppService(this);
46987
+ this.cloudBucketMounts = new CloudBucketMountService(this);
46929
46988
  this.cls = new ClsService(this);
46930
46989
  this.functions = new FunctionService(this);
46931
46990
  this.functionCalls = new FunctionCallService(this);
@@ -47159,7 +47218,7 @@ var defaultClient;
47159
47218
  var defaultClientOptions;
47160
47219
  function getDefaultClient() {
47161
47220
  if (!defaultClient) {
47162
- defaultClient = new ModalClient(defaultClientOptions);
47221
+ defaultClient = new ModalClient2(defaultClientOptions);
47163
47222
  }
47164
47223
  return defaultClient;
47165
47224
  }
@@ -47174,7 +47233,7 @@ function initializeClient(options) {
47174
47233
  tokenSecret: options.tokenSecret,
47175
47234
  environment: options.environment
47176
47235
  };
47177
- defaultClient = new ModalClient(defaultClientOptions);
47236
+ defaultClient = new ModalClient2(defaultClientOptions);
47178
47237
  }
47179
47238
  function close() {
47180
47239
  if (defaultClient) {
@@ -47215,7 +47274,7 @@ var AppService = class {
47215
47274
  };
47216
47275
  function parseGpuConfig(gpu) {
47217
47276
  if (!gpu) {
47218
- return void 0;
47277
+ return GPUConfig.create({});
47219
47278
  }
47220
47279
  let gpuType = gpu;
47221
47280
  let count = 1;
@@ -47229,12 +47288,10 @@ function parseGpuConfig(gpu) {
47229
47288
  );
47230
47289
  }
47231
47290
  }
47232
- return {
47233
- type: 0,
47234
- // Deprecated field, but required by proto
47291
+ return GPUConfig.create({
47235
47292
  count,
47236
47293
  gpuType: gpuType.toUpperCase()
47237
- };
47294
+ });
47238
47295
  }
47239
47296
  var App2 = class {
47240
47297
  appId;
@@ -47247,6 +47304,7 @@ var App2 = class {
47247
47304
  /**
47248
47305
  * @deprecated Use {@link AppService#fromName client.apps.fromName()} instead.
47249
47306
  */
47307
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
47250
47308
  static async lookup(name, options = {}) {
47251
47309
  return getDefaultClient().apps.fromName(name, options);
47252
47310
  }
@@ -47281,6 +47339,7 @@ var App2 = class {
47281
47339
  App,
47282
47340
  AppService,
47283
47341
  CloudBucketMount,
47342
+ CloudBucketMountService,
47284
47343
  Cls,
47285
47344
  ClsInstance,
47286
47345
  ClsService,