@farm.js/core 0.1.35 → 0.1.36
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/{metadata-WGpBYcGn.d.mts → agent-mcp-BVExL8rZ.d.mts} +45 -2
- package/dist/agent-mcp-handler-LPJ2CADR.mjs +19 -0
- package/dist/agent-mcp-handler-SJAHFSPI.cjs +19 -0
- package/dist/{metadata-BnLDsUOS.d.ts → agent-mcp-luzfXK-P.d.ts} +45 -2
- package/dist/agent-runtime.cjs +5 -3
- package/dist/agent-runtime.d.mts +1 -1
- package/dist/agent-runtime.d.ts +1 -1
- package/dist/agent-runtime.mjs +4 -2
- package/dist/api/route-manager.cjs +6 -6
- package/dist/api/route-manager.d.mts +10 -4
- package/dist/api/route-manager.d.ts +10 -4
- package/dist/api/route-manager.mjs +5 -5
- package/dist/api/runtime.cjs +4 -4
- package/dist/api/runtime.d.mts +4 -62
- package/dist/api/runtime.d.ts +4 -62
- package/dist/api/runtime.mjs +3 -3
- package/dist/api.cjs +8 -8
- package/dist/api.d.mts +3 -3
- package/dist/api.d.ts +3 -3
- package/dist/api.mjs +7 -7
- package/dist/{app-D_j9AAVL.d.mts → app-BukwkFIu.d.mts} +17 -3
- package/dist/{app-BXB3JZYc.d.ts → app-Yfa2dsFy.d.ts} +17 -3
- package/dist/app-markdown.cjs +4 -4
- package/dist/app-markdown.d.mts +2 -2
- package/dist/app-markdown.d.ts +2 -2
- package/dist/app-markdown.mjs +3 -3
- package/dist/{chunk-4KU5Q7C2.mjs → chunk-276VJSM6.mjs} +221 -48
- package/dist/{chunk-PFQKNERH.cjs → chunk-2YTDKNH3.cjs} +364 -197
- package/dist/{chunk-UAORZ7QA.mjs → chunk-35GKZR22.mjs} +2 -2
- package/dist/{chunk-QAU5JD57.mjs → chunk-35WV46VL.mjs} +203 -36
- package/dist/{chunk-EFCQPQMG.cjs → chunk-42NHY7HW.cjs} +54 -4
- package/dist/{chunk-SOMHP6GF.mjs → chunk-4E5AEDKY.mjs} +2 -2
- package/dist/{chunk-UJD4PHYB.mjs → chunk-4E5WBPEG.mjs} +72 -354
- package/dist/{chunk-2G2NNLE2.mjs → chunk-4GLOOWKE.mjs} +4 -4
- package/dist/{chunk-62XKI4Z3.cjs → chunk-4NU4GGTC.cjs} +2 -2
- package/dist/{chunk-4JHOT46A.mjs → chunk-4WCZJQUO.mjs} +54 -4
- package/dist/chunk-4WQ6PGSJ.cjs +367 -0
- package/dist/{chunk-SMHH4VDG.mjs → chunk-4WVJLURN.mjs} +1 -1
- package/dist/chunk-6SJ2KJTA.mjs +314 -0
- package/dist/{chunk-IPXLADVO.cjs → chunk-ADEA7PN3.cjs} +122 -61
- package/dist/{chunk-CP6X4AEU.mjs → chunk-AJQTP4OO.mjs} +1 -1
- package/dist/{chunk-4MMGO7YW.cjs → chunk-AO6RYU5N.cjs} +31 -28
- package/dist/{chunk-GFHLGIYC.cjs → chunk-BO36SELV.cjs} +6 -6
- package/dist/{chunk-6TJOML4E.mjs → chunk-BPOF5U4N.mjs} +4 -4
- package/dist/{chunk-JHWOVJ3M.mjs → chunk-BUGNQIHC.mjs} +5 -0
- package/dist/{chunk-FTFYRMQQ.cjs → chunk-CBJJH36A.cjs} +50 -25
- package/dist/{chunk-7GLPRTTF.cjs → chunk-CGBDNNSM.cjs} +2 -2
- package/dist/chunk-CI6E3UOH.cjs +314 -0
- package/dist/{chunk-X6W5FFH6.cjs → chunk-D2BB47NB.cjs} +8 -8
- package/dist/chunk-DTSROJ5J.mjs +367 -0
- package/dist/{chunk-YC6V7PWE.cjs → chunk-DZ5ZVB2K.cjs} +1 -1
- package/dist/{chunk-DBNKI4NF.cjs → chunk-ECKF2BAM.cjs} +6 -2
- package/dist/{chunk-Q3VLETLE.mjs → chunk-EWET4D2Y.mjs} +1 -1
- package/dist/{chunk-HUMR6LSU.mjs → chunk-F5QO4FNE.mjs} +1 -1
- package/dist/{chunk-XBML5KMG.cjs → chunk-FO427P6I.cjs} +22 -12
- package/dist/{chunk-SAZVVAJH.mjs → chunk-GQRWBCWK.mjs} +8 -5
- package/dist/{chunk-F6Z3Y5KL.mjs → chunk-HOXTWRGB.mjs} +1 -1
- package/dist/{chunk-3BHT4WMP.cjs → chunk-JNUWCDTF.cjs} +6 -6
- package/dist/{chunk-73MC7ZL6.cjs → chunk-JZVADIOD.cjs} +2 -2
- package/dist/{chunk-OMVAU7NT.cjs → chunk-KIIMAWOJ.cjs} +11 -1
- package/dist/{chunk-43FMW62B.mjs → chunk-KMTGADN3.mjs} +28 -0
- package/dist/{chunk-6ETPZTJI.mjs → chunk-LSZAVSGY.mjs} +116 -55
- package/dist/{chunk-HMIJDL7S.mjs → chunk-MKYBIIAQ.mjs} +30 -5
- package/dist/{chunk-RU3M2FIK.cjs → chunk-MRMI32SN.cjs} +87 -369
- package/dist/{chunk-7L5V3KQO.cjs → chunk-N2NAS2AM.cjs} +2 -2
- package/dist/{chunk-ICUQICNC.cjs → chunk-NMHWYWDR.cjs} +654 -481
- package/dist/{chunk-G5RT7EXW.mjs → chunk-O6I6VWKK.mjs} +20 -5
- package/dist/{chunk-PDOCJ6RY.mjs → chunk-O6WAEAX3.mjs} +1 -1
- package/dist/{chunk-6DU6UFUE.mjs → chunk-OHLSEQ57.mjs} +16 -8
- package/dist/{chunk-3PXS2MHU.mjs → chunk-OO7GXVSR.mjs} +1 -1
- package/dist/chunk-PTZ2TVNE.cjs +178 -0
- package/dist/{chunk-6WCPV6G2.mjs → chunk-Q2KB32T4.mjs} +1 -1
- package/dist/{chunk-XCACDAZZ.cjs → chunk-S6VYG6XU.cjs} +23 -8
- package/dist/{chunk-VFQT3LAO.cjs → chunk-SDOTRX3V.cjs} +5 -5
- package/dist/{chunk-BMWDEBXO.cjs → chunk-SHLXACIO.cjs} +31 -3
- package/dist/{chunk-22LTEMGQ.mjs → chunk-T4OGRVNS.mjs} +13 -3
- package/dist/{chunk-ZXGAFZDO.cjs → chunk-T5OJPTVN.cjs} +2 -2
- package/dist/{chunk-VZJR7UTG.cjs → chunk-TE2G5ION.cjs} +2 -2
- package/dist/chunk-UH6QN5NL.mjs +178 -0
- package/dist/{chunk-F7JZENPF.cjs → chunk-UVW64BCB.cjs} +38 -30
- package/dist/{chunk-OKZUAXHF.mjs → chunk-VILIPRS6.mjs} +1 -1
- package/dist/{chunk-3UH7MRCP.cjs → chunk-VQLJWDVW.cjs} +13 -13
- package/dist/{chunk-VPTDD5XF.cjs → chunk-WBKEZXWE.cjs} +5 -0
- package/dist/{chunk-EJQT6KZN.cjs → chunk-WC5UV2HQ.cjs} +2 -2
- package/dist/{chunk-YULD7ICZ.mjs → chunk-WZ77PUAM.mjs} +1 -1
- package/dist/{chunk-C35ZBVRD.mjs → chunk-YWMZUGIS.mjs} +6 -2
- package/dist/{chunk-Y7RKTKYI.mjs → chunk-ZMMBIVQM.mjs} +10 -0
- package/dist/{chunk-ULAWS2PA.cjs → chunk-ZWM5KGAA.cjs} +6 -6
- package/dist/{client-plugins-CvIslxWi.d.mts → client-plugins-C3mK9Pol.d.mts} +98 -5
- package/dist/{client-plugins-HIyulDym.d.ts → client-plugins-r1mOtSC1.d.ts} +98 -5
- package/dist/client-plugins.d.mts +1 -1
- package/dist/client-plugins.d.ts +1 -1
- package/dist/client.d.mts +5 -5
- package/dist/client.d.ts +5 -5
- package/dist/config.d.mts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/{devtools-EE5RIBTH.mjs → devtools-ERIKFEXX.mjs} +9 -8
- package/dist/{devtools-EAUY43BS.cjs → devtools-JZJJY5CK.cjs} +10 -9
- package/dist/docs.cjs +12 -11
- package/dist/docs.d.mts +2 -2
- package/dist/docs.d.ts +2 -2
- package/dist/docs.mjs +11 -10
- package/dist/image/server.cjs +3 -3
- package/dist/image/server.mjs +2 -2
- package/dist/image/sharp.cjs +10 -10
- package/dist/image/sharp.mjs +2 -2
- package/dist/index.cjs +39 -35
- package/dist/index.d.mts +13 -13
- package/dist/index.d.ts +13 -13
- package/dist/index.mjs +38 -34
- package/dist/integrations.cjs +7 -3
- package/dist/integrations.d.mts +1 -1
- package/dist/integrations.d.ts +1 -1
- package/dist/integrations.mjs +6 -2
- package/dist/internal/app-markdown-runtime.cjs +3 -3
- package/dist/internal/app-markdown-runtime.d.mts +2 -2
- package/dist/internal/app-markdown-runtime.d.ts +2 -2
- package/dist/internal/app-markdown-runtime.mjs +2 -2
- package/dist/internal/build-runtime.cjs +27 -25
- package/dist/internal/build-runtime.d.mts +3 -3
- package/dist/internal/build-runtime.d.ts +3 -3
- package/dist/internal/build-runtime.mjs +26 -24
- package/dist/internal/cli-runtime.cjs +14 -13
- package/dist/internal/cli-runtime.mjs +13 -12
- package/dist/internal/client-error-overlay.cjs +4 -4
- package/dist/internal/client-error-overlay.mjs +1 -1
- package/dist/internal/compression-runtime.d.mts +1 -1
- package/dist/internal/compression-runtime.d.ts +1 -1
- package/dist/internal/config-runtime.cjs +10 -9
- package/dist/internal/config-runtime.d.mts +2 -2
- package/dist/internal/config-runtime.d.ts +2 -2
- package/dist/internal/config-runtime.mjs +9 -8
- package/dist/internal/isolated-boundary.cjs +2 -2
- package/dist/internal/isolated-boundary.d.mts +0 -1
- package/dist/internal/isolated-boundary.d.ts +0 -1
- package/dist/internal/isolated-boundary.mjs +1 -1
- package/dist/internal/metadata-image-runtime.cjs +3 -3
- package/dist/internal/metadata-image-runtime.mjs +2 -2
- package/dist/internal/product-telemetry-runtime.cjs +2 -2
- package/dist/internal/product-telemetry-runtime.mjs +1 -1
- package/dist/internal/production-runtime.cjs +23 -9
- package/dist/internal/production-runtime.d.mts +67 -27
- package/dist/internal/production-runtime.d.ts +67 -27
- package/dist/internal/production-runtime.mjs +26 -12
- package/dist/{layers-BRQF5ETU.mjs → layers-UTB43B7A.mjs} +1 -1
- package/dist/layers-VUWIOFEO.cjs +18 -0
- package/dist/markdown.cjs +1 -1
- package/dist/markdown.d.mts +1 -1
- package/dist/markdown.d.ts +1 -1
- package/dist/markdown.mjs +1 -1
- package/dist/middleware.cjs +6 -6
- package/dist/middleware.d.mts +3 -3
- package/dist/middleware.d.ts +3 -3
- package/dist/middleware.mjs +5 -5
- package/dist/navigation.d.mts +4 -4
- package/dist/navigation.d.ts +4 -4
- package/dist/{nitro-OVPLKPE2.cjs → nitro-7F65EZZS.cjs} +3 -3
- package/dist/{nitro-MXRDCXWZ.mjs → nitro-MPZJGPXK.mjs} +2 -2
- package/dist/plugin.cjs +23 -21
- package/dist/plugin.d.mts +1 -1
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.mjs +22 -20
- package/dist/{production-runtime-0sRnuYc-.d.mts → production-runtime-CCeVZXst.d.mts} +1 -1
- package/dist/{production-runtime-Crq6CJ-2.d.ts → production-runtime-DPnsggY3.d.ts} +1 -1
- package/dist/{render-plan-gs9GGPzw.d.mts → render-plan-BuGaPFe6.d.mts} +1 -1
- package/dist/{render-plan-8ozZh2Qv.d.ts → render-plan-Cw4f5E_R.d.ts} +1 -1
- package/dist/renderer/react/server.cjs +3 -3
- package/dist/renderer/react/server.mjs +1 -1
- package/dist/renderer-client.d.mts +3 -3
- package/dist/renderer-client.d.ts +3 -3
- package/dist/{request-B5G8rLBH.d.ts → request-Bkg-hERy.d.ts} +1 -1
- package/dist/{request-fkhiynXe.d.mts → request-CsGARcAw.d.mts} +1 -1
- package/dist/request.d.mts +2 -2
- package/dist/request.d.ts +2 -2
- package/dist/{route-rendering-lS5nUMua.d.ts → route-rendering-BUZMH_64.d.ts} +1 -1
- package/dist/{route-rendering-CLnS7AZj.d.mts → route-rendering-f11yh5dp.d.mts} +1 -1
- package/dist/{router-4iuiYnOW.d.ts → router-B8CR9GbD.d.ts} +1 -1
- package/dist/{router-ifU8Dui1.d.mts → router-Cp6yRE8A.d.mts} +1 -1
- package/dist/routes.d.mts +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/runtime-CAEv36S_.d.mts +69 -0
- package/dist/runtime-CQVsT330.d.ts +69 -0
- package/dist/server-action-security.cjs +3 -3
- package/dist/server-action-security.mjs +2 -2
- package/dist/server-fn.cjs +4 -4
- package/dist/server-fn.mjs +3 -3
- package/dist/server-plugins.d.mts +2 -2
- package/dist/server-plugins.d.ts +2 -2
- package/dist/server-query.cjs +5 -5
- package/dist/server-query.mjs +4 -4
- package/dist/server.cjs +42 -40
- package/dist/server.d.mts +7 -7
- package/dist/server.d.ts +7 -7
- package/dist/server.mjs +25 -23
- package/dist/{spa-router-DaJk5Re4.d.ts → spa-router-BuciWGCZ.d.ts} +2 -2
- package/dist/{spa-router-Bq7VCOwx.d.mts → spa-router-DwbqGkGj.d.mts} +2 -2
- package/dist/testing.cjs +8 -8
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.mjs +6 -6
- package/dist/{utils-XJn0c9bj.d.mts → utils-B9V0S_yL.d.mts} +1 -1
- package/dist/{utils-BXLmFsVR.d.ts → utils-CaoWKtYM.d.ts} +1 -1
- package/dist/version.cjs +2 -2
- package/dist/version.mjs +1 -1
- package/dist/vite.cjs +23 -21
- package/dist/vite.d.mts +1 -1
- package/dist/vite.d.ts +1 -1
- package/dist/vite.mjs +22 -20
- package/dist/workflows.cjs +3 -3
- package/dist/workflows.mjs +2 -2
- package/package.json +1 -1
- package/dist/layers-4PNE2HOY.cjs +0 -18
- package/dist/{chunk-2F2ER724.cjs → chunk-BPMATOP2.cjs} +2 -2
- package/dist/{chunk-IHNMMXHV.mjs → chunk-OTC5BDGO.mjs} +3 -3
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
var _chunkEUV4YR3Vcjs = require('./chunk-EUV4YR3V.cjs');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
var _chunkJ63ZH5OQcjs = require('./chunk-J63ZH5OQ.cjs');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
var _chunkI57LW5VZcjs = require('./chunk-I57LW5VZ.cjs');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
var _chunkS3JO3N4Hcjs = require('./chunk-S3JO3N4H.cjs');
|
|
15
|
+
|
|
16
|
+
// src/llms-txt.ts
|
|
17
|
+
function resolveFarmLlmsTxtConfig(input) {
|
|
18
|
+
if (!input) return { enabled: false, include: [], exclude: [], full: false };
|
|
19
|
+
if (input === true) return { enabled: true, include: [], exclude: [], full: true };
|
|
20
|
+
if (typeof input !== "object") {
|
|
21
|
+
throw new TypeError("agent.llmsTxt must be true, false, or an options object.");
|
|
22
|
+
}
|
|
23
|
+
if (input.enabled === false) return { enabled: false, include: [], exclude: [], full: false };
|
|
24
|
+
for (const key of ["title", "summary", "details"]) {
|
|
25
|
+
if (input[key] !== void 0 && typeof input[key] !== "string") {
|
|
26
|
+
throw new TypeError(`agent.llmsTxt.${key} must be a string.`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (input.full !== void 0 && typeof input.full !== "boolean") {
|
|
30
|
+
throw new TypeError("agent.llmsTxt.full must be a boolean.");
|
|
31
|
+
}
|
|
32
|
+
if (input.revalidate !== void 0 && (typeof input.revalidate !== "number" || !Number.isFinite(input.revalidate) || input.revalidate < 0)) {
|
|
33
|
+
throw new TypeError("agent.llmsTxt.revalidate must be a number of seconds.");
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
enabled: true,
|
|
37
|
+
...input.title ? { title: input.title } : {},
|
|
38
|
+
...input.summary ? { summary: input.summary } : {},
|
|
39
|
+
...input.details ? { details: input.details } : {},
|
|
40
|
+
include: resolveRoutePatterns(input.include, "include"),
|
|
41
|
+
exclude: resolveRoutePatterns(input.exclude, "exclude"),
|
|
42
|
+
full: input.full !== false,
|
|
43
|
+
...input.revalidate !== void 0 ? { revalidate: input.revalidate } : {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, resolveFarmLlmsTxtConfig, "resolveFarmLlmsTxtConfig");
|
|
47
|
+
function resolveRoutePatterns(value, key) {
|
|
48
|
+
if (value === void 0) return [];
|
|
49
|
+
if (!Array.isArray(value)) {
|
|
50
|
+
throw new TypeError(`agent.llmsTxt.${key} must be an array of route patterns.`);
|
|
51
|
+
}
|
|
52
|
+
return value.map((pattern, index) => {
|
|
53
|
+
if (typeof pattern !== "string" || !pattern.startsWith("/")) {
|
|
54
|
+
throw new TypeError(
|
|
55
|
+
`agent.llmsTxt.${key}[${index}] must be a route pattern starting with "/", such as "/docs/[...slug]".`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return pattern;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, resolveRoutePatterns, "resolveRoutePatterns");
|
|
62
|
+
function collectFarmLlmsTxtPages(sources, options) {
|
|
63
|
+
const include = _nullishCoalesce(options.include, () => ( []));
|
|
64
|
+
const exclude = _nullishCoalesce(options.exclude, () => ( []));
|
|
65
|
+
const pages = /* @__PURE__ */ new Map();
|
|
66
|
+
for (const source of sources) {
|
|
67
|
+
const path = source.pattern;
|
|
68
|
+
if (path.includes("[") || pages.has(path)) continue;
|
|
69
|
+
if (include.length && !include.some((pattern) => _chunkEUV4YR3Vcjs.matchesFarmMarkdownRoutePattern.call(void 0, pattern, path))) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (exclude.some((pattern) => _chunkEUV4YR3Vcjs.matchesFarmMarkdownRoutePattern.call(void 0, pattern, path))) continue;
|
|
73
|
+
if (isFarmMetadataNoIndex(source.metadata)) continue;
|
|
74
|
+
const target = _chunkEUV4YR3Vcjs.isFarmMarkdownMirrorExposed.call(void 0, options.markdown, path) ? _chunkEUV4YR3Vcjs.getFarmMarkdownMirrorPath.call(void 0, path) : path;
|
|
75
|
+
const description = readMetadataString(source.metadata, "description");
|
|
76
|
+
pages.set(path, {
|
|
77
|
+
path,
|
|
78
|
+
title: _nullishCoalesce(readMetadataTitle(source.metadata), () => ( fallbackPageTitle(path))),
|
|
79
|
+
url: `${options.origin}${_chunkI57LW5VZcjs.applyFarmBasePath.call(void 0, target, options.basePath)}`,
|
|
80
|
+
...description ? { description } : {}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return [...pages.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
84
|
+
}
|
|
85
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, collectFarmLlmsTxtPages, "collectFarmLlmsTxtPages");
|
|
86
|
+
function isFarmMetadataNoIndex(metadata) {
|
|
87
|
+
if (!metadata || typeof metadata !== "object") return false;
|
|
88
|
+
const robots = metadata.robots;
|
|
89
|
+
if (typeof robots === "string") {
|
|
90
|
+
return robots.split(",").map((directive) => directive.trim().toLowerCase()).some((directive) => directive === "noindex" || directive === "none");
|
|
91
|
+
}
|
|
92
|
+
return Boolean(
|
|
93
|
+
robots && typeof robots === "object" && robots.index === false
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, isFarmMetadataNoIndex, "isFarmMetadataNoIndex");
|
|
97
|
+
function createFarmDefaultLlmsTxt(options) {
|
|
98
|
+
const { config, pages } = options;
|
|
99
|
+
const mcp = _nullishCoalesce(options.mcp, () => ( []));
|
|
100
|
+
const summary = _nullishCoalesce(config.summary, () => ( readMetadataString(options.rootMetadata, "description")));
|
|
101
|
+
return {
|
|
102
|
+
title: _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(config.title, () => ( readMetadataTitle(options.rootMetadata))), () => ( _optionalChain([pages, 'access', _ => _.find, 'call', _2 => _2((page) => page.path === "/"), 'optionalAccess', _3 => _3.title]))), () => ( new URL(options.origin).host)),
|
|
103
|
+
...summary ? { summary } : {},
|
|
104
|
+
...config.details ? { details: config.details } : {},
|
|
105
|
+
sections: [
|
|
106
|
+
...pages.length ? [
|
|
107
|
+
{
|
|
108
|
+
title: "Pages",
|
|
109
|
+
links: pages.map(({ title, url, description }) => ({
|
|
110
|
+
title,
|
|
111
|
+
url,
|
|
112
|
+
...description ? { description } : {}
|
|
113
|
+
}))
|
|
114
|
+
}
|
|
115
|
+
] : [],
|
|
116
|
+
...mcp.length ? [{ title: "MCP", links: mcp }] : []
|
|
117
|
+
]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, createFarmDefaultLlmsTxt, "createFarmDefaultLlmsTxt");
|
|
121
|
+
function serializeFarmLlmsTxt(value) {
|
|
122
|
+
if (typeof value === "string") {
|
|
123
|
+
if (!value.trim()) throw new TypeError("llms.ts returned an empty llms.txt");
|
|
124
|
+
return value.endsWith("\n") ? value : `${value}
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
if (!isRecord(value)) {
|
|
128
|
+
throw new TypeError("llms.ts must return llms.txt text, an llms.txt object, or a Response");
|
|
129
|
+
}
|
|
130
|
+
const title = requireText(value.title, "llms.ts must return a non-empty title");
|
|
131
|
+
const summary = optionalText(value.summary, "llms.ts summary must be a string");
|
|
132
|
+
const details = optionalText(value.details, "llms.ts details must be a string");
|
|
133
|
+
if (value.sections !== void 0 && !Array.isArray(value.sections)) {
|
|
134
|
+
throw new TypeError("llms.ts sections must be an array");
|
|
135
|
+
}
|
|
136
|
+
const blocks = [`# ${toSingleLine(title)}`];
|
|
137
|
+
if (_optionalChain([summary, 'optionalAccess', _4 => _4.trim, 'call', _5 => _5()])) blocks.push(`> ${toSingleLine(summary)}`);
|
|
138
|
+
if (_optionalChain([details, 'optionalAccess', _6 => _6.trim, 'call', _7 => _7()])) blocks.push(details.trim());
|
|
139
|
+
for (const [sectionIndex, section] of (_nullishCoalesce(value.sections, () => ( []))).entries()) {
|
|
140
|
+
if (!isRecord(section)) {
|
|
141
|
+
throw new TypeError(`llms.ts section ${sectionIndex} must be an object`);
|
|
142
|
+
}
|
|
143
|
+
const sectionTitle = requireText(
|
|
144
|
+
section.title,
|
|
145
|
+
`llms.ts section ${sectionIndex} must include a non-empty title`
|
|
146
|
+
);
|
|
147
|
+
if (!Array.isArray(section.links)) {
|
|
148
|
+
throw new TypeError(`llms.ts section "${sectionTitle}" must include a links array`);
|
|
149
|
+
}
|
|
150
|
+
const links = section.links.map((link, linkIndex) => {
|
|
151
|
+
const where = `llms.ts section "${sectionTitle}" link ${linkIndex}`;
|
|
152
|
+
if (!isRecord(link)) throw new TypeError(`${where} must be an object`);
|
|
153
|
+
const linkTitle = requireText(link.title, `${where} must include a non-empty title`);
|
|
154
|
+
const url = requireText(link.url, `${where} must include a non-empty url`);
|
|
155
|
+
const description = optionalText(link.description, `${where} description must be a string`);
|
|
156
|
+
return `- [${escapeLinkText(linkTitle)}](${escapeLinkUrl(url)})${_optionalChain([description, 'optionalAccess', _8 => _8.trim, 'call', _9 => _9()]) ? `: ${toSingleLine(description)}` : ""}`;
|
|
157
|
+
});
|
|
158
|
+
blocks.push(`## ${toSingleLine(sectionTitle)}`, links.join("\n"));
|
|
159
|
+
}
|
|
160
|
+
return `${blocks.filter(Boolean).join("\n\n")}
|
|
161
|
+
`;
|
|
162
|
+
}
|
|
163
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, serializeFarmLlmsTxt, "serializeFarmLlmsTxt");
|
|
164
|
+
function createFarmLlmsMarkdownReader(fetchMarkdown) {
|
|
165
|
+
return async (url) => {
|
|
166
|
+
try {
|
|
167
|
+
const response = await fetchMarkdown(
|
|
168
|
+
new Request(url, {
|
|
169
|
+
headers: { accept: "text/markdown", [_chunkJ63ZH5OQcjs.FARM_INTERNAL_READ_HEADER]: "llms-full" }
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
const type = _nullishCoalesce(response.headers.get("content-type"), () => ( ""));
|
|
173
|
+
if (!response.ok || !type.toLowerCase().startsWith("text/markdown")) {
|
|
174
|
+
await _optionalChain([response, 'access', _10 => _10.body, 'optionalAccess', _11 => _11.cancel, 'call', _12 => _12()]);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
return await response.text();
|
|
178
|
+
} catch (e) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, createFarmLlmsMarkdownReader, "createFarmLlmsMarkdownReader");
|
|
184
|
+
var FARM_LLMS_FULL_PAGE_TIMEOUT_MS = 3e4;
|
|
185
|
+
var LLMS_PAGE_TIMED_OUT = /* @__PURE__ */ Symbol("farm.llms.pageTimedOut");
|
|
186
|
+
async function renderFarmLlmsFullTxt(value, options) {
|
|
187
|
+
const index = serializeFarmLlmsTxt(value);
|
|
188
|
+
if (typeof value === "string") return index;
|
|
189
|
+
const llms = value;
|
|
190
|
+
const links = [];
|
|
191
|
+
const seen = /* @__PURE__ */ new Set();
|
|
192
|
+
for (const section of _nullishCoalesce(llms.sections, () => ( []))) {
|
|
193
|
+
for (const link of section.links) {
|
|
194
|
+
if (seen.has(link.url)) continue;
|
|
195
|
+
seen.add(link.url);
|
|
196
|
+
links.push(link);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const pageTimeout = _nullishCoalesce(options.pageTimeout, () => ( FARM_LLMS_FULL_PAGE_TIMEOUT_MS));
|
|
200
|
+
const readWithinTimeout = /* @__PURE__ */ _chunkS3JO3N4Hcjs.__name.call(void 0, async (url) => {
|
|
201
|
+
let timer;
|
|
202
|
+
const timedOut = new Promise((resolve) => {
|
|
203
|
+
timer = setTimeout(resolve, pageTimeout, LLMS_PAGE_TIMED_OUT);
|
|
204
|
+
});
|
|
205
|
+
try {
|
|
206
|
+
const content = await Promise.race([options.readMarkdown(url), timedOut]);
|
|
207
|
+
if (content !== LLMS_PAGE_TIMED_OUT) return content;
|
|
208
|
+
_optionalChain([options, 'access', _13 => _13.onIncomplete, 'optionalCall', _14 => _14()]);
|
|
209
|
+
return null;
|
|
210
|
+
} finally {
|
|
211
|
+
clearTimeout(timer);
|
|
212
|
+
}
|
|
213
|
+
}, "readWithinTimeout");
|
|
214
|
+
const contents = await mapWithConcurrency(
|
|
215
|
+
links,
|
|
216
|
+
_nullishCoalesce(options.concurrency, () => ( 4)),
|
|
217
|
+
(link) => shouldInlineLlmsLink(link.url, options.origin) ? readWithinTimeout(link.url) : null
|
|
218
|
+
);
|
|
219
|
+
const blocks = [`# ${toSingleLine(llms.title)}`];
|
|
220
|
+
if (_optionalChain([llms, 'access', _15 => _15.summary, 'optionalAccess', _16 => _16.trim, 'call', _17 => _17()])) blocks.push(`> ${toSingleLine(llms.summary)}`);
|
|
221
|
+
if (_optionalChain([llms, 'access', _18 => _18.details, 'optionalAccess', _19 => _19.trim, 'call', _20 => _20()])) blocks.push(llms.details.trim());
|
|
222
|
+
const pages = links.map((link, index2) => {
|
|
223
|
+
const parts = [`## ${toSingleLine(link.title)}`, `URL: ${escapeLinkUrl(link.url)}`];
|
|
224
|
+
if (_optionalChain([link, 'access', _21 => _21.description, 'optionalAccess', _22 => _22.trim, 'call', _23 => _23()])) parts.push(toSingleLine(link.description));
|
|
225
|
+
const content = stripFrontmatter(_nullishCoalesce(contents[index2], () => ( ""))).trim();
|
|
226
|
+
if (content) parts.push(content);
|
|
227
|
+
return parts.join("\n\n");
|
|
228
|
+
});
|
|
229
|
+
if (pages.length) blocks.push(pages.join("\n\n---\n\n"));
|
|
230
|
+
return `${blocks.join("\n\n")}
|
|
231
|
+
`;
|
|
232
|
+
}
|
|
233
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, renderFarmLlmsFullTxt, "renderFarmLlmsFullTxt");
|
|
234
|
+
function createFarmLlmsFullRenderer(options = {}) {
|
|
235
|
+
const now = _nullishCoalesce(options.now, () => ( Date.now));
|
|
236
|
+
const ttl = (_nullishCoalesce(options.revalidate, () => ( 0))) * 1e3;
|
|
237
|
+
const maxEntries = _nullishCoalesce(options.maxEntries, () => ( 16));
|
|
238
|
+
const entries = /* @__PURE__ */ new Map();
|
|
239
|
+
let queue = Promise.resolve();
|
|
240
|
+
const settle = /* @__PURE__ */ _chunkS3JO3N4Hcjs.__name.call(void 0, (body, complete, onSettled) => onSettled ? body.then((value) => (onSettled(complete()), value)) : body, "settle");
|
|
241
|
+
return (origin, render, onSettled) => {
|
|
242
|
+
const entry = entries.get(origin);
|
|
243
|
+
if (entry && entry.expires > now()) return settle(entry.body, entry.complete, onSettled);
|
|
244
|
+
entries.delete(origin);
|
|
245
|
+
let complete = true;
|
|
246
|
+
const body = queue.then(
|
|
247
|
+
() => render({
|
|
248
|
+
incomplete: /* @__PURE__ */ _chunkS3JO3N4Hcjs.__name.call(void 0, () => {
|
|
249
|
+
complete = false;
|
|
250
|
+
}, "incomplete")
|
|
251
|
+
})
|
|
252
|
+
);
|
|
253
|
+
queue = body.catch(() => {
|
|
254
|
+
});
|
|
255
|
+
const pending = { body, expires: Number.POSITIVE_INFINITY, complete: /* @__PURE__ */ _chunkS3JO3N4Hcjs.__name.call(void 0, () => complete, "complete") };
|
|
256
|
+
entries.set(origin, pending);
|
|
257
|
+
if (entries.size > maxEntries) entries.delete(entries.keys().next().value);
|
|
258
|
+
body.then(
|
|
259
|
+
() => {
|
|
260
|
+
if (entries.get(origin) !== pending) return;
|
|
261
|
+
if (ttl > 0 && complete) pending.expires = now() + ttl;
|
|
262
|
+
else entries.delete(origin);
|
|
263
|
+
},
|
|
264
|
+
() => {
|
|
265
|
+
if (entries.get(origin) === pending) entries.delete(origin);
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
return settle(body, pending.complete, onSettled);
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, createFarmLlmsFullRenderer, "createFarmLlmsFullRenderer");
|
|
272
|
+
function stripFrontmatter(markdown) {
|
|
273
|
+
const match = /^\uFEFF?---\r?\n[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)/.exec(markdown);
|
|
274
|
+
return match ? markdown.slice(match[0].length) : markdown;
|
|
275
|
+
}
|
|
276
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, stripFrontmatter, "stripFrontmatter");
|
|
277
|
+
function shouldInlineLlmsLink(url, origin) {
|
|
278
|
+
let target;
|
|
279
|
+
try {
|
|
280
|
+
target = new URL(url, origin);
|
|
281
|
+
} catch (e2) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
return target.origin === origin && !/\/llms(-full)?\.txt$/.test(target.pathname);
|
|
285
|
+
}
|
|
286
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, shouldInlineLlmsLink, "shouldInlineLlmsLink");
|
|
287
|
+
async function mapWithConcurrency(items, limit, run) {
|
|
288
|
+
const results = [];
|
|
289
|
+
let next = 0;
|
|
290
|
+
const worker = /* @__PURE__ */ _chunkS3JO3N4Hcjs.__name.call(void 0, async () => {
|
|
291
|
+
while (next < items.length) {
|
|
292
|
+
const index = next++;
|
|
293
|
+
results[index] = await run(items[index]);
|
|
294
|
+
}
|
|
295
|
+
}, "worker");
|
|
296
|
+
await Promise.all(Array.from({ length: Math.min(Math.max(limit, 1), items.length) }, worker));
|
|
297
|
+
return results;
|
|
298
|
+
}
|
|
299
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, mapWithConcurrency, "mapWithConcurrency");
|
|
300
|
+
function readMetadataTitle(metadata) {
|
|
301
|
+
if (!isRecord(metadata)) return void 0;
|
|
302
|
+
const { title } = metadata;
|
|
303
|
+
if (typeof title === "string") return title.trim() || void 0;
|
|
304
|
+
if (isRecord(title)) {
|
|
305
|
+
for (const key of ["absolute", "default"]) {
|
|
306
|
+
const value = title[key];
|
|
307
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return void 0;
|
|
311
|
+
}
|
|
312
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, readMetadataTitle, "readMetadataTitle");
|
|
313
|
+
function readMetadataString(metadata, key) {
|
|
314
|
+
if (!isRecord(metadata)) return void 0;
|
|
315
|
+
const value = metadata[key];
|
|
316
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
317
|
+
}
|
|
318
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, readMetadataString, "readMetadataString");
|
|
319
|
+
function fallbackPageTitle(path) {
|
|
320
|
+
if (path === "/") return "Home";
|
|
321
|
+
const segments = path.split("/").filter(Boolean);
|
|
322
|
+
const segment = _nullishCoalesce(segments[segments.length - 1], () => ( path));
|
|
323
|
+
const words = segment.replace(/[-_]+/g, " ").trim();
|
|
324
|
+
return words ? words[0].toUpperCase() + words.slice(1) : path;
|
|
325
|
+
}
|
|
326
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, fallbackPageTitle, "fallbackPageTitle");
|
|
327
|
+
function toSingleLine(value) {
|
|
328
|
+
return value.replace(/\s+/g, " ").trim();
|
|
329
|
+
}
|
|
330
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, toSingleLine, "toSingleLine");
|
|
331
|
+
function escapeLinkText(value) {
|
|
332
|
+
return toSingleLine(value).replace(/[\\[\]]/g, "\\$&");
|
|
333
|
+
}
|
|
334
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, escapeLinkText, "escapeLinkText");
|
|
335
|
+
function escapeLinkUrl(value) {
|
|
336
|
+
return value.trim().replace(
|
|
337
|
+
/[\s()<>]/g,
|
|
338
|
+
(character) => character === "(" ? "%28" : character === ")" ? "%29" : encodeURIComponent(character)
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, escapeLinkUrl, "escapeLinkUrl");
|
|
342
|
+
function requireText(value, message) {
|
|
343
|
+
if (typeof value !== "string" || !value.trim()) throw new TypeError(message);
|
|
344
|
+
return value;
|
|
345
|
+
}
|
|
346
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, requireText, "requireText");
|
|
347
|
+
function optionalText(value, message) {
|
|
348
|
+
if (value === void 0) return void 0;
|
|
349
|
+
if (typeof value !== "string") throw new TypeError(message);
|
|
350
|
+
return value;
|
|
351
|
+
}
|
|
352
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, optionalText, "optionalText");
|
|
353
|
+
function isRecord(value) {
|
|
354
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
355
|
+
}
|
|
356
|
+
_chunkS3JO3N4Hcjs.__name.call(void 0, isRecord, "isRecord");
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
exports.resolveFarmLlmsTxtConfig = resolveFarmLlmsTxtConfig; exports.collectFarmLlmsTxtPages = collectFarmLlmsTxtPages; exports.isFarmMetadataNoIndex = isFarmMetadataNoIndex; exports.createFarmDefaultLlmsTxt = createFarmDefaultLlmsTxt; exports.serializeFarmLlmsTxt = serializeFarmLlmsTxt; exports.createFarmLlmsMarkdownReader = createFarmLlmsMarkdownReader; exports.renderFarmLlmsFullTxt = renderFarmLlmsFullTxt; exports.createFarmLlmsFullRenderer = createFarmLlmsFullRenderer;
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FARM_DEFAULT_MCP_DESCRIPTION,
|
|
3
|
+
FARM_DEFAULT_MCP_PROTOCOL_VERSIONS,
|
|
4
|
+
FARM_DEFAULT_MCP_TOOLS
|
|
5
|
+
} from "./chunk-UH6QN5NL.mjs";
|
|
6
|
+
import {
|
|
7
|
+
stripFarmBasePath
|
|
8
|
+
} from "./chunk-4RB44K6Z.mjs";
|
|
9
|
+
import {
|
|
10
|
+
__name
|
|
11
|
+
} from "./chunk-LPEX4YW6.mjs";
|
|
12
|
+
|
|
13
|
+
// src/agent-mcp-handler.ts
|
|
14
|
+
var INSTRUCTIONS = "This server reads the site's public pages. Call list_pages or search_pages to find a page, then read_page with its path to get the page as Markdown. It cannot change anything.";
|
|
15
|
+
var MAX_BODY_BYTES = 64 * 1024;
|
|
16
|
+
var MAX_BATCH = 16;
|
|
17
|
+
var READ_TIMEOUT_MS = 3e4;
|
|
18
|
+
var _FarmMCPRequestError = class _FarmMCPRequestError extends Error {
|
|
19
|
+
constructor(code, message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.code = code;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
__name(_FarmMCPRequestError, "FarmMCPRequestError");
|
|
25
|
+
var FarmMCPRequestError = _FarmMCPRequestError;
|
|
26
|
+
var PARSE_ERROR = -32700;
|
|
27
|
+
var INVALID_REQUEST = -32600;
|
|
28
|
+
var METHOD_NOT_FOUND = -32601;
|
|
29
|
+
var INVALID_PARAMS = -32602;
|
|
30
|
+
var INTERNAL_ERROR = -32603;
|
|
31
|
+
var TRANSPORT_ERROR = -32e3;
|
|
32
|
+
async function handleFarmDefaultMCPRequest(request, context) {
|
|
33
|
+
const method = request.method.toUpperCase();
|
|
34
|
+
if (method !== "POST") {
|
|
35
|
+
return transportError(405, "Method not allowed. Send MCP requests with POST.", {
|
|
36
|
+
Allow: "POST"
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const origin = request.headers.get("origin");
|
|
40
|
+
if (origin && !isSameOrigin(origin, request.url)) {
|
|
41
|
+
return transportError(403, "Cross-origin requests are not allowed.");
|
|
42
|
+
}
|
|
43
|
+
const protocolHeader = request.headers.get("mcp-protocol-version");
|
|
44
|
+
if (protocolHeader && !FARM_DEFAULT_MCP_PROTOCOL_VERSIONS.includes(protocolHeader.trim())) {
|
|
45
|
+
return transportError(
|
|
46
|
+
400,
|
|
47
|
+
`Unsupported MCP-Protocol-Version: ${protocolHeader.slice(0, 32)}. Supported: ${FARM_DEFAULT_MCP_PROTOCOL_VERSIONS.join(", ")}.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const contentType = request.headers.get("content-type") ?? "";
|
|
51
|
+
if (!/^application\/json\s*(?:;|$)/i.test(contentType.trim())) {
|
|
52
|
+
return transportError(415, "Content-Type must be application/json.");
|
|
53
|
+
}
|
|
54
|
+
const text = await readBody(request);
|
|
55
|
+
if (text === null) return transportError(413, "The request body is too large.");
|
|
56
|
+
let message;
|
|
57
|
+
try {
|
|
58
|
+
message = JSON.parse(text);
|
|
59
|
+
} catch {
|
|
60
|
+
return json(400, rpcError(null, { code: PARSE_ERROR, message: "Parse error." }));
|
|
61
|
+
}
|
|
62
|
+
if (Array.isArray(message)) {
|
|
63
|
+
if (message.length === 0 || message.length > MAX_BATCH) {
|
|
64
|
+
return json(
|
|
65
|
+
400,
|
|
66
|
+
rpcError(null, { code: INVALID_REQUEST, message: "Invalid batch of JSON-RPC messages." })
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const replies = (await Promise.all(message.map((item) => answerMessage(item, request, context)))).filter((reply2) => reply2 !== void 0);
|
|
70
|
+
return replies.length ? json(200, replies) : new Response(null, { status: 202 });
|
|
71
|
+
}
|
|
72
|
+
const reply = await answerMessage(message, request, context);
|
|
73
|
+
return reply === void 0 ? new Response(null, { status: 202 }) : json(200, reply);
|
|
74
|
+
}
|
|
75
|
+
__name(handleFarmDefaultMCPRequest, "handleFarmDefaultMCPRequest");
|
|
76
|
+
async function answerMessage(message, request, context) {
|
|
77
|
+
if (!isRecord(message) || message.jsonrpc !== "2.0") {
|
|
78
|
+
return rpcError(readId(message), { code: INVALID_REQUEST, message: "Invalid Request." });
|
|
79
|
+
}
|
|
80
|
+
const hasId = "id" in message;
|
|
81
|
+
if (typeof message.method !== "string") {
|
|
82
|
+
if (hasId && ("result" in message || "error" in message)) return void 0;
|
|
83
|
+
return rpcError(readId(message), { code: INVALID_REQUEST, message: "Invalid Request." });
|
|
84
|
+
}
|
|
85
|
+
if (!hasId) return void 0;
|
|
86
|
+
const id = readId(message);
|
|
87
|
+
if (id === void 0) {
|
|
88
|
+
return rpcError(null, { code: INVALID_REQUEST, message: "Invalid Request id." });
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const result = await dispatch(message.method, message.params, request, context);
|
|
92
|
+
return { jsonrpc: "2.0", id, result };
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error instanceof FarmMCPRequestError) {
|
|
95
|
+
return rpcError(id, { code: error.code, message: error.message });
|
|
96
|
+
}
|
|
97
|
+
return rpcError(id, { code: INTERNAL_ERROR, message: "Internal error." });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
__name(answerMessage, "answerMessage");
|
|
101
|
+
async function dispatch(method, params, request, context) {
|
|
102
|
+
switch (method) {
|
|
103
|
+
case "initialize": {
|
|
104
|
+
const requested = isRecord(params) ? params.protocolVersion : void 0;
|
|
105
|
+
const protocolVersion = typeof requested === "string" && FARM_DEFAULT_MCP_PROTOCOL_VERSIONS.includes(requested) ? requested : FARM_DEFAULT_MCP_PROTOCOL_VERSIONS[0];
|
|
106
|
+
return {
|
|
107
|
+
protocolVersion,
|
|
108
|
+
capabilities: { tools: { listChanged: false } },
|
|
109
|
+
serverInfo: {
|
|
110
|
+
name: context.server.name,
|
|
111
|
+
version: context.server.version,
|
|
112
|
+
...context.server.title ? { title: context.server.title } : {},
|
|
113
|
+
description: FARM_DEFAULT_MCP_DESCRIPTION
|
|
114
|
+
},
|
|
115
|
+
instructions: INSTRUCTIONS
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
case "ping":
|
|
119
|
+
return {};
|
|
120
|
+
case "tools/list":
|
|
121
|
+
return { tools: FARM_DEFAULT_MCP_TOOLS };
|
|
122
|
+
case "tools/call":
|
|
123
|
+
return callTool(params, request, context);
|
|
124
|
+
default:
|
|
125
|
+
throw new FarmMCPRequestError(METHOD_NOT_FOUND, `Method not found: ${method.slice(0, 64)}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
__name(dispatch, "dispatch");
|
|
129
|
+
async function callTool(params, request, context) {
|
|
130
|
+
if (!isRecord(params) || typeof params.name !== "string") {
|
|
131
|
+
throw new FarmMCPRequestError(INVALID_PARAMS, "tools/call needs a tool name.");
|
|
132
|
+
}
|
|
133
|
+
const tool = FARM_DEFAULT_MCP_TOOLS.find(({ name }) => name === params.name);
|
|
134
|
+
if (!tool) {
|
|
135
|
+
throw new FarmMCPRequestError(INVALID_PARAMS, `Unknown tool: ${params.name.slice(0, 64)}`);
|
|
136
|
+
}
|
|
137
|
+
const args = params.arguments ?? {};
|
|
138
|
+
if (!isRecord(args)) return toolError("Arguments must be an object.");
|
|
139
|
+
const allowed = new Set(Object.keys(tool.inputSchema.properties));
|
|
140
|
+
const unknown = Object.keys(args).find((key) => !allowed.has(key));
|
|
141
|
+
if (unknown) return toolError(`Unknown argument: ${unknown.slice(0, 64)}.`);
|
|
142
|
+
if (tool.name === "list_pages") return pagesResult(await context.listPages());
|
|
143
|
+
if (tool.name === "search_pages") {
|
|
144
|
+
const query = args.query;
|
|
145
|
+
if (typeof query !== "string" || !query.trim() || query.length > 200) {
|
|
146
|
+
return toolError("query must be a non-empty string of at most 200 characters.");
|
|
147
|
+
}
|
|
148
|
+
const limit = args.limit ?? 10;
|
|
149
|
+
if (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1 || limit > 50) {
|
|
150
|
+
return toolError("limit must be a whole number from 1 to 50.");
|
|
151
|
+
}
|
|
152
|
+
return pagesResult(searchPages(await context.listPages(), query, limit));
|
|
153
|
+
}
|
|
154
|
+
const path = args.path;
|
|
155
|
+
if (typeof path !== "string" || !path.trim() || path.length > 2048) {
|
|
156
|
+
return toolError('path must be a page path such as "/about".');
|
|
157
|
+
}
|
|
158
|
+
const pagePath = toPagePath(path, request.url, context.basePath);
|
|
159
|
+
const pages = await context.listPages();
|
|
160
|
+
const page = pagePath ? pages.find((candidate) => candidate.path === pagePath) : void 0;
|
|
161
|
+
if (!page) {
|
|
162
|
+
return toolError(
|
|
163
|
+
`No public page at ${JSON.stringify(path.slice(0, 200))}. Call list_pages for the pages this server can read.`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
const markdown = await readWithTimeout(context.readMarkdown, page.markdownUrl);
|
|
167
|
+
if (markdown === null) {
|
|
168
|
+
return toolError(`${page.path} did not answer with Markdown. Try again later.`);
|
|
169
|
+
}
|
|
170
|
+
return { content: [{ type: "text", text: markdown }] };
|
|
171
|
+
}
|
|
172
|
+
__name(callTool, "callTool");
|
|
173
|
+
function pagesResult(pages) {
|
|
174
|
+
const structured = {
|
|
175
|
+
pages: pages.map(({ path, title, url, description }) => ({
|
|
176
|
+
path,
|
|
177
|
+
title,
|
|
178
|
+
url,
|
|
179
|
+
...description ? { description } : {}
|
|
180
|
+
}))
|
|
181
|
+
};
|
|
182
|
+
return {
|
|
183
|
+
content: [{ type: "text", text: JSON.stringify(structured) }],
|
|
184
|
+
structuredContent: structured
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
__name(pagesResult, "pagesResult");
|
|
188
|
+
function toolError(text) {
|
|
189
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
190
|
+
}
|
|
191
|
+
__name(toolError, "toolError");
|
|
192
|
+
function searchPages(pages, query, limit) {
|
|
193
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean).slice(0, 16);
|
|
194
|
+
return pages.map((page) => {
|
|
195
|
+
const title = page.title.toLowerCase();
|
|
196
|
+
const rest = `${page.description ?? ""} ${page.path}`.toLowerCase();
|
|
197
|
+
let score = 0;
|
|
198
|
+
for (const term of terms) {
|
|
199
|
+
if (title.includes(term)) score += 2;
|
|
200
|
+
else if (rest.includes(term)) score += 1;
|
|
201
|
+
else return null;
|
|
202
|
+
}
|
|
203
|
+
return { page, score };
|
|
204
|
+
}).filter((match) => match !== null).sort(
|
|
205
|
+
(left, right) => right.score - left.score || left.page.path.localeCompare(right.page.path)
|
|
206
|
+
).slice(0, limit).map(({ page }) => page);
|
|
207
|
+
}
|
|
208
|
+
__name(searchPages, "searchPages");
|
|
209
|
+
function toPagePath(input, requestUrl, basePath) {
|
|
210
|
+
let url;
|
|
211
|
+
try {
|
|
212
|
+
url = new URL(input.trim(), requestUrl);
|
|
213
|
+
} catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
if (url.origin !== new URL(requestUrl).origin) return null;
|
|
217
|
+
let path = url.pathname;
|
|
218
|
+
try {
|
|
219
|
+
path = decodeURI(path);
|
|
220
|
+
} catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
path = stripFarmBasePath(path, basePath);
|
|
224
|
+
if (path === "/index.md") return "/";
|
|
225
|
+
if (path.toLowerCase().endsWith(".md")) path = path.slice(0, -3) || "/";
|
|
226
|
+
return path.length > 1 ? path.replace(/\/+$/, "") || "/" : path;
|
|
227
|
+
}
|
|
228
|
+
__name(toPagePath, "toPagePath");
|
|
229
|
+
async function readWithTimeout(read, url) {
|
|
230
|
+
let timer;
|
|
231
|
+
const timedOut = new Promise((resolve) => {
|
|
232
|
+
timer = setTimeout(resolve, READ_TIMEOUT_MS, null);
|
|
233
|
+
});
|
|
234
|
+
try {
|
|
235
|
+
return await Promise.race([read(url), timedOut]);
|
|
236
|
+
} finally {
|
|
237
|
+
clearTimeout(timer);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
__name(readWithTimeout, "readWithTimeout");
|
|
241
|
+
async function readBody(request) {
|
|
242
|
+
const declared = Number(request.headers.get("content-length"));
|
|
243
|
+
if (Number.isFinite(declared) && declared > MAX_BODY_BYTES) {
|
|
244
|
+
await request.body?.cancel().catch(() => {
|
|
245
|
+
});
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
if (!request.body) return "";
|
|
249
|
+
const reader = request.body.getReader();
|
|
250
|
+
const chunks = [];
|
|
251
|
+
let size = 0;
|
|
252
|
+
for (; ; ) {
|
|
253
|
+
const { done, value } = await reader.read();
|
|
254
|
+
if (done) break;
|
|
255
|
+
size += value.byteLength;
|
|
256
|
+
if (size > MAX_BODY_BYTES) {
|
|
257
|
+
await reader.cancel().catch(() => {
|
|
258
|
+
});
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
chunks.push(value);
|
|
262
|
+
}
|
|
263
|
+
const body = new Uint8Array(size);
|
|
264
|
+
let offset = 0;
|
|
265
|
+
for (const chunk of chunks) {
|
|
266
|
+
body.set(chunk, offset);
|
|
267
|
+
offset += chunk.byteLength;
|
|
268
|
+
}
|
|
269
|
+
return new TextDecoder().decode(body);
|
|
270
|
+
}
|
|
271
|
+
__name(readBody, "readBody");
|
|
272
|
+
function isSameOrigin(origin, requestUrl) {
|
|
273
|
+
try {
|
|
274
|
+
return new URL(origin).host === new URL(requestUrl).host;
|
|
275
|
+
} catch {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
__name(isSameOrigin, "isSameOrigin");
|
|
280
|
+
function readId(message) {
|
|
281
|
+
if (!isRecord(message)) return null;
|
|
282
|
+
const { id } = message;
|
|
283
|
+
return typeof id === "string" || typeof id === "number" && Number.isFinite(id) || id === null ? id : void 0;
|
|
284
|
+
}
|
|
285
|
+
__name(readId, "readId");
|
|
286
|
+
function rpcError(id, error) {
|
|
287
|
+
return { jsonrpc: "2.0", id: id ?? null, error };
|
|
288
|
+
}
|
|
289
|
+
__name(rpcError, "rpcError");
|
|
290
|
+
function json(status, body) {
|
|
291
|
+
return new Response(JSON.stringify(body), {
|
|
292
|
+
status,
|
|
293
|
+
headers: {
|
|
294
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
295
|
+
"Cache-Control": "no-store",
|
|
296
|
+
"X-Content-Type-Options": "nosniff"
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
__name(json, "json");
|
|
301
|
+
function transportError(status, message, headers = {}) {
|
|
302
|
+
const response = json(status, rpcError(null, { code: TRANSPORT_ERROR, message }));
|
|
303
|
+
for (const [name, value] of Object.entries(headers)) response.headers.set(name, value);
|
|
304
|
+
return response;
|
|
305
|
+
}
|
|
306
|
+
__name(transportError, "transportError");
|
|
307
|
+
function isRecord(value) {
|
|
308
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
309
|
+
}
|
|
310
|
+
__name(isRecord, "isRecord");
|
|
311
|
+
|
|
312
|
+
export {
|
|
313
|
+
handleFarmDefaultMCPRequest
|
|
314
|
+
};
|