@aigne/afs-cloudflare 1.11.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE.md +26 -0
  2. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
  4. package/dist/cache.cjs +37 -0
  5. package/dist/cache.mjs +37 -0
  6. package/dist/cache.mjs.map +1 -0
  7. package/dist/client.cjs +53 -0
  8. package/dist/client.mjs +53 -0
  9. package/dist/client.mjs.map +1 -0
  10. package/dist/cloudflare-afs.cjs +2724 -0
  11. package/dist/cloudflare-afs.d.cts +387 -0
  12. package/dist/cloudflare-afs.d.cts.map +1 -0
  13. package/dist/cloudflare-afs.d.mts +387 -0
  14. package/dist/cloudflare-afs.d.mts.map +1 -0
  15. package/dist/cloudflare-afs.mjs +2725 -0
  16. package/dist/cloudflare-afs.mjs.map +1 -0
  17. package/dist/errors.cjs +85 -0
  18. package/dist/errors.d.cts +26 -0
  19. package/dist/errors.d.cts.map +1 -0
  20. package/dist/errors.d.mts +26 -0
  21. package/dist/errors.d.mts.map +1 -0
  22. package/dist/errors.mjs +82 -0
  23. package/dist/errors.mjs.map +1 -0
  24. package/dist/index.cjs +22 -0
  25. package/dist/index.d.cts +5 -0
  26. package/dist/index.d.mts +5 -0
  27. package/dist/index.mjs +6 -0
  28. package/dist/platform-ref.cjs +20 -0
  29. package/dist/platform-ref.d.cts +11 -0
  30. package/dist/platform-ref.d.cts.map +1 -0
  31. package/dist/platform-ref.d.mts +11 -0
  32. package/dist/platform-ref.d.mts.map +1 -0
  33. package/dist/platform-ref.mjs +17 -0
  34. package/dist/platform-ref.mjs.map +1 -0
  35. package/dist/types.cjs +52 -0
  36. package/dist/types.d.cts +63 -0
  37. package/dist/types.d.cts.map +1 -0
  38. package/dist/types.d.mts +63 -0
  39. package/dist/types.d.mts.map +1 -0
  40. package/dist/types.mjs +46 -0
  41. package/dist/types.mjs.map +1 -0
  42. package/package.json +61 -0
@@ -0,0 +1,2725 @@
1
+ import { CloudflareCache } from "./cache.mjs";
2
+ import { createCloudflareClient } from "./client.mjs";
3
+ import { confirmationRequired, mapCloudflareError } from "./errors.mjs";
4
+ import { generateKVPlatformRef, generatePagesPlatformRef, generateWorkerPlatformRef } from "./platform-ref.mjs";
5
+ import { AFSCloudflareConfigSchema, KINDS, normalizeConfig } from "./types.mjs";
6
+ import { __decorate } from "./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs";
7
+ import { AFSBaseProvider, AFSNotFoundError, AFSReadonlyError, Actions, Delete, Explain, List, Meta, Read, Search, Stat, Write } from "@aigne/afs";
8
+ import { zodParse } from "@aigne/afs/utils/zod";
9
+ import { minimatch } from "minimatch";
10
+ import { joinURL } from "ufo";
11
+
12
+ //#region src/cloudflare-afs.ts
13
+ function camelize(obj) {
14
+ const result = {};
15
+ for (const [key, value] of Object.entries(obj)) {
16
+ const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
17
+ if (value && typeof value === "object" && !Array.isArray(value)) result[camelKey] = camelize(value);
18
+ else result[camelKey] = value;
19
+ }
20
+ return result;
21
+ }
22
+ var AFSCloudflare = class AFSCloudflare extends AFSBaseProvider {
23
+ name;
24
+ description;
25
+ accessMode;
26
+ config;
27
+ cfClient;
28
+ cache;
29
+ destroyed = false;
30
+ constructor(config) {
31
+ super();
32
+ this.config = normalizeConfig(AFSCloudflareConfigSchema.parse(config));
33
+ this.name = this.config.name;
34
+ this.description = this.config.description;
35
+ this.accessMode = this.config.accessMode;
36
+ this.cfClient = createCloudflareClient(this.config);
37
+ this.cache = new CloudflareCache(this.config.cacheTtl, this.config.cacheDisabled);
38
+ }
39
+ static schema() {
40
+ return AFSCloudflareConfigSchema;
41
+ }
42
+ static async load({ basePath, config } = {}) {
43
+ return new AFSCloudflare(zodParse(AFSCloudflareConfigSchema, camelize(config ?? {}), { prefix: basePath }));
44
+ }
45
+ setClient(client) {
46
+ this.cfClient.setClient(client);
47
+ }
48
+ get client() {
49
+ return this.cfClient.getClient();
50
+ }
51
+ async accountId() {
52
+ return this.cfClient.getAccountId();
53
+ }
54
+ ensureNotDestroyed() {
55
+ if (this.destroyed) throw new Error("Provider has been destroyed");
56
+ }
57
+ requireReadwrite(path) {
58
+ if (this.config.accessMode !== "readwrite") throw new AFSReadonlyError(`Read-only mode: Cannot perform write operation at ${path}`);
59
+ }
60
+ async resolveKVNamespaceId(title) {
61
+ const cacheKey = `kv:title:${title}`;
62
+ const cached = this.cache.get(cacheKey);
63
+ if (cached) return cached;
64
+ const accountId = await this.accountId();
65
+ const namespaces = this.client.kv.namespaces.list({ account_id: accountId });
66
+ for await (const ns of namespaces) if (ns.title === title) {
67
+ const id = ns.id;
68
+ this.cache.set(cacheKey, id);
69
+ return id;
70
+ }
71
+ throw new AFSNotFoundError(`KV namespace not found: ${title}`);
72
+ }
73
+ async collect(iter) {
74
+ const items = [];
75
+ for await (const item of iter) items.push(item);
76
+ return items;
77
+ }
78
+ validateAfsPath(path) {
79
+ if (!path.startsWith("/") && !path.startsWith("$afs")) throw new Error(`Invalid path: "${path}". Must be an AFS path (starting with / or $afs)`);
80
+ if (/^\/[a-zA-Z]:\//.test(path) || path.includes("..")) throw new Error(`Invalid path: "${path}". System filesystem paths are not allowed. Use an AFS mount path.`);
81
+ }
82
+ getAfsFromContext(ctx) {
83
+ const afs = ctx.options?.context?.afs;
84
+ if (!afs) throw new Error("AFS context not available. Directory operations require the AFS instance in exec context.");
85
+ return afs;
86
+ }
87
+ async readDirectoryFiles(afs, basePath) {
88
+ const files = /* @__PURE__ */ new Map();
89
+ const { data: entries } = await afs.list(basePath, { recursive: true });
90
+ for (const entry of entries) {
91
+ if (entry.meta?.kind === "directory" || entry.meta?.childrenCount !== void 0) continue;
92
+ try {
93
+ const { data } = await afs.read(entry.path);
94
+ if (data?.content !== void 0 && data?.content !== null) {
95
+ const relPath = entry.path.slice(basePath.length).replace(/^\//, "");
96
+ files.set(relPath, typeof data.content === "string" ? data.content : JSON.stringify(data.content));
97
+ }
98
+ } catch {}
99
+ }
100
+ if (files.size === 0) throw new Error(`No files found in directory: ${basePath}`);
101
+ return files;
102
+ }
103
+ detectMainModule(files) {
104
+ for (const candidate of [
105
+ "index.js",
106
+ "index.mjs",
107
+ "index.ts",
108
+ "worker.js",
109
+ "worker.mjs",
110
+ "src/index.js",
111
+ "src/index.ts"
112
+ ]) if (files.has(candidate)) return candidate;
113
+ for (const name of files.keys()) if (name.endsWith(".js") || name.endsWith(".mjs") || name.endsWith(".ts")) return name;
114
+ throw new Error("No JavaScript/TypeScript main module found in directory");
115
+ }
116
+ getContentType(filename) {
117
+ if (filename.endsWith(".html") || filename.endsWith(".htm")) return "text/html";
118
+ if (filename.endsWith(".css")) return "text/css";
119
+ if (filename.endsWith(".js") || filename.endsWith(".mjs")) return "application/javascript+module";
120
+ if (filename.endsWith(".ts")) return "application/typescript";
121
+ if (filename.endsWith(".json")) return "application/json";
122
+ if (filename.endsWith(".svg")) return "image/svg+xml";
123
+ if (filename.endsWith(".png")) return "image/png";
124
+ if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) return "image/jpeg";
125
+ if (filename.endsWith(".gif")) return "image/gif";
126
+ if (filename.endsWith(".ico")) return "image/x-icon";
127
+ if (filename.endsWith(".woff2")) return "font/woff2";
128
+ if (filename.endsWith(".woff")) return "font/woff";
129
+ if (filename.endsWith(".wasm")) return "application/wasm";
130
+ if (filename.endsWith(".xml")) return "application/xml";
131
+ if (filename.endsWith(".txt")) return "text/plain";
132
+ return "application/octet-stream";
133
+ }
134
+ async listRoot(_ctx) {
135
+ this.ensureNotDestroyed();
136
+ const accountId = await this.accountId();
137
+ const [workers, kvNamespaces, pagesProjects, zones] = await Promise.all([
138
+ this.collect(this.client.workers.scripts.list({ account_id: accountId })),
139
+ this.collect(this.client.kv.namespaces.list({ account_id: accountId })),
140
+ this.collect(this.client.pages.projects.list({ account_id: accountId })),
141
+ this.collect(this.client.zones.list())
142
+ ]);
143
+ return { data: [
144
+ {
145
+ id: "WORLD.md",
146
+ path: "/WORLD.md",
147
+ meta: {
148
+ kind: KINDS.NODE,
149
+ kinds: [KINDS.NODE],
150
+ contentType: "text/markdown"
151
+ }
152
+ },
153
+ {
154
+ id: "workers",
155
+ path: "/workers",
156
+ meta: {
157
+ kind: KINDS.NODE,
158
+ kinds: [KINDS.NODE],
159
+ childrenCount: workers.length
160
+ }
161
+ },
162
+ {
163
+ id: "kv",
164
+ path: "/kv",
165
+ meta: {
166
+ kind: KINDS.NODE,
167
+ kinds: [KINDS.NODE],
168
+ childrenCount: kvNamespaces.length
169
+ }
170
+ },
171
+ {
172
+ id: "pages",
173
+ path: "/pages",
174
+ meta: {
175
+ kind: KINDS.NODE,
176
+ kinds: [KINDS.NODE],
177
+ childrenCount: pagesProjects.length
178
+ }
179
+ },
180
+ {
181
+ id: "by-zone",
182
+ path: "/by-zone",
183
+ meta: {
184
+ kind: KINDS.NODE,
185
+ kinds: [KINDS.NODE],
186
+ childrenCount: zones.length
187
+ }
188
+ }
189
+ ] };
190
+ }
191
+ async readCapabilities(_ctx) {
192
+ return {
193
+ id: "/.meta/.capabilities",
194
+ path: "/.meta/.capabilities",
195
+ content: {
196
+ schemaVersion: 1,
197
+ provider: this.name,
198
+ description: this.description ?? "Cloudflare provider",
199
+ tools: [],
200
+ actions: [],
201
+ operations: this.getOperationsDeclaration()
202
+ },
203
+ meta: { kind: "afs:capabilities" }
204
+ };
205
+ }
206
+ async readRoot(_ctx) {
207
+ return {
208
+ id: "/",
209
+ path: "/",
210
+ content: "",
211
+ meta: {
212
+ kind: KINDS.NODE,
213
+ kinds: [KINDS.NODE],
214
+ childrenCount: 5
215
+ }
216
+ };
217
+ }
218
+ async readWorldMd(_ctx) {
219
+ this.ensureNotDestroyed();
220
+ const accountId = await this.accountId();
221
+ const cacheKey = "world-md";
222
+ const cached = this.cache.get(cacheKey);
223
+ if (cached) return {
224
+ id: "WORLD.md",
225
+ path: "/WORLD.md",
226
+ content: cached,
227
+ meta: {
228
+ kind: KINDS.NODE,
229
+ kinds: [KINDS.NODE],
230
+ contentType: "text/markdown"
231
+ }
232
+ };
233
+ const [workers, kvNamespaces, pagesProjects, zones] = await Promise.all([
234
+ this.collect(this.client.workers.scripts.list({ account_id: accountId })),
235
+ this.collect(this.client.kv.namespaces.list({ account_id: accountId })),
236
+ this.collect(this.client.pages.projects.list({ account_id: accountId })),
237
+ this.collect(this.client.zones.list())
238
+ ]);
239
+ const content = `# Cloudflare Account Overview
240
+
241
+ **Account ID**: ${accountId}
242
+
243
+ ## Resources
244
+
245
+ | Resource | Count |
246
+ |----------|-------|
247
+ | Workers | ${workers.length} |
248
+ | KV Namespaces | ${kvNamespaces.length} |
249
+ | Pages Projects | ${pagesProjects.length} |
250
+ | Zones | ${zones.length} |
251
+
252
+ ## Workers
253
+ ${workers.map((w) => `- ${w.id}`).join("\n")}
254
+
255
+ ## KV Namespaces
256
+ ${kvNamespaces.map((ns) => `- ${ns.title}`).join("\n")}
257
+
258
+ ## Pages Projects
259
+ ${pagesProjects.map((p) => `- ${p.name}`).join("\n")}
260
+
261
+ ## Zones
262
+ ${zones.map((z) => `- ${z.name}`).join("\n")}
263
+ `;
264
+ this.cache.set(cacheKey, content);
265
+ return {
266
+ id: "WORLD.md",
267
+ path: "/WORLD.md",
268
+ content,
269
+ meta: {
270
+ kind: KINDS.NODE,
271
+ kinds: [KINDS.NODE],
272
+ contentType: "text/markdown"
273
+ }
274
+ };
275
+ }
276
+ async metaRoot(_ctx) {
277
+ return {
278
+ id: "",
279
+ path: "/.meta",
280
+ meta: {
281
+ kind: KINDS.NODE,
282
+ kinds: [KINDS.NODE],
283
+ childrenCount: 5
284
+ }
285
+ };
286
+ }
287
+ async statRoot(ctx) {
288
+ if (ctx.path === "/WORLD.md" || ctx.path === "/WORLD.md/.stat") return { data: {
289
+ id: "WORLD.md",
290
+ path: "/WORLD.md",
291
+ meta: {
292
+ kind: KINDS.NODE,
293
+ kinds: [KINDS.NODE],
294
+ contentType: "text/markdown"
295
+ }
296
+ } };
297
+ return { data: {
298
+ id: "/",
299
+ path: "/",
300
+ meta: {
301
+ kind: KINDS.NODE,
302
+ kinds: [KINDS.NODE],
303
+ childrenCount: 5
304
+ }
305
+ } };
306
+ }
307
+ async explainRoot(_ctx) {
308
+ return {
309
+ format: "markdown",
310
+ content: `# Cloudflare Provider
311
+
312
+ This provider gives file-system-like access to Cloudflare Developer Platform resources.
313
+
314
+ **Account ID**: ${await this.accountId()}
315
+
316
+ ## Available Paths
317
+
318
+ - \`/workers\` — Cloudflare Workers (edge functions)
319
+ - \`/kv\` — KV Namespaces (key-value storage)
320
+ - \`/pages\` — Pages Projects (static site hosting)
321
+ - \`/by-zone\` — Resources grouped by DNS zone
322
+ - \`/WORLD.md\` — Account overview and resource statistics
323
+ `
324
+ };
325
+ }
326
+ async listFileNode(_ctx) {
327
+ return { data: [] };
328
+ }
329
+ async metaWorldMd(_ctx) {
330
+ return {
331
+ id: "WORLD.md",
332
+ path: "/WORLD.md/.meta",
333
+ meta: {
334
+ kind: KINDS.NODE,
335
+ kinds: [KINDS.NODE],
336
+ contentType: "text/markdown"
337
+ }
338
+ };
339
+ }
340
+ async metaWorkerScript(ctx) {
341
+ return {
342
+ id: "script.js",
343
+ path: joinURL("/workers", ctx.params.name, "script.js", ".meta"),
344
+ meta: {
345
+ kind: KINDS.NODE,
346
+ kinds: [KINDS.NODE],
347
+ contentType: "application/javascript"
348
+ }
349
+ };
350
+ }
351
+ async metaWorkerSettings(ctx) {
352
+ return {
353
+ id: "settings.json",
354
+ path: joinURL("/workers", ctx.params.name, "settings.json", ".meta"),
355
+ meta: {
356
+ kind: KINDS.NODE,
357
+ kinds: [KINDS.NODE],
358
+ contentType: "application/json"
359
+ }
360
+ };
361
+ }
362
+ async metaWorkerBindings(ctx) {
363
+ return {
364
+ id: "bindings",
365
+ path: joinURL("/workers", ctx.params.name, "bindings", ".meta"),
366
+ meta: {
367
+ kind: KINDS.NODE,
368
+ kinds: [KINDS.NODE]
369
+ }
370
+ };
371
+ }
372
+ async metaWorkerBinding(ctx) {
373
+ return {
374
+ id: ctx.params.binding,
375
+ path: joinURL("/workers", ctx.params.name, "bindings", ctx.params.binding, ".meta"),
376
+ meta: {
377
+ kind: KINDS.BINDING,
378
+ kinds: [KINDS.BINDING, KINDS.NODE]
379
+ }
380
+ };
381
+ }
382
+ async metaWorkerRoutes(ctx) {
383
+ return {
384
+ id: "routes",
385
+ path: joinURL("/workers", ctx.params.name, "routes", ".meta"),
386
+ meta: {
387
+ kind: KINDS.NODE,
388
+ kinds: [KINDS.NODE]
389
+ }
390
+ };
391
+ }
392
+ async metaWorkerRoute(ctx) {
393
+ return {
394
+ id: ctx.params.route,
395
+ path: joinURL("/workers", ctx.params.name, "routes", ctx.params.route, ".meta"),
396
+ meta: {
397
+ kind: KINDS.ROUTE,
398
+ kinds: [KINDS.ROUTE, KINDS.NODE]
399
+ }
400
+ };
401
+ }
402
+ async metaWorkerCronTriggers(ctx) {
403
+ return {
404
+ id: "cron-triggers",
405
+ path: joinURL("/workers", ctx.params.name, "cron-triggers", ".meta"),
406
+ meta: {
407
+ kind: KINDS.NODE,
408
+ kinds: [KINDS.NODE]
409
+ }
410
+ };
411
+ }
412
+ async metaWorkerCronTrigger(ctx) {
413
+ return {
414
+ id: ctx.params.cron,
415
+ path: joinURL("/workers", ctx.params.name, "cron-triggers", ctx.params.cron, ".meta"),
416
+ meta: {
417
+ kind: KINDS.CRON_TRIGGER,
418
+ kinds: [KINDS.CRON_TRIGGER, KINDS.NODE]
419
+ }
420
+ };
421
+ }
422
+ async metaKVMetadataFile(ctx) {
423
+ return {
424
+ id: "metadata.json",
425
+ path: joinURL("/kv", ctx.params.title, "metadata.json", ".meta"),
426
+ meta: {
427
+ kind: KINDS.NODE,
428
+ kinds: [KINDS.NODE],
429
+ contentType: "application/json"
430
+ }
431
+ };
432
+ }
433
+ async metaKVKeys(ctx) {
434
+ return {
435
+ id: "keys",
436
+ path: joinURL("/kv", ctx.params.title, "keys", ".meta"),
437
+ meta: {
438
+ kind: KINDS.NODE,
439
+ kinds: [KINDS.NODE]
440
+ }
441
+ };
442
+ }
443
+ async metaKVKey(ctx) {
444
+ return {
445
+ id: decodeURIComponent(ctx.params.key),
446
+ path: joinURL("/kv", ctx.params.title, "keys", ctx.params.key, ".meta"),
447
+ meta: {
448
+ kind: KINDS.KV_KEY,
449
+ kinds: [KINDS.KV_KEY, KINDS.NODE]
450
+ }
451
+ };
452
+ }
453
+ async metaPagesMetadataFile(ctx) {
454
+ return {
455
+ id: "metadata.json",
456
+ path: joinURL("/pages", ctx.params.project, "metadata.json", ".meta"),
457
+ meta: {
458
+ kind: KINDS.NODE,
459
+ kinds: [KINDS.NODE],
460
+ contentType: "application/json"
461
+ }
462
+ };
463
+ }
464
+ async metaPagesDeployments(ctx) {
465
+ return {
466
+ id: "deployments",
467
+ path: joinURL("/pages", ctx.params.project, "deployments", ".meta"),
468
+ meta: {
469
+ kind: KINDS.NODE,
470
+ kinds: [KINDS.NODE]
471
+ }
472
+ };
473
+ }
474
+ async metaPagesDeployment(ctx) {
475
+ return {
476
+ id: ctx.params.id,
477
+ path: joinURL("/pages", ctx.params.project, "deployments", ctx.params.id, ".meta"),
478
+ meta: {
479
+ kind: KINDS.DEPLOYMENT,
480
+ kinds: [KINDS.DEPLOYMENT, KINDS.NODE]
481
+ }
482
+ };
483
+ }
484
+ async metaPagesDomains(ctx) {
485
+ return {
486
+ id: "domains",
487
+ path: joinURL("/pages", ctx.params.project, "domains", ".meta"),
488
+ meta: {
489
+ kind: KINDS.NODE,
490
+ kinds: [KINDS.NODE]
491
+ }
492
+ };
493
+ }
494
+ async metaPagesDomain(ctx) {
495
+ return {
496
+ id: ctx.params.domain,
497
+ path: joinURL("/pages", ctx.params.project, "domains", ctx.params.domain, ".meta"),
498
+ meta: {
499
+ kind: KINDS.DOMAIN,
500
+ kinds: [KINDS.DOMAIN, KINDS.NODE]
501
+ }
502
+ };
503
+ }
504
+ async metaByZoneDir(ctx) {
505
+ return {
506
+ id: ctx.params.zone,
507
+ path: joinURL("/by-zone", ctx.params.zone, ".meta"),
508
+ meta: {
509
+ kind: KINDS.ZONE,
510
+ kinds: [KINDS.ZONE, KINDS.NODE],
511
+ childrenCount: 2
512
+ }
513
+ };
514
+ }
515
+ async metaByZoneWorkers(ctx) {
516
+ return {
517
+ id: "workers",
518
+ path: joinURL("/by-zone", ctx.params.zone, "workers", ".meta"),
519
+ meta: {
520
+ kind: KINDS.NODE,
521
+ kinds: [KINDS.NODE]
522
+ }
523
+ };
524
+ }
525
+ async metaByZoneWorker(ctx) {
526
+ return {
527
+ id: ctx.params.name,
528
+ path: joinURL("/by-zone", ctx.params.zone, "workers", ctx.params.name, ".meta"),
529
+ meta: {
530
+ kind: KINDS.WORKER,
531
+ kinds: [KINDS.WORKER, KINDS.NODE]
532
+ }
533
+ };
534
+ }
535
+ async metaByZonePages(ctx) {
536
+ return {
537
+ id: "pages",
538
+ path: joinURL("/by-zone", ctx.params.zone, "pages", ".meta"),
539
+ meta: {
540
+ kind: KINDS.NODE,
541
+ kinds: [KINDS.NODE]
542
+ }
543
+ };
544
+ }
545
+ async metaByZoneProject(ctx) {
546
+ return {
547
+ id: ctx.params.project,
548
+ path: joinURL("/by-zone", ctx.params.zone, "pages", ctx.params.project, ".meta"),
549
+ meta: {
550
+ kind: KINDS.PAGES_PROJECT,
551
+ kinds: [KINDS.PAGES_PROJECT, KINDS.NODE]
552
+ }
553
+ };
554
+ }
555
+ async listGlobalActions(_ctx) {
556
+ return { data: [{
557
+ id: "refresh",
558
+ path: "/.actions/refresh",
559
+ summary: "Refresh all cached data",
560
+ meta: {
561
+ kind: KINDS.EXECUTABLE,
562
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE]
563
+ }
564
+ }] };
565
+ }
566
+ async execRefresh(_ctx, _args) {
567
+ this.ensureNotDestroyed();
568
+ this.cache.clear();
569
+ return {
570
+ success: true,
571
+ data: { message: "Cache cleared" }
572
+ };
573
+ }
574
+ async listWorkers(_ctx) {
575
+ this.ensureNotDestroyed();
576
+ const accountId = await this.accountId();
577
+ const cacheKey = "list:workers";
578
+ const cached = this.cache.get(cacheKey);
579
+ if (cached) return { data: cached };
580
+ const entries = (await this.collect(this.client.workers.scripts.list({ account_id: accountId }))).map((w) => ({
581
+ id: w.id,
582
+ path: joinURL("/workers", w.id),
583
+ summary: w.id,
584
+ meta: {
585
+ kind: KINDS.WORKER,
586
+ kinds: [KINDS.WORKER, KINDS.NODE],
587
+ childrenCount: 5
588
+ }
589
+ }));
590
+ this.cache.set(cacheKey, entries);
591
+ return { data: entries };
592
+ }
593
+ async listWorkerDir(ctx) {
594
+ this.ensureNotDestroyed();
595
+ const { name } = ctx.params;
596
+ const accountId = await this.accountId();
597
+ const basePath = joinURL("/workers", name);
598
+ const bindings = (await this.client.workers.scripts.settings.get(name, { account_id: accountId }))?.result?.bindings || [];
599
+ const triggers = (await this.client.workers.scripts.schedules.get(name, { account_id: accountId }))?.schedules || [];
600
+ const zones = await this.collect(this.client.zones.list());
601
+ let routeCount = 0;
602
+ for (const zone of zones) {
603
+ const routes = await this.collect(this.client.workers.routes.list({ zone_id: zone.id }));
604
+ routeCount += routes.filter((r) => r.script === name).length;
605
+ }
606
+ return { data: [
607
+ {
608
+ id: "script.js",
609
+ path: joinURL(basePath, "script.js"),
610
+ meta: {
611
+ kind: KINDS.NODE,
612
+ kinds: [KINDS.NODE],
613
+ contentType: "application/javascript"
614
+ }
615
+ },
616
+ {
617
+ id: "settings.json",
618
+ path: joinURL(basePath, "settings.json"),
619
+ meta: {
620
+ kind: KINDS.NODE,
621
+ kinds: [KINDS.NODE],
622
+ contentType: "application/json"
623
+ }
624
+ },
625
+ {
626
+ id: "bindings",
627
+ path: joinURL(basePath, "bindings"),
628
+ meta: {
629
+ kind: KINDS.NODE,
630
+ kinds: [KINDS.NODE],
631
+ childrenCount: bindings.length
632
+ }
633
+ },
634
+ {
635
+ id: "routes",
636
+ path: joinURL(basePath, "routes"),
637
+ meta: {
638
+ kind: KINDS.NODE,
639
+ kinds: [KINDS.NODE],
640
+ childrenCount: routeCount
641
+ }
642
+ },
643
+ {
644
+ id: "cron-triggers",
645
+ path: joinURL(basePath, "cron-triggers"),
646
+ meta: {
647
+ kind: KINDS.NODE,
648
+ kinds: [KINDS.NODE],
649
+ childrenCount: triggers.length
650
+ }
651
+ }
652
+ ] };
653
+ }
654
+ async listWorkerBindings(ctx) {
655
+ this.ensureNotDestroyed();
656
+ const { name } = ctx.params;
657
+ const accountId = await this.accountId();
658
+ return { data: ((await this.client.workers.scripts.settings.get(name, { account_id: accountId }))?.result?.bindings || []).map((b) => ({
659
+ id: b.name,
660
+ path: joinURL("/workers", name, "bindings", b.name),
661
+ content: JSON.stringify(b, null, 2),
662
+ meta: {
663
+ kind: KINDS.BINDING,
664
+ kinds: [KINDS.BINDING, KINDS.NODE],
665
+ bindingType: b.type
666
+ }
667
+ })) };
668
+ }
669
+ async listWorkerRoutes(ctx) {
670
+ this.ensureNotDestroyed();
671
+ const { name } = ctx.params;
672
+ const zones = await this.collect(this.client.zones.list());
673
+ const entries = [];
674
+ for (const zone of zones) {
675
+ const routes = await this.collect(this.client.workers.routes.list({ zone_id: zone.id }));
676
+ for (const route of routes) if (route.script === name) {
677
+ const pattern = route.pattern;
678
+ entries.push({
679
+ id: pattern,
680
+ path: joinURL("/workers", name, "routes", pattern),
681
+ content: JSON.stringify(route, null, 2),
682
+ meta: {
683
+ kind: KINDS.ROUTE,
684
+ kinds: [KINDS.ROUTE, KINDS.NODE],
685
+ zoneName: zone.name
686
+ }
687
+ });
688
+ }
689
+ }
690
+ return { data: entries };
691
+ }
692
+ async listWorkerCronTriggers(ctx) {
693
+ this.ensureNotDestroyed();
694
+ const { name } = ctx.params;
695
+ const accountId = await this.accountId();
696
+ return { data: ((await this.client.workers.scripts.schedules.get(name, { account_id: accountId }))?.schedules || []).map((t) => ({
697
+ id: t.cron,
698
+ path: joinURL("/workers", name, "cron-triggers", t.cron),
699
+ content: JSON.stringify(t, null, 2),
700
+ meta: {
701
+ kind: KINDS.CRON_TRIGGER,
702
+ kinds: [KINDS.CRON_TRIGGER, KINDS.NODE]
703
+ }
704
+ })) };
705
+ }
706
+ async readWorkers(_ctx) {
707
+ this.ensureNotDestroyed();
708
+ const accountId = await this.accountId();
709
+ const workers = await this.collect(this.client.workers.scripts.list({ account_id: accountId }));
710
+ return {
711
+ id: "workers",
712
+ path: "/workers",
713
+ content: "",
714
+ meta: {
715
+ kind: KINDS.NODE,
716
+ kinds: [KINDS.NODE],
717
+ childrenCount: workers.length
718
+ }
719
+ };
720
+ }
721
+ async readWorkerDir(ctx) {
722
+ return {
723
+ id: ctx.params.name,
724
+ path: joinURL("/workers", ctx.params.name),
725
+ content: "",
726
+ meta: {
727
+ kind: KINDS.WORKER,
728
+ kinds: [KINDS.WORKER, KINDS.NODE],
729
+ childrenCount: 5
730
+ }
731
+ };
732
+ }
733
+ async readWorkerBindingsDir(ctx) {
734
+ this.ensureNotDestroyed();
735
+ const { name } = ctx.params;
736
+ const accountId = await this.accountId();
737
+ const bindings = (await this.client.workers.scripts.settings.get(name, { account_id: accountId }))?.result?.bindings || [];
738
+ return {
739
+ id: "bindings",
740
+ path: joinURL("/workers", name, "bindings"),
741
+ content: "",
742
+ meta: {
743
+ kind: KINDS.NODE,
744
+ kinds: [KINDS.NODE],
745
+ childrenCount: bindings.length
746
+ }
747
+ };
748
+ }
749
+ async readWorkerRoutesDir(ctx) {
750
+ this.ensureNotDestroyed();
751
+ const { name } = ctx.params;
752
+ const zones = await this.collect(this.client.zones.list());
753
+ let routeCount = 0;
754
+ for (const zone of zones) {
755
+ const routes = await this.collect(this.client.workers.routes.list({ zone_id: zone.id }));
756
+ routeCount += routes.filter((r) => r.script === name).length;
757
+ }
758
+ return {
759
+ id: "routes",
760
+ path: joinURL("/workers", name, "routes"),
761
+ content: "",
762
+ meta: {
763
+ kind: KINDS.NODE,
764
+ kinds: [KINDS.NODE],
765
+ childrenCount: routeCount
766
+ }
767
+ };
768
+ }
769
+ async readWorkerCronTriggersDir(ctx) {
770
+ this.ensureNotDestroyed();
771
+ const { name } = ctx.params;
772
+ const accountId = await this.accountId();
773
+ const triggers = (await this.client.workers.scripts.schedules.get(name, { account_id: accountId }))?.schedules || [];
774
+ return {
775
+ id: "cron-triggers",
776
+ path: joinURL("/workers", name, "cron-triggers"),
777
+ content: "",
778
+ meta: {
779
+ kind: KINDS.NODE,
780
+ kinds: [KINDS.NODE],
781
+ childrenCount: triggers.length
782
+ }
783
+ };
784
+ }
785
+ async readWorkerScript(ctx) {
786
+ this.ensureNotDestroyed();
787
+ const { name } = ctx.params;
788
+ const accountId = await this.accountId();
789
+ try {
790
+ const response = await this.client.workers.scripts.content.get(name, { account_id: accountId });
791
+ const content = response instanceof Response ? await response.text() : typeof response === "string" ? response : "";
792
+ return {
793
+ id: "script.js",
794
+ path: joinURL("/workers", name, "script.js"),
795
+ content,
796
+ meta: {
797
+ kind: KINDS.NODE,
798
+ kinds: [KINDS.NODE],
799
+ contentType: "application/javascript"
800
+ }
801
+ };
802
+ } catch (error) {
803
+ throw mapCloudflareError(error, ctx.path);
804
+ }
805
+ }
806
+ async readWorkerSettings(ctx) {
807
+ this.ensureNotDestroyed();
808
+ const { name } = ctx.params;
809
+ const accountId = await this.accountId();
810
+ try {
811
+ const settings = await this.client.workers.scripts.settings.get(name, { account_id: accountId });
812
+ const content = JSON.stringify(settings?.result || {}, null, 2);
813
+ return {
814
+ id: "settings.json",
815
+ path: joinURL("/workers", name, "settings.json"),
816
+ content,
817
+ meta: {
818
+ kind: KINDS.NODE,
819
+ kinds: [KINDS.NODE],
820
+ contentType: "application/json"
821
+ }
822
+ };
823
+ } catch (error) {
824
+ throw mapCloudflareError(error, ctx.path);
825
+ }
826
+ }
827
+ async readWorkerBinding(ctx) {
828
+ this.ensureNotDestroyed();
829
+ const { name, binding } = ctx.params;
830
+ const accountId = await this.accountId();
831
+ const found = ((await this.client.workers.scripts.settings.get(name, { account_id: accountId }))?.result?.bindings || []).find((b) => b.name === binding);
832
+ if (!found) throw new AFSNotFoundError(`Binding not found: ${binding}`);
833
+ return {
834
+ id: binding,
835
+ path: joinURL("/workers", name, "bindings", binding),
836
+ content: JSON.stringify(found, null, 2),
837
+ meta: {
838
+ kind: KINDS.BINDING,
839
+ kinds: [KINDS.BINDING, KINDS.NODE],
840
+ bindingType: found.type
841
+ }
842
+ };
843
+ }
844
+ async readWorkerRoute(ctx) {
845
+ this.ensureNotDestroyed();
846
+ const { name, route } = ctx.params;
847
+ const decodedRoute = route;
848
+ const zones = await this.collect(this.client.zones.list());
849
+ for (const zone of zones) {
850
+ const routes = await this.collect(this.client.workers.routes.list({ zone_id: zone.id }));
851
+ for (const r of routes) if (r.script === name && r.pattern === decodedRoute) return {
852
+ id: decodedRoute,
853
+ path: joinURL("/workers", name, "routes", route),
854
+ content: JSON.stringify(r, null, 2),
855
+ meta: {
856
+ kind: KINDS.ROUTE,
857
+ kinds: [KINDS.ROUTE, KINDS.NODE],
858
+ zoneName: zone.name
859
+ }
860
+ };
861
+ }
862
+ throw new AFSNotFoundError(`Route not found: ${decodedRoute}`);
863
+ }
864
+ async readWorkerCronTrigger(ctx) {
865
+ this.ensureNotDestroyed();
866
+ const { name, cron } = ctx.params;
867
+ const decodedCron = cron;
868
+ const accountId = await this.accountId();
869
+ const found = ((await this.client.workers.scripts.schedules.get(name, { account_id: accountId }))?.schedules || []).find((t) => t.cron === decodedCron);
870
+ if (!found) throw new AFSNotFoundError(`Cron trigger not found: ${decodedCron}`);
871
+ return {
872
+ id: decodedCron,
873
+ path: joinURL("/workers", name, "cron-triggers", cron),
874
+ content: JSON.stringify(found, null, 2),
875
+ meta: {
876
+ kind: KINDS.CRON_TRIGGER,
877
+ kinds: [KINDS.CRON_TRIGGER, KINDS.NODE]
878
+ }
879
+ };
880
+ }
881
+ async metaWorkers(_ctx) {
882
+ return {
883
+ id: "workers",
884
+ path: "/workers/.meta",
885
+ meta: {
886
+ kind: KINDS.NODE,
887
+ kinds: [KINDS.NODE]
888
+ }
889
+ };
890
+ }
891
+ async metaWorker(ctx) {
892
+ this.ensureNotDestroyed();
893
+ const { name } = ctx.params;
894
+ const accountId = await this.accountId();
895
+ const worker = await this.client.workers.scripts.get(name, { account_id: accountId });
896
+ if (!worker) throw new AFSNotFoundError(`Worker not found: ${name}`);
897
+ const settings = await this.client.workers.scripts.settings.get(name, { account_id: accountId });
898
+ const bindings = settings?.result?.bindings || [];
899
+ const cronTriggers = (await this.client.workers.scripts.schedules.get(name, { account_id: accountId }))?.schedules || [];
900
+ const zones = await this.collect(this.client.zones.list());
901
+ let routeCount = 0;
902
+ for (const zone of zones) {
903
+ const routes = await this.collect(this.client.workers.routes.list({ zone_id: zone.id }));
904
+ routeCount += routes.filter((r) => r.script === name).length;
905
+ }
906
+ return {
907
+ id: name,
908
+ path: joinURL("/workers", name, ".meta"),
909
+ meta: {
910
+ kind: KINDS.WORKER,
911
+ kinds: [KINDS.WORKER, KINDS.NODE],
912
+ childrenCount: 5,
913
+ scriptName: name,
914
+ createdOn: worker.created_on,
915
+ modifiedOn: worker.modified_on,
916
+ compatibilityDate: settings?.result?.compatibility_date,
917
+ usageModel: settings?.result?.usage_model,
918
+ routeCount,
919
+ bindingCount: bindings.length,
920
+ cronTriggerCount: cronTriggers.length,
921
+ platformRef: generateWorkerPlatformRef(accountId, name)
922
+ }
923
+ };
924
+ }
925
+ async statWorkers(ctx) {
926
+ const parts = ctx.path.replace(/\/.stat$/, "").split("/").filter(Boolean);
927
+ return { data: {
928
+ id: parts[parts.length - 1] || "workers",
929
+ path: ctx.path.replace(/\/.stat$/, ""),
930
+ meta: {
931
+ kind: KINDS.NODE,
932
+ kinds: [KINDS.NODE]
933
+ }
934
+ } };
935
+ }
936
+ async explainWorkers(_ctx) {
937
+ return {
938
+ format: "markdown",
939
+ content: `# Workers
940
+
941
+ Cloudflare Workers are serverless edge functions. Each worker contains:
942
+ - \`script.js\` — The worker source code
943
+ - \`settings.json\` — Configuration (compatibility_date, usage_model)
944
+ - \`bindings/\` — Resource bindings (KV, R2, D1, etc.)
945
+ - \`routes/\` — URL patterns that trigger this worker
946
+ - \`cron-triggers/\` — Scheduled execution patterns
947
+ `
948
+ };
949
+ }
950
+ async explainWorker(ctx) {
951
+ this.ensureNotDestroyed();
952
+ const { name } = ctx.params;
953
+ const accountId = await this.accountId();
954
+ const worker = await this.client.workers.scripts.get(name, { account_id: accountId });
955
+ if (!worker) throw new AFSNotFoundError(`Worker not found: ${name}`);
956
+ return {
957
+ format: "markdown",
958
+ content: `# Worker: ${name}
959
+
960
+ **Created**: ${worker.created_on}
961
+ **Modified**: ${worker.modified_on}
962
+
963
+ ## Structure
964
+ - \`script.js\` — Source code (read/write)
965
+ - \`settings.json\` — Configuration
966
+ - \`bindings/\` — Resource bindings
967
+ - \`routes/\` — URL route patterns
968
+ - \`cron-triggers/\` — Scheduled triggers
969
+
970
+ ## Actions
971
+ - \`deploy\` — Deploy latest version to production
972
+ - \`rollback\` — Rollback to previous version
973
+ - \`delete\` — Delete this worker (requires confirm: true)
974
+ `
975
+ };
976
+ }
977
+ async writeWorkerScript(ctx, payload) {
978
+ this.ensureNotDestroyed();
979
+ this.requireReadwrite(ctx.path);
980
+ const { name } = ctx.params;
981
+ const accountId = await this.accountId();
982
+ const content = typeof payload.content === "string" ? payload.content : "";
983
+ try {
984
+ const scriptFile = new File([content], "script.js", { type: "application/javascript+module" });
985
+ const result = await this.client.workers.scripts.update(name, {
986
+ account_id: accountId,
987
+ metadata: { main_module: "script.js" },
988
+ files: { "script.js": scriptFile }
989
+ });
990
+ this.cache.invalidate("list:workers");
991
+ this.cache.invalidate(`worker:${name}`);
992
+ return { data: {
993
+ id: "script.js",
994
+ path: joinURL("/workers", name, "script.js"),
995
+ content,
996
+ meta: {
997
+ kind: KINDS.NODE,
998
+ kinds: [KINDS.NODE],
999
+ contentType: "application/javascript",
1000
+ etag: result?.etag
1001
+ }
1002
+ } };
1003
+ } catch (error) {
1004
+ throw mapCloudflareError(error, ctx.path);
1005
+ }
1006
+ }
1007
+ async writeWorkerSettings(ctx, payload) {
1008
+ this.ensureNotDestroyed();
1009
+ this.requireReadwrite(ctx.path);
1010
+ const { name } = ctx.params;
1011
+ const accountId = await this.accountId();
1012
+ try {
1013
+ const settings = typeof payload.content === "string" ? JSON.parse(payload.content) : payload.content || {};
1014
+ const applied = {};
1015
+ if ("workers_dev" in settings) {
1016
+ await this.client.workers.scripts.subdomain.create(name, {
1017
+ account_id: accountId,
1018
+ enabled: !!settings.workers_dev
1019
+ });
1020
+ applied.workers_dev = !!settings.workers_dev;
1021
+ }
1022
+ this.cache.invalidate(`worker:${name}`);
1023
+ return { data: {
1024
+ id: "settings.json",
1025
+ path: joinURL("/workers", name, "settings.json"),
1026
+ content: JSON.stringify(applied, null, 2),
1027
+ meta: {
1028
+ kind: KINDS.NODE,
1029
+ kinds: [KINDS.NODE],
1030
+ contentType: "application/json"
1031
+ }
1032
+ } };
1033
+ } catch (error) {
1034
+ throw mapCloudflareError(error, ctx.path);
1035
+ }
1036
+ }
1037
+ async listRootWorkerActions(_ctx) {
1038
+ if (this.config.accessMode !== "readwrite") throw new AFSNotFoundError("Actions not available in readonly mode");
1039
+ return { data: [{
1040
+ id: "create",
1041
+ path: "/workers/.actions/create",
1042
+ summary: "Create and deploy a new worker",
1043
+ meta: {
1044
+ kind: KINDS.EXECUTABLE,
1045
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE],
1046
+ inputSchema: {
1047
+ type: "object",
1048
+ required: ["name"],
1049
+ properties: {
1050
+ name: {
1051
+ type: "string",
1052
+ description: "Worker name"
1053
+ },
1054
+ script: {
1055
+ type: "string",
1056
+ description: "Inline script content (for single-file workers)"
1057
+ },
1058
+ directory: {
1059
+ type: "string",
1060
+ description: "AFS directory path containing worker files (must be an AFS mount path)"
1061
+ },
1062
+ mainModule: {
1063
+ type: "string",
1064
+ description: "Main module filename (auto-detected if not provided)"
1065
+ },
1066
+ bindings: {
1067
+ type: "array",
1068
+ description: "KV/R2/etc bindings, e.g. [{\"type\":\"kv_namespace\",\"name\":\"KV\",\"namespace_id\":\"...\"}]",
1069
+ items: { type: "object" }
1070
+ },
1071
+ compatibilityDate: {
1072
+ type: "string",
1073
+ description: "Compatibility date (default: today)"
1074
+ }
1075
+ }
1076
+ }
1077
+ }
1078
+ }] };
1079
+ }
1080
+ async execWorkerCreate(ctx, args) {
1081
+ this.ensureNotDestroyed();
1082
+ this.requireReadwrite(ctx.path);
1083
+ const accountId = await this.accountId();
1084
+ const name = args.name;
1085
+ const script = args.script;
1086
+ const directory = args.directory;
1087
+ const mainModuleArg = args.mainModule;
1088
+ const rawBindings = args.bindings;
1089
+ let bindings;
1090
+ if (typeof rawBindings === "string") try {
1091
+ bindings = JSON.parse(rawBindings);
1092
+ } catch {
1093
+ return {
1094
+ success: false,
1095
+ error: {
1096
+ code: "VALIDATION_ERROR",
1097
+ message: "bindings must be valid JSON array"
1098
+ }
1099
+ };
1100
+ }
1101
+ else bindings = rawBindings;
1102
+ const compatibilityDate = args.compatibilityDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1103
+ if (!name) return {
1104
+ success: false,
1105
+ error: {
1106
+ code: "VALIDATION_ERROR",
1107
+ message: "name is required"
1108
+ }
1109
+ };
1110
+ if (!script && !directory) return {
1111
+ success: false,
1112
+ error: {
1113
+ code: "VALIDATION_ERROR",
1114
+ message: "Either script (inline content) or directory (AFS path) is required"
1115
+ }
1116
+ };
1117
+ try {
1118
+ const hasBindings = bindings && bindings.length > 0;
1119
+ let fileCount = 1;
1120
+ if (script) {
1121
+ const scriptFile = new File([script], "index.js", { type: "application/javascript+module" });
1122
+ await this.client.workers.scripts.update(name, {
1123
+ account_id: accountId,
1124
+ metadata: {
1125
+ main_module: "index.js",
1126
+ ...hasBindings ? { bindings } : {},
1127
+ compatibility_date: compatibilityDate
1128
+ },
1129
+ files: { "index.js": scriptFile }
1130
+ });
1131
+ } else if (directory) {
1132
+ this.validateAfsPath(directory);
1133
+ const afs = this.getAfsFromContext(ctx);
1134
+ const files = await this.readDirectoryFiles(afs, directory);
1135
+ const mainModule = mainModuleArg || this.detectMainModule(files);
1136
+ fileCount = files.size;
1137
+ const fileUploads = {};
1138
+ for (const [filename, content] of files) fileUploads[filename] = new File([content], filename, { type: this.getContentType(filename) });
1139
+ await this.client.workers.scripts.update(name, {
1140
+ account_id: accountId,
1141
+ metadata: {
1142
+ main_module: mainModule,
1143
+ ...hasBindings ? { bindings } : {},
1144
+ compatibility_date: compatibilityDate
1145
+ },
1146
+ files: fileUploads
1147
+ });
1148
+ }
1149
+ try {
1150
+ await this.client.workers.scripts.subdomain.create(name, {
1151
+ account_id: accountId,
1152
+ enabled: true
1153
+ });
1154
+ } catch {}
1155
+ this.cache.invalidate("list:workers");
1156
+ this.cache.invalidate(`worker:${name}`);
1157
+ return {
1158
+ success: true,
1159
+ data: {
1160
+ name,
1161
+ type: script ? "single-file" : "directory",
1162
+ fileCount
1163
+ }
1164
+ };
1165
+ } catch (error) {
1166
+ throw mapCloudflareError(error, ctx.path);
1167
+ }
1168
+ }
1169
+ async listWorkerActions(ctx) {
1170
+ if (this.config.accessMode !== "readwrite") throw new AFSNotFoundError("Actions not available in readonly mode");
1171
+ const basePath = joinURL("/workers", ctx.params.name, ".actions");
1172
+ return { data: [
1173
+ {
1174
+ id: "deploy",
1175
+ path: joinURL(basePath, "deploy"),
1176
+ summary: "Deploy version to production",
1177
+ meta: {
1178
+ kind: KINDS.EXECUTABLE,
1179
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE]
1180
+ }
1181
+ },
1182
+ {
1183
+ id: "rollback",
1184
+ path: joinURL(basePath, "rollback"),
1185
+ summary: "Rollback to previous version",
1186
+ meta: {
1187
+ kind: KINDS.EXECUTABLE,
1188
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE]
1189
+ }
1190
+ },
1191
+ {
1192
+ id: "delete",
1193
+ path: joinURL(basePath, "delete"),
1194
+ summary: "Delete worker (requires confirm: true)",
1195
+ meta: {
1196
+ kind: KINDS.EXECUTABLE,
1197
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE],
1198
+ inputSchema: {
1199
+ type: "object",
1200
+ required: ["confirm"],
1201
+ properties: { confirm: {
1202
+ type: "boolean",
1203
+ description: "Must be true to confirm deletion"
1204
+ } }
1205
+ }
1206
+ }
1207
+ }
1208
+ ] };
1209
+ }
1210
+ async execWorkerDeploy(ctx, args) {
1211
+ this.ensureNotDestroyed();
1212
+ this.requireReadwrite(ctx.path);
1213
+ const { name } = ctx.params;
1214
+ const accountId = await this.accountId();
1215
+ try {
1216
+ const directory = args.directory;
1217
+ if (directory) {
1218
+ this.validateAfsPath(directory);
1219
+ const afs = this.getAfsFromContext(ctx);
1220
+ const files = await this.readDirectoryFiles(afs, directory);
1221
+ const mainModule = args.mainModule || this.detectMainModule(files);
1222
+ const fileUploads = {};
1223
+ for (const [filename, content] of files) fileUploads[filename] = new File([content], filename, { type: this.getContentType(filename) });
1224
+ await this.client.workers.scripts.update(name, {
1225
+ account_id: accountId,
1226
+ metadata: { main_module: mainModule },
1227
+ files: fileUploads
1228
+ });
1229
+ }
1230
+ let versionId = args.versionId;
1231
+ if (!versionId) {
1232
+ const versions$1 = await this.collect(this.client.workers.scripts.versions.list(name, { account_id: accountId }));
1233
+ if (versions$1.length > 0) versionId = versions$1[0].id;
1234
+ }
1235
+ const versions = versionId ? [{
1236
+ version_id: versionId,
1237
+ percentage: 100
1238
+ }] : [];
1239
+ const deployment = await this.client.workers.scripts.deployments.create(name, {
1240
+ account_id: accountId,
1241
+ strategy: "percentage",
1242
+ versions
1243
+ });
1244
+ this.cache.invalidate("list:workers");
1245
+ this.cache.invalidate(`worker:${name}`);
1246
+ return {
1247
+ success: true,
1248
+ data: {
1249
+ deploymentId: deployment?.id,
1250
+ versionId
1251
+ }
1252
+ };
1253
+ } catch (error) {
1254
+ throw mapCloudflareError(error, ctx.path);
1255
+ }
1256
+ }
1257
+ async execWorkerRollback(ctx, _args) {
1258
+ this.ensureNotDestroyed();
1259
+ this.requireReadwrite(ctx.path);
1260
+ const { name } = ctx.params;
1261
+ const accountId = await this.accountId();
1262
+ try {
1263
+ const versions = await this.collect(this.client.workers.scripts.versions.list(name, { account_id: accountId }));
1264
+ if (versions.length < 2) return {
1265
+ success: false,
1266
+ error: {
1267
+ code: "NO_PREVIOUS_VERSION",
1268
+ message: "No previous version to rollback to"
1269
+ }
1270
+ };
1271
+ const previousVersionId = versions[1].id;
1272
+ const deployment = await this.client.workers.scripts.deployments.create(name, {
1273
+ account_id: accountId,
1274
+ strategy: "percentage",
1275
+ versions: [{
1276
+ version_id: previousVersionId,
1277
+ percentage: 100
1278
+ }]
1279
+ });
1280
+ this.cache.invalidate("list:workers");
1281
+ this.cache.invalidate(`worker:${name}`);
1282
+ return {
1283
+ success: true,
1284
+ data: {
1285
+ deploymentId: deployment?.id,
1286
+ versionId: previousVersionId
1287
+ }
1288
+ };
1289
+ } catch (error) {
1290
+ throw mapCloudflareError(error, ctx.path);
1291
+ }
1292
+ }
1293
+ async execWorkerDelete(ctx, args) {
1294
+ this.ensureNotDestroyed();
1295
+ this.requireReadwrite(ctx.path);
1296
+ if (args.confirm !== true) throw confirmationRequired("delete", ctx.path);
1297
+ const { name } = ctx.params;
1298
+ const accountId = await this.accountId();
1299
+ try {
1300
+ await this.client.workers.scripts.delete(name, { account_id: accountId });
1301
+ this.cache.invalidate("list:workers");
1302
+ this.cache.invalidate(`worker:${name}`);
1303
+ return {
1304
+ success: true,
1305
+ data: { deleted: name }
1306
+ };
1307
+ } catch (error) {
1308
+ throw mapCloudflareError(error, ctx.path);
1309
+ }
1310
+ }
1311
+ async searchWorkers(_ctx, query, options) {
1312
+ this.ensureNotDestroyed();
1313
+ const accountId = await this.accountId();
1314
+ const matched = (await this.collect(this.client.workers.scripts.list({ account_id: accountId }))).filter((w) => minimatch(w.id, query));
1315
+ const limit = options?.limit ?? 100;
1316
+ return { data: matched.slice(0, limit).map((w) => ({
1317
+ id: w.id,
1318
+ path: joinURL("/workers", w.id),
1319
+ summary: w.id,
1320
+ meta: {
1321
+ kind: KINDS.WORKER,
1322
+ kinds: [KINDS.WORKER, KINDS.NODE]
1323
+ }
1324
+ })) };
1325
+ }
1326
+ async listKV(_ctx) {
1327
+ this.ensureNotDestroyed();
1328
+ const accountId = await this.accountId();
1329
+ const cacheKey = "list:kv";
1330
+ const cached = this.cache.get(cacheKey);
1331
+ if (cached) return { data: cached };
1332
+ const entries = (await this.collect(this.client.kv.namespaces.list({ account_id: accountId }))).map((ns) => ({
1333
+ id: ns.title,
1334
+ path: joinURL("/kv", ns.title),
1335
+ summary: ns.title,
1336
+ meta: {
1337
+ kind: KINDS.KV_NAMESPACE,
1338
+ kinds: [KINDS.KV_NAMESPACE, KINDS.NODE],
1339
+ childrenCount: 2
1340
+ }
1341
+ }));
1342
+ this.cache.set(cacheKey, entries);
1343
+ return { data: entries };
1344
+ }
1345
+ async listKVNamespaceDir(ctx) {
1346
+ this.ensureNotDestroyed();
1347
+ const { title } = ctx.params;
1348
+ const accountId = await this.accountId();
1349
+ const namespaceId = await this.resolveKVNamespaceId(title);
1350
+ const basePath = joinURL("/kv", title);
1351
+ const keys = await this.collect(this.client.kv.namespaces.keys.list(namespaceId, { account_id: accountId }));
1352
+ return { data: [{
1353
+ id: "metadata.json",
1354
+ path: joinURL(basePath, "metadata.json"),
1355
+ meta: {
1356
+ kind: KINDS.NODE,
1357
+ kinds: [KINDS.NODE],
1358
+ contentType: "application/json"
1359
+ }
1360
+ }, {
1361
+ id: "keys",
1362
+ path: joinURL(basePath, "keys"),
1363
+ meta: {
1364
+ kind: KINDS.NODE,
1365
+ kinds: [KINDS.NODE],
1366
+ childrenCount: keys.length
1367
+ }
1368
+ }] };
1369
+ }
1370
+ async listKVKeys(ctx) {
1371
+ this.ensureNotDestroyed();
1372
+ const { title } = ctx.params;
1373
+ const accountId = await this.accountId();
1374
+ const namespaceId = await this.resolveKVNamespaceId(title);
1375
+ return { data: (await this.collect(this.client.kv.namespaces.keys.list(namespaceId, { account_id: accountId }))).map((k) => ({
1376
+ id: k.name,
1377
+ path: joinURL("/kv", title, "keys", k.name),
1378
+ meta: {
1379
+ kind: KINDS.KV_KEY,
1380
+ kinds: [KINDS.KV_KEY, KINDS.NODE]
1381
+ }
1382
+ })) };
1383
+ }
1384
+ async readKV(_ctx) {
1385
+ this.ensureNotDestroyed();
1386
+ const accountId = await this.accountId();
1387
+ const namespaces = await this.collect(this.client.kv.namespaces.list({ account_id: accountId }));
1388
+ return {
1389
+ id: "kv",
1390
+ path: "/kv",
1391
+ content: "",
1392
+ meta: {
1393
+ kind: KINDS.NODE,
1394
+ kinds: [KINDS.NODE],
1395
+ childrenCount: namespaces.length
1396
+ }
1397
+ };
1398
+ }
1399
+ async readKVNamespaceDir(ctx) {
1400
+ return {
1401
+ id: ctx.params.title,
1402
+ path: joinURL("/kv", ctx.params.title),
1403
+ content: "",
1404
+ meta: {
1405
+ kind: KINDS.KV_NAMESPACE,
1406
+ kinds: [KINDS.KV_NAMESPACE, KINDS.NODE],
1407
+ childrenCount: 2
1408
+ }
1409
+ };
1410
+ }
1411
+ async readKVMetadata(ctx) {
1412
+ this.ensureNotDestroyed();
1413
+ const { title } = ctx.params;
1414
+ const namespaceId = await this.resolveKVNamespaceId(title);
1415
+ const content = JSON.stringify({
1416
+ id: namespaceId,
1417
+ title
1418
+ }, null, 2);
1419
+ return {
1420
+ id: "metadata.json",
1421
+ path: joinURL("/kv", title, "metadata.json"),
1422
+ content,
1423
+ meta: {
1424
+ kind: KINDS.NODE,
1425
+ kinds: [KINDS.NODE],
1426
+ contentType: "application/json"
1427
+ }
1428
+ };
1429
+ }
1430
+ async readKVKeysDir(ctx) {
1431
+ this.ensureNotDestroyed();
1432
+ const { title } = ctx.params;
1433
+ const accountId = await this.accountId();
1434
+ const namespaceId = await this.resolveKVNamespaceId(title);
1435
+ const keys = await this.collect(this.client.kv.namespaces.keys.list(namespaceId, { account_id: accountId }));
1436
+ return {
1437
+ id: "keys",
1438
+ path: joinURL("/kv", title, "keys"),
1439
+ content: "",
1440
+ meta: {
1441
+ kind: KINDS.NODE,
1442
+ kinds: [KINDS.NODE],
1443
+ childrenCount: keys.length
1444
+ }
1445
+ };
1446
+ }
1447
+ async readKVKey(ctx) {
1448
+ this.ensureNotDestroyed();
1449
+ const { title, key } = ctx.params;
1450
+ const accountId = await this.accountId();
1451
+ const namespaceId = await this.resolveKVNamespaceId(title);
1452
+ const decodedKey = key;
1453
+ try {
1454
+ const response = await this.client.kv.namespaces.values.get(namespaceId, decodedKey, { account_id: accountId });
1455
+ if (!response) throw new AFSNotFoundError(`KV key not found: ${decodedKey}`);
1456
+ const content = response instanceof Response ? await response.text() : typeof response === "string" ? response : String(response);
1457
+ return {
1458
+ id: decodedKey,
1459
+ path: joinURL("/kv", title, "keys", key),
1460
+ content,
1461
+ meta: {
1462
+ kind: KINDS.KV_KEY,
1463
+ kinds: [KINDS.KV_KEY, KINDS.NODE]
1464
+ }
1465
+ };
1466
+ } catch (error) {
1467
+ if (error instanceof AFSNotFoundError) throw error;
1468
+ throw mapCloudflareError(error, ctx.path);
1469
+ }
1470
+ }
1471
+ async metaKV(_ctx) {
1472
+ return {
1473
+ id: "kv",
1474
+ path: "/kv/.meta",
1475
+ meta: {
1476
+ kind: KINDS.NODE,
1477
+ kinds: [KINDS.NODE]
1478
+ }
1479
+ };
1480
+ }
1481
+ async metaKVNamespace(ctx) {
1482
+ this.ensureNotDestroyed();
1483
+ const { title } = ctx.params;
1484
+ const accountId = await this.accountId();
1485
+ const namespaceId = await this.resolveKVNamespaceId(title);
1486
+ return {
1487
+ id: title,
1488
+ path: joinURL("/kv", title, ".meta"),
1489
+ meta: {
1490
+ kind: KINDS.KV_NAMESPACE,
1491
+ kinds: [KINDS.KV_NAMESPACE, KINDS.NODE],
1492
+ childrenCount: 2,
1493
+ namespaceId,
1494
+ title,
1495
+ platformRef: generateKVPlatformRef(accountId, namespaceId)
1496
+ }
1497
+ };
1498
+ }
1499
+ async statKV(ctx) {
1500
+ const parts = ctx.path.replace(/\/.stat$/, "").split("/").filter(Boolean);
1501
+ return { data: {
1502
+ id: parts[parts.length - 1] || "kv",
1503
+ path: ctx.path.replace(/\/.stat$/, ""),
1504
+ meta: {
1505
+ kind: KINDS.NODE,
1506
+ kinds: [KINDS.NODE]
1507
+ }
1508
+ } };
1509
+ }
1510
+ async explainKV(_ctx) {
1511
+ return {
1512
+ format: "markdown",
1513
+ content: `# KV Namespaces
1514
+
1515
+ Cloudflare KV is a global, low-latency key-value data store. Each namespace contains:
1516
+ - \`metadata.json\` — Namespace configuration
1517
+ - \`keys/\` — Key-value pairs (read/write/delete)
1518
+ `
1519
+ };
1520
+ }
1521
+ async explainKVNamespace(ctx) {
1522
+ return {
1523
+ format: "markdown",
1524
+ content: `# KV Namespace: ${ctx.params.title}
1525
+
1526
+ ## Structure
1527
+ - \`metadata.json\` — Namespace metadata
1528
+ - \`keys/\` — All key-value pairs
1529
+
1530
+ ## Actions
1531
+ - \`delete\` — Delete this namespace (requires confirm: true)
1532
+ `
1533
+ };
1534
+ }
1535
+ async writeKVKey(ctx, payload) {
1536
+ this.ensureNotDestroyed();
1537
+ this.requireReadwrite(ctx.path);
1538
+ const { title, key } = ctx.params;
1539
+ const accountId = await this.accountId();
1540
+ const namespaceId = await this.resolveKVNamespaceId(title);
1541
+ const decodedKey = key;
1542
+ const content = typeof payload.content === "string" ? payload.content : JSON.stringify(payload.content);
1543
+ try {
1544
+ await this.client.kv.namespaces.values.update(namespaceId, decodedKey, {
1545
+ account_id: accountId,
1546
+ value: content
1547
+ });
1548
+ this.cache.invalidate(`list:kv:${title}`);
1549
+ return { data: {
1550
+ id: decodedKey,
1551
+ path: joinURL("/kv", title, "keys", key),
1552
+ content,
1553
+ meta: {
1554
+ kind: KINDS.KV_KEY,
1555
+ kinds: [KINDS.KV_KEY, KINDS.NODE]
1556
+ }
1557
+ } };
1558
+ } catch (error) {
1559
+ throw mapCloudflareError(error, ctx.path);
1560
+ }
1561
+ }
1562
+ async deleteKVKey(ctx) {
1563
+ this.ensureNotDestroyed();
1564
+ this.requireReadwrite(ctx.path);
1565
+ const { title, key } = ctx.params;
1566
+ const accountId = await this.accountId();
1567
+ const namespaceId = await this.resolveKVNamespaceId(title);
1568
+ const decodedKey = key;
1569
+ try {
1570
+ await this.client.kv.namespaces.values.delete(namespaceId, decodedKey, { account_id: accountId });
1571
+ this.cache.invalidate(`list:kv:${title}`);
1572
+ return { message: `Key '${decodedKey}' deleted` };
1573
+ } catch (error) {
1574
+ throw mapCloudflareError(error, ctx.path);
1575
+ }
1576
+ }
1577
+ async listRootKVActions(_ctx) {
1578
+ if (this.config.accessMode !== "readwrite") throw new AFSNotFoundError("Actions not available in readonly mode");
1579
+ return { data: [{
1580
+ id: "create",
1581
+ path: "/kv/.actions/create",
1582
+ summary: "Create a new KV namespace",
1583
+ meta: {
1584
+ kind: KINDS.EXECUTABLE,
1585
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE],
1586
+ inputSchema: {
1587
+ type: "object",
1588
+ required: ["title"],
1589
+ properties: { title: {
1590
+ type: "string",
1591
+ description: "Namespace title"
1592
+ } }
1593
+ }
1594
+ }
1595
+ }] };
1596
+ }
1597
+ async execKVCreate(ctx, args) {
1598
+ this.ensureNotDestroyed();
1599
+ this.requireReadwrite(ctx.path);
1600
+ const accountId = await this.accountId();
1601
+ const title = args.title;
1602
+ if (!title) return {
1603
+ success: false,
1604
+ error: {
1605
+ code: "VALIDATION_ERROR",
1606
+ message: "title is required"
1607
+ }
1608
+ };
1609
+ try {
1610
+ const namespace = await this.client.kv.namespaces.create({
1611
+ account_id: accountId,
1612
+ title
1613
+ });
1614
+ this.cache.invalidate("list:kv");
1615
+ return {
1616
+ success: true,
1617
+ data: {
1618
+ id: namespace.id,
1619
+ title: namespace.title
1620
+ }
1621
+ };
1622
+ } catch (error) {
1623
+ throw mapCloudflareError(error, ctx.path);
1624
+ }
1625
+ }
1626
+ async listKVActions(ctx) {
1627
+ if (this.config.accessMode !== "readwrite") throw new AFSNotFoundError("Actions not available in readonly mode");
1628
+ return { data: [{
1629
+ id: "delete",
1630
+ path: joinURL(joinURL("/kv", ctx.params.title, ".actions"), "delete"),
1631
+ summary: "Delete namespace (requires confirm: true)",
1632
+ meta: {
1633
+ kind: KINDS.EXECUTABLE,
1634
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE],
1635
+ inputSchema: {
1636
+ type: "object",
1637
+ required: ["confirm"],
1638
+ properties: { confirm: {
1639
+ type: "boolean",
1640
+ description: "Must be true to confirm deletion"
1641
+ } }
1642
+ }
1643
+ }
1644
+ }] };
1645
+ }
1646
+ async execKVDelete(ctx, args) {
1647
+ this.ensureNotDestroyed();
1648
+ this.requireReadwrite(ctx.path);
1649
+ if (args.confirm !== true) throw confirmationRequired("delete", ctx.path);
1650
+ const { title } = ctx.params;
1651
+ const accountId = await this.accountId();
1652
+ const namespaceId = await this.resolveKVNamespaceId(title);
1653
+ try {
1654
+ await this.client.kv.namespaces.delete(namespaceId, { account_id: accountId });
1655
+ this.cache.invalidate("list:kv");
1656
+ this.cache.invalidate(`kv:title:${title}`);
1657
+ return {
1658
+ success: true,
1659
+ data: { deleted: title }
1660
+ };
1661
+ } catch (error) {
1662
+ throw mapCloudflareError(error, ctx.path);
1663
+ }
1664
+ }
1665
+ async searchKV(_ctx, query, options) {
1666
+ this.ensureNotDestroyed();
1667
+ const accountId = await this.accountId();
1668
+ const matched = (await this.collect(this.client.kv.namespaces.list({ account_id: accountId }))).filter((ns) => minimatch(ns.title, query));
1669
+ const limit = options?.limit ?? 100;
1670
+ return { data: matched.slice(0, limit).map((ns) => ({
1671
+ id: ns.title,
1672
+ path: joinURL("/kv", ns.title),
1673
+ summary: ns.title,
1674
+ meta: {
1675
+ kind: KINDS.KV_NAMESPACE,
1676
+ kinds: [KINDS.KV_NAMESPACE, KINDS.NODE]
1677
+ }
1678
+ })) };
1679
+ }
1680
+ async listPages(_ctx) {
1681
+ this.ensureNotDestroyed();
1682
+ const accountId = await this.accountId();
1683
+ const cacheKey = "list:pages";
1684
+ const cached = this.cache.get(cacheKey);
1685
+ if (cached) return { data: cached };
1686
+ const entries = (await this.collect(this.client.pages.projects.list({ account_id: accountId }))).map((p) => ({
1687
+ id: p.name,
1688
+ path: joinURL("/pages", p.name),
1689
+ summary: p.name,
1690
+ meta: {
1691
+ kind: KINDS.PAGES_PROJECT,
1692
+ kinds: [KINDS.PAGES_PROJECT, KINDS.NODE],
1693
+ childrenCount: 3
1694
+ }
1695
+ }));
1696
+ this.cache.set(cacheKey, entries);
1697
+ return { data: entries };
1698
+ }
1699
+ async listPagesProjectDir(ctx) {
1700
+ this.ensureNotDestroyed();
1701
+ const { project } = ctx.params;
1702
+ const accountId = await this.accountId();
1703
+ const basePath = joinURL("/pages", project);
1704
+ const [deployments, domains] = await Promise.all([this.collect(this.client.pages.projects.deployments.list(project, { account_id: accountId })), this.collect(this.client.pages.projects.domains.list(project, { account_id: accountId }))]);
1705
+ return { data: [
1706
+ {
1707
+ id: "metadata.json",
1708
+ path: joinURL(basePath, "metadata.json"),
1709
+ meta: {
1710
+ kind: KINDS.NODE,
1711
+ kinds: [KINDS.NODE],
1712
+ contentType: "application/json"
1713
+ }
1714
+ },
1715
+ {
1716
+ id: "deployments",
1717
+ path: joinURL(basePath, "deployments"),
1718
+ meta: {
1719
+ kind: KINDS.NODE,
1720
+ kinds: [KINDS.NODE],
1721
+ childrenCount: deployments.length
1722
+ }
1723
+ },
1724
+ {
1725
+ id: "domains",
1726
+ path: joinURL(basePath, "domains"),
1727
+ meta: {
1728
+ kind: KINDS.NODE,
1729
+ kinds: [KINDS.NODE],
1730
+ childrenCount: domains.length
1731
+ }
1732
+ }
1733
+ ] };
1734
+ }
1735
+ async listPagesDeployments(ctx) {
1736
+ this.ensureNotDestroyed();
1737
+ const { project } = ctx.params;
1738
+ const accountId = await this.accountId();
1739
+ return { data: (await this.collect(this.client.pages.projects.deployments.list(project, { account_id: accountId }))).map((d) => ({
1740
+ id: d.id,
1741
+ path: joinURL("/pages", project, "deployments", d.id),
1742
+ summary: `${d.environment} - ${d.created_on}`,
1743
+ meta: {
1744
+ kind: KINDS.DEPLOYMENT,
1745
+ kinds: [KINDS.DEPLOYMENT, KINDS.NODE]
1746
+ }
1747
+ })) };
1748
+ }
1749
+ async listPagesDomains(ctx) {
1750
+ this.ensureNotDestroyed();
1751
+ const { project } = ctx.params;
1752
+ const accountId = await this.accountId();
1753
+ return { data: (await this.collect(this.client.pages.projects.domains.list(project, { account_id: accountId }))).map((d) => ({
1754
+ id: d.name,
1755
+ path: joinURL("/pages", project, "domains", d.name),
1756
+ content: JSON.stringify(d, null, 2),
1757
+ meta: {
1758
+ kind: KINDS.DOMAIN,
1759
+ kinds: [KINDS.DOMAIN, KINDS.NODE]
1760
+ }
1761
+ })) };
1762
+ }
1763
+ async readPages(_ctx) {
1764
+ this.ensureNotDestroyed();
1765
+ const accountId = await this.accountId();
1766
+ const projects = await this.collect(this.client.pages.projects.list({ account_id: accountId }));
1767
+ return {
1768
+ id: "pages",
1769
+ path: "/pages",
1770
+ content: "",
1771
+ meta: {
1772
+ kind: KINDS.NODE,
1773
+ kinds: [KINDS.NODE],
1774
+ childrenCount: projects.length
1775
+ }
1776
+ };
1777
+ }
1778
+ async readPagesProjectDir(ctx) {
1779
+ return {
1780
+ id: ctx.params.project,
1781
+ path: joinURL("/pages", ctx.params.project),
1782
+ content: "",
1783
+ meta: {
1784
+ kind: KINDS.PAGES_PROJECT,
1785
+ kinds: [KINDS.PAGES_PROJECT, KINDS.NODE],
1786
+ childrenCount: 3
1787
+ }
1788
+ };
1789
+ }
1790
+ async readPagesMetadata(ctx) {
1791
+ this.ensureNotDestroyed();
1792
+ const { project } = ctx.params;
1793
+ const accountId = await this.accountId();
1794
+ const projectData = await this.client.pages.projects.get(project, { account_id: accountId });
1795
+ if (!projectData) throw new AFSNotFoundError(`Pages project not found: ${project}`);
1796
+ const content = JSON.stringify(projectData, null, 2);
1797
+ return {
1798
+ id: "metadata.json",
1799
+ path: joinURL("/pages", project, "metadata.json"),
1800
+ content,
1801
+ meta: {
1802
+ kind: KINDS.NODE,
1803
+ kinds: [KINDS.NODE],
1804
+ contentType: "application/json"
1805
+ }
1806
+ };
1807
+ }
1808
+ async readPagesDeploymentsDir(ctx) {
1809
+ this.ensureNotDestroyed();
1810
+ const { project } = ctx.params;
1811
+ const accountId = await this.accountId();
1812
+ const deployments = await this.collect(this.client.pages.projects.deployments.list(project, { account_id: accountId }));
1813
+ return {
1814
+ id: "deployments",
1815
+ path: joinURL("/pages", project, "deployments"),
1816
+ content: "",
1817
+ meta: {
1818
+ kind: KINDS.NODE,
1819
+ kinds: [KINDS.NODE],
1820
+ childrenCount: deployments.length
1821
+ }
1822
+ };
1823
+ }
1824
+ async readPagesDeployment(ctx) {
1825
+ this.ensureNotDestroyed();
1826
+ const { project, id } = ctx.params;
1827
+ const accountId = await this.accountId();
1828
+ const deployment = await this.client.pages.projects.deployments.get(project, id, { account_id: accountId });
1829
+ if (!deployment) throw new AFSNotFoundError(`Deployment not found: ${id}`);
1830
+ return {
1831
+ id,
1832
+ path: joinURL("/pages", project, "deployments", id),
1833
+ content: JSON.stringify(deployment, null, 2),
1834
+ meta: {
1835
+ kind: KINDS.DEPLOYMENT,
1836
+ kinds: [KINDS.DEPLOYMENT, KINDS.NODE]
1837
+ }
1838
+ };
1839
+ }
1840
+ async readPagesDomainsDir(ctx) {
1841
+ this.ensureNotDestroyed();
1842
+ const { project } = ctx.params;
1843
+ const accountId = await this.accountId();
1844
+ const domains = await this.collect(this.client.pages.projects.domains.list(project, { account_id: accountId }));
1845
+ return {
1846
+ id: "domains",
1847
+ path: joinURL("/pages", project, "domains"),
1848
+ content: "",
1849
+ meta: {
1850
+ kind: KINDS.NODE,
1851
+ kinds: [KINDS.NODE],
1852
+ childrenCount: domains.length
1853
+ }
1854
+ };
1855
+ }
1856
+ async readPagesDomain(ctx) {
1857
+ this.ensureNotDestroyed();
1858
+ const { project, domain } = ctx.params;
1859
+ const accountId = await this.accountId();
1860
+ const found = (await this.collect(this.client.pages.projects.domains.list(project, { account_id: accountId }))).find((d) => d.name === domain);
1861
+ if (!found) throw new AFSNotFoundError(`Domain not found: ${domain}`);
1862
+ return {
1863
+ id: domain,
1864
+ path: joinURL("/pages", project, "domains", domain),
1865
+ content: JSON.stringify(found, null, 2),
1866
+ meta: {
1867
+ kind: KINDS.DOMAIN,
1868
+ kinds: [KINDS.DOMAIN, KINDS.NODE]
1869
+ }
1870
+ };
1871
+ }
1872
+ async metaPages(_ctx) {
1873
+ return {
1874
+ id: "pages",
1875
+ path: "/pages/.meta",
1876
+ meta: {
1877
+ kind: KINDS.NODE,
1878
+ kinds: [KINDS.NODE]
1879
+ }
1880
+ };
1881
+ }
1882
+ async metaPagesProject(ctx) {
1883
+ this.ensureNotDestroyed();
1884
+ const { project } = ctx.params;
1885
+ const accountId = await this.accountId();
1886
+ const projectData = await this.client.pages.projects.get(project, { account_id: accountId });
1887
+ if (!projectData) throw new AFSNotFoundError(`Pages project not found: ${project}`);
1888
+ const domains = await this.collect(this.client.pages.projects.domains.list(project, { account_id: accountId }));
1889
+ return {
1890
+ id: project,
1891
+ path: joinURL("/pages", project, ".meta"),
1892
+ meta: {
1893
+ kind: KINDS.PAGES_PROJECT,
1894
+ kinds: [KINDS.PAGES_PROJECT, KINDS.NODE],
1895
+ childrenCount: 3,
1896
+ projectName: project,
1897
+ subdomain: projectData.subdomain,
1898
+ productionBranch: projectData.production_branch,
1899
+ latestDeployment: projectData.latest_deployment ? {
1900
+ id: projectData.latest_deployment.id,
1901
+ url: projectData.latest_deployment.url,
1902
+ environment: projectData.latest_deployment.environment,
1903
+ createdOn: projectData.latest_deployment.created_on
1904
+ } : void 0,
1905
+ domainCount: domains.length,
1906
+ platformRef: generatePagesPlatformRef(accountId, project)
1907
+ }
1908
+ };
1909
+ }
1910
+ async statPages(ctx) {
1911
+ const parts = ctx.path.replace(/\/.stat$/, "").split("/").filter(Boolean);
1912
+ return { data: {
1913
+ id: parts[parts.length - 1] || "pages",
1914
+ path: ctx.path.replace(/\/.stat$/, ""),
1915
+ meta: {
1916
+ kind: KINDS.NODE,
1917
+ kinds: [KINDS.NODE]
1918
+ }
1919
+ } };
1920
+ }
1921
+ async explainPages(_ctx) {
1922
+ return {
1923
+ format: "markdown",
1924
+ content: `# Pages Projects
1925
+
1926
+ Cloudflare Pages provides static site hosting with CI/CD. Each project contains:
1927
+ - \`metadata.json\` — Project configuration
1928
+ - \`deployments/\` — Deployment history
1929
+ - \`domains/\` — Custom domains
1930
+ `
1931
+ };
1932
+ }
1933
+ async explainPagesProject(ctx) {
1934
+ return {
1935
+ format: "markdown",
1936
+ content: `# Pages Project: ${ctx.params.project}
1937
+
1938
+ ## Structure
1939
+ - \`metadata.json\` — Project metadata
1940
+ - \`deployments/\` — Deployment history
1941
+ - \`domains/\` — Custom domain mappings
1942
+
1943
+ ## Actions
1944
+ - \`deploy\` — Deploy new version
1945
+ - \`rollback\` — Rollback to a specific deployment
1946
+ - \`delete\` — Delete project (requires confirm: true)
1947
+ `
1948
+ };
1949
+ }
1950
+ async listRootPagesActions(_ctx) {
1951
+ if (this.config.accessMode !== "readwrite") throw new AFSNotFoundError("Actions not available in readonly mode");
1952
+ return { data: [{
1953
+ id: "create",
1954
+ path: "/pages/.actions/create",
1955
+ summary: "Create a new Pages project and deploy files",
1956
+ meta: {
1957
+ kind: KINDS.EXECUTABLE,
1958
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE],
1959
+ inputSchema: {
1960
+ type: "object",
1961
+ required: ["name", "directory"],
1962
+ properties: {
1963
+ name: {
1964
+ type: "string",
1965
+ description: "Project name"
1966
+ },
1967
+ directory: {
1968
+ type: "string",
1969
+ description: "AFS directory path containing static files to deploy"
1970
+ },
1971
+ productionBranch: {
1972
+ type: "string",
1973
+ description: "Production branch name (default: \"main\")"
1974
+ }
1975
+ }
1976
+ }
1977
+ }
1978
+ }] };
1979
+ }
1980
+ async execPagesCreate(ctx, args) {
1981
+ this.ensureNotDestroyed();
1982
+ this.requireReadwrite(ctx.path);
1983
+ const accountId = await this.accountId();
1984
+ const name = args.name;
1985
+ const directory = args.directory;
1986
+ const productionBranch = args.productionBranch || "main";
1987
+ if (!name) return {
1988
+ success: false,
1989
+ error: {
1990
+ code: "VALIDATION_ERROR",
1991
+ message: "name is required"
1992
+ }
1993
+ };
1994
+ if (!directory) return {
1995
+ success: false,
1996
+ error: {
1997
+ code: "VALIDATION_ERROR",
1998
+ message: "directory (AFS path) is required"
1999
+ }
2000
+ };
2001
+ this.validateAfsPath(directory);
2002
+ const afs = this.getAfsFromContext(ctx);
2003
+ try {
2004
+ await this.client.pages.projects.create({
2005
+ account_id: accountId,
2006
+ name,
2007
+ production_branch: productionBranch
2008
+ });
2009
+ const deployment = await this.uploadPagesFiles(name, afs, directory);
2010
+ this.cache.invalidate("list:pages");
2011
+ return {
2012
+ success: true,
2013
+ data: {
2014
+ name,
2015
+ deploymentId: deployment.id,
2016
+ url: deployment.url
2017
+ }
2018
+ };
2019
+ } catch (error) {
2020
+ throw mapCloudflareError(error, ctx.path);
2021
+ }
2022
+ }
2023
+ /**
2024
+ * Upload files to a Pages project via the Direct Upload API.
2025
+ *
2026
+ * Follows the same multi-step flow as wrangler:
2027
+ * 1. Read files from AFS directory
2028
+ * 2. Hash each file with BLAKE3 (base64Content + extension, truncated to 32 hex chars)
2029
+ * 3. Get upload JWT token
2030
+ * 4. Check which files are missing (deduplication)
2031
+ * 5. Upload missing files as JSON with base64 content
2032
+ * 6. Upsert all hashes
2033
+ * 7. Create deployment with manifest
2034
+ */
2035
+ async uploadPagesFiles(projectName, afs, directory) {
2036
+ const accountId = await this.accountId();
2037
+ const apiToken = this.config.apiToken || process.env.CLOUDFLARE_API_TOKEN;
2038
+ if (!apiToken) throw new Error("API token is required for Pages Direct Upload. Set apiToken in config or CLOUDFLARE_API_TOKEN env var.");
2039
+ const baseUrl = "https://api.cloudflare.com/client/v4";
2040
+ const files = await this.readDirectoryFiles(afs, directory);
2041
+ let workerScript;
2042
+ const staticFiles = /* @__PURE__ */ new Map();
2043
+ for (const [filePath, content] of files) if (filePath === "_worker.js") workerScript = content;
2044
+ else staticFiles.set(filePath, content);
2045
+ const { hash: blake3hash } = await import("blake3-wasm");
2046
+ const manifest = {};
2047
+ const uploadEntries = [];
2048
+ for (const [filePath, content] of staticFiles) {
2049
+ const base64Content = Buffer.from(content, "utf-8").toString("base64");
2050
+ const hash = blake3hash(base64Content + (filePath.includes(".") ? filePath.split(".").pop() : "")).toString("hex").slice(0, 32);
2051
+ manifest[`/${filePath}`] = hash;
2052
+ uploadEntries.push({
2053
+ key: hash,
2054
+ value: base64Content,
2055
+ metadata: { contentType: this.getContentType(filePath) },
2056
+ base64: true
2057
+ });
2058
+ }
2059
+ const tokenRes = await fetch(`${baseUrl}/accounts/${accountId}/pages/projects/${projectName}/upload-token`, { headers: { Authorization: `Bearer ${apiToken}` } });
2060
+ if (!tokenRes.ok) throw new Error(`Failed to get upload token: ${tokenRes.status} ${await tokenRes.text()}`);
2061
+ const jwt = (await tokenRes.json()).result?.jwt;
2062
+ if (!jwt) throw new Error("Failed to get upload JWT from Cloudflare");
2063
+ const allHashes = uploadEntries.map((e) => e.key);
2064
+ const checkRes = await fetch(`${baseUrl}/pages/assets/check-missing`, {
2065
+ method: "POST",
2066
+ headers: {
2067
+ Authorization: `Bearer ${jwt}`,
2068
+ "Content-Type": "application/json"
2069
+ },
2070
+ body: JSON.stringify({ hashes: allHashes })
2071
+ });
2072
+ if (!checkRes.ok) throw new Error(`Failed to check missing assets: ${checkRes.status} ${await checkRes.text()}`);
2073
+ const missingHashes = new Set((await checkRes.json()).result || []);
2074
+ const toUpload = uploadEntries.filter((e) => missingHashes.has(e.key));
2075
+ if (toUpload.length > 0) {
2076
+ const uploadRes = await fetch(`${baseUrl}/pages/assets/upload`, {
2077
+ method: "POST",
2078
+ headers: {
2079
+ Authorization: `Bearer ${jwt}`,
2080
+ "Content-Type": "application/json"
2081
+ },
2082
+ body: JSON.stringify(toUpload)
2083
+ });
2084
+ if (!uploadRes.ok) throw new Error(`Failed to upload files: ${uploadRes.status} ${await uploadRes.text()}`);
2085
+ }
2086
+ const upsertRes = await fetch(`${baseUrl}/pages/assets/upsert-hashes`, {
2087
+ method: "POST",
2088
+ headers: {
2089
+ Authorization: `Bearer ${jwt}`,
2090
+ "Content-Type": "application/json"
2091
+ },
2092
+ body: JSON.stringify({ hashes: allHashes })
2093
+ });
2094
+ if (!upsertRes.ok) throw new Error(`Failed to upsert hashes: ${upsertRes.status} ${await upsertRes.text()}`);
2095
+ const formData = new FormData();
2096
+ formData.append("manifest", JSON.stringify(manifest));
2097
+ if (workerScript) formData.append("_worker.js", new Blob([workerScript], { type: "application/javascript+module" }), "_worker.js");
2098
+ const deployRes = await fetch(`${baseUrl}/accounts/${accountId}/pages/projects/${projectName}/deployments`, {
2099
+ method: "POST",
2100
+ headers: { Authorization: `Bearer ${apiToken}` },
2101
+ body: formData
2102
+ });
2103
+ if (!deployRes.ok) throw new Error(`Failed to create deployment: ${deployRes.status} ${await deployRes.text()}`);
2104
+ const { result } = await deployRes.json();
2105
+ return {
2106
+ id: result?.id || "unknown",
2107
+ url: result?.url || `https://${projectName}.pages.dev`
2108
+ };
2109
+ }
2110
+ async listPagesActions(ctx) {
2111
+ if (this.config.accessMode !== "readwrite") throw new AFSNotFoundError("Actions not available in readonly mode");
2112
+ const basePath = joinURL("/pages", ctx.params.project, ".actions");
2113
+ return { data: [
2114
+ {
2115
+ id: "deploy",
2116
+ path: joinURL(basePath, "deploy"),
2117
+ summary: "Deploy new version",
2118
+ meta: {
2119
+ kind: KINDS.EXECUTABLE,
2120
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE]
2121
+ }
2122
+ },
2123
+ {
2124
+ id: "rollback",
2125
+ path: joinURL(basePath, "rollback"),
2126
+ summary: "Rollback to specific deployment",
2127
+ meta: {
2128
+ kind: KINDS.EXECUTABLE,
2129
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE]
2130
+ }
2131
+ },
2132
+ {
2133
+ id: "delete",
2134
+ path: joinURL(basePath, "delete"),
2135
+ summary: "Delete project (requires confirm: true)",
2136
+ meta: {
2137
+ kind: KINDS.EXECUTABLE,
2138
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE],
2139
+ inputSchema: {
2140
+ type: "object",
2141
+ required: ["confirm"],
2142
+ properties: { confirm: {
2143
+ type: "boolean",
2144
+ description: "Must be true to confirm deletion"
2145
+ } }
2146
+ }
2147
+ }
2148
+ },
2149
+ {
2150
+ id: "configure-bindings",
2151
+ path: joinURL(basePath, "configure-bindings"),
2152
+ summary: "Configure Pages Functions bindings (KV, D1, R2, etc.)",
2153
+ meta: {
2154
+ kind: KINDS.EXECUTABLE,
2155
+ kinds: [KINDS.EXECUTABLE, KINDS.NODE],
2156
+ inputSchema: {
2157
+ type: "object",
2158
+ properties: {
2159
+ environment: {
2160
+ type: "string",
2161
+ description: "Environment to configure (default: production)"
2162
+ },
2163
+ kv_namespaces: {
2164
+ type: "object",
2165
+ description: "KV namespace bindings, e.g. {\"KV\":{\"namespace_id\":\"...\"}}"
2166
+ },
2167
+ d1_databases: {
2168
+ type: "object",
2169
+ description: "D1 database bindings, e.g. {\"DB\":{\"id\":\"...\"}}"
2170
+ },
2171
+ r2_buckets: {
2172
+ type: "object",
2173
+ description: "R2 bucket bindings, e.g. {\"BUCKET\":{\"name\":\"...\"}}"
2174
+ },
2175
+ compatibility_date: {
2176
+ type: "string",
2177
+ description: "Compatibility date for Pages Functions"
2178
+ }
2179
+ }
2180
+ }
2181
+ }
2182
+ }
2183
+ ] };
2184
+ }
2185
+ async execPagesDeploy(ctx, args) {
2186
+ this.ensureNotDestroyed();
2187
+ this.requireReadwrite(ctx.path);
2188
+ const { project } = ctx.params;
2189
+ const accountId = await this.accountId();
2190
+ try {
2191
+ const directory = args.directory;
2192
+ if (directory) {
2193
+ this.validateAfsPath(directory);
2194
+ const afs = this.getAfsFromContext(ctx);
2195
+ const deployment$1 = await this.uploadPagesFiles(project, afs, directory);
2196
+ this.cache.invalidate("list:pages");
2197
+ return {
2198
+ success: true,
2199
+ data: {
2200
+ deploymentId: deployment$1.id,
2201
+ url: deployment$1.url
2202
+ }
2203
+ };
2204
+ }
2205
+ const deployment = await this.client.pages.projects.deployments.create(project, { account_id: accountId });
2206
+ this.cache.invalidate("list:pages");
2207
+ return {
2208
+ success: true,
2209
+ data: {
2210
+ deploymentId: deployment?.id,
2211
+ url: deployment?.url
2212
+ }
2213
+ };
2214
+ } catch (error) {
2215
+ throw mapCloudflareError(error, ctx.path);
2216
+ }
2217
+ }
2218
+ async execPagesRollback(ctx, args) {
2219
+ this.ensureNotDestroyed();
2220
+ this.requireReadwrite(ctx.path);
2221
+ const { project } = ctx.params;
2222
+ const accountId = await this.accountId();
2223
+ const deploymentId = args.deploymentId;
2224
+ try {
2225
+ const deployment = await this.client.pages.projects.deployments.create(project, { account_id: accountId });
2226
+ this.cache.invalidate("list:pages");
2227
+ return {
2228
+ success: true,
2229
+ data: {
2230
+ deploymentId: deployment?.id,
2231
+ rolledBackTo: deploymentId
2232
+ }
2233
+ };
2234
+ } catch (error) {
2235
+ throw mapCloudflareError(error, ctx.path);
2236
+ }
2237
+ }
2238
+ async execPagesDelete(ctx, args) {
2239
+ this.ensureNotDestroyed();
2240
+ this.requireReadwrite(ctx.path);
2241
+ if (args.confirm !== true) throw confirmationRequired("delete", ctx.path);
2242
+ const { project } = ctx.params;
2243
+ const accountId = await this.accountId();
2244
+ try {
2245
+ await this.client.pages.projects.delete(project, { account_id: accountId });
2246
+ this.cache.invalidate("list:pages");
2247
+ return {
2248
+ success: true,
2249
+ data: { deleted: project }
2250
+ };
2251
+ } catch (error) {
2252
+ throw mapCloudflareError(error, ctx.path);
2253
+ }
2254
+ }
2255
+ async execPagesConfigureBindings(ctx, args) {
2256
+ this.ensureNotDestroyed();
2257
+ this.requireReadwrite(ctx.path);
2258
+ const { project } = ctx.params;
2259
+ const accountId = await this.accountId();
2260
+ const environment = args.environment || "production";
2261
+ const envConfig = {};
2262
+ if (args.kv_namespaces) envConfig.kv_namespaces = args.kv_namespaces;
2263
+ if (args.d1_databases) envConfig.d1_databases = args.d1_databases;
2264
+ if (args.r2_buckets) envConfig.r2_buckets = args.r2_buckets;
2265
+ if (args.compatibility_date) envConfig.compatibility_date = args.compatibility_date;
2266
+ try {
2267
+ await this.client.pages.projects.edit(project, {
2268
+ account_id: accountId,
2269
+ deployment_configs: { [environment]: envConfig }
2270
+ });
2271
+ this.cache.invalidate("list:pages");
2272
+ return {
2273
+ success: true,
2274
+ data: {
2275
+ project,
2276
+ environment,
2277
+ bindings: Object.keys(envConfig)
2278
+ }
2279
+ };
2280
+ } catch (error) {
2281
+ throw mapCloudflareError(error, ctx.path);
2282
+ }
2283
+ }
2284
+ async searchPages(_ctx, query, options) {
2285
+ this.ensureNotDestroyed();
2286
+ const accountId = await this.accountId();
2287
+ const matched = (await this.collect(this.client.pages.projects.list({ account_id: accountId }))).filter((p) => minimatch(p.name, query));
2288
+ const limit = options?.limit ?? 100;
2289
+ return { data: matched.slice(0, limit).map((p) => ({
2290
+ id: p.name,
2291
+ path: joinURL("/pages", p.name),
2292
+ summary: p.name,
2293
+ meta: {
2294
+ kind: KINDS.PAGES_PROJECT,
2295
+ kinds: [KINDS.PAGES_PROJECT, KINDS.NODE]
2296
+ }
2297
+ })) };
2298
+ }
2299
+ async listByZone(_ctx) {
2300
+ this.ensureNotDestroyed();
2301
+ const cacheKey = "list:by-zone";
2302
+ const cached = this.cache.get(cacheKey);
2303
+ if (cached) return { data: cached };
2304
+ const entries = (await this.collect(this.client.zones.list())).map((z) => ({
2305
+ id: z.name,
2306
+ path: joinURL("/by-zone", z.name),
2307
+ summary: z.name,
2308
+ meta: {
2309
+ kind: KINDS.ZONE,
2310
+ kinds: [KINDS.ZONE, KINDS.NODE],
2311
+ childrenCount: 2
2312
+ }
2313
+ }));
2314
+ this.cache.set(cacheKey, entries);
2315
+ return { data: entries };
2316
+ }
2317
+ async listByZoneDir(ctx) {
2318
+ this.ensureNotDestroyed();
2319
+ const { zone } = ctx.params;
2320
+ const accountId = await this.accountId();
2321
+ const basePath = joinURL("/by-zone", zone);
2322
+ const zoneData = (await this.collect(this.client.zones.list())).find((z) => z.name === zone);
2323
+ let workerCount = 0;
2324
+ if (zoneData) {
2325
+ const routes = await this.collect(this.client.workers.routes.list({ zone_id: zoneData.id }));
2326
+ workerCount = [...new Set(routes.map((r) => r.script).filter(Boolean))].length;
2327
+ }
2328
+ const projects = await this.collect(this.client.pages.projects.list({ account_id: accountId }));
2329
+ let pagesCount = 0;
2330
+ for (const project of projects) if ((await this.collect(this.client.pages.projects.domains.list(project.name, { account_id: accountId }))).some((d) => d.name.endsWith(zone))) pagesCount++;
2331
+ return { data: [{
2332
+ id: "workers",
2333
+ path: joinURL(basePath, "workers"),
2334
+ meta: {
2335
+ kind: KINDS.NODE,
2336
+ kinds: [KINDS.NODE],
2337
+ childrenCount: workerCount
2338
+ }
2339
+ }, {
2340
+ id: "pages",
2341
+ path: joinURL(basePath, "pages"),
2342
+ meta: {
2343
+ kind: KINDS.NODE,
2344
+ kinds: [KINDS.NODE],
2345
+ childrenCount: pagesCount
2346
+ }
2347
+ }] };
2348
+ }
2349
+ async listByZoneWorkers(ctx) {
2350
+ this.ensureNotDestroyed();
2351
+ const { zone } = ctx.params;
2352
+ const zoneData = (await this.collect(this.client.zones.list())).find((z) => z.name === zone);
2353
+ if (!zoneData) throw new AFSNotFoundError(`Zone not found: ${zone}`);
2354
+ const routes = await this.collect(this.client.workers.routes.list({ zone_id: zoneData.id }));
2355
+ return { data: [...new Set(routes.map((r) => r.script).filter(Boolean))].map((name) => ({
2356
+ id: name,
2357
+ path: joinURL("/by-zone", zone, "workers", name),
2358
+ meta: {
2359
+ kind: KINDS.WORKER,
2360
+ kinds: [KINDS.WORKER, KINDS.NODE]
2361
+ }
2362
+ })) };
2363
+ }
2364
+ async listByZonePages(ctx) {
2365
+ this.ensureNotDestroyed();
2366
+ const { zone } = ctx.params;
2367
+ const accountId = await this.accountId();
2368
+ const projects = await this.collect(this.client.pages.projects.list({ account_id: accountId }));
2369
+ const matchingProjects = [];
2370
+ for (const project of projects) if ((await this.collect(this.client.pages.projects.domains.list(project.name, { account_id: accountId }))).some((d) => d.name.endsWith(zone))) matchingProjects.push({
2371
+ id: project.name,
2372
+ path: joinURL("/by-zone", zone, "pages", project.name),
2373
+ meta: {
2374
+ kind: KINDS.PAGES_PROJECT,
2375
+ kinds: [KINDS.PAGES_PROJECT, KINDS.NODE]
2376
+ }
2377
+ });
2378
+ return { data: matchingProjects };
2379
+ }
2380
+ async readByZone(_ctx) {
2381
+ this.ensureNotDestroyed();
2382
+ const zones = await this.collect(this.client.zones.list());
2383
+ return {
2384
+ id: "by-zone",
2385
+ path: "/by-zone",
2386
+ content: "",
2387
+ meta: {
2388
+ kind: KINDS.NODE,
2389
+ kinds: [KINDS.NODE],
2390
+ childrenCount: zones.length
2391
+ }
2392
+ };
2393
+ }
2394
+ async readByZoneDir(ctx) {
2395
+ return {
2396
+ id: ctx.params.zone,
2397
+ path: joinURL("/by-zone", ctx.params.zone),
2398
+ content: "",
2399
+ meta: {
2400
+ kind: KINDS.ZONE,
2401
+ kinds: [KINDS.ZONE, KINDS.NODE],
2402
+ childrenCount: 2
2403
+ }
2404
+ };
2405
+ }
2406
+ async readByZoneWorkersDir(ctx) {
2407
+ this.ensureNotDestroyed();
2408
+ const { zone } = ctx.params;
2409
+ const zoneData = (await this.collect(this.client.zones.list())).find((z) => z.name === zone);
2410
+ if (!zoneData) throw new AFSNotFoundError(`Zone not found: ${zone}`);
2411
+ const routes = await this.collect(this.client.workers.routes.list({ zone_id: zoneData.id }));
2412
+ const workerNames = [...new Set(routes.map((r) => r.script).filter(Boolean))];
2413
+ return {
2414
+ id: "workers",
2415
+ path: joinURL("/by-zone", zone, "workers"),
2416
+ content: "",
2417
+ meta: {
2418
+ kind: KINDS.NODE,
2419
+ kinds: [KINDS.NODE],
2420
+ childrenCount: workerNames.length
2421
+ }
2422
+ };
2423
+ }
2424
+ async readByZoneWorker(ctx) {
2425
+ return {
2426
+ id: ctx.params.name,
2427
+ path: joinURL("/by-zone", ctx.params.zone, "workers", ctx.params.name),
2428
+ content: "",
2429
+ meta: {
2430
+ kind: KINDS.WORKER,
2431
+ kinds: [KINDS.WORKER, KINDS.NODE]
2432
+ }
2433
+ };
2434
+ }
2435
+ async readByZonePagesDir(ctx) {
2436
+ this.ensureNotDestroyed();
2437
+ const { zone } = ctx.params;
2438
+ const accountId = await this.accountId();
2439
+ const projects = await this.collect(this.client.pages.projects.list({ account_id: accountId }));
2440
+ let matchCount = 0;
2441
+ for (const project of projects) if ((await this.collect(this.client.pages.projects.domains.list(project.name, { account_id: accountId }))).some((d) => d.name.endsWith(zone))) matchCount++;
2442
+ return {
2443
+ id: "pages",
2444
+ path: joinURL("/by-zone", zone, "pages"),
2445
+ content: "",
2446
+ meta: {
2447
+ kind: KINDS.NODE,
2448
+ kinds: [KINDS.NODE],
2449
+ childrenCount: matchCount
2450
+ }
2451
+ };
2452
+ }
2453
+ async readByZoneProject(ctx) {
2454
+ return {
2455
+ id: ctx.params.project,
2456
+ path: joinURL("/by-zone", ctx.params.zone, "pages", ctx.params.project),
2457
+ content: "",
2458
+ meta: {
2459
+ kind: KINDS.PAGES_PROJECT,
2460
+ kinds: [KINDS.PAGES_PROJECT, KINDS.NODE]
2461
+ }
2462
+ };
2463
+ }
2464
+ async metaByZone(_ctx) {
2465
+ return {
2466
+ id: "by-zone",
2467
+ path: "/by-zone/.meta",
2468
+ meta: {
2469
+ kind: KINDS.NODE,
2470
+ kinds: [KINDS.NODE]
2471
+ }
2472
+ };
2473
+ }
2474
+ async statByZone(ctx) {
2475
+ const parts = ctx.path.replace(/\/.stat$/, "").split("/").filter(Boolean);
2476
+ return { data: {
2477
+ id: parts[parts.length - 1] || "by-zone",
2478
+ path: ctx.path.replace(/\/.stat$/, ""),
2479
+ meta: {
2480
+ kind: KINDS.NODE,
2481
+ kinds: [KINDS.NODE]
2482
+ }
2483
+ } };
2484
+ }
2485
+ async explainByZone(_ctx) {
2486
+ return {
2487
+ format: "markdown",
2488
+ content: `# By Zone
2489
+
2490
+ Resources grouped by DNS zone. Workers are associated through route bindings, Pages through custom domains.
2491
+ `
2492
+ };
2493
+ }
2494
+ async searchRoot(_ctx, query, options) {
2495
+ this.ensureNotDestroyed();
2496
+ const accountId = await this.accountId();
2497
+ const limit = options?.limit ?? 100;
2498
+ const results = [];
2499
+ const [workers, kvNamespaces, pagesProjects] = await Promise.all([
2500
+ this.collect(this.client.workers.scripts.list({ account_id: accountId })),
2501
+ this.collect(this.client.kv.namespaces.list({ account_id: accountId })),
2502
+ this.collect(this.client.pages.projects.list({ account_id: accountId }))
2503
+ ]);
2504
+ for (const w of workers) if (minimatch(w.id, query) && results.length < limit) results.push({
2505
+ id: w.id,
2506
+ path: joinURL("/workers", w.id),
2507
+ summary: w.id,
2508
+ meta: {
2509
+ kind: KINDS.WORKER,
2510
+ kinds: [KINDS.WORKER, KINDS.NODE]
2511
+ }
2512
+ });
2513
+ for (const ns of kvNamespaces) if (minimatch(ns.title, query) && results.length < limit) results.push({
2514
+ id: ns.title,
2515
+ path: joinURL("/kv", ns.title),
2516
+ summary: ns.title,
2517
+ meta: {
2518
+ kind: KINDS.KV_NAMESPACE,
2519
+ kinds: [KINDS.KV_NAMESPACE, KINDS.NODE]
2520
+ }
2521
+ });
2522
+ for (const p of pagesProjects) if (minimatch(p.name, query) && results.length < limit) results.push({
2523
+ id: p.name,
2524
+ path: joinURL("/pages", p.name),
2525
+ summary: p.name,
2526
+ meta: {
2527
+ kind: KINDS.PAGES_PROJECT,
2528
+ kinds: [KINDS.PAGES_PROJECT, KINDS.NODE]
2529
+ }
2530
+ });
2531
+ return { data: results };
2532
+ }
2533
+ async deleteCatchAll(ctx) {
2534
+ this.ensureNotDestroyed();
2535
+ this.requireReadwrite(ctx.path);
2536
+ throw new AFSNotFoundError(`Cannot delete: ${ctx.path}`);
2537
+ }
2538
+ async destroy() {
2539
+ if (this.destroyed) return;
2540
+ this.destroyed = true;
2541
+ await this.cfClient.destroy();
2542
+ this.cache.clear();
2543
+ }
2544
+ clearCache() {
2545
+ this.cache.clear();
2546
+ }
2547
+ toJSON() {
2548
+ return {
2549
+ name: this.name,
2550
+ description: this.description,
2551
+ accessMode: this.accessMode,
2552
+ accountId: this.config.accountId
2553
+ };
2554
+ }
2555
+ };
2556
+ __decorate([List("/")], AFSCloudflare.prototype, "listRoot", null);
2557
+ __decorate([Read("/.meta/.capabilities")], AFSCloudflare.prototype, "readCapabilities", null);
2558
+ __decorate([Read("/")], AFSCloudflare.prototype, "readRoot", null);
2559
+ __decorate([Read("/WORLD.md")], AFSCloudflare.prototype, "readWorldMd", null);
2560
+ __decorate([Meta("/")], AFSCloudflare.prototype, "metaRoot", null);
2561
+ __decorate([Stat("/"), Stat("/WORLD.md")], AFSCloudflare.prototype, "statRoot", null);
2562
+ __decorate([Explain("/")], AFSCloudflare.prototype, "explainRoot", null);
2563
+ __decorate([
2564
+ List("/WORLD.md"),
2565
+ List("/workers/:name/script.js"),
2566
+ List("/workers/:name/settings.json"),
2567
+ List("/workers/:name/bindings/:binding"),
2568
+ List("/workers/:name/routes/:route+"),
2569
+ List("/workers/:name/cron-triggers/:cron+"),
2570
+ List("/kv/:title/metadata.json"),
2571
+ List("/kv/:title/keys/:key+"),
2572
+ List("/pages/:project/metadata.json"),
2573
+ List("/pages/:project/deployments/:id"),
2574
+ List("/pages/:project/domains/:domain"),
2575
+ List("/by-zone/:zone/workers/:name"),
2576
+ List("/by-zone/:zone/pages/:project")
2577
+ ], AFSCloudflare.prototype, "listFileNode", null);
2578
+ __decorate([Meta("/WORLD.md")], AFSCloudflare.prototype, "metaWorldMd", null);
2579
+ __decorate([Meta("/workers/:name/script.js")], AFSCloudflare.prototype, "metaWorkerScript", null);
2580
+ __decorate([Meta("/workers/:name/settings.json")], AFSCloudflare.prototype, "metaWorkerSettings", null);
2581
+ __decorate([Meta("/workers/:name/bindings")], AFSCloudflare.prototype, "metaWorkerBindings", null);
2582
+ __decorate([Meta("/workers/:name/bindings/:binding")], AFSCloudflare.prototype, "metaWorkerBinding", null);
2583
+ __decorate([Meta("/workers/:name/routes")], AFSCloudflare.prototype, "metaWorkerRoutes", null);
2584
+ __decorate([Meta("/workers/:name/routes/:route+")], AFSCloudflare.prototype, "metaWorkerRoute", null);
2585
+ __decorate([Meta("/workers/:name/cron-triggers")], AFSCloudflare.prototype, "metaWorkerCronTriggers", null);
2586
+ __decorate([Meta("/workers/:name/cron-triggers/:cron+")], AFSCloudflare.prototype, "metaWorkerCronTrigger", null);
2587
+ __decorate([Meta("/kv/:title/metadata.json")], AFSCloudflare.prototype, "metaKVMetadataFile", null);
2588
+ __decorate([Meta("/kv/:title/keys")], AFSCloudflare.prototype, "metaKVKeys", null);
2589
+ __decorate([Meta("/kv/:title/keys/:key+")], AFSCloudflare.prototype, "metaKVKey", null);
2590
+ __decorate([Meta("/pages/:project/metadata.json")], AFSCloudflare.prototype, "metaPagesMetadataFile", null);
2591
+ __decorate([Meta("/pages/:project/deployments")], AFSCloudflare.prototype, "metaPagesDeployments", null);
2592
+ __decorate([Meta("/pages/:project/deployments/:id")], AFSCloudflare.prototype, "metaPagesDeployment", null);
2593
+ __decorate([Meta("/pages/:project/domains")], AFSCloudflare.prototype, "metaPagesDomains", null);
2594
+ __decorate([Meta("/pages/:project/domains/:domain")], AFSCloudflare.prototype, "metaPagesDomain", null);
2595
+ __decorate([Meta("/by-zone/:zone")], AFSCloudflare.prototype, "metaByZoneDir", null);
2596
+ __decorate([Meta("/by-zone/:zone/workers")], AFSCloudflare.prototype, "metaByZoneWorkers", null);
2597
+ __decorate([Meta("/by-zone/:zone/workers/:name")], AFSCloudflare.prototype, "metaByZoneWorker", null);
2598
+ __decorate([Meta("/by-zone/:zone/pages")], AFSCloudflare.prototype, "metaByZonePages", null);
2599
+ __decorate([Meta("/by-zone/:zone/pages/:project")], AFSCloudflare.prototype, "metaByZoneProject", null);
2600
+ __decorate([Actions("")], AFSCloudflare.prototype, "listGlobalActions", null);
2601
+ __decorate([Actions.Exec("", "refresh")], AFSCloudflare.prototype, "execRefresh", null);
2602
+ __decorate([List("/workers")], AFSCloudflare.prototype, "listWorkers", null);
2603
+ __decorate([List("/workers/:name")], AFSCloudflare.prototype, "listWorkerDir", null);
2604
+ __decorate([List("/workers/:name/bindings")], AFSCloudflare.prototype, "listWorkerBindings", null);
2605
+ __decorate([List("/workers/:name/routes")], AFSCloudflare.prototype, "listWorkerRoutes", null);
2606
+ __decorate([List("/workers/:name/cron-triggers")], AFSCloudflare.prototype, "listWorkerCronTriggers", null);
2607
+ __decorate([Read("/workers")], AFSCloudflare.prototype, "readWorkers", null);
2608
+ __decorate([Read("/workers/:name")], AFSCloudflare.prototype, "readWorkerDir", null);
2609
+ __decorate([Read("/workers/:name/bindings")], AFSCloudflare.prototype, "readWorkerBindingsDir", null);
2610
+ __decorate([Read("/workers/:name/routes")], AFSCloudflare.prototype, "readWorkerRoutesDir", null);
2611
+ __decorate([Read("/workers/:name/cron-triggers")], AFSCloudflare.prototype, "readWorkerCronTriggersDir", null);
2612
+ __decorate([Read("/workers/:name/script.js")], AFSCloudflare.prototype, "readWorkerScript", null);
2613
+ __decorate([Read("/workers/:name/settings.json")], AFSCloudflare.prototype, "readWorkerSettings", null);
2614
+ __decorate([Read("/workers/:name/bindings/:binding")], AFSCloudflare.prototype, "readWorkerBinding", null);
2615
+ __decorate([Read("/workers/:name/routes/:route+")], AFSCloudflare.prototype, "readWorkerRoute", null);
2616
+ __decorate([Read("/workers/:name/cron-triggers/:cron+")], AFSCloudflare.prototype, "readWorkerCronTrigger", null);
2617
+ __decorate([Meta("/workers")], AFSCloudflare.prototype, "metaWorkers", null);
2618
+ __decorate([Meta("/workers/:name")], AFSCloudflare.prototype, "metaWorker", null);
2619
+ __decorate([
2620
+ Stat("/workers"),
2621
+ Stat("/workers/:name"),
2622
+ Stat("/workers/:name/script.js"),
2623
+ Stat("/workers/:name/settings.json"),
2624
+ Stat("/workers/:name/bindings"),
2625
+ Stat("/workers/:name/bindings/:binding"),
2626
+ Stat("/workers/:name/routes"),
2627
+ Stat("/workers/:name/routes/:route+"),
2628
+ Stat("/workers/:name/cron-triggers"),
2629
+ Stat("/workers/:name/cron-triggers/:cron+")
2630
+ ], AFSCloudflare.prototype, "statWorkers", null);
2631
+ __decorate([Explain("/workers")], AFSCloudflare.prototype, "explainWorkers", null);
2632
+ __decorate([Explain("/workers/:name")], AFSCloudflare.prototype, "explainWorker", null);
2633
+ __decorate([Write("/workers/:name/script.js")], AFSCloudflare.prototype, "writeWorkerScript", null);
2634
+ __decorate([Write("/workers/:name/settings.json")], AFSCloudflare.prototype, "writeWorkerSettings", null);
2635
+ __decorate([Actions("/workers")], AFSCloudflare.prototype, "listRootWorkerActions", null);
2636
+ __decorate([Actions.Exec("/workers", "create")], AFSCloudflare.prototype, "execWorkerCreate", null);
2637
+ __decorate([Actions("/workers/:name")], AFSCloudflare.prototype, "listWorkerActions", null);
2638
+ __decorate([Actions.Exec("/workers/:name", "deploy")], AFSCloudflare.prototype, "execWorkerDeploy", null);
2639
+ __decorate([Actions.Exec("/workers/:name", "rollback")], AFSCloudflare.prototype, "execWorkerRollback", null);
2640
+ __decorate([Actions.Exec("/workers/:name", "delete")], AFSCloudflare.prototype, "execWorkerDelete", null);
2641
+ __decorate([Search("/workers")], AFSCloudflare.prototype, "searchWorkers", null);
2642
+ __decorate([List("/kv")], AFSCloudflare.prototype, "listKV", null);
2643
+ __decorate([List("/kv/:title")], AFSCloudflare.prototype, "listKVNamespaceDir", null);
2644
+ __decorate([List("/kv/:title/keys")], AFSCloudflare.prototype, "listKVKeys", null);
2645
+ __decorate([Read("/kv")], AFSCloudflare.prototype, "readKV", null);
2646
+ __decorate([Read("/kv/:title")], AFSCloudflare.prototype, "readKVNamespaceDir", null);
2647
+ __decorate([Read("/kv/:title/metadata.json")], AFSCloudflare.prototype, "readKVMetadata", null);
2648
+ __decorate([Read("/kv/:title/keys")], AFSCloudflare.prototype, "readKVKeysDir", null);
2649
+ __decorate([Read("/kv/:title/keys/:key+")], AFSCloudflare.prototype, "readKVKey", null);
2650
+ __decorate([Meta("/kv")], AFSCloudflare.prototype, "metaKV", null);
2651
+ __decorate([Meta("/kv/:title")], AFSCloudflare.prototype, "metaKVNamespace", null);
2652
+ __decorate([
2653
+ Stat("/kv"),
2654
+ Stat("/kv/:title"),
2655
+ Stat("/kv/:title/metadata.json"),
2656
+ Stat("/kv/:title/keys"),
2657
+ Stat("/kv/:title/keys/:key+")
2658
+ ], AFSCloudflare.prototype, "statKV", null);
2659
+ __decorate([Explain("/kv")], AFSCloudflare.prototype, "explainKV", null);
2660
+ __decorate([Explain("/kv/:title")], AFSCloudflare.prototype, "explainKVNamespace", null);
2661
+ __decorate([Write("/kv/:title/keys/:key+")], AFSCloudflare.prototype, "writeKVKey", null);
2662
+ __decorate([Delete("/kv/:title/keys/:key+")], AFSCloudflare.prototype, "deleteKVKey", null);
2663
+ __decorate([Actions("/kv")], AFSCloudflare.prototype, "listRootKVActions", null);
2664
+ __decorate([Actions.Exec("/kv", "create")], AFSCloudflare.prototype, "execKVCreate", null);
2665
+ __decorate([Actions("/kv/:title")], AFSCloudflare.prototype, "listKVActions", null);
2666
+ __decorate([Actions.Exec("/kv/:title", "delete")], AFSCloudflare.prototype, "execKVDelete", null);
2667
+ __decorate([Search("/kv")], AFSCloudflare.prototype, "searchKV", null);
2668
+ __decorate([List("/pages")], AFSCloudflare.prototype, "listPages", null);
2669
+ __decorate([List("/pages/:project")], AFSCloudflare.prototype, "listPagesProjectDir", null);
2670
+ __decorate([List("/pages/:project/deployments")], AFSCloudflare.prototype, "listPagesDeployments", null);
2671
+ __decorate([List("/pages/:project/domains")], AFSCloudflare.prototype, "listPagesDomains", null);
2672
+ __decorate([Read("/pages")], AFSCloudflare.prototype, "readPages", null);
2673
+ __decorate([Read("/pages/:project")], AFSCloudflare.prototype, "readPagesProjectDir", null);
2674
+ __decorate([Read("/pages/:project/metadata.json")], AFSCloudflare.prototype, "readPagesMetadata", null);
2675
+ __decorate([Read("/pages/:project/deployments")], AFSCloudflare.prototype, "readPagesDeploymentsDir", null);
2676
+ __decorate([Read("/pages/:project/deployments/:id")], AFSCloudflare.prototype, "readPagesDeployment", null);
2677
+ __decorate([Read("/pages/:project/domains")], AFSCloudflare.prototype, "readPagesDomainsDir", null);
2678
+ __decorate([Read("/pages/:project/domains/:domain")], AFSCloudflare.prototype, "readPagesDomain", null);
2679
+ __decorate([Meta("/pages")], AFSCloudflare.prototype, "metaPages", null);
2680
+ __decorate([Meta("/pages/:project")], AFSCloudflare.prototype, "metaPagesProject", null);
2681
+ __decorate([
2682
+ Stat("/pages"),
2683
+ Stat("/pages/:project"),
2684
+ Stat("/pages/:project/metadata.json"),
2685
+ Stat("/pages/:project/deployments"),
2686
+ Stat("/pages/:project/deployments/:id"),
2687
+ Stat("/pages/:project/domains"),
2688
+ Stat("/pages/:project/domains/:domain")
2689
+ ], AFSCloudflare.prototype, "statPages", null);
2690
+ __decorate([Explain("/pages")], AFSCloudflare.prototype, "explainPages", null);
2691
+ __decorate([Explain("/pages/:project")], AFSCloudflare.prototype, "explainPagesProject", null);
2692
+ __decorate([Actions("/pages")], AFSCloudflare.prototype, "listRootPagesActions", null);
2693
+ __decorate([Actions.Exec("/pages", "create")], AFSCloudflare.prototype, "execPagesCreate", null);
2694
+ __decorate([Actions("/pages/:project")], AFSCloudflare.prototype, "listPagesActions", null);
2695
+ __decorate([Actions.Exec("/pages/:project", "deploy")], AFSCloudflare.prototype, "execPagesDeploy", null);
2696
+ __decorate([Actions.Exec("/pages/:project", "rollback")], AFSCloudflare.prototype, "execPagesRollback", null);
2697
+ __decorate([Actions.Exec("/pages/:project", "delete")], AFSCloudflare.prototype, "execPagesDelete", null);
2698
+ __decorate([Actions.Exec("/pages/:project", "configure-bindings")], AFSCloudflare.prototype, "execPagesConfigureBindings", null);
2699
+ __decorate([Search("/pages")], AFSCloudflare.prototype, "searchPages", null);
2700
+ __decorate([List("/by-zone")], AFSCloudflare.prototype, "listByZone", null);
2701
+ __decorate([List("/by-zone/:zone")], AFSCloudflare.prototype, "listByZoneDir", null);
2702
+ __decorate([List("/by-zone/:zone/workers")], AFSCloudflare.prototype, "listByZoneWorkers", null);
2703
+ __decorate([List("/by-zone/:zone/pages")], AFSCloudflare.prototype, "listByZonePages", null);
2704
+ __decorate([Read("/by-zone")], AFSCloudflare.prototype, "readByZone", null);
2705
+ __decorate([Read("/by-zone/:zone")], AFSCloudflare.prototype, "readByZoneDir", null);
2706
+ __decorate([Read("/by-zone/:zone/workers")], AFSCloudflare.prototype, "readByZoneWorkersDir", null);
2707
+ __decorate([Read("/by-zone/:zone/workers/:name")], AFSCloudflare.prototype, "readByZoneWorker", null);
2708
+ __decorate([Read("/by-zone/:zone/pages")], AFSCloudflare.prototype, "readByZonePagesDir", null);
2709
+ __decorate([Read("/by-zone/:zone/pages/:project")], AFSCloudflare.prototype, "readByZoneProject", null);
2710
+ __decorate([Meta("/by-zone")], AFSCloudflare.prototype, "metaByZone", null);
2711
+ __decorate([
2712
+ Stat("/by-zone"),
2713
+ Stat("/by-zone/:zone"),
2714
+ Stat("/by-zone/:zone/workers"),
2715
+ Stat("/by-zone/:zone/workers/:name"),
2716
+ Stat("/by-zone/:zone/pages"),
2717
+ Stat("/by-zone/:zone/pages/:project")
2718
+ ], AFSCloudflare.prototype, "statByZone", null);
2719
+ __decorate([Explain("/by-zone")], AFSCloudflare.prototype, "explainByZone", null);
2720
+ __decorate([Search("/")], AFSCloudflare.prototype, "searchRoot", null);
2721
+ __decorate([Delete("/:path+")], AFSCloudflare.prototype, "deleteCatchAll", null);
2722
+
2723
+ //#endregion
2724
+ export { AFSCloudflare };
2725
+ //# sourceMappingURL=cloudflare-afs.mjs.map