modal 0.3.20 → 0.3.22

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.cjs CHANGED
@@ -31,7 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AlreadyExistsError: () => AlreadyExistsError,
34
- App: () => App,
34
+ App: () => App2,
35
35
  CloudBucketMount: () => CloudBucketMount2,
36
36
  Cls: () => Cls,
37
37
  ClsInstance: () => ClsInstance,
@@ -6520,6 +6520,104 @@ var AutoscalerSettings = {
6520
6520
  return message;
6521
6521
  }
6522
6522
  };
6523
+ function createBaseAutoscalingMetrics() {
6524
+ return { cpuUsagePercent: 0, memoryUsagePercent: 0, concurrentRequests: 0, timestamp: 0 };
6525
+ }
6526
+ var AutoscalingMetrics = {
6527
+ encode(message, writer = new BinaryWriter()) {
6528
+ if (message.cpuUsagePercent !== 0) {
6529
+ writer.uint32(9).double(message.cpuUsagePercent);
6530
+ }
6531
+ if (message.memoryUsagePercent !== 0) {
6532
+ writer.uint32(17).double(message.memoryUsagePercent);
6533
+ }
6534
+ if (message.concurrentRequests !== 0) {
6535
+ writer.uint32(24).uint32(message.concurrentRequests);
6536
+ }
6537
+ if (message.timestamp !== 0) {
6538
+ writer.uint32(33).double(message.timestamp);
6539
+ }
6540
+ return writer;
6541
+ },
6542
+ decode(input, length) {
6543
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
6544
+ let end = length === void 0 ? reader.len : reader.pos + length;
6545
+ const message = createBaseAutoscalingMetrics();
6546
+ while (reader.pos < end) {
6547
+ const tag = reader.uint32();
6548
+ switch (tag >>> 3) {
6549
+ case 1: {
6550
+ if (tag !== 9) {
6551
+ break;
6552
+ }
6553
+ message.cpuUsagePercent = reader.double();
6554
+ continue;
6555
+ }
6556
+ case 2: {
6557
+ if (tag !== 17) {
6558
+ break;
6559
+ }
6560
+ message.memoryUsagePercent = reader.double();
6561
+ continue;
6562
+ }
6563
+ case 3: {
6564
+ if (tag !== 24) {
6565
+ break;
6566
+ }
6567
+ message.concurrentRequests = reader.uint32();
6568
+ continue;
6569
+ }
6570
+ case 4: {
6571
+ if (tag !== 33) {
6572
+ break;
6573
+ }
6574
+ message.timestamp = reader.double();
6575
+ continue;
6576
+ }
6577
+ }
6578
+ if ((tag & 7) === 4 || tag === 0) {
6579
+ break;
6580
+ }
6581
+ reader.skip(tag & 7);
6582
+ }
6583
+ return message;
6584
+ },
6585
+ fromJSON(object) {
6586
+ return {
6587
+ cpuUsagePercent: isSet3(object.cpuUsagePercent) ? globalThis.Number(object.cpuUsagePercent) : 0,
6588
+ memoryUsagePercent: isSet3(object.memoryUsagePercent) ? globalThis.Number(object.memoryUsagePercent) : 0,
6589
+ concurrentRequests: isSet3(object.concurrentRequests) ? globalThis.Number(object.concurrentRequests) : 0,
6590
+ timestamp: isSet3(object.timestamp) ? globalThis.Number(object.timestamp) : 0
6591
+ };
6592
+ },
6593
+ toJSON(message) {
6594
+ const obj = {};
6595
+ if (message.cpuUsagePercent !== 0) {
6596
+ obj.cpuUsagePercent = message.cpuUsagePercent;
6597
+ }
6598
+ if (message.memoryUsagePercent !== 0) {
6599
+ obj.memoryUsagePercent = message.memoryUsagePercent;
6600
+ }
6601
+ if (message.concurrentRequests !== 0) {
6602
+ obj.concurrentRequests = Math.round(message.concurrentRequests);
6603
+ }
6604
+ if (message.timestamp !== 0) {
6605
+ obj.timestamp = message.timestamp;
6606
+ }
6607
+ return obj;
6608
+ },
6609
+ create(base) {
6610
+ return AutoscalingMetrics.fromPartial(base ?? {});
6611
+ },
6612
+ fromPartial(object) {
6613
+ const message = createBaseAutoscalingMetrics();
6614
+ message.cpuUsagePercent = object.cpuUsagePercent ?? 0;
6615
+ message.memoryUsagePercent = object.memoryUsagePercent ?? 0;
6616
+ message.concurrentRequests = object.concurrentRequests ?? 0;
6617
+ message.timestamp = object.timestamp ?? 0;
6618
+ return message;
6619
+ }
6620
+ };
6523
6621
  function createBaseBaseImage() {
6524
6622
  return { imageId: "", dockerTag: "" };
6525
6623
  }
@@ -35073,6 +35171,108 @@ var TaskCurrentInputsResponse = {
35073
35171
  return message;
35074
35172
  }
35075
35173
  };
35174
+ function createBaseTaskGetAutoscalingMetricsRequest() {
35175
+ return { taskId: "" };
35176
+ }
35177
+ var TaskGetAutoscalingMetricsRequest = {
35178
+ encode(message, writer = new BinaryWriter()) {
35179
+ if (message.taskId !== "") {
35180
+ writer.uint32(10).string(message.taskId);
35181
+ }
35182
+ return writer;
35183
+ },
35184
+ decode(input, length) {
35185
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
35186
+ let end = length === void 0 ? reader.len : reader.pos + length;
35187
+ const message = createBaseTaskGetAutoscalingMetricsRequest();
35188
+ while (reader.pos < end) {
35189
+ const tag = reader.uint32();
35190
+ switch (tag >>> 3) {
35191
+ case 1: {
35192
+ if (tag !== 10) {
35193
+ break;
35194
+ }
35195
+ message.taskId = reader.string();
35196
+ continue;
35197
+ }
35198
+ }
35199
+ if ((tag & 7) === 4 || tag === 0) {
35200
+ break;
35201
+ }
35202
+ reader.skip(tag & 7);
35203
+ }
35204
+ return message;
35205
+ },
35206
+ fromJSON(object) {
35207
+ return { taskId: isSet3(object.taskId) ? globalThis.String(object.taskId) : "" };
35208
+ },
35209
+ toJSON(message) {
35210
+ const obj = {};
35211
+ if (message.taskId !== "") {
35212
+ obj.taskId = message.taskId;
35213
+ }
35214
+ return obj;
35215
+ },
35216
+ create(base) {
35217
+ return TaskGetAutoscalingMetricsRequest.fromPartial(base ?? {});
35218
+ },
35219
+ fromPartial(object) {
35220
+ const message = createBaseTaskGetAutoscalingMetricsRequest();
35221
+ message.taskId = object.taskId ?? "";
35222
+ return message;
35223
+ }
35224
+ };
35225
+ function createBaseTaskGetAutoscalingMetricsResponse() {
35226
+ return { metrics: void 0 };
35227
+ }
35228
+ var TaskGetAutoscalingMetricsResponse = {
35229
+ encode(message, writer = new BinaryWriter()) {
35230
+ if (message.metrics !== void 0) {
35231
+ AutoscalingMetrics.encode(message.metrics, writer.uint32(10).fork()).join();
35232
+ }
35233
+ return writer;
35234
+ },
35235
+ decode(input, length) {
35236
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
35237
+ let end = length === void 0 ? reader.len : reader.pos + length;
35238
+ const message = createBaseTaskGetAutoscalingMetricsResponse();
35239
+ while (reader.pos < end) {
35240
+ const tag = reader.uint32();
35241
+ switch (tag >>> 3) {
35242
+ case 1: {
35243
+ if (tag !== 10) {
35244
+ break;
35245
+ }
35246
+ message.metrics = AutoscalingMetrics.decode(reader, reader.uint32());
35247
+ continue;
35248
+ }
35249
+ }
35250
+ if ((tag & 7) === 4 || tag === 0) {
35251
+ break;
35252
+ }
35253
+ reader.skip(tag & 7);
35254
+ }
35255
+ return message;
35256
+ },
35257
+ fromJSON(object) {
35258
+ return { metrics: isSet3(object.metrics) ? AutoscalingMetrics.fromJSON(object.metrics) : void 0 };
35259
+ },
35260
+ toJSON(message) {
35261
+ const obj = {};
35262
+ if (message.metrics !== void 0) {
35263
+ obj.metrics = AutoscalingMetrics.toJSON(message.metrics);
35264
+ }
35265
+ return obj;
35266
+ },
35267
+ create(base) {
35268
+ return TaskGetAutoscalingMetricsResponse.fromPartial(base ?? {});
35269
+ },
35270
+ fromPartial(object) {
35271
+ const message = createBaseTaskGetAutoscalingMetricsResponse();
35272
+ message.metrics = object.metrics !== void 0 && object.metrics !== null ? AutoscalingMetrics.fromPartial(object.metrics) : void 0;
35273
+ return message;
35274
+ }
35275
+ };
35076
35276
  function createBaseTaskInfo() {
35077
35277
  return {
35078
35278
  id: "",
@@ -40979,6 +41179,15 @@ var ModalClientDefinition = {
40979
41179
  responseStream: false,
40980
41180
  options: {}
40981
41181
  },
41182
+ /** Used for flash autoscaling */
41183
+ taskGetAutoscalingMetrics: {
41184
+ name: "TaskGetAutoscalingMetrics",
41185
+ requestType: TaskGetAutoscalingMetricsRequest,
41186
+ requestStream: false,
41187
+ responseType: TaskGetAutoscalingMetricsResponse,
41188
+ responseStream: false,
41189
+ options: {}
41190
+ },
40982
41191
  taskList: {
40983
41192
  name: "TaskList",
40984
41193
  requestType: TaskListRequest,
@@ -41563,11 +41772,20 @@ var Image2 = class _Image {
41563
41772
  #imageId;
41564
41773
  #tag;
41565
41774
  #imageRegistryConfig;
41775
+ #layers;
41566
41776
  /** @ignore */
41567
- constructor(imageId, tag, imageRegistryConfig) {
41777
+ constructor(imageId, tag, imageRegistryConfig, layers) {
41568
41778
  this.#imageId = imageId;
41569
41779
  this.#tag = tag;
41570
41780
  this.#imageRegistryConfig = imageRegistryConfig;
41781
+ this.#layers = layers || [
41782
+ {
41783
+ commands: [],
41784
+ secrets: void 0,
41785
+ gpuConfig: void 0,
41786
+ forceBuild: false
41787
+ }
41788
+ ];
41571
41789
  }
41572
41790
  get imageId() {
41573
41791
  return this.#imageId;
@@ -41652,6 +41870,42 @@ var Image2 = class _Image {
41652
41870
  }
41653
41871
  return new _Image("", tag, imageRegistryConfig);
41654
41872
  }
41873
+ static validateDockerfileCommands(commands) {
41874
+ for (const command of commands) {
41875
+ const trimmed = command.trim().toUpperCase();
41876
+ if (trimmed.startsWith("COPY ") && !trimmed.startsWith("COPY --FROM=")) {
41877
+ throw new InvalidError(
41878
+ "COPY commands that copy from local context are not yet supported."
41879
+ );
41880
+ }
41881
+ }
41882
+ }
41883
+ /**
41884
+ * Extend an image with arbitrary Dockerfile-like commands.
41885
+ *
41886
+ * Each call creates a new Image layer that will be built sequentially.
41887
+ * The provided options apply only to this layer.
41888
+ *
41889
+ * @param commands - Array of Dockerfile commands as strings
41890
+ * @param options - Optional configuration for this layer's build
41891
+ * @returns A new Image instance
41892
+ */
41893
+ dockerfileCommands(commands, options) {
41894
+ if (commands.length === 0) {
41895
+ return this;
41896
+ }
41897
+ _Image.validateDockerfileCommands(commands);
41898
+ const newLayer = {
41899
+ commands: [...commands],
41900
+ secrets: options?.secrets,
41901
+ gpuConfig: options?.gpu ? parseGpuConfig(options.gpu) : void 0,
41902
+ forceBuild: options?.forceBuild
41903
+ };
41904
+ return new _Image("", this.#tag, this.#imageRegistryConfig, [
41905
+ ...this.#layers,
41906
+ newLayer
41907
+ ]);
41908
+ }
41655
41909
  /**
41656
41910
  * Eagerly builds an Image on Modal.
41657
41911
  *
@@ -41661,58 +41915,75 @@ var Image2 = class _Image {
41661
41915
  if (this.imageId !== "") {
41662
41916
  return this;
41663
41917
  }
41664
- const resp = await client.imageGetOrCreate({
41665
- appId: app.appId,
41666
- image: {
41667
- dockerfileCommands: [`FROM ${this.#tag}`],
41668
- imageRegistryConfig: this.#imageRegistryConfig
41669
- },
41670
- builderVersion: imageBuilderVersion()
41671
- });
41672
- let result;
41673
- let metadata = void 0;
41674
- if (resp.result?.status) {
41675
- result = resp.result;
41676
- metadata = resp.metadata;
41677
- } else {
41678
- let lastEntryId = "";
41679
- let resultJoined = void 0;
41680
- while (!resultJoined) {
41681
- for await (const item of client.imageJoinStreaming({
41682
- imageId: resp.imageId,
41683
- timeout: 55,
41684
- lastEntryId
41685
- })) {
41686
- if (item.entryId) lastEntryId = item.entryId;
41687
- if (item.result?.status) {
41688
- resultJoined = item.result;
41689
- metadata = item.metadata;
41690
- break;
41918
+ let baseImageId;
41919
+ for (let i = 0; i < this.#layers.length; i++) {
41920
+ const layer = this.#layers[i];
41921
+ const secretIds = layer.secrets?.map((secret) => secret.secretId) || [];
41922
+ const gpuConfig = layer.gpuConfig;
41923
+ let dockerfileCommands;
41924
+ let baseImages;
41925
+ if (i === 0) {
41926
+ dockerfileCommands = [`FROM ${this.#tag}`, ...layer.commands];
41927
+ baseImages = [];
41928
+ } else {
41929
+ dockerfileCommands = ["FROM base", ...layer.commands];
41930
+ baseImages = [{ dockerTag: "base", imageId: baseImageId }];
41931
+ }
41932
+ const resp = await client.imageGetOrCreate({
41933
+ appId: app.appId,
41934
+ image: Image.create({
41935
+ dockerfileCommands,
41936
+ imageRegistryConfig: this.#imageRegistryConfig,
41937
+ secretIds,
41938
+ gpuConfig,
41939
+ contextFiles: [],
41940
+ baseImages
41941
+ }),
41942
+ builderVersion: imageBuilderVersion(),
41943
+ forceBuild: layer.forceBuild || false
41944
+ });
41945
+ let result;
41946
+ if (resp.result?.status) {
41947
+ result = resp.result;
41948
+ } else {
41949
+ let lastEntryId = "";
41950
+ let resultJoined = void 0;
41951
+ while (!resultJoined) {
41952
+ for await (const item of client.imageJoinStreaming({
41953
+ imageId: resp.imageId,
41954
+ timeout: 55,
41955
+ lastEntryId
41956
+ })) {
41957
+ if (item.entryId) lastEntryId = item.entryId;
41958
+ if (item.result?.status) {
41959
+ resultJoined = item.result;
41960
+ break;
41961
+ }
41691
41962
  }
41692
41963
  }
41964
+ result = resultJoined;
41693
41965
  }
41694
- result = resultJoined;
41695
- }
41696
- void metadata;
41697
- if (result.status === 2 /* GENERIC_STATUS_FAILURE */) {
41698
- throw new Error(
41699
- `Image build for ${resp.imageId} failed with the exception:
41966
+ if (result.status === 2 /* GENERIC_STATUS_FAILURE */) {
41967
+ throw new Error(
41968
+ `Image build for ${resp.imageId} failed with the exception:
41700
41969
  ${result.exception}`
41701
- );
41702
- } else if (result.status === 3 /* GENERIC_STATUS_TERMINATED */) {
41703
- throw new Error(
41704
- `Image build for ${resp.imageId} terminated due to external shut-down. Please try again.`
41705
- );
41706
- } else if (result.status === 4 /* GENERIC_STATUS_TIMEOUT */) {
41707
- throw new Error(
41708
- `Image build for ${resp.imageId} timed out. Please try again with a larger timeout parameter.`
41709
- );
41710
- } else if (result.status !== 1 /* GENERIC_STATUS_SUCCESS */) {
41711
- throw new Error(
41712
- `Image build for ${resp.imageId} failed with unknown status: ${result.status}`
41713
- );
41970
+ );
41971
+ } else if (result.status === 3 /* GENERIC_STATUS_TERMINATED */) {
41972
+ throw new Error(
41973
+ `Image build for ${resp.imageId} terminated due to external shut-down. Please try again.`
41974
+ );
41975
+ } else if (result.status === 4 /* GENERIC_STATUS_TIMEOUT */) {
41976
+ throw new Error(
41977
+ `Image build for ${resp.imageId} timed out. Please try again with a larger timeout parameter.`
41978
+ );
41979
+ } else if (result.status !== 1 /* GENERIC_STATUS_SUCCESS */) {
41980
+ throw new Error(
41981
+ `Image build for ${resp.imageId} failed with unknown status: ${result.status}`
41982
+ );
41983
+ }
41984
+ baseImageId = resp.imageId;
41714
41985
  }
41715
- this.#imageId = resp.imageId;
41986
+ this.#imageId = baseImageId;
41716
41987
  return this;
41717
41988
  }
41718
41989
  /** Delete an Image by ID. Warning: This removes an *entire Image*, and cannot be undone. */
@@ -42464,7 +42735,7 @@ function parseGpuConfig(gpu) {
42464
42735
  gpuType: gpuType.toUpperCase()
42465
42736
  };
42466
42737
  }
42467
- var App = class _App {
42738
+ var App2 = class _App {
42468
42739
  appId;
42469
42740
  name;
42470
42741
  /** @ignore */
@@ -42491,7 +42762,12 @@ var App = class _App {
42491
42762
  const gpuConfig = parseGpuConfig(options.gpu);
42492
42763
  if (options.timeout && options.timeout % 1e3 !== 0) {
42493
42764
  throw new Error(
42494
- `Timeout must be a multiple of 1000ms, got ${options.timeout}`
42765
+ `timeout must be a multiple of 1000ms, got ${options.timeout}`
42766
+ );
42767
+ }
42768
+ if (options.idleTimeout && options.idleTimeout % 1e3 !== 0) {
42769
+ throw new Error(
42770
+ `idleTimeout must be a multiple of 1000ms, got ${options.idleTimeout}`
42495
42771
  );
42496
42772
  }
42497
42773
  await image.build(this);
@@ -42570,6 +42846,7 @@ var App = class _App {
42570
42846
  entrypointArgs: options.command ?? ["sleep", "48h"],
42571
42847
  imageId: image.imageId,
42572
42848
  timeoutSecs: options.timeout != void 0 ? options.timeout / 1e3 : 600,
42849
+ idleTimeoutSecs: options.idleTimeout != void 0 ? options.idleTimeout / 1e3 : void 0,
42573
42850
  workdir: options.workdir ?? void 0,
42574
42851
  networkAccess,
42575
42852
  resources: {
package/dist/index.d.cts CHANGED
@@ -1,5 +1,22 @@
1
1
  import { BinaryWriter, BinaryReader } from '@bufbuild/protobuf/wire';
2
2
 
3
+ declare enum GPUType {
4
+ /**
5
+ * GPU_TYPE_UNSPECIFIED - Note: this enum is no longer used by current clients - don't add new types
6
+ * Old clients still send it, so we use it server-side for compatibility
7
+ */
8
+ GPU_TYPE_UNSPECIFIED = 0,
9
+ GPU_TYPE_T4 = 1,
10
+ GPU_TYPE_A100 = 2,
11
+ GPU_TYPE_A10G = 3,
12
+ GPU_TYPE_ANY = 4,
13
+ GPU_TYPE_A100_80GB = 8,
14
+ GPU_TYPE_L4 = 9,
15
+ GPU_TYPE_H100 = 10,
16
+ GPU_TYPE_L40S = 11,
17
+ GPU_TYPE_H200 = 12,
18
+ UNRECOGNIZED = -1
19
+ }
3
20
  declare enum ParameterType {
4
21
  PARAM_TYPE_UNSPECIFIED = 0,
5
22
  PARAM_TYPE_STRING = 1,
@@ -40,6 +57,13 @@ interface ClassParameterSpec {
40
57
  fullType: GenericPayloadType | undefined;
41
58
  }
42
59
  declare const ClassParameterSpec: MessageFns<ClassParameterSpec>;
60
+ interface GPUConfig {
61
+ /** Deprecated, at some point */
62
+ type: GPUType;
63
+ count: number;
64
+ gpuType: string;
65
+ }
66
+ declare const GPUConfig: MessageFns<GPUConfig>;
43
67
  interface GenericPayloadType {
44
68
  baseType: ParameterType;
45
69
  /** sub-type for generic types like lists */
@@ -85,11 +109,27 @@ declare class Secret {
85
109
 
86
110
  /** Options for deleting an Image. */
87
111
  type ImageDeleteOptions = Record<never, never>;
112
+ /** Options for Image.dockerfileCommands(). */
113
+ type ImageDockerfileCommandsOptions = {
114
+ /** Secrets that will be made available to this layer's build environment. */
115
+ secrets?: Secret[];
116
+ /** GPU reservation for this layer's build environment (e.g. "A100", "T4:2", "A100-80GB:4"). */
117
+ gpu?: string;
118
+ /** Ignore cached builds for this layer, similar to 'docker build --no-cache'. */
119
+ forceBuild?: boolean;
120
+ };
121
+ /** Represents a single image layer with its build configuration. */
122
+ type Layer = {
123
+ commands: string[];
124
+ secrets?: Secret[];
125
+ gpuConfig?: GPUConfig;
126
+ forceBuild?: boolean;
127
+ };
88
128
  /** A container image, used for starting Sandboxes. */
89
129
  declare class Image {
90
130
  #private;
91
131
  /** @ignore */
92
- constructor(imageId: string, tag: string, imageRegistryConfig?: ImageRegistryConfig);
132
+ constructor(imageId: string, tag: string, imageRegistryConfig?: ImageRegistryConfig, layers?: Layer[]);
93
133
  get imageId(): string;
94
134
  /**
95
135
  * Creates an Image from an Image ID
@@ -118,6 +158,18 @@ declare class Image {
118
158
  * @param secret - A Secret containing credentials for registry authentication.
119
159
  */
120
160
  static fromGcpArtifactRegistry(tag: string, secret: Secret): Image;
161
+ private static validateDockerfileCommands;
162
+ /**
163
+ * Extend an image with arbitrary Dockerfile-like commands.
164
+ *
165
+ * Each call creates a new Image layer that will be built sequentially.
166
+ * The provided options apply only to this layer.
167
+ *
168
+ * @param commands - Array of Dockerfile commands as strings
169
+ * @param options - Optional configuration for this layer's build
170
+ * @returns A new Image instance
171
+ */
172
+ dockerfileCommands(commands: string[], options?: ImageDockerfileCommandsOptions): Image;
121
173
  /**
122
174
  * Eagerly builds an Image on Modal.
123
175
  *
@@ -418,6 +470,8 @@ type SandboxCreateOptions = {
418
470
  gpu?: string;
419
471
  /** Timeout of the Sandbox container, defaults to 10 minutes. */
420
472
  timeout?: number;
473
+ /** The amount of time in milliseconds that a sandbox can be idle before being terminated. */
474
+ idleTimeout?: number;
421
475
  /** Working directory of the Sandbox. */
422
476
  workdir?: string;
423
477
  /**
@@ -745,4 +799,4 @@ declare class Queue {
745
799
  iterate(options?: QueueIterateOptions): AsyncGenerator<any, void, unknown>;
746
800
  }
747
801
 
748
- export { AlreadyExistsError, App, type ClientOptions, CloudBucketMount, Cls, type ClsBatchingOptions, type ClsConcurrencyOptions, ClsInstance, type ClsOptions, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, type FunctionStats, FunctionTimeoutError, Function_, Image, type ImageDeleteOptions, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Proxy, type ProxyFromNameOptions, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Retries, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, type SandboxListOptions, SandboxTimeoutError, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Tunnel, type UpdateAutoscalerOptions, Volume, type VolumeFromNameOptions, initializeClient };
802
+ export { AlreadyExistsError, App, type ClientOptions, CloudBucketMount, Cls, type ClsBatchingOptions, type ClsConcurrencyOptions, ClsInstance, type ClsOptions, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, type FunctionStats, FunctionTimeoutError, Function_, Image, type ImageDeleteOptions, type ImageDockerfileCommandsOptions, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Proxy, type ProxyFromNameOptions, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Retries, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, type SandboxListOptions, SandboxTimeoutError, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Tunnel, type UpdateAutoscalerOptions, Volume, type VolumeFromNameOptions, initializeClient };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,22 @@
1
1
  import { BinaryWriter, BinaryReader } from '@bufbuild/protobuf/wire';
2
2
 
3
+ declare enum GPUType {
4
+ /**
5
+ * GPU_TYPE_UNSPECIFIED - Note: this enum is no longer used by current clients - don't add new types
6
+ * Old clients still send it, so we use it server-side for compatibility
7
+ */
8
+ GPU_TYPE_UNSPECIFIED = 0,
9
+ GPU_TYPE_T4 = 1,
10
+ GPU_TYPE_A100 = 2,
11
+ GPU_TYPE_A10G = 3,
12
+ GPU_TYPE_ANY = 4,
13
+ GPU_TYPE_A100_80GB = 8,
14
+ GPU_TYPE_L4 = 9,
15
+ GPU_TYPE_H100 = 10,
16
+ GPU_TYPE_L40S = 11,
17
+ GPU_TYPE_H200 = 12,
18
+ UNRECOGNIZED = -1
19
+ }
3
20
  declare enum ParameterType {
4
21
  PARAM_TYPE_UNSPECIFIED = 0,
5
22
  PARAM_TYPE_STRING = 1,
@@ -40,6 +57,13 @@ interface ClassParameterSpec {
40
57
  fullType: GenericPayloadType | undefined;
41
58
  }
42
59
  declare const ClassParameterSpec: MessageFns<ClassParameterSpec>;
60
+ interface GPUConfig {
61
+ /** Deprecated, at some point */
62
+ type: GPUType;
63
+ count: number;
64
+ gpuType: string;
65
+ }
66
+ declare const GPUConfig: MessageFns<GPUConfig>;
43
67
  interface GenericPayloadType {
44
68
  baseType: ParameterType;
45
69
  /** sub-type for generic types like lists */
@@ -85,11 +109,27 @@ declare class Secret {
85
109
 
86
110
  /** Options for deleting an Image. */
87
111
  type ImageDeleteOptions = Record<never, never>;
112
+ /** Options for Image.dockerfileCommands(). */
113
+ type ImageDockerfileCommandsOptions = {
114
+ /** Secrets that will be made available to this layer's build environment. */
115
+ secrets?: Secret[];
116
+ /** GPU reservation for this layer's build environment (e.g. "A100", "T4:2", "A100-80GB:4"). */
117
+ gpu?: string;
118
+ /** Ignore cached builds for this layer, similar to 'docker build --no-cache'. */
119
+ forceBuild?: boolean;
120
+ };
121
+ /** Represents a single image layer with its build configuration. */
122
+ type Layer = {
123
+ commands: string[];
124
+ secrets?: Secret[];
125
+ gpuConfig?: GPUConfig;
126
+ forceBuild?: boolean;
127
+ };
88
128
  /** A container image, used for starting Sandboxes. */
89
129
  declare class Image {
90
130
  #private;
91
131
  /** @ignore */
92
- constructor(imageId: string, tag: string, imageRegistryConfig?: ImageRegistryConfig);
132
+ constructor(imageId: string, tag: string, imageRegistryConfig?: ImageRegistryConfig, layers?: Layer[]);
93
133
  get imageId(): string;
94
134
  /**
95
135
  * Creates an Image from an Image ID
@@ -118,6 +158,18 @@ declare class Image {
118
158
  * @param secret - A Secret containing credentials for registry authentication.
119
159
  */
120
160
  static fromGcpArtifactRegistry(tag: string, secret: Secret): Image;
161
+ private static validateDockerfileCommands;
162
+ /**
163
+ * Extend an image with arbitrary Dockerfile-like commands.
164
+ *
165
+ * Each call creates a new Image layer that will be built sequentially.
166
+ * The provided options apply only to this layer.
167
+ *
168
+ * @param commands - Array of Dockerfile commands as strings
169
+ * @param options - Optional configuration for this layer's build
170
+ * @returns A new Image instance
171
+ */
172
+ dockerfileCommands(commands: string[], options?: ImageDockerfileCommandsOptions): Image;
121
173
  /**
122
174
  * Eagerly builds an Image on Modal.
123
175
  *
@@ -418,6 +470,8 @@ type SandboxCreateOptions = {
418
470
  gpu?: string;
419
471
  /** Timeout of the Sandbox container, defaults to 10 minutes. */
420
472
  timeout?: number;
473
+ /** The amount of time in milliseconds that a sandbox can be idle before being terminated. */
474
+ idleTimeout?: number;
421
475
  /** Working directory of the Sandbox. */
422
476
  workdir?: string;
423
477
  /**
@@ -745,4 +799,4 @@ declare class Queue {
745
799
  iterate(options?: QueueIterateOptions): AsyncGenerator<any, void, unknown>;
746
800
  }
747
801
 
748
- export { AlreadyExistsError, App, type ClientOptions, CloudBucketMount, Cls, type ClsBatchingOptions, type ClsConcurrencyOptions, ClsInstance, type ClsOptions, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, type FunctionStats, FunctionTimeoutError, Function_, Image, type ImageDeleteOptions, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Proxy, type ProxyFromNameOptions, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Retries, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, type SandboxListOptions, SandboxTimeoutError, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Tunnel, type UpdateAutoscalerOptions, Volume, type VolumeFromNameOptions, initializeClient };
802
+ export { AlreadyExistsError, App, type ClientOptions, CloudBucketMount, Cls, type ClsBatchingOptions, type ClsConcurrencyOptions, ClsInstance, type ClsOptions, ContainerProcess, type DeleteOptions, type EphemeralOptions, type ExecOptions, FunctionCall, type FunctionCallCancelOptions, type FunctionCallGetOptions, type FunctionStats, FunctionTimeoutError, Function_, Image, type ImageDeleteOptions, type ImageDockerfileCommandsOptions, InternalFailure, InvalidError, type LookupOptions, type ModalReadStream, type ModalWriteStream, NotFoundError, Proxy, type ProxyFromNameOptions, Queue, type QueueClearOptions, QueueEmptyError, QueueFullError, type QueueGetOptions, type QueueIterateOptions, type QueueLenOptions, type QueuePutOptions, RemoteError, Retries, Sandbox, type SandboxCreateOptions, SandboxFile, type SandboxFileMode, type SandboxListOptions, SandboxTimeoutError, Secret, type SecretFromNameOptions, type StdioBehavior, type StreamMode, Tunnel, type UpdateAutoscalerOptions, Volume, type VolumeFromNameOptions, initializeClient };
package/dist/index.js CHANGED
@@ -6460,6 +6460,104 @@ var AutoscalerSettings = {
6460
6460
  return message;
6461
6461
  }
6462
6462
  };
6463
+ function createBaseAutoscalingMetrics() {
6464
+ return { cpuUsagePercent: 0, memoryUsagePercent: 0, concurrentRequests: 0, timestamp: 0 };
6465
+ }
6466
+ var AutoscalingMetrics = {
6467
+ encode(message, writer = new BinaryWriter()) {
6468
+ if (message.cpuUsagePercent !== 0) {
6469
+ writer.uint32(9).double(message.cpuUsagePercent);
6470
+ }
6471
+ if (message.memoryUsagePercent !== 0) {
6472
+ writer.uint32(17).double(message.memoryUsagePercent);
6473
+ }
6474
+ if (message.concurrentRequests !== 0) {
6475
+ writer.uint32(24).uint32(message.concurrentRequests);
6476
+ }
6477
+ if (message.timestamp !== 0) {
6478
+ writer.uint32(33).double(message.timestamp);
6479
+ }
6480
+ return writer;
6481
+ },
6482
+ decode(input, length) {
6483
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
6484
+ let end = length === void 0 ? reader.len : reader.pos + length;
6485
+ const message = createBaseAutoscalingMetrics();
6486
+ while (reader.pos < end) {
6487
+ const tag = reader.uint32();
6488
+ switch (tag >>> 3) {
6489
+ case 1: {
6490
+ if (tag !== 9) {
6491
+ break;
6492
+ }
6493
+ message.cpuUsagePercent = reader.double();
6494
+ continue;
6495
+ }
6496
+ case 2: {
6497
+ if (tag !== 17) {
6498
+ break;
6499
+ }
6500
+ message.memoryUsagePercent = reader.double();
6501
+ continue;
6502
+ }
6503
+ case 3: {
6504
+ if (tag !== 24) {
6505
+ break;
6506
+ }
6507
+ message.concurrentRequests = reader.uint32();
6508
+ continue;
6509
+ }
6510
+ case 4: {
6511
+ if (tag !== 33) {
6512
+ break;
6513
+ }
6514
+ message.timestamp = reader.double();
6515
+ continue;
6516
+ }
6517
+ }
6518
+ if ((tag & 7) === 4 || tag === 0) {
6519
+ break;
6520
+ }
6521
+ reader.skip(tag & 7);
6522
+ }
6523
+ return message;
6524
+ },
6525
+ fromJSON(object) {
6526
+ return {
6527
+ cpuUsagePercent: isSet3(object.cpuUsagePercent) ? globalThis.Number(object.cpuUsagePercent) : 0,
6528
+ memoryUsagePercent: isSet3(object.memoryUsagePercent) ? globalThis.Number(object.memoryUsagePercent) : 0,
6529
+ concurrentRequests: isSet3(object.concurrentRequests) ? globalThis.Number(object.concurrentRequests) : 0,
6530
+ timestamp: isSet3(object.timestamp) ? globalThis.Number(object.timestamp) : 0
6531
+ };
6532
+ },
6533
+ toJSON(message) {
6534
+ const obj = {};
6535
+ if (message.cpuUsagePercent !== 0) {
6536
+ obj.cpuUsagePercent = message.cpuUsagePercent;
6537
+ }
6538
+ if (message.memoryUsagePercent !== 0) {
6539
+ obj.memoryUsagePercent = message.memoryUsagePercent;
6540
+ }
6541
+ if (message.concurrentRequests !== 0) {
6542
+ obj.concurrentRequests = Math.round(message.concurrentRequests);
6543
+ }
6544
+ if (message.timestamp !== 0) {
6545
+ obj.timestamp = message.timestamp;
6546
+ }
6547
+ return obj;
6548
+ },
6549
+ create(base) {
6550
+ return AutoscalingMetrics.fromPartial(base ?? {});
6551
+ },
6552
+ fromPartial(object) {
6553
+ const message = createBaseAutoscalingMetrics();
6554
+ message.cpuUsagePercent = object.cpuUsagePercent ?? 0;
6555
+ message.memoryUsagePercent = object.memoryUsagePercent ?? 0;
6556
+ message.concurrentRequests = object.concurrentRequests ?? 0;
6557
+ message.timestamp = object.timestamp ?? 0;
6558
+ return message;
6559
+ }
6560
+ };
6463
6561
  function createBaseBaseImage() {
6464
6562
  return { imageId: "", dockerTag: "" };
6465
6563
  }
@@ -35013,6 +35111,108 @@ var TaskCurrentInputsResponse = {
35013
35111
  return message;
35014
35112
  }
35015
35113
  };
35114
+ function createBaseTaskGetAutoscalingMetricsRequest() {
35115
+ return { taskId: "" };
35116
+ }
35117
+ var TaskGetAutoscalingMetricsRequest = {
35118
+ encode(message, writer = new BinaryWriter()) {
35119
+ if (message.taskId !== "") {
35120
+ writer.uint32(10).string(message.taskId);
35121
+ }
35122
+ return writer;
35123
+ },
35124
+ decode(input, length) {
35125
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
35126
+ let end = length === void 0 ? reader.len : reader.pos + length;
35127
+ const message = createBaseTaskGetAutoscalingMetricsRequest();
35128
+ while (reader.pos < end) {
35129
+ const tag = reader.uint32();
35130
+ switch (tag >>> 3) {
35131
+ case 1: {
35132
+ if (tag !== 10) {
35133
+ break;
35134
+ }
35135
+ message.taskId = reader.string();
35136
+ continue;
35137
+ }
35138
+ }
35139
+ if ((tag & 7) === 4 || tag === 0) {
35140
+ break;
35141
+ }
35142
+ reader.skip(tag & 7);
35143
+ }
35144
+ return message;
35145
+ },
35146
+ fromJSON(object) {
35147
+ return { taskId: isSet3(object.taskId) ? globalThis.String(object.taskId) : "" };
35148
+ },
35149
+ toJSON(message) {
35150
+ const obj = {};
35151
+ if (message.taskId !== "") {
35152
+ obj.taskId = message.taskId;
35153
+ }
35154
+ return obj;
35155
+ },
35156
+ create(base) {
35157
+ return TaskGetAutoscalingMetricsRequest.fromPartial(base ?? {});
35158
+ },
35159
+ fromPartial(object) {
35160
+ const message = createBaseTaskGetAutoscalingMetricsRequest();
35161
+ message.taskId = object.taskId ?? "";
35162
+ return message;
35163
+ }
35164
+ };
35165
+ function createBaseTaskGetAutoscalingMetricsResponse() {
35166
+ return { metrics: void 0 };
35167
+ }
35168
+ var TaskGetAutoscalingMetricsResponse = {
35169
+ encode(message, writer = new BinaryWriter()) {
35170
+ if (message.metrics !== void 0) {
35171
+ AutoscalingMetrics.encode(message.metrics, writer.uint32(10).fork()).join();
35172
+ }
35173
+ return writer;
35174
+ },
35175
+ decode(input, length) {
35176
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
35177
+ let end = length === void 0 ? reader.len : reader.pos + length;
35178
+ const message = createBaseTaskGetAutoscalingMetricsResponse();
35179
+ while (reader.pos < end) {
35180
+ const tag = reader.uint32();
35181
+ switch (tag >>> 3) {
35182
+ case 1: {
35183
+ if (tag !== 10) {
35184
+ break;
35185
+ }
35186
+ message.metrics = AutoscalingMetrics.decode(reader, reader.uint32());
35187
+ continue;
35188
+ }
35189
+ }
35190
+ if ((tag & 7) === 4 || tag === 0) {
35191
+ break;
35192
+ }
35193
+ reader.skip(tag & 7);
35194
+ }
35195
+ return message;
35196
+ },
35197
+ fromJSON(object) {
35198
+ return { metrics: isSet3(object.metrics) ? AutoscalingMetrics.fromJSON(object.metrics) : void 0 };
35199
+ },
35200
+ toJSON(message) {
35201
+ const obj = {};
35202
+ if (message.metrics !== void 0) {
35203
+ obj.metrics = AutoscalingMetrics.toJSON(message.metrics);
35204
+ }
35205
+ return obj;
35206
+ },
35207
+ create(base) {
35208
+ return TaskGetAutoscalingMetricsResponse.fromPartial(base ?? {});
35209
+ },
35210
+ fromPartial(object) {
35211
+ const message = createBaseTaskGetAutoscalingMetricsResponse();
35212
+ message.metrics = object.metrics !== void 0 && object.metrics !== null ? AutoscalingMetrics.fromPartial(object.metrics) : void 0;
35213
+ return message;
35214
+ }
35215
+ };
35016
35216
  function createBaseTaskInfo() {
35017
35217
  return {
35018
35218
  id: "",
@@ -40919,6 +41119,15 @@ var ModalClientDefinition = {
40919
41119
  responseStream: false,
40920
41120
  options: {}
40921
41121
  },
41122
+ /** Used for flash autoscaling */
41123
+ taskGetAutoscalingMetrics: {
41124
+ name: "TaskGetAutoscalingMetrics",
41125
+ requestType: TaskGetAutoscalingMetricsRequest,
41126
+ requestStream: false,
41127
+ responseType: TaskGetAutoscalingMetricsResponse,
41128
+ responseStream: false,
41129
+ options: {}
41130
+ },
40922
41131
  taskList: {
40923
41132
  name: "TaskList",
40924
41133
  requestType: TaskListRequest,
@@ -41509,11 +41718,20 @@ var Image2 = class _Image {
41509
41718
  #imageId;
41510
41719
  #tag;
41511
41720
  #imageRegistryConfig;
41721
+ #layers;
41512
41722
  /** @ignore */
41513
- constructor(imageId, tag, imageRegistryConfig) {
41723
+ constructor(imageId, tag, imageRegistryConfig, layers) {
41514
41724
  this.#imageId = imageId;
41515
41725
  this.#tag = tag;
41516
41726
  this.#imageRegistryConfig = imageRegistryConfig;
41727
+ this.#layers = layers || [
41728
+ {
41729
+ commands: [],
41730
+ secrets: void 0,
41731
+ gpuConfig: void 0,
41732
+ forceBuild: false
41733
+ }
41734
+ ];
41517
41735
  }
41518
41736
  get imageId() {
41519
41737
  return this.#imageId;
@@ -41598,6 +41816,42 @@ var Image2 = class _Image {
41598
41816
  }
41599
41817
  return new _Image("", tag, imageRegistryConfig);
41600
41818
  }
41819
+ static validateDockerfileCommands(commands) {
41820
+ for (const command of commands) {
41821
+ const trimmed = command.trim().toUpperCase();
41822
+ if (trimmed.startsWith("COPY ") && !trimmed.startsWith("COPY --FROM=")) {
41823
+ throw new InvalidError(
41824
+ "COPY commands that copy from local context are not yet supported."
41825
+ );
41826
+ }
41827
+ }
41828
+ }
41829
+ /**
41830
+ * Extend an image with arbitrary Dockerfile-like commands.
41831
+ *
41832
+ * Each call creates a new Image layer that will be built sequentially.
41833
+ * The provided options apply only to this layer.
41834
+ *
41835
+ * @param commands - Array of Dockerfile commands as strings
41836
+ * @param options - Optional configuration for this layer's build
41837
+ * @returns A new Image instance
41838
+ */
41839
+ dockerfileCommands(commands, options) {
41840
+ if (commands.length === 0) {
41841
+ return this;
41842
+ }
41843
+ _Image.validateDockerfileCommands(commands);
41844
+ const newLayer = {
41845
+ commands: [...commands],
41846
+ secrets: options?.secrets,
41847
+ gpuConfig: options?.gpu ? parseGpuConfig(options.gpu) : void 0,
41848
+ forceBuild: options?.forceBuild
41849
+ };
41850
+ return new _Image("", this.#tag, this.#imageRegistryConfig, [
41851
+ ...this.#layers,
41852
+ newLayer
41853
+ ]);
41854
+ }
41601
41855
  /**
41602
41856
  * Eagerly builds an Image on Modal.
41603
41857
  *
@@ -41607,58 +41861,75 @@ var Image2 = class _Image {
41607
41861
  if (this.imageId !== "") {
41608
41862
  return this;
41609
41863
  }
41610
- const resp = await client.imageGetOrCreate({
41611
- appId: app.appId,
41612
- image: {
41613
- dockerfileCommands: [`FROM ${this.#tag}`],
41614
- imageRegistryConfig: this.#imageRegistryConfig
41615
- },
41616
- builderVersion: imageBuilderVersion()
41617
- });
41618
- let result;
41619
- let metadata = void 0;
41620
- if (resp.result?.status) {
41621
- result = resp.result;
41622
- metadata = resp.metadata;
41623
- } else {
41624
- let lastEntryId = "";
41625
- let resultJoined = void 0;
41626
- while (!resultJoined) {
41627
- for await (const item of client.imageJoinStreaming({
41628
- imageId: resp.imageId,
41629
- timeout: 55,
41630
- lastEntryId
41631
- })) {
41632
- if (item.entryId) lastEntryId = item.entryId;
41633
- if (item.result?.status) {
41634
- resultJoined = item.result;
41635
- metadata = item.metadata;
41636
- break;
41864
+ let baseImageId;
41865
+ for (let i = 0; i < this.#layers.length; i++) {
41866
+ const layer = this.#layers[i];
41867
+ const secretIds = layer.secrets?.map((secret) => secret.secretId) || [];
41868
+ const gpuConfig = layer.gpuConfig;
41869
+ let dockerfileCommands;
41870
+ let baseImages;
41871
+ if (i === 0) {
41872
+ dockerfileCommands = [`FROM ${this.#tag}`, ...layer.commands];
41873
+ baseImages = [];
41874
+ } else {
41875
+ dockerfileCommands = ["FROM base", ...layer.commands];
41876
+ baseImages = [{ dockerTag: "base", imageId: baseImageId }];
41877
+ }
41878
+ const resp = await client.imageGetOrCreate({
41879
+ appId: app.appId,
41880
+ image: Image.create({
41881
+ dockerfileCommands,
41882
+ imageRegistryConfig: this.#imageRegistryConfig,
41883
+ secretIds,
41884
+ gpuConfig,
41885
+ contextFiles: [],
41886
+ baseImages
41887
+ }),
41888
+ builderVersion: imageBuilderVersion(),
41889
+ forceBuild: layer.forceBuild || false
41890
+ });
41891
+ let result;
41892
+ if (resp.result?.status) {
41893
+ result = resp.result;
41894
+ } else {
41895
+ let lastEntryId = "";
41896
+ let resultJoined = void 0;
41897
+ while (!resultJoined) {
41898
+ for await (const item of client.imageJoinStreaming({
41899
+ imageId: resp.imageId,
41900
+ timeout: 55,
41901
+ lastEntryId
41902
+ })) {
41903
+ if (item.entryId) lastEntryId = item.entryId;
41904
+ if (item.result?.status) {
41905
+ resultJoined = item.result;
41906
+ break;
41907
+ }
41637
41908
  }
41638
41909
  }
41910
+ result = resultJoined;
41639
41911
  }
41640
- result = resultJoined;
41641
- }
41642
- void metadata;
41643
- if (result.status === 2 /* GENERIC_STATUS_FAILURE */) {
41644
- throw new Error(
41645
- `Image build for ${resp.imageId} failed with the exception:
41912
+ if (result.status === 2 /* GENERIC_STATUS_FAILURE */) {
41913
+ throw new Error(
41914
+ `Image build for ${resp.imageId} failed with the exception:
41646
41915
  ${result.exception}`
41647
- );
41648
- } else if (result.status === 3 /* GENERIC_STATUS_TERMINATED */) {
41649
- throw new Error(
41650
- `Image build for ${resp.imageId} terminated due to external shut-down. Please try again.`
41651
- );
41652
- } else if (result.status === 4 /* GENERIC_STATUS_TIMEOUT */) {
41653
- throw new Error(
41654
- `Image build for ${resp.imageId} timed out. Please try again with a larger timeout parameter.`
41655
- );
41656
- } else if (result.status !== 1 /* GENERIC_STATUS_SUCCESS */) {
41657
- throw new Error(
41658
- `Image build for ${resp.imageId} failed with unknown status: ${result.status}`
41659
- );
41916
+ );
41917
+ } else if (result.status === 3 /* GENERIC_STATUS_TERMINATED */) {
41918
+ throw new Error(
41919
+ `Image build for ${resp.imageId} terminated due to external shut-down. Please try again.`
41920
+ );
41921
+ } else if (result.status === 4 /* GENERIC_STATUS_TIMEOUT */) {
41922
+ throw new Error(
41923
+ `Image build for ${resp.imageId} timed out. Please try again with a larger timeout parameter.`
41924
+ );
41925
+ } else if (result.status !== 1 /* GENERIC_STATUS_SUCCESS */) {
41926
+ throw new Error(
41927
+ `Image build for ${resp.imageId} failed with unknown status: ${result.status}`
41928
+ );
41929
+ }
41930
+ baseImageId = resp.imageId;
41660
41931
  }
41661
- this.#imageId = resp.imageId;
41932
+ this.#imageId = baseImageId;
41662
41933
  return this;
41663
41934
  }
41664
41935
  /** Delete an Image by ID. Warning: This removes an *entire Image*, and cannot be undone. */
@@ -42410,7 +42681,7 @@ function parseGpuConfig(gpu) {
42410
42681
  gpuType: gpuType.toUpperCase()
42411
42682
  };
42412
42683
  }
42413
- var App = class _App {
42684
+ var App2 = class _App {
42414
42685
  appId;
42415
42686
  name;
42416
42687
  /** @ignore */
@@ -42437,7 +42708,12 @@ var App = class _App {
42437
42708
  const gpuConfig = parseGpuConfig(options.gpu);
42438
42709
  if (options.timeout && options.timeout % 1e3 !== 0) {
42439
42710
  throw new Error(
42440
- `Timeout must be a multiple of 1000ms, got ${options.timeout}`
42711
+ `timeout must be a multiple of 1000ms, got ${options.timeout}`
42712
+ );
42713
+ }
42714
+ if (options.idleTimeout && options.idleTimeout % 1e3 !== 0) {
42715
+ throw new Error(
42716
+ `idleTimeout must be a multiple of 1000ms, got ${options.idleTimeout}`
42441
42717
  );
42442
42718
  }
42443
42719
  await image.build(this);
@@ -42516,6 +42792,7 @@ var App = class _App {
42516
42792
  entrypointArgs: options.command ?? ["sleep", "48h"],
42517
42793
  imageId: image.imageId,
42518
42794
  timeoutSecs: options.timeout != void 0 ? options.timeout / 1e3 : 600,
42795
+ idleTimeoutSecs: options.idleTimeout != void 0 ? options.idleTimeout / 1e3 : void 0,
42519
42796
  workdir: options.workdir ?? void 0,
42520
42797
  networkAccess,
42521
42798
  resources: {
@@ -43952,7 +44229,7 @@ var Proxy2 = class _Proxy {
43952
44229
  };
43953
44230
  export {
43954
44231
  AlreadyExistsError,
43955
- App,
44232
+ App2 as App,
43956
44233
  CloudBucketMount2 as CloudBucketMount,
43957
44234
  Cls,
43958
44235
  ClsInstance,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modal",
3
- "version": "0.3.20",
3
+ "version": "0.3.22",
4
4
  "description": "Modal client library for JavaScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://modal.com/docs",