Port main updates to next#811
Conversation
🦋 Changeset detectedLatest commit: 75941ed The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
🔍 enableDefaultSession removed from SandboxOptions — public API surface change
The enableDefaultSession field was removed from SandboxOptions in packages/shared/src/types.ts (old lines 783-792). This is a public API change that removes a documented option. The corresponding changelog entry was also removed from packages/sandbox/CHANGELOG.md (old line 56). If any consumers of @cloudflare/sandbox are passing enableDefaultSession: false, their code will get a TypeScript error after upgrading. This appears intentional but is not mentioned in any changeset description in this PR.
Was this helpful? React with 👍 or 👎 to provide feedback.
| `${apiPrefix}/sandbox/:id/pty`, | ||
| traced('pty', async (c) => { | ||
| // 1. Require WebSocket upgrade | ||
| const upgrade = c.req.header('Upgrade'); | ||
| if (!upgrade || upgrade.toLowerCase() !== 'websocket') { | ||
| return errorJson('WebSocket upgrade required', 'invalid_request', 400); | ||
| } | ||
|
|
||
| const sandbox = getSandbox(getSandboxNs(c.env), c.get('containerUUID')); | ||
|
|
||
| // 2. Parse terminal options from query params | ||
| const colsParam = c.req.query('cols'); | ||
| const rowsParam = c.req.query('rows'); | ||
| const shell = c.req.query('shell'); | ||
| const cwd = c.req.query('cwd'); | ||
| const terminalId = | ||
| c.req.header('Terminal-Id') ?? | ||
| c.req.query('terminalId') ?? | ||
| c.req.query('terminal'); | ||
|
|
||
| const cols = colsParam ? Number(colsParam) : 80; | ||
| const rows = rowsParam ? Number(rowsParam) : 24; | ||
|
|
||
| if (Number.isNaN(cols) || Number.isNaN(rows)) { | ||
| return errorJson( | ||
| 'cols and rows must be valid numbers', | ||
| 'invalid_request', | ||
| 400 | ||
| ); | ||
| } | ||
| const sandbox = getSandbox(getSandboxNs(c.env), c.get('containerUUID')); | ||
|
|
||
| const terminalOptions: TerminalOptions = {}; | ||
| const connectOptions: TerminalConnectOptions = { cols, rows }; | ||
| if (shell) { | ||
| terminalOptions.shell = shell; | ||
| } | ||
| if (cwd) { | ||
| terminalOptions.cwd = cwd; | ||
| } | ||
| if (!terminalId) { | ||
| return errorJson( | ||
| 'terminalId query parameter or Terminal-Id header required', | ||
| 'invalid_request', | ||
| 400 | ||
| ); | ||
| } | ||
| // 2. Parse PtyOptions from query params | ||
| const colsParam = c.req.query('cols'); | ||
| const rowsParam = c.req.query('rows'); | ||
| const shell = c.req.query('shell'); | ||
| const sessionId = c.req.header('Session-Id') || c.req.query('session'); | ||
|
|
||
| const validatedId = validateSessionId(terminalId); | ||
| if (!validatedId) | ||
| return errorJson('Invalid terminal ID format', 'invalid_request', 400); | ||
| terminalOptions.id = validatedId; | ||
| const cols = colsParam ? Number(colsParam) : 80; | ||
| const rows = rowsParam ? Number(rowsParam) : 24; | ||
|
|
||
| try { | ||
| return await sandbox | ||
| .terminal(terminalOptions) | ||
| .connect(c.req.raw, connectOptions); | ||
| } catch (err) { | ||
| const msg = err instanceof Error ? err.message : String(err); | ||
| return errorJson(`terminal failed: ${msg}`, 'exec_transport_error', 502); | ||
| } | ||
| }); | ||
| if (Number.isNaN(cols) || Number.isNaN(rows)) { | ||
| return errorJson( | ||
| 'cols and rows must be valid numbers', | ||
| 'invalid_request', | ||
| 400 | ||
| ); | ||
| } | ||
|
|
||
| annotate(c, 'pty.cols', cols); | ||
| annotate(c, 'pty.rows', rows); | ||
|
|
||
| const opts: PtyOptions = { cols, rows }; | ||
| if (shell) { | ||
| opts.shell = shell; | ||
| annotate(c, 'pty.shell', shell); | ||
| } | ||
|
|
||
| try { | ||
| if (sessionId) { | ||
| const validatedId = validateSessionId(sessionId); | ||
| if (!validatedId) | ||
| return errorJson( | ||
| 'Invalid session ID format', | ||
| 'invalid_request', | ||
| 400 | ||
| ); | ||
| annotate(c, 'session.id', validatedId); | ||
| const sess = await sandbox.getSession(validatedId); | ||
| return await sess.terminal(c.req.raw, opts); | ||
| } | ||
| return await sandbox.terminal(c.req.raw, opts); | ||
| } catch (err) { | ||
| const msg = err instanceof Error ? err.message : String(err); | ||
| return errorJson( | ||
| `terminal failed: ${msg}`, | ||
| 'exec_transport_error', | ||
| 502 | ||
| ); | ||
| } | ||
| }) | ||
| ); |
There was a problem hiding this comment.
🔍 PTY route handler changed from terminal ID to session-based dispatch
The PTY WebSocket handler at packages/sandbox/src/bridge/routes.ts:785-844 was significantly refactored. Previously it required a Terminal-Id header or terminalId/terminal query parameter and called sandbox.terminal(terminalOptions).connect(request, connectOptions). Now it uses an optional Session-Id header or session query parameter and calls either sandbox.terminal(request, opts) or sess.terminal(request, opts). The cwd query parameter is no longer parsed from the request. The BridgeSandbox type was updated to match at lines 62-67, changing terminal() from returning a SandboxTerminal to accepting a Request and returning Promise<Response>. This is a breaking change for any bridge API consumers that relied on the Terminal-Id header, terminalId/terminal query parameters, or the cwd query parameter for PTY connections.
Was this helpful? React with 👍 or 👎 to provide feedback.
commit: |
🐳 Docker Images Published
Usage: FROM cloudflare/sandbox:0.0.0-pr-811-75941ed0Version: 📦 Standalone BinaryFor arbitrary Dockerfiles: COPY --from=cloudflare/sandbox:0.0.0-pr-811-75941ed0 /container-server/sandbox /sandbox
ENTRYPOINT ["/sandbox"]Download via GitHub CLI: gh run download 28815463957 -n sandbox-binaryExtract from Docker: docker run --rm cloudflare/sandbox:0.0.0-pr-811-75941ed0 cat /container-server/sandbox > sandbox && chmod +x sandbox |
Bring over the relevant main changes while preserving next-only execution semantics and excluding deprecated default-session flags.
This pull request brings the relevant
mainbranch changes intonextwhile preserving thenextexecution model.Integrated upstream pull requests:
The release tooling changes from
mainare included so prerelease and stable artifact generation use the same orchestration path. The sandbox container labels API is included with its SDK option, storage behavior, and tests. The bridge warm pool changes are included with the configured instance ceiling, eager refill, bounded parallel scale-up, and custom span tracing. The bridge example dependency update and backup/restore benchmark are also included.The stable version package commits from #783, #785, and #806 were not applied directly because
nextshould keep its prerelease versioning flow. #778 was also not carried forward because theenableDefaultSessionoption is deprecated and must not exist onnext. The resulting branch has noenableDefaultSessionreferences.Some
mainchanges were not clean cherry-picks becausenexthas moved to the unified process API. The bridge/execstreaming route from #786 was adapted from the oldstream: truecallback shape to read from theSandboxProcessstdoutandstderrstreams. The bridge persist and hydrate routes were also adjusted to use.output({ encoding: 'utf8' })when checking command exit codes.This also includes a small SDK fix found while validating the minimal example:
withSandboxOperationContext()now preserves decoratedsandbox.exec()promises so helper methods like.output(),.text(),.json(), and.kill()survive the wrapper. A regression test covers that behavior.Validation run: