@blaxel/core 0.3.34-preview.340 → 0.3.35-preview.342

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 (27) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/common/settings.js +2 -2
  3. package/dist/cjs/sandbox/filesystem/copy-no-overwrite.js +20 -0
  4. package/dist/cjs/sandbox/filesystem/filesystem.js +49 -1
  5. package/dist/cjs/sandbox/filesystem/index.js +3 -0
  6. package/dist/cjs/types/sandbox/filesystem/copy-no-overwrite.d.ts +13 -0
  7. package/dist/cjs/types/sandbox/filesystem/filesystem.d.ts +3 -1
  8. package/dist/cjs/types/sandbox/filesystem/index.d.ts +1 -0
  9. package/dist/cjs-browser/.tsbuildinfo +1 -1
  10. package/dist/cjs-browser/common/settings.js +2 -2
  11. package/dist/cjs-browser/sandbox/filesystem/copy-no-overwrite.js +20 -0
  12. package/dist/cjs-browser/sandbox/filesystem/filesystem.js +49 -1
  13. package/dist/cjs-browser/sandbox/filesystem/index.js +3 -0
  14. package/dist/cjs-browser/types/sandbox/filesystem/copy-no-overwrite.d.ts +13 -0
  15. package/dist/cjs-browser/types/sandbox/filesystem/filesystem.d.ts +3 -1
  16. package/dist/cjs-browser/types/sandbox/filesystem/index.d.ts +1 -0
  17. package/dist/esm/.tsbuildinfo +1 -1
  18. package/dist/esm/common/settings.js +2 -2
  19. package/dist/esm/sandbox/filesystem/copy-no-overwrite.js +16 -0
  20. package/dist/esm/sandbox/filesystem/filesystem.js +49 -1
  21. package/dist/esm/sandbox/filesystem/index.js +1 -0
  22. package/dist/esm-browser/.tsbuildinfo +1 -1
  23. package/dist/esm-browser/common/settings.js +2 -2
  24. package/dist/esm-browser/sandbox/filesystem/copy-no-overwrite.js +16 -0
  25. package/dist/esm-browser/sandbox/filesystem/filesystem.js +49 -1
  26. package/dist/esm-browser/sandbox/filesystem/index.js +1 -0
  27. package/package.json +1 -1
@@ -24,8 +24,8 @@ function missingCredentialsMessage() {
24
24
  return "No Blaxel credentials found. Set the BL_API_KEY and BL_WORKSPACE environment variables, or run `bl login`.";
25
25
  }
26
26
  // Build info - these placeholders are replaced at build time by build:replace-imports
27
- const BUILD_VERSION = "0.3.34-preview.340";
28
- const BUILD_COMMIT = "e1289af07e1a5c5efeb0af7b397d2c2fd138d89f";
27
+ const BUILD_VERSION = "0.3.35-preview.342";
28
+ const BUILD_COMMIT = "a85b1b2bd40aafb7ab4d2a9cedb22713f44fc94c";
29
29
  const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
30
30
  const BLAXEL_API_VERSION = "2026-09-22";
31
31
  // Bun < 1.3.11 never sends connection-level WINDOW_UPDATE: the pooled h2
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Thrown by `cp(..., { noOverwrite: true })` when the sandbox API reports that the
3
+ * final target already exists (HTTP 409, code `FILE_ALREADY_EXISTS`). The target is
4
+ * left unchanged. Paths are the original request strings, not the resolved child path.
5
+ */
6
+ export class SandboxFileExistsError extends Error {
7
+ source;
8
+ destination;
9
+ code = "FILE_ALREADY_EXISTS";
10
+ constructor(source, destination, options) {
11
+ super(`Could not copy ${source} to ${destination}: destination already exists`, options);
12
+ this.source = source;
13
+ this.destination = destination;
14
+ this.name = "SandboxFileExistsError";
15
+ }
16
+ }
@@ -5,6 +5,7 @@ import { isTransientResetError, retryOnTransientReset } from "../../common/trans
5
5
  import { shellQuote } from "../../common/shell.js";
6
6
  import { SandboxAction } from "../action.js";
7
7
  import { deleteFilesystemByPath, deleteFilesystemMultipartByUploadIdAbort, getFilesystemByPath, getFilesystemContentSearchByPath, getFilesystemFindByPath, getFilesystemSearchByPath, getFilesystemTreeByPath, getWatchFilesystemByPath, postFilesystemMultipartByUploadIdComplete, postFilesystemMultipartInitiateByPath, putFilesystemByPath, putFilesystemMultipartByUploadIdPart } from "../client/index.js";
8
+ import { SandboxFileExistsError } from "./copy-no-overwrite.js";
8
9
  // Multipart upload constants
9
10
  const MULTIPART_THRESHOLD = 5 * 1024 * 1024; // 5MB
10
11
  const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB per part
@@ -376,7 +377,10 @@ export class SandboxFileSystem extends SandboxAction {
376
377
  return result.data;
377
378
  });
378
379
  }
379
- async cp(source, destination, { maxWait = 180000 } = {}) {
380
+ async cp(source, destination, { maxWait = 180000, noOverwrite = false } = {}) {
381
+ if (noOverwrite) {
382
+ return this.copyNoOverwrite(source, destination, maxWait);
383
+ }
380
384
  // Quote both paths so the shell that runs this command treats them as
381
385
  // single literal arguments instead of interpreting metacharacters in them.
382
386
  let process = await this.process.exec({
@@ -392,6 +396,50 @@ export class SandboxFileSystem extends SandboxAction {
392
396
  destination,
393
397
  };
394
398
  }
399
+ // One request: the sandbox API copies with exclusive creates and answers 409
400
+ // FILE_ALREADY_EXISTS when the final target exists. Not retried: a POST copy
401
+ // may have created entries before a connection reset.
402
+ // maxWait bounds the request like the default path's wait; -1 waits indefinitely.
403
+ async copyNoOverwrite(source, destination, maxWait) {
404
+ if (!source || !destination) {
405
+ throw new RangeError("source and destination must be nonempty paths");
406
+ }
407
+ if (!Number.isFinite(maxWait) || (maxWait < 0 && maxWait !== -1)) {
408
+ throw new RangeError("maxWait must be -1 or finite and non-negative");
409
+ }
410
+ const controller = new AbortController();
411
+ const timeoutError = new Error(`Could not copy ${source} to ${destination} within ${maxWait} ms; the sandbox may still be copying`);
412
+ const timeout = maxWait === -1 ? undefined : setTimeout(() => controller.abort(timeoutError), maxWait);
413
+ let result;
414
+ try {
415
+ result = await this.client.post({
416
+ url: "/filesystem-copy",
417
+ body: { source, destination, noOverwrite: true },
418
+ headers: { "Content-Type": "application/json" },
419
+ baseUrl: this.url,
420
+ signal: controller.signal,
421
+ });
422
+ }
423
+ catch (error) {
424
+ // Fetch implementations differ in what they reject with on abort.
425
+ if (controller.signal.aborted)
426
+ throw timeoutError;
427
+ throw error;
428
+ }
429
+ finally {
430
+ clearTimeout(timeout);
431
+ }
432
+ const { response, data, error } = result;
433
+ if (response.status === 409 && error?.code === "FILE_ALREADY_EXISTS") {
434
+ throw new SandboxFileExistsError(source, destination, { cause: error });
435
+ }
436
+ if (response.status === 404 && !error?.code && !error?.error) {
437
+ // An older sandbox API has no copy endpoint. Never fall back to an overwriting copy.
438
+ throw new Error("cp with noOverwrite needs a newer sandbox API: this sandbox has no /filesystem-copy endpoint; update its image");
439
+ }
440
+ this.handleResponseError(response, data, error);
441
+ return { message: "Files copied", source, destination };
442
+ }
395
443
  watch(path, callback, options) {
396
444
  path = this.formatPath(path);
397
445
  let closed = false;
@@ -1,2 +1,3 @@
1
1
  export * from "./filesystem.js";
2
+ export { SandboxFileExistsError } from "./copy-no-overwrite.js";
2
3
  export * from "./types.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blaxel/core",
3
- "version": "0.3.34-preview.340",
3
+ "version": "0.3.35-preview.342",
4
4
  "description": "Blaxel Core SDK for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": "Blaxel, INC (https://blaxel.ai)",