computesdk 1.10.3 → 1.11.0
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 +15 -4
- package/dist/index.d.mts +80 -20
- package/dist/index.d.ts +80 -20
- package/dist/index.js +139 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +139 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -485,6 +485,17 @@ var WebSocketManager = class {
|
|
|
485
485
|
data: { terminal_id: terminalId, cols, rows }
|
|
486
486
|
});
|
|
487
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* Start a pending streaming command
|
|
490
|
+
* Used in two-phase streaming flow: HTTP request creates pending command,
|
|
491
|
+
* then this signal triggers execution after client has subscribed.
|
|
492
|
+
*/
|
|
493
|
+
startCommand(cmdId) {
|
|
494
|
+
this.sendRaw({
|
|
495
|
+
type: "command:start",
|
|
496
|
+
data: { cmd_id: cmdId }
|
|
497
|
+
});
|
|
498
|
+
}
|
|
488
499
|
on(event, handler) {
|
|
489
500
|
if (!this.eventHandlers.has(event)) {
|
|
490
501
|
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
@@ -958,6 +969,7 @@ var TerminalInstance = class {
|
|
|
958
969
|
throw new Error("Destroy handler not set");
|
|
959
970
|
}
|
|
960
971
|
await this._destroyHandler();
|
|
972
|
+
this._status = "stopped";
|
|
961
973
|
this.cleanup();
|
|
962
974
|
}
|
|
963
975
|
/**
|
|
@@ -1863,7 +1875,8 @@ var Sandbox = class {
|
|
|
1863
1875
|
headers: config.headers || {},
|
|
1864
1876
|
timeout: config.timeout || 3e4,
|
|
1865
1877
|
protocol: config.protocol || "binary",
|
|
1866
|
-
metadata: config.metadata
|
|
1878
|
+
metadata: config.metadata,
|
|
1879
|
+
destroyHandler: config.destroyHandler
|
|
1867
1880
|
};
|
|
1868
1881
|
this.WebSocketImpl = config.WebSocket || globalThis.WebSocket;
|
|
1869
1882
|
if (!this.WebSocketImpl) {
|
|
@@ -2286,18 +2299,24 @@ API request failed (${response.status}): ${error}`
|
|
|
2286
2299
|
* Get file metadata (without content)
|
|
2287
2300
|
*/
|
|
2288
2301
|
async getFile(path) {
|
|
2289
|
-
return this.request(`/files/${
|
|
2302
|
+
return this.request(`/files/${this.encodeFilePath(path)}`);
|
|
2303
|
+
}
|
|
2304
|
+
/**
|
|
2305
|
+
* Encode a file path for use in URLs
|
|
2306
|
+
* Strips leading slash and encodes each segment separately to preserve path structure
|
|
2307
|
+
*/
|
|
2308
|
+
encodeFilePath(path) {
|
|
2309
|
+
const pathWithoutLeadingSlash = path.startsWith("/") ? path.slice(1) : path;
|
|
2310
|
+
const segments = pathWithoutLeadingSlash.split("/");
|
|
2311
|
+
return segments.map((s) => encodeURIComponent(s)).join("/");
|
|
2290
2312
|
}
|
|
2291
2313
|
/**
|
|
2292
2314
|
* Read file content
|
|
2293
2315
|
*/
|
|
2294
2316
|
async readFile(path) {
|
|
2295
2317
|
const params = new URLSearchParams({ content: "true" });
|
|
2296
|
-
const pathWithoutLeadingSlash = path.startsWith("/") ? path.slice(1) : path;
|
|
2297
|
-
const segments = pathWithoutLeadingSlash.split("/");
|
|
2298
|
-
const encodedPath = segments.map((s) => encodeURIComponent(s)).join("/");
|
|
2299
2318
|
const response = await this.request(
|
|
2300
|
-
`/files/${
|
|
2319
|
+
`/files/${this.encodeFilePath(path)}?${params}`
|
|
2301
2320
|
);
|
|
2302
2321
|
return response.data.content || "";
|
|
2303
2322
|
}
|
|
@@ -2314,7 +2333,7 @@ API request failed (${response.status}): ${error}`
|
|
|
2314
2333
|
* Delete a file or directory
|
|
2315
2334
|
*/
|
|
2316
2335
|
async deleteFile(path) {
|
|
2317
|
-
return this.request(`/files/${
|
|
2336
|
+
return this.request(`/files/${this.encodeFilePath(path)}`, {
|
|
2318
2337
|
method: "DELETE"
|
|
2319
2338
|
});
|
|
2320
2339
|
}
|
|
@@ -2333,7 +2352,7 @@ API request failed (${response.status}): ${error}`
|
|
|
2333
2352
|
headers["Authorization"] = `Bearer ${this._token}`;
|
|
2334
2353
|
}
|
|
2335
2354
|
const response = await fetch(
|
|
2336
|
-
`${this.config.sandboxUrl}/files/${
|
|
2355
|
+
`${this.config.sandboxUrl}/files/${this.encodeFilePath(path)}`,
|
|
2337
2356
|
{
|
|
2338
2357
|
method: "HEAD",
|
|
2339
2358
|
headers,
|
|
@@ -2855,6 +2874,8 @@ API request failed (${response.status}): ${error}`
|
|
|
2855
2874
|
* @param options.background - Run in background (server uses goroutines)
|
|
2856
2875
|
* @param options.cwd - Working directory (server uses cmd.Dir)
|
|
2857
2876
|
* @param options.env - Environment variables (server uses cmd.Env)
|
|
2877
|
+
* @param options.onStdout - Callback for streaming stdout data
|
|
2878
|
+
* @param options.onStderr - Callback for streaming stderr data
|
|
2858
2879
|
* @returns Command execution result
|
|
2859
2880
|
*
|
|
2860
2881
|
* @example
|
|
@@ -2870,10 +2891,77 @@ API request failed (${response.status}): ${error}`
|
|
|
2870
2891
|
* background: true,
|
|
2871
2892
|
* env: { PORT: '3000' }
|
|
2872
2893
|
* })
|
|
2894
|
+
*
|
|
2895
|
+
* // With streaming output
|
|
2896
|
+
* await sandbox.runCommand('npm install', {
|
|
2897
|
+
* onStdout: (data) => console.log(data),
|
|
2898
|
+
* onStderr: (data) => console.error(data),
|
|
2899
|
+
* })
|
|
2873
2900
|
* ```
|
|
2874
2901
|
*/
|
|
2875
2902
|
async runCommand(command, options) {
|
|
2876
|
-
|
|
2903
|
+
const hasStreamingCallbacks = options?.onStdout || options?.onStderr;
|
|
2904
|
+
if (!hasStreamingCallbacks) {
|
|
2905
|
+
return this.run.command(command, options);
|
|
2906
|
+
}
|
|
2907
|
+
const ws = await this.ensureWebSocket();
|
|
2908
|
+
const result = await this.runCommandRequest({
|
|
2909
|
+
command,
|
|
2910
|
+
stream: true,
|
|
2911
|
+
cwd: options?.cwd,
|
|
2912
|
+
env: options?.env
|
|
2913
|
+
});
|
|
2914
|
+
const { cmd_id, channel } = result.data;
|
|
2915
|
+
if (!cmd_id || !channel) {
|
|
2916
|
+
throw new Error("Server did not return streaming channel info");
|
|
2917
|
+
}
|
|
2918
|
+
ws.subscribe(channel);
|
|
2919
|
+
let stdout = "";
|
|
2920
|
+
let stderr = "";
|
|
2921
|
+
let exitCode = 0;
|
|
2922
|
+
let resolvePromise = null;
|
|
2923
|
+
const cleanup = () => {
|
|
2924
|
+
ws.off("command:stdout", handleStdout);
|
|
2925
|
+
ws.off("command:stderr", handleStderr);
|
|
2926
|
+
ws.off("command:exit", handleExit);
|
|
2927
|
+
ws.unsubscribe(channel);
|
|
2928
|
+
};
|
|
2929
|
+
const handleStdout = (msg) => {
|
|
2930
|
+
if (msg.channel === channel && msg.data.cmd_id === cmd_id) {
|
|
2931
|
+
stdout += msg.data.output;
|
|
2932
|
+
options?.onStdout?.(msg.data.output);
|
|
2933
|
+
}
|
|
2934
|
+
};
|
|
2935
|
+
const handleStderr = (msg) => {
|
|
2936
|
+
if (msg.channel === channel && msg.data.cmd_id === cmd_id) {
|
|
2937
|
+
stderr += msg.data.output;
|
|
2938
|
+
options?.onStderr?.(msg.data.output);
|
|
2939
|
+
}
|
|
2940
|
+
};
|
|
2941
|
+
const handleExit = (msg) => {
|
|
2942
|
+
if (msg.channel === channel && msg.data.cmd_id === cmd_id) {
|
|
2943
|
+
exitCode = msg.data.exit_code;
|
|
2944
|
+
cleanup();
|
|
2945
|
+
if (resolvePromise) {
|
|
2946
|
+
resolvePromise({ stdout, stderr, exitCode, durationMs: 0 });
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
};
|
|
2950
|
+
ws.on("command:stdout", handleStdout);
|
|
2951
|
+
ws.on("command:stderr", handleStderr);
|
|
2952
|
+
ws.on("command:exit", handleExit);
|
|
2953
|
+
ws.startCommand(cmd_id);
|
|
2954
|
+
if (options?.background) {
|
|
2955
|
+
return {
|
|
2956
|
+
stdout: "",
|
|
2957
|
+
stderr: "",
|
|
2958
|
+
exitCode: 0,
|
|
2959
|
+
durationMs: 0
|
|
2960
|
+
};
|
|
2961
|
+
}
|
|
2962
|
+
return new Promise((resolve) => {
|
|
2963
|
+
resolvePromise = resolve;
|
|
2964
|
+
});
|
|
2877
2965
|
}
|
|
2878
2966
|
/**
|
|
2879
2967
|
* Get server information
|
|
@@ -2926,9 +3014,15 @@ API request failed (${response.status}): ${error}`
|
|
|
2926
3014
|
}
|
|
2927
3015
|
/**
|
|
2928
3016
|
* Destroy the sandbox (Sandbox interface method)
|
|
3017
|
+
*
|
|
3018
|
+
* If a destroyHandler was provided (e.g., from gateway), calls it to destroy
|
|
3019
|
+
* the sandbox on the backend. Otherwise, only disconnects the WebSocket.
|
|
2929
3020
|
*/
|
|
2930
3021
|
async destroy() {
|
|
2931
3022
|
await this.disconnect();
|
|
3023
|
+
if (this.config.destroyHandler) {
|
|
3024
|
+
await this.config.destroyHandler();
|
|
3025
|
+
}
|
|
2932
3026
|
}
|
|
2933
3027
|
/**
|
|
2934
3028
|
* Disconnect WebSocket
|
|
@@ -3367,9 +3461,17 @@ function buildConfigExample(provider, authOptions) {
|
|
|
3367
3461
|
return options.join("\n\n");
|
|
3368
3462
|
}
|
|
3369
3463
|
function createConfigFromExplicit(config) {
|
|
3370
|
-
|
|
3464
|
+
const computesdkApiKey = config.computesdkApiKey || config.apiKey;
|
|
3465
|
+
if (!computesdkApiKey) {
|
|
3371
3466
|
throw new Error(
|
|
3372
|
-
`Missing ComputeSDK API key.
|
|
3467
|
+
`Missing ComputeSDK API key. Set 'computesdkApiKey' in your config.
|
|
3468
|
+
|
|
3469
|
+
Example:
|
|
3470
|
+
compute.setConfig({
|
|
3471
|
+
provider: 'e2b',
|
|
3472
|
+
computesdkApiKey: process.env.COMPUTESDK_API_KEY,
|
|
3473
|
+
e2b: { apiKey: process.env.E2B_API_KEY }
|
|
3474
|
+
})
|
|
3373
3475
|
|
|
3374
3476
|
Get your API key at: https://computesdk.com/dashboard`
|
|
3375
3477
|
);
|
|
@@ -3377,7 +3479,7 @@ Get your API key at: https://computesdk.com/dashboard`
|
|
|
3377
3479
|
validateProviderConfig(config);
|
|
3378
3480
|
const providerHeaders = buildProviderHeaders2(config);
|
|
3379
3481
|
return {
|
|
3380
|
-
apiKey:
|
|
3482
|
+
apiKey: computesdkApiKey,
|
|
3381
3483
|
gatewayUrl: config.gatewayUrl || GATEWAY_URL,
|
|
3382
3484
|
provider: config.provider,
|
|
3383
3485
|
providerHeaders
|
|
@@ -3493,7 +3595,12 @@ var ComputeManager = class {
|
|
|
3493
3595
|
...name && { name },
|
|
3494
3596
|
...namespace && { namespace }
|
|
3495
3597
|
},
|
|
3496
|
-
WebSocket: globalThis.WebSocket
|
|
3598
|
+
WebSocket: globalThis.WebSocket,
|
|
3599
|
+
destroyHandler: async () => {
|
|
3600
|
+
await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
|
|
3601
|
+
method: "DELETE"
|
|
3602
|
+
});
|
|
3603
|
+
}
|
|
3497
3604
|
});
|
|
3498
3605
|
await waitForComputeReady(sandbox);
|
|
3499
3606
|
return sandbox;
|
|
@@ -3514,7 +3621,12 @@ var ComputeManager = class {
|
|
|
3514
3621
|
provider,
|
|
3515
3622
|
token: token || config.apiKey,
|
|
3516
3623
|
metadata,
|
|
3517
|
-
WebSocket: globalThis.WebSocket
|
|
3624
|
+
WebSocket: globalThis.WebSocket,
|
|
3625
|
+
destroyHandler: async () => {
|
|
3626
|
+
await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
|
|
3627
|
+
method: "DELETE"
|
|
3628
|
+
});
|
|
3629
|
+
}
|
|
3518
3630
|
});
|
|
3519
3631
|
await waitForComputeReady(sandbox);
|
|
3520
3632
|
return sandbox;
|
|
@@ -3564,7 +3676,12 @@ var ComputeManager = class {
|
|
|
3564
3676
|
name: result.data.name,
|
|
3565
3677
|
namespace: result.data.namespace
|
|
3566
3678
|
},
|
|
3567
|
-
WebSocket: globalThis.WebSocket
|
|
3679
|
+
WebSocket: globalThis.WebSocket,
|
|
3680
|
+
destroyHandler: async () => {
|
|
3681
|
+
await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
|
|
3682
|
+
method: "DELETE"
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3568
3685
|
});
|
|
3569
3686
|
await waitForComputeReady(sandbox);
|
|
3570
3687
|
return sandbox;
|
|
@@ -3595,7 +3712,12 @@ var ComputeManager = class {
|
|
|
3595
3712
|
name,
|
|
3596
3713
|
namespace
|
|
3597
3714
|
},
|
|
3598
|
-
WebSocket: globalThis.WebSocket
|
|
3715
|
+
WebSocket: globalThis.WebSocket,
|
|
3716
|
+
destroyHandler: async () => {
|
|
3717
|
+
await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
|
|
3718
|
+
method: "DELETE"
|
|
3719
|
+
});
|
|
3720
|
+
}
|
|
3599
3721
|
});
|
|
3600
3722
|
await waitForComputeReady(sandbox);
|
|
3601
3723
|
return sandbox;
|
|
@@ -3636,7 +3758,7 @@ var ComputeManager = class {
|
|
|
3636
3758
|
|
|
3637
3759
|
Options:
|
|
3638
3760
|
1. Zero-config: Set COMPUTESDK_API_KEY and provider credentials (e.g., E2B_API_KEY)
|
|
3639
|
-
2. Explicit: Call compute.setConfig({ provider: "e2b",
|
|
3761
|
+
2. Explicit: Call compute.setConfig({ provider: "e2b", computesdkApiKey: "...", e2b: { apiKey: "..." } })
|
|
3640
3762
|
3. Use provider directly: import { e2b } from '@computesdk/e2b'
|
|
3641
3763
|
|
|
3642
3764
|
Docs: https://computesdk.com/docs/quickstart`
|