Skip to content

Commit 58df438

Browse files
committed
Added durable object support, with migrations, workers_dev subdomain publishing and Workers Logs for observability
1 parent 5cb5771 commit 58df438

File tree

3 files changed

+218
-19
lines changed

3 files changed

+218
-19
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"devDependencies": {
3838
"@cloudflare/workers-types": "^4.20241112.0",
3939
"@types/node": "^22.10.0",
40+
"@types/which": "^3.0.4",
4041
"prettier": "^3.4.1",
4142
"shx": "^0.3.4",
4243
"tsup": "^8.3.5",

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/workers.ts

Lines changed: 209 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,6 @@ interface CloudflareWorkerResponse {
3636
messages: any[]
3737
}
3838

39-
// Interface for Worker bindings
40-
interface WorkerBinding {
41-
type: 'kv_namespace' | 'r2_bucket' | 'd1_database' | 'service' | 'analytics_engine' | 'queue'
42-
name: string
43-
namespace_id?: string // For KV
44-
bucket_name?: string // For R2
45-
database_id?: string // For D1
46-
service?: string // For service bindings
47-
dataset?: string // For analytics
48-
queue_name?: string // For queues
49-
}
50-
5139
// New Worker Tool definitions
5240
const WORKER_LIST_TOOL: Tool = {
5341
name: 'worker_list',
@@ -75,7 +63,7 @@ const WORKER_GET_TOOL: Tool = {
7563
// Update the WORKER_PUT_TOOL definition
7664
const WORKER_PUT_TOOL: Tool = {
7765
name: 'worker_put',
78-
description: 'Create or update a Worker script using Module Syntax with optional bindings and compatibility settings',
66+
description: 'Create or update a Worker script with optional bindings and compatibility settings',
7967
inputSchema: {
8068
type: 'object',
8169
properties: {
@@ -95,8 +83,17 @@ const WORKER_PUT_TOOL: Tool = {
9583
properties: {
9684
type: {
9785
type: 'string',
98-
description: 'Type of binding (kv_namespace, r2_bucket, d1_database, service, analytics_engine, queue)',
99-
enum: ['kv_namespace', 'r2_bucket', 'd1_database', 'service', 'analytics_engine', 'queue'],
86+
description:
87+
'Type of binding (kv_namespace, r2_bucket, d1_database, service, analytics_engine, queue, durable_object)',
88+
enum: [
89+
'kv_namespace',
90+
'r2_bucket',
91+
'd1_database',
92+
'service',
93+
'analytics_engine',
94+
'queue',
95+
'durable_object_namespace',
96+
],
10097
},
10198
name: {
10299
type: 'string',
@@ -126,10 +123,59 @@ const WORKER_PUT_TOOL: Tool = {
126123
type: 'string',
127124
description: 'Name of the queue (required for queue type)',
128125
},
126+
class_name: {
127+
type: 'string',
128+
description: 'Name of the Durable Object class (required for durable_object_namespace type)',
129+
},
130+
script_name: {
131+
type: 'string',
132+
description: 'Optional script name for external Durable Object bindings',
133+
},
129134
},
130135
required: ['type', 'name'],
131136
},
132137
},
138+
migrations: {
139+
type: 'object',
140+
description:
141+
'Optional migrations object which describes the set of new/changed/deleted Durable Objects to apply when deploying this worker e.g. adding a new Durable Object for the first time requires an entry in the "new_sqlite_classes" or "new_classes" property.',
142+
items: {
143+
properties: {
144+
new_tag: {
145+
type: 'string',
146+
description: 'The current version after applying this migration (e.g., "v1", "v2")',
147+
},
148+
new_classes: {
149+
type: 'array',
150+
items: { type: 'string' },
151+
description: 'The new Durable Objects using legacy storage being added',
152+
},
153+
new_sqlite_classes: {
154+
type: 'array',
155+
items: { type: 'string' },
156+
description: 'The new Durable Objects using the new, improved SQLite storage being added',
157+
},
158+
renamed_classes: {
159+
type: 'array',
160+
items: {
161+
type: 'object',
162+
properties: {
163+
from: { type: 'string' },
164+
to: { type: 'string' },
165+
},
166+
required: ['from', 'to'],
167+
},
168+
description: 'The Durable Objects being renamed',
169+
},
170+
deleted_classes: {
171+
type: 'array',
172+
items: { type: 'string' },
173+
description: 'The Durable Objects being removed',
174+
},
175+
},
176+
required: ['tag'],
177+
},
178+
},
133179
compatibility_date: {
134180
type: 'string',
135181
description: 'Optional compatibility date for the Worker (e.g., "2024-01-01")',
@@ -141,6 +187,15 @@ const WORKER_PUT_TOOL: Tool = {
141187
type: 'string',
142188
},
143189
},
190+
skip_workers_dev: {
191+
type: 'boolean',
192+
description: `Do not deploy the Worker on your workers.dev subdomain. Should be set to true if the user already has a domain name, or doesn't want this worker to be publicly accessible..`,
193+
},
194+
no_observability: {
195+
type: 'boolean',
196+
description:
197+
'Disable Worker Logs for this worker, which automatically ingests logs emitted from Cloudflare Workers and lets you filter, and analyze them in the Cloudflare dashboard.',
198+
},
144199
},
145200
required: ['name', 'script'],
146201
},
@@ -203,13 +258,105 @@ export async function handleWorkerGet(name: string) {
203258
return data
204259
}
205260

261+
export interface Observability {
262+
/** If observability is enabled for this Worker */
263+
enabled: boolean
264+
/** The sampling rate */
265+
head_sampling_rate?: number
266+
}
267+
268+
interface CfDurableObjectMigrations {
269+
tag: string
270+
new_classes?: string[]
271+
new_sqlite_classes?: string[]
272+
renamed_classes?: {
273+
from: string
274+
to: string
275+
}[]
276+
deleted_classes?: string[]
277+
}
278+
279+
interface DurableObjectBinding {
280+
type: 'durable_object_namespace'
281+
name: string
282+
class_name: string
283+
script_name?: string // Optional, defaults to the current worker
284+
}
285+
286+
// Update WorkerBinding to include Durable Objects
287+
type WorkerMetadataBinding =
288+
| {
289+
type: 'kv_namespace'
290+
name: string
291+
namespace_id: string
292+
}
293+
| {
294+
type: 'r2_bucket'
295+
name: string
296+
bucket_name: string
297+
}
298+
| {
299+
type: 'd1_database'
300+
name: string
301+
database_id: string
302+
}
303+
| {
304+
type: 'service'
305+
name: string
306+
service: string
307+
}
308+
| {
309+
type: 'analytics_engine'
310+
name: string
311+
dataset: string
312+
}
313+
| {
314+
type: 'queue'
315+
name: string
316+
queue_name: string
317+
}
318+
| DurableObjectBinding
319+
320+
type WorkerMetadataPut = {
321+
/** The name of the entry point module. Only exists when the worker is in the ES module format */
322+
main_module?: string
323+
/** The name of the entry point module. Only exists when the worker is in the service-worker format */
324+
// body_part?: string;
325+
compatibility_date?: string
326+
compatibility_flags?: string[]
327+
// usage_model?: "bundled" | "unbound";
328+
migrations?: CfDurableObjectMigrations
329+
// capnp_schema?: string;
330+
bindings: WorkerMetadataBinding[]
331+
// keep_bindings?: (
332+
// | WorkerMetadataBinding["type"]
333+
// | "secret_text"
334+
// | "secret_key"
335+
// )[];
336+
// logpush?: boolean;
337+
// placement?: CfPlacement;
338+
// tail_consumers?: CfTailConsumer[];
339+
// limits?: CfUserLimits;
340+
341+
// assets?: {
342+
// jwt: string;
343+
// config?: AssetConfig;
344+
// };
345+
observability?: Observability | undefined
346+
// Allow unsafe.metadata to add arbitrary properties at runtime
347+
[key: string]: unknown
348+
}
349+
206350
// Update the handleWorkerPut function
207351
export async function handleWorkerPut(
208352
name: string,
209353
script: string,
210-
bindings?: WorkerBinding[],
354+
bindings?: WorkerMetadataBinding[],
211355
compatibility_date?: string,
212356
compatibility_flags?: string[],
357+
migrations?: CfDurableObjectMigrations,
358+
workers_dev?: boolean,
359+
observability?: boolean,
213360
) {
214361
log('Executing worker_put for script:', name)
215362
const url = `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/workers/scripts/${name}`
@@ -219,6 +366,8 @@ export async function handleWorkerPut(
219366
bindings: bindings || [],
220367
compatibility_date: compatibility_date || '2024-01-01',
221368
compatibility_flags: compatibility_flags || [],
369+
...(migrations ? { migrations } : {}),
370+
observability: observability ? { enabled: true } : undefined,
222371
}
223372

224373
// Create form data with metadata and script
@@ -247,6 +396,26 @@ export async function handleWorkerPut(
247396
throw new Error(`Failed to put worker: ${error}`)
248397
}
249398

399+
if (workers_dev) {
400+
const response = await fetch(url + '/subdomain', {
401+
method: 'POST',
402+
body: JSON.stringify({
403+
enabled: true,
404+
}),
405+
headers: {
406+
Authorization: `Bearer ${config.apiToken}`,
407+
'Content-Type': 'application/json',
408+
},
409+
})
410+
log('Subdomain post response status:', response.status)
411+
412+
if (!response.ok) {
413+
const error = await response.text()
414+
log('Worker subdomain POST error:', error)
415+
throw new Error(`Failed to update subdomain: ${error}`)
416+
}
417+
}
418+
250419
return 'Success'
251420
}
252421

@@ -301,14 +470,35 @@ export const WORKERS_HANDLERS: ToolHandlers = {
301470
},
302471

303472
worker_put: async (request) => {
304-
const { name, script, bindings, compatibility_date, compatibility_flags } = request.params.arguments as {
473+
const {
474+
name,
475+
script,
476+
bindings,
477+
compatibility_date,
478+
compatibility_flags,
479+
migrations,
480+
skip_workers_dev,
481+
no_observability,
482+
} = request.params.arguments as {
305483
name: string
306484
script: string
307-
bindings?: WorkerBinding[]
485+
bindings?: WorkerMetadataBinding[]
308486
compatibility_date?: string
309487
compatibility_flags?: string[]
488+
migrations?: CfDurableObjectMigrations
489+
skip_workers_dev: boolean
490+
no_observability: boolean
310491
}
311-
await handleWorkerPut(name, script, bindings, compatibility_date, compatibility_flags)
492+
await handleWorkerPut(
493+
name,
494+
script,
495+
bindings,
496+
compatibility_date,
497+
compatibility_flags,
498+
migrations,
499+
!skip_workers_dev,
500+
!no_observability,
501+
)
312502
return {
313503
toolResult: {
314504
content: [

0 commit comments

Comments
 (0)