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.
- package/build/actions/config/define.d.ts +6 -0
- package/build/content/article.js +4 -11
- package/build/content-collections.js +3 -5
- package/build/helpers/agent-skills.d.ts +22 -0
- package/build/helpers/agent-skills.js +46 -0
- package/build/helpers/agent-skills.unit.d.ts +1 -0
- package/build/helpers/agent-skills.unit.js +49 -0
- package/build/helpers/article.d.ts +13 -0
- package/build/helpers/article.js +17 -1
- package/build/helpers/head.d.ts +7 -2
- package/build/helpers/head.js +13 -2
- package/build/helpers/head.unit.js +4 -2
- package/build/helpers/llms.d.ts +33 -0
- package/build/helpers/llms.js +72 -0
- package/build/helpers/llms.unit.d.ts +1 -0
- package/build/helpers/llms.unit.js +98 -0
- package/build/helpers/resolve-includes.d.ts +1 -1
- package/build/helpers/resolve-includes.js +8 -8
- package/build/helpers/resolve-includes.unit.js +23 -23
- package/build/models/config.d.ts +20 -0
- package/build/models/config.js +8 -1
- package/build/plugins/vite-agents.d.ts +17 -0
- package/build/plugins/vite-agents.js +110 -0
- package/build/plugins/vite-agents.unit.d.ts +1 -0
- package/build/plugins/vite-agents.unit.js +20 -0
- package/build/plugins/vite-robots.d.ts +4 -2
- package/build/plugins/vite-robots.js +15 -2
- package/build/plugins/vite-robots.unit.js +5 -1
- package/build/vite.config.js +3 -1
- package/package.json +4 -2
- package/skills/livemark-configuration/SKILL.md +4 -0
- package/skills/livemark-customization/SKILL.md +1 -0
- package/source/content/article.ts +8 -13
- package/source/content-collections.ts +2 -5
- package/source/helpers/agent-skills.ts +59 -0
- package/source/helpers/agent-skills.unit.ts +56 -0
- package/source/helpers/article.ts +17 -0
- package/source/helpers/head.ts +12 -1
- package/source/helpers/head.unit.ts +3 -1
- package/source/helpers/llms.ts +109 -0
- package/source/helpers/llms.unit.ts +115 -0
- package/source/helpers/resolve-includes.ts +12 -12
- package/source/helpers/resolve-includes.unit.ts +46 -22
- package/source/models/config.ts +11 -0
- package/source/plugins/vite-agents.ts +137 -0
- package/source/plugins/vite-agents.unit.ts +26 -0
- package/source/plugins/vite-robots.ts +18 -3
- package/source/plugins/vite-robots.unit.ts +7 -0
- 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">
|
|
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: *"
|
|
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
|
-
|
|
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,
|
|
24
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1yb2JvdHMudW5pdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS9wbHVnaW5zL3ZpdGUtcm9ib3RzLnVuaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBRS9DLFFBQVEsQ0FBQyxjQUFjLEVBQUUsR0FBRyxFQUFFO0lBQzVCLEVBQUUsQ0FBQyw4Q0FBOEMsRUFBRSxHQUFHLEVBQUU7UUFDdEQsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxDQUFBO0lBQzVELENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLHlDQUF5QyxFQUFFLEdBQUcsRUFBRTtRQUNqRCxNQUFNLENBQUMsWUFBWSxDQUFDLEVBQUUsSUFBSSxFQUFFLHNCQUFzQixFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FDekQsd0VBQXdFLENBQ3pFLENBQUE7SUFDSCxDQUFDLENBQUMsQ0FBQTtJQUVGLEVBQUUsQ0FBQyw0REFBNEQsRUFBRSxHQUFHLEVBQUU7UUFDcEUsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLElBQUksRUFBRSx1QkFBdUIsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzFELHdFQUF3RSxDQUN6RSxDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7SUFFRixFQUFFLENBQUMsMkRBQTJELEVBQUUsR0FBRyxFQUFFO1FBQ25FLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxJQUFJLEVBQUUsd0JBQXdCLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzFFLCtFQUErRSxDQUNoRixDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7SUFFRixFQUFFLENBQUMsbUNBQW1DLEVBQUUsR0FBRyxFQUFFO1FBQzNDLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxDQUFBO0lBQzNFLENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLG9EQUFvRCxFQUFFLEdBQUcsRUFBRTtRQUM1RCxNQUFNLGNBQWMsR0FBRyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUE7UUFDdEUsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzNDLGtGQUFrRixDQUNuRixDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7QUFDSixDQUFDLENBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGRlc2NyaWJlLCBleHBlY3QsIGl0IH0gZnJvbSBcInZpdGUtcGx1cy90ZXN0XCJcbmltcG9ydCB7IHJlbmRlclJvYm90cyB9IGZyb20gXCIuL3ZpdGUtcm9ib3RzLnRzXCJcblxuZGVzY3JpYmUoXCJyZW5kZXJSb2JvdHNcIiwgKCkgPT4ge1xuICBpdChcImVtaXRzIGFuIGFsbG93LWFsbCBib2R5IHdoZW4gc2l0ZSBpcyBub3Qgc2V0XCIsICgpID0+IHtcbiAgICBleHBlY3QocmVuZGVyUm9ib3RzKHt9KSkudG9CZShcIlVzZXItYWdlbnQ6ICpcXG5BbGxvdzogL1xcblwiKVxuICB9KVxuXG4gIGl0KFwiYXBwZW5kcyBhIFNpdGVtYXAgbGluZSB3aGVuIHNpdGUgaXMgc2V0XCIsICgpID0+IHtcbiAgICBleHBlY3QocmVuZGVyUm9ib3RzKHsgc2l0ZTogXCJodHRwczovL2xpdmVtYXJrLmRldlwiIH0pKS50b0JlKFxuICAgICAgXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cXG5TaXRlbWFwOiBodHRwczovL2xpdmVtYXJrLmRldi9zaXRlbWFwLnhtbFxcblwiLFxuICAgIClcbiAgfSlcblxuICBpdChcInN0cmlwcyBhIHRyYWlsaW5nIHNsYXNoIGZyb20gc2l0ZSBiZWZvcmUgY29tcG9zaW5nIHRoZSBVUkxcIiwgKCkgPT4ge1xuICAgIGV4cGVjdChyZW5kZXJSb2JvdHMoeyBzaXRlOiBcImh0dHBzOi8vbGl2ZW1hcmsuZGV2L1wiIH0pKS50b0JlKFxuICAgICAgXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cXG5TaXRlbWFwOiBodHRwczovL2xpdmVtYXJrLmRldi9zaXRlbWFwLnhtbFxcblwiLFxuICAgIClcbiAgfSlcblxuICBpdChcImluY2x1ZGVzIHRoZSBiYXNlIHBhdGggc28gdGhlIFVSTCBtYXRjaGVzIHRoZSBidWlsZCBtb3VudFwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlbmRlclJvYm90cyh7IHNpdGU6IFwiaHR0cHM6Ly91c2VyLmdpdGh1Yi5pb1wiLCBiYXNlOiBcIi9yZXBvXCIgfSkpLnRvQmUoXG4gICAgICBcIlVzZXItYWdlbnQ6ICpcXG5BbGxvdzogL1xcblxcblNpdGVtYXA6IGh0dHBzOi8vdXNlci5naXRodWIuaW8vcmVwby9zaXRlbWFwLnhtbFxcblwiLFxuICAgIClcbiAgfSlcblxuICBpdChcImlnbm9yZXMgYmFzZSB3aGVuIHNpdGUgaXMgbm90IHNldFwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlbmRlclJvYm90cyh7IGJhc2U6IFwiL3JlcG9cIiB9KSkudG9CZShcIlVzZXItYWdlbnQ6ICpcXG5BbGxvdzogL1xcblwiKVxuICB9KVxuXG4gIGl0KFwicHV0cyB0aGUgY29udGVudCBzaWduYWxzIGluc2lkZSB0aGUgd2lsZGNhcmQgZ3JvdXBcIiwgKCkgPT4ge1xuICAgIGNvbnN0IGNvbnRlbnRTaWduYWxzID0geyBzZWFyY2g6IHRydWUsIGFpSW5wdXQ6IHRydWUsIGFpVHJhaW46IGZhbHNlIH1cbiAgICBleHBlY3QocmVuZGVyUm9ib3RzKHsgY29udGVudFNpZ25hbHMgfSkpLnRvQmUoXG4gICAgICBcIlVzZXItYWdlbnQ6ICpcXG5Db250ZW50LVNpZ25hbDogc2VhcmNoPXllcywgYWktaW5wdXQ9eWVzLCBhaS10cmFpbj1ub1xcbkFsbG93OiAvXFxuXCIsXG4gICAgKVxuICB9KVxufSlcbiJdfQ==
|
package/build/vite.config.js
CHANGED
|
@@ -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,
|
|
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.
|
|
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
|
|
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 {
|
|
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
|
+
}
|
package/source/helpers/head.ts
CHANGED
|
@@ -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:
|
|
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", () => {
|