@cloudflare/sandbox 0.6.5 → 0.6.6
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/index.d.ts +1 -1
- package/dist/index.js +79 -45
- package/dist/index.js.map +1 -1
- package/dist/openai/index.d.ts +1 -1
- package/dist/opencode/index.d.ts +1 -1
- package/dist/{sandbox-C9WRqWBO.d.ts → sandbox-09Ce7yli.d.ts} +44 -7
- package/dist/sandbox-09Ce7yli.d.ts.map +1 -0
- package/package.json +3 -2
- package/dist/sandbox-C9WRqWBO.d.ts.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as ProcessInfoResult, A as RequestConfig, B as FileChunk, C as WriteFileRequest, D as ContainerStub, E as BaseApiResponse, F as BucketProvider, G as ListFilesOptions, H as FileStreamEvent, I as ExecEvent, J as PortCloseResult, K as LogEvent, L as ExecOptions, M as SessionRequest, N as BaseExecOptions, O as ErrorResponse, P as BucketCredentials, Q as ProcessCleanupResult, R as ExecResult, S as ReadFileRequest, T as ExecuteResponse, U as GitCheckoutResult, V as FileMetadata, W as ISandbox, X as PortListResult, Y as PortExposeResult, Z as Process, _ as GitCheckoutRequest, _t as ExecutionResult, a as CreateSessionRequest, at as ProcessStatus, b as FileOperationRequest, c as DeleteSessionResponse, ct as StreamOptions, d as ProcessClient, dt as isExecResult, et as ProcessKillResult, f as ExposePortRequest, ft as isProcess, g as InterpreterClient, gt as Execution, h as ExecutionCallbacks, ht as CreateContextOptions, i as CommandsResponse, it as ProcessStartResult, j as ResponseHandler, k as HttpClientOptions, l as PingResponse, lt as WaitForLogResult, m as UnexposePortRequest, mt as CodeContext, n as getSandbox, nt as ProcessLogsResult, o as CreateSessionResponse, ot as SandboxOptions, p as PortClient, pt as isProcessStatus, q as MountBucketOptions, r as SandboxClient, rt as ProcessOptions, s as DeleteSessionRequest, st as SessionOptions, t as Sandbox, tt as ProcessListResult, u as UtilityClient, ut as WaitForPortOptions, v as GitClient, vt as RunCodeOptions, w as CommandClient, x as MkdirRequest, y as FileClient, z as ExecutionSession } from "./sandbox-
|
|
1
|
+
import { $ as ProcessInfoResult, A as RequestConfig, B as FileChunk, C as WriteFileRequest, D as ContainerStub, E as BaseApiResponse, F as BucketProvider, G as ListFilesOptions, H as FileStreamEvent, I as ExecEvent, J as PortCloseResult, K as LogEvent, L as ExecOptions, M as SessionRequest, N as BaseExecOptions, O as ErrorResponse, P as BucketCredentials, Q as ProcessCleanupResult, R as ExecResult, S as ReadFileRequest, T as ExecuteResponse, U as GitCheckoutResult, V as FileMetadata, W as ISandbox, X as PortListResult, Y as PortExposeResult, Z as Process, _ as GitCheckoutRequest, _t as ExecutionResult, a as CreateSessionRequest, at as ProcessStatus, b as FileOperationRequest, c as DeleteSessionResponse, ct as StreamOptions, d as ProcessClient, dt as isExecResult, et as ProcessKillResult, f as ExposePortRequest, ft as isProcess, g as InterpreterClient, gt as Execution, h as ExecutionCallbacks, ht as CreateContextOptions, i as CommandsResponse, it as ProcessStartResult, j as ResponseHandler, k as HttpClientOptions, l as PingResponse, lt as WaitForLogResult, m as UnexposePortRequest, mt as CodeContext, n as getSandbox, nt as ProcessLogsResult, o as CreateSessionResponse, ot as SandboxOptions, p as PortClient, pt as isProcessStatus, q as MountBucketOptions, r as SandboxClient, rt as ProcessOptions, s as DeleteSessionRequest, st as SessionOptions, t as Sandbox, tt as ProcessListResult, u as UtilityClient, ut as WaitForPortOptions, v as GitClient, vt as RunCodeOptions, w as CommandClient, x as MkdirRequest, y as FileClient, z as ExecutionSession } from "./sandbox-09Ce7yli.js";
|
|
2
2
|
import { a as OperationType, i as ErrorResponse$1, n as ProcessExitedBeforeReadyContext, o as ErrorCode, r as ProcessReadyTimeoutContext } from "./contexts-CdrlvHWK.js";
|
|
3
3
|
|
|
4
4
|
//#region ../shared/dist/request-types.d.ts
|
package/dist/index.js
CHANGED
|
@@ -586,14 +586,14 @@ var BaseHttpClient = class {
|
|
|
586
586
|
}
|
|
587
587
|
/**
|
|
588
588
|
* Core HTTP request method with automatic retry for container startup delays
|
|
589
|
-
* Retries
|
|
589
|
+
* Retries on 503 (Service Unavailable) which indicates container is starting
|
|
590
590
|
*/
|
|
591
591
|
async doFetch(path, options) {
|
|
592
592
|
const startTime = Date.now();
|
|
593
593
|
let attempt = 0;
|
|
594
594
|
while (true) {
|
|
595
595
|
const response = await this.executeFetch(path, options);
|
|
596
|
-
if (
|
|
596
|
+
if (this.isRetryableContainerError(response)) {
|
|
597
597
|
const elapsed = Date.now() - startTime;
|
|
598
598
|
const remaining = TIMEOUT_MS - elapsed;
|
|
599
599
|
if (remaining > MIN_TIME_FOR_RETRY_MS) {
|
|
@@ -706,51 +706,19 @@ var BaseHttpClient = class {
|
|
|
706
706
|
}
|
|
707
707
|
/**
|
|
708
708
|
* Check if response indicates a retryable container error
|
|
709
|
-
* Uses fail-safe strategy: only retry known transient errors
|
|
710
709
|
*
|
|
711
|
-
*
|
|
712
|
-
*
|
|
713
|
-
*
|
|
710
|
+
* The Sandbox DO returns proper HTTP status codes:
|
|
711
|
+
* - 503 Service Unavailable: Transient errors (container starting, port not ready)
|
|
712
|
+
* - 500 Internal Server Error: Permanent errors (bad config, missing image)
|
|
713
|
+
*
|
|
714
|
+
* We only retry on 503, which indicates the container is starting up.
|
|
715
|
+
* The Retry-After header suggests how long to wait.
|
|
714
716
|
*
|
|
715
717
|
* @param response - HTTP response to check
|
|
716
|
-
* @returns true if error is retryable
|
|
718
|
+
* @returns true if error is retryable (503), false otherwise
|
|
717
719
|
*/
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
try {
|
|
721
|
-
const text = await response.clone().text();
|
|
722
|
-
const textLower = text.toLowerCase();
|
|
723
|
-
if ([
|
|
724
|
-
"no such image",
|
|
725
|
-
"container already exists",
|
|
726
|
-
"malformed containerinspect"
|
|
727
|
-
].some((err) => textLower.includes(err))) {
|
|
728
|
-
this.logger.debug("Detected permanent error, not retrying", { text });
|
|
729
|
-
return false;
|
|
730
|
-
}
|
|
731
|
-
const shouldRetry = [
|
|
732
|
-
"no container instance available",
|
|
733
|
-
"currently provisioning",
|
|
734
|
-
"container port not found",
|
|
735
|
-
"connection refused: container port",
|
|
736
|
-
"the container is not listening",
|
|
737
|
-
"failed to verify port",
|
|
738
|
-
"container did not start",
|
|
739
|
-
"network connection lost",
|
|
740
|
-
"container suddenly disconnected",
|
|
741
|
-
"monitor failed to find container",
|
|
742
|
-
"timed out",
|
|
743
|
-
"timeout"
|
|
744
|
-
].some((err) => textLower.includes(err));
|
|
745
|
-
if (!shouldRetry) this.logger.debug("Unknown error pattern, not retrying", {
|
|
746
|
-
status: response.status,
|
|
747
|
-
text: text.substring(0, 200)
|
|
748
|
-
});
|
|
749
|
-
return shouldRetry;
|
|
750
|
-
} catch (error) {
|
|
751
|
-
this.logger.error("Error checking if response is retryable", error instanceof Error ? error : new Error(String(error)));
|
|
752
|
-
return false;
|
|
753
|
-
}
|
|
720
|
+
isRetryableContainerError(response) {
|
|
721
|
+
return response.status === 503;
|
|
754
722
|
}
|
|
755
723
|
async executeFetch(path, options) {
|
|
756
724
|
const url = this.options.stub ? `http://localhost:${this.options.port}${path}` : `${this.baseUrl}${path}`;
|
|
@@ -2019,7 +1987,7 @@ function resolveS3fsOptions(provider, userOptions) {
|
|
|
2019
1987
|
* This file is auto-updated by .github/changeset-version.ts during releases
|
|
2020
1988
|
* DO NOT EDIT MANUALLY - Changes will be overwritten on the next version bump
|
|
2021
1989
|
*/
|
|
2022
|
-
const SDK_VERSION = "0.6.
|
|
1990
|
+
const SDK_VERSION = "0.6.6";
|
|
2023
1991
|
|
|
2024
1992
|
//#endregion
|
|
2025
1993
|
//#region src/sandbox.ts
|
|
@@ -2379,18 +2347,57 @@ var Sandbox = class extends Container {
|
|
|
2379
2347
|
status: 503,
|
|
2380
2348
|
headers: { "Retry-After": "10" }
|
|
2381
2349
|
});
|
|
2382
|
-
this.
|
|
2350
|
+
if (this.isTransientStartupError(e)) {
|
|
2351
|
+
this.logger.debug("Transient container startup error, returning 503", { error: e instanceof Error ? e.message : String(e) });
|
|
2352
|
+
return new Response("Container is starting. Please retry in a moment.", {
|
|
2353
|
+
status: 503,
|
|
2354
|
+
headers: { "Retry-After": "3" }
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
this.logger.error("Container startup failed with permanent error", e instanceof Error ? e : new Error(String(e)));
|
|
2383
2358
|
return new Response(`Failed to start container: ${e instanceof Error ? e.message : String(e)}`, { status: 500 });
|
|
2384
2359
|
}
|
|
2385
2360
|
return await super.containerFetch(requestOrUrl, portOrInit, portParam);
|
|
2386
2361
|
}
|
|
2387
2362
|
/**
|
|
2388
2363
|
* Helper: Check if error is "no container instance available"
|
|
2364
|
+
* This indicates the container VM is still being provisioned.
|
|
2389
2365
|
*/
|
|
2390
2366
|
isNoInstanceError(error) {
|
|
2391
2367
|
return error instanceof Error && error.message.toLowerCase().includes("no container instance");
|
|
2392
2368
|
}
|
|
2393
2369
|
/**
|
|
2370
|
+
* Helper: Check if error is a transient startup error that should trigger retry
|
|
2371
|
+
*
|
|
2372
|
+
* These errors occur during normal container startup and are recoverable:
|
|
2373
|
+
* - Port not yet mapped (container starting, app not listening yet)
|
|
2374
|
+
* - Connection refused (port mapped but app not ready)
|
|
2375
|
+
* - Timeouts during startup (recoverable with retry)
|
|
2376
|
+
* - Network transients (temporary connectivity issues)
|
|
2377
|
+
*
|
|
2378
|
+
* Errors NOT included (permanent failures):
|
|
2379
|
+
* - "no such image" - missing Docker image
|
|
2380
|
+
* - "container already exists" - name collision
|
|
2381
|
+
* - Configuration errors
|
|
2382
|
+
*/
|
|
2383
|
+
isTransientStartupError(error) {
|
|
2384
|
+
if (!(error instanceof Error)) return false;
|
|
2385
|
+
const msg = error.message.toLowerCase();
|
|
2386
|
+
return [
|
|
2387
|
+
"container port not found",
|
|
2388
|
+
"connection refused: container port",
|
|
2389
|
+
"the container is not listening",
|
|
2390
|
+
"failed to verify port",
|
|
2391
|
+
"container did not start",
|
|
2392
|
+
"network connection lost",
|
|
2393
|
+
"container suddenly disconnected",
|
|
2394
|
+
"monitor failed to find container",
|
|
2395
|
+
"timed out",
|
|
2396
|
+
"timeout",
|
|
2397
|
+
"the operation was aborted"
|
|
2398
|
+
].some((pattern) => msg.includes(pattern));
|
|
2399
|
+
}
|
|
2400
|
+
/**
|
|
2394
2401
|
* Helper: Parse containerFetch arguments (supports multiple signatures)
|
|
2395
2402
|
*/
|
|
2396
2403
|
parseContainerFetchArgs(requestOrUrl, portOrInit, portParam) {
|
|
@@ -2606,6 +2613,9 @@ var Sandbox = class extends Container {
|
|
|
2606
2613
|
},
|
|
2607
2614
|
waitForPort: async (port, options) => {
|
|
2608
2615
|
await this.waitForPortReady(data.id, data.command, port, options);
|
|
2616
|
+
},
|
|
2617
|
+
waitForExit: async (timeout) => {
|
|
2618
|
+
return this.waitForProcessExit(data.id, data.command, timeout);
|
|
2609
2619
|
}
|
|
2610
2620
|
};
|
|
2611
2621
|
}
|
|
@@ -2731,6 +2741,30 @@ var Sandbox = class extends Container {
|
|
|
2731
2741
|
}
|
|
2732
2742
|
}
|
|
2733
2743
|
/**
|
|
2744
|
+
* Wait for a process to exit
|
|
2745
|
+
* Returns the exit code
|
|
2746
|
+
*/
|
|
2747
|
+
async waitForProcessExit(processId, command, timeout) {
|
|
2748
|
+
const stream = await this.streamProcessLogs(processId);
|
|
2749
|
+
let timeoutId;
|
|
2750
|
+
let timeoutPromise;
|
|
2751
|
+
if (timeout !== void 0) timeoutPromise = new Promise((_, reject) => {
|
|
2752
|
+
timeoutId = setTimeout(() => {
|
|
2753
|
+
reject(this.createReadyTimeoutError(processId, command, "process exit", timeout));
|
|
2754
|
+
}, timeout);
|
|
2755
|
+
});
|
|
2756
|
+
try {
|
|
2757
|
+
const streamProcessor = async () => {
|
|
2758
|
+
for await (const event of parseSSEStream(stream)) if (event.type === "exit") return { exitCode: event.exitCode ?? 1 };
|
|
2759
|
+
throw new Error(`Process ${processId} stream ended unexpectedly without exit event`);
|
|
2760
|
+
};
|
|
2761
|
+
if (timeoutPromise) return await Promise.race([streamProcessor(), timeoutPromise]);
|
|
2762
|
+
return await streamProcessor();
|
|
2763
|
+
} finally {
|
|
2764
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
/**
|
|
2734
2768
|
* Match a pattern against text
|
|
2735
2769
|
*/
|
|
2736
2770
|
matchPattern(text, pattern) {
|