livemark 0.26.0 → 0.27.1

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.
Files changed (49) hide show
  1. package/build/actions/config/define.d.ts +6 -0
  2. package/build/content/article.js +4 -11
  3. package/build/content-collections.js +3 -5
  4. package/build/helpers/agent-skills.d.ts +22 -0
  5. package/build/helpers/agent-skills.js +46 -0
  6. package/build/helpers/agent-skills.unit.d.ts +1 -0
  7. package/build/helpers/agent-skills.unit.js +49 -0
  8. package/build/helpers/article.d.ts +13 -0
  9. package/build/helpers/article.js +17 -1
  10. package/build/helpers/head.d.ts +7 -2
  11. package/build/helpers/head.js +13 -2
  12. package/build/helpers/head.unit.js +4 -2
  13. package/build/helpers/llms.d.ts +33 -0
  14. package/build/helpers/llms.js +72 -0
  15. package/build/helpers/llms.unit.d.ts +1 -0
  16. package/build/helpers/llms.unit.js +98 -0
  17. package/build/helpers/resolve-includes.d.ts +1 -1
  18. package/build/helpers/resolve-includes.js +8 -8
  19. package/build/helpers/resolve-includes.unit.js +23 -23
  20. package/build/models/config.d.ts +20 -0
  21. package/build/models/config.js +8 -1
  22. package/build/plugins/vite-agents.d.ts +17 -0
  23. package/build/plugins/vite-agents.js +110 -0
  24. package/build/plugins/vite-agents.unit.d.ts +1 -0
  25. package/build/plugins/vite-agents.unit.js +20 -0
  26. package/build/plugins/vite-robots.d.ts +4 -2
  27. package/build/plugins/vite-robots.js +15 -2
  28. package/build/plugins/vite-robots.unit.js +5 -1
  29. package/build/vite.config.js +3 -1
  30. package/package.json +4 -2
  31. package/skills/livemark-configuration/SKILL.md +4 -0
  32. package/skills/livemark-customization/SKILL.md +1 -0
  33. package/source/content/article.ts +8 -13
  34. package/source/content-collections.ts +2 -5
  35. package/source/helpers/agent-skills.ts +59 -0
  36. package/source/helpers/agent-skills.unit.ts +56 -0
  37. package/source/helpers/article.ts +17 -0
  38. package/source/helpers/head.ts +12 -1
  39. package/source/helpers/head.unit.ts +3 -1
  40. package/source/helpers/llms.ts +109 -0
  41. package/source/helpers/llms.unit.ts +115 -0
  42. package/source/helpers/resolve-includes.ts +12 -12
  43. package/source/helpers/resolve-includes.unit.ts +46 -22
  44. package/source/models/config.ts +11 -0
  45. package/source/plugins/vite-agents.ts +137 -0
  46. package/source/plugins/vite-agents.unit.ts +26 -0
  47. package/source/plugins/vite-robots.ts +18 -3
  48. package/source/plugins/vite-robots.unit.ts +7 -0
  49. package/source/vite.config.ts +2 -0
@@ -0,0 +1,110 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { existsSync, readFileSync } from "node:fs";
10
+ import { join, resolve } from "node:path";
11
+ import { pathToFileURL } from "node:url";
12
+ import { z } from "zod";
13
+ import { buildSkillFileName, loadAgentSkills, renderSkillsIndex, } from "../helpers/agent-skills.js";
14
+ import { buildTwinFileName, renderLlmsText, renderMarkdownTwin, renderSectionIndex, } from "../helpers/llms.js";
15
+ import { resolveIncludes } from "../helpers/resolve-includes.js";
16
+ const GeneratedArticles = z.array(z.object({
17
+ path: z.string(),
18
+ title: z.string(),
19
+ description: z.string().optional(),
20
+ order: z.number().optional(),
21
+ date: z.string().optional(),
22
+ file: z.string(),
23
+ content: z.string(),
24
+ }));
25
+ /** Emit what AI agents look for into the client build: a Markdown twin of
26
+ * every article and section listing at `<path>index.md`, `llms.txt`, the Agent Skills index for
27
+ * `config.skills`, and a `_headers` file whose `Link` points agents at them. */
28
+ export function agents(opts) {
29
+ const publicDir = join(opts.config.root, ".livemark", "public");
30
+ let root = "";
31
+ return {
32
+ name: "livemark:agents",
33
+ applyToEnvironment(env) {
34
+ return env.name === "client";
35
+ },
36
+ configResolved(resolved) {
37
+ root = resolved.root;
38
+ },
39
+ async generateBundle() {
40
+ // Vite copies the publicDir before writeBundle, so emitting a file the
41
+ // project ships under `.livemark/public/` would silently replace it.
42
+ const emit = (fileName, source) => {
43
+ if (existsSync(join(publicDir, fileName)))
44
+ return;
45
+ this.emitFile({ type: "asset", fileName, source });
46
+ };
47
+ const articles = await loadGeneratedArticles(root);
48
+ const contents = await Promise.all(articles.map(article => resolveIncludes(article.content, resolve(opts.config.root, article.file))));
49
+ articles.forEach((article, index) => {
50
+ const content = contents[index] ?? article.content;
51
+ emit(buildTwinFileName(article.path), renderMarkdownTwin({ article, content }));
52
+ });
53
+ const llms = renderLlmsText({ config: opts.config, articles });
54
+ emit("llms.txt", llms);
55
+ const hasTwin = (path) => articles.some(a => a.path === path);
56
+ if (!hasTwin("/"))
57
+ emit("index.md", llms);
58
+ // A rewrite to a missing twin is a 404, so every listing page needs one too.
59
+ for (const section of opts.config.sections ?? []) {
60
+ if (section.type === "custom" || section.prefix === "/")
61
+ continue;
62
+ if (hasTwin(section.prefix))
63
+ continue;
64
+ emit(buildTwinFileName(section.prefix), renderSectionIndex({ config: opts.config, section, articles }));
65
+ }
66
+ const patterns = opts.config.skills ?? [];
67
+ const skills = loadAgentSkills({
68
+ root: opts.config.root,
69
+ patterns: Array.isArray(patterns) ? patterns : [patterns],
70
+ });
71
+ for (const skill of skills)
72
+ emit(buildSkillFileName(skill.name), skill.source);
73
+ if (skills.length) {
74
+ emit(".well-known/agent-skills/index.json", renderSkillsIndex({ skills, base: opts.config.base }));
75
+ }
76
+ const headersFile = join(publicDir, "_headers");
77
+ this.emitFile({
78
+ type: "asset",
79
+ fileName: "_headers",
80
+ source: renderHeaders({
81
+ base: opts.config.base,
82
+ hasSkills: skills.length > 0,
83
+ existing: existsSync(headersFile) ? readFileSync(headersFile, "utf-8") : "",
84
+ }),
85
+ });
86
+ },
87
+ };
88
+ }
89
+ /** Render `_headers` (Cloudflare, Netlify): a `Link` on the home page naming
90
+ * `llms.txt`, its Markdown twin and the skills index, after any rules the
91
+ * project ships itself. */
92
+ export function renderHeaders(props) {
93
+ const base = props.base ?? "";
94
+ const links = [
95
+ `<${base}/llms.txt>; rel="describedby"; type="text/plain"`,
96
+ `<${base}/index.md>; rel="alternate"; type="text/markdown"`,
97
+ ];
98
+ if (props.hasSkills) {
99
+ links.push(`<${base}/.well-known/agent-skills/index.json>; rel="describedby"; type="application/json"`);
100
+ }
101
+ const rule = `${base}/\n Link: ${links.join(", ")}\n`;
102
+ const existing = props.existing?.trimEnd();
103
+ return existing ? `${existing}\n\n${rule}` : rule;
104
+ }
105
+ async function loadGeneratedArticles(root) {
106
+ const file = join(root, ".content-collections", "generated", "allArticles.js");
107
+ const module = await import(__rewriteRelativeImportExtension(pathToFileURL(file).href));
108
+ return GeneratedArticles.parse(module.default);
109
+ }
110
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1hZ2VudHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvcGx1Z2lucy92aXRlLWFnZW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7OztBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQ2xELE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBQ3pDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxVQUFVLENBQUE7QUFFeEMsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQTtBQUN2QixPQUFPLEVBQ0wsa0JBQWtCLEVBQ2xCLGVBQWUsRUFDZixpQkFBaUIsR0FDbEIsTUFBTSw0QkFBNEIsQ0FBQTtBQUNuQyxPQUFPLEVBQ0wsaUJBQWlCLEVBQ2pCLGNBQWMsRUFDZCxrQkFBa0IsRUFDbEIsa0JBQWtCLEdBQ25CLE1BQU0sb0JBQW9CLENBQUE7QUFDM0IsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFBO0FBT2hFLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FDL0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUNQLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFO0lBQ2hCLEtBQUssRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFO0lBQ2pCLFdBQVcsRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ2xDLEtBQUssRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQzVCLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQzNCLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFO0lBQ2hCLE9BQU8sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFO0NBQ3BCLENBQUMsQ0FDSCxDQUFBO0FBRUQ7O2lG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QUM1QixNQUFNLFVBQVUsYUFBYSxDQUFDLEtBSTdCO0lBQ0MsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLElBQUksSUFBSSxFQUFFLENBQUE7SUFDN0IsTUFBTSxLQUFLLEdBQUc7UUFDWixJQUFJLElBQUksa0RBQWtEO1FBQzFELElBQUksSUFBSSxtREFBbUQ7S0FDNUQsQ0FBQTtJQUNELElBQUksS0FBSyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ3BCLEtBQUssQ0FBQyxJQUFJLENBQ1IsSUFBSSxJQUFJLG1GQUFtRixDQUM1RixDQUFBO0lBQ0gsQ0FBQztJQUNELE1BQU0sSUFBSSxHQUFHLEdBQUcsSUFBSSxjQUFjLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQTtJQUN0RCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFBO0lBQzFDLE9BQU8sUUFBUSxDQUFDLENBQUMsQ0FBQyxHQUFHLFFBQVEsT0FBTyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFBO0FBQ25ELENBQUM7QUFFRCxLQUFLLFVBQVUscUJBQXFCLENBQUMsSUFBWTtJQUMvQyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLHNCQUFzQixFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFBO0lBQzlFLE1BQU0sTUFBTSxHQUF5QixNQUFNLE1BQU0sa0NBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBQyxDQUFBO0lBQzNFLE9BQU8saUJBQWlCLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQTtBQUNoRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZXhpc3RzU3luYywgcmVhZEZpbGVTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIlxuaW1wb3J0IHsgam9pbiwgcmVzb2x2ZSB9IGZyb20gXCJub2RlOnBhdGhcIlxuaW1wb3J0IHsgcGF0aFRvRmlsZVVSTCB9IGZyb20gXCJub2RlOnVybFwiXG5pbXBvcnQgdHlwZSB7IFBsdWdpbiB9IGZyb20gXCJ2aXRlXCJcbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCJcbmltcG9ydCB7XG4gIGJ1aWxkU2tpbGxGaWxlTmFtZSxcbiAgbG9hZEFnZW50U2tpbGxzLFxuICByZW5kZXJTa2lsbHNJbmRleCxcbn0gZnJvbSBcIi4uL2hlbHBlcnMvYWdlbnQtc2tpbGxzLnRzXCJcbmltcG9ydCB7XG4gIGJ1aWxkVHdpbkZpbGVOYW1lLFxuICByZW5kZXJMbG1zVGV4dCxcbiAgcmVuZGVyTWFya2Rvd25Ud2luLFxuICByZW5kZXJTZWN0aW9uSW5kZXgsXG59IGZyb20gXCIuLi9oZWxwZXJzL2xsbXMudHNcIlxuaW1wb3J0IHsgcmVzb2x2ZUluY2x1ZGVzIH0gZnJvbSBcIi4uL2hlbHBlcnMvcmVzb2x2ZS1pbmNsdWRlcy50c1wiXG5pbXBvcnQgdHlwZSB7IENvbmZpZyB9IGZyb20gXCIuLi9tb2RlbHMvY29uZmlnLnRzXCJcblxuZXhwb3J0IGludGVyZmFjZSBBZ2VudHNPcHRpb25zIHtcbiAgY29uZmlnOiBDb25maWdcbn1cblxuY29uc3QgR2VuZXJhdGVkQXJ0aWNsZXMgPSB6LmFycmF5KFxuICB6Lm9iamVjdCh7XG4gICAgcGF0aDogei5zdHJpbmcoKSxcbiAgICB0aXRsZTogei5zdHJpbmcoKSxcbiAgICBkZXNjcmlwdGlvbjogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICAgIG9yZGVyOiB6Lm51bWJlcigpLm9wdGlvbmFsKCksXG4gICAgZGF0ZTogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICAgIGZpbGU6IHouc3RyaW5nKCksXG4gICAgY29udGVudDogei5zdHJpbmcoKSxcbiAgfSksXG4pXG5cbi8qKiBFbWl0IHdoYXQgQUkgYWdlbnRzIGxvb2sgZm9yIGludG8gdGhlIGNsaWVudCBidWlsZDogYSBNYXJrZG93biB0d2luIG9mXG4gKiAgZXZlcnkgYXJ0aWNsZSBhbmQgc2VjdGlvbiBsaXN0aW5nIGF0IGA8cGF0aD5pbmRleC5tZGAsIGBsbG1zLnR4dGAsIHRoZSBBZ2VudCBTa2lsbHMgaW5kZXggZm9yXG4gKiAgYGNvbmZpZy5za2lsbHNgLCBhbmQgYSBgX2hlYWRlcnNgIGZpbGUgd2hvc2UgYExpbmtgIHBvaW50cyBhZ2VudHMgYXQgdGhlbS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhZ2VudHMob3B0czogQWdlbnRzT3B0aW9ucyk6IFBsdWdpbiB7XG4gIGNvbnN0IHB1YmxpY0RpciA9IGpvaW4ob3B0cy5jb25maWcucm9vdCwgXCIubGl2ZW1hcmtcIiwgXCJwdWJsaWNcIilcbiAgbGV0IHJvb3QgPSBcIlwiXG4gIHJldHVybiB7XG4gICAgbmFtZTogXCJsaXZlbWFyazphZ2VudHNcIixcbiAgICBhcHBseVRvRW52aXJvbm1lbnQoZW52KSB7XG4gICAgICByZXR1cm4gZW52Lm5hbWUgPT09IFwiY2xpZW50XCJcbiAgICB9LFxuICAgIGNvbmZpZ1Jlc29sdmVkKHJlc29sdmVkKSB7XG4gICAgICByb290ID0gcmVzb2x2ZWQucm9vdFxuICAgIH0sXG4gICAgYXN5bmMgZ2VuZXJhdGVCdW5kbGUoKSB7XG4gICAgICAvLyBWaXRlIGNvcGllcyB0aGUgcHVibGljRGlyIGJlZm9yZSB3cml0ZUJ1bmRsZSwgc28gZW1pdHRpbmcgYSBmaWxlIHRoZVxuICAgICAgLy8gcHJvamVjdCBzaGlwcyB1bmRlciBgLmxpdmVtYXJrL3B1YmxpYy9gIHdvdWxkIHNpbGVudGx5IHJlcGxhY2UgaXQuXG4gICAgICBjb25zdCBlbWl0ID0gKGZpbGVOYW1lOiBzdHJpbmcsIHNvdXJjZTogc3RyaW5nIHwgQnVmZmVyKSA9PiB7XG4gICAgICAgIGlmIChleGlzdHNTeW5jKGpvaW4ocHVibGljRGlyLCBmaWxlTmFtZSkpKSByZXR1cm5cbiAgICAgICAgdGhpcy5lbWl0RmlsZSh7IHR5cGU6IFwiYXNzZXRcIiwgZmlsZU5hbWUsIHNvdXJjZSB9KVxuICAgICAgfVxuXG4gICAgICBjb25zdCBhcnRpY2xlcyA9IGF3YWl0IGxvYWRHZW5lcmF0ZWRBcnRpY2xlcyhyb290KVxuICAgICAgY29uc3QgY29udGVudHMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgYXJ0aWNsZXMubWFwKGFydGljbGUgPT5cbiAgICAgICAgICByZXNvbHZlSW5jbHVkZXMoYXJ0aWNsZS5jb250ZW50LCByZXNvbHZlKG9wdHMuY29uZmlnLnJvb3QsIGFydGljbGUuZmlsZSkpLFxuICAgICAgICApLFxuICAgICAgKVxuICAgICAgYXJ0aWNsZXMuZm9yRWFjaCgoYXJ0aWNsZSwgaW5kZXgpID0+IHtcbiAgICAgICAgY29uc3QgY29udGVudCA9IGNvbnRlbnRzW2luZGV4XSA/PyBhcnRpY2xlLmNvbnRlbnRcbiAgICAgICAgZW1pdChidWlsZFR3aW5GaWxlTmFtZShhcnRpY2xlLnBhdGgpLCByZW5kZXJNYXJrZG93blR3aW4oeyBhcnRpY2xlLCBjb250ZW50IH0pKVxuICAgICAgfSlcblxuICAgICAgY29uc3QgbGxtcyA9IHJlbmRlckxsbXNUZXh0KHsgY29uZmlnOiBvcHRzLmNvbmZpZywgYXJ0aWNsZXMgfSlcbiAgICAgIGVtaXQoXCJsbG1zLnR4dFwiLCBsbG1zKVxuICAgICAgY29uc3QgaGFzVHdpbiA9IChwYXRoOiBzdHJpbmcpID0+IGFydGljbGVzLnNvbWUoYSA9PiBhLnBhdGggPT09IHBhdGgpXG4gICAgICBpZiAoIWhhc1R3aW4oXCIvXCIpKSBlbWl0KFwiaW5kZXgubWRcIiwgbGxtcylcbiAgICAgIC8vIEEgcmV3cml0ZSB0byBhIG1pc3NpbmcgdHdpbiBpcyBhIDQwNCwgc28gZXZlcnkgbGlzdGluZyBwYWdlIG5lZWRzIG9uZSB0b28uXG4gICAgICBmb3IgKGNvbnN0IHNlY3Rpb24gb2Ygb3B0cy5jb25maWcuc2VjdGlvbnMgPz8gW10pIHtcbiAgICAgICAgaWYgKHNlY3Rpb24udHlwZSA9PT0gXCJjdXN0b21cIiB8fCBzZWN0aW9uLnByZWZpeCA9PT0gXCIvXCIpIGNvbnRpbnVlXG4gICAgICAgIGlmIChoYXNUd2luKHNlY3Rpb24ucHJlZml4KSkgY29udGludWVcbiAgICAgICAgZW1pdChcbiAgICAgICAgICBidWlsZFR3aW5GaWxlTmFtZShzZWN0aW9uLnByZWZpeCksXG4gICAgICAgICAgcmVuZGVyU2VjdGlvbkluZGV4KHsgY29uZmlnOiBvcHRzLmNvbmZpZywgc2VjdGlvbiwgYXJ0aWNsZXMgfSksXG4gICAgICAgIClcbiAgICAgIH1cblxuICAgICAgY29uc3QgcGF0dGVybnMgPSBvcHRzLmNvbmZpZy5za2lsbHMgPz8gW11cbiAgICAgIGNvbnN0IHNraWxscyA9IGxvYWRBZ2VudFNraWxscyh7XG4gICAgICAgIHJvb3Q6IG9wdHMuY29uZmlnLnJvb3QsXG4gICAgICAgIHBhdHRlcm5zOiBBcnJheS5pc0FycmF5KHBhdHRlcm5zKSA/IHBhdHRlcm5zIDogW3BhdHRlcm5zXSxcbiAgICAgIH0pXG4gICAgICBmb3IgKGNvbnN0IHNraWxsIG9mIHNraWxscykgZW1pdChidWlsZFNraWxsRmlsZU5hbWUoc2tpbGwubmFtZSksIHNraWxsLnNvdXJjZSlcbiAgICAgIGlmIChza2lsbHMubGVuZ3RoKSB7XG4gICAgICAgIGVtaXQoXG4gICAgICAgICAgXCIud2VsbC1rbm93bi9hZ2VudC1za2lsbHMvaW5kZXguanNvblwiLFxuICAgICAgICAgIHJlbmRlclNraWxsc0luZGV4KHsgc2tpbGxzLCBiYXNlOiBvcHRzLmNvbmZpZy5iYXNlIH0pLFxuICAgICAgICApXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGhlYWRlcnNGaWxlID0gam9pbihwdWJsaWNEaXIsIFwiX2hlYWRlcnNcIilcbiAgICAgIHRoaXMuZW1pdEZpbGUoe1xuICAgICAgICB0eXBlOiBcImFzc2V0XCIsXG4gICAgICAgIGZpbGVOYW1lOiBcIl9oZWFkZXJzXCIsXG4gICAgICAgIHNvdXJjZTogcmVuZGVySGVhZGVycyh7XG4gICAgICAgICAgYmFzZTogb3B0cy5jb25maWcuYmFzZSxcbiAgICAgICAgICBoYXNTa2lsbHM6IHNraWxscy5sZW5ndGggPiAwLFxuICAgICAgICAgIGV4aXN0aW5nOiBleGlzdHNTeW5jKGhlYWRlcnNGaWxlKSA/IHJlYWRGaWxlU3luYyhoZWFkZXJzRmlsZSwgXCJ1dGYtOFwiKSA6IFwiXCIsXG4gICAgICAgIH0pLFxuICAgICAgfSlcbiAgICB9LFxuICB9XG59XG5cbi8qKiBSZW5kZXIgYF9oZWFkZXJzYCAoQ2xvdWRmbGFyZSwgTmV0bGlmeSk6IGEgYExpbmtgIG9uIHRoZSBob21lIHBhZ2UgbmFtaW5nXG4gKiAgYGxsbXMudHh0YCwgaXRzIE1hcmtkb3duIHR3aW4gYW5kIHRoZSBza2lsbHMgaW5kZXgsIGFmdGVyIGFueSBydWxlcyB0aGVcbiAqICBwcm9qZWN0IHNoaXBzIGl0c2VsZi4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJIZWFkZXJzKHByb3BzOiB7XG4gIGJhc2U/OiBzdHJpbmdcbiAgaGFzU2tpbGxzOiBib29sZWFuXG4gIGV4aXN0aW5nPzogc3RyaW5nXG59KSB7XG4gIGNvbnN0IGJhc2UgPSBwcm9wcy5iYXNlID8/IFwiXCJcbiAgY29uc3QgbGlua3MgPSBbXG4gICAgYDwke2Jhc2V9L2xsbXMudHh0PjsgcmVsPVwiZGVzY3JpYmVkYnlcIjsgdHlwZT1cInRleHQvcGxhaW5cImAsXG4gICAgYDwke2Jhc2V9L2luZGV4Lm1kPjsgcmVsPVwiYWx0ZXJuYXRlXCI7IHR5cGU9XCJ0ZXh0L21hcmtkb3duXCJgLFxuICBdXG4gIGlmIChwcm9wcy5oYXNTa2lsbHMpIHtcbiAgICBsaW5rcy5wdXNoKFxuICAgICAgYDwke2Jhc2V9Ly53ZWxsLWtub3duL2FnZW50LXNraWxscy9pbmRleC5qc29uPjsgcmVsPVwiZGVzY3JpYmVkYnlcIjsgdHlwZT1cImFwcGxpY2F0aW9uL2pzb25cImAsXG4gICAgKVxuICB9XG4gIGNvbnN0IHJ1bGUgPSBgJHtiYXNlfS9cXG4gIExpbms6ICR7bGlua3Muam9pbihcIiwgXCIpfVxcbmBcbiAgY29uc3QgZXhpc3RpbmcgPSBwcm9wcy5leGlzdGluZz8udHJpbUVuZCgpXG4gIHJldHVybiBleGlzdGluZyA/IGAke2V4aXN0aW5nfVxcblxcbiR7cnVsZX1gIDogcnVsZVxufVxuXG5hc3luYyBmdW5jdGlvbiBsb2FkR2VuZXJhdGVkQXJ0aWNsZXMocm9vdDogc3RyaW5nKSB7XG4gIGNvbnN0IGZpbGUgPSBqb2luKHJvb3QsIFwiLmNvbnRlbnQtY29sbGVjdGlvbnNcIiwgXCJnZW5lcmF0ZWRcIiwgXCJhbGxBcnRpY2xlcy5qc1wiKVxuICBjb25zdCBtb2R1bGU6IHsgZGVmYXVsdDogdW5rbm93biB9ID0gYXdhaXQgaW1wb3J0KHBhdGhUb0ZpbGVVUkwoZmlsZSkuaHJlZilcbiAgcmV0dXJuIEdlbmVyYXRlZEFydGljbGVzLnBhcnNlKG1vZHVsZS5kZWZhdWx0KVxufVxuIl19
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from "vite-plus/test";
2
+ import { renderHeaders } from "./vite-agents.js";
3
+ describe("renderHeaders", () => {
4
+ it("links the home page to llms.txt and its markdown twin", () => {
5
+ expect(renderHeaders({ hasSkills: false })).toBe('/\n Link: </llms.txt>; rel="describedby"; type="text/plain", </index.md>; rel="alternate"; type="text/markdown"\n');
6
+ });
7
+ it("adds the skills index and mounts everything under base", () => {
8
+ const headers = renderHeaders({ base: "/repo", hasSkills: true });
9
+ expect(headers.startsWith("/repo/\n Link: </repo/llms.txt>")).toBe(true);
10
+ expect(headers).toContain('</repo/.well-known/agent-skills/index.json>; rel="describedby"; type="application/json"');
11
+ });
12
+ it("keeps the project's own rules ahead of the generated one", () => {
13
+ const headers = renderHeaders({
14
+ hasSkills: false,
15
+ existing: "/*\n X-Frame-Options: DENY\n\n",
16
+ });
17
+ expect(headers.startsWith("/*\n X-Frame-Options: DENY\n\n/\n Link: ")).toBe(true);
18
+ });
19
+ });
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1hZ2VudHMudW5pdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS9wbHVnaW5zL3ZpdGUtYWdlbnRzLnVuaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFDckQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBRWhELFFBQVEsQ0FBQyxlQUFlLEVBQUUsR0FBRyxFQUFFO0lBQzdCLEVBQUUsQ0FBQyx1REFBdUQsRUFBRSxHQUFHLEVBQUU7UUFDL0QsTUFBTSxDQUFDLGFBQWEsQ0FBQyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUM5QyxvSEFBb0gsQ0FDckgsQ0FBQTtJQUNILENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLHdEQUF3RCxFQUFFLEdBQUcsRUFBRTtRQUNoRSxNQUFNLE9BQU8sR0FBRyxhQUFhLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFBO1FBQ2pFLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLGtDQUFrQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUE7UUFDekUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLFNBQVMsQ0FDdkIseUZBQXlGLENBQzFGLENBQUE7SUFDSCxDQUFDLENBQUMsQ0FBQTtJQUVGLEVBQUUsQ0FBQywwREFBMEQsRUFBRSxHQUFHLEVBQUU7UUFDbEUsTUFBTSxPQUFPLEdBQUcsYUFBYSxDQUFDO1lBQzVCLFNBQVMsRUFBRSxLQUFLO1lBQ2hCLFFBQVEsRUFBRSxpQ0FBaUM7U0FDNUMsQ0FBQyxDQUFBO1FBQ0YsTUFBTSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsNENBQTRDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUNyRixDQUFDLENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZGVzY3JpYmUsIGV4cGVjdCwgaXQgfSBmcm9tIFwidml0ZS1wbHVzL3Rlc3RcIlxuaW1wb3J0IHsgcmVuZGVySGVhZGVycyB9IGZyb20gXCIuL3ZpdGUtYWdlbnRzLnRzXCJcblxuZGVzY3JpYmUoXCJyZW5kZXJIZWFkZXJzXCIsICgpID0+IHtcbiAgaXQoXCJsaW5rcyB0aGUgaG9tZSBwYWdlIHRvIGxsbXMudHh0IGFuZCBpdHMgbWFya2Rvd24gdHdpblwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlbmRlckhlYWRlcnMoeyBoYXNTa2lsbHM6IGZhbHNlIH0pKS50b0JlKFxuICAgICAgJy9cXG4gIExpbms6IDwvbGxtcy50eHQ+OyByZWw9XCJkZXNjcmliZWRieVwiOyB0eXBlPVwidGV4dC9wbGFpblwiLCA8L2luZGV4Lm1kPjsgcmVsPVwiYWx0ZXJuYXRlXCI7IHR5cGU9XCJ0ZXh0L21hcmtkb3duXCJcXG4nLFxuICAgIClcbiAgfSlcblxuICBpdChcImFkZHMgdGhlIHNraWxscyBpbmRleCBhbmQgbW91bnRzIGV2ZXJ5dGhpbmcgdW5kZXIgYmFzZVwiLCAoKSA9PiB7XG4gICAgY29uc3QgaGVhZGVycyA9IHJlbmRlckhlYWRlcnMoeyBiYXNlOiBcIi9yZXBvXCIsIGhhc1NraWxsczogdHJ1ZSB9KVxuICAgIGV4cGVjdChoZWFkZXJzLnN0YXJ0c1dpdGgoXCIvcmVwby9cXG4gIExpbms6IDwvcmVwby9sbG1zLnR4dD5cIikpLnRvQmUodHJ1ZSlcbiAgICBleHBlY3QoaGVhZGVycykudG9Db250YWluKFxuICAgICAgJzwvcmVwby8ud2VsbC1rbm93bi9hZ2VudC1za2lsbHMvaW5kZXguanNvbj47IHJlbD1cImRlc2NyaWJlZGJ5XCI7IHR5cGU9XCJhcHBsaWNhdGlvbi9qc29uXCInLFxuICAgIClcbiAgfSlcblxuICBpdChcImtlZXBzIHRoZSBwcm9qZWN0J3Mgb3duIHJ1bGVzIGFoZWFkIG9mIHRoZSBnZW5lcmF0ZWQgb25lXCIsICgpID0+IHtcbiAgICBjb25zdCBoZWFkZXJzID0gcmVuZGVySGVhZGVycyh7XG4gICAgICBoYXNTa2lsbHM6IGZhbHNlLFxuICAgICAgZXhpc3Rpbmc6IFwiLypcXG4gIFgtRnJhbWUtT3B0aW9uczogREVOWVxcblxcblwiLFxuICAgIH0pXG4gICAgZXhwZWN0KGhlYWRlcnMuc3RhcnRzV2l0aChcIi8qXFxuICBYLUZyYW1lLU9wdGlvbnM6IERFTllcXG5cXG4vXFxuICBMaW5rOiBcIikpLnRvQmUodHJ1ZSlcbiAgfSlcbn0pXG4iXX0=
@@ -1,5 +1,5 @@
1
1
  import type { Plugin } from "vite";
2
- import type { Config } from "../models/config.ts";
2
+ import type { Config, ContentSignals } from "../models/config.ts";
3
3
  export interface RobotsOptions {
4
4
  config: Config;
5
5
  }
@@ -14,4 +14,6 @@ export declare function robots(opts: RobotsOptions): Plugin;
14
14
  * line is appended only when `site` is set, because the directive
15
15
  * requires a fully-qualified URL; `base` is included so the URL matches
16
16
  * where the build (and the `sitemap.xml` inside it) is actually mounted. */
17
- export declare function renderRobots(config: Pick<Config, "site" | "base">): string;
17
+ export declare function renderRobots(config: Pick<Config, "site" | "base"> & {
18
+ contentSignals?: ContentSignals;
19
+ }): string;
@@ -29,7 +29,11 @@ export function robots(opts) {
29
29
  * requires a fully-qualified URL; `base` is included so the URL matches
30
30
  * where the build (and the `sitemap.xml` inside it) is actually mounted. */
31
31
  export function renderRobots(config) {
32
- const lines = ["User-agent: *", "Allow: /"];
32
+ const lines = ["User-agent: *"];
33
+ // Agents read the signal as part of a user-agent group, so it must sit inside one.
34
+ if (config.contentSignals)
35
+ lines.push(renderContentSignal(config.contentSignals));
36
+ lines.push("Allow: /");
33
37
  if (config.site) {
34
38
  const host = config.site.replace(/\/+$/, "");
35
39
  const base = config.base ?? "";
@@ -37,4 +41,13 @@ export function renderRobots(config) {
37
41
  }
38
42
  return `${lines.join("\n")}\n`;
39
43
  }
40
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1yb2JvdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvcGx1Z2lucy92aXRlLXJvYm90cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFRaEM7Ozs7O29FQUtvRTtBQUNwRSxNQUFNLFVBQVUsTUFBTSxDQUFDLElBQW1CO0lBQ3hDLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLFlBQVksQ0FBQyxDQUFBO0lBQ2hGLE9BQU87UUFDTCxJQUFJLEVBQUUsaUJBQWlCO1FBQ3ZCLGtCQUFrQixDQUFDLEdBQUc7WUFDcEIsT0FBTyxHQUFHLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQTtRQUM5QixDQUFDO1FBQ0QsY0FBYztZQUNaLElBQUksVUFBVSxDQUFDLFlBQVksQ0FBQztnQkFBRSxPQUFNO1lBQ3BDLElBQUksQ0FBQyxRQUFRLENBQUM7Z0JBQ1osSUFBSSxFQUFFLE9BQU87Z0JBQ2IsUUFBUSxFQUFFLFlBQVk7Z0JBQ3RCLE1BQU0sRUFBRSxZQUFZLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQzthQUNsQyxDQUFDLENBQUE7UUFDSixDQUFDO0tBQ0YsQ0FBQTtBQUNILENBQUM7QUFFRDs7OzZFQUc2RTtBQUM3RSxNQUFNLFVBQVUsWUFBWSxDQUFDLE1BQXFDO0lBQ2hFLE1BQU0sS0FBSyxHQUFHLENBQUMsZUFBZSxFQUFFLFVBQVUsQ0FBQyxDQUFBO0lBQzNDLElBQUksTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ2hCLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQTtRQUM1QyxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQTtRQUM5QixLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxZQUFZLElBQUksR0FBRyxJQUFJLGNBQWMsQ0FBQyxDQUFBO0lBQ3ZELENBQUM7SUFDRCxPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFBO0FBQ2hDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBleGlzdHNTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIlxuaW1wb3J0IHsgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIlxuaW1wb3J0IHR5cGUgeyBQbHVnaW4gfSBmcm9tIFwidml0ZVwiXG5pbXBvcnQgdHlwZSB7IENvbmZpZyB9IGZyb20gXCIuLi9tb2RlbHMvY29uZmlnLnRzXCJcblxuZXhwb3J0IGludGVyZmFjZSBSb2JvdHNPcHRpb25zIHtcbiAgY29uZmlnOiBDb25maWdcbn1cblxuLyoqIEVtaXQgYSBkZWZhdWx0IGByb2JvdHMudHh0YCBpbnRvIHRoZSBjbGllbnQgYnVpbGQgb3V0cHV0LCB3aXRoIGFcbiAqICBgU2l0ZW1hcDpgIGxpbmUgcG9pbnRpbmcgYXQgdGhlIFRhblN0YWNrLVN0YXJ0LWdlbmVyYXRlZCBgc2l0ZW1hcC54bWxgXG4gKiAgd2hlbiB7QGxpbmsgQ29uZmlnLnNpdGV9IGlzIHNldC4gU2tpcHBlZCB3aGVuIHRoZSBjb25zdW1lciBzaGlwc1xuICogIHRoZWlyIG93biBgLmxpdmVtYXJrL3B1YmxpYy9yb2JvdHMudHh0YCBzbyB0aGUgc3RhbmRhcmQgcHVibGljRGlyXG4gKiAgb3ZlcmxheSB3aW5zIOKAlCBWaXRlIGNvcGllcyB0aGUgcHVibGljRGlyIGJlZm9yZSB3cml0ZUJ1bmRsZSwgc29cbiAqICB1bmNvbmRpdGlvbmFsIGVtaXRGaWxlIHdvdWxkIHNpbGVudGx5IG92ZXJ3cml0ZSB0aGUgb3ZlcnJpZGUuICovXG5leHBvcnQgZnVuY3Rpb24gcm9ib3RzKG9wdHM6IFJvYm90c09wdGlvbnMpOiBQbHVnaW4ge1xuICBjb25zdCBvdmVycmlkZUZpbGUgPSBqb2luKG9wdHMuY29uZmlnLnJvb3QsIFwiLmxpdmVtYXJrXCIsIFwicHVibGljXCIsIFwicm9ib3RzLnR4dFwiKVxuICByZXR1cm4ge1xuICAgIG5hbWU6IFwibGl2ZW1hcms6cm9ib3RzXCIsXG4gICAgYXBwbHlUb0Vudmlyb25tZW50KGVudikge1xuICAgICAgcmV0dXJuIGVudi5uYW1lID09PSBcImNsaWVudFwiXG4gICAgfSxcbiAgICBnZW5lcmF0ZUJ1bmRsZSgpIHtcbiAgICAgIGlmIChleGlzdHNTeW5jKG92ZXJyaWRlRmlsZSkpIHJldHVyblxuICAgICAgdGhpcy5lbWl0RmlsZSh7XG4gICAgICAgIHR5cGU6IFwiYXNzZXRcIixcbiAgICAgICAgZmlsZU5hbWU6IFwicm9ib3RzLnR4dFwiLFxuICAgICAgICBzb3VyY2U6IHJlbmRlclJvYm90cyhvcHRzLmNvbmZpZyksXG4gICAgICB9KVxuICAgIH0sXG4gIH1cbn1cblxuLyoqIFJlbmRlciB0aGUgYm9keSBvZiBhIHBlcm1pc3NpdmUgZGVmYXVsdCBgcm9ib3RzLnR4dGAuIEEgYFNpdGVtYXA6YFxuICogIGxpbmUgaXMgYXBwZW5kZWQgb25seSB3aGVuIGBzaXRlYCBpcyBzZXQsIGJlY2F1c2UgdGhlIGRpcmVjdGl2ZVxuICogIHJlcXVpcmVzIGEgZnVsbHktcXVhbGlmaWVkIFVSTDsgYGJhc2VgIGlzIGluY2x1ZGVkIHNvIHRoZSBVUkwgbWF0Y2hlc1xuICogIHdoZXJlIHRoZSBidWlsZCAoYW5kIHRoZSBgc2l0ZW1hcC54bWxgIGluc2lkZSBpdCkgaXMgYWN0dWFsbHkgbW91bnRlZC4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJSb2JvdHMoY29uZmlnOiBQaWNrPENvbmZpZywgXCJzaXRlXCIgfCBcImJhc2VcIj4pIHtcbiAgY29uc3QgbGluZXMgPSBbXCJVc2VyLWFnZW50OiAqXCIsIFwiQWxsb3c6IC9cIl1cbiAgaWYgKGNvbmZpZy5zaXRlKSB7XG4gICAgY29uc3QgaG9zdCA9IGNvbmZpZy5zaXRlLnJlcGxhY2UoL1xcLyskLywgXCJcIilcbiAgICBjb25zdCBiYXNlID0gY29uZmlnLmJhc2UgPz8gXCJcIlxuICAgIGxpbmVzLnB1c2goXCJcIiwgYFNpdGVtYXA6ICR7aG9zdH0ke2Jhc2V9L3NpdGVtYXAueG1sYClcbiAgfVxuICByZXR1cm4gYCR7bGluZXMuam9pbihcIlxcblwiKX1cXG5gXG59XG4iXX0=
44
+ function renderContentSignal(signals) {
45
+ const flag = (value) => (value ? "yes" : "no");
46
+ const pairs = [
47
+ `search=${flag(signals.search)}`,
48
+ `ai-input=${flag(signals.aiInput)}`,
49
+ `ai-train=${flag(signals.aiTrain)}`,
50
+ ];
51
+ return `Content-Signal: ${pairs.join(", ")}`;
52
+ }
53
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1yb2JvdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvcGx1Z2lucy92aXRlLXJvYm90cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFRaEM7Ozs7O29FQUtvRTtBQUNwRSxNQUFNLFVBQVUsTUFBTSxDQUFDLElBQW1CO0lBQ3hDLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLFlBQVksQ0FBQyxDQUFBO0lBQ2hGLE9BQU87UUFDTCxJQUFJLEVBQUUsaUJBQWlCO1FBQ3ZCLGtCQUFrQixDQUFDLEdBQUc7WUFDcEIsT0FBTyxHQUFHLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQTtRQUM5QixDQUFDO1FBQ0QsY0FBYztZQUNaLElBQUksVUFBVSxDQUFDLFlBQVksQ0FBQztnQkFBRSxPQUFNO1lBQ3BDLElBQUksQ0FBQyxRQUFRLENBQUM7Z0JBQ1osSUFBSSxFQUFFLE9BQU87Z0JBQ2IsUUFBUSxFQUFFLFlBQVk7Z0JBQ3RCLE1BQU0sRUFBRSxZQUFZLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQzthQUNsQyxDQUFDLENBQUE7UUFDSixDQUFDO0tBQ0YsQ0FBQTtBQUNILENBQUM7QUFFRDs7OzZFQUc2RTtBQUM3RSxNQUFNLFVBQVUsWUFBWSxDQUMxQixNQUEyRTtJQUUzRSxNQUFNLEtBQUssR0FBRyxDQUFDLGVBQWUsQ0FBQyxDQUFBO0lBQy9CLG1GQUFtRjtJQUNuRixJQUFJLE1BQU0sQ0FBQyxjQUFjO1FBQUUsS0FBSyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQTtJQUNqRixLQUFLLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFBO0lBQ3RCLElBQUksTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ2hCLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQTtRQUM1QyxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQTtRQUM5QixLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxZQUFZLElBQUksR0FBRyxJQUFJLGNBQWMsQ0FBQyxDQUFBO0lBQ3ZELENBQUM7SUFDRCxPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFBO0FBQ2hDLENBQUM7QUFFRCxTQUFTLG1CQUFtQixDQUFDLE9BQXVCO0lBQ2xELE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBYyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUN2RCxNQUFNLEtBQUssR0FBRztRQUNaLFVBQVUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRTtRQUNoQyxZQUFZLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUU7UUFDbkMsWUFBWSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO0tBQ3BDLENBQUE7SUFDRCxPQUFPLG1CQUFtQixLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUE7QUFDOUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4aXN0c1N5bmMgfSBmcm9tIFwibm9kZTpmc1wiXG5pbXBvcnQgeyBqb2luIH0gZnJvbSBcIm5vZGU6cGF0aFwiXG5pbXBvcnQgdHlwZSB7IFBsdWdpbiB9IGZyb20gXCJ2aXRlXCJcbmltcG9ydCB0eXBlIHsgQ29uZmlnLCBDb250ZW50U2lnbmFscyB9IGZyb20gXCIuLi9tb2RlbHMvY29uZmlnLnRzXCJcblxuZXhwb3J0IGludGVyZmFjZSBSb2JvdHNPcHRpb25zIHtcbiAgY29uZmlnOiBDb25maWdcbn1cblxuLyoqIEVtaXQgYSBkZWZhdWx0IGByb2JvdHMudHh0YCBpbnRvIHRoZSBjbGllbnQgYnVpbGQgb3V0cHV0LCB3aXRoIGFcbiAqICBgU2l0ZW1hcDpgIGxpbmUgcG9pbnRpbmcgYXQgdGhlIFRhblN0YWNrLVN0YXJ0LWdlbmVyYXRlZCBgc2l0ZW1hcC54bWxgXG4gKiAgd2hlbiB7QGxpbmsgQ29uZmlnLnNpdGV9IGlzIHNldC4gU2tpcHBlZCB3aGVuIHRoZSBjb25zdW1lciBzaGlwc1xuICogIHRoZWlyIG93biBgLmxpdmVtYXJrL3B1YmxpYy9yb2JvdHMudHh0YCBzbyB0aGUgc3RhbmRhcmQgcHVibGljRGlyXG4gKiAgb3ZlcmxheSB3aW5zIOKAlCBWaXRlIGNvcGllcyB0aGUgcHVibGljRGlyIGJlZm9yZSB3cml0ZUJ1bmRsZSwgc29cbiAqICB1bmNvbmRpdGlvbmFsIGVtaXRGaWxlIHdvdWxkIHNpbGVudGx5IG92ZXJ3cml0ZSB0aGUgb3ZlcnJpZGUuICovXG5leHBvcnQgZnVuY3Rpb24gcm9ib3RzKG9wdHM6IFJvYm90c09wdGlvbnMpOiBQbHVnaW4ge1xuICBjb25zdCBvdmVycmlkZUZpbGUgPSBqb2luKG9wdHMuY29uZmlnLnJvb3QsIFwiLmxpdmVtYXJrXCIsIFwicHVibGljXCIsIFwicm9ib3RzLnR4dFwiKVxuICByZXR1cm4ge1xuICAgIG5hbWU6IFwibGl2ZW1hcms6cm9ib3RzXCIsXG4gICAgYXBwbHlUb0Vudmlyb25tZW50KGVudikge1xuICAgICAgcmV0dXJuIGVudi5uYW1lID09PSBcImNsaWVudFwiXG4gICAgfSxcbiAgICBnZW5lcmF0ZUJ1bmRsZSgpIHtcbiAgICAgIGlmIChleGlzdHNTeW5jKG92ZXJyaWRlRmlsZSkpIHJldHVyblxuICAgICAgdGhpcy5lbWl0RmlsZSh7XG4gICAgICAgIHR5cGU6IFwiYXNzZXRcIixcbiAgICAgICAgZmlsZU5hbWU6IFwicm9ib3RzLnR4dFwiLFxuICAgICAgICBzb3VyY2U6IHJlbmRlclJvYm90cyhvcHRzLmNvbmZpZyksXG4gICAgICB9KVxuICAgIH0sXG4gIH1cbn1cblxuLyoqIFJlbmRlciB0aGUgYm9keSBvZiBhIHBlcm1pc3NpdmUgZGVmYXVsdCBgcm9ib3RzLnR4dGAuIEEgYFNpdGVtYXA6YFxuICogIGxpbmUgaXMgYXBwZW5kZWQgb25seSB3aGVuIGBzaXRlYCBpcyBzZXQsIGJlY2F1c2UgdGhlIGRpcmVjdGl2ZVxuICogIHJlcXVpcmVzIGEgZnVsbHktcXVhbGlmaWVkIFVSTDsgYGJhc2VgIGlzIGluY2x1ZGVkIHNvIHRoZSBVUkwgbWF0Y2hlc1xuICogIHdoZXJlIHRoZSBidWlsZCAoYW5kIHRoZSBgc2l0ZW1hcC54bWxgIGluc2lkZSBpdCkgaXMgYWN0dWFsbHkgbW91bnRlZC4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJSb2JvdHMoXG4gIGNvbmZpZzogUGljazxDb25maWcsIFwic2l0ZVwiIHwgXCJiYXNlXCI+ICYgeyBjb250ZW50U2lnbmFscz86IENvbnRlbnRTaWduYWxzIH0sXG4pIHtcbiAgY29uc3QgbGluZXMgPSBbXCJVc2VyLWFnZW50OiAqXCJdXG4gIC8vIEFnZW50cyByZWFkIHRoZSBzaWduYWwgYXMgcGFydCBvZiBhIHVzZXItYWdlbnQgZ3JvdXAsIHNvIGl0IG11c3Qgc2l0IGluc2lkZSBvbmUuXG4gIGlmIChjb25maWcuY29udGVudFNpZ25hbHMpIGxpbmVzLnB1c2gocmVuZGVyQ29udGVudFNpZ25hbChjb25maWcuY29udGVudFNpZ25hbHMpKVxuICBsaW5lcy5wdXNoKFwiQWxsb3c6IC9cIilcbiAgaWYgKGNvbmZpZy5zaXRlKSB7XG4gICAgY29uc3QgaG9zdCA9IGNvbmZpZy5zaXRlLnJlcGxhY2UoL1xcLyskLywgXCJcIilcbiAgICBjb25zdCBiYXNlID0gY29uZmlnLmJhc2UgPz8gXCJcIlxuICAgIGxpbmVzLnB1c2goXCJcIiwgYFNpdGVtYXA6ICR7aG9zdH0ke2Jhc2V9L3NpdGVtYXAueG1sYClcbiAgfVxuICByZXR1cm4gYCR7bGluZXMuam9pbihcIlxcblwiKX1cXG5gXG59XG5cbmZ1bmN0aW9uIHJlbmRlckNvbnRlbnRTaWduYWwoc2lnbmFsczogQ29udGVudFNpZ25hbHMpIHtcbiAgY29uc3QgZmxhZyA9ICh2YWx1ZTogYm9vbGVhbikgPT4gKHZhbHVlID8gXCJ5ZXNcIiA6IFwibm9cIilcbiAgY29uc3QgcGFpcnMgPSBbXG4gICAgYHNlYXJjaD0ke2ZsYWcoc2lnbmFscy5zZWFyY2gpfWAsXG4gICAgYGFpLWlucHV0PSR7ZmxhZyhzaWduYWxzLmFpSW5wdXQpfWAsXG4gICAgYGFpLXRyYWluPSR7ZmxhZyhzaWduYWxzLmFpVHJhaW4pfWAsXG4gIF1cbiAgcmV0dXJuIGBDb250ZW50LVNpZ25hbDogJHtwYWlycy5qb2luKFwiLCBcIil9YFxufVxuIl19
@@ -16,5 +16,9 @@ describe("renderRobots", () => {
16
16
  it("ignores base when site is not set", () => {
17
17
  expect(renderRobots({ base: "/repo" })).toBe("User-agent: *\nAllow: /\n");
18
18
  });
19
+ it("puts the content signals inside the wildcard group", () => {
20
+ const contentSignals = { search: true, aiInput: true, aiTrain: false };
21
+ expect(renderRobots({ contentSignals })).toBe("User-agent: *\nContent-Signal: search=yes, ai-input=yes, ai-train=no\nAllow: /\n");
22
+ });
19
23
  });
20
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1yb2JvdHMudW5pdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS9wbHVnaW5zL3ZpdGUtcm9ib3RzLnVuaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBRS9DLFFBQVEsQ0FBQyxjQUFjLEVBQUUsR0FBRyxFQUFFO0lBQzVCLEVBQUUsQ0FBQyw4Q0FBOEMsRUFBRSxHQUFHLEVBQUU7UUFDdEQsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxDQUFBO0lBQzVELENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLHlDQUF5QyxFQUFFLEdBQUcsRUFBRTtRQUNqRCxNQUFNLENBQUMsWUFBWSxDQUFDLEVBQUUsSUFBSSxFQUFFLHNCQUFzQixFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FDekQsd0VBQXdFLENBQ3pFLENBQUE7SUFDSCxDQUFDLENBQUMsQ0FBQTtJQUVGLEVBQUUsQ0FBQyw0REFBNEQsRUFBRSxHQUFHLEVBQUU7UUFDcEUsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLElBQUksRUFBRSx1QkFBdUIsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzFELHdFQUF3RSxDQUN6RSxDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7SUFFRixFQUFFLENBQUMsMkRBQTJELEVBQUUsR0FBRyxFQUFFO1FBQ25FLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxJQUFJLEVBQUUsd0JBQXdCLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzFFLCtFQUErRSxDQUNoRixDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7SUFFRixFQUFFLENBQUMsbUNBQW1DLEVBQUUsR0FBRyxFQUFFO1FBQzNDLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxDQUFBO0lBQzNFLENBQUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBkZXNjcmliZSwgZXhwZWN0LCBpdCB9IGZyb20gXCJ2aXRlLXBsdXMvdGVzdFwiXG5pbXBvcnQgeyByZW5kZXJSb2JvdHMgfSBmcm9tIFwiLi92aXRlLXJvYm90cy50c1wiXG5cbmRlc2NyaWJlKFwicmVuZGVyUm9ib3RzXCIsICgpID0+IHtcbiAgaXQoXCJlbWl0cyBhbiBhbGxvdy1hbGwgYm9keSB3aGVuIHNpdGUgaXMgbm90IHNldFwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlbmRlclJvYm90cyh7fSkpLnRvQmUoXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cIilcbiAgfSlcblxuICBpdChcImFwcGVuZHMgYSBTaXRlbWFwIGxpbmUgd2hlbiBzaXRlIGlzIHNldFwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlbmRlclJvYm90cyh7IHNpdGU6IFwiaHR0cHM6Ly9saXZlbWFyay5kZXZcIiB9KSkudG9CZShcbiAgICAgIFwiVXNlci1hZ2VudDogKlxcbkFsbG93OiAvXFxuXFxuU2l0ZW1hcDogaHR0cHM6Ly9saXZlbWFyay5kZXYvc2l0ZW1hcC54bWxcXG5cIixcbiAgICApXG4gIH0pXG5cbiAgaXQoXCJzdHJpcHMgYSB0cmFpbGluZyBzbGFzaCBmcm9tIHNpdGUgYmVmb3JlIGNvbXBvc2luZyB0aGUgVVJMXCIsICgpID0+IHtcbiAgICBleHBlY3QocmVuZGVyUm9ib3RzKHsgc2l0ZTogXCJodHRwczovL2xpdmVtYXJrLmRldi9cIiB9KSkudG9CZShcbiAgICAgIFwiVXNlci1hZ2VudDogKlxcbkFsbG93OiAvXFxuXFxuU2l0ZW1hcDogaHR0cHM6Ly9saXZlbWFyay5kZXYvc2l0ZW1hcC54bWxcXG5cIixcbiAgICApXG4gIH0pXG5cbiAgaXQoXCJpbmNsdWRlcyB0aGUgYmFzZSBwYXRoIHNvIHRoZSBVUkwgbWF0Y2hlcyB0aGUgYnVpbGQgbW91bnRcIiwgKCkgPT4ge1xuICAgIGV4cGVjdChyZW5kZXJSb2JvdHMoeyBzaXRlOiBcImh0dHBzOi8vdXNlci5naXRodWIuaW9cIiwgYmFzZTogXCIvcmVwb1wiIH0pKS50b0JlKFxuICAgICAgXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cXG5TaXRlbWFwOiBodHRwczovL3VzZXIuZ2l0aHViLmlvL3JlcG8vc2l0ZW1hcC54bWxcXG5cIixcbiAgICApXG4gIH0pXG5cbiAgaXQoXCJpZ25vcmVzIGJhc2Ugd2hlbiBzaXRlIGlzIG5vdCBzZXRcIiwgKCkgPT4ge1xuICAgIGV4cGVjdChyZW5kZXJSb2JvdHMoeyBiYXNlOiBcIi9yZXBvXCIgfSkpLnRvQmUoXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cIilcbiAgfSlcbn0pXG4iXX0=
24
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1yb2JvdHMudW5pdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS9wbHVnaW5zL3ZpdGUtcm9ib3RzLnVuaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBRS9DLFFBQVEsQ0FBQyxjQUFjLEVBQUUsR0FBRyxFQUFFO0lBQzVCLEVBQUUsQ0FBQyw4Q0FBOEMsRUFBRSxHQUFHLEVBQUU7UUFDdEQsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxDQUFBO0lBQzVELENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLHlDQUF5QyxFQUFFLEdBQUcsRUFBRTtRQUNqRCxNQUFNLENBQUMsWUFBWSxDQUFDLEVBQUUsSUFBSSxFQUFFLHNCQUFzQixFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FDekQsd0VBQXdFLENBQ3pFLENBQUE7SUFDSCxDQUFDLENBQUMsQ0FBQTtJQUVGLEVBQUUsQ0FBQyw0REFBNEQsRUFBRSxHQUFHLEVBQUU7UUFDcEUsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLElBQUksRUFBRSx1QkFBdUIsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzFELHdFQUF3RSxDQUN6RSxDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7SUFFRixFQUFFLENBQUMsMkRBQTJELEVBQUUsR0FBRyxFQUFFO1FBQ25FLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxJQUFJLEVBQUUsd0JBQXdCLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzFFLCtFQUErRSxDQUNoRixDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7SUFFRixFQUFFLENBQUMsbUNBQW1DLEVBQUUsR0FBRyxFQUFFO1FBQzNDLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxDQUFBO0lBQzNFLENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLG9EQUFvRCxFQUFFLEdBQUcsRUFBRTtRQUM1RCxNQUFNLGNBQWMsR0FBRyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUE7UUFDdEUsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzNDLGtGQUFrRixDQUNuRixDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7QUFDSixDQUFDLENBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGRlc2NyaWJlLCBleHBlY3QsIGl0IH0gZnJvbSBcInZpdGUtcGx1cy90ZXN0XCJcbmltcG9ydCB7IHJlbmRlclJvYm90cyB9IGZyb20gXCIuL3ZpdGUtcm9ib3RzLnRzXCJcblxuZGVzY3JpYmUoXCJyZW5kZXJSb2JvdHNcIiwgKCkgPT4ge1xuICBpdChcImVtaXRzIGFuIGFsbG93LWFsbCBib2R5IHdoZW4gc2l0ZSBpcyBub3Qgc2V0XCIsICgpID0+IHtcbiAgICBleHBlY3QocmVuZGVyUm9ib3RzKHt9KSkudG9CZShcIlVzZXItYWdlbnQ6ICpcXG5BbGxvdzogL1xcblwiKVxuICB9KVxuXG4gIGl0KFwiYXBwZW5kcyBhIFNpdGVtYXAgbGluZSB3aGVuIHNpdGUgaXMgc2V0XCIsICgpID0+IHtcbiAgICBleHBlY3QocmVuZGVyUm9ib3RzKHsgc2l0ZTogXCJodHRwczovL2xpdmVtYXJrLmRldlwiIH0pKS50b0JlKFxuICAgICAgXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cXG5TaXRlbWFwOiBodHRwczovL2xpdmVtYXJrLmRldi9zaXRlbWFwLnhtbFxcblwiLFxuICAgIClcbiAgfSlcblxuICBpdChcInN0cmlwcyBhIHRyYWlsaW5nIHNsYXNoIGZyb20gc2l0ZSBiZWZvcmUgY29tcG9zaW5nIHRoZSBVUkxcIiwgKCkgPT4ge1xuICAgIGV4cGVjdChyZW5kZXJSb2JvdHMoeyBzaXRlOiBcImh0dHBzOi8vbGl2ZW1hcmsuZGV2L1wiIH0pKS50b0JlKFxuICAgICAgXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cXG5TaXRlbWFwOiBodHRwczovL2xpdmVtYXJrLmRldi9zaXRlbWFwLnhtbFxcblwiLFxuICAgIClcbiAgfSlcblxuICBpdChcImluY2x1ZGVzIHRoZSBiYXNlIHBhdGggc28gdGhlIFVSTCBtYXRjaGVzIHRoZSBidWlsZCBtb3VudFwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlbmRlclJvYm90cyh7IHNpdGU6IFwiaHR0cHM6Ly91c2VyLmdpdGh1Yi5pb1wiLCBiYXNlOiBcIi9yZXBvXCIgfSkpLnRvQmUoXG4gICAgICBcIlVzZXItYWdlbnQ6ICpcXG5BbGxvdzogL1xcblxcblNpdGVtYXA6IGh0dHBzOi8vdXNlci5naXRodWIuaW8vcmVwby9zaXRlbWFwLnhtbFxcblwiLFxuICAgIClcbiAgfSlcblxuICBpdChcImlnbm9yZXMgYmFzZSB3aGVuIHNpdGUgaXMgbm90IHNldFwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlbmRlclJvYm90cyh7IGJhc2U6IFwiL3JlcG9cIiB9KSkudG9CZShcIlVzZXItYWdlbnQ6ICpcXG5BbGxvdzogL1xcblwiKVxuICB9KVxuXG4gIGl0KFwicHV0cyB0aGUgY29udGVudCBzaWduYWxzIGluc2lkZSB0aGUgd2lsZGNhcmQgZ3JvdXBcIiwgKCkgPT4ge1xuICAgIGNvbnN0IGNvbnRlbnRTaWduYWxzID0geyBzZWFyY2g6IHRydWUsIGFpSW5wdXQ6IHRydWUsIGFpVHJhaW46IGZhbHNlIH1cbiAgICBleHBlY3QocmVuZGVyUm9ib3RzKHsgY29udGVudFNpZ25hbHMgfSkpLnRvQmUoXG4gICAgICBcIlVzZXItYWdlbnQ6ICpcXG5Db250ZW50LVNpZ25hbDogc2VhcmNoPXllcywgYWktaW5wdXQ9eWVzLCBhaS10cmFpbj1ub1xcbkFsbG93OiAvXFxuXCIsXG4gICAgKVxuICB9KVxufSlcbiJdfQ==
@@ -6,6 +6,7 @@ import react from "@vitejs/plugin-react";
6
6
  import { defineConfig } from "vite";
7
7
  import svgr from "vite-plugin-svgr";
8
8
  import { loadConfig } from "./actions/config/load.js";
9
+ import { agents } from "./plugins/vite-agents.js";
9
10
  import { livemark } from "./plugins/vite-livemark.js";
10
11
  import { robots } from "./plugins/vite-robots.js";
11
12
  const config = await loadConfig();
@@ -25,6 +26,7 @@ export default defineConfig({
25
26
  plugins: [
26
27
  livemark({ config }),
27
28
  robots({ config }),
29
+ agents({ config }),
28
30
  devtools(),
29
31
  tailwind(),
30
32
  contentCollections({ configPath: "content-collections.ts" }),
@@ -53,4 +55,4 @@ export default defineConfig({
53
55
  svgr(),
54
56
  ],
55
57
  });
56
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2Uvdml0ZS5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxrQkFBa0IsTUFBTSwyQkFBMkIsQ0FBQTtBQUMxRCxPQUFPLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQTtBQUN4QyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0seUJBQXlCLENBQUE7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1DQUFtQyxDQUFBO0FBQ2pFLE9BQU8sS0FBSyxNQUFNLHNCQUFzQixDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxNQUFNLENBQUE7QUFDbkMsT0FBTyxJQUFJLE1BQU0sa0JBQWtCLENBQUE7QUFDbkMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFBO0FBQ3JELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQTtBQUNyRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMEJBQTBCLENBQUE7QUFFakQsTUFBTSxNQUFNLEdBQUcsTUFBTSxVQUFVLEVBQUUsQ0FBQTtBQUVqQyx1RUFBdUU7QUFDdkUsd0VBQXdFO0FBQ3hFLHlFQUF5RTtBQUN6RSxtREFBbUQ7QUFDbkQsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLElBQUk7SUFDL0IsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUM7U0FDcEIsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxNQUFNLENBQUM7U0FDOUIsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNULElBQUksRUFBRSxHQUFHLE1BQU0sQ0FBQyxJQUFJLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQyxNQUFNLFNBQVM7UUFDOUMsT0FBTyxFQUFFLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRTtLQUMzQixDQUFDLENBQUM7SUFDUCxDQUFDLENBQUMsRUFBRSxDQUFBO0FBRU4sZUFBZSxZQUFZLENBQUM7SUFDMUIsT0FBTyxFQUFFO1FBQ1AsUUFBUSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7UUFDcEIsTUFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7UUFDbEIsUUFBUSxFQUFFO1FBQ1YsUUFBUSxFQUFFO1FBQ1Ysa0JBQWtCLENBQUMsRUFBRSxVQUFVLEVBQUUsd0JBQXdCLEVBQUUsQ0FBQztRQUM1RCxhQUFhLENBQUM7WUFDWixZQUFZLEVBQUUsR0FBRztZQUNqQixpRUFBaUU7WUFDakUsZ0VBQWdFO1lBQ2hFLHlEQUF5RDtZQUN6RCxLQUFLLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsR0FBRyxhQUFhLENBQUM7WUFDMUUsZ0VBQWdFO1lBQ2hFLG9FQUFvRTtZQUNwRSxrRUFBa0U7WUFDbEUsNkJBQTZCO1lBQzdCLFNBQVMsRUFBRTtnQkFDVCxPQUFPLEVBQUUsSUFBSTtnQkFDYixVQUFVLEVBQUUsSUFBSTtnQkFDaEIsd0JBQXdCLEVBQUUsS0FBSzthQUNoQztZQUNELE9BQU8sRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFO1lBQ2hGLE1BQU0sRUFBRTtnQkFDTixrQkFBa0IsRUFBRSxrQkFBa0I7Z0JBQ3RDLFFBQVEsRUFBRSxNQUFNLENBQUMsSUFBSTthQUN0QjtTQUNGLENBQUM7UUFDRixLQUFLLEVBQUU7UUFDUCxJQUFJLEVBQUU7S0FDUDtDQUNGLENBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjb250ZW50Q29sbGVjdGlvbnMgZnJvbSBcIkBjb250ZW50LWNvbGxlY3Rpb25zL3ZpdGVcIlxuaW1wb3J0IHRhaWx3aW5kIGZyb20gXCJAdGFpbHdpbmRjc3Mvdml0ZVwiXG5pbXBvcnQgeyBkZXZ0b29scyB9IGZyb20gXCJAdGFuc3RhY2svZGV2dG9vbHMtdml0ZVwiXG5pbXBvcnQgeyB0YW5zdGFja1N0YXJ0IH0gZnJvbSBcIkB0YW5zdGFjay9yZWFjdC1zdGFydC9wbHVnaW4vdml0ZVwiXG5pbXBvcnQgcmVhY3QgZnJvbSBcIkB2aXRlanMvcGx1Z2luLXJlYWN0XCJcbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gXCJ2aXRlXCJcbmltcG9ydCBzdmdyIGZyb20gXCJ2aXRlLXBsdWdpbi1zdmdyXCJcbmltcG9ydCB7IGxvYWRDb25maWcgfSBmcm9tIFwiLi9hY3Rpb25zL2NvbmZpZy9sb2FkLnRzXCJcbmltcG9ydCB7IGxpdmVtYXJrIH0gZnJvbSBcIi4vcGx1Z2lucy92aXRlLWxpdmVtYXJrLnRzXCJcbmltcG9ydCB7IHJvYm90cyB9IGZyb20gXCIuL3BsdWdpbnMvdml0ZS1yb2JvdHMudHNcIlxuXG5jb25zdCBjb25maWcgPSBhd2FpdCBsb2FkQ29uZmlnKClcblxuLy8gUGVyLWJsb2ctc2VjdGlvbiBSU1MgZmVlZCBwYXRocyB0byBwcmVyZW5kZXIuIEdhdGVkIG9uIGBjb25maWcuc2l0ZWBcbi8vIGJlY2F1c2UgZmVlZCBVUkxzIGFyZSBhYnNvbHV0ZSAobWF0Y2hpbmcgdGhlIHNpdGVtYXAvcm9ib3RzLnR4dCBnYXRlKVxuLy8gYW5kIHRoZSByb3V0ZSByZXR1cm5zIDQwNCB3aXRob3V0IGl0IGFueXdheS4gRXhjbHVkZWQgZnJvbSB0aGUgc2l0ZW1hcFxuLy8gc2luY2UgYC54bWxgIHBheWxvYWRzIGRvbid0IGJlbG9uZyBpbiBhIHNpdGVtYXAuXG5jb25zdCBibG9nRmVlZFBhZ2VzID0gY29uZmlnLnNpdGVcbiAgPyAoY29uZmlnLnNlY3Rpb25zID8/IFtdKVxuICAgICAgLmZpbHRlcihzID0+IHMudHlwZSA9PT0gXCJibG9nXCIpXG4gICAgICAubWFwKHMgPT4gKHtcbiAgICAgICAgcGF0aDogYCR7Y29uZmlnLmJhc2UgPz8gXCJcIn0ke3MucHJlZml4fXJzcy54bWxgLFxuICAgICAgICBzaXRlbWFwOiB7IGV4Y2x1ZGU6IHRydWUgfSxcbiAgICAgIH0pKVxuICA6IFtdXG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFtcbiAgICBsaXZlbWFyayh7IGNvbmZpZyB9KSxcbiAgICByb2JvdHMoeyBjb25maWcgfSksXG4gICAgZGV2dG9vbHMoKSxcbiAgICB0YWlsd2luZCgpLFxuICAgIGNvbnRlbnRDb2xsZWN0aW9ucyh7IGNvbmZpZ1BhdGg6IFwiY29udGVudC1jb2xsZWN0aW9ucy50c1wiIH0pLFxuICAgIHRhbnN0YWNrU3RhcnQoe1xuICAgICAgc3JjRGlyZWN0b3J5OiBcIi5cIixcbiAgICAgIC8vIFNlZWQgcGF0aCBtdXN0IG1hdGNoIHdoYXQgdGhlIGNyYXdsZXIgcHJvZHVjZXMgKHJvdXRlci1lbWl0dGVkXG4gICAgICAvLyBocmVmcyBpbmNsdWRlIHRoZSBiYXNlcGF0aCksIG90aGVyd2lzZSBwcmVyZW5kZXIgcmVjb3JkcyBib3RoXG4gICAgICAvLyBzaGFwZXMgYW5kIHRoZSBzaXRlbWFwIGVuZHMgdXAgd2l0aCBkdXBsaWNhdGUgZW50cmllcy5cbiAgICAgIHBhZ2VzOiBbeyBwYXRoOiBjb25maWcuYmFzZSA/IGAke2NvbmZpZy5iYXNlfS9gIDogXCIvXCIgfSwgLi4uYmxvZ0ZlZWRQYWdlc10sXG4gICAgICAvLyBEaXNhYmxlIHJvdXRlLXRyZWUgYXV0by1kaXNjb3Zlcnk6IGl0IHByb2R1Y2VzIGJhc2VwYXRoLW5haXZlXG4gICAgICAvLyBwYXRocyAoZS5nLiBgL2ApIHRoYXQgZHVwbGljYXRlIHRoZSBjcmF3bC1kZXJpdmVkIHByZWZpeGVkIHBhdGhzLFxuICAgICAgLy8gYmxvYXRpbmcgcGFnZXMuanNvbiBhbmQgc2l0ZW1hcC4gQ3Jhd2xpbmcgZnJvbSB0aGUgc2VlZCBhbHJlYWR5XG4gICAgICAvLyByZWFjaGVzIGV2ZXJ5IGxpbmtlZCBwYWdlLlxuICAgICAgcHJlcmVuZGVyOiB7XG4gICAgICAgIGVuYWJsZWQ6IHRydWUsXG4gICAgICAgIGNyYXdsTGlua3M6IHRydWUsXG4gICAgICAgIGF1dG9TdGF0aWNQYXRoc0Rpc2NvdmVyeTogZmFsc2UsXG4gICAgICB9LFxuICAgICAgc2l0ZW1hcDogY29uZmlnLnNpdGUgPyB7IGVuYWJsZWQ6IHRydWUsIGhvc3Q6IGNvbmZpZy5zaXRlIH0gOiB7IGVuYWJsZWQ6IGZhbHNlIH0sXG4gICAgICByb3V0ZXI6IHtcbiAgICAgICAgZ2VuZXJhdGVkUm91dGVUcmVlOiBcInJvdXRlVHJlZS5nZW4udHNcIixcbiAgICAgICAgYmFzZXBhdGg6IGNvbmZpZy5iYXNlLFxuICAgICAgfSxcbiAgICB9KSxcbiAgICByZWFjdCgpLFxuICAgIHN2Z3IoKSxcbiAgXSxcbn0pXG4iXX0=
58
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2Uvdml0ZS5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxrQkFBa0IsTUFBTSwyQkFBMkIsQ0FBQTtBQUMxRCxPQUFPLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQTtBQUN4QyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0seUJBQXlCLENBQUE7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1DQUFtQyxDQUFBO0FBQ2pFLE9BQU8sS0FBSyxNQUFNLHNCQUFzQixDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxNQUFNLENBQUE7QUFDbkMsT0FBTyxJQUFJLE1BQU0sa0JBQWtCLENBQUE7QUFDbkMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFBO0FBQ3JELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQTtBQUNqRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sNEJBQTRCLENBQUE7QUFDckQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDBCQUEwQixDQUFBO0FBRWpELE1BQU0sTUFBTSxHQUFHLE1BQU0sVUFBVSxFQUFFLENBQUE7QUFFakMsdUVBQXVFO0FBQ3ZFLHdFQUF3RTtBQUN4RSx5RUFBeUU7QUFDekUsbURBQW1EO0FBQ25ELE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQyxJQUFJO0lBQy9CLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLElBQUksRUFBRSxDQUFDO1NBQ3BCLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssTUFBTSxDQUFDO1NBQzlCLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDVCxJQUFJLEVBQUUsR0FBRyxNQUFNLENBQUMsSUFBSSxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsTUFBTSxTQUFTO1FBQzlDLE9BQU8sRUFBRSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUU7S0FDM0IsQ0FBQyxDQUFDO0lBQ1AsQ0FBQyxDQUFDLEVBQUUsQ0FBQTtBQUVOLGVBQWUsWUFBWSxDQUFDO0lBQzFCLE9BQU8sRUFBRTtRQUNQLFFBQVEsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ3BCLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ2xCLE1BQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ2xCLFFBQVEsRUFBRTtRQUNWLFFBQVEsRUFBRTtRQUNWLGtCQUFrQixDQUFDLEVBQUUsVUFBVSxFQUFFLHdCQUF3QixFQUFFLENBQUM7UUFDNUQsYUFBYSxDQUFDO1lBQ1osWUFBWSxFQUFFLEdBQUc7WUFDakIsaUVBQWlFO1lBQ2pFLGdFQUFnRTtZQUNoRSx5REFBeUQ7WUFDekQsS0FBSyxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLEdBQUcsYUFBYSxDQUFDO1lBQzFFLGdFQUFnRTtZQUNoRSxvRUFBb0U7WUFDcEUsa0VBQWtFO1lBQ2xFLDZCQUE2QjtZQUM3QixTQUFTLEVBQUU7Z0JBQ1QsT0FBTyxFQUFFLElBQUk7Z0JBQ2IsVUFBVSxFQUFFLElBQUk7Z0JBQ2hCLHdCQUF3QixFQUFFLEtBQUs7YUFDaEM7WUFDRCxPQUFPLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRTtZQUNoRixNQUFNLEVBQUU7Z0JBQ04sa0JBQWtCLEVBQUUsa0JBQWtCO2dCQUN0QyxRQUFRLEVBQUUsTUFBTSxDQUFDLElBQUk7YUFDdEI7U0FDRixDQUFDO1FBQ0YsS0FBSyxFQUFFO1FBQ1AsSUFBSSxFQUFFO0tBQ1A7Q0FDRixDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgY29udGVudENvbGxlY3Rpb25zIGZyb20gXCJAY29udGVudC1jb2xsZWN0aW9ucy92aXRlXCJcbmltcG9ydCB0YWlsd2luZCBmcm9tIFwiQHRhaWx3aW5kY3NzL3ZpdGVcIlxuaW1wb3J0IHsgZGV2dG9vbHMgfSBmcm9tIFwiQHRhbnN0YWNrL2RldnRvb2xzLXZpdGVcIlxuaW1wb3J0IHsgdGFuc3RhY2tTdGFydCB9IGZyb20gXCJAdGFuc3RhY2svcmVhY3Qtc3RhcnQvcGx1Z2luL3ZpdGVcIlxuaW1wb3J0IHJlYWN0IGZyb20gXCJAdml0ZWpzL3BsdWdpbi1yZWFjdFwiXG5pbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tIFwidml0ZVwiXG5pbXBvcnQgc3ZnciBmcm9tIFwidml0ZS1wbHVnaW4tc3ZnclwiXG5pbXBvcnQgeyBsb2FkQ29uZmlnIH0gZnJvbSBcIi4vYWN0aW9ucy9jb25maWcvbG9hZC50c1wiXG5pbXBvcnQgeyBhZ2VudHMgfSBmcm9tIFwiLi9wbHVnaW5zL3ZpdGUtYWdlbnRzLnRzXCJcbmltcG9ydCB7IGxpdmVtYXJrIH0gZnJvbSBcIi4vcGx1Z2lucy92aXRlLWxpdmVtYXJrLnRzXCJcbmltcG9ydCB7IHJvYm90cyB9IGZyb20gXCIuL3BsdWdpbnMvdml0ZS1yb2JvdHMudHNcIlxuXG5jb25zdCBjb25maWcgPSBhd2FpdCBsb2FkQ29uZmlnKClcblxuLy8gUGVyLWJsb2ctc2VjdGlvbiBSU1MgZmVlZCBwYXRocyB0byBwcmVyZW5kZXIuIEdhdGVkIG9uIGBjb25maWcuc2l0ZWBcbi8vIGJlY2F1c2UgZmVlZCBVUkxzIGFyZSBhYnNvbHV0ZSAobWF0Y2hpbmcgdGhlIHNpdGVtYXAvcm9ib3RzLnR4dCBnYXRlKVxuLy8gYW5kIHRoZSByb3V0ZSByZXR1cm5zIDQwNCB3aXRob3V0IGl0IGFueXdheS4gRXhjbHVkZWQgZnJvbSB0aGUgc2l0ZW1hcFxuLy8gc2luY2UgYC54bWxgIHBheWxvYWRzIGRvbid0IGJlbG9uZyBpbiBhIHNpdGVtYXAuXG5jb25zdCBibG9nRmVlZFBhZ2VzID0gY29uZmlnLnNpdGVcbiAgPyAoY29uZmlnLnNlY3Rpb25zID8/IFtdKVxuICAgICAgLmZpbHRlcihzID0+IHMudHlwZSA9PT0gXCJibG9nXCIpXG4gICAgICAubWFwKHMgPT4gKHtcbiAgICAgICAgcGF0aDogYCR7Y29uZmlnLmJhc2UgPz8gXCJcIn0ke3MucHJlZml4fXJzcy54bWxgLFxuICAgICAgICBzaXRlbWFwOiB7IGV4Y2x1ZGU6IHRydWUgfSxcbiAgICAgIH0pKVxuICA6IFtdXG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFtcbiAgICBsaXZlbWFyayh7IGNvbmZpZyB9KSxcbiAgICByb2JvdHMoeyBjb25maWcgfSksXG4gICAgYWdlbnRzKHsgY29uZmlnIH0pLFxuICAgIGRldnRvb2xzKCksXG4gICAgdGFpbHdpbmQoKSxcbiAgICBjb250ZW50Q29sbGVjdGlvbnMoeyBjb25maWdQYXRoOiBcImNvbnRlbnQtY29sbGVjdGlvbnMudHNcIiB9KSxcbiAgICB0YW5zdGFja1N0YXJ0KHtcbiAgICAgIHNyY0RpcmVjdG9yeTogXCIuXCIsXG4gICAgICAvLyBTZWVkIHBhdGggbXVzdCBtYXRjaCB3aGF0IHRoZSBjcmF3bGVyIHByb2R1Y2VzIChyb3V0ZXItZW1pdHRlZFxuICAgICAgLy8gaHJlZnMgaW5jbHVkZSB0aGUgYmFzZXBhdGgpLCBvdGhlcndpc2UgcHJlcmVuZGVyIHJlY29yZHMgYm90aFxuICAgICAgLy8gc2hhcGVzIGFuZCB0aGUgc2l0ZW1hcCBlbmRzIHVwIHdpdGggZHVwbGljYXRlIGVudHJpZXMuXG4gICAgICBwYWdlczogW3sgcGF0aDogY29uZmlnLmJhc2UgPyBgJHtjb25maWcuYmFzZX0vYCA6IFwiL1wiIH0sIC4uLmJsb2dGZWVkUGFnZXNdLFxuICAgICAgLy8gRGlzYWJsZSByb3V0ZS10cmVlIGF1dG8tZGlzY292ZXJ5OiBpdCBwcm9kdWNlcyBiYXNlcGF0aC1uYWl2ZVxuICAgICAgLy8gcGF0aHMgKGUuZy4gYC9gKSB0aGF0IGR1cGxpY2F0ZSB0aGUgY3Jhd2wtZGVyaXZlZCBwcmVmaXhlZCBwYXRocyxcbiAgICAgIC8vIGJsb2F0aW5nIHBhZ2VzLmpzb24gYW5kIHNpdGVtYXAuIENyYXdsaW5nIGZyb20gdGhlIHNlZWQgYWxyZWFkeVxuICAgICAgLy8gcmVhY2hlcyBldmVyeSBsaW5rZWQgcGFnZS5cbiAgICAgIHByZXJlbmRlcjoge1xuICAgICAgICBlbmFibGVkOiB0cnVlLFxuICAgICAgICBjcmF3bExpbmtzOiB0cnVlLFxuICAgICAgICBhdXRvU3RhdGljUGF0aHNEaXNjb3Zlcnk6IGZhbHNlLFxuICAgICAgfSxcbiAgICAgIHNpdGVtYXA6IGNvbmZpZy5zaXRlID8geyBlbmFibGVkOiB0cnVlLCBob3N0OiBjb25maWcuc2l0ZSB9IDogeyBlbmFibGVkOiBmYWxzZSB9LFxuICAgICAgcm91dGVyOiB7XG4gICAgICAgIGdlbmVyYXRlZFJvdXRlVHJlZTogXCJyb3V0ZVRyZWUuZ2VuLnRzXCIsXG4gICAgICAgIGJhc2VwYXRoOiBjb25maWcuYmFzZSxcbiAgICAgIH0sXG4gICAgfSksXG4gICAgcmVhY3QoKSxcbiAgICBzdmdyKCksXG4gIF0sXG59KVxuIl19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livemark",
3
- "version": "0.26.0",
3
+ "version": "0.27.1",
4
4
  "description": "Livemark description",
5
5
  "license": "MIT",
6
6
  "author": "Evgeny Karev",
@@ -91,6 +91,7 @@
91
91
  "unist-util-visit": "^5.1.0",
92
92
  "vite": "8.3.4",
93
93
  "vite-plugin-svgr": "5.2.0",
94
+ "yaml": "^2.9.1",
94
95
  "zod": "4.6.6"
95
96
  },
96
97
  "devDependencies": {
@@ -123,11 +124,12 @@
123
124
  "check:lint": "vp fmt --check && vp lint",
124
125
  "check:types": "tsgo --noEmit",
125
126
  "format": "vp fmt --no-error-on-unmatched-pattern",
127
+ "measure:coverage": "vp test --run --coverage",
126
128
  "release": "pnpm publish --access public --ignore-scripts --no-git-checks",
127
129
  "scan": "pnpm scan:leaks && pnpm scan:vulns",
128
130
  "scan:leaks": "gitleaks git --config=.gitleaks.toml --verbose --redact",
129
131
  "scan:vulns": "semgrep --config=p/typescript --config=p/nodejs --config=p/owasp-top-ten --config=p/secrets --exclude=\"**/fixtures/**\" --metrics=off",
130
- "squash": "node .scripts/squash-branch.ts",
132
+ "squash": "node scripts/squash-branch.ts",
131
133
  "start": "node source/entrypoints/main.ts start",
132
134
  "test": "pnpm check && pnpm test:unit",
133
135
  "test:unit": "vp test",
@@ -42,6 +42,10 @@ export default defineConfig({
42
42
  scripts: [{ src: "https://example.com/analytics.js", defer: "" }],
43
43
  },
44
44
 
45
+ // Optional: AI agents (llms.txt and Markdown twins are always emitted)
46
+ contentSignals: { search: true, aiInput: true, aiTrain: true }, // robots.txt Content-Signal; all default true
47
+ skills: "skills/*/SKILL.md", // publish skills at /.well-known/agent-skills/
48
+
45
49
  // Optional: content selection
46
50
  exclude: "docs/drafts/**", // string | string[]
47
51
 
@@ -25,6 +25,7 @@ Invoke when the user asks to:
25
25
  | Replace a default component/style | Drop a file at `.livemark/components/<Name>.tsx` etc. |
26
26
  | Add a new page at an exact URL | Create `.livemark/routes/<path>.tsx` |
27
27
  | Serve a static asset at `/<path>` | Put the file in `.livemark/public/<path>` |
28
+ | Extend the generated `_headers` | `.livemark/public/_headers` — kept, `Link` appended |
28
29
  | Rewrite site-wide `<head>` tags | `.livemark/head.ts` exporting `customizeHead(head)` |
29
30
 
30
31
  None require config changes. The Vite plugin (`source/plugins/vite-livemark.ts`) watches `.livemark/**` and hot-reloads.
@@ -1,7 +1,12 @@
1
1
  import { config } from "livemark:virtual"
2
2
  import { allArticles } from "content-collections"
3
3
  import type { ArticleNode } from "../models/article.ts"
4
- import { buildArticleTree, flattenArticleTree } from "../helpers/article.ts"
4
+ import {
5
+ buildArticleTree,
6
+ compareArticleDate,
7
+ compareArticleOrder,
8
+ flattenArticleTree,
9
+ } from "../helpers/article.ts"
5
10
  import { matchSection, partitionBySection } from "../helpers/section.ts"
6
11
 
7
12
  /**
@@ -9,9 +14,7 @@ import { matchSection, partitionBySection } from "../helpers/section.ts"
9
14
  * positive values first (ascending), then unordered, then negative values
10
15
  * (so `order: -1` lands at the very end, `-2` just before it, and so on).
11
16
  */
12
- export const sortedArticles = [...allArticles].sort(
13
- (a, b) => orderKey(a.order) - orderKey(b.order),
14
- )
17
+ export const sortedArticles = [...allArticles].sort(compareArticleOrder)
15
18
 
16
19
  /** The article whose path is `/`, if any (rendered in place at the root) */
17
20
  export const homeArticle = sortedArticles.find(a => a.path === "/")
@@ -25,12 +28,6 @@ export const flatArticles = flattenArticleTree(articleTree)
25
28
  /** The first article in sidebar order, used as the default landing page */
26
29
  export const firstArticle = sortedArticles.find(a => a.path === flatArticles[0])
27
30
 
28
- function orderKey(order?: number) {
29
- if (order === undefined) return Number.MAX_SAFE_INTEGER / 2
30
- if (order < 0) return Number.MAX_SAFE_INTEGER + order
31
- return order
32
- }
33
-
34
31
  const configSections = config.sections
35
32
 
36
33
  /** Route-bound sections (everything except `custom`). Custom sections
@@ -53,9 +50,7 @@ if (routedSections.length) {
53
50
  const section = sectionByPrefix.get(key)
54
51
  const sorted =
55
52
  section?.type === "blog" || section?.type === "changelog"
56
- ? [...bucket].sort(
57
- (a, b) => new Date(b.date ?? 0).getTime() - new Date(a.date ?? 0).getTime(),
58
- )
53
+ ? [...bucket].sort(compareArticleDate)
59
54
  : bucket
60
55
  const tree = buildArticleTree(sorted)
61
56
  const flat = flattenArticleTree(tree)
@@ -49,6 +49,7 @@ import { transformerIcon } from "./plugins/shiki-icon.ts"
49
49
  import { resolveAssetPath } from "./helpers/resolve-asset-path.ts"
50
50
  import { resolveIncludes } from "./helpers/resolve-includes.ts"
51
51
  import { extractSearchText } from "./helpers/search-text.ts"
52
+ import { stripLeadingH1 } from "./helpers/llms.ts"
52
53
  import { extractToc } from "./helpers/toc.ts"
53
54
 
54
55
  const config = await loadConfig()
@@ -98,10 +99,6 @@ function getLastUpdated(filePath: string) {
98
99
  }
99
100
  }
100
101
 
101
- function stripLeadingH1(content: string) {
102
- return content.replace(/^[ \t]*#\s+[^\n]*\n+/, "")
103
- }
104
-
105
102
  function extractTitle(content: string, path: string) {
106
103
  const match = /^#\s+(.+)$/m.exec(content)
107
104
  if (match) return match[1]!
@@ -123,7 +120,7 @@ const articles = defineCollection({
123
120
  const patch = config.patches?.find(p => p.file === document._meta.filePath)
124
121
  const doc = patch ? { ...document, ...patch.article } : document
125
122
  const filePath = resolve(config.root, doc._meta.filePath)
126
- const content = resolveIncludes(doc.content, filePath)
123
+ const content = await resolveIncludes(doc.content, filePath)
127
124
  const title = doc.title ?? extractTitle(content, doc._meta.path)
128
125
  const icon = doc.icon
129
126
  const path = doc.path ?? toPath(doc._meta.filePath)
@@ -0,0 +1,59 @@
1
+ import { createHash } from "node:crypto"
2
+ import { globSync, readFileSync } from "node:fs"
3
+ import { resolve, sep } from "node:path"
4
+ import { parse } from "yaml"
5
+ import { z } from "zod"
6
+
7
+ const SKILLS_SCHEMA = "https://schemas.agentskills.io/discovery/0.2.0/schema.json"
8
+ const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/
9
+
10
+ const SkillFrontmatter = z.object({
11
+ name: z
12
+ .string()
13
+ .max(64)
14
+ .regex(/^[a-z0-9]+(-[a-z0-9]+)*$/),
15
+ description: z.string().min(1).max(1024),
16
+ })
17
+
18
+ /** A `SKILL.md` published for agents, with the raw bytes its digest covers. */
19
+ export interface AgentSkill {
20
+ name: string
21
+ description: string
22
+ source: Buffer
23
+ }
24
+
25
+ /** Read every `SKILL.md` the `skills` globs match, in a stable order. */
26
+ export function loadAgentSkills(props: { root: string; patterns: string[] }) {
27
+ // Windows globs with `\`, which would leak into messages and change the sort.
28
+ return globSync(props.patterns, { cwd: props.root })
29
+ .map(file => file.split(sep).join("/"))
30
+ .sort()
31
+ .map(file => {
32
+ const source = readFileSync(resolve(props.root, file))
33
+ const frontmatter = FRONTMATTER_RE.exec(source.toString("utf-8"))?.[1] ?? ""
34
+ const parsed = SkillFrontmatter.safeParse(parse(frontmatter))
35
+ if (!parsed.success) {
36
+ throw new Error(
37
+ `${file}: a published skill needs a frontmatter \`name\` (lowercase, hyphenated) and \`description\``,
38
+ )
39
+ }
40
+ return { ...parsed.data, source }
41
+ })
42
+ }
43
+
44
+ /** Where a skill is published inside the build output (no base). */
45
+ export function buildSkillFileName(name: string) {
46
+ return `.well-known/agent-skills/${name}/SKILL.md`
47
+ }
48
+
49
+ /** Render the Agent Skills discovery index (v0.2.0) for the published skills. */
50
+ export function renderSkillsIndex(props: { skills: AgentSkill[]; base?: string }) {
51
+ const skills = props.skills.map(skill => ({
52
+ name: skill.name,
53
+ type: "skill-md",
54
+ description: skill.description,
55
+ url: `${props.base ?? ""}/${buildSkillFileName(skill.name)}`,
56
+ digest: `sha256:${createHash("sha256").update(skill.source).digest("hex")}`,
57
+ }))
58
+ return `${JSON.stringify({ $schema: SKILLS_SCHEMA, skills }, null, 2)}\n`
59
+ }
@@ -0,0 +1,56 @@
1
+ import { createHash } from "node:crypto"
2
+ import { mkdirSync, writeFileSync } from "node:fs"
3
+ import { join } from "node:path"
4
+ import { describe, expect, it } from "vite-plus/test"
5
+ import { temporaryDirectory } from "tempy"
6
+ import { loadAgentSkills, renderSkillsIndex } from "./agent-skills.ts"
7
+
8
+ function writeSkill(root: string, dir: string, source: string) {
9
+ mkdirSync(join(root, "skills", dir), { recursive: true })
10
+ writeFileSync(join(root, "skills", dir, "SKILL.md"), source)
11
+ }
12
+
13
+ describe("loadAgentSkills", () => {
14
+ it("reads name and description from each matched SKILL.md", () => {
15
+ const root = temporaryDirectory()
16
+ writeSkill(root, "b", "---\nname: beta\ndescription: >\n Folded\n text\n---\n# B\n")
17
+ writeSkill(root, "a", "---\nname: alpha\ndescription: First\n---\n# A\n")
18
+ const skills = loadAgentSkills({ root, patterns: ["skills/*/SKILL.md"] })
19
+ expect(skills.map(s => [s.name, s.description])).toEqual([
20
+ ["alpha", "First"],
21
+ ["beta", "Folded text\n"],
22
+ ])
23
+ })
24
+
25
+ it("rejects a skill whose name agents cannot address", () => {
26
+ const root = temporaryDirectory()
27
+ writeSkill(root, "a", "---\nname: Not Valid\ndescription: First\n---\n")
28
+ expect(() => loadAgentSkills({ root, patterns: ["skills/*/SKILL.md"] })).toThrow(
29
+ "skills/a/SKILL.md",
30
+ )
31
+ })
32
+ })
33
+
34
+ describe("renderSkillsIndex", () => {
35
+ it("publishes each skill with a digest of its exact bytes", () => {
36
+ const source = Buffer.from("---\nname: alpha\ndescription: First\n---\n")
37
+ const index = JSON.parse(
38
+ renderSkillsIndex({
39
+ skills: [{ name: "alpha", description: "First", source }],
40
+ base: "/repo",
41
+ }),
42
+ )
43
+ expect(index).toEqual({
44
+ $schema: "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
45
+ skills: [
46
+ {
47
+ name: "alpha",
48
+ type: "skill-md",
49
+ description: "First",
50
+ url: "/repo/.well-known/agent-skills/alpha/SKILL.md",
51
+ digest: `sha256:${createHash("sha256").update(source).digest("hex")}`,
52
+ },
53
+ ],
54
+ })
55
+ })
56
+ })
@@ -50,3 +50,20 @@ export function flattenArticleTree(nodes: ArticleNode[]) {
50
50
  }
51
51
  return result
52
52
  }
53
+
54
+ /** Sort comparator on frontmatter `order`: positive ascending, then unordered,
55
+ * then negative (so `order: -1` lands last) */
56
+ export function compareArticleOrder(a: { order?: number }, b: { order?: number }) {
57
+ return orderKey(a.order) - orderKey(b.order)
58
+ }
59
+
60
+ /** Sort comparator putting the newest `date` first, as blog and changelog list */
61
+ export function compareArticleDate(a: { date?: string }, b: { date?: string }) {
62
+ return new Date(b.date ?? 0).getTime() - new Date(a.date ?? 0).getTime()
63
+ }
64
+
65
+ function orderKey(order?: number) {
66
+ if (order === undefined) return Number.MAX_SAFE_INTEGER / 2
67
+ if (order < 0) return Number.MAX_SAFE_INTEGER + order
68
+ return order
69
+ }
@@ -83,7 +83,18 @@ export function buildPageHead(props: {
83
83
  ...(url ? [{ property: "og:url", content: url }] : []),
84
84
  ...(image ? [{ property: "og:image", content: image }] : []),
85
85
  ],
86
- links: url ? [{ rel: "canonical", href: url }] : [],
86
+ links: [
87
+ ...(url ? [{ rel: "canonical", href: url }] : []),
88
+ ...(props.isArticle
89
+ ? [
90
+ {
91
+ rel: "alternate",
92
+ type: "text/markdown",
93
+ href: prefixUrl(`${props.path}index.md`, props.config.base),
94
+ },
95
+ ]
96
+ : []),
97
+ ],
87
98
  }
88
99
  }
89
100
 
@@ -79,7 +79,9 @@ describe("buildPageHead", () => {
79
79
  { property: "og:description", content: "How to" },
80
80
  { property: "og:type", content: "article" },
81
81
  ])
82
- expect(head.links).toEqual([])
82
+ expect(head.links).toEqual([
83
+ { rel: "alternate", type: "text/markdown", href: "/guide/index.md" },
84
+ ])
83
85
  })
84
86
 
85
87
  it("adds absolute url, canonical and image when site is set", () => {