modal 0.10.2-dev.5 → 0.10.2-dev.7

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
@@ -75,6 +75,7 @@ __export(index_exports, {
75
75
  SandboxFilesystemNotFoundError: () => SandboxFilesystemNotFoundError,
76
76
  SandboxFilesystemPathAlreadyExistsError: () => SandboxFilesystemPathAlreadyExistsError,
77
77
  SandboxFilesystemPermissionError: () => SandboxFilesystemPermissionError,
78
+ SandboxLogsManager: () => SandboxLogsManager,
78
79
  SandboxService: () => SandboxService,
79
80
  SandboxSnapshot: () => SandboxSnapshot,
80
81
  SandboxSnapshotService: () => SandboxSnapshotService,
@@ -48067,6 +48068,108 @@ var SecretDeleteRequest = {
48067
48068
  return message;
48068
48069
  }
48069
48070
  };
48071
+ function createBaseSecretGetInfoRequest() {
48072
+ return { secretId: "" };
48073
+ }
48074
+ var SecretGetInfoRequest = {
48075
+ encode(message, writer = new BinaryWriter()) {
48076
+ if (message.secretId !== "") {
48077
+ writer.uint32(10).string(message.secretId);
48078
+ }
48079
+ return writer;
48080
+ },
48081
+ decode(input, length) {
48082
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
48083
+ let end = length === void 0 ? reader.len : reader.pos + length;
48084
+ const message = createBaseSecretGetInfoRequest();
48085
+ while (reader.pos < end) {
48086
+ const tag = reader.uint32();
48087
+ switch (tag >>> 3) {
48088
+ case 1: {
48089
+ if (tag !== 10) {
48090
+ break;
48091
+ }
48092
+ message.secretId = reader.string();
48093
+ continue;
48094
+ }
48095
+ }
48096
+ if ((tag & 7) === 4 || tag === 0) {
48097
+ break;
48098
+ }
48099
+ reader.skip(tag & 7);
48100
+ }
48101
+ return message;
48102
+ },
48103
+ fromJSON(object) {
48104
+ return { secretId: isSet5(object.secretId) ? globalThis.String(object.secretId) : "" };
48105
+ },
48106
+ toJSON(message) {
48107
+ const obj = {};
48108
+ if (message.secretId !== "") {
48109
+ obj.secretId = message.secretId;
48110
+ }
48111
+ return obj;
48112
+ },
48113
+ create(base) {
48114
+ return SecretGetInfoRequest.fromPartial(base ?? {});
48115
+ },
48116
+ fromPartial(object) {
48117
+ const message = createBaseSecretGetInfoRequest();
48118
+ message.secretId = object.secretId ?? "";
48119
+ return message;
48120
+ }
48121
+ };
48122
+ function createBaseSecretGetInfoResponse() {
48123
+ return { metadata: void 0 };
48124
+ }
48125
+ var SecretGetInfoResponse = {
48126
+ encode(message, writer = new BinaryWriter()) {
48127
+ if (message.metadata !== void 0) {
48128
+ SecretMetadata.encode(message.metadata, writer.uint32(10).fork()).join();
48129
+ }
48130
+ return writer;
48131
+ },
48132
+ decode(input, length) {
48133
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
48134
+ let end = length === void 0 ? reader.len : reader.pos + length;
48135
+ const message = createBaseSecretGetInfoResponse();
48136
+ while (reader.pos < end) {
48137
+ const tag = reader.uint32();
48138
+ switch (tag >>> 3) {
48139
+ case 1: {
48140
+ if (tag !== 10) {
48141
+ break;
48142
+ }
48143
+ message.metadata = SecretMetadata.decode(reader, reader.uint32());
48144
+ continue;
48145
+ }
48146
+ }
48147
+ if ((tag & 7) === 4 || tag === 0) {
48148
+ break;
48149
+ }
48150
+ reader.skip(tag & 7);
48151
+ }
48152
+ return message;
48153
+ },
48154
+ fromJSON(object) {
48155
+ return { metadata: isSet5(object.metadata) ? SecretMetadata.fromJSON(object.metadata) : void 0 };
48156
+ },
48157
+ toJSON(message) {
48158
+ const obj = {};
48159
+ if (message.metadata !== void 0) {
48160
+ obj.metadata = SecretMetadata.toJSON(message.metadata);
48161
+ }
48162
+ return obj;
48163
+ },
48164
+ create(base) {
48165
+ return SecretGetInfoResponse.fromPartial(base ?? {});
48166
+ },
48167
+ fromPartial(object) {
48168
+ const message = createBaseSecretGetInfoResponse();
48169
+ message.metadata = object.metadata !== void 0 && object.metadata !== null ? SecretMetadata.fromPartial(object.metadata) : void 0;
48170
+ return message;
48171
+ }
48172
+ };
48070
48173
  function createBaseSecretGetOrCreateRequest() {
48071
48174
  return { deploymentName: "", environmentName: "", objectCreationType: 0, envDict: {}, appId: "", requiredKeys: [] };
48072
48175
  }
@@ -48613,7 +48716,7 @@ var SecretListResponse = {
48613
48716
  }
48614
48717
  };
48615
48718
  function createBaseSecretMetadata() {
48616
- return { name: "", creationInfo: void 0, keys: [] };
48719
+ return { name: "", creationInfo: void 0, keys: [], environmentName: "" };
48617
48720
  }
48618
48721
  var SecretMetadata = {
48619
48722
  encode(message, writer = new BinaryWriter()) {
@@ -48626,6 +48729,9 @@ var SecretMetadata = {
48626
48729
  for (const v of message.keys) {
48627
48730
  writer.uint32(26).string(v);
48628
48731
  }
48732
+ if (message.environmentName !== "") {
48733
+ writer.uint32(34).string(message.environmentName);
48734
+ }
48629
48735
  return writer;
48630
48736
  },
48631
48737
  decode(input, length) {
@@ -48656,6 +48762,13 @@ var SecretMetadata = {
48656
48762
  message.keys.push(reader.string());
48657
48763
  continue;
48658
48764
  }
48765
+ case 4: {
48766
+ if (tag !== 34) {
48767
+ break;
48768
+ }
48769
+ message.environmentName = reader.string();
48770
+ continue;
48771
+ }
48659
48772
  }
48660
48773
  if ((tag & 7) === 4 || tag === 0) {
48661
48774
  break;
@@ -48668,7 +48781,8 @@ var SecretMetadata = {
48668
48781
  return {
48669
48782
  name: isSet5(object.name) ? globalThis.String(object.name) : "",
48670
48783
  creationInfo: isSet5(object.creationInfo) ? CreationInfo.fromJSON(object.creationInfo) : void 0,
48671
- keys: globalThis.Array.isArray(object?.keys) ? object.keys.map((e) => globalThis.String(e)) : []
48784
+ keys: globalThis.Array.isArray(object?.keys) ? object.keys.map((e) => globalThis.String(e)) : [],
48785
+ environmentName: isSet5(object.environmentName) ? globalThis.String(object.environmentName) : ""
48672
48786
  };
48673
48787
  },
48674
48788
  toJSON(message) {
@@ -48682,6 +48796,9 @@ var SecretMetadata = {
48682
48796
  if (message.keys?.length) {
48683
48797
  obj.keys = message.keys;
48684
48798
  }
48799
+ if (message.environmentName !== "") {
48800
+ obj.environmentName = message.environmentName;
48801
+ }
48685
48802
  return obj;
48686
48803
  },
48687
48804
  create(base) {
@@ -48692,6 +48809,7 @@ var SecretMetadata = {
48692
48809
  message.name = object.name ?? "";
48693
48810
  message.creationInfo = object.creationInfo !== void 0 && object.creationInfo !== null ? CreationInfo.fromPartial(object.creationInfo) : void 0;
48694
48811
  message.keys = object.keys?.map((e) => e) || [];
48812
+ message.environmentName = object.environmentName ?? "";
48695
48813
  return message;
48696
48814
  }
48697
48815
  };
@@ -62076,6 +62194,14 @@ var ModalClientDefinition = {
62076
62194
  responseStream: false,
62077
62195
  options: {}
62078
62196
  },
62197
+ secretGetInfo: {
62198
+ name: "SecretGetInfo",
62199
+ requestType: SecretGetInfoRequest,
62200
+ requestStream: false,
62201
+ responseType: SecretGetInfoResponse,
62202
+ responseStream: false,
62203
+ options: {}
62204
+ },
62079
62205
  secretGetOrCreate: {
62080
62206
  name: "SecretGetOrCreate",
62081
62207
  requestType: SecretGetOrCreateRequest,
@@ -70555,8 +70681,151 @@ var SandboxFilesystem = class {
70555
70681
  };
70556
70682
 
70557
70683
  // src/sandbox_sidecar.ts
70558
- var import_nice_grpc10 = require("nice-grpc");
70684
+ var import_nice_grpc11 = require("nice-grpc");
70559
70685
  var import_uuid = require("uuid");
70686
+
70687
+ // src/volume.ts
70688
+ var import_nice_grpc10 = require("nice-grpc");
70689
+ var VolumeService = class {
70690
+ #client;
70691
+ constructor(client) {
70692
+ this.#client = client;
70693
+ }
70694
+ /**
70695
+ * Reference a {@link Volume} by its name.
70696
+ */
70697
+ async fromName(name, params) {
70698
+ try {
70699
+ const resp = await this.#client.cpClient.volumeGetOrCreate({
70700
+ deploymentName: name,
70701
+ environmentName: this.#client.environmentName(params?.environment),
70702
+ objectCreationType: params?.createIfMissing ? 1 /* OBJECT_CREATION_TYPE_CREATE_IF_MISSING */ : 0 /* OBJECT_CREATION_TYPE_UNSPECIFIED */
70703
+ });
70704
+ this.#client.logger.debug(
70705
+ "Retrieved Volume",
70706
+ "volume_id",
70707
+ resp.volumeId,
70708
+ "volume_name",
70709
+ name
70710
+ );
70711
+ return new Volume(resp.volumeId, name);
70712
+ } catch (err) {
70713
+ if (err instanceof import_nice_grpc10.ClientError && err.code === import_nice_grpc10.Status.NOT_FOUND)
70714
+ throw new NotFoundError(err.details);
70715
+ throw err;
70716
+ }
70717
+ }
70718
+ /**
70719
+ * Create a nameless, temporary {@link Volume}.
70720
+ * It persists until closeEphemeral() is called, or the process exits.
70721
+ */
70722
+ async ephemeral(params = {}) {
70723
+ const resp = await this.#client.cpClient.volumeGetOrCreate({
70724
+ objectCreationType: 5 /* OBJECT_CREATION_TYPE_EPHEMERAL */,
70725
+ environmentName: this.#client.environmentName(params.environment)
70726
+ });
70727
+ this.#client.logger.debug(
70728
+ "Created ephemeral Volume",
70729
+ "volume_id",
70730
+ resp.volumeId
70731
+ );
70732
+ const ephemeralHbManager = new EphemeralHeartbeatManager(
70733
+ () => this.#client.cpClient.volumeHeartbeat({ volumeId: resp.volumeId })
70734
+ );
70735
+ return new Volume(resp.volumeId, void 0, void 0, ephemeralHbManager);
70736
+ }
70737
+ /**
70738
+ * Delete a named {@link Volume}.
70739
+ *
70740
+ * Warning: Deletion is irreversible and will affect any Apps currently using the Volume.
70741
+ */
70742
+ async delete(name, params) {
70743
+ try {
70744
+ const volume = await this.fromName(name, {
70745
+ environment: params?.environment,
70746
+ createIfMissing: false
70747
+ });
70748
+ await this.#client.cpClient.volumeDelete({
70749
+ volumeId: volume.volumeId
70750
+ });
70751
+ this.#client.logger.debug(
70752
+ "Deleted Volume",
70753
+ "volume_name",
70754
+ name,
70755
+ "volume_id",
70756
+ volume.volumeId
70757
+ );
70758
+ } catch (err) {
70759
+ const isNotFound = err instanceof NotFoundError || err instanceof import_nice_grpc10.ClientError && err.code === import_nice_grpc10.Status.NOT_FOUND;
70760
+ if (isNotFound && params?.allowMissing) {
70761
+ return;
70762
+ }
70763
+ throw err;
70764
+ }
70765
+ }
70766
+ };
70767
+ var DEFAULT_MOUNT_OPTIONS = {
70768
+ readOnly: false,
70769
+ subPath: void 0
70770
+ };
70771
+ var Volume = class _Volume {
70772
+ volumeId;
70773
+ name;
70774
+ /**
70775
+ * @hidden
70776
+ * @internal
70777
+ */
70778
+ _mountOptions = DEFAULT_MOUNT_OPTIONS;
70779
+ #ephemeralHbManager;
70780
+ /** @ignore */
70781
+ constructor(volumeId, name, mountOptions, ephemeralHbManager) {
70782
+ this.volumeId = volumeId;
70783
+ this.name = name;
70784
+ if (mountOptions) {
70785
+ this._mountOptions = mountOptions;
70786
+ }
70787
+ this.#ephemeralHbManager = ephemeralHbManager;
70788
+ }
70789
+ /**
70790
+ * Configure options used when mounting this Volume. Fields left undefined preserve their value
70791
+ * from any previous withMountOptions call on the same Volume (stacking).
70792
+ */
70793
+ withMountOptions(params = {}) {
70794
+ let subPath = this._mountOptions.subPath;
70795
+ if (params.subPath !== void 0) {
70796
+ subPath = params.subPath === "/" ? void 0 : params.subPath;
70797
+ }
70798
+ return new _Volume(
70799
+ this.volumeId,
70800
+ this.name,
70801
+ {
70802
+ readOnly: params.readOnly ?? this._mountOptions.readOnly,
70803
+ subPath
70804
+ },
70805
+ this.#ephemeralHbManager
70806
+ );
70807
+ }
70808
+ /** Delete the ephemeral Volume. Only usable with emphemeral Volumes. */
70809
+ closeEphemeral() {
70810
+ if (this.#ephemeralHbManager) {
70811
+ this.#ephemeralHbManager.stop();
70812
+ } else {
70813
+ throw new InvalidError("Volume is not ephemeral.");
70814
+ }
70815
+ }
70816
+ };
70817
+ function volumeToMountProto(mountPath, volume) {
70818
+ const opts = volume._mountOptions ?? DEFAULT_MOUNT_OPTIONS;
70819
+ return {
70820
+ volumeId: volume.volumeId,
70821
+ mountPath,
70822
+ allowBackgroundCommits: true,
70823
+ readOnly: opts.readOnly,
70824
+ subPath: opts.subPath
70825
+ };
70826
+ }
70827
+
70828
+ // src/sandbox_sidecar.ts
70560
70829
  var MAIN_CONTAINER_NAME = "main";
70561
70830
  var CONTAINER_WAIT_POLL_TIMEOUT_SECONDS = 10;
70562
70831
  var CONTROL_PLANE_SIDECAR_CREATE_ENV_VAR = "MODAL_USE_CONTROL_PLANE_SIDECAR_CREATE";
@@ -70573,6 +70842,29 @@ function validateSidecarName(name) {
70573
70842
  );
70574
70843
  }
70575
70844
  }
70845
+ function buildSidecarVolumeMounts(volumes) {
70846
+ const mountPathsByVolumeId = /* @__PURE__ */ new Map();
70847
+ const volumeMounts = [];
70848
+ for (const [mountPath, volume] of Object.entries(volumes ?? {})) {
70849
+ if (!volume) {
70850
+ throw new InvalidError(
70851
+ `Volume mounted at "${mountPath}" must not be null or undefined`
70852
+ );
70853
+ }
70854
+ const mountPaths = mountPathsByVolumeId.get(volume.volumeId) ?? [];
70855
+ mountPaths.push(mountPath);
70856
+ mountPathsByVolumeId.set(volume.volumeId, mountPaths);
70857
+ volumeMounts.push(volumeToMountProto(mountPath, volume));
70858
+ }
70859
+ for (const mountPaths of mountPathsByVolumeId.values()) {
70860
+ if (mountPaths.length > 1) {
70861
+ throw new InvalidError(
70862
+ "The same Volume cannot be mounted at multiple paths in a sidecar: " + mountPaths.sort().join(", ")
70863
+ );
70864
+ }
70865
+ }
70866
+ return volumeMounts;
70867
+ }
70576
70868
  function sidecarContainerFromProto(access, info) {
70577
70869
  return new SidecarContainer(
70578
70870
  access,
@@ -70610,6 +70902,7 @@ var SidecarService = class {
70610
70902
  Object.assign(envDict, params?.env ?? {});
70611
70903
  await hydrateSecrets(this.#access.client, resolvableSecrets);
70612
70904
  const secretIds = collectSecretIds(resolvableSecrets);
70905
+ const volumeMounts = buildSidecarVolumeMounts(params?.volumes);
70613
70906
  const ptyInfo = params?.pty ? defaultSandboxPTYInfo() : void 0;
70614
70907
  const networkAccess = buildOutboundNetworkAccess(
70615
70908
  false,
@@ -70629,6 +70922,7 @@ var SidecarService = class {
70629
70922
  entrypointArgs: command,
70630
70923
  workdir: params?.workdir ?? void 0,
70631
70924
  secretIds,
70925
+ volumeMounts,
70632
70926
  networkAccess,
70633
70927
  ptyInfo
70634
70928
  }),
@@ -70646,17 +70940,18 @@ var SidecarService = class {
70646
70940
  env: envDict,
70647
70941
  workdir: params?.workdir ?? "",
70648
70942
  secretIds,
70943
+ volumeMounts,
70649
70944
  networkAccess,
70650
70945
  ptyInfo
70651
70946
  })
70652
70947
  );
70653
70948
  }
70654
70949
  } catch (err) {
70655
- if (err instanceof import_nice_grpc10.ClientError) {
70656
- if (err.code === import_nice_grpc10.Status.ALREADY_EXISTS) {
70950
+ if (err instanceof import_nice_grpc11.ClientError) {
70951
+ if (err.code === import_nice_grpc11.Status.ALREADY_EXISTS) {
70657
70952
  throw new AlreadyExistsError(err.details || err.message);
70658
70953
  }
70659
- if (err.code === import_nice_grpc10.Status.INVALID_ARGUMENT) {
70954
+ if (err.code === import_nice_grpc11.Status.INVALID_ARGUMENT) {
70660
70955
  throw new InvalidError(err.details || err.message);
70661
70956
  }
70662
70957
  }
@@ -70682,7 +70977,7 @@ var SidecarService = class {
70682
70977
  })
70683
70978
  );
70684
70979
  } catch (err) {
70685
- if (err instanceof import_nice_grpc10.ClientError && err.code === import_nice_grpc10.Status.NOT_FOUND) {
70980
+ if (err instanceof import_nice_grpc11.ClientError && err.code === import_nice_grpc11.Status.NOT_FOUND) {
70686
70981
  throw new NotFoundError(`Sidecar container "${name}" not found`);
70687
70982
  }
70688
70983
  throw err;
@@ -71053,151 +71348,11 @@ var SandboxSnapshotService = class {
71053
71348
  }
71054
71349
  };
71055
71350
 
71056
- // src/volume.ts
71057
- var import_nice_grpc11 = require("nice-grpc");
71058
- var VolumeService = class {
71059
- #client;
71060
- constructor(client) {
71061
- this.#client = client;
71062
- }
71063
- /**
71064
- * Reference a {@link Volume} by its name.
71065
- */
71066
- async fromName(name, params) {
71067
- try {
71068
- const resp = await this.#client.cpClient.volumeGetOrCreate({
71069
- deploymentName: name,
71070
- environmentName: this.#client.environmentName(params?.environment),
71071
- objectCreationType: params?.createIfMissing ? 1 /* OBJECT_CREATION_TYPE_CREATE_IF_MISSING */ : 0 /* OBJECT_CREATION_TYPE_UNSPECIFIED */
71072
- });
71073
- this.#client.logger.debug(
71074
- "Retrieved Volume",
71075
- "volume_id",
71076
- resp.volumeId,
71077
- "volume_name",
71078
- name
71079
- );
71080
- return new Volume(resp.volumeId, name);
71081
- } catch (err) {
71082
- if (err instanceof import_nice_grpc11.ClientError && err.code === import_nice_grpc11.Status.NOT_FOUND)
71083
- throw new NotFoundError(err.details);
71084
- throw err;
71085
- }
71086
- }
71087
- /**
71088
- * Create a nameless, temporary {@link Volume}.
71089
- * It persists until closeEphemeral() is called, or the process exits.
71090
- */
71091
- async ephemeral(params = {}) {
71092
- const resp = await this.#client.cpClient.volumeGetOrCreate({
71093
- objectCreationType: 5 /* OBJECT_CREATION_TYPE_EPHEMERAL */,
71094
- environmentName: this.#client.environmentName(params.environment)
71095
- });
71096
- this.#client.logger.debug(
71097
- "Created ephemeral Volume",
71098
- "volume_id",
71099
- resp.volumeId
71100
- );
71101
- const ephemeralHbManager = new EphemeralHeartbeatManager(
71102
- () => this.#client.cpClient.volumeHeartbeat({ volumeId: resp.volumeId })
71103
- );
71104
- return new Volume(resp.volumeId, void 0, void 0, ephemeralHbManager);
71105
- }
71106
- /**
71107
- * Delete a named {@link Volume}.
71108
- *
71109
- * Warning: Deletion is irreversible and will affect any Apps currently using the Volume.
71110
- */
71111
- async delete(name, params) {
71112
- try {
71113
- const volume = await this.fromName(name, {
71114
- environment: params?.environment,
71115
- createIfMissing: false
71116
- });
71117
- await this.#client.cpClient.volumeDelete({
71118
- volumeId: volume.volumeId
71119
- });
71120
- this.#client.logger.debug(
71121
- "Deleted Volume",
71122
- "volume_name",
71123
- name,
71124
- "volume_id",
71125
- volume.volumeId
71126
- );
71127
- } catch (err) {
71128
- const isNotFound = err instanceof NotFoundError || err instanceof import_nice_grpc11.ClientError && err.code === import_nice_grpc11.Status.NOT_FOUND;
71129
- if (isNotFound && params?.allowMissing) {
71130
- return;
71131
- }
71132
- throw err;
71133
- }
71134
- }
71135
- };
71136
- var DEFAULT_MOUNT_OPTIONS = {
71137
- readOnly: false,
71138
- subPath: void 0
71139
- };
71140
- var Volume = class _Volume {
71141
- volumeId;
71142
- name;
71143
- /**
71144
- * @hidden
71145
- * @internal
71146
- */
71147
- _mountOptions = DEFAULT_MOUNT_OPTIONS;
71148
- #ephemeralHbManager;
71149
- /** @ignore */
71150
- constructor(volumeId, name, mountOptions, ephemeralHbManager) {
71151
- this.volumeId = volumeId;
71152
- this.name = name;
71153
- if (mountOptions) {
71154
- this._mountOptions = mountOptions;
71155
- }
71156
- this.#ephemeralHbManager = ephemeralHbManager;
71157
- }
71158
- /**
71159
- * Configure options used when mounting this Volume. Fields left undefined preserve their value
71160
- * from any previous withMountOptions call on the same Volume (stacking).
71161
- */
71162
- withMountOptions(params = {}) {
71163
- let subPath = this._mountOptions.subPath;
71164
- if (params.subPath !== void 0) {
71165
- subPath = params.subPath === "/" ? void 0 : params.subPath;
71166
- }
71167
- return new _Volume(
71168
- this.volumeId,
71169
- this.name,
71170
- {
71171
- readOnly: params.readOnly ?? this._mountOptions.readOnly,
71172
- subPath
71173
- },
71174
- this.#ephemeralHbManager
71175
- );
71176
- }
71177
- /** Delete the ephemeral Volume. Only usable with emphemeral Volumes. */
71178
- closeEphemeral() {
71179
- if (this.#ephemeralHbManager) {
71180
- this.#ephemeralHbManager.stop();
71181
- } else {
71182
- throw new InvalidError("Volume is not ephemeral.");
71183
- }
71184
- }
71185
- };
71186
- function volumeToMountProto(mountPath, volume) {
71187
- const opts = volume._mountOptions ?? DEFAULT_MOUNT_OPTIONS;
71188
- return {
71189
- volumeId: volume.volumeId,
71190
- mountPath,
71191
- allowBackgroundCommits: true,
71192
- readOnly: opts.readOnly,
71193
- subPath: opts.subPath
71194
- };
71195
- }
71196
-
71197
71351
  // src/sandbox.ts
71198
71352
  var SB_LOGS_INITIAL_DELAY_MS = 10;
71199
71353
  var SB_LOGS_DELAY_FACTOR = 2;
71200
71354
  var SB_LOGS_MAX_RETRIES = 10;
71355
+ var SB_LOGS_TASK_ID_TIMEOUT_SECONDS = 0.5;
71201
71356
  var TTL_NO_EXPIRY_SENTINEL = -1;
71202
71357
  var CUSTOMER_SUPPLIED_ENCRYPTION_KEY_MIN_LENGTH = 16;
71203
71358
  var CUSTOMER_SUPPLIED_ENCRYPTION_KEY_MAX_LENGTH = 512;
@@ -72055,6 +72210,7 @@ function buildTaskSnapshotDirectoryRequestProto(taskId, path2, snapshotId, ttlSe
72055
72210
  var Sandbox2 = class _Sandbox {
72056
72211
  #client;
72057
72212
  sandboxId;
72213
+ #appId;
72058
72214
  #stdin;
72059
72215
  #stdout;
72060
72216
  #stderr;
@@ -72101,6 +72257,25 @@ var Sandbox2 = class _Sandbox {
72101
72257
  }
72102
72258
  return this.#stderr;
72103
72259
  }
72260
+ /**
72261
+ * Access persisted entrypoint logs emitted by this Sandbox.
72262
+ *
72263
+ * Use {@link SandboxLogsManager#fetch fetch()} to read logs from a UTC time
72264
+ * range and {@link SandboxLogsManager#tail tail()} to read the most recent
72265
+ * logs.
72266
+ *
72267
+ * This is separate from {@link Sandbox#stdout stdout} and
72268
+ * {@link Sandbox#stderr stderr}, which expose the Sandbox's live output
72269
+ * streams.
72270
+ */
72271
+ get logs() {
72272
+ return new SandboxLogsManager(
72273
+ this.#client,
72274
+ this.sandboxId,
72275
+ () => this.#getAppId(),
72276
+ () => this.#resolveTaskIdForLogs()
72277
+ );
72278
+ }
72104
72279
  #outputStream(fileDescriptor) {
72105
72280
  if (this.#isV2) {
72106
72281
  this.#ensureAttached();
@@ -72333,8 +72508,11 @@ var Sandbox2 = class _Sandbox {
72333
72508
  }
72334
72509
  return this.#client.cpClient.sandboxWait(req);
72335
72510
  }
72336
- #sandboxGetTaskId(signal) {
72337
- const req = { sandboxId: this.sandboxId };
72511
+ #sandboxGetTaskId(signal, timeout) {
72512
+ const req = SandboxGetTaskIdRequest.create({
72513
+ sandboxId: this.sandboxId,
72514
+ timeout
72515
+ });
72338
72516
  if (this.#isV2) {
72339
72517
  return this.#client.cpClient.sandboxGetTaskIdV2(req, { signal });
72340
72518
  }
@@ -72364,6 +72542,42 @@ var Sandbox2 = class _Sandbox {
72364
72542
  }
72365
72543
  static #maxGetTaskIdAttempts = 600;
72366
72544
  // 5 minutes at 500ms intervals
72545
+ async #getAppId() {
72546
+ if (this.#appId !== void 0) {
72547
+ return this.#appId;
72548
+ }
72549
+ const resp = await this.#sandboxWait(0);
72550
+ const appId = resp.metadata?.appId;
72551
+ if (!appId) {
72552
+ throw new ExecutionError(
72553
+ "Sandbox app ID should have been set during hydration"
72554
+ );
72555
+ }
72556
+ this.#appId = appId;
72557
+ return appId;
72558
+ }
72559
+ async #resolveTaskIdForLogs() {
72560
+ if (this.#taskId !== void 0) {
72561
+ return this.#taskId;
72562
+ }
72563
+ let resp;
72564
+ try {
72565
+ resp = await this.#sandboxGetTaskId(
72566
+ void 0,
72567
+ SB_LOGS_TASK_ID_TIMEOUT_SECONDS
72568
+ );
72569
+ } catch (error) {
72570
+ if (error instanceof import_nice_grpc12.ClientError && error.code === import_nice_grpc12.Status.DEADLINE_EXCEEDED) {
72571
+ throw new ExecutionError("Sandbox task ID is not available yet.");
72572
+ }
72573
+ throw error;
72574
+ }
72575
+ if (!resp.taskId) {
72576
+ throw new ExecutionError("Sandbox task ID is not available.");
72577
+ }
72578
+ this.#taskId = resp.taskId;
72579
+ return this.#taskId;
72580
+ }
72367
72581
  async #getTaskId(signal) {
72368
72582
  if (this.#taskId !== void 0) {
72369
72583
  return this.#taskId;
@@ -73372,7 +73586,7 @@ var AuthTokenManager = class {
73372
73586
  };
73373
73587
 
73374
73588
  // src/version.ts
73375
- var SDK_VERSION = "0.10.2-dev.5";
73589
+ var SDK_VERSION = "0.10.2-dev.7";
73376
73590
  function getSDKVersion() {
73377
73591
  return SDK_VERSION;
73378
73592
  }
@@ -74113,6 +74327,8 @@ function contextIds(batch, item, ObjectId) {
74113
74327
  item.inputId || batch.inputId,
74114
74328
  item.containerId || batch.taskId
74115
74329
  ].filter((id) => id);
74330
+ case "sb":
74331
+ return [item.containerId || batch.taskId].filter((id) => id);
74116
74332
  default:
74117
74333
  return [];
74118
74334
  }
@@ -74933,6 +75149,97 @@ var FunctionCallLogsManager = class extends LogsManager {
74933
75149
  );
74934
75150
  }
74935
75151
  };
75152
+ var SandboxLogsManager = class {
75153
+ /** @ignore */
75154
+ constructor(client, sandboxId, getAppId, getTaskId) {
75155
+ this.client = client;
75156
+ this.sandboxId = sandboxId;
75157
+ this.getAppId = getAppId;
75158
+ this.getTaskId = getTaskId;
75159
+ }
75160
+ /**
75161
+ * Fetch Sandbox entrypoint logs corresponding to a UTC time range and
75162
+ * optional filters.
75163
+ *
75164
+ * Entries are returned in chronological order.
75165
+ *
75166
+ * @returns An async iterable of {@link LogEntry} objects.
75167
+ *
75168
+ * @example
75169
+ * ```typescript
75170
+ * import { ModalClient } from "modal";
75171
+ *
75172
+ * const modal = new ModalClient();
75173
+ * const sandbox = await modal.sandboxes.fromId("sb-...");
75174
+ *
75175
+ * for await (const entry of sandbox.logs.fetch({
75176
+ * since: new Date(Date.now() - 60 * 60 * 1_000),
75177
+ * source: "stdout",
75178
+ * })) {
75179
+ * process.stdout.write(entry.message);
75180
+ * }
75181
+ * ```
75182
+ */
75183
+ async *fetch(params) {
75184
+ const [appId, taskId] = await Promise.all([
75185
+ this.getAppId(),
75186
+ this.getTaskId()
75187
+ ]);
75188
+ if (!taskId) {
75189
+ throw new ExecutionError(
75190
+ "Cannot resolve Sandbox metadata needed for fetching logs. Wait for the Sandbox to be scheduled."
75191
+ );
75192
+ }
75193
+ yield* fetchLogs(
75194
+ this.client,
75195
+ {
75196
+ appId,
75197
+ objectId: this.sandboxId,
75198
+ taskId,
75199
+ source: sourceToFileDescriptor(params.source),
75200
+ searchText: params.searchText
75201
+ },
75202
+ params.since,
75203
+ params.until
75204
+ );
75205
+ }
75206
+ /**
75207
+ * Fetch the most recent Sandbox entrypoint logs.
75208
+ *
75209
+ * Entries are returned in chronological order.
75210
+ *
75211
+ * @returns An async iterable of {@link LogEntry} objects.
75212
+ *
75213
+ * @example
75214
+ * ```typescript
75215
+ * import { ModalClient } from "modal";
75216
+ *
75217
+ * const modal = new ModalClient();
75218
+ * const sandbox = await modal.sandboxes.fromId("sb-...");
75219
+ *
75220
+ * for await (const entry of sandbox.logs.tail({ entries: 20 })) {
75221
+ * process.stdout.write(entry.message);
75222
+ * }
75223
+ * ```
75224
+ */
75225
+ async *tail(params = {}) {
75226
+ const [appId, taskId] = await Promise.all([
75227
+ this.getAppId(),
75228
+ this.getTaskId()
75229
+ ]);
75230
+ if (!taskId) {
75231
+ throw new ExecutionError(
75232
+ "Sandbox task ID cannot be empty when fetching logs. Wait for the Sandbox to be scheduled."
75233
+ );
75234
+ }
75235
+ yield* tailLogs(this.client, params.entries ?? 100, {
75236
+ appId,
75237
+ objectId: this.sandboxId,
75238
+ taskId,
75239
+ source: sourceToFileDescriptor(params.source)
75240
+ });
75241
+ }
75242
+ };
74936
75243
 
74937
75244
  // src/function_call.ts
74938
75245
  var FunctionCallService = class {
@@ -75870,6 +76177,7 @@ var OutboundPolicy2 = class _OutboundPolicy {
75870
76177
  SandboxFilesystemNotFoundError,
75871
76178
  SandboxFilesystemPathAlreadyExistsError,
75872
76179
  SandboxFilesystemPermissionError,
76180
+ SandboxLogsManager,
75873
76181
  SandboxService,
75874
76182
  SandboxSnapshot,
75875
76183
  SandboxSnapshotService,