{"_id":"@agent-ready-web/cloudflare","_rev":"3-f7ea35e8f74a5d7467ee1e8007e52e9f","name":"@agent-ready-web/cloudflare","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@agent-ready-web/cloudflare","version":"0.1.0","keywords":["agent-readiness","cloudflare-workers","robots","sitemap"],"license":"MIT","_id":"@agent-ready-web/cloudflare@0.1.0","maintainers":[{"name":"alexabushanab","email":"abushanab10+code@gmail.com"}],"homepage":"https://github.com/lexabu/agent-ready-web#readme","bugs":{"url":"https://github.com/lexabu/agent-ready-web/issues"},"dist":{"shasum":"1eff796e333ae15b4dbab3c1913a31178a9264ad","tarball":"https://registry.npmjs.org/@agent-ready-web/cloudflare/-/cloudflare-0.1.0.tgz","fileCount":6,"integrity":"sha512-V2hsMfofF5hUBYcuOUG0O2ACdWL2KtadNwQhUGVAPlCu1+S6KWUFlE5GVRiBJDQUlbZIH9kv+ubCFIo7MTtKiQ==","signatures":[{"sig":"MEQCIH8qYFqkU3V8X1UeV1r8K4vsaOpZtvQM+9EsRFrU2zDvAiBv5BRe6PZnBudII1Hbo9rfb5gD6NYh2X6TP929repQlg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21267},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"2500ed0326e6418be3042897342cf6d8f12e46c6","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"alexabushanab","email":"abushanab10+code@gmail.com"},"repository":{"url":"git+https://github.com/lexabu/agent-ready-web.git","type":"git","directory":"packages/cloudflare"},"_npmVersion":"11.12.1","description":"Cloudflare Workers adapter for agent-ready-web — handleAgentRoutes for robots/sitemap/well-known paths.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","dependencies":{"agent-ready-web":"workspace:*"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"@cloudflare/workers-types":"^4.20241106.0"},"peerDependencies":{"@cloudflare/workers-types":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/cloudflare_0.1.0_1776775642010_0.032287486120532005","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@agent-ready-web/cloudflare","version":"0.1.1","keywords":["agent-readiness","cloudflare-workers","robots","sitemap"],"license":"MIT","_id":"@agent-ready-web/cloudflare@0.1.1","maintainers":[{"name":"alexabushanab","email":"abushanab10+code@gmail.com"}],"homepage":"https://github.com/lexabu/agent-ready-web#readme","bugs":{"url":"https://github.com/lexabu/agent-ready-web/issues"},"dist":{"shasum":"799ce83e98d21222335902cd707d573bed99a0ea","tarball":"https://registry.npmjs.org/@agent-ready-web/cloudflare/-/cloudflare-0.1.1.tgz","fileCount":6,"integrity":"sha512-M6DMy4OckHCyuwrr2AAEBBUig6HjiIw2pm3EP5CJgzupwEQA+S6eYNvioGYWVRbGydY2GrnOaFA5PSlXTjYtGw==","signatures":[{"sig":"MEUCIAE4eYKM4ZHD6KYntNr8j/IV3j7jk9S90flKCl2Rt3XIAiEA5jcB3bXHI1OjhxKIIL6IZzm2plb74rYkOxiWUEw2xkQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21236},"main":"./dist/index.js","type":"module","_from":"file:agent-ready-web-cloudflare-0.1.1.tgz","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"alexabushanab","email":"abushanab10+code@gmail.com"},"_resolved":"/private/var/folders/g3/lcrtdz7s0fd8fxf7dtf56mf80000gn/T/cc123b3c9601d896ec1ce1ae43c23886/agent-ready-web-cloudflare-0.1.1.tgz","_integrity":"sha512-M6DMy4OckHCyuwrr2AAEBBUig6HjiIw2pm3EP5CJgzupwEQA+S6eYNvioGYWVRbGydY2GrnOaFA5PSlXTjYtGw==","repository":{"url":"git+https://github.com/lexabu/agent-ready-web.git","type":"git","directory":"packages/cloudflare"},"_npmVersion":"11.12.1","description":"Cloudflare Workers adapter for agent-ready-web — handleAgentRoutes for robots/sitemap/well-known paths.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","dependencies":{"agent-ready-web":"0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@cloudflare/workers-types":"^4.20241106.0"},"peerDependencies":{"@cloudflare/workers-types":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/cloudflare_0.1.1_1776778790218_0.534592371240284","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@agent-ready-web/cloudflare","version":"0.1.2","description":"Cloudflare Workers adapter for agent-ready-web — handleAgentRoutes for robots/sitemap/well-known paths.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/lexabu/agent-ready-web.git","directory":"packages/cloudflare"},"homepage":"https://github.com/lexabu/agent-ready-web#readme","bugs":{"url":"https://github.com/lexabu/agent-ready-web/issues"},"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"publishConfig":{"access":"public"},"sideEffects":false,"dependencies":{"agent-ready-web":"0.1.1"},"peerDependencies":{"@cloudflare/workers-types":">=4.0.0"},"devDependencies":{"@cloudflare/workers-types":"^4.20241106.0"},"engines":{"node":">=20.0.0"},"keywords":["agent-readiness","cloudflare-workers","robots","sitemap"],"scripts":{"build":"tsup","typecheck":"tsc --noEmit","test":"vitest run"},"_id":"@agent-ready-web/cloudflare@0.1.2","_integrity":"sha512-4e401sAdXIIZk6hzJ4X5ahz857SLGlpUWkoTZuHcM+Bzu4a4mKUMhuI++ybQM22i1qGcxg7Ei5iseNpPLUgC3g==","_resolved":"/private/var/folders/g3/lcrtdz7s0fd8fxf7dtf56mf80000gn/T/e4b84833d413c0fb194f7ef47de30c40/agent-ready-web-cloudflare-0.1.2.tgz","_from":"file:agent-ready-web-cloudflare-0.1.2.tgz","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-4e401sAdXIIZk6hzJ4X5ahz857SLGlpUWkoTZuHcM+Bzu4a4mKUMhuI++ybQM22i1qGcxg7Ei5iseNpPLUgC3g==","shasum":"5d44c06bca73f9676e1a541ff591189dba994462","tarball":"https://registry.npmjs.org/@agent-ready-web/cloudflare/-/cloudflare-0.1.2.tgz","fileCount":6,"unpackedSize":21340,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIB0lHE5SxA8Fdh3oW3YNn2Oqz9H95AYioaHCTBsb/+xWAiEAxuQjjR23SF+ZL1aGpv9Us529AC0gjtcWyRBr85Cc5b4="}]},"_npmUser":{"name":"alexabushanab","email":"abushanab10+code@gmail.com"},"directories":{},"maintainers":[{"name":"alexabushanab","email":"abushanab10+code@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cloudflare_0.1.2_1776858013924_0.059897328574922026"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-21T12:47:21.910Z","modified":"2026-04-22T11:40:14.195Z","0.1.0":"2026-04-21T12:47:22.165Z","0.1.1":"2026-04-21T13:39:50.368Z","0.1.2":"2026-04-22T11:40:14.053Z"},"bugs":{"url":"https://github.com/lexabu/agent-ready-web/issues"},"license":"MIT","homepage":"https://github.com/lexabu/agent-ready-web#readme","keywords":["agent-readiness","cloudflare-workers","robots","sitemap"],"repository":{"type":"git","url":"git+https://github.com/lexabu/agent-ready-web.git","directory":"packages/cloudflare"},"description":"Cloudflare Workers adapter for agent-ready-web — handleAgentRoutes for robots/sitemap/well-known paths.","maintainers":[{"name":"alexabushanab","email":"abushanab10+code@gmail.com"}],"readme":"# agent-ready-web\n\nAgent readiness primitives for Next.js and Cloudflare Workers. Generates spec-compliant `robots.txt`, `sitemap.xml`, `Link:` headers (RFC 8288), `.well-known/api-catalog` (RFC 9727), and `.well-known/agent-skills/index.json` from a single config file.\n\nOne config, three packages:\n\n```\n┌──────────────────────────────┐    ┌────────────────────────┐\n│  agent-readiness.config.ts   │───▶│  agent-ready-web       │ pure generators\n└──────────────┬───────────────┘    └────────────────────────┘\n               │                         ▲\n               ├────────────────────────▶│  @agent-ready-web/next        Next.js App Router\n               │                         │\n               └────────────────────────▶│  @agent-ready-web/cloudflare  Workers handler\n```\n\n## Why\n\nCloudflare's Agent Readiness Score checker at [isitagentready.com](https://isitagentready.com) validates a 15-item checklist across Discoverability, Content, Bot Access Control, API/Auth/MCP discovery, and Commerce. As AI agents become a significant traffic source — Cloudflare reports AI-referred traffic up 7x since January 2025 and AI-attributed orders up 11x — agent legibility has become foundational web infrastructure alongside SEO.\n\nThis package covers the **Foundation + Discovery** subset (items 1, 2, 3, 5, 6, 7, 11). Item 4 (markdown content negotiation) is deferred to v0.2 — see [§ Markdown negotiation](#markdown-negotiation-deferred-to-v02). Items 8–10 and 12–15 are out of scope until use cases emerge.\n\n## Install\n\nPick the adapter for your framework:\n\n```bash\n# Next.js App Router site\npnpm add agent-ready-web @agent-ready-web/next\n\n# Cloudflare Worker\npnpm add agent-ready-web @agent-ready-web/cloudflare\n```\n\nBoth adapters pull `agent-ready-web` transitively; it's listed above so the config file can `import { defineAgentReadiness } from 'agent-ready-web'` directly.\n\n## Quickstart: Next.js\n\nOne config file, five one-liners.\n\n```ts\n// src/agent-readiness.config.ts\nimport { defineAgentReadiness } from 'agent-ready-web';\n\nexport default defineAgentReadiness({\n  siteUrl: 'https://example.com',\n  contentSignals: { 'ai-train': 'yes', search: 'yes', 'ai-input': 'yes' },\n  aiCrawlers: {\n    GPTBot: 'allow',\n    'OAI-SearchBot': 'allow',\n    'Claude-Web': 'allow',\n    'anthropic-ai': 'allow',\n    'Google-Extended': 'allow',\n    PerplexityBot: 'allow',\n    CCBot: 'allow',\n  },\n  userAgents: [{ name: '*', allow: ['/'], disallow: ['/api/', '/admin'] }],\n  sitemap: {\n    urls: async () => [\n      {\n        url: 'https://example.com/',\n        lastModified: '2026-04-21',\n        changeFrequency: 'weekly',\n        priority: 1.0,\n      },\n      { url: 'https://example.com/about' },\n    ],\n  },\n  linkHeaders: {\n    '/': [\n      { href: '/.well-known/api-catalog', rel: 'api-catalog' },\n      { href: '/sitemap.xml', rel: 'sitemap' },\n    ],\n  },\n  apiCatalog: {\n    linkset: [\n      {\n        anchor: 'https://example.com/api/contact',\n        'service-desc': [\n          { href: '/api/openapi.json', type: 'application/openapi+json;version=3.1' },\n        ],\n      },\n    ],\n  },\n  agentSkills: [\n    {\n      id: 'contact',\n      name: 'Contact',\n      description: 'Send a message',\n      manifest: { endpoint: '/api/contact' },\n    },\n  ],\n});\n```\n\n```ts\n// src/app/robots.txt/route.ts\nimport { createRobotsRoute } from '@agent-ready-web/next';\nimport config from '@/agent-readiness.config';\nexport const dynamic = 'force-dynamic';\nexport const GET = createRobotsRoute(config);\n```\n\n```ts\n// src/app/sitemap.ts\nimport { createSitemap } from '@agent-ready-web/next';\nimport config from '@/agent-readiness.config';\nexport const dynamic = 'force-dynamic';\nexport default createSitemap(config);\n```\n\n```ts\n// src/middleware.ts\nimport { createMiddleware } from '@agent-ready-web/next';\nimport configModule from '@/agent-readiness.config';\nexport const middleware = createMiddleware(configModule);\nexport const config = {\n  matcher: [\n    '/((?!_next/static|_next/image|favicon.ico|.*\\\\.(?:png|jpg|jpeg|svg|webp|ico|woff2?)).*)',\n  ],\n};\n```\n\n```ts\n// src/app/.well-known/api-catalog/route.ts\nimport { createApiCatalogRoute } from '@agent-ready-web/next';\nimport config from '@/agent-readiness.config';\nexport const dynamic = 'force-dynamic';\nexport const GET = createApiCatalogRoute(config);\n```\n\n```ts\n// src/app/.well-known/agent-skills/index.json/route.ts\nimport { createAgentSkillsRoute } from '@agent-ready-web/next';\nimport config from '@/agent-readiness.config';\nexport const GET = createAgentSkillsRoute(config);\n```\n\n`createRobots(config)` is also available if you want to use Next.js's built-in `MetadataRoute.Robots` export — but it **cannot emit the `Content-Signal:` directive** because Next's metadata shape has no field for it. Use `createRobotsRoute` when you want the full robots.txt including Content-Signal.\n\n## Quickstart: Cloudflare Workers\n\n```ts\n// src/index.ts\nimport { handleAgentRoutes } from '@agent-ready-web/cloudflare';\nimport config from './agent-readiness.config.js';\n\nexport default {\n  async fetch(request: Request): Promise<Response> {\n    const handled = await handleAgentRoutes(request, config);\n    if (handled) return handled;\n    // ... your normal Worker logic, or:\n    return new Response('Not found', { status: 404 });\n  },\n} satisfies ExportedHandler;\n```\n\n`handleAgentRoutes` returns a `Response` for exactly four paths — `/robots.txt`, `/sitemap.xml`, `/.well-known/api-catalog` (only if `config.apiCatalog` is defined), and `/.well-known/agent-skills/index.json` (only if `config.agentSkills` is defined). For any other path it returns `null`, leaving the Worker's existing logic free to handle it. It never proxies, augments, or buffers responses on other paths.\n\n## Full config reference\n\nEvery field of `AgentReadinessConfig`:\n\n| Field                            | Type                                            | Purpose                                                                                                                                                                                                                     |\n| -------------------------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `siteUrl`                        | `string`                                        | Absolute origin of the site, e.g. `'https://example.com'`. Used to build the `Sitemap:` line in robots.txt and any absolute URLs downstream.                                                                                |\n| `contentSignals`                 | `{ 'ai-train', 'search', 'ai-input' }`          | Emitted as a `Content-Signal:` directive inside the `User-agent: *` stanza (per [draft-romm-aipref-contentsignals](https://datatracker.ietf.org/doc/draft-romm-aipref-contentsignals/)). Values are `'yes'` or `'no'`. See [contentsignals.org](https://contentsignals.org/).                                                                  |\n| `aiCrawlers`                     | `Record<string, 'allow' \\| 'disallow'>`         | Per-bot dispositions appended to `robots.txt` as `User-agent:` stanzas with `Allow: /` or `Disallow: /`. Common keys: `GPTBot`, `OAI-SearchBot`, `Claude-Web`, `anthropic-ai`, `Google-Extended`, `PerplexityBot`, `CCBot`. |\n| `userAgents`                     | `UserAgentRule[]`                               | Human-equivalent `User-agent:` stanzas with `allow` and `disallow` path arrays (plus optional `crawlDelay`). Typically `[{ name: '*', allow: ['/'], disallow: ['/api/', '/admin'] }]`.                                      |\n| `sitemap`                        | `{ urls: () => Promise<SitemapEntry[]> }`       | Async URL source for `/sitemap.xml`. Each entry has `url` plus optional `lastModified`, `changeFrequency`, `priority`.                                                                                                      |\n| `linkHeaders`                    | `Record<pathname, LinkHeaderEntry[]>`           | Per-path `Link:` header directives per RFC 8288. Use to advertise `api-catalog`, `sitemap`, and other rel links from HTML responses. Applied by `createMiddleware`.                                                         |\n| `apiCatalog` (optional)          | `{ linkset: ApiCatalogLinksetItem[] }`          | RFC 9727 linkset served at `/.well-known/api-catalog`. Each item has an `anchor` URL and arrays of `service-desc`, `service-doc`, `service-meta` links. Omit if the site has no public API.                                 |\n| `agentSkills` (optional)         | `AgentSkill[]`                                  | Skills declared to agents. Each skill has `id`, `name`, `description`, `manifest` (arbitrary JSON). Served at `/.well-known/agent-skills/index.json` with auto-computed SHA-256 digests over canonical-JSON manifests.      |\n| `markdownNegotiation` (optional) | `{ enabled: boolean, exclude?, maxBodyBytes? }` | **Not implemented in v0.1.** Setting `enabled: true` causes `createMiddleware` to throw `Not implemented`. See [Markdown negotiation](#markdown-negotiation-deferred-to-v02).                                               |\n\n### Content-Signals classification\n\n- `ai-train=yes` means \"use my content for AI model training\". Set `no` to opt out of training.\n- `search=yes` means \"allow search-style retrieval for citation and grounding\". Most sites want `yes`.\n- `ai-input=yes` means \"allow use as input to user-facing AI features\" (e.g., summaries in chat answers). Related to but distinct from training.\n\n### AI crawler classification gotchas\n\n- `Google-Extended` is Google's **training-only** opt-out token (Bard/Gemini), not a search crawler. Regular `Googlebot` handles Google Search and follows standard `User-agent: *` rules.\n- `Claude-Web` is Anthropic's retrieval bot for Claude search/citation. `anthropic-ai` is Anthropic's **training** bot. On sites with `ai-train=no`, `Claude-Web` can still be allowed (search) while `anthropic-ai` is disallowed (training).\n- `OAI-SearchBot` is OpenAI's search crawler. `GPTBot` is OpenAI's training bot.\n\n## Per-RFC mapping\n\nWhich `isitagentready.com` check each function satisfies:\n\n| isitagentready item              | Spec                                                                                                             | Function                                     |\n| -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |\n| 1. robots.txt present            | [RFC 9309](https://www.rfc-editor.org/rfc/rfc9309)                                                               | `robotsTxt`, `createRobotsRoute`             |\n| 2. sitemap.xml present           | [sitemaps.org 0.9](https://www.sitemaps.org/protocol.html)                                                       | `sitemap`, `createSitemap`                   |\n| 3. Content-Signals in robots.txt | [contentsignals.org](https://contentsignals.org/)                                                                | `robotsTxt`, `createRobotsRoute`             |\n| 4. Markdown negotiation          | [Cloudflare: Markdown for Agents](https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/) | **v0.2**                                     |\n| 5. Link headers                  | [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288)                                                               | `linkHeaders`, `createMiddleware`            |\n| 6. Agent Skills Discovery        | [Cloudflare RFC](https://github.com/cloudflare/agent-skills-discovery-rfc)                                       | `agentSkillsIndex`, `createAgentSkillsRoute` |\n| 7. API Catalog                   | [RFC 9727](https://www.rfc-editor.org/rfc/rfc9727)                                                               | `apiCatalog`, `createApiCatalogRoute`        |\n\n## Markdown negotiation (deferred to v0.2)\n\nMarkdown negotiation requires a post-render response-transformation point that Next.js/Vercel Edge middleware does not provide — middleware runs before rendering and cannot inspect or rewrite the rendered HTML body. The contract below will be implemented in v0.2 via a Cloudflare Worker front layer helper (`@agent-ready-web/cloudflare/markdown-negotiate`).\n\nWhen v0.2 ships, the adapter will transform HTML → Markdown only when **all** of the following hold:\n\n1. Request method is `GET` or `HEAD`.\n2. Request `Accept` header prefers `text/markdown` over `text/html` by proper q-value parsing (not substring match).\n3. Upstream response is `200 OK`.\n4. Upstream response `Content-Type` is `text/html` (any charset).\n5. Upstream response body size does not exceed configured `maxBodyBytes` (default 2 MB). If exceeded, the HTML is returned unchanged.\n6. Request path is not in `markdownNegotiation.exclude` (defaults: `/api`, `/admin`, `/auth`, `/_next`).\n7. `Vary: Accept` is always emitted on eligible paths regardless of which variant is served. The HTML path preserves streaming via `new Response(body, { ...init, headers })` without reading the body stream.\n\nSetting `config.markdownNegotiation.enabled = true` in v0.1 causes `createMiddleware` to throw `Not implemented` so that accidental early opt-in is caught at boot rather than silently returning HTML forever.\n\n## Contributing\n\n- Fork, clone, `pnpm install`.\n- Tests: `pnpm run test`. TDD is expected — write a failing test before adding behavior.\n- Format: `pnpm run format`. Lint: `pnpm run lint`. Typecheck: `pnpm run typecheck`.\n- Build: `pnpm -r --filter \"./packages/*\" run build`.\n- Pack smoke: `./scripts/pack-smoke.sh` (packs each package, installs tarballs into scratch copies of the examples, runs their test suites).\n- Before opening a PR, add a changeset: `pnpm changeset`.\n- The CI matrix runs on Node 20.x and 22.x with pnpm 9.x. All checks must be green before merge.\n- Releases are driven by [changesets](https://github.com/changesets/changesets). A PR with changesets is version-bumped and published automatically when merged to `main` via npm OIDC provenance — no long-lived tokens.\n\n## License\n\nMIT © [lexabu](https://github.com/lexabu)\n","readmeFilename":"README.md"}