@blaxel/core 0.2.49-dev.210 → 0.2.49-dev.212

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 (57) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/common/settings.js +2 -2
  3. package/dist/cjs/sandbox/codegen/codegen-ws.js +30 -0
  4. package/dist/cjs/sandbox/filesystem/filesystem-ws.js +106 -0
  5. package/dist/cjs/sandbox/filesystem/filesystem.js +4 -15
  6. package/dist/cjs/sandbox/network/network-ws.js +12 -0
  7. package/dist/cjs/sandbox/process/process-ws.js +139 -0
  8. package/dist/cjs/sandbox/sandbox.js +67 -10
  9. package/dist/cjs/sandbox/websocket/client.js +275 -0
  10. package/dist/cjs/sandbox/websocket/index.js +17 -0
  11. package/dist/cjs/types/sandbox/codegen/codegen-ws.d.ts +10 -0
  12. package/dist/cjs/types/sandbox/filesystem/filesystem-ws.d.ts +35 -0
  13. package/dist/cjs/types/sandbox/network/network-ws.d.ts +7 -0
  14. package/dist/cjs/types/sandbox/process/process-ws.d.ts +27 -0
  15. package/dist/cjs/types/sandbox/sandbox.d.ts +12 -6
  16. package/dist/cjs/types/sandbox/types.d.ts +3 -0
  17. package/dist/cjs/types/sandbox/websocket/client.d.ts +49 -0
  18. package/dist/cjs/types/sandbox/websocket/index.d.ts +1 -0
  19. package/dist/cjs-browser/.tsbuildinfo +1 -1
  20. package/dist/cjs-browser/common/settings.js +2 -2
  21. package/dist/cjs-browser/sandbox/codegen/codegen-ws.js +30 -0
  22. package/dist/cjs-browser/sandbox/filesystem/filesystem-ws.js +106 -0
  23. package/dist/cjs-browser/sandbox/filesystem/filesystem.js +4 -15
  24. package/dist/cjs-browser/sandbox/network/network-ws.js +12 -0
  25. package/dist/cjs-browser/sandbox/process/process-ws.js +139 -0
  26. package/dist/cjs-browser/sandbox/sandbox.js +67 -10
  27. package/dist/cjs-browser/sandbox/websocket/client.js +275 -0
  28. package/dist/cjs-browser/sandbox/websocket/index.js +17 -0
  29. package/dist/cjs-browser/types/sandbox/codegen/codegen-ws.d.ts +10 -0
  30. package/dist/cjs-browser/types/sandbox/filesystem/filesystem-ws.d.ts +35 -0
  31. package/dist/cjs-browser/types/sandbox/network/network-ws.d.ts +7 -0
  32. package/dist/cjs-browser/types/sandbox/process/process-ws.d.ts +27 -0
  33. package/dist/cjs-browser/types/sandbox/sandbox.d.ts +12 -6
  34. package/dist/cjs-browser/types/sandbox/types.d.ts +3 -0
  35. package/dist/cjs-browser/types/sandbox/websocket/client.d.ts +49 -0
  36. package/dist/cjs-browser/types/sandbox/websocket/index.d.ts +1 -0
  37. package/dist/esm/.tsbuildinfo +1 -1
  38. package/dist/esm/common/settings.js +2 -2
  39. package/dist/esm/sandbox/codegen/codegen-ws.js +26 -0
  40. package/dist/esm/sandbox/filesystem/filesystem-ws.js +102 -0
  41. package/dist/esm/sandbox/filesystem/filesystem.js +4 -15
  42. package/dist/esm/sandbox/network/network-ws.js +8 -0
  43. package/dist/esm/sandbox/process/process-ws.js +135 -0
  44. package/dist/esm/sandbox/sandbox.js +67 -10
  45. package/dist/esm/sandbox/websocket/client.js +271 -0
  46. package/dist/esm/sandbox/websocket/index.js +1 -0
  47. package/dist/esm-browser/.tsbuildinfo +1 -1
  48. package/dist/esm-browser/common/settings.js +2 -2
  49. package/dist/esm-browser/sandbox/codegen/codegen-ws.js +26 -0
  50. package/dist/esm-browser/sandbox/filesystem/filesystem-ws.js +102 -0
  51. package/dist/esm-browser/sandbox/filesystem/filesystem.js +4 -15
  52. package/dist/esm-browser/sandbox/network/network-ws.js +8 -0
  53. package/dist/esm-browser/sandbox/process/process-ws.js +135 -0
  54. package/dist/esm-browser/sandbox/sandbox.js +67 -10
  55. package/dist/esm-browser/sandbox/websocket/client.js +271 -0
  56. package/dist/esm-browser/sandbox/websocket/index.js +1 -0
  57. package/package.json +2 -2
@@ -0,0 +1,102 @@
1
+ import { SandboxAction } from "../action.js";
2
+ import { SandboxFileSystem } from "./filesystem.js";
3
+ export class SandboxFileSystemWebSocket extends SandboxAction {
4
+ process;
5
+ wsClient;
6
+ httpClient;
7
+ constructor(sandbox, process, wsClient) {
8
+ super(sandbox);
9
+ this.process = process;
10
+ this.wsClient = wsClient;
11
+ // Create HTTP client for fallback operations
12
+ this.httpClient = new SandboxFileSystem(sandbox, process);
13
+ }
14
+ async mkdir(path, permissions = "0755") {
15
+ path = this.formatPath(path);
16
+ const data = await this.wsClient.send("filesystem:create", {
17
+ path,
18
+ isDirectory: true,
19
+ permissions,
20
+ });
21
+ return data;
22
+ }
23
+ async write(path, content) {
24
+ path = this.formatPath(path);
25
+ const data = await this.wsClient.send("filesystem:create", {
26
+ path,
27
+ content,
28
+ isDirectory: false,
29
+ });
30
+ return data;
31
+ }
32
+ async writeBinary(path, content) {
33
+ return this.httpClient.writeBinary(path, content);
34
+ }
35
+ async writeTree(files, destinationPath = null) {
36
+ const path = this.formatPath(destinationPath ?? "");
37
+ const filesMap = files.reduce((acc, file) => {
38
+ acc[file.path] = file.content;
39
+ return acc;
40
+ }, {});
41
+ const data = await this.wsClient.send("filesystem:tree:create", {
42
+ path,
43
+ files: filesMap,
44
+ });
45
+ return data;
46
+ }
47
+ async read(path) {
48
+ path = this.formatPath(path);
49
+ const data = await this.wsClient.send("filesystem:get", { path });
50
+ return data.content;
51
+ }
52
+ async readBinary(path) {
53
+ // Binary downloads are better suited for HTTP
54
+ // Fall back to HTTP client for binary operations
55
+ return this.httpClient.readBinary(path);
56
+ }
57
+ async download(src, destinationPath, options = {}) {
58
+ // File downloads are better suited for HTTP
59
+ // Fall back to HTTP client
60
+ return this.httpClient.download(src, destinationPath, options);
61
+ }
62
+ async rm(path, recursive = false) {
63
+ path = this.formatPath(path);
64
+ const data = await this.wsClient.send("filesystem:delete", {
65
+ path,
66
+ recursive,
67
+ });
68
+ return data;
69
+ }
70
+ async ls(path) {
71
+ path = this.formatPath(path);
72
+ const data = await this.wsClient.send("filesystem:get", { path });
73
+ return data;
74
+ }
75
+ async cp(source, destination, { maxWait = 180000 } = {}) {
76
+ // Copy operation is typically done via process execution
77
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
78
+ let process = await this.process.exec({
79
+ command: `cp -r ${source} ${destination}`,
80
+ });
81
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument
82
+ process = await this.process.wait(process.pid, { maxWait, interval: 100 });
83
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
84
+ if (process.status === "failed") {
85
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
86
+ throw new Error(`Could not copy ${source} to ${destination} cause: ${process.logs}`);
87
+ }
88
+ return {
89
+ message: "Files copied",
90
+ source,
91
+ destination,
92
+ };
93
+ }
94
+ watch(path, callback, options) {
95
+ // File watching uses HTTP streaming which is already optimized
96
+ // Fall back to HTTP client
97
+ return this.httpClient.watch(path, callback, options);
98
+ }
99
+ formatPath(path) {
100
+ return path;
101
+ }
102
+ }
@@ -5,7 +5,7 @@ import { deleteFilesystemByPath, getFilesystemByPath, getWatchFilesystemByPath,
5
5
  // Multipart upload constants
6
6
  const MULTIPART_THRESHOLD = 5 * 1024 * 1024; // 5MB
7
7
  const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB per part
8
- const MAX_PARALLEL_UPLOADS = 3; // Number of parallel part uploads
8
+ const MAX_PARALLEL_UPLOADS = 20; // Number of parallel part uploads
9
9
  export class SandboxFileSystem extends SandboxAction {
10
10
  process;
11
11
  constructor(sandbox, process) {
@@ -301,7 +301,6 @@ export class SandboxFileSystem extends SandboxAction {
301
301
  // Multipart upload helper methods
302
302
  async initiateMultipartUpload(path, permissions = "0644") {
303
303
  path = this.formatPath(path);
304
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
305
304
  const { data } = await postFilesystemMultipartInitiateByPath({
306
305
  path: { path },
307
306
  body: { permissions },
@@ -312,7 +311,6 @@ export class SandboxFileSystem extends SandboxAction {
312
311
  return data;
313
312
  }
314
313
  async uploadPart(uploadId, partNumber, fileBlob) {
315
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
316
314
  const { data } = await putFilesystemMultipartByUploadIdPart({
317
315
  path: { uploadId },
318
316
  query: { partNumber },
@@ -324,7 +322,6 @@ export class SandboxFileSystem extends SandboxAction {
324
322
  return data;
325
323
  }
326
324
  async completeMultipartUpload(uploadId, parts) {
327
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
328
325
  const { data } = await postFilesystemMultipartByUploadIdComplete({
329
326
  path: { uploadId },
330
327
  body: { parts },
@@ -335,19 +332,17 @@ export class SandboxFileSystem extends SandboxAction {
335
332
  return data;
336
333
  }
337
334
  async abortMultipartUpload(uploadId) {
338
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
339
- await deleteFilesystemMultipartByUploadIdAbort({
335
+ const { data } = await deleteFilesystemMultipartByUploadIdAbort({
340
336
  path: { uploadId },
341
337
  baseUrl: this.url,
342
338
  client: this.client,
343
339
  throwOnError: true,
344
340
  });
341
+ return data;
345
342
  }
346
343
  async uploadWithMultipart(path, blob, permissions = "0644") {
347
344
  // Initiate multipart upload
348
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
349
345
  const initResponse = await this.initiateMultipartUpload(path, permissions);
350
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
351
346
  const uploadId = initResponse.uploadId;
352
347
  if (!uploadId) {
353
348
  throw new Error("Failed to get upload ID from initiate response");
@@ -364,26 +359,20 @@ export class SandboxFileSystem extends SandboxAction {
364
359
  const start = (partNumber - 1) * CHUNK_SIZE;
365
360
  const end = Math.min(start + CHUNK_SIZE, size);
366
361
  const chunk = blob.slice(start, end);
367
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
368
362
  batch.push(this.uploadPart(uploadId, partNumber, chunk));
369
363
  }
370
364
  // Wait for batch to complete
371
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
372
365
  const batchResults = await Promise.all(batch);
373
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument
374
- parts.push(...batchResults.map((r) => ({ partNumber: r.partNumber, etag: r.etag })));
366
+ parts.push(...batchResults);
375
367
  }
376
368
  // Sort parts by partNumber to ensure correct order
377
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
378
369
  parts.sort((a, b) => (a.partNumber ?? 0) - (b.partNumber ?? 0));
379
370
  // Complete the upload
380
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
381
371
  return await this.completeMultipartUpload(uploadId, parts);
382
372
  }
383
373
  catch (error) {
384
374
  // Abort the upload on failure
385
375
  try {
386
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
387
376
  await this.abortMultipartUpload(uploadId);
388
377
  }
389
378
  catch (abortError) {
@@ -0,0 +1,8 @@
1
+ import { SandboxAction } from "../action.js";
2
+ export class SandboxNetworkWebSocket extends SandboxAction {
3
+ wsClient;
4
+ constructor(sandbox, wsClient) {
5
+ super(sandbox);
6
+ this.wsClient = wsClient;
7
+ }
8
+ }
@@ -0,0 +1,135 @@
1
+ import { SandboxAction } from "../action.js";
2
+ import { SandboxProcess } from "./process.js";
3
+ export class SandboxProcessWebSocket extends SandboxAction {
4
+ wsClient;
5
+ httpClient;
6
+ constructor(sandbox, wsClient) {
7
+ super(sandbox);
8
+ this.wsClient = wsClient;
9
+ // Create HTTP client for fallback operations
10
+ this.httpClient = new SandboxProcess(sandbox);
11
+ }
12
+ streamLogs(identifier, options) {
13
+ const streamId = this.wsClient.sendStream("process:logs:stream:start", { identifier }, (data) => {
14
+ // Handle streaming log data
15
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
16
+ if (data && data.log) {
17
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
18
+ const log = String(data.log);
19
+ // Parse log format: "stdout:" or "stderr:" prefix
20
+ if (log.startsWith('stdout:')) {
21
+ const stdout = log.slice(7);
22
+ options.onStdout?.(stdout);
23
+ options.onLog?.(stdout);
24
+ }
25
+ else if (log.startsWith('stderr:')) {
26
+ const stderr = log.slice(7);
27
+ options.onStderr?.(stderr);
28
+ options.onLog?.(stderr);
29
+ }
30
+ else {
31
+ options.onLog?.(log);
32
+ }
33
+ }
34
+ }, () => {
35
+ // Stream ended
36
+ });
37
+ return {
38
+ close: () => this.wsClient.cancelStream(streamId),
39
+ };
40
+ }
41
+ async exec(process) {
42
+ let onLog;
43
+ if ('onLog' in process && process.onLog) {
44
+ onLog = process.onLog;
45
+ delete process.onLog;
46
+ }
47
+ // Store original wait_for_completion setting
48
+ const shouldWaitForCompletion = process.waitForCompletion;
49
+ // Always start process without wait_for_completion to avoid server-side blocking
50
+ if (shouldWaitForCompletion && onLog) {
51
+ process.waitForCompletion = false;
52
+ }
53
+ const data = await this.wsClient.send("process:execute", process);
54
+ let result = data;
55
+ // Handle wait_for_completion with parallel log streaming
56
+ if (shouldWaitForCompletion && onLog) {
57
+ const streamControl = this.streamLogs(result.pid, { onLog });
58
+ try {
59
+ // Wait for process completion
60
+ result = await this.wait(result.pid, { interval: 500, maxWait: 1000 * 60 * 60 });
61
+ }
62
+ finally {
63
+ // Clean up log streaming
64
+ if (streamControl) {
65
+ streamControl.close();
66
+ }
67
+ }
68
+ }
69
+ else {
70
+ // For non-blocking execution, set up log streaming immediately if requested
71
+ if (onLog) {
72
+ const streamControl = this.streamLogs(result.pid, { onLog });
73
+ return {
74
+ ...result,
75
+ close() {
76
+ if (streamControl) {
77
+ streamControl.close();
78
+ }
79
+ },
80
+ };
81
+ }
82
+ }
83
+ return { ...result, close: () => { } };
84
+ }
85
+ async wait(identifier, { maxWait = 60000, interval = 1000 } = {}) {
86
+ const startTime = Date.now();
87
+ let status = "running";
88
+ let data = await this.get(identifier);
89
+ while (status === "running") {
90
+ await new Promise((resolve) => setTimeout(resolve, interval));
91
+ try {
92
+ data = await this.get(identifier);
93
+ status = data.status ?? "running";
94
+ }
95
+ catch {
96
+ break;
97
+ }
98
+ if (Date.now() - startTime > maxWait) {
99
+ throw new Error("Process did not finish in time");
100
+ }
101
+ }
102
+ return data;
103
+ }
104
+ async get(identifier) {
105
+ const data = await this.wsClient.send("process:get", { identifier });
106
+ return data;
107
+ }
108
+ async list() {
109
+ const data = await this.wsClient.send("process:list", {});
110
+ return data;
111
+ }
112
+ async stop(identifier) {
113
+ const data = await this.wsClient.send("process:stop", { identifier });
114
+ return data;
115
+ }
116
+ async kill(identifier) {
117
+ const data = await this.wsClient.send("process:kill", { identifier });
118
+ return data;
119
+ }
120
+ async logs(identifier, type = "all") {
121
+ const data = await this.wsClient.send("process:logs", {
122
+ identifier,
123
+ });
124
+ if (type === "all") {
125
+ return data.logs || "";
126
+ }
127
+ else if (type === "stdout") {
128
+ return data.stdout || "";
129
+ }
130
+ else if (type === "stderr") {
131
+ return data.stderr || "";
132
+ }
133
+ throw new Error("Unsupported log type");
134
+ }
135
+ }
@@ -2,11 +2,16 @@ 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
4
  import { SandboxFileSystem } from "./filesystem/index.js";
5
+ import { SandboxFileSystemWebSocket } from "./filesystem/filesystem-ws.js";
5
6
  import { SandboxNetwork } from "./network/index.js";
7
+ import { SandboxNetworkWebSocket } from "./network/network-ws.js";
6
8
  import { SandboxPreviews } from "./preview.js";
7
9
  import { SandboxProcess } from "./process/index.js";
10
+ import { SandboxProcessWebSocket } from "./process/process-ws.js";
8
11
  import { SandboxCodegen } from "./codegen/index.js";
12
+ import { SandboxCodegenWebSocket } from "./codegen/codegen-ws.js";
9
13
  import { SandboxSessions } from "./session.js";
14
+ import { WebSocketClient } from "./websocket/index.js";
10
15
  import { normalizeEnvs, normalizePorts, normalizeVolumes } from "./types.js";
11
16
  export class SandboxInstance {
12
17
  sandbox;
@@ -16,14 +21,32 @@ export class SandboxInstance {
16
21
  previews;
17
22
  sessions;
18
23
  codegen;
24
+ wsClient;
19
25
  constructor(sandbox) {
20
26
  this.sandbox = sandbox;
21
- this.process = new SandboxProcess(sandbox);
22
- this.fs = new SandboxFileSystem(sandbox, this.process);
23
- this.network = new SandboxNetwork(sandbox);
27
+ // If connection type is websocket, initialize WebSocket client and use WebSocket transport layers
28
+ if (sandbox.connectionType === "websocket") {
29
+ const url = sandbox.forceUrl || sandbox.metadata?.url || "";
30
+ this.wsClient = new WebSocketClient({
31
+ url,
32
+ headers: sandbox.headers,
33
+ });
34
+ // Initialize WebSocket-based action handlers
35
+ this.process = new SandboxProcessWebSocket(sandbox, this.wsClient);
36
+ this.fs = new SandboxFileSystemWebSocket(sandbox, this.process, this.wsClient);
37
+ this.network = new SandboxNetworkWebSocket(sandbox, this.wsClient);
38
+ this.codegen = new SandboxCodegenWebSocket(sandbox, this.wsClient);
39
+ }
40
+ else {
41
+ // Default to HTTP-based action handlers
42
+ this.process = new SandboxProcess(sandbox);
43
+ this.fs = new SandboxFileSystem(sandbox, this.process);
44
+ this.network = new SandboxNetwork(sandbox);
45
+ this.codegen = new SandboxCodegen(sandbox);
46
+ }
47
+ // These are always HTTP-based
24
48
  this.previews = new SandboxPreviews(sandbox);
25
49
  this.sessions = new SandboxSessions(sandbox);
26
- this.codegen = new SandboxCodegen(sandbox);
27
50
  }
28
51
  get metadata() {
29
52
  return this.sandbox.metadata;
@@ -42,10 +65,17 @@ export class SandboxInstance {
42
65
  logger.warn("⚠️ Warning: sandbox.wait() is deprecated. You don't need to wait for the sandbox to be deployed anymore.");
43
66
  return this;
44
67
  }
68
+ closeConnection() {
69
+ if (this.wsClient) {
70
+ this.wsClient.close();
71
+ }
72
+ }
45
73
  static async create(sandbox, { safe = true } = {}) {
46
74
  const defaultName = `sandbox-${uuidv4().replace(/-/g, '').substring(0, 8)}`;
47
75
  const defaultImage = `blaxel/base-image:latest`;
48
76
  const defaultMemory = 4096;
77
+ // Store connection type if provided
78
+ let connectionType;
49
79
  // Handle SandboxCreateConfiguration or simple dict with name/image/memory/ports/envs/volumes keys
50
80
  if (!sandbox ||
51
81
  'name' in sandbox ||
@@ -55,7 +85,8 @@ export class SandboxInstance {
55
85
  'envs' in sandbox ||
56
86
  'volumes' in sandbox ||
57
87
  'lifecycle' in sandbox ||
58
- 'snapshotEnabled' in sandbox) {
88
+ 'snapshotEnabled' in sandbox ||
89
+ 'connectionType' in sandbox) {
59
90
  if (!sandbox)
60
91
  sandbox = {};
61
92
  if (!sandbox.name)
@@ -64,6 +95,7 @@ export class SandboxInstance {
64
95
  sandbox.image = defaultImage;
65
96
  if (!sandbox.memory)
66
97
  sandbox.memory = defaultMemory;
98
+ connectionType = sandbox.connectionType;
67
99
  const ports = normalizePorts(sandbox.ports);
68
100
  const envs = normalizeEnvs(sandbox.envs);
69
101
  const volumes = normalizeVolumes(sandbox.volumes);
@@ -112,7 +144,16 @@ export class SandboxInstance {
112
144
  body: sandbox,
113
145
  throwOnError: true,
114
146
  });
115
- const instance = new SandboxInstance(data);
147
+ // Add connection type to configuration
148
+ const config = {
149
+ ...data,
150
+ connectionType: connectionType || "http",
151
+ };
152
+ const instance = new SandboxInstance(config);
153
+ // Connect WebSocket if needed
154
+ if (connectionType === "websocket" && instance.wsClient) {
155
+ await instance.wsClient.connect();
156
+ }
116
157
  // TODO remove this part once we have a better way to handle this
117
158
  if (safe) {
118
159
  try {
@@ -122,20 +163,34 @@ export class SandboxInstance {
122
163
  }
123
164
  return instance;
124
165
  }
125
- static async get(sandboxName) {
166
+ static async get(sandboxName, connectionType) {
126
167
  const { data } = await getSandbox({
127
168
  path: {
128
169
  sandboxName,
129
170
  },
130
171
  throwOnError: true,
131
172
  });
132
- return new SandboxInstance(data);
173
+ // Add connection type to configuration
174
+ const config = {
175
+ ...data,
176
+ connectionType: connectionType || "http",
177
+ };
178
+ const instance = new SandboxInstance(config);
179
+ // Connect WebSocket if needed
180
+ if (connectionType === "websocket" && instance.wsClient) {
181
+ await instance.wsClient.connect();
182
+ }
183
+ return instance;
133
184
  }
134
185
  static async list() {
135
186
  const { data } = await listSandboxes({ throwOnError: true });
136
187
  return data.map((sandbox) => new SandboxInstance(sandbox));
137
188
  }
138
- static async delete(sandboxName) {
189
+ static async delete(sandboxName, instance) {
190
+ // Close WebSocket connection if instance is provided
191
+ if (instance && instance.wsClient) {
192
+ instance.closeConnection();
193
+ }
139
194
  const { data } = await deleteSandbox({
140
195
  path: {
141
196
  sandboxName,
@@ -165,8 +220,10 @@ export class SandboxInstance {
165
220
  if (!name) {
166
221
  throw new Error("Sandbox name is required");
167
222
  }
223
+ // Get connection type if specified
224
+ const connectionType = 'connectionType' in sandbox ? sandbox.connectionType : undefined;
168
225
  // Get the existing sandbox to check its status
169
- const sandboxInstance = await SandboxInstance.get(name);
226
+ const sandboxInstance = await SandboxInstance.get(name, connectionType);
170
227
  // If the sandbox is TERMINATED, treat it as not existing
171
228
  if (sandboxInstance.status === "TERMINATED") {
172
229
  // Create a new sandbox - backend will handle cleanup of the terminated one