Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .fallowrc.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"packages/producer/src/perf-gate.ts",
"packages/producer/src/runtime-conformance.ts",
"packages/producer/src/benchmark.ts",
"packages/producer/src/cloudflareSandboxBench.ts",
"packages/producer/src/services/distributed/renderChunkCli.ts",
"packages/producer/scripts/generate-font-data.ts",
"packages/producer/we-render.mjs",
"packages/producer/scripts/validate-fast-video.ts",
Expand Down Expand Up @@ -62,6 +64,8 @@
"packages/engine/spikes/**",
"packages/producer/de-*.mjs",
"packages/producer/tests/**",
// Nested Cloudflare worker + generated sandbox image artifacts.
"packages/producer/cloudflare-sandbox/**",
"packages/player/tests/**",
"packages/engine/tests/**",
"skills/**/test-corpus/**",
Expand Down Expand Up @@ -790,6 +794,9 @@
// stage's runCompileStage under the cyclo/cognitive thresholds.
"packages/producer/src/server.ts",
"packages/producer/src/services/distributed/plan.ts",
// Standalone sandbox bench harness: CLI parse + retry loops trip CRAP
// without coverage mapping. Not library code.
"packages/producer/src/cloudflareSandboxBench.ts",
// Sibling-surface fix (PR #2529 R2): lambda.ts's top-level `run`
// (cyclo 39, CRAP 1560) is the big subcommand switch that pre-dates
// this PR. The change threads two additional variables through the
Expand Down
6 changes: 6 additions & 0 deletions packages/producer/cloudflare-sandbox/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
chunk-worker.mjs
.wrangler/
dist/
.dev.vars
runtime/
62 changes: 62 additions & 0 deletions packages/producer/cloudflare-sandbox/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
FROM docker.io/cloudflare/sandbox:0.12.5

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
ffmpeg \
libgbm1 \
libnss3 \
libatk-bridge2.0-0 \
libdrm2 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
libasound2 \
libpangocairo-1.0-0 \
libxshmfence1 \
libgtk-3-0 \
fonts-liberation \
fonts-dejavu-core \
fontconfig \
&& rm -rf /var/lib/apt/lists/* \
&& fc-cache -fv

# Ubuntu jammy has no `chromium` apt package. Install the same
# chrome-headless-shell the producer regression image uses.
RUN npx --yes @puppeteer/browsers install chrome-headless-shell@148.0.7778.167 \
--path /opt/hf/chrome \
&& find /opt/hf/chrome -name "chrome-headless-shell" -type f

ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV CONTAINER=true
ENV CHROME_PATH=/opt/hf/chrome
# resolveHeadlessShellPath also searches ~/.cache/puppeteer; keep a copy there.
ENV PRODUCER_HEADLESS_SHELL_PATH=/opt/hf/chrome

RUN mkdir -p /opt/hf /workspace /root/.cache \
&& ln -sfn /opt/hf/chrome /root/.cache/puppeteer

COPY chunk-worker.mjs /opt/hf/chunk-worker.mjs
COPY runtime/hyperframe.manifest.json /opt/hf/runtime/hyperframe.manifest.json
COPY runtime/hyperframe.runtime.iife.js /opt/hf/runtime/hyperframe.runtime.iife.js
ENV PRODUCER_HYPERFRAME_MANIFEST_PATH=/opt/hf/runtime/hyperframe.manifest.json
WORKDIR /opt/hf
RUN printf '%s\n' '{"name":"hf-chunk-worker","type":"module"}' > /opt/hf/package.json \
&& npm install --omit=dev ws \
&& test -d /opt/hf/node_modules/ws

# Wrapper so `chrome-headless-shell --version` works in /smoke.
RUN printf '%s\n' '#!/bin/sh' 'exec "$(find /opt/hf/chrome -name chrome-headless-shell -type f | head -n1)" "$@"' \
> /usr/local/bin/chrome-headless-shell \
&& chmod +x /usr/local/bin/chrome-headless-shell \
&& printf '%s\n' '#!/bin/sh' 'exec /usr/local/bin/chrome-headless-shell "$@"' \
> /usr/local/bin/chromium \
&& chmod +x /usr/local/bin/chromium

ENV PUPPETEER_EXECUTABLE_PATH=/usr/local/bin/chrome-headless-shell
ENV PRODUCER_HEADLESS_SHELL_PATH=/usr/local/bin/chrome-headless-shell
ENV NODE_PATH=/opt/hf/node_modules

RUN curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash -s "bun-v1.3.14"

EXPOSE 8080
208 changes: 208 additions & 0 deletions packages/producer/cloudflare-sandbox/bun.lock

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions packages/producer/cloudflare-sandbox/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "hf-render-sandbox",
"private": true,
"type": "module",
"scripts": {
"build:chunk-worker": "bun build ../src/services/distributed/renderChunkCli.ts --outfile chunk-worker.mjs --target bun && mkdir -p runtime && cp ../../core/dist/hyperframe.manifest.json ../../core/dist/hyperframe.runtime.iife.js runtime/",
"dev": "bun run build:chunk-worker && wrangler dev",
"deploy": "bun run build:chunk-worker && wrangler deploy"
},
"dependencies": {
"@cloudflare/sandbox": "^0.12.5"
},
"devDependencies": {
"wrangler": "^4.83.0"
}
}
150 changes: 150 additions & 0 deletions packages/producer/cloudflare-sandbox/src/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { getSandbox, type Sandbox } from "@cloudflare/sandbox";

export { Sandbox } from "@cloudflare/sandbox";

export type Env = {
Sandbox: DurableObjectNamespace<Sandbox>;
};

const MAX_INSTANCES = 10;

function sandboxId(chunkIndex: number): string {
if (!Number.isInteger(chunkIndex) || chunkIndex < 0 || chunkIndex >= MAX_INSTANCES) {
throw new Error(`chunkIndex must be 0..${MAX_INSTANCES - 1}`);
}
return `hf-chunk-${chunkIndex}`;
}

export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
try {
if (request.method === "POST" && url.pathname === "/destroy") {
const chunkIndex = Number(url.searchParams.get("chunk") ?? "0");
const sandbox = getSandbox(env.Sandbox, sandboxId(chunkIndex));
await sandbox.destroy();
return Response.json({ ok: true, destroyed: sandboxId(chunkIndex) });
}

if (request.method === "GET" && url.pathname === "/health") {
return Response.json({
ok: true,
accountPinned: true,
maxInstances: MAX_INSTANCES,
});
}

if (request.method === "POST" && url.pathname === "/smoke") {
const chunkIndex = Number(url.searchParams.get("chunk") ?? "0");
const sandbox = getSandbox(env.Sandbox, sandboxId(chunkIndex));
const ffmpeg = await sandbox.exec("ffmpeg -version");
const chrome = await sandbox.exec(
"sh -c 'chromium --version 2>/dev/null || chrome-headless-shell --version 2>/dev/null || echo missing'",
);
return Response.json({
ffmpeg: { ok: ffmpeg.success, out: ffmpeg.stdout.slice(0, 200) },
chrome: { ok: chrome.success, out: chrome.stdout.slice(0, 200) },
});
}

if (request.method === "POST" && url.pathname === "/plan") {
const chunkIndex = Number(url.searchParams.get("chunk") ?? "0");
const sandbox = getSandbox(env.Sandbox, sandboxId(chunkIndex));
const bytes = new Uint8Array(await request.arrayBuffer());
await sandbox.writeFile("/tmp/project.tar.gz.b64", uint8ToBase64(bytes));
const unpacked = await sandbox.exec(
"sh -c 'base64 -d /tmp/project.tar.gz.b64 > /tmp/project.tar.gz && rm -rf /workspace/project /workspace/plan && mkdir -p /workspace/project /workspace/plan && tar -xzf /tmp/project.tar.gz -C /workspace/project'",
);
if (!unpacked.success) {
return Response.json(
{ ok: false, error: unpacked.stderr || unpacked.stdout },
{ status: 500 },
);
}
const planned = await sandbox.exec(
"sh -c 'cd /opt/hf && mkdir -p node_modules && (test -d node_modules/ws || npm install --omit=dev ws) && NODE_PATH=/opt/hf/node_modules PRODUCER_HYPERFRAME_MANIFEST_PATH=/opt/hf/runtime/hyperframe.manifest.json HF_ACTION=plan HF_PROJECT_DIR=/workspace/project HF_PLAN_DIR=/workspace/plan HF_MAX_SANDBOXES=10 bun ./chunk-worker.mjs'",
);
if (!planned.success) {
return Response.json(
{ ok: false, error: planned.stderr || planned.stdout },
{ status: 500 },
);
}
const packed = await sandbox.exec(
"sh -c 'tar -czf /tmp/plan.tar.gz -C /workspace/plan . && base64 /tmp/plan.tar.gz'",
);
if (!packed.success) {
return Response.json({ ok: false, error: packed.stderr }, { status: 500 });
}
return Response.json({
ok: true,
meta: planned.stdout.trim(),
planTarBase64: packed.stdout.replace(/\s+/g, ""),
});
}

if (request.method === "POST" && url.pathname === "/write-plan") {
const chunkIndex = Number(url.searchParams.get("chunk") ?? "0");
const sandbox = getSandbox(env.Sandbox, sandboxId(chunkIndex));
const bytes = new Uint8Array(await request.arrayBuffer());
const b64 = uint8ToBase64(bytes);
await sandbox.writeFile("/tmp/plan.tar.gz.b64", b64);
const unpacked = await sandbox.exec(
"sh -c 'base64 -d /tmp/plan.tar.gz.b64 > /tmp/plan.tar.gz && rm -rf /workspace/plan && mkdir -p /workspace/plan && tar -xzf /tmp/plan.tar.gz -C /workspace/plan'",
);
if (!unpacked.success) {
return Response.json(
{ ok: false, error: unpacked.stderr || unpacked.stdout },
{ status: 500 },
);
}
return Response.json({
ok: true,
sandboxId: sandboxId(chunkIndex),
bytes: bytes.byteLength,
});
}

if (request.method === "POST" && url.pathname === "/render-chunk") {
const chunkIndex = Number(url.searchParams.get("chunk") ?? "0");
const sandbox = getSandbox(env.Sandbox, sandboxId(chunkIndex));
const started = Date.now();
const result = await sandbox.exec(
`sh -c 'cd /opt/hf && mkdir -p node_modules && (test -d node_modules/ws || npm install --omit=dev ws) && NODE_PATH=/opt/hf/node_modules PRODUCER_HYPERFRAME_MANIFEST_PATH=/opt/hf/runtime/hyperframe.manifest.json HF_ACTION=render HF_PLAN_DIR=/workspace/plan HF_CHUNK_INDEX=${chunkIndex} HF_OUTPUT=/workspace/chunk.mp4 bun ./chunk-worker.mjs'`,
);
if (!result.success) {
return Response.json(
{ ok: false, error: result.stderr || result.stdout, elapsedMs: Date.now() - started },
{ status: 500 },
);
}
const encoded = await sandbox.exec("base64 /workspace/chunk.mp4");
if (!encoded.success) {
return Response.json({ ok: false, error: encoded.stderr }, { status: 500 });
}
return Response.json({
ok: true,
chunkIndex,
elapsedMs: Date.now() - started,
mp4Base64: encoded.stdout.replace(/\s+/g, ""),
});
}

return new Response("not found", { status: 404 });
} catch (err) {
return Response.json(
{ ok: false, error: err instanceof Error ? err.message : String(err) },
{ status: 500 },
);
}
},
};

function uint8ToBase64(bytes: Uint8Array): string {
let binary = "";
const chunk = 0x8000;
for (let i = 0; i < bytes.length; i += chunk) {
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
}
return btoa(binary);
}
33 changes: 33 additions & 0 deletions packages/producer/cloudflare-sandbox/wrangler.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "hf-render-sandbox",
"main": "src/worker.ts",
"compatibility_date": "2026-08-01",
"compatibility_flags": ["nodejs_compat"],
"account_id": "86bb57b655af7915f42b29dfc2d8807d",
"workers_dev": true,
"limits": {
"cpu_ms": 300000
},
"containers": [
{
"class_name": "Sandbox",
"image": "./Dockerfile",
"instance_type": "standard-3",
"max_instances": 10
}
],
"durable_objects": {
"bindings": [
{
"class_name": "Sandbox",
"name": "Sandbox"
}
]
},
"migrations": [
{
"new_sqlite_classes": ["Sandbox"],
"tag": "v1"
}
]
}
1 change: 1 addition & 0 deletions packages/producer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"check:runtime-conformance": "tsx src/runtime-conformance.ts",
"benchmark": "tsx src/benchmark.ts",
"bench:hdr": "tsx src/benchmark.ts --tags hdr",
"bench:sandbox": "tsx src/cloudflareSandboxBench.ts",
"test": "bun run test:unit",
"test:classification": "node --test scripts/test-classification.test.mjs && node scripts/check-test-classification.mjs",
"test:unit": "bun run test:classification && node scripts/run-test-lane.mjs unit",
Expand Down
Loading