blume 1.7.2 → 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 +10 -0
- package/dist/cli/chunk-0qymqwzz.js +164 -0
- package/dist/cli/chunk-0qymqwzz.js.map +15 -0
- package/dist/cli/{chunk-9bkjd11x.js → chunk-0xjyb285.js} +1 -1
- package/dist/cli/{chunk-hdm2dkd2.js → chunk-1jefwnfs.js} +13 -7
- package/dist/cli/{chunk-hdm2dkd2.js.map → chunk-1jefwnfs.js.map} +3 -3
- package/dist/cli/{chunk-n9sra6sy.js → chunk-3r45185y.js} +5 -7
- package/dist/cli/{chunk-n9sra6sy.js.map → chunk-3r45185y.js.map} +2 -2
- package/dist/cli/{chunk-cvky9gb2.js → chunk-4x36ddpw.js} +3 -3
- package/dist/cli/{chunk-mqb2ka8m.js → chunk-5093q3n7.js} +12 -12
- package/dist/cli/{chunk-9he6crym.js → chunk-5g0w1e2c.js} +4 -4
- package/dist/cli/{chunk-mt76t7dj.js → chunk-5qk08vmp.js} +11 -11
- package/dist/cli/{chunk-61j18dwk.js → chunk-7s8hm3b6.js} +7 -2
- package/dist/cli/{chunk-61j18dwk.js.map → chunk-7s8hm3b6.js.map} +3 -3
- package/dist/cli/{chunk-196vjxp9.js → chunk-8cjtbafj.js} +11 -9
- package/dist/cli/{chunk-196vjxp9.js.map → chunk-8cjtbafj.js.map} +2 -2
- package/dist/cli/{chunk-aztttvb3.js → chunk-97r59kpr.js} +4 -4
- package/dist/cli/{chunk-t3tj0dgr.js → chunk-ahnw3kxw.js} +8 -8
- package/dist/cli/{chunk-hs3gbh8p.js → chunk-b27xqwn9.js} +3 -3
- package/dist/cli/{chunk-450a7rcr.js → chunk-bf6bt1xt.js} +2 -2
- package/dist/cli/{chunk-eevwt1sc.js → chunk-bvwwhd84.js} +13 -13
- package/dist/cli/{chunk-12dxjqk7.js → chunk-cjtn640a.js} +17 -17
- package/dist/cli/{chunk-12dxjqk7.js.map → chunk-cjtn640a.js.map} +1 -1
- package/dist/cli/{chunk-ra1v2nc2.js → chunk-ct47dqpx.js} +14 -3
- package/dist/cli/{chunk-ra1v2nc2.js.map → chunk-ct47dqpx.js.map} +4 -3
- package/dist/cli/{chunk-ppfvdcd4.js → chunk-dwgcp5sm.js} +1 -1
- package/dist/cli/{chunk-3w7b2vcx.js → chunk-e7f42gdj.js} +2 -2
- package/dist/cli/{chunk-vkrsvbr5.js → chunk-esphfr8p.js} +8 -8
- package/dist/cli/{chunk-vkrsvbr5.js.map → chunk-esphfr8p.js.map} +1 -1
- package/dist/cli/{chunk-fmceyezb.js → chunk-ex56aa81.js} +27 -18
- package/dist/cli/chunk-ex56aa81.js.map +13 -0
- package/dist/cli/{chunk-jbj4qhfw.js → chunk-garjf5z9.js} +2 -2
- package/dist/cli/{chunk-wjt80jps.js → chunk-js7saxwm.js} +14 -18
- package/dist/cli/{chunk-wjt80jps.js.map → chunk-js7saxwm.js.map} +4 -6
- package/dist/cli/{chunk-5n7t497w.js → chunk-k79xp7av.js} +53 -91
- package/dist/cli/chunk-k79xp7av.js.map +39 -0
- package/dist/cli/{chunk-688e0dde.js → chunk-nn13znc2.js} +1 -1
- package/dist/cli/{chunk-ejjx8znq.js → chunk-ps4m1xh4.js} +15 -9
- package/dist/cli/{chunk-ejjx8znq.js.map → chunk-ps4m1xh4.js.map} +3 -3
- package/dist/cli/{chunk-xhtpx3ff.js → chunk-rqy0s5wh.js} +13 -13
- package/dist/cli/{chunk-30e87n55.js → chunk-rz9jmfhz.js} +4 -4
- package/dist/cli/{chunk-exeeb35e.js → chunk-vacwm2hv.js} +2 -2
- package/dist/cli/{chunk-8cd8tj54.js → chunk-yg63d42r.js} +7 -7
- package/dist/cli/index.js +15 -15
- package/dist/types/core/config-input.d.ts +33 -0
- package/dist/types/core/data.d.ts +2 -0
- package/dist/types/core/schema.d.ts +20 -0
- package/dist/types/core/types.d.ts +5 -0
- package/docs/configuration/ask-ai.mdx +14 -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/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 +2 -0
- package/src/cli/commands/build.ts +3 -1
- package/src/components/islands/hooks.ts +50 -1
- package/src/components/layout/RootLayout.astro +24 -1
- package/src/components/layout/analytics-client.ts +36 -7
- package/src/core/config-input.ts +34 -0
- package/src/core/data.ts +2 -0
- package/src/core/navigation.ts +22 -3
- package/src/core/schema.ts +32 -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/search/build.ts +25 -3
- package/src/theme/entry.ts +23 -0
- package/dist/cli/chunk-5n7t497w.js.map +0 -40
- package/dist/cli/chunk-88by27n5.js +0 -17
- package/dist/cli/chunk-88by27n5.js.map +0 -10
- package/dist/cli/chunk-fmceyezb.js.map +0 -13
- package/dist/cli/chunk-tqa1s0k8.js +0 -69
- package/dist/cli/chunk-tqa1s0k8.js.map +0 -11
- /package/dist/cli/{chunk-9bkjd11x.js.map → chunk-0xjyb285.js.map} +0 -0
- /package/dist/cli/{chunk-cvky9gb2.js.map → chunk-4x36ddpw.js.map} +0 -0
- /package/dist/cli/{chunk-mqb2ka8m.js.map → chunk-5093q3n7.js.map} +0 -0
- /package/dist/cli/{chunk-9he6crym.js.map → chunk-5g0w1e2c.js.map} +0 -0
- /package/dist/cli/{chunk-mt76t7dj.js.map → chunk-5qk08vmp.js.map} +0 -0
- /package/dist/cli/{chunk-aztttvb3.js.map → chunk-97r59kpr.js.map} +0 -0
- /package/dist/cli/{chunk-t3tj0dgr.js.map → chunk-ahnw3kxw.js.map} +0 -0
- /package/dist/cli/{chunk-hs3gbh8p.js.map → chunk-b27xqwn9.js.map} +0 -0
- /package/dist/cli/{chunk-450a7rcr.js.map → chunk-bf6bt1xt.js.map} +0 -0
- /package/dist/cli/{chunk-eevwt1sc.js.map → chunk-bvwwhd84.js.map} +0 -0
- /package/dist/cli/{chunk-ppfvdcd4.js.map → chunk-dwgcp5sm.js.map} +0 -0
- /package/dist/cli/{chunk-3w7b2vcx.js.map → chunk-e7f42gdj.js.map} +0 -0
- /package/dist/cli/{chunk-jbj4qhfw.js.map → chunk-garjf5z9.js.map} +0 -0
- /package/dist/cli/{chunk-688e0dde.js.map → chunk-nn13znc2.js.map} +0 -0
- /package/dist/cli/{chunk-xhtpx3ff.js.map → chunk-rqy0s5wh.js.map} +0 -0
- /package/dist/cli/{chunk-30e87n55.js.map → chunk-rz9jmfhz.js.map} +0 -0
- /package/dist/cli/{chunk-exeeb35e.js.map → chunk-vacwm2hv.js.map} +0 -0
- /package/dist/cli/{chunk-8cd8tj54.js.map → chunk-yg63d42r.js.map} +0 -0
|
@@ -2,28 +2,28 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ensureGitignore
|
|
4
4
|
} from "./chunk-yzhm0j9q.js";
|
|
5
|
-
import"./chunk-
|
|
5
|
+
import"./chunk-7s8hm3b6.js";
|
|
6
6
|
import {
|
|
7
7
|
commandMeta,
|
|
8
8
|
logger
|
|
9
9
|
} from "./index.js";
|
|
10
10
|
import {
|
|
11
11
|
refuseIfDevRunning
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import"./chunk-
|
|
14
|
-
import"./chunk-
|
|
15
|
-
import"./chunk-
|
|
16
|
-
import"./chunk-
|
|
17
|
-
import"./chunk-
|
|
18
|
-
import"./chunk-
|
|
12
|
+
} from "./chunk-vacwm2hv.js";
|
|
13
|
+
import"./chunk-ct47dqpx.js";
|
|
14
|
+
import"./chunk-0qymqwzz.js";
|
|
15
|
+
import"./chunk-1jefwnfs.js";
|
|
16
|
+
import"./chunk-k79xp7av.js";
|
|
17
|
+
import"./chunk-js7saxwm.js";
|
|
18
|
+
import"./chunk-bf6bt1xt.js";
|
|
19
19
|
import"./chunk-jq5n4avg.js";
|
|
20
|
-
import"./chunk-
|
|
21
|
-
import"./chunk-
|
|
20
|
+
import"./chunk-ps4m1xh4.js";
|
|
21
|
+
import"./chunk-nn13znc2.js";
|
|
22
22
|
import"./chunk-vrfp10qk.js";
|
|
23
23
|
import {
|
|
24
24
|
prepareProject
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import"./chunk-
|
|
25
|
+
} from "./chunk-esphfr8p.js";
|
|
26
|
+
import"./chunk-garjf5z9.js";
|
|
27
27
|
|
|
28
28
|
// src/cli/commands/check.ts
|
|
29
29
|
import { existsSync } from "node:fs";
|
|
@@ -84,4 +84,4 @@ export {
|
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
//# debugId=785FB9FFBD7A7B1164756E2164756E21
|
|
87
|
-
//# sourceMappingURL=chunk-
|
|
87
|
+
//# sourceMappingURL=chunk-rqy0s5wh.js.map
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
reportDiagnosticsJson,
|
|
10
10
|
reportDiagnostics
|
|
11
11
|
} from "./index.js";
|
|
12
|
-
import"./chunk-
|
|
12
|
+
import"./chunk-7s8hm3b6.js";
|
|
13
13
|
import {
|
|
14
14
|
scanProject
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-1jefwnfs.js";
|
|
16
16
|
import {
|
|
17
17
|
serverFeatures
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-garjf5z9.js";
|
|
19
19
|
|
|
20
20
|
// src/cli/commands/doctor.ts
|
|
21
21
|
import { readFileSync } from "node:fs";
|
|
@@ -105,4 +105,4 @@ export {
|
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
//# debugId=8F001C9B0DBCC9F964756E2164756E21
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
108
|
+
//# sourceMappingURL=chunk-rz9jmfhz.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveRuntimeDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7s8hm3b6.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./index.js";
|
|
@@ -133,4 +133,4 @@ var refuseIfDevRunning = (root, action, options = {}) => {
|
|
|
133
133
|
export { readDevLock, DevLockHeldError, acquireDevLock, updateDevLockPort, describeDevLock, refuseIfDevRunning };
|
|
134
134
|
|
|
135
135
|
//# debugId=1007843429DAE30664756E2164756E21
|
|
136
|
-
//# sourceMappingURL=chunk-
|
|
136
|
+
//# sourceMappingURL=chunk-vacwm2hv.js.map
|
|
@@ -17,24 +17,24 @@ import {
|
|
|
17
17
|
writeAgentReport,
|
|
18
18
|
fixPrompt,
|
|
19
19
|
launchAgent
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-0xjyb285.js";
|
|
21
21
|
import {
|
|
22
22
|
deployStaticDir,
|
|
23
23
|
examplesRouteBase
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-ps4m1xh4.js";
|
|
25
25
|
import {
|
|
26
26
|
frontmatter_default,
|
|
27
27
|
scanProject
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-1jefwnfs.js";
|
|
29
29
|
import {
|
|
30
30
|
SITE_INFERRING_ADAPTERS
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-7s8hm3b6.js";
|
|
32
32
|
import {
|
|
33
33
|
gradeExternal,
|
|
34
34
|
probeAll
|
|
35
35
|
} from "./chunk-vh9w1sgp.js";
|
|
36
|
-
import"./chunk-
|
|
37
|
-
import"./chunk-
|
|
36
|
+
import"./chunk-ct47dqpx.js";
|
|
37
|
+
import"./chunk-0qymqwzz.js";
|
|
38
38
|
import {
|
|
39
39
|
normalizeBasePath,
|
|
40
40
|
normalizePath,
|
|
@@ -1957,4 +1957,4 @@ export {
|
|
|
1957
1957
|
};
|
|
1958
1958
|
|
|
1959
1959
|
//# debugId=59B0974018DD3C3464756E2164756E21
|
|
1960
|
-
//# sourceMappingURL=chunk-
|
|
1960
|
+
//# sourceMappingURL=chunk-yg63d42r.js.map
|
package/dist/cli/index.js
CHANGED
|
@@ -400,21 +400,21 @@ var main = defineCommand({
|
|
|
400
400
|
version: getBlumeVersion()
|
|
401
401
|
},
|
|
402
402
|
subCommands: {
|
|
403
|
-
add: lazyCommand(commandMeta.add, () => import("./chunk-
|
|
404
|
-
audit: lazyCommand(commandMeta.audit, () => import("./chunk-
|
|
405
|
-
build: lazyCommand(commandMeta.build, () => import("./chunk-
|
|
406
|
-
check: lazyCommand(commandMeta.check, () => import("./chunk-
|
|
407
|
-
dev: lazyCommand(commandMeta.dev, () => import("./chunk-
|
|
408
|
-
doctor: lazyCommand(commandMeta.doctor, () => import("./chunk-
|
|
409
|
-
eject: lazyCommand(commandMeta.eject, () => import("./chunk-
|
|
410
|
-
eval: lazyCommand(commandMeta.eval, () => import("./chunk-
|
|
411
|
-
init: lazyCommand(commandMeta.init, () => import("./chunk-
|
|
412
|
-
"mcp-stdio": lazyCommand(commandMeta["mcp-stdio"], () => import("./chunk-
|
|
413
|
-
preview: lazyCommand(commandMeta.preview, () => import("./chunk-
|
|
414
|
-
sync: lazyCommand(commandMeta.sync, () => import("./chunk-
|
|
415
|
-
translate: lazyCommand(commandMeta.translate, () => import("./chunk-
|
|
416
|
-
validate: lazyCommand(commandMeta.validate, () => import("./chunk-
|
|
417
|
-
version: lazyCommand(commandMeta.version, () => import("./chunk-
|
|
403
|
+
add: lazyCommand(commandMeta.add, () => import("./chunk-e7f42gdj.js"), "addCommand"),
|
|
404
|
+
audit: lazyCommand(commandMeta.audit, () => import("./chunk-yg63d42r.js"), "auditCommand"),
|
|
405
|
+
build: lazyCommand(commandMeta.build, () => import("./chunk-ex56aa81.js"), "buildCommand"),
|
|
406
|
+
check: lazyCommand(commandMeta.check, () => import("./chunk-rqy0s5wh.js"), "checkCommand"),
|
|
407
|
+
dev: lazyCommand(commandMeta.dev, () => import("./chunk-cjtn640a.js"), "devCommand"),
|
|
408
|
+
doctor: lazyCommand(commandMeta.doctor, () => import("./chunk-rz9jmfhz.js"), "doctorCommand"),
|
|
409
|
+
eject: lazyCommand(commandMeta.eject, () => import("./chunk-5093q3n7.js"), "ejectCommand"),
|
|
410
|
+
eval: lazyCommand(commandMeta.eval, () => import("./chunk-ahnw3kxw.js"), "evalCommand"),
|
|
411
|
+
init: lazyCommand(commandMeta.init, () => import("./chunk-5qk08vmp.js"), "initCommand"),
|
|
412
|
+
"mcp-stdio": lazyCommand(commandMeta["mcp-stdio"], () => import("./chunk-3r45185y.js"), "mcpStdioCommand"),
|
|
413
|
+
preview: lazyCommand(commandMeta.preview, () => import("./chunk-b27xqwn9.js"), "previewCommand"),
|
|
414
|
+
sync: lazyCommand(commandMeta.sync, () => import("./chunk-bvwwhd84.js"), "syncCommand"),
|
|
415
|
+
translate: lazyCommand(commandMeta.translate, () => import("./chunk-5g0w1e2c.js"), "translateCommand"),
|
|
416
|
+
validate: lazyCommand(commandMeta.validate, () => import("./chunk-97r59kpr.js"), "validateCommand"),
|
|
417
|
+
version: lazyCommand(commandMeta.version, () => import("./chunk-4x36ddpw.js"), "versionCommand")
|
|
418
418
|
}
|
|
419
419
|
});
|
|
420
420
|
loadEnvFiles(process.cwd());
|
|
@@ -680,6 +680,30 @@ export interface AskConfig {
|
|
|
680
680
|
/** Starter prompts shown before the first question. */
|
|
681
681
|
suggestions?: AskSuggestion[];
|
|
682
682
|
}
|
|
683
|
+
/** What the AI Catalog (ARD) manifest carries. */
|
|
684
|
+
export interface AiCatalogConfig {
|
|
685
|
+
/** Emit `/.well-known/ai-catalog.json` and `/.well-known/ard.json`. Defaults to `true`. */
|
|
686
|
+
enabled?: boolean;
|
|
687
|
+
/**
|
|
688
|
+
* Representative queries per entry, keyed by the entry's `<namespace>:<name>`
|
|
689
|
+
* — its identifier minus the `urn:air:<host>:` prefix (`mcp:docs`,
|
|
690
|
+
* `skill:blume`, `api:docs`, `reference:<slug>`, `docs:llms-txt`). Each
|
|
691
|
+
* list replaces the generated defaults for that entry: 2–5 short
|
|
692
|
+
* natural-language questions the resource can answer, which agent
|
|
693
|
+
* registries embed for semantic search.
|
|
694
|
+
*
|
|
695
|
+
* ```ts
|
|
696
|
+
* ai: {
|
|
697
|
+
* catalog: {
|
|
698
|
+
* queries: {
|
|
699
|
+
* "mcp:acme": ["how do I install Acme", "search the Acme docs"],
|
|
700
|
+
* },
|
|
701
|
+
* },
|
|
702
|
+
* }
|
|
703
|
+
* ```
|
|
704
|
+
*/
|
|
705
|
+
queries?: Record<string, string[]>;
|
|
706
|
+
}
|
|
683
707
|
/** What the `llms.txt`/`llms-full.txt` files include. */
|
|
684
708
|
export interface LlmsTxtConfig {
|
|
685
709
|
/**
|
|
@@ -734,6 +758,15 @@ export interface AiConfig {
|
|
|
734
758
|
api?: boolean;
|
|
735
759
|
/** The Ask AI chat assistant. */
|
|
736
760
|
ask?: AskConfig;
|
|
761
|
+
/**
|
|
762
|
+
* The AI Catalog / ARD manifest (`/.well-known/ai-catalog.json`, mirrored
|
|
763
|
+
* at `/.well-known/ard.json`): a domain-level index of the agent-facing
|
|
764
|
+
* resources the site publishes — MCP server, agent skills, the JSON docs
|
|
765
|
+
* API, API references, llms.txt — for agent registries. Needs a
|
|
766
|
+
* `deployment.site`. Defaults to `true`; the object form overrides the
|
|
767
|
+
* generated representative queries per entry.
|
|
768
|
+
*/
|
|
769
|
+
catalog?: boolean | AiCatalogConfig;
|
|
737
770
|
/**
|
|
738
771
|
* Emit `llms.txt` (an index of the docs for LLMs). Defaults to `true`.
|
|
739
772
|
* The object form adds knobs for what the files include.
|
|
@@ -143,6 +143,8 @@ export interface BlumeDataConfig {
|
|
|
143
143
|
*/
|
|
144
144
|
discovery: {
|
|
145
145
|
agentReadability: boolean;
|
|
146
|
+
/** Whether the AI Catalog / ARD manifest is published (`ai.catalog`). */
|
|
147
|
+
aiCatalog: boolean;
|
|
146
148
|
/** Whether the JSON docs API and its `/openapi.json` are published. */
|
|
147
149
|
api: boolean;
|
|
148
150
|
llmsTxt: boolean;
|
|
@@ -370,6 +370,16 @@ declare const aiConfigSchema: z.ZodObject<{
|
|
|
370
370
|
label: z.ZodString;
|
|
371
371
|
}, z.core.$strict>>>;
|
|
372
372
|
}, z.core.$strict>>;
|
|
373
|
+
catalog: z.ZodPipe<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
374
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
375
|
+
queries: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
376
|
+
}, z.core.$strict>]>>, z.ZodTransform<{
|
|
377
|
+
enabled: boolean;
|
|
378
|
+
queries: Record<string, string[]>;
|
|
379
|
+
}, boolean | {
|
|
380
|
+
enabled: boolean;
|
|
381
|
+
queries: Record<string, string[]>;
|
|
382
|
+
}>>;
|
|
373
383
|
llmsTxt: z.ZodPipe<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
374
384
|
details: z.ZodOptional<z.ZodString>;
|
|
375
385
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -622,6 +632,16 @@ export declare const blumeConfigSchema: z.ZodObject<{
|
|
|
622
632
|
label: z.ZodString;
|
|
623
633
|
}, z.core.$strict>>>;
|
|
624
634
|
}, z.core.$strict>>;
|
|
635
|
+
catalog: z.ZodPipe<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
636
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
637
|
+
queries: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
638
|
+
}, z.core.$strict>]>>, z.ZodTransform<{
|
|
639
|
+
enabled: boolean;
|
|
640
|
+
queries: Record<string, string[]>;
|
|
641
|
+
}, boolean | {
|
|
642
|
+
enabled: boolean;
|
|
643
|
+
queries: Record<string, string[]>;
|
|
644
|
+
}>>;
|
|
625
645
|
llmsTxt: z.ZodPipe<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
626
646
|
details: z.ZodOptional<z.ZodString>;
|
|
627
647
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -208,6 +208,11 @@ export type NavNode = {
|
|
|
208
208
|
*/
|
|
209
209
|
display: SidebarDisplay;
|
|
210
210
|
icon?: string;
|
|
211
|
+
/**
|
|
212
|
+
* The group row's link: an explicit-config group's `root`, or the
|
|
213
|
+
* generated folder's index page route. Absent when there is no page at
|
|
214
|
+
* the group's own path, so the row never links to a 404.
|
|
215
|
+
*/
|
|
211
216
|
route?: string;
|
|
212
217
|
/**
|
|
213
218
|
* The group's URL path (its folder route prefix), even when the folder
|
|
@@ -235,6 +235,20 @@ ai: {
|
|
|
235
235
|
|
|
236
236
|
The value is sent as the backend's own reasoning-effort control. Through the gateway it travels as the [AI SDK's `reasoning` option](https://ai-sdk.dev/docs/ai-sdk-core/reasoning), which the gateway maps to the model's setting — OpenAI's `reasoning_effort`, for example. On OpenRouter it is sent as `reasoning.effort`, and on LLMGateway or a custom `openai-compatible` endpoint as `reasoning_effort` in the request, so the endpoint has to accept that parameter. The model has to support the level you pick: OpenAI rejects a level a model doesn't offer (`"none"` and `"xhigh"` exist only on some), so check the model's documentation before setting one. Inkeep runs its own QA pipeline and has no reasoning control, so setting `reasoning` with that backend is a config error. Leave it unset to keep the model's default. Like [retrieval size](#retrieval-size), it trades thoroughness for time-to-first-token, and answers stay grounded either way.
|
|
237
237
|
|
|
238
|
+
## Analytics
|
|
239
|
+
|
|
240
|
+
With an [analytics provider](/docs/configuration/analytics) configured, the assistant reports its usage through the same `track()` the page feedback widget uses, so questions land next to your pageviews:
|
|
241
|
+
|
|
242
|
+
| Event | When | Properties |
|
|
243
|
+
| --- | --- | --- |
|
|
244
|
+
| `ask` | A question is sent | `path`, `questionChars` |
|
|
245
|
+
| `ask_answer` | The answer finishes streaming | `path`, `questionChars`, `ms`, `chars` |
|
|
246
|
+
| `ask_error` | The request fails, breaks, or comes back empty | `path`, `questionChars`, `ms`, `status` |
|
|
247
|
+
|
|
248
|
+
`path` is the page the reader asked from (the served pathname, so it matches the feedback widget and your pageviews under a `base`), `questionChars` the question's length, `ms` the time from sending the question to the last chunk, and `chars` the answer's length. `status` is the HTTP status: `0` when no response arrived at all (offline, DNS, CORS), and `200` when the response was fine but its stream broke mid-answer — how a provider or credential error surfaces, since the backend has already sent its headers — or delivered nothing. Clearing the conversation mid-answer reports neither outcome.
|
|
249
|
+
|
|
250
|
+
The question's text never reaches a provider: it is free-form reader input (pasted keys, error logs, names) that would breach most providers' terms and per-value size limits. It travels only on the `blume:track` DOM event, as `question` in `detail.props`, so a listener you write can forward it wherever you decide it belongs. A custom chat UI built on `useAskAI` from `blume/hooks` reports the same events. With no provider configured the built-in provider calls are no-ops, but the `blume:track` event still fires, so a custom integration listening for it receives them.
|
|
251
|
+
|
|
238
252
|
## Rate limiting
|
|
239
253
|
|
|
240
254
|
The `POST /api/ask` endpoint is **unauthenticated** — it has to be, so the in-page assistant can call it. Blume validates each request — rejecting malformed bodies, capping it to 1–40 messages, and accepting only `user`/`assistant` roles so a caller can't inject their own system prompt and repurpose the route as a general LLM proxy — to bound how much a single call can spend against your model, but it can't stop someone from calling the endpoint repeatedly. If cost abuse is a concern, put the route behind a rate limiter — your host's (e.g. Vercel's) edge rate limiting, a middleware, or your model provider's per-key spend limits.
|
|
@@ -65,9 +65,11 @@ export default defineConfig({
|
|
|
65
65
|
},
|
|
66
66
|
},
|
|
67
67
|
|
|
68
|
-
// AI — llms.txt, MCP; see the Discoverability section
|
|
68
|
+
// AI — llms.txt, MCP, the AI catalog; see the Discoverability section
|
|
69
69
|
ai: {
|
|
70
70
|
llmsTxt: true,
|
|
71
|
+
// AI Catalog / ARD manifest at /.well-known/ai-catalog.json (needs deployment.site)
|
|
72
|
+
catalog: true,
|
|
71
73
|
// MCP server (needs server output)
|
|
72
74
|
mcp: {
|
|
73
75
|
enabled: false,
|
|
@@ -12,6 +12,7 @@ By default the sidebar mirrors your content tree:
|
|
|
12
12
|
- folders become **groups**, files become **pages**
|
|
13
13
|
- a page's label is its frontmatter `title`; a group's label is the humanized folder name
|
|
14
14
|
- items sort by [numeric prefix](/docs/content), then alphabetically, and a folder's `index` page comes first
|
|
15
|
+
- a folder with an `index` page links its group row to that page, so clicking the section name opens the section's landing page
|
|
15
16
|
|
|
16
17
|
That's enough for many sites — everything below is opt-in.
|
|
17
18
|
|
|
@@ -133,6 +134,8 @@ sidebar:
|
|
|
133
134
|
hidden: true
|
|
134
135
|
```
|
|
135
136
|
|
|
137
|
+
A folder's `index` page appears both as the group row's link and as the first row inside the group. Hide the index page to keep only the linked header: the group row still opens the landing page, and previous/next links still pass through it.
|
|
138
|
+
|
|
136
139
|
## Tabs
|
|
137
140
|
|
|
138
141
|
Render top-level sections as tabs in the header, useful for splitting a large site into distinct areas — say adapters, an API, and AI guides. A tab is highlighted when the current route falls under its `path`:
|
package/docs/content/syntax.mdx
CHANGED
|
@@ -59,6 +59,16 @@ Inline formatting for stressing words, marking deletions, and showing code or ke
|
|
|
59
59
|
**Bold**, _italic_, ~~strikethrough~~, and `inline code`.
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
## Keyboard keys
|
|
63
|
+
|
|
64
|
+
For shortcuts and keystrokes. A `<kbd>` element renders as the same bordered key badge the search dialog uses, in Markdown, MDX, and inside components like `<Steps>` and `<Callout>`.
|
|
65
|
+
|
|
66
|
+
Press <kbd>⌘</kbd> <kbd>K</kbd> to open search, or <kbd>Esc</kbd> to close it.
|
|
67
|
+
|
|
68
|
+
```md
|
|
69
|
+
Press <kbd>⌘</kbd> <kbd>K</kbd> to open search, or <kbd>Esc</kbd> to close it.
|
|
70
|
+
```
|
|
71
|
+
|
|
62
72
|
## Superscript and subscript
|
|
63
73
|
|
|
64
74
|
For footnote markers, ordinals, and scientific or chemical notation inline.
|
|
@@ -55,13 +55,14 @@ Agents that probe a site don't know to look for the manifest — so Blume also a
|
|
|
55
55
|
|
|
56
56
|
```http
|
|
57
57
|
Link: </.well-known/api-catalog>; rel="api-catalog"; type="application/linkset+json",
|
|
58
|
+
</.well-known/ai-catalog.json>; rel="ai-catalog"; type="application/ai-catalog+json",
|
|
58
59
|
</openapi.json>; rel="service-desc"; type="application/json",
|
|
59
60
|
</agent-readability.json>; rel="describedby"; type="application/json",
|
|
60
61
|
</llms.txt>; rel="describedby"; type="text/plain",
|
|
61
62
|
</index.md>; rel="alternate"; type="text/markdown"
|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
Each entry appears only when its feature is on. The `alternate` link points at the homepage's Markdown mirror — the page's own [raw Markdown](/docs/discoverability/markdown) when the home route is a content page, or the synthesized `llms.txt` fallback when it's a landing page. The `service-desc` link ([RFC 8631](https://www.rfc-editor.org/rfc/rfc8631)) points at the [JSON API](/docs/discoverability/json-api)'s OpenAPI description,
|
|
65
|
+
Each entry appears only when its feature is on. The `alternate` link points at the homepage's Markdown mirror — the page's own [raw Markdown](/docs/discoverability/markdown) when the home route is a content page, or the synthesized `llms.txt` fallback when it's a landing page. The `service-desc` link ([RFC 8631](https://www.rfc-editor.org/rfc/rfc8631)) points at the [JSON API](/docs/discoverability/json-api)'s OpenAPI description, `api-catalog` at the [generated API catalog](#api-catalog), and `ai-catalog` at the [AI catalog](#ai-catalog). The header rides on every surface Blume controls: the dev server (check it with `curl -I localhost:4321`), static builds via the emitted `_headers` file (Netlify and Cloudflare), and Vercel server builds via the deploy's routing rules.
|
|
65
66
|
|
|
66
67
|
Not every agent enters through the root, though — one following a search result or a shared link lands on a deep page and never sees the homepage header. So every rendered page also carries the same discovery links in its HTML `<head>`, using the same IANA-registered relations:
|
|
67
68
|
|
|
@@ -72,6 +73,12 @@ Not every agent enters through the root, though — one following a search resul
|
|
|
72
73
|
type="application/json"
|
|
73
74
|
/>
|
|
74
75
|
<link rel="describedby" href="/llms.txt" type="text/plain" />
|
|
76
|
+
<link
|
|
77
|
+
rel="ai-catalog"
|
|
78
|
+
href="/.well-known/ai-catalog.json"
|
|
79
|
+
type="application/ai-catalog+json"
|
|
80
|
+
/>
|
|
81
|
+
<link rel="ard" href="/.well-known/ard.json" type="application/json" />
|
|
75
82
|
<link rel="alternate" href="/docs/example.md" type="text/markdown" />
|
|
76
83
|
```
|
|
77
84
|
|
|
@@ -121,6 +128,80 @@ When the site publishes APIs, Blume generates an [RFC 9727](https://www.rfc-edit
|
|
|
121
128
|
|
|
122
129
|
A site with no API references, no MCP server, and the [JSON API](/docs/discoverability/json-api) turned off emits no catalog — there'd be nothing in it. As everywhere, a `public/.well-known/api-catalog` file you ship yourself wins over the generated one.
|
|
123
130
|
|
|
131
|
+
## AI catalog
|
|
132
|
+
|
|
133
|
+
The API catalog lists APIs. The **AI catalog** lists everything an agent could pick up from the site — the MCP server, each published skill, the JSON API, each rendered API reference, and `llms.txt` — in the format agent registries index: an [AI Catalog](https://github.com/Agent-Card/ai-catalog) document at `/.well-known/ai-catalog.json`, which is also the manifest [Agentic Resource Discovery (ARD)](https://agenticresourcediscovery.org/) consumers resolve. Each entry carries a domain-anchored `urn:air:<host>:<namespace>:<name>` identifier, a display name, the artifact's media type, its URL, and a handful of `representativeQueries` — sample questions the resource can answer, which registries embed for semantic search:
|
|
134
|
+
|
|
135
|
+
```json .well-known/ai-catalog.json
|
|
136
|
+
{
|
|
137
|
+
"specVersion": "1.0",
|
|
138
|
+
"host": {
|
|
139
|
+
"displayName": "Acme",
|
|
140
|
+
"identifier": "did:web:docs.example.com",
|
|
141
|
+
"documentationUrl": "https://docs.example.com/"
|
|
142
|
+
},
|
|
143
|
+
"entries": [
|
|
144
|
+
{
|
|
145
|
+
"identifier": "urn:air:docs.example.com:mcp:acme",
|
|
146
|
+
"displayName": "Acme",
|
|
147
|
+
"type": "application/mcp-server-card+json",
|
|
148
|
+
"url": "https://docs.example.com/.well-known/mcp/server-card.json",
|
|
149
|
+
"capabilities": [
|
|
150
|
+
"search_docs",
|
|
151
|
+
"get_page",
|
|
152
|
+
"list_pages",
|
|
153
|
+
"get_navigation"
|
|
154
|
+
],
|
|
155
|
+
"representativeQueries": [
|
|
156
|
+
"search the Acme documentation",
|
|
157
|
+
"get a Acme docs page as Markdown",
|
|
158
|
+
"list every page in the Acme docs"
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"identifier": "urn:air:docs.example.com:skill:acme",
|
|
163
|
+
"displayName": "acme",
|
|
164
|
+
"type": "application/agent-skills+md",
|
|
165
|
+
"url": "https://docs.example.com/.well-known/agent-skills/acme/SKILL.md",
|
|
166
|
+
"representativeQueries": [
|
|
167
|
+
"load the acme agent skill",
|
|
168
|
+
"how do I use acme"
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"identifier": "urn:air:docs.example.com:api:docs",
|
|
173
|
+
"displayName": "Acme docs API",
|
|
174
|
+
"type": "application/vnd.oai.openapi+json",
|
|
175
|
+
"url": "https://docs.example.com/openapi.json",
|
|
176
|
+
"representativeQueries": [
|
|
177
|
+
"fetch a Acme docs page as JSON",
|
|
178
|
+
"list the pages in the Acme docs",
|
|
179
|
+
"get the Acme docs navigation tree"
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
ARD's current revision reads the manifest from `/.well-known/ard.json` and calls `ai-catalog.json` the predecessor path, so Blume writes the same document to both, advertises it under both link relations (`ai-catalog` and `ard`) in every page's head, and lists it in `llms.txt` and `agent-readability.json`. The catalog and its `.well-known` neighbors (the API catalog, the MCP discovery files) are served with `Access-Control-Allow-Origin: *` on every build surface, so a registry reading them from another origin isn't blocked.
|
|
187
|
+
|
|
188
|
+
Entry identifiers are anchored on your domain, so the catalog needs a [`deployment.site`](/docs/deployment) — without one nothing is emitted. It's on by default; `ai.catalog: false` turns it off. The generated queries are derived from the site title and each entry's own description. To write your own for an entry, key them by the identifier's tail (`<namespace>:<name>`):
|
|
189
|
+
|
|
190
|
+
```ts blume.config.ts
|
|
191
|
+
export default defineConfig({
|
|
192
|
+
ai: {
|
|
193
|
+
catalog: {
|
|
194
|
+
queries: {
|
|
195
|
+
"mcp:acme": ["how do I install Acme", "search the Acme docs"],
|
|
196
|
+
"skill:acme": ["set up an Acme project", "write an Acme plugin"],
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
A list replaces the generated queries for that entry; entries you don't name keep theirs. As everywhere, a `public/.well-known/ai-catalog.json` (or `ard.json`) you ship yourself wins over the generated one.
|
|
204
|
+
|
|
124
205
|
## WebMCP
|
|
125
206
|
|
|
126
207
|
[WebMCP](https://webmachinelearning.github.io/webmcp/) is an emerging browser API that lets a page register tools directly with an agentic browser — no separate server connection needed. Every Blume page registers the docs' read-only surface on the page's model context: `search_docs` (site search), `get_page` (a page's [raw Markdown](/docs/discoverability/markdown)), and `list_pages` (the [`llms.txt`](/docs/discoverability/llms-txt) index). The script is tiny, loads no search machinery until a tool is actually called, and silently no-ops in every browser without the API — which today is all of them outside [Chrome's early preview](https://developer.chrome.com/blog/webmcp-epp). It registers on whichever surface the in-flux spec exposes (`navigator.modelContext` or `document.modelContext`), via `provideContext` or per-tool `registerTool`.
|
|
@@ -37,7 +37,7 @@ Most of this is sharper with an absolute site URL — set [`deployment.site`](/d
|
|
|
37
37
|
| Raw Markdown mirrors, content negotiation, Copy as Markdown, Open in chat | `/<route>.md` | on | [Markdown for agents](/docs/discoverability/markdown) |
|
|
38
38
|
| JSON API and its OpenAPI description | `/api/docs/…`, `/openapi.json` | on | [JSON API](/docs/discoverability/json-api) |
|
|
39
39
|
| MCP server | `/mcp` | opt-in, server output | [MCP server](/docs/discoverability/mcp) |
|
|
40
|
-
| `agent-readability.json`, `Link` headers, API catalog, WebMCP, skills, Web Bot Auth | site root, `/.well-known/…` | on | [Agent discovery](/docs/discoverability/agent-discovery) |
|
|
40
|
+
| `agent-readability.json`, `Link` headers, API catalog, AI catalog (ARD), WebMCP, skills, Web Bot Auth | site root, `/.well-known/…` | on | [Agent discovery](/docs/discoverability/agent-discovery) |
|
|
41
41
|
|
|
42
42
|
Every generated file yields to one you ship yourself: drop a `robots.txt`, `sitemap.xml`, `llms.txt`, `openapi.json`, or `agent-readability.json` in `public/` and Blume serves yours in its place.
|
|
43
43
|
|
|
@@ -47,7 +47,7 @@ ai: {
|
|
|
47
47
|
|
|
48
48
|
## Generated sections
|
|
49
49
|
|
|
50
|
-
`llms.txt` closes with two generated sections that need no configuration. **Agent skills** lists each skill published through [`ai.skills`](/docs/discoverability/agent-discovery#skills-discovery) with its description (where a skill says when to use it). **Agent resources** links every machine-readable artifact the build emits — `llms-full.txt`, the per-page [raw Markdown](/docs/discoverability/markdown) mirror, the [MCP server](/docs/discoverability/mcp) and its discovery document, the skills index, the [API catalog](/docs/discoverability/agent-discovery#api-catalog), [`agent-readability.json`](/docs/discoverability/agent-discovery#agent-readability), and the [sitemap](/docs/discoverability/sitemap-and-robots#sitemap) — each only when it exists, so an agent that reads nothing but `llms.txt` still finds the whole surface.
|
|
50
|
+
`llms.txt` closes with two generated sections that need no configuration. **Agent skills** lists each skill published through [`ai.skills`](/docs/discoverability/agent-discovery#skills-discovery) with its description (where a skill says when to use it). **Agent resources** links every machine-readable artifact the build emits — `llms-full.txt`, the per-page [raw Markdown](/docs/discoverability/markdown) mirror, the [MCP server](/docs/discoverability/mcp) and its discovery document, the skills index, the [API catalog](/docs/discoverability/agent-discovery#api-catalog), the [AI catalog](/docs/discoverability/agent-discovery#ai-catalog), [`agent-readability.json`](/docs/discoverability/agent-discovery#agent-readability), and the [sitemap](/docs/discoverability/sitemap-and-robots#sitemap) — each only when it exists, so an agent that reads nothing but `llms.txt` still finds the whole surface.
|
|
51
51
|
|
|
52
52
|
## Excluding a page
|
|
53
53
|
|
|
@@ -49,6 +49,8 @@ sidebar:
|
|
|
49
49
|
display: page
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
`hidden` removes the page from the sidebar and from previous/next pagination. On a folder's `index` page it removes only the page's own row: the group row keeps linking to the page, and previous/next links still pass through it.
|
|
53
|
+
|
|
52
54
|
`display` sets the render mode of the page's folder group ([per-group overrides](/docs/content/navigation#per-group-overrides)) and is only meaningful on a folder's `index` page under the generated sidebar — anywhere else (a non-index page, the content root's own `index` page, or any page under an explicit `navigation.sidebar`) it has no group to configure, and Blume warns with `BLUME_SIDEBAR_DISPLAY_IGNORED`.
|
|
53
55
|
|
|
54
56
|
## SEO
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import type { BlumeProject } from "../core/project-graph.ts";
|
|
|
4
4
|
import type { ContentSignalPolicy, ContentSignals } from "../core/schema.ts";
|
|
5
5
|
import { absoluteUrl } from "../core/site-url.ts";
|
|
6
6
|
import { buildRssFeeds } from "../deploy/rss.ts";
|
|
7
|
+
import { AI_CATALOG_PATH, hasAiCatalog } from "./ai-catalog.ts";
|
|
7
8
|
import { hasApiCatalog } from "./api-catalog.ts";
|
|
8
9
|
import { API_PAGES_PATH, API_SEARCH_PATH, OPENAPI_PATH } from "./api/paths.ts";
|
|
9
10
|
|
|
@@ -52,6 +53,7 @@ const askApiUrl = (
|
|
|
52
53
|
/** The `.well-known` discovery URLs a site can publish. */
|
|
53
54
|
interface WellKnownArtifacts {
|
|
54
55
|
httpMessageSignaturesDirectory?: string;
|
|
56
|
+
aiCatalog?: string;
|
|
55
57
|
apiCatalog?: string;
|
|
56
58
|
agentSkills?: string;
|
|
57
59
|
}
|
|
@@ -138,6 +140,9 @@ const wellKnownArtifacts = (
|
|
|
138
140
|
"/.well-known/http-message-signatures-directory"
|
|
139
141
|
);
|
|
140
142
|
}
|
|
143
|
+
if (hasAiCatalog(config)) {
|
|
144
|
+
artifacts.aiCatalog = abs(AI_CATALOG_PATH);
|
|
145
|
+
}
|
|
141
146
|
if (hasApiCatalog(config)) {
|
|
142
147
|
artifacts.apiCatalog = abs("/.well-known/api-catalog");
|
|
143
148
|
}
|