@blaxel/core 0.3.25 → 0.3.26-preview.310

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 (33) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/common/error-types.js +133 -0
  3. package/dist/cjs/common/settings.js +33 -3
  4. package/dist/cjs/common/settings.test.js +70 -0
  5. package/dist/cjs/index.js +1 -0
  6. package/dist/cjs/sandbox/index.js +1 -0
  7. package/dist/cjs/types/common/error-types.d.ts +159 -0
  8. package/dist/cjs/types/common/settings.d.ts +16 -0
  9. package/dist/cjs/types/index.d.ts +1 -0
  10. package/dist/cjs/types/sandbox/index.d.ts +1 -0
  11. package/dist/cjs-browser/.tsbuildinfo +1 -1
  12. package/dist/cjs-browser/common/error-types.js +133 -0
  13. package/dist/cjs-browser/common/settings.js +33 -3
  14. package/dist/cjs-browser/common/settings.test.js +70 -0
  15. package/dist/cjs-browser/index.js +1 -0
  16. package/dist/cjs-browser/sandbox/index.js +1 -0
  17. package/dist/cjs-browser/types/common/error-types.d.ts +159 -0
  18. package/dist/cjs-browser/types/common/settings.d.ts +16 -0
  19. package/dist/cjs-browser/types/index.d.ts +1 -0
  20. package/dist/cjs-browser/types/sandbox/index.d.ts +1 -0
  21. package/dist/esm/.tsbuildinfo +1 -1
  22. package/dist/esm/common/error-types.js +126 -0
  23. package/dist/esm/common/settings.js +33 -3
  24. package/dist/esm/common/settings.test.js +71 -1
  25. package/dist/esm/index.js +1 -0
  26. package/dist/esm/sandbox/index.js +1 -0
  27. package/dist/esm-browser/.tsbuildinfo +1 -1
  28. package/dist/esm-browser/common/error-types.js +126 -0
  29. package/dist/esm-browser/common/settings.js +33 -3
  30. package/dist/esm-browser/common/settings.test.js +71 -1
  31. package/dist/esm-browser/index.js +1 -0
  32. package/dist/esm-browser/sandbox/index.js +1 -0
  33. package/package.json +1 -1
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isBlaxelError = isBlaxelError;
4
+ exports.getBlaxelErrorCode = getBlaxelErrorCode;
5
+ exports.getBlaxelErrorStatus = getBlaxelErrorStatus;
6
+ exports.getBlaxelErrorMessage = getBlaxelErrorMessage;
7
+ exports.getBlaxelErrorRequestId = getBlaxelErrorRequestId;
8
+ const action_js_1 = require("../sandbox/action.js");
9
+ const sandbox_js_1 = require("../sandbox/sandbox.js");
10
+ function readProperty(value, key) {
11
+ try {
12
+ return typeof value === "object" && value !== null
13
+ ? value[key]
14
+ : undefined;
15
+ }
16
+ catch {
17
+ // Catch hostile getters and revoked proxies, including in custom transports.
18
+ return undefined;
19
+ }
20
+ }
21
+ const isCode = (value) => (typeof value === "string" && value.length > 0) ||
22
+ (typeof value === "number" && Number.isFinite(value));
23
+ const isStatus = (value) => typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599;
24
+ /**
25
+ * True only for a value that carries a Blaxel error code or HTTP status in a
26
+ * shape the SDK throws for API failures: a `ResponseError` (including
27
+ * `SandboxGatewayError`), a `SandboxCreationTimeoutError`, or a raw control-plane
28
+ * error body (`{ error, code }`, `{ code, message, status_code }` or
29
+ * `{ error: { code, message, status } }`). Plain `Error`s (including network
30
+ * errors with a `code` such as `ECONNRESET`), strings, raw HTML/text bodies, empty
31
+ * bodies and a bare `{ error: string }` are false: they carry neither. Matching
32
+ * `ResponseError` uses `instanceof`, so an error thrown by a second copy of
33
+ * `@blaxel/core` does not match. Does not wrap or mutate the value. Never throws.
34
+ * Like `is_blaxel_error` in the Python SDK, it is false for plain exceptions and
35
+ * strings.
36
+ */
37
+ function isBlaxelError(err) {
38
+ try {
39
+ if (err instanceof Error) {
40
+ return err instanceof action_js_1.ResponseError || err instanceof sandbox_js_1.SandboxCreationTimeoutError;
41
+ }
42
+ if (typeof err !== "object" || err === null || Array.isArray(err))
43
+ return false;
44
+ const error = readProperty(err, "error");
45
+ if (typeof error === "string")
46
+ return isStatus(readProperty(err, "code"));
47
+ if (typeof readProperty(err, "code") === "string" &&
48
+ typeof readProperty(err, "message") === "string" &&
49
+ isStatus(readProperty(err, "status_code")))
50
+ return true;
51
+ return (typeof error === "object" && error !== null && !Array.isArray(error) &&
52
+ typeof readProperty(error, "code") === "string" &&
53
+ typeof readProperty(error, "message") === "string" &&
54
+ isStatus(readProperty(error, "status")));
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }
60
+ // Fixed-depth inspection: ResponseError carries `error`/`data`; gateway bodies
61
+ // nest their fields once more in `error`. No recursion, including on cyclic input.
62
+ function errorCandidates(err) {
63
+ const bodies = [err, readProperty(err, "error"), readProperty(err, "data")];
64
+ return bodies.flatMap((body) => [body, readProperty(body, "error")]);
65
+ }
66
+ function readHeader(err, name) {
67
+ try {
68
+ const headers = readProperty(readProperty(err, "response"), "headers");
69
+ const get = readProperty(headers, "get");
70
+ if (typeof get !== "function")
71
+ return undefined;
72
+ const value = Reflect.apply(get, headers, [name]);
73
+ return typeof value === "string" && value.length > 0 ? value : undefined;
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ }
79
+ /** Read a code from the existing error/body, then X-Blaxel-Error-Code. Never throws. */
80
+ function getBlaxelErrorCode(err) {
81
+ for (const candidate of errorCandidates(err)) {
82
+ const code = readProperty(candidate, "code");
83
+ if (isCode(code))
84
+ return code;
85
+ }
86
+ return readHeader(err, "x-blaxel-error-code");
87
+ }
88
+ /**
89
+ * Read HTTP status from the existing error/response, then the body (`status`,
90
+ * `status_code`, or a numeric `code`). Raw text and plain Error have none.
91
+ * Never throws.
92
+ */
93
+ function getBlaxelErrorStatus(err) {
94
+ const status = readProperty(err, "status");
95
+ if (isStatus(status))
96
+ return status;
97
+ const responseStatus = readProperty(readProperty(err, "response"), "status");
98
+ if (isStatus(responseStatus))
99
+ return responseStatus;
100
+ for (const candidate of errorCandidates(err)) {
101
+ for (const key of ["status", "status_code", "code"]) {
102
+ const value = readProperty(candidate, key);
103
+ if (isStatus(value))
104
+ return value;
105
+ }
106
+ }
107
+ return undefined;
108
+ }
109
+ /**
110
+ * Read the existing Error.message, body message/error text, or raw text/HTML.
111
+ * Returns undefined for an empty/unrecognized body. Never throws.
112
+ */
113
+ function getBlaxelErrorMessage(err) {
114
+ for (const candidate of errorCandidates(err)) {
115
+ const message = typeof candidate === "string" ? candidate : readProperty(candidate, "message");
116
+ if (typeof message === "string" && message.length > 0)
117
+ return message;
118
+ }
119
+ return undefined;
120
+ }
121
+ /**
122
+ * Read X-Cf-Request-Id, X-Amz-Cf-Id, then CF-Ray from a retained response.
123
+ * Raw control-plane bodies and SandboxCreationTimeoutError do not retain
124
+ * response headers, so their request ID is unavailable. Never throws.
125
+ */
126
+ function getBlaxelErrorRequestId(err) {
127
+ for (const name of ["x-cf-request-id", "x-amz-cf-id", "cf-ray"]) {
128
+ const value = readHeader(err, name);
129
+ if (value !== undefined)
130
+ return value;
131
+ }
132
+ return undefined;
133
+ }
@@ -30,8 +30,8 @@ function missingCredentialsMessage() {
30
30
  return "No Blaxel credentials found. Set the BL_API_KEY and BL_WORKSPACE environment variables, or run `bl login`.";
31
31
  }
32
32
  // Build info - these placeholders are replaced at build time by build:replace-imports
33
- const BUILD_VERSION = "0.3.25";
34
- const BUILD_COMMIT = "fb45a5005d56241cc56b832f6a624248e8e34f9f";
33
+ const BUILD_VERSION = "0.3.26-preview.310";
34
+ const BUILD_COMMIT = "13bd8f75d15c2c81add546bcabeee2f20a224417";
35
35
  const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
36
36
  const BLAXEL_API_VERSION = "2026-09-22";
37
37
  // Bun < 1.3.11 never sends connection-level WINDOW_UPDATE: the pooled h2
@@ -100,8 +100,14 @@ function getOsArch() {
100
100
  }
101
101
  return "browser/unknown";
102
102
  }
103
+ /**
104
+ * An integration built on this SDK identifies itself with one User-Agent
105
+ * product token: `<name>/<semver>`, lowercase name.
106
+ */
107
+ const INTEGRATION_TOKEN_PATTERN = /^[a-z0-9][a-z0-9._-]*\/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
103
108
  class Settings {
104
109
  _credentials;
110
+ _warnedIntegration = null;
105
111
  config;
106
112
  constructor() {
107
113
  // `credentials` are resolved lazily on first access so that simply
@@ -196,14 +202,38 @@ class Settings {
196
202
  get apiVersion() {
197
203
  return env_js_1.env.BL_API_VERSION || BLAXEL_API_VERSION;
198
204
  }
205
+ /**
206
+ * Product token identifying the integration built on this SDK, or `""`.
207
+ * Resolved from `config.integration`, then `BL_INTEGRATION`. See `Config.integration`.
208
+ */
209
+ get integration() {
210
+ const value = this.config.integration ?? env_js_1.env.BL_INTEGRATION ?? "";
211
+ if (!value) {
212
+ return "";
213
+ }
214
+ if (!INTEGRATION_TOKEN_PATTERN.test(value)) {
215
+ // Resolved on every request, so warn once per invalid value rather than per request.
216
+ if (this._warnedIntegration !== value) {
217
+ this._warnedIntegration = value;
218
+ logger_js_1.logger.warn(`Ignoring invalid Blaxel integration token ${JSON.stringify(value)}: expected <name>/<semver> with a lowercase name`);
219
+ }
220
+ return "";
221
+ }
222
+ return value;
223
+ }
224
+ set integration(value) {
225
+ this.config.integration = value;
226
+ }
199
227
  get headers() {
200
228
  this.assertCredentials();
201
229
  const osArch = getOsArch();
230
+ const sdkUserAgent = `blaxel/sdk/typescript/${this.version} (${osArch}) blaxel/${this.commit}`;
231
+ const integration = this.integration;
202
232
  return {
203
233
  "x-blaxel-authorization": this.authorization,
204
234
  "x-blaxel-workspace": this.workspace || "",
205
235
  "Blaxel-Version": this.apiVersion,
206
- "User-Agent": `blaxel/sdk/typescript/${this.version} (${osArch}) blaxel/${this.commit}`,
236
+ "User-Agent": integration ? `${sdkUserAgent} ${integration}` : sdkUserAgent,
207
237
  };
208
238
  }
209
239
  get name() {
@@ -251,3 +251,73 @@ const h2_runtime_js_1 = require("./h2-runtime.js");
251
251
  });
252
252
  });
253
253
  });
254
+ (0, vitest_1.describe)('Settings.integration', () => {
255
+ const sdkUserAgent = /^blaxel\/sdk\/typescript\/\S+ \([^)]+\) blaxel\/\S+/;
256
+ async function authenticated() {
257
+ const { settings } = await Promise.resolve().then(() => __importStar(require('./settings.js')));
258
+ settings.credentials = new apikey_js_1.ApiKey({ apiKey: 'test-key', workspace: 'test-ws' });
259
+ delete settings.config.integration;
260
+ delete process.env.BL_INTEGRATION;
261
+ return settings;
262
+ }
263
+ (0, vitest_1.afterEach)(async () => {
264
+ const { settings } = await Promise.resolve().then(() => __importStar(require('./settings.js')));
265
+ delete settings.config.integration;
266
+ delete process.env.BL_INTEGRATION;
267
+ });
268
+ (0, vitest_1.it)('sends the plain SDK User-Agent by default', async () => {
269
+ const settings = await authenticated();
270
+ (0, vitest_1.expect)(settings.integration).toBe('');
271
+ (0, vitest_1.expect)(settings.headers['User-Agent']).toMatch(new RegExp(sdkUserAgent.source + '$'));
272
+ });
273
+ (0, vitest_1.it)('appends the token set programmatically, once', async () => {
274
+ const settings = await authenticated();
275
+ settings.integration = 'deepseek-harness-blaxel-sandbox/0.1.2';
276
+ const userAgent = settings.headers['User-Agent'];
277
+ (0, vitest_1.expect)(userAgent).toMatch(sdkUserAgent);
278
+ (0, vitest_1.expect)(userAgent.endsWith(' deepseek-harness-blaxel-sandbox/0.1.2')).toBe(true);
279
+ (0, vitest_1.expect)(userAgent.split('deepseek-harness-blaxel-sandbox').length).toBe(2);
280
+ });
281
+ (0, vitest_1.it)('reads BL_INTEGRATION when nothing is set programmatically, and config wins', async () => {
282
+ const settings = await authenticated();
283
+ process.env.BL_INTEGRATION = 'herdr/1.4.0-rc.1';
284
+ (0, vitest_1.expect)(settings.headers['User-Agent'].endsWith(' herdr/1.4.0-rc.1')).toBe(true);
285
+ settings.setConfig({ ...settings.config, integration: 'deepseek-harness-blaxel-sandbox/0.1.2' });
286
+ (0, vitest_1.expect)(settings.headers['User-Agent'].endsWith(' deepseek-harness-blaxel-sandbox/0.1.2')).toBe(true);
287
+ });
288
+ vitest_1.it.each(['Deepseek/0.1.2', 'deepseek-harness', 'deepseek/0.1', 'a/1.0.0 b/1.0.0', 'deepseek/0.1.2 (extra)', ' x/1.0.0', 'x/1.0.0\n', 'x/1.0.0\r\n'])('ignores the invalid token %j', async (value) => {
289
+ const settings = await authenticated();
290
+ settings.integration = value;
291
+ (0, vitest_1.expect)(settings.integration).toBe('');
292
+ (0, vitest_1.expect)(settings.headers['User-Agent']).toMatch(new RegExp(sdkUserAgent.source + '$'));
293
+ });
294
+ (0, vitest_1.it)('changes no header other than User-Agent', async () => {
295
+ const settings = await authenticated();
296
+ const plain = { ...settings.headers };
297
+ settings.integration = 'deepseek-harness-blaxel-sandbox/0.1.2';
298
+ const tagged = { ...settings.headers };
299
+ delete plain['User-Agent'];
300
+ delete tagged['User-Agent'];
301
+ (0, vitest_1.expect)(tagged).toEqual(plain);
302
+ });
303
+ });
304
+ (0, vitest_1.describe)('Settings.integration warnings', () => {
305
+ (0, vitest_1.it)('warns once per invalid value, not on every request', async () => {
306
+ const { settings } = await Promise.resolve().then(() => __importStar(require('./settings.js')));
307
+ const { logger } = await Promise.resolve().then(() => __importStar(require('./logger.js')));
308
+ const warn = vitest_1.vi.spyOn(logger, 'warn').mockImplementation(() => { });
309
+ try {
310
+ settings.integration = 'Not-Valid';
311
+ for (let i = 0; i < 5; i++)
312
+ (0, vitest_1.expect)(settings.integration).toBe('');
313
+ (0, vitest_1.expect)(warn).toHaveBeenCalledTimes(1);
314
+ settings.integration = 'also bad';
315
+ (0, vitest_1.expect)(settings.integration).toBe('');
316
+ (0, vitest_1.expect)(warn).toHaveBeenCalledTimes(2);
317
+ }
318
+ finally {
319
+ warn.mockRestore();
320
+ delete settings.config.integration;
321
+ }
322
+ });
323
+ });
@@ -23,6 +23,7 @@ __exportStar(require("./common/env.js"), exports);
23
23
  __exportStar(require("./common/h2-runtime.js"), exports);
24
24
  __exportStar(require("./common/node.js"), exports);
25
25
  __exportStar(require("./common/errors.js"), exports);
26
+ __exportStar(require("./common/error-types.js"), exports);
26
27
  __exportStar(require("./common/internal.js"), exports);
27
28
  __exportStar(require("./common/logger.js"), exports);
28
29
  __exportStar(require("./common/pagination.js"), exports);
@@ -37,6 +37,7 @@ Object.defineProperty(exports, "ResponseError", { enumerable: true, get: functio
37
37
  Object.defineProperty(exports, "SandboxGatewayError", { enumerable: true, get: function () { return action_js_1.SandboxGatewayError; } });
38
38
  Object.defineProperty(exports, "isGatewayError", { enumerable: true, get: function () { return action_js_1.isGatewayError; } });
39
39
  Object.defineProperty(exports, "isGatewayTimeout", { enumerable: true, get: function () { return action_js_1.isGatewayTimeout; } });
40
+ __exportStar(require("../common/error-types.js"), exports);
40
41
  __exportStar(require("./filesystem/index.js"), exports);
41
42
  __exportStar(require("./codegen/index.js"), exports);
42
43
  var index_js_2 = require("./drive/index.js");
@@ -0,0 +1,159 @@
1
+ import { ResponseError } from "../sandbox/action.js";
2
+ import { SandboxCreationTimeoutError } from "../sandbox/sandbox.js";
3
+ /**
4
+ * Codes the edge gateway returns for sandbox, agent, function and MCP URLs,
5
+ * in `error.code` and the `X-Blaxel-Error-Code` header.
6
+ */
7
+ export type BlaxelGatewayErrorCode =
8
+ /** 404: the URL does not map to a known workload. */
9
+ "ROUTE_NOT_FOUND"
10
+ /** 404 (401 for unauthenticated callers): the workload does not exist. */
11
+ | "WORKLOAD_NOT_FOUND"
12
+ /** 404: the workspace in the URL does not exist. */
13
+ | "WORKSPACE_NOT_FOUND"
14
+ /** 404, retryable: the workload exists but is not serving. */
15
+ | "WORKLOAD_UNAVAILABLE"
16
+ /** The workload failed and will not serve. */
17
+ | "WORKLOAD_FAILED"
18
+ /** Retryable: routing is briefly unavailable. */
19
+ | "ROUTING_UNAVAILABLE"
20
+ /** 401: no credentials were sent. */
21
+ | "AUTHENTICATION_REQUIRED"
22
+ /** 401: the credentials were rejected. */
23
+ | "AUTHENTICATION_FAILED"
24
+ /** 401: the token was revoked; get a new one. */
25
+ | "TOKEN_REVOKED"
26
+ /** 403: authenticated but not allowed on this resource. */
27
+ | "FORBIDDEN"
28
+ /** 400: malformed request. */
29
+ | "BAD_REQUEST"
30
+ /** 402: the workspace exceeded its plan's usage limits. */
31
+ | "USAGE_LIMIT_EXCEEDED"
32
+ /** Reserved: defined by the gateway but not currently returned. */
33
+ | "POLICY_VIOLATION"
34
+ /** Retryable: could not connect to the workload. */
35
+ | "UPSTREAM_CONNECT_FAILED"
36
+ /** Retryable: connecting to the workload timed out. */
37
+ | "UPSTREAM_CONNECT_TIMEOUT"
38
+ /** Retryable: the request to the workload failed. */
39
+ | "UPSTREAM_ERROR"
40
+ /** Retryable: the workload did not answer in time. */
41
+ | "UPSTREAM_TIMEOUT"
42
+ /** Retryable: the gateway itself failed. */
43
+ | "GATEWAY_INTERNAL_ERROR";
44
+ /**
45
+ * Authentication/authorization codes from sandbox creation and fork.
46
+ * `TOKEN_REVOKED` is also returned by every control-plane endpoint.
47
+ */
48
+ export type BlaxelAuthErrorCode =
49
+ /** 401: the token was revoked; get a new one. */
50
+ "TOKEN_REVOKED"
51
+ /** 403: the credentials were rejected. */
52
+ | "UNAUTHORIZED"
53
+ /** 403: no (write) access to the requested workspace. */
54
+ | "UNAUTHORIZED_WORKSPACE"
55
+ /** 503, retryable: credentials could not be verified right now. */
56
+ | "AUTHENTICATION_UNAVAILABLE"
57
+ /** 400: the credentials reach several workspaces (or none); set one. */
58
+ | "WORKSPACE_REQUIRED";
59
+ /** Codes the control plane returns when creating or forking a sandbox. */
60
+ export type BlaxelSandboxCreationErrorCode = "READ_FAILED" | "PARSE_FAILED" | "VALIDATION_FAILED" | "VALIDATION_ERROR" | "INVALID_INPUT" | "INVALID_IMAGE" | "IMAGE_NOT_FOUND" | "UPLOAD_NOT_SUPPORTED" | "UNSUPPORTED_GENERATION" | "KERNEL_GENERATION_MISMATCH" | "INVALID_PORTS" | "INVALID_REGION" | "REGION_MISMATCH" | "INVALID_VOLUME" | "INVALID_VOLUME_COUNT" | "INVALID_VOLUME_REGION" | "VOLUME_ALREADY_ATTACHED" | "VOLUME_PROVIDER_MISMATCH" | "VOLUME_NOT_FOUND" | "WORKSPACE_NOT_FOUND" | "ACCOUNT_NOT_FOUND" | "SANDBOX_ALREADY_EXISTS" | "SANDBOX_DELETION_IN_PROGRESS" | "SANDBOX_DELETED" | "VOLUME_DELETED" | "CREATION_TIMEOUT" | "QUOTA_EXCEEDED" | "RATE_LIMIT_EXCEEDED" | "WORKSPACE_RATE_LIMIT_EXCEEDED" | "SERVICE_UNAVAILABLE" | "DATABASE_ERROR" | "LOCK_ACQUISITION_FAILED" | "VOLUME_LOOKUP_FAILED" | "BUILD_TRIGGER_ERROR" | "PRESIGNED_URL_ERROR" | "UPLOAD_UNAVAILABLE" | "DEPLOYMENT_FAILED" | "CLUSTER_GATEWAY_ERROR" | "LOCKDOWN_ERROR" | "PROXY_CONFIG_ERROR" | "VALIDATED_DATA_ERROR" | "WORKSPACE_DATA_MISSING" | "CALLBACK_SEND_FAILED" | "FORK_FAILED" | "HANDLER_ERROR" | "INTERNAL_ERROR" | "UNKNOWN_ERROR";
61
+ /** Known backend codes, including the reserved POLICY_VIOLATION code. */
62
+ export type BlaxelErrorCode = BlaxelGatewayErrorCode | BlaxelAuthErrorCode | BlaxelSandboxCreationErrorCode;
63
+ /** Known/new string codes, or the numeric HTTP code of a generic control-plane body. */
64
+ export type BlaxelErrorCodeValue = BlaxelErrorCode | (string & Record<never, never>) | number;
65
+ /** Generic control-plane error body, returned by most `/v0` endpoints. */
66
+ export type BlaxelApiErrorBody = {
67
+ /** Human-readable message. */
68
+ error: string;
69
+ /** HTTP status code (a number, not a string code). */
70
+ code: number;
71
+ };
72
+ /** Control-plane error body of sandbox creation and fork. */
73
+ export type BlaxelActionErrorBody = {
74
+ code: BlaxelSandboxCreationErrorCode | BlaxelAuthErrorCode | (string & Record<never, never>);
75
+ message: string;
76
+ /** HTTP status code. */
77
+ status_code: number;
78
+ /** Extra discriminant, e.g. "CREATION_IN_PROGRESS" on SANDBOX_ALREADY_EXISTS. */
79
+ reason?: string;
80
+ /** Creation step that failed. */
81
+ step?: string;
82
+ workspace?: string;
83
+ sandbox_name?: string;
84
+ timestamp?: string;
85
+ cause?: string;
86
+ details?: Record<string, unknown>;
87
+ };
88
+ /** Edge gateway error body; also used by the control plane for TOKEN_REVOKED. */
89
+ export type BlaxelPlatformErrorBody = {
90
+ error: {
91
+ code: BlaxelGatewayErrorCode | (string & Record<never, never>);
92
+ message: string;
93
+ /** HTTP status code. */
94
+ status: number;
95
+ /** When present, "platform": the gateway answered, not the workload. */
96
+ origin?: "platform";
97
+ /** Whether retrying the same request can succeed. */
98
+ retryable?: boolean;
99
+ /** Whether the workload may have seen the request before it failed. */
100
+ dispatch_state?: "not_dispatched" | "dispatched_unknown";
101
+ /** True only when retryable and the workload never saw the request. */
102
+ safe_to_retry_request?: boolean;
103
+ action?: string;
104
+ do_not?: string;
105
+ docs_url?: string;
106
+ timestamp?: string;
107
+ };
108
+ };
109
+ /**
110
+ * Error body of the sandbox API (`sandbox.fs`, `sandbox.process`, ...). It has no
111
+ * code or status of its own: callers receive it inside `ResponseError`
112
+ * (`err.data` or `err.error`), which carries the HTTP status.
113
+ */
114
+ export type BlaxelSandboxApiErrorBody = {
115
+ error: string;
116
+ };
117
+ /**
118
+ * Values {@link isBlaxelError} recognizes: the error classes the SDK throws for
119
+ * API failures (`ResponseError`, which includes `SandboxGatewayError`, and
120
+ * `SandboxCreationTimeoutError`) and the raw error bodies the control-plane
121
+ * client throws, each with its required code and status fields. This is a union
122
+ * of existing values, not a new error class or normalized shape.
123
+ */
124
+ export type BlaxelErrorLike = ResponseError | SandboxCreationTimeoutError | BlaxelApiErrorBody | Pick<BlaxelActionErrorBody, "code" | "message" | "status_code"> | {
125
+ error: Pick<BlaxelPlatformErrorBody["error"], "code" | "message" | "status">;
126
+ };
127
+ /**
128
+ * True only for a value that carries a Blaxel error code or HTTP status in a
129
+ * shape the SDK throws for API failures: a `ResponseError` (including
130
+ * `SandboxGatewayError`), a `SandboxCreationTimeoutError`, or a raw control-plane
131
+ * error body (`{ error, code }`, `{ code, message, status_code }` or
132
+ * `{ error: { code, message, status } }`). Plain `Error`s (including network
133
+ * errors with a `code` such as `ECONNRESET`), strings, raw HTML/text bodies, empty
134
+ * bodies and a bare `{ error: string }` are false: they carry neither. Matching
135
+ * `ResponseError` uses `instanceof`, so an error thrown by a second copy of
136
+ * `@blaxel/core` does not match. Does not wrap or mutate the value. Never throws.
137
+ * Like `is_blaxel_error` in the Python SDK, it is false for plain exceptions and
138
+ * strings.
139
+ */
140
+ export declare function isBlaxelError(err: unknown): err is BlaxelErrorLike;
141
+ /** Read a code from the existing error/body, then X-Blaxel-Error-Code. Never throws. */
142
+ export declare function getBlaxelErrorCode(err: unknown): BlaxelErrorCodeValue | undefined;
143
+ /**
144
+ * Read HTTP status from the existing error/response, then the body (`status`,
145
+ * `status_code`, or a numeric `code`). Raw text and plain Error have none.
146
+ * Never throws.
147
+ */
148
+ export declare function getBlaxelErrorStatus(err: unknown): number | undefined;
149
+ /**
150
+ * Read the existing Error.message, body message/error text, or raw text/HTML.
151
+ * Returns undefined for an empty/unrecognized body. Never throws.
152
+ */
153
+ export declare function getBlaxelErrorMessage(err: unknown): string | undefined;
154
+ /**
155
+ * Read X-Cf-Request-Id, X-Amz-Cf-Id, then CF-Ray from a retained response.
156
+ * Raw control-plane bodies and SandboxCreationTimeoutError do not retain
157
+ * response headers, so their request ID is unavailable. Never throws.
158
+ */
159
+ export declare function getBlaxelErrorRequestId(err: unknown): string | undefined;
@@ -11,6 +11,15 @@ export type Config = {
11
11
  proxy?: string;
12
12
  apikey?: string;
13
13
  workspace?: string;
14
+ /**
15
+ * Product token identifying the integration built on this SDK, appended to
16
+ * the `User-Agent` so Blaxel can attribute traffic to it. Format
17
+ * `<name>/<semver>` with a lowercase name, e.g.
18
+ * `"deepseek-harness-blaxel-sandbox/0.1.2"`. Also settable through
19
+ * `BL_INTEGRATION` or `settings.integration`. An invalid value is ignored
20
+ * with a warning.
21
+ */
22
+ integration?: string;
14
23
  /**
15
24
  * Disables the SDK-managed HTTP/2 transport. Defaults to `false` (H2 on);
16
25
  * set this to `true` (or `BL_DISABLE_H2=1`) to opt out of H2. Note that H2 is
@@ -89,6 +98,7 @@ export type Config = {
89
98
  };
90
99
  declare class Settings {
91
100
  private _credentials;
101
+ private _warnedIntegration;
92
102
  config: Config;
93
103
  constructor();
94
104
  get credentials(): Credentials;
@@ -104,6 +114,12 @@ declare class Settings {
104
114
  get commit(): string;
105
115
  get sentryDsn(): string;
106
116
  get apiVersion(): string;
117
+ /**
118
+ * Product token identifying the integration built on this SDK, or `""`.
119
+ * Resolved from `config.integration`, then `BL_INTEGRATION`. See `Config.integration`.
120
+ */
121
+ get integration(): string;
122
+ set integration(value: string | undefined);
107
123
  get headers(): Record<string, string>;
108
124
  get name(): string;
109
125
  get type(): string;
@@ -7,6 +7,7 @@ export * from "./common/env.js";
7
7
  export * from "./common/h2-runtime.js";
8
8
  export * from "./common/node.js";
9
9
  export * from "./common/errors.js";
10
+ export * from "./common/error-types.js";
10
11
  export * from "./common/internal.js";
11
12
  export * from "./common/logger.js";
12
13
  export * from "./common/pagination.js";
@@ -1,5 +1,6 @@
1
1
  export { deleteFilesystemByPath, deleteNetworkProcessByPidMonitor, deleteProcessByIdentifier, deleteProcessByIdentifierKill, Directory, ErrorResponse, File, FileRequest, FileWithContent, getFilesystemByPath, getNetworkProcessByPidPorts, putCodegenFastapplyByPath, ApplyEditResponse, ApplyEditRequest, getCodegenRerankingByPath, RerankingResponse, getProcess, getProcessByIdentifier, getProcessByIdentifierLogs, getProcessByIdentifierLogsStream, PortMonitorRequest, postNetworkProcessByPidMonitor, postProcess, ProcessRequest, ProcessResponse, putFilesystemByPath, SuccessResponse } from "./client/index.js";
2
2
  export { ResponseError, SandboxGatewayError, isGatewayError, isGatewayTimeout } from "./action.js";
3
+ export * from "../common/error-types.js";
3
4
  export * from "./filesystem/index.js";
4
5
  export * from "./codegen/index.js";
5
6
  export { SandboxDrive, type DriveMountRequest, type DriveMountResponse, type DriveMountInfo, type DriveUnmountResponse } from "./drive/index.js";