@relvehq/agent-ready 0.0.0-stage → 0.2.1
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.
- package/LICENSE +21 -0
- package/README.md +338 -3
- package/SETUP.md +337 -0
- package/dist/bin/agent-ready.d.ts +23 -0
- package/dist/bin/agent-ready.js +350 -0
- package/dist/cli/init.d.ts +70 -0
- package/dist/cli/init.js +366 -0
- package/dist/core/accept.d.ts +8 -0
- package/dist/core/accept.js +76 -0
- package/dist/core/config.d.ts +145 -0
- package/dist/core/config.js +103 -0
- package/dist/core/convert.d.ts +22 -0
- package/dist/core/convert.js +334 -0
- package/dist/core/detect.d.ts +30 -0
- package/dist/core/detect.js +82 -0
- package/dist/core/easy.d.ts +19 -0
- package/dist/core/easy.js +117 -0
- package/dist/core/enrich.d.ts +30 -0
- package/dist/core/enrich.js +155 -0
- package/dist/core/llms.d.ts +11 -0
- package/dist/core/llms.js +71 -0
- package/dist/core/report.d.ts +15 -0
- package/dist/core/report.js +46 -0
- package/dist/core/resolve.d.ts +43 -0
- package/dist/core/resolve.js +204 -0
- package/dist/core/respond.d.ts +18 -0
- package/dist/core/respond.js +59 -0
- package/dist/core/sitemap.d.ts +31 -0
- package/dist/core/sitemap.js +140 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +76 -0
- package/dist/next/dashboard.d.ts +14 -0
- package/dist/next/dashboard.js +56 -0
- package/dist/next/index.d.ts +42 -0
- package/dist/next/index.js +100 -0
- package/dist/next/route.d.ts +26 -0
- package/dist/next/route.js +167 -0
- package/dist/node/index.d.ts +8 -0
- package/dist/node/index.js +191 -0
- package/dist/proof/audit.d.ts +47 -0
- package/dist/proof/audit.js +253 -0
- package/dist/proof/bots.d.ts +24 -0
- package/dist/proof/bots.js +60 -0
- package/dist/proof/dashboard.d.ts +8 -0
- package/dist/proof/dashboard.js +77 -0
- package/dist/proof/hits.d.ts +18 -0
- package/dist/proof/hits.js +51 -0
- package/dist/proof/jsonl.d.ts +5 -0
- package/dist/proof/jsonl.js +80 -0
- package/dist/proof/stats.d.ts +39 -0
- package/dist/proof/stats.js +127 -0
- package/dist/refine/check.d.ts +17 -0
- package/dist/refine/check.js +134 -0
- package/dist/refine/extract.d.ts +7 -0
- package/dist/refine/extract.js +120 -0
- package/dist/refine/file-store.d.ts +6 -0
- package/dist/refine/file-store.js +87 -0
- package/dist/refine/model.d.ts +30 -0
- package/dist/refine/model.js +105 -0
- package/dist/refine/prompts.d.ts +49 -0
- package/dist/refine/prompts.js +178 -0
- package/dist/refine/refine.d.ts +38 -0
- package/dist/refine/refine.js +75 -0
- package/dist/refine/run.d.ts +52 -0
- package/dist/refine/run.js +66 -0
- package/dist/refine/types.d.ts +17 -0
- package/dist/refine/types.js +8 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-admin.php +363 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-converter.php +762 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-llms.php +192 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-markdown-builder.php +447 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-negotiation.php +334 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-response.php +226 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-router.php +421 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-settings.php +115 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-store.php +543 -0
- package/dist/wp/relve-agent-ready/includes/index.php +2 -0
- package/dist/wp/relve-agent-ready/index.php +2 -0
- package/dist/wp/relve-agent-ready/readme.txt +75 -0
- package/dist/wp/relve-agent-ready/relve-agent-ready.php +70 -0
- package/dist/wp/relve-agent-ready/uninstall.php +24 -0
- package/package.json +64 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Relve
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,338 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# @relvehq/agent-ready
|
|
2
|
+
|
|
3
|
+
Make your site agent-ready. When an AI agent (ChatGPT, Claude, Perplexity,
|
|
4
|
+
Copilot…) asks for one of your pages as Markdown, it gets a clean, small
|
|
5
|
+
Markdown version instead of 50,000+ tokens of HTML. Browsers get exactly what
|
|
6
|
+
they got before.
|
|
7
|
+
|
|
8
|
+
- **Serve**: Markdown on `Accept: text/markdown`, on `<page>.md` and on
|
|
9
|
+
`?format=md`, plus a generated [`/llms.txt`](https://llmstxt.org). No AI
|
|
10
|
+
runs per request: it is plain code, in milliseconds.
|
|
11
|
+
- **Refine** (optional, with your own prompt files): an AI model writes a
|
|
12
|
+
better Markdown file for each page ahead of time, a script checks every
|
|
13
|
+
number and URL against the page,
|
|
14
|
+
and unchanged pages are skipped on the next run.
|
|
15
|
+
- **Proof**: a free checker that scores any site out of 100, and a dashboard
|
|
16
|
+
of which AI agents read which pages.
|
|
17
|
+
|
|
18
|
+
On a real tool page: raw HTML 97,944 tokens, a generic converter 4,618, a
|
|
19
|
+
refined file 1,695.
|
|
20
|
+
|
|
21
|
+
**Start here:** the onboarding guide at <https://relvehq.com/agent-ready>
|
|
22
|
+
(every stack, the WordPress plugin download, and how to test it).
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- Node.js 20 or newer
|
|
27
|
+
- For the Next.js adapter: Next.js 13.4+ with the App Router
|
|
28
|
+
- Anything else: the core is plain `Request` / `Response` (see "Other frameworks")
|
|
29
|
+
|
|
30
|
+
## Quick start: one command
|
|
31
|
+
|
|
32
|
+
In your project's root:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
npx @relvehq/agent-ready init
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
It detects Next.js, Node servers and WordPress, and sets up the one it finds:
|
|
39
|
+
|
|
40
|
+
| It finds | It does |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `app/` or `src/app/` and `next` in `package.json` | writes the Next.js files (below) |
|
|
43
|
+
| `wp-config.php` or `wp-content/` | copies the bundled plugin to `wp-content/plugins/relve-agent-ready/`, then tells you to activate it and tick Enabled on Settings > Agent Ready (an existing copy is kept unless you add `--force`) |
|
|
44
|
+
| `express`, `connect`, `polka`, `fastify`, `@nestjs/core`, `h3` or `nitropack` in `package.json` | writes nothing; prints the exact lines for that framework and the env vars |
|
|
45
|
+
| none of these | prints what it looked for and the guide's URL, and exits 1 |
|
|
46
|
+
|
|
47
|
+
`--stack next|node|wordpress` skips detection; `--dry-run` shows the plan and
|
|
48
|
+
writes nothing. Step-by-step for every stack: [SETUP.md](./SETUP.md).
|
|
49
|
+
|
|
50
|
+
### Next.js
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
npm install @relvehq/agent-ready
|
|
54
|
+
npx agent-ready init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`init` looks at your project (`app/` or `src/app/`, TypeScript or JavaScript)
|
|
58
|
+
and writes:
|
|
59
|
+
|
|
60
|
+
| File | What it holds |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `agent-ready.config.ts` | `export default createAgentReady()`, smart defaults |
|
|
63
|
+
| `middleware.ts` | `export default withAgentReady(agentReady)` and the matcher (only when you have no middleware yet) |
|
|
64
|
+
| `app/%5F%5Fmd/[[...path]]/route.ts` | the internal route that builds the Markdown (`%5F` is `_`) |
|
|
65
|
+
| `.env.local` | `AGENT_READY_ENABLED=1` and `NEXT_PUBLIC_SITE_URL=http://localhost:3000` |
|
|
66
|
+
|
|
67
|
+
It never edits a file you already have. If you have a middleware, it prints
|
|
68
|
+
the two lines to add:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { withAgentReady } from "@relvehq/agent-ready/next";
|
|
72
|
+
import agentReady from "./agent-ready.config";
|
|
73
|
+
export default withAgentReady(agentReady, yourMiddleware);
|
|
74
|
+
// and add to your matcher: "/((?!_next/).+\\.md)", "/llms.txt"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Running `init` again changes nothing; `--dry-run` shows the plan.
|
|
78
|
+
|
|
79
|
+
Check it with the dev server running:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
curl -i -H "Accept: text/markdown" http://localhost:3000/
|
|
83
|
+
curl -i http://localhost:3000/index.md
|
|
84
|
+
curl -i http://localhost:3000/llms.txt
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
In production set `AGENT_READY_ENABLED=1` and `NEXT_PUBLIC_SITE_URL=https://your-site.com`
|
|
88
|
+
on your host. Without a site URL the package stays off (it never fails your build).
|
|
89
|
+
|
|
90
|
+
### Node server and WordPress
|
|
91
|
+
|
|
92
|
+
For a Node server, `init` prints the lines for your framework (Express,
|
|
93
|
+
Connect, Polka, Nest: `app.use(...)`; Fastify through `@fastify/middie`;
|
|
94
|
+
Nitro and h3 through `fromNodeMiddleware`), and the two env vars:
|
|
95
|
+
`AGENT_READY_ENABLED=1` and `SITE_URL=https://your-site.com`. See "Other
|
|
96
|
+
frameworks" below. For WordPress, the plugin ships inside this package
|
|
97
|
+
(without its tests), so `init` needs no download.
|
|
98
|
+
|
|
99
|
+
### What `createAgentReady()` works out on its own
|
|
100
|
+
|
|
101
|
+
| Setting | Default |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `site` | the first set of `NEXT_PUBLIC_SITE_URL`, `SITE_URL`, `NEXT_PUBLIC_APP_URL`, `URL` (Netlify), `VERCEL_PROJECT_PRODUCTION_URL`, `VERCEL_URL` |
|
|
104
|
+
| `enabled` | `AGENT_READY_ENABLED` is `1` or `true` |
|
|
105
|
+
| `exclude` | `/api`, `/_next`, auth and admin paths, plus common private pages (account, checkout, cart, dashboard, settings, billing…) and yours; `excludeCommon: false` drops the common list |
|
|
106
|
+
| `llms` | `/llms.txt` built from your `sitemap.xml`; `llms: false` for none |
|
|
107
|
+
| `onHit` | sends each visit to a dashboard when `AGENT_READY_KEY` and `AGENT_READY_ENDPOINT` are set |
|
|
108
|
+
|
|
109
|
+
Anything you pass wins:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
export default createAgentReady({
|
|
113
|
+
exclude: ["/members"],
|
|
114
|
+
templates: { "/blog/:slug": async ({ params }) => renderPostMarkdown(params.slug) },
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## How an agent asks
|
|
119
|
+
|
|
120
|
+
| Ask | Example |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `Accept` header | `Accept: text/markdown`, preferred over (or tied with) `text/html` |
|
|
123
|
+
| `.md` URL | `/pricing.md`, `/index.md` for `/` |
|
|
124
|
+
| query | `/pricing?format=md` |
|
|
125
|
+
|
|
126
|
+
A browser's `Accept`, a bare `*/*`, or `text/markdown` with a lower q-value
|
|
127
|
+
than `text/html` is not a Markdown request. The user agent is never used to
|
|
128
|
+
choose what to serve: that would be cloaking.
|
|
129
|
+
|
|
130
|
+
## How it works
|
|
131
|
+
|
|
132
|
+
1. **Detect** (middleware, Edge): is this a Markdown request? If not, nothing changes.
|
|
133
|
+
2. **Rewrite** to the internal route `/__md/<path>`; the public URL stays the same.
|
|
134
|
+
3. **Resolve** (Node route), first hit wins:
|
|
135
|
+
1. a **template** for the route (`"/blog/:slug"`), written from your data
|
|
136
|
+
2. a **stored** file (`store.get(path)`), e.g. one written by `refine`
|
|
137
|
+
3. **convert**: fetch the page's own HTML (public, no cookies) and turn its
|
|
138
|
+
main content into Markdown (Readability + turndown, GFM tables). Under 200
|
|
139
|
+
characters of real text is discarded.
|
|
140
|
+
4. nothing usable: an `Accept` request gets the normal HTML page, a `.md`
|
|
141
|
+
request gets a 404
|
|
142
|
+
4. **Enrich**: YAML front matter (title, canonical, source), facts from JSON-LD, token count.
|
|
143
|
+
5. **Respond**:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
Content-Type: text/markdown; charset=utf-8
|
|
147
|
+
Vary: Accept
|
|
148
|
+
Link: <https://your-site.com/pricing>; rel="canonical"
|
|
149
|
+
X-Robots-Tag: noindex (search engines keep indexing the HTML page)
|
|
150
|
+
x-markdown-tokens: 314
|
|
151
|
+
ETag: W/"…" (If-None-Match → 304)
|
|
152
|
+
Cache-Control: see "Caching"
|
|
153
|
+
x-agent-ready-source: native | stored | convert
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
6. **Measure**: one `HitEvent` per request to `onHit`, fire-and-forget.
|
|
157
|
+
|
|
158
|
+
## Caching behind a CDN
|
|
159
|
+
|
|
160
|
+
Most CDNs (CloudFront included) don't vary their cache on `Accept`, so:
|
|
161
|
+
|
|
162
|
+
- Responses negotiated by `Accept` use `negotiatedCacheControl`, default
|
|
163
|
+
`private, max-age=300`. A shared cache never stores them, so a browser is
|
|
164
|
+
never served Markdown.
|
|
165
|
+
- `.md`, `?format=md` and `llms.txt` are their own URLs and use `cacheControl`,
|
|
166
|
+
default `public, s-maxage=300, stale-while-revalidate=86400`.
|
|
167
|
+
- If the CDN already has the HTML page cached, an `Accept` request may get that
|
|
168
|
+
HTML. Safe, just not Markdown. `.md` URLs always work; reliable negotiation
|
|
169
|
+
needs `Accept` in the CDN cache key.
|
|
170
|
+
|
|
171
|
+
## Refine: better Markdown, written ahead of time
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
npx agent-ready refine --site https://your-site.com --prompts ./prompts --sitemap --limit 20 --dry-run
|
|
175
|
+
npx agent-ready refine --site https://your-site.com --prompts ./prompts --site-name "Your Name" --sitemap --limit 20
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
For each page: fetch it, extract its text, links, JSON-LD and a fingerprint;
|
|
179
|
+
skip it if the fingerprint matches the saved file; otherwise fill the prompt,
|
|
180
|
+
call the model once, check the result, retry once with the problems listed,
|
|
181
|
+
and save `<out>/<path>.md` plus `<out>/<path>.json` (default out:
|
|
182
|
+
`.agent-ready/md`). `--dry-run` only reads pages: no model call, nothing written.
|
|
183
|
+
|
|
184
|
+
| Setting | |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `OPENAI_API_KEY` | OpenAI, default model `gpt-4o` |
|
|
187
|
+
| `ANTHROPIC_API_KEY` | Claude, default model `claude-opus-5-5` |
|
|
188
|
+
| `AGENT_READY_MODEL` | any other model id, e.g. `gpt-4o-mini` |
|
|
189
|
+
|
|
190
|
+
Keys are read from `.env.local` and `.env`. Serve the files from the route
|
|
191
|
+
file (`fileStore` is Node only, so keep it out of `agent-ready.config.ts`,
|
|
192
|
+
which the Edge middleware imports):
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
// app/%5F%5Fmd/[[...path]]/route.ts
|
|
196
|
+
import { createMarkdownRoute } from "@relvehq/agent-ready/next/route";
|
|
197
|
+
import { fileStore } from "@relvehq/agent-ready/refine/file-store";
|
|
198
|
+
import agentReady from "../../../agent-ready.config";
|
|
199
|
+
export const { GET, HEAD } = createMarkdownRoute({ ...agentReady, store: fileStore(".agent-ready/md") });
|
|
200
|
+
export const runtime = "nodejs";
|
|
201
|
+
export const dynamic = "force-dynamic";
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
On serverless hosts (Vercel, Amplify) the folder must be bundled with the
|
|
205
|
+
route (`outputFileTracingIncludes`); see [SETUP.md](./SETUP.md).
|
|
206
|
+
|
|
207
|
+
**The check** rejects a file when a number or URL in it is not on the page, a
|
|
208
|
+
required section is missing, it has more or fewer than one H1, HTML tags, a
|
|
209
|
+
wrong fingerprint, or it is over the token budget. It cannot catch a reworded
|
|
210
|
+
claim, so read a sample before you switch on.
|
|
211
|
+
|
|
212
|
+
**The prompt.** The package ships none. Point `refine` at a folder of prompt
|
|
213
|
+
files with `--prompts <dir>` (several: `a,b`) or the `AGENT_READY_PROMPTS`
|
|
214
|
+
variable (`.env.local`, `.env` or the shell); without one it stops before
|
|
215
|
+
fetching anything. A prompt file is Markdown with `## SYSTEM`, `## USER`
|
|
216
|
+
(`{{PAGE_URL}}`, `{{PAGE_TEXT}}`, `{{PAGE_LINKS}}`, `{{JSON_LD}}`,
|
|
217
|
+
`{{SOURCE_HASH}}`) and a `## Settings` table (`page_type`, `version`,
|
|
218
|
+
`required_sections`, `token_budget`). A `page_type` of `page` takes every
|
|
219
|
+
path no other prompt claims; for it the code guesses the page's kind from its
|
|
220
|
+
URL and JSON-LD (home, product, pricing, article, docs, listing, event, about,
|
|
221
|
+
contact, legal, other) and passes the guess as `{{PAGE_KIND_HINT}}`.
|
|
222
|
+
|
|
223
|
+
## Proof
|
|
224
|
+
|
|
225
|
+
### Free checker
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
npx agent-ready check https://any-site.com/pricing
|
|
229
|
+
npx agent-ready check any-site.com --json
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Read-only. Scores the site out of 100 (A to F): Markdown for `Accept` (25),
|
|
233
|
+
a `.md` URL (15), `/llms.txt` (15), robots.txt letting AI answer bots in (15),
|
|
234
|
+
a sitemap (10), JSON-LD (10), `Vary: Accept` (5), a canonical (5). Each failing
|
|
235
|
+
check says what to do, and it compares the tokens an agent reads today with
|
|
236
|
+
what agent-ready would serve. In code: `auditSite(url)`.
|
|
237
|
+
|
|
238
|
+
### Dashboard
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
// agent-ready.config.ts
|
|
242
|
+
import { createAgentReady, memoryHitStore } from "@relvehq/agent-ready";
|
|
243
|
+
export const hits = memoryHitStore();
|
|
244
|
+
export default createAgentReady({ hits });
|
|
245
|
+
|
|
246
|
+
// app/agent-ready-dashboard/route.ts
|
|
247
|
+
import { createDashboardRoute } from "@relvehq/agent-ready/next/dashboard";
|
|
248
|
+
import { hits } from "../../agent-ready.config";
|
|
249
|
+
export const { GET } = createDashboardRoute({ store: hits, site: "your-site.com" });
|
|
250
|
+
export const dynamic = "force-dynamic";
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Open `/agent-ready-dashboard?key=<AGENT_READY_DASHBOARD_KEY>` (add
|
|
254
|
+
`&format=json` for the data, `&days=7` for a shorter window). Without a key
|
|
255
|
+
configured the route answers 404. It shows visits per day, who is reading (AI
|
|
256
|
+
assistants, AI search, training bots, search engines), top pages, how agents
|
|
257
|
+
asked, where the Markdown came from, tokens served and response time.
|
|
258
|
+
|
|
259
|
+
A `HitStore` is two methods, `add` and `list`. `memoryHitStore()` suits a
|
|
260
|
+
long-running server; `jsonlHitStore(file)` (`@relvehq/agent-ready/proof/jsonl`)
|
|
261
|
+
writes a log that `npx agent-ready stats --log <file>` summarises. On
|
|
262
|
+
serverless hosts (Vercel, Amplify) memory and local files don't last between
|
|
263
|
+
requests: implement `HitStore` on your database.
|
|
264
|
+
|
|
265
|
+
## Other frameworks
|
|
266
|
+
|
|
267
|
+
Express, Connect, Polka, Fastify (via `@fastify/middie`) or a plain `http`
|
|
268
|
+
server: one middleware. Mount it before your static files and routes:
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import express from "express";
|
|
272
|
+
import { createAgentReady } from "@relvehq/agent-ready";
|
|
273
|
+
import { agentReadyNode } from "@relvehq/agent-ready/node";
|
|
274
|
+
|
|
275
|
+
const app = express();
|
|
276
|
+
app.use(agentReadyNode(createAgentReady({ site: "https://your-site.com" })));
|
|
277
|
+
// app.use(express.static("public")); your routes...
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
It answers Markdown requests and calls `next()` for everything else (and for
|
|
281
|
+
an `Accept` request when no Markdown is available, so the page's HTML is
|
|
282
|
+
served). Plain `http`: `http.createServer((req, res) => md(req, res, () => app(req, res)))`.
|
|
283
|
+
|
|
284
|
+
On other runtimes (Bun, Deno, Workers, Hono…) the core uses only `Request` and
|
|
285
|
+
`Response`. Detect, enrich and respond run anywhere; `resolveMarkdown` (its
|
|
286
|
+
convert step) needs Node, so on Edge-only runtimes use templates and a store
|
|
287
|
+
with `fallback: "none"`:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
import { defineAgentReady, wantsMarkdown, enrich, markdownResponse } from "@relvehq/agent-ready";
|
|
291
|
+
import { resolveMarkdown } from "@relvehq/agent-ready/core/resolve"; // Node only
|
|
292
|
+
|
|
293
|
+
const cfg = defineAgentReady({ site: "https://your-site.com", enabled: true });
|
|
294
|
+
const d = wantsMarkdown(request);
|
|
295
|
+
if (d.markdown) {
|
|
296
|
+
const r = await resolveMarkdown(cfg, { path: d.path, search: d.search, request });
|
|
297
|
+
if (r.doc) {
|
|
298
|
+
const { body, tokens } = enrich(r.doc, { canonical: cfg.site + d.path, sourceUrl: r.sourceUrl, source: r.source! });
|
|
299
|
+
return markdownResponse(body, { request, canonical: cfg.site + d.path, tokens, cacheControl: cfg.cacheControl });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
`@relvehq/agent-ready` and `@relvehq/agent-ready/next` are Edge-safe.
|
|
305
|
+
`/node`, `/next/route`, `/core/resolve`, `/core/convert` and `/refine/*` need Node.
|
|
306
|
+
|
|
307
|
+
## Config reference (`defineAgentReady` / `createAgentReady`)
|
|
308
|
+
|
|
309
|
+
| Key | Default | Notes |
|
|
310
|
+
|---|---|---|
|
|
311
|
+
| `site` | required (`createAgentReady`: from env) | public origin, for canonicals and absolute links |
|
|
312
|
+
| `enabled` | `false` | boolean or `(request) => boolean` |
|
|
313
|
+
| `exclude` | `[]` | path prefixes or RegExps, added to the always-excluded list |
|
|
314
|
+
| `templates` | `{}` | `{ "/blog/:slug": fn }`, first matching pattern wins |
|
|
315
|
+
| `store` | `null` | `{ get(path) }` |
|
|
316
|
+
| `fallback` | `"convert"` | `"none"` serves only templates and stored files |
|
|
317
|
+
| `cacheControl` | `public, s-maxage=300, stale-while-revalidate=86400` | `.md`, `?format=md`, llms.txt |
|
|
318
|
+
| `negotiatedCacheControl` | `private, max-age=300` | `Accept` requests |
|
|
319
|
+
| `llms` | `null` (`createAgentReady`: from the sitemap) | `{ path, title, summary, details, entries }` |
|
|
320
|
+
| `onHit` | `null` | `(HitEvent) => void` |
|
|
321
|
+
| `internalPrefix` | `/__md` | rewrite target |
|
|
322
|
+
| `fetchTimeoutMs` | `8000` | page fetch for convert |
|
|
323
|
+
| `minChars` | `200` | quality gate for convert |
|
|
324
|
+
|
|
325
|
+
## Commands
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
agent-ready init [--stack next|node|wordpress] [--dry-run] [--force] [--cwd <dir>]
|
|
329
|
+
agent-ready check <url> [--json]
|
|
330
|
+
agent-ready refine --site <origin> (--paths /a,/b | --sitemap) --prompts <dir> [--site-name <name>]
|
|
331
|
+
[--out <dir>] [--limit <n>] [--force] [--concurrency <n>] [--dry-run]
|
|
332
|
+
agent-ready stats --log <hits.jsonl> [--days <n>] [--json]
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## License
|
|
336
|
+
|
|
337
|
+
MIT © [Relve](https://relvehq.com). Built by the team behind Relve, the AI
|
|
338
|
+
tools and market intelligence platform.
|