@cloudflare/sandbox 0.7.12 → 0.7.13

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/Dockerfile CHANGED
@@ -199,6 +199,12 @@ ENTRYPOINT ["/container-server/sandbox"]
199
199
  # ============================================================================
200
200
  FROM runtime-base AS opencode
201
201
 
202
+ RUN --mount=type=secret,id=wrangler_ca \
203
+ if [ -f /run/secrets/wrangler_ca ] && [ -s /run/secrets/wrangler_ca ]; then \
204
+ cp /run/secrets/wrangler_ca /usr/local/share/ca-certificates/wrangler-dev-ca.crt && \
205
+ update-ca-certificates; \
206
+ fi
207
+
202
208
  # Install OpenCode CLI via npm (avoids GitHub API rate limits)
203
209
  RUN npm i -g opencode-ai \
204
210
  && opencode --version
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as DirectoryBackup, A as DesktopStopResponse, At as WaitForPortOptions, B as ExecuteResponse, Bt as CreateContextOptions, C as ClickOptions, Ct as ProcessStartResult, D as DesktopStartOptions, Dt as SessionOptions, E as DesktopClient, Et as SandboxOptions, F as ScreenshotRegion, Ft as ExecuteRequest, G as HttpClientOptions, H as BaseApiResponse, Ht as ExecutionResult, I as ScreenshotResponse, It as ExposePortRequest, J as SessionRequest, K as RequestConfig, L as ScrollDirection, Lt as StartProcessRequest, M as ScreenSizeResponse, Mt as isExecResult, N as ScreenshotBytesResponse, Nt as isProcess, O as DesktopStartResponse, Ot as StreamOptions, P as ScreenshotOptions, Pt as isProcessStatus, Q as BucketProvider, R as TypeOptions, Rt as PtyOptions, S as WriteFileRequest, St as ProcessOptions, T as Desktop, Tt as RestoreBackupResult, U as ContainerStub, Ut as RunCodeOptions, V as BackupClient, Vt as Execution, W as ErrorResponse, X as BaseExecOptions, Y as BackupOptions, Z as BucketCredentials, _ as GitClient, _t as ProcessCleanupResult, a as CreateSessionRequest, at as FileMetadata, b as MkdirRequest, bt as ProcessListResult, c as DeleteSessionResponse, ct as GitCheckoutResult, d as ProcessClient, dt as LogEvent, et as ExecEvent, f as PortClient, ft as MountBucketOptions, g as GitCheckoutRequest, gt as Process, h as InterpreterClient, ht as PortListResult, i as CommandsResponse, it as FileChunk, j as KeyInput, jt as WatchOptions, k as DesktopStatusResponse, kt as WaitForLogResult, l as PingResponse, lt as ISandbox, m as ExecutionCallbacks, mt as PortExposeResult, n as getSandbox, nt as ExecResult, o as CreateSessionResponse, ot as FileStreamEvent, p as UnexposePortRequest, pt as PortCloseResult, q as ResponseHandler, r as SandboxClient, rt as ExecutionSession, s as DeleteSessionRequest, st as FileWatchSSEEvent, t as Sandbox, tt as ExecOptions, u as UtilityClient, ut as ListFilesOptions, v as FileClient, vt as ProcessInfoResult, w as CursorPositionResponse, wt as ProcessStatus, x as ReadFileRequest, xt as ProcessLogsResult, y as FileOperationRequest, yt as ProcessKillResult, z as CommandClient, zt as CodeContext } from "./sandbox-Buy5jfCP.js";
1
+ import { $ as DirectoryBackup, A as DesktopStopResponse, At as WaitForPortOptions, B as ExecuteResponse, Bt as CreateContextOptions, C as ClickOptions, Ct as ProcessStartResult, D as DesktopStartOptions, Dt as SessionOptions, E as DesktopClient, Et as SandboxOptions, F as ScreenshotRegion, Ft as ExecuteRequest, G as HttpClientOptions, H as BaseApiResponse, Ht as ExecutionResult, I as ScreenshotResponse, It as ExposePortRequest, J as SessionRequest, K as RequestConfig, L as ScrollDirection, Lt as StartProcessRequest, M as ScreenSizeResponse, Mt as isExecResult, N as ScreenshotBytesResponse, Nt as isProcess, O as DesktopStartResponse, Ot as StreamOptions, P as ScreenshotOptions, Pt as isProcessStatus, Q as BucketProvider, R as TypeOptions, Rt as PtyOptions, S as WriteFileRequest, St as ProcessOptions, T as Desktop, Tt as RestoreBackupResult, U as ContainerStub, Ut as RunCodeOptions, V as BackupClient, Vt as Execution, W as ErrorResponse, X as BaseExecOptions, Y as BackupOptions, Z as BucketCredentials, _ as GitClient, _t as ProcessCleanupResult, a as CreateSessionRequest, at as FileMetadata, b as MkdirRequest, bt as ProcessListResult, c as DeleteSessionResponse, ct as GitCheckoutResult, d as ProcessClient, dt as LogEvent, et as ExecEvent, f as PortClient, ft as MountBucketOptions, g as GitCheckoutRequest, gt as Process, h as InterpreterClient, ht as PortListResult, i as CommandsResponse, it as FileChunk, j as KeyInput, jt as WatchOptions, k as DesktopStatusResponse, kt as WaitForLogResult, l as PingResponse, lt as ISandbox, m as ExecutionCallbacks, mt as PortExposeResult, n as getSandbox, nt as ExecResult, o as CreateSessionResponse, ot as FileStreamEvent, p as UnexposePortRequest, pt as PortCloseResult, q as ResponseHandler, r as SandboxClient, rt as ExecutionSession, s as DeleteSessionRequest, st as FileWatchSSEEvent, t as Sandbox, tt as ExecOptions, u as UtilityClient, ut as ListFilesOptions, v as FileClient, vt as ProcessInfoResult, w as CursorPositionResponse, wt as ProcessStatus, x as ReadFileRequest, xt as ProcessLogsResult, y as FileOperationRequest, yt as ProcessKillResult, z as CommandClient, zt as CodeContext } from "./sandbox-B2OybvGn.js";
2
2
  import { a as DesktopCoordinateErrorContext, d as ErrorResponse$1, f as OperationType, i as BackupRestoreContext, l as ProcessExitedBeforeReadyContext, n as BackupExpiredContext, o as DesktopErrorContext, p as ErrorCode, r as BackupNotFoundContext, s as InvalidBackupConfigContext, t as BackupCreateContext, u as ProcessReadyTimeoutContext } from "./contexts-CeQR115r.js";
3
3
 
4
4
  //#region src/errors/classes.d.ts
package/dist/index.js CHANGED
@@ -1123,7 +1123,22 @@ var WebSocketTransport = class extends BaseTransport {
1123
1123
  if (!firstMessageReceived) {
1124
1124
  firstMessageReceived = true;
1125
1125
  const pending = this.pendingRequests.get(id);
1126
- if (pending) pending.streamController = streamController;
1126
+ if (pending) {
1127
+ pending.streamController = streamController;
1128
+ if (pending.bufferedChunks) {
1129
+ try {
1130
+ for (const buffered of pending.bufferedChunks) streamController.enqueue(buffered);
1131
+ } catch (error) {
1132
+ this.logger.debug("Failed to flush buffered chunks, cleaning up", {
1133
+ id,
1134
+ error: error instanceof Error ? error.message : String(error)
1135
+ });
1136
+ if (pending.timeoutId) clearTimeout(pending.timeoutId);
1137
+ this.pendingRequests.delete(id);
1138
+ }
1139
+ pending.bufferedChunks = void 0;
1140
+ }
1141
+ }
1127
1142
  resolveStream(stream);
1128
1143
  }
1129
1144
  }
@@ -1194,7 +1209,12 @@ var WebSocketTransport = class extends BaseTransport {
1194
1209
  pending.onFirstChunk = void 0;
1195
1210
  }
1196
1211
  if (!pending.streamController) {
1197
- this.logger.warn("Stream chunk received but controller not ready", { id: chunk.id });
1212
+ if (!pending.bufferedChunks) pending.bufferedChunks = [];
1213
+ const encoder$1 = new TextEncoder();
1214
+ let sseData$1;
1215
+ if (chunk.event) sseData$1 = `event: ${chunk.event}\ndata: ${chunk.data}\n\n`;
1216
+ else sseData$1 = `data: ${chunk.data}\n\n`;
1217
+ pending.bufferedChunks.push(encoder$1.encode(sseData$1));
1198
1218
  return;
1199
1219
  }
1200
1220
  const encoder = new TextEncoder();
@@ -3328,7 +3348,7 @@ function buildS3fsSource(bucket, prefix) {
3328
3348
  * This file is auto-updated by .github/changeset-version.ts during releases
3329
3349
  * DO NOT EDIT MANUALLY - Changes will be overwritten on the next version bump
3330
3350
  */
3331
- const SDK_VERSION = "0.7.12";
3351
+ const SDK_VERSION = "0.7.13";
3332
3352
 
3333
3353
  //#endregion
3334
3354
  //#region src/sandbox.ts
@@ -3848,22 +3868,84 @@ var Sandbox = class Sandbox extends Container {
3848
3868
  }
3849
3869
  });
3850
3870
  } catch (e) {
3851
- if (this.isNoInstanceError(e)) return new Response("Container is currently provisioning. This can take several minutes on first deployment. Please retry in a moment.", {
3852
- status: 503,
3853
- headers: { "Retry-After": "10" }
3854
- });
3871
+ if (this.isNoInstanceError(e)) {
3872
+ const errorBody$1 = {
3873
+ code: ErrorCode.INTERNAL_ERROR,
3874
+ message: "Container is currently provisioning. This can take several minutes on first deployment.",
3875
+ context: { phase: "provisioning" },
3876
+ httpStatus: 503,
3877
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3878
+ suggestion: "This is expected during first deployment. The SDK will retry automatically."
3879
+ };
3880
+ return new Response(JSON.stringify(errorBody$1), {
3881
+ status: 503,
3882
+ headers: {
3883
+ "Content-Type": "application/json",
3884
+ "Retry-After": "10"
3885
+ }
3886
+ });
3887
+ }
3888
+ if (this.isPermanentStartupError(e)) {
3889
+ this.logger.error("Permanent container startup error, returning 500", e instanceof Error ? e : new Error(String(e)));
3890
+ const errorBody$1 = {
3891
+ code: ErrorCode.INTERNAL_ERROR,
3892
+ message: "Container failed to start due to a permanent error. Check your container configuration.",
3893
+ context: {
3894
+ phase: "startup",
3895
+ error: e instanceof Error ? e.message : String(e)
3896
+ },
3897
+ httpStatus: 500,
3898
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3899
+ suggestion: "This error will not resolve with retries. Check container logs, image name, and resource limits."
3900
+ };
3901
+ return new Response(JSON.stringify(errorBody$1), {
3902
+ status: 500,
3903
+ headers: { "Content-Type": "application/json" }
3904
+ });
3905
+ }
3855
3906
  if (this.isTransientStartupError(e)) {
3856
3907
  if (staleStateDetected) {
3857
3908
  this.logger.warn("Container startup failed after stale state detection, aborting DO for recovery", { error: e instanceof Error ? e.message : String(e) });
3858
3909
  this.ctx.abort();
3859
3910
  } else this.logger.debug("Transient container startup error, returning 503", { error: e instanceof Error ? e.message : String(e) });
3860
- return new Response("Container is starting. Please retry in a moment.", {
3911
+ const errorBody$1 = {
3912
+ code: ErrorCode.INTERNAL_ERROR,
3913
+ message: "Container is starting. Please retry in a moment.",
3914
+ context: {
3915
+ phase: "startup",
3916
+ error: e instanceof Error ? e.message : String(e)
3917
+ },
3918
+ httpStatus: 503,
3919
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3920
+ suggestion: "The container is booting. The SDK will retry automatically."
3921
+ };
3922
+ return new Response(JSON.stringify(errorBody$1), {
3861
3923
  status: 503,
3862
- headers: { "Retry-After": "3" }
3924
+ headers: {
3925
+ "Content-Type": "application/json",
3926
+ "Retry-After": "3"
3927
+ }
3863
3928
  });
3864
3929
  }
3865
- this.logger.error("Container startup failed with permanent error", e instanceof Error ? e : new Error(String(e)));
3866
- return new Response(`Failed to start container: ${e instanceof Error ? e.message : String(e)}`, { status: 500 });
3930
+ this.logger.warn("Unrecognized container startup error, returning 503 for retry", { error: e instanceof Error ? e.message : String(e) });
3931
+ const errorBody = {
3932
+ code: ErrorCode.INTERNAL_ERROR,
3933
+ message: "Container is starting. Please retry in a moment.",
3934
+ context: {
3935
+ phase: "startup",
3936
+ error: e instanceof Error ? e.message : String(e)
3937
+ },
3938
+ httpStatus: 503,
3939
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3940
+ suggestion: "The SDK will retry automatically. If this persists, the container may need redeployment."
3941
+ };
3942
+ return new Response(JSON.stringify(errorBody), {
3943
+ status: 503,
3944
+ headers: {
3945
+ "Content-Type": "application/json",
3946
+ "Retry-After": "5"
3947
+ }
3948
+ });
3867
3949
  }
3868
3950
  }
3869
3951
  return await super.containerFetch(requestOrUrl, portOrInit, portParam);
@@ -3909,6 +3991,30 @@ var Sandbox = class Sandbox extends Container {
3909
3991
  ].some((pattern) => msg.includes(pattern));
3910
3992
  }
3911
3993
  /**
3994
+ * Helper: Check if error is a permanent startup failure that will never recover
3995
+ *
3996
+ * These errors indicate resource exhaustion, misconfiguration, or missing images.
3997
+ * Retrying will never succeed, so the SDK should fail fast with HTTP 500.
3998
+ *
3999
+ * Error sources (traced from platform internals):
4000
+ * - Container runtime: OOM, PID limit
4001
+ * - Scheduling/provisioning: no matching app, no namespace configured
4002
+ * - workerd container-client.c++: no such image
4003
+ * - @cloudflare/containers: did not call start
4004
+ */
4005
+ isPermanentStartupError(error) {
4006
+ if (!(error instanceof Error)) return false;
4007
+ const msg = error.message.toLowerCase();
4008
+ return [
4009
+ "ran out of memory",
4010
+ "too many subprocesses",
4011
+ "no application that matches",
4012
+ "no container application assigned",
4013
+ "no such image",
4014
+ "did not call start"
4015
+ ].some((pattern) => msg.includes(pattern));
4016
+ }
4017
+ /**
3912
4018
  * Helper: Parse containerFetch arguments (supports multiple signatures)
3913
4019
  */
3914
4020
  parseContainerFetchArgs(requestOrUrl, portOrInit, portParam) {
@@ -4168,9 +4274,6 @@ var Sandbox = class Sandbox extends Container {
4168
4274
  }
4169
4275
  try {
4170
4276
  const streamProcessor = async () => {
4171
- const DEBOUNCE_MS = 50;
4172
- let lastCheckTime = 0;
4173
- let pendingCheck = false;
4174
4277
  const checkPattern = () => {
4175
4278
  const stdoutResult = this.matchPattern(collectedStdout, pattern);
4176
4279
  if (stdoutResult) return stdoutResult;
@@ -4183,27 +4286,17 @@ var Sandbox = class Sandbox extends Container {
4183
4286
  const data = event.data || "";
4184
4287
  if (event.type === "stdout") collectedStdout += data;
4185
4288
  else collectedStderr += data;
4186
- pendingCheck = true;
4187
- const now = Date.now();
4188
- if (now - lastCheckTime >= DEBOUNCE_MS) {
4189
- lastCheckTime = now;
4190
- pendingCheck = false;
4191
- const result = checkPattern();
4192
- if (result) return result;
4193
- }
4289
+ const result = checkPattern();
4290
+ if (result) return result;
4194
4291
  }
4195
4292
  if (event.type === "exit") {
4196
- if (pendingCheck) {
4197
- const result = checkPattern();
4198
- if (result) return result;
4199
- }
4293
+ const result = checkPattern();
4294
+ if (result) return result;
4200
4295
  throw this.createExitedBeforeReadyError(processId, command, conditionStr, event.exitCode ?? 1);
4201
4296
  }
4202
4297
  }
4203
- if (pendingCheck) {
4204
- const result = checkPattern();
4205
- if (result) return result;
4206
- }
4298
+ const finalResult = checkPattern();
4299
+ if (finalResult) return finalResult;
4207
4300
  throw this.createExitedBeforeReadyError(processId, command, conditionStr, 0);
4208
4301
  };
4209
4302
  if (timeoutPromise) return await Promise.race([streamProcessor(), timeoutPromise]);