radiant-docs 0.1.66 → 0.1.68
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/package.json +1 -1
- package/template/astro.config.mjs +24 -2
- package/template/package-lock.json +8 -8
- package/template/package.json +2 -2
- package/template/src/components/MdxPage.astro +12 -2
- package/template/src/components/OpenApiPage.astro +13 -3
- package/template/src/components/PageAiActions.astro +715 -0
- package/template/src/components/endpoint/PlaygroundButton.astro +1 -1
- package/template/src/components/endpoint/PlaygroundForm.astro +8 -0
- package/template/src/layouts/Layout.astro +6 -1
- package/template/src/lib/ai-artifacts.ts +295 -13
- package/template/src/pages/{[...slug]/index.md.ts → [...slug].md.ts} +1 -1
|
@@ -65,7 +65,7 @@ import { Icon } from "astro-icon/components";
|
|
|
65
65
|
x-on:pointerdown="onOverlayPointerDown($event)"
|
|
66
66
|
x-on:pointerup="onOverlayPointerUp($event)"
|
|
67
67
|
x-on:pointercancel="backdropInteractionStarted = false"
|
|
68
|
-
class="relative z-50 flex
|
|
68
|
+
class="relative z-50 flex h-dvh items-center justify-center p-2 xs:p-4 md:p-8"
|
|
69
69
|
>
|
|
70
70
|
<div
|
|
71
71
|
x-on:click.stop
|
|
@@ -1062,6 +1062,14 @@ const headerValuePrefixes = Object.fromEntries(
|
|
|
1062
1062
|
</div>
|
|
1063
1063
|
</div>
|
|
1064
1064
|
|
|
1065
|
+
<style is:global>
|
|
1066
|
+
@media (max-width: 639px) {
|
|
1067
|
+
[data-playground-form-root] :is(input, select, textarea) {
|
|
1068
|
+
font-size: 16px;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
</style>
|
|
1072
|
+
|
|
1065
1073
|
<script>
|
|
1066
1074
|
import {
|
|
1067
1075
|
highlightAssistantCodeToHtml,
|
|
@@ -75,6 +75,8 @@ const navigationTabs = config.navigation.tabs as
|
|
|
75
75
|
const hasNavigationTabs =
|
|
76
76
|
Array.isArray(navigationTabs?.items) && navigationTabs.items.length > 0;
|
|
77
77
|
const tabsPresentation = navigationTabs?.presentation ?? "topbar";
|
|
78
|
+
const hasTopbarNavigationTabs =
|
|
79
|
+
hasNavigationTabs && tabsPresentation === "topbar";
|
|
78
80
|
---
|
|
79
81
|
|
|
80
82
|
<!doctype html>
|
|
@@ -356,7 +358,10 @@ const tabsPresentation = navigationTabs?.presentation ?? "topbar";
|
|
|
356
358
|
|
|
357
359
|
<!-- Main Content -->
|
|
358
360
|
<div
|
|
359
|
-
class=
|
|
361
|
+
class:list={[
|
|
362
|
+
"mx-1 mt-1 px-4 sm:px-6 lg:pl-[calc(288px+32px)] pt-16 lg:pr-8 bg-background",
|
|
363
|
+
hasTopbarNavigationTabs && "lg:pt-[108px]",
|
|
364
|
+
]}
|
|
360
365
|
>
|
|
361
366
|
<main class="mx-auto pt-16 pb-20 min-h-[calc(100vh-64px)]">
|
|
362
367
|
<slot />
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type MdxRoute,
|
|
10
10
|
type OpenApiRoute,
|
|
11
11
|
} from "./routes";
|
|
12
|
+
import { parseOpenApiEndpoint } from "./utils";
|
|
12
13
|
import {
|
|
13
14
|
getOpenApiOperationDoc,
|
|
14
15
|
OPENAPI_REQUEST_SECTION_LABELS,
|
|
@@ -18,10 +19,19 @@ import {
|
|
|
18
19
|
type OpenApiRequestSectionVariantData,
|
|
19
20
|
} from "./openapi/operation-doc";
|
|
20
21
|
import { resolvePageDescription } from "./page-description";
|
|
21
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
getConfig,
|
|
24
|
+
type DocsConfig,
|
|
25
|
+
type NavGroup,
|
|
26
|
+
type NavMenu,
|
|
27
|
+
type NavOpenApi,
|
|
28
|
+
type NavOpenApiPage,
|
|
29
|
+
type NavPage,
|
|
30
|
+
} from "./validation";
|
|
22
31
|
|
|
23
32
|
const DOCS_ROOT = path.join(process.cwd(), "src/content/docs");
|
|
24
33
|
const MAX_DESCRIPTION_LENGTH = 300;
|
|
34
|
+
const PRO_TIER = 3;
|
|
25
35
|
|
|
26
36
|
const OPENAPI_SPEC_EXTENSIONS = new Set([".json", ".yaml", ".yml"]);
|
|
27
37
|
|
|
@@ -29,6 +39,7 @@ export const MARKDOWN_CONTENT_TYPE = "text/markdown; charset=utf-8";
|
|
|
29
39
|
export const PLAIN_TEXT_CONTENT_TYPE = "text/plain; charset=utf-8";
|
|
30
40
|
|
|
31
41
|
export type AiMarkdownPage = {
|
|
42
|
+
routeIdentity?: string;
|
|
32
43
|
filePath: string;
|
|
33
44
|
routeSlug?: string;
|
|
34
45
|
title: string;
|
|
@@ -90,6 +101,24 @@ function buildPublicUrl(pathname: string): string {
|
|
|
90
101
|
return new URL(publicPath, siteOrigin).toString();
|
|
91
102
|
}
|
|
92
103
|
|
|
104
|
+
function getOrgTier(): number {
|
|
105
|
+
const parsed = Number.parseInt((process.env.ORG_TIER ?? "1").trim(), 10);
|
|
106
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function formatMcpSection(): string[] {
|
|
110
|
+
if (getOrgTier() < PRO_TIER) return [];
|
|
111
|
+
|
|
112
|
+
return [
|
|
113
|
+
"## MCP",
|
|
114
|
+
"",
|
|
115
|
+
"This documentation exposes a read-only MCP server for AI agents:",
|
|
116
|
+
"",
|
|
117
|
+
`- MCP server: ${buildPublicUrl("/_mcp/server")}`,
|
|
118
|
+
"- Tool: searchDocs(query)",
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
|
|
93
122
|
function getMarkdownPathForSlug(routeSlug: string): string {
|
|
94
123
|
const normalizedSlug = routeSlug.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
95
124
|
return normalizedSlug ? `/${normalizedSlug}.md` : "/index.md";
|
|
@@ -142,19 +171,23 @@ async function getMdxEntryByFilePath(filePath: string) {
|
|
|
142
171
|
async function createMarkdownPage(args: {
|
|
143
172
|
filePath: string;
|
|
144
173
|
routeSlug?: string;
|
|
174
|
+
routeIdentity?: string;
|
|
145
175
|
title?: string;
|
|
146
176
|
markdownPath?: string;
|
|
147
177
|
canonicalPath?: string;
|
|
148
178
|
}): Promise<AiMarkdownPage> {
|
|
149
179
|
const entry = await getMdxEntryByFilePath(args.filePath);
|
|
150
180
|
if (!entry) {
|
|
151
|
-
throw new Error(
|
|
181
|
+
throw new Error(
|
|
182
|
+
`Could not find content collection entry for "${args.filePath}".`,
|
|
183
|
+
);
|
|
152
184
|
}
|
|
153
185
|
|
|
154
186
|
const { sourcePath, source } = await readMdxSource(args.filePath);
|
|
155
187
|
const routeSlug = args.routeSlug ?? "";
|
|
156
188
|
const markdownPath = args.markdownPath ?? getMarkdownPathForSlug(routeSlug);
|
|
157
|
-
const canonicalPath =
|
|
189
|
+
const canonicalPath =
|
|
190
|
+
args.canonicalPath ?? getCanonicalPathForSlug(routeSlug);
|
|
158
191
|
const title =
|
|
159
192
|
args.title ??
|
|
160
193
|
resolveMdxPageTitle({
|
|
@@ -164,6 +197,7 @@ async function createMarkdownPage(args: {
|
|
|
164
197
|
const config = await getConfig();
|
|
165
198
|
|
|
166
199
|
return {
|
|
200
|
+
routeIdentity: args.routeIdentity,
|
|
167
201
|
filePath: normalizeDocsRootRelativePath(args.filePath),
|
|
168
202
|
routeSlug,
|
|
169
203
|
title,
|
|
@@ -205,6 +239,7 @@ export async function getLlmsListedMarkdownPages(): Promise<AiMarkdownPage[]> {
|
|
|
205
239
|
await createMarkdownPage({
|
|
206
240
|
filePath: config.home,
|
|
207
241
|
routeSlug: "",
|
|
242
|
+
routeIdentity: homeRoute?.routeIdentity,
|
|
208
243
|
title: homeRoute?.title,
|
|
209
244
|
markdownPath: "/index.md",
|
|
210
245
|
canonicalPath: "/",
|
|
@@ -277,6 +312,234 @@ function formatDocsListItem(page: AiMarkdownPage): string {
|
|
|
277
312
|
return `- [${page.title}](${page.markdownUrl})${description}`;
|
|
278
313
|
}
|
|
279
314
|
|
|
315
|
+
type LlmsTxtSection = {
|
|
316
|
+
title: string;
|
|
317
|
+
pages: AiMarkdownPage[];
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
type LlmsNavPageItem = string | NavPage | NavGroup | NavOpenApiPage;
|
|
321
|
+
|
|
322
|
+
type LlmsNavigationContainer = {
|
|
323
|
+
pages?: LlmsNavPageItem[];
|
|
324
|
+
menu?: NavMenu;
|
|
325
|
+
openapi?: string | NavOpenApi;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
function getAiMarkdownPageKey(page: AiMarkdownPage): string {
|
|
329
|
+
return page.routeIdentity ?? `${page.filePath}:${page.markdownUrl}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function buildLlmsSectionTitle(parts: string[]): string {
|
|
333
|
+
const title = parts
|
|
334
|
+
.map((part) => part.trim())
|
|
335
|
+
.filter(Boolean)
|
|
336
|
+
.join(": ");
|
|
337
|
+
|
|
338
|
+
return title || "Docs";
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function isNavPageItem(item: LlmsNavPageItem): item is string | NavPage {
|
|
342
|
+
return typeof item === "string" || "page" in item;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function isNavGroupItem(item: LlmsNavPageItem): item is NavGroup {
|
|
346
|
+
return typeof item !== "string" && "group" in item;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function isNavOpenApiPageItem(
|
|
350
|
+
item: LlmsNavPageItem,
|
|
351
|
+
): item is NavOpenApiPage {
|
|
352
|
+
return typeof item !== "string" && "openapi" in item;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function getNavPageRouteIdentity(item: string | NavPage): string {
|
|
356
|
+
return `mdx:${typeof item === "string" ? item : item.page}`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function getNavOpenApiPageRouteIdentity(item: NavOpenApiPage): string | null {
|
|
360
|
+
const parsedEndpoint = parseOpenApiEndpoint(item.openapi.endpoint);
|
|
361
|
+
if (!parsedEndpoint) return null;
|
|
362
|
+
|
|
363
|
+
return `openapi:${item.openapi.source}:${parsedEndpoint.method} ${parsedEndpoint.path}`;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function getNavOpenApiSource(openapi: string | NavOpenApi): string {
|
|
367
|
+
return typeof openapi === "string" ? openapi : openapi.source;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function getOpenApiEndpointKey(method: string, pathStr: string): string {
|
|
371
|
+
return `${method.toUpperCase()} ${pathStr.toLowerCase()}`;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function matchesOpenApiEndpointFilter(
|
|
375
|
+
route: OpenApiRoute,
|
|
376
|
+
endpoints: string[],
|
|
377
|
+
): boolean {
|
|
378
|
+
const routeEndpointKey = getOpenApiEndpointKey(
|
|
379
|
+
route.openApiMethod,
|
|
380
|
+
route.openApiPath,
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
return endpoints.some((endpoint) => {
|
|
384
|
+
const parsedEndpoint = parseOpenApiEndpoint(endpoint);
|
|
385
|
+
if (!parsedEndpoint) return false;
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
getOpenApiEndpointKey(parsedEndpoint.method, parsedEndpoint.path) ===
|
|
389
|
+
routeEndpointKey
|
|
390
|
+
);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function getOpenApiFileRouteIdentities(
|
|
395
|
+
openapi: string | NavOpenApi,
|
|
396
|
+
routes: Route[],
|
|
397
|
+
): string[] {
|
|
398
|
+
const source = getNavOpenApiSource(openapi);
|
|
399
|
+
const include = typeof openapi === "string" ? undefined : openapi.include;
|
|
400
|
+
const exclude = typeof openapi === "string" ? undefined : openapi.exclude;
|
|
401
|
+
|
|
402
|
+
return routes
|
|
403
|
+
.filter((route): route is OpenApiRoute => {
|
|
404
|
+
if (route.type !== "openapi" || route.filePath !== source) return false;
|
|
405
|
+
if (include && !matchesOpenApiEndpointFilter(route, include)) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
if (exclude && matchesOpenApiEndpointFilter(route, exclude)) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return true;
|
|
413
|
+
})
|
|
414
|
+
.map((route) => route.routeIdentity);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function buildLlmsTxtSections(args: {
|
|
418
|
+
config: DocsConfig;
|
|
419
|
+
pages: AiMarkdownPage[];
|
|
420
|
+
routes: Route[];
|
|
421
|
+
}): LlmsTxtSection[] {
|
|
422
|
+
const pageByRouteIdentity = new Map(
|
|
423
|
+
args.pages
|
|
424
|
+
.filter((page) => page.routeIdentity)
|
|
425
|
+
.map((page) => [page.routeIdentity as string, page]),
|
|
426
|
+
);
|
|
427
|
+
const emittedPageKeys = new Set<string>();
|
|
428
|
+
const sections: LlmsTxtSection[] = [];
|
|
429
|
+
|
|
430
|
+
const appendSection = (titleParts: string[], routeIdentities: string[]) => {
|
|
431
|
+
const sectionPages: AiMarkdownPage[] = [];
|
|
432
|
+
|
|
433
|
+
for (const routeIdentity of routeIdentities) {
|
|
434
|
+
const page = pageByRouteIdentity.get(routeIdentity);
|
|
435
|
+
if (!page) continue;
|
|
436
|
+
|
|
437
|
+
const pageKey = getAiMarkdownPageKey(page);
|
|
438
|
+
if (emittedPageKeys.has(pageKey)) continue;
|
|
439
|
+
|
|
440
|
+
emittedPageKeys.add(pageKey);
|
|
441
|
+
sectionPages.push(page);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (sectionPages.length === 0) return;
|
|
445
|
+
|
|
446
|
+
const title = buildLlmsSectionTitle(titleParts);
|
|
447
|
+
const existingSection = sections.find((section) => section.title === title);
|
|
448
|
+
|
|
449
|
+
if (existingSection) {
|
|
450
|
+
existingSection.pages.push(...sectionPages);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
sections.push({
|
|
455
|
+
title,
|
|
456
|
+
pages: sectionPages,
|
|
457
|
+
});
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const collectPages = (items: LlmsNavPageItem[], titleParts: string[]) => {
|
|
461
|
+
let directRouteIdentities: string[] = [];
|
|
462
|
+
const flushDirectPages = () => {
|
|
463
|
+
appendSection(titleParts, directRouteIdentities);
|
|
464
|
+
directRouteIdentities = [];
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
for (const item of items) {
|
|
468
|
+
if (isNavPageItem(item)) {
|
|
469
|
+
directRouteIdentities.push(getNavPageRouteIdentity(item));
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (isNavOpenApiPageItem(item)) {
|
|
474
|
+
const routeIdentity = getNavOpenApiPageRouteIdentity(item);
|
|
475
|
+
if (routeIdentity) directRouteIdentities.push(routeIdentity);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (isNavGroupItem(item)) {
|
|
480
|
+
flushDirectPages();
|
|
481
|
+
collectNavigationContainer(
|
|
482
|
+
{ pages: item.pages },
|
|
483
|
+
[...titleParts, item.group],
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
flushDirectPages();
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const collectNavigationContainer = (
|
|
492
|
+
container: LlmsNavigationContainer,
|
|
493
|
+
titleParts: string[],
|
|
494
|
+
) => {
|
|
495
|
+
if (container.pages) {
|
|
496
|
+
collectPages(container.pages, titleParts);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (container.openapi) {
|
|
500
|
+
appendSection(
|
|
501
|
+
titleParts.length ? titleParts : ["API Reference"],
|
|
502
|
+
getOpenApiFileRouteIdentities(container.openapi, args.routes),
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (container.menu) {
|
|
507
|
+
for (const menuItem of container.menu.items) {
|
|
508
|
+
collectNavigationContainer(menuItem, [...titleParts, menuItem.label]);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
const navigation = args.config.navigation;
|
|
514
|
+
|
|
515
|
+
if (navigation.pages) {
|
|
516
|
+
collectPages(navigation.pages, []);
|
|
517
|
+
} else if (navigation.menu) {
|
|
518
|
+
collectNavigationContainer({ menu: navigation.menu }, []);
|
|
519
|
+
} else if (navigation.openapi) {
|
|
520
|
+
collectNavigationContainer({ openapi: navigation.openapi }, [
|
|
521
|
+
"API Reference",
|
|
522
|
+
]);
|
|
523
|
+
} else if (navigation.tabs) {
|
|
524
|
+
for (const tabItem of navigation.tabs.items) {
|
|
525
|
+
collectNavigationContainer(tabItem, [tabItem.label]);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const remainingPages = args.pages.filter(
|
|
530
|
+
(page) => !emittedPageKeys.has(getAiMarkdownPageKey(page)),
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
if (remainingPages.length > 0) {
|
|
534
|
+
sections.push({
|
|
535
|
+
title: sections.length ? "Additional Docs" : "Docs",
|
|
536
|
+
pages: remainingPages,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return sections;
|
|
541
|
+
}
|
|
542
|
+
|
|
280
543
|
function normalizeLocalOpenApiSource(source: string): string {
|
|
281
544
|
return normalizeDocsRootRelativePath(source);
|
|
282
545
|
}
|
|
@@ -297,7 +560,9 @@ function getOpenApiSpecLabel(source: string): string {
|
|
|
297
560
|
if (isRemoteUrl(source)) {
|
|
298
561
|
try {
|
|
299
562
|
const url = new URL(source);
|
|
300
|
-
return
|
|
563
|
+
return (
|
|
564
|
+
path.posix.basename(url.pathname).replace(/\.[^.]+$/, "") || source
|
|
565
|
+
);
|
|
301
566
|
} catch {
|
|
302
567
|
return source;
|
|
303
568
|
}
|
|
@@ -344,7 +609,9 @@ function createOpenApiSpecArtifact(source: string): OpenApiSpecArtifact | null {
|
|
|
344
609
|
};
|
|
345
610
|
}
|
|
346
611
|
|
|
347
|
-
export async function getOpenApiSpecArtifacts(): Promise<
|
|
612
|
+
export async function getOpenApiSpecArtifacts(): Promise<
|
|
613
|
+
OpenApiSpecArtifact[]
|
|
614
|
+
> {
|
|
348
615
|
const routes = await getAllRoutes();
|
|
349
616
|
const sources = new Set(
|
|
350
617
|
routes
|
|
@@ -566,10 +833,7 @@ function getResponseContentTypes(response: any): string[] {
|
|
|
566
833
|
return Object.keys(content);
|
|
567
834
|
}
|
|
568
835
|
|
|
569
|
-
function appendOpenApiResponsesMarkdown(
|
|
570
|
-
lines: string[],
|
|
571
|
-
responses: unknown,
|
|
572
|
-
) {
|
|
836
|
+
function appendOpenApiResponsesMarkdown(lines: string[], responses: unknown) {
|
|
573
837
|
if (!responses || typeof responses !== "object") return;
|
|
574
838
|
|
|
575
839
|
const responseEntries = Object.entries(responses).filter(
|
|
@@ -662,6 +926,7 @@ async function createOpenApiMarkdownPage(args: {
|
|
|
662
926
|
args.canonicalPath ?? getCanonicalPathForSlug(routeSlug);
|
|
663
927
|
|
|
664
928
|
return {
|
|
929
|
+
routeIdentity: args.route.routeIdentity,
|
|
665
930
|
filePath: normalizeDocsRootRelativePath(args.route.filePath),
|
|
666
931
|
routeSlug,
|
|
667
932
|
title: args.route.title,
|
|
@@ -690,6 +955,7 @@ async function createRouteMarkdownPage(
|
|
|
690
955
|
return createMarkdownPage({
|
|
691
956
|
filePath: route.filePath,
|
|
692
957
|
routeSlug: route.slug,
|
|
958
|
+
routeIdentity: route.routeIdentity,
|
|
693
959
|
title: route.title,
|
|
694
960
|
...overrides,
|
|
695
961
|
});
|
|
@@ -729,16 +995,30 @@ export async function getLlmsTxt(): Promise<string> {
|
|
|
729
995
|
|
|
730
996
|
const config = await getConfig();
|
|
731
997
|
const pages = await getLlmsListedMarkdownPages();
|
|
998
|
+
const routes = await getAllRoutes();
|
|
732
999
|
const specs = await getOpenApiSpecArtifacts();
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
1000
|
+
const sections = buildLlmsTxtSections({ config, pages, routes });
|
|
1001
|
+
const lines = [`# ${config.title}`];
|
|
1002
|
+
|
|
1003
|
+
for (const section of sections) {
|
|
1004
|
+
lines.push(
|
|
1005
|
+
"",
|
|
1006
|
+
`## ${section.title}`,
|
|
1007
|
+
"",
|
|
1008
|
+
...section.pages.map(formatDocsListItem),
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
736
1011
|
|
|
737
1012
|
const specsSection = formatOpenApiSpecsSection(specs);
|
|
738
1013
|
if (specsSection.length > 0) {
|
|
739
1014
|
lines.push("", ...specsSection);
|
|
740
1015
|
}
|
|
741
1016
|
|
|
1017
|
+
const mcpSection = formatMcpSection();
|
|
1018
|
+
if (mcpSection.length > 0) {
|
|
1019
|
+
lines.push("", ...mcpSection);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
742
1022
|
return `${lines.join("\n").trimEnd()}\n`;
|
|
743
1023
|
}
|
|
744
1024
|
|
|
@@ -785,7 +1065,9 @@ export async function getLlmsFullTxt(): Promise<string> {
|
|
|
785
1065
|
continue;
|
|
786
1066
|
}
|
|
787
1067
|
|
|
788
|
-
sections.push(
|
|
1068
|
+
sections.push(
|
|
1069
|
+
formatLlmsFullSpec(spec, await readOpenApiSpecArtifact(spec)),
|
|
1070
|
+
);
|
|
789
1071
|
}
|
|
790
1072
|
|
|
791
1073
|
return `${sections.join("\n\n---\n\n").trimEnd()}\n`;
|