Skip to content
Merged
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 examples/think-compare-runtimes/.dev.vars.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copy to .dev.vars for local development.
#
# wrangler dev / vite dev under colima or Docker Desktop does not
# expose /dev/fuse to Workers Containers. This tells the Workspace
# container to run wsd with its userspace shim locally. Production
# leaves this unset so wsd uses real kernel FUSE when available.
FUSE_MOUNT=shim
31 changes: 31 additions & 0 deletions examples/think-compare-runtimes/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# wrangler files
.wrangler
.dev.vars*
!.dev.vars.example
.env*
!.env.example
6 changes: 6 additions & 0 deletions examples/think-compare-runtimes/Dockerfile.sandbox
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Sandbox runtime image for the comparison example. This extends
# the Sandbox SDK base image used by getSandbox(env.Sandbox, ...).

FROM docker.io/cloudflare/sandbox:0.11.0

EXPOSE 8080
30 changes: 30 additions & 0 deletions examples/think-compare-runtimes/Dockerfile.workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Workspace runtime image for the comparison example. It runs the
# wsd daemon so shell commands can see the same /workspace tree that
# Workspace.fs stores in Durable Object storage.
#
# Base image is node:22-trixie-slim so the agent has node + npm
# available out of the box for in-container verification (e.g. npm
# install / npm test). It's also Debian-glibc, which is what the wsd
# SEA binary is built against.
#
# FUSE_MOUNT=auto lets wsd use real kernel FUSE when available and
# fall back to the userspace shim in local development environments
# that do not expose /dev/fuse.

FROM ghcr.io/cloudflare/workspace-wsd-linux-x64:0.0.0-alpha.7 AS wsd

FROM --platform=linux/amd64 node:22-trixie-slim

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates git fuse3 libfuse2t64 \
&& rm -rf /var/lib/apt/lists/*

COPY --from=wsd /usr/local/bin/wsd /usr/local/bin/wsd

ENV PORT=8080
ENV MOUNT_POINT=/workspace
ENV FUSE_MOUNT=auto
EXPOSE 8080

ENTRYPOINT ["/usr/local/bin/wsd"]
12 changes: 12 additions & 0 deletions examples/think-compare-runtimes/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Think Runtime Comparison</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
56 changes: 56 additions & 0 deletions examples/think-compare-runtimes/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "@cloudflare/example-think-compare-runtimes",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"build:wsd": "npm run build:docker --workspace @cloudflare/workspace-wsd",
"predev": "npm run build:wsd",
"predeploy": "npm run build:wsd",
"dev": "vite",
"build": "tsc -b && vite build",
"test": "vitest run",
"typecheck": "tsc -b --pretty false",
"preview": "npm run build && vite preview",
"deploy": "npm run build && wrangler deploy",
"cf-typegen": "wrangler types"
},
"dependencies": {
"@cloudflare/kumo": "^2.4.1",
"@cloudflare/sandbox": "^0.11.0",
"@cloudflare/think": "^0.8.2",
"@cloudflare/workspace": "*",
"@cloudflare/workspace-rpc": "*",
"@phosphor-icons/react": "^2.1.10",
"@shikijs/core": "^4.2.0",
"@shikijs/engine-javascript": "^4.2.0",
"@shikijs/langs": "^4.2.0",
"@shikijs/themes": "^4.2.0",
"agents": "^0.14.1",
"ai": "^6.0.196",
"partyserver": "^0.5.6",
"partysocket": "^1.1.19",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"shiki": "^4.2.0",
"workers-ai-provider": "^3.1.14",
"zod": "^4.4.3"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^1.39.2",
"@tailwindcss/vite": "^4.3.0",
"@testing-library/react": "^16.3.2",
"@types/node": "^25.9.1",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"jsdom": "^29.1.1",
"tailwindcss": "^4.3.0",
"typescript": "^6.0.3",
"vite": "^8.0.14",
"vitest": "^4.1.7",
"wrangler": "^4.95.0"
}
}
35 changes: 35 additions & 0 deletions examples/think-compare-runtimes/shared/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export type RuntimeId = "workspace" | "sandbox";
export type EventRuntime = RuntimeId | "both";
export type ExecutionTarget = "worker-shell" | "workspace-container" | "sandbox-container";

export type RunEventKind =
| "run_started"
| "run_completed"
| "runtime_started"
| "runtime_completed"
| "runtime_failed"
| "runtime_note"
| "container_acquired"
| "container_released"
| "container_release_scheduled"
| "agent_message"
| "agent_message_delta"
| "agent_thinking_delta"
| "agent_step"
| "agent_tool_call"
| "agent_tool_result"
| "agent_tool_error"
| "tool_call"
| "tool_result"
| "tool_error";

export interface RunEvent {
id: string;
runId: string;
sequence: number;
runtime: EventRuntime;
kind: RunEventKind;
title: string;
detail: string;
timestamp: string;
}
191 changes: 191 additions & 0 deletions examples/think-compare-runtimes/shared/fixture.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { describe, expect, test } from "vitest";
import { comparisonFixture } from "./fixture";

describe("comparisonFixture", () => {
test("defines a docs feature task for both runtimes", () => {
expect(comparisonFixture.root).toBe("/workspace/repo");
expect(comparisonFixture.files.map((file) => file.path)).toEqual([
"package.json",
"README.md",
"style-guide.md",
"docs-nav.json",
"feature-briefs/smart-request-policies.md",
"docs/workers/index.md",
"docs/workers/routing.md",
"docs/workers/security.md",
"docs/workers/examples/authenticated-api.md",
"docs/workers/examples/rate-limit.md",
"docs/_partials/beta-note.md",
"scripts/check-docs.mjs",
]);
expect(comparisonFixture.task).toContain("Add documentation for Smart Request Policies");
expect(comparisonFixture.task).toContain("create a new Workers docs page");
expect(comparisonFixture.task).toContain("update the docs navigation");
});

test("provides source material for a file-first docs workflow", () => {
const brief = fileContents("feature-briefs/smart-request-policies.md");
const styleGuide = fileContents("style-guide.md");
const nav = fileContents("docs-nav.json");
const checker = fileContents("scripts/check-docs.mjs");

expect(brief).toContain("Smart Request Policies");
expect(brief).toContain("Enterprise report exports");
expect(styleGuide).toContain("Frontmatter");
expect(styleGuide).toContain("Workers docs style");
expect(nav).toContain("Workers");
expect(checker).toContain("docs/workers/smart-request-policies.md");
expect(checker).toContain("docs-nav.json");
});

test("reports all docs validation failures as repair instructions", () => {
const files = createFixtureFiles();
files.set(
`${comparisonFixture.root}/docs/workers/smart-request-policies.md`,
[
"---",
"title: Smart Request Policies",
"description: Configure Smart Request Policies.",
"lastUpdated: 2026-06-05",
"---",
"",
"# Smart Request Policies",
"",
"This draft intentionally misses several validation requirements.",
"",
"~~~ts",
"export default { async fetch() { return new Response('ok'); } };",
"~~~",
"",
].join("\n"),
);

const result = runDocsCheck(files);

expect(result.exitCode).toBe(1);
expect(result.output).toContain("docs validation failed:");
expect(result.output).toContain('exact header name "x-bypass-token"');
expect(result.output).toContain('exact phrase "Enterprise report exports"');
expect(result.output).toContain('path "/workers/smart-request-policies/"');
expect(result.output).toContain('README.md must include "smart-request-policies"');
});

test("accepts docs that satisfy the validation contract", () => {
const files = createFixtureFiles();
files.set(
`${comparisonFixture.root}/docs/workers/smart-request-policies.md`,
[
"---",
"title: Smart Request Policies",
"description: Configure Smart Request Policies for Workers requests.",
"lastUpdated: 2026-06-05",
"---",
"",
"# Smart Request Policies",
"",
"Smart Request Policies evaluate method, path, header, and risk-signal rules before a Worker handler runs.",
"",
"Enterprise report exports can use a route-specific bypass token for scheduled jobs.",
"The Worker can check the `x-bypass-token` header before allowing sensitive export routes.",
"",
"~~~ts",
"export default { async fetch(request: Request, env: Env): Promise<Response> {",
" const url = new URL(request.url);",
" if (url.pathname === '/reports/export' && request.method !== 'GET') {",
" if (request.headers.get('x-bypass-token') !== env.EXPORT_BYPASS_TOKEN) {",
" return new Response('Policy denied', { status: 403 });",
" }",
" }",
" return fetch(request);",
"} };",
"~~~",
"",
].join("\n"),
);
files.set(
`${comparisonFixture.root}/docs-nav.json`,
JSON.stringify(
{
sections: [
{
title: "Workers",
items: [
{ title: "Smart Request Policies", path: "/workers/smart-request-policies/" },
],
},
],
},
null,
2,
),
);
files.set(
`${comparisonFixture.root}/README.md`,
"See docs/workers/smart-request-policies.md for Smart Request Policies.\n",
);

const result = runDocsCheck(files);

expect(result).toEqual({ exitCode: 0, output: "docs check passed" });
});
});

function createFixtureFiles(): Map<string, string> {
return new Map(
comparisonFixture.files.map((file) => [
`${comparisonFixture.root}/${file.path}`,
file.contents,
]),
);
}

function runDocsCheck(files: Map<string, string>): { exitCode: number; output: string } {
const output: string[] = [];
const script = fileContents("scripts/check-docs.mjs").replace(
'import { readFileSync } from "node:fs";',
"",
);
const readFileSync = (path: string): string => {
const contents = files.get(`${comparisonFixture.root}/${path}`);
if (contents === undefined) throw new Error(`ENOENT: ${path}`);
return contents;
};
const consoleLike = {
error: (message: string) => output.push(message),
log: (message: string) => output.push(message),
};
const processLike = {
exit(code: number) {
throw new DocsCheckExit(code);
},
};

try {
new Function("readFileSync", "console", "process", script)(
readFileSync,
consoleLike,
processLike,
);
return { exitCode: 0, output: output.join("\n") };
} catch (error) {
if (error instanceof DocsCheckExit) {
return { exitCode: error.code, output: output.join("\n") };
}
throw error;
}
}

class DocsCheckExit extends Error {
readonly code: number;

constructor(code: number) {
super(`docs check exited with ${code}`);
this.code = code;
}
}

function fileContents(path: string): string {
const file = comparisonFixture.files.find((candidate) => candidate.path === path);
expect(file, `missing fixture file ${path}`).toBeTruthy();
return file?.contents ?? "";
}
Loading