mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
docs(website): serve every page as raw Markdown with an llms.txt index
Append .md to any published route for the page as plain Markdown: the build emits a raw-Markdown twin of every route (frontmatter dropped, home bodies kept, images beside pages) plus a manifest-generated llms.txt, the dev server serves both per request, and the post-build gate fails when either is missing. Fixes #2846.
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
/** VitePress configuration for the locally projected documentation site. */
|
||||
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { readFileSync, writeFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import type { DefaultTheme, PageData } from 'vitepress'
|
||||
import type { DefaultTheme, PageData, SiteConfig } from 'vitepress'
|
||||
import type { ViteDevServer } from 'vite'
|
||||
import { withMermaid } from 'vitepress-plugin-mermaid'
|
||||
import { landingLink, orderedPages, routeLink, sectionSpec, type DocsLocale, type DocsPage, type DocsSidebar } from '../docs.ts'
|
||||
import { docsSourceFiles, projectDocs } from '../../scripts/project-doc-site.ts'
|
||||
import { docsSourceFiles, emitRawMarkdownPages, llmsTxt, projectDocs, rawMarkdownRoute } from '../../scripts/project-doc-site.ts'
|
||||
|
||||
projectDocs()
|
||||
|
||||
@@ -111,6 +111,43 @@ function watchCanonicalDocs(server: ViteDevServer): void {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve the raw-Markdown twin of each route and llms.txt during development,
|
||||
* matching what `buildEnd` emits into the static build. Pages project from
|
||||
* their canonical sources per request, so an edit shows without a rebuild.
|
||||
*/
|
||||
function serveRawMarkdown(server: ViteDevServer): void {
|
||||
server.middlewares.use((req, res, next) => {
|
||||
if (req.url === undefined || (req.method !== 'GET' && req.method !== 'HEAD')) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
// The dev client imports page modules at these same `.md` URLs, and a
|
||||
// module script must reach Vite's transform. Browsers declare the purpose:
|
||||
// `script` for module imports, `document` for address-bar navigation.
|
||||
// Header-less clients (curl, agents) read the raw twin.
|
||||
const fetchDest = req.headers['sec-fetch-dest']
|
||||
if (fetchDest !== undefined && fetchDest !== 'document') {
|
||||
next()
|
||||
return
|
||||
}
|
||||
const pathname = req.url.split(/[?#]/, 1)[0] ?? ''
|
||||
const sitePath = pathname.startsWith(base) ? pathname.slice(base.length) : pathname.replace(/^\//, '')
|
||||
if (sitePath === 'llms.txt') {
|
||||
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||
res.end(llmsTxt({ base, ...siteIdentity }))
|
||||
return
|
||||
}
|
||||
const content = sitePath.endsWith('.md') ? rawMarkdownRoute(sitePath) : undefined
|
||||
if (content === undefined) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
res.setHeader('Content-Type', 'text/markdown; charset=utf-8')
|
||||
res.end(content)
|
||||
})
|
||||
}
|
||||
|
||||
function escapeVueInterpolation(html: string): string {
|
||||
return html.replaceAll('{{', '{{').replaceAll('}}', '}}')
|
||||
}
|
||||
@@ -163,6 +200,12 @@ const sharedTheme: Pick<DefaultTheme.Config, 'search' | 'socialLinks' | 'editLin
|
||||
/** Site base path, carrying the leading and trailing slashes VitePress requires. */
|
||||
const base = process.env.DOCS_BASE ?? '/'
|
||||
|
||||
/** Site identity shared by the VitePress configuration and the llms.txt index. */
|
||||
const siteIdentity = {
|
||||
title: 'DeepSeek Harness',
|
||||
description: '用于构建 Agent Harness 的插件化 SDK',
|
||||
}
|
||||
|
||||
/**
|
||||
* The DeepSeek wordmark, inlined so its `currentColor` fills follow the active
|
||||
* theme. An `<img>` would freeze the mark at the colors the file declares.
|
||||
@@ -247,9 +290,14 @@ function siteTitle(previewTag: string): string {
|
||||
}
|
||||
|
||||
export default withMermaid({
|
||||
title: 'DeepSeek Harness',
|
||||
description: '用于构建 Agent Harness 的插件化 SDK',
|
||||
title: siteIdentity.title,
|
||||
description: siteIdentity.description,
|
||||
base,
|
||||
/** Emit the raw-Markdown twin of every route plus llms.txt beside the rendered site. */
|
||||
buildEnd(siteConfig: SiteConfig) {
|
||||
emitRawMarkdownPages(siteConfig.outDir)
|
||||
writeFileSync(resolve(siteConfig.outDir, 'llms.txt'), llmsTxt({ base, ...siteIdentity }))
|
||||
},
|
||||
head: [
|
||||
// VitePress leaves head hrefs untouched, so the base belongs here explicitly.
|
||||
['link', { rel: 'icon', type: 'image/svg+xml', href: `${base}favicon.svg` }],
|
||||
@@ -322,7 +370,10 @@ export default withMermaid({
|
||||
plugins: [
|
||||
{
|
||||
name: 'deepseek-harness-doc-projector',
|
||||
configureServer: watchCanonicalDocs,
|
||||
configureServer(server) {
|
||||
watchCanonicalDocs(server)
|
||||
serveRawMarkdown(server)
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -10,4 +10,6 @@ Keep canonical prose and generated catalogs in their owning `docs/` tier, then e
|
||||
|
||||
The projector writes disposable Markdown to the ignored `website/.generated/` directory. Never edit or commit `.generated/`, `.cache/`, or `.dist/`.
|
||||
|
||||
The build also emits each route's raw-Markdown twin and a root `llms.txt` index into `.dist/`, so `<page URL>.md` serves the page as plain Markdown. Both derive from the publication manifest at build time; neither is ever a file in this tree.
|
||||
|
||||
Run `pnpm docs:check` after changing this subtree; the gate rejects additional non-ignored Markdown under `website/`.
|
||||
|
||||
Reference in New Issue
Block a user