@farming-labs/docs 0.2.114 → 0.2.116
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-5jyEosML.mjs → agent-CEZCaYto.mjs} +52 -7
- package/dist/{agent-Dlswu0Mm.mjs → agent-DGNTD-N6.mjs} +3 -3
- package/dist/{agent-evals-CnQC3W2a.mjs → agent-evals-CmeTjDMC.mjs} +3 -3
- package/dist/{agent-export-yilcRMg6.mjs → agent-export-DIKwVWYN.mjs} +11 -10
- package/dist/agent-skills-bundle.d.mts +8 -2
- package/dist/agent-skills-bundle.mjs +7 -1
- package/dist/{agent-skills-progressive-disclosure-DL-q4JZo.mjs → agent-skills-progressive-disclosure-BDSjGGFd.mjs} +1 -1
- package/dist/{agent-skills-server-CF6iVTBR.d.mts → agent-skills-server-BWRJjIQG.d.mts} +2 -2
- package/dist/{agent-skills-server-Ddbf15pY.mjs → agent-skills-server-Ddm90WL6.mjs} +1 -1
- package/dist/agent-skills-vite.d.mts +4 -4
- package/dist/agent-skills-vite.mjs +7 -3
- package/dist/{agents-D5waM-jZ.mjs → agents-l8RtkFim.mjs} +10 -9
- package/dist/{analytics-CwXxM9oL.d.mts → analytics-R9c-Md4b.d.mts} +1 -1
- package/dist/api-reference-0XdEWya8.mjs +3033 -0
- package/dist/{authoring-CsR0sewh.mjs → authoring-CvsCmYuq.mjs} +4 -4
- package/dist/{authoring-mcp-BpwboE3k.mjs → authoring-mcp-C35lj3cI.mjs} +2 -2
- package/dist/browser.d.mts +3 -3
- package/dist/cli/index.mjs +75 -34
- package/dist/client/react.d.mts +1 -1
- package/dist/{cloud-Bvof6mET.mjs → cloud-DtmNQJg2.mjs} +3 -3
- package/dist/{cloud-ask-ai-C9dvEBpW.d.mts → cloud-ask-ai-DLyrydIR.d.mts} +1 -1
- package/dist/{codeblocks-C911CdyJ.mjs → codeblocks-DEcqNXve.mjs} +2 -2
- package/dist/{content-change-hydration-CDuohygf.d.mts → content-change-hydration-BWJNUIot.d.mts} +33 -5
- package/dist/{dev-Bx0JNglW.mjs → dev-Df77AKR1.mjs} +9 -24
- package/dist/docs-cloud-server.d.mts +2 -2
- package/dist/docs-cloud-server.mjs +1 -1
- package/dist/{doctor-DbsNvdb8.mjs → doctor-zNmt9s6j.mjs} +266 -17
- package/dist/{downgrade-BXmQMkMy.mjs → downgrade-C6DSsOO9.mjs} +2 -2
- package/dist/{feedback-DWVUMrxy.mjs → feedback-CNsQP2jY.mjs} +1 -1
- package/dist/{golden-evaluations-D49vhw5u.mjs → golden-evaluations-B1QDOzYj.mjs} +6 -2
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +5 -5
- package/dist/{init-CCV2eRNB.mjs → init-BXKzipSS.mjs} +3 -3
- package/dist/{mcp-D-1u1RBW.mjs → mcp-Bj3ZoEtt.mjs} +33 -19
- package/dist/mcp.d.mts +12 -4
- package/dist/mcp.mjs +50 -8
- package/dist/openapi-anOaQn9o.mjs +20 -0
- package/dist/openapi-diff-DKW3xkQU.mjs +461 -0
- package/dist/{openapi-mcp-D8NVpTW1.mjs → openapi-mcp-UF4Cx885.mjs} +43 -91
- package/dist/openapi-operations-DFpaqOqA.mjs +324 -0
- package/dist/openapi-sdk-DqRHPsCb.mjs +415 -0
- package/dist/openapi-source-CiKbUK71.mjs +37 -0
- package/dist/{package-version-Ci99L0yo.mjs → package-version-CTkii52k.mjs} +1 -1
- package/dist/{prompt-references-CV1zw72C.mjs → prompt-references-Cficetgk.mjs} +2 -2
- package/dist/{retrieval-digest-CBhsp9LK.d.mts → retrieval-digest-PD4fFy7P.d.mts} +2 -2
- package/dist/{review-BdM27yh7.mjs → review-CcplK9Hj.mjs} +9 -9
- package/dist/{robots-Bne9ODlC.mjs → robots-BPYWIGqD.mjs} +1 -1
- package/dist/{robots-D6VufwPn.mjs → robots-D4T3zZr_.mjs} +4 -4
- package/dist/{search-BCxmGlxh.mjs → search-BNmlDQJ2.mjs} +27 -11
- package/dist/server.d.mts +132 -9
- package/dist/server.mjs +12 -10
- package/dist/{sidebar-Bne72SU-.d.mts → sidebar-CGbvEPTX.d.mts} +1 -1
- package/dist/{sitemap-ChOEBnrk.mjs → sitemap-Bc4LkIcm.mjs} +10 -9
- package/dist/sitemap-server-C4bpuoLi.mjs +235 -0
- package/dist/{skills-qswruIKO.mjs → skills-B6wl1bUL.mjs} +10 -9
- package/dist/{standards-discovery-CjyUCxdy.d.mts → standards-discovery-C1RLKwV9.d.mts} +1 -1
- package/dist/{templates-DdB4BBGq.mjs → templates-BiORflC3.mjs} +1 -2
- package/dist/{types-BkAk6B2d.d.mts → types-Dkl2DUfp.d.mts} +39 -9
- package/dist/{upgrade-B92U83xo.mjs → upgrade-8zStRidw.mjs} +2 -2
- package/package.json +2 -1
- package/dist/sitemap-server-D3mFJkjE.mjs +0 -1166
- /package/dist/{agent-feedback-loop-vaC-M1sx.mjs → agent-feedback-loop-CcZL_sb1.mjs} +0 -0
- /package/dist/{audience-exposure-CkNZNeVX.d.mts → audience-exposure-BAjHjaTT.d.mts} +0 -0
- /package/dist/{cloud-ask-ai-CK2qlNns.mjs → cloud-ask-ai-BDtBbbZZ.mjs} +0 -0
- /package/dist/{code-blocks-D90CCJFs.mjs → code-blocks-GYdvS3BH.mjs} +0 -0
- /package/dist/{config-DrZ3fXgf.mjs → config-BnUOoj07.mjs} +0 -0
- /package/dist/{errors-BDmra6mz.mjs → errors-BkS29NUA.mjs} +0 -0
- /package/dist/{feedback-evals-k0sFDdms.mjs → feedback-evals-CYuS2wTu.mjs} +0 -0
- /package/dist/{openapi-mcp-node-vO3IXVGI.mjs → openapi-mcp-node-C4jUpZG6.mjs} +0 -0
- /package/dist/{propose-DJOiHu9Y.mjs → propose-ZH_mGMY_.mjs} +0 -0
- /package/dist/{utils-Cc1SrVza.mjs → utils-c9arebCi.mjs} +0 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import "./markdown-sections-BKy4labo.mjs";
|
|
2
|
+
import "./agent-CEZCaYto.mjs";
|
|
3
|
+
import "./standards-discovery-Bpz90VYq.mjs";
|
|
4
|
+
import { n as buildNormalizedOpenApiModel } from "./openapi-operations-DFpaqOqA.mjs";
|
|
5
|
+
import "./api-reference-0XdEWya8.mjs";
|
|
6
|
+
import { n as loadOpenApiCliSource, t as displayOpenApiCliSource } from "./openapi-source-CiKbUK71.mjs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import pc from "picocolors";
|
|
9
|
+
|
|
10
|
+
//#region src/cli/openapi-diff.ts
|
|
11
|
+
const SCHEMA_ANNOTATION_KEYS = new Set([
|
|
12
|
+
"$comment",
|
|
13
|
+
"deprecated",
|
|
14
|
+
"description",
|
|
15
|
+
"example",
|
|
16
|
+
"examples",
|
|
17
|
+
"externalDocs",
|
|
18
|
+
"title",
|
|
19
|
+
"xml"
|
|
20
|
+
]);
|
|
21
|
+
function parseOpenApiDiffArgs(argv) {
|
|
22
|
+
const positionals = [];
|
|
23
|
+
let baseline;
|
|
24
|
+
let current;
|
|
25
|
+
let baseUrl;
|
|
26
|
+
let failOn = "breaking";
|
|
27
|
+
let json = false;
|
|
28
|
+
let help = false;
|
|
29
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
30
|
+
const argument = argv[index] ?? "";
|
|
31
|
+
if (argument === "--help" || argument === "-h") {
|
|
32
|
+
help = true;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (argument === "--json") {
|
|
36
|
+
json = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const flagSeparator = argument.startsWith("--") ? argument.indexOf("=") : -1;
|
|
40
|
+
const rawFlag = argument.startsWith("--") ? argument.slice(2, flagSeparator === -1 ? void 0 : flagSeparator) : void 0;
|
|
41
|
+
const inlineValue = flagSeparator === -1 ? void 0 : argument.slice(flagSeparator + 1);
|
|
42
|
+
if (rawFlag) {
|
|
43
|
+
const value = inlineValue ?? argv[index + 1];
|
|
44
|
+
if (value === void 0 || value.startsWith("--")) throw new Error(`Missing value for --${rawFlag}.`);
|
|
45
|
+
if (inlineValue === void 0) index += 1;
|
|
46
|
+
if (rawFlag === "base" || rawFlag === "baseline") baseline = value;
|
|
47
|
+
else if (rawFlag === "head" || rawFlag === "current") current = value;
|
|
48
|
+
else if (rawFlag === "base-url") baseUrl = value;
|
|
49
|
+
else if (rawFlag === "fail-on") {
|
|
50
|
+
if (value !== "breaking" && value !== "any" && value !== "never") throw new Error("`--fail-on` must be \"breaking\", \"any\", or \"never\".");
|
|
51
|
+
failOn = value;
|
|
52
|
+
} else throw new Error(`Unknown OpenAPI diff option: --${rawFlag}.`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
positionals.push(argument);
|
|
56
|
+
}
|
|
57
|
+
for (const source of positionals) if (!baseline) baseline = source;
|
|
58
|
+
else if (!current) current = source;
|
|
59
|
+
else throw new Error("Pass exactly two OpenAPI sources: baseline and current.");
|
|
60
|
+
if (!help && (!baseline || !current)) throw new Error("Pass baseline and current OpenAPI sources as positional arguments or with --base and --head.");
|
|
61
|
+
return {
|
|
62
|
+
baseline,
|
|
63
|
+
current,
|
|
64
|
+
baseUrl,
|
|
65
|
+
failOn,
|
|
66
|
+
json,
|
|
67
|
+
help
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function runOpenApiDiff(options) {
|
|
71
|
+
if (!options.baseline || !options.current) throw new Error("Both baseline and current OpenAPI sources are required.");
|
|
72
|
+
const rootDir = path.resolve(options.rootDir ?? process.cwd());
|
|
73
|
+
const [baseline, current] = await Promise.all([loadOpenApiCliSource(options.baseline, {
|
|
74
|
+
rootDir,
|
|
75
|
+
baseUrl: options.baseUrl
|
|
76
|
+
}), loadOpenApiCliSource(options.current, {
|
|
77
|
+
rootDir,
|
|
78
|
+
baseUrl: options.baseUrl
|
|
79
|
+
})]);
|
|
80
|
+
const report = compareOpenApiDocuments(baseline, current, {
|
|
81
|
+
baselineSource: displayOpenApiCliSource(options.baseline),
|
|
82
|
+
currentSource: displayOpenApiCliSource(options.current)
|
|
83
|
+
});
|
|
84
|
+
if (options.json) console.log(JSON.stringify(report, null, 2));
|
|
85
|
+
else printOpenApiDiffReport(report);
|
|
86
|
+
if (options.failOn === "breaking" && report.breaking || options.failOn === "any" && report.changes.length > 0) process.exitCode = 1;
|
|
87
|
+
return report;
|
|
88
|
+
}
|
|
89
|
+
function compareOpenApiDocuments(baselineDocument, currentDocument, sources = {}) {
|
|
90
|
+
const baselineModel = buildNormalizedOpenApiModel(baselineDocument);
|
|
91
|
+
const currentModel = buildNormalizedOpenApiModel(currentDocument);
|
|
92
|
+
const changes = [];
|
|
93
|
+
const baselineOperations = new Map(baselineModel.operations.map((operation) => [operation.selector, operation]));
|
|
94
|
+
const currentOperations = new Map(currentModel.operations.map((operation) => [operation.selector, operation]));
|
|
95
|
+
for (const selector of Array.from(baselineOperations.keys()).sort()) {
|
|
96
|
+
const baseline = baselineOperations.get(selector);
|
|
97
|
+
const current = currentOperations.get(selector);
|
|
98
|
+
if (!current) {
|
|
99
|
+
changes.push(change("breaking", "operation-removed", selector, "Operation was removed."));
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
compareOperation(baseline, current, baselineDocument, currentDocument, changes);
|
|
103
|
+
}
|
|
104
|
+
for (const selector of Array.from(currentOperations.keys()).sort()) {
|
|
105
|
+
if (baselineOperations.has(selector)) continue;
|
|
106
|
+
changes.push(change("non-breaking", "operation-added", selector, "Operation was added."));
|
|
107
|
+
}
|
|
108
|
+
if (baselineModel.specificationVersion !== currentModel.specificationVersion) changes.push({
|
|
109
|
+
severity: "non-breaking",
|
|
110
|
+
kind: "specification-version-changed",
|
|
111
|
+
detail: `OpenAPI version changed from ${baselineModel.specificationVersion} to ${currentModel.specificationVersion}.`,
|
|
112
|
+
location: "/openapi"
|
|
113
|
+
});
|
|
114
|
+
changes.sort(compareChanges);
|
|
115
|
+
const breakingCount = changes.filter((entry) => entry.severity === "breaking").length;
|
|
116
|
+
return {
|
|
117
|
+
format: "farming-labs-openapi-diff.v1",
|
|
118
|
+
baseline: summarizeSource(sources.baselineSource ?? "baseline", baselineModel),
|
|
119
|
+
current: summarizeSource(sources.currentSource ?? "current", currentModel),
|
|
120
|
+
breaking: breakingCount > 0,
|
|
121
|
+
breakingCount,
|
|
122
|
+
nonBreakingCount: changes.length - breakingCount,
|
|
123
|
+
changes
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function compareOperation(baseline, current, baselineDocument, currentDocument, changes) {
|
|
127
|
+
const selector = baseline.selector;
|
|
128
|
+
if (baseline.operationId !== current.operationId) changes.push(change("breaking", "operation-id-changed", selector, `operationId changed from ${baseline.operationId} to ${current.operationId}.`, `${selector} operationId`));
|
|
129
|
+
compareParameters(baseline.parameters, current.parameters, baselineDocument, currentDocument, selector, changes);
|
|
130
|
+
compareRequestBody(baseline, current, baselineDocument, currentDocument, selector, changes);
|
|
131
|
+
compareResponses(baseline.responses, current.responses, baselineDocument, currentDocument, selector, changes);
|
|
132
|
+
if (normalizeSecurityForDiff(baseline.security) !== normalizeSecurityForDiff(current.security)) changes.push(change(baseline.security.length === 0 && current.security.length > 0 ? "breaking" : baseline.security.length > 0 && current.security.length === 0 ? "non-breaking" : "breaking", "security-changed", selector, "Security requirements changed.", `${selector} security`));
|
|
133
|
+
if (stableJson(baseline.securitySchemes) !== stableJson(current.securitySchemes)) changes.push(change("breaking", "security-scheme-changed", selector, "A security scheme used by the operation changed.", `${selector} securitySchemes`));
|
|
134
|
+
if (baseline.servers.length > 0 && !baseline.servers.some((server) => current.servers.includes(server))) changes.push(change("breaking", "server-changed", selector, "The operation no longer exposes any previously declared server URL.", `${selector} servers`));
|
|
135
|
+
if (!baseline.deprecated && current.deprecated) changes.push(change("non-breaking", "operation-deprecated", selector, "Operation is now deprecated."));
|
|
136
|
+
}
|
|
137
|
+
function compareParameters(baseline, current, baselineDocument, currentDocument, selector, changes) {
|
|
138
|
+
const baselineMap = new Map(baseline.map((parameter) => [parameterKey(parameter), parameter]));
|
|
139
|
+
const currentMap = new Map(current.map((parameter) => [parameterKey(parameter), parameter]));
|
|
140
|
+
for (const key of Array.from(baselineMap.keys()).sort()) {
|
|
141
|
+
const before = baselineMap.get(key);
|
|
142
|
+
const after = currentMap.get(key);
|
|
143
|
+
const location = `${selector} parameter ${before.in}:${before.name}`;
|
|
144
|
+
if (!after) {
|
|
145
|
+
changes.push(change("breaking", "parameter-removed", selector, `${before.in} parameter ${before.name} was removed.`, location));
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (!before.required && after.required) changes.push(change("breaking", "parameter-required", selector, `${after.in} parameter ${after.name} is now required.`, location));
|
|
149
|
+
else if (before.required && !after.required) changes.push(change("non-breaking", "parameter-optional", selector, `${after.in} parameter ${after.name} is now optional.`, location));
|
|
150
|
+
compareSchema(before.schema, after.schema, baselineDocument, currentDocument, "request", selector, location, "parameter-schema-changed", changes);
|
|
151
|
+
}
|
|
152
|
+
for (const key of Array.from(currentMap.keys()).sort()) {
|
|
153
|
+
if (baselineMap.has(key)) continue;
|
|
154
|
+
const parameter = currentMap.get(key);
|
|
155
|
+
changes.push(change(parameter.required ? "breaking" : "non-breaking", parameter.required ? "required-parameter-added" : "optional-parameter-added", selector, `${parameter.required ? "Required" : "Optional"} ${parameter.in} parameter ${parameter.name} was added.`, `${selector} parameter ${parameter.in}:${parameter.name}`));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function compareRequestBody(baseline, current, baselineDocument, currentDocument, selector, changes) {
|
|
159
|
+
const before = baseline.requestBody;
|
|
160
|
+
const after = current.requestBody;
|
|
161
|
+
const location = `${selector} requestBody`;
|
|
162
|
+
if (before && !after) {
|
|
163
|
+
changes.push(change("breaking", "request-body-removed", selector, "Request body was removed.", location));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!before && after) {
|
|
167
|
+
changes.push(change(after.required ? "breaking" : "non-breaking", after.required ? "required-request-body-added" : "optional-request-body-added", selector, `${after.required ? "Required" : "Optional"} request body was added.`, location));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (!before || !after) return;
|
|
171
|
+
if (!before.required && after.required) changes.push(change("breaking", "request-body-required", selector, "Request body is now required.", location));
|
|
172
|
+
else if (before.required && !after.required) changes.push(change("non-breaking", "request-body-optional", selector, "Request body is now optional.", location));
|
|
173
|
+
compareMediaTypes(before.content, after.content, baselineDocument, currentDocument, "request", selector, location, changes);
|
|
174
|
+
}
|
|
175
|
+
function compareResponses(baseline, current, baselineDocument, currentDocument, selector, changes) {
|
|
176
|
+
const baselineMap = new Map(baseline.map((response) => [response.status, response]));
|
|
177
|
+
const currentMap = new Map(current.map((response) => [response.status, response]));
|
|
178
|
+
for (const status of Array.from(baselineMap.keys()).sort()) {
|
|
179
|
+
const before = baselineMap.get(status);
|
|
180
|
+
const after = currentMap.get(status);
|
|
181
|
+
const location = `${selector} response ${status}`;
|
|
182
|
+
if (!after) {
|
|
183
|
+
changes.push(change("breaking", "response-removed", selector, `Response ${status} was removed.`, location));
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
compareMediaTypes(before.content, after.content, baselineDocument, currentDocument, "response", selector, location, changes);
|
|
187
|
+
}
|
|
188
|
+
for (const status of Array.from(currentMap.keys()).sort()) {
|
|
189
|
+
if (baselineMap.has(status)) continue;
|
|
190
|
+
changes.push(change("non-breaking", "response-added", selector, `Response ${status} was added.`, `${selector} response ${status}`));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function compareMediaTypes(baseline, current, baselineDocument, currentDocument, mode, selector, parentLocation, changes) {
|
|
194
|
+
const baselineMap = new Map(baseline.map((media) => [media.mediaType, media]));
|
|
195
|
+
const currentMap = new Map(current.map((media) => [media.mediaType, media]));
|
|
196
|
+
for (const mediaType of Array.from(baselineMap.keys()).sort()) {
|
|
197
|
+
const before = baselineMap.get(mediaType);
|
|
198
|
+
const after = currentMap.get(mediaType);
|
|
199
|
+
const location = `${parentLocation} ${mediaType}`;
|
|
200
|
+
if (!after) {
|
|
201
|
+
changes.push(change("breaking", "media-type-removed", selector, `${mediaType} was removed.`, location));
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
compareSchema(before.schema, after.schema, baselineDocument, currentDocument, mode, selector, location, `${mode}-schema-changed`, changes);
|
|
205
|
+
}
|
|
206
|
+
for (const mediaType of Array.from(currentMap.keys()).sort()) {
|
|
207
|
+
if (baselineMap.has(mediaType)) continue;
|
|
208
|
+
changes.push(change("non-breaking", "media-type-added", selector, `${mediaType} was added.`, `${parentLocation} ${mediaType}`));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function compareSchema(baseline, current, baselineDocument, currentDocument, mode, selector, location, kind, changes) {
|
|
212
|
+
const before = normalizeSchema(baselineDocument, baseline);
|
|
213
|
+
const after = normalizeSchema(currentDocument, current);
|
|
214
|
+
if (stableJson(before) === stableJson(after)) return;
|
|
215
|
+
const breakingReason = findBreakingSchemaReason(before, after, mode);
|
|
216
|
+
changes.push(change(breakingReason ? "breaking" : "non-breaking", kind, selector, breakingReason ?? (mode === "request" ? "Request schema was expanded compatibly." : "Response schema was narrowed compatibly."), location));
|
|
217
|
+
}
|
|
218
|
+
function findBreakingSchemaReason(baseline, current, mode, schemaPath = "schema") {
|
|
219
|
+
if (baseline === void 0 || current === void 0) {
|
|
220
|
+
if (baseline === current) return void 0;
|
|
221
|
+
const constrained = baseline === void 0 ? "current" : "baseline";
|
|
222
|
+
return (mode === "request" ? constrained === "current" : constrained === "baseline") ? `${schemaPath} changed incompatibly.` : void 0;
|
|
223
|
+
}
|
|
224
|
+
const before = asRecord(baseline);
|
|
225
|
+
const after = asRecord(current);
|
|
226
|
+
if (!before || !after) return stableJson(baseline) === stableJson(current) ? void 0 : `${schemaPath} changed incompatibly.`;
|
|
227
|
+
if (restrictionChangedIncompatibly(normalizeTypes(before.type), normalizeTypes(after.type), mode)) return `${schemaPath} type changed incompatibly.`;
|
|
228
|
+
if (restrictionChangedIncompatibly(Array.isArray(before.enum) ? before.enum.map(stableJson) : [], Array.isArray(after.enum) ? after.enum.map(stableJson) : [], mode)) return `${schemaPath} enum changed incompatibly.`;
|
|
229
|
+
const beforeConst = Object.hasOwn(before, "const") ? [stableJson(before.const)] : [];
|
|
230
|
+
const afterConst = Object.hasOwn(after, "const") ? [stableJson(after.const)] : [];
|
|
231
|
+
if (stableJson(beforeConst) !== stableJson(afterConst)) {
|
|
232
|
+
if (beforeConst.length > 0 && afterConst.length > 0 && beforeConst[0] !== afterConst[0]) return `${schemaPath} constant value changed.`;
|
|
233
|
+
if (restrictionChangedIncompatibly(beforeConst, afterConst, mode)) return `${schemaPath} constant constraint changed incompatibly.`;
|
|
234
|
+
}
|
|
235
|
+
const scalarRestrictionReason = compareScalarSchemaRestrictions(before, after, mode, schemaPath);
|
|
236
|
+
if (scalarRestrictionReason) return scalarRestrictionReason;
|
|
237
|
+
const limitReason = compareSchemaLimits(before, after, mode, schemaPath);
|
|
238
|
+
if (limitReason) return limitReason;
|
|
239
|
+
const beforeRequired = new Set(stringArray(before.required));
|
|
240
|
+
const afterRequired = new Set(stringArray(after.required));
|
|
241
|
+
if (mode === "request") {
|
|
242
|
+
const addedRequired = Array.from(afterRequired).find((name) => !beforeRequired.has(name));
|
|
243
|
+
if (addedRequired) return `${schemaPath}.${addedRequired} is newly required.`;
|
|
244
|
+
} else {
|
|
245
|
+
const relaxedRequired = Array.from(beforeRequired).find((name) => !afterRequired.has(name));
|
|
246
|
+
if (relaxedRequired) return `${schemaPath}.${relaxedRequired} is no longer guaranteed.`;
|
|
247
|
+
}
|
|
248
|
+
const beforeProperties = asRecord(before.properties) ?? {};
|
|
249
|
+
const afterProperties = asRecord(after.properties) ?? {};
|
|
250
|
+
if (mode === "response") {
|
|
251
|
+
const removed = Object.keys(beforeProperties).find((name) => !Object.hasOwn(afterProperties, name));
|
|
252
|
+
if (removed) return `${schemaPath}.${removed} was removed from the response.`;
|
|
253
|
+
} else if (after.additionalProperties === false) {
|
|
254
|
+
const rejected = Object.keys(beforeProperties).find((name) => !Object.hasOwn(afterProperties, name));
|
|
255
|
+
if (rejected) return `${schemaPath}.${rejected} is no longer accepted.`;
|
|
256
|
+
}
|
|
257
|
+
for (const name of Object.keys(beforeProperties).sort()) {
|
|
258
|
+
if (!Object.hasOwn(afterProperties, name)) continue;
|
|
259
|
+
const nested = findBreakingSchemaReason(beforeProperties[name], afterProperties[name], mode, `${schemaPath}.${name}`);
|
|
260
|
+
if (nested) return nested;
|
|
261
|
+
}
|
|
262
|
+
if (stableJson(before.items) !== stableJson(after.items)) {
|
|
263
|
+
const nested = findBreakingSchemaReason(before.items, after.items, mode, `${schemaPath}[]`);
|
|
264
|
+
if (nested) return nested;
|
|
265
|
+
}
|
|
266
|
+
const beforeAdditional = before.additionalProperties ?? true;
|
|
267
|
+
const afterAdditional = after.additionalProperties ?? true;
|
|
268
|
+
if (stableJson(beforeAdditional) !== stableJson(afterAdditional)) {
|
|
269
|
+
const beforeAdditionalSchema = asRecord(beforeAdditional);
|
|
270
|
+
const afterAdditionalSchema = asRecord(afterAdditional);
|
|
271
|
+
if (beforeAdditionalSchema && afterAdditionalSchema) {
|
|
272
|
+
const nested = findBreakingSchemaReason(beforeAdditionalSchema, afterAdditionalSchema, mode, `${schemaPath}.*`);
|
|
273
|
+
if (nested) return nested;
|
|
274
|
+
} else {
|
|
275
|
+
const becameRestricted = beforeAdditional !== false && afterAdditional === false;
|
|
276
|
+
const becameUnrestricted = beforeAdditional === false && afterAdditional !== false;
|
|
277
|
+
if (mode === "request" && becameRestricted || mode === "response" && becameUnrestricted || !becameRestricted && !becameUnrestricted) return `${schemaPath}.additionalProperties changed incompatibly.`;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
for (const keyword of [
|
|
281
|
+
"allOf",
|
|
282
|
+
"anyOf",
|
|
283
|
+
"oneOf",
|
|
284
|
+
"not",
|
|
285
|
+
"discriminator"
|
|
286
|
+
]) if (stableJson(before[keyword]) !== stableJson(after[keyword])) return `${schemaPath}.${keyword} changed; compatibility cannot be preserved safely.`;
|
|
287
|
+
}
|
|
288
|
+
function compareScalarSchemaRestrictions(baseline, current, mode, schemaPath) {
|
|
289
|
+
for (const keyword of [
|
|
290
|
+
"pattern",
|
|
291
|
+
"format",
|
|
292
|
+
"multipleOf"
|
|
293
|
+
]) {
|
|
294
|
+
if (stableJson(baseline[keyword]) === stableJson(current[keyword])) continue;
|
|
295
|
+
const before = baseline[keyword] === void 0 ? [] : [stableJson(baseline[keyword])];
|
|
296
|
+
const after = current[keyword] === void 0 ? [] : [stableJson(current[keyword])];
|
|
297
|
+
if (before.length > 0 && after.length > 0) return `${schemaPath}.${keyword} changed; compatibility cannot be proven.`;
|
|
298
|
+
if (restrictionChangedIncompatibly(before, after, mode)) return `${schemaPath}.${keyword} changed incompatibly.`;
|
|
299
|
+
}
|
|
300
|
+
const beforeNullable = baseline.nullable === true;
|
|
301
|
+
const afterNullable = current.nullable === true;
|
|
302
|
+
if (mode === "request" && beforeNullable && !afterNullable || mode === "response" && !beforeNullable && afterNullable) return `${schemaPath}.nullable changed incompatibly.`;
|
|
303
|
+
if (mode === "request" && baseline.readOnly !== true && current.readOnly === true) return `${schemaPath}.readOnly now rejects request input.`;
|
|
304
|
+
if (mode === "response" && baseline.writeOnly !== true && current.writeOnly === true) return `${schemaPath}.writeOnly removes response output.`;
|
|
305
|
+
}
|
|
306
|
+
function compareSchemaLimits(baseline, current, mode, schemaPath) {
|
|
307
|
+
for (const [keyword, direction] of [
|
|
308
|
+
["minimum", "min"],
|
|
309
|
+
["exclusiveMinimum", "min"],
|
|
310
|
+
["minLength", "min"],
|
|
311
|
+
["minItems", "min"],
|
|
312
|
+
["minProperties", "min"],
|
|
313
|
+
["maximum", "max"],
|
|
314
|
+
["exclusiveMaximum", "max"],
|
|
315
|
+
["maxLength", "max"],
|
|
316
|
+
["maxItems", "max"],
|
|
317
|
+
["maxProperties", "max"]
|
|
318
|
+
]) {
|
|
319
|
+
const before = numberValue(baseline[keyword]);
|
|
320
|
+
const after = numberValue(current[keyword]);
|
|
321
|
+
if (before === after) continue;
|
|
322
|
+
const narrowed = before === void 0 ? after !== void 0 : after === void 0 ? false : direction === "min" ? after > before : after < before;
|
|
323
|
+
if (mode === "request" ? narrowed : !narrowed) return `${schemaPath}.${keyword} changed incompatibly.`;
|
|
324
|
+
}
|
|
325
|
+
if (typeof baseline.pattern === "string" && typeof current.pattern === "string" && baseline.pattern !== current.pattern) return `${schemaPath}.pattern changed; compatibility cannot be proven.`;
|
|
326
|
+
}
|
|
327
|
+
function normalizeSchema(document, value) {
|
|
328
|
+
return normalizeSchemaValue(document, value, /* @__PURE__ */ new Set());
|
|
329
|
+
}
|
|
330
|
+
function normalizeSchemaValue(document, value, references) {
|
|
331
|
+
if (Array.isArray(value)) return value.map((entry) => normalizeSchemaValue(document, entry, references));
|
|
332
|
+
const record = asRecord(value);
|
|
333
|
+
if (!record) return value;
|
|
334
|
+
const reference = typeof record.$ref === "string" ? record.$ref : void 0;
|
|
335
|
+
if (reference?.startsWith("#/")) {
|
|
336
|
+
if (references.has(reference)) return { $ref: reference };
|
|
337
|
+
const target = resolvePointer(document, reference.slice(1));
|
|
338
|
+
if (target !== void 0) {
|
|
339
|
+
const nextReferences = new Set(references).add(reference);
|
|
340
|
+
const siblings = Object.fromEntries(Object.entries(record).filter(([key]) => key !== "$ref"));
|
|
341
|
+
const resolved = normalizeSchemaValue(document, target, nextReferences);
|
|
342
|
+
const resolvedRecord = asRecord(resolved);
|
|
343
|
+
return resolvedRecord ? normalizeSchemaValue(document, {
|
|
344
|
+
...resolvedRecord,
|
|
345
|
+
...siblings
|
|
346
|
+
}, nextReferences) : resolved;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return Object.fromEntries(Object.keys(record).filter((key) => !SCHEMA_ANNOTATION_KEYS.has(key)).sort().map((key) => [key, normalizeSchemaValue(document, record[key], references)]));
|
|
350
|
+
}
|
|
351
|
+
function summarizeSource(source, model) {
|
|
352
|
+
return {
|
|
353
|
+
source,
|
|
354
|
+
title: model.title,
|
|
355
|
+
version: model.version,
|
|
356
|
+
specificationVersion: model.specificationVersion,
|
|
357
|
+
operationCount: model.operationCount
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function printOpenApiDiffReport(report) {
|
|
361
|
+
console.log(`${pc.bold("@farming-labs/docs")} ${pc.dim("—")} ${pc.bold("openapi diff")}`);
|
|
362
|
+
console.log();
|
|
363
|
+
console.log(`${pc.bold("Baseline:")} ${report.baseline.source} ${pc.dim(`(${report.baseline.version}, ${report.baseline.operationCount} operations)`)}`);
|
|
364
|
+
console.log(`${pc.bold("Current:")} ${report.current.source} ${pc.dim(`(${report.current.version}, ${report.current.operationCount} operations)`)}`);
|
|
365
|
+
console.log(`${pc.bold("Changes:")} ${report.breakingCount} breaking ${pc.dim("•")} ${report.nonBreakingCount} non-breaking`);
|
|
366
|
+
if (report.changes.length === 0) {
|
|
367
|
+
console.log();
|
|
368
|
+
console.log(pc.green("No contract changes found."));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
for (const severity of ["breaking", "non-breaking"]) {
|
|
372
|
+
const entries = report.changes.filter((entry) => entry.severity === severity);
|
|
373
|
+
if (entries.length === 0) continue;
|
|
374
|
+
console.log();
|
|
375
|
+
console.log(severity === "breaking" ? pc.red(pc.bold("BREAKING")) : pc.cyan(pc.bold("NON-BREAKING")));
|
|
376
|
+
for (const entry of entries) {
|
|
377
|
+
const target = entry.selector ? `${entry.selector}: ` : "";
|
|
378
|
+
console.log(`- ${pc.dim(`[${entry.kind}]`)} ${target}${entry.detail}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function printOpenApiDiffHelp() {
|
|
383
|
+
console.log(`${pc.bold("docs openapi diff")} — Compare two OpenAPI contracts.
|
|
384
|
+
|
|
385
|
+
Usage:
|
|
386
|
+
docs openapi diff <baseline> <current> [options]
|
|
387
|
+
docs api diff --base <baseline> --head <current> [options]
|
|
388
|
+
|
|
389
|
+
Sources:
|
|
390
|
+
Project-relative or absolute JSON/YAML files, file: URLs, and HTTP(S) URLs.
|
|
391
|
+
|
|
392
|
+
Options:
|
|
393
|
+
${pc.cyan("--base, --baseline <source>")} Baseline contract
|
|
394
|
+
${pc.cyan("--head, --current <source>")} Current contract
|
|
395
|
+
${pc.cyan("--base-url <url>")} Resolve request-relative remote sources
|
|
396
|
+
${pc.cyan("--fail-on <policy>")} breaking (default), any, or never
|
|
397
|
+
${pc.cyan("--json")} Print ${pc.dim("farming-labs-openapi-diff.v1")} JSON
|
|
398
|
+
${pc.cyan("--help")} Show this help
|
|
399
|
+
`);
|
|
400
|
+
}
|
|
401
|
+
function change(severity, kind, selector, detail, location) {
|
|
402
|
+
return {
|
|
403
|
+
severity,
|
|
404
|
+
kind,
|
|
405
|
+
selector,
|
|
406
|
+
detail,
|
|
407
|
+
...location ? { location } : {}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function compareChanges(left, right) {
|
|
411
|
+
if (left.severity !== right.severity) return left.severity === "breaking" ? -1 : 1;
|
|
412
|
+
return `${left.selector ?? ""}\0${left.kind}\0${left.location ?? ""}`.localeCompare(`${right.selector ?? ""}\0${right.kind}\0${right.location ?? ""}`);
|
|
413
|
+
}
|
|
414
|
+
function parameterKey(parameter) {
|
|
415
|
+
return `${parameter.in}:${parameter.name}`;
|
|
416
|
+
}
|
|
417
|
+
function normalizeTypes(value) {
|
|
418
|
+
if (typeof value === "string") return [value];
|
|
419
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string").sort() : [];
|
|
420
|
+
}
|
|
421
|
+
function restrictionChangedIncompatibly(baseline, current, mode) {
|
|
422
|
+
if (baseline.length === 0 && current.length === 0) return false;
|
|
423
|
+
if (mode === "request") {
|
|
424
|
+
if (baseline.length === 0) return current.length > 0;
|
|
425
|
+
if (current.length === 0) return false;
|
|
426
|
+
return baseline.some((value) => !current.includes(value));
|
|
427
|
+
}
|
|
428
|
+
if (current.length === 0) return baseline.length > 0;
|
|
429
|
+
if (baseline.length === 0) return false;
|
|
430
|
+
return current.some((value) => !baseline.includes(value));
|
|
431
|
+
}
|
|
432
|
+
function normalizeSecurityForDiff(security) {
|
|
433
|
+
return security.map((requirement) => stableJson(Object.fromEntries(Object.keys(requirement).sort().map((name) => [name, [...requirement[name] ?? []].sort()])))).sort().join("|");
|
|
434
|
+
}
|
|
435
|
+
function stringArray(value) {
|
|
436
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
437
|
+
}
|
|
438
|
+
function numberValue(value) {
|
|
439
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
440
|
+
}
|
|
441
|
+
function stableJson(value) {
|
|
442
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
443
|
+
const record = asRecord(value);
|
|
444
|
+
if (record) return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
|
|
445
|
+
return JSON.stringify(value) ?? "undefined";
|
|
446
|
+
}
|
|
447
|
+
function asRecord(value) {
|
|
448
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
449
|
+
}
|
|
450
|
+
function resolvePointer(document, pointer) {
|
|
451
|
+
let current = document;
|
|
452
|
+
for (const token of pointer.slice(1).split("/")) {
|
|
453
|
+
const record = asRecord(current);
|
|
454
|
+
if (!record) return void 0;
|
|
455
|
+
current = record[token.replaceAll("~1", "/").replaceAll("~0", "~")];
|
|
456
|
+
}
|
|
457
|
+
return current;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
//#endregion
|
|
461
|
+
export { parseOpenApiDiffArgs, printOpenApiDiffHelp, runOpenApiDiff };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { s as parseDocsMarkdownSections } from "./markdown-sections-BKy4labo.mjs";
|
|
2
|
-
import { $ as isDocsSkillRequest, Fn as
|
|
2
|
+
import { $ as isDocsSkillRequest, Fn as DocsPaginationCursorError, In as createDocsPaginationCursor, K as isDocsAgentDiscoveryRequest, Rn as resolveDocsPaginationOffset, St as resolveDocsSkillFormat, Xt as resolveDocsContentChangesConfig, Yt as isDocsContentChangesRequest, dt as resolveDocsAgentFeedbackRequest, ft as resolveDocsAgentsFormat, mt as resolveDocsLlmsTxtRequest, q as isDocsAgentsRequest, tn as buildDocsRetrievalDigestProjection, ut as resolveDocsAgentFeedbackConfig, vt as resolveDocsMarkdownRequest } from "./agent-CEZCaYto.mjs";
|
|
3
3
|
import { n as digestDocsRetrievalContent } from "./retrieval-digest-B-SgtXKK.mjs";
|
|
4
|
+
import { n as buildNormalizedOpenApiModel } from "./openapi-operations-DFpaqOqA.mjs";
|
|
4
5
|
import matter from "gray-matter";
|
|
5
6
|
|
|
6
7
|
//#region src/telemetry.ts
|
|
@@ -606,15 +607,6 @@ function hydrateDocsContentChanges(options) {
|
|
|
606
607
|
|
|
607
608
|
//#endregion
|
|
608
609
|
//#region src/openapi-mcp.ts
|
|
609
|
-
const HTTP_METHODS = [
|
|
610
|
-
"get",
|
|
611
|
-
"post",
|
|
612
|
-
"put",
|
|
613
|
-
"patch",
|
|
614
|
-
"delete",
|
|
615
|
-
"options",
|
|
616
|
-
"head"
|
|
617
|
-
];
|
|
618
610
|
const OPENAPI_RATE_WINDOW_MS = 6e4;
|
|
619
611
|
const openApiRateWindows = /* @__PURE__ */ new Map();
|
|
620
612
|
function acquireDocsOpenApiMcpBudget(key, config, now = Date.now()) {
|
|
@@ -682,104 +674,64 @@ async function readDocsOpenApiMcpResponse(response, maxBytes = 1e6) {
|
|
|
682
674
|
function asRecord(value) {
|
|
683
675
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
684
676
|
}
|
|
685
|
-
function selectorFor(method, path) {
|
|
686
|
-
return `${method.toUpperCase()} ${path}`;
|
|
687
|
-
}
|
|
688
677
|
function sanitizeToolName(value) {
|
|
689
678
|
return `api_${value.trim().replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 56) || "operation"}`;
|
|
690
679
|
}
|
|
691
680
|
function operationExtensionEnabled(operation) {
|
|
692
|
-
const extension = operation["x-farming-labs-mcp"];
|
|
681
|
+
const extension = operation.extensions["x-farming-labs-mcp"];
|
|
693
682
|
return extension === true || asRecord(extension)?.enabled === true;
|
|
694
683
|
}
|
|
695
|
-
function normalizeSecurity(value) {
|
|
696
|
-
if (!Array.isArray(value)) return [];
|
|
697
|
-
return value.flatMap((requirement) => {
|
|
698
|
-
const record = asRecord(requirement);
|
|
699
|
-
if (!record) return [];
|
|
700
|
-
const normalized = {};
|
|
701
|
-
for (const [name, scopes] of Object.entries(record)) normalized[name] = Array.isArray(scopes) ? scopes.filter((scope) => typeof scope === "string") : [];
|
|
702
|
-
return [normalized];
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
function normalizeParameters(...values) {
|
|
706
|
-
const parameters = values.flatMap((value) => Array.isArray(value) ? value : []);
|
|
707
|
-
const seen = /* @__PURE__ */ new Set();
|
|
708
|
-
return parameters.flatMap((parameter) => {
|
|
709
|
-
const record = asRecord(parameter);
|
|
710
|
-
if (!record) return [];
|
|
711
|
-
const name = typeof record.name === "string" ? record.name.trim() : "";
|
|
712
|
-
const location = record?.in;
|
|
713
|
-
if (!name || location !== "path" && location !== "query" && location !== "header" && location !== "cookie") return [];
|
|
714
|
-
const key = `${location}:${name}`;
|
|
715
|
-
if (seen.has(key)) return [];
|
|
716
|
-
seen.add(key);
|
|
717
|
-
return [{
|
|
718
|
-
name,
|
|
719
|
-
in: location,
|
|
720
|
-
required: record.required === true || location === "path"
|
|
721
|
-
}];
|
|
722
|
-
});
|
|
723
|
-
}
|
|
724
684
|
function resolveDocsOpenApiMcpOperations(document, config) {
|
|
725
685
|
if (!config || config.enabled === false) return [];
|
|
726
686
|
const allow = new Set((config.operations ?? []).map((value) => value.trim()).filter(Boolean));
|
|
727
|
-
const
|
|
728
|
-
const securitySchemes = asRecord(asRecord(document.components)?.securitySchemes) ?? {};
|
|
729
|
-
const globalSecurity = normalizeSecurity(document.security);
|
|
687
|
+
const model = buildNormalizedOpenApiModel(document);
|
|
730
688
|
const operations = [];
|
|
731
689
|
const names = /* @__PURE__ */ new Set();
|
|
732
|
-
for (const
|
|
733
|
-
|
|
734
|
-
if (!
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
});
|
|
771
|
-
}
|
|
690
|
+
for (const operation of model.operations) {
|
|
691
|
+
if (operation.method === "TRACE") continue;
|
|
692
|
+
if (!(allow.has(operation.operationId) || allow.has(operation.selector) || operationExtensionEnabled(operation))) continue;
|
|
693
|
+
if (![
|
|
694
|
+
"GET",
|
|
695
|
+
"HEAD",
|
|
696
|
+
"OPTIONS"
|
|
697
|
+
].includes(operation.method) && config.allowMutations !== true) continue;
|
|
698
|
+
const toolName = sanitizeToolName(operation.operationId);
|
|
699
|
+
if (names.has(toolName)) throw new Error(`OpenAPI MCP tool name collision: ${toolName}`);
|
|
700
|
+
names.add(toolName);
|
|
701
|
+
operations.push({
|
|
702
|
+
toolName,
|
|
703
|
+
operationId: operation.operationId,
|
|
704
|
+
method: operation.method,
|
|
705
|
+
path: operation.path,
|
|
706
|
+
title: operation.title,
|
|
707
|
+
...operation.description ? { description: operation.description } : {},
|
|
708
|
+
parameters: operation.parameters.flatMap((parameter) => {
|
|
709
|
+
if (parameter.in !== "path" && parameter.in !== "query" && parameter.in !== "header" && parameter.in !== "cookie") return [];
|
|
710
|
+
return [{
|
|
711
|
+
name: parameter.name,
|
|
712
|
+
in: parameter.in,
|
|
713
|
+
required: parameter.required
|
|
714
|
+
}];
|
|
715
|
+
}),
|
|
716
|
+
security: operation.security,
|
|
717
|
+
securitySchemes: operation.securitySchemes,
|
|
718
|
+
readOnly: operation.method === "GET" || operation.method === "HEAD",
|
|
719
|
+
destructive: operation.method === "DELETE",
|
|
720
|
+
idempotent: [
|
|
721
|
+
"GET",
|
|
722
|
+
"HEAD",
|
|
723
|
+
"PUT",
|
|
724
|
+
"DELETE",
|
|
725
|
+
"OPTIONS"
|
|
726
|
+
].includes(operation.method)
|
|
727
|
+
});
|
|
772
728
|
}
|
|
773
729
|
return operations.sort((left, right) => left.toolName.localeCompare(right.toolName));
|
|
774
730
|
}
|
|
775
731
|
function resolveDocsOpenApiMcpBaseUrl(document, config) {
|
|
776
732
|
const configured = config.baseUrl?.trim();
|
|
777
733
|
if (configured) return configured;
|
|
778
|
-
|
|
779
|
-
for (const server of servers) {
|
|
780
|
-
const url = asRecord(server)?.url;
|
|
781
|
-
if (typeof url === "string" && url.trim()) return url.trim();
|
|
782
|
-
}
|
|
734
|
+
return buildNormalizedOpenApiModel(document).servers[0];
|
|
783
735
|
}
|
|
784
736
|
|
|
785
737
|
//#endregion
|