@blaxel/core 0.2.70 → 0.2.71-dev.105

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 (93) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/agents/index.js +2 -19
  3. package/dist/cjs/common/autoload.js +64 -0
  4. package/dist/cjs/common/h2fetch.js +207 -0
  5. package/dist/cjs/common/h2pool.js +137 -0
  6. package/dist/cjs/common/h2warm.js +54 -0
  7. package/dist/cjs/common/settings.js +2 -2
  8. package/dist/cjs/jobs/jobs.js +10 -28
  9. package/dist/cjs/mcp/server.js +4 -65
  10. package/dist/cjs/sandbox/action.js +33 -0
  11. package/dist/cjs/sandbox/drive/drive.js +3 -3
  12. package/dist/cjs/sandbox/filesystem/filesystem.js +1 -2
  13. package/dist/cjs/sandbox/interpreter.js +12 -2
  14. package/dist/cjs/sandbox/process/process.js +2 -2
  15. package/dist/cjs/sandbox/sandbox.js +84 -10
  16. package/dist/cjs/tools/index.js +3 -1
  17. package/dist/cjs/tools/mcpTool.js +15 -43
  18. package/dist/cjs/types/agents/index.d.ts +1 -2
  19. package/dist/cjs/types/common/autoload.d.ts +5 -0
  20. package/dist/cjs/types/common/h2fetch.d.ts +22 -0
  21. package/dist/cjs/types/common/h2pool.d.ts +38 -0
  22. package/dist/cjs/types/common/h2warm.d.ts +2 -0
  23. package/dist/cjs/types/jobs/jobs.d.ts +1 -2
  24. package/dist/cjs/types/sandbox/action.d.ts +8 -1
  25. package/dist/cjs/types/sandbox/client/types.gen.d.ts +11 -0
  26. package/dist/cjs/types/sandbox/interpreter.d.ts +1 -0
  27. package/dist/cjs/types/sandbox/sandbox.d.ts +6 -0
  28. package/dist/cjs/types/sandbox/types.d.ts +2 -0
  29. package/dist/cjs/types/tools/index.d.ts +1 -0
  30. package/dist/cjs-browser/.tsbuildinfo +1 -1
  31. package/dist/cjs-browser/agents/index.js +2 -19
  32. package/dist/cjs-browser/common/autoload.js +64 -0
  33. package/dist/cjs-browser/common/h2fetch.js +4 -0
  34. package/dist/cjs-browser/common/h2pool.js +4 -0
  35. package/dist/cjs-browser/common/h2warm.js +2 -0
  36. package/dist/cjs-browser/common/settings.js +2 -2
  37. package/dist/cjs-browser/jobs/jobs.js +10 -28
  38. package/dist/cjs-browser/mcp/server.js +4 -65
  39. package/dist/cjs-browser/sandbox/action.js +33 -0
  40. package/dist/cjs-browser/sandbox/drive/drive.js +3 -3
  41. package/dist/cjs-browser/sandbox/filesystem/filesystem.js +1 -2
  42. package/dist/cjs-browser/sandbox/interpreter.js +12 -2
  43. package/dist/cjs-browser/sandbox/process/process.js +2 -2
  44. package/dist/cjs-browser/sandbox/sandbox.js +84 -10
  45. package/dist/cjs-browser/tools/index.js +3 -1
  46. package/dist/cjs-browser/tools/mcpTool.js +15 -43
  47. package/dist/cjs-browser/types/agents/index.d.ts +1 -2
  48. package/dist/cjs-browser/types/common/autoload.d.ts +5 -0
  49. package/dist/cjs-browser/types/common/h2fetch.d.ts +22 -0
  50. package/dist/cjs-browser/types/common/h2pool.d.ts +38 -0
  51. package/dist/cjs-browser/types/common/h2warm.d.ts +2 -0
  52. package/dist/cjs-browser/types/jobs/jobs.d.ts +1 -2
  53. package/dist/cjs-browser/types/sandbox/action.d.ts +8 -1
  54. package/dist/cjs-browser/types/sandbox/client/types.gen.d.ts +11 -0
  55. package/dist/cjs-browser/types/sandbox/interpreter.d.ts +1 -0
  56. package/dist/cjs-browser/types/sandbox/sandbox.d.ts +6 -0
  57. package/dist/cjs-browser/types/sandbox/types.d.ts +2 -0
  58. package/dist/cjs-browser/types/tools/index.d.ts +1 -0
  59. package/dist/esm/.tsbuildinfo +1 -1
  60. package/dist/esm/agents/index.js +1 -19
  61. package/dist/esm/common/autoload.js +30 -0
  62. package/dist/esm/common/h2fetch.js +202 -0
  63. package/dist/esm/common/h2pool.js +101 -0
  64. package/dist/esm/common/h2warm.js +48 -0
  65. package/dist/esm/common/settings.js +2 -2
  66. package/dist/esm/jobs/jobs.js +9 -28
  67. package/dist/esm/mcp/server.js +4 -65
  68. package/dist/esm/sandbox/action.js +33 -0
  69. package/dist/esm/sandbox/drive/drive.js +3 -3
  70. package/dist/esm/sandbox/filesystem/filesystem.js +1 -2
  71. package/dist/esm/sandbox/interpreter.js +12 -2
  72. package/dist/esm/sandbox/process/process.js +2 -2
  73. package/dist/esm/sandbox/sandbox.js +51 -10
  74. package/dist/esm/tools/index.js +1 -0
  75. package/dist/esm/tools/mcpTool.js +15 -43
  76. package/dist/esm-browser/.tsbuildinfo +1 -1
  77. package/dist/esm-browser/agents/index.js +1 -19
  78. package/dist/esm-browser/common/autoload.js +30 -0
  79. package/dist/esm-browser/common/h2fetch.js +4 -0
  80. package/dist/esm-browser/common/h2pool.js +4 -0
  81. package/dist/esm-browser/common/h2warm.js +2 -0
  82. package/dist/esm-browser/common/settings.js +2 -2
  83. package/dist/esm-browser/jobs/jobs.js +9 -28
  84. package/dist/esm-browser/mcp/server.js +4 -65
  85. package/dist/esm-browser/sandbox/action.js +33 -0
  86. package/dist/esm-browser/sandbox/drive/drive.js +3 -3
  87. package/dist/esm-browser/sandbox/filesystem/filesystem.js +1 -2
  88. package/dist/esm-browser/sandbox/interpreter.js +12 -2
  89. package/dist/esm-browser/sandbox/process/process.js +2 -2
  90. package/dist/esm-browser/sandbox/sandbox.js +51 -10
  91. package/dist/esm-browser/tools/index.js +1 -0
  92. package/dist/esm-browser/tools/mcpTool.js +15 -43
  93. package/package.json +1 -1
@@ -14,7 +14,7 @@ export class SandboxDrive extends SandboxAction {
14
14
  mountPath: request.mountPath,
15
15
  drivePath: request.drivePath || "/",
16
16
  };
17
- const response = await fetch(`${this.url}/drives/mount`, {
17
+ const response = await this.h2Fetch(`${this.url}/drives/mount`, {
18
18
  method: 'POST',
19
19
  headers: {
20
20
  ...headers,
@@ -37,7 +37,7 @@ export class SandboxDrive extends SandboxAction {
37
37
  const normalizedPath = mountPath.startsWith('/') ? mountPath : `/${mountPath}`;
38
38
  // Remove leading slash for URL (DELETE /drives/mnt/test not /drives//mnt/test)
39
39
  const urlPath = normalizedPath.substring(1);
40
- const response = await fetch(`${this.url}/drives/mount/${urlPath}`, {
40
+ const response = await this.h2Fetch(`${this.url}/drives/mount/${urlPath}`, {
41
41
  method: 'DELETE',
42
42
  headers,
43
43
  });
@@ -52,7 +52,7 @@ export class SandboxDrive extends SandboxAction {
52
52
  */
53
53
  async list() {
54
54
  const headers = this.sandbox.forceUrl ? this.sandbox.headers : settings.headers;
55
- const response = await fetch(`${this.url}/drives/mount`, {
55
+ const response = await this.h2Fetch(`${this.url}/drives/mount`, {
56
56
  method: 'GET',
57
57
  headers,
58
58
  });
@@ -98,8 +98,7 @@ export class SandboxFileSystem extends SandboxAction {
98
98
  if (this.forcedUrl) {
99
99
  url = `${this.forcedUrl.toString()}/filesystem/${path}`;
100
100
  }
101
- // Make the request using fetch instead of axios for better FormData handling
102
- const response = await fetch(url, {
101
+ const response = await this.h2Fetch(url, {
103
102
  method: 'PUT',
104
103
  headers: {
105
104
  ...settings.headers,
@@ -1,3 +1,4 @@
1
+ import { h2RequestDirect } from "../common/h2fetch.js";
1
2
  import { logger } from "../common/logger.js";
2
3
  import { settings } from "../common/settings.js";
3
4
  import { SandboxInstance } from "./sandbox.js";
@@ -23,6 +24,7 @@ export class CodeInterpreter extends SandboxInstance {
23
24
  spec: base.spec,
24
25
  status: base.status,
25
26
  events: base.events,
27
+ h2Session: base.h2Session,
26
28
  };
27
29
  return new CodeInterpreter(config);
28
30
  }
@@ -80,6 +82,7 @@ export class CodeInterpreter extends SandboxInstance {
80
82
  spec: baseInstance.spec,
81
83
  status: baseInstance.status,
82
84
  events: baseInstance.events,
85
+ h2Session: baseInstance.h2Session,
83
86
  };
84
87
  // Preserve forceUrl and headers from input if it was a dict-like object
85
88
  if (sandbox && typeof sandbox === "object" && !Array.isArray(sandbox)) {
@@ -98,6 +101,13 @@ export class CodeInterpreter extends SandboxInstance {
98
101
  get _jupyterUrl() {
99
102
  return this.process.url;
100
103
  }
104
+ _fetch(input, init) {
105
+ const session = this._sandboxConfig.h2Session;
106
+ if (session && !session.closed && !session.destroyed) {
107
+ return h2RequestDirect(session, input.toString(), init);
108
+ }
109
+ return globalThis.fetch(input, init);
110
+ }
101
111
  static OutputMessage = class {
102
112
  text;
103
113
  timestamp;
@@ -257,7 +267,7 @@ export class CodeInterpreter extends SandboxInstance {
257
267
  }, readTimeout * 1000);
258
268
  }
259
269
  try {
260
- const response = await fetch(`${this._jupyterUrl}/port/8888/execute`, {
270
+ const response = await this._fetch(`${this._jupyterUrl}/port/8888/execute`, {
261
271
  method: "POST",
262
272
  headers: {
263
273
  ...headers,
@@ -356,7 +366,7 @@ export class CodeInterpreter extends SandboxInstance {
356
366
  }, requestTimeout * 1000);
357
367
  }
358
368
  try {
359
- const response = await fetch(`${this._jupyterUrl}/port/8888/contexts`, {
369
+ const response = await this._fetch(`${this._jupyterUrl}/port/8888/contexts`, {
360
370
  method: "POST",
361
371
  headers: {
362
372
  ...headers,
@@ -18,7 +18,7 @@ export class SandboxProcess extends SandboxAction {
18
18
  const done = (async () => {
19
19
  try {
20
20
  const headers = this.sandbox.forceUrl ? this.sandbox.headers : settings.headers;
21
- const stream = await fetch(`${this.url}/process/${identifier}/logs/stream`, {
21
+ const stream = await this.h2Fetch(`${this.url}/process/${identifier}/logs/stream`, {
22
22
  method: 'GET',
23
23
  signal: controller.signal,
24
24
  headers,
@@ -120,7 +120,7 @@ export class SandboxProcess extends SandboxAction {
120
120
  async execWithStreaming(processRequest, options) {
121
121
  const headers = this.sandbox.forceUrl ? this.sandbox.headers : settings.headers;
122
122
  const controller = new AbortController();
123
- const response = await fetch(`${this.url}/process`, {
123
+ const response = await this.h2Fetch(`${this.url}/process`, {
124
124
  method: 'POST',
125
125
  signal: controller.signal,
126
126
  headers: {
@@ -21,6 +21,8 @@ export class SandboxInstance {
21
21
  codegen;
22
22
  system;
23
23
  drives;
24
+ /* eslint-disable @typescript-eslint/no-explicit-any */
25
+ h2Session;
24
26
  constructor(sandbox) {
25
27
  this.sandbox = sandbox;
26
28
  this.process = new SandboxProcess(sandbox);
@@ -31,6 +33,7 @@ export class SandboxInstance {
31
33
  this.codegen = new SandboxCodegen(sandbox);
32
34
  this.system = new SandboxSystem(sandbox);
33
35
  this.drives = new SandboxDrive(sandbox);
36
+ this.h2Session = null;
34
37
  }
35
38
  get metadata() {
36
39
  return this.sandbox.metadata;
@@ -47,6 +50,27 @@ export class SandboxInstance {
47
50
  get lastUsedAt() {
48
51
  return this.sandbox.lastUsedAt;
49
52
  }
53
+ /**
54
+ * Warm and attach an H2 session based on the sandbox's region.
55
+ * Shared by create(), get(), list(), and update helpers.
56
+ */
57
+ static async attachH2Session(instance) {
58
+ const region = instance.spec?.region;
59
+ if (!region)
60
+ return instance;
61
+ const edgeSuffix = settings.env === "prod" ? "bl.run" : "runv2.blaxel.dev";
62
+ const edgeDomain = `any.${region}.${edgeSuffix}`;
63
+ try {
64
+ const { h2Pool } = await import("../common/h2pool.js");
65
+ const h2Session = await h2Pool.get(edgeDomain);
66
+ instance.h2Session = h2Session;
67
+ instance.sandbox.h2Session = h2Session;
68
+ }
69
+ catch {
70
+ // H2 warming is best-effort; fall back to regular fetch
71
+ }
72
+ return instance;
73
+ }
50
74
  get expiresIn() {
51
75
  return this.sandbox.expiresIn;
52
76
  }
@@ -128,11 +152,24 @@ export class SandboxInstance {
128
152
  }
129
153
  sandbox.spec.runtime.image = sandbox.spec.runtime.image || defaultImage;
130
154
  sandbox.spec.runtime.memory = sandbox.spec.runtime.memory || defaultMemory;
131
- const { data } = await createSandbox({
132
- body: sandbox,
133
- throwOnError: true,
134
- });
135
- const instance = new SandboxInstance(data);
155
+ const edgeSuffix = settings.env === "prod" ? "bl.run" : "runv2.blaxel.dev";
156
+ const edgeDomain = sandbox.spec?.region ? `any.${sandbox.spec.region}.${edgeSuffix}` : null;
157
+ // Kick off warming so h2Pool.get() can join it during the API call
158
+ if (edgeDomain) {
159
+ import("../common/h2pool.js").then(({ h2Pool }) => h2Pool.warm(edgeDomain)).catch(() => { });
160
+ }
161
+ const [{ data }, h2Session] = await Promise.all([
162
+ createSandbox({
163
+ body: sandbox,
164
+ throwOnError: true,
165
+ }),
166
+ edgeDomain ? import("../common/h2pool.js").then(({ h2Pool }) => h2Pool.get(edgeDomain)).catch(() => null) : Promise.resolve(null),
167
+ ]);
168
+ // Inject the H2 session into the config so subsystems can use it
169
+ const config = { ...data, h2Session };
170
+ const instance = new SandboxInstance(config);
171
+ instance.h2Session = h2Session;
172
+ // Note: H2 session already attached via Promise.all above, no need for attachH2Session()
136
173
  // TODO remove this part once we have a better way to handle this
137
174
  if (safe) {
138
175
  try {
@@ -149,11 +186,13 @@ export class SandboxInstance {
149
186
  },
150
187
  throwOnError: true,
151
188
  });
152
- return new SandboxInstance(data);
189
+ const instance = new SandboxInstance(data);
190
+ return SandboxInstance.attachH2Session(instance);
153
191
  }
154
192
  static async list() {
155
193
  const { data } = await listSandboxes({ throwOnError: true });
156
- return data.map((sandbox) => new SandboxInstance(sandbox));
194
+ const instances = data.map((sandbox) => new SandboxInstance(sandbox));
195
+ return Promise.all(instances.map((instance) => SandboxInstance.attachH2Session(instance)));
157
196
  }
158
197
  static async delete(sandboxName) {
159
198
  const { data } = await deleteSandbox({
@@ -165,6 +204,8 @@ export class SandboxInstance {
165
204
  return data;
166
205
  }
167
206
  async delete() {
207
+ // Don't close the H2 session — it's shared via h2Pool
208
+ this.h2Session = null;
168
209
  return await SandboxInstance.delete(this.metadata.name);
169
210
  }
170
211
  static async updateMetadata(sandboxName, metadata) {
@@ -176,7 +217,7 @@ export class SandboxInstance {
176
217
  throwOnError: true,
177
218
  });
178
219
  const instance = new SandboxInstance(data);
179
- return instance;
220
+ return SandboxInstance.attachH2Session(instance);
180
221
  }
181
222
  static async updateTtl(sandboxName, ttl) {
182
223
  const sandbox = await SandboxInstance.get(sandboxName);
@@ -187,7 +228,7 @@ export class SandboxInstance {
187
228
  throwOnError: true,
188
229
  });
189
230
  const instance = new SandboxInstance(data);
190
- return instance;
231
+ return SandboxInstance.attachH2Session(instance);
191
232
  }
192
233
  static async updateLifecycle(sandboxName, lifecycle) {
193
234
  const sandbox = await SandboxInstance.get(sandboxName);
@@ -198,7 +239,7 @@ export class SandboxInstance {
198
239
  throwOnError: true,
199
240
  });
200
241
  const instance = new SandboxInstance(data);
201
- return instance;
242
+ return SandboxInstance.attachH2Session(instance);
202
243
  }
203
244
  static async createIfNotExists(sandbox) {
204
245
  try {
@@ -2,6 +2,7 @@ import { findFromCache } from "../cache/index.js";
2
2
  import { getFunction } from "../client/client.js";
3
3
  import { getForcedUrl } from "../common/internal.js";
4
4
  import { getMcpTool } from "./mcpTool.js";
5
+ export { McpTool } from "./mcpTool.js";
5
6
  export const getTool = async (name, options) => {
6
7
  return await getMcpTool(name, options);
7
8
  };
@@ -6,7 +6,6 @@ import { logger } from "../common/logger.js";
6
6
  import { settings } from "../common/settings.js";
7
7
  import { authenticate, SandboxInstance } from "../index.js";
8
8
  import { BlaxelMcpClientTransport } from "../mcp/client.js";
9
- import { startSpan } from "../telemetry/telemetry.js";
10
9
  import { schemaToZodSchema } from "./zodSchema.js";
11
10
  const McpToolCache = new Map();
12
11
  export class McpTool {
@@ -138,47 +137,24 @@ export class McpTool {
138
137
  }
139
138
  }
140
139
  async listTools() {
141
- const span = startSpan(this.name, {
142
- attributes: {
143
- "span.type": "tool.list",
144
- },
140
+ logger.debug(`MCP:${this.name}:Listing tools`);
141
+ await this.start();
142
+ const { tools } = (await this.client.listTools());
143
+ await this.close();
144
+ const result = tools.map((tool) => {
145
+ return {
146
+ name: tool.name,
147
+ description: tool.description,
148
+ inputSchema: schemaToZodSchema(tool.inputSchema),
149
+ originalSchema: tool.inputSchema,
150
+ call: (input) => {
151
+ return this.call(tool.name, input);
152
+ },
153
+ };
145
154
  });
146
- try {
147
- logger.debug(`MCP:${this.name}:Listing tools`);
148
- await this.start();
149
- const { tools } = (await this.client.listTools());
150
- await this.close();
151
- const result = tools.map((tool) => {
152
- return {
153
- name: tool.name,
154
- description: tool.description,
155
- inputSchema: schemaToZodSchema(tool.inputSchema),
156
- originalSchema: tool.inputSchema,
157
- call: (input) => {
158
- return this.call(tool.name, input);
159
- },
160
- };
161
- });
162
- span.setAttribute("tool.list.result", JSON.stringify(result));
163
- return result;
164
- }
165
- catch (err) {
166
- span.setStatus("error");
167
- span.recordException(err);
168
- throw err;
169
- }
170
- finally {
171
- span.end();
172
- }
155
+ return result;
173
156
  }
174
157
  async call(toolName, args) {
175
- const span = startSpan(this.name + "." + toolName, {
176
- attributes: {
177
- "span.type": "tool.call",
178
- "tool.name": toolName,
179
- "tool.args": JSON.stringify(args),
180
- },
181
- });
182
158
  try {
183
159
  logger.debug(`MCP:${this.name}:Tool calling`, toolName, JSON.stringify(args));
184
160
  logger.debug(`MCP:${this.name}:Tool calling:start`);
@@ -192,7 +168,6 @@ export class McpTool {
192
168
  logger.debug(`MCP:${this.name}:Tool calling:result`);
193
169
  await this.close();
194
170
  logger.debug(`MCP:${this.name}:Tool result`, toolName, JSON.stringify(args));
195
- span.setAttribute("tool.call.result", JSON.stringify(result));
196
171
  return result;
197
172
  }
198
173
  catch (err) {
@@ -207,9 +182,6 @@ export class McpTool {
207
182
  }
208
183
  throw err;
209
184
  }
210
- finally {
211
- span.end();
212
- }
213
185
  }
214
186
  async getTransport(forcedUrl) {
215
187
  if (!this.transportName) {