@copilotkit/aimock 1.42.1 → 1.44.0

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 (442) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +65 -0
  4. package/README.md +108 -16
  5. package/dist/agui-handler.d.ts.map +1 -1
  6. package/dist/agui-mock.cjs +1 -1
  7. package/dist/agui-mock.cjs.map +1 -1
  8. package/dist/agui-mock.d.cts.map +1 -1
  9. package/dist/agui-mock.d.ts.map +1 -1
  10. package/dist/agui-mock.js +1 -1
  11. package/dist/agui-mock.js.map +1 -1
  12. package/dist/agui-recorder.cjs +33 -11
  13. package/dist/agui-recorder.cjs.map +1 -1
  14. package/dist/agui-recorder.d.cts +10 -1
  15. package/dist/agui-recorder.d.cts.map +1 -1
  16. package/dist/agui-recorder.d.ts +10 -1
  17. package/dist/agui-recorder.d.ts.map +1 -1
  18. package/dist/agui-recorder.js +33 -11
  19. package/dist/agui-recorder.js.map +1 -1
  20. package/dist/agui-types.d.cts +1 -0
  21. package/dist/agui-types.d.cts.map +1 -1
  22. package/dist/agui-types.d.ts +1 -0
  23. package/dist/agui-types.d.ts.map +1 -1
  24. package/dist/aimock-cli.cjs +38 -10
  25. package/dist/aimock-cli.cjs.map +1 -1
  26. package/dist/aimock-cli.d.cts +5 -0
  27. package/dist/aimock-cli.d.cts.map +1 -1
  28. package/dist/aimock-cli.d.ts +5 -0
  29. package/dist/aimock-cli.d.ts.map +1 -1
  30. package/dist/aimock-cli.js +38 -10
  31. package/dist/aimock-cli.js.map +1 -1
  32. package/dist/batches.cjs +458 -0
  33. package/dist/batches.cjs.map +1 -0
  34. package/dist/batches.d.cts +57 -0
  35. package/dist/batches.d.cts.map +1 -0
  36. package/dist/batches.d.ts +57 -0
  37. package/dist/batches.d.ts.map +1 -0
  38. package/dist/batches.js +453 -0
  39. package/dist/batches.js.map +1 -0
  40. package/dist/bedrock-converse.cjs +44 -12
  41. package/dist/bedrock-converse.cjs.map +1 -1
  42. package/dist/bedrock-converse.d.cts.map +1 -1
  43. package/dist/bedrock-converse.d.ts.map +1 -1
  44. package/dist/bedrock-converse.js +46 -14
  45. package/dist/bedrock-converse.js.map +1 -1
  46. package/dist/bedrock.cjs +10 -8
  47. package/dist/bedrock.cjs.map +1 -1
  48. package/dist/bedrock.d.cts.map +1 -1
  49. package/dist/bedrock.d.ts.map +1 -1
  50. package/dist/bedrock.js +12 -10
  51. package/dist/bedrock.js.map +1 -1
  52. package/dist/byteplus-video.cjs +3 -3
  53. package/dist/byteplus-video.cjs.map +1 -1
  54. package/dist/byteplus-video.js +4 -4
  55. package/dist/byteplus-video.js.map +1 -1
  56. package/dist/chaos.cjs +625 -72
  57. package/dist/chaos.cjs.map +1 -1
  58. package/dist/chaos.d.cts +164 -7
  59. package/dist/chaos.d.cts.map +1 -1
  60. package/dist/chaos.d.ts +164 -7
  61. package/dist/chaos.d.ts.map +1 -1
  62. package/dist/chaos.js +616 -74
  63. package/dist/chaos.js.map +1 -1
  64. package/dist/cli.cjs +131 -30
  65. package/dist/cli.cjs.map +1 -1
  66. package/dist/cli.js +132 -31
  67. package/dist/cli.js.map +1 -1
  68. package/dist/cohere.cjs +37 -9
  69. package/dist/cohere.cjs.map +1 -1
  70. package/dist/cohere.d.cts.map +1 -1
  71. package/dist/cohere.d.ts.map +1 -1
  72. package/dist/cohere.js +39 -11
  73. package/dist/cohere.js.map +1 -1
  74. package/dist/config-loader.cjs +1 -1
  75. package/dist/config-loader.js +1 -1
  76. package/dist/constants.cjs +8 -0
  77. package/dist/constants.cjs.map +1 -1
  78. package/dist/constants.d.cts +6 -2
  79. package/dist/constants.d.ts +6 -2
  80. package/dist/constants.js +8 -1
  81. package/dist/constants.js.map +1 -1
  82. package/dist/convert.cjs +1 -1
  83. package/dist/convert.cjs.map +1 -1
  84. package/dist/convert.js +1 -1
  85. package/dist/convert.js.map +1 -1
  86. package/dist/echo-text.cjs +65 -0
  87. package/dist/echo-text.cjs.map +1 -0
  88. package/dist/echo-text.d.cts +22 -0
  89. package/dist/echo-text.d.cts.map +1 -0
  90. package/dist/echo-text.d.ts +22 -0
  91. package/dist/echo-text.d.ts.map +1 -0
  92. package/dist/echo-text.js +65 -0
  93. package/dist/echo-text.js.map +1 -0
  94. package/dist/elevenlabs-audio.cjs +3 -3
  95. package/dist/elevenlabs-audio.cjs.map +1 -1
  96. package/dist/elevenlabs-audio.js +4 -4
  97. package/dist/elevenlabs-audio.js.map +1 -1
  98. package/dist/elevenlabs-voice.cjs +1053 -0
  99. package/dist/elevenlabs-voice.cjs.map +1 -0
  100. package/dist/elevenlabs-voice.d.cts +72 -0
  101. package/dist/elevenlabs-voice.d.cts.map +1 -0
  102. package/dist/elevenlabs-voice.d.ts +72 -0
  103. package/dist/elevenlabs-voice.d.ts.map +1 -0
  104. package/dist/elevenlabs-voice.js +1048 -0
  105. package/dist/elevenlabs-voice.js.map +1 -0
  106. package/dist/embeddings.cjs +26 -5
  107. package/dist/embeddings.cjs.map +1 -1
  108. package/dist/embeddings.d.cts +1 -1
  109. package/dist/embeddings.d.cts.map +1 -1
  110. package/dist/embeddings.d.ts +1 -1
  111. package/dist/embeddings.d.ts.map +1 -1
  112. package/dist/embeddings.js +27 -6
  113. package/dist/embeddings.js.map +1 -1
  114. package/dist/fal-audio.cjs +3 -3
  115. package/dist/fal-audio.cjs.map +1 -1
  116. package/dist/fal-audio.js +4 -4
  117. package/dist/fal-audio.js.map +1 -1
  118. package/dist/fal.cjs +28 -4
  119. package/dist/fal.cjs.map +1 -1
  120. package/dist/fal.d.cts +16 -1
  121. package/dist/fal.d.cts.map +1 -1
  122. package/dist/fal.d.ts +16 -1
  123. package/dist/fal.d.ts.map +1 -1
  124. package/dist/fal.js +28 -5
  125. package/dist/fal.js.map +1 -1
  126. package/dist/files.cjs +1069 -0
  127. package/dist/files.cjs.map +1 -0
  128. package/dist/files.d.cts +68 -0
  129. package/dist/files.d.cts.map +1 -0
  130. package/dist/files.d.ts +68 -0
  131. package/dist/files.d.ts.map +1 -0
  132. package/dist/files.js +1056 -0
  133. package/dist/files.js.map +1 -0
  134. package/dist/fine-tuning.cjs +1033 -0
  135. package/dist/fine-tuning.cjs.map +1 -0
  136. package/dist/fine-tuning.d.cts +96 -0
  137. package/dist/fine-tuning.d.cts.map +1 -0
  138. package/dist/fine-tuning.d.ts +96 -0
  139. package/dist/fine-tuning.d.ts.map +1 -0
  140. package/dist/fine-tuning.js +1026 -0
  141. package/dist/fine-tuning.js.map +1 -0
  142. package/dist/fixture-loader-services.cjs +116 -0
  143. package/dist/fixture-loader-services.cjs.map +1 -0
  144. package/dist/fixture-loader-services.d.cts +48 -0
  145. package/dist/fixture-loader-services.d.cts.map +1 -0
  146. package/dist/fixture-loader-services.d.ts +48 -0
  147. package/dist/fixture-loader-services.d.ts.map +1 -0
  148. package/dist/fixture-loader-services.js +114 -0
  149. package/dist/fixture-loader-services.js.map +1 -0
  150. package/dist/fixture-loader.cjs +0 -0
  151. package/dist/fixture-loader.cjs.map +1 -1
  152. package/dist/fixture-loader.d.cts +131 -5
  153. package/dist/fixture-loader.d.cts.map +1 -1
  154. package/dist/fixture-loader.d.ts +131 -5
  155. package/dist/fixture-loader.d.ts.map +1 -1
  156. package/dist/fixture-loader.js +0 -0
  157. package/dist/fixture-loader.js.map +1 -1
  158. package/dist/gemini-embeddings.cjs +44 -4
  159. package/dist/gemini-embeddings.cjs.map +1 -1
  160. package/dist/gemini-embeddings.js +46 -6
  161. package/dist/gemini-embeddings.js.map +1 -1
  162. package/dist/gemini-interactions.cjs +51 -7
  163. package/dist/gemini-interactions.cjs.map +1 -1
  164. package/dist/gemini-interactions.d.cts.map +1 -1
  165. package/dist/gemini-interactions.d.ts.map +1 -1
  166. package/dist/gemini-interactions.js +53 -9
  167. package/dist/gemini-interactions.js.map +1 -1
  168. package/dist/gemini.cjs +19 -8
  169. package/dist/gemini.cjs.map +1 -1
  170. package/dist/gemini.d.cts.map +1 -1
  171. package/dist/gemini.d.ts.map +1 -1
  172. package/dist/gemini.js +21 -10
  173. package/dist/gemini.js.map +1 -1
  174. package/dist/grok-video.cjs +14 -14
  175. package/dist/grok-video.cjs.map +1 -1
  176. package/dist/grok-video.js +15 -15
  177. package/dist/grok-video.js.map +1 -1
  178. package/dist/helpers.cjs +497 -10
  179. package/dist/helpers.cjs.map +1 -1
  180. package/dist/helpers.d.cts +38 -2
  181. package/dist/helpers.d.cts.map +1 -1
  182. package/dist/helpers.d.ts +38 -2
  183. package/dist/helpers.d.ts.map +1 -1
  184. package/dist/helpers.js +487 -12
  185. package/dist/helpers.js.map +1 -1
  186. package/dist/images.cjs +57 -125
  187. package/dist/images.cjs.map +1 -1
  188. package/dist/images.d.cts.map +1 -1
  189. package/dist/images.d.ts.map +1 -1
  190. package/dist/images.js +58 -126
  191. package/dist/images.js.map +1 -1
  192. package/dist/index.cjs +71 -9
  193. package/dist/index.d.cts +17 -7
  194. package/dist/index.d.ts +17 -7
  195. package/dist/index.js +21 -11
  196. package/dist/jest.cjs +24 -12
  197. package/dist/jest.cjs.map +1 -1
  198. package/dist/jest.d.cts +6 -3
  199. package/dist/jest.d.cts.map +1 -1
  200. package/dist/jest.d.ts +6 -3
  201. package/dist/jest.d.ts.map +1 -1
  202. package/dist/jest.js +25 -13
  203. package/dist/jest.js.map +1 -1
  204. package/dist/journal.cjs +61 -4
  205. package/dist/journal.cjs.map +1 -1
  206. package/dist/journal.d.cts +60 -3
  207. package/dist/journal.d.cts.map +1 -1
  208. package/dist/journal.d.ts +60 -3
  209. package/dist/journal.d.ts.map +1 -1
  210. package/dist/journal.js +61 -5
  211. package/dist/journal.js.map +1 -1
  212. package/dist/live-fixture.cjs +1192 -0
  213. package/dist/live-fixture.cjs.map +1 -0
  214. package/dist/live-fixture.js +1187 -0
  215. package/dist/live-fixture.js.map +1 -0
  216. package/dist/live-recorder.cjs +256 -0
  217. package/dist/live-recorder.cjs.map +1 -0
  218. package/dist/live-recorder.js +254 -0
  219. package/dist/live-recorder.js.map +1 -0
  220. package/dist/live-replay.cjs +286 -0
  221. package/dist/live-replay.cjs.map +1 -0
  222. package/dist/live-replay.js +285 -0
  223. package/dist/live-replay.js.map +1 -0
  224. package/dist/live-sanitize.cjs +196 -0
  225. package/dist/live-sanitize.cjs.map +1 -0
  226. package/dist/live-sanitize.js +196 -0
  227. package/dist/live-sanitize.js.map +1 -0
  228. package/dist/live-types.d.cts +71 -0
  229. package/dist/live-types.d.cts.map +1 -0
  230. package/dist/live-types.d.ts +71 -0
  231. package/dist/live-types.d.ts.map +1 -0
  232. package/dist/llmock.cjs +132 -49
  233. package/dist/llmock.cjs.map +1 -1
  234. package/dist/llmock.d.cts +51 -5
  235. package/dist/llmock.d.cts.map +1 -1
  236. package/dist/llmock.d.ts +51 -5
  237. package/dist/llmock.d.ts.map +1 -1
  238. package/dist/llmock.js +133 -50
  239. package/dist/llmock.js.map +1 -1
  240. package/dist/mcp-fakes-mount.cjs +216 -0
  241. package/dist/mcp-fakes-mount.cjs.map +1 -0
  242. package/dist/mcp-fakes-mount.js +210 -0
  243. package/dist/mcp-fakes-mount.js.map +1 -0
  244. package/dist/mcp-fakes.cjs +1619 -0
  245. package/dist/mcp-fakes.cjs.map +1 -0
  246. package/dist/mcp-fakes.d.cts +101 -0
  247. package/dist/mcp-fakes.d.cts.map +1 -0
  248. package/dist/mcp-fakes.d.ts +101 -0
  249. package/dist/mcp-fakes.d.ts.map +1 -0
  250. package/dist/mcp-fakes.js +1614 -0
  251. package/dist/mcp-fakes.js.map +1 -0
  252. package/dist/mcp-handler.cjs +331 -17
  253. package/dist/mcp-handler.cjs.map +1 -1
  254. package/dist/mcp-handler.js +330 -18
  255. package/dist/mcp-handler.js.map +1 -1
  256. package/dist/mcp-mock.cjs +217 -49
  257. package/dist/mcp-mock.cjs.map +1 -1
  258. package/dist/mcp-mock.d.cts +66 -1
  259. package/dist/mcp-mock.d.cts.map +1 -1
  260. package/dist/mcp-mock.d.ts +66 -1
  261. package/dist/mcp-mock.d.ts.map +1 -1
  262. package/dist/mcp-mock.js +218 -50
  263. package/dist/mcp-mock.js.map +1 -1
  264. package/dist/mcp-stub.cjs +5 -1
  265. package/dist/mcp-stub.d.cts +3 -1
  266. package/dist/mcp-stub.d.ts +3 -1
  267. package/dist/mcp-stub.js +3 -1
  268. package/dist/mcp-types.d.cts +6 -0
  269. package/dist/mcp-types.d.ts +6 -0
  270. package/dist/message-text.cjs +192 -0
  271. package/dist/message-text.cjs.map +1 -0
  272. package/dist/message-text.d.cts +31 -0
  273. package/dist/message-text.d.cts.map +1 -0
  274. package/dist/message-text.d.ts +31 -0
  275. package/dist/message-text.d.ts.map +1 -0
  276. package/dist/message-text.js +184 -0
  277. package/dist/message-text.js.map +1 -0
  278. package/dist/messages.cjs +49 -7
  279. package/dist/messages.cjs.map +1 -1
  280. package/dist/messages.d.cts.map +1 -1
  281. package/dist/messages.d.ts.map +1 -1
  282. package/dist/messages.js +51 -9
  283. package/dist/messages.js.map +1 -1
  284. package/dist/metrics.cjs +278 -45
  285. package/dist/metrics.cjs.map +1 -1
  286. package/dist/metrics.d.cts +17 -7
  287. package/dist/metrics.d.cts.map +1 -1
  288. package/dist/metrics.d.ts +17 -7
  289. package/dist/metrics.d.ts.map +1 -1
  290. package/dist/metrics.js +270 -29
  291. package/dist/metrics.js.map +1 -1
  292. package/dist/moderation.cjs +10 -1
  293. package/dist/moderation.cjs.map +1 -1
  294. package/dist/moderation.d.cts.map +1 -1
  295. package/dist/moderation.d.ts.map +1 -1
  296. package/dist/moderation.js +10 -1
  297. package/dist/moderation.js.map +1 -1
  298. package/dist/ollama.cjs +10 -6
  299. package/dist/ollama.cjs.map +1 -1
  300. package/dist/ollama.d.cts.map +1 -1
  301. package/dist/ollama.d.ts.map +1 -1
  302. package/dist/ollama.js +11 -7
  303. package/dist/ollama.js.map +1 -1
  304. package/dist/openapi.cjs +3405 -0
  305. package/dist/openapi.cjs.map +1 -0
  306. package/dist/openapi.d.cts +30 -0
  307. package/dist/openapi.d.cts.map +1 -0
  308. package/dist/openapi.d.ts +30 -0
  309. package/dist/openapi.d.ts.map +1 -0
  310. package/dist/openapi.js +3404 -0
  311. package/dist/openapi.js.map +1 -0
  312. package/dist/openrouter-video.cjs +9 -9
  313. package/dist/openrouter-video.cjs.map +1 -1
  314. package/dist/openrouter-video.d.cts.map +1 -1
  315. package/dist/openrouter-video.d.ts.map +1 -1
  316. package/dist/openrouter-video.js +10 -10
  317. package/dist/openrouter-video.js.map +1 -1
  318. package/dist/recorder.cjs +54 -10
  319. package/dist/recorder.cjs.map +1 -1
  320. package/dist/recorder.d.cts.map +1 -1
  321. package/dist/recorder.d.ts.map +1 -1
  322. package/dist/recorder.js +52 -12
  323. package/dist/recorder.js.map +1 -1
  324. package/dist/responses.cjs +47 -7
  325. package/dist/responses.cjs.map +1 -1
  326. package/dist/responses.d.cts.map +1 -1
  327. package/dist/responses.d.ts.map +1 -1
  328. package/dist/responses.js +48 -9
  329. package/dist/responses.js.map +1 -1
  330. package/dist/route-registry.cjs +951 -0
  331. package/dist/route-registry.cjs.map +1 -0
  332. package/dist/route-registry.d.cts +33 -0
  333. package/dist/route-registry.d.cts.map +1 -0
  334. package/dist/route-registry.d.ts +33 -0
  335. package/dist/route-registry.d.ts.map +1 -0
  336. package/dist/route-registry.js +866 -0
  337. package/dist/route-registry.js.map +1 -0
  338. package/dist/router.cjs +21 -34
  339. package/dist/router.cjs.map +1 -1
  340. package/dist/router.d.cts +4 -1
  341. package/dist/router.d.cts.map +1 -1
  342. package/dist/router.d.ts +4 -1
  343. package/dist/router.d.ts.map +1 -1
  344. package/dist/router.js +21 -35
  345. package/dist/router.js.map +1 -1
  346. package/dist/server.cjs +1252 -777
  347. package/dist/server.cjs.map +1 -1
  348. package/dist/server.d.cts +5 -1
  349. package/dist/server.d.cts.map +1 -1
  350. package/dist/server.d.ts +5 -1
  351. package/dist/server.d.ts.map +1 -1
  352. package/dist/server.js +1185 -710
  353. package/dist/server.js.map +1 -1
  354. package/dist/speech.cjs +6 -4
  355. package/dist/speech.cjs.map +1 -1
  356. package/dist/speech.js +7 -5
  357. package/dist/speech.js.map +1 -1
  358. package/dist/suite.cjs +1 -1
  359. package/dist/suite.js +1 -1
  360. package/dist/transcription.cjs +6 -6
  361. package/dist/transcription.cjs.map +1 -1
  362. package/dist/transcription.d.cts.map +1 -1
  363. package/dist/transcription.d.ts.map +1 -1
  364. package/dist/transcription.js +7 -7
  365. package/dist/transcription.js.map +1 -1
  366. package/dist/types.cjs +18 -0
  367. package/dist/types.cjs.map +1 -0
  368. package/dist/types.d.cts +320 -17
  369. package/dist/types.d.cts.map +1 -1
  370. package/dist/types.d.ts +320 -17
  371. package/dist/types.d.ts.map +1 -1
  372. package/dist/types.js +17 -0
  373. package/dist/types.js.map +1 -0
  374. package/dist/validate-cli.cjs +983 -0
  375. package/dist/validate-cli.cjs.map +1 -0
  376. package/dist/validate-cli.js +982 -0
  377. package/dist/validate-cli.js.map +1 -0
  378. package/dist/vector-stores.cjs +1180 -0
  379. package/dist/vector-stores.cjs.map +1 -0
  380. package/dist/vector-stores.d.cts +106 -0
  381. package/dist/vector-stores.d.cts.map +1 -0
  382. package/dist/vector-stores.d.ts +106 -0
  383. package/dist/vector-stores.d.ts.map +1 -0
  384. package/dist/vector-stores.js +1164 -0
  385. package/dist/vector-stores.js.map +1 -0
  386. package/dist/vector-types.d.cts.map +1 -1
  387. package/dist/vector-types.d.ts.map +1 -1
  388. package/dist/veo-video.cjs +13 -13
  389. package/dist/veo-video.cjs.map +1 -1
  390. package/dist/veo-video.js +14 -14
  391. package/dist/veo-video.js.map +1 -1
  392. package/dist/video.cjs +23 -4
  393. package/dist/video.cjs.map +1 -1
  394. package/dist/video.d.cts +1 -1
  395. package/dist/video.d.cts.map +1 -1
  396. package/dist/video.d.ts +1 -1
  397. package/dist/video.d.ts.map +1 -1
  398. package/dist/video.js +24 -5
  399. package/dist/video.js.map +1 -1
  400. package/dist/vitest.cjs +23 -11
  401. package/dist/vitest.cjs.map +1 -1
  402. package/dist/vitest.d.cts +5 -2
  403. package/dist/vitest.d.cts.map +1 -1
  404. package/dist/vitest.d.ts +5 -2
  405. package/dist/vitest.d.ts.map +1 -1
  406. package/dist/vitest.js +25 -13
  407. package/dist/vitest.js.map +1 -1
  408. package/dist/watcher.cjs +2 -1
  409. package/dist/watcher.cjs.map +1 -1
  410. package/dist/watcher.js +2 -1
  411. package/dist/watcher.js.map +1 -1
  412. package/dist/ws-framing.cjs +150 -35
  413. package/dist/ws-framing.cjs.map +1 -1
  414. package/dist/ws-framing.d.cts +18 -3
  415. package/dist/ws-framing.d.cts.map +1 -1
  416. package/dist/ws-framing.d.ts +18 -3
  417. package/dist/ws-framing.d.ts.map +1 -1
  418. package/dist/ws-framing.js +151 -36
  419. package/dist/ws-framing.js.map +1 -1
  420. package/dist/ws-gemini-live.cjs +2 -2
  421. package/dist/ws-gemini-live.js +2 -2
  422. package/dist/ws-live.cjs +401 -0
  423. package/dist/ws-live.cjs.map +1 -0
  424. package/dist/ws-live.js +400 -0
  425. package/dist/ws-live.js.map +1 -0
  426. package/dist/ws-realtime.cjs +46 -2
  427. package/dist/ws-realtime.cjs.map +1 -1
  428. package/dist/ws-realtime.d.cts.map +1 -1
  429. package/dist/ws-realtime.d.ts.map +1 -1
  430. package/dist/ws-realtime.js +46 -2
  431. package/dist/ws-realtime.js.map +1 -1
  432. package/dist/ws-responses.cjs +16 -4
  433. package/dist/ws-responses.cjs.map +1 -1
  434. package/dist/ws-responses.js +16 -4
  435. package/dist/ws-responses.js.map +1 -1
  436. package/dist/ws-upstream.cjs +118 -0
  437. package/dist/ws-upstream.cjs.map +1 -0
  438. package/dist/ws-upstream.js +115 -0
  439. package/dist/ws-upstream.js.map +1 -0
  440. package/fixtures/openai-live-client.json +390 -0
  441. package/fixtures/openai-live-managed.json +709 -0
  442. package/package.json +4 -1
@@ -0,0 +1,983 @@
1
+ const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
+ const require_message_text = require('./message-text.cjs');
3
+ const require_fixture_loader = require('./fixture-loader.cjs');
4
+ const require_logger = require('./logger.cjs');
5
+ const require_mcp_fakes = require('./mcp-fakes.cjs');
6
+ let node_fs = require("node:fs");
7
+ let node_path = require("node:path");
8
+ let node_util = require("node:util");
9
+
10
+ //#region src/validate-cli.ts
11
+ /**
12
+ * `aimock validate` subcommand — offline fixture lint.
13
+ *
14
+ * Usage: `aimock validate [--strict] [--json] [--] <path> [more paths ...]`
15
+ *
16
+ * Each path is a fixture file or a directory; directories are walked
17
+ * recursively for `*.json` in the same order `--fixtures <dir>` loads them.
18
+ * For each file: read bytes, JSON-parse, require a top-level
19
+ * `{ "fixtures": [...] }` envelope, convert the entries (surfacing the
20
+ * per-entry conversion warnings the server logs), then run the
21
+ * `validateFixtures` rules over that file's entries alone, so its findings
22
+ * are attributed to it.
23
+ *
24
+ * `validateFixtures` is then run a SECOND time over the union of every
25
+ * successfully-parsed file's entries, in load order. Its rules are not all
26
+ * per-fixture: `duplicate userMessage` and the catch-all ordering check look
27
+ * at the other fixtures in the array they are given. Run per file those two
28
+ * cannot see across files, so the findings the union pass adds — a
29
+ * `userMessage` duplicated in two files, a catch-all that is last in its own
30
+ * file but not last overall — are the cross-file ones. They are reported
31
+ * against the file that owns the fixture, with the other file named in the
32
+ * message; findings the per-file pass already made are not repeated.
33
+ *
34
+ * How this compares to the server's `--validate-on-load`, in both directions:
35
+ * - Stricter: there an unreadable, unparseable or wrong-shape file is
36
+ * non-fatal — it warns on stderr, contributes zero fixtures, and startup
37
+ * continues as long as the run loaded at least one fixture from somewhere.
38
+ * Here every such file fails the run — with one deliberate exception, for
39
+ * parity: a `*.json` file the WALK turned up that parses but carries no
40
+ * top-level `fixtures` array is not a fixture file (this package's own
41
+ * `fixtures/` tree holds aimock config files alongside fixtures), and is
42
+ * passed over with a note, exactly as the loader passes over it. A file
43
+ * the caller NAMES is still validated, wrong shape and all. Two walk
44
+ * hazards the server does not
45
+ * survive are refused outright rather than mirrored: a `*.json` path that
46
+ * is not a regular file (a FIFO, a socket, a device) is a per-file error
47
+ * here, where the server's `readFileSync` blocks on it forever; and a
48
+ * directory symlink that points back at a directory already being walked
49
+ * is reported once as a cycle, where the server recurses through it until
50
+ * the kernel returns ELOOP (~32 levels deep) and loads those levels'
51
+ * fixtures over and over. Malformed scalar or array matches are rejected
52
+ * before conversion, as they are by the server's fixture loader. The lint
53
+ * reports an entry error and continues validating valid siblings.
54
+ * - Equal: the `validateFixtures` rules themselves, because of the union
55
+ * pass above, and the zero-fixture rule — a run whose inputs yield no
56
+ * fixtures at all is an error here, matching the server's "No fixtures
57
+ * loaded and validation/strict mode is enabled — aborting." A single
58
+ * `{"fixtures": []}` file alongside files that do load fixtures is an
59
+ * error in neither place. Equal too in what the inputs expand to: a path
60
+ * named twice, and a directory reached both directly and through an
61
+ * acyclic symlink to it, are validated once per mention, exactly as the
62
+ * server loads one `--fixtures` source per mention — so the duplicate
63
+ * fixtures that produces are reported here as the server reports them,
64
+ * one finding per (fixture, rule), the same count the server logs.
65
+ *
66
+ * A path mentioned more than once in one run is therefore several DIFFERENT
67
+ * loads that happen to share a name, and printing that bare name for each of
68
+ * them made them indistinguishable: a warning read as if a fixture shadowed
69
+ * itself. Each mention is numbered, and every rendering surface carries that
70
+ * number — human lines and their summary, cross-file references, and a
71
+ * `mention` field on each `--json` `files[]` entry. The `<path>[<n>]` suffix
72
+ * is printed only for a path that IS mentioned more than once, because for
73
+ * every other path the name alone already names one load; the `--json` field
74
+ * is always present so a consumer never has to infer it.
75
+ * - Weaker: this is a static lint. It never starts a server, so it covers
76
+ * nothing about binding, remote `--fixtures <url>` sources (it reads local
77
+ * paths only), `--watch` reloads, or any runtime behaviour.
78
+ *
79
+ * Nothing the filesystem or a fixture entry can do is allowed to escape this
80
+ * function: a malformed entry, an unreadable path, a stat failure or a symlink
81
+ * cycle each becomes a per-file `[error]` in the report, and a crash inside
82
+ * either `validateFixtures` pass becomes a file-level or run-level `[error]`,
83
+ * so the run still emits a report (valid JSON under `--json`) and still exits
84
+ * 1. Run-level errors print after the per-file output, and the "no fixtures
85
+ * loaded" one is withheld when a file already reported why it loaded none.
86
+ *
87
+ * Prints human lines by default (`file: [error|warning] #i message`) or a
88
+ * JSON document with `--json`. Everything that contributes to a non-zero exit
89
+ * goes to stderr, so a CI log's stderr always shows the reason; `--json` keeps
90
+ * stdout to the report alone and puts the reason on stderr. The two modes
91
+ * report the same facts: a fatal file is one record that both the stdout
92
+ * tally and `files[].errors` count, and `--json` emits a document for a usage
93
+ * error too — stdout is never empty on a `--json` run, whatever failed it.
94
+ * A path naming a remote source (`https://...`) is refused as unsupported
95
+ * rather than read as a filename. Exit codes:
96
+ * 0 = clean (warnings allowed unless `--strict`), 1 = everything that fails
97
+ * the run — errors / unreadable / unparseable / wrong shape / unwalkable path
98
+ * / no fixtures loaded at all, and a usage error (no paths or an unknown
99
+ * option) too, because every other CLI surface in this package exits 1 on a
100
+ * usage error and nothing here uses sysexits codes. `--help` is handled
101
+ * separately and exits 0.
102
+ */
103
+ const VALIDATE_HELP = `
104
+ Usage: aimock validate [options] [--] <path> [more paths ...]
105
+
106
+ Validate aimock fixture files offline. Each path is a fixture file or a
107
+ directory of them (walked recursively for *.json, like --fixtures <dir>).
108
+ Files are checked one at a time and then as one combined set, so the rules
109
+ that span files -- a userMessage duplicated across two files, a catch-all
110
+ that is not last overall -- are reported here as the server reports them.
111
+
112
+ Where the server's --validate-on-load differs: there an unreadable,
113
+ unparseable or wrong-shape file only warns and startup continues, while here
114
+ every such file fails the run. The one exception, which matches the loader: a
115
+ *.json file found by WALKING a directory that has no top-level "fixtures"
116
+ array is not a fixture file (an aimock config, say) and is skipped with a
117
+ note -- a file you name on the command line is still validated. As on the
118
+ server, a run that loads no fixtures at all is an error. This is a static lint: it starts no server and reads
119
+ local paths only (no remote --fixtures URL).
120
+
121
+ A path given twice is validated twice, because the server loads one set of
122
+ fixtures per --fixtures it is given; the duplicate fixtures that produces are
123
+ reported as the server reports them, one finding per fixture and rule. Such a
124
+ path is printed as <path>[<n>], numbering its mentions from 1, so each load
125
+ and every reference to its fixtures names one of them (--json reports the
126
+ number as "mention" on every file). A directory symlink pointing back at a
127
+ directory already being walked is a symlink cycle and fails the run; one
128
+ pointing anywhere else is followed, as the server follows it.
129
+
130
+ Options:
131
+ --strict Treat warnings as errors (exit 1 on warnings)
132
+ --json Emit a JSON report instead of human lines
133
+ -h, --help Show this help message
134
+ -- Stop option parsing; every later argument is a path
135
+ (use this for a path that begins with "-")
136
+
137
+ Exit codes:
138
+ 0 all files valid (warnings allowed unless --strict)
139
+ 1 anything that failed the run: validation errors; a malformed fixture
140
+ entry; an unreadable or unparseable file; a named path of the wrong
141
+ shape (a walked one is skipped instead); a path that
142
+ cannot be stat'd, read or walked (including a symlink cycle, or a
143
+ *.json path that is not a regular file); a directory holding no *.json;
144
+ no fixtures loaded from any input; --strict warnings; or a usage error
145
+ (no paths given, or an unknown option)
146
+ `.trim();
147
+ /** The indices a ref names, in a fixed order, for identity purposes. */
148
+ function refIndices(ref) {
149
+ return ref.rule === "duplicate-user-message" ? [ref.shadows] : [ref.shadowsFrom, ref.shadowsThrough];
150
+ }
151
+ /**
152
+ * A path argument that names a remote source rather than a local file: any
153
+ * `scheme://` prefix, not just http(s), so `ftp://` and friends get the same
154
+ * explanation instead of a filesystem error. A Windows drive letter ("C:\\x")
155
+ * has no `//` and is not matched.
156
+ */
157
+ const REMOTE_SOURCE = /^[a-z][a-z0-9+.-]*:\/\//i;
158
+ /** Render an unknown thrown value as a one-line message. */
159
+ function errText(err) {
160
+ return err instanceof Error ? err.message : String(err);
161
+ }
162
+ /**
163
+ * Record why a file produced no report at all. A fatal is ONE fact, so it is
164
+ * written once, here, into both shapes that report it: `fatal`, which names
165
+ * the reason, and `errors`, which is what every tally — the human summary
166
+ * line, the `--json` consumer counting `files[].errors` — actually counts.
167
+ * Set on `fatal` alone, the two modes disagreed: stdout said "1 error(s)"
168
+ * while the same run's JSON said `"errors": []`, so a CI script adding up the
169
+ * JSON saw a clean run that had exited 1.
170
+ *
171
+ * The finding carries no `index`: a fatal is about the file, not an entry
172
+ * (see `Finding.index`).
173
+ */
174
+ function setFatal(report, message) {
175
+ report.fatal = message;
176
+ report.errors.push({ message });
177
+ }
178
+ /** A file that never got as far as a report, with its fatal already recorded. */
179
+ function fatalReport(file, mention, message) {
180
+ const report = {
181
+ file,
182
+ mention,
183
+ fixtures: 0,
184
+ errors: [],
185
+ warnings: []
186
+ };
187
+ setFatal(report, message);
188
+ return report;
189
+ }
190
+ /**
191
+ * Logger that routes `warn` AND `error` into caller-supplied sinks instead of
192
+ * the console, so the per-entry conversion diagnostics `entryToFixture` emits
193
+ * land in the report rather than being dropped on the floor — and, under
194
+ * `--json`, never interleave with the JSON document on stdout/stderr.
195
+ * Exported for tests only; it is not part of the package's public API.
196
+ * `info`/`debug` are inert: they are already below this logger's "warn" level,
197
+ * and silencing them explicitly keeps future call sites off stdout too.
198
+ */
199
+ var CollectingLogger = class CollectingLogger extends require_logger.Logger {
200
+ warnSink;
201
+ errorSink;
202
+ constructor(warnSink, errorSink) {
203
+ super("warn");
204
+ this.warnSink = warnSink;
205
+ this.errorSink = errorSink;
206
+ }
207
+ /**
208
+ * Serialise exactly as the sink this logger replaces does — byte for byte.
209
+ * `Logger` writes `console.warn("[aimock]", ...args)`, and console's
210
+ * formatting IS `util.format`, so the call it makes is
211
+ * `format("[aimock]", ...args)`: `"[aimock]"` is the FORMAT STRING and holds
212
+ * no specifiers, which is what makes a caller's own `%s`/`%d` inert and its
213
+ * arguments plain trailing values. Calling `format(...args)` instead
214
+ * promoted the caller's message into the format slot — so a message that
215
+ * happened to contain `%s` (a fixture path, say) swallowed the next
216
+ * argument — and dropped the `[aimock]` prefix, leaving the report and the
217
+ * server's own stderr disagreeing about the same diagnostic.
218
+ * `util.inspect` still renders object arguments readably, which is what
219
+ * `String(a)` could not do.
220
+ */
221
+ static format(args) {
222
+ return (0, node_util.format)("[aimock]", ...args);
223
+ }
224
+ warn(...args) {
225
+ this.warnSink(CollectingLogger.format(args));
226
+ }
227
+ error(...args) {
228
+ this.errorSink(CollectingLogger.format(args));
229
+ }
230
+ info(...args) {}
231
+ debug(...args) {}
232
+ };
233
+ /**
234
+ * Why a `*.json` file the WALK turned up is not a fixture file, or
235
+ * `undefined` if it is one (or if the question cannot be answered cheaply).
236
+ *
237
+ * A directory is not a fixture directory: this package ships `fixtures/`
238
+ * holding both fixture files and eight aimock CONFIG files — the six
239
+ * `fixtures/examples/**\/*-config.json`, `full-suite.json`, and
240
+ * `agui/agui-text-response.json`, whose AG-UI fixtures sit NESTED under
241
+ * `agui` where `--fixtures` cannot see them — which is why the documented
242
+ * `aimock validate ./fixtures/` exited 1 on the package's own tree. The
243
+ * server has the same mixture to deal with and passes over it:
244
+ * `loadFixtureFile` warns "Missing or invalid \"fixtures\" array", returns no
245
+ * fixtures, and startup continues. This mirrors that skip, so a walked
246
+ * directory reports on the fixture files in it and says what it passed over.
247
+ *
248
+ * Non-fixture means the file carries no top-level `fixtures` key at all (or
249
+ * is an aimock config). A `fixtures` key that is present but not an array is
250
+ * a MALFORMED fixture file and still fails the run, as the loader would refuse
251
+ * it. The skip applies ONLY to files the walk discovered — a path the caller named explicitly is still validated, and a
252
+ * wrong-shape file there still fails the run, because naming a file is
253
+ * asking about that file.
254
+ *
255
+ * A file carrying an aimock config section (`configSections`) is named as
256
+ * what it is rather than by what it lacks, because "not a fixture file" on
257
+ * its own is read as "holds no fixtures" — false of `agui`-sectioned configs,
258
+ * which hold AG-UI fixtures that `aimock --config`, not `--fixtures`, serves.
259
+ *
260
+ * Unreadable and unparseable are deliberately NOT skips. Returning
261
+ * `undefined` for them hands the file to `validateOneFile`, which reports the
262
+ * real read error or JSON syntax error — a defect in any `*.json` file, and
263
+ * one no config file has an excuse for either.
264
+ */
265
+ function nonFixtureSkipReason(file) {
266
+ let parsed;
267
+ try {
268
+ parsed = JSON.parse((0, node_fs.readFileSync)(file, "utf-8"));
269
+ } catch {
270
+ return;
271
+ }
272
+ if (require_fixture_loader.hasMcpFakesKey(parsed)) return void 0;
273
+ const asConfig = aimockConfigDescription(parsed);
274
+ if (asConfig !== void 0) return `not a fixture file — ${asConfig}`;
275
+ if (typeof parsed === "object" && parsed !== null && "fixtures" in parsed) return void 0;
276
+ return "not a fixture file — no top-level \"fixtures\" key";
277
+ }
278
+ /**
279
+ * The mock sections of `AimockConfig` (config-loader.ts). An object carrying
280
+ * any of them is an aimock config file: `aimock --config <path>` starts a
281
+ * server from it, while `--fixtures <path>` loads nothing from it.
282
+ */
283
+ const CONFIG_SECTIONS = [
284
+ "llm",
285
+ "mcp",
286
+ "a2a",
287
+ "agui",
288
+ "vector",
289
+ "services"
290
+ ];
291
+ /**
292
+ * What a parsed-but-not-fixture-shaped JSON document IS, when it is an aimock
293
+ * config file, or `undefined` when it is not one. Used by both the walk's skip
294
+ * note and the fatal a NAMED file of that shape gets, so the two surfaces
295
+ * describe the same file the same way. The sections found are listed, so an
296
+ * `agui` config is never described as a file holding no fixtures — it holds
297
+ * AG-UI ones, nested where the fixture loader does not look.
298
+ */
299
+ function aimockConfigDescription(parsed) {
300
+ if (typeof parsed !== "object" || parsed === null) return void 0;
301
+ const doc = parsed;
302
+ const sections = CONFIG_SECTIONS.filter((key) => typeof doc[key] === "object" && doc[key]);
303
+ if (sections.length === 0) return void 0;
304
+ return `an aimock config file (${sections.join(", ")}), which "aimock --config" serves and "--fixtures" does not load`;
305
+ }
306
+ /**
307
+ * Collect `*.json` files under `dirPath`, recursing into subdirectories.
308
+ * Mirrors `loadFixturesFromDir`: within a directory the `*.json` files come
309
+ * first in sorted order, then its subdirectories in sorted order, so the
310
+ * reported order matches the server's load order, and the same stat-error
311
+ * handling — a vanished entry (ENOENT) is skipped silently, every other
312
+ * failure is surfaced. Unlike the loader, which only warns, each surfaced
313
+ * failure becomes a `fatal` entry that fails the run, and it is emitted in
314
+ * walk position (an unreadable subdirectory is reported where that
315
+ * subdirectory was reached, not after every file in the tree) so the report
316
+ * reads in encounter order. Entries that are neither directories nor
317
+ * `*.json` files are ignored, as they are by the loader.
318
+ *
319
+ * `ancestors` holds the real paths of the directories this call is nested
320
+ * inside. A directory whose real path is one of them closes a symlink cycle
321
+ * and is reported instead of walked; the server has no such guard and
322
+ * recurses through the cycle until the kernel returns ELOOP. A repeat visit
323
+ * that is NOT an ancestor — `root/link -> root/sub` alongside `root/sub` — is
324
+ * no cycle: it is walked again, because the server walks it again and loads
325
+ * those fixtures twice.
326
+ *
327
+ * A `*.json` entry that is not a regular file (a FIFO, a socket, a device)
328
+ * is a `fatal` entry rather than a target, because reading it is what the
329
+ * containment promise above cannot keep: `readFileSync` on a FIFO with no
330
+ * writer blocks forever, as it does on the server.
331
+ */
332
+ function collectJsonFiles(dirPath, ancestors, out = []) {
333
+ let realPath;
334
+ try {
335
+ realPath = (0, node_fs.realpathSync)(dirPath);
336
+ } catch (err) {
337
+ out.push({
338
+ file: dirPath,
339
+ fatal: `Could not resolve directory: ${errText(err)}`
340
+ });
341
+ return out;
342
+ }
343
+ if (ancestors.includes(realPath)) {
344
+ out.push({
345
+ file: dirPath,
346
+ fatal: `Symlink cycle detected: already walking ${realPath}`
347
+ });
348
+ return out;
349
+ }
350
+ let names;
351
+ try {
352
+ names = (0, node_fs.readdirSync)(dirPath);
353
+ } catch (err) {
354
+ out.push({
355
+ file: dirPath,
356
+ fatal: `Could not read directory: ${errText(err)}`
357
+ });
358
+ return out;
359
+ }
360
+ const subdirs = [];
361
+ for (const name of [...names].sort()) {
362
+ const fullPath = (0, node_path.join)(dirPath, name);
363
+ let stats;
364
+ try {
365
+ stats = (0, node_fs.statSync)(fullPath);
366
+ } catch (err) {
367
+ if (err.code !== "ENOENT") out.push({
368
+ file: fullPath,
369
+ fatal: `Could not stat: ${errText(err)}`
370
+ });
371
+ continue;
372
+ }
373
+ if (stats.isDirectory()) subdirs.push(name);
374
+ else if (!name.endsWith(".json")) continue;
375
+ else if (stats.isFile()) {
376
+ const skipped = nonFixtureSkipReason(fullPath);
377
+ out.push(skipped === void 0 ? { file: fullPath } : {
378
+ file: fullPath,
379
+ skipped
380
+ });
381
+ } else out.push({
382
+ file: fullPath,
383
+ fatal: "Not a regular file"
384
+ });
385
+ }
386
+ const chain = [...ancestors, realPath];
387
+ for (const sub of subdirs) collectJsonFiles((0, node_path.join)(dirPath, sub), chain, out);
388
+ return out;
389
+ }
390
+ /** The entry shape the loader and the server both assume, quoted verbatim. */
391
+ const ENTRY_SHAPE = "every entry needs { \"match\": { ... }, \"response\": { ... } }";
392
+ /** Name a value the way a fixture author would read it back in their JSON. */
393
+ function gotText(value) {
394
+ if (value === null) return "null";
395
+ if (value === void 0) return "absent";
396
+ if (Array.isArray(value)) return "an array";
397
+ return `a ${typeof value}`;
398
+ }
399
+ /**
400
+ * Classify a raw entry's shape, or return `undefined` when nothing about it is
401
+ * worth saying. Runs before `entryToFixture`, so every branch below is driven
402
+ * by the entry itself rather than by whichever exception the conversion
403
+ * happened to throw.
404
+ *
405
+ * A missing or non-object `response` is deliberately NOT classified here: the
406
+ * conversion accepts it (`{ ...undefined }` is `{}`), the server accepts it,
407
+ * and the `validateFixtures` rules already reject it by name — "response is
408
+ * not a recognized type (must have content, toolCalls, ...)" — which is the
409
+ * more actionable message of the two. Saying it here as well would report one
410
+ * defect twice. It is still named alongside `match` when `match` is fatal,
411
+ * because then the rules never run on the entry at all and an author fixing
412
+ * `{}` would otherwise need one round trip per missing key.
413
+ */
414
+ function classifyEntry(entry) {
415
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return {
416
+ severity: "error",
417
+ convertible: false,
418
+ why: `entry is ${gotText(entry)}, expected an object — ${ENTRY_SHAPE}`
419
+ };
420
+ const match = entry.match;
421
+ if (match === null || match === void 0) return {
422
+ severity: "error",
423
+ convertible: false,
424
+ why: `missing or non-object ${["match", "response"].filter((key) => {
425
+ const value = entry[key];
426
+ return value === null || typeof value !== "object" || Array.isArray(value);
427
+ }).map((k) => `"${k}"`).join(" and ")} — ${ENTRY_SHAPE}`
428
+ };
429
+ if (typeof match !== "object" || Array.isArray(match)) return {
430
+ severity: "error",
431
+ convertible: false,
432
+ why: `"match" is ${gotText(match)}, expected an object — ${ENTRY_SHAPE}`
433
+ };
434
+ }
435
+ /**
436
+ * The backstop for an entry `classifyEntry` passed and `entryToFixture` threw
437
+ * on anyway (a `toolCalls: [null]`, say, which `normalizeResponse` walks).
438
+ * Which entry it was is the actionable half, so that leads; the raw thrown
439
+ * text follows as `detail`, which `--json` carries as its own field and the
440
+ * human report prints on an indented continuation line. It used to say
441
+ * `see "detail"`, naming a field only the `--json` report has — in human mode
442
+ * that pointed at nothing at all.
443
+ */
444
+ function unexpectedEntryFailure(index, err) {
445
+ return {
446
+ index,
447
+ message: `Invalid fixture entry #${index}: could not be converted to a fixture`,
448
+ detail: errText(err)
449
+ };
450
+ }
451
+ /**
452
+ * Validate one fixture file into a report. Every failure mode — unreadable,
453
+ * unparseable, wrong shape, or a single malformed entry — is recorded in the
454
+ * returned report rather than thrown.
455
+ *
456
+ * The converted fixtures are handed back alongside the report, with
457
+ * `indices[i]` giving the entry index `fixtures[i]` was converted from, so the
458
+ * cross-file union pass can append them and map its findings back to a file
459
+ * and an entry index.
460
+ */
461
+ function validateOneFile(file, mention, source) {
462
+ const report = {
463
+ file,
464
+ mention,
465
+ fixtures: 0,
466
+ errors: [],
467
+ warnings: []
468
+ };
469
+ const fixtures = [];
470
+ const sourceIndex = [];
471
+ const runErrors = [];
472
+ const identities = [];
473
+ const mcpFakes = [];
474
+ let raw;
475
+ try {
476
+ raw = (0, node_fs.readFileSync)(file, "utf-8");
477
+ } catch (err) {
478
+ setFatal(report, `Could not read file: ${errText(err)}`);
479
+ return {
480
+ report,
481
+ fixtures,
482
+ indices: sourceIndex,
483
+ identities,
484
+ runErrors,
485
+ mcpFakes
486
+ };
487
+ }
488
+ let parsed;
489
+ try {
490
+ parsed = JSON.parse(raw);
491
+ } catch (err) {
492
+ setFatal(report, `Invalid JSON: ${errText(err)}`);
493
+ return {
494
+ report,
495
+ fixtures,
496
+ indices: sourceIndex,
497
+ identities,
498
+ runErrors,
499
+ mcpFakes
500
+ };
501
+ }
502
+ const fakesDoc = require_fixture_loader.hasMcpFakesKey(parsed) ? parsed : void 0;
503
+ const fixturesLeftOut = fakesDoc !== void 0 && !Object.prototype.hasOwnProperty.call(fakesDoc, "fixtures");
504
+ const badShape = !fixturesLeftOut && (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.fixtures));
505
+ if (badShape) {
506
+ const asConfig = aimockConfigDescription(parsed);
507
+ setFatal(report, asConfig === void 0 ? "Missing or invalid \"fixtures\" array (expected { \"fixtures\": [...] })" : `Missing or invalid "fixtures" array (expected { "fixtures": [...] }) — this is ${asConfig}`);
508
+ }
509
+ if (fakesDoc !== void 0) {
510
+ const fakes = fakesDoc.mcpFakes;
511
+ const validation = require_mcp_fakes.validateMcpFakes(fakes, source);
512
+ report.errors.push(...validation.errors.map(loadErrorFinding));
513
+ report.mcpFakeBlocks = 0;
514
+ if (validation.errors.length > 0) report.warnings.push(...validation.warnings.map(issueFinding));
515
+ else if (Array.isArray(fakes)) fakes.forEach((block, blockIndex) => {
516
+ mcpFakes.push({
517
+ source,
518
+ blockIndex,
519
+ raw: block
520
+ });
521
+ });
522
+ else mcpFakes.push({
523
+ source,
524
+ blockIndex: null,
525
+ raw: fakes
526
+ });
527
+ }
528
+ if (fixturesLeftOut || badShape) return {
529
+ report,
530
+ fixtures,
531
+ indices: sourceIndex,
532
+ identities,
533
+ runErrors,
534
+ mcpFakes
535
+ };
536
+ let entryIndex = 0;
537
+ const entryLogger = new CollectingLogger((message) => report.warnings.push({
538
+ index: entryIndex,
539
+ message
540
+ }), (message) => report.errors.push({
541
+ index: entryIndex,
542
+ message
543
+ }));
544
+ const entries = parsed.fixtures;
545
+ for (let index = 0; index < entries.length; index++) {
546
+ entryIndex = index;
547
+ const defect = classifyEntry(entries[index]);
548
+ if (defect !== void 0) {
549
+ const label = defect.severity === "error" ? "Invalid fixture entry" : "Fixture entry";
550
+ const finding = {
551
+ index,
552
+ message: `${label} #${index}: ${defect.why}`
553
+ };
554
+ if (!defect.convertible) {
555
+ report.errors.push(finding);
556
+ continue;
557
+ }
558
+ report.warnings.push(finding);
559
+ }
560
+ try {
561
+ fixtures.push(require_fixture_loader.entryToFixture(entries[index], entryLogger));
562
+ sourceIndex.push(index);
563
+ } catch (err) {
564
+ report.errors.push(unexpectedEntryFailure(index, err));
565
+ }
566
+ }
567
+ report.fixtures = fixtures.length;
568
+ const unresolved = /* @__PURE__ */ new Set();
569
+ const toEntryIndex = (fixtureIndex) => {
570
+ const entry = sourceIndex[fixtureIndex];
571
+ if (entry !== void 0) return entry;
572
+ if (!unresolved.has(fixtureIndex)) {
573
+ unresolved.add(fixtureIndex);
574
+ runErrors.push(`finding for fixture index ${fixtureIndex}, which maps to no entry in this file`);
575
+ }
576
+ return fixtureIndex;
577
+ };
578
+ try {
579
+ for (const r of require_fixture_loader.validateFixtures(fixtures)) {
580
+ const index = toEntryIndex(r.fixtureIndex);
581
+ const refs = r.ref === void 0 ? [] : refIndices(r.ref).map(toEntryIndex);
582
+ const finding = {
583
+ index,
584
+ message: r.ref === void 0 ? r.message : require_fixture_loader.renderValidationRef(r.ref, (i) => String(toEntryIndex(i)))
585
+ };
586
+ identities.push({
587
+ severity: r.severity,
588
+ index,
589
+ kind: r.ref === void 0 ? `message:${r.message}` : r.ref.rule,
590
+ refs,
591
+ finding
592
+ });
593
+ if (r.severity === "error") report.errors.push(finding);
594
+ else report.warnings.push(finding);
595
+ }
596
+ } catch (err) {
597
+ report.errors.push({ message: `Validation failed for this file: ${errText(err)}` });
598
+ }
599
+ return {
600
+ report,
601
+ fixtures,
602
+ indices: sourceIndex,
603
+ identities,
604
+ runErrors,
605
+ mcpFakes
606
+ };
607
+ }
608
+ /**
609
+ * A `FixtureLoadError` from an `mcpFakes` block as a file-level finding (no
610
+ * fixture entry index): its message names the rule, file, block and entry.
611
+ */
612
+ function loadErrorFinding(err) {
613
+ return { message: err.message };
614
+ }
615
+ /** An L8 shadowed-entry warning as a file-level finding. */
616
+ function issueFinding(issue) {
617
+ return { message: issue.message };
618
+ }
619
+ /**
620
+ * `#<index> ` for a finding that belongs to one entry; empty for a file-level
621
+ * finding, which names no entry (see Finding.index). Shared by the human lines
622
+ * and the failure summary so a reader of either can locate the same entry.
623
+ */
624
+ const findingAt = (f) => f.index === void 0 ? "" : `#${f.index} `;
625
+ /**
626
+ * One-line reason a run failed, for the stderr side of `--json` output.
627
+ *
628
+ * It NAMES the failure first — the file and the finding that caused the
629
+ * non-zero exit — and only then totals what else failed, because the line is
630
+ * often the single line a CI log shows. Leading with a fixed list of counts
631
+ * put two zeros ("0 unreadable/invalid file(s), 0 file(s) with errors") in
632
+ * front of every strict-warning failure, where the reader has to get past the
633
+ * things that did NOT happen to reach the one that did. Counts that are zero
634
+ * are omitted for the same reason, and a file whose fatal is already the lead
635
+ * is counted as unreadable/invalid only, not a second time under "with
636
+ * errors" — its fatal now rides in `errors` as well (see `setFatal`).
637
+ */
638
+ function summarizeFailure(reports, strict, runErrors, name) {
639
+ const fatalFiles = reports.filter((r) => r.fatal !== void 0);
640
+ const erroredFiles = reports.filter((r) => r.fatal === void 0 && r.errors.length > 0);
641
+ const warnedFiles = reports.filter((r) => r.warnings.length > 0);
642
+ let lead;
643
+ if (fatalFiles[0] !== void 0) lead = `${name(fatalFiles[0])}: ${fatalFiles[0].fatal}`;
644
+ else if (erroredFiles[0] !== void 0) lead = `${name(erroredFiles[0])}: ${findingAt(erroredFiles[0].errors[0])}${erroredFiles[0].errors[0].message}`;
645
+ else if (runErrors[0] !== void 0) lead = runErrors[0];
646
+ else if (warnedFiles[0] !== void 0) lead = `${name(warnedFiles[0])}: ${findingAt(warnedFiles[0].warnings[0])}${warnedFiles[0].warnings[0].message} (--strict)`;
647
+ else lead = "the run failed";
648
+ const counts = [];
649
+ if (fatalFiles.length > 0) counts.push(`${fatalFiles.length} unreadable/invalid file(s)`);
650
+ if (erroredFiles.length > 0) counts.push(`${erroredFiles.length} file(s) with errors`);
651
+ if (runErrors.length > 0) counts.push(`${runErrors.length} run-level error(s)`);
652
+ if (strict && warnedFiles.length > 0) counts.push(`${warnedFiles.length} file(s) with warnings (--strict)`);
653
+ return `Error: fixture validation failed — ${lead} [${counts.join(", ")}].`;
654
+ }
655
+ function runValidateCli(deps = {}) {
656
+ const argv = deps.argv ?? process.argv.slice(2);
657
+ const log = deps.log ?? console.log.bind(console);
658
+ const logError = deps.logError ?? console.error.bind(console);
659
+ const exit = deps.exit ?? process.exit.bind(process);
660
+ /**
661
+ * Is `flag` present as an OPTION in argv — i.e. before the `--` terminator,
662
+ * after which every argument is a path? Needed because a usage error can be
663
+ * a `parseArgs` throw, which leaves no parsed `values` to consult, and the
664
+ * output mode still has to be honoured. `--flag=value` counts: these are all
665
+ * boolean options, so giving one a value IS the usage error being reported,
666
+ * and matching the bare token alone answered "no `--json` here" for the one
667
+ * argv that most obviously asked for JSON — leaving stdout empty.
668
+ */
669
+ const flagInArgv = (flag) => {
670
+ for (const arg of argv) {
671
+ if (arg === "--") return false;
672
+ if (arg === flag || arg.startsWith(`${flag}=`)) return true;
673
+ }
674
+ return false;
675
+ };
676
+ /**
677
+ * A usage error, in whichever mode the argv asked for. `--json` promises
678
+ * "one document on stdout" (README), and a run that fails on its own argv is
679
+ * still a run: emitting nothing there handed a CI job parsing stdout an
680
+ * empty buffer — a JSON parse error standing in for a perfectly ordinary
681
+ * "no paths given". The document reports the usage error the same way a
682
+ * run-level error is reported, so one consumer shape reads both. The
683
+ * human-readable reason plus the help text stay on stderr in both modes, so
684
+ * `--json` stdout is still the document alone.
685
+ */
686
+ const usageError = (message) => {
687
+ logError(`Error: ${message}\n\n${VALIDATE_HELP}`);
688
+ if (flagInArgv("--json")) log(JSON.stringify({
689
+ strict: flagInArgv("--strict"),
690
+ failed: true,
691
+ files: [],
692
+ run: {
693
+ fixtures: 0,
694
+ mcpFakeBlocks: 0,
695
+ errors: [message]
696
+ }
697
+ }, null, 2));
698
+ exit(1);
699
+ };
700
+ let values;
701
+ let paths;
702
+ try {
703
+ const parsed = (0, node_util.parseArgs)({
704
+ args: argv,
705
+ options: {
706
+ strict: {
707
+ type: "boolean",
708
+ default: false
709
+ },
710
+ json: {
711
+ type: "boolean",
712
+ default: false
713
+ },
714
+ help: {
715
+ type: "boolean",
716
+ short: "h",
717
+ default: false
718
+ }
719
+ },
720
+ strict: true,
721
+ allowPositionals: true
722
+ });
723
+ values = parsed.values;
724
+ paths = parsed.positionals;
725
+ } catch (err) {
726
+ usageError(err instanceof Error ? err.message : String(err));
727
+ return;
728
+ }
729
+ if (values.help === true) {
730
+ log(values.json === true ? JSON.stringify({ help: VALIDATE_HELP }, null, 2) : VALIDATE_HELP);
731
+ exit(0);
732
+ return;
733
+ }
734
+ const strict = values.strict === true;
735
+ const json = values.json === true;
736
+ if (paths.length === 0) {
737
+ usageError("no fixture paths given.");
738
+ return;
739
+ }
740
+ const targets = [];
741
+ for (const path of paths) {
742
+ if (REMOTE_SOURCE.test(path)) {
743
+ targets.push({
744
+ file: path,
745
+ fatal: "Remote fixture source: this lint reads local paths only (the server's --fixtures accepts a URL; validate does not) — download the file and validate the local copy"
746
+ });
747
+ continue;
748
+ }
749
+ let stats;
750
+ try {
751
+ stats = (0, node_fs.statSync)(path);
752
+ } catch (err) {
753
+ if (err.code === "ENOENT") targets.push({ file: path });
754
+ else targets.push({
755
+ file: path,
756
+ fatal: `Could not stat: ${errText(err)}`
757
+ });
758
+ continue;
759
+ }
760
+ if (!stats.isDirectory()) {
761
+ if (stats.isFile()) targets.push({ file: path });
762
+ else targets.push({
763
+ file: path,
764
+ fatal: "Not a regular file"
765
+ });
766
+ continue;
767
+ }
768
+ const entries = collectJsonFiles(path, []);
769
+ if (entries.length === 0) {
770
+ targets.push({
771
+ file: path,
772
+ fatal: "No .json fixture files found in directory"
773
+ });
774
+ continue;
775
+ }
776
+ for (const entry of entries) targets.push({
777
+ ...entry,
778
+ source: (0, node_path.relative)(path, entry.file).split(node_path.sep).join("/")
779
+ });
780
+ }
781
+ const mentionKey = (file) => (0, node_path.resolve)(file);
782
+ const mentionCount = /* @__PURE__ */ new Map();
783
+ for (const target of targets) mentionCount.set(mentionKey(target.file), (mentionCount.get(mentionKey(target.file)) ?? 0) + 1);
784
+ const mentionsSoFar = /* @__PURE__ */ new Map();
785
+ const nextMention = (file) => {
786
+ const mention = (mentionsSoFar.get(mentionKey(file)) ?? 0) + 1;
787
+ mentionsSoFar.set(mentionKey(file), mention);
788
+ return mention;
789
+ };
790
+ /**
791
+ * How a file is NAMED in human output and in cross-file references. A path
792
+ * this run loads more than once gets its mention number, because the bare
793
+ * path names all of those loads at once: without it a second load's findings
794
+ * read as a file shadowing itself. A path loaded once is printed unadorned —
795
+ * there is nothing to tell it apart from, and "[1]" on every line of an
796
+ * ordinary run is noise. The `--json` report carries `mention` regardless.
797
+ */
798
+ const displayName = (report) => (mentionCount.get(mentionKey(report.file)) ?? 1) > 1 ? `${report.file}[${report.mention}]` : report.file;
799
+ const reports = [];
800
+ const union = [];
801
+ const provenance = [];
802
+ const reportedBySite = /* @__PURE__ */ new Map();
803
+ const fileIds = /* @__PURE__ */ new Map();
804
+ const runErrors = [];
805
+ const fakeStore = new require_mcp_fakes.McpFakeStore();
806
+ let mcpFakeBlocks = 0;
807
+ const siteKey = (fileId, index, severity, kind) => [
808
+ fileId,
809
+ index,
810
+ severity,
811
+ kind
812
+ ].join("\0");
813
+ for (const target of targets) {
814
+ if (target.skipped !== void 0) {
815
+ reports.push({
816
+ file: target.file,
817
+ mention: nextMention(target.file),
818
+ fixtures: 0,
819
+ errors: [],
820
+ warnings: [],
821
+ skipped: target.skipped
822
+ });
823
+ continue;
824
+ }
825
+ if (target.fatal !== void 0) {
826
+ reports.push(fatalReport(target.file, nextMention(target.file), target.fatal));
827
+ continue;
828
+ }
829
+ let report;
830
+ let fixtures = [];
831
+ let indices = [];
832
+ let identities = [];
833
+ let fileRunErrors = [];
834
+ let mcpFakes = [];
835
+ const mention = nextMention(target.file);
836
+ try {
837
+ ({report, fixtures, indices, identities, runErrors: fileRunErrors, mcpFakes} = validateOneFile(target.file, mention, target.source ?? target.file));
838
+ } catch (err) {
839
+ report = fatalReport(target.file, mention, `Validation failed: ${errText(err)}`);
840
+ }
841
+ if (mcpFakes.length > 0) try {
842
+ const added = fakeStore.add(mcpFakes, { kind: "file" });
843
+ report.mcpFakeBlocks = mcpFakes.length;
844
+ mcpFakeBlocks += mcpFakes.length;
845
+ report.warnings.push(...added.warnings.map(issueFinding));
846
+ } catch (err) {
847
+ if (err instanceof require_mcp_fakes.McpFakesAddError) {
848
+ report.errors.push(...err.errors.map(loadErrorFinding));
849
+ report.warnings.push(...err.warnings.map(issueFinding));
850
+ } else report.errors.push({ message: require_message_text.build(require_message_text.msg`mcpFakes check failed: ${require_message_text.plainText(errText(err), Infinity)}`) });
851
+ }
852
+ reports.push(report);
853
+ runErrors.push(...fileRunErrors.map((e) => `${displayName(report)}: ${e}`));
854
+ const fileId = fileIds.size;
855
+ fileIds.set(report, fileId);
856
+ for (const identity of identities) {
857
+ const key = siteKey(fileId, identity.index, identity.severity, identity.kind);
858
+ const atSite = reportedBySite.get(key) ?? [];
859
+ atSite.push({
860
+ finding: identity.finding,
861
+ refs: identity.refs.map((ref) => `${fileId}:${ref}`)
862
+ });
863
+ reportedBySite.set(key, atSite);
864
+ }
865
+ for (let i = 0; i < fixtures.length; i++) {
866
+ const index = indices[i];
867
+ if (index === void 0) runErrors.push(`${displayName(report)}: converted fixture ${i} has no source entry index; cross-file findings against it are reported at its array position`);
868
+ union.push(fixtures[i]);
869
+ provenance.push({
870
+ report,
871
+ index: index ?? i
872
+ });
873
+ }
874
+ }
875
+ let noFixturesLoaded = false;
876
+ const causeAlreadyReported = reports.some((r) => r.fatal !== void 0 || r.errors.length > 0);
877
+ if (union.length === 0) {
878
+ if (!causeAlreadyReported && mcpFakeBlocks === 0) {
879
+ runErrors.push("No fixtures loaded from any input — the server aborts startup on this under --validate-on-load/--strict");
880
+ noFixturesLoaded = true;
881
+ }
882
+ } else {
883
+ const describe = (unionIndex) => {
884
+ const origin = provenance[unionIndex];
885
+ return origin === void 0 ? String(unionIndex) : `${displayName(origin.report)} #${origin.index}`;
886
+ };
887
+ const targetOf = (unionIndex) => {
888
+ const origin = provenance[unionIndex];
889
+ if (origin === void 0) return `union:${unionIndex}`;
890
+ const fileId = fileIds.get(origin.report);
891
+ return fileId === void 0 ? `union:${unionIndex}` : `${fileId}:${origin.index}`;
892
+ };
893
+ const renderCrossFile = (ref) => {
894
+ if (ref.rule === "duplicate-user-message") return `duplicate userMessage '${ref.userMessage}' — shadows fixture ${describe(ref.shadows)}`;
895
+ const head = "empty match acts as catch-all but is not the last fixture";
896
+ return ref.shadowsFrom === ref.shadowsThrough ? `${head} — shadows ${describe(ref.shadowsFrom)}` : `${head} — shadows every later fixture, from ${describe(ref.shadowsFrom)} through ${describe(ref.shadowsThrough)}`;
897
+ };
898
+ try {
899
+ for (const r of require_fixture_loader.validateFixtures(union)) {
900
+ const origin = provenance[r.fixtureIndex];
901
+ if (origin === void 0) {
902
+ runErrors.push(`Cross-file finding for an unknown fixture index ${r.fixtureIndex}: ${r.message}`);
903
+ continue;
904
+ }
905
+ const fileId = fileIds.get(origin.report);
906
+ if (fileId === void 0) {
907
+ runErrors.push(`Cross-file finding for an unrecognised file: ${r.message}`);
908
+ continue;
909
+ }
910
+ const kind = r.ref === void 0 ? `message:${r.message}` : r.ref.rule;
911
+ const refTargets = r.ref === void 0 ? [] : refIndices(r.ref).map(targetOf);
912
+ const message = `cross-file: ${r.ref === void 0 ? r.message : renderCrossFile(r.ref)}`;
913
+ const already = reportedBySite.get(siteKey(fileId, origin.index, r.severity, kind))?.shift();
914
+ if (already !== void 0) {
915
+ if (!(already.refs.length === refTargets.length && already.refs.every((target, i) => target === refTargets[i]))) already.finding.message = message;
916
+ continue;
917
+ }
918
+ if (r.severity === "error") origin.report.errors.push({
919
+ index: origin.index,
920
+ message
921
+ });
922
+ else origin.report.warnings.push({
923
+ index: origin.index,
924
+ message
925
+ });
926
+ }
927
+ } catch (err) {
928
+ runErrors.push(`Cross-file validation failed: ${errText(err)}`);
929
+ }
930
+ }
931
+ const hasFatal = reports.some((r) => r.fatal !== void 0);
932
+ const hasErrors = reports.some((r) => r.errors.length > 0) || runErrors.length > 0;
933
+ const hasWarnings = reports.some((r) => r.warnings.length > 0);
934
+ const failed = hasFatal || hasErrors || strict && hasWarnings;
935
+ const logWarning = strict ? logError : log;
936
+ if (json) {
937
+ log(JSON.stringify({
938
+ strict,
939
+ failed,
940
+ files: reports,
941
+ run: {
942
+ fixtures: union.length,
943
+ mcpFakeBlocks,
944
+ errors: runErrors
945
+ }
946
+ }, null, 2));
947
+ if (failed) logError(summarizeFailure(reports, strict, runErrors, displayName));
948
+ } else {
949
+ const at = findingAt;
950
+ const emitDetail = (f, sink) => {
951
+ if (f.detail !== void 0) sink(` ${f.detail}`);
952
+ };
953
+ for (const r of reports) {
954
+ const name = displayName(r);
955
+ if (r.skipped !== void 0) {
956
+ log(`${name}: skipped (${r.skipped})`);
957
+ continue;
958
+ }
959
+ for (const e of r.errors) {
960
+ logError(`${name}: [error] ${at(e)}${e.message}`);
961
+ emitDetail(e, logError);
962
+ }
963
+ for (const w of r.warnings) {
964
+ logWarning(`${name}: [warning] ${at(w)}${w.message}`);
965
+ emitDetail(w, logWarning);
966
+ }
967
+ if (r.errors.length === 0 && r.warnings.length === 0) {
968
+ const blocks = r.mcpFakeBlocks ?? 0;
969
+ if (r.fixtures === 0 && blocks === 0 && failed) {
970
+ const why = noFixturesLoaded ? "see the run-level error" : "the run failed elsewhere";
971
+ log(`${name}: ${r.fixtures} fixture(s), loaded nothing — ${why}`);
972
+ } else if (blocks > 0) log(require_message_text.build(require_message_text.msg`${require_message_text.plainText(name, Infinity)}: OK (${r.fixtures} fixture(s), ${blocks} mcpFakes block(s))`));
973
+ else log(`${name}: OK (${r.fixtures} fixture(s))`);
974
+ } else log(`${name}: ${r.fixtures} fixture(s), ${r.errors.length} error(s), ${r.warnings.length} warning(s)`);
975
+ }
976
+ for (const e of runErrors) logError(`[error] ${e}`);
977
+ }
978
+ exit(failed ? 1 : 0);
979
+ }
980
+
981
+ //#endregion
982
+ exports.runValidateCli = runValidateCli;
983
+ //# sourceMappingURL=validate-cli.cjs.map