@cloudflare/sandbox 0.6.1 → 0.6.3
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/dist/{dist-gVyG2H2h.js → dist-D0sZt0AD.js} +8 -2
- package/dist/{dist-gVyG2H2h.js.map → dist-D0sZt0AD.js.map} +1 -1
- package/dist/index.d.ts +203 -62
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +288 -3
- package/dist/index.js.map +1 -1
- package/dist/openai/index.d.ts +1 -1
- package/dist/openai/index.js +1 -1
- package/dist/{sandbox-HQazw9bn.d.ts → sandbox-bAj-cB2H.d.ts} +130 -3
- package/dist/sandbox-bAj-cB2H.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/sandbox-HQazw9bn.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as shellEscape, c as TraceContext, d as GitLogger, f as getEnvString, i as isTerminalStatus, l as Execution, n as isProcess, o as createLogger, r as isProcessStatus, s as createNoOpLogger, t as isExecResult, u as ResultImpl } from "./dist-D0sZt0AD.js";
|
|
2
2
|
import { Container, getContainer, switchPort } from "@cloudflare/containers";
|
|
3
3
|
|
|
4
4
|
//#region ../shared/dist/errors/codes.js
|
|
@@ -51,6 +51,8 @@ const ErrorCode = {
|
|
|
51
51
|
CONTEXT_NOT_FOUND: "CONTEXT_NOT_FOUND",
|
|
52
52
|
CODE_EXECUTION_ERROR: "CODE_EXECUTION_ERROR",
|
|
53
53
|
PYTHON_NOT_AVAILABLE: "PYTHON_NOT_AVAILABLE",
|
|
54
|
+
PROCESS_READY_TIMEOUT: "PROCESS_READY_TIMEOUT",
|
|
55
|
+
PROCESS_EXITED_BEFORE_READY: "PROCESS_EXITED_BEFORE_READY",
|
|
54
56
|
VALIDATION_FAILED: "VALIDATION_FAILED",
|
|
55
57
|
INVALID_JSON_RESPONSE: "INVALID_JSON_RESPONSE",
|
|
56
58
|
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
|
@@ -96,6 +98,8 @@ const ERROR_STATUS_MAP = {
|
|
|
96
98
|
[ErrorCode.GIT_NETWORK_ERROR]: 502,
|
|
97
99
|
[ErrorCode.PYTHON_NOT_AVAILABLE]: 501,
|
|
98
100
|
[ErrorCode.INTERPRETER_NOT_READY]: 503,
|
|
101
|
+
[ErrorCode.PROCESS_READY_TIMEOUT]: 408,
|
|
102
|
+
[ErrorCode.PROCESS_EXITED_BEFORE_READY]: 500,
|
|
99
103
|
[ErrorCode.NO_SPACE]: 500,
|
|
100
104
|
[ErrorCode.TOO_MANY_FILES]: 500,
|
|
101
105
|
[ErrorCode.TOO_MANY_LINKS]: 500,
|
|
@@ -570,6 +574,48 @@ var ValidationFailedError = class extends SandboxError {
|
|
|
570
574
|
return this.context.validationErrors;
|
|
571
575
|
}
|
|
572
576
|
};
|
|
577
|
+
/**
|
|
578
|
+
* Error thrown when a process does not become ready within the timeout period
|
|
579
|
+
*/
|
|
580
|
+
var ProcessReadyTimeoutError = class extends SandboxError {
|
|
581
|
+
constructor(errorResponse) {
|
|
582
|
+
super(errorResponse);
|
|
583
|
+
this.name = "ProcessReadyTimeoutError";
|
|
584
|
+
}
|
|
585
|
+
get processId() {
|
|
586
|
+
return this.context.processId;
|
|
587
|
+
}
|
|
588
|
+
get command() {
|
|
589
|
+
return this.context.command;
|
|
590
|
+
}
|
|
591
|
+
get condition() {
|
|
592
|
+
return this.context.condition;
|
|
593
|
+
}
|
|
594
|
+
get timeout() {
|
|
595
|
+
return this.context.timeout;
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
/**
|
|
599
|
+
* Error thrown when a process exits before becoming ready
|
|
600
|
+
*/
|
|
601
|
+
var ProcessExitedBeforeReadyError = class extends SandboxError {
|
|
602
|
+
constructor(errorResponse) {
|
|
603
|
+
super(errorResponse);
|
|
604
|
+
this.name = "ProcessExitedBeforeReadyError";
|
|
605
|
+
}
|
|
606
|
+
get processId() {
|
|
607
|
+
return this.context.processId;
|
|
608
|
+
}
|
|
609
|
+
get command() {
|
|
610
|
+
return this.context.command;
|
|
611
|
+
}
|
|
612
|
+
get condition() {
|
|
613
|
+
return this.context.condition;
|
|
614
|
+
}
|
|
615
|
+
get exitCode() {
|
|
616
|
+
return this.context.exitCode;
|
|
617
|
+
}
|
|
618
|
+
};
|
|
573
619
|
|
|
574
620
|
//#endregion
|
|
575
621
|
//#region src/errors/adapter.ts
|
|
@@ -1371,6 +1417,20 @@ var PortClient = class extends BaseHttpClient {
|
|
|
1371
1417
|
throw error;
|
|
1372
1418
|
}
|
|
1373
1419
|
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Check if a port is ready to accept connections
|
|
1422
|
+
* @param request - Port check configuration
|
|
1423
|
+
*/
|
|
1424
|
+
async checkPortReady(request) {
|
|
1425
|
+
try {
|
|
1426
|
+
return await this.post("/api/port-check", request);
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
return {
|
|
1429
|
+
ready: false,
|
|
1430
|
+
error: error instanceof Error ? error.message : "Port check failed"
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1374
1434
|
};
|
|
1375
1435
|
|
|
1376
1436
|
//#endregion
|
|
@@ -2063,7 +2123,7 @@ function resolveS3fsOptions(provider, userOptions) {
|
|
|
2063
2123
|
* This file is auto-updated by .github/changeset-version.ts during releases
|
|
2064
2124
|
* DO NOT EDIT MANUALLY - Changes will be overwritten on the next version bump
|
|
2065
2125
|
*/
|
|
2066
|
-
const SDK_VERSION = "0.6.
|
|
2126
|
+
const SDK_VERSION = "0.6.3";
|
|
2067
2127
|
|
|
2068
2128
|
//#endregion
|
|
2069
2129
|
//#region src/sandbox.ts
|
|
@@ -2645,9 +2705,209 @@ var Sandbox = class extends Container {
|
|
|
2645
2705
|
stdout: logs.stdout,
|
|
2646
2706
|
stderr: logs.stderr
|
|
2647
2707
|
};
|
|
2708
|
+
},
|
|
2709
|
+
waitForLog: async (pattern, timeout) => {
|
|
2710
|
+
return this.waitForLogPattern(data.id, data.command, pattern, timeout);
|
|
2711
|
+
},
|
|
2712
|
+
waitForPort: async (port, options) => {
|
|
2713
|
+
await this.waitForPortReady(data.id, data.command, port, options);
|
|
2648
2714
|
}
|
|
2649
2715
|
};
|
|
2650
2716
|
}
|
|
2717
|
+
/**
|
|
2718
|
+
* Wait for a log pattern to appear in process output
|
|
2719
|
+
*/
|
|
2720
|
+
async waitForLogPattern(processId, command, pattern, timeout) {
|
|
2721
|
+
const startTime = Date.now();
|
|
2722
|
+
const conditionStr = this.conditionToString(pattern);
|
|
2723
|
+
let collectedStdout = "";
|
|
2724
|
+
let collectedStderr = "";
|
|
2725
|
+
try {
|
|
2726
|
+
const existingLogs = await this.getProcessLogs(processId);
|
|
2727
|
+
collectedStdout = existingLogs.stdout;
|
|
2728
|
+
if (collectedStdout && !collectedStdout.endsWith("\n")) collectedStdout += "\n";
|
|
2729
|
+
collectedStderr = existingLogs.stderr;
|
|
2730
|
+
if (collectedStderr && !collectedStderr.endsWith("\n")) collectedStderr += "\n";
|
|
2731
|
+
const stdoutResult = this.matchPattern(existingLogs.stdout, pattern);
|
|
2732
|
+
if (stdoutResult) return stdoutResult;
|
|
2733
|
+
const stderrResult = this.matchPattern(existingLogs.stderr, pattern);
|
|
2734
|
+
if (stderrResult) return stderrResult;
|
|
2735
|
+
} catch (error) {
|
|
2736
|
+
this.logger.debug("Could not get existing logs, will stream", {
|
|
2737
|
+
processId,
|
|
2738
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
2741
|
+
const stream = await this.streamProcessLogs(processId);
|
|
2742
|
+
let timeoutId;
|
|
2743
|
+
let timeoutPromise;
|
|
2744
|
+
if (timeout !== void 0) {
|
|
2745
|
+
const remainingTime = timeout - (Date.now() - startTime);
|
|
2746
|
+
if (remainingTime <= 0) throw this.createReadyTimeoutError(processId, command, conditionStr, timeout);
|
|
2747
|
+
timeoutPromise = new Promise((_, reject) => {
|
|
2748
|
+
timeoutId = setTimeout(() => {
|
|
2749
|
+
reject(this.createReadyTimeoutError(processId, command, conditionStr, timeout));
|
|
2750
|
+
}, remainingTime);
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
try {
|
|
2754
|
+
const streamProcessor = async () => {
|
|
2755
|
+
const DEBOUNCE_MS = 50;
|
|
2756
|
+
let lastCheckTime = 0;
|
|
2757
|
+
let pendingCheck = false;
|
|
2758
|
+
const checkPattern = () => {
|
|
2759
|
+
const stdoutResult = this.matchPattern(collectedStdout, pattern);
|
|
2760
|
+
if (stdoutResult) return stdoutResult;
|
|
2761
|
+
const stderrResult = this.matchPattern(collectedStderr, pattern);
|
|
2762
|
+
if (stderrResult) return stderrResult;
|
|
2763
|
+
return null;
|
|
2764
|
+
};
|
|
2765
|
+
for await (const event of parseSSEStream(stream)) {
|
|
2766
|
+
if (event.type === "stdout" || event.type === "stderr") {
|
|
2767
|
+
const data = event.data || "";
|
|
2768
|
+
if (event.type === "stdout") collectedStdout += data;
|
|
2769
|
+
else collectedStderr += data;
|
|
2770
|
+
pendingCheck = true;
|
|
2771
|
+
const now = Date.now();
|
|
2772
|
+
if (now - lastCheckTime >= DEBOUNCE_MS) {
|
|
2773
|
+
lastCheckTime = now;
|
|
2774
|
+
pendingCheck = false;
|
|
2775
|
+
const result = checkPattern();
|
|
2776
|
+
if (result) return result;
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
if (event.type === "exit") {
|
|
2780
|
+
if (pendingCheck) {
|
|
2781
|
+
const result = checkPattern();
|
|
2782
|
+
if (result) return result;
|
|
2783
|
+
}
|
|
2784
|
+
throw this.createExitedBeforeReadyError(processId, command, conditionStr, event.exitCode ?? 1);
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
if (pendingCheck) {
|
|
2788
|
+
const result = checkPattern();
|
|
2789
|
+
if (result) return result;
|
|
2790
|
+
}
|
|
2791
|
+
throw this.createExitedBeforeReadyError(processId, command, conditionStr, 0);
|
|
2792
|
+
};
|
|
2793
|
+
if (timeoutPromise) return await Promise.race([streamProcessor(), timeoutPromise]);
|
|
2794
|
+
return await streamProcessor();
|
|
2795
|
+
} finally {
|
|
2796
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
/**
|
|
2800
|
+
* Wait for a port to become available (for process readiness checking)
|
|
2801
|
+
*/
|
|
2802
|
+
async waitForPortReady(processId, command, port, options) {
|
|
2803
|
+
const { mode = "http", path = "/", status = {
|
|
2804
|
+
min: 200,
|
|
2805
|
+
max: 399
|
|
2806
|
+
}, timeout, interval = 500 } = options ?? {};
|
|
2807
|
+
const startTime = Date.now();
|
|
2808
|
+
const conditionStr = mode === "http" ? `port ${port} (HTTP ${path})` : `port ${port} (TCP)`;
|
|
2809
|
+
const targetInterval = interval;
|
|
2810
|
+
let checkCount = 0;
|
|
2811
|
+
const checkRequest = {
|
|
2812
|
+
port,
|
|
2813
|
+
mode,
|
|
2814
|
+
path,
|
|
2815
|
+
statusMin: typeof status === "number" ? status : status.min,
|
|
2816
|
+
statusMax: typeof status === "number" ? status : status.max
|
|
2817
|
+
};
|
|
2818
|
+
while (true) {
|
|
2819
|
+
if (timeout !== void 0) {
|
|
2820
|
+
if (timeout - (Date.now() - startTime) <= 0) throw this.createReadyTimeoutError(processId, command, conditionStr, timeout);
|
|
2821
|
+
}
|
|
2822
|
+
const iterationStart = Date.now();
|
|
2823
|
+
if (checkCount % 3 === 0) {
|
|
2824
|
+
const processInfo = await this.getProcess(processId);
|
|
2825
|
+
if (!processInfo || isTerminalStatus(processInfo.status)) throw this.createExitedBeforeReadyError(processId, command, conditionStr, processInfo?.exitCode ?? 1);
|
|
2826
|
+
}
|
|
2827
|
+
try {
|
|
2828
|
+
if ((await this.client.ports.checkPortReady(checkRequest)).ready) return;
|
|
2829
|
+
} catch {}
|
|
2830
|
+
checkCount++;
|
|
2831
|
+
const iterationDuration = Date.now() - iterationStart;
|
|
2832
|
+
const sleepTime = Math.max(0, targetInterval - iterationDuration);
|
|
2833
|
+
if (sleepTime > 0) {
|
|
2834
|
+
if (timeout === void 0 || Date.now() - startTime + sleepTime < timeout) await new Promise((resolve) => setTimeout(resolve, sleepTime));
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
/**
|
|
2839
|
+
* Match a pattern against text
|
|
2840
|
+
*/
|
|
2841
|
+
matchPattern(text, pattern) {
|
|
2842
|
+
if (typeof pattern === "string") {
|
|
2843
|
+
if (text.includes(pattern)) {
|
|
2844
|
+
const lines = text.split("\n");
|
|
2845
|
+
for (const line of lines) if (line.includes(pattern)) return { line };
|
|
2846
|
+
return { line: pattern };
|
|
2847
|
+
}
|
|
2848
|
+
} else {
|
|
2849
|
+
const safePattern = new RegExp(pattern.source, pattern.flags.replace("g", ""));
|
|
2850
|
+
const match = text.match(safePattern);
|
|
2851
|
+
if (match) {
|
|
2852
|
+
const lines = text.split("\n");
|
|
2853
|
+
for (const line of lines) {
|
|
2854
|
+
const lineMatch = line.match(safePattern);
|
|
2855
|
+
if (lineMatch) return {
|
|
2856
|
+
line,
|
|
2857
|
+
match: lineMatch
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
return {
|
|
2861
|
+
line: match[0],
|
|
2862
|
+
match
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
return null;
|
|
2867
|
+
}
|
|
2868
|
+
/**
|
|
2869
|
+
* Convert a log pattern to a human-readable string
|
|
2870
|
+
*/
|
|
2871
|
+
conditionToString(pattern) {
|
|
2872
|
+
if (typeof pattern === "string") return `"${pattern}"`;
|
|
2873
|
+
return pattern.toString();
|
|
2874
|
+
}
|
|
2875
|
+
/**
|
|
2876
|
+
* Create a ProcessReadyTimeoutError
|
|
2877
|
+
*/
|
|
2878
|
+
createReadyTimeoutError(processId, command, condition, timeout) {
|
|
2879
|
+
return new ProcessReadyTimeoutError({
|
|
2880
|
+
code: ErrorCode.PROCESS_READY_TIMEOUT,
|
|
2881
|
+
message: `Process did not become ready within ${timeout}ms. Waiting for: ${condition}`,
|
|
2882
|
+
context: {
|
|
2883
|
+
processId,
|
|
2884
|
+
command,
|
|
2885
|
+
condition,
|
|
2886
|
+
timeout
|
|
2887
|
+
},
|
|
2888
|
+
httpStatus: 408,
|
|
2889
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2890
|
+
suggestion: `Check if your process outputs ${condition}. You can increase the timeout parameter.`
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
/**
|
|
2894
|
+
* Create a ProcessExitedBeforeReadyError
|
|
2895
|
+
*/
|
|
2896
|
+
createExitedBeforeReadyError(processId, command, condition, exitCode) {
|
|
2897
|
+
return new ProcessExitedBeforeReadyError({
|
|
2898
|
+
code: ErrorCode.PROCESS_EXITED_BEFORE_READY,
|
|
2899
|
+
message: `Process exited with code ${exitCode} before becoming ready. Waiting for: ${condition}`,
|
|
2900
|
+
context: {
|
|
2901
|
+
processId,
|
|
2902
|
+
command,
|
|
2903
|
+
condition,
|
|
2904
|
+
exitCode
|
|
2905
|
+
},
|
|
2906
|
+
httpStatus: 500,
|
|
2907
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2908
|
+
suggestion: "Check process logs with getLogs() for error messages"
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2651
2911
|
async startProcess(command, options, sessionId) {
|
|
2652
2912
|
try {
|
|
2653
2913
|
const session = sessionId ?? await this.ensureDefaultSession();
|
|
@@ -2670,12 +2930,37 @@ var Sandbox = class extends Container {
|
|
|
2670
2930
|
exitCode: void 0
|
|
2671
2931
|
}, session);
|
|
2672
2932
|
if (options?.onStart) options.onStart(processObj);
|
|
2933
|
+
if (options?.onOutput || options?.onExit) this.startProcessCallbackStream(response.processId, options).catch(() => {});
|
|
2673
2934
|
return processObj;
|
|
2674
2935
|
} catch (error) {
|
|
2675
2936
|
if (options?.onError && error instanceof Error) options.onError(error);
|
|
2676
2937
|
throw error;
|
|
2677
2938
|
}
|
|
2678
2939
|
}
|
|
2940
|
+
/**
|
|
2941
|
+
* Start background streaming for process callbacks
|
|
2942
|
+
* Opens SSE stream to container and routes events to callbacks
|
|
2943
|
+
*/
|
|
2944
|
+
async startProcessCallbackStream(processId, options) {
|
|
2945
|
+
try {
|
|
2946
|
+
const stream = await this.client.processes.streamProcessLogs(processId);
|
|
2947
|
+
for await (const event of parseSSEStream(stream)) switch (event.type) {
|
|
2948
|
+
case "stdout":
|
|
2949
|
+
if (event.data && options.onOutput) options.onOutput("stdout", event.data);
|
|
2950
|
+
break;
|
|
2951
|
+
case "stderr":
|
|
2952
|
+
if (event.data && options.onOutput) options.onOutput("stderr", event.data);
|
|
2953
|
+
break;
|
|
2954
|
+
case "exit":
|
|
2955
|
+
case "complete":
|
|
2956
|
+
if (options.onExit) options.onExit(event.exitCode ?? null);
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2959
|
+
} catch (error) {
|
|
2960
|
+
if (options.onError && error instanceof Error) options.onError(error);
|
|
2961
|
+
this.logger.error("Background process streaming failed", error instanceof Error ? error : new Error(String(error)), { processId });
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2679
2964
|
async listProcesses(sessionId) {
|
|
2680
2965
|
const session = sessionId ?? await this.ensureDefaultSession();
|
|
2681
2966
|
return (await this.client.processes.listProcesses()).processes.map((processData) => this.createProcessFromDTO({
|
|
@@ -3141,5 +3426,5 @@ async function collectFile(stream) {
|
|
|
3141
3426
|
}
|
|
3142
3427
|
|
|
3143
3428
|
//#endregion
|
|
3144
|
-
export { BucketMountError, CodeInterpreter, CommandClient, FileClient, GitClient, InvalidMountConfigError, MissingCredentialsError, PortClient, ProcessClient, S3FSMountError, Sandbox, SandboxClient, UtilityClient, asyncIterableToSSEStream, collectFile, getSandbox, isExecResult, isProcess, isProcessStatus, parseSSEStream, proxyToSandbox, responseToAsyncIterable, streamFile };
|
|
3429
|
+
export { BucketMountError, CodeInterpreter, CommandClient, FileClient, GitClient, InvalidMountConfigError, MissingCredentialsError, PortClient, ProcessClient, ProcessExitedBeforeReadyError, ProcessReadyTimeoutError, S3FSMountError, Sandbox, SandboxClient, UtilityClient, asyncIterableToSSEStream, collectFile, getSandbox, isExecResult, isProcess, isProcessStatus, parseSSEStream, proxyToSandbox, responseToAsyncIterable, streamFile };
|
|
3145
3430
|
//# sourceMappingURL=index.js.map
|