+// wrangler.jsonc
+{
+ "name": "app-name-goes-here", // name of the app
+ "main": "src/index.ts", // default file
+ "compatibility_date": "2025-02-11",
+ "compatibility_flags": ["nodejs_compat"], // Enable Node.js compatibility
+ "observability": {
+ // Enable logging by default
+ "enabled": true,
+ }
+}
+
+
+import { DurableObject } from "cloudflare:workers";
+
+interface Env {
+WEBSOCKET_HIBERNATION_SERVER: DurableObject;
+}
+
+// Durable Object
+export class WebSocketHibernationServer extends DurableObject {
+async fetch(request) {
+// Creates two ends of a WebSocket connection.
+const webSocketPair = new WebSocketPair();
+const [client, server] = Object.values(webSocketPair);
+
+ // Calling `acceptWebSocket()` informs the runtime that this WebSocket is to begin terminating
+ // request within the Durable Object. It has the effect of "accepting" the connection,
+ // and allowing the WebSocket to send and receive messages.
+ // Unlike `ws.accept()`, `state.acceptWebSocket(ws)` informs the Workers Runtime that the WebSocket
+ // is "hibernatable", so the runtime does not need to pin this Durable Object to memory while
+ // the connection is open. During periods of inactivity, the Durable Object can be evicted
+ // from memory, but the WebSocket connection will remain open. If at some later point the
+ // WebSocket receives a message, the runtime will recreate the Durable Object
+ // (run the `constructor`) and deliver the message to the appropriate handler.
+ this.ctx.acceptWebSocket(server);
+
+ return new Response(null, {
+ status: 101,
+ webSocket: client,
+ });
+
+ },
+
+ async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): void | Promise {
+ // Upon receiving a message from the client, reply with the same message,
+ // but will prefix the message with "[Durable Object]: " and return the
+ // total number of connections.
+ ws.send(
+ `[Durable Object] message: ${message}, connections: ${this.ctx.getWebSockets().length}`,
+ );
+ },
+
+ async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) void | Promise {
+ // If the client closes the connection, the runtime will invoke the webSocketClose() handler.
+ ws.close(code, "Durable Object is closing WebSocket");
+ },
+
+ async webSocketError(ws: WebSocket, error: unknown): void | Promise {
+ console.error("WebSocket error:", error);
+ ws.close(1011, "WebSocket error");
+ }
+
+}
+
+
+
+
+import { DurableObject } from "cloudflare:workers";
+
+interface Env {
+ALARM_EXAMPLE: DurableObject;
+}
+
+export default {
+ async fetch(request, env) {
+ let url = new URL(request.url);
+ let userId = url.searchParams.get("userId") || crypto.randomUUID();
+ let id = env.ALARM_EXAMPLE.idFromName(userId);
+ return await env.ALARM_EXAMPLE.get(id).fetch(request);
+ },
+};
+
+const SECONDS = 1000;
+
+export class AlarmExample extends DurableObject {
+constructor(ctx, env) {
+this.ctx = ctx;
+this.storage = ctx.storage;
+}
+async fetch(request) {
+// If there is no alarm currently set, set one for 10 seconds from now
+let currentAlarm = await this.storage.getAlarm();
+if (currentAlarm == null) {
+this.storage.setAlarm(Date.now() + 10 \_ SECONDS);
+}
+}
+async alarm(alarmInfo) {
+// The alarm handler will be invoked whenever an alarm fires.
+// You can use this to do work, read from the Storage API, make HTTP calls
+// and set future alarms to run using this.storage.setAlarm() from within this handler.
+if (alarmInfo?.retryCount != 0) {
+console.log("This alarm event has been attempted ${alarmInfo?.retryCount} times before.");
+}
+
+// Set a new alarm for 10 seconds from now before exiting the handler
+this.storage.setAlarm(Date.now() + 10 \_ SECONDS);
+}
+}
+
+
+
+
+// src/index.ts
+import { Hono } from 'hono'
+import { cors } from 'hono/cors'
+
+interface Env {
+AUTH_TOKENS: KVNamespace;
+}
+
+const app = new Hono<{ Bindings: Env }>()
+
+// Add CORS middleware
+app.use('\*', cors())
+
+app.get('/', async (c) => {
+try {
+// Get token from header or cookie
+const token = c.req.header('Authorization')?.slice(7) ||
+c.req.header('Cookie')?.match(/auth_token=([^;]+)/)?.[1];
+if (!token) {
+return c.json({
+authenticated: false,
+message: 'No authentication token provided'
+}, 403)
+}
+
+ // Check token in KV
+ const userData = await c.env.AUTH_TOKENS.get(token)
+
+ if (!userData) {
+ return c.json({
+ authenticated: false,
+ message: 'Invalid or expired token'
+ }, 403)
+ }
+
+ return c.json({
+ authenticated: true,
+ message: 'Authentication successful',
+ data: JSON.parse(userData)
+ })
+
+} catch (error) {
+console.error('Authentication error:', error)
+return c.json({
+authenticated: false,
+message: 'Internal server error'
+}, 500)
+}
+})
+
+export default app
+
+
+
+// src/producer.ts
+interface Env {
+ REQUEST_QUEUE: Queue;
+ UPSTREAM_API_URL: string;
+ UPSTREAM_API_KEY: string;
+}
+
+export default {
+async fetch(request: Request, env: Env) {
+const info = {
+timestamp: new Date().toISOString(),
+method: request.method,
+url: request.url,
+headers: Object.fromEntries(request.headers),
+};
+await env.REQUEST_QUEUE.send(info);
+
+return Response.json({
+message: 'Request logged',
+requestId: crypto.randomUUID()
+});
+
+},
+
+async queue(batch: MessageBatch, env: Env) {
+const requests = batch.messages.map(msg => msg.body);
+
+ const response = await fetch(env.UPSTREAM_API_URL, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${env.UPSTREAM_API_KEY}`
+ },
+ body: JSON.stringify({
+ timestamp: new Date().toISOString(),
+ batchSize: requests.length,
+ requests
+ })
+ });
+
+ if (!response.ok) {
+ throw new Error(`Upstream API error: ${response.status}`);
+ }
+
+}
+};
+
+
+
+
+// Postgres.js 3.4.5 or later is recommended
+import postgres from "postgres";
+
+export interface Env {
+// If you set another name in the Wrangler config file as the value for 'binding',
+// replace "HYPERDRIVE" with the variable name you defined.
+HYPERDRIVE: Hyperdrive;
+}
+
+export default {
+async fetch(request, env, ctx): Promise {
+console.log(JSON.stringify(env));
+// Create a database client that connects to your database via Hyperdrive.
+//
+// Hyperdrive generates a unique connection string you can pass to
+// supported drivers, including node-postgres, Postgres.js, and the many
+// ORMs and query builders that use these drivers.
+const sql = postgres(env.HYPERDRIVE.connectionString)
+
+ try {
+ // Test query
+ const results = await sql`SELECT * FROM pg_tables`;
+
+ // Clean up the client, ensuring we don't kill the worker before that is
+ // completed.
+ ctx.waitUntil(sql.end());
+
+ // Return result rows as JSON
+ return Response.json(results);
+ } catch (e) {
+ console.error(e);
+ return Response.json(
+ { error: e instanceof Error ? e.message : e },
+ { status: 500 },
+ );
+ }
+
+},
+} satisfies ExportedHandler;
+
+
+
+
+import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
+
+type Env = {
+// Add your bindings here, e.g. Workers KV, D1, Workers AI, etc.
+MY_WORKFLOW: Workflow;
+};
+
+// User-defined params passed to your workflow
+type Params = {
+email: string;
+metadata: Record;
+};
+
+export class MyWorkflow extends WorkflowEntrypoint {
+async run(event: WorkflowEvent, step: WorkflowStep) {
+// Can access bindings on `this.env`
+// Can access params on `event.payload`
+const files = await step.do('my first step', async () => {
+// Fetch a list of files from $SOME_SERVICE
+return {
+files: [
+'doc_7392_rev3.pdf',
+'report_x29_final.pdf',
+'memo_2024_05_12.pdf',
+'file_089_update.pdf',
+'proj_alpha_v2.pdf',
+'data_analysis_q2.pdf',
+'notes_meeting_52.pdf',
+'summary_fy24_draft.pdf',
+],
+};
+});
+
+ const apiResponse = await step.do('some other step', async () => {
+ let resp = await fetch('https://api.cloudflare.com/client/v4/ips');
+ return await resp.json();
+ });
+
+ await step.sleep('wait on something', '1 minute');
+
+ await step.do(
+ 'make a call to write that could maybe, just might, fail',
+ // Define a retry strategy
+ {
+ retries: {
+ limit: 5,
+ delay: '5 second',
+ backoff: 'exponential',
+ },
+ timeout: '15 minutes',
+ },
+ async () => {
+ // Do stuff here, with access to the state from our previous steps
+ if (Math.random() > 0.5) {
+ throw new Error('API call to $STORAGE_SYSTEM failed');
+ }
+ },
+ );
+
+}
+}
+
+export default {
+async fetch(req: Request, env: Env): Promise {
+let url = new URL(req.url);
+
+ if (url.pathname.startsWith('/favicon')) {
+ return Response.json({}, { status: 404 });
+ }
+
+ // Get the status of an existing instance, if provided
+ let id = url.searchParams.get('instanceId');
+ if (id) {
+ let instance = await env.MY_WORKFLOW.get(id);
+ return Response.json({
+ status: await instance.status(),
+ });
+ }
+
+ const data = await req.json()
+
+ // Spawn a new instance and return the ID and status
+ let instance = await env.MY_WORKFLOW.create({
+ // Define an ID for the Workflow instance
+ id: crypto.randomUUID(),
+ // Pass data to the Workflow instance
+ // Available on the WorkflowEvent
+ params: data,
+ });
+
+ return Response.json({
+ id: instance.id,
+ details: await instance.status(),
+ });
+
+},
+};
+
+
+
+
+interface Env {
+ USER_EVENTS: AnalyticsEngineDataset;
+}
+
+export default {
+async fetch(req: Request, env: Env): Promise {
+let url = new URL(req.url);
+let path = url.pathname;
+let userId = url.searchParams.get("userId");
+
+ // Write a datapoint for this visit, associating the data with
+ // the userId as our Analytics Engine 'index'
+ env.USER_EVENTS.writeDataPoint({
+ // Write metrics data: counters, gauges or latency statistics
+ doubles: [],
+ // Write text labels - URLs, app names, event_names, etc
+ blobs: [path],
+ // Provide an index that groups your data correctly.
+ indexes: [userId],
+ });
+
+ return Response.json({
+ hello: "world",
+ });
+ ,
+
+};
+
+
+
+
+import puppeteer from "@cloudflare/puppeteer";
+
+interface Env {
+ BROWSER_RENDERING: Fetcher;
+}
+
+export default {
+ async fetch(request, env): Promise {
+ const { searchParams } = new URL(request.url);
+ let url = searchParams.get("url");
+
+ if (url) {
+ url = new URL(url).toString(); // normalize
+ const browser = await puppeteer.launch(env.MYBROWSER);
+ const page = await browser.newPage();
+ await page.goto(url);
+ // Parse the page content
+ const content = await page.content();
+ // Find text within the page content
+ const text = await page.$eval("body", (el) => el.textContent);
+ // Do something with the text
+ // e.g. log it to the console, write it to KV, or store it in a database.
+ console.log(text);
+
+ // Ensure we close the browser session
+ await browser.close();
+
+ return Response.json({
+ bodyText: text,
+ })
+ } else {
+ return Response.json({
+ error: "Please add an ?url=https://example.com/ parameter"
+ }, { status: 400 })
+ }
+ },
+} satisfies ExportedHandler;
+
+
+
+// src/index.ts
+
+interface Env {
+ ASSETS: Fetcher;
+}
+
+export default {
+ fetch(request, env) {
+ const url = new URL(request.url);
+
+ if (url.pathname.startsWith("/api/")) {
+ return Response.json({
+ name: "Cloudflare",
+ });
+ }
+
+ return env.ASSETS.fetch(request);
+ },
+} satisfies ExportedHandler;
+
+
+
+Build an AI Agent on Cloudflare Workers, using the agents-sdk, and the state management and syncing APIs built into the agents-sdk.
+
+
+
+// src/index.ts
+import { Agent, AgentNamespace, Connection, ConnectionContext, getAgentByName, routeAgentRequest, WSMessage } from 'agents-sdk';
+import { OpenAI } from "openai";
+
+interface Env {
+ AIAgent: AgentNamespace;
+ OPENAI_API_KEY: string;
+}
+
+export class AIAgent extends Agent {
+ // Handle HTTP requests with your Agent
+ async onRequest(request) {
+ // Connect with AI capabilities
+ const ai = new OpenAI({
+ apiKey: this.env.OPENAI_API_KEY,
+ });
+
+ // Process and understand
+ const response = await ai.chat.completions.create({
+ model: "gpt-4",
+ messages: [{ role: "user", content: await request.text() }],
+ });
+
+ return new Response(response.choices[0].message.content);
+ }
+
+ async processTask(task) {
+ await this.understand(task);
+ await this.act();
+ await this.reflect();
+ }
+
+ // Handle WebSockets
+ async onConnect(connection: Connection) {
+ await this.initiate(connection);
+ connection.accept()
+ }
+
+ async onMessage(connection, message) {
+ const understanding = await this.comprehend(message);
+ await this.respond(connection, understanding);
+ }
+
+ async evolve(newInsight) {
+ this.setState({
+ ...this.state,
+ insights: [...(this.state.insights || []), newInsight],
+ understanding: this.state.understanding + 1,
+ });
+ }
+
+ onStateUpdate(state, source) {
+ console.log("Understanding deepened:", {
+ newState: state,
+ origin: source,
+ });
+ }
+
+ // Scheduling APIs
+ // An Agent can schedule tasks to be run in the future by calling this.schedule(when, callback, data), where when can be a delay, a Date, or a cron string; callback the function name to call, and data is an object of data to pass to the function.
+ //
+ // Scheduled tasks can do anything a request or message from a user can: make requests, query databases, send emails, read+write state: scheduled tasks can invoke any regular method on your Agent.
+ async scheduleExamples() {
+ // schedule a task to run in 10 seconds
+ let task = await this.schedule(10, "someTask", { message: "hello" });
+
+ // schedule a task to run at a specific date
+ let task = await this.schedule(new Date("2025-01-01"), "someTask", {});
+
+ // schedule a task to run every 10 seconds
+ let { id } = await this.schedule("*/10 * * * *", "someTask", { message: "hello" });
+
+ // schedule a task to run every 10 seconds, but only on Mondays
+ let task = await this.schedule("0 0 * * 1", "someTask", { message: "hello" });
+
+ // cancel a scheduled task
+ this.cancelSchedule(task.id);
+
+ // Get a specific schedule by ID
+ // Returns undefined if the task does not exist
+ let task = await this.getSchedule(task.id)
+
+ // Get all scheduled tasks
+ // Returns an array of Schedule objects
+ let tasks = this.getSchedules();
+
+ // Cancel a task by its ID
+ // Returns true if the task was cancelled, false if it did not exist
+ await this.cancelSchedule(task.id);
+
+ // Filter for specific tasks
+ // e.g. all tasks starting in the next hour
+ let tasks = this.getSchedules({
+ timeRange: {
+ start: new Date(Date.now()),
+ end: new Date(Date.now() + 60 * 60 * 1000),
+ }
+ });
+ }
+
+ async someTask(data) {
+ await this.callReasoningModel(data.message);
+ }
+
+ // Use the this.sql API within the Agent to access the underlying SQLite database
+ async callReasoningModel(prompt: Prompt) {
+ interface Prompt {
+ userId: string;
+ user: string;
+ system: string;
+ metadata: Record;
+ }
+
+ interface History {
+ timestamp: Date;
+ entry: string;
+ }
+
+ let result = this.sql`SELECT * FROM history WHERE user = ${prompt.userId} ORDER BY timestamp DESC LIMIT 1000`;
+ let context = [];
+ for await (const row of result) {
+ context.push(row.entry);
+ }
+
+ const client = new OpenAI({
+ apiKey: this.env.OPENAI_API_KEY,
+ });
+
+ // Combine user history with the current prompt
+ const systemPrompt = prompt.system || 'You are a helpful assistant.';
+ const userPrompt = `${prompt.user}\n\nUser history:\n${context.join('\n')}`;
+
+ try {
+ const completion = await client.chat.completions.create({
+ model: this.env.MODEL || 'o3-mini',
+ messages: [
+ { role: 'system', content: systemPrompt },
+ { role: 'user', content: userPrompt },
+ ],
+ temperature: 0.7,
+ max_tokens: 1000,
+ });
+
+ // Store the response in history
+ this
+ .sql`INSERT INTO history (timestamp, user, entry) VALUES (${new Date()}, ${prompt.userId}, ${completion.choices[0].message.content})`;
+
+ return completion.choices[0].message.content;
+ } catch (error) {
+ console.error('Error calling reasoning model:', error);
+ throw error;
+ }
+ }
+
+ // Use the SQL API with a type parameter
+ async queryUser(userId: string) {
+ type User = {
+ id: string;
+ name: string;
+ email: string;
+ };
+ // Supply the type paramter to the query when calling this.sql
+ // This assumes the results returns one or more User rows with "id", "name", and "email" columns
+ // You do not need to specify an array type (`User[]` or `Array`) as `this.sql` will always return an array of the specified type.
+ const user = await this.sql`SELECT * FROM users WHERE id = ${userId}`;
+ return user
+ }
+
+ // Run and orchestrate Workflows from Agents
+ async runWorkflow(data) {
+ let instance = await env.MY_WORKFLOW.create({
+ id: data.id,
+ params: data,
+ })
+
+ // Schedule another task that checks the Workflow status every 5 minutes...
+ await this.schedule("*/5 * * * *", "checkWorkflowStatus", { id: instance.id });
+ }
+}
+
+export default {
+ async fetch(request, env, ctx): Promise {
+ // Routed addressing
+ // Automatically routes HTTP requests and/or WebSocket connections to /agents/:agent/:name
+ // Best for: connecting React apps directly to Agents using useAgent from @cloudflare/agents/react
+ return (await routeAgentRequest(request, env)) || Response.json({ msg: 'no agent here' }, { status: 404 });
+
+ // Named addressing
+ // Best for: convenience method for creating or retrieving an agent by name/ID.
+ let namedAgent = getAgentByName(env.AIAgent, 'agent-456');
+ // Pass the incoming request straight to your Agent
+ let namedResp = (await namedAgent).fetch(request);
+ return namedResp;
+
+ // Durable Objects-style addressing
+ // Best for: controlling ID generation, associating IDs with your existing systems,
+ // and customizing when/how an Agent is created or invoked
+ const id = env.AIAgent.newUniqueId();
+ const agent = env.AIAgent.get(id);
+ // Pass the incoming request straight to your Agent
+ let resp = await agent.fetch(request);
+
+ // return Response.json({ hello: 'visit https://developers.cloudflare.com/agents for more' });
+ },
+} satisfies ExportedHandler;
+
+
+
+// client.js
+import { AgentClient } from "agents-sdk/client";
+
+const connection = new AgentClient({
+ agent: "dialogue-agent",
+ name: "insight-seeker",
+});
+
+connection.addEventListener("message", (event) => {
+ console.log("Received:", event.data);
+});
+
+connection.send(
+ JSON.stringify({
+ type: "inquiry",
+ content: "What patterns do you see?",
+ })
+);
+
+
+
+// app.tsx
+// React client hook for the agents-sdk
+import { useAgent } from "agents-sdk/react";
+import { useState } from "react";
+
+// useAgent client API
+function AgentInterface() {
+ const connection = useAgent({
+ agent: "dialogue-agent",
+ name: "insight-seeker",
+ onMessage: (message) => {
+ console.log("Understanding received:", message.data);
+ },
+ onOpen: () => console.log("Connection established"),
+ onClose: () => console.log("Connection closed"),
+ });
+
+ const inquire = () => {
+ connection.send(
+ JSON.stringify({
+ type: "inquiry",
+ content: "What insights have you gathered?",
+ })
+ );
+ };
+
+ return (
+
+
+
+ );
+}
+
+// State synchronization
+function StateInterface() {
+ const [state, setState] = useState({ counter: 0 });
+
+ const agent = useAgent({
+ agent: "thinking-agent",
+ onStateUpdate: (newState) => setState(newState),
+ });
+
+ const increment = () => {
+ agent.setState({ counter: state.counter + 1 });
+ };
+
+ return (
+
+ Count: {state.counter}
+
+
+ );
+}
+
+
+
+ {
+ "durable_objects": {
+ "bindings": [
+ {
+ "binding": "AIAgent",
+ "class_name": "AIAgent"
+ }
+ ]
+ },
+ "migrations": [
+ {
+ "tag": "v1",
+ // Mandatory for the Agent to store state
+ "new_sqlite_classes": ["AIAgent"]
+ }
+ ]
+}
+
+
+
+- Imports the `Agent` class from the `agents-sdk` package
+- Extends the `Agent` class and implements the methods exposed by the `Agent`, including `onRequest` for HTTP requests, or `onConnect` and `onMessage` for WebSockets.
+- Uses the `this.schedule` scheduling API to schedule future tasks.
+- Uses the `this.setState` API within the Agent for syncing state, and uses type parameters to ensure the state is typed.
+- Uses the `this.sql` as a lower-level query API.
+- For frontend applications, uses the optional `useAgent` hook to connect to the Agent via WebSockets
+
+
+
+import { OpenAI } from "openai";
+
+interface Env {
+ OPENAI_API_KEY: string;
+}
+
+// Define your JSON schema for a calendar event
+const CalendarEventSchema = {
+ type: 'object',
+ properties: {
+ name: { type: 'string' },
+ date: { type: 'string' },
+ participants: { type: 'array', items: { type: 'string' } },
+ },
+ required: ['name', 'date', 'participants']
+};
+
+export default {
+ async fetch(request: Request, env: Env) {
+ const client = new OpenAI({
+ apiKey: env.OPENAI_API_KEY,
+ // Optional: use AI Gateway to bring logs, evals & caching to your AI requests
+ // https://developers.cloudflare.com/ai-gateway/providers/openai/
+ // baseUrl: "https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/openai"
+ });
+
+ const response = await client.chat.completions.create({
+ model: 'gpt-4o-2024-08-06',
+ messages: [
+ { role: 'system', content: 'Extract the event information.' },
+ { role: 'user', content: 'Alice and Bob are going to a science fair on Friday.' },
+ ],
+ // Use the `response_format` option to request a structured JSON output
+ response_format: {
+ // Set json_schema and provide ra schema, or json_object and parse it yourself
+ type: 'json_schema',
+ schema: CalendarEventSchema, // provide a schema
+ },
+ });
+
+ // This will be of type CalendarEventSchema
+ const event = response.choices[0].message.parsed;
+
+ return Response.json({
+ "calendar_event": event,
+ })
+ }
+}
+
+
+// wrangler.jsonc
+{
+ "name": "app-name-goes-here", // name of the app
+ "main": "src/index.ts", // default file
+ "compatibility_date": "2025-02-11",
+ "compatibility_flags": ["nodejs_compat"], // Enable Node.js compatibility
+ "observability": {
+ // Enable logging by default
+ "enabled": true,
+ }
+}
+
+
+import { DurableObject } from "cloudflare:workers";
+
+interface Env {
+WEBSOCKET_HIBERNATION_SERVER: DurableObject;
+}
+
+// Durable Object
+export class WebSocketHibernationServer extends DurableObject {
+async fetch(request) {
+// Creates two ends of a WebSocket connection.
+const webSocketPair = new WebSocketPair();
+const [client, server] = Object.values(webSocketPair);
+
+ // Calling `acceptWebSocket()` informs the runtime that this WebSocket is to begin terminating
+ // request within the Durable Object. It has the effect of "accepting" the connection,
+ // and allowing the WebSocket to send and receive messages.
+ // Unlike `ws.accept()`, `state.acceptWebSocket(ws)` informs the Workers Runtime that the WebSocket
+ // is "hibernatable", so the runtime does not need to pin this Durable Object to memory while
+ // the connection is open. During periods of inactivity, the Durable Object can be evicted
+ // from memory, but the WebSocket connection will remain open. If at some later point the
+ // WebSocket receives a message, the runtime will recreate the Durable Object
+ // (run the `constructor`) and deliver the message to the appropriate handler.
+ this.ctx.acceptWebSocket(server);
+
+ return new Response(null, {
+ status: 101,
+ webSocket: client,
+ });
+
+ },
+
+ async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): void | Promise {
+ // Upon receiving a message from the client, reply with the same message,
+ // but will prefix the message with "[Durable Object]: " and return the
+ // total number of connections.
+ ws.send(
+ `[Durable Object] message: ${message}, connections: ${this.ctx.getWebSockets().length}`,
+ );
+ },
+
+ async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) void | Promise {
+ // If the client closes the connection, the runtime will invoke the webSocketClose() handler.
+ ws.close(code, "Durable Object is closing WebSocket");
+ },
+
+ async webSocketError(ws: WebSocket, error: unknown): void | Promise {
+ console.error("WebSocket error:", error);
+ ws.close(1011, "WebSocket error");
+ }
+
+}
+
+
+
+
+import { DurableObject } from "cloudflare:workers";
+
+interface Env {
+ALARM_EXAMPLE: DurableObject;
+}
+
+export default {
+ async fetch(request, env) {
+ let url = new URL(request.url);
+ let userId = url.searchParams.get("userId") || crypto.randomUUID();
+ let id = env.ALARM_EXAMPLE.idFromName(userId);
+ return await env.ALARM_EXAMPLE.get(id).fetch(request);
+ },
+};
+
+const SECONDS = 1000;
+
+export class AlarmExample extends DurableObject {
+constructor(ctx, env) {
+this.ctx = ctx;
+this.storage = ctx.storage;
+}
+async fetch(request) {
+// If there is no alarm currently set, set one for 10 seconds from now
+let currentAlarm = await this.storage.getAlarm();
+if (currentAlarm == null) {
+this.storage.setAlarm(Date.now() + 10 \_ SECONDS);
+}
+}
+async alarm(alarmInfo) {
+// The alarm handler will be invoked whenever an alarm fires.
+// You can use this to do work, read from the Storage API, make HTTP calls
+// and set future alarms to run using this.storage.setAlarm() from within this handler.
+if (alarmInfo?.retryCount != 0) {
+console.log("This alarm event has been attempted ${alarmInfo?.retryCount} times before.");
+}
+
+// Set a new alarm for 10 seconds from now before exiting the handler
+this.storage.setAlarm(Date.now() + 10 \_ SECONDS);
+}
+}
+
+
+
+
+// src/index.ts
+import { Hono } from 'hono'
+import { cors } from 'hono/cors'
+
+interface Env {
+AUTH_TOKENS: KVNamespace;
+}
+
+const app = new Hono<{ Bindings: Env }>()
+
+// Add CORS middleware
+app.use('\*', cors())
+
+app.get('/', async (c) => {
+try {
+// Get token from header or cookie
+const token = c.req.header('Authorization')?.slice(7) ||
+c.req.header('Cookie')?.match(/auth_token=([^;]+)/)?.[1];
+if (!token) {
+return c.json({
+authenticated: false,
+message: 'No authentication token provided'
+}, 403)
+}
+
+ // Check token in KV
+ const userData = await c.env.AUTH_TOKENS.get(token)
+
+ if (!userData) {
+ return c.json({
+ authenticated: false,
+ message: 'Invalid or expired token'
+ }, 403)
+ }
+
+ return c.json({
+ authenticated: true,
+ message: 'Authentication successful',
+ data: JSON.parse(userData)
+ })
+
+} catch (error) {
+console.error('Authentication error:', error)
+return c.json({
+authenticated: false,
+message: 'Internal server error'
+}, 500)
+}
+})
+
+export default app
+
+
+
+// src/producer.ts
+interface Env {
+ REQUEST_QUEUE: Queue;
+ UPSTREAM_API_URL: string;
+ UPSTREAM_API_KEY: string;
+}
+
+export default {
+async fetch(request: Request, env: Env) {
+const info = {
+timestamp: new Date().toISOString(),
+method: request.method,
+url: request.url,
+headers: Object.fromEntries(request.headers),
+};
+await env.REQUEST_QUEUE.send(info);
+
+return Response.json({
+message: 'Request logged',
+requestId: crypto.randomUUID()
+});
+
+},
+
+async queue(batch: MessageBatch, env: Env) {
+const requests = batch.messages.map(msg => msg.body);
+
+ const response = await fetch(env.UPSTREAM_API_URL, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${env.UPSTREAM_API_KEY}`
+ },
+ body: JSON.stringify({
+ timestamp: new Date().toISOString(),
+ batchSize: requests.length,
+ requests
+ })
+ });
+
+ if (!response.ok) {
+ throw new Error(`Upstream API error: ${response.status}`);
+ }
+
+}
+};
+
+
+
+
+// Postgres.js 3.4.5 or later is recommended
+import postgres from "postgres";
+
+export interface Env {
+// If you set another name in the Wrangler config file as the value for 'binding',
+// replace "HYPERDRIVE" with the variable name you defined.
+HYPERDRIVE: Hyperdrive;
+}
+
+export default {
+async fetch(request, env, ctx): Promise {
+console.log(JSON.stringify(env));
+// Create a database client that connects to your database via Hyperdrive.
+//
+// Hyperdrive generates a unique connection string you can pass to
+// supported drivers, including node-postgres, Postgres.js, and the many
+// ORMs and query builders that use these drivers.
+const sql = postgres(env.HYPERDRIVE.connectionString)
+
+ try {
+ // Test query
+ const results = await sql`SELECT * FROM pg_tables`;
+
+ // Clean up the client, ensuring we don't kill the worker before that is
+ // completed.
+ ctx.waitUntil(sql.end());
+
+ // Return result rows as JSON
+ return Response.json(results);
+ } catch (e) {
+ console.error(e);
+ return Response.json(
+ { error: e instanceof Error ? e.message : e },
+ { status: 500 },
+ );
+ }
+
+},
+} satisfies ExportedHandler;
+
+
+
+
+import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
+
+type Env = {
+// Add your bindings here, e.g. Workers KV, D1, Workers AI, etc.
+MY_WORKFLOW: Workflow;
+};
+
+// User-defined params passed to your workflow
+type Params = {
+email: string;
+metadata: Record;
+};
+
+export class MyWorkflow extends WorkflowEntrypoint {
+async run(event: WorkflowEvent, step: WorkflowStep) {
+// Can access bindings on `this.env`
+// Can access params on `event.payload`
+const files = await step.do('my first step', async () => {
+// Fetch a list of files from $SOME_SERVICE
+return {
+files: [
+'doc_7392_rev3.pdf',
+'report_x29_final.pdf',
+'memo_2024_05_12.pdf',
+'file_089_update.pdf',
+'proj_alpha_v2.pdf',
+'data_analysis_q2.pdf',
+'notes_meeting_52.pdf',
+'summary_fy24_draft.pdf',
+],
+};
+});
+
+ const apiResponse = await step.do('some other step', async () => {
+ let resp = await fetch('https://api.cloudflare.com/client/v4/ips');
+ return await resp.json();
+ });
+
+ await step.sleep('wait on something', '1 minute');
+
+ await step.do(
+ 'make a call to write that could maybe, just might, fail',
+ // Define a retry strategy
+ {
+ retries: {
+ limit: 5,
+ delay: '5 second',
+ backoff: 'exponential',
+ },
+ timeout: '15 minutes',
+ },
+ async () => {
+ // Do stuff here, with access to the state from our previous steps
+ if (Math.random() > 0.5) {
+ throw new Error('API call to $STORAGE_SYSTEM failed');
+ }
+ },
+ );
+
+}
+}
+
+export default {
+async fetch(req: Request, env: Env): Promise {
+let url = new URL(req.url);
+
+ if (url.pathname.startsWith('/favicon')) {
+ return Response.json({}, { status: 404 });
+ }
+
+ // Get the status of an existing instance, if provided
+ let id = url.searchParams.get('instanceId');
+ if (id) {
+ let instance = await env.MY_WORKFLOW.get(id);
+ return Response.json({
+ status: await instance.status(),
+ });
+ }
+
+ const data = await req.json()
+
+ // Spawn a new instance and return the ID and status
+ let instance = await env.MY_WORKFLOW.create({
+ // Define an ID for the Workflow instance
+ id: crypto.randomUUID(),
+ // Pass data to the Workflow instance
+ // Available on the WorkflowEvent
+ params: data,
+ });
+
+ return Response.json({
+ id: instance.id,
+ details: await instance.status(),
+ });
+
+},
+};
+
+
+
+
+interface Env {
+ USER_EVENTS: AnalyticsEngineDataset;
+}
+
+export default {
+async fetch(req: Request, env: Env): Promise {
+let url = new URL(req.url);
+let path = url.pathname;
+let userId = url.searchParams.get("userId");
+
+ // Write a datapoint for this visit, associating the data with
+ // the userId as our Analytics Engine 'index'
+ env.USER_EVENTS.writeDataPoint({
+ // Write metrics data: counters, gauges or latency statistics
+ doubles: [],
+ // Write text labels - URLs, app names, event_names, etc
+ blobs: [path],
+ // Provide an index that groups your data correctly.
+ indexes: [userId],
+ });
+
+ return Response.json({
+ hello: "world",
+ });
+ ,
+
+};
+
+
+
+
+import puppeteer from "@cloudflare/puppeteer";
+
+interface Env {
+ BROWSER_RENDERING: Fetcher;
+}
+
+export default {
+ async fetch(request, env): Promise {
+ const { searchParams } = new URL(request.url);
+ let url = searchParams.get("url");
+
+ if (url) {
+ url = new URL(url).toString(); // normalize
+ const browser = await puppeteer.launch(env.MYBROWSER);
+ const page = await browser.newPage();
+ await page.goto(url);
+ // Parse the page content
+ const content = await page.content();
+ // Find text within the page content
+ const text = await page.$eval("body", (el) => el.textContent);
+ // Do something with the text
+ // e.g. log it to the console, write it to KV, or store it in a database.
+ console.log(text);
+
+ // Ensure we close the browser session
+ await browser.close();
+
+ return Response.json({
+ bodyText: text,
+ })
+ } else {
+ return Response.json({
+ error: "Please add an ?url=https://example.com/ parameter"
+ }, { status: 400 })
+ }
+ },
+} satisfies ExportedHandler;
+
+
+
+// src/index.ts
+
+interface Env {
+ ASSETS: Fetcher;
+}
+
+export default {
+ fetch(request, env) {
+ const url = new URL(request.url);
+
+ if (url.pathname.startsWith("/api/")) {
+ return Response.json({
+ name: "Cloudflare",
+ });
+ }
+
+ return env.ASSETS.fetch(request);
+ },
+} satisfies ExportedHandler;
+
+
+
+Build an AI Agent on Cloudflare Workers, using the agents-sdk, and the state management and syncing APIs built into the agents-sdk.
+
+
+
+// src/index.ts
+import { Agent, AgentNamespace, Connection, ConnectionContext, getAgentByName, routeAgentRequest, WSMessage } from 'agents-sdk';
+import { OpenAI } from "openai";
+
+interface Env {
+ AIAgent: AgentNamespace;
+ OPENAI_API_KEY: string;
+}
+
+export class AIAgent extends Agent {
+ // Handle HTTP requests with your Agent
+ async onRequest(request) {
+ // Connect with AI capabilities
+ const ai = new OpenAI({
+ apiKey: this.env.OPENAI_API_KEY,
+ });
+
+ // Process and understand
+ const response = await ai.chat.completions.create({
+ model: "gpt-4",
+ messages: [{ role: "user", content: await request.text() }],
+ });
+
+ return new Response(response.choices[0].message.content);
+ }
+
+ async processTask(task) {
+ await this.understand(task);
+ await this.act();
+ await this.reflect();
+ }
+
+ // Handle WebSockets
+ async onConnect(connection: Connection) {
+ await this.initiate(connection);
+ connection.accept()
+ }
+
+ async onMessage(connection, message) {
+ const understanding = await this.comprehend(message);
+ await this.respond(connection, understanding);
+ }
+
+ async evolve(newInsight) {
+ this.setState({
+ ...this.state,
+ insights: [...(this.state.insights || []), newInsight],
+ understanding: this.state.understanding + 1,
+ });
+ }
+
+ onStateUpdate(state, source) {
+ console.log("Understanding deepened:", {
+ newState: state,
+ origin: source,
+ });
+ }
+
+ // Scheduling APIs
+ // An Agent can schedule tasks to be run in the future by calling this.schedule(when, callback, data), where when can be a delay, a Date, or a cron string; callback the function name to call, and data is an object of data to pass to the function.
+ //
+ // Scheduled tasks can do anything a request or message from a user can: make requests, query databases, send emails, read+write state: scheduled tasks can invoke any regular method on your Agent.
+ async scheduleExamples() {
+ // schedule a task to run in 10 seconds
+ let task = await this.schedule(10, "someTask", { message: "hello" });
+
+ // schedule a task to run at a specific date
+ let task = await this.schedule(new Date("2025-01-01"), "someTask", {});
+
+ // schedule a task to run every 10 seconds
+ let { id } = await this.schedule("*/10 * * * *", "someTask", { message: "hello" });
+
+ // schedule a task to run every 10 seconds, but only on Mondays
+ let task = await this.schedule("0 0 * * 1", "someTask", { message: "hello" });
+
+ // cancel a scheduled task
+ this.cancelSchedule(task.id);
+
+ // Get a specific schedule by ID
+ // Returns undefined if the task does not exist
+ let task = await this.getSchedule(task.id)
+
+ // Get all scheduled tasks
+ // Returns an array of Schedule objects
+ let tasks = this.getSchedules();
+
+ // Cancel a task by its ID
+ // Returns true if the task was cancelled, false if it did not exist
+ await this.cancelSchedule(task.id);
+
+ // Filter for specific tasks
+ // e.g. all tasks starting in the next hour
+ let tasks = this.getSchedules({
+ timeRange: {
+ start: new Date(Date.now()),
+ end: new Date(Date.now() + 60 * 60 * 1000),
+ }
+ });
+ }
+
+ async someTask(data) {
+ await this.callReasoningModel(data.message);
+ }
+
+ // Use the this.sql API within the Agent to access the underlying SQLite database
+ async callReasoningModel(prompt: Prompt) {
+ interface Prompt {
+ userId: string;
+ user: string;
+ system: string;
+ metadata: Record;
+ }
+
+ interface History {
+ timestamp: Date;
+ entry: string;
+ }
+
+ let result = this.sql`SELECT * FROM history WHERE user = ${prompt.userId} ORDER BY timestamp DESC LIMIT 1000`;
+ let context = [];
+ for await (const row of result) {
+ context.push(row.entry);
+ }
+
+ const client = new OpenAI({
+ apiKey: this.env.OPENAI_API_KEY,
+ });
+
+ // Combine user history with the current prompt
+ const systemPrompt = prompt.system || 'You are a helpful assistant.';
+ const userPrompt = `${prompt.user}\n\nUser history:\n${context.join('\n')}`;
+
+ try {
+ const completion = await client.chat.completions.create({
+ model: this.env.MODEL || 'o3-mini',
+ messages: [
+ { role: 'system', content: systemPrompt },
+ { role: 'user', content: userPrompt },
+ ],
+ temperature: 0.7,
+ max_tokens: 1000,
+ });
+
+ // Store the response in history
+ this
+ .sql`INSERT INTO history (timestamp, user, entry) VALUES (${new Date()}, ${prompt.userId}, ${completion.choices[0].message.content})`;
+
+ return completion.choices[0].message.content;
+ } catch (error) {
+ console.error('Error calling reasoning model:', error);
+ throw error;
+ }
+ }
+
+ // Use the SQL API with a type parameter
+ async queryUser(userId: string) {
+ type User = {
+ id: string;
+ name: string;
+ email: string;
+ };
+ // Supply the type paramter to the query when calling this.sql
+ // This assumes the results returns one or more User rows with "id", "name", and "email" columns
+ // You do not need to specify an array type (`User[]` or `Array`) as `this.sql` will always return an array of the specified type.
+ const user = await this.sql`SELECT * FROM users WHERE id = ${userId}`;
+ return user
+ }
+
+ // Run and orchestrate Workflows from Agents
+ async runWorkflow(data) {
+ let instance = await env.MY_WORKFLOW.create({
+ id: data.id,
+ params: data,
+ })
+
+ // Schedule another task that checks the Workflow status every 5 minutes...
+ await this.schedule("*/5 * * * *", "checkWorkflowStatus", { id: instance.id });
+ }
+}
+
+export default {
+ async fetch(request, env, ctx): Promise {
+ // Routed addressing
+ // Automatically routes HTTP requests and/or WebSocket connections to /agents/:agent/:name
+ // Best for: connecting React apps directly to Agents using useAgent from @cloudflare/agents/react
+ return (await routeAgentRequest(request, env)) || Response.json({ msg: 'no agent here' }, { status: 404 });
+
+ // Named addressing
+ // Best for: convenience method for creating or retrieving an agent by name/ID.
+ let namedAgent = getAgentByName(env.AIAgent, 'agent-456');
+ // Pass the incoming request straight to your Agent
+ let namedResp = (await namedAgent).fetch(request);
+ return namedResp;
+
+ // Durable Objects-style addressing
+ // Best for: controlling ID generation, associating IDs with your existing systems,
+ // and customizing when/how an Agent is created or invoked
+ const id = env.AIAgent.newUniqueId();
+ const agent = env.AIAgent.get(id);
+ // Pass the incoming request straight to your Agent
+ let resp = await agent.fetch(request);
+
+ // return Response.json({ hello: 'visit https://developers.cloudflare.com/agents for more' });
+ },
+} satisfies ExportedHandler;
+
+
+
+// client.js
+import { AgentClient } from "agents-sdk/client";
+
+const connection = new AgentClient({
+ agent: "dialogue-agent",
+ name: "insight-seeker",
+});
+
+connection.addEventListener("message", (event) => {
+ console.log("Received:", event.data);
+});
+
+connection.send(
+ JSON.stringify({
+ type: "inquiry",
+ content: "What patterns do you see?",
+ })
+);
+
+
+
+// app.tsx
+// React client hook for the agents-sdk
+import { useAgent } from "agents-sdk/react";
+import { useState } from "react";
+
+// useAgent client API
+function AgentInterface() {
+ const connection = useAgent({
+ agent: "dialogue-agent",
+ name: "insight-seeker",
+ onMessage: (message) => {
+ console.log("Understanding received:", message.data);
+ },
+ onOpen: () => console.log("Connection established"),
+ onClose: () => console.log("Connection closed"),
+ });
+
+ const inquire = () => {
+ connection.send(
+ JSON.stringify({
+ type: "inquiry",
+ content: "What insights have you gathered?",
+ })
+ );
+ };
+
+ return (
+
+
+
+ );
+}
+
+// State synchronization
+function StateInterface() {
+ const [state, setState] = useState({ counter: 0 });
+
+ const agent = useAgent({
+ agent: "thinking-agent",
+ onStateUpdate: (newState) => setState(newState),
+ });
+
+ const increment = () => {
+ agent.setState({ counter: state.counter + 1 });
+ };
+
+ return (
+
+ Count: {state.counter}
+
+
+ );
+}
+
+
+
+ {
+ "durable_objects": {
+ "bindings": [
+ {
+ "binding": "AIAgent",
+ "class_name": "AIAgent"
+ }
+ ]
+ },
+ "migrations": [
+ {
+ "tag": "v1",
+ // Mandatory for the Agent to store state
+ "new_sqlite_classes": ["AIAgent"]
+ }
+ ]
+}
+
+
+
+- Imports the `Agent` class from the `agents-sdk` package
+- Extends the `Agent` class and implements the methods exposed by the `Agent`, including `onRequest` for HTTP requests, or `onConnect` and `onMessage` for WebSockets.
+- Uses the `this.schedule` scheduling API to schedule future tasks.
+- Uses the `this.setState` API within the Agent for syncing state, and uses type parameters to ensure the state is typed.
+- Uses the `this.sql` as a lower-level query API.
+- For frontend applications, uses the optional `useAgent` hook to connect to the Agent via WebSockets
+
+
+
+import { OpenAI } from "openai";
+
+interface Env {
+ OPENAI_API_KEY: string;
+}
+
+// Define your JSON schema for a calendar event
+const CalendarEventSchema = {
+ type: 'object',
+ properties: {
+ name: { type: 'string' },
+ date: { type: 'string' },
+ participants: { type: 'array', items: { type: 'string' } },
+ },
+ required: ['name', 'date', 'participants']
+};
+
+export default {
+ async fetch(request: Request, env: Env) {
+ const client = new OpenAI({
+ apiKey: env.OPENAI_API_KEY,
+ // Optional: use AI Gateway to bring logs, evals & caching to your AI requests
+ // https://developers.cloudflare.com/ai-gateway/providers/openai/
+ // baseUrl: "https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/openai"
+ });
+
+ const response = await client.chat.completions.create({
+ model: 'gpt-4o-2024-08-06',
+ messages: [
+ { role: 'system', content: 'Extract the event information.' },
+ { role: 'user', content: 'Alice and Bob are going to a science fair on Friday.' },
+ ],
+ // Use the `response_format` option to request a structured JSON output
+ response_format: {
+ // Set json_schema and provide ra schema, or json_object and parse it yourself
+ type: 'json_schema',
+ schema: CalendarEventSchema, // provide a schema
+ },
+ });
+
+ // This will be of type CalendarEventSchema
+ const event = response.choices[0].message.parsed;
+
+ return Response.json({
+ "calendar_event": event,
+ })
+ }
+}
+
+