blume 1.7.1 → 1.7.3
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 +18 -0
- package/dist/cli/chunk-0qymqwzz.js +164 -0
- package/dist/cli/chunk-0qymqwzz.js.map +15 -0
- package/dist/cli/{chunk-8gnpdsn1.js → chunk-0xjyb285.js} +2 -2
- package/dist/cli/{chunk-12dzsn9b.js → chunk-1jefwnfs.js} +82 -81
- package/dist/cli/{chunk-12dzsn9b.js.map → chunk-1jefwnfs.js.map} +3 -3
- package/dist/cli/{chunk-27gtm2ym.js → chunk-2mzebbbz.js} +1 -1
- package/dist/cli/{chunk-s5e5jt53.js → chunk-2z47ypj8.js} +1 -1
- package/dist/cli/{chunk-jtb45atp.js → chunk-3r45185y.js} +10 -12
- package/dist/cli/{chunk-jtb45atp.js.map → chunk-3r45185y.js.map} +2 -2
- package/dist/cli/{chunk-6mq7qkve.js → chunk-4x36ddpw.js} +18 -24
- package/dist/cli/{chunk-6mq7qkve.js.map → chunk-4x36ddpw.js.map} +2 -2
- package/dist/cli/{chunk-he2zfgah.js → chunk-5093q3n7.js} +22 -30
- package/dist/cli/{chunk-he2zfgah.js.map → chunk-5093q3n7.js.map} +2 -2
- package/dist/cli/{chunk-k0v1f8bb.js → chunk-5g0w1e2c.js} +21 -28
- package/dist/cli/{chunk-k0v1f8bb.js.map → chunk-5g0w1e2c.js.map} +2 -2
- package/dist/cli/{chunk-5gfw0q4j.js → chunk-5qk08vmp.js} +26 -34
- package/dist/cli/{chunk-5gfw0q4j.js.map → chunk-5qk08vmp.js.map} +2 -2
- package/dist/cli/{chunk-r99hynxh.js → chunk-7s8hm3b6.js} +41 -9
- package/dist/cli/{chunk-r99hynxh.js.map → chunk-7s8hm3b6.js.map} +3 -3
- package/dist/cli/{chunk-vyqj481z.js → chunk-8cjtbafj.js} +68 -66
- package/dist/cli/chunk-8cjtbafj.js.map +13 -0
- package/dist/cli/{chunk-aqjvpd03.js → chunk-97r59kpr.js} +27 -33
- package/dist/cli/{chunk-aqjvpd03.js.map → chunk-97r59kpr.js.map} +2 -2
- package/dist/cli/{chunk-np8dmfb0.js → chunk-ahnw3kxw.js} +26 -33
- package/dist/cli/{chunk-np8dmfb0.js.map → chunk-ahnw3kxw.js.map} +2 -2
- package/dist/cli/{chunk-j5f2wrj5.js → chunk-b27xqwn9.js} +10 -15
- package/dist/cli/{chunk-j5f2wrj5.js.map → chunk-b27xqwn9.js.map} +2 -2
- package/dist/cli/{chunk-kmx2mydj.js → chunk-bf6bt1xt.js} +8 -8
- package/dist/cli/{chunk-kmx2mydj.js.map → chunk-bf6bt1xt.js.map} +1 -1
- package/dist/cli/{chunk-90pdhkpm.js → chunk-bvwwhd84.js} +23 -32
- package/dist/cli/{chunk-90pdhkpm.js.map → chunk-bvwwhd84.js.map} +2 -2
- package/dist/cli/{chunk-mfm4sjwx.js → chunk-cjtn640a.js} +32 -43
- package/dist/cli/{chunk-mfm4sjwx.js.map → chunk-cjtn640a.js.map} +2 -2
- package/dist/cli/{chunk-pxj10x8y.js → chunk-ct47dqpx.js} +14 -3
- package/dist/cli/{chunk-pxj10x8y.js.map → chunk-ct47dqpx.js.map} +4 -3
- package/dist/cli/{chunk-x66c5yjn.js → chunk-dwgcp5sm.js} +2 -2
- package/dist/cli/{chunk-4trphnvy.js → chunk-e7f42gdj.js} +10 -13
- package/dist/cli/{chunk-4trphnvy.js.map → chunk-e7f42gdj.js.map} +2 -2
- package/dist/cli/{chunk-82atea4k.js → chunk-esphfr8p.js} +14 -18
- package/dist/cli/{chunk-82atea4k.js.map → chunk-esphfr8p.js.map} +2 -2
- package/dist/cli/{chunk-q56730e0.js → chunk-ex56aa81.js} +53 -53
- package/dist/cli/chunk-ex56aa81.js.map +13 -0
- package/dist/cli/{chunk-ywn7t0pb.js → chunk-garjf5z9.js} +3 -3
- package/dist/cli/{chunk-ev67ycx0.js → chunk-jq5n4avg.js} +1 -1
- package/dist/cli/{chunk-ka5k7cz9.js → chunk-js7saxwm.js} +35 -39
- package/dist/cli/{chunk-ka5k7cz9.js.map → chunk-js7saxwm.js.map} +4 -6
- package/dist/cli/{chunk-x1wvw7a8.js → chunk-k79xp7av.js} +168 -208
- package/dist/cli/chunk-k79xp7av.js.map +39 -0
- package/dist/cli/{chunk-3r94j3tc.js → chunk-nn13znc2.js} +2 -2
- package/dist/cli/{chunk-4ae4f395.js → chunk-ps4m1xh4.js} +60 -35
- package/dist/cli/chunk-ps4m1xh4.js.map +15 -0
- package/dist/cli/{chunk-wd27zjcz.js → chunk-q4rae3bg.js} +1 -1
- package/dist/cli/{chunk-pdwg3q9g.js → chunk-rqy0s5wh.js} +21 -30
- package/dist/cli/{chunk-pdwg3q9g.js.map → chunk-rqy0s5wh.js.map} +2 -2
- package/dist/cli/{chunk-52cwcqvp.js → chunk-rz9jmfhz.js} +15 -24
- package/dist/cli/{chunk-52cwcqvp.js.map → chunk-rz9jmfhz.js.map} +2 -2
- package/dist/cli/{chunk-8p3xe5jv.js → chunk-vacwm2hv.js} +3 -3
- package/dist/cli/{chunk-cbjnx4s8.js → chunk-vh9w1sgp.js} +1 -1
- package/dist/cli/{chunk-sbdqrjbb.js → chunk-vrfp10qk.js} +1 -1
- package/dist/cli/{chunk-h9ekmtz7.js → chunk-yg63d42r.js} +28 -35
- package/dist/cli/{chunk-h9ekmtz7.js.map → chunk-yg63d42r.js.map} +2 -2
- package/dist/cli/{chunk-5hs6gb7n.js → chunk-yzhm0j9q.js} +1 -1
- package/dist/cli/index.js +397 -34
- package/dist/cli/index.js.map +12 -4
- package/dist/types/core/config-input.d.ts +59 -0
- package/dist/types/core/data.d.ts +2 -0
- package/dist/types/core/schema.d.ts +47 -3
- package/dist/types/core/types.d.ts +5 -0
- package/docs/configuration/ask-ai.mdx +61 -0
- package/docs/configuration/index.mdx +3 -1
- package/docs/content/navigation.mdx +3 -0
- package/docs/content/syntax.mdx +10 -0
- package/docs/discoverability/agent-discovery.mdx +82 -1
- package/docs/discoverability/index.mdx +1 -1
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/reference/frontmatter.mdx +2 -0
- package/package.json +1 -1
- package/src/ai/agent-readability.ts +5 -0
- package/src/ai/ai-catalog.ts +241 -0
- package/src/ai/cors.ts +87 -0
- package/src/ai/link-headers.ts +12 -0
- package/src/ai/llms.ts +6 -0
- package/src/ai/mcp/discovery.ts +1 -1
- package/src/astro/generate.ts +4 -0
- package/src/astro/templates.ts +88 -23
- package/src/cli/commands/build.ts +3 -1
- package/src/components/islands/hooks.ts +50 -1
- package/src/components/layout/NavTree.astro +75 -66
- package/src/components/layout/RootLayout.astro +28 -2
- package/src/components/layout/analytics-client.ts +36 -7
- package/src/components/layout/nav-utils.ts +17 -3
- package/src/core/adapter.ts +61 -0
- package/src/core/config-input.ts +60 -0
- package/src/core/data.ts +2 -0
- package/src/core/navigation.ts +22 -3
- package/src/core/schema.ts +88 -0
- package/src/core/types.ts +5 -0
- package/src/deploy/artifacts.ts +12 -1
- package/src/deploy/headers.ts +6 -0
- package/src/deploy/vercel-negotiation.ts +25 -2
- package/src/registry/eject.ts +2 -0
- package/src/search/build.ts +25 -3
- package/src/theme/entry.ts +23 -0
- package/dist/cli/chunk-2aj8ddew.js +0 -72
- package/dist/cli/chunk-2aj8ddew.js.map +0 -10
- package/dist/cli/chunk-4ae4f395.js.map +0 -15
- package/dist/cli/chunk-4xyggvgf.js +0 -21
- package/dist/cli/chunk-4xyggvgf.js.map +0 -10
- package/dist/cli/chunk-6kzzpsx8.js +0 -26
- package/dist/cli/chunk-6kzzpsx8.js.map +0 -10
- package/dist/cli/chunk-bcy492zc.js +0 -16
- package/dist/cli/chunk-bcy492zc.js.map +0 -10
- package/dist/cli/chunk-btfr9yvw.js +0 -41
- package/dist/cli/chunk-btfr9yvw.js.map +0 -10
- package/dist/cli/chunk-ey89bjj1.js +0 -209
- package/dist/cli/chunk-ey89bjj1.js.map +0 -11
- package/dist/cli/chunk-q56730e0.js.map +0 -13
- package/dist/cli/chunk-qvvpnwaz.js +0 -69
- package/dist/cli/chunk-qvvpnwaz.js.map +0 -11
- package/dist/cli/chunk-vt8fgygt.js +0 -23
- package/dist/cli/chunk-vt8fgygt.js.map +0 -10
- package/dist/cli/chunk-vxv4x1n8.js +0 -17
- package/dist/cli/chunk-vxv4x1n8.js.map +0 -10
- package/dist/cli/chunk-vyqj481z.js.map +0 -13
- package/dist/cli/chunk-x1wvw7a8.js.map +0 -40
- /package/dist/cli/{chunk-8gnpdsn1.js.map → chunk-0xjyb285.js.map} +0 -0
- /package/dist/cli/{chunk-27gtm2ym.js.map → chunk-2mzebbbz.js.map} +0 -0
- /package/dist/cli/{chunk-s5e5jt53.js.map → chunk-2z47ypj8.js.map} +0 -0
- /package/dist/cli/{chunk-x66c5yjn.js.map → chunk-dwgcp5sm.js.map} +0 -0
- /package/dist/cli/{chunk-ywn7t0pb.js.map → chunk-garjf5z9.js.map} +0 -0
- /package/dist/cli/{chunk-ev67ycx0.js.map → chunk-jq5n4avg.js.map} +0 -0
- /package/dist/cli/{chunk-3r94j3tc.js.map → chunk-nn13znc2.js.map} +0 -0
- /package/dist/cli/{chunk-wd27zjcz.js.map → chunk-q4rae3bg.js.map} +0 -0
- /package/dist/cli/{chunk-8p3xe5jv.js.map → chunk-vacwm2hv.js.map} +0 -0
- /package/dist/cli/{chunk-cbjnx4s8.js.map → chunk-vh9w1sgp.js.map} +0 -0
- /package/dist/cli/{chunk-sbdqrjbb.js.map → chunk-vrfp10qk.js.map} +0 -0
- /package/dist/cli/{chunk-5hs6gb7n.js.map → chunk-yzhm0j9q.js.map} +0 -0
package/src/core/schema.ts
CHANGED
|
@@ -767,6 +767,19 @@ const searchConfigSchema = z
|
|
|
767
767
|
}
|
|
768
768
|
});
|
|
769
769
|
|
|
770
|
+
/**
|
|
771
|
+
* The `ai.ask.reasoning` levels: the AI SDK's top-level `reasoning` values
|
|
772
|
+
* minus `provider-default`, which is what omitting the field means.
|
|
773
|
+
*/
|
|
774
|
+
export const askReasoningLevels = [
|
|
775
|
+
"none",
|
|
776
|
+
"minimal",
|
|
777
|
+
"low",
|
|
778
|
+
"medium",
|
|
779
|
+
"high",
|
|
780
|
+
"xhigh",
|
|
781
|
+
] as const;
|
|
782
|
+
|
|
770
783
|
/** Ask AI backends. `gateway` (default) routes through the Vercel AI Gateway. */
|
|
771
784
|
export const askAiProviders = [
|
|
772
785
|
"gateway",
|
|
@@ -856,6 +869,23 @@ const llmsTxtObjectSchema = z.strictObject({
|
|
|
856
869
|
|
|
857
870
|
type LlmsTxtResolved = z.output<typeof llmsTxtObjectSchema>;
|
|
858
871
|
|
|
872
|
+
/** The object form of `ai.catalog`; a bare boolean normalizes onto it. */
|
|
873
|
+
const aiCatalogObjectSchema = z.strictObject({
|
|
874
|
+
enabled: z.boolean().default(true),
|
|
875
|
+
/**
|
|
876
|
+
* Representative queries per catalog entry, keyed by the entry's
|
|
877
|
+
* `<namespace>:<name>` (the identifier minus its `urn:air:<host>:` prefix,
|
|
878
|
+
* e.g. `mcp:docs`, `skill:blume`, `api:docs`). Replaces the generated
|
|
879
|
+
* defaults for that entry; 2–5 short natural-language questions the
|
|
880
|
+
* resource can answer, which registries embed for semantic search.
|
|
881
|
+
*/
|
|
882
|
+
queries: z
|
|
883
|
+
.record(z.string().min(1), z.array(z.string().trim().min(1)).min(1))
|
|
884
|
+
.default({}),
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
type AiCatalogResolved = z.output<typeof aiCatalogObjectSchema>;
|
|
888
|
+
|
|
859
889
|
const aiConfigSchema = z.strictObject({
|
|
860
890
|
/**
|
|
861
891
|
* The JSON docs API: the page index, per-page JSON, and navigation under
|
|
@@ -872,6 +902,21 @@ const aiConfigSchema = z.strictObject({
|
|
|
872
902
|
// Base URL of the backend. Required for `openai-compatible` only when no
|
|
873
903
|
// external endpoint is supplied; for named providers it overrides the preset.
|
|
874
904
|
baseUrl: z.url().optional(),
|
|
905
|
+
// Origins allowed to call the generated `/api/ask` from another site (a
|
|
906
|
+
// marketing page that embeds an ask box, say), or `"*"` for every
|
|
907
|
+
// origin. Each URL is reduced to its origin so a trailing slash or path
|
|
908
|
+
// can't defeat the exact match the route performs. Read by the
|
|
909
|
+
// generated route only; an external `endpoint` owns its own CORS.
|
|
910
|
+
cors: z
|
|
911
|
+
.array(
|
|
912
|
+
z.union([
|
|
913
|
+
z.literal("*"),
|
|
914
|
+
z
|
|
915
|
+
.url({ protocol: /^https?$/u })
|
|
916
|
+
.transform((value) => new URL(value).origin),
|
|
917
|
+
])
|
|
918
|
+
)
|
|
919
|
+
.optional(),
|
|
875
920
|
enabled: z.boolean().default(false),
|
|
876
921
|
// Optional external endpoint for projects that keep their docs static
|
|
877
922
|
// and host Ask AI in an existing backend. Absolute URLs and root-relative
|
|
@@ -888,6 +933,12 @@ const aiConfigSchema = z.strictObject({
|
|
|
888
933
|
instructions: z.string().trim().min(1).optional(),
|
|
889
934
|
model: z.string().default("openai/gpt-5.5"),
|
|
890
935
|
provider: z.enum(askAiProviders).default("gateway"),
|
|
936
|
+
// How much the model reasons before answering, sent as the backend's
|
|
937
|
+
// own reasoning-effort control (see `askEndpointTemplate`). Omitted
|
|
938
|
+
// keeps the provider's default; `none` is the fastest and cheapest for
|
|
939
|
+
// grounded docs Q&A, where the excerpts carry the answer. Not for
|
|
940
|
+
// Inkeep, which has no such control (refined below).
|
|
941
|
+
reasoning: z.enum(askReasoningLevels).optional(),
|
|
891
942
|
// How much documentation each question carries. Injected characters are
|
|
892
943
|
// the dominant term in time-to-first-token on a self-hosted backend, so
|
|
893
944
|
// these trade recall for latency. No zod defaults here: only what the
|
|
@@ -926,8 +977,44 @@ const aiConfigSchema = z.strictObject({
|
|
|
926
977
|
path: ["baseUrl"],
|
|
927
978
|
});
|
|
928
979
|
}
|
|
980
|
+
// `cors` configures the generated route, which an external `endpoint`
|
|
981
|
+
// replaces; accepting both would silently do nothing.
|
|
982
|
+
if (value.cors && value.endpoint) {
|
|
983
|
+
ctx.addIssue({
|
|
984
|
+
code: z.ZodIssueCode.custom,
|
|
985
|
+
message:
|
|
986
|
+
"ai.ask.cors only applies to the generated route; with ai.ask.endpoint, CORS is that backend's job.",
|
|
987
|
+
path: ["cors"],
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
// Inkeep runs its own QA pipeline behind an OpenAI-compatible endpoint
|
|
991
|
+
// with no reasoning control; a level would only reach it as an
|
|
992
|
+
// unsupported `reasoning_effort`, so refuse it up front.
|
|
993
|
+
if (value.provider === "inkeep" && value.reasoning) {
|
|
994
|
+
ctx.addIssue({
|
|
995
|
+
code: z.ZodIssueCode.custom,
|
|
996
|
+
message:
|
|
997
|
+
'ai.ask.reasoning is not supported when provider is "inkeep".',
|
|
998
|
+
path: ["reasoning"],
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
929
1001
|
})
|
|
930
1002
|
.optional(),
|
|
1003
|
+
/**
|
|
1004
|
+
* The AI Catalog / ARD manifest at `/.well-known/ai-catalog.json` (mirrored
|
|
1005
|
+
* at `/.well-known/ard.json`): one entry per agent-facing resource the site
|
|
1006
|
+
* publishes — the MCP server card, each agent skill, the JSON docs API's
|
|
1007
|
+
* OpenAPI document, each rendered API reference, and llms.txt — so agent
|
|
1008
|
+
* registries can index the site from its domain alone. Needs a
|
|
1009
|
+
* `deployment.site` (identifiers are domain-anchored URNs). On by default;
|
|
1010
|
+
* the object form overrides the generated representative queries.
|
|
1011
|
+
*/
|
|
1012
|
+
catalog: z
|
|
1013
|
+
.union([z.boolean(), aiCatalogObjectSchema])
|
|
1014
|
+
.default(true)
|
|
1015
|
+
.transform((value): AiCatalogResolved =>
|
|
1016
|
+
isBoolean(value) ? { enabled: value, queries: {} } : value
|
|
1017
|
+
),
|
|
931
1018
|
/**
|
|
932
1019
|
* `llms.txt`/`llms-full.txt` emission. A bare boolean toggles it; the object
|
|
933
1020
|
* form adds `openapi: false` to keep generated API reference pages out of
|
|
@@ -1078,6 +1165,7 @@ const navigationConfigSchema = z.strictObject({
|
|
|
1078
1165
|
});
|
|
1079
1166
|
|
|
1080
1167
|
export type AskAiProvider = (typeof askAiProviders)[number];
|
|
1168
|
+
export type AskReasoning = (typeof askReasoningLevels)[number];
|
|
1081
1169
|
export type AskAiConfig = NonNullable<z.infer<typeof aiConfigSchema>["ask"]>;
|
|
1082
1170
|
export { openInChatProviders } from "./open-in-chat.ts";
|
|
1083
1171
|
export type { OpenInChatProvider } from "./open-in-chat.ts";
|
package/src/core/types.ts
CHANGED
|
@@ -220,6 +220,11 @@ export type NavNode =
|
|
|
220
220
|
*/
|
|
221
221
|
display: SidebarDisplay;
|
|
222
222
|
icon?: string;
|
|
223
|
+
/**
|
|
224
|
+
* The group row's link: an explicit-config group's `root`, or the
|
|
225
|
+
* generated folder's index page route. Absent when there is no page at
|
|
226
|
+
* the group's own path, so the row never links to a 404.
|
|
227
|
+
*/
|
|
223
228
|
route?: string;
|
|
224
229
|
/**
|
|
225
230
|
* The group's URL path (its folder route prefix), even when the folder
|
package/src/deploy/artifacts.ts
CHANGED
|
@@ -4,6 +4,11 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { dirname, join, resolve } from "pathe";
|
|
5
5
|
|
|
6
6
|
import { buildAgentReadability } from "../ai/agent-readability.ts";
|
|
7
|
+
import {
|
|
8
|
+
AI_CATALOG_PATH,
|
|
9
|
+
ARD_MANIFEST_PATH,
|
|
10
|
+
buildAiCatalog,
|
|
11
|
+
} from "../ai/ai-catalog.ts";
|
|
7
12
|
import { API_CATALOG_PATH, buildApiCatalog } from "../ai/api-catalog.ts";
|
|
8
13
|
import { buildHomeLinkHeader } from "../ai/link-headers.ts";
|
|
9
14
|
import { buildLlmsFiles } from "../ai/llms.ts";
|
|
@@ -234,8 +239,12 @@ const emitAgentSkills = async (
|
|
|
234
239
|
const emitWellKnownFiles = async (
|
|
235
240
|
config: ResolvedConfig,
|
|
236
241
|
distDir: string,
|
|
242
|
+
skills: readonly SkillArtifact[],
|
|
237
243
|
logger: ArtifactLogger
|
|
238
244
|
): Promise<void> => {
|
|
245
|
+
// One document, two paths: the ai-catalog spec's well-known URI and the
|
|
246
|
+
// ARD v0.91 one (see `ai/ai-catalog.ts`).
|
|
247
|
+
const aiCatalog = buildAiCatalog(config, skills);
|
|
239
248
|
const files = [
|
|
240
249
|
{
|
|
241
250
|
content: buildSignaturesDirectory(config),
|
|
@@ -247,6 +256,8 @@ const emitWellKnownFiles = async (
|
|
|
247
256
|
label: "RFC 9727",
|
|
248
257
|
path: API_CATALOG_PATH,
|
|
249
258
|
},
|
|
259
|
+
{ content: aiCatalog, label: "AI Catalog", path: AI_CATALOG_PATH },
|
|
260
|
+
{ content: aiCatalog, label: "ARD manifest", path: ARD_MANIFEST_PATH },
|
|
250
261
|
];
|
|
251
262
|
for (const file of files) {
|
|
252
263
|
const target = join(distDir, file.path.slice(1));
|
|
@@ -362,7 +373,7 @@ export const publishBuildArtifacts = async (
|
|
|
362
373
|
logger.info("Generated agent-readability.json");
|
|
363
374
|
}
|
|
364
375
|
|
|
365
|
-
await emitWellKnownFiles(project.config, distDir, logger);
|
|
376
|
+
await emitWellKnownFiles(project.config, distDir, skills, logger);
|
|
366
377
|
await emitAgentSkills(project, distDir, skills, logger);
|
|
367
378
|
|
|
368
379
|
await emitRedirectFiles(project.config, distDir, logger);
|
package/src/deploy/headers.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { crossOriginDiscoveryPaths } from "../ai/ai-catalog.ts";
|
|
1
2
|
import {
|
|
2
3
|
API_CATALOG_PATH,
|
|
3
4
|
API_CATALOG_TYPE,
|
|
@@ -95,6 +96,11 @@ export const buildNetlifyHeaders = (
|
|
|
95
96
|
`${deployBase}${SIGNATURES_DIRECTORY_PATH}\n Content-Type: ${SIGNATURES_DIRECTORY_TYPE}`
|
|
96
97
|
);
|
|
97
98
|
}
|
|
99
|
+
// Agent registries fetch the discovery documents cross-origin; a static
|
|
100
|
+
// host sends no CORS header unless told to.
|
|
101
|
+
for (const path of crossOriginDiscoveryPaths(config)) {
|
|
102
|
+
rules.push(`${deployBase}${path}\n Access-Control-Allow-Origin: *`);
|
|
103
|
+
}
|
|
98
104
|
// Published skills live at the deployment base, outside `basePath` — the
|
|
99
105
|
// `.md` charset rule above misses them whenever a basePath is set, and the
|
|
100
106
|
// RFC wants archives served as application/gzip explicitly.
|
|
@@ -235,6 +235,26 @@ export const buildNegotiationRoutes = (
|
|
|
235
235
|
/** The `src` of the injected homepage `Link` header route. */
|
|
236
236
|
const HOME_SRC = "^/$";
|
|
237
237
|
|
|
238
|
+
const ALLOW_ANY_ORIGIN = "*";
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A main-phase `continue` route stamping `Access-Control-Allow-Origin: *` on
|
|
242
|
+
* one static discovery document (see `crossOriginDiscoveryPaths`), so a
|
|
243
|
+
* registry reading it from another origin isn't refused by the browser.
|
|
244
|
+
*/
|
|
245
|
+
const corsRoute = (path: string): VercelRoute => ({
|
|
246
|
+
continue: true,
|
|
247
|
+
headers: { "access-control-allow-origin": ALLOW_ANY_ORIGIN },
|
|
248
|
+
src: `^${routePattern(path)}$`,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
/** Whether a route is a `corsRoute` — the same three-field shape test as the others. */
|
|
252
|
+
const isCorsRoute = (route: VercelRoute): boolean =>
|
|
253
|
+
route.continue === true &&
|
|
254
|
+
route.headers?.["access-control-allow-origin"] === ALLOW_ANY_ORIGIN &&
|
|
255
|
+
isString(route.src) &&
|
|
256
|
+
Object.keys(route).length === 3;
|
|
257
|
+
|
|
238
258
|
/**
|
|
239
259
|
* Whether a route is one this module previously injected, so re-injection
|
|
240
260
|
* replaces rather than duplicates. Rewrites are identified by their `accept`
|
|
@@ -258,7 +278,8 @@ const isNegotiationRoute = (route: VercelRoute): boolean =>
|
|
|
258
278
|
(route.continue === true &&
|
|
259
279
|
isString(route.headers?.link) &&
|
|
260
280
|
route.src === HOME_SRC &&
|
|
261
|
-
Object.keys(route).length === 3)
|
|
281
|
+
Object.keys(route).length === 3) ||
|
|
282
|
+
isCorsRoute(route);
|
|
262
283
|
|
|
263
284
|
/**
|
|
264
285
|
* Splice the negotiation routes into a Build Output `config.json`, plus — when
|
|
@@ -287,7 +308,8 @@ export const injectNegotiationRoutes = (
|
|
|
287
308
|
homeLinkHeader?: string | null,
|
|
288
309
|
contentTypeOverrides?: Record<string, string>,
|
|
289
310
|
homeTokens?: number,
|
|
290
|
-
notFound: NotFoundVariants = {}
|
|
311
|
+
notFound: NotFoundVariants = {},
|
|
312
|
+
corsPaths: readonly string[] = []
|
|
291
313
|
): string | null => {
|
|
292
314
|
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
293
315
|
let config: {
|
|
@@ -325,6 +347,7 @@ export const injectNegotiationRoutes = (
|
|
|
325
347
|
src: HOME_SRC,
|
|
326
348
|
});
|
|
327
349
|
}
|
|
350
|
+
headerRoutes.push(...corsPaths.map(corsRoute));
|
|
328
351
|
// Headers first: `continue` routes accumulate, so a request the rewrite
|
|
329
352
|
// route then terminates (Markdown negotiation on the homepage) still carries
|
|
330
353
|
// the Link header.
|
package/src/registry/eject.ts
CHANGED
|
@@ -137,7 +137,9 @@ const askFiles = async (
|
|
|
137
137
|
const files = [
|
|
138
138
|
{
|
|
139
139
|
content: askEndpointTemplate(resolveAskBackend(ask), grounded, {
|
|
140
|
+
cors: ask.cors,
|
|
140
141
|
instructions: ask.instructions,
|
|
142
|
+
reasoning: ask.reasoning,
|
|
141
143
|
retrieval: ask.retrieval,
|
|
142
144
|
}),
|
|
143
145
|
path: join(srcDir, "pages", "api", "ask.ts"),
|
package/src/search/build.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import { dirname, join } from "pathe";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Build a local Pagefind search index over the built site. Pagefind indexes
|
|
@@ -6,6 +8,15 @@ import { join } from "pathe";
|
|
|
6
8
|
* `data-pagefind-ignore`, which Blume stamps on non-indexable pages
|
|
7
9
|
* (search-excluded, or hidden without the opt-in), so those stay out.
|
|
8
10
|
*
|
|
11
|
+
* The index files are fetched with `getFiles()` and written here rather than
|
|
12
|
+
* with Pagefind's `writeFiles()`: in service mode Pagefind acknowledges
|
|
13
|
+
* `writeFiles()` once tokio has buffered the writes, not once they are on
|
|
14
|
+
* disk, and `close()` kills the backend, so a loaded CI host can ship a
|
|
15
|
+
* truncated `pagefind-entry.json` (Pagefind/pagefind#1271). Writing the bytes
|
|
16
|
+
* ourselves puts them on disk before the backend goes away. Fixed upstream in
|
|
17
|
+
* Pagefind/pagefind#1272; switch back to `writeFiles()` once a release ships
|
|
18
|
+
* that fix.
|
|
19
|
+
*
|
|
9
20
|
* Returns the number of pages indexed.
|
|
10
21
|
*/
|
|
11
22
|
export const buildSearchIndex = async (outDir: string): Promise<number> => {
|
|
@@ -17,11 +28,22 @@ export const buildSearchIndex = async (outDir: string): Promise<number> => {
|
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
// These awaits are strictly ordered, not independent: the directory must be
|
|
20
|
-
// indexed before its files are
|
|
31
|
+
// indexed before its files are read, and the index closed only after.
|
|
21
32
|
// oxlint-disable-next-line react-doctor/async-parallel
|
|
22
33
|
const result = await index.addDirectory({ path: outDir });
|
|
23
|
-
|
|
34
|
+
const { files } = await index.getFiles();
|
|
24
35
|
await pagefind.close();
|
|
25
36
|
|
|
37
|
+
const searchDir = join(outDir, "pagefind");
|
|
38
|
+
const directories = new Set(
|
|
39
|
+
files.map((file) => dirname(join(searchDir, file.path)))
|
|
40
|
+
);
|
|
41
|
+
await Promise.all(
|
|
42
|
+
[...directories].map((directory) => mkdir(directory, { recursive: true }))
|
|
43
|
+
);
|
|
44
|
+
await Promise.all(
|
|
45
|
+
files.map((file) => writeFile(join(searchDir, file.path), file.content))
|
|
46
|
+
);
|
|
47
|
+
|
|
26
48
|
return result.page_count;
|
|
27
49
|
};
|
package/src/theme/entry.ts
CHANGED
|
@@ -298,6 +298,11 @@ ${THEME_MAPPING}
|
|
|
298
298
|
--tw-prose-pre-bg: var(--blume-code-background);
|
|
299
299
|
--tw-prose-th-borders: var(--blume-border);
|
|
300
300
|
--tw-prose-td-borders: var(--blume-border);
|
|
301
|
+
/* Neutralized: the plugin defaults are near-black text and shadow with no
|
|
302
|
+
dark counterpart (Blume never applies prose-invert), and the kbd rule
|
|
303
|
+
below draws the badge with Blume tokens instead. */
|
|
304
|
+
--tw-prose-kbd: inherit;
|
|
305
|
+
--tw-prose-kbd-shadows: transparent;
|
|
301
306
|
color: var(--blume-muted-foreground);
|
|
302
307
|
font-size: 0.875rem;
|
|
303
308
|
line-height: 1.7;
|
|
@@ -827,6 +832,24 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
827
832
|
content: none;
|
|
828
833
|
}
|
|
829
834
|
|
|
835
|
+
/* Keyboard keys (<kbd>) render as the same bordered badge the search dialog
|
|
836
|
+
uses for its shortcut hints. Typography's own kbd rule is scoped with
|
|
837
|
+
:not(.not-prose *), so it never reaches a key inside <Steps>, <Callout>,
|
|
838
|
+
<Card>, or any other not-prose component; this rule is deliberately
|
|
839
|
+
unscoped from that exclusion so a key looks the same everywhere in the
|
|
840
|
+
content column. Unlayered, so it outranks the plugin's layered rule. */
|
|
841
|
+
.prose kbd {
|
|
842
|
+
background: var(--blume-muted);
|
|
843
|
+
border: 1px solid var(--blume-border);
|
|
844
|
+
border-radius: 0.25rem;
|
|
845
|
+
box-shadow: none;
|
|
846
|
+
color: inherit;
|
|
847
|
+
font-family: var(--font-mono);
|
|
848
|
+
font-size: 0.85em;
|
|
849
|
+
font-weight: 500;
|
|
850
|
+
padding: 0.125rem 0.25rem;
|
|
851
|
+
}
|
|
852
|
+
|
|
830
853
|
/* Inline code highlighting: Shiki colors the tokens of a \`code\`{:lang} snippet
|
|
831
854
|
via the same dual-theme CSS variables as fenced blocks, keeping the inline
|
|
832
855
|
pill background. Always on — it only fires on the trailing {:lang} marker. */
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
|
-
|
|
5
|
-
// src/cli/command-meta.ts
|
|
6
|
-
var commandMeta = {
|
|
7
|
-
add: {
|
|
8
|
-
description: "Install a source component or template from the registry.",
|
|
9
|
-
name: "add"
|
|
10
|
-
},
|
|
11
|
-
audit: {
|
|
12
|
-
description: "Audit the built site for SEO and site-health issues.",
|
|
13
|
-
name: "audit"
|
|
14
|
-
},
|
|
15
|
-
build: {
|
|
16
|
-
description: "Build the docs site for production.",
|
|
17
|
-
name: "build"
|
|
18
|
-
},
|
|
19
|
-
check: {
|
|
20
|
-
description: "Type-check the docs site with astro check.",
|
|
21
|
-
name: "check"
|
|
22
|
-
},
|
|
23
|
-
dev: {
|
|
24
|
-
description: "Start the Blume development server.",
|
|
25
|
-
name: "dev"
|
|
26
|
-
},
|
|
27
|
-
doctor: {
|
|
28
|
-
description: "Diagnose common configuration and content problems.",
|
|
29
|
-
name: "doctor"
|
|
30
|
-
},
|
|
31
|
-
eject: {
|
|
32
|
-
description: "Promote the generated runtime into an owned Astro project.",
|
|
33
|
-
name: "eject"
|
|
34
|
-
},
|
|
35
|
-
eval: {
|
|
36
|
-
description: "Test the docs: an agent answers your questions using only the documentation.",
|
|
37
|
-
name: "eval"
|
|
38
|
-
},
|
|
39
|
-
init: {
|
|
40
|
-
description: "Scaffold a minimal Blume project.",
|
|
41
|
-
name: "init"
|
|
42
|
-
},
|
|
43
|
-
"mcp-stdio": {
|
|
44
|
-
description: "Serve an MCP data snapshot over stdio (internal, used by `blume eval`).",
|
|
45
|
-
name: "mcp-stdio"
|
|
46
|
-
},
|
|
47
|
-
preview: {
|
|
48
|
-
description: "Preview the last production build.",
|
|
49
|
-
name: "preview"
|
|
50
|
-
},
|
|
51
|
-
sync: {
|
|
52
|
-
description: "Re-fetch remote content sources and regenerate the runtime.",
|
|
53
|
-
name: "sync"
|
|
54
|
-
},
|
|
55
|
-
translate: {
|
|
56
|
-
description: "Translate docs into the configured locales with a local agent CLI.",
|
|
57
|
-
name: "translate"
|
|
58
|
-
},
|
|
59
|
-
validate: {
|
|
60
|
-
description: "Validate internal, anchor, asset, and external links.",
|
|
61
|
-
name: "validate"
|
|
62
|
-
},
|
|
63
|
-
version: {
|
|
64
|
-
description: "Freeze the current docs as an archived version.",
|
|
65
|
-
name: "version"
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export { __require, commandMeta };
|
|
70
|
-
|
|
71
|
-
//# debugId=3DF60EB79641093164756E2164756E21
|
|
72
|
-
//# sourceMappingURL=chunk-2aj8ddew.js.map
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/cli/command-meta.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import type { CommandMeta } from \"citty\";\n\n/**\n * Every command's `meta`, held apart from the command modules themselves.\n *\n * The CLI entry loads each command lazily (see `lazy-command.ts`), but citty\n * still reads every subcommand's `meta` to render `blume --help` and to match\n * an unknown name against aliases. Keeping that table here lets those paths\n * run without importing a single command module — `dev` alone drags in Astro,\n * `mcp-stdio` the MCP SDK. The command modules read their `meta` from this\n * table too, so the entry and the command can't drift.\n */\nexport const commandMeta = {\n add: {\n description: \"Install a source component or template from the registry.\",\n name: \"add\",\n },\n audit: {\n description: \"Audit the built site for SEO and site-health issues.\",\n name: \"audit\",\n },\n build: {\n description: \"Build the docs site for production.\",\n name: \"build\",\n },\n check: {\n description: \"Type-check the docs site with astro check.\",\n name: \"check\",\n },\n dev: {\n description: \"Start the Blume development server.\",\n name: \"dev\",\n },\n doctor: {\n description: \"Diagnose common configuration and content problems.\",\n name: \"doctor\",\n },\n eject: {\n description: \"Promote the generated runtime into an owned Astro project.\",\n name: \"eject\",\n },\n eval: {\n description:\n \"Test the docs: an agent answers your questions using only the documentation.\",\n name: \"eval\",\n },\n init: {\n description: \"Scaffold a minimal Blume project.\",\n name: \"init\",\n },\n \"mcp-stdio\": {\n description:\n \"Serve an MCP data snapshot over stdio (internal, used by `blume eval`).\",\n name: \"mcp-stdio\",\n },\n preview: {\n description: \"Preview the last production build.\",\n name: \"preview\",\n },\n sync: {\n description: \"Re-fetch remote content sources and regenerate the runtime.\",\n name: \"sync\",\n },\n translate: {\n description:\n \"Translate docs into the configured locales with a local agent CLI.\",\n name: \"translate\",\n },\n validate: {\n description: \"Validate internal, anchor, asset, and external links.\",\n name: \"validate\",\n },\n version: {\n description: \"Freeze the current docs as an archived version.\",\n name: \"version\",\n },\n} satisfies Record<string, CommandMeta>;\n"
|
|
6
|
-
],
|
|
7
|
-
"mappings": ";;;;;AAYO,IAAM,cAAc;AAAA,EACzB,KAAK;AAAA,IACH,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,OAAO;AAAA,IACL,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,OAAO;AAAA,IACL,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,OAAO;AAAA,IACL,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,KAAK;AAAA,IACH,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,QAAQ;AAAA,IACN,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,OAAO;AAAA,IACL,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,MAAM;AAAA,IACJ,aACE;AAAA,IACF,MAAM;AAAA,EACR;AAAA,EACA,MAAM;AAAA,IACJ,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,aAAa;AAAA,IACX,aACE;AAAA,IACF,MAAM;AAAA,EACR;AAAA,EACA,SAAS;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,MAAM;AAAA,IACJ,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,WAAW;AAAA,IACT,aACE;AAAA,IACF,MAAM;AAAA,EACR;AAAA,EACA,UAAU;AAAA,IACR,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AAAA,EACA,SAAS;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AACF;",
|
|
8
|
-
"debugId": "3DF60EB79641093164756E2164756E21",
|
|
9
|
-
"names": []
|
|
10
|
-
}
|