@farming-labs/docs 0.2.62 → 0.2.64
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/dist/agent-CQTH7NFu.mjs +624 -0
- package/dist/agent-DKKptIgy.mjs +4365 -0
- package/dist/agent-evals-B7MIxuEX.mjs +2144 -0
- package/dist/agent-export-CBgWgPvH.mjs +910 -0
- package/dist/agent-scope-C_U--OZ7.mjs +283 -0
- package/dist/agent-skills-bundle.d.mts +13 -0
- package/dist/agent-skills-bundle.mjs +12 -0
- package/dist/agent-skills-server-CPja6Syt.d.mts +14 -0
- package/dist/agent-skills-server-DraIb6FV.mjs +415 -0
- package/dist/agent-skills-vite.d.mts +31 -0
- package/dist/agent-skills-vite.mjs +70 -0
- package/dist/agents-XWZBub6f.mjs +221 -0
- package/dist/analytics-Bx44lg6d.mjs +177 -0
- package/dist/cli/index.d.mts +15 -0
- package/dist/cli/index.mjs +452 -0
- package/dist/client/react.d.mts +45 -0
- package/dist/client/react.mjs +223 -0
- package/dist/cloud-analytics-CSyFE6SS.mjs +132 -0
- package/dist/cloud-ask-ai-sbpjOR2K.mjs +382 -0
- package/dist/cloud-ask-ai-zpwkdwnF.d.mts +23 -0
- package/dist/cloud-pdNC-tyj.mjs +1615 -0
- package/dist/code-blocks-DnNVNK2M.mjs +871 -0
- package/dist/codeblocks-CFuurVIH.mjs +250 -0
- package/dist/config-Wcdj-D0a.mjs +369 -0
- package/dist/dev-Cmy6DtdF.mjs +1333 -0
- package/dist/docs-cloud-server.d.mts +70 -0
- package/dist/docs-cloud-server.mjs +310 -0
- package/dist/doctor-DtGYZ41i.mjs +2036 -0
- package/dist/downgrade-w7e6Se0L.mjs +184 -0
- package/dist/errors-DbOhkE1h.mjs +20 -0
- package/dist/golden-evaluations-Dj-9Eo3v.mjs +1785 -0
- package/dist/i18n-CCaFUnAN.mjs +40 -0
- package/dist/index.d.mts +1150 -0
- package/dist/index.mjs +10 -0
- package/dist/init-CQY0Woe3.mjs +1264 -0
- package/dist/mcp-B9dcsivk.mjs +156 -0
- package/dist/mcp.d.mts +298 -0
- package/dist/mcp.mjs +4430 -0
- package/dist/metadata-DWExHQnx.mjs +237 -0
- package/dist/package-version-n5AFur8a.mjs +128 -0
- package/dist/reading-time-CYZ5VvKU.mjs +742 -0
- package/dist/review-CLoHTywU.mjs +673 -0
- package/dist/robots-BIpC4j4P.mjs +201 -0
- package/dist/robots-CUTahhoY.mjs +179 -0
- package/dist/search-B6V6qtiI.mjs +1826 -0
- package/dist/search-CaSyi6H6.d.mts +279 -0
- package/dist/search-DSjCeOk7.mjs +104 -0
- package/dist/server.d.mts +343 -0
- package/dist/server.mjs +14 -0
- package/dist/sitemap-Cykpe3Tz.mjs +249 -0
- package/dist/sitemap-server-C_6Wes83.mjs +1137 -0
- package/dist/standards-discovery-C4HUqMd2.d.mts +227 -0
- package/dist/standards-discovery-jkykaXq1.mjs +519 -0
- package/dist/templates-Bq_P7ctv.mjs +2465 -0
- package/dist/types-lMBIdZg0.d.mts +3315 -0
- package/dist/upgrade-oz-GChgt.mjs +56 -0
- package/dist/utils-DpiIioYb.mjs +225 -0
- package/package.json +1 -1
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { T as buildDocsPublishedAgentSkill, u as DEFAULT_AGENT_SKILLS_ROUTE_PREFIX } from "./standards-discovery-jkykaXq1.mjs";
|
|
2
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { access, lstat, readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
6
|
+
import { gzip, gzipSync } from "node:zlib";
|
|
7
|
+
|
|
8
|
+
//#region src/agent-skills-server.ts
|
|
9
|
+
const COMPANION_DIRECTORIES = new Set([
|
|
10
|
+
"references",
|
|
11
|
+
"scripts",
|
|
12
|
+
"assets"
|
|
13
|
+
]);
|
|
14
|
+
const IGNORED_COLLECTION_DIRECTORIES = new Set([".git", "node_modules"]);
|
|
15
|
+
const MAX_COLLECTION_DEPTH = 12;
|
|
16
|
+
const MAX_FILES_PER_SKILL = 256;
|
|
17
|
+
const MAX_FILE_BYTES = 1024 * 1024;
|
|
18
|
+
const MAX_SKILL_BYTES = 8 * 1024 * 1024;
|
|
19
|
+
function isInside(parent, candidate) {
|
|
20
|
+
const relative = path.relative(parent, candidate);
|
|
21
|
+
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
22
|
+
}
|
|
23
|
+
function findWorkspaceRoot(rootDir) {
|
|
24
|
+
let current = realpathSync(rootDir);
|
|
25
|
+
for (;;) {
|
|
26
|
+
if (existsSync(path.join(current, ".git")) || existsSync(path.join(current, "pnpm-workspace.yaml")) || existsSync(path.join(current, "pnpm-workspace.yml"))) return current;
|
|
27
|
+
const parent = path.dirname(current);
|
|
28
|
+
if (parent === current) return realpathSync(rootDir);
|
|
29
|
+
current = parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function pathExists(candidate) {
|
|
33
|
+
try {
|
|
34
|
+
await access(candidate);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function findWorkspaceRootAsync(rootDir) {
|
|
41
|
+
const resolvedRoot = await realpath(rootDir);
|
|
42
|
+
let current = resolvedRoot;
|
|
43
|
+
for (;;) {
|
|
44
|
+
if (await pathExists(path.join(current, ".git")) || await pathExists(path.join(current, "pnpm-workspace.yaml")) || await pathExists(path.join(current, "pnpm-workspace.yml"))) return current;
|
|
45
|
+
const parent = path.dirname(current);
|
|
46
|
+
if (parent === current) return resolvedRoot;
|
|
47
|
+
current = parent;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function normalizeConfiguredPaths(input) {
|
|
51
|
+
if (!input) return [];
|
|
52
|
+
const value = typeof input === "string" || Array.isArray(input) ? input : input.paths;
|
|
53
|
+
return (typeof value === "string" ? [value] : [...value]).map((candidate) => candidate.trim()).filter(Boolean);
|
|
54
|
+
}
|
|
55
|
+
function resolveSafeConfiguredPath(configuredPath, rootDir, workspaceRoot) {
|
|
56
|
+
const candidate = path.resolve(rootDir, configuredPath);
|
|
57
|
+
if (!existsSync(candidate)) throw new Error(`Configured Agent Skill path does not exist: ${configuredPath}`);
|
|
58
|
+
if (lstatSync(candidate).isSymbolicLink()) throw new Error(`Configured Agent Skill paths may not be symlinks: ${configuredPath}`);
|
|
59
|
+
const resolved = realpathSync(candidate);
|
|
60
|
+
if (!isInside(workspaceRoot, resolved)) throw new Error(`Agent Skill symlink escapes the workspace: ${configuredPath}`);
|
|
61
|
+
return resolved;
|
|
62
|
+
}
|
|
63
|
+
async function resolveSafeConfiguredPathAsync(configuredPath, rootDir, workspaceRoot) {
|
|
64
|
+
const candidate = path.resolve(rootDir, configuredPath);
|
|
65
|
+
if (!await pathExists(candidate)) throw new Error(`Configured Agent Skill path does not exist: ${configuredPath}`);
|
|
66
|
+
if ((await lstat(candidate)).isSymbolicLink()) throw new Error(`Configured Agent Skill paths may not be symlinks: ${configuredPath}`);
|
|
67
|
+
const resolved = await realpath(candidate);
|
|
68
|
+
if (!isInside(workspaceRoot, resolved)) throw new Error(`Agent Skill symlink escapes the workspace: ${configuredPath}`);
|
|
69
|
+
return resolved;
|
|
70
|
+
}
|
|
71
|
+
function collectSkillDocuments(candidate, workspaceRoot, results, visited, depth = 0) {
|
|
72
|
+
if (depth > MAX_COLLECTION_DEPTH) throw new Error(`Agent Skill collection exceeds ${MAX_COLLECTION_DEPTH} directory levels.`);
|
|
73
|
+
const resolved = realpathSync(candidate);
|
|
74
|
+
if (!isInside(workspaceRoot, resolved)) throw new Error(`Agent Skill symlink escapes the workspace: ${candidate}`);
|
|
75
|
+
if (visited.has(resolved)) return;
|
|
76
|
+
visited.add(resolved);
|
|
77
|
+
const info = statSync(resolved);
|
|
78
|
+
if (info.isFile()) {
|
|
79
|
+
if (path.basename(resolved) !== "SKILL.md") throw new Error(`Agent Skill file must be named SKILL.md: ${candidate}`);
|
|
80
|
+
results.add(resolved);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!info.isDirectory()) throw new Error(`Agent Skill path must be a regular file or directory: ${candidate}`);
|
|
84
|
+
const directSkill = path.join(resolved, "SKILL.md");
|
|
85
|
+
if (existsSync(directSkill)) {
|
|
86
|
+
const directInfo = lstatSync(directSkill);
|
|
87
|
+
if (!directInfo.isFile() || directInfo.isSymbolicLink()) throw new Error(`Agent Skill SKILL.md must be a non-symlink regular file: ${directSkill}`);
|
|
88
|
+
results.add(directSkill);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
for (const entry of readdirSync(resolved, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
92
|
+
if (IGNORED_COLLECTION_DIRECTORIES.has(entry.name) || entry.name.startsWith(".") || COMPANION_DIRECTORIES.has(entry.name)) continue;
|
|
93
|
+
const entryPath = path.join(resolved, entry.name);
|
|
94
|
+
if (entry.isSymbolicLink()) throw new Error(`Agent Skill collections may not contain symlinks: ${entryPath}`);
|
|
95
|
+
else if (entry.isDirectory() || entry.isFile()) {
|
|
96
|
+
if (entry.isDirectory() || entry.name === "SKILL.md") collectSkillDocuments(entryPath, workspaceRoot, results, visited, depth + 1);
|
|
97
|
+
} else throw new Error(`Unsafe filesystem entry in Agent Skill collection: ${entryPath}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function collectSkillDocumentsAsync(candidate, workspaceRoot, results, visited, depth = 0) {
|
|
101
|
+
if (depth > MAX_COLLECTION_DEPTH) throw new Error(`Agent Skill collection exceeds ${MAX_COLLECTION_DEPTH} directory levels.`);
|
|
102
|
+
const resolved = await realpath(candidate);
|
|
103
|
+
if (!isInside(workspaceRoot, resolved)) throw new Error(`Agent Skill symlink escapes the workspace: ${candidate}`);
|
|
104
|
+
if (visited.has(resolved)) return;
|
|
105
|
+
visited.add(resolved);
|
|
106
|
+
const info = await stat(resolved);
|
|
107
|
+
if (info.isFile()) {
|
|
108
|
+
if (path.basename(resolved) !== "SKILL.md") throw new Error(`Agent Skill file must be named SKILL.md: ${candidate}`);
|
|
109
|
+
results.add(resolved);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!info.isDirectory()) throw new Error(`Agent Skill path must be a regular file or directory: ${candidate}`);
|
|
113
|
+
const directSkill = path.join(resolved, "SKILL.md");
|
|
114
|
+
if (await pathExists(directSkill)) {
|
|
115
|
+
const directInfo = await lstat(directSkill);
|
|
116
|
+
if (!directInfo.isFile() || directInfo.isSymbolicLink()) throw new Error(`Agent Skill SKILL.md must be a non-symlink regular file: ${directSkill}`);
|
|
117
|
+
results.add(directSkill);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const entries = await readdir(resolved, { withFileTypes: true });
|
|
121
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
122
|
+
if (IGNORED_COLLECTION_DIRECTORIES.has(entry.name) || entry.name.startsWith(".") || COMPANION_DIRECTORIES.has(entry.name)) continue;
|
|
123
|
+
const entryPath = path.join(resolved, entry.name);
|
|
124
|
+
if (entry.isSymbolicLink()) throw new Error(`Agent Skill collections may not contain symlinks: ${entryPath}`);
|
|
125
|
+
else if (entry.isDirectory() || entry.isFile()) {
|
|
126
|
+
if (entry.isDirectory() || entry.name === "SKILL.md") await collectSkillDocumentsAsync(entryPath, workspaceRoot, results, visited, depth + 1);
|
|
127
|
+
} else throw new Error(`Unsafe filesystem entry in Agent Skill collection: ${entryPath}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function encodeSkillFileUrl(name, relativePath) {
|
|
131
|
+
const encodedPath = relativePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
132
|
+
return `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/${encodeURIComponent(name)}/${encodedPath}`;
|
|
133
|
+
}
|
|
134
|
+
function mediaTypeFor(relativePath) {
|
|
135
|
+
const extension = path.extname(relativePath).toLowerCase();
|
|
136
|
+
if ([".md", ".mdx"].includes(extension)) return "text/markdown";
|
|
137
|
+
if ([
|
|
138
|
+
".txt",
|
|
139
|
+
".sh",
|
|
140
|
+
".bash",
|
|
141
|
+
".zsh",
|
|
142
|
+
".py",
|
|
143
|
+
".js",
|
|
144
|
+
".mjs",
|
|
145
|
+
".cjs",
|
|
146
|
+
".ts",
|
|
147
|
+
".tsx",
|
|
148
|
+
".jsx",
|
|
149
|
+
".css",
|
|
150
|
+
".html",
|
|
151
|
+
".svg",
|
|
152
|
+
".csv",
|
|
153
|
+
".yaml",
|
|
154
|
+
".yml"
|
|
155
|
+
].includes(extension)) return "text/plain";
|
|
156
|
+
if (extension === ".json") return "application/json";
|
|
157
|
+
if (extension === ".png") return "image/png";
|
|
158
|
+
if ([".jpg", ".jpeg"].includes(extension)) return "image/jpeg";
|
|
159
|
+
if (extension === ".gif") return "image/gif";
|
|
160
|
+
if (extension === ".webp") return "image/webp";
|
|
161
|
+
if (extension === ".pdf") return "application/pdf";
|
|
162
|
+
return "application/octet-stream";
|
|
163
|
+
}
|
|
164
|
+
function hashBytes(content) {
|
|
165
|
+
return createHash("sha256").update(content).digest("hex");
|
|
166
|
+
}
|
|
167
|
+
function preserveFileBytes(content, mediaType) {
|
|
168
|
+
if (mediaType.startsWith("text/") || mediaType === "application/json") {
|
|
169
|
+
const text = content.toString("utf8");
|
|
170
|
+
if (Buffer.from(text, "utf8").equals(content)) return text;
|
|
171
|
+
}
|
|
172
|
+
return new Uint8Array(content);
|
|
173
|
+
}
|
|
174
|
+
function compareSkillFilePath(left, right) {
|
|
175
|
+
if (left.path === "SKILL.md") return -1;
|
|
176
|
+
if (right.path === "SKILL.md") return 1;
|
|
177
|
+
return left.path.localeCompare(right.path);
|
|
178
|
+
}
|
|
179
|
+
function readCompanionFiles(skillDir, name, rootSkillBytes) {
|
|
180
|
+
const files = [];
|
|
181
|
+
let totalBytes = rootSkillBytes;
|
|
182
|
+
function visit(absoluteDir, relativeDir, depth = 0) {
|
|
183
|
+
if (depth > MAX_COLLECTION_DEPTH) throw new Error(`Agent Skill companion tree exceeds ${MAX_COLLECTION_DEPTH} levels: ${skillDir}`);
|
|
184
|
+
for (const entry of readdirSync(absoluteDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
185
|
+
const absolutePath = path.join(absoluteDir, entry.name);
|
|
186
|
+
const relativePath = path.posix.join(relativeDir, entry.name);
|
|
187
|
+
if (entry.isSymbolicLink()) throw new Error(`Agent Skill companion files may not be symlinks: ${absolutePath}`);
|
|
188
|
+
if (entry.isDirectory()) {
|
|
189
|
+
visit(absolutePath, relativePath, depth + 1);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (!entry.isFile()) throw new Error(`Unsafe filesystem entry in Agent Skill: ${absolutePath}`);
|
|
193
|
+
const executable = (statSync(absolutePath).mode & 73) !== 0;
|
|
194
|
+
const content = readFileSync(absolutePath);
|
|
195
|
+
if (content.byteLength > MAX_FILE_BYTES) throw new Error(`Agent Skill file exceeds ${MAX_FILE_BYTES} bytes: ${absolutePath}`);
|
|
196
|
+
totalBytes += content.byteLength;
|
|
197
|
+
if (totalBytes > MAX_SKILL_BYTES || files.length >= MAX_FILES_PER_SKILL - 1) throw new Error(`Agent Skill exceeds safe publication limits: ${skillDir}`);
|
|
198
|
+
const mediaType = mediaTypeFor(relativePath);
|
|
199
|
+
const value = preserveFileBytes(content, mediaType);
|
|
200
|
+
const sha256 = hashBytes(value);
|
|
201
|
+
files.push({
|
|
202
|
+
path: relativePath,
|
|
203
|
+
url: encodeSkillFileUrl(name, relativePath),
|
|
204
|
+
mediaType,
|
|
205
|
+
content: value,
|
|
206
|
+
sha256,
|
|
207
|
+
digest: `sha256:${sha256}`,
|
|
208
|
+
executable
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const directory of [...COMPANION_DIRECTORIES].sort()) {
|
|
213
|
+
const absoluteDir = path.join(skillDir, directory);
|
|
214
|
+
if (!existsSync(absoluteDir)) continue;
|
|
215
|
+
const info = lstatSync(absoluteDir);
|
|
216
|
+
if (info.isSymbolicLink() || !info.isDirectory()) throw new Error(`Agent Skill ${directory}/ must be a non-symlink directory: ${absoluteDir}`);
|
|
217
|
+
visit(absoluteDir, directory);
|
|
218
|
+
}
|
|
219
|
+
return files;
|
|
220
|
+
}
|
|
221
|
+
async function readCompanionFilesAsync(skillDir, name, rootSkillBytes) {
|
|
222
|
+
const files = [];
|
|
223
|
+
let totalBytes = rootSkillBytes;
|
|
224
|
+
async function visit(absoluteDir, relativeDir, depth = 0) {
|
|
225
|
+
if (depth > MAX_COLLECTION_DEPTH) throw new Error(`Agent Skill companion tree exceeds ${MAX_COLLECTION_DEPTH} levels: ${skillDir}`);
|
|
226
|
+
const entries = await readdir(absoluteDir, { withFileTypes: true });
|
|
227
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
228
|
+
const absolutePath = path.join(absoluteDir, entry.name);
|
|
229
|
+
const relativePath = path.posix.join(relativeDir, entry.name);
|
|
230
|
+
if (entry.isSymbolicLink()) throw new Error(`Agent Skill companion files may not be symlinks: ${absolutePath}`);
|
|
231
|
+
if (entry.isDirectory()) {
|
|
232
|
+
await visit(absolutePath, relativePath, depth + 1);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (!entry.isFile()) throw new Error(`Unsafe filesystem entry in Agent Skill: ${absolutePath}`);
|
|
236
|
+
const executable = ((await stat(absolutePath)).mode & 73) !== 0;
|
|
237
|
+
const content = await readFile(absolutePath);
|
|
238
|
+
if (content.byteLength > MAX_FILE_BYTES) throw new Error(`Agent Skill file exceeds ${MAX_FILE_BYTES} bytes: ${absolutePath}`);
|
|
239
|
+
totalBytes += content.byteLength;
|
|
240
|
+
if (totalBytes > MAX_SKILL_BYTES || files.length >= MAX_FILES_PER_SKILL - 1) throw new Error(`Agent Skill exceeds safe publication limits: ${skillDir}`);
|
|
241
|
+
const mediaType = mediaTypeFor(relativePath);
|
|
242
|
+
const value = preserveFileBytes(content, mediaType);
|
|
243
|
+
const sha256 = hashBytes(value);
|
|
244
|
+
files.push({
|
|
245
|
+
path: relativePath,
|
|
246
|
+
url: encodeSkillFileUrl(name, relativePath),
|
|
247
|
+
mediaType,
|
|
248
|
+
content: value,
|
|
249
|
+
sha256,
|
|
250
|
+
digest: `sha256:${sha256}`,
|
|
251
|
+
executable
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
for (const directory of [...COMPANION_DIRECTORIES].sort()) {
|
|
256
|
+
const absoluteDir = path.join(skillDir, directory);
|
|
257
|
+
if (!await pathExists(absoluteDir)) continue;
|
|
258
|
+
const info = await lstat(absoluteDir);
|
|
259
|
+
if (info.isSymbolicLink() || !info.isDirectory()) throw new Error(`Agent Skill ${directory}/ must be a non-symlink directory: ${absoluteDir}`);
|
|
260
|
+
await visit(absoluteDir, directory);
|
|
261
|
+
}
|
|
262
|
+
return files;
|
|
263
|
+
}
|
|
264
|
+
function writeTarString(header, offset, length, value) {
|
|
265
|
+
const encoded = Buffer.from(value, "utf8");
|
|
266
|
+
if (encoded.byteLength > length) throw new Error(`Agent Skill archive path is too long: ${value}`);
|
|
267
|
+
header.set(encoded, offset);
|
|
268
|
+
}
|
|
269
|
+
function writeTarOctal(header, offset, length, value) {
|
|
270
|
+
writeTarString(header, offset, length, value.toString(8).padStart(length - 1, "0"));
|
|
271
|
+
}
|
|
272
|
+
function createTarHeader(name, size, executable) {
|
|
273
|
+
const header = new Uint8Array(512);
|
|
274
|
+
writeTarString(header, 0, 100, name);
|
|
275
|
+
writeTarOctal(header, 100, 8, executable ? 493 : 420);
|
|
276
|
+
writeTarOctal(header, 108, 8, 0);
|
|
277
|
+
writeTarOctal(header, 116, 8, 0);
|
|
278
|
+
writeTarOctal(header, 124, 12, size);
|
|
279
|
+
writeTarOctal(header, 136, 12, 0);
|
|
280
|
+
header.fill(32, 148, 156);
|
|
281
|
+
header[156] = "0".charCodeAt(0);
|
|
282
|
+
writeTarString(header, 257, 6, "ustar");
|
|
283
|
+
writeTarString(header, 263, 2, "00");
|
|
284
|
+
writeTarString(header, 148, 8, `${header.reduce((sum, byte) => sum + byte, 0).toString(8).padStart(6, "0")}\0 `);
|
|
285
|
+
return header;
|
|
286
|
+
}
|
|
287
|
+
function createDeterministicTar(files) {
|
|
288
|
+
const chunks = [];
|
|
289
|
+
let total = 0;
|
|
290
|
+
for (const file of [...files].sort(compareSkillFilePath)) {
|
|
291
|
+
const content = typeof file.content === "string" ? Buffer.from(file.content) : file.content;
|
|
292
|
+
const header = createTarHeader(file.path, content.byteLength, file.executable === true);
|
|
293
|
+
chunks.push(header, content);
|
|
294
|
+
total += header.byteLength + content.byteLength;
|
|
295
|
+
const padding = (512 - content.byteLength % 512) % 512;
|
|
296
|
+
if (padding) {
|
|
297
|
+
chunks.push(new Uint8Array(padding));
|
|
298
|
+
total += padding;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
chunks.push(new Uint8Array(1024));
|
|
302
|
+
total += 1024;
|
|
303
|
+
const tar = new Uint8Array(total);
|
|
304
|
+
let offset = 0;
|
|
305
|
+
for (const chunk of chunks) {
|
|
306
|
+
tar.set(chunk, offset);
|
|
307
|
+
offset += chunk.byteLength;
|
|
308
|
+
}
|
|
309
|
+
return tar;
|
|
310
|
+
}
|
|
311
|
+
function normalizeGzipArchive(content) {
|
|
312
|
+
const archive = new Uint8Array(content);
|
|
313
|
+
if (archive.byteLength > 9) archive[9] = 255;
|
|
314
|
+
return archive;
|
|
315
|
+
}
|
|
316
|
+
function createDeterministicTarGzip(files) {
|
|
317
|
+
return normalizeGzipArchive(gzipSync(createDeterministicTar(files), { level: 9 }));
|
|
318
|
+
}
|
|
319
|
+
function createDeterministicTarGzipAsync(files) {
|
|
320
|
+
return new Promise((resolve, reject) => {
|
|
321
|
+
gzip(createDeterministicTar(files), { level: 9 }, (error, content) => {
|
|
322
|
+
if (error) reject(error);
|
|
323
|
+
else resolve(normalizeGzipArchive(content));
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
function publishSkillDocumentSync(skillPath) {
|
|
328
|
+
const skillDir = path.dirname(skillPath);
|
|
329
|
+
const skillDocument = readFileSync(skillPath, "utf8");
|
|
330
|
+
const skillDocumentBytes = Buffer.byteLength(skillDocument, "utf8");
|
|
331
|
+
if (skillDocumentBytes > MAX_FILE_BYTES) throw new Error(`Agent Skill SKILL.md exceeds ${MAX_FILE_BYTES} bytes: ${skillPath}`);
|
|
332
|
+
const base = buildDocsPublishedAgentSkill(skillDocument, hashBytes(skillDocument));
|
|
333
|
+
if (base.name !== path.basename(skillDir)) throw new Error(`Agent Skill frontmatter name "${base.name}" must match its directory "${path.basename(skillDir)}".`);
|
|
334
|
+
const companions = readCompanionFiles(skillDir, base.name, skillDocumentBytes);
|
|
335
|
+
const files = [...base.files, ...companions].sort(compareSkillFilePath);
|
|
336
|
+
if (companions.length === 0) return {
|
|
337
|
+
...base,
|
|
338
|
+
files
|
|
339
|
+
};
|
|
340
|
+
const content = createDeterministicTarGzip(files);
|
|
341
|
+
const sha256 = hashBytes(content);
|
|
342
|
+
return {
|
|
343
|
+
name: base.name,
|
|
344
|
+
type: "archive",
|
|
345
|
+
description: base.description,
|
|
346
|
+
url: `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/${encodeURIComponent(base.name)}.tar.gz`,
|
|
347
|
+
digest: `sha256:${sha256}`,
|
|
348
|
+
content,
|
|
349
|
+
sha256,
|
|
350
|
+
skillDocument,
|
|
351
|
+
files
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
async function publishSkillDocumentAsync(skillPath) {
|
|
355
|
+
const skillDir = path.dirname(skillPath);
|
|
356
|
+
const skillDocument = await readFile(skillPath, "utf8");
|
|
357
|
+
const skillDocumentBytes = Buffer.byteLength(skillDocument, "utf8");
|
|
358
|
+
if (skillDocumentBytes > MAX_FILE_BYTES) throw new Error(`Agent Skill SKILL.md exceeds ${MAX_FILE_BYTES} bytes: ${skillPath}`);
|
|
359
|
+
const base = buildDocsPublishedAgentSkill(skillDocument, hashBytes(skillDocument));
|
|
360
|
+
if (base.name !== path.basename(skillDir)) throw new Error(`Agent Skill frontmatter name "${base.name}" must match its directory "${path.basename(skillDir)}".`);
|
|
361
|
+
const companions = await readCompanionFilesAsync(skillDir, base.name, skillDocumentBytes);
|
|
362
|
+
const files = [...base.files, ...companions].sort(compareSkillFilePath);
|
|
363
|
+
if (companions.length === 0) return {
|
|
364
|
+
...base,
|
|
365
|
+
files
|
|
366
|
+
};
|
|
367
|
+
const content = await createDeterministicTarGzipAsync(files);
|
|
368
|
+
const sha256 = hashBytes(content);
|
|
369
|
+
return {
|
|
370
|
+
name: base.name,
|
|
371
|
+
type: "archive",
|
|
372
|
+
description: base.description,
|
|
373
|
+
url: `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/${encodeURIComponent(base.name)}.tar.gz`,
|
|
374
|
+
digest: `sha256:${sha256}`,
|
|
375
|
+
content,
|
|
376
|
+
sha256,
|
|
377
|
+
skillDocument,
|
|
378
|
+
files
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function validateAndSortSkills(published) {
|
|
382
|
+
const names = /* @__PURE__ */ new Set();
|
|
383
|
+
for (const skill of published) {
|
|
384
|
+
if (names.has(skill.name)) throw new Error(`Duplicate configured Agent Skill name: ${skill.name}`);
|
|
385
|
+
names.add(skill.name);
|
|
386
|
+
}
|
|
387
|
+
return published.sort((left, right) => left.name.localeCompare(right.name));
|
|
388
|
+
}
|
|
389
|
+
/** Resolve and safely package all project skills configured through `agent.skills` synchronously. */
|
|
390
|
+
function resolveConfiguredAgentSkillsSync(input, options = {}) {
|
|
391
|
+
const configuredPaths = normalizeConfiguredPaths(input);
|
|
392
|
+
if (configuredPaths.length === 0) return [];
|
|
393
|
+
const rootDir = realpathSync(options.rootDir ?? process.cwd());
|
|
394
|
+
const workspaceRoot = realpathSync(options.workspaceRoot ?? findWorkspaceRoot(rootDir));
|
|
395
|
+
if (!isInside(workspaceRoot, rootDir)) throw new Error("Agent Skill rootDir must stay inside the configured workspace root.");
|
|
396
|
+
const documents = /* @__PURE__ */ new Set();
|
|
397
|
+
const visited = /* @__PURE__ */ new Set();
|
|
398
|
+
for (const configuredPath of configuredPaths) collectSkillDocuments(resolveSafeConfiguredPath(configuredPath, rootDir, workspaceRoot), workspaceRoot, documents, visited);
|
|
399
|
+
return validateAndSortSkills([...documents].sort().map(publishSkillDocumentSync));
|
|
400
|
+
}
|
|
401
|
+
/** Resolve and package configured skills without blocking runtime filesystem or compression work. */
|
|
402
|
+
async function resolveConfiguredAgentSkills(input, options = {}) {
|
|
403
|
+
const configuredPaths = normalizeConfiguredPaths(input);
|
|
404
|
+
if (configuredPaths.length === 0) return [];
|
|
405
|
+
const rootDir = await realpath(options.rootDir ?? process.cwd());
|
|
406
|
+
const workspaceRoot = options.workspaceRoot === void 0 ? await findWorkspaceRootAsync(rootDir) : await realpath(options.workspaceRoot);
|
|
407
|
+
if (!isInside(workspaceRoot, rootDir)) throw new Error("Agent Skill rootDir must stay inside the configured workspace root.");
|
|
408
|
+
const documents = /* @__PURE__ */ new Set();
|
|
409
|
+
const visited = /* @__PURE__ */ new Set();
|
|
410
|
+
for (const configuredPath of configuredPaths) await collectSkillDocumentsAsync(await resolveSafeConfiguredPathAsync(configuredPath, rootDir, workspaceRoot), workspaceRoot, documents, visited);
|
|
411
|
+
return validateAndSortSkills(await Promise.all([...documents].sort().map(publishSkillDocumentAsync)));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
//#endregion
|
|
415
|
+
export { resolveConfiguredAgentSkillsSync as n, resolveConfiguredAgentSkills as t };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { A as DocsPublishedAgentSkill } from "./standards-discovery-C4HUqMd2.mjs";
|
|
2
|
+
import { P as DocsAgentSkillsInput } from "./types-lMBIdZg0.mjs";
|
|
3
|
+
import { t as ResolveConfiguredAgentSkillsOptions } from "./agent-skills-server-CPja6Syt.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/agent-skills-vite.d.ts
|
|
6
|
+
declare const DOCS_AGENT_SKILLS_BUNDLE_MODULE = "@farming-labs/docs/agent-skills-bundle";
|
|
7
|
+
interface DocsAgentSkillsBuildConfig {
|
|
8
|
+
agent?: {
|
|
9
|
+
skills?: DocsAgentSkillsInput;
|
|
10
|
+
};
|
|
11
|
+
rootDir?: string;
|
|
12
|
+
}
|
|
13
|
+
interface DocsAgentSkillsBuildPluginOptions extends ResolveConfiguredAgentSkillsOptions {}
|
|
14
|
+
/** Minimal Rollup/Vite plugin surface, kept dependency-free for framework configs. */
|
|
15
|
+
interface DocsAgentSkillsBuildPlugin {
|
|
16
|
+
name: string;
|
|
17
|
+
enforce: "pre";
|
|
18
|
+
resolveId(source: string): string | undefined;
|
|
19
|
+
load(id: string): Promise<string | undefined>;
|
|
20
|
+
}
|
|
21
|
+
/** Render a self-contained virtual module without relying on Node at runtime. */
|
|
22
|
+
declare function renderDocsAgentSkillsBundle(skills: readonly DocsPublishedAgentSkill[]): string;
|
|
23
|
+
/**
|
|
24
|
+
* Snapshot configured Agent Skills into the server bundle.
|
|
25
|
+
*
|
|
26
|
+
* The same plugin object works in Vite, Astro/Svelte/TanStack, and Nitro's
|
|
27
|
+
* Rollup build. Paths remain relative to the project root unless overridden.
|
|
28
|
+
*/
|
|
29
|
+
declare function docsAgentSkills(config: DocsAgentSkillsBuildConfig, options?: DocsAgentSkillsBuildPluginOptions): DocsAgentSkillsBuildPlugin;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { DOCS_AGENT_SKILLS_BUNDLE_MODULE, DocsAgentSkillsBuildPlugin, DocsAgentSkillsBuildPluginOptions, docsAgentSkills, renderDocsAgentSkillsBundle };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import "./standards-discovery-jkykaXq1.mjs";
|
|
2
|
+
import { t as resolveConfiguredAgentSkills } from "./agent-skills-server-DraIb6FV.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/agent-skills-vite.ts
|
|
5
|
+
const DOCS_AGENT_SKILLS_BUNDLE_MODULE = "@farming-labs/docs/agent-skills-bundle";
|
|
6
|
+
const RESOLVED_BUNDLE_MODULE = "\0farming-labs:agent-skills-bundle";
|
|
7
|
+
function isBundleModuleId(id) {
|
|
8
|
+
if (id === RESOLVED_BUNDLE_MODULE) return true;
|
|
9
|
+
const cleanId = id.split("?", 1)[0].replaceAll("\\", "/");
|
|
10
|
+
return /(?:^|\/)agent-skills-bundle\.(?:[cm]?js|ts)$/.test(cleanId);
|
|
11
|
+
}
|
|
12
|
+
function serializeContent(content) {
|
|
13
|
+
return typeof content === "string" ? { text: content } : { base64: Buffer.from(content).toString("base64") };
|
|
14
|
+
}
|
|
15
|
+
function serializeSkill(skill) {
|
|
16
|
+
return {
|
|
17
|
+
...skill,
|
|
18
|
+
content: serializeContent(skill.content),
|
|
19
|
+
files: skill.files.map((file) => ({
|
|
20
|
+
...file,
|
|
21
|
+
content: serializeContent(file.content)
|
|
22
|
+
}))
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Render a self-contained virtual module without relying on Node at runtime. */
|
|
26
|
+
function renderDocsAgentSkillsBundle(skills) {
|
|
27
|
+
return `const snapshot = ${JSON.stringify(skills.map(serializeSkill)).replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029")};
|
|
28
|
+
function decodeBase64(value) {
|
|
29
|
+
const binary = globalThis.atob(value);
|
|
30
|
+
const bytes = new Uint8Array(binary.length);
|
|
31
|
+
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
|
|
32
|
+
return bytes;
|
|
33
|
+
}
|
|
34
|
+
function hydrateContent(content) {
|
|
35
|
+
return typeof content.text === "string" ? content.text : decodeBase64(content.base64);
|
|
36
|
+
}
|
|
37
|
+
export const bundledAgentSkills = snapshot.map((skill) => ({
|
|
38
|
+
...skill,
|
|
39
|
+
content: hydrateContent(skill.content),
|
|
40
|
+
files: skill.files.map((file) => ({ ...file, content: hydrateContent(file.content) })),
|
|
41
|
+
}));
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Snapshot configured Agent Skills into the server bundle.
|
|
46
|
+
*
|
|
47
|
+
* The same plugin object works in Vite, Astro/Svelte/TanStack, and Nitro's
|
|
48
|
+
* Rollup build. Paths remain relative to the project root unless overridden.
|
|
49
|
+
*/
|
|
50
|
+
function docsAgentSkills(config, options = {}) {
|
|
51
|
+
let source;
|
|
52
|
+
return {
|
|
53
|
+
name: "farming-labs-agent-skills",
|
|
54
|
+
enforce: "pre",
|
|
55
|
+
resolveId(id) {
|
|
56
|
+
return id === DOCS_AGENT_SKILLS_BUNDLE_MODULE ? RESOLVED_BUNDLE_MODULE : void 0;
|
|
57
|
+
},
|
|
58
|
+
async load(id) {
|
|
59
|
+
if (!isBundleModuleId(id)) return void 0;
|
|
60
|
+
source ??= resolveConfiguredAgentSkills(config.agent?.skills, {
|
|
61
|
+
rootDir: options.rootDir ?? config.rootDir ?? process.cwd(),
|
|
62
|
+
workspaceRoot: options.workspaceRoot
|
|
63
|
+
}).then(renderDocsAgentSkillsBundle);
|
|
64
|
+
return source;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { DOCS_AGENT_SKILLS_BUNDLE_MODULE, docsAgentSkills, renderDocsAgentSkillsBundle };
|