@blaxel/core 0.2.86-preview.165 → 0.2.87-dev.173

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 (108) hide show
  1. package/README.md +37 -2
  2. package/dist/cjs/.tsbuildinfo +1 -1
  3. package/dist/cjs/authentication/credentials.js +11 -1
  4. package/dist/cjs/authentication/index.js +3 -1
  5. package/dist/cjs/client/sdk.gen.js +63 -12
  6. package/dist/cjs/common/credentials.test.js +71 -0
  7. package/dist/cjs/common/errors.js +15 -0
  8. package/dist/cjs/common/h2fetch.js +101 -50
  9. package/dist/cjs/common/h2pool.js +9 -0
  10. package/dist/cjs/common/pagination.js +87 -0
  11. package/dist/cjs/common/pagination.test.js +62 -0
  12. package/dist/cjs/common/settings.js +41 -3
  13. package/dist/cjs/common/settings.test.js +12 -3
  14. package/dist/cjs/drive/index.js +39 -3
  15. package/dist/cjs/index.js +1 -0
  16. package/dist/cjs/jobs/jobs.js +42 -9
  17. package/dist/cjs/sandbox/client/sdk.gen.js +1 -1
  18. package/dist/cjs/sandbox/preview.js +8 -3
  19. package/dist/cjs/sandbox/sandbox.js +41 -4
  20. package/dist/cjs/types/authentication/credentials.d.ts +9 -0
  21. package/dist/cjs/types/client/sdk.gen.d.ts +34 -19
  22. package/dist/cjs/types/client/types.gen.d.ts +704 -22
  23. package/dist/cjs/types/common/credentials.test.d.ts +1 -0
  24. package/dist/cjs/types/common/errors.d.ts +10 -0
  25. package/dist/cjs/types/common/h2fetch.d.ts +9 -4
  26. package/dist/cjs/types/common/pagination.d.ts +35 -0
  27. package/dist/cjs/types/common/pagination.test.d.ts +1 -0
  28. package/dist/cjs/types/common/settings.d.ts +9 -0
  29. package/dist/cjs/types/drive/index.d.ts +35 -4
  30. package/dist/cjs/types/index.d.ts +1 -0
  31. package/dist/cjs/types/jobs/jobs.d.ts +33 -3
  32. package/dist/cjs/types/sandbox/client/sdk.gen.d.ts +1 -1
  33. package/dist/cjs/types/sandbox/client/types.gen.d.ts +24 -0
  34. package/dist/cjs/types/sandbox/preview.d.ts +2 -2
  35. package/dist/cjs/types/sandbox/sandbox.d.ts +36 -2
  36. package/dist/cjs/types/volume/index.d.ts +37 -4
  37. package/dist/cjs/volume/index.js +41 -3
  38. package/dist/cjs-browser/.tsbuildinfo +1 -1
  39. package/dist/cjs-browser/authentication/credentials.js +11 -1
  40. package/dist/cjs-browser/authentication/index.js +3 -1
  41. package/dist/cjs-browser/client/sdk.gen.js +63 -12
  42. package/dist/cjs-browser/common/credentials.test.js +71 -0
  43. package/dist/cjs-browser/common/errors.js +15 -0
  44. package/dist/cjs-browser/common/pagination.js +87 -0
  45. package/dist/cjs-browser/common/pagination.test.js +62 -0
  46. package/dist/cjs-browser/common/settings.js +41 -3
  47. package/dist/cjs-browser/common/settings.test.js +12 -3
  48. package/dist/cjs-browser/drive/index.js +39 -3
  49. package/dist/cjs-browser/index.js +1 -0
  50. package/dist/cjs-browser/jobs/jobs.js +42 -9
  51. package/dist/cjs-browser/sandbox/client/sdk.gen.js +1 -1
  52. package/dist/cjs-browser/sandbox/preview.js +8 -3
  53. package/dist/cjs-browser/sandbox/sandbox.js +41 -4
  54. package/dist/cjs-browser/types/authentication/credentials.d.ts +9 -0
  55. package/dist/cjs-browser/types/client/sdk.gen.d.ts +34 -19
  56. package/dist/cjs-browser/types/client/types.gen.d.ts +704 -22
  57. package/dist/cjs-browser/types/common/credentials.test.d.ts +1 -0
  58. package/dist/cjs-browser/types/common/errors.d.ts +10 -0
  59. package/dist/cjs-browser/types/common/h2fetch.d.ts +9 -4
  60. package/dist/cjs-browser/types/common/pagination.d.ts +35 -0
  61. package/dist/cjs-browser/types/common/pagination.test.d.ts +1 -0
  62. package/dist/cjs-browser/types/common/settings.d.ts +9 -0
  63. package/dist/cjs-browser/types/drive/index.d.ts +35 -4
  64. package/dist/cjs-browser/types/index.d.ts +1 -0
  65. package/dist/cjs-browser/types/jobs/jobs.d.ts +33 -3
  66. package/dist/cjs-browser/types/sandbox/client/sdk.gen.d.ts +1 -1
  67. package/dist/cjs-browser/types/sandbox/client/types.gen.d.ts +24 -0
  68. package/dist/cjs-browser/types/sandbox/preview.d.ts +2 -2
  69. package/dist/cjs-browser/types/sandbox/sandbox.d.ts +36 -2
  70. package/dist/cjs-browser/types/volume/index.d.ts +37 -4
  71. package/dist/cjs-browser/volume/index.js +41 -3
  72. package/dist/esm/.tsbuildinfo +1 -1
  73. package/dist/esm/authentication/credentials.js +9 -0
  74. package/dist/esm/authentication/index.js +4 -2
  75. package/dist/esm/client/sdk.gen.js +57 -9
  76. package/dist/esm/common/credentials.test.js +69 -0
  77. package/dist/esm/common/errors.js +13 -0
  78. package/dist/esm/common/h2fetch.js +101 -50
  79. package/dist/esm/common/h2pool.js +9 -0
  80. package/dist/esm/common/pagination.js +83 -0
  81. package/dist/esm/common/pagination.test.js +60 -0
  82. package/dist/esm/common/settings.js +41 -3
  83. package/dist/esm/common/settings.test.js +12 -3
  84. package/dist/esm/drive/index.js +39 -3
  85. package/dist/esm/index.js +1 -0
  86. package/dist/esm/jobs/jobs.js +42 -9
  87. package/dist/esm/sandbox/client/sdk.gen.js +1 -1
  88. package/dist/esm/sandbox/preview.js +8 -3
  89. package/dist/esm/sandbox/sandbox.js +41 -4
  90. package/dist/esm/volume/index.js +41 -3
  91. package/dist/esm-browser/.tsbuildinfo +1 -1
  92. package/dist/esm-browser/authentication/credentials.js +9 -0
  93. package/dist/esm-browser/authentication/index.js +4 -2
  94. package/dist/esm-browser/client/sdk.gen.js +57 -9
  95. package/dist/esm-browser/common/credentials.test.js +69 -0
  96. package/dist/esm-browser/common/errors.js +13 -0
  97. package/dist/esm-browser/common/pagination.js +83 -0
  98. package/dist/esm-browser/common/pagination.test.js +60 -0
  99. package/dist/esm-browser/common/settings.js +41 -3
  100. package/dist/esm-browser/common/settings.test.js +12 -3
  101. package/dist/esm-browser/drive/index.js +39 -3
  102. package/dist/esm-browser/index.js +1 -0
  103. package/dist/esm-browser/jobs/jobs.js +42 -9
  104. package/dist/esm-browser/sandbox/client/sdk.gen.js +1 -1
  105. package/dist/esm-browser/sandbox/preview.js +8 -3
  106. package/dist/esm-browser/sandbox/sandbox.js +41 -4
  107. package/dist/esm-browser/volume/index.js +41 -3
  108. package/package.json +1 -1
@@ -1,14 +1,31 @@
1
1
  import yaml from 'yaml';
2
2
  import { ApiKey } from "../authentication/apikey.js";
3
3
  import { ClientCredentials } from "../authentication/clientcredentials.js";
4
+ import { MissingCredentials } from "../authentication/credentials.js";
4
5
  import { authentication } from "../authentication/index.js";
5
6
  import { env } from "../common/env.js";
7
+ import { CredentialsError } from "../common/errors.js";
6
8
  import { fs, os, path } from "../common/node.js";
9
+ /**
10
+ * Build an actionable "credentials missing" message naming exactly which piece
11
+ * is absent, based on the env vars currently set.
12
+ */
13
+ function missingCredentialsMessage() {
14
+ const hasWorkspace = !!env.BL_WORKSPACE;
15
+ const hasApiKey = !!env.BL_API_KEY;
16
+ if (hasWorkspace && !hasApiKey) {
17
+ return "Blaxel API key is missing. Set the BL_API_KEY environment variable, or run `bl login`, to authenticate (BL_WORKSPACE is already set).";
18
+ }
19
+ if (hasApiKey && !hasWorkspace) {
20
+ return "Blaxel workspace is missing. Set the BL_WORKSPACE environment variable, or run `bl login`, to authenticate (BL_API_KEY is already set).";
21
+ }
22
+ return "No Blaxel credentials found. Set the BL_API_KEY and BL_WORKSPACE environment variables, or run `bl login`.";
23
+ }
7
24
  // Build info - these placeholders are replaced at build time by build:replace-imports
8
- const BUILD_VERSION = "0.2.86-preview.165";
9
- const BUILD_COMMIT = "e53a861ff9a1ab4392f576a45a9ab18a3bdf47fe";
25
+ const BUILD_VERSION = "0.2.87-dev.173";
26
+ const BUILD_COMMIT = "43df206258fd8138bafddefa56333aadab2be731";
10
27
  const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
11
- const BLAXEL_API_VERSION = "2026-04-16";
28
+ const BLAXEL_API_VERSION = "2026-04-28";
12
29
  // Cache for config.yaml tracking value
13
30
  let configTrackingValue = null;
14
31
  let configTrackingLoaded = false;
@@ -167,6 +184,7 @@ class Settings {
167
184
  return env.BL_API_VERSION || BLAXEL_API_VERSION;
168
185
  }
169
186
  get headers() {
187
+ this.assertCredentials();
170
188
  const osArch = getOsArch();
171
189
  return {
172
190
  "x-blaxel-authorization": this.authorization,
@@ -252,7 +270,27 @@ class Settings {
252
270
  }
253
271
  return 0;
254
272
  }
273
+ /**
274
+ * Fail fast with a clear, actionable error when credentials are missing or
275
+ * incomplete, instead of sending empty workspace/authorization headers and
276
+ * surfacing a misleading server-side "workspace is required". Skipped for
277
+ * `forceUrl` sandbox sessions, which carry their own headers and never read
278
+ * `settings.headers`. Leaves `workspace` itself non-throwing so telemetry
279
+ * tagging stays safe.
280
+ */
281
+ assertCredentials() {
282
+ const hasConfigCredentials = !!(this.config.apikey ||
283
+ this.config.apiKey ||
284
+ this.config.clientCredentials);
285
+ if (!hasConfigCredentials && this.credentials instanceof MissingCredentials) {
286
+ throw new CredentialsError(missingCredentialsMessage());
287
+ }
288
+ if (!this.workspace) {
289
+ throw new CredentialsError("Blaxel workspace is missing. Set the BL_WORKSPACE environment variable, or run `bl login`, to authenticate your requests.");
290
+ }
291
+ }
255
292
  async authenticate() {
293
+ this.assertCredentials();
256
294
  await this.credentials.authenticate();
257
295
  }
258
296
  }
@@ -1,4 +1,5 @@
1
1
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
+ import { ApiKey } from '../authentication/apikey.js';
2
3
  import { env } from './env.js';
3
4
  describe('Settings.apiVersion', () => {
4
5
  beforeEach(() => {
@@ -7,14 +8,22 @@ describe('Settings.apiVersion', () => {
7
8
  afterEach(() => {
8
9
  delete env.BL_API_VERSION;
9
10
  });
10
- it('defaults to 2026-04-16 when BL_API_VERSION is not set', async () => {
11
+ it('defaults to 2026-04-28 when BL_API_VERSION is not set', async () => {
11
12
  delete env.BL_API_VERSION;
12
13
  const { settings } = await import('./settings.js');
13
- expect(settings.apiVersion).toBe('2026-04-16');
14
+ expect(settings.apiVersion).toBe('2026-04-28');
14
15
  });
15
16
  it('headers include Blaxel-Version set to the default', async () => {
16
17
  delete env.BL_API_VERSION;
17
18
  const { settings } = await import('./settings.js');
18
- expect(settings.headers['Blaxel-Version']).toBe('2026-04-16');
19
+ // headers now requires resolvable credentials; run in an authenticated context
20
+ const previous = settings.credentials;
21
+ settings.credentials = new ApiKey({ apiKey: 'test-key', workspace: 'test-ws' });
22
+ try {
23
+ expect(settings.headers['Blaxel-Version']).toBe('2026-04-28');
24
+ }
25
+ finally {
26
+ settings.credentials = previous;
27
+ }
19
28
  });
20
29
  });
@@ -1,5 +1,6 @@
1
1
  import { v4 as uuidv4 } from "uuid";
2
2
  import { createDrive, deleteDrive, getDrive, listDrives, updateDrive } from "../client/index.js";
3
+ import { createPaginatedList } from "../common/pagination.js";
3
4
  import { settings } from "../common/settings.js";
4
5
  export class DriveInstance {
5
6
  drive;
@@ -83,9 +84,44 @@ export class DriveInstance {
83
84
  });
84
85
  return new DriveInstance(data);
85
86
  }
86
- static async list() {
87
- const { data } = await listDrives({ throwOnError: true });
88
- return data.map((drive) => new DriveInstance(drive));
87
+ /**
88
+ * List one page of drives.
89
+ *
90
+ * The returned page exposes `data` for the current page, `meta` for cursor
91
+ * metadata, and helpers to fetch more pages only when you need them.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * const page = await DriveInstance.list({ limit: 50 });
96
+ *
97
+ * for (const drive of page.data) {
98
+ * console.log(drive.name);
99
+ * }
100
+ *
101
+ * const nextPage = await page.nextPage();
102
+ * ```
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * const allDrives = await (await DriveInstance.list()).autoPagingToArray({
107
+ * limit: 1000,
108
+ * });
109
+ * ```
110
+ */
111
+ static async list(query) {
112
+ const fetchPage = async (pageQuery) => {
113
+ const { data } = await listDrives({
114
+ query: pageQuery,
115
+ throwOnError: true,
116
+ });
117
+ return data;
118
+ };
119
+ return createPaginatedList({
120
+ response: await fetchPage(query),
121
+ fetchPage,
122
+ mapItem: (drive) => new DriveInstance(drive),
123
+ query,
124
+ });
89
125
  }
90
126
  static async delete(driveName) {
91
127
  const { data } = await deleteDrive({
package/dist/esm/index.js CHANGED
@@ -7,6 +7,7 @@ export * from "./common/node.js";
7
7
  export * from "./common/errors.js";
8
8
  export * from "./common/internal.js";
9
9
  export * from "./common/logger.js";
10
+ export * from "./common/pagination.js";
10
11
  export * from "./common/settings.js";
11
12
  export * from "./common/webhook.js";
12
13
  export * from "./drive/index.js";
@@ -1,5 +1,6 @@
1
1
  import { createJobExecution, deleteJobExecution, getJobExecution, listJobExecutions, } from "../client/index.js";
2
2
  import { logger } from "../common/logger.js";
3
+ import { createPaginatedList } from "../common/pagination.js";
3
4
  import { settings } from "../common/settings.js";
4
5
  import { startSpan } from "../telemetry/telemetry.js";
5
6
  class BlJob {
@@ -75,18 +76,50 @@ class BlJob {
75
76
  return data;
76
77
  }
77
78
  /**
78
- * List all executions for this job
79
+ * List one page of executions for this job.
80
+ *
81
+ * The returned page exposes `data` for the current page, `meta` for cursor
82
+ * metadata, and helpers to fetch more pages only when you need them.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const job = blJob("daily-import");
87
+ * const page = await job.listExecutions({ limit: 50 });
88
+ *
89
+ * for (const execution of page.data) {
90
+ * console.log(execution.status);
91
+ * }
92
+ *
93
+ * const nextPage = await page.nextPage();
94
+ * ```
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * const job = blJob("daily-import");
99
+ * const executions = await (await job.listExecutions()).autoPagingToArray({
100
+ * limit: 1000,
101
+ * });
102
+ * ```
79
103
  */
80
- async listExecutions() {
104
+ async listExecutions(query) {
81
105
  logger.debug(`Listing executions for job: ${this.jobName}`);
82
- const { data } = await listJobExecutions({
83
- path: {
84
- jobId: this.jobName,
85
- },
86
- headers: settings.headers,
87
- throwOnError: true,
106
+ const fetchPage = async (pageQuery) => {
107
+ const { data } = await listJobExecutions({
108
+ path: {
109
+ jobId: this.jobName,
110
+ },
111
+ query: pageQuery,
112
+ headers: settings.headers,
113
+ throwOnError: true,
114
+ });
115
+ return data;
116
+ };
117
+ return createPaginatedList({
118
+ response: await fetchPage(query),
119
+ fetchPage,
120
+ mapItem: (execution) => execution,
121
+ query,
88
122
  });
89
- return data ?? [];
90
123
  }
91
124
  /**
92
125
  * Get the status of a specific execution
@@ -86,7 +86,7 @@ export const getDrivesMount = (options) => {
86
86
  };
87
87
  /**
88
88
  * Attach a drive to a local path
89
- * Mounts an agent drive using the blfs binary to a local path, optionally mounting a subpath within the drive
89
+ * Mounts an agent drive using the blfs binary to a local path, optionally mounting a subpath within the drive. Supports optional UID/GID mapping to remap file ownership between the local sandbox and the filer (always mapped to filer UID/GID 0). Mapping values can be set per-request via uidMap/gidMap fields, or globally via BLFS_UID_MAP/BLFS_GID_MAP environment variables (request values take precedence).
90
90
  */
91
91
  export const postDrivesMount = (options) => {
92
92
  return (options.client ?? _heyApiClient).post({
@@ -99,24 +99,29 @@ export class SandboxPreviews {
99
99
  });
100
100
  return data.map((preview) => new SandboxPreview(preview));
101
101
  }
102
- async create(preview) {
102
+ async create(preview, force) {
103
+ const query = {};
104
+ if (force) {
105
+ query['force'] = 'true';
106
+ }
103
107
  const { data } = await createSandboxPreview({
104
108
  path: {
105
109
  sandboxName: this.sandboxName,
106
110
  },
111
+ query,
107
112
  body: preview,
108
113
  throwOnError: true,
109
114
  });
110
115
  return new SandboxPreview(data);
111
116
  }
112
- async createIfNotExists(preview) {
117
+ async createIfNotExists(preview, force) {
113
118
  try {
114
119
  const previewInstance = await this.get(preview.metadata.name);
115
120
  return previewInstance;
116
121
  }
117
122
  catch (e) {
118
123
  if (typeof e === "object" && e !== null && "code" in e && e.code === 404) {
119
- return this.create(preview);
124
+ return this.create(preview, force);
120
125
  }
121
126
  throw e;
122
127
  }
@@ -1,6 +1,7 @@
1
1
  import { v4 as uuidv4 } from "uuid";
2
2
  import { createSandbox, deleteSandbox, getSandbox, listSandboxes, updateSandbox } from "../client/index.js";
3
3
  import { logger } from "../common/logger.js";
4
+ import { createPaginatedList } from "../common/pagination.js";
4
5
  import { settings } from "../common/settings.js";
5
6
  import { SandboxCodegen } from "./codegen/index.js";
6
7
  import { SandboxDrive } from "./drive/index.js";
@@ -219,10 +220,46 @@ export class SandboxInstance {
219
220
  const instance = new SandboxInstance(data);
220
221
  return SandboxInstance.attachH2Session(instance);
221
222
  }
222
- static async list() {
223
- const { data } = await listSandboxes({ throwOnError: true });
224
- const instances = data.map((sandbox) => new SandboxInstance(sandbox));
225
- return Promise.all(instances.map((instance) => SandboxInstance.attachH2Session(instance)));
223
+ /**
224
+ * List one page of sandboxes.
225
+ *
226
+ * The returned page exposes `data` for the current page, `meta` for cursor
227
+ * metadata, and helpers to fetch more pages only when you need them.
228
+ *
229
+ * @example
230
+ * ```ts
231
+ * const page = await SandboxInstance.list({ limit: 50 });
232
+ *
233
+ * for (const sandbox of page.data) {
234
+ * console.log(sandbox.metadata.name);
235
+ * }
236
+ *
237
+ * const nextPage = await page.nextPage();
238
+ * ```
239
+ *
240
+ * @example
241
+ * ```ts
242
+ * const page = await SandboxInstance.list({ limit: 100 });
243
+ *
244
+ * for await (const sandbox of page) {
245
+ * console.log(sandbox.metadata.name);
246
+ * }
247
+ * ```
248
+ */
249
+ static async list(query) {
250
+ const fetchPage = async (pageQuery) => {
251
+ const { data } = await listSandboxes({
252
+ query: pageQuery,
253
+ throwOnError: true,
254
+ });
255
+ return data;
256
+ };
257
+ return createPaginatedList({
258
+ response: await fetchPage(query),
259
+ fetchPage,
260
+ mapItem: (sandbox) => SandboxInstance.attachH2Session(new SandboxInstance(sandbox)),
261
+ query,
262
+ });
226
263
  }
227
264
  static async delete(sandboxName) {
228
265
  const { data } = await deleteSandbox({
@@ -1,5 +1,6 @@
1
1
  import { v4 as uuidv4 } from "uuid";
2
2
  import { createVolume, deleteVolume, getVolume, listVolumes, updateVolume } from "../client/index.js";
3
+ import { createPaginatedList } from "../common/pagination.js";
3
4
  import { settings } from "../common/settings.js";
4
5
  export class VolumeInstance {
5
6
  volume;
@@ -85,9 +86,46 @@ export class VolumeInstance {
85
86
  });
86
87
  return new VolumeInstance(data);
87
88
  }
88
- static async list() {
89
- const { data } = await listVolumes({ throwOnError: true });
90
- return data.map((volume) => new VolumeInstance(volume));
89
+ /**
90
+ * List one page of volumes.
91
+ *
92
+ * The returned page exposes `data` for the current page, `meta` for cursor
93
+ * metadata, and helpers to fetch more pages only when you need them.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * const page = await VolumeInstance.list({ limit: 50 });
98
+ *
99
+ * for (const volume of page.data) {
100
+ * console.log(volume.name);
101
+ * }
102
+ *
103
+ * const nextPage = await page.nextPage();
104
+ * ```
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * const page = await VolumeInstance.list({ limit: 100 });
109
+ *
110
+ * for await (const volume of page) {
111
+ * console.log(volume.name);
112
+ * }
113
+ * ```
114
+ */
115
+ static async list(query) {
116
+ const fetchPage = async (pageQuery) => {
117
+ const { data } = await listVolumes({
118
+ query: pageQuery,
119
+ throwOnError: true,
120
+ });
121
+ return data;
122
+ };
123
+ return createPaginatedList({
124
+ response: await fetchPage(query),
125
+ fetchPage,
126
+ mapItem: (volume) => new VolumeInstance(volume),
127
+ query,
128
+ });
91
129
  }
92
130
  static async delete(volumeName) {
93
131
  const { data } = await deleteVolume({