feat(website): publish agent discovery and Markdown responses

This commit is contained in:
morluto committed 2026-11-03 09:33:41 +08:00
1 parent 510e83eadc
commit eef8f44003
14 files changed
+591 -8

No files matched your search

+4
View File
@@ -21,6 +21,10 @@ jobs:
python-version: "3.12"
cache: pip
cache-dependency-path: website/requirements.txt
- name: Set up Node.js for the website Worker checks
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Install sharing-image dependencies
run: |
sudo apt-get update
+41 -3
View File
@@ -54,6 +54,10 @@ jobs:
python-version: "3.12"
cache: pip
cache-dependency-path: website/requirements.txt
- name: Set up Node.js for the website Worker checks
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Install sharing-image dependencies
run: |
sudo apt-get update
@@ -75,10 +79,21 @@ jobs:
PY
- name: Configure Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
# upload-pages-artifact excludes dot directories, including .well-known.
- name: Package the website including agent discovery
run: |
tar --dereference --hard-dereference --directory website/_site \
--exclude=.git --exclude=.github -cf "$RUNNER_TEMP/artifact.tar" .
tar -tf "$RUNNER_TEMP/artifact.tar" \
./.well-known/ard.json ./.well-known/ai-catalog.json \
./.well-known/agent-skills/index.json > /dev/null
- name: Upload the website
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
path: website/_site
name: github-pages
path: ${{ runner.temp }}/artifact.tar
retention-days: 1
if-no-files-found: error
deploy:
needs: prepare
@@ -130,12 +145,13 @@ jobs:
PUBLISH_TARGET: ${{ inputs.target }}
run: |
python3 -u - <<'PY'
import hashlib
import json
import os
import sys
import time
from urllib.error import URLError
from urllib.parse import urljoin
from urllib.parse import urljoin, urlsplit
from urllib.request import Request, urlopen
expected = os.environ["GITHUB_SHA"]
@@ -172,6 +188,28 @@ jobs:
time.sleep(5)
if pending:
sys.exit("Could not verify published revision on: " + ", ".join(pending))
for name, base in hosts.items():
with urlopen(urljoin(base, ".well-known/agent-skills/index.json"), timeout=15) as response:
skill, = json.load(response)["skills"]
# Fetch from each selected host, including the /rea/ mirror.
artifact_url = urljoin(base, urlsplit(skill["url"]).path.lstrip("/"))
with urlopen(artifact_url, timeout=15) as response:
digest = "sha256:" + hashlib.sha256(response.read()).hexdigest()
if digest != skill["digest"]:
sys.exit(name + " serves a skill archive with the wrong digest")
for manifest in ("ard.json", "ai-catalog.json"):
with urlopen(urljoin(base, ".well-known/" + manifest), timeout=15) as response:
if not json.load(response).get("entries"):
sys.exit(name + " serves an empty discovery manifest")
if name == "Cloudflare":
request = Request(base, headers={"Accept": "text/markdown"})
with urlopen(request, timeout=15) as response:
vary = {value.strip().lower() for value in response.headers.get("Vary", "").split(",")}
if response.headers.get_content_type() != "text/markdown" or "accept" not in vary:
sys.exit("Cloudflare did not negotiate Markdown with Vary: Accept")
if 'rel="ard"' not in response.headers.get("Link", ""):
sys.exit("Cloudflare is missing its discovery Link header")
print(name + " serves the discovery manifests and verified skill archive")
with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as summary:
links = " and ".join(f"[{name}]({url})" for name, url in hosts.items())
summary.write(f"Published commit `{expected}` to {links}.\n")
+3 -1
View File
@@ -704,7 +704,9 @@ export const CI_LANES = {
agentenv: sameJobs(PATH_SCOPES.agentenv, ["agentenv"]),
nativeaot: sameJobs(PATH_SCOPES.nativeaot, ["nativeaot"]),
ghidra_bridge: sameJobs(PATH_SCOPES.ghidra_bridge, ["ghidra_bridge"]),
website: sameJobs(PATH_SCOPES.website, ["website"]),
website: sameJobs(PATH_SCOPES.website, ["website"], {
earlyInputs: [".agents/skills/reverse-engineer-anything/**"],
}),
};
// An input owner omitted from the registry must never silently lose a lane.
for (const [name, paths] of Object.entries(PATH_SCOPES))
+3
View File
@@ -7,6 +7,7 @@ import xml.etree.ElementTree as ET
from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
from website import PageMetadata, page_url
from website_agents import prepare_discovery, prepare_markdown
EXAMPLE_FILES = (
@@ -81,6 +82,8 @@ def prepare_assets(site):
prepare_sharing_image(site)
prepare_sitemap(site)
prepare_notes_example(site)
prepare_discovery(Path(__file__).resolve().parent.parent, site)
prepare_markdown(site)
if __name__ == "__main__":
+109
View File
@@ -0,0 +1,109 @@
"""Check the public build artifacts and Markdown content contract."""
import hashlib
import json
from pathlib import Path
import tempfile
import unittest
from zipfile import ZipFile
from bs4 import BeautifulSoup
import yaml
from website_agents import prepare_discovery, prepare_markdown
ROOT = Path(__file__).resolve().parent.parent
class AgentPublishingChecks(unittest.TestCase):
def test_published_skill_digest_covers_every_reference_and_catalogs_resolve(self):
site = ROOT / "website/_site"
index = json.loads((site / ".well-known/agent-skills/index.json").read_text())
self.assertEqual(index["$schema"], "https://schemas.agentskills.io/discovery/0.2.0/schema.json")
skill, = index["skills"]
self.assertEqual(skill["name"], "reverse-engineer-anything")
self.assertEqual(skill["type"], "archive")
artifact = site / skill["url"].removeprefix("https://rea.tools/")
self.assertEqual(skill["digest"], "sha256:" + hashlib.sha256(artifact.read_bytes()).hexdigest())
source = ROOT / ".agents/skills/reverse-engineer-anything"
with ZipFile(artifact) as archive:
expected = {f"{source.name}/{p.relative_to(source).as_posix()}": p.read_bytes()
for p in source.rglob("*.md")}
self.assertEqual(set(archive.namelist()), set(expected))
for name, data in expected.items():
self.assertEqual(archive.read(name), data)
manifest = archive.read(f"{source.name}/SKILL.md").decode()
metadata = yaml.safe_load(manifest[4:].split("\n---\n", 1)[0])
self.assertEqual(skill["description"], metadata["description"])
catalog = json.loads((site / ".well-known/ai-catalog.json").read_text())
self.assertEqual(catalog["specVersion"], "1.0")
entry, = catalog["entries"]
self.assertEqual(entry["identifier"], "urn:air:rea.tools:skill:reverse-engineer-anything")
self.assertEqual(entry["url"], skill["url"])
self.assertNotIn("data", entry)
ard = json.loads((site / ".well-known/ard.json").read_text())
resource, = ard["entries"]
self.assertEqual(resource["url"], skill["url"])
self.assertTrue(2 <= len(resource["representativeQueries"]) <= 5)
def test_reference_changes_invalidate_digest_and_yaml_scalars_are_parsed(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
source = root / ".agents/skills/reverse-engineer-anything"
source.mkdir(parents=True)
(source / "SKILL.md").write_text(
'---\nname: reverse-engineer-anything\ndescription: >-\n Inspect programs:\n preserve evidence.\n---\n# REA\n'
)
(source / "references").mkdir()
reference = source / "references/evidence.md"
reference.write_text("First revision")
site = root / "site"
def build():
prepare_discovery(root, site)
return json.loads((site / ".well-known/agent-skills/index.json").read_text())["skills"][0]
before = build()
self.assertEqual(before["description"], "Inspect programs: preserve evidence.")
self.assertEqual(build(), before)
reference.write_text("Second revision")
self.assertNotEqual(build()["digest"], before["digest"])
def test_markdown_preserves_content_and_resources_without_publishing_excluded_pages(self):
with tempfile.TemporaryDirectory() as directory:
site = Path(directory)
page = site / "zh-CN/guides/test/index.html"
page.parent.mkdir(parents=True)
page.write_text('''<html><head><title>REA: test</title></head><body>
<nav>Outside navigation</nav><main><aside>Shortened display paths.</aside>
<h1>Inspect a program</h1><nav>Contents navigation</nav>
<ol><li><span class="flow-label">Read</span><span class="flow-node"><code>input.exe</code></span><span class="flow-note">Keep evidence.</span></li></ol>
<p><a href="../other/?q=a%2Fb#code">Another guide</a></p>
<pre><code>const value = a &lt; b ? "x_y" : "&amp;";\n keepIndent();</code></pre>
<details><summary>Evidence</summary><p>Hidden evidence remains available.</p></details>
<img src="../../../assets/diagram.svg" alt="Investigation diagram">
<button>Copy code</button><script>doNotPublish()</script></main></body></html>''')
fixture = site / "examples/notes-web/index.html"
fixture.parent.mkdir(parents=True)
fixture.write_text("<main>Target application</main>")
fallback = site / "zh-CN/fallback/index.html"
fallback.parent.mkdir(parents=True)
fallback.write_text('<head><meta name="robots" content="noindex, follow"></head><main>English fallback notice.</main>')
prepare_markdown(site)
markdown = page.with_suffix(".md").read_text()
self.assertIn('# Inspect a program', markdown)
self.assertIn('const value = a < b ? "x_y" : "&";\n keepIndent();', markdown)
self.assertIn("Hidden evidence remains available.", markdown)
self.assertIn("Shortened display paths.", markdown)
self.assertNotIn("Read`input.exe`", markdown)
self.assertIn("https://rea.tools/zh-CN/guides/other/?q=a%2Fb#code", markdown)
self.assertIn("https://rea.tools/assets/diagram.svg", markdown)
for text in ("Outside navigation", "Contents navigation", "Copy code", "doNotPublish"):
self.assertNotIn(text, markdown)
self.assertFalse(fixture.with_suffix(".md").exists())
self.assertFalse(fallback.with_suffix(".md").exists())
html = BeautifulSoup(page.read_text(), "html.parser")
self.assertEqual(html.find("link", rel="alternate")["href"], "https://rea.tools/zh-CN/guides/test/index.md")
if __name__ == "__main__":
unittest.main()
+2 -1
View File
@@ -327,8 +327,9 @@ def main():
refresh_contributors(root)
elif args.command == "check":
sync_pages(root, check=True)
for name in ("prepare-website.py", "verify-website.py", "test-website.py"):
for name in ("prepare-website.py", "verify-website.py", "test-website.py", "test-website-agents.py"):
run_script(root, name)
subprocess.run(["node", "--test", "website/worker.test.mjs"], cwd=root, check=True)
elif args.command == "links":
run_script(root, "audit-website-links.py", ["--report", args.report] if args.report else [])
else:
+116
View File
@@ -0,0 +1,116 @@
"""Build public agent discovery and Markdown from their maintained sources."""
import hashlib
import json
from urllib.parse import urljoin
from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
from bs4 import BeautifulSoup
from markdownify import MarkdownConverter
import yaml
from website import NOTES_FIXTURES, PageMetadata, SITE_ORIGIN, page_url
def write_json(path, value):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
def prepare_discovery(root, site):
"""Publish the complete skill, including its relative reference documents."""
source = root / ".agents/skills/reverse-engineer-anything"
skill = (source / "SKILL.md").read_text(encoding="utf-8")
if not skill.startswith("---\n") or "\n---\n" not in skill[4:]:
raise ValueError("Public REA skill needs YAML frontmatter")
metadata = yaml.safe_load(skill[4:].split("\n---\n", 1)[0])
if not isinstance(metadata, dict):
raise ValueError("Public REA skill needs YAML metadata")
name, description = metadata["name"], metadata["description"]
if name != source.name or not isinstance(description, str) or not description.strip():
raise ValueError("Invalid public REA skill metadata")
discovery = site / ".well-known/agent-skills"
discovery.mkdir(parents=True, exist_ok=True)
artifact = discovery / f"{name}.zip"
with ZipFile(artifact, "w") as archive:
for path in [source / "SKILL.md", *sorted(source.glob("references/**/*.md"))]:
relative = path.relative_to(source)
entry = ZipInfo(f"{name}/{relative.as_posix()}", (1980, 1, 1, 0, 0, 0))
entry.create_system = 3
entry.external_attr = 0o100644 << 16
archive.writestr(entry, path.read_bytes(), compress_type=ZIP_DEFLATED)
artifact_url = f"{SITE_ORIGIN}/.well-known/agent-skills/{artifact.name}"
write_json(discovery / "index.json", {
"$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
"skills": [{
"name": name, "type": "archive", "description": description,
"url": artifact_url,
"digest": "sha256:" + hashlib.sha256(artifact.read_bytes()).hexdigest(),
}],
})
entry = {
"identifier": f"urn:air:rea.tools:skill:{name}",
"displayName": "REA reverse engineering skill",
"description": description,
"type": "application/zip",
"url": artifact_url,
}
write_json(site / ".well-known/ai-catalog.json", {
"specVersion": "1.0",
"host": {"displayName": "REA", "documentationUrl": f"{SITE_ORIGIN}/get-started/"},
"entries": [entry],
})
write_json(site / ".well-known/ard.json", {"entries": [{
**entry,
"representativeQueries": [
"Explain how a feature works in a shipped executable using REA.",
"Trace an Electron application's behavior back to its packaged JavaScript.",
"Build an AgentEnv bundle from a web application capture.",
],
}]})
print("Prepared agent skill archive, discovery index, AI Catalog and ARD manifest.")
def prepare_markdown(site):
"""Convert rendered reading pages, keeping code and resolving resource URLs."""
converter = MarkdownConverter(heading_style="ATX", bullets="-", escape_underscores=False)
count = 0
for path in sorted(site.rglob("*.html")):
if path.relative_to(site).as_posix() in NOTES_FIXTURES:
continue
source = path.read_text(encoding="utf-8")
metadata = PageMetadata()
metadata.feed(source)
# Drafts and English fallbacks keep their HTML indexing policy intact.
if metadata.noindex:
continue
soup = BeautifulSoup(source, "html.parser")
main = soup.find("main")
if main is None:
continue
# Preserve fallback notices and closed disclosures; remove interface chrome.
for node in main.select('script, style, nav, button, svg, .home-page-nav, [aria-hidden="true"]'):
if node.parent is not None:
node.decompose()
# These spans are separate lines in the shared flow component's CSS.
for node in main.select("span.flow-label, span.flow-node, span.flow-note"):
node.name = "div"
canonical = page_url(site, path)
for node in main.select("[href], [src]"):
for attribute in ("href", "src"):
if node.has_attr(attribute):
node[attribute] = urljoin(canonical, node[attribute])
title = metadata.titles[0] if metadata.titles else "REA"
header = yaml.safe_dump({"title": title, "url": canonical}, allow_unicode=True, sort_keys=False)
markdown = converter.convert_soup(main).strip()
path.with_suffix(".md").write_text(f"---\n{header}---\n\n{markdown}\n", encoding="utf-8")
alternate = page_url(site, path.with_suffix(".md"))
links = (
f'<link rel="alternate" type="text/markdown" href="{alternate}" />\n'
f'<link rel="ard" type="application/json" href="{SITE_ORIGIN}/.well-known/ard.json" />\n'
f'<link rel="ai-catalog" type="application/ai-catalog+json" href="{SITE_ORIGIN}/.well-known/ai-catalog.json" />\n'
f'<link rel="related" type="application/json" title="Agent skills index" href="{SITE_ORIGIN}/.well-known/agent-skills/index.json" />\n'
)
path.write_text(source.replace("</head>", links + "</head>", 1), encoding="utf-8")
count += 1
print(f"Prepared Markdown for {count} reading pages.")
@@ -879,7 +879,14 @@ it.each([
["install.sh", { code: true, runtime: true, package: true, evm: false }],
[
".agents/skills/reverse-engineer-anything/SKILL.md",
{ code: true, runtime: true, docs: true, package: false, evm: false },
{
code: true,
runtime: true,
docs: true,
website: true,
package: false,
evm: false,
},
],
[
"scripts/verify-real-ghidra.mjs",
+57 -1
View File
@@ -486,10 +486,66 @@ The mask SVG is drawn from the seventh decoded mask. It selects zero-based
position 21 and compares its character code with 55 (`7`). Keep the figure,
solver constants and evidence notes aligned when changing this case.
## Agent discovery and Markdown
`scripts/website_agents.py` runs during asset preparation, after translations.
It generates these public files in ignored `_site/`:
- `.well-known/agent-skills/index.json`: the draft v0.2.0 skills discovery index.
Its archive contains the maintained
`.agents/skills/reverse-engineer-anything/SKILL.md` and Markdown references.
The name and description come from YAML frontmatter; SHA-256 covers the
complete, reproducible ZIP, so reference changes invalidate the digest too.
- `.well-known/ard.json`: an ARD manifest with representative analyst queries.
- `.well-known/ai-catalog.json`: an AI Catalog 1.0 document listing that same
downloadable skill. These advertise installation instructions for the local
tool, without claiming a hosted analysis endpoint.
- `index.md` beside each indexable rendered reading page, including translations.
Markdown retains code, evidence disclosures and image descriptions, with
absolute resource links. Drafts, labeled English fallbacks and target example
applications retain their HTML and indexing policy.
The [skills discovery draft](https://github.com/cloudflare/agent-skills-discovery-rfc),
[ARD proposal](https://agenticresourcediscovery.org/spec/) and
[AI Catalog proposal](https://ai-catalog.io/) are evolving formats. ARD v0.91
uses `ard.json` and `rel="ard"`; AI Catalog uses its separate `ai-catalog.json`
envelope. Both entries point to the same ZIP with the registered
`application/zip` media type.
Cloudflare's Worker selects Markdown when an explicit `Accept: text/markdown`
has higher quality than HTML. HTML wins ties and remains the default. Successful
reading-page responses include `Vary: Accept` and an alternate Markdown Link;
Markdown uses `Content-Type: text/markdown; charset=utf-8`. Static `_headers`
advertises discovery resources, sets discovery CORS to `*`, and supplies MIME
types. Build-generated HTML `<link>` elements also advertise these resources.
GitHub Pages and the Python preview serve the same files and HTML links, with
direct Markdown URLs such as `guides/javascript/index.md`. They do not execute
the Worker or apply Cloudflare's `_headers`: content negotiation, custom MIME
overrides and discovery CORS headers are Cloudflare behavior. The publish
workflow packages `.well-known` explicitly because `upload-pages-artifact`
excludes dot directories by default.
`python3 scripts/website.py check` includes artifact/content regressions and
Worker request-contract tests. To check the actual local Cloudflare runtime
after preparing assets, use the repository's pinned Node.js and Wrangler:
```sh
npx wrangler@4.149.0 dev --cwd website --env production --local
curl -i -H 'Accept: text/markdown' http://localhost:8787/guides/javascript/
curl -I http://localhost:8787/.well-known/agent-skills/index.json
```
The `production` environment here skips the preview build and uses the already
verified artifact; `--local` runs on your machine. Check HTML and Markdown,
GET and HEAD, discovery CORS, the downloaded ZIP digest, and unknown routes.
Production remains published only by the manual website workflow.
## Cloudflare Workers
`wrangler.toml` serves `website/_site/` through Workers Static Assets. There is
no Worker script or frontend framework. Before a preview or deployment, Wrangler
one small Worker (`worker.mjs`) for Markdown content negotiation and no frontend
framework. Before a preview or deployment, Wrangler
runs the Python scripts to build the language editions, prepare the example ZIP and verify the
website. Node.js and Python 3 must be available; the commands below use a pinned
Wrangler version without adding it to REA's package dependencies.
+11
View File
@@ -0,0 +1,11 @@
/*
Link: <https://rea.tools/.well-known/ard.json>; rel="ard", <https://rea.tools/.well-known/ai-catalog.json>; rel="ai-catalog", <https://rea.tools/.well-known/agent-skills/index.json>; rel="related"; type="application/json"; title="Agent skills index"
/.well-known/*
Access-Control-Allow-Origin: *
/.well-known/ai-catalog.json
Content-Type: application/ai-catalog+json
/*.md
Content-Type: text/markdown; charset=utf-8
+3
View File
@@ -2,3 +2,6 @@
CairoSVG==2.9.1
# Standard gettext PO/POT catalogs; no translation service is used by builds.
polib==1.2.0
# Build Markdown reading pages and parse the maintained skill's YAML metadata.
markdownify==1.2.3
PyYAML==6.0.3
+103
View File
@@ -0,0 +1,103 @@
// Negotiate prebuilt Markdown while keeping the static asset service's routing.
function prefersMarkdown(accept) {
const ranges = (accept ?? "")
.toLowerCase()
.split(",")
.map((range) => {
const [type, ...parameters] = range.trim().split(";");
const quality = parameters
.map((value) => value.trim())
.find((value) => value.startsWith("q="));
const q = quality === undefined ? 1 : Number(quality.slice(2));
return {
type: type.trim(),
q: Number.isFinite(q) && q >= 0 && q <= 1 ? q : 0,
};
});
const quality = (type) => {
for (const match of [type, "text/*", "*/*"]) {
const matches = ranges.filter((range) => range.type === match);
if (matches.length) return Math.max(...matches.map((range) => range.q));
}
return 0;
};
// Wildcards alone never opt a browser into Markdown; HTML wins ties.
return (
ranges.some((range) => range.type === "text/markdown") &&
quality("text/markdown") > 0 &&
quality("text/markdown") > quality("text/html")
);
}
function advertiseMarkdown(response, path) {
const vary = new Set(
(response.headers.get("Vary") ?? "")
.split(",")
.map((s) => s.trim())
.filter(Boolean),
);
if (![...vary].some((s) => s.toLowerCase() === "accept" || s === "*"))
vary.add("Accept");
response.headers.set("Vary", [...vary].join(", "));
response.headers.append(
"Link",
`<${path}>; rel="alternate"; type="text/markdown"`,
);
return response;
}
export default {
async fetch(request, env) {
const useMarkdown = prefersMarkdown(request.headers.get("Accept"));
// HTML validators describe a different representation. Probe the route first,
// then let the asset service evaluate validators/ranges against the Markdown.
const probe = new Request(request);
if (useMarkdown) {
for (const name of [
"If-None-Match",
"If-Modified-Since",
"If-Match",
"If-Unmodified-Since",
"If-Range",
"Range",
])
probe.headers.delete(name);
}
const original = await env.ASSETS.fetch(probe);
if (
![200, 206, 304].includes(original.status) ||
!["GET", "HEAD"].includes(request.method) ||
!original.headers.get("Content-Type")?.startsWith("text/html")
)
return original;
const url = new URL(request.url);
if (!url.pathname.endsWith("/") && !url.pathname.endsWith(".html"))
return original;
const markdown = new URL(url);
markdown.pathname = url.pathname.endsWith("/")
? `${url.pathname}index.md`
: url.pathname.slice(0, -5) + ".md";
const candidate = await env.ASSETS.fetch(
useMarkdown
? new Request(markdown, request)
: new Request(markdown, { method: "HEAD" }),
);
if (!candidate.ok && ![304, 412, 416].includes(candidate.status)) {
await candidate.body?.cancel();
return original;
}
const response = new Response(
useMarkdown ? candidate.body : original.body,
useMarkdown ? candidate : original,
);
if (useMarkdown) {
await original.body?.cancel();
if (candidate.status < 400)
response.headers.set("Content-Type", "text/markdown; charset=utf-8");
} else {
await candidate.body?.cancel();
}
return advertiseMarkdown(response, markdown.pathname);
},
};
+127
View File
@@ -0,0 +1,127 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import worker from "./worker.mjs";
function assets({ markdownStatus = 200 } = {}) {
return {
ASSETS: {
async fetch(request) {
const url = new URL(request.url);
const markdown = url.pathname.endsWith("index.md");
if (url.pathname === "/missing/")
return new Response("Missing", { status: 404 });
if (url.pathname === "/guide")
return Response.redirect(`${url.origin}/guide/`, 307);
const headers = {
"Content-Type": markdown ? "text/plain" : "text/html",
ETag: markdown ? '"markdown"' : '"html"',
Link: '<https://rea.tools/.well-known/ard.json>; rel="ard"',
};
const status =
request.headers.get("If-None-Match") === headers.ETag
? 304
: markdown
? markdownStatus
: 200;
return new Response(
request.method === "HEAD" || status === 304
? null
: markdown
? "# Guide"
: "<h1>Guide</h1>",
{ headers, status },
);
},
},
};
}
test("HTML is the default and explicit Accept preferences select Markdown", async () => {
for (const [accept, markdown] of [
[null, false],
["*/*", false],
["text/html", false],
["text/markdown", true],
["text/markdown;q=0", false],
["text/markdown;q=0.5,text/html", false],
["text/markdown,text/html;q=0.5", true],
["text/markdown,text/html", false],
["TEXT/MARKDOWN; q=1,text/*;q=0.5", true],
["text/markdown;q=bad", false],
["text/markdown;q=0,*/*;q=1", false],
]) {
const response = await worker.fetch(
new Request("https://rea.tools/guide/", {
headers: accept === null ? {} : { Accept: accept },
}),
assets(),
);
assert.equal(
response.headers.get("Content-Type"),
markdown ? "text/markdown; charset=utf-8" : "text/html",
accept,
);
assert.equal(
await response.text(),
markdown ? "# Guide" : "<h1>Guide</h1>",
accept,
);
assert.match(response.headers.get("Vary"), /Accept/);
assert.match(response.headers.get("Link"), /rel="ard"/);
assert.match(
response.headers.get("Link"),
/<\/guide\/index.md>; rel="alternate"/,
);
}
});
test("HEAD and validators use the selected representation", async () => {
for (const method of ["GET", "HEAD"]) {
const response = await worker.fetch(
new Request("https://rea.tools/guide/", {
method,
headers: { Accept: "text/markdown", "If-None-Match": '"html"' },
}),
assets(),
);
assert.equal(response.status, 200);
assert.equal(response.headers.get("ETag"), '"markdown"');
assert.equal(await response.text(), method === "HEAD" ? "" : "# Guide");
}
for (const [accept, tag] of [
["text/html", '"html"'],
["text/markdown", '"markdown"'],
]) {
const response = await worker.fetch(
new Request("https://rea.tools/guide/", {
headers: { Accept: accept, "If-None-Match": tag },
}),
assets(),
);
assert.equal(response.status, 304);
assert.equal(await response.text(), "");
assert.match(response.headers.get("Vary"), /Accept/);
}
});
test("missing Markdown keeps HTML, and asset redirects and missing routes survive", async () => {
const html = await worker.fetch(
new Request("https://rea.tools/guide/", {
headers: { Accept: "text/markdown" },
}),
assets({ markdownStatus: 404 }),
);
assert.equal(await html.text(), "<h1>Guide</h1>");
for (const [path, status] of [
["/guide", 307],
["/missing/", 404],
]) {
const response = await worker.fetch(
new Request(`https://rea.tools${path}`, {
headers: { Accept: "text/markdown" },
}),
assets(),
);
assert.equal(response.status, status);
}
});
+4 -1
View File
@@ -1,18 +1,21 @@
# Serve only the public website. The default deployment is a workers.dev preview.
name = "rea-website-preview"
main = "worker.mjs"
compatibility_date = "2026-10-08"
workers_dev = true
preview_urls = false
[assets]
directory = "./_site"
binding = "ASSETS"
run_worker_first = ["/", "/*/", "/*.html"]
html_handling = "auto-trailing-slash"
not_found_handling = "none"
[build]
cwd = ".."
command = "python3 scripts/website.py check"
watch_dir = ["./public", "./locales", "./templates"]
watch_dir = ["./public", "./locales", "./templates", "../.agents/skills/reverse-engineer-anything"]
# Public HTTPS depends on zone activation and certificate readiness.
[env.production]