blume 1.6.1 → 1.6.2
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/CHANGELOG.md +6 -0
- package/dist/cli/index.js +894 -130
- package/dist/cli/index.js.map +18 -14
- package/dist/types/core/config-input.d.ts +9 -0
- package/dist/types/core/data.d.ts +2 -0
- package/dist/types/core/i18n-ui.d.ts +2 -0
- package/dist/types/core/schema.d.ts +2 -0
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/configuration/ai.mdx +72 -7
- package/docs/index.mdx +2 -2
- package/package.json +1 -1
- package/skills/blume/SKILL.md +2 -2
- package/src/ai/agent-readability.ts +60 -17
- package/src/ai/api/handlers.ts +273 -0
- package/src/ai/api/paths.ts +14 -0
- package/src/ai/api/problem.ts +63 -0
- package/src/ai/api/spec.ts +681 -0
- package/src/ai/api-catalog.ts +11 -1
- package/src/ai/link-headers.ts +12 -3
- package/src/ai/llms.ts +9 -2
- package/src/ai/mcp/query.ts +390 -0
- package/src/ai/mcp/server.ts +32 -352
- package/src/astro/generate.ts +166 -12
- package/src/astro/templates.ts +157 -0
- package/src/cli/commands/build.ts +8 -6
- package/src/core/config-input.ts +9 -0
- package/src/core/data.ts +7 -1
- package/src/core/i18n-ui.ts +2 -0
- package/src/core/schema.ts +7 -0
- package/src/deploy/vercel-negotiation.ts +56 -8
package/dist/cli/index.js
CHANGED
|
@@ -3267,6 +3267,14 @@ var builtinProxyKinds = (config) => {
|
|
|
3267
3267
|
};
|
|
3268
3268
|
var needsPlaygroundProxy = (config) => builtinProxyKinds(config).length > 0;
|
|
3269
3269
|
|
|
3270
|
+
// src/ai/api/paths.ts
|
|
3271
|
+
var OPENAPI_PATH = "/openapi.json";
|
|
3272
|
+
var API_BASE = "/api/docs";
|
|
3273
|
+
var API_PAGES_PATH = `${API_BASE}/pages.json`;
|
|
3274
|
+
var API_PAGE_PATH = `${API_BASE}/pages/{route}.json`;
|
|
3275
|
+
var API_NAVIGATION_PATH = `${API_BASE}/navigation.json`;
|
|
3276
|
+
var API_SEARCH_PATH = `${API_BASE}/search`;
|
|
3277
|
+
|
|
3270
3278
|
// src/ai/api-catalog.ts
|
|
3271
3279
|
var API_CATALOG_PATH = "/.well-known/api-catalog";
|
|
3272
3280
|
var API_CATALOG_TYPE = "application/linkset+json";
|
|
@@ -3290,6 +3298,13 @@ var linksetEntries = (config) => {
|
|
|
3290
3298
|
}
|
|
3291
3299
|
entries.push(entry);
|
|
3292
3300
|
}
|
|
3301
|
+
if (config.ai.api) {
|
|
3302
|
+
entries.push({
|
|
3303
|
+
anchor: abs(API_BASE),
|
|
3304
|
+
"service-desc": [{ href: abs(OPENAPI_PATH), type: "application/json" }],
|
|
3305
|
+
"service-doc": [{ href: abs("/"), type: "text/html" }]
|
|
3306
|
+
});
|
|
3307
|
+
}
|
|
3293
3308
|
if (config.ai.mcp.enabled) {
|
|
3294
3309
|
entries.push({
|
|
3295
3310
|
anchor: abs(config.ai.mcp.route),
|
|
@@ -3318,6 +3333,9 @@ var buildHomeLinkHeader = (config, routePaths) => {
|
|
|
3318
3333
|
if (hasApiCatalog(config)) {
|
|
3319
3334
|
links.push(`<${deployBase}${API_CATALOG_PATH}>; rel="api-catalog"; type="application/linkset+json"`);
|
|
3320
3335
|
}
|
|
3336
|
+
if (config.ai.api) {
|
|
3337
|
+
links.push(`<${deployBase}${OPENAPI_PATH}>; rel="service-desc"; type="application/json"`);
|
|
3338
|
+
}
|
|
3321
3339
|
if (config.seo.agentReadability) {
|
|
3322
3340
|
links.push(`<${deployBase}/agent-readability.json>; rel="describedby"; type="application/json"`);
|
|
3323
3341
|
}
|
|
@@ -5662,6 +5680,9 @@ const links = [
|
|
|
5662
5680
|
...(data.config.discovery.llmsTxt
|
|
5663
5681
|
? [{ href: href("/llms.txt"), label: nf.llms }]
|
|
5664
5682
|
: []),
|
|
5683
|
+
...(data.config.discovery.api
|
|
5684
|
+
? [{ href: href("/openapi.json"), label: nf.api }]
|
|
5685
|
+
: []),
|
|
5665
5686
|
];
|
|
5666
5687
|
|
|
5667
5688
|
const body = [
|
|
@@ -5685,6 +5706,114 @@ export function GET() {
|
|
|
5685
5706
|
});
|
|
5686
5707
|
}
|
|
5687
5708
|
`;
|
|
5709
|
+
var notFoundJsonTemplate = () => `// Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
|
|
5710
|
+
import { problem } from "blume/ai/api/problem.ts";
|
|
5711
|
+
import { withBase } from "blume/components/islands/base-path.ts";
|
|
5712
|
+
import { absoluteUrl } from "blume/core/site-url.ts";
|
|
5713
|
+
import data from "blume:data";
|
|
5714
|
+
|
|
5715
|
+
export const prerender = true;
|
|
5716
|
+
|
|
5717
|
+
const nf = data.ui.notFound;
|
|
5718
|
+
|
|
5719
|
+
// Absolute for internal routes when the site is known; an external tab href
|
|
5720
|
+
// passes through untouched.
|
|
5721
|
+
const href = (path: string): string => {
|
|
5722
|
+
const based = withBase(path);
|
|
5723
|
+
return data.config.site && based.startsWith("/") && !based.startsWith("//")
|
|
5724
|
+
? absoluteUrl(data.config.site, based)
|
|
5725
|
+
: based;
|
|
5726
|
+
};
|
|
5727
|
+
|
|
5728
|
+
// The recovery set of 404.astro: home, every top-level section (a tab links to
|
|
5729
|
+
// its resolved target), then the machine-readable indexes that exist.
|
|
5730
|
+
const links = [
|
|
5731
|
+
{ href: href("/"), label: nf.home },
|
|
5732
|
+
...data.navigation.tabs.map((tab) => ({
|
|
5733
|
+
href: href(tab.href ?? tab.path),
|
|
5734
|
+
label: tab.label,
|
|
5735
|
+
})),
|
|
5736
|
+
...(data.config.discovery.sitemap
|
|
5737
|
+
? [{ href: href("/sitemap.xml"), label: nf.sitemap }]
|
|
5738
|
+
: []),
|
|
5739
|
+
...(data.config.discovery.llmsTxt
|
|
5740
|
+
? [{ href: href("/llms.txt"), label: nf.llms }]
|
|
5741
|
+
: []),
|
|
5742
|
+
...(data.config.discovery.api
|
|
5743
|
+
? [{ href: href("/openapi.json"), label: nf.api }]
|
|
5744
|
+
: []),
|
|
5745
|
+
];
|
|
5746
|
+
|
|
5747
|
+
const body = problem({
|
|
5748
|
+
code: "PAGE_NOT_FOUND",
|
|
5749
|
+
detail: nf.description,
|
|
5750
|
+
links,
|
|
5751
|
+
resolution: nf.suggestions + ": " + links.map((link) => link.href).join(", "),
|
|
5752
|
+
status: 404,
|
|
5753
|
+
title: nf.title,
|
|
5754
|
+
});
|
|
5755
|
+
|
|
5756
|
+
export function GET() {
|
|
5757
|
+
return new Response(JSON.stringify(body, null, 2) + "\\n", {
|
|
5758
|
+
headers: { "Content-Type": "application/problem+json; charset=utf-8" },
|
|
5759
|
+
});
|
|
5760
|
+
}
|
|
5761
|
+
`;
|
|
5762
|
+
var apiPagesIndexTemplate = () => `// Generated by Blume. Do not edit.
|
|
5763
|
+
import { pagesIndexResponse } from "blume/ai/api/handlers.ts";
|
|
5764
|
+
import data from "blume:mcp-data";
|
|
5765
|
+
|
|
5766
|
+
export const prerender = true;
|
|
5767
|
+
|
|
5768
|
+
export function GET() {
|
|
5769
|
+
return pagesIndexResponse(data);
|
|
5770
|
+
}
|
|
5771
|
+
`;
|
|
5772
|
+
var apiPageTemplate = () => `// Generated by Blume. Do not edit.
|
|
5773
|
+
import { pageParams, pageResponse } from "blume/ai/api/handlers.ts";
|
|
5774
|
+
import data from "blume:mcp-data";
|
|
5775
|
+
|
|
5776
|
+
export const prerender = true;
|
|
5777
|
+
|
|
5778
|
+
export function getStaticPaths() {
|
|
5779
|
+
return pageParams(data);
|
|
5780
|
+
}
|
|
5781
|
+
|
|
5782
|
+
export function GET({ props }: { props: { route: string } }) {
|
|
5783
|
+
return pageResponse(data, props.route);
|
|
5784
|
+
}
|
|
5785
|
+
`;
|
|
5786
|
+
var apiNavigationTemplate = () => `// Generated by Blume. Do not edit.
|
|
5787
|
+
import { navigationResponse } from "blume/ai/api/handlers.ts";
|
|
5788
|
+
import data from "blume:mcp-data";
|
|
5789
|
+
|
|
5790
|
+
export const prerender = true;
|
|
5791
|
+
|
|
5792
|
+
export function GET() {
|
|
5793
|
+
return navigationResponse(data);
|
|
5794
|
+
}
|
|
5795
|
+
`;
|
|
5796
|
+
var apiSearchTemplate = () => `// Generated by Blume. Do not edit.
|
|
5797
|
+
import type { APIRoute } from "astro";
|
|
5798
|
+
import { createSearchHandler } from "blume/ai/api/handlers.ts";
|
|
5799
|
+
import data from "blume:mcp-data";
|
|
5800
|
+
|
|
5801
|
+
export const prerender = false;
|
|
5802
|
+
|
|
5803
|
+
const handler = createSearchHandler(data);
|
|
5804
|
+
|
|
5805
|
+
export const GET: APIRoute = ({ request }) => handler(request);
|
|
5806
|
+
`;
|
|
5807
|
+
var apiNotFoundTemplate = (context) => `// Generated by Blume. Do not edit.
|
|
5808
|
+
import type { APIRoute } from "astro";
|
|
5809
|
+
import { apiNotFoundResponse } from "blume/ai/api/handlers.ts";
|
|
5810
|
+
|
|
5811
|
+
export const prerender = false;
|
|
5812
|
+
|
|
5813
|
+
const context = ${JSON.stringify(context)};
|
|
5814
|
+
|
|
5815
|
+
export const ALL: APIRoute = ({ request }) => apiNotFoundResponse(request, context);
|
|
5816
|
+
`;
|
|
5688
5817
|
var islandDirective = (spec) => spec.client === "only" ? `client:only="${spec.framework}"` : `client:${spec.client}`;
|
|
5689
5818
|
var wrapperPropsType = (name) => `type Props = typeof ${name} extends (
|
|
5690
5819
|
props: infer P extends object,
|
|
@@ -10091,6 +10220,7 @@ var uiStringsObject = z.object({
|
|
|
10091
10220
|
toggleTheme: z.string().default("Toggle color theme")
|
|
10092
10221
|
}).prefault({}),
|
|
10093
10222
|
notFound: z.object({
|
|
10223
|
+
api: z.string().default("JSON API description (openapi.json)"),
|
|
10094
10224
|
description: z.string().default("We couldn't find the page you're looking for."),
|
|
10095
10225
|
home: z.string().default("Back to home"),
|
|
10096
10226
|
llms: z.string().default("Docs index for AI agents (llms.txt)"),
|
|
@@ -10597,6 +10727,7 @@ var llmsTxtObjectSchema = z2.strictObject({
|
|
|
10597
10727
|
openapi: z2.boolean().default(true)
|
|
10598
10728
|
});
|
|
10599
10729
|
var aiConfigSchema = z2.strictObject({
|
|
10730
|
+
api: z2.boolean().default(true),
|
|
10600
10731
|
ask: z2.strictObject({
|
|
10601
10732
|
apiKeyEnv: z2.string().optional(),
|
|
10602
10733
|
baseUrl: z2.url().optional(),
|
|
@@ -16381,6 +16512,23 @@ var askApiUrl = (endpoint, site, abs) => {
|
|
|
16381
16512
|
}
|
|
16382
16513
|
return site && endpoint.startsWith("/") ? absoluteUrl(site, endpoint) : endpoint;
|
|
16383
16514
|
};
|
|
16515
|
+
var markdownArtifact = (config, abs) => {
|
|
16516
|
+
const negotiates = config.deployment.output === "server" && (config.deployment.adapter === "vercel" || config.deployment.adapter === "cloudflare");
|
|
16517
|
+
return negotiates ? { contentNegotiation: "text/markdown", pattern: abs("/{route}.md") } : { pattern: abs("/{route}.md") };
|
|
16518
|
+
};
|
|
16519
|
+
var apiArtifact = (config, abs) => {
|
|
16520
|
+
if (!config.ai.api) {
|
|
16521
|
+
return null;
|
|
16522
|
+
}
|
|
16523
|
+
const api = {
|
|
16524
|
+
openapi: abs(OPENAPI_PATH),
|
|
16525
|
+
pages: abs(API_PAGES_PATH)
|
|
16526
|
+
};
|
|
16527
|
+
if (config.deployment.output === "server") {
|
|
16528
|
+
api.search = abs(API_SEARCH_PATH);
|
|
16529
|
+
}
|
|
16530
|
+
return api;
|
|
16531
|
+
};
|
|
16384
16532
|
var wellKnownArtifacts = (config, abs) => {
|
|
16385
16533
|
const artifacts = {};
|
|
16386
16534
|
if (config.ai.webBotAuth.keys.length > 0) {
|
|
@@ -16405,9 +16553,13 @@ var buildAgentReadability = (project) => {
|
|
|
16405
16553
|
const based = withBasePath(deployBase, path);
|
|
16406
16554
|
return site ? absoluteUrl(site, based) : based;
|
|
16407
16555
|
};
|
|
16408
|
-
const
|
|
16409
|
-
|
|
16410
|
-
|
|
16556
|
+
const artifacts = {
|
|
16557
|
+
markdown: markdownArtifact(config, abs)
|
|
16558
|
+
};
|
|
16559
|
+
const api = apiArtifact(config, abs);
|
|
16560
|
+
if (api) {
|
|
16561
|
+
artifacts.api = api;
|
|
16562
|
+
}
|
|
16411
16563
|
if (config.ai.llmsTxt.enabled) {
|
|
16412
16564
|
artifacts.llmsFullTxt = abs("/llms-full.txt");
|
|
16413
16565
|
artifacts.llmsTxt = abs("/llms.txt");
|
|
@@ -17123,6 +17275,9 @@ var agentResourceLines = (project) => {
|
|
|
17123
17275
|
`- [llms-full.txt](${url("/llms-full.txt")}): The full Markdown of every page in one file.`,
|
|
17124
17276
|
`- [Page Markdown](${url("/index.md")}): Append \`.md\` to any page URL to fetch that page as raw Markdown.`
|
|
17125
17277
|
];
|
|
17278
|
+
if (config.ai.api) {
|
|
17279
|
+
lines.push(`- [JSON API](${url(API_PAGES_PATH)}): Page index of the JSON docs API; each entry links the page's JSON and Markdown forms. Described by the OpenAPI document at ${url(OPENAPI_PATH)}.`);
|
|
17280
|
+
}
|
|
17126
17281
|
if (config.ai.mcp.enabled) {
|
|
17127
17282
|
lines.push(`- [MCP server](${url(config.ai.mcp.route)}): Streamable HTTP Model Context Protocol server with search_docs, get_page, list_pages, and get_navigation tools, plus every page as a resource. Discovery document: ${url("/.well-known/mcp.json")}`);
|
|
17128
17283
|
}
|
|
@@ -18062,12 +18217,17 @@ ${references.join(`
|
|
|
18062
18217
|
|
|
18063
18218
|
// src/deploy/vercel-negotiation.ts
|
|
18064
18219
|
var ACCEPT_MARKDOWN_HEADER_VALUE = "(.*,)?\\s*text/(x-)?markdown(\\s*[;,].*)?$";
|
|
18220
|
+
var ACCEPT_JSON_HEADER_VALUE = "(.*,)?\\s*application/(problem\\+)?json(\\s*[;,].*)?$";
|
|
18065
18221
|
var isString6 = (value) => typeof value === "string";
|
|
18066
18222
|
var ACCEPT_MARKDOWN_CONDITION = [
|
|
18067
18223
|
{ key: "accept", type: "header", value: ACCEPT_MARKDOWN_HEADER_VALUE }
|
|
18068
18224
|
];
|
|
18225
|
+
var ACCEPT_JSON_CONDITION = [
|
|
18226
|
+
{ key: "accept", type: "header", value: ACCEPT_JSON_HEADER_VALUE }
|
|
18227
|
+
];
|
|
18069
18228
|
var VARY_ACCEPT = { vary: "Accept" };
|
|
18070
18229
|
var NOT_FOUND_MARKDOWN_DEST = "/404.md";
|
|
18230
|
+
var NOT_FOUND_JSON_DEST = "/404.json";
|
|
18071
18231
|
var NOT_FOUND_HTML_DEST = "/404.html";
|
|
18072
18232
|
var NOT_FOUND_MARKDOWN_ROUTES = [
|
|
18073
18233
|
{
|
|
@@ -18079,6 +18239,16 @@ var NOT_FOUND_MARKDOWN_ROUTES = [
|
|
|
18079
18239
|
},
|
|
18080
18240
|
{ dest: NOT_FOUND_MARKDOWN_DEST, src: "^/.*\\.mdx?$", status: 404 }
|
|
18081
18241
|
];
|
|
18242
|
+
var NOT_FOUND_JSON_ROUTES = [
|
|
18243
|
+
{
|
|
18244
|
+
dest: NOT_FOUND_JSON_DEST,
|
|
18245
|
+
has: ACCEPT_JSON_CONDITION,
|
|
18246
|
+
headers: VARY_ACCEPT,
|
|
18247
|
+
src: "^/.*$",
|
|
18248
|
+
status: 404
|
|
18249
|
+
},
|
|
18250
|
+
{ dest: NOT_FOUND_JSON_DEST, src: "^/.*\\.json$", status: 404 }
|
|
18251
|
+
];
|
|
18082
18252
|
var MAX_ALTERNATION_LENGTH = 3900;
|
|
18083
18253
|
var REGEX_SPECIALS = /[$()*+.?[\]^{|}\\]/gu;
|
|
18084
18254
|
var routePattern = (route) => encodeURI(route).replace(REGEX_SPECIALS, "\\$&");
|
|
@@ -18134,8 +18304,8 @@ var TRAILING_SLASH_REDIRECT = {
|
|
|
18134
18304
|
src: "^/(.+)/$",
|
|
18135
18305
|
status: 308
|
|
18136
18306
|
};
|
|
18137
|
-
var isNegotiationRoute = (route) => route.has?.some((condition) => condition.value === ACCEPT_MARKDOWN_HEADER_VALUE) === true || route.dest === NOT_FOUND_MARKDOWN_DEST && route.status === 404 || route.continue === true && route.headers?.vary === "Accept" && isString6(route.src) && Object.keys(route).length === 3 || route.continue === true && isString6(route.headers?.link) && route.src === HOME_SRC && Object.keys(route).length === 3 || route.status === TRAILING_SLASH_REDIRECT.status && route.src === TRAILING_SLASH_REDIRECT.src;
|
|
18138
|
-
var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTypeOverrides, homeTokens,
|
|
18307
|
+
var isNegotiationRoute = (route) => route.has?.some((condition) => condition.value === ACCEPT_MARKDOWN_HEADER_VALUE) === true || route.dest === NOT_FOUND_MARKDOWN_DEST && route.status === 404 || route.dest === NOT_FOUND_JSON_DEST && route.status === 404 || route.continue === true && route.headers?.vary === "Accept" && isString6(route.src) && Object.keys(route).length === 3 || route.continue === true && isString6(route.headers?.link) && route.src === HOME_SRC && Object.keys(route).length === 3 || route.status === TRAILING_SLASH_REDIRECT.status && route.src === TRAILING_SLASH_REDIRECT.src;
|
|
18308
|
+
var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTypeOverrides, homeTokens, notFound = {}) => {
|
|
18139
18309
|
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
18140
18310
|
let config;
|
|
18141
18311
|
try {
|
|
@@ -18163,10 +18333,14 @@ var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTy
|
|
|
18163
18333
|
});
|
|
18164
18334
|
}
|
|
18165
18335
|
routes.splice(filesystemIndex, 0, ...headerRoutes, ...rewriteRoutes, TRAILING_SLASH_REDIRECT);
|
|
18166
|
-
|
|
18336
|
+
const notFoundRoutes = [
|
|
18337
|
+
...notFound.markdown ? NOT_FOUND_MARKDOWN_ROUTES : [],
|
|
18338
|
+
...notFound.json ? NOT_FOUND_JSON_ROUTES : []
|
|
18339
|
+
];
|
|
18340
|
+
if (notFoundRoutes.length > 0) {
|
|
18167
18341
|
const fallbackIndex = routes.findIndex((route) => route.status === 404 && route.dest === NOT_FOUND_HTML_DEST);
|
|
18168
18342
|
if (fallbackIndex !== -1) {
|
|
18169
|
-
routes.splice(fallbackIndex, 0, ...
|
|
18343
|
+
routes.splice(fallbackIndex, 0, ...notFoundRoutes);
|
|
18170
18344
|
}
|
|
18171
18345
|
}
|
|
18172
18346
|
config.routes = routes;
|
|
@@ -18731,6 +18905,519 @@ import {
|
|
|
18731
18905
|
} from "pathe";
|
|
18732
18906
|
import { glob as glob7 } from "tinyglobby";
|
|
18733
18907
|
|
|
18908
|
+
// src/ai/api/problem.ts
|
|
18909
|
+
var PROBLEM_TYPE = "application/problem+json";
|
|
18910
|
+
|
|
18911
|
+
// src/ai/api/spec.ts
|
|
18912
|
+
var JSON_TYPE = "application/json";
|
|
18913
|
+
var MARKDOWN_TYPE = "text/markdown";
|
|
18914
|
+
var TEXT_TYPE = "text/plain";
|
|
18915
|
+
var ref = (name) => ({
|
|
18916
|
+
$ref: `#/components/schemas/${name}`
|
|
18917
|
+
});
|
|
18918
|
+
var jsonResponse = (description, schema) => ({
|
|
18919
|
+
content: { [JSON_TYPE]: { schema: ref(schema) } },
|
|
18920
|
+
description
|
|
18921
|
+
});
|
|
18922
|
+
var problemResponse = (description) => ({
|
|
18923
|
+
content: { [PROBLEM_TYPE]: { schema: ref("Problem") } },
|
|
18924
|
+
description
|
|
18925
|
+
});
|
|
18926
|
+
var textResponse = (description, type) => ({
|
|
18927
|
+
content: { [type]: { schema: { type: "string" } } },
|
|
18928
|
+
description
|
|
18929
|
+
});
|
|
18930
|
+
var string = (description) => ({ description, type: "string" });
|
|
18931
|
+
var facetsSchema = {
|
|
18932
|
+
additionalProperties: { type: "string" },
|
|
18933
|
+
description: "Facet values the site declares for the page's content type (`content.types.<type>.facets`), key → value.",
|
|
18934
|
+
type: "object"
|
|
18935
|
+
};
|
|
18936
|
+
var versionProperty = {
|
|
18937
|
+
description: 'Docs version the page belongs to on a versioned site: `""` for the current docs, else an archived version id. Absent on unversioned sites.',
|
|
18938
|
+
type: "string"
|
|
18939
|
+
};
|
|
18940
|
+
var searchParameters = [
|
|
18941
|
+
{
|
|
18942
|
+
description: "The search query.",
|
|
18943
|
+
in: "query",
|
|
18944
|
+
name: "q",
|
|
18945
|
+
required: true,
|
|
18946
|
+
schema: { minLength: 1, type: "string" }
|
|
18947
|
+
},
|
|
18948
|
+
{
|
|
18949
|
+
description: "Maximum hits to return (default 8, at most 20).",
|
|
18950
|
+
in: "query",
|
|
18951
|
+
name: "limit",
|
|
18952
|
+
required: false,
|
|
18953
|
+
schema: { default: 8, maximum: 20, minimum: 1, type: "integer" }
|
|
18954
|
+
},
|
|
18955
|
+
{
|
|
18956
|
+
description: "Only include pages of these content types (frontmatter `type`, e.g. `doc`, `rfc`). Comma-separated or repeated. Omit for every type.",
|
|
18957
|
+
explode: false,
|
|
18958
|
+
in: "query",
|
|
18959
|
+
name: "contentTypes",
|
|
18960
|
+
required: false,
|
|
18961
|
+
schema: { items: { type: "string" }, type: "array" },
|
|
18962
|
+
style: "form"
|
|
18963
|
+
},
|
|
18964
|
+
{
|
|
18965
|
+
description: "Only include pages in this locale (e.g. `fr`). Omit for every language.",
|
|
18966
|
+
in: "query",
|
|
18967
|
+
name: "locale",
|
|
18968
|
+
required: false,
|
|
18969
|
+
schema: { type: "string" }
|
|
18970
|
+
},
|
|
18971
|
+
{
|
|
18972
|
+
description: "Docs version to scope to on a versioned site: `latest` (the default — current docs only), `all`, or an archived version id. Ignored when the site is unversioned.",
|
|
18973
|
+
in: "query",
|
|
18974
|
+
name: "version",
|
|
18975
|
+
required: false,
|
|
18976
|
+
schema: { type: "string" }
|
|
18977
|
+
},
|
|
18978
|
+
{
|
|
18979
|
+
description: "Only include pages matching every facet, as `filters[key]=value` pairs (e.g. `filters[status]=enforced`). Facets are metadata the site declares per content type; the page index shows each page's values.",
|
|
18980
|
+
explode: true,
|
|
18981
|
+
in: "query",
|
|
18982
|
+
name: "filters",
|
|
18983
|
+
required: false,
|
|
18984
|
+
schema: { additionalProperties: { type: "string" }, type: "object" },
|
|
18985
|
+
style: "deepObject"
|
|
18986
|
+
}
|
|
18987
|
+
];
|
|
18988
|
+
var schemas = {
|
|
18989
|
+
JsonRpcRequest: {
|
|
18990
|
+
description: "A JSON-RPC 2.0 request, as the Model Context Protocol sends.",
|
|
18991
|
+
properties: {
|
|
18992
|
+
id: { description: "Request id (absent on notifications)." },
|
|
18993
|
+
jsonrpc: { const: "2.0", type: "string" },
|
|
18994
|
+
method: string("The MCP method, e.g. `initialize` or `tools/call`."),
|
|
18995
|
+
params: { description: "Method parameters.", type: "object" }
|
|
18996
|
+
},
|
|
18997
|
+
required: ["jsonrpc", "method"],
|
|
18998
|
+
type: "object"
|
|
18999
|
+
},
|
|
19000
|
+
JsonRpcResponse: {
|
|
19001
|
+
description: "A JSON-RPC 2.0 response: a `result` or an `error`.",
|
|
19002
|
+
properties: {
|
|
19003
|
+
error: {
|
|
19004
|
+
properties: {
|
|
19005
|
+
code: { type: "integer" },
|
|
19006
|
+
data: {},
|
|
19007
|
+
message: { type: "string" }
|
|
19008
|
+
},
|
|
19009
|
+
required: ["code", "message"],
|
|
19010
|
+
type: "object"
|
|
19011
|
+
},
|
|
19012
|
+
id: {},
|
|
19013
|
+
jsonrpc: { const: "2.0", type: "string" },
|
|
19014
|
+
result: { type: "object" }
|
|
19015
|
+
},
|
|
19016
|
+
required: ["jsonrpc"],
|
|
19017
|
+
type: "object"
|
|
19018
|
+
},
|
|
19019
|
+
NavLink: {
|
|
19020
|
+
properties: {
|
|
19021
|
+
href: string("Link target — an internal route or an external URL."),
|
|
19022
|
+
icon: string("Optional icon name."),
|
|
19023
|
+
label: { type: "string" }
|
|
19024
|
+
},
|
|
19025
|
+
required: ["href", "label"],
|
|
19026
|
+
type: "object"
|
|
19027
|
+
},
|
|
19028
|
+
NavNode: {
|
|
19029
|
+
description: "A sidebar entry: a page (linking to its route) or a group holding further nodes.",
|
|
19030
|
+
oneOf: [
|
|
19031
|
+
{
|
|
19032
|
+
properties: {
|
|
19033
|
+
badge: { type: "string" },
|
|
19034
|
+
deprecated: { type: "boolean" },
|
|
19035
|
+
description: { type: "string" },
|
|
19036
|
+
icon: { type: "string" },
|
|
19037
|
+
kind: { const: "page", type: "string" },
|
|
19038
|
+
label: { type: "string" },
|
|
19039
|
+
pageId: string("The page's stable content id."),
|
|
19040
|
+
route: string("The page's route.")
|
|
19041
|
+
},
|
|
19042
|
+
required: ["kind", "label", "pageId", "route"],
|
|
19043
|
+
type: "object"
|
|
19044
|
+
},
|
|
19045
|
+
{
|
|
19046
|
+
properties: {
|
|
19047
|
+
badge: { type: "string" },
|
|
19048
|
+
children: { items: ref("NavNode"), type: "array" },
|
|
19049
|
+
collapsed: { type: "boolean" },
|
|
19050
|
+
icon: { type: "string" },
|
|
19051
|
+
kind: { const: "group", type: "string" },
|
|
19052
|
+
label: { type: "string" },
|
|
19053
|
+
path: string("The group's route prefix (not necessarily a page)."),
|
|
19054
|
+
route: string("The group's index page route, when it has one.")
|
|
19055
|
+
},
|
|
19056
|
+
required: ["children", "kind", "label"],
|
|
19057
|
+
type: "object"
|
|
19058
|
+
}
|
|
19059
|
+
]
|
|
19060
|
+
},
|
|
19061
|
+
NavSelector: {
|
|
19062
|
+
description: "A top-level partition selector (products, versions, languages).",
|
|
19063
|
+
properties: {
|
|
19064
|
+
items: {
|
|
19065
|
+
items: {
|
|
19066
|
+
properties: {
|
|
19067
|
+
description: { type: "string" },
|
|
19068
|
+
label: { type: "string" },
|
|
19069
|
+
path: { type: "string" },
|
|
19070
|
+
tag: { type: "string" }
|
|
19071
|
+
},
|
|
19072
|
+
required: ["label", "path"],
|
|
19073
|
+
type: "object"
|
|
19074
|
+
},
|
|
19075
|
+
type: "array"
|
|
19076
|
+
},
|
|
19077
|
+
kind: { type: "string" },
|
|
19078
|
+
label: { type: "string" }
|
|
19079
|
+
},
|
|
19080
|
+
required: ["items", "kind", "label"],
|
|
19081
|
+
type: "object"
|
|
19082
|
+
},
|
|
19083
|
+
NavTab: {
|
|
19084
|
+
properties: {
|
|
19085
|
+
href: string("The clickable target when it differs from `path` (the section's first page)."),
|
|
19086
|
+
icon: { type: "string" },
|
|
19087
|
+
label: { type: "string" },
|
|
19088
|
+
path: string("The tab's section prefix.")
|
|
19089
|
+
},
|
|
19090
|
+
required: ["label", "path"],
|
|
19091
|
+
type: "object"
|
|
19092
|
+
},
|
|
19093
|
+
Navigation: {
|
|
19094
|
+
description: "The docs navigation model: header tabs, the sidebar tree, partition selectors, and pinned links.",
|
|
19095
|
+
properties: {
|
|
19096
|
+
actions: { items: ref("NavLink"), type: "array" },
|
|
19097
|
+
cta: { oneOf: [ref("NavLink"), { type: "null" }] },
|
|
19098
|
+
featured: { items: ref("NavLink"), type: "array" },
|
|
19099
|
+
repoUrl: { type: ["string", "null"] },
|
|
19100
|
+
root: string("The tree root's route (`/`, or a locale/version prefix)."),
|
|
19101
|
+
selectors: { items: ref("NavSelector"), type: "array" },
|
|
19102
|
+
sidebar: { items: ref("NavNode"), type: "array" },
|
|
19103
|
+
tabs: { items: ref("NavTab"), type: "array" }
|
|
19104
|
+
},
|
|
19105
|
+
required: ["featured", "selectors", "sidebar", "tabs"],
|
|
19106
|
+
type: "object"
|
|
19107
|
+
},
|
|
19108
|
+
Page: {
|
|
19109
|
+
allOf: [
|
|
19110
|
+
ref("PageSummary"),
|
|
19111
|
+
{
|
|
19112
|
+
properties: {
|
|
19113
|
+
markdown: string("The page as agent Markdown: frontmatter included, components downleveled to plain Markdown.")
|
|
19114
|
+
},
|
|
19115
|
+
required: ["markdown"],
|
|
19116
|
+
type: "object"
|
|
19117
|
+
}
|
|
19118
|
+
],
|
|
19119
|
+
description: "A page's index entry plus its full Markdown body."
|
|
19120
|
+
},
|
|
19121
|
+
PageSummary: {
|
|
19122
|
+
properties: {
|
|
19123
|
+
contentType: string("The page's content type (frontmatter `type`)."),
|
|
19124
|
+
description: { type: "string" },
|
|
19125
|
+
facets: facetsSchema,
|
|
19126
|
+
json: {
|
|
19127
|
+
description: "This page's JSON representation (the `getPage` operation).",
|
|
19128
|
+
format: "uri-reference",
|
|
19129
|
+
type: "string"
|
|
19130
|
+
},
|
|
19131
|
+
lastModified: {
|
|
19132
|
+
description: "ISO 8601 last-modified date, when known.",
|
|
19133
|
+
type: ["string", "null"]
|
|
19134
|
+
},
|
|
19135
|
+
locale: string("The page's locale code."),
|
|
19136
|
+
markdownUrl: {
|
|
19137
|
+
description: "The page's raw-Markdown mirror (the `getPageMarkdown` operation).",
|
|
19138
|
+
format: "uri-reference",
|
|
19139
|
+
type: "string"
|
|
19140
|
+
},
|
|
19141
|
+
route: string("The page's route (`/guides/install`); the key every other operation takes."),
|
|
19142
|
+
title: { type: "string" },
|
|
19143
|
+
url: {
|
|
19144
|
+
description: "Where the rendered page is served.",
|
|
19145
|
+
format: "uri-reference",
|
|
19146
|
+
type: "string"
|
|
19147
|
+
},
|
|
19148
|
+
version: versionProperty
|
|
19149
|
+
},
|
|
19150
|
+
required: [
|
|
19151
|
+
"contentType",
|
|
19152
|
+
"json",
|
|
19153
|
+
"lastModified",
|
|
19154
|
+
"locale",
|
|
19155
|
+
"markdownUrl",
|
|
19156
|
+
"route",
|
|
19157
|
+
"title",
|
|
19158
|
+
"url"
|
|
19159
|
+
],
|
|
19160
|
+
type: "object"
|
|
19161
|
+
},
|
|
19162
|
+
PagesIndex: {
|
|
19163
|
+
properties: {
|
|
19164
|
+
count: { type: "integer" },
|
|
19165
|
+
generator: string("The Blume version that built the site."),
|
|
19166
|
+
pages: { items: ref("PageSummary"), type: "array" },
|
|
19167
|
+
site: { type: ["string", "null"] }
|
|
19168
|
+
},
|
|
19169
|
+
required: ["count", "generator", "pages", "site"],
|
|
19170
|
+
type: "object"
|
|
19171
|
+
},
|
|
19172
|
+
Problem: {
|
|
19173
|
+
description: "RFC 9457 problem details, with a stable code and a resolution hint.",
|
|
19174
|
+
properties: {
|
|
19175
|
+
code: string("Stable error code (e.g. `PAGE_NOT_FOUND`)."),
|
|
19176
|
+
detail: string("Human-readable explanation of this occurrence."),
|
|
19177
|
+
instance: string("The request path the problem occurred on."),
|
|
19178
|
+
links: {
|
|
19179
|
+
description: "Recovery links, when there is somewhere useful to go.",
|
|
19180
|
+
items: {
|
|
19181
|
+
properties: { href: { type: "string" }, label: { type: "string" } },
|
|
19182
|
+
required: ["href", "label"],
|
|
19183
|
+
type: "object"
|
|
19184
|
+
},
|
|
19185
|
+
type: "array"
|
|
19186
|
+
},
|
|
19187
|
+
resolution: string("What to do next."),
|
|
19188
|
+
status: { type: "integer" },
|
|
19189
|
+
title: { type: "string" },
|
|
19190
|
+
type: string("Problem type URI; `about:blank` by default.")
|
|
19191
|
+
},
|
|
19192
|
+
required: ["code", "detail", "resolution", "status", "title", "type"],
|
|
19193
|
+
type: "object"
|
|
19194
|
+
},
|
|
19195
|
+
SearchHit: {
|
|
19196
|
+
properties: {
|
|
19197
|
+
contentType: { type: "string" },
|
|
19198
|
+
excerpt: string("The page description, else the start of its content."),
|
|
19199
|
+
facets: facetsSchema,
|
|
19200
|
+
route: string("The page's route; pass it to `getPage`."),
|
|
19201
|
+
title: { type: "string" },
|
|
19202
|
+
url: { format: "uri-reference", type: "string" },
|
|
19203
|
+
version: versionProperty
|
|
19204
|
+
},
|
|
19205
|
+
required: ["excerpt", "route", "title", "url"],
|
|
19206
|
+
type: "object"
|
|
19207
|
+
},
|
|
19208
|
+
SearchResponse: {
|
|
19209
|
+
properties: {
|
|
19210
|
+
count: { type: "integer" },
|
|
19211
|
+
query: { type: "string" },
|
|
19212
|
+
results: { items: ref("SearchHit"), type: "array" }
|
|
19213
|
+
},
|
|
19214
|
+
required: ["count", "query", "results"],
|
|
19215
|
+
type: "object"
|
|
19216
|
+
}
|
|
19217
|
+
};
|
|
19218
|
+
var ROUTE_PARAM = {
|
|
19219
|
+
description: "The page route without its leading slash (`guides/install`), or `index` for the home page. May contain slashes.",
|
|
19220
|
+
in: "path",
|
|
19221
|
+
name: "route",
|
|
19222
|
+
required: true,
|
|
19223
|
+
schema: { type: "string" }
|
|
19224
|
+
};
|
|
19225
|
+
var serverUrl = (input) => {
|
|
19226
|
+
if (input.site) {
|
|
19227
|
+
return input.base ? absoluteUrl(input.site, input.base) : siteRoot(input.site);
|
|
19228
|
+
}
|
|
19229
|
+
return input.base || "/";
|
|
19230
|
+
};
|
|
19231
|
+
var buildApiSpec = (input) => {
|
|
19232
|
+
const pathEntries = [
|
|
19233
|
+
[
|
|
19234
|
+
API_PAGES_PATH,
|
|
19235
|
+
{
|
|
19236
|
+
get: {
|
|
19237
|
+
description: "Every documentation page with its route, title, description, content type, locale, facets, and the URLs of its rendered, Markdown, and JSON forms. Unfiltered; on a versioned site every version is listed with its `version`. Use it to enumerate the docs or to find a page when search is too narrow.",
|
|
19238
|
+
operationId: "listPages",
|
|
19239
|
+
responses: {
|
|
19240
|
+
"200": jsonResponse("The page index.", "PagesIndex"),
|
|
19241
|
+
default: problemResponse("An error, as problem details.")
|
|
19242
|
+
},
|
|
19243
|
+
summary: "List every page",
|
|
19244
|
+
tags: ["Pages"]
|
|
19245
|
+
}
|
|
19246
|
+
}
|
|
19247
|
+
],
|
|
19248
|
+
[
|
|
19249
|
+
API_PAGE_PATH,
|
|
19250
|
+
{
|
|
19251
|
+
get: {
|
|
19252
|
+
description: "A single page as JSON: its index entry plus the page's agent Markdown (frontmatter included, components downleveled to plain Markdown). Take `route` from `listPages` or `searchDocs`.",
|
|
19253
|
+
operationId: "getPage",
|
|
19254
|
+
parameters: [ROUTE_PARAM],
|
|
19255
|
+
responses: {
|
|
19256
|
+
"200": jsonResponse("The page.", "Page"),
|
|
19257
|
+
"404": problemResponse("No page has that route."),
|
|
19258
|
+
default: problemResponse("An error, as problem details.")
|
|
19259
|
+
},
|
|
19260
|
+
summary: "Get a page as JSON",
|
|
19261
|
+
tags: ["Pages"]
|
|
19262
|
+
}
|
|
19263
|
+
}
|
|
19264
|
+
],
|
|
19265
|
+
[
|
|
19266
|
+
API_NAVIGATION_PATH,
|
|
19267
|
+
{
|
|
19268
|
+
get: {
|
|
19269
|
+
description: "The navigation tree (header tabs and the sidebar hierarchy) as readers see it, for the default locale and the current docs.",
|
|
19270
|
+
operationId: "getNavigation",
|
|
19271
|
+
responses: {
|
|
19272
|
+
"200": jsonResponse("The navigation tree.", "Navigation"),
|
|
19273
|
+
default: problemResponse("An error, as problem details.")
|
|
19274
|
+
},
|
|
19275
|
+
summary: "Get the navigation tree",
|
|
19276
|
+
tags: ["Navigation"]
|
|
19277
|
+
}
|
|
19278
|
+
}
|
|
19279
|
+
]
|
|
19280
|
+
];
|
|
19281
|
+
if (input.search) {
|
|
19282
|
+
pathEntries.push([
|
|
19283
|
+
API_SEARCH_PATH,
|
|
19284
|
+
{
|
|
19285
|
+
get: {
|
|
19286
|
+
description: "Full-text search across the documentation. Returns matching pages with their title, route, content type, and a short excerpt; narrow by content type, locale, version, or facet. Use it first to discover relevant pages, then `getPage` to read one in full.",
|
|
19287
|
+
operationId: "searchDocs",
|
|
19288
|
+
parameters: searchParameters,
|
|
19289
|
+
responses: {
|
|
19290
|
+
"200": jsonResponse("The matching pages, best first.", "SearchResponse"),
|
|
19291
|
+
"400": problemResponse("The query was missing or blank."),
|
|
19292
|
+
default: problemResponse("An error, as problem details.")
|
|
19293
|
+
},
|
|
19294
|
+
summary: "Search the docs",
|
|
19295
|
+
tags: ["Search"]
|
|
19296
|
+
}
|
|
19297
|
+
}
|
|
19298
|
+
]);
|
|
19299
|
+
}
|
|
19300
|
+
pathEntries.push([
|
|
19301
|
+
"/{route}.md",
|
|
19302
|
+
{
|
|
19303
|
+
get: {
|
|
19304
|
+
description: "A page's raw-Markdown mirror: append `.md` to any page URL. Components are downleveled to plain Markdown; `.mdx` serves the source as written. The same body the `getPage` operation carries in its `markdown` field.",
|
|
19305
|
+
operationId: "getPageMarkdown",
|
|
19306
|
+
parameters: [ROUTE_PARAM],
|
|
19307
|
+
responses: {
|
|
19308
|
+
"200": textResponse("The page as Markdown.", MARKDOWN_TYPE),
|
|
19309
|
+
"404": textResponse("No page has that route; the body lists where to look next.", MARKDOWN_TYPE)
|
|
19310
|
+
},
|
|
19311
|
+
summary: "Get a page as Markdown",
|
|
19312
|
+
tags: ["Markdown"]
|
|
19313
|
+
}
|
|
19314
|
+
}
|
|
19315
|
+
]);
|
|
19316
|
+
if (input.llmsTxt) {
|
|
19317
|
+
pathEntries.push([
|
|
19318
|
+
"/llms.txt",
|
|
19319
|
+
{
|
|
19320
|
+
get: {
|
|
19321
|
+
description: "The llms.txt index: the site's summary, when to use it, and every page with a one-line description, grouped by section.",
|
|
19322
|
+
operationId: "getLlmsTxt",
|
|
19323
|
+
responses: {
|
|
19324
|
+
"200": textResponse("The index.", TEXT_TYPE)
|
|
19325
|
+
},
|
|
19326
|
+
summary: "Get llms.txt",
|
|
19327
|
+
tags: ["Markdown"]
|
|
19328
|
+
}
|
|
19329
|
+
}
|
|
19330
|
+
], [
|
|
19331
|
+
"/llms-full.txt",
|
|
19332
|
+
{
|
|
19333
|
+
get: {
|
|
19334
|
+
description: "The full Markdown of every current-docs page in one file.",
|
|
19335
|
+
operationId: "getLlmsFullTxt",
|
|
19336
|
+
responses: {
|
|
19337
|
+
"200": textResponse("Every page's Markdown.", TEXT_TYPE)
|
|
19338
|
+
},
|
|
19339
|
+
summary: "Get llms-full.txt",
|
|
19340
|
+
tags: ["Markdown"]
|
|
19341
|
+
}
|
|
19342
|
+
}
|
|
19343
|
+
]);
|
|
19344
|
+
}
|
|
19345
|
+
if (input.agentReadability) {
|
|
19346
|
+
pathEntries.push([
|
|
19347
|
+
"/agent-readability.json",
|
|
19348
|
+
{
|
|
19349
|
+
get: {
|
|
19350
|
+
description: "A manifest indexing every agent-facing artifact the site publishes — this API, the Markdown mirrors, llms.txt, the MCP server, feeds, and the sitemap.",
|
|
19351
|
+
operationId: "getAgentReadability",
|
|
19352
|
+
responses: {
|
|
19353
|
+
"200": {
|
|
19354
|
+
content: { [JSON_TYPE]: { schema: { type: "object" } } },
|
|
19355
|
+
description: "The manifest."
|
|
19356
|
+
}
|
|
19357
|
+
},
|
|
19358
|
+
summary: "Get the agent-readability manifest",
|
|
19359
|
+
tags: ["Discovery"]
|
|
19360
|
+
}
|
|
19361
|
+
}
|
|
19362
|
+
]);
|
|
19363
|
+
}
|
|
19364
|
+
if (input.mcpRoute) {
|
|
19365
|
+
pathEntries.push([
|
|
19366
|
+
input.mcpRoute,
|
|
19367
|
+
{
|
|
19368
|
+
post: {
|
|
19369
|
+
description: "The Model Context Protocol server (Streamable HTTP, stateless, JSON responses). Tools: `search_docs`, `get_page`, `list_pages`, `get_navigation` — the same operations this API exposes — plus every page as a `text/markdown` resource. Discovery document at `/.well-known/mcp.json`.",
|
|
19370
|
+
operationId: "mcp",
|
|
19371
|
+
requestBody: {
|
|
19372
|
+
content: { [JSON_TYPE]: { schema: ref("JsonRpcRequest") } },
|
|
19373
|
+
required: true
|
|
19374
|
+
},
|
|
19375
|
+
responses: {
|
|
19376
|
+
"200": jsonResponse("The JSON-RPC response.", "JsonRpcResponse")
|
|
19377
|
+
},
|
|
19378
|
+
summary: "Call the MCP server",
|
|
19379
|
+
tags: ["MCP"]
|
|
19380
|
+
}
|
|
19381
|
+
}
|
|
19382
|
+
]);
|
|
19383
|
+
}
|
|
19384
|
+
const tags = [
|
|
19385
|
+
{ description: "Enumerate and read documentation pages.", name: "Pages" },
|
|
19386
|
+
...input.search ? [{ description: "Full-text search over the docs.", name: "Search" }] : [],
|
|
19387
|
+
{ description: "How the docs are organized.", name: "Navigation" },
|
|
19388
|
+
{ description: "Plain-text and Markdown surfaces.", name: "Markdown" },
|
|
19389
|
+
...input.agentReadability ? [{ description: "Agent discovery documents.", name: "Discovery" }] : [],
|
|
19390
|
+
...input.mcpRoute ? [{ description: "The Model Context Protocol endpoint.", name: "MCP" }] : []
|
|
19391
|
+
];
|
|
19392
|
+
const info = {
|
|
19393
|
+
description: [
|
|
19394
|
+
`Read-only JSON API over the ${input.name} documentation${input.description ? `: ${input.description}` : "."}`,
|
|
19395
|
+
"Every operation is public and needs no authentication. Errors are RFC 9457 problem details (`application/problem+json`) with a stable `code`, a `detail`, and a `resolution` hint."
|
|
19396
|
+
].join(`
|
|
19397
|
+
|
|
19398
|
+
`),
|
|
19399
|
+
title: `${input.name} API`,
|
|
19400
|
+
version: input.version,
|
|
19401
|
+
"x-generator": `blume@${input.version}`
|
|
19402
|
+
};
|
|
19403
|
+
const document = {
|
|
19404
|
+
components: { schemas },
|
|
19405
|
+
info,
|
|
19406
|
+
openapi: "3.1.0",
|
|
19407
|
+
paths: Object.fromEntries(pathEntries),
|
|
19408
|
+
security: [],
|
|
19409
|
+
servers: [{ description: input.name, url: serverUrl(input) }],
|
|
19410
|
+
tags
|
|
19411
|
+
};
|
|
19412
|
+
if (input.site) {
|
|
19413
|
+
document.externalDocs = {
|
|
19414
|
+
description: `${input.name} documentation`,
|
|
19415
|
+
url: absoluteUrl(input.site, withBasePath(input.base, "/"))
|
|
19416
|
+
};
|
|
19417
|
+
}
|
|
19418
|
+
return document;
|
|
19419
|
+
};
|
|
19420
|
+
|
|
18734
19421
|
// src/ai/ask-data.ts
|
|
18735
19422
|
var buildAskData = async (project) => {
|
|
18736
19423
|
const documents = await buildSearchDocuments(project, {
|
|
@@ -18854,7 +19541,7 @@ var MCP_TOOLS = [
|
|
|
18854
19541
|
];
|
|
18855
19542
|
|
|
18856
19543
|
// src/ai/mcp/discovery.ts
|
|
18857
|
-
var
|
|
19544
|
+
var serverUrl2 = (input) => {
|
|
18858
19545
|
const path = withBasePath(input.base, input.route);
|
|
18859
19546
|
return input.site ? absoluteUrl(input.site, path) : path;
|
|
18860
19547
|
};
|
|
@@ -18863,7 +19550,7 @@ var buildMcpDiscovery = (input) => ({
|
|
|
18863
19550
|
{
|
|
18864
19551
|
name: input.name,
|
|
18865
19552
|
transport: "streamable-http",
|
|
18866
|
-
url:
|
|
19553
|
+
url: serverUrl2(input)
|
|
18867
19554
|
}
|
|
18868
19555
|
]
|
|
18869
19556
|
});
|
|
@@ -18884,7 +19571,7 @@ var reverseDnsName = (input) => {
|
|
|
18884
19571
|
};
|
|
18885
19572
|
var HTTP_URL2 = /^https?:\/\//u;
|
|
18886
19573
|
var buildMcpServerCard = (input) => {
|
|
18887
|
-
const url =
|
|
19574
|
+
const url = serverUrl2(input);
|
|
18888
19575
|
const card2 = {
|
|
18889
19576
|
$schema: SERVER_CARD_SCHEMA,
|
|
18890
19577
|
capabilities: {
|
|
@@ -19534,52 +20221,52 @@ var specConfiguration = async (spec, root) => {
|
|
|
19534
20221
|
};
|
|
19535
20222
|
}
|
|
19536
20223
|
};
|
|
19537
|
-
var acceptScalarReference = (
|
|
19538
|
-
if (
|
|
20224
|
+
var acceptScalarReference = (ref2, seen, contentRoutes, warnings) => {
|
|
20225
|
+
if (ref2.renderer !== "scalar") {
|
|
19539
20226
|
return null;
|
|
19540
20227
|
}
|
|
19541
|
-
if (seen.has(
|
|
19542
|
-
warnings.push(`Two API reference sources resolve to ${
|
|
20228
|
+
if (seen.has(ref2.route)) {
|
|
20229
|
+
warnings.push(`Two API reference sources resolve to ${ref2.route}; keeping the first.`);
|
|
19543
20230
|
return null;
|
|
19544
20231
|
}
|
|
19545
|
-
if (contentRoutes.has(
|
|
19546
|
-
warnings.push(`API reference route ${
|
|
20232
|
+
if (contentRoutes.has(ref2.route)) {
|
|
20233
|
+
warnings.push(`API reference route ${ref2.route} collides with a content page; skipping the reference there.`);
|
|
19547
20234
|
return null;
|
|
19548
20235
|
}
|
|
19549
|
-
seen.add(
|
|
19550
|
-
return
|
|
20236
|
+
seen.add(ref2.route);
|
|
20237
|
+
return ref2;
|
|
19551
20238
|
};
|
|
19552
20239
|
var buildReferenceFiles = async (options) => {
|
|
19553
20240
|
const { config, root, contentRoutes } = options;
|
|
19554
20241
|
const warnings = [];
|
|
19555
20242
|
const seen = new Set;
|
|
19556
20243
|
const accepted = [];
|
|
19557
|
-
for (const
|
|
19558
|
-
const next = acceptScalarReference(
|
|
20244
|
+
for (const ref2 of resolveReferences(config)) {
|
|
20245
|
+
const next = acceptScalarReference(ref2, seen, contentRoutes, warnings);
|
|
19559
20246
|
if (next) {
|
|
19560
20247
|
accepted.push(next);
|
|
19561
20248
|
}
|
|
19562
20249
|
}
|
|
19563
|
-
const built = await Promise.all(accepted.map(async (
|
|
19564
|
-
ref,
|
|
19565
|
-
spec: await specConfiguration(
|
|
20250
|
+
const built = await Promise.all(accepted.map(async (ref2) => ({
|
|
20251
|
+
ref: ref2,
|
|
20252
|
+
spec: await specConfiguration(ref2.spec, root)
|
|
19566
20253
|
})));
|
|
19567
20254
|
const files = [];
|
|
19568
|
-
for (const { ref, spec } of built) {
|
|
20255
|
+
for (const { ref: ref2, spec } of built) {
|
|
19569
20256
|
if (spec.warning) {
|
|
19570
20257
|
warnings.push(spec.warning);
|
|
19571
20258
|
}
|
|
19572
|
-
const pagePath = referencePagePath(
|
|
20259
|
+
const pagePath = referencePagePath(ref2.route);
|
|
19573
20260
|
files.push({
|
|
19574
20261
|
content: scalarReferenceTemplate({
|
|
19575
20262
|
configuration: {
|
|
19576
20263
|
...spec.config,
|
|
19577
|
-
...themeConfiguration(config,
|
|
19578
|
-
...
|
|
20264
|
+
...themeConfiguration(config, ref2.theme),
|
|
20265
|
+
...ref2.scalar
|
|
19579
20266
|
},
|
|
19580
|
-
noindex:
|
|
19581
|
-
route:
|
|
19582
|
-
title:
|
|
20267
|
+
noindex: ref2.noindex,
|
|
20268
|
+
route: ref2.route,
|
|
20269
|
+
title: ref2.label
|
|
19583
20270
|
}),
|
|
19584
20271
|
pagePath
|
|
19585
20272
|
});
|
|
@@ -21311,6 +21998,7 @@ var buildRuntimeData = (project) => {
|
|
|
21311
21998
|
description: config.description,
|
|
21312
21999
|
discovery: {
|
|
21313
22000
|
agentReadability: config.seo.agentReadability,
|
|
22001
|
+
api: config.ai.api,
|
|
21314
22002
|
llmsTxt: config.ai.llmsTxt.enabled,
|
|
21315
22003
|
sitemap: config.seo.sitemap && Boolean(config.deployment.site)
|
|
21316
22004
|
},
|
|
@@ -21439,12 +22127,18 @@ var planMcp = (project, srcDir, userPages) => {
|
|
|
21439
22127
|
enabled: true
|
|
21440
22128
|
};
|
|
21441
22129
|
};
|
|
21442
|
-
var
|
|
21443
|
-
if (!
|
|
21444
|
-
return;
|
|
22130
|
+
var publishAgentData = async (project, plans, modules) => {
|
|
22131
|
+
if (!(plans.mcp.enabled || plans.api.enabled)) {
|
|
22132
|
+
return null;
|
|
21445
22133
|
}
|
|
21446
22134
|
const data = await buildMcpData(project);
|
|
21447
22135
|
modules.set("blume:mcp-data", JSON.stringify(data));
|
|
22136
|
+
return data;
|
|
22137
|
+
};
|
|
22138
|
+
var writeMcpFiles = async (plan, write, data) => {
|
|
22139
|
+
if (!(plan.enabled && data)) {
|
|
22140
|
+
return;
|
|
22141
|
+
}
|
|
21448
22142
|
const discoveryInput = {
|
|
21449
22143
|
base: data.base,
|
|
21450
22144
|
name: data.name,
|
|
@@ -21458,6 +22152,52 @@ var writeMcpFiles = async (project, plan, write, modules) => {
|
|
|
21458
22152
|
write(join31(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
|
|
21459
22153
|
]);
|
|
21460
22154
|
};
|
|
22155
|
+
var ownsApiRest = (page) => page.pattern.startsWith("/api/[");
|
|
22156
|
+
var contentUnderApi = (page) => page.route === "/api" || page.route.startsWith("/api/");
|
|
22157
|
+
var planApi = (project, srcDir, userPages) => {
|
|
22158
|
+
const { config, context } = project;
|
|
22159
|
+
const server = config.deployment.output === "server";
|
|
22160
|
+
return {
|
|
22161
|
+
catchAll: server && !userPages.some(ownsApiRest) && !project.graph.pages.some(contentUnderApi),
|
|
22162
|
+
enabled: config.ai.api,
|
|
22163
|
+
server,
|
|
22164
|
+
spec: !routeIsTaken(userPages, project.graph.pages, OPENAPI_PATH) && !existsSync17(join31(context.root, "public", "openapi.json")),
|
|
22165
|
+
srcDir
|
|
22166
|
+
};
|
|
22167
|
+
};
|
|
22168
|
+
var writeApiFiles = async (project, plan, write, data, mcp) => {
|
|
22169
|
+
if (!(plan.enabled && data)) {
|
|
22170
|
+
return;
|
|
22171
|
+
}
|
|
22172
|
+
const { config } = project;
|
|
22173
|
+
const mcpRoute = mcp.enabled ? mcp.route : null;
|
|
22174
|
+
const apiDir = join31(plan.srcDir, "pages", "api");
|
|
22175
|
+
const writes = [
|
|
22176
|
+
write(join31(apiDir, "docs", "pages.json.ts"), apiPagesIndexTemplate()),
|
|
22177
|
+
write(join31(apiDir, "docs", "pages", "[...route].json.ts"), apiPageTemplate()),
|
|
22178
|
+
write(join31(apiDir, "docs", "navigation.json.ts"), apiNavigationTemplate())
|
|
22179
|
+
];
|
|
22180
|
+
if (plan.server) {
|
|
22181
|
+
writes.push(write(join31(apiDir, "docs", "search.ts"), apiSearchTemplate()));
|
|
22182
|
+
}
|
|
22183
|
+
if (plan.catchAll) {
|
|
22184
|
+
writes.push(write(join31(apiDir, "[...path].ts"), apiNotFoundTemplate({ base: data.base, site: data.site })));
|
|
22185
|
+
}
|
|
22186
|
+
if (plan.spec) {
|
|
22187
|
+
writes.push(write(join31(plan.srcDir, "pages", "openapi.json.ts"), staticJsonEndpointTemplate(buildApiSpec({
|
|
22188
|
+
agentReadability: config.seo.agentReadability,
|
|
22189
|
+
base: data.base,
|
|
22190
|
+
description: config.description,
|
|
22191
|
+
llmsTxt: config.ai.llmsTxt.enabled,
|
|
22192
|
+
mcpRoute,
|
|
22193
|
+
name: config.title,
|
|
22194
|
+
search: plan.server,
|
|
22195
|
+
site: data.site,
|
|
22196
|
+
version: data.version
|
|
22197
|
+
}))));
|
|
22198
|
+
}
|
|
22199
|
+
await Promise.all(writes);
|
|
22200
|
+
};
|
|
21461
22201
|
var planPlaygroundProxy = (config, srcDir) => ({
|
|
21462
22202
|
enabled: needsPlaygroundProxy(config),
|
|
21463
22203
|
entrypoint: join31(srcDir, "blume-openapi", "api-proxy.ts"),
|
|
@@ -21514,7 +22254,8 @@ var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
|
|
|
21514
22254
|
}
|
|
21515
22255
|
await Promise.all([
|
|
21516
22256
|
write(join31(srcDir, "pages", "404.astro"), notFoundPageTemplate()),
|
|
21517
|
-
write(join31(srcDir, "pages", "404.md.ts"), notFoundMarkdownTemplate())
|
|
22257
|
+
write(join31(srcDir, "pages", "404.md.ts"), notFoundMarkdownTemplate()),
|
|
22258
|
+
write(join31(srcDir, "pages", "404.json.ts"), notFoundJsonTemplate())
|
|
21518
22259
|
]);
|
|
21519
22260
|
};
|
|
21520
22261
|
var diagnosticWarning = (diagnostic) => diagnostic.suggestion ? `${diagnostic.message} ${diagnostic.suggestion}` : diagnostic.message;
|
|
@@ -21604,6 +22345,8 @@ var generateRuntime = async (project) => {
|
|
|
21604
22345
|
const changelogIndex = hasGeneratedChangelog(project, pages);
|
|
21605
22346
|
const mcp = planMcp(project, srcDir, pages);
|
|
21606
22347
|
pages.push(...mcp.discoveryPages);
|
|
22348
|
+
const api = planApi(project, srcDir, pages);
|
|
22349
|
+
const agentData = await publishAgentData(project, { api, mcp }, modules);
|
|
21607
22350
|
const openApiSource2 = project.sources.find(isOpenApiSource);
|
|
21608
22351
|
const openApiData = openApiSource2 ? openApiSource2.openApiData() : {};
|
|
21609
22352
|
const playgroundProxy = planPlaygroundProxy(config, srcDir);
|
|
@@ -21676,7 +22419,8 @@ var generateRuntime = async (project) => {
|
|
|
21676
22419
|
Promise.all(slotPlan.wrappers.map((wrapper) => write(join31(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content))),
|
|
21677
22420
|
Promise.all(exampleDiscovery.examples.map((example) => write(join31(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example)))),
|
|
21678
22421
|
writeAskFiles(project, srcDir, write, modules),
|
|
21679
|
-
writeMcpFiles(
|
|
22422
|
+
writeMcpFiles(mcp, write, agentData),
|
|
22423
|
+
writeApiFiles(project, api, write, agentData, mcp),
|
|
21680
22424
|
playgroundProxy.enabled ? write(playgroundProxy.entrypoint, playgroundProxyTemplate(proxyOrigins)) : Promise.resolve(false)
|
|
21681
22425
|
]);
|
|
21682
22426
|
if (config.seo.og.enabled) {
|
|
@@ -21988,8 +22732,11 @@ var emitVercelNegotiation = async (project, routePaths, root) => {
|
|
|
21988
22732
|
}
|
|
21989
22733
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
21990
22734
|
const home = rawMarkdown["/"];
|
|
21991
|
-
const
|
|
21992
|
-
const injected = injectNegotiationRoutes(await readFile20(configPath, "utf-8"), routePaths, buildHomeLinkHeader(config2, routePaths), overrides, home ? markdownTokenCount(agentMarkdown(home)) : undefined,
|
|
22735
|
+
const staticDir = join33(root, ".vercel", "output", "static");
|
|
22736
|
+
const injected = injectNegotiationRoutes(await readFile20(configPath, "utf-8"), routePaths, buildHomeLinkHeader(config2, routePaths), overrides, home ? markdownTokenCount(agentMarkdown(home)) : undefined, {
|
|
22737
|
+
json: existsSync19(join33(staticDir, "404.json")),
|
|
22738
|
+
markdown: existsSync19(join33(staticDir, "404.md"))
|
|
22739
|
+
});
|
|
21993
22740
|
if (injected === null) {
|
|
21994
22741
|
logger.warn("Could not wire Accept: text/markdown negotiation into .vercel/output/config.json — raw Markdown stays available at the .md URLs.");
|
|
21995
22742
|
return;
|
|
@@ -24489,6 +25236,9 @@ import {
|
|
|
24489
25236
|
McpError,
|
|
24490
25237
|
ReadResourceRequestSchema
|
|
24491
25238
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
25239
|
+
import { z as z8 } from "zod";
|
|
25240
|
+
|
|
25241
|
+
// src/ai/mcp/query.ts
|
|
24492
25242
|
import { z as z7 } from "zod";
|
|
24493
25243
|
|
|
24494
25244
|
// src/search/orama-index.ts
|
|
@@ -24632,13 +25382,10 @@ var queryOramaIndex = async (db, term, limit, filters) => {
|
|
|
24632
25382
|
return found.hits.map((hit) => hit.document);
|
|
24633
25383
|
};
|
|
24634
25384
|
|
|
24635
|
-
// src/ai/mcp/
|
|
25385
|
+
// src/ai/mcp/query.ts
|
|
24636
25386
|
var DEFAULT_SEARCH_LIMIT = 8;
|
|
24637
25387
|
var MAX_SEARCH_LIMIT = 20;
|
|
24638
25388
|
var EXCERPT_LENGTH = 200;
|
|
24639
|
-
var RESOURCE_MIME_TYPE = "text/markdown";
|
|
24640
|
-
var RESOURCE_NOT_FOUND = -32002;
|
|
24641
|
-
var LOCAL_RESOURCE_SCHEME = "blume:";
|
|
24642
25389
|
var contentTypesField = z7.preprocess((value) => {
|
|
24643
25390
|
const list2 = (Array.isArray(value) ? value : [value]).filter((entry) => typeof entry === "string");
|
|
24644
25391
|
return list2.length > 0 ? list2 : undefined;
|
|
@@ -24690,21 +25437,6 @@ var TOOL_INPUTS = {
|
|
|
24690
25437
|
version: versionField
|
|
24691
25438
|
})
|
|
24692
25439
|
};
|
|
24693
|
-
var inputSchemaFor = (schema) => {
|
|
24694
|
-
const {
|
|
24695
|
-
$schema: _dialect,
|
|
24696
|
-
additionalProperties: _closed,
|
|
24697
|
-
...rest
|
|
24698
|
-
} = z7.toJSONSchema(schema);
|
|
24699
|
-
return rest;
|
|
24700
|
-
};
|
|
24701
|
-
var TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({
|
|
24702
|
-
annotations: tool.annotations,
|
|
24703
|
-
description: tool.description,
|
|
24704
|
-
inputSchema: inputSchemaFor(TOOL_INPUTS[tool.name]),
|
|
24705
|
-
name: tool.name,
|
|
24706
|
-
title: tool.title
|
|
24707
|
-
}));
|
|
24708
25440
|
var matchesFacets = (facets, filters) => Object.entries(filters).every(([key, value]) => facets?.[key] === value);
|
|
24709
25441
|
var asVersionScope = (value, data) => {
|
|
24710
25442
|
if (!data.archivedVersions) {
|
|
@@ -24736,8 +25468,6 @@ var urlFor = (route, data) => {
|
|
|
24736
25468
|
const path = withBasePath(data.base, route);
|
|
24737
25469
|
return data.site ? absoluteUrl(data.site, path) : path;
|
|
24738
25470
|
};
|
|
24739
|
-
var resourceUri = (route, data) => data.site ? urlFor(route, data) : `${LOCAL_RESOURCE_SCHEME}${withBasePath(data.base, route)}`;
|
|
24740
|
-
var resourceRoute = (uri, data) => normalizeRoute2(uri.startsWith(LOCAL_RESOURCE_SCHEME) ? uri.slice(LOCAL_RESOURCE_SCHEME.length) : uri, data);
|
|
24741
25471
|
var excerptFor = (doc) => {
|
|
24742
25472
|
if (doc.description) {
|
|
24743
25473
|
return doc.description;
|
|
@@ -24745,10 +25475,6 @@ var excerptFor = (doc) => {
|
|
|
24745
25475
|
const head = doc.content.slice(0, EXCERPT_LENGTH).trim();
|
|
24746
25476
|
return doc.content.length > EXCERPT_LENGTH ? `${head}…` : head;
|
|
24747
25477
|
};
|
|
24748
|
-
var text = (value, isError = false) => {
|
|
24749
|
-
const content = [{ text: value, type: "text" }];
|
|
24750
|
-
return isError ? { content, isError: true } : { content };
|
|
24751
|
-
};
|
|
24752
25478
|
var createIndexProvider = (documents, locale) => {
|
|
24753
25479
|
let dbPromise = null;
|
|
24754
25480
|
return function provideIndex() {
|
|
@@ -24756,6 +25482,90 @@ var createIndexProvider = (documents, locale) => {
|
|
|
24756
25482
|
return dbPromise;
|
|
24757
25483
|
};
|
|
24758
25484
|
};
|
|
25485
|
+
var searchDocs = async (data, index, input) => {
|
|
25486
|
+
const db = await index();
|
|
25487
|
+
const hits = await queryOramaIndex(db, input.query, input.limit ?? DEFAULT_SEARCH_LIMIT, {
|
|
25488
|
+
contentTypes: input.contentTypes,
|
|
25489
|
+
facets: input.filters,
|
|
25490
|
+
locale: input.locale,
|
|
25491
|
+
version: asVersionScope(input.version, data)
|
|
25492
|
+
});
|
|
25493
|
+
return hits.map((doc) => {
|
|
25494
|
+
const hit = {
|
|
25495
|
+
contentType: doc.contentType,
|
|
25496
|
+
excerpt: excerptFor(doc),
|
|
25497
|
+
facets: doc.facets,
|
|
25498
|
+
route: doc.route,
|
|
25499
|
+
title: doc.title,
|
|
25500
|
+
url: urlFor(doc.route, data)
|
|
25501
|
+
};
|
|
25502
|
+
if (data.archivedVersions) {
|
|
25503
|
+
hit.version = doc.version ?? "";
|
|
25504
|
+
}
|
|
25505
|
+
return hit;
|
|
25506
|
+
});
|
|
25507
|
+
};
|
|
25508
|
+
var getPageMarkdown = (data, route) => data.pages[route];
|
|
25509
|
+
var listPages = (data, input) => {
|
|
25510
|
+
const { contentTypes, filters, locale } = input;
|
|
25511
|
+
const versionScope = asVersionScope(input.version, data);
|
|
25512
|
+
return data.routes.filter((route) => (!contentTypes || contentTypes.includes(route.contentType)) && (!filters || matchesFacets(route.facets, filters)) && (!locale || route.locale === locale) && (versionScope === undefined || route.version === versionScope)).map((route) => {
|
|
25513
|
+
const listing = {
|
|
25514
|
+
contentType: route.contentType,
|
|
25515
|
+
description: route.description,
|
|
25516
|
+
facets: route.facets,
|
|
25517
|
+
lastModified: route.lastModified,
|
|
25518
|
+
route: route.route,
|
|
25519
|
+
title: route.title,
|
|
25520
|
+
url: urlFor(route.route, data)
|
|
25521
|
+
};
|
|
25522
|
+
if (data.archivedVersions) {
|
|
25523
|
+
listing.version = route.version;
|
|
25524
|
+
}
|
|
25525
|
+
return listing;
|
|
25526
|
+
});
|
|
25527
|
+
};
|
|
25528
|
+
var getNavigation = (data, input) => {
|
|
25529
|
+
const { locale, version: versionId } = input;
|
|
25530
|
+
const unknownVersion = unknownVersionError(versionId, data);
|
|
25531
|
+
if (unknownVersion) {
|
|
25532
|
+
return { error: unknownVersion };
|
|
25533
|
+
}
|
|
25534
|
+
let { navigation } = data;
|
|
25535
|
+
const byLocale = versionId ? data.navigationByVersion?.[versionId] : undefined;
|
|
25536
|
+
if (byLocale) {
|
|
25537
|
+
navigation = (locale ? byLocale[locale] : undefined) ?? byLocale[data.defaultLocale ?? ""] ?? Object.values(byLocale)[0] ?? navigation;
|
|
25538
|
+
} else if (locale && data.navigationByLocale?.[locale]) {
|
|
25539
|
+
navigation = data.navigationByLocale[locale];
|
|
25540
|
+
}
|
|
25541
|
+
return { navigation };
|
|
25542
|
+
};
|
|
25543
|
+
|
|
25544
|
+
// src/ai/mcp/server.ts
|
|
25545
|
+
var RESOURCE_MIME_TYPE = "text/markdown";
|
|
25546
|
+
var RESOURCE_NOT_FOUND = -32002;
|
|
25547
|
+
var LOCAL_RESOURCE_SCHEME = "blume:";
|
|
25548
|
+
var inputSchemaFor = (schema) => {
|
|
25549
|
+
const {
|
|
25550
|
+
$schema: _dialect,
|
|
25551
|
+
additionalProperties: _closed,
|
|
25552
|
+
...rest
|
|
25553
|
+
} = z8.toJSONSchema(schema);
|
|
25554
|
+
return rest;
|
|
25555
|
+
};
|
|
25556
|
+
var TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({
|
|
25557
|
+
annotations: tool.annotations,
|
|
25558
|
+
description: tool.description,
|
|
25559
|
+
inputSchema: inputSchemaFor(TOOL_INPUTS[tool.name]),
|
|
25560
|
+
name: tool.name,
|
|
25561
|
+
title: tool.title
|
|
25562
|
+
}));
|
|
25563
|
+
var resourceUri = (route, data) => data.site ? urlFor(route, data) : `${LOCAL_RESOURCE_SCHEME}${withBasePath(data.base, route)}`;
|
|
25564
|
+
var resourceRoute = (uri, data) => normalizeRoute2(uri.startsWith(LOCAL_RESOURCE_SCHEME) ? uri.slice(LOCAL_RESOURCE_SCHEME.length) : uri, data);
|
|
25565
|
+
var text = (value, isError = false) => {
|
|
25566
|
+
const content = [{ text: value, type: "text" }];
|
|
25567
|
+
return isError ? { content, isError: true } : { content };
|
|
25568
|
+
};
|
|
24759
25569
|
var buildServer = (data, index) => {
|
|
24760
25570
|
const capabilities = { resources: {}, tools: {} };
|
|
24761
25571
|
const serverOptions = data.instructions ? { capabilities, instructions: data.instructions } : { capabilities };
|
|
@@ -24779,7 +25589,7 @@ var buildServer = (data, index) => {
|
|
|
24779
25589
|
}));
|
|
24780
25590
|
server.setRequestHandler(ReadResourceRequestSchema, (request2) => {
|
|
24781
25591
|
const { uri } = request2.params;
|
|
24782
|
-
const markdown = data
|
|
25592
|
+
const markdown = getPageMarkdown(data, resourceRoute(uri, data));
|
|
24783
25593
|
if (markdown === undefined) {
|
|
24784
25594
|
throw new McpError(RESOURCE_NOT_FOUND, `No page found at "${uri}". Use resources/list or list_pages to find valid URIs.`);
|
|
24785
25595
|
}
|
|
@@ -24790,74 +25600,28 @@ var buildServer = (data, index) => {
|
|
|
24790
25600
|
server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
24791
25601
|
const { arguments: args = {}, name } = request2.params;
|
|
24792
25602
|
if (name === "search_docs") {
|
|
24793
|
-
const
|
|
24794
|
-
const db = await index();
|
|
24795
|
-
const hits = await queryOramaIndex(db, input.query, input.limit ?? DEFAULT_SEARCH_LIMIT, {
|
|
24796
|
-
contentTypes: input.contentTypes,
|
|
24797
|
-
facets: input.filters,
|
|
24798
|
-
locale: input.locale,
|
|
24799
|
-
version: asVersionScope(input.version, data)
|
|
24800
|
-
});
|
|
24801
|
-
const results = hits.map((doc) => {
|
|
24802
|
-
const hit = {
|
|
24803
|
-
contentType: doc.contentType,
|
|
24804
|
-
excerpt: excerptFor(doc),
|
|
24805
|
-
facets: doc.facets,
|
|
24806
|
-
route: doc.route,
|
|
24807
|
-
title: doc.title,
|
|
24808
|
-
url: urlFor(doc.route, data)
|
|
24809
|
-
};
|
|
24810
|
-
if (data.archivedVersions) {
|
|
24811
|
-
hit.version = doc.version ?? "";
|
|
24812
|
-
}
|
|
24813
|
-
return hit;
|
|
24814
|
-
});
|
|
25603
|
+
const results = await searchDocs(data, index, TOOL_INPUTS.search_docs.parse(args));
|
|
24815
25604
|
return text(JSON.stringify(results, null, 2));
|
|
24816
25605
|
}
|
|
24817
25606
|
if (name === "get_page") {
|
|
24818
25607
|
const input = TOOL_INPUTS.get_page.parse(args);
|
|
24819
25608
|
const key = normalizeRoute2(input.route, data);
|
|
24820
|
-
const markdown = data
|
|
25609
|
+
const markdown = getPageMarkdown(data, key);
|
|
24821
25610
|
if (markdown === undefined) {
|
|
24822
25611
|
return text(`No page found at "${key}". Use list_pages or search_docs to find valid routes.`, true);
|
|
24823
25612
|
}
|
|
24824
25613
|
return text(markdown);
|
|
24825
25614
|
}
|
|
24826
25615
|
if (name === "list_pages") {
|
|
24827
|
-
const
|
|
24828
|
-
|
|
24829
|
-
const versionScope = asVersionScope(input.version, data);
|
|
24830
|
-
const routes = data.routes.filter((route) => (!contentTypes || contentTypes.includes(route.contentType)) && (!filters || matchesFacets(route.facets, filters)) && (!locale || route.locale === locale) && (versionScope === undefined || route.version === versionScope));
|
|
24831
|
-
return text(JSON.stringify(routes.map((route) => {
|
|
24832
|
-
const listing = {
|
|
24833
|
-
contentType: route.contentType,
|
|
24834
|
-
description: route.description,
|
|
24835
|
-
facets: route.facets,
|
|
24836
|
-
lastModified: route.lastModified,
|
|
24837
|
-
route: route.route,
|
|
24838
|
-
title: route.title,
|
|
24839
|
-
url: urlFor(route.route, data)
|
|
24840
|
-
};
|
|
24841
|
-
if (data.archivedVersions) {
|
|
24842
|
-
listing.version = route.version;
|
|
24843
|
-
}
|
|
24844
|
-
return listing;
|
|
24845
|
-
}), null, 2));
|
|
25616
|
+
const listing = listPages(data, TOOL_INPUTS.list_pages.parse(args));
|
|
25617
|
+
return text(JSON.stringify(listing, null, 2));
|
|
24846
25618
|
}
|
|
24847
25619
|
if (name === "get_navigation") {
|
|
24848
|
-
const
|
|
24849
|
-
|
|
24850
|
-
|
|
24851
|
-
return text(unknownVersion, true);
|
|
25620
|
+
const result = getNavigation(data, TOOL_INPUTS.get_navigation.parse(args));
|
|
25621
|
+
if ("error" in result) {
|
|
25622
|
+
return text(result.error, true);
|
|
24852
25623
|
}
|
|
24853
|
-
|
|
24854
|
-
const byLocale = versionId ? data.navigationByVersion?.[versionId] : undefined;
|
|
24855
|
-
if (byLocale) {
|
|
24856
|
-
navigation = (locale ? byLocale[locale] : undefined) ?? byLocale[data.defaultLocale ?? ""] ?? Object.values(byLocale)[0] ?? navigation;
|
|
24857
|
-
} else if (locale && data.navigationByLocale?.[locale]) {
|
|
24858
|
-
navigation = data.navigationByLocale[locale];
|
|
24859
|
-
}
|
|
24860
|
-
return text(JSON.stringify(navigation, null, 2));
|
|
25624
|
+
return text(JSON.stringify(result.navigation, null, 2));
|
|
24861
25625
|
}
|
|
24862
25626
|
return text(`Unknown tool: ${name}`, true);
|
|
24863
25627
|
});
|
|
@@ -25009,11 +25773,11 @@ var translateAgentArgs = (kind, lastMessagePath) => kind === "claude" ? claudeAr
|
|
|
25009
25773
|
import { createHash as createHash5 } from "node:crypto";
|
|
25010
25774
|
import { readFile as readFile26 } from "node:fs/promises";
|
|
25011
25775
|
import { join as join45 } from "pathe";
|
|
25012
|
-
import { z as
|
|
25776
|
+
import { z as z9 } from "zod";
|
|
25013
25777
|
var LEDGER_FILE = "blume.translations.json";
|
|
25014
|
-
var ledgerSchema =
|
|
25015
|
-
files:
|
|
25016
|
-
version:
|
|
25778
|
+
var ledgerSchema = z9.object({
|
|
25779
|
+
files: z9.record(z9.string(), z9.record(z9.string(), z9.string())),
|
|
25780
|
+
version: z9.literal(1)
|
|
25017
25781
|
});
|
|
25018
25782
|
var emptyLedger = () => ({
|
|
25019
25783
|
files: {},
|
|
@@ -26150,18 +26914,18 @@ var checkPathLink = (resolved, fragment, page2, link, site, ctx, via = "") => {
|
|
|
26150
26914
|
};
|
|
26151
26915
|
};
|
|
26152
26916
|
var checkExternalLinks = async (refs) => {
|
|
26153
|
-
const results = await probeAll(refs.map((
|
|
26917
|
+
const results = await probeAll(refs.map((ref2) => ref2.url));
|
|
26154
26918
|
const diagnostics = [];
|
|
26155
|
-
for (const
|
|
26156
|
-
const result = results.get(
|
|
26919
|
+
for (const ref2 of refs) {
|
|
26920
|
+
const result = results.get(ref2.url);
|
|
26157
26921
|
const grade = result ? gradeExternal(result) : null;
|
|
26158
26922
|
if (grade) {
|
|
26159
26923
|
diagnostics.push({
|
|
26160
26924
|
code: "BLUME_DEAD_LINK",
|
|
26161
|
-
column:
|
|
26162
|
-
file:
|
|
26163
|
-
line:
|
|
26164
|
-
message: `External link ${
|
|
26925
|
+
column: ref2.column,
|
|
26926
|
+
file: ref2.file,
|
|
26927
|
+
line: ref2.line,
|
|
26928
|
+
message: `External link ${ref2.url} is unreachable (${grade.detail}).`,
|
|
26165
26929
|
severity: grade.severity
|
|
26166
26930
|
});
|
|
26167
26931
|
}
|
|
@@ -26215,7 +26979,7 @@ var validateLinks = async (graph, options) => {
|
|
|
26215
26979
|
let uncheckedAssets = 0;
|
|
26216
26980
|
for (const page2 of graph.pages) {
|
|
26217
26981
|
for (const link of page2.links) {
|
|
26218
|
-
const result = classifyLink(page2, link, ctx, (
|
|
26982
|
+
const result = classifyLink(page2, link, ctx, (ref2) => external.push(ref2));
|
|
26219
26983
|
if (result === "asset-unchecked") {
|
|
26220
26984
|
uncheckedAssets += 1;
|
|
26221
26985
|
} else if (result) {
|
|
@@ -26613,5 +27377,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
26613
27377
|
});
|
|
26614
27378
|
runMain(main, { rawArgs: normalizeHostArgs(process.argv.slice(2)) });
|
|
26615
27379
|
|
|
26616
|
-
//# debugId=
|
|
27380
|
+
//# debugId=9AB8E5F1D71CCEF764756E2164756E21
|
|
26617
27381
|
//# sourceMappingURL=index.js.map
|