@robinmordasiewicz/f5xc-api-mcp 1.0.82-2512312028

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +415 -0
  3. package/dist/auth/credential-manager.d.ts +151 -0
  4. package/dist/auth/credential-manager.d.ts.map +1 -0
  5. package/dist/auth/credential-manager.js +330 -0
  6. package/dist/auth/credential-manager.js.map +1 -0
  7. package/dist/auth/http-client.d.ts +81 -0
  8. package/dist/auth/http-client.d.ts.map +1 -0
  9. package/dist/auth/http-client.js +197 -0
  10. package/dist/auth/http-client.js.map +1 -0
  11. package/dist/auth/index.d.ts +8 -0
  12. package/dist/auth/index.d.ts.map +1 -0
  13. package/dist/auth/index.js +6 -0
  14. package/dist/auth/index.js.map +1 -0
  15. package/dist/config/config-manager.d.ts +72 -0
  16. package/dist/config/config-manager.d.ts.map +1 -0
  17. package/dist/config/config-manager.js +247 -0
  18. package/dist/config/config-manager.js.map +1 -0
  19. package/dist/config/index.d.ts +7 -0
  20. package/dist/config/index.d.ts.map +1 -0
  21. package/dist/config/index.js +7 -0
  22. package/dist/config/index.js.map +1 -0
  23. package/dist/config/schema.d.ts +74 -0
  24. package/dist/config/schema.d.ts.map +1 -0
  25. package/dist/config/schema.js +75 -0
  26. package/dist/config/schema.js.map +1 -0
  27. package/dist/config/types.d.ts +77 -0
  28. package/dist/config/types.d.ts.map +1 -0
  29. package/dist/config/types.js +31 -0
  30. package/dist/config/types.js.map +1 -0
  31. package/dist/generator/dependency-extractor.d.ts +103 -0
  32. package/dist/generator/dependency-extractor.d.ts.map +1 -0
  33. package/dist/generator/dependency-extractor.js +473 -0
  34. package/dist/generator/dependency-extractor.js.map +1 -0
  35. package/dist/generator/dependency-graph.d.ts +67 -0
  36. package/dist/generator/dependency-graph.d.ts.map +1 -0
  37. package/dist/generator/dependency-graph.js +330 -0
  38. package/dist/generator/dependency-graph.js.map +1 -0
  39. package/dist/generator/dependency-types.d.ts +206 -0
  40. package/dist/generator/dependency-types.d.ts.map +1 -0
  41. package/dist/generator/dependency-types.js +37 -0
  42. package/dist/generator/dependency-types.js.map +1 -0
  43. package/dist/generator/index.d.ts +7 -0
  44. package/dist/generator/index.d.ts.map +1 -0
  45. package/dist/generator/index.js +7 -0
  46. package/dist/generator/index.js.map +1 -0
  47. package/dist/generator/naming/acronyms.d.ts +81 -0
  48. package/dist/generator/naming/acronyms.d.ts.map +1 -0
  49. package/dist/generator/naming/acronyms.js +253 -0
  50. package/dist/generator/naming/acronyms.js.map +1 -0
  51. package/dist/generator/naming/index.d.ts +6 -0
  52. package/dist/generator/naming/index.d.ts.map +1 -0
  53. package/dist/generator/naming/index.js +6 -0
  54. package/dist/generator/naming/index.js.map +1 -0
  55. package/dist/generator/naming/volterra-mapping.d.ts +102 -0
  56. package/dist/generator/naming/volterra-mapping.d.ts.map +1 -0
  57. package/dist/generator/naming/volterra-mapping.js +259 -0
  58. package/dist/generator/naming/volterra-mapping.js.map +1 -0
  59. package/dist/generator/openapi-parser.d.ts +701 -0
  60. package/dist/generator/openapi-parser.d.ts.map +1 -0
  61. package/dist/generator/openapi-parser.js +704 -0
  62. package/dist/generator/openapi-parser.js.map +1 -0
  63. package/dist/generator/tool-generator.d.ts +118 -0
  64. package/dist/generator/tool-generator.d.ts.map +1 -0
  65. package/dist/generator/tool-generator.js +529 -0
  66. package/dist/generator/tool-generator.js.map +1 -0
  67. package/dist/generator/transformers/index.d.ts +7 -0
  68. package/dist/generator/transformers/index.d.ts.map +1 -0
  69. package/dist/generator/transformers/index.js +7 -0
  70. package/dist/generator/transformers/index.js.map +1 -0
  71. package/dist/generator/transformers/normalize-examples.d.ts +48 -0
  72. package/dist/generator/transformers/normalize-examples.d.ts.map +1 -0
  73. package/dist/generator/transformers/normalize-examples.js +66 -0
  74. package/dist/generator/transformers/normalize-examples.js.map +1 -0
  75. package/dist/index.d.ts +22 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +101 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/prompts/error-resolution.d.ts +70 -0
  80. package/dist/prompts/error-resolution.d.ts.map +1 -0
  81. package/dist/prompts/error-resolution.js +350 -0
  82. package/dist/prompts/error-resolution.js.map +1 -0
  83. package/dist/prompts/index.d.ts +8 -0
  84. package/dist/prompts/index.d.ts.map +1 -0
  85. package/dist/prompts/index.js +7 -0
  86. package/dist/prompts/index.js.map +1 -0
  87. package/dist/prompts/workflows.d.ts +59 -0
  88. package/dist/prompts/workflows.d.ts.map +1 -0
  89. package/dist/prompts/workflows.js +441 -0
  90. package/dist/prompts/workflows.js.map +1 -0
  91. package/dist/resources/handlers.d.ts +70 -0
  92. package/dist/resources/handlers.d.ts.map +1 -0
  93. package/dist/resources/handlers.js +270 -0
  94. package/dist/resources/handlers.js.map +1 -0
  95. package/dist/resources/index.d.ts +8 -0
  96. package/dist/resources/index.d.ts.map +1 -0
  97. package/dist/resources/index.js +6 -0
  98. package/dist/resources/index.js.map +1 -0
  99. package/dist/resources/templates.d.ts +86 -0
  100. package/dist/resources/templates.d.ts.map +1 -0
  101. package/dist/resources/templates.js +248 -0
  102. package/dist/resources/templates.js.map +1 -0
  103. package/dist/server.d.ts +78 -0
  104. package/dist/server.d.ts.map +1 -0
  105. package/dist/server.js +721 -0
  106. package/dist/server.js.map +1 -0
  107. package/dist/tools/discovery/best-practices.d.ts +140 -0
  108. package/dist/tools/discovery/best-practices.d.ts.map +1 -0
  109. package/dist/tools/discovery/best-practices.js +499 -0
  110. package/dist/tools/discovery/best-practices.js.map +1 -0
  111. package/dist/tools/discovery/consolidate.d.ts +97 -0
  112. package/dist/tools/discovery/consolidate.d.ts.map +1 -0
  113. package/dist/tools/discovery/consolidate.js +200 -0
  114. package/dist/tools/discovery/consolidate.js.map +1 -0
  115. package/dist/tools/discovery/cost-estimator.d.ts +114 -0
  116. package/dist/tools/discovery/cost-estimator.d.ts.map +1 -0
  117. package/dist/tools/discovery/cost-estimator.js +273 -0
  118. package/dist/tools/discovery/cost-estimator.js.map +1 -0
  119. package/dist/tools/discovery/dependencies.d.ts +113 -0
  120. package/dist/tools/discovery/dependencies.d.ts.map +1 -0
  121. package/dist/tools/discovery/dependencies.js +258 -0
  122. package/dist/tools/discovery/dependencies.js.map +1 -0
  123. package/dist/tools/discovery/describe.d.ts +133 -0
  124. package/dist/tools/discovery/describe.d.ts.map +1 -0
  125. package/dist/tools/discovery/describe.js +208 -0
  126. package/dist/tools/discovery/describe.js.map +1 -0
  127. package/dist/tools/discovery/execute.d.ts +96 -0
  128. package/dist/tools/discovery/execute.d.ts.map +1 -0
  129. package/dist/tools/discovery/execute.js +220 -0
  130. package/dist/tools/discovery/execute.js.map +1 -0
  131. package/dist/tools/discovery/index-loader.d.ts +28 -0
  132. package/dist/tools/discovery/index-loader.d.ts.map +1 -0
  133. package/dist/tools/discovery/index-loader.js +72 -0
  134. package/dist/tools/discovery/index-loader.js.map +1 -0
  135. package/dist/tools/discovery/index.d.ts +363 -0
  136. package/dist/tools/discovery/index.d.ts.map +1 -0
  137. package/dist/tools/discovery/index.js +361 -0
  138. package/dist/tools/discovery/index.js.map +1 -0
  139. package/dist/tools/discovery/resolver.d.ts +119 -0
  140. package/dist/tools/discovery/resolver.d.ts.map +1 -0
  141. package/dist/tools/discovery/resolver.js +369 -0
  142. package/dist/tools/discovery/resolver.js.map +1 -0
  143. package/dist/tools/discovery/search.d.ts +41 -0
  144. package/dist/tools/discovery/search.d.ts.map +1 -0
  145. package/dist/tools/discovery/search.js +176 -0
  146. package/dist/tools/discovery/search.js.map +1 -0
  147. package/dist/tools/discovery/types.d.ts +91 -0
  148. package/dist/tools/discovery/types.d.ts.map +1 -0
  149. package/dist/tools/discovery/types.js +9 -0
  150. package/dist/tools/discovery/types.js.map +1 -0
  151. package/dist/tools/discovery/validate.d.ts +63 -0
  152. package/dist/tools/discovery/validate.d.ts.map +1 -0
  153. package/dist/tools/discovery/validate.js +239 -0
  154. package/dist/tools/discovery/validate.js.map +1 -0
  155. package/dist/tools/generated/admin_console_and_ui/index.d.ts +8 -0
  156. package/dist/tools/generated/admin_console_and_ui/index.d.ts.map +1 -0
  157. package/dist/tools/generated/admin_console_and_ui/index.js +265 -0
  158. package/dist/tools/generated/admin_console_and_ui/index.js.map +1 -0
  159. package/dist/tools/generated/api/index.d.ts +8 -0
  160. package/dist/tools/generated/api/index.d.ts.map +1 -0
  161. package/dist/tools/generated/api/index.js +7016 -0
  162. package/dist/tools/generated/api/index.js.map +1 -0
  163. package/dist/tools/generated/authentication/index.d.ts +8 -0
  164. package/dist/tools/generated/authentication/index.d.ts.map +1 -0
  165. package/dist/tools/generated/authentication/index.js +907 -0
  166. package/dist/tools/generated/authentication/index.js.map +1 -0
  167. package/dist/tools/generated/bigip/index.d.ts +8 -0
  168. package/dist/tools/generated/bigip/index.d.ts.map +1 -0
  169. package/dist/tools/generated/bigip/index.js +3152 -0
  170. package/dist/tools/generated/bigip/index.js.map +1 -0
  171. package/dist/tools/generated/billing_and_usage/index.d.ts +8 -0
  172. package/dist/tools/generated/billing_and_usage/index.d.ts.map +1 -0
  173. package/dist/tools/generated/billing_and_usage/index.js +2452 -0
  174. package/dist/tools/generated/billing_and_usage/index.js.map +1 -0
  175. package/dist/tools/generated/blindfold/index.d.ts +8 -0
  176. package/dist/tools/generated/blindfold/index.d.ts.map +1 -0
  177. package/dist/tools/generated/blindfold/index.js +3790 -0
  178. package/dist/tools/generated/blindfold/index.js.map +1 -0
  179. package/dist/tools/generated/bot_and_threat_defense/index.d.ts +8 -0
  180. package/dist/tools/generated/bot_and_threat_defense/index.d.ts.map +1 -0
  181. package/dist/tools/generated/bot_and_threat_defense/index.js +2746 -0
  182. package/dist/tools/generated/bot_and_threat_defense/index.js.map +1 -0
  183. package/dist/tools/generated/cdn/index.d.ts +8 -0
  184. package/dist/tools/generated/cdn/index.d.ts.map +1 -0
  185. package/dist/tools/generated/cdn/index.js +2686 -0
  186. package/dist/tools/generated/cdn/index.js.map +1 -0
  187. package/dist/tools/generated/ce_management/index.d.ts +8 -0
  188. package/dist/tools/generated/ce_management/index.d.ts.map +1 -0
  189. package/dist/tools/generated/ce_management/index.js +3812 -0
  190. package/dist/tools/generated/ce_management/index.js.map +1 -0
  191. package/dist/tools/generated/certificates/index.d.ts +8 -0
  192. package/dist/tools/generated/certificates/index.d.ts.map +1 -0
  193. package/dist/tools/generated/certificates/index.js +2547 -0
  194. package/dist/tools/generated/certificates/index.js.map +1 -0
  195. package/dist/tools/generated/cloud_infrastructure/index.d.ts +8 -0
  196. package/dist/tools/generated/cloud_infrastructure/index.d.ts.map +1 -0
  197. package/dist/tools/generated/cloud_infrastructure/index.js +3959 -0
  198. package/dist/tools/generated/cloud_infrastructure/index.js.map +1 -0
  199. package/dist/tools/generated/container_services/index.d.ts +8 -0
  200. package/dist/tools/generated/container_services/index.d.ts.map +1 -0
  201. package/dist/tools/generated/container_services/index.js +2018 -0
  202. package/dist/tools/generated/container_services/index.js.map +1 -0
  203. package/dist/tools/generated/data_and_privacy_security/index.d.ts +8 -0
  204. package/dist/tools/generated/data_and_privacy_security/index.d.ts.map +1 -0
  205. package/dist/tools/generated/data_and_privacy_security/index.js +1662 -0
  206. package/dist/tools/generated/data_and_privacy_security/index.js.map +1 -0
  207. package/dist/tools/generated/data_intelligence/index.d.ts +8 -0
  208. package/dist/tools/generated/data_intelligence/index.d.ts.map +1 -0
  209. package/dist/tools/generated/data_intelligence/index.js +1600 -0
  210. package/dist/tools/generated/data_intelligence/index.js.map +1 -0
  211. package/dist/tools/generated/ddos/index.d.ts +8 -0
  212. package/dist/tools/generated/ddos/index.d.ts.map +1 -0
  213. package/dist/tools/generated/ddos/index.js +8091 -0
  214. package/dist/tools/generated/ddos/index.js.map +1 -0
  215. package/dist/tools/generated/dependency-graph.json +26358 -0
  216. package/dist/tools/generated/dns/index.d.ts +8 -0
  217. package/dist/tools/generated/dns/index.d.ts.map +1 -0
  218. package/dist/tools/generated/dns/index.js +6096 -0
  219. package/dist/tools/generated/dns/index.js.map +1 -0
  220. package/dist/tools/generated/generative_ai/index.d.ts +8 -0
  221. package/dist/tools/generated/generative_ai/index.d.ts.map +1 -0
  222. package/dist/tools/generated/generative_ai/index.js +1019 -0
  223. package/dist/tools/generated/generative_ai/index.js.map +1 -0
  224. package/dist/tools/generated/managed_kubernetes/index.d.ts +8 -0
  225. package/dist/tools/generated/managed_kubernetes/index.d.ts.map +1 -0
  226. package/dist/tools/generated/managed_kubernetes/index.js +3368 -0
  227. package/dist/tools/generated/managed_kubernetes/index.js.map +1 -0
  228. package/dist/tools/generated/marketplace/index.d.ts +8 -0
  229. package/dist/tools/generated/marketplace/index.d.ts.map +1 -0
  230. package/dist/tools/generated/marketplace/index.js +4329 -0
  231. package/dist/tools/generated/marketplace/index.js.map +1 -0
  232. package/dist/tools/generated/network/index.d.ts +8 -0
  233. package/dist/tools/generated/network/index.d.ts.map +1 -0
  234. package/dist/tools/generated/network/index.js +12424 -0
  235. package/dist/tools/generated/network/index.js.map +1 -0
  236. package/dist/tools/generated/network_security/index.d.ts +8 -0
  237. package/dist/tools/generated/network_security/index.d.ts.map +1 -0
  238. package/dist/tools/generated/network_security/index.js +9637 -0
  239. package/dist/tools/generated/network_security/index.js.map +1 -0
  240. package/dist/tools/generated/nginx_one/index.d.ts +8 -0
  241. package/dist/tools/generated/nginx_one/index.d.ts.map +1 -0
  242. package/dist/tools/generated/nginx_one/index.js +1692 -0
  243. package/dist/tools/generated/nginx_one/index.js.map +1 -0
  244. package/dist/tools/generated/object_storage/index.d.ts +8 -0
  245. package/dist/tools/generated/object_storage/index.d.ts.map +1 -0
  246. package/dist/tools/generated/object_storage/index.js +996 -0
  247. package/dist/tools/generated/object_storage/index.js.map +1 -0
  248. package/dist/tools/generated/observability/index.d.ts +8 -0
  249. package/dist/tools/generated/observability/index.d.ts.map +1 -0
  250. package/dist/tools/generated/observability/index.js +5285 -0
  251. package/dist/tools/generated/observability/index.js.map +1 -0
  252. package/dist/tools/generated/rate_limiting/index.d.ts +8 -0
  253. package/dist/tools/generated/rate_limiting/index.d.ts.map +1 -0
  254. package/dist/tools/generated/rate_limiting/index.js +2108 -0
  255. package/dist/tools/generated/rate_limiting/index.js.map +1 -0
  256. package/dist/tools/generated/secops_and_incident_response/index.d.ts +8 -0
  257. package/dist/tools/generated/secops_and_incident_response/index.d.ts.map +1 -0
  258. package/dist/tools/generated/secops_and_incident_response/index.js +710 -0
  259. package/dist/tools/generated/secops_and_incident_response/index.js.map +1 -0
  260. package/dist/tools/generated/service_mesh/index.d.ts +8 -0
  261. package/dist/tools/generated/service_mesh/index.d.ts.map +1 -0
  262. package/dist/tools/generated/service_mesh/index.js +6062 -0
  263. package/dist/tools/generated/service_mesh/index.js.map +1 -0
  264. package/dist/tools/generated/shape/index.d.ts +8 -0
  265. package/dist/tools/generated/shape/index.d.ts.map +1 -0
  266. package/dist/tools/generated/shape/index.js +19381 -0
  267. package/dist/tools/generated/shape/index.js.map +1 -0
  268. package/dist/tools/generated/sites/index.d.ts +8 -0
  269. package/dist/tools/generated/sites/index.d.ts.map +1 -0
  270. package/dist/tools/generated/sites/index.js +13160 -0
  271. package/dist/tools/generated/sites/index.js.map +1 -0
  272. package/dist/tools/generated/statistics/index.d.ts +8 -0
  273. package/dist/tools/generated/statistics/index.d.ts.map +1 -0
  274. package/dist/tools/generated/statistics/index.js +8131 -0
  275. package/dist/tools/generated/statistics/index.js.map +1 -0
  276. package/dist/tools/generated/support/index.d.ts +8 -0
  277. package/dist/tools/generated/support/index.d.ts.map +1 -0
  278. package/dist/tools/generated/support/index.js +5608 -0
  279. package/dist/tools/generated/support/index.js.map +1 -0
  280. package/dist/tools/generated/telemetry_and_insights/index.d.ts +8 -0
  281. package/dist/tools/generated/telemetry_and_insights/index.d.ts.map +1 -0
  282. package/dist/tools/generated/telemetry_and_insights/index.js +2404 -0
  283. package/dist/tools/generated/telemetry_and_insights/index.js.map +1 -0
  284. package/dist/tools/generated/tenant_and_identity/index.d.ts +8 -0
  285. package/dist/tools/generated/tenant_and_identity/index.d.ts.map +1 -0
  286. package/dist/tools/generated/tenant_and_identity/index.js +18938 -0
  287. package/dist/tools/generated/tenant_and_identity/index.js.map +1 -0
  288. package/dist/tools/generated/threat_campaign/index.d.ts +8 -0
  289. package/dist/tools/generated/threat_campaign/index.d.ts.map +1 -0
  290. package/dist/tools/generated/threat_campaign/index.js +102 -0
  291. package/dist/tools/generated/threat_campaign/index.js.map +1 -0
  292. package/dist/tools/generated/users/index.d.ts +8 -0
  293. package/dist/tools/generated/users/index.d.ts.map +1 -0
  294. package/dist/tools/generated/users/index.js +1515 -0
  295. package/dist/tools/generated/users/index.js.map +1 -0
  296. package/dist/tools/generated/virtual/index.d.ts +8 -0
  297. package/dist/tools/generated/virtual/index.d.ts.map +1 -0
  298. package/dist/tools/generated/virtual/index.js +11263 -0
  299. package/dist/tools/generated/virtual/index.js.map +1 -0
  300. package/dist/tools/generated/vpm_and_node_management/index.d.ts +8 -0
  301. package/dist/tools/generated/vpm_and_node_management/index.d.ts.map +1 -0
  302. package/dist/tools/generated/vpm_and_node_management/index.js +88 -0
  303. package/dist/tools/generated/vpm_and_node_management/index.js.map +1 -0
  304. package/dist/tools/generated/waf/index.d.ts +8 -0
  305. package/dist/tools/generated/waf/index.d.ts.map +1 -0
  306. package/dist/tools/generated/waf/index.js +4586 -0
  307. package/dist/tools/generated/waf/index.js.map +1 -0
  308. package/dist/tools/index.d.ts +7 -0
  309. package/dist/tools/index.d.ts.map +1 -0
  310. package/dist/tools/index.js +6 -0
  311. package/dist/tools/index.js.map +1 -0
  312. package/dist/tools/registry.d.ts +27 -0
  313. package/dist/tools/registry.d.ts.map +1 -0
  314. package/dist/tools/registry.js +115 -0
  315. package/dist/tools/registry.js.map +1 -0
  316. package/dist/utils/error-handling.d.ts +109 -0
  317. package/dist/utils/error-handling.d.ts.map +1 -0
  318. package/dist/utils/error-handling.js +239 -0
  319. package/dist/utils/error-handling.js.map +1 -0
  320. package/dist/utils/index.d.ts +7 -0
  321. package/dist/utils/index.d.ts.map +1 -0
  322. package/dist/utils/index.js +6 -0
  323. package/dist/utils/index.js.map +1 -0
  324. package/dist/utils/logging.d.ts +75 -0
  325. package/dist/utils/logging.d.ts.map +1 -0
  326. package/dist/utils/logging.js +131 -0
  327. package/dist/utils/logging.js.map +1 -0
  328. package/manifest.json +143 -0
  329. package/package.json +110 -0
@@ -0,0 +1,704 @@
1
+ /**
2
+ * OpenAPI Specification Parser
3
+ *
4
+ * Parses F5 Distributed Cloud OpenAPI specifications and extracts
5
+ * operation metadata for MCP tool generation.
6
+ */
7
+ import { readFileSync, readdirSync, existsSync } from "fs";
8
+ import { join, extname, relative, basename } from "path";
9
+ import YAML from "yaml";
10
+ import { z } from "zod";
11
+ import { transformText, extractDomainFromPath, extractResourceFromPath, methodToOperation, generateToolName, } from "./naming/index.js";
12
+ import { normalizeTitleAcronyms } from "./naming/acronyms.js";
13
+ import { normalizeExamples } from "./transformers/index.js";
14
+ import { logger } from "../utils/logging.js";
15
+ import { extractOperationDependencies, mapResourceToSubscriptions, formatAddonDisplayName, extractTierFromAddon, resolveResourceDomain, } from "./dependency-extractor.js";
16
+ /**
17
+ * OpenAPI Schema Types
18
+ */
19
+ const OpenApiParameterSchema = z.object({
20
+ name: z.string(),
21
+ in: z.enum(["path", "query", "header", "cookie"]),
22
+ required: z.boolean().optional(),
23
+ description: z.string().optional(),
24
+ schema: z.record(z.string(), z.unknown()).optional(),
25
+ // Rich metadata fields from enriched specs
26
+ "x-displayname": z.string().optional(),
27
+ "x-ves-example": z.string().optional(),
28
+ "x-ves-validation-rules": z.record(z.string(), z.string()).optional(),
29
+ "x-ves-required": z.boolean().optional(),
30
+ });
31
+ const OpenApiRequestBodySchema = z.object({
32
+ required: z.boolean().optional(),
33
+ description: z.string().optional(),
34
+ content: z
35
+ .record(z.string(), z.object({
36
+ schema: z.record(z.string(), z.unknown()).optional(),
37
+ }))
38
+ .optional(),
39
+ });
40
+ const OpenApiResponseSchema = z.object({
41
+ description: z.string().optional(),
42
+ content: z
43
+ .record(z.string(), z.object({
44
+ schema: z.record(z.string(), z.unknown()).optional(),
45
+ }))
46
+ .optional(),
47
+ });
48
+ /**
49
+ * Side effects schema for operations
50
+ */
51
+ const SideEffectsSchema = z.object({
52
+ creates: z.array(z.string()).optional(),
53
+ modifies: z.array(z.string()).optional(),
54
+ deletes: z.array(z.string()).optional(),
55
+ });
56
+ /**
57
+ * Operation metadata schema from enriched specs
58
+ */
59
+ const OperationMetadataSchema = z.object({
60
+ purpose: z.string().optional(),
61
+ required_fields: z.array(z.string()).optional(),
62
+ optional_fields: z.array(z.string()).optional(),
63
+ field_docs: z.record(z.string(), z.unknown()).optional(),
64
+ conditions: z
65
+ .object({
66
+ prerequisites: z.array(z.string()).optional(),
67
+ postconditions: z.array(z.string()).optional(),
68
+ })
69
+ .optional(),
70
+ side_effects: SideEffectsSchema.optional(),
71
+ danger_level: z.enum(["low", "medium", "high"]).optional(),
72
+ confirmation_required: z.boolean().optional(),
73
+ common_errors: z
74
+ .array(z.object({
75
+ code: z.number(),
76
+ message: z.string(),
77
+ solution: z.string().optional(),
78
+ }))
79
+ .optional(),
80
+ performance_impact: z
81
+ .object({
82
+ latency: z.string().optional(),
83
+ resource_usage: z.string().optional(),
84
+ })
85
+ .optional(),
86
+ });
87
+ const OpenApiOperationSchema = z.object({
88
+ operationId: z.string().optional(),
89
+ summary: z.string().optional(),
90
+ description: z.string().optional(),
91
+ tags: z.array(z.string()).optional(),
92
+ parameters: z.array(OpenApiParameterSchema).optional(),
93
+ requestBody: OpenApiRequestBodySchema.optional(),
94
+ responses: z.record(z.string(), OpenApiResponseSchema).optional(),
95
+ security: z.array(z.record(z.string(), z.array(z.string()))).optional(),
96
+ // Existing x-* field
97
+ "x-ves-proto-rpc": z.string().optional(),
98
+ // Rich metadata fields from enriched specs v1.0.63
99
+ "x-ves-danger-level": z.enum(["low", "medium", "high"]).optional(),
100
+ "x-ves-side-effects": SideEffectsSchema.optional(),
101
+ "x-ves-required-fields": z.array(z.string()).optional(),
102
+ "x-ves-confirmation-required": z.boolean().optional(),
103
+ "x-ves-operation-metadata": OperationMetadataSchema.optional(),
104
+ });
105
+ const OpenApiPathItemSchema = z.object({
106
+ get: OpenApiOperationSchema.optional(),
107
+ post: OpenApiOperationSchema.optional(),
108
+ put: OpenApiOperationSchema.optional(),
109
+ delete: OpenApiOperationSchema.optional(),
110
+ patch: OpenApiOperationSchema.optional(),
111
+ parameters: z.array(OpenApiParameterSchema).optional(),
112
+ });
113
+ const OpenApiSpecSchema = z.object({
114
+ openapi: z.string().optional(),
115
+ swagger: z.string().optional(),
116
+ info: z.object({
117
+ title: z.string(),
118
+ version: z.string(),
119
+ description: z.string().optional(),
120
+ }),
121
+ paths: z.record(z.string(), OpenApiPathItemSchema).optional(),
122
+ components: z
123
+ .object({
124
+ schemas: z.record(z.string(), z.unknown()).optional(),
125
+ securitySchemes: z.record(z.string(), z.unknown()).optional(),
126
+ })
127
+ .optional(),
128
+ });
129
+ /**
130
+ * Parse a single OpenAPI specification file
131
+ * @param filePath - Absolute path to the spec file
132
+ * @param basePath - Optional base path for creating relative sourceFile paths (for deterministic output)
133
+ * @deprecated Use parseDomainSpecFile() for pre-enriched domain specs from robinmordasiewicz/f5xc-api-enriched
134
+ */
135
+ export function parseSpecFile(filePath, basePath) {
136
+ try {
137
+ const content = readFileSync(filePath, "utf-8");
138
+ const ext = extname(filePath).toLowerCase();
139
+ let rawSpec;
140
+ if (ext === ".yaml" || ext === ".yml") {
141
+ rawSpec = YAML.parse(content);
142
+ }
143
+ else if (ext === ".json") {
144
+ rawSpec = JSON.parse(content);
145
+ }
146
+ else {
147
+ logger.warn(`Unsupported file extension: ${ext}`, { file: filePath });
148
+ return null;
149
+ }
150
+ // Validate spec structure
151
+ const parseResult = OpenApiSpecSchema.safeParse(rawSpec);
152
+ if (!parseResult.success) {
153
+ logger.debug(`Invalid OpenAPI spec: ${filePath}`, {
154
+ errors: parseResult.error.issues,
155
+ });
156
+ return null;
157
+ }
158
+ const spec = parseResult.data;
159
+ // Use relative path for sourceFile to ensure deterministic output across environments
160
+ const sourceFile = basePath ? relative(basePath, filePath) : filePath;
161
+ const operations = extractOperations(spec, sourceFile);
162
+ return {
163
+ filePath,
164
+ title: transformText(normalizeTitleAcronyms(spec.info.title)),
165
+ version: spec.info.version,
166
+ operations,
167
+ schemas: spec.components?.schemas ?? {},
168
+ };
169
+ }
170
+ catch (error) {
171
+ logger.error(`Failed to parse spec file: ${filePath}`, {
172
+ error: error instanceof Error ? error.message : String(error),
173
+ });
174
+ return null;
175
+ }
176
+ }
177
+ /**
178
+ * Extract curl examples from component schemas
179
+ * Returns a map of normalized API path to curl example
180
+ */
181
+ function extractCurlExamplesFromSchemas(schemas) {
182
+ const curlExamples = new Map();
183
+ if (!schemas) {
184
+ return curlExamples;
185
+ }
186
+ for (const schema of Object.values(schemas)) {
187
+ if (!schema || typeof schema !== "object") {
188
+ continue;
189
+ }
190
+ const schemaObj = schema;
191
+ const minConfig = schemaObj["x-ves-minimum-configuration"];
192
+ if (!minConfig || typeof minConfig !== "object") {
193
+ continue;
194
+ }
195
+ const config = minConfig;
196
+ const exampleCurl = config["example_curl"];
197
+ if (typeof exampleCurl !== "string") {
198
+ continue;
199
+ }
200
+ // Extract the API path from the curl command
201
+ // Pattern: $F5XC_API_URL/api/... or "$F5XC_API_URL/api/..."
202
+ const pathMatch = exampleCurl.match(/\$F5XC_API_URL(\/api\/[^\s"\\]+)/);
203
+ if (!pathMatch || !pathMatch[1]) {
204
+ continue;
205
+ }
206
+ const apiPath = pathMatch[1];
207
+ // Normalize the path by replacing concrete namespace/name values with placeholders
208
+ // /api/config/namespaces/default/app_firewalls -> /api/config/namespaces/{namespace}/app_firewalls
209
+ const normalizedPath = apiPath
210
+ .replace(/\/namespaces\/[^/]+\//, "/namespaces/{namespace}/")
211
+ .replace(/\/system\//, "/{system_namespace}/");
212
+ // Store if not already present (first match wins)
213
+ if (!curlExamples.has(normalizedPath)) {
214
+ curlExamples.set(normalizedPath, exampleCurl);
215
+ }
216
+ }
217
+ return curlExamples;
218
+ }
219
+ /**
220
+ * Match an operation path to a curl example
221
+ * Handles path template differences like {metadata.namespace} vs {namespace}
222
+ */
223
+ function matchCurlExample(operationPath, curlExamples) {
224
+ // Normalize the operation path similarly
225
+ const normalizedOpPath = operationPath
226
+ .replace(/\{metadata\.namespace\}/g, "{namespace}")
227
+ .replace(/\{metadata\.name\}/g, "{name}")
228
+ .replace(/\{[^}]+\}/g, (match) => {
229
+ // Keep just the last part of dotted names
230
+ const simplified = match.replace(/\{[^.]+\./g, "{");
231
+ return simplified;
232
+ });
233
+ // Try exact match first
234
+ if (curlExamples.has(normalizedOpPath)) {
235
+ return curlExamples.get(normalizedOpPath);
236
+ }
237
+ // Try matching the base path (without trailing name/id parameter)
238
+ // e.g., /api/config/namespaces/{namespace}/resources/{name} -> /api/config/namespaces/{namespace}/resources
239
+ const basePathMatch = normalizedOpPath.match(/^(.+?)(?:\/\{[^}]+\})?$/);
240
+ if (basePathMatch && basePathMatch[1]) {
241
+ const basePath = basePathMatch[1];
242
+ if (curlExamples.has(basePath)) {
243
+ return curlExamples.get(basePath);
244
+ }
245
+ }
246
+ return null;
247
+ }
248
+ /**
249
+ * Extract operations from parsed spec
250
+ */
251
+ function extractOperations(spec, sourceFile, curlExamples) {
252
+ const operations = [];
253
+ if (!spec.paths) {
254
+ return operations;
255
+ }
256
+ // Extract curl examples from schemas if not provided
257
+ const curlMap = curlExamples ??
258
+ extractCurlExamplesFromSchemas(spec.components?.schemas);
259
+ const httpMethods = ["get", "post", "put", "delete", "patch"];
260
+ // Sort paths alphabetically for deterministic output (use < > for locale-independent sorting)
261
+ const sortedPaths = Object.entries(spec.paths).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
262
+ for (const [path, pathItem] of sortedPaths) {
263
+ // Check if path has a name parameter (indicates single resource operations)
264
+ const hasNameParam = path.includes("{name}") || path.includes("{id}");
265
+ // Get path-level parameters
266
+ const pathLevelParams = pathItem.parameters ?? [];
267
+ for (const method of httpMethods) {
268
+ const operation = pathItem[method];
269
+ if (!operation) {
270
+ continue;
271
+ }
272
+ // Determine operation type and generate tool name
273
+ const operationType = methodToOperation(method, hasNameParam);
274
+ const domain = extractDomainFromPath(path);
275
+ const resource = extractResourceFromPath(path);
276
+ const toolName = generateToolName(domain, resource, operationType);
277
+ // Combine path and operation parameters
278
+ const allParams = [...pathLevelParams, ...(operation.parameters ?? [])];
279
+ const pathParameters = allParams.filter((p) => p.in === "path");
280
+ const queryParameters = allParams.filter((p) => p.in === "query");
281
+ // Extract request body schema
282
+ let requestBodySchema = null;
283
+ if (operation.requestBody?.content) {
284
+ const jsonContent = operation.requestBody.content["application/json"];
285
+ if (jsonContent?.schema) {
286
+ requestBodySchema = jsonContent.schema;
287
+ }
288
+ }
289
+ // Extract response schema (from 200 or first success response)
290
+ let responseSchema = null;
291
+ if (operation.responses) {
292
+ const successResponse = operation.responses["200"] ?? operation.responses["201"];
293
+ if (successResponse?.content) {
294
+ const jsonContent = successResponse.content["application/json"];
295
+ if (jsonContent?.schema) {
296
+ responseSchema = jsonContent.schema;
297
+ }
298
+ }
299
+ }
300
+ // Collect required parameters
301
+ const requiredParams = [];
302
+ for (const param of allParams) {
303
+ if (param.required) {
304
+ requiredParams.push(param.name);
305
+ }
306
+ }
307
+ if (operation.requestBody?.required) {
308
+ requiredParams.push("body");
309
+ }
310
+ // Transform text content and normalize examples
311
+ const summary = transformText(normalizeTitleAcronyms(operation.summary ?? `${operationType} ${resource}`));
312
+ const description = normalizeExamples(transformText(operation.description ?? ""));
313
+ // Normalize parameter descriptions
314
+ const normalizedPathParams = pathParameters.map((p) => ({
315
+ ...p,
316
+ description: p.description ? normalizeExamples(p.description) : p.description,
317
+ }));
318
+ const normalizedQueryParams = queryParameters.map((p) => ({
319
+ ...p,
320
+ description: p.description ? normalizeExamples(p.description) : p.description,
321
+ }));
322
+ // Extract rich metadata from x-* fields (if present in older specs)
323
+ const dangerLevel = operation["x-ves-danger-level"] ?? null;
324
+ const sideEffects = operation["x-ves-side-effects"] ?? null;
325
+ const requiredFields = operation["x-ves-required-fields"] ?? [];
326
+ const confirmationRequired = operation["x-ves-confirmation-required"] ?? false;
327
+ const operationMetadata = operation["x-ves-operation-metadata"] ?? null;
328
+ // Extract parameter-level metadata
329
+ const parameterExamples = {};
330
+ const validationRules = {};
331
+ let displayName = null;
332
+ for (const param of allParams) {
333
+ if (param["x-ves-example"]) {
334
+ parameterExamples[param.name] = param["x-ves-example"];
335
+ }
336
+ if (param["x-ves-validation-rules"]) {
337
+ validationRules[param.name] = param["x-ves-validation-rules"];
338
+ }
339
+ }
340
+ // Get path-level displayname if available
341
+ const pathItemAny = pathItem;
342
+ if (pathItemAny["x-displayname"] && typeof pathItemAny["x-displayname"] === "string") {
343
+ displayName = pathItemAny["x-displayname"];
344
+ }
345
+ // Match curl example for this operation
346
+ const curlExample = matchCurlExample(path, curlMap);
347
+ // Extract dependency intelligence (v1.0.67)
348
+ const componentSchemas = spec.components?.schemas ?? {};
349
+ const extractedDeps = extractOperationDependencies(requestBodySchema, componentSchemas);
350
+ // Resolve domain for each reference
351
+ const dependencies = extractedDeps.references.map((ref) => ({
352
+ ...ref,
353
+ domain: ref.domain || resolveResourceDomain(ref.resourceType),
354
+ }));
355
+ // Get subscription requirements based on resource and domain
356
+ const subscriptionIds = mapResourceToSubscriptions(resource, domain);
357
+ const subscriptionRequirements = subscriptionIds.map((id) => ({
358
+ addonService: id,
359
+ displayName: formatAddonDisplayName(id),
360
+ tier: extractTierFromAddon(id),
361
+ required: false, // Heuristic mapping, not strictly required
362
+ }));
363
+ operations.push({
364
+ toolName,
365
+ method: method.toUpperCase(),
366
+ path,
367
+ operation: operationType,
368
+ domain,
369
+ resource,
370
+ summary,
371
+ description,
372
+ pathParameters: normalizedPathParams,
373
+ queryParameters: normalizedQueryParams,
374
+ requestBodySchema,
375
+ responseSchema,
376
+ requiredParams,
377
+ operationId: operation.operationId ?? null,
378
+ tags: operation.tags ?? [],
379
+ sourceFile,
380
+ // Rich metadata (defaults for backward compatibility)
381
+ displayName,
382
+ dangerLevel,
383
+ sideEffects,
384
+ requiredFields,
385
+ confirmationRequired,
386
+ parameterExamples,
387
+ validationRules,
388
+ operationMetadata,
389
+ curlExample,
390
+ // Dependency intelligence (v1.0.67)
391
+ dependencies,
392
+ oneOfGroups: extractedDeps.oneOfGroups,
393
+ subscriptionRequirements,
394
+ });
395
+ }
396
+ }
397
+ return operations;
398
+ }
399
+ /**
400
+ * Parse all spec files in a directory
401
+ * @deprecated Use parseDomainsDirectory() for pre-enriched domain specs from robinmordasiewicz/f5xc-api-enriched
402
+ */
403
+ export function parseSpecDirectory(dirPath) {
404
+ const specs = [];
405
+ if (!existsSync(dirPath)) {
406
+ logger.warn(`Spec directory does not exist: ${dirPath}`);
407
+ return specs;
408
+ }
409
+ // Use parent of dirPath as base for relative paths (makes paths like "raw/filename.json")
410
+ const basePath = join(dirPath, "..");
411
+ function scanDir(currentDir) {
412
+ const entries = readdirSync(currentDir, { withFileTypes: true });
413
+ // Sort entries alphabetically for deterministic output across different filesystems (locale-independent)
414
+ entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
415
+ for (const entry of entries) {
416
+ const fullPath = join(currentDir, entry.name);
417
+ if (entry.isDirectory()) {
418
+ scanDir(fullPath);
419
+ }
420
+ else if (entry.name.endsWith(".json") ||
421
+ entry.name.endsWith(".yaml") ||
422
+ entry.name.endsWith(".yml")) {
423
+ const spec = parseSpecFile(fullPath, basePath);
424
+ if (spec && spec.operations.length > 0) {
425
+ specs.push(spec);
426
+ }
427
+ }
428
+ }
429
+ }
430
+ scanDir(dirPath);
431
+ logger.info(`Parsed ${specs.length} spec files`, {
432
+ totalOperations: specs.reduce((sum, s) => sum + s.operations.length, 0),
433
+ });
434
+ return specs;
435
+ }
436
+ /**
437
+ * Get all unique operations across all specs
438
+ */
439
+ export function getAllOperations(specs) {
440
+ const operationsMap = new Map();
441
+ for (const spec of specs) {
442
+ for (const operation of spec.operations) {
443
+ // Use tool name as unique key to deduplicate
444
+ if (!operationsMap.has(operation.toolName)) {
445
+ operationsMap.set(operation.toolName, operation);
446
+ }
447
+ }
448
+ }
449
+ // Sort by toolName for deterministic output (locale-independent)
450
+ return Array.from(operationsMap.values()).sort((a, b) => a.toolName < b.toolName ? -1 : a.toolName > b.toolName ? 1 : 0);
451
+ }
452
+ /**
453
+ * Group operations by domain
454
+ */
455
+ export function groupOperationsByDomain(operations) {
456
+ const grouped = new Map();
457
+ for (const operation of operations) {
458
+ const domain = operation.domain;
459
+ if (!grouped.has(domain)) {
460
+ grouped.set(domain, []);
461
+ }
462
+ grouped.get(domain).push(operation);
463
+ }
464
+ // Sort operations within each domain by toolName for deterministic output (locale-independent)
465
+ for (const ops of grouped.values()) {
466
+ ops.sort((a, b) => (a.toolName < b.toolName ? -1 : a.toolName > b.toolName ? 1 : 0));
467
+ }
468
+ // Return a new Map with sorted domain keys for deterministic iteration order
469
+ const sortedGrouped = new Map();
470
+ const sortedDomains = Array.from(grouped.keys()).sort();
471
+ for (const domain of sortedDomains) {
472
+ sortedGrouped.set(domain, grouped.get(domain));
473
+ }
474
+ return sortedGrouped;
475
+ }
476
+ /**
477
+ * Extract operations from an enriched domain spec (pre-normalized upstream)
478
+ * This skips transformation since enriched specs are already normalized
479
+ */
480
+ function extractDomainOperations(spec, domain, sourceFile, curlExamples) {
481
+ const operations = [];
482
+ if (!spec.paths) {
483
+ return operations;
484
+ }
485
+ // Extract curl examples from schemas if not provided
486
+ const curlMap = curlExamples ??
487
+ extractCurlExamplesFromSchemas(spec.components?.schemas);
488
+ const httpMethods = ["get", "post", "put", "delete", "patch"];
489
+ // Sort paths alphabetically for deterministic output
490
+ const sortedPaths = Object.entries(spec.paths).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
491
+ for (const [path, pathItem] of sortedPaths) {
492
+ // Check if path has a name parameter (indicates single resource operations)
493
+ const hasNameParam = path.includes("{name}") || path.includes("{id}");
494
+ // Get path-level parameters
495
+ const pathLevelParams = pathItem.parameters ?? [];
496
+ for (const method of httpMethods) {
497
+ const operation = pathItem[method];
498
+ if (!operation) {
499
+ continue;
500
+ }
501
+ // Determine operation type and generate tool name
502
+ const operationType = methodToOperation(method, hasNameParam);
503
+ const resource = extractResourceFromPath(path);
504
+ const toolName = generateToolName(domain, resource, operationType);
505
+ // Combine path and operation parameters
506
+ const allParams = [...pathLevelParams, ...(operation.parameters ?? [])];
507
+ const pathParameters = allParams.filter((p) => p.in === "path");
508
+ const queryParameters = allParams.filter((p) => p.in === "query");
509
+ // Extract request body schema
510
+ let requestBodySchema = null;
511
+ if (operation.requestBody?.content) {
512
+ const jsonContent = operation.requestBody.content["application/json"];
513
+ if (jsonContent?.schema) {
514
+ requestBodySchema = jsonContent.schema;
515
+ }
516
+ }
517
+ // Extract response schema (from 200 or first success response)
518
+ let responseSchema = null;
519
+ if (operation.responses) {
520
+ const successResponse = operation.responses["200"] ?? operation.responses["201"];
521
+ if (successResponse?.content) {
522
+ const jsonContent = successResponse.content["application/json"];
523
+ if (jsonContent?.schema) {
524
+ responseSchema = jsonContent.schema;
525
+ }
526
+ }
527
+ }
528
+ // Collect required parameters
529
+ const requiredParams = [];
530
+ for (const param of allParams) {
531
+ if (param.required) {
532
+ requiredParams.push(param.name);
533
+ }
534
+ }
535
+ if (operation.requestBody?.required) {
536
+ requiredParams.push("body");
537
+ }
538
+ // Use content as-is - enriched specs are already normalized
539
+ const summary = operation.summary ?? `${operationType} ${resource}`;
540
+ const description = operation.description ?? "";
541
+ // Extract rich metadata from x-* fields (v1.0.63 enriched specs)
542
+ const dangerLevel = operation["x-ves-danger-level"] ?? null;
543
+ const sideEffects = operation["x-ves-side-effects"] ?? null;
544
+ const requiredFields = operation["x-ves-required-fields"] ?? [];
545
+ const confirmationRequired = operation["x-ves-confirmation-required"] ?? false;
546
+ const operationMetadata = operation["x-ves-operation-metadata"] ?? null;
547
+ // Extract parameter-level metadata (examples, validation rules, displaynames)
548
+ const parameterExamples = {};
549
+ const validationRules = {};
550
+ let displayName = null;
551
+ for (const param of allParams) {
552
+ // Extract parameter examples
553
+ if (param["x-ves-example"]) {
554
+ parameterExamples[param.name] = param["x-ves-example"];
555
+ }
556
+ // Extract validation rules
557
+ if (param["x-ves-validation-rules"]) {
558
+ validationRules[param.name] = param["x-ves-validation-rules"];
559
+ }
560
+ }
561
+ // Get path-level displayname if available (from pathItem)
562
+ const pathItemAny = pathItem;
563
+ if (pathItemAny["x-displayname"] && typeof pathItemAny["x-displayname"] === "string") {
564
+ displayName = pathItemAny["x-displayname"];
565
+ }
566
+ // Match curl example for this operation
567
+ const curlExample = matchCurlExample(path, curlMap);
568
+ // Extract dependency intelligence (v1.0.67)
569
+ const componentSchemas = spec.components?.schemas ?? {};
570
+ const extractedDeps = extractOperationDependencies(requestBodySchema, componentSchemas);
571
+ // Resolve domain for each reference
572
+ const dependencies = extractedDeps.references.map((ref) => ({
573
+ ...ref,
574
+ domain: ref.domain || resolveResourceDomain(ref.resourceType),
575
+ }));
576
+ // Get subscription requirements based on resource and domain
577
+ const subscriptionIds = mapResourceToSubscriptions(resource, domain);
578
+ const subscriptionRequirements = subscriptionIds.map((id) => ({
579
+ addonService: id,
580
+ displayName: formatAddonDisplayName(id),
581
+ tier: extractTierFromAddon(id),
582
+ required: false, // Heuristic mapping, not strictly required
583
+ }));
584
+ operations.push({
585
+ toolName,
586
+ method: method.toUpperCase(),
587
+ path,
588
+ operation: operationType,
589
+ domain,
590
+ resource,
591
+ summary,
592
+ description,
593
+ pathParameters,
594
+ queryParameters,
595
+ requestBodySchema,
596
+ responseSchema,
597
+ requiredParams,
598
+ operationId: operation.operationId ?? null,
599
+ tags: operation.tags ?? [],
600
+ sourceFile,
601
+ // Rich metadata from enriched specs
602
+ displayName,
603
+ dangerLevel,
604
+ sideEffects,
605
+ requiredFields,
606
+ confirmationRequired,
607
+ parameterExamples,
608
+ validationRules,
609
+ operationMetadata,
610
+ curlExample,
611
+ // Dependency intelligence (v1.0.67)
612
+ dependencies,
613
+ oneOfGroups: extractedDeps.oneOfGroups,
614
+ subscriptionRequirements,
615
+ });
616
+ }
617
+ }
618
+ return operations;
619
+ }
620
+ /**
621
+ * Parse a single enriched domain specification file
622
+ * Domain is derived from the filename (e.g., load_balancer.json → load_balancer)
623
+ *
624
+ * @param filePath - Absolute path to the domain spec file
625
+ * @param basePath - Optional base path for creating relative sourceFile paths
626
+ */
627
+ export function parseDomainSpecFile(filePath, basePath) {
628
+ try {
629
+ const content = readFileSync(filePath, "utf-8");
630
+ const ext = extname(filePath).toLowerCase();
631
+ if (ext !== ".json") {
632
+ logger.warn(`Domain specs must be JSON: ${ext}`, { file: filePath });
633
+ return null;
634
+ }
635
+ const rawSpec = JSON.parse(content);
636
+ // Validate spec structure
637
+ const parseResult = OpenApiSpecSchema.safeParse(rawSpec);
638
+ if (!parseResult.success) {
639
+ logger.debug(`Invalid OpenAPI spec: ${filePath}`, {
640
+ errors: parseResult.error.issues,
641
+ });
642
+ return null;
643
+ }
644
+ const spec = parseResult.data;
645
+ // Derive domain from filename (load_balancer.json → load_balancer)
646
+ const filename = basename(filePath, ext);
647
+ const domain = filename.replace(/-/g, "_");
648
+ // Use relative path for sourceFile to ensure deterministic output
649
+ const sourceFile = basePath ? relative(basePath, filePath) : filePath;
650
+ // Extract operations using domain-specific function (no transformations)
651
+ const operations = extractDomainOperations(spec, domain, sourceFile);
652
+ // Title is already normalized in enriched specs
653
+ return {
654
+ filePath,
655
+ title: spec.info.title,
656
+ version: spec.info.version,
657
+ operations,
658
+ schemas: spec.components?.schemas ?? {},
659
+ };
660
+ }
661
+ catch (error) {
662
+ logger.error(`Failed to parse domain spec file: ${filePath}`, {
663
+ error: error instanceof Error ? error.message : String(error),
664
+ });
665
+ return null;
666
+ }
667
+ }
668
+ /**
669
+ * Parse all enriched domain specs from a directory
670
+ * This is the primary entry point for the new enriched spec format
671
+ *
672
+ * @param dirPath - Path to the domains directory (specs/domains/)
673
+ */
674
+ export function parseDomainsDirectory(dirPath) {
675
+ const specs = [];
676
+ if (!existsSync(dirPath)) {
677
+ logger.warn(`Domains directory does not exist: ${dirPath}`);
678
+ return specs;
679
+ }
680
+ // Use parent of dirPath as base for relative paths (makes paths like "domains/filename.json")
681
+ const basePath = join(dirPath, "..");
682
+ const entries = readdirSync(dirPath, { withFileTypes: true });
683
+ // Sort entries alphabetically for deterministic output
684
+ entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
685
+ for (const entry of entries) {
686
+ if (entry.isDirectory()) {
687
+ continue;
688
+ }
689
+ if (!entry.name.endsWith(".json")) {
690
+ continue;
691
+ }
692
+ const fullPath = join(dirPath, entry.name);
693
+ const spec = parseDomainSpecFile(fullPath, basePath);
694
+ if (spec && spec.operations.length > 0) {
695
+ specs.push(spec);
696
+ }
697
+ }
698
+ logger.info(`Parsed ${specs.length} domain spec files`, {
699
+ totalOperations: specs.reduce((sum, s) => sum + s.operations.length, 0),
700
+ domains: specs.map((s) => basename(s.filePath, ".json")),
701
+ });
702
+ return specs;
703
+ }
704
+ //# sourceMappingURL=openapi-parser.js.map