computesdk 1.10.3 → 1.11.1
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.
- package/README.md +294 -4
- package/dist/index.d.mts +80 -20
- package/dist/index.d.ts +80 -20
- package/dist/index.js +179 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +179 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -250,6 +250,42 @@ interface TerminalErrorMessage {
|
|
|
250
250
|
error: string;
|
|
251
251
|
};
|
|
252
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Command stdout streaming message (exec mode with stream: true)
|
|
255
|
+
*/
|
|
256
|
+
interface CommandStdoutMessage {
|
|
257
|
+
type: 'command:stdout';
|
|
258
|
+
channel: string;
|
|
259
|
+
data: {
|
|
260
|
+
terminal_id: string;
|
|
261
|
+
cmd_id: string;
|
|
262
|
+
output: string;
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Command stderr streaming message (exec mode with stream: true)
|
|
267
|
+
*/
|
|
268
|
+
interface CommandStderrMessage {
|
|
269
|
+
type: 'command:stderr';
|
|
270
|
+
channel: string;
|
|
271
|
+
data: {
|
|
272
|
+
terminal_id: string;
|
|
273
|
+
cmd_id: string;
|
|
274
|
+
output: string;
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Command exit message (exec mode with stream: true)
|
|
279
|
+
*/
|
|
280
|
+
interface CommandExitMessage {
|
|
281
|
+
type: 'command:exit';
|
|
282
|
+
channel: string;
|
|
283
|
+
data: {
|
|
284
|
+
terminal_id: string;
|
|
285
|
+
cmd_id: string;
|
|
286
|
+
exit_code: number;
|
|
287
|
+
};
|
|
288
|
+
}
|
|
253
289
|
/**
|
|
254
290
|
* File watcher created notification
|
|
255
291
|
*/
|
|
@@ -427,6 +463,12 @@ declare class WebSocketManager {
|
|
|
427
463
|
* Resize terminal window
|
|
428
464
|
*/
|
|
429
465
|
resizeTerminal(terminalId: string, cols: number, rows: number): void;
|
|
466
|
+
/**
|
|
467
|
+
* Start a pending streaming command
|
|
468
|
+
* Used in two-phase streaming flow: HTTP request creates pending command,
|
|
469
|
+
* then this signal triggers execution after client has subscribed.
|
|
470
|
+
*/
|
|
471
|
+
startCommand(cmdId: string): void;
|
|
430
472
|
/**
|
|
431
473
|
* Register event handler
|
|
432
474
|
*/
|
|
@@ -438,6 +480,9 @@ declare class WebSocketManager {
|
|
|
438
480
|
on(event: 'terminal:output', handler: MessageHandler<TerminalOutputMessage>): void;
|
|
439
481
|
on(event: 'terminal:destroyed', handler: MessageHandler<TerminalDestroyedMessage>): void;
|
|
440
482
|
on(event: 'terminal:error', handler: MessageHandler<TerminalErrorMessage>): void;
|
|
483
|
+
on(event: 'command:stdout', handler: MessageHandler<CommandStdoutMessage>): void;
|
|
484
|
+
on(event: 'command:stderr', handler: MessageHandler<CommandStderrMessage>): void;
|
|
485
|
+
on(event: 'command:exit', handler: MessageHandler<CommandExitMessage>): void;
|
|
441
486
|
on(event: 'watcher:created', handler: MessageHandler<WatcherCreatedMessage>): void;
|
|
442
487
|
on(event: 'file:changed', handler: MessageHandler<FileChangedMessage>): void;
|
|
443
488
|
on(event: 'watcher:destroyed', handler: MessageHandler<WatcherDestroyedMessage>): void;
|
|
@@ -1957,6 +2002,13 @@ interface SandboxConfig {
|
|
|
1957
2002
|
protocol?: 'json' | 'binary';
|
|
1958
2003
|
/** Optional metadata associated with the sandbox */
|
|
1959
2004
|
metadata?: Record<string, unknown>;
|
|
2005
|
+
/**
|
|
2006
|
+
* Handler called when destroy() is invoked.
|
|
2007
|
+
* If provided, this is called to destroy the sandbox (e.g., via gateway API).
|
|
2008
|
+
* If not provided, destroy() only disconnects the WebSocket.
|
|
2009
|
+
* @internal
|
|
2010
|
+
*/
|
|
2011
|
+
destroyHandler?: () => Promise<void>;
|
|
1960
2012
|
}
|
|
1961
2013
|
/**
|
|
1962
2014
|
* Health check response
|
|
@@ -2076,7 +2128,7 @@ interface FileResponse {
|
|
|
2076
2128
|
};
|
|
2077
2129
|
}
|
|
2078
2130
|
/**
|
|
2079
|
-
* Command execution response
|
|
2131
|
+
* Command execution response (used by both /run/command and /terminals/{id}/execute)
|
|
2080
2132
|
*/
|
|
2081
2133
|
interface CommandExecutionResponse {
|
|
2082
2134
|
message: string;
|
|
@@ -2084,12 +2136,12 @@ interface CommandExecutionResponse {
|
|
|
2084
2136
|
terminal_id?: string;
|
|
2085
2137
|
cmd_id?: string;
|
|
2086
2138
|
command: string;
|
|
2087
|
-
output?: string;
|
|
2088
2139
|
stdout: string;
|
|
2089
2140
|
stderr: string;
|
|
2090
2141
|
exit_code?: number;
|
|
2091
2142
|
duration_ms?: number;
|
|
2092
2143
|
status?: 'running' | 'completed' | 'failed';
|
|
2144
|
+
channel?: string;
|
|
2093
2145
|
pty?: boolean;
|
|
2094
2146
|
};
|
|
2095
2147
|
}
|
|
@@ -2142,22 +2194,6 @@ interface CodeExecutionResponse {
|
|
|
2142
2194
|
language: string;
|
|
2143
2195
|
};
|
|
2144
2196
|
}
|
|
2145
|
-
/**
|
|
2146
|
-
* Run command response (POST /run/command)
|
|
2147
|
-
*/
|
|
2148
|
-
interface RunCommandResponse {
|
|
2149
|
-
message: string;
|
|
2150
|
-
data: {
|
|
2151
|
-
terminal_id?: string;
|
|
2152
|
-
cmd_id?: string;
|
|
2153
|
-
command: string;
|
|
2154
|
-
stdout: string;
|
|
2155
|
-
stderr: string;
|
|
2156
|
-
exit_code?: number;
|
|
2157
|
-
duration_ms?: number;
|
|
2158
|
-
status?: 'running' | 'completed' | 'failed';
|
|
2159
|
-
};
|
|
2160
|
-
}
|
|
2161
2197
|
/**
|
|
2162
2198
|
* File watcher information
|
|
2163
2199
|
*/
|
|
@@ -2629,9 +2665,10 @@ declare class Sandbox {
|
|
|
2629
2665
|
command: string;
|
|
2630
2666
|
shell?: string;
|
|
2631
2667
|
background?: boolean;
|
|
2668
|
+
stream?: boolean;
|
|
2632
2669
|
cwd?: string;
|
|
2633
2670
|
env?: Record<string, string>;
|
|
2634
|
-
}): Promise<
|
|
2671
|
+
}): Promise<CommandExecutionResponse>;
|
|
2635
2672
|
/**
|
|
2636
2673
|
* List files at the specified path
|
|
2637
2674
|
*/
|
|
@@ -2644,6 +2681,11 @@ declare class Sandbox {
|
|
|
2644
2681
|
* Get file metadata (without content)
|
|
2645
2682
|
*/
|
|
2646
2683
|
getFile(path: string): Promise<FileResponse>;
|
|
2684
|
+
/**
|
|
2685
|
+
* Encode a file path for use in URLs
|
|
2686
|
+
* Strips leading slash and encodes each segment separately to preserve path structure
|
|
2687
|
+
*/
|
|
2688
|
+
private encodeFilePath;
|
|
2647
2689
|
/**
|
|
2648
2690
|
* Read file content
|
|
2649
2691
|
*/
|
|
@@ -2916,6 +2958,8 @@ declare class Sandbox {
|
|
|
2916
2958
|
* @param options.background - Run in background (server uses goroutines)
|
|
2917
2959
|
* @param options.cwd - Working directory (server uses cmd.Dir)
|
|
2918
2960
|
* @param options.env - Environment variables (server uses cmd.Env)
|
|
2961
|
+
* @param options.onStdout - Callback for streaming stdout data
|
|
2962
|
+
* @param options.onStderr - Callback for streaming stderr data
|
|
2919
2963
|
* @returns Command execution result
|
|
2920
2964
|
*
|
|
2921
2965
|
* @example
|
|
@@ -2931,12 +2975,20 @@ declare class Sandbox {
|
|
|
2931
2975
|
* background: true,
|
|
2932
2976
|
* env: { PORT: '3000' }
|
|
2933
2977
|
* })
|
|
2978
|
+
*
|
|
2979
|
+
* // With streaming output
|
|
2980
|
+
* await sandbox.runCommand('npm install', {
|
|
2981
|
+
* onStdout: (data) => console.log(data),
|
|
2982
|
+
* onStderr: (data) => console.error(data),
|
|
2983
|
+
* })
|
|
2934
2984
|
* ```
|
|
2935
2985
|
*/
|
|
2936
2986
|
runCommand(command: string, options?: {
|
|
2937
2987
|
background?: boolean;
|
|
2938
2988
|
cwd?: string;
|
|
2939
2989
|
env?: Record<string, string>;
|
|
2990
|
+
onStdout?: (data: string) => void;
|
|
2991
|
+
onStderr?: (data: string) => void;
|
|
2940
2992
|
}): Promise<{
|
|
2941
2993
|
stdout: string;
|
|
2942
2994
|
stderr: string;
|
|
@@ -2979,6 +3031,9 @@ declare class Sandbox {
|
|
|
2979
3031
|
getInstance(): this;
|
|
2980
3032
|
/**
|
|
2981
3033
|
* Destroy the sandbox (Sandbox interface method)
|
|
3034
|
+
*
|
|
3035
|
+
* If a destroyHandler was provided (e.g., from gateway), calls it to destroy
|
|
3036
|
+
* the sandbox on the backend. Otherwise, only disconnects the WebSocket.
|
|
2982
3037
|
*/
|
|
2983
3038
|
destroy(): Promise<void>;
|
|
2984
3039
|
/**
|
|
@@ -3076,8 +3131,13 @@ declare function getMissingEnvVars(provider: ProviderName): string[];
|
|
|
3076
3131
|
interface ExplicitComputeConfig {
|
|
3077
3132
|
/** Provider name to use */
|
|
3078
3133
|
provider: ProviderName;
|
|
3134
|
+
/**
|
|
3135
|
+
* ComputeSDK API key (required for gateway mode)
|
|
3136
|
+
* @deprecated Use `computesdkApiKey` for clarity
|
|
3137
|
+
*/
|
|
3138
|
+
apiKey?: string;
|
|
3079
3139
|
/** ComputeSDK API key (required for gateway mode) */
|
|
3080
|
-
|
|
3140
|
+
computesdkApiKey?: string;
|
|
3081
3141
|
/** Optional gateway URL override */
|
|
3082
3142
|
gatewayUrl?: string;
|
|
3083
3143
|
/** Provider-specific configurations */
|
package/dist/index.js
CHANGED
|
@@ -538,6 +538,17 @@ var WebSocketManager = class {
|
|
|
538
538
|
data: { terminal_id: terminalId, cols, rows }
|
|
539
539
|
});
|
|
540
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* Start a pending streaming command
|
|
543
|
+
* Used in two-phase streaming flow: HTTP request creates pending command,
|
|
544
|
+
* then this signal triggers execution after client has subscribed.
|
|
545
|
+
*/
|
|
546
|
+
startCommand(cmdId) {
|
|
547
|
+
this.sendRaw({
|
|
548
|
+
type: "command:start",
|
|
549
|
+
data: { cmd_id: cmdId }
|
|
550
|
+
});
|
|
551
|
+
}
|
|
541
552
|
on(event, handler) {
|
|
542
553
|
if (!this.eventHandlers.has(event)) {
|
|
543
554
|
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
@@ -1011,6 +1022,7 @@ var TerminalInstance = class {
|
|
|
1011
1022
|
throw new Error("Destroy handler not set");
|
|
1012
1023
|
}
|
|
1013
1024
|
await this._destroyHandler();
|
|
1025
|
+
this._status = "stopped";
|
|
1014
1026
|
this.cleanup();
|
|
1015
1027
|
}
|
|
1016
1028
|
/**
|
|
@@ -1916,7 +1928,8 @@ var Sandbox = class {
|
|
|
1916
1928
|
headers: config.headers || {},
|
|
1917
1929
|
timeout: config.timeout || 3e4,
|
|
1918
1930
|
protocol: config.protocol || "binary",
|
|
1919
|
-
metadata: config.metadata
|
|
1931
|
+
metadata: config.metadata,
|
|
1932
|
+
destroyHandler: config.destroyHandler
|
|
1920
1933
|
};
|
|
1921
1934
|
this.WebSocketImpl = config.WebSocket || globalThis.WebSocket;
|
|
1922
1935
|
if (!this.WebSocketImpl) {
|
|
@@ -2339,18 +2352,24 @@ API request failed (${response.status}): ${error}`
|
|
|
2339
2352
|
* Get file metadata (without content)
|
|
2340
2353
|
*/
|
|
2341
2354
|
async getFile(path) {
|
|
2342
|
-
return this.request(`/files/${
|
|
2355
|
+
return this.request(`/files/${this.encodeFilePath(path)}`);
|
|
2356
|
+
}
|
|
2357
|
+
/**
|
|
2358
|
+
* Encode a file path for use in URLs
|
|
2359
|
+
* Strips leading slash and encodes each segment separately to preserve path structure
|
|
2360
|
+
*/
|
|
2361
|
+
encodeFilePath(path) {
|
|
2362
|
+
const pathWithoutLeadingSlash = path.startsWith("/") ? path.slice(1) : path;
|
|
2363
|
+
const segments = pathWithoutLeadingSlash.split("/");
|
|
2364
|
+
return segments.map((s) => encodeURIComponent(s)).join("/");
|
|
2343
2365
|
}
|
|
2344
2366
|
/**
|
|
2345
2367
|
* Read file content
|
|
2346
2368
|
*/
|
|
2347
2369
|
async readFile(path) {
|
|
2348
2370
|
const params = new URLSearchParams({ content: "true" });
|
|
2349
|
-
const pathWithoutLeadingSlash = path.startsWith("/") ? path.slice(1) : path;
|
|
2350
|
-
const segments = pathWithoutLeadingSlash.split("/");
|
|
2351
|
-
const encodedPath = segments.map((s) => encodeURIComponent(s)).join("/");
|
|
2352
2371
|
const response = await this.request(
|
|
2353
|
-
`/files/${
|
|
2372
|
+
`/files/${this.encodeFilePath(path)}?${params}`
|
|
2354
2373
|
);
|
|
2355
2374
|
return response.data.content || "";
|
|
2356
2375
|
}
|
|
@@ -2367,7 +2386,7 @@ API request failed (${response.status}): ${error}`
|
|
|
2367
2386
|
* Delete a file or directory
|
|
2368
2387
|
*/
|
|
2369
2388
|
async deleteFile(path) {
|
|
2370
|
-
return this.request(`/files/${
|
|
2389
|
+
return this.request(`/files/${this.encodeFilePath(path)}`, {
|
|
2371
2390
|
method: "DELETE"
|
|
2372
2391
|
});
|
|
2373
2392
|
}
|
|
@@ -2386,7 +2405,7 @@ API request failed (${response.status}): ${error}`
|
|
|
2386
2405
|
headers["Authorization"] = `Bearer ${this._token}`;
|
|
2387
2406
|
}
|
|
2388
2407
|
const response = await fetch(
|
|
2389
|
-
`${this.config.sandboxUrl}/files/${
|
|
2408
|
+
`${this.config.sandboxUrl}/files/${this.encodeFilePath(path)}`,
|
|
2390
2409
|
{
|
|
2391
2410
|
method: "HEAD",
|
|
2392
2411
|
headers,
|
|
@@ -2908,6 +2927,8 @@ API request failed (${response.status}): ${error}`
|
|
|
2908
2927
|
* @param options.background - Run in background (server uses goroutines)
|
|
2909
2928
|
* @param options.cwd - Working directory (server uses cmd.Dir)
|
|
2910
2929
|
* @param options.env - Environment variables (server uses cmd.Env)
|
|
2930
|
+
* @param options.onStdout - Callback for streaming stdout data
|
|
2931
|
+
* @param options.onStderr - Callback for streaming stderr data
|
|
2911
2932
|
* @returns Command execution result
|
|
2912
2933
|
*
|
|
2913
2934
|
* @example
|
|
@@ -2923,10 +2944,77 @@ API request failed (${response.status}): ${error}`
|
|
|
2923
2944
|
* background: true,
|
|
2924
2945
|
* env: { PORT: '3000' }
|
|
2925
2946
|
* })
|
|
2947
|
+
*
|
|
2948
|
+
* // With streaming output
|
|
2949
|
+
* await sandbox.runCommand('npm install', {
|
|
2950
|
+
* onStdout: (data) => console.log(data),
|
|
2951
|
+
* onStderr: (data) => console.error(data),
|
|
2952
|
+
* })
|
|
2926
2953
|
* ```
|
|
2927
2954
|
*/
|
|
2928
2955
|
async runCommand(command, options) {
|
|
2929
|
-
|
|
2956
|
+
const hasStreamingCallbacks = options?.onStdout || options?.onStderr;
|
|
2957
|
+
if (!hasStreamingCallbacks) {
|
|
2958
|
+
return this.run.command(command, options);
|
|
2959
|
+
}
|
|
2960
|
+
const ws = await this.ensureWebSocket();
|
|
2961
|
+
const result = await this.runCommandRequest({
|
|
2962
|
+
command,
|
|
2963
|
+
stream: true,
|
|
2964
|
+
cwd: options?.cwd,
|
|
2965
|
+
env: options?.env
|
|
2966
|
+
});
|
|
2967
|
+
const { cmd_id, channel } = result.data;
|
|
2968
|
+
if (!cmd_id || !channel) {
|
|
2969
|
+
throw new Error("Server did not return streaming channel info");
|
|
2970
|
+
}
|
|
2971
|
+
ws.subscribe(channel);
|
|
2972
|
+
let stdout = "";
|
|
2973
|
+
let stderr = "";
|
|
2974
|
+
let exitCode = 0;
|
|
2975
|
+
let resolvePromise = null;
|
|
2976
|
+
const cleanup = () => {
|
|
2977
|
+
ws.off("command:stdout", handleStdout);
|
|
2978
|
+
ws.off("command:stderr", handleStderr);
|
|
2979
|
+
ws.off("command:exit", handleExit);
|
|
2980
|
+
ws.unsubscribe(channel);
|
|
2981
|
+
};
|
|
2982
|
+
const handleStdout = (msg) => {
|
|
2983
|
+
if (msg.channel === channel && msg.data.cmd_id === cmd_id) {
|
|
2984
|
+
stdout += msg.data.output;
|
|
2985
|
+
options?.onStdout?.(msg.data.output);
|
|
2986
|
+
}
|
|
2987
|
+
};
|
|
2988
|
+
const handleStderr = (msg) => {
|
|
2989
|
+
if (msg.channel === channel && msg.data.cmd_id === cmd_id) {
|
|
2990
|
+
stderr += msg.data.output;
|
|
2991
|
+
options?.onStderr?.(msg.data.output);
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
2994
|
+
const handleExit = (msg) => {
|
|
2995
|
+
if (msg.channel === channel && msg.data.cmd_id === cmd_id) {
|
|
2996
|
+
exitCode = msg.data.exit_code;
|
|
2997
|
+
cleanup();
|
|
2998
|
+
if (resolvePromise) {
|
|
2999
|
+
resolvePromise({ stdout, stderr, exitCode, durationMs: 0 });
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
};
|
|
3003
|
+
ws.on("command:stdout", handleStdout);
|
|
3004
|
+
ws.on("command:stderr", handleStderr);
|
|
3005
|
+
ws.on("command:exit", handleExit);
|
|
3006
|
+
ws.startCommand(cmd_id);
|
|
3007
|
+
if (options?.background) {
|
|
3008
|
+
return {
|
|
3009
|
+
stdout: "",
|
|
3010
|
+
stderr: "",
|
|
3011
|
+
exitCode: 0,
|
|
3012
|
+
durationMs: 0
|
|
3013
|
+
};
|
|
3014
|
+
}
|
|
3015
|
+
return new Promise((resolve) => {
|
|
3016
|
+
resolvePromise = resolve;
|
|
3017
|
+
});
|
|
2930
3018
|
}
|
|
2931
3019
|
/**
|
|
2932
3020
|
* Get server information
|
|
@@ -2979,9 +3067,15 @@ API request failed (${response.status}): ${error}`
|
|
|
2979
3067
|
}
|
|
2980
3068
|
/**
|
|
2981
3069
|
* Destroy the sandbox (Sandbox interface method)
|
|
3070
|
+
*
|
|
3071
|
+
* If a destroyHandler was provided (e.g., from gateway), calls it to destroy
|
|
3072
|
+
* the sandbox on the backend. Otherwise, only disconnects the WebSocket.
|
|
2982
3073
|
*/
|
|
2983
3074
|
async destroy() {
|
|
2984
3075
|
await this.disconnect();
|
|
3076
|
+
if (this.config.destroyHandler) {
|
|
3077
|
+
await this.config.destroyHandler();
|
|
3078
|
+
}
|
|
2985
3079
|
}
|
|
2986
3080
|
/**
|
|
2987
3081
|
* Disconnect WebSocket
|
|
@@ -3420,9 +3514,17 @@ function buildConfigExample(provider, authOptions) {
|
|
|
3420
3514
|
return options.join("\n\n");
|
|
3421
3515
|
}
|
|
3422
3516
|
function createConfigFromExplicit(config) {
|
|
3423
|
-
|
|
3517
|
+
const computesdkApiKey = config.computesdkApiKey || config.apiKey;
|
|
3518
|
+
if (!computesdkApiKey) {
|
|
3424
3519
|
throw new Error(
|
|
3425
|
-
`Missing ComputeSDK API key.
|
|
3520
|
+
`Missing ComputeSDK API key. Set 'computesdkApiKey' in your config.
|
|
3521
|
+
|
|
3522
|
+
Example:
|
|
3523
|
+
compute.setConfig({
|
|
3524
|
+
provider: 'e2b',
|
|
3525
|
+
computesdkApiKey: process.env.COMPUTESDK_API_KEY,
|
|
3526
|
+
e2b: { apiKey: process.env.E2B_API_KEY }
|
|
3527
|
+
})
|
|
3426
3528
|
|
|
3427
3529
|
Get your API key at: https://computesdk.com/dashboard`
|
|
3428
3530
|
);
|
|
@@ -3430,7 +3532,7 @@ Get your API key at: https://computesdk.com/dashboard`
|
|
|
3430
3532
|
validateProviderConfig(config);
|
|
3431
3533
|
const providerHeaders = buildProviderHeaders2(config);
|
|
3432
3534
|
return {
|
|
3433
|
-
apiKey:
|
|
3535
|
+
apiKey: computesdkApiKey,
|
|
3434
3536
|
gatewayUrl: config.gatewayUrl || GATEWAY_URL,
|
|
3435
3537
|
provider: config.provider,
|
|
3436
3538
|
providerHeaders
|
|
@@ -3518,6 +3620,34 @@ async function gatewayFetch(url, config, options = {}) {
|
|
|
3518
3620
|
throw error;
|
|
3519
3621
|
}
|
|
3520
3622
|
}
|
|
3623
|
+
async function waitForSandboxStatus(config, endpoint, body, options = {}) {
|
|
3624
|
+
const maxWaitMs = options.maxWaitMs ?? 6e4;
|
|
3625
|
+
const initialDelayMs = 500;
|
|
3626
|
+
const maxDelayMs = 2e3;
|
|
3627
|
+
const backoffFactor = 1.5;
|
|
3628
|
+
const startTime = Date.now();
|
|
3629
|
+
let currentDelay = initialDelayMs;
|
|
3630
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
3631
|
+
const result = await gatewayFetch(endpoint, config, {
|
|
3632
|
+
method: "POST",
|
|
3633
|
+
body: JSON.stringify(body)
|
|
3634
|
+
});
|
|
3635
|
+
if (!result.success || !result.data) {
|
|
3636
|
+
return result;
|
|
3637
|
+
}
|
|
3638
|
+
if (result.data.status !== "creating") {
|
|
3639
|
+
return result;
|
|
3640
|
+
}
|
|
3641
|
+
if (process.env.COMPUTESDK_DEBUG) {
|
|
3642
|
+
console.log(`[Compute] Sandbox still creating, waiting ${currentDelay}ms...`);
|
|
3643
|
+
}
|
|
3644
|
+
await new Promise((resolve) => setTimeout(resolve, currentDelay));
|
|
3645
|
+
currentDelay = Math.min(currentDelay * backoffFactor, maxDelayMs);
|
|
3646
|
+
}
|
|
3647
|
+
throw new Error(
|
|
3648
|
+
`Sandbox is still being created after ${maxWaitMs}ms. This may indicate the sandbox failed to start. Check your provider dashboard.`
|
|
3649
|
+
);
|
|
3650
|
+
}
|
|
3521
3651
|
var ComputeManager = class {
|
|
3522
3652
|
constructor() {
|
|
3523
3653
|
this.config = null;
|
|
@@ -3546,7 +3676,12 @@ var ComputeManager = class {
|
|
|
3546
3676
|
...name && { name },
|
|
3547
3677
|
...namespace && { namespace }
|
|
3548
3678
|
},
|
|
3549
|
-
WebSocket: globalThis.WebSocket
|
|
3679
|
+
WebSocket: globalThis.WebSocket,
|
|
3680
|
+
destroyHandler: async () => {
|
|
3681
|
+
await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
|
|
3682
|
+
method: "DELETE"
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3550
3685
|
});
|
|
3551
3686
|
await waitForComputeReady(sandbox);
|
|
3552
3687
|
return sandbox;
|
|
@@ -3567,7 +3702,12 @@ var ComputeManager = class {
|
|
|
3567
3702
|
provider,
|
|
3568
3703
|
token: token || config.apiKey,
|
|
3569
3704
|
metadata,
|
|
3570
|
-
WebSocket: globalThis.WebSocket
|
|
3705
|
+
WebSocket: globalThis.WebSocket,
|
|
3706
|
+
destroyHandler: async () => {
|
|
3707
|
+
await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
|
|
3708
|
+
method: "DELETE"
|
|
3709
|
+
});
|
|
3710
|
+
}
|
|
3571
3711
|
});
|
|
3572
3712
|
await waitForComputeReady(sandbox);
|
|
3573
3713
|
return sandbox;
|
|
@@ -3595,14 +3735,15 @@ var ComputeManager = class {
|
|
|
3595
3735
|
findOrCreate: async (options) => {
|
|
3596
3736
|
const config = this.getGatewayConfig();
|
|
3597
3737
|
const { name, namespace, ...restOptions } = options;
|
|
3598
|
-
const result = await
|
|
3599
|
-
|
|
3600
|
-
|
|
3738
|
+
const result = await waitForSandboxStatus(
|
|
3739
|
+
config,
|
|
3740
|
+
`${config.gatewayUrl}/v1/sandboxes/find-or-create`,
|
|
3741
|
+
{
|
|
3601
3742
|
namespace: namespace || "default",
|
|
3602
3743
|
name,
|
|
3603
3744
|
...restOptions
|
|
3604
|
-
}
|
|
3605
|
-
|
|
3745
|
+
}
|
|
3746
|
+
);
|
|
3606
3747
|
if (!result.success || !result.data) {
|
|
3607
3748
|
throw new Error(`Gateway returned invalid response`);
|
|
3608
3749
|
}
|
|
@@ -3617,7 +3758,12 @@ var ComputeManager = class {
|
|
|
3617
3758
|
name: result.data.name,
|
|
3618
3759
|
namespace: result.data.namespace
|
|
3619
3760
|
},
|
|
3620
|
-
WebSocket: globalThis.WebSocket
|
|
3761
|
+
WebSocket: globalThis.WebSocket,
|
|
3762
|
+
destroyHandler: async () => {
|
|
3763
|
+
await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
|
|
3764
|
+
method: "DELETE"
|
|
3765
|
+
});
|
|
3766
|
+
}
|
|
3621
3767
|
});
|
|
3622
3768
|
await waitForComputeReady(sandbox);
|
|
3623
3769
|
return sandbox;
|
|
@@ -3627,13 +3773,14 @@ var ComputeManager = class {
|
|
|
3627
3773
|
*/
|
|
3628
3774
|
find: async (options) => {
|
|
3629
3775
|
const config = this.getGatewayConfig();
|
|
3630
|
-
const result = await
|
|
3631
|
-
|
|
3632
|
-
|
|
3776
|
+
const result = await waitForSandboxStatus(
|
|
3777
|
+
config,
|
|
3778
|
+
`${config.gatewayUrl}/v1/sandboxes/find`,
|
|
3779
|
+
{
|
|
3633
3780
|
namespace: options.namespace || "default",
|
|
3634
3781
|
name: options.name
|
|
3635
|
-
}
|
|
3636
|
-
|
|
3782
|
+
}
|
|
3783
|
+
);
|
|
3637
3784
|
if (!result.success || !result.data) {
|
|
3638
3785
|
return null;
|
|
3639
3786
|
}
|
|
@@ -3648,7 +3795,12 @@ var ComputeManager = class {
|
|
|
3648
3795
|
name,
|
|
3649
3796
|
namespace
|
|
3650
3797
|
},
|
|
3651
|
-
WebSocket: globalThis.WebSocket
|
|
3798
|
+
WebSocket: globalThis.WebSocket,
|
|
3799
|
+
destroyHandler: async () => {
|
|
3800
|
+
await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
|
|
3801
|
+
method: "DELETE"
|
|
3802
|
+
});
|
|
3803
|
+
}
|
|
3652
3804
|
});
|
|
3653
3805
|
await waitForComputeReady(sandbox);
|
|
3654
3806
|
return sandbox;
|
|
@@ -3689,7 +3841,7 @@ var ComputeManager = class {
|
|
|
3689
3841
|
|
|
3690
3842
|
Options:
|
|
3691
3843
|
1. Zero-config: Set COMPUTESDK_API_KEY and provider credentials (e.g., E2B_API_KEY)
|
|
3692
|
-
2. Explicit: Call compute.setConfig({ provider: "e2b",
|
|
3844
|
+
2. Explicit: Call compute.setConfig({ provider: "e2b", computesdkApiKey: "...", e2b: { apiKey: "..." } })
|
|
3693
3845
|
3. Use provider directly: import { e2b } from '@computesdk/e2b'
|
|
3694
3846
|
|
|
3695
3847
|
Docs: https://computesdk.com/docs/quickstart`
|