@blaxel/core 0.2.83 → 0.2.84-preview.155

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.
Files changed (47) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/common/settings.js +10 -5
  3. package/dist/cjs/sandbox/action.js +11 -2
  4. package/dist/cjs/sandbox/codegen/codegen.js +4 -6
  5. package/dist/cjs/sandbox/drive/drive.js +6 -9
  6. package/dist/cjs/sandbox/filesystem/filesystem.js +28 -43
  7. package/dist/cjs/sandbox/interpreter.js +2 -2
  8. package/dist/cjs/sandbox/process/process.js +12 -18
  9. package/dist/cjs/sandbox/sandbox.js +30 -19
  10. package/dist/cjs/sandbox/system.js +2 -3
  11. package/dist/cjs/types/sandbox/action.d.ts +3 -0
  12. package/dist/cjs/types/sandbox/interpreter.d.ts +2 -1
  13. package/dist/cjs/types/sandbox/sandbox.d.ts +2 -1
  14. package/dist/cjs-browser/.tsbuildinfo +1 -1
  15. package/dist/cjs-browser/common/settings.js +10 -5
  16. package/dist/cjs-browser/sandbox/action.js +11 -2
  17. package/dist/cjs-browser/sandbox/codegen/codegen.js +4 -6
  18. package/dist/cjs-browser/sandbox/drive/drive.js +6 -9
  19. package/dist/cjs-browser/sandbox/filesystem/filesystem.js +28 -43
  20. package/dist/cjs-browser/sandbox/interpreter.js +2 -2
  21. package/dist/cjs-browser/sandbox/process/process.js +12 -18
  22. package/dist/cjs-browser/sandbox/sandbox.js +30 -19
  23. package/dist/cjs-browser/sandbox/system.js +2 -3
  24. package/dist/cjs-browser/types/sandbox/action.d.ts +3 -0
  25. package/dist/cjs-browser/types/sandbox/interpreter.d.ts +2 -1
  26. package/dist/cjs-browser/types/sandbox/sandbox.d.ts +2 -1
  27. package/dist/esm/.tsbuildinfo +1 -1
  28. package/dist/esm/common/settings.js +10 -5
  29. package/dist/esm/sandbox/action.js +11 -2
  30. package/dist/esm/sandbox/codegen/codegen.js +4 -6
  31. package/dist/esm/sandbox/drive/drive.js +6 -9
  32. package/dist/esm/sandbox/filesystem/filesystem.js +28 -43
  33. package/dist/esm/sandbox/interpreter.js +2 -2
  34. package/dist/esm/sandbox/process/process.js +12 -18
  35. package/dist/esm/sandbox/sandbox.js +30 -19
  36. package/dist/esm/sandbox/system.js +2 -3
  37. package/dist/esm-browser/.tsbuildinfo +1 -1
  38. package/dist/esm-browser/common/settings.js +10 -5
  39. package/dist/esm-browser/sandbox/action.js +11 -2
  40. package/dist/esm-browser/sandbox/codegen/codegen.js +4 -6
  41. package/dist/esm-browser/sandbox/drive/drive.js +6 -9
  42. package/dist/esm-browser/sandbox/filesystem/filesystem.js +28 -43
  43. package/dist/esm-browser/sandbox/interpreter.js +2 -2
  44. package/dist/esm-browser/sandbox/process/process.js +12 -18
  45. package/dist/esm-browser/sandbox/sandbox.js +30 -19
  46. package/dist/esm-browser/sandbox/system.js +2 -3
  47. package/package.json +1 -1
@@ -11,8 +11,8 @@ const index_js_1 = require("../authentication/index.js");
11
11
  const env_js_1 = require("../common/env.js");
12
12
  const node_js_1 = require("../common/node.js");
13
13
  // Build info - these placeholders are replaced at build time by build:replace-imports
14
- const BUILD_VERSION = "0.2.83";
15
- const BUILD_COMMIT = "d6f07458e0f766e5ef3c547eec3b0e00cf3297f7";
14
+ const BUILD_VERSION = "0.2.84-preview.155";
15
+ const BUILD_COMMIT = "b37c7bd0ef1257de732b54a2f784c6c91a8d450d";
16
16
  const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
17
17
  const BLAXEL_API_VERSION = "2026-04-16";
18
18
  // Cache for config.yaml tracking value
@@ -72,6 +72,10 @@ function getOsArch() {
72
72
  }
73
73
  return "browser/unknown";
74
74
  }
75
+ function isDenoRuntime() {
76
+ const runtime = globalThis;
77
+ return typeof runtime.Deno !== "undefined";
78
+ }
75
79
  class Settings {
76
80
  _credentials;
77
81
  config;
@@ -223,9 +227,10 @@ class Settings {
223
227
  return this.config.disableH2;
224
228
  }
225
229
  const value = env_js_1.env.BL_DISABLE_H2;
226
- if (!value)
227
- return false;
228
- return ["1", "true", "yes", "on"].includes(value.toLowerCase());
230
+ if (value) {
231
+ return ["1", "true", "yes", "on"].includes(value.toLowerCase());
232
+ }
233
+ return isDenoRuntime();
229
234
  }
230
235
  async authenticate() {
231
236
  await this.credentials.authenticate();
@@ -65,7 +65,7 @@ class SandboxAction {
65
65
  });
66
66
  }
67
67
  const h2Domain = this.sandbox.h2Domain;
68
- if (h2Domain) {
68
+ if (h2Domain && !settings_js_1.settings.disableH2) {
69
69
  if (!this._h2Client || this._h2ClientDomain !== h2Domain) {
70
70
  this._h2Client = (0, client_fetch_1.createClient)({
71
71
  fetch: (0, h2fetch_js_1.createPoolBackedH2Fetch)(h2pool_js_1.h2Pool, h2Domain),
@@ -86,13 +86,22 @@ class SandboxAction {
86
86
  this._h2ClientDomain = null;
87
87
  return client_gen_js_1.client;
88
88
  }
89
+ withClient(options) {
90
+ const requestOptions = { ...options };
91
+ Object.defineProperty(requestOptions, 'client', {
92
+ value: this.client,
93
+ enumerable: false,
94
+ configurable: true,
95
+ });
96
+ return requestOptions;
97
+ }
89
98
  /**
90
99
  * Routes through the H2 session when available, falling back to
91
100
  * globalThis.fetch. Uses a direct H2 path that avoids Request allocation.
92
101
  */
93
102
  h2Fetch(input, init) {
94
103
  const h2Domain = this.sandbox.h2Domain;
95
- if (h2Domain) {
104
+ if (h2Domain && !settings_js_1.settings.disableH2) {
96
105
  return (0, h2fetch_js_1.h2RequestDirectFromPool)(h2pool_js_1.h2Pool, h2Domain, input.toString(), init);
97
106
  }
98
107
  return globalThis.fetch(input, init);
@@ -8,22 +8,20 @@ class SandboxCodegen extends action_js_1.SandboxAction {
8
8
  super(sandbox);
9
9
  }
10
10
  async fastapply(path, codeEdit, model) {
11
- const result = await (0, sdk_gen_js_1.putCodegenFastapplyByPath)({
11
+ const result = await (0, sdk_gen_js_1.putCodegenFastapplyByPath)(this.withClient({
12
12
  path: { path },
13
13
  body: { codeEdit, model },
14
14
  baseUrl: this.url,
15
- client: this.client,
16
- });
15
+ }));
17
16
  this.handleResponseError(result.response, result.data, result.error);
18
17
  return result.data;
19
18
  }
20
19
  async reranking(path, query, scoreThreshold, tokenLimit, filePattern) {
21
- const result = await (0, sdk_gen_js_1.getCodegenRerankingByPath)({
20
+ const result = await (0, sdk_gen_js_1.getCodegenRerankingByPath)(this.withClient({
22
21
  path: { path },
23
22
  query: { query, scoreThreshold, tokenLimit, filePattern },
24
23
  baseUrl: this.url,
25
- client: this.client,
26
- });
24
+ }));
27
25
  this.handleResponseError(result.response, result.data, result.error);
28
26
  return result.data;
29
27
  }
@@ -11,11 +11,10 @@ class SandboxDrive extends action_js_1.SandboxAction {
11
11
  * Mount a drive to the sandbox at a specific mount path
12
12
  */
13
13
  async mount(request) {
14
- const { response, data, error } = await (0, index_js_1.postDrivesMount)({
14
+ const { response, data, error } = await (0, index_js_1.postDrivesMount)(this.withClient({
15
15
  baseUrl: this.url,
16
- client: this.client,
17
16
  body: request,
18
- });
17
+ }));
19
18
  this.handleResponseError(response, data, error);
20
19
  return data;
21
20
  }
@@ -26,11 +25,10 @@ class SandboxDrive extends action_js_1.SandboxAction {
26
25
  // Strip leading slash for the path parameter since the URL template
27
26
  // already includes the slash: /drives/mount/{mountPath}
28
27
  const paramPath = mountPath.startsWith("/") ? mountPath.substring(1) : mountPath;
29
- const { response, data, error } = await (0, index_js_1.deleteDrivesMountByMountPath)({
28
+ const { response, data, error } = await (0, index_js_1.deleteDrivesMountByMountPath)(this.withClient({
30
29
  baseUrl: this.url,
31
- client: this.client,
32
30
  path: { mountPath: paramPath },
33
- });
31
+ }));
34
32
  this.handleResponseError(response, data, error);
35
33
  return data;
36
34
  }
@@ -38,10 +36,9 @@ class SandboxDrive extends action_js_1.SandboxAction {
38
36
  * List all mounted drives in the sandbox
39
37
  */
40
38
  async list() {
41
- const { response, data, error } = await (0, index_js_1.getDrivesMount)({
39
+ const { response, data, error } = await (0, index_js_1.getDrivesMount)(this.withClient({
42
40
  baseUrl: this.url,
43
- client: this.client,
44
- });
41
+ }));
45
42
  this.handleResponseError(response, data, error);
46
43
  const result = data;
47
44
  return result.mounts ?? [];
@@ -18,12 +18,11 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
18
18
  }
19
19
  async mkdir(path, permissions = "0755") {
20
20
  path = this.formatPath(path);
21
- const { response, data, error } = await (0, index_js_1.putFilesystemByPath)({
21
+ const { response, data, error } = await (0, index_js_1.putFilesystemByPath)(this.withClient({
22
22
  path: { path },
23
23
  body: { isDirectory: true, permissions },
24
24
  baseUrl: this.url,
25
- client: this.client,
26
- });
25
+ }));
27
26
  this.handleResponseError(response, data, error);
28
27
  return data;
29
28
  }
@@ -37,12 +36,11 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
37
36
  return await this.uploadWithMultipart(path, blob, "0644");
38
37
  }
39
38
  // Use regular upload for small files
40
- const { response, data, error } = await (0, index_js_1.putFilesystemByPath)({
39
+ const { response, data, error } = await (0, index_js_1.putFilesystemByPath)(this.withClient({
41
40
  path: { path },
42
41
  body: { content },
43
42
  baseUrl: this.url,
44
- client: this.client,
45
- });
43
+ }));
46
44
  this.handleResponseError(response, data, error);
47
45
  return data;
48
46
  }
@@ -123,7 +121,6 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
123
121
  }, {}),
124
122
  },
125
123
  baseUrl: this.url,
126
- client: this.client,
127
124
  };
128
125
  const path = this.formatPath(destinationPath ?? "");
129
126
  const { response, data, error } = await this.client.put({
@@ -138,11 +135,10 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
138
135
  }
139
136
  async read(path) {
140
137
  path = this.formatPath(path);
141
- const { response, data, error } = await (0, index_js_1.getFilesystemByPath)({
138
+ const { response, data, error } = await (0, index_js_1.getFilesystemByPath)(this.withClient({
142
139
  path: { path },
143
140
  baseUrl: this.url,
144
- client: this.client,
145
- });
141
+ }));
146
142
  this.handleResponseError(response, data, error);
147
143
  if (data && 'content' in data) {
148
144
  return data.content;
@@ -151,14 +147,13 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
151
147
  }
152
148
  async readBinary(path) {
153
149
  path = this.formatPath(path);
154
- const { response, data, error } = await (0, index_js_1.getFilesystemByPath)({
150
+ const { response, data, error } = await (0, index_js_1.getFilesystemByPath)(this.withClient({
155
151
  path: { path },
156
152
  baseUrl: this.url,
157
- client: this.client,
158
153
  headers: {
159
154
  'Accept': 'application/octet-stream',
160
155
  },
161
- });
156
+ }));
162
157
  this.handleResponseError(response, data, error);
163
158
  if (typeof data === 'string') {
164
159
  return new Blob([data]);
@@ -176,22 +171,20 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
176
171
  }
177
172
  async rm(path, recursive = false) {
178
173
  path = this.formatPath(path);
179
- const { response, data, error } = await (0, index_js_1.deleteFilesystemByPath)({
174
+ const { response, data, error } = await (0, index_js_1.deleteFilesystemByPath)(this.withClient({
180
175
  path: { path },
181
176
  query: { recursive },
182
177
  baseUrl: this.url,
183
- client: this.client,
184
- });
178
+ }));
185
179
  this.handleResponseError(response, data, error);
186
180
  return data;
187
181
  }
188
182
  async ls(path) {
189
183
  path = this.formatPath(path);
190
- const { response, data, error } = await (0, index_js_1.getFilesystemByPath)({
184
+ const { response, data, error } = await (0, index_js_1.getFilesystemByPath)(this.withClient({
191
185
  path: { path },
192
186
  baseUrl: this.url,
193
- client: this.client,
194
- });
187
+ }));
195
188
  this.handleResponseError(response, data, error);
196
189
  if (!data || !('files' in data || 'subdirectories' in data)) {
197
190
  throw new Error(JSON.stringify({ error: "Directory not found" }));
@@ -213,12 +206,11 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
213
206
  if (options?.excludeHidden !== undefined) {
214
207
  queryParams.excludeHidden = options.excludeHidden;
215
208
  }
216
- const result = await (0, index_js_1.getFilesystemSearchByPath)({
209
+ const result = await (0, index_js_1.getFilesystemSearchByPath)(this.withClient({
217
210
  path: { path: formattedPath },
218
211
  query: queryParams,
219
212
  baseUrl: this.url,
220
- client: this.client,
221
- });
213
+ }));
222
214
  this.handleResponseError(result.response, result.data, result.error);
223
215
  return result.data;
224
216
  }
@@ -240,12 +232,11 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
240
232
  if (options?.excludeHidden !== undefined) {
241
233
  queryParams.excludeHidden = options.excludeHidden;
242
234
  }
243
- const result = await (0, index_js_1.getFilesystemFindByPath)({
235
+ const result = await (0, index_js_1.getFilesystemFindByPath)(this.withClient({
244
236
  path: { path: formattedPath },
245
237
  query: queryParams,
246
238
  baseUrl: this.url,
247
- client: this.client,
248
- });
239
+ }));
249
240
  this.handleResponseError(result.response, result.data, result.error);
250
241
  return result.data;
251
242
  }
@@ -269,12 +260,11 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
269
260
  if (options?.excludeDirs && options.excludeDirs.length > 0) {
270
261
  queryParams.excludeDirs = options.excludeDirs.join(',');
271
262
  }
272
- const result = await (0, index_js_1.getFilesystemContentSearchByPath)({
263
+ const result = await (0, index_js_1.getFilesystemContentSearchByPath)(this.withClient({
273
264
  path: { path: formattedPath },
274
265
  query: queryParams,
275
266
  baseUrl: this.url,
276
- client: this.client,
277
- });
267
+ }));
278
268
  this.handleResponseError(result.response, result.data, result.error);
279
269
  return result.data;
280
270
  }
@@ -301,14 +291,13 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
301
291
  if (options?.ignore) {
302
292
  query.ignore = options.ignore.join(",");
303
293
  }
304
- const { response, data, error } = await (0, index_js_1.getWatchFilesystemByPath)({
305
- client: this.client,
294
+ const { response, data, error } = await (0, index_js_1.getWatchFilesystemByPath)(this.withClient({
306
295
  path: { path },
307
296
  query,
308
297
  baseUrl: this.url,
309
298
  parseAs: 'stream',
310
299
  signal: controller.signal,
311
- });
300
+ }));
312
301
  if (error)
313
302
  throw new Error(error instanceof Error ? error.message : JSON.stringify(error));
314
303
  const stream = data ?? response.body;
@@ -383,43 +372,39 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
383
372
  // Multipart upload helper methods
384
373
  async initiateMultipartUpload(path, permissions = "0644") {
385
374
  path = this.formatPath(path);
386
- const { data } = await (0, index_js_1.postFilesystemMultipartInitiateByPath)({
375
+ const { data } = await (0, index_js_1.postFilesystemMultipartInitiateByPath)(this.withClient({
387
376
  path: { path },
388
377
  body: { permissions },
389
378
  baseUrl: this.url,
390
- client: this.client,
391
379
  throwOnError: true,
392
- });
380
+ }));
393
381
  return data;
394
382
  }
395
383
  async uploadPart(uploadId, partNumber, fileBlob) {
396
- const { data } = await (0, index_js_1.putFilesystemMultipartByUploadIdPart)({
384
+ const { data } = await (0, index_js_1.putFilesystemMultipartByUploadIdPart)(this.withClient({
397
385
  path: { uploadId },
398
386
  query: { partNumber },
399
387
  body: { file: fileBlob },
400
388
  baseUrl: this.url,
401
- client: this.client,
402
389
  throwOnError: true,
403
- });
390
+ }));
404
391
  return data;
405
392
  }
406
393
  async completeMultipartUpload(uploadId, parts) {
407
- const { data } = await (0, index_js_1.postFilesystemMultipartByUploadIdComplete)({
394
+ const { data } = await (0, index_js_1.postFilesystemMultipartByUploadIdComplete)(this.withClient({
408
395
  path: { uploadId },
409
396
  body: { parts },
410
397
  baseUrl: this.url,
411
- client: this.client,
412
398
  throwOnError: true,
413
- });
399
+ }));
414
400
  return data;
415
401
  }
416
402
  async abortMultipartUpload(uploadId) {
417
- const { data } = await (0, index_js_1.deleteFilesystemMultipartByUploadIdAbort)({
403
+ const { data } = await (0, index_js_1.deleteFilesystemMultipartByUploadIdAbort)(this.withClient({
418
404
  path: { uploadId },
419
405
  baseUrl: this.url,
420
- client: this.client,
421
406
  throwOnError: true,
422
- });
407
+ }));
423
408
  return data;
424
409
  }
425
410
  async uploadWithMultipart(path, blob, permissions = "0644") {
@@ -33,7 +33,7 @@ class CodeInterpreter extends sandbox_js_1.SandboxInstance {
33
33
  };
34
34
  return new CodeInterpreter(config);
35
35
  }
36
- static async create(sandbox, { safe = true } = {}) {
36
+ static async create(sandbox, { safe = true, createIfNotExist = false } = {}) {
37
37
  // Build a SandboxCreateConfiguration with CodeInterpreter defaults
38
38
  const defaults = {
39
39
  image: CodeInterpreter.DEFAULT_IMAGE,
@@ -66,7 +66,7 @@ class CodeInterpreter extends sandbox_js_1.SandboxInstance {
66
66
  else {
67
67
  merged = defaults;
68
68
  }
69
- const baseInstance = await super.create(merged, { safe });
69
+ const baseInstance = await super.create(merged, { safe, createIfNotExist });
70
70
  // Create config from the instance
71
71
  const config = {
72
72
  metadata: baseInstance.metadata,
@@ -111,11 +111,10 @@ class SandboxProcess extends action_js_1.SandboxAction {
111
111
  return await this.execWithStreaming(process, { onLog, onStdout, onStderr });
112
112
  }
113
113
  else {
114
- const { response, data, error } = await (0, index_js_1.postProcess)({
114
+ const { response, data, error } = await (0, index_js_1.postProcess)(this.withClient({
115
115
  body: process,
116
116
  baseUrl: this.url,
117
- client: this.client,
118
- });
117
+ }));
119
118
  this.handleResponseError(response, data, error);
120
119
  const result = data;
121
120
  if (onLog || onStdout || onStderr) {
@@ -303,46 +302,41 @@ class SandboxProcess extends action_js_1.SandboxAction {
303
302
  return data;
304
303
  }
305
304
  async get(identifier) {
306
- const { response, data, error } = await (0, index_js_1.getProcessByIdentifier)({
305
+ const { response, data, error } = await (0, index_js_1.getProcessByIdentifier)(this.withClient({
307
306
  path: { identifier },
308
307
  baseUrl: this.url,
309
- client: this.client,
310
- });
308
+ }));
311
309
  this.handleResponseError(response, data, error);
312
310
  return data;
313
311
  }
314
312
  async list() {
315
- const { response, data, error } = await (0, index_js_1.getProcess)({
313
+ const { response, data, error } = await (0, index_js_1.getProcess)(this.withClient({
316
314
  baseUrl: this.url,
317
- client: this.client,
318
- });
315
+ }));
319
316
  this.handleResponseError(response, data, error);
320
317
  return data;
321
318
  }
322
319
  async stop(identifier) {
323
- const { response, data, error } = await (0, index_js_1.deleteProcessByIdentifier)({
320
+ const { response, data, error } = await (0, index_js_1.deleteProcessByIdentifier)(this.withClient({
324
321
  path: { identifier },
325
322
  baseUrl: this.url,
326
- client: this.client,
327
- });
323
+ }));
328
324
  this.handleResponseError(response, data, error);
329
325
  return data;
330
326
  }
331
327
  async kill(identifier) {
332
- const { response, data, error } = await (0, index_js_1.deleteProcessByIdentifierKill)({
328
+ const { response, data, error } = await (0, index_js_1.deleteProcessByIdentifierKill)(this.withClient({
333
329
  path: { identifier },
334
330
  baseUrl: this.url,
335
- client: this.client,
336
- });
331
+ }));
337
332
  this.handleResponseError(response, data, error);
338
333
  return data;
339
334
  }
340
335
  async logs(identifier, type = "all") {
341
- const { response, data, error } = await (0, index_js_1.getProcessByIdentifierLogs)({
336
+ const { response, data, error } = await (0, index_js_1.getProcessByIdentifierLogs)(this.withClient({
342
337
  path: { identifier },
343
338
  baseUrl: this.url,
344
- client: this.client,
345
- });
339
+ }));
346
340
  this.handleResponseError(response, data, error);
347
341
  if (type === "all") {
348
342
  return data?.logs || "";
@@ -47,6 +47,13 @@ const index_js_6 = require("./process/index.js");
47
47
  const session_js_1 = require("./session.js");
48
48
  const system_js_1 = require("./system.js");
49
49
  const types_js_1 = require("./types.js");
50
+ const NON_REUSABLE_SANDBOX_STATUSES = new Set([
51
+ "FAILED",
52
+ "TERMINATED",
53
+ "TERMINATING",
54
+ "DELETING",
55
+ "DEACTIVATING",
56
+ ]);
50
57
  class SandboxInstance {
51
58
  sandbox;
52
59
  fs;
@@ -132,7 +139,7 @@ class SandboxInstance {
132
139
  logger_js_1.logger.warn("⚠️ Warning: sandbox.wait() is deprecated. You don't need to wait for the sandbox to be deployed anymore.");
133
140
  return this;
134
141
  }
135
- static async create(sandbox, { safe = false } = {}) {
142
+ static async create(sandbox, { safe = false, createIfNotExist = false } = {}) {
136
143
  const defaultName = `sandbox-${(0, uuid_1.v4)().replace(/-/g, '').substring(0, 8)}`;
137
144
  const defaultImage = `blaxel/base-image:latest`;
138
145
  const defaultMemory = 4096;
@@ -216,6 +223,7 @@ class SandboxInstance {
216
223
  const [{ data }, h2Session] = await Promise.all([
217
224
  (0, index_js_1.createSandbox)({
218
225
  body: sandbox,
226
+ query: createIfNotExist ? { createIfNotExist } : undefined,
219
227
  throwOnError: true,
220
228
  }),
221
229
  edgeDomain && !settings_js_1.settings.disableH2 ? Promise.resolve().then(() => __importStar(require("../common/h2pool.js"))).then(({ h2Pool }) => h2Pool.get(edgeDomain)).catch(() => null) : Promise.resolve(null),
@@ -302,27 +310,30 @@ class SandboxInstance {
302
310
  return SandboxInstance.attachH2Session(instance);
303
311
  }
304
312
  static async createIfNotExists(sandbox) {
305
- try {
306
- return await this.create(sandbox);
307
- }
308
- catch (e) {
309
- if (typeof e === "object" && e !== null && "code" in e && (e.code === 409 || e.code === 'SANDBOX_ALREADY_EXISTS')) {
310
- const name = 'name' in sandbox ? sandbox.name : sandbox.metadata.name;
311
- if (!name) {
312
- throw new Error("Sandbox name is required");
313
- }
314
- // Get the existing sandbox to check its status
315
- const sandboxInstance = await this.get(name);
316
- // If the sandbox is TERMINATED, treat it as not existing
317
- if (sandboxInstance.status === "TERMINATED") {
318
- // Create a new sandbox - backend will handle cleanup of the terminated one
319
- return await this.create(sandbox);
313
+ const ATTEMPTS = 3;
314
+ for (let i = 0; i < ATTEMPTS; ++i) {
315
+ try {
316
+ return await this.create(sandbox, { createIfNotExist: true });
317
+ }
318
+ catch (e) {
319
+ if (typeof e === "object" && e !== null && "code" in e && (e.code === 409 || e.code === 'SANDBOX_ALREADY_EXISTS')) {
320
+ const name = 'name' in sandbox ? sandbox.name : sandbox.metadata.name;
321
+ if (!name) {
322
+ throw new Error("Sandbox name is required");
323
+ }
324
+ // Get the existing sandbox to check its status
325
+ const sandboxInstance = await this.get(name);
326
+ // Recreate instead of returning sandbox records that cannot be reused.
327
+ if (!NON_REUSABLE_SANDBOX_STATUSES.has(sandboxInstance.status ?? "")) {
328
+ return sandboxInstance;
329
+ }
330
+ // Retry creation. We want the same error handling on the retry as creates can race.
331
+ continue;
320
332
  }
321
- // Otherwise return the existing running sandbox
322
- return sandboxInstance;
333
+ throw e;
323
334
  }
324
- throw e;
325
335
  }
336
+ throw new Error(`Unable to create sandbox after ${ATTEMPTS} attempts.`);
326
337
  }
327
338
  /* eslint-disable */
328
339
  static async fromSession(session) {
@@ -8,11 +8,10 @@ class SandboxSystem extends action_js_1.SandboxAction {
8
8
  super(sandbox);
9
9
  }
10
10
  async upgrade(body) {
11
- const { response, data, error } = await (0, index_js_1.postUpgrade)({
11
+ const { response, data, error } = await (0, index_js_1.postUpgrade)(this.withClient({
12
12
  baseUrl: this.url,
13
- client: this.client,
14
13
  body,
15
- });
14
+ }));
16
15
  this.handleResponseError(response, data, error);
17
16
  return data;
18
17
  }
@@ -16,6 +16,9 @@ export declare class SandboxAction {
16
16
  get externalUrl(): string;
17
17
  get internalUrl(): string;
18
18
  get client(): Client;
19
+ protected withClient<T extends object>(options: T): T & {
20
+ client: Client;
21
+ };
19
22
  /**
20
23
  * Routes through the H2 session when available, falling back to
21
24
  * globalThis.fetch. Uses a direct H2 path that avoids Request allocation.
@@ -8,8 +8,9 @@ export declare class CodeInterpreter extends SandboxInstance {
8
8
  private _sandboxConfig;
9
9
  constructor(sandbox: SandboxConfiguration);
10
10
  static get(sandboxName: string): Promise<CodeInterpreter>;
11
- static create(sandbox?: Sandbox | SandboxCreateConfiguration | Record<string, any> | null, { safe }?: {
11
+ static create(sandbox?: Sandbox | SandboxCreateConfiguration | Record<string, any> | null, { safe, createIfNotExist }?: {
12
12
  safe?: boolean;
13
+ createIfNotExist?: boolean;
13
14
  }): Promise<CodeInterpreter>;
14
15
  static createIfNotExists(sandbox: Sandbox | SandboxCreateConfiguration): Promise<CodeInterpreter>;
15
16
  get _jupyterUrl(): string;
@@ -46,8 +46,9 @@ export declare class SandboxInstance {
46
46
  maxWait?: number;
47
47
  interval?: number;
48
48
  }): Promise<this>;
49
- static create(sandbox?: SandboxModel | SandboxCreateConfiguration, { safe }?: {
49
+ static create(sandbox?: SandboxModel | SandboxCreateConfiguration, { safe, createIfNotExist }?: {
50
50
  safe?: boolean;
51
+ createIfNotExist?: boolean;
51
52
  }): Promise<SandboxInstance>;
52
53
  static get(sandboxName: string): Promise<SandboxInstance>;
53
54
  static list(): Promise<SandboxInstance[]>;