tensorlake 0.5.0 → 0.5.2

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.
@@ -1 +1 @@
1
- export { y as CreateSandboxImageOptions, A as DockerfileBuildPlan, B as DockerfileInstruction, Q as SandboxImageSource, Y as createSandboxImage, _ as defaultRegisteredName, $ as loadDockerfilePlan, a0 as loadImagePlan, a1 as logicalDockerfileLines, a2 as runCreateSandboxImageCli } from './sandbox-image-CMJ_FOOV.cjs';
1
+ export { z as CreateSandboxImageOptions, B as DockerfileBuildPlan, E as DockerfileInstruction, T as SandboxImageSource, Y as createSandboxImage, _ as defaultRegisteredName, $ as loadDockerfilePlan, a0 as loadImagePlan, a1 as logicalDockerfileLines, a2 as runCreateSandboxImageCli } from './sandbox-image-B0WMhyoM.cjs';
@@ -1 +1 @@
1
- export { y as CreateSandboxImageOptions, A as DockerfileBuildPlan, B as DockerfileInstruction, Q as SandboxImageSource, Y as createSandboxImage, _ as defaultRegisteredName, $ as loadDockerfilePlan, a0 as loadImagePlan, a1 as logicalDockerfileLines, a2 as runCreateSandboxImageCli } from './sandbox-image-CMJ_FOOV.js';
1
+ export { z as CreateSandboxImageOptions, B as DockerfileBuildPlan, E as DockerfileInstruction, T as SandboxImageSource, Y as createSandboxImage, _ as defaultRegisteredName, $ as loadDockerfilePlan, a0 as loadImagePlan, a1 as logicalDockerfileLines, a2 as runCreateSandboxImageCli } from './sandbox-image-B0WMhyoM.js';
@@ -3103,8 +3103,11 @@ var init_sandbox = __esm({
3103
3103
  wsHeaders;
3104
3104
  ownsSandbox = false;
3105
3105
  lifecycleClient = null;
3106
+ lifecycleIdentifier;
3107
+ sandboxName = null;
3106
3108
  constructor(options) {
3107
3109
  this.sandboxId = options.sandboxId;
3110
+ this.lifecycleIdentifier = options.sandboxId;
3108
3111
  const proxyUrl = options.proxyUrl ?? SANDBOX_PROXY_URL;
3109
3112
  const { baseUrl, hostHeader } = resolveProxyTarget(proxyUrl, options.sandboxId);
3110
3113
  this.baseUrl = baseUrl;
@@ -3130,6 +3133,17 @@ var init_sandbox = __esm({
3130
3133
  routingHint: options.routingHint
3131
3134
  });
3132
3135
  }
3136
+ get name() {
3137
+ return this.sandboxName;
3138
+ }
3139
+ /** @internal Used by client wiring to keep locally cached name in sync. */
3140
+ _setName(name) {
3141
+ this.sandboxName = name;
3142
+ }
3143
+ /** @internal Used by lifecycle operations to pin to canonical sandbox ID. */
3144
+ _setLifecycleIdentifier(identifier) {
3145
+ this.lifecycleIdentifier = identifier;
3146
+ }
3133
3147
  /** @internal Used by SandboxClient.createAndConnect to set ownership. */
3134
3148
  _setOwner(client) {
3135
3149
  this.ownsSandbox = true;
@@ -3167,9 +3181,15 @@ var init_sandbox = __esm({
3167
3181
  /* _internal */
3168
3182
  true
3169
3183
  );
3170
- await client.get(options.sandboxId);
3171
- const sandbox = client.connect(options.sandboxId, options.proxyUrl, options.routingHint);
3184
+ const info = await client.get(options.sandboxId);
3185
+ const sandbox = client.connect(
3186
+ info.sandboxId,
3187
+ options.proxyUrl,
3188
+ options.routingHint ?? info.routingHint
3189
+ );
3172
3190
  sandbox.lifecycleClient = client;
3191
+ sandbox._setLifecycleIdentifier(info.sandboxId);
3192
+ sandbox._setName(info.name ?? null);
3173
3193
  return sandbox;
3174
3194
  }
3175
3195
  // --- Static snapshot management ---
@@ -3202,6 +3222,32 @@ var init_sandbox = __esm({
3202
3222
  }
3203
3223
  return this.lifecycleClient;
3204
3224
  }
3225
+ /**
3226
+ * Fetch the current sandbox status from the server.
3227
+ *
3228
+ * Always hits the network — the value is not cached locally because the
3229
+ * status changes over the sandbox's lifecycle.
3230
+ */
3231
+ async status() {
3232
+ const client = this.requireLifecycleClient("read_status");
3233
+ const info = await client.get(this.lifecycleIdentifier);
3234
+ this._setLifecycleIdentifier(info.sandboxId);
3235
+ this._setName(info.name ?? null);
3236
+ return info.status;
3237
+ }
3238
+ /**
3239
+ * Update this sandbox's properties (name, exposed ports, proxy auth).
3240
+ *
3241
+ * Naming an ephemeral sandbox makes it non-ephemeral and enables
3242
+ * suspend/resume.
3243
+ */
3244
+ async update(options) {
3245
+ const client = this.requireLifecycleClient("update");
3246
+ const info = await client.update(this.lifecycleIdentifier, options);
3247
+ this._setLifecycleIdentifier(info.sandboxId);
3248
+ this._setName(info.name ?? null);
3249
+ return info;
3250
+ }
3205
3251
  /**
3206
3252
  * Suspend this sandbox.
3207
3253
  *
@@ -3210,7 +3256,7 @@ var init_sandbox = __esm({
3210
3256
  */
3211
3257
  async suspend(options) {
3212
3258
  const client = this.requireLifecycleClient("suspend");
3213
- await client.suspend(this.sandboxId, options);
3259
+ await client.suspend(this.lifecycleIdentifier, options);
3214
3260
  }
3215
3261
  /**
3216
3262
  * Resume this sandbox.
@@ -3220,7 +3266,7 @@ var init_sandbox = __esm({
3220
3266
  */
3221
3267
  async resume(options) {
3222
3268
  const client = this.requireLifecycleClient("resume");
3223
- await client.resume(this.sandboxId, options);
3269
+ await client.resume(this.lifecycleIdentifier, options);
3224
3270
  }
3225
3271
  /**
3226
3272
  * Create a snapshot of this sandbox's filesystem and wait for it to
@@ -3233,10 +3279,10 @@ var init_sandbox = __esm({
3233
3279
  async checkpoint(options) {
3234
3280
  const client = this.requireLifecycleClient("checkpoint");
3235
3281
  if (options?.wait === false) {
3236
- await client.snapshot(this.sandboxId, { contentMode: options.contentMode });
3282
+ await client.snapshot(this.lifecycleIdentifier, { contentMode: options.contentMode });
3237
3283
  return void 0;
3238
3284
  }
3239
- return client.snapshotAndWait(this.sandboxId, {
3285
+ return client.snapshotAndWait(this.lifecycleIdentifier, {
3240
3286
  timeout: options?.timeout,
3241
3287
  pollInterval: options?.pollInterval,
3242
3288
  contentMode: options?.contentMode
@@ -3248,7 +3294,8 @@ var init_sandbox = __esm({
3248
3294
  async listSnapshots() {
3249
3295
  const client = this.requireLifecycleClient("listSnapshots");
3250
3296
  const all = await client.listSnapshots();
3251
- return all.filter((s) => s.sandboxId === this.sandboxId);
3297
+ const filtered = all.filter((s) => s.sandboxId === this.lifecycleIdentifier);
3298
+ return Object.assign(filtered, { traceId: all.traceId });
3252
3299
  }
3253
3300
  /** Close the HTTP client. The sandbox keeps running. */
3254
3301
  close() {
@@ -3261,7 +3308,7 @@ var init_sandbox = __esm({
3261
3308
  this.lifecycleClient = null;
3262
3309
  this.close();
3263
3310
  if (client) {
3264
- await client.delete(this.sandboxId);
3311
+ await client.delete(this.lifecycleIdentifier);
3265
3312
  }
3266
3313
  }
3267
3314
  // --- High-level convenience ---
@@ -3342,7 +3389,8 @@ var init_sandbox = __esm({
3342
3389
  "GET",
3343
3390
  "/api/v1/processes"
3344
3391
  );
3345
- return (raw.processes ?? []).map((p) => fromSnakeKeys(p));
3392
+ const processes = (raw.processes ?? []).map((p) => fromSnakeKeys(p));
3393
+ return Object.assign(processes, { traceId: raw.traceId });
3346
3394
  }
3347
3395
  /** Get current status and metadata for a process by PID. */
3348
3396
  async getProcess(pid) {
@@ -3594,6 +3642,38 @@ __export(client_exports, {
3594
3642
  function sleep2(ms) {
3595
3643
  return new Promise((resolve) => setTimeout(resolve, ms));
3596
3644
  }
3645
+ function formatStartupFailureMessage(sandboxId, status, options) {
3646
+ const prefix = status === "terminated" /* TERMINATED */ ? `Sandbox ${sandboxId} terminated during startup` : `Sandbox ${sandboxId} became ${status} during startup`;
3647
+ const detail = formatErrorDetails(options.errorDetails);
3648
+ if (detail) {
3649
+ return `${prefix}: ${detail}`;
3650
+ }
3651
+ if (options.terminationReason) {
3652
+ return `${prefix}: termination reason: ${options.terminationReason}`;
3653
+ }
3654
+ return prefix;
3655
+ }
3656
+ function formatErrorDetails(errorDetails) {
3657
+ if (errorDetails == null) return void 0;
3658
+ if (typeof errorDetails === "string") {
3659
+ const detail = errorDetails.trim();
3660
+ return detail || void 0;
3661
+ }
3662
+ if (Array.isArray(errorDetails)) {
3663
+ const parts = errorDetails.map((item) => formatErrorDetails(item)).filter((item) => Boolean(item));
3664
+ return parts.length > 0 ? parts.join("; ") : JSON.stringify(errorDetails);
3665
+ }
3666
+ if (typeof errorDetails === "object") {
3667
+ for (const key of ["message", "detail", "error", "reason"]) {
3668
+ const value = errorDetails[key];
3669
+ if (typeof value === "string" && value.trim()) {
3670
+ return value.trim();
3671
+ }
3672
+ }
3673
+ return JSON.stringify(errorDetails);
3674
+ }
3675
+ return String(errorDetails);
3676
+ }
3597
3677
  function normalizeUserPorts(ports) {
3598
3678
  return dedupeAndSortPorts(ports.map(validateUserPort));
3599
3679
  }
@@ -3679,7 +3759,7 @@ var init_client = __esm({
3679
3759
  resources: {
3680
3760
  cpus: options?.cpus ?? 1,
3681
3761
  memory_mb: options?.memoryMb ?? 1024,
3682
- ephemeral_disk_mb: options?.ephemeralDiskMb ?? 1024
3762
+ ...options?.diskMb != null ? { disk_mb: options.diskMb } : {}
3683
3763
  }
3684
3764
  };
3685
3765
  if (options?.image != null) body.image = options.image;
@@ -3717,9 +3797,10 @@ var init_client = __esm({
3717
3797
  "GET",
3718
3798
  this.path("sandboxes")
3719
3799
  );
3720
- return (raw.sandboxes ?? []).map(
3800
+ const sandboxes = (raw.sandboxes ?? []).map(
3721
3801
  (s) => fromSnakeKeys(s, "sandboxId")
3722
3802
  );
3803
+ return Object.assign(sandboxes, { traceId: raw.traceId });
3723
3804
  }
3724
3805
  /** Update sandbox properties such as name, exposed ports, and proxy auth settings. */
3725
3806
  async update(sandboxId, options) {
@@ -3889,9 +3970,10 @@ var init_client = __esm({
3889
3970
  "GET",
3890
3971
  this.path("snapshots")
3891
3972
  );
3892
- return (raw.snapshots ?? []).map(
3973
+ const snapshots = (raw.snapshots ?? []).map(
3893
3974
  (s) => fromSnakeKeys(s, "snapshotId")
3894
3975
  );
3976
+ return Object.assign(snapshots, { traceId: raw.traceId });
3895
3977
  }
3896
3978
  /** Delete a snapshot by ID. */
3897
3979
  async deleteSnapshot(snapshotId) {
@@ -3971,9 +4053,10 @@ var init_client = __esm({
3971
4053
  "GET",
3972
4054
  this.path("sandbox-pools")
3973
4055
  );
3974
- return (raw.pools ?? []).map(
4056
+ const pools = (raw.pools ?? []).map(
3975
4057
  (p) => fromSnakeKeys(p, "poolId")
3976
4058
  );
4059
+ return Object.assign(pools, { traceId: raw.traceId });
3977
4060
  }
3978
4061
  /** Replace the configuration of an existing sandbox pool. */
3979
4062
  async updatePool(poolId, options) {
@@ -4028,30 +4111,39 @@ var init_client = __esm({
4028
4111
  */
4029
4112
  async createAndConnect(options) {
4030
4113
  const startupTimeout = options?.startupTimeout ?? 60;
4031
- let result;
4032
- if (options?.poolId != null) {
4033
- result = await this.claim(options.poolId);
4034
- } else {
4035
- result = await this.create(options);
4036
- }
4037
- if (result.status === "running" /* RUNNING */) {
4038
- const sandbox = this.connect(result.sandboxId, options?.proxyUrl, result.routingHint);
4114
+ const result = options?.poolId != null ? await this.claim(options.poolId) : await this.create(options);
4115
+ const requestedName = options?.poolId != null ? null : options?.name ?? null;
4116
+ const finishConnect = (routingHint, name) => {
4117
+ const sandbox = this.connect(result.sandboxId, options?.proxyUrl, routingHint);
4039
4118
  sandbox._setOwner(this);
4040
4119
  sandbox.traceId = result.traceId;
4120
+ sandbox._setLifecycleIdentifier(result.sandboxId);
4121
+ sandbox._setName(name ?? requestedName);
4041
4122
  return sandbox;
4123
+ };
4124
+ if (result.status === "running" /* RUNNING */) {
4125
+ return finishConnect(result.routingHint, result.name);
4126
+ }
4127
+ if (result.status === "suspended" /* SUSPENDED */ || result.status === "terminated" /* TERMINATED */) {
4128
+ throw new SandboxError(
4129
+ formatStartupFailureMessage(result.sandboxId, result.status, {
4130
+ errorDetails: result.errorDetails,
4131
+ terminationReason: result.terminationReason
4132
+ })
4133
+ );
4042
4134
  }
4043
4135
  const deadline = Date.now() + startupTimeout * 1e3;
4044
4136
  while (Date.now() < deadline) {
4045
4137
  const info = await this.get(result.sandboxId);
4046
4138
  if (info.status === "running" /* RUNNING */) {
4047
- const sandbox = this.connect(result.sandboxId, options?.proxyUrl, info.routingHint);
4048
- sandbox._setOwner(this);
4049
- sandbox.traceId = result.traceId;
4050
- return sandbox;
4139
+ return finishConnect(info.routingHint, info.name);
4051
4140
  }
4052
- if (info.status === "terminated" /* TERMINATED */) {
4141
+ if (info.status === "suspended" /* SUSPENDED */ || info.status === "terminated" /* TERMINATED */) {
4053
4142
  throw new SandboxError(
4054
- `Sandbox ${result.sandboxId} terminated during startup`
4143
+ formatStartupFailureMessage(result.sandboxId, info.status, {
4144
+ errorDetails: info.errorDetails,
4145
+ terminationReason: info.terminationReason
4146
+ })
4055
4147
  );
4056
4148
  }
4057
4149
  await sleep2(500);
@@ -4728,7 +4820,7 @@ async function executeDockerfilePlan(sandbox, plan, emit, sleep3) {
4728
4820
  );
4729
4821
  }
4730
4822
  }
4731
- async function registerImage(context, name, dockerfile, snapshotId, snapshotUri, isPublic) {
4823
+ async function registerImage(context, name, dockerfile, snapshotId, snapshotSandboxId, snapshotUri, snapshotSizeBytes, rootfsDiskBytes, isPublic) {
4732
4824
  if (!context.organizationId || !context.projectId) {
4733
4825
  throw new Error(
4734
4826
  "Organization ID and Project ID are required. Run 'tl login' and 'tl init'."
@@ -4755,8 +4847,11 @@ async function registerImage(context, name, dockerfile, snapshotId, snapshotUri,
4755
4847
  name,
4756
4848
  dockerfile,
4757
4849
  snapshotId,
4850
+ snapshotSandboxId,
4758
4851
  snapshotUri,
4759
- isPublic
4852
+ snapshotSizeBytes,
4853
+ rootfsDiskBytes,
4854
+ public: isPublic
4760
4855
  })
4761
4856
  });
4762
4857
  if (!response.ok) {
@@ -4786,7 +4881,8 @@ async function createSandboxImage(source, options = {}, deps = {}) {
4786
4881
  sandbox = await client.createAndConnect({
4787
4882
  ...plan.baseImage == null ? {} : { image: plan.baseImage },
4788
4883
  cpus: options.cpus ?? 2,
4789
- memoryMb: options.memoryMb ?? 4096
4884
+ memoryMb: options.memoryMb ?? 4096,
4885
+ ...options.diskMb != null ? { diskMb: options.diskMb } : {}
4790
4886
  });
4791
4887
  emit({
4792
4888
  type: "status",
@@ -4799,14 +4895,23 @@ async function createSandboxImage(source, options = {}, deps = {}) {
4799
4895
  });
4800
4896
  emit({
4801
4897
  type: "snapshot_created",
4802
- snapshot_id: snapshot.snapshotId,
4803
- snapshot_uri: snapshot.snapshotUri ?? null
4898
+ snapshot_id: snapshot.snapshotId
4804
4899
  });
4805
4900
  if (!snapshot.snapshotUri) {
4806
4901
  throw new Error(
4807
4902
  `Snapshot ${snapshot.snapshotId} is missing snapshotUri and cannot be registered as a sandbox image.`
4808
4903
  );
4809
4904
  }
4905
+ if (snapshot.sizeBytes == null) {
4906
+ throw new Error(
4907
+ `Snapshot ${snapshot.snapshotId} is missing sizeBytes and cannot be registered as a sandbox image.`
4908
+ );
4909
+ }
4910
+ if (snapshot.rootfsDiskBytes == null) {
4911
+ throw new Error(
4912
+ `Snapshot ${snapshot.snapshotId} is missing rootfsDiskBytes and cannot be registered as a sandbox image.`
4913
+ );
4914
+ }
4810
4915
  emit({
4811
4916
  type: "status",
4812
4917
  message: `Registering image '${plan.registeredName}'...`
@@ -4816,7 +4921,10 @@ async function createSandboxImage(source, options = {}, deps = {}) {
4816
4921
  plan.registeredName,
4817
4922
  plan.dockerfileText,
4818
4923
  snapshot.snapshotId,
4924
+ snapshot.sandboxId,
4819
4925
  snapshot.snapshotUri,
4926
+ snapshot.sizeBytes,
4927
+ snapshot.rootfsDiskBytes,
4820
4928
  options.isPublic ?? false
4821
4929
  );
4822
4930
  emit({
@@ -4844,27 +4952,33 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
4844
4952
  name: { type: "string", short: "n" },
4845
4953
  cpus: { type: "string" },
4846
4954
  memory: { type: "string" },
4955
+ disk: { type: "string" },
4847
4956
  public: { type: "boolean", default: false }
4848
4957
  }
4849
4958
  });
4850
4959
  const dockerfilePath = parsed.positionals[0];
4851
4960
  if (!dockerfilePath) {
4852
- throw new Error("Usage: tensorlake-create-sandbox-image <dockerfile_path> [--name NAME] [--cpus N] [--memory MB] [--public]");
4961
+ throw new Error("Usage: tensorlake-create-sandbox-image <dockerfile_path> [--name NAME] [--cpus N] [--memory MB] [--disk GB] [--public]");
4853
4962
  }
4854
4963
  const cpus = parsed.values.cpus != null ? Number(parsed.values.cpus) : void 0;
4855
4964
  const memoryMb = parsed.values.memory != null ? Number(parsed.values.memory) : void 0;
4965
+ const diskGb = parsed.values.disk != null ? Number(parsed.values.disk) : void 0;
4856
4966
  if (cpus != null && !Number.isFinite(cpus)) {
4857
4967
  throw new Error(`Invalid --cpus value: ${parsed.values.cpus}`);
4858
4968
  }
4859
4969
  if (memoryMb != null && !Number.isInteger(memoryMb)) {
4860
4970
  throw new Error(`Invalid --memory value: ${parsed.values.memory}`);
4861
4971
  }
4972
+ if (diskGb != null && !Number.isInteger(diskGb)) {
4973
+ throw new Error(`Invalid --disk value: ${parsed.values.disk}`);
4974
+ }
4862
4975
  await createSandboxImage(
4863
4976
  dockerfilePath,
4864
4977
  {
4865
4978
  registeredName: parsed.values.name,
4866
4979
  cpus,
4867
4980
  memoryMb,
4981
+ diskMb: diskGb != null ? diskGb * 1024 : void 0,
4868
4982
  isPublic: parsed.values.public
4869
4983
  },
4870
4984
  { emit: ndjsonStdoutEmit }