#!/usr/bin/env node import fs from "node:fs"; import path from "node:path"; const root = process.cwd(); const site = path.join(root, "dist", "docs-site"); const required = [ "index.html", "tools/reactions/index.html", "it/channels/index.html", "zh-CN/tools/reactions/index.html", "concepts/models.md", "llm.txt", "llms.txt", ".well-known/llms.txt", "robots.txt", "sitemap.xml", "de/tools/reactions/index.html", "de/gateway/heartbeat/index.html", "pagefind/pagefind.js", "assets/docs-site.css", "assets/docs-site.js" ]; const poison = [ /\banalysis\s+to=functions\./iu, /\b(?:commentary|final)\s+to=functions\./iu, /\bfunctions\.(?:read|write|exec|search|run)\b/iu, /OPENCLAW_DOCS_MARKER/u, /<\/?openclaw_docs_i18n_input>/iu, /\/home\/runner\/work\//u, /彩神马争霸/u ]; for (const rel of required) { const file = path.join(site, rel); if (!fs.existsSync(file)) throw new Error(`missing ${rel}`); if (!rel.endsWith(".html")) continue; const html = fs.readFileSync(file, "utf8"); if (!/[^<]+<\/title>/i.test(html)) throw new Error(`${rel}: missing title`); for (const pattern of poison) { if (pattern.test(html)) throw new Error(`${rel}: poison matched ${pattern}`); } } for (const rel of ["llms-full.txt", ".well-known/llms-full.txt"]) { if (fs.existsSync(path.join(site, rel))) throw new Error(`${rel}: full-site LLM corpus should not be emitted`); } const llms = fs.readFileSync(path.join(site, "llms.txt"), "utf8"); if (/llms-full\.txt/.test(llms)) throw new Error("llms.txt: should not advertise llms-full.txt"); if (!/Accept: text\/markdown|\.md/.test(llms)) throw new Error("llms.txt: should advertise page-level Markdown"); const wellKnownLlms = fs.readFileSync(path.join(site, ".well-known/llms.txt"), "utf8"); if (wellKnownLlms !== llms) throw new Error(".well-known/llms.txt: does not match root llms.txt"); const robots = fs.readFileSync(path.join(site, "robots.txt"), "utf8"); if (!/Sitemap: https:\/\/documentation\.openclaw\.ai\/sitemap\.xml/.test(robots)) { throw new Error("robots.txt: sitemap directive missing"); } if (!/Disallow: \/llms-full\.txt/.test(robots) || !/LLMS: https:\/\/documentation\.openclaw\.ai\/llms\.txt/.test(robots)) { throw new Error("robots.txt: LLM directives missing"); } const zhReactions = fs.readFileSync(path.join(site, "zh-CN/tools/reactions/index.html"), "utf8"); if (!/href="(?:\/docs)?\/zh-CN\/tools\/reactions"/.test(zhReactions)) { throw new Error("zh-CN reactions: language picker does not preserve current page"); } if (!/href="(?:\/docs)?\/zh-CN\/tools\/agent-send/.test(zhReactions)) { throw new Error("zh-CN reactions: article links do not stay in locale"); } const itChannels = fs.readFileSync(path.join(site, "it/channels/index.html"), "utf8"); if (!/class="tab-link active" href="(?:\/docs)?\/it\/channels"/.test(itChannels)) { throw new Error("it channels: localized tabs are missing active Channels tab"); } if (!/<section class="nav-section"><h2>Overview<\/h2>/.test(itChannels)) { throw new Error("it channels: localized sidebar is missing"); } const index = fs.readFileSync(path.join(site, "index.html"), "utf8"); if (!/data-language-picker/.test(index) || !/class="language-option active"[^>]*aria-selected="true"/.test(index)) { throw new Error("index: custom language picker is missing active state"); } if (!/Português \(BR\)/.test(index)) { throw new Error("index: language picker labels were not rendered"); } if (!/data-docs-chat/.test(index) || !/OPENCLAW_DOCS_CHAT_API/.test(index)) { throw new Error("index: docs chat widget was not rendered"); } if (!/class="tok-key">channels<\/span>/.test(index) || !/class="tok-string">"\+15555550123"<\/span>/.test(index) || !/class="tok-literal">true<\/span>/.test(index)) { throw new Error("index: json5 config example was not syntax-highlighted"); } const modelsMarkdown = fs.readFileSync(path.join(site, "concepts/models.md"), "utf8"); if (!/^---\nsummary: /m.test(modelsMarkdown) || !/title: "Models CLI"/m.test(modelsMarkdown)) { throw new Error("concepts/models.md: source markdown was not emitted"); } if (process.env.DOCS_SITE_BASE_PATH && (/src="\/assets\//.test(index) || /href="\/assets\//.test(index))) { throw new Error("index: absolute asset paths were not base-path rewritten"); } if (!process.env.DOCS_SITE_BASE_PATH && !/href="\/assets\/docs-site\.css\?v=[^"]+"/.test(index)) { throw new Error("index: custom-domain build did not emit root asset paths"); } if (process.env.DOCS_SITE_CNAME) { const cnamePath = path.join(site, "CNAME"); if (!fs.existsSync(cnamePath) || fs.readFileSync(cnamePath, "utf8").trim() !== process.env.DOCS_SITE_CNAME) { throw new Error("CNAME: custom domain file missing or wrong"); } } const siteJs = fs.readFileSync(path.join(site, "assets/docs-site.js"), "utf8"); if (!/function syncSidebar/.test(siteJs) || !/async function navigateTo/.test(siteJs)) { throw new Error("assets: docs PJAX navigation is missing"); } if (/data-locale/.test(siteJs)) { throw new Error("assets: stale native language select handler is still present"); } if (!/function initChat/.test(siteJs) || !/data-chat-form/.test(siteJs)) { throw new Error("assets: docs chat behavior is missing"); } if (!/function runSearch/.test(siteJs) || !/setTimeout\(\(\)=>runSearch\(q,id\),140\)/.test(siteJs)) { throw new Error("assets: search input is not debounced"); } const platformsIndex = fs.readFileSync(path.join(site, "platforms/index.html"), "utf8"); if (/VPS &amp; hosting/.test(platformsIndex)) { throw new Error("platforms index: TOC double-escaped ampersand"); } const dateTime = fs.readFileSync(path.join(site, "date-time/index.html"), "utf8"); if (/Current Date &amp; Time/.test(dateTime)) { throw new Error("date-time: TOC double-escaped ampersand"); } const legacyDigitalOcean = path.join(site, "docs/platforms/digitalocean/index.html"); if (!fs.existsSync(legacyDigitalOcean)) { throw new Error("legacy DigitalOcean redirect: missing /docs/platforms/digitalocean compatibility file"); } if (!/url=\/(?:docs\/)?install\/digitalocean/.test(fs.readFileSync(legacyDigitalOcean, "utf8"))) { throw new Error("legacy DigitalOcean redirect: wrong destination"); } const showcase = fs.readFileSync(path.join(site, "start/showcase/index.html"), "utf8"); if (!/href="https:\/\/www\.youtube\.com\/watch\?v=SaWSPZoPX34"/.test(showcase)) { throw new Error("showcase: external card href was not rendered"); } console.log(`docs site smoke ok: ${required.length} checks`);