From 1bf3639858192baea98ce6c20785891e80de9c32 Mon Sep 17 00:00:00 2001 From: hude Date: Thu, 7 May 2026 15:42:47 +0900 Subject: [PATCH 1/6] Add AEO publish docs and crawler controls --- wikibrowser/README.md | 37 +++++++- wikibrowser/app/answers/[slug]/page.tsx | 62 ++++++++++++ wikibrowser/app/llms.txt/route.ts | 16 ++++ wikibrowser/app/robots.ts | 26 +++++ wikibrowser/app/sitemap.ts | 11 +++ wikibrowser/app/w/page.tsx | 8 ++ wikibrowser/components/aeo-article.tsx | 51 ++++++++++ wikibrowser/lib/aeo/load-page.ts | 28 ++++++ wikibrowser/lib/aeo/pages.ts | 93 ++++++++++++++++++ wikibrowser/lib/aeo/parse-markdown.ts | 120 ++++++++++++++++++++++++ wikibrowser/lib/aeo/site.ts | 25 +++++ wikibrowser/next.config.ts | 14 ++- wikibrowser/package.json | 2 +- wikibrowser/scripts/check-aeo.mjs | 69 ++++++++++++++ 14 files changed, 558 insertions(+), 4 deletions(-) create mode 100644 wikibrowser/app/answers/[slug]/page.tsx create mode 100644 wikibrowser/app/llms.txt/route.ts create mode 100644 wikibrowser/app/robots.ts create mode 100644 wikibrowser/app/sitemap.ts create mode 100644 wikibrowser/components/aeo-article.tsx create mode 100644 wikibrowser/lib/aeo/load-page.ts create mode 100644 wikibrowser/lib/aeo/pages.ts create mode 100644 wikibrowser/lib/aeo/parse-markdown.ts create mode 100644 wikibrowser/lib/aeo/site.ts create mode 100644 wikibrowser/scripts/check-aeo.mjs diff --git a/wikibrowser/README.md b/wikibrowser/README.md index 78ce95dc..6f810793 100644 --- a/wikibrowser/README.md +++ b/wikibrowser/README.md @@ -106,7 +106,40 @@ Covered methods: Initial deployment target is Vercel with `NEXT_PUBLIC_WIKI_IC_HOST=https://icp0.io`. The app is public read-only and accepts arbitrary canister IDs. The target DB must grant reader access to anonymous principal `2vxsx-fae`. Canister unreachable / API failures are shown as browser errors and are not treated as not-found states. -The `/w//db//...` URLs are served by a static `/w` shell through `vercel.json` rewrites; read queries go directly from the browser to the IC gateway. Legacy `/w//...` URLs redirect to `/db/default/...`. +The `/w//db//...` URLs are served by a `/w` shell through `vercel.json` rewrites; read queries go directly from the browser to the IC gateway. Legacy `/w//...` URLs redirect to `/db/default/...`. + +## AEO Publish MVP + +`/answers/` serves allowlisted AI-readable public memory pages. These pages run on the Next server with ISR and read Markdown from the configured Kinic Wiki canister. + +Environment: + +```bash +KINIC_AEO_CANISTER_ID= +NEXT_PUBLIC_SITE_URL=https://kinic.xyz +``` + +Rules: + +- `/answers/*` is indexable and rendered with the page body in the initial HTML. +- `/w/*` remains the arbitrary canister browser and is marked `noindex`. +- `sitemap.xml` only lists `/answers/*`. +- `robots.txt` allows `OAI-SearchBot` and blocks `/w/*`. + +AEO Markdown must include limited frontmatter: + +```md +--- +title: What is Kinic? +description: Kinic is an AI memory for important information. +answer_summary: Kinic helps people browse and access important information in one organized place. +updated: 2026-05-07 +index: true +entities: + - Kinic + - AI memory +--- +``` ## Troubleshooting @@ -126,7 +159,7 @@ Dashboard settings: - Install Command: `pnpm install` - Build Command: `pnpm build` - Output Directory: Vercel default -- Environment Variables: `NEXT_PUBLIC_WIKI_IC_HOST=https://icp0.io` for Preview and Production +- Environment Variables: `NEXT_PUBLIC_WIKI_IC_HOST=https://icp0.io`, `KINIC_AEO_CANISTER_ID=`, and `NEXT_PUBLIC_SITE_URL=` for Preview and Production - Routing: keep `vercel.json` so `/w/:segments*` rewrites to the static `/w` shell CLI deploy from this directory: diff --git a/wikibrowser/app/answers/[slug]/page.tsx b/wikibrowser/app/answers/[slug]/page.tsx new file mode 100644 index 00000000..dcacd154 --- /dev/null +++ b/wikibrowser/app/answers/[slug]/page.tsx @@ -0,0 +1,62 @@ +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { AeoArticle } from "@/components/aeo-article"; +import { absoluteUrl } from "@/lib/aeo/site"; +import { loadAeoPage } from "@/lib/aeo/load-page"; + +export const runtime = "nodejs"; +export const revalidate = 3600; + +type PageProps = { + params: Promise<{ + slug: string; + }>; +}; + +export async function generateMetadata({ params }: PageProps): Promise { + const { slug } = await params; + const page = await loadAeoPage(slug); + if (!page) { + return {}; + } + const canonicalPath = page.parsed.frontmatter.canonical ?? page.config.canonicalPath; + const url = absoluteUrl(canonicalPath); + return { + title: page.parsed.frontmatter.title, + description: page.parsed.frontmatter.description, + alternates: { + canonical: url + }, + openGraph: { + type: "article", + title: page.parsed.frontmatter.title, + description: page.parsed.frontmatter.description, + url, + locale: page.config.locale + } + }; +} + +export default async function AnswerPage({ params }: PageProps) { + const { slug } = await params; + const page = await loadAeoPage(slug); + if (!page) { + notFound(); + } + const canonicalPath = page.parsed.frontmatter.canonical ?? page.config.canonicalPath; + const jsonLd = { + "@context": "https://schema.org", + "@type": "Article", + headline: page.parsed.frontmatter.title, + description: page.parsed.frontmatter.description, + dateModified: page.parsed.frontmatter.updated, + mainEntityOfPage: absoluteUrl(canonicalPath), + about: page.parsed.frontmatter.entities + }; + return ( + <> +