@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
@@ -38,8 +38,6 @@ const uuid_1 = require("uuid");
38
38
  const ws_1 = __importStar(require("ws"));
39
39
  const env_js_1 = require("../common/env.js");
40
40
  const logger_js_1 = require("../common/logger.js");
41
- const telemetry_js_1 = require("../telemetry/telemetry.js");
42
- const spans = new Map();
43
41
  class BlaxelMcpServerTransport {
44
42
  port;
45
43
  wss;
@@ -74,26 +72,10 @@ class BlaxelMcpServerTransport {
74
72
  ws,
75
73
  });
76
74
  this.onconnection?.(clientId);
77
- ws.on("message", (data) => {
78
- const span = (0, telemetry_js_1.startSpan)("message", {
79
- attributes: {
80
- "mcp.client.id": clientId,
81
- "span.type": "mcp.message",
82
- },
83
- isRoot: false,
84
- });
75
+ ws.on("message", async (data) => {
85
76
  try {
86
77
  const msg = JSON.parse(data.toString());
87
- this.messageHandler?.(msg, clientId);
88
- if ("method" in msg && "id" in msg && "params" in msg) {
89
- span.setAttributes({
90
- "mcp.message.parsed": true,
91
- "mcp.method": msg.method,
92
- "mcp.messageId": msg.id,
93
- "mcp.toolName": msg.params?.name,
94
- });
95
- spans.set(clientId + ":" + msg.id, span);
96
- }
78
+ await this.messageHandler?.(msg, clientId);
97
79
  // Handle msg.id safely
98
80
  const msgId = msg.id ? String(msg.id) : "";
99
81
  const [cId, parsedMsgId] = msgId.split(":");
@@ -101,27 +83,7 @@ class BlaxelMcpServerTransport {
101
83
  // Use optional chaining for safe access
102
84
  const client = this.clients.get(cId ?? "");
103
85
  if (client?.ws?.readyState === ws_1.default.OPEN) {
104
- const msgSpan = spans.get(cId + ":" + (msg.id ?? ""));
105
- try {
106
- client.ws.send(JSON.stringify(msg));
107
- if (msgSpan) {
108
- msgSpan.setAttributes({
109
- "mcp.message.response_sent": true,
110
- });
111
- }
112
- }
113
- catch (err) {
114
- if (msgSpan) {
115
- msgSpan.setStatus("error"); // Error status
116
- msgSpan.recordException(err);
117
- }
118
- throw err;
119
- }
120
- finally {
121
- if (msgSpan) {
122
- msgSpan.end();
123
- }
124
- }
86
+ client.ws.send(JSON.stringify(msg));
125
87
  }
126
88
  else {
127
89
  this.clients.delete(cId);
@@ -130,14 +92,11 @@ class BlaxelMcpServerTransport {
130
92
  }
131
93
  catch (err) {
132
94
  if (err instanceof Error) {
133
- span.setStatus("error"); // Error status
134
- span.recordException(err);
135
95
  this.onerror?.(err);
136
96
  }
137
97
  else {
138
98
  this.onerror?.(new Error(`Failed to parse message: ${String(err)}`));
139
99
  }
140
- span.end();
141
100
  }
142
101
  });
143
102
  ws.on("close", () => {
@@ -159,27 +118,7 @@ class BlaxelMcpServerTransport {
159
118
  // Send to specific client
160
119
  const client = this.clients.get(cId);
161
120
  if (client?.ws?.readyState === ws_1.default.OPEN) {
162
- const msgSpan = spans.get(cId + ":" + msg.id);
163
- try {
164
- client.ws.send(data);
165
- if (msgSpan) {
166
- msgSpan.setAttributes({
167
- "mcp.message.response_sent": true,
168
- });
169
- }
170
- }
171
- catch (err) {
172
- if (msgSpan) {
173
- msgSpan.setStatus("error"); // Error status
174
- msgSpan.recordException(err);
175
- }
176
- throw err;
177
- }
178
- finally {
179
- if (msgSpan) {
180
- msgSpan.end();
181
- }
182
- }
121
+ client.ws.send(data);
183
122
  }
184
123
  else {
185
124
  this.clients.delete(cId);
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SandboxAction = exports.ResponseError = void 0;
4
4
  const client_fetch_1 = require("@hey-api/client-fetch");
5
+ const interceptors_js_1 = require("../client/interceptors.js");
6
+ const responseInterceptor_js_1 = require("../client/responseInterceptor.js");
7
+ const h2fetch_js_1 = require("../common/h2fetch.js");
5
8
  const internal_js_1 = require("../common/internal.js");
6
9
  const settings_js_1 = require("../common/settings.js");
7
10
  const client_gen_js_1 = require("./client/client.gen.js");
@@ -32,6 +35,7 @@ class ResponseError extends Error {
32
35
  exports.ResponseError = ResponseError;
33
36
  class SandboxAction {
34
37
  sandbox;
38
+ _h2Client = null;
35
39
  constructor(sandbox) {
36
40
  this.sandbox = sandbox;
37
41
  }
@@ -58,8 +62,37 @@ class SandboxAction {
58
62
  headers: this.sandbox.headers,
59
63
  });
60
64
  }
65
+ const session = this.sandbox.h2Session;
66
+ if (session && !session.closed && !session.destroyed) {
67
+ if (!this._h2Client) {
68
+ this._h2Client = (0, client_fetch_1.createClient)({
69
+ fetch: (0, h2fetch_js_1.createH2Fetch)(session),
70
+ });
71
+ for (const interceptor of interceptors_js_1.interceptors) {
72
+ // @ts-expect-error - Interceptor is not typed
73
+ this._h2Client.interceptors.request.use(interceptor);
74
+ }
75
+ for (const interceptor of responseInterceptor_js_1.responseInterceptors) {
76
+ this._h2Client.interceptors.response.use(interceptor);
77
+ }
78
+ }
79
+ return this._h2Client;
80
+ }
81
+ // Invalidate cached H2 client when session is no longer usable
82
+ this._h2Client = null;
61
83
  return client_gen_js_1.client;
62
84
  }
85
+ /**
86
+ * Routes through the H2 session when available, falling back to
87
+ * globalThis.fetch. Uses a direct H2 path that avoids Request allocation.
88
+ */
89
+ h2Fetch(input, init) {
90
+ const session = this.sandbox.h2Session;
91
+ if (session && !session.closed && !session.destroyed) {
92
+ return (0, h2fetch_js_1.h2RequestDirect)(session, input.toString(), init);
93
+ }
94
+ return globalThis.fetch(input, init);
95
+ }
63
96
  get forcedUrl() {
64
97
  if (this.sandbox.forceUrl)
65
98
  return this.sandbox.forceUrl;
@@ -17,7 +17,7 @@ class SandboxDrive extends action_js_1.SandboxAction {
17
17
  mountPath: request.mountPath,
18
18
  drivePath: request.drivePath || "/",
19
19
  };
20
- const response = await fetch(`${this.url}/drives/mount`, {
20
+ const response = await this.h2Fetch(`${this.url}/drives/mount`, {
21
21
  method: 'POST',
22
22
  headers: {
23
23
  ...headers,
@@ -40,7 +40,7 @@ class SandboxDrive extends action_js_1.SandboxAction {
40
40
  const normalizedPath = mountPath.startsWith('/') ? mountPath : `/${mountPath}`;
41
41
  // Remove leading slash for URL (DELETE /drives/mnt/test not /drives//mnt/test)
42
42
  const urlPath = normalizedPath.substring(1);
43
- const response = await fetch(`${this.url}/drives/mount/${urlPath}`, {
43
+ const response = await this.h2Fetch(`${this.url}/drives/mount/${urlPath}`, {
44
44
  method: 'DELETE',
45
45
  headers,
46
46
  });
@@ -55,7 +55,7 @@ class SandboxDrive extends action_js_1.SandboxAction {
55
55
  */
56
56
  async list() {
57
57
  const headers = this.sandbox.forceUrl ? this.sandbox.headers : settings_js_1.settings.headers;
58
- const response = await fetch(`${this.url}/drives/mount`, {
58
+ const response = await this.h2Fetch(`${this.url}/drives/mount`, {
59
59
  method: 'GET',
60
60
  headers,
61
61
  });
@@ -101,8 +101,7 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
101
101
  if (this.forcedUrl) {
102
102
  url = `${this.forcedUrl.toString()}/filesystem/${path}`;
103
103
  }
104
- // Make the request using fetch instead of axios for better FormData handling
105
- const response = await fetch(url, {
104
+ const response = await this.h2Fetch(url, {
106
105
  method: 'PUT',
107
106
  headers: {
108
107
  ...settings_js_1.settings.headers,
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CodeInterpreter = void 0;
4
+ const h2fetch_js_1 = require("../common/h2fetch.js");
4
5
  const logger_js_1 = require("../common/logger.js");
5
6
  const settings_js_1 = require("../common/settings.js");
6
7
  const sandbox_js_1 = require("./sandbox.js");
@@ -26,6 +27,7 @@ class CodeInterpreter extends sandbox_js_1.SandboxInstance {
26
27
  spec: base.spec,
27
28
  status: base.status,
28
29
  events: base.events,
30
+ h2Session: base.h2Session,
29
31
  };
30
32
  return new CodeInterpreter(config);
31
33
  }
@@ -83,6 +85,7 @@ class CodeInterpreter extends sandbox_js_1.SandboxInstance {
83
85
  spec: baseInstance.spec,
84
86
  status: baseInstance.status,
85
87
  events: baseInstance.events,
88
+ h2Session: baseInstance.h2Session,
86
89
  };
87
90
  // Preserve forceUrl and headers from input if it was a dict-like object
88
91
  if (sandbox && typeof sandbox === "object" && !Array.isArray(sandbox)) {
@@ -101,6 +104,13 @@ class CodeInterpreter extends sandbox_js_1.SandboxInstance {
101
104
  get _jupyterUrl() {
102
105
  return this.process.url;
103
106
  }
107
+ _fetch(input, init) {
108
+ const session = this._sandboxConfig.h2Session;
109
+ if (session && !session.closed && !session.destroyed) {
110
+ return (0, h2fetch_js_1.h2RequestDirect)(session, input.toString(), init);
111
+ }
112
+ return globalThis.fetch(input, init);
113
+ }
104
114
  static OutputMessage = class {
105
115
  text;
106
116
  timestamp;
@@ -260,7 +270,7 @@ class CodeInterpreter extends sandbox_js_1.SandboxInstance {
260
270
  }, readTimeout * 1000);
261
271
  }
262
272
  try {
263
- const response = await fetch(`${this._jupyterUrl}/port/8888/execute`, {
273
+ const response = await this._fetch(`${this._jupyterUrl}/port/8888/execute`, {
264
274
  method: "POST",
265
275
  headers: {
266
276
  ...headers,
@@ -359,7 +369,7 @@ class CodeInterpreter extends sandbox_js_1.SandboxInstance {
359
369
  }, requestTimeout * 1000);
360
370
  }
361
371
  try {
362
- const response = await fetch(`${this._jupyterUrl}/port/8888/contexts`, {
372
+ const response = await this._fetch(`${this._jupyterUrl}/port/8888/contexts`, {
363
373
  method: "POST",
364
374
  headers: {
365
375
  ...headers,
@@ -21,7 +21,7 @@ class SandboxProcess extends action_js_1.SandboxAction {
21
21
  const done = (async () => {
22
22
  try {
23
23
  const headers = this.sandbox.forceUrl ? this.sandbox.headers : settings_js_1.settings.headers;
24
- const stream = await fetch(`${this.url}/process/${identifier}/logs/stream`, {
24
+ const stream = await this.h2Fetch(`${this.url}/process/${identifier}/logs/stream`, {
25
25
  method: 'GET',
26
26
  signal: controller.signal,
27
27
  headers,
@@ -123,7 +123,7 @@ class SandboxProcess extends action_js_1.SandboxAction {
123
123
  async execWithStreaming(processRequest, options) {
124
124
  const headers = this.sandbox.forceUrl ? this.sandbox.headers : settings_js_1.settings.headers;
125
125
  const controller = new AbortController();
126
- const response = await fetch(`${this.url}/process`, {
126
+ const response = await this.h2Fetch(`${this.url}/process`, {
127
127
  method: 'POST',
128
128
  signal: controller.signal,
129
129
  headers: {
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.SandboxInstance = void 0;
4
37
  const uuid_1 = require("uuid");
@@ -24,6 +57,8 @@ class SandboxInstance {
24
57
  codegen;
25
58
  system;
26
59
  drives;
60
+ /* eslint-disable @typescript-eslint/no-explicit-any */
61
+ h2Session;
27
62
  constructor(sandbox) {
28
63
  this.sandbox = sandbox;
29
64
  this.process = new index_js_6.SandboxProcess(sandbox);
@@ -34,6 +69,7 @@ class SandboxInstance {
34
69
  this.codegen = new index_js_2.SandboxCodegen(sandbox);
35
70
  this.system = new system_js_1.SandboxSystem(sandbox);
36
71
  this.drives = new index_js_3.SandboxDrive(sandbox);
72
+ this.h2Session = null;
37
73
  }
38
74
  get metadata() {
39
75
  return this.sandbox.metadata;
@@ -50,6 +86,27 @@ class SandboxInstance {
50
86
  get lastUsedAt() {
51
87
  return this.sandbox.lastUsedAt;
52
88
  }
89
+ /**
90
+ * Warm and attach an H2 session based on the sandbox's region.
91
+ * Shared by create(), get(), list(), and update helpers.
92
+ */
93
+ static async attachH2Session(instance) {
94
+ const region = instance.spec?.region;
95
+ if (!region)
96
+ return instance;
97
+ const edgeSuffix = settings_js_1.settings.env === "prod" ? "bl.run" : "runv2.blaxel.dev";
98
+ const edgeDomain = `any.${region}.${edgeSuffix}`;
99
+ try {
100
+ const { h2Pool } = await Promise.resolve().then(() => __importStar(require("../common/h2pool.js")));
101
+ const h2Session = await h2Pool.get(edgeDomain);
102
+ instance.h2Session = h2Session;
103
+ instance.sandbox.h2Session = h2Session;
104
+ }
105
+ catch {
106
+ // H2 warming is best-effort; fall back to regular fetch
107
+ }
108
+ return instance;
109
+ }
53
110
  get expiresIn() {
54
111
  return this.sandbox.expiresIn;
55
112
  }
@@ -131,11 +188,24 @@ class SandboxInstance {
131
188
  }
132
189
  sandbox.spec.runtime.image = sandbox.spec.runtime.image || defaultImage;
133
190
  sandbox.spec.runtime.memory = sandbox.spec.runtime.memory || defaultMemory;
134
- const { data } = await (0, index_js_1.createSandbox)({
135
- body: sandbox,
136
- throwOnError: true,
137
- });
138
- const instance = new SandboxInstance(data);
191
+ const edgeSuffix = settings_js_1.settings.env === "prod" ? "bl.run" : "runv2.blaxel.dev";
192
+ const edgeDomain = sandbox.spec?.region ? `any.${sandbox.spec.region}.${edgeSuffix}` : null;
193
+ // Kick off warming so h2Pool.get() can join it during the API call
194
+ if (edgeDomain) {
195
+ Promise.resolve().then(() => __importStar(require("../common/h2pool.js"))).then(({ h2Pool }) => h2Pool.warm(edgeDomain)).catch(() => { });
196
+ }
197
+ const [{ data }, h2Session] = await Promise.all([
198
+ (0, index_js_1.createSandbox)({
199
+ body: sandbox,
200
+ throwOnError: true,
201
+ }),
202
+ edgeDomain ? Promise.resolve().then(() => __importStar(require("../common/h2pool.js"))).then(({ h2Pool }) => h2Pool.get(edgeDomain)).catch(() => null) : Promise.resolve(null),
203
+ ]);
204
+ // Inject the H2 session into the config so subsystems can use it
205
+ const config = { ...data, h2Session };
206
+ const instance = new SandboxInstance(config);
207
+ instance.h2Session = h2Session;
208
+ // Note: H2 session already attached via Promise.all above, no need for attachH2Session()
139
209
  // TODO remove this part once we have a better way to handle this
140
210
  if (safe) {
141
211
  try {
@@ -152,11 +222,13 @@ class SandboxInstance {
152
222
  },
153
223
  throwOnError: true,
154
224
  });
155
- return new SandboxInstance(data);
225
+ const instance = new SandboxInstance(data);
226
+ return SandboxInstance.attachH2Session(instance);
156
227
  }
157
228
  static async list() {
158
229
  const { data } = await (0, index_js_1.listSandboxes)({ throwOnError: true });
159
- return data.map((sandbox) => new SandboxInstance(sandbox));
230
+ const instances = data.map((sandbox) => new SandboxInstance(sandbox));
231
+ return Promise.all(instances.map((instance) => SandboxInstance.attachH2Session(instance)));
160
232
  }
161
233
  static async delete(sandboxName) {
162
234
  const { data } = await (0, index_js_1.deleteSandbox)({
@@ -168,6 +240,8 @@ class SandboxInstance {
168
240
  return data;
169
241
  }
170
242
  async delete() {
243
+ // Don't close the H2 session — it's shared via h2Pool
244
+ this.h2Session = null;
171
245
  return await SandboxInstance.delete(this.metadata.name);
172
246
  }
173
247
  static async updateMetadata(sandboxName, metadata) {
@@ -179,7 +253,7 @@ class SandboxInstance {
179
253
  throwOnError: true,
180
254
  });
181
255
  const instance = new SandboxInstance(data);
182
- return instance;
256
+ return SandboxInstance.attachH2Session(instance);
183
257
  }
184
258
  static async updateTtl(sandboxName, ttl) {
185
259
  const sandbox = await SandboxInstance.get(sandboxName);
@@ -190,7 +264,7 @@ class SandboxInstance {
190
264
  throwOnError: true,
191
265
  });
192
266
  const instance = new SandboxInstance(data);
193
- return instance;
267
+ return SandboxInstance.attachH2Session(instance);
194
268
  }
195
269
  static async updateLifecycle(sandboxName, lifecycle) {
196
270
  const sandbox = await SandboxInstance.get(sandboxName);
@@ -201,7 +275,7 @@ class SandboxInstance {
201
275
  throwOnError: true,
202
276
  });
203
277
  const instance = new SandboxInstance(data);
204
- return instance;
278
+ return SandboxInstance.attachH2Session(instance);
205
279
  }
206
280
  static async createIfNotExists(sandbox) {
207
281
  try {
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getToolMetadata = exports.blTool = exports.blTools = exports.BLTools = exports.getTool = void 0;
3
+ exports.getToolMetadata = exports.blTool = exports.blTools = exports.BLTools = exports.getTool = exports.McpTool = void 0;
4
4
  const index_js_1 = require("../cache/index.js");
5
5
  const client_js_1 = require("../client/client.js");
6
6
  const internal_js_1 = require("../common/internal.js");
7
7
  const mcpTool_js_1 = require("./mcpTool.js");
8
+ var mcpTool_js_2 = require("./mcpTool.js");
9
+ Object.defineProperty(exports, "McpTool", { enumerable: true, get: function () { return mcpTool_js_2.McpTool; } });
8
10
  const getTool = async (name, options) => {
9
11
  return await (0, mcpTool_js_1.getMcpTool)(name, options);
10
12
  };
@@ -9,7 +9,6 @@ const logger_js_1 = require("../common/logger.js");
9
9
  const settings_js_1 = require("../common/settings.js");
10
10
  const index_js_2 = require("../index.js");
11
11
  const client_js_1 = require("../mcp/client.js");
12
- const telemetry_js_1 = require("../telemetry/telemetry.js");
13
12
  const zodSchema_js_1 = require("./zodSchema.js");
14
13
  const McpToolCache = new Map();
15
14
  class McpTool {
@@ -141,47 +140,24 @@ class McpTool {
141
140
  }
142
141
  }
143
142
  async listTools() {
144
- const span = (0, telemetry_js_1.startSpan)(this.name, {
145
- attributes: {
146
- "span.type": "tool.list",
147
- },
143
+ logger_js_1.logger.debug(`MCP:${this.name}:Listing tools`);
144
+ await this.start();
145
+ const { tools } = (await this.client.listTools());
146
+ await this.close();
147
+ const result = tools.map((tool) => {
148
+ return {
149
+ name: tool.name,
150
+ description: tool.description,
151
+ inputSchema: (0, zodSchema_js_1.schemaToZodSchema)(tool.inputSchema),
152
+ originalSchema: tool.inputSchema,
153
+ call: (input) => {
154
+ return this.call(tool.name, input);
155
+ },
156
+ };
148
157
  });
149
- try {
150
- logger_js_1.logger.debug(`MCP:${this.name}:Listing tools`);
151
- await this.start();
152
- const { tools } = (await this.client.listTools());
153
- await this.close();
154
- const result = tools.map((tool) => {
155
- return {
156
- name: tool.name,
157
- description: tool.description,
158
- inputSchema: (0, zodSchema_js_1.schemaToZodSchema)(tool.inputSchema),
159
- originalSchema: tool.inputSchema,
160
- call: (input) => {
161
- return this.call(tool.name, input);
162
- },
163
- };
164
- });
165
- span.setAttribute("tool.list.result", JSON.stringify(result));
166
- return result;
167
- }
168
- catch (err) {
169
- span.setStatus("error");
170
- span.recordException(err);
171
- throw err;
172
- }
173
- finally {
174
- span.end();
175
- }
158
+ return result;
176
159
  }
177
160
  async call(toolName, args) {
178
- const span = (0, telemetry_js_1.startSpan)(this.name + "." + toolName, {
179
- attributes: {
180
- "span.type": "tool.call",
181
- "tool.name": toolName,
182
- "tool.args": JSON.stringify(args),
183
- },
184
- });
185
161
  try {
186
162
  logger_js_1.logger.debug(`MCP:${this.name}:Tool calling`, toolName, JSON.stringify(args));
187
163
  logger_js_1.logger.debug(`MCP:${this.name}:Tool calling:start`);
@@ -195,7 +171,6 @@ class McpTool {
195
171
  logger_js_1.logger.debug(`MCP:${this.name}:Tool calling:result`);
196
172
  await this.close();
197
173
  logger_js_1.logger.debug(`MCP:${this.name}:Tool result`, toolName, JSON.stringify(args));
198
- span.setAttribute("tool.call.result", JSON.stringify(result));
199
174
  return result;
200
175
  }
201
176
  catch (err) {
@@ -210,9 +185,6 @@ class McpTool {
210
185
  }
211
186
  throw err;
212
187
  }
213
- finally {
214
- span.end();
215
- }
216
188
  }
217
189
  async getTransport(forcedUrl) {
218
190
  if (!this.transportName) {
@@ -1,5 +1,5 @@
1
1
  import { Agent } from "../client/index.js";
2
- declare class BlAgent {
2
+ export declare class BlAgent {
3
3
  agentName: string;
4
4
  constructor(agentName: string);
5
5
  get fallbackUrl(): import("url").URL | null;
@@ -12,4 +12,3 @@ declare class BlAgent {
12
12
  }
13
13
  export declare const blAgent: (agentName: string) => BlAgent;
14
14
  export declare const getAgentMetadata: (agent: string) => Promise<Agent | null>;
15
- export {};
@@ -1,3 +1,8 @@
1
1
  import { Config } from "./settings.js";
2
2
  export declare function initialize(config: Config): void;
3
3
  export declare function authenticate(): Promise<void>;
4
+ /**
5
+ * Close all pooled H2 connections. Call this for explicit cleanup
6
+ * (e.g. in test teardown or before process exit).
7
+ */
8
+ export declare function closeConnections(): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import http2 from "http2";
2
+ import type { h2Pool as H2PoolType } from "./h2pool.js";
3
+ /**
4
+ * Creates a fetch()-compatible function that sends requests over an existing
5
+ * HTTP/2 session. Falls back to global fetch() if the session is closed,
6
+ * destroyed, or if the H2 request times out.
7
+ */
8
+ export declare function createH2Fetch(session: http2.ClientHttp2Session): (input: Request) => Promise<Response>;
9
+ /**
10
+ * Creates a fetch()-compatible function backed by the H2 session pool.
11
+ *
12
+ * Non-blocking: checks the pool cache synchronously. If a warm session is
13
+ * available it's used immediately; otherwise the request goes through
14
+ * regular fetch with zero delay (the pool keeps warming in the background
15
+ * so subsequent calls get H2).
16
+ */
17
+ export declare function createPoolBackedH2Fetch(pool: typeof H2PoolType, domain: string): (input: Request) => Promise<Response>;
18
+ /**
19
+ * Low-level H2 request that takes raw URL + init, skipping Request construction.
20
+ * Used by SandboxAction.h2Fetch() for direct calls from subsystems.
21
+ */
22
+ export declare function h2RequestDirect(session: http2.ClientHttp2Session, url: string, init?: RequestInit): Promise<Response>;
@@ -0,0 +1,38 @@
1
+ import type http2 from "http2";
2
+ /**
3
+ * Singleton H2 session pool keyed by edge domain.
4
+ *
5
+ * - `warm(domain)` starts a background connection (fire-and-forget).
6
+ * - `get(domain)` returns a live session immediately if cached, or awaits
7
+ * an in-flight warming, or establishes a fresh one.
8
+ * - Closed / destroyed sessions are automatically evicted.
9
+ */
10
+ declare class H2Pool {
11
+ private sessions;
12
+ private inflight;
13
+ private _establish;
14
+ /**
15
+ * Lazily resolve the establish function so the http2 / tls / dns modules
16
+ * are only imported in Node.js environments.
17
+ */
18
+ private establish;
19
+ /**
20
+ * Fire-and-forget background warming. Safe to call multiple times for
21
+ * the same domain — only one connection attempt per domain at a time.
22
+ */
23
+ warm(domain: string): void;
24
+ /**
25
+ * Synchronous cache check. Returns a live cached session or `null`.
26
+ * Never blocks, never establishes — use for non-blocking fast paths.
27
+ */
28
+ tryGet(domain: string): http2.ClientHttp2Session | null;
29
+ /**
30
+ * Get a live H2 session for `domain`. Returns immediately from cache,
31
+ * joins an in-flight warming, or starts a new one.
32
+ */
33
+ get(domain: string): Promise<http2.ClientHttp2Session | null>;
34
+ /** Close all sessions (for cleanup). */
35
+ closeAll(): void;
36
+ }
37
+ export declare const h2Pool: H2Pool;
38
+ export {};
@@ -0,0 +1,2 @@
1
+ import http2 from "http2";
2
+ export declare function establishH2(sniHostname: string): Promise<http2.ClientHttp2Session>;
@@ -1,5 +1,5 @@
1
1
  import { CreateJobExecutionRequest, JobExecution } from "../client/index.js";
2
- declare class BlJob {
2
+ export declare class BlJob {
3
3
  jobName: string;
4
4
  constructor(jobName: string);
5
5
  run(tasks: Record<string, unknown>[], options?: {
@@ -39,4 +39,3 @@ declare class BlJob {
39
39
  }): Promise<JobExecution>;
40
40
  }
41
41
  export declare const blJob: (jobName: string) => BlJob;
42
- export {};
@@ -1,3 +1,4 @@
1
+ import { type Client } from "@hey-api/client-fetch";
1
2
  import { SandboxConfiguration } from "./types.js";
2
3
  export declare class ResponseError extends Error {
3
4
  response: Response;
@@ -7,12 +8,18 @@ export declare class ResponseError extends Error {
7
8
  }
8
9
  export declare class SandboxAction {
9
10
  protected sandbox: SandboxConfiguration;
11
+ private _h2Client;
10
12
  constructor(sandbox: SandboxConfiguration);
11
13
  get name(): string;
12
14
  get fallbackUrl(): string | null;
13
15
  get externalUrl(): string;
14
16
  get internalUrl(): string;
15
- get client(): import("@hey-api/client-fetch").Client;
17
+ get client(): Client;
18
+ /**
19
+ * Routes through the H2 session when available, falling back to
20
+ * globalThis.fetch. Uses a direct H2 path that avoids Request allocation.
21
+ */
22
+ protected h2Fetch(input: string | URL, init?: RequestInit): Promise<Response>;
16
23
  get forcedUrl(): string | import("url").URL | null;
17
24
  get url(): string;
18
25
  handleResponseError(response: Response, data: unknown, error: unknown): void;