@blaxel/core 0.3.26-preview.310 → 0.3.26-preview.312

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 (29) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/common/settings.js +2 -2
  3. package/dist/cjs/sandbox/client/sdk.gen.js +17 -8
  4. package/dist/cjs/sandbox/filesystem/filesystem.js +1 -1
  5. package/dist/cjs/sandbox/preview.js +104 -1
  6. package/dist/cjs/types/sandbox/client/sdk.gen.d.ts +17 -8
  7. package/dist/cjs/types/sandbox/client/types.gen.d.ts +21 -5
  8. package/dist/cjs/types/sandbox/preview.d.ts +12 -3
  9. package/dist/cjs/types/sandbox/types.d.ts +7 -0
  10. package/dist/cjs-browser/.tsbuildinfo +1 -1
  11. package/dist/cjs-browser/common/settings.js +2 -2
  12. package/dist/cjs-browser/sandbox/client/sdk.gen.js +17 -8
  13. package/dist/cjs-browser/sandbox/filesystem/filesystem.js +1 -1
  14. package/dist/cjs-browser/sandbox/preview.js +104 -1
  15. package/dist/cjs-browser/types/sandbox/client/sdk.gen.d.ts +17 -8
  16. package/dist/cjs-browser/types/sandbox/client/types.gen.d.ts +21 -5
  17. package/dist/cjs-browser/types/sandbox/preview.d.ts +12 -3
  18. package/dist/cjs-browser/types/sandbox/types.d.ts +7 -0
  19. package/dist/esm/.tsbuildinfo +1 -1
  20. package/dist/esm/common/settings.js +2 -2
  21. package/dist/esm/sandbox/client/sdk.gen.js +17 -8
  22. package/dist/esm/sandbox/filesystem/filesystem.js +1 -1
  23. package/dist/esm/sandbox/preview.js +104 -1
  24. package/dist/esm-browser/.tsbuildinfo +1 -1
  25. package/dist/esm-browser/common/settings.js +2 -2
  26. package/dist/esm-browser/sandbox/client/sdk.gen.js +17 -8
  27. package/dist/esm-browser/sandbox/filesystem/filesystem.js +1 -1
  28. package/dist/esm-browser/sandbox/preview.js +104 -1
  29. 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.26-preview.310";
28
- const BUILD_COMMIT = "13bd8f75d15c2c81add546bcabeee2f20a224417";
27
+ const BUILD_VERSION = "0.3.26-preview.312";
28
+ const BUILD_COMMIT = "45891cd9862d690bb6532b5758e75084083d511f";
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
@@ -277,7 +277,7 @@ export const postFilesystemMultipartByUploadIdComplete = (options) => {
277
277
  };
278
278
  /**
279
279
  * Upload part
280
- * Upload a single part of a multipart upload
280
+ * Upload a single part of a multipart upload. Re-uploading a part number replaces that part. Wait for the previous request for that part to finish before retrying.
281
281
  */
282
282
  export const putFilesystemMultipartByUploadIdPart = (options) => {
283
283
  return (options.client ?? _heyApiClient).put({
@@ -334,7 +334,8 @@ export const postFilesystemMultipartInitiateByPath = (options) => {
334
334
  };
335
335
  /**
336
336
  * Fuzzy search for files and directories
337
- * Performs fuzzy search on filesystem paths using fuzzy matching algorithm. Optimized alternative to find and grep commands.
337
+ * Ranks the files and directories under a path by how well their relative path fuzzy-matches `query` (fzf algorithm: the query's characters must appear in order, not necessarily next to each other), best match first.
338
+ * Fuzzy search is for "jump to file" lookups from a partial name. The `patterns` parameter is currently ignored by this endpoint; use find for exact glob filtering.
338
339
  */
339
340
  export const getFilesystemSearchByPath = (options) => {
340
341
  return (options.client ?? _heyApiClient).get({
@@ -382,7 +383,11 @@ export const getFilesystemByPath = (options) => {
382
383
  };
383
384
  /**
384
385
  * Create or update a file or directory
385
- * Create or update a file or directory
386
+ * Create or update a file or directory.
387
+ *
388
+ * Idempotent: an existing file is overwritten (truncated, not appended to) and an existing directory is kept, so retrying the same request is safe.
389
+ *
390
+ * Send either a JSON body (FileRequest) or `multipart/form-data` for binary files. Multipart fields, in any order: `file` (required, the file content), `permissions` (optional octal mode such as `0755`, applied when the file is created, default `0644`; an existing file keeps its mode), `path` (optional, ignored: the target is always the URL path).
386
391
  */
387
392
  export const putFilesystemByPath = (options) => {
388
393
  return (options.client ?? _heyApiClient).put({
@@ -434,7 +439,7 @@ export const getFilesystemTreeByPath = (options) => {
434
439
  };
435
440
  /**
436
441
  * Create or update directory tree
437
- * Create or update multiple files within a directory tree structure
442
+ * Create or update multiple files within a directory tree structure. Idempotent: existing files are overwritten, so retrying the same request is safe.
438
443
  */
439
444
  export const putFilesystemTreeByPath = (options) => {
440
445
  return (options.client ?? _heyApiClient).put({
@@ -575,7 +580,10 @@ export const getProcess = (options) => {
575
580
  };
576
581
  /**
577
582
  * Execute a command
578
- * Execute a command and return process information. If Accept header is text/event-stream, streams logs in SSE format and returns the process response as a final event.
583
+ * Execute a command and return process information.
584
+ *
585
+ * Streaming: with `Accept: application/x-ndjson` (or `Accept: text/event-stream`, kept for compatibility) the response is NDJSON (`Content-Type: application/x-ndjson`), not SSE: one JSON object per line, `{"type": "...", "data": "..."}`.
586
+ * `type` is `stdout` or `stderr` (`data` is a raw output chunk, sent as soon as the process writes it, newlines included; if the process finished before any chunk was streamed, its output is sent instead as one event per line, without the newline), `keepalive` (every 5 seconds, no data), `error` (`data` is the message, ends the stream) or `result` (last event, `data` is the ProcessResponse as a JSON string).
579
587
  */
580
588
  export const postProcess = (options) => {
581
589
  return (options.client ?? _heyApiClient).post({
@@ -595,7 +603,7 @@ export const postProcess = (options) => {
595
603
  };
596
604
  /**
597
605
  * Stop a process
598
- * Gracefully stop a running process
606
+ * Request graceful termination. Poll GET /process/{identifier} until terminal status confirms the managed process has exited.
599
607
  */
600
608
  export const deleteProcessByIdentifier = (options) => {
601
609
  return (options.client ?? _heyApiClient).delete({
@@ -627,7 +635,7 @@ export const getProcessByIdentifier = (options) => {
627
635
  };
628
636
  /**
629
637
  * Kill a process
630
- * Forcefully kill a running process
638
+ * Request forceful termination. Poll GET /process/{identifier} until terminal status confirms the managed process has exited.
631
639
  */
632
640
  export const deleteProcessByIdentifierKill = (options) => {
633
641
  return (options.client ?? _heyApiClient).delete({
@@ -659,7 +667,8 @@ export const getProcessByIdentifierLogs = (options) => {
659
667
  };
660
668
  /**
661
669
  * Stream process logs in real time
662
- * Streams the stdout and stderr output of a process in real time, one line per log, prefixed with 'stdout:' or 'stderr:'. Closes when the process exits or the client disconnects.
670
+ * Streams the stdout and stderr output of a process in real time: the output so far, then live output as the process writes it. Closes when the process exits or the client disconnects.
671
+ * Each output line starts with `stdout:` or `stderr:` and keeps its trailing newline. A partial line (e.g. a prompt) is sent as soon as it is written; when the process completes it, the rest follows without a new prefix. `[keepalive]` lines are sent every 30 seconds.
663
672
  */
664
673
  export const getProcessByIdentifierLogsStream = (options) => {
665
674
  return (options.client ?? _heyApiClient).get({
@@ -262,7 +262,7 @@ export class SandboxFileSystem extends SandboxAction {
262
262
  }
263
263
  async search(query, path = "/", options) {
264
264
  const formattedPath = this.formatPath(path);
265
- const queryParams = {};
265
+ const queryParams = { query };
266
266
  if (options?.maxResults !== undefined) {
267
267
  queryParams.maxResults = options.maxResults;
268
268
  }
@@ -1,9 +1,46 @@
1
1
  import { createSandboxPreview, createSandboxPreviewToken, deleteSandboxPreview, deleteSandboxPreviewToken, getSandboxPreview, listSandboxPreviews, listSandboxPreviewTokens } from "../client/index.js";
2
+ function isShorthand(preview) {
3
+ return !("metadata" in preview || "spec" in preview);
4
+ }
5
+ function normalizePreview(preview) {
6
+ if (!isShorthand(preview))
7
+ return preview;
8
+ if (!Number.isInteger(preview.port) || preview.port < 1 || preview.port > 65535) {
9
+ throw new RangeError("Preview port must be an integer between 1 and 65535");
10
+ }
11
+ if (preview.name !== undefined && (typeof preview.name !== "string" || preview.name.length === 0)) {
12
+ throw new RangeError("Preview name must be a nonempty string");
13
+ }
14
+ if (preview.public !== undefined && typeof preview.public !== "boolean") {
15
+ throw new RangeError("Preview public must be a boolean");
16
+ }
17
+ return {
18
+ metadata: { name: preview.name ?? `preview-${preview.port}` },
19
+ spec: { port: preview.port, public: preview.public ?? false },
20
+ };
21
+ }
22
+ // Explicit HTTP status takes precedence over a legacy numeric service code.
23
+ function httpStatus(error) {
24
+ if (typeof error !== "object" || error === null)
25
+ return undefined;
26
+ const fields = error;
27
+ const response = fields.response;
28
+ const responseStatus = typeof response === "object" && response !== null && "status" in response
29
+ ? response.status : undefined;
30
+ for (const status of [fields.status, fields.statusCode, responseStatus, fields.code]) {
31
+ if (typeof status === "number")
32
+ return status;
33
+ }
34
+ return undefined;
35
+ }
2
36
  export class SandboxPreviewToken {
3
37
  previewToken;
4
38
  constructor(previewToken) {
5
39
  this.previewToken = previewToken;
6
40
  }
41
+ get name() {
42
+ return this.previewToken.metadata?.name ?? "";
43
+ }
7
44
  get value() {
8
45
  return this.previewToken.spec.token ?? "";
9
46
  }
@@ -43,6 +80,49 @@ export class SandboxPreviewTokens {
43
80
  });
44
81
  return new SandboxPreviewToken(data);
45
82
  }
83
+ /**
84
+ * Reuse the latest token within the requested expiry ceiling, or mint one.
85
+ * minValidity is in milliseconds. Concurrent callers may create separate tokens.
86
+ */
87
+ async createIfExpired(expiresAt, minValidity = 3_600_000) {
88
+ const now = Date.now();
89
+ if (!Number.isFinite(minValidity) || minValidity < 0) {
90
+ throw new RangeError("minValidity must be finite and non-negative");
91
+ }
92
+ const requestedExpiry = expiresAt ?? new Date(now + 86_400_000);
93
+ const ceiling = requestedExpiry instanceof Date ? requestedExpiry.getTime() : NaN;
94
+ if (!Number.isFinite(ceiling) || ceiling <= now || ceiling < now + minValidity) {
95
+ throw new RangeError("expiresAt must be a valid future date at least minValidity from now");
96
+ }
97
+ if (this.preview.spec?.public === true) {
98
+ throw new Error("Cannot create or reuse a token for a public preview");
99
+ }
100
+ const { data } = await listSandboxPreviewTokens({
101
+ path: {
102
+ sandboxName: this.resourceName,
103
+ previewName: this.previewName,
104
+ },
105
+ throwOnError: true,
106
+ });
107
+ if (!Array.isArray(data))
108
+ throw new Error("Failed to list preview tokens");
109
+ let selected;
110
+ let latestExpiry = -Infinity;
111
+ for (const token of data) {
112
+ const spec = token?.spec;
113
+ if (!spec || typeof spec.token !== "string" || spec.token.length === 0 || spec.expired === true ||
114
+ typeof spec.expiresAt !== "string")
115
+ continue;
116
+ const expiry = Date.parse(spec.expiresAt);
117
+ if (!Number.isFinite(expiry) || expiry <= now || expiry < now + minValidity || expiry > ceiling)
118
+ continue;
119
+ if (expiry > latestExpiry) {
120
+ selected = token;
121
+ latestExpiry = expiry;
122
+ }
123
+ }
124
+ return selected ? new SandboxPreviewToken(selected) : this.create(requestedExpiry);
125
+ }
46
126
  async list() {
47
127
  const { data } = await listSandboxPreviewTokens({
48
128
  path: {
@@ -75,6 +155,9 @@ export class SandboxPreview {
75
155
  get name() {
76
156
  return this.preview.metadata.name;
77
157
  }
158
+ get url() {
159
+ return this.preview.spec?.url ?? "";
160
+ }
78
161
  get metadata() {
79
162
  return this.preview.metadata;
80
163
  }
@@ -100,6 +183,7 @@ export class SandboxPreviews {
100
183
  return data.map((preview) => new SandboxPreview(preview));
101
184
  }
102
185
  async create(preview, force) {
186
+ const normalized = normalizePreview(preview);
103
187
  const query = {};
104
188
  if (force) {
105
189
  query['force'] = 'true';
@@ -109,12 +193,31 @@ export class SandboxPreviews {
109
193
  sandboxName: this.sandboxName,
110
194
  },
111
195
  query,
112
- body: preview,
196
+ body: normalized,
113
197
  throwOnError: true,
114
198
  });
115
199
  return new SandboxPreview(data);
116
200
  }
201
+ /** Existing previews are returned as-is; shorthand's private default applies only on creation. */
117
202
  async createIfNotExists(preview, force) {
203
+ if (isShorthand(preview)) {
204
+ const normalized = normalizePreview(preview);
205
+ try {
206
+ return await this.get(normalized.metadata.name);
207
+ }
208
+ catch (error) {
209
+ if (httpStatus(error) !== 404)
210
+ throw error;
211
+ }
212
+ try {
213
+ return await this.create(normalized, force);
214
+ }
215
+ catch (error) {
216
+ if (httpStatus(error) !== 409)
217
+ throw error;
218
+ return this.get(normalized.metadata.name);
219
+ }
220
+ }
118
221
  try {
119
222
  const previewInstance = await this.get(preview.metadata.name);
120
223
  return previewInstance;