@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
package/dist/cli.js CHANGED
@@ -1,15 +1,19 @@
1
1
  #!/usr/bin/env node
2
+ import { build, msg } from "./message-text.js";
2
3
  import { resolveInboundAuth, selectInboundAuthSource } from "./api-key-auth.js";
4
+ import { CHAOS_FIELDS, parseChaosField } from "./chaos.js";
5
+ import { FixtureLoadError, validateFixtures } from "./fixture-loader.js";
6
+ import { readProviderKeysFromEnv } from "./provider-auth.js";
3
7
  import { Logger } from "./logger.js";
4
8
  import { AGUIMock } from "./agui-mock.js";
5
- import { loadFixtureFile, loadFixturesFromDir, validateFixtures } from "./fixture-loader.js";
6
- import { readProviderKeysFromEnv } from "./provider-auth.js";
9
+ import { McpFakesAddError } from "./mcp-fakes.js";
7
10
  import { createServer } from "./server.js";
11
+ import { loadFixtureFileWithServices, loadFixturesFromDirWithServices } from "./fixture-loader-services.js";
8
12
  import { watchFixtures } from "./watcher.js";
9
13
  import { resolveFixturesValue } from "./fixtures-remote.js";
10
14
  import { statSync } from "node:fs";
11
15
  import { resolve } from "node:path";
12
- import { parseArgs } from "node:util";
16
+ import { isDeepStrictEqual, parseArgs } from "node:util";
13
17
 
14
18
  //#region src/cli.ts
15
19
  const HELP = `
@@ -54,6 +58,8 @@ Options:
54
58
  --chaos-drop <rate> Probability (0-1) of dropping requests with 500
55
59
  --chaos-malformed <rate> Probability (0-1) of returning malformed JSON
56
60
  --chaos-disconnect <rate> Probability (0-1) of destroying connection
61
+ --chaos-ratelimit <rate> Probability (0-1) of 429 with Retry-After
62
+ --chaos-latency <ms> Delay (0-${CHAOS_FIELDS.latencyMs.max}ms) injected before handling
57
63
  AIMOCK_API_KEYS Comma-separated inbound test API keys (environment only)
58
64
  --help Show this help message
59
65
  `.trim();
@@ -147,6 +153,8 @@ const { values } = parseArgs({
147
153
  "chaos-drop": { type: "string" },
148
154
  "chaos-malformed": { type: "string" },
149
155
  "chaos-disconnect": { type: "string" },
156
+ "chaos-ratelimit": { type: "string" },
157
+ "chaos-latency": { type: "string" },
150
158
  "journal-max": {
151
159
  type: "string",
152
160
  default: "1000"
@@ -254,31 +262,45 @@ let chaos;
254
262
  const dropStr = values["chaos-drop"];
255
263
  const malformedStr = values["chaos-malformed"];
256
264
  const disconnectStr = values["chaos-disconnect"];
257
- if (dropStr !== void 0 || malformedStr !== void 0 || disconnectStr !== void 0) {
265
+ const ratelimitStr = values["chaos-ratelimit"];
266
+ const latencyStr = values["chaos-latency"];
267
+ if (dropStr !== void 0 || malformedStr !== void 0 || disconnectStr !== void 0 || ratelimitStr !== void 0 || latencyStr !== void 0) {
258
268
  chaos = {};
259
- if (dropStr !== void 0) {
260
- const val = parseFloat(dropStr);
261
- if (isNaN(val) || val < 0 || val > 1) {
262
- console.error(`Invalid chaos-drop: ${dropStr} (must be 0-1)`);
263
- process.exit(1);
264
- }
265
- chaos.dropRate = val;
266
- }
267
- if (malformedStr !== void 0) {
268
- const val = parseFloat(malformedStr);
269
- if (isNaN(val) || val < 0 || val > 1) {
270
- console.error(`Invalid chaos-malformed: ${malformedStr} (must be 0-1)`);
271
- process.exit(1);
269
+ const flagFields = [
270
+ {
271
+ flag: "chaos-drop",
272
+ field: "dropRate",
273
+ raw: dropStr
274
+ },
275
+ {
276
+ flag: "chaos-malformed",
277
+ field: "malformedRate",
278
+ raw: malformedStr
279
+ },
280
+ {
281
+ flag: "chaos-disconnect",
282
+ field: "disconnectRate",
283
+ raw: disconnectStr
284
+ },
285
+ {
286
+ flag: "chaos-ratelimit",
287
+ field: "rateLimitRate",
288
+ raw: ratelimitStr
289
+ },
290
+ {
291
+ flag: "chaos-latency",
292
+ field: "latencyMs",
293
+ raw: latencyStr
272
294
  }
273
- chaos.malformedRate = val;
274
- }
275
- if (disconnectStr !== void 0) {
276
- const val = parseFloat(disconnectStr);
277
- if (isNaN(val) || val < 0 || val > 1) {
278
- console.error(`Invalid chaos-disconnect: ${disconnectStr} (must be 0-1)`);
295
+ ];
296
+ for (const { flag, field, raw } of flagFields) {
297
+ if (raw === void 0) continue;
298
+ const val = parseChaosField(field, raw);
299
+ if (val === void 0) {
300
+ console.error(`Invalid ${flag}: ${raw} (must be 0-${CHAOS_FIELDS[field].max})`);
279
301
  process.exit(1);
280
302
  }
281
- chaos.disconnectRate = val;
303
+ chaos[field] = val;
282
304
  }
283
305
  }
284
306
  }
@@ -389,13 +411,67 @@ async function resolveAllFixtureSources() {
389
411
  return resolved;
390
412
  }
391
413
  function loadSource(source) {
392
- return source.isDir ? loadFixturesFromDir(source.path, logger) : loadFixtureFile(source.path, logger);
414
+ return source.isDir ? loadFixturesFromDirWithServices(source.path, logger) : loadFixtureFileWithServices(source.path, logger, void 0, source.source);
393
415
  }
416
+ /** Each source's `mcpFakes` blocks, in load order, for the --watch comparison. */
417
+ function fakesBySource(blocks) {
418
+ const bySource = /* @__PURE__ */ new Map();
419
+ for (const block of blocks) {
420
+ const list = bySource.get(block.source) ?? [];
421
+ list.push([block.blockIndex, block.raw]);
422
+ bySource.set(block.source, list);
423
+ }
424
+ return bySource;
425
+ }
426
+ /**
427
+ * R9/F11: fakes are not reloaded by --watch, so a reload whose `mcpFakes`
428
+ * differ from the boot set is rejected. Returns the first changed source.
429
+ * A source in `unreadable` could not be read or parsed: it is not compared,
430
+ * because its blocks are unknown, not removed.
431
+ */
432
+ function changedFakesSource(boot, reload, unreadable) {
433
+ const before = fakesBySource(boot);
434
+ const after = fakesBySource(reload);
435
+ for (const source of new Set([...before.keys(), ...after.keys()])) {
436
+ if (unreadable.has(source)) continue;
437
+ if (!isDeepStrictEqual(before.get(source), after.get(source))) return source;
438
+ }
439
+ return null;
440
+ }
441
+ /**
442
+ * L3: a load error on stderr, whatever the log level: every error of a
443
+ * `McpFakesAddError`, one per line, then its L8 warnings.
444
+ */
445
+ function printLoadError(err) {
446
+ const errors = err instanceof McpFakesAddError ? err.errors : [err];
447
+ for (const e of errors) console.error(e.message);
448
+ if (err instanceof McpFakesAddError) for (const w of err.warnings) console.warn(w.message);
449
+ }
450
+ /** A --watch reload that `loadFn` rejected and already printed (L3, L7). */
451
+ var ReloadRejected = class extends Error {};
452
+ /**
453
+ * The --watch logger: as `Logger`, except that the watcher's own report of a
454
+ * {@link ReloadRejected} reload is dropped, so the error is printed once.
455
+ */
456
+ var WatchLogger = class extends Logger {
457
+ error(...args) {
458
+ if (args.some((a) => a instanceof ReloadRejected)) return;
459
+ super.error(...args);
460
+ }
461
+ };
394
462
  async function main() {
395
463
  const sources = await resolveAllFixtureSources();
396
464
  const fixtures = [];
397
- for (const src of sources) fixtures.push(...loadSource(src));
398
- if (fixtures.length === 0) {
465
+ const mcpFakes = [];
466
+ let primaryFakes = [];
467
+ for (const src of sources) {
468
+ const loaded = loadSource(src);
469
+ fixtures.push(...loaded.fixtures);
470
+ mcpFakes.push(...loaded.mcpFakes);
471
+ if (src === sources[0]) primaryFakes = loaded.mcpFakes;
472
+ }
473
+ if (fixtures.length === 0 && mcpFakes.length > 0) console.warn(build(msg`Warning: No LLM fixtures loaded; LLM requests will return 404`));
474
+ else if (fixtures.length === 0) {
399
475
  if (validateOnLoad || values.strict) {
400
476
  console.error("Error: No fixtures loaded and validation/strict mode is enabled — aborting.");
401
477
  process.exit(1);
@@ -430,7 +506,12 @@ async function main() {
430
506
  journalMaxEntries: journalMax,
431
507
  fixtureCountsMaxTestIds: fixtureCountsMax,
432
508
  auth: resolveInboundAuth(selectInboundAuthSource(void 0)).publicConfig
433
- }, mounts);
509
+ }, mounts, {
510
+ search: [],
511
+ rerank: [],
512
+ moderation: [],
513
+ mcpFakes
514
+ });
434
515
  let watcher = null;
435
516
  function shutdown() {
436
517
  logger.info("Shutting down...");
@@ -446,9 +527,28 @@ async function main() {
446
527
  const primary = sources[0];
447
528
  if (!primary) logger.warn("--watch requested but no resolvable fixture sources; skipping watcher");
448
529
  else {
449
- const loadFn = () => loadSource(primary);
530
+ const reject = (err) => {
531
+ printLoadError(err);
532
+ throw new ReloadRejected(build(msg`--watch reload rejected`));
533
+ };
534
+ const loadFn = () => {
535
+ let loaded;
536
+ try {
537
+ loaded = loadSource(primary);
538
+ } catch (err) {
539
+ if (err instanceof FixtureLoadError) return reject(err);
540
+ throw err;
541
+ }
542
+ const changed = changedFakesSource(primaryFakes, loaded.mcpFakes, new Set(loaded.unreadable));
543
+ if (changed !== null) return reject(new FixtureLoadError({
544
+ rule: "mcp-fakes/watch-reload-changed",
545
+ file: changed,
546
+ detail: msg`mcpFakes changed on --watch reload; MCP fakes are not reloaded, so the whole reload is rejected and the previous fixtures stay loaded. Restart aimock to load the new fakes`
547
+ }));
548
+ return loaded.fixtures;
549
+ };
450
550
  watcher = watchFixtures(primary.path, fixtures, loadFn, {
451
- logger,
551
+ logger: new WatchLogger(logLevel),
452
552
  validate: validateOnLoad,
453
553
  validateFn: validateFixtures
454
554
  });
@@ -457,7 +557,8 @@ async function main() {
457
557
  }
458
558
  }
459
559
  main().catch((err) => {
460
- console.error(err);
560
+ if (err instanceof FixtureLoadError) printLoadError(err);
561
+ else console.error(err);
461
562
  process.exit(1);
462
563
  });
463
564
 
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from \"node:util\";\nimport { statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createServer } from \"./server.js\";\nimport { loadFixtureFile, loadFixturesFromDir, validateFixtures } from \"./fixture-loader.js\";\nimport { Logger, type LogLevel } from \"./logger.js\";\nimport { watchFixtures } from \"./watcher.js\";\nimport { AGUIMock } from \"./agui-mock.js\";\nimport { resolveFixturesValue } from \"./fixtures-remote.js\";\nimport { readProviderKeysFromEnv } from \"./provider-auth.js\";\nimport { resolveInboundAuth, selectInboundAuthSource } from \"./api-key-auth.js\";\nimport type { Fixture, ChaosConfig, RecordConfig } from \"./types.js\";\n\nconst HELP = `\nUsage: aimock [options]\n\nOptions:\n -p, --port <number> Port to listen on (default: 4010)\n -h, --host <string> Host to bind to (default: 127.0.0.1)\n -f, --fixtures <value> Fixture source (repeatable). Accepts:\n - filesystem path to a directory or .json file (default: ./fixtures)\n - https:// or http:// URL to a .json fixture file\n -l, --latency <ms> Latency in ms between SSE chunks (default: 0)\n -c, --chunk-size <chars> Chunk size in characters (default: 20)\n -w, --watch Watch fixture path for changes and reload\n --log-level <level> Log verbosity: silent, warn, info, debug (default: info)\n --validate-on-load Validate fixture schemas at startup\n --metrics Enable Prometheus metrics at GET /metrics\n --record Record mode: proxy unmatched requests and save fixtures\n --record-full-model-version Record exact model version without date stripping (default: false)\n --proxy-only Proxy mode: forward unmatched requests without saving\n --strict Strict mode: fail on unmatched requests (overridable per-request via X-AIMock-Strict header)\n --journal-max <n> Max request entries retained in memory (default: 1000, 0 = unbounded)\n --fixture-counts-max <n> Max unique testIds retained in fixture match-count map (default: 500, 0 = unbounded)\n --provider-openai <url> Upstream URL for OpenAI (used with --record)\n --provider-anthropic <url> Upstream URL for Anthropic\n --provider-gemini <url> Upstream URL for Gemini\n --provider-vertexai <url> Upstream URL for Vertex AI\n --provider-bedrock <url> Upstream URL for Bedrock\n --provider-azure <url> Upstream URL for Azure OpenAI\n --provider-ollama <url> Upstream URL for Ollama\n --provider-cohere <url> Upstream URL for Cohere\n --provider-openrouter <url> Upstream URL for OpenRouter (video record proxy)\n --provider-byteplus <url> Upstream ORIGIN for BytePlus Ark (e.g. https://ark.ap-southeast.bytepluses.com — origin only, no /api/v3)\n --upstream-timeout-ms <ms> Idle timeout (ms) on upstream socket before response (default: 30000)\n --body-timeout-ms <ms> Idle timeout (ms) on upstream response body between chunks (default: 30000)\n --max-proxy-buffer-bytes <n> Cap (bytes) on in-memory proxy-path buffer; full body still relayed (default: 67108864)\n --max-proxy-buffer-frames <n> Cap (frames) on in-memory proxy-path per-frame state; full body still relayed (default: 5000000)\n --agui-record Enable AG-UI recording (proxy unmatched AG-UI requests)\n --agui-upstream <url> Upstream AG-UI agent URL (used with --agui-record)\n --agui-proxy-only AG-UI proxy mode: forward without saving\n --replay-speed <n> Replay speed multiplier (default: 1.0, 2.0 = 2x faster)\n --chaos-drop <rate> Probability (0-1) of dropping requests with 500\n --chaos-malformed <rate> Probability (0-1) of returning malformed JSON\n --chaos-disconnect <rate> Probability (0-1) of destroying connection\n AIMOCK_API_KEYS Comma-separated inbound test API keys (environment only)\n --help Show this help message\n`.trim();\n\nconst { values } = parseArgs({\n options: {\n port: { type: \"string\", short: \"p\", default: \"4010\" },\n host: { type: \"string\", short: \"h\", default: \"127.0.0.1\" },\n fixtures: { type: \"string\", short: \"f\", multiple: true },\n latency: { type: \"string\", short: \"l\", default: \"0\" },\n \"chunk-size\": { type: \"string\", short: \"c\", default: \"20\" },\n watch: { type: \"boolean\", short: \"w\", default: false },\n \"log-level\": { type: \"string\", default: \"info\" },\n \"validate-on-load\": { type: \"boolean\", default: false },\n metrics: { type: \"boolean\", default: false },\n record: { type: \"boolean\", default: false },\n \"record-full-model-version\": { type: \"boolean\", default: false },\n \"proxy-only\": { type: \"boolean\", default: false },\n strict: { type: \"boolean\", default: false },\n \"provider-openai\": { type: \"string\" },\n \"provider-anthropic\": { type: \"string\" },\n \"provider-gemini\": { type: \"string\" },\n \"provider-vertexai\": { type: \"string\" },\n \"provider-bedrock\": { type: \"string\" },\n \"provider-azure\": { type: \"string\" },\n \"provider-ollama\": { type: \"string\" },\n \"provider-cohere\": { type: \"string\" },\n \"provider-openrouter\": { type: \"string\" },\n \"provider-byteplus\": { type: \"string\" },\n \"upstream-timeout-ms\": { type: \"string\" },\n \"body-timeout-ms\": { type: \"string\" },\n \"max-proxy-buffer-bytes\": { type: \"string\" },\n \"max-proxy-buffer-frames\": { type: \"string\" },\n \"agui-record\": { type: \"boolean\", default: false },\n \"agui-upstream\": { type: \"string\" },\n \"agui-proxy-only\": { type: \"boolean\", default: false },\n \"replay-speed\": { type: \"string\", default: \"1.0\" },\n \"chaos-drop\": { type: \"string\" },\n \"chaos-malformed\": { type: \"string\" },\n \"chaos-disconnect\": { type: \"string\" },\n \"journal-max\": { type: \"string\", default: \"1000\" },\n \"fixture-counts-max\": { type: \"string\", default: \"500\" },\n help: { type: \"boolean\", default: false },\n },\n strict: true,\n});\n\nif (values.help) {\n console.log(HELP);\n process.exit(0);\n}\n\nconst port = Number(values.port);\nconst host = values.host!;\nconst latency = Number(values.latency);\nconst chunkSize = Number(values[\"chunk-size\"]);\nconst fixtureValues: string[] =\n values.fixtures && values.fixtures.length > 0 ? values.fixtures : [\"./fixtures\"];\nconst watchMode = values.watch!;\nconst validateOnLoad = values[\"validate-on-load\"]!;\nconst logLevelStr = values[\"log-level\"]!;\n\nif (![\"silent\", \"warn\", \"info\", \"debug\"].includes(logLevelStr)) {\n console.error(`Invalid log-level: ${logLevelStr} (must be silent, warn, info, or debug)`);\n process.exit(1);\n}\nconst logLevel = logLevelStr as LogLevel;\n\nif (Number.isNaN(port) || port < 0 || port > 65535) {\n console.error(`Invalid port: ${values.port}`);\n process.exit(1);\n}\n\nif (Number.isNaN(latency) || latency < 0) {\n console.error(`Invalid latency: ${values.latency}`);\n process.exit(1);\n}\n\nif (Number.isNaN(chunkSize) || chunkSize < 1) {\n console.error(`Invalid chunk-size: ${values[\"chunk-size\"]}`);\n process.exit(1);\n}\n\nconst replaySpeed = Number(values[\"replay-speed\"]);\nif (Number.isNaN(replaySpeed) || replaySpeed <= 0) {\n console.error(\"--replay-speed must be a positive number\");\n process.exit(1);\n}\n\nconst journalMax = Number(values[\"journal-max\"]);\nif (Number.isNaN(journalMax) || !Number.isInteger(journalMax) || journalMax < 0) {\n console.error(\n `Invalid journal-max: ${values[\"journal-max\"]} (must be a non-negative integer; 0 = unbounded)`,\n );\n process.exit(1);\n}\n\nconst fixtureCountsMaxStr = values[\"fixture-counts-max\"];\nconst fixtureCountsMax = Number(fixtureCountsMaxStr);\nif (Number.isNaN(fixtureCountsMax) || !Number.isInteger(fixtureCountsMax) || fixtureCountsMax < 0) {\n console.error(\n `Invalid fixture-counts-max: ${fixtureCountsMaxStr} (must be a non-negative integer; 0 = unbounded)`,\n );\n process.exit(1);\n}\n\nconst upstreamTimeoutMsStr = values[\"upstream-timeout-ms\"];\nlet upstreamTimeoutMs: number | undefined;\nif (upstreamTimeoutMsStr !== undefined) {\n upstreamTimeoutMs = Number(upstreamTimeoutMsStr);\n if (!Number.isFinite(upstreamTimeoutMs) || upstreamTimeoutMs <= 0) {\n console.error(\n `Invalid upstream-timeout-ms: ${upstreamTimeoutMsStr} (must be a positive finite number)`,\n );\n process.exit(1);\n }\n}\n\nconst bodyTimeoutMsStr = values[\"body-timeout-ms\"];\nlet bodyTimeoutMs: number | undefined;\nif (bodyTimeoutMsStr !== undefined) {\n bodyTimeoutMs = Number(bodyTimeoutMsStr);\n if (!Number.isFinite(bodyTimeoutMs) || bodyTimeoutMs <= 0) {\n console.error(\n `Invalid body-timeout-ms: ${bodyTimeoutMsStr} (must be a positive finite number)`,\n );\n process.exit(1);\n }\n}\n\nconst maxProxyBufferBytesStr = values[\"max-proxy-buffer-bytes\"];\nlet maxProxyBufferBytes: number | undefined;\nif (maxProxyBufferBytesStr !== undefined) {\n maxProxyBufferBytes = Number(maxProxyBufferBytesStr);\n if (!Number.isFinite(maxProxyBufferBytes) || maxProxyBufferBytes <= 0) {\n console.error(\n `Invalid max-proxy-buffer-bytes: ${maxProxyBufferBytesStr} (must be a positive finite number)`,\n );\n process.exit(1);\n }\n}\n\nconst maxProxyBufferFramesStr = values[\"max-proxy-buffer-frames\"];\nlet maxProxyBufferFrames: number | undefined;\nif (maxProxyBufferFramesStr !== undefined) {\n maxProxyBufferFrames = Number(maxProxyBufferFramesStr);\n if (!Number.isFinite(maxProxyBufferFrames) || maxProxyBufferFrames <= 0) {\n console.error(\n `Invalid max-proxy-buffer-frames: ${maxProxyBufferFramesStr} (must be a positive finite number)`,\n );\n process.exit(1);\n }\n}\n\nconst logger = new Logger(logLevel);\n\n// Parse chaos config from CLI flags\nlet chaos: ChaosConfig | undefined;\n{\n const dropStr = values[\"chaos-drop\"];\n const malformedStr = values[\"chaos-malformed\"];\n const disconnectStr = values[\"chaos-disconnect\"];\n\n if (dropStr !== undefined || malformedStr !== undefined || disconnectStr !== undefined) {\n chaos = {};\n if (dropStr !== undefined) {\n const val = parseFloat(dropStr);\n if (isNaN(val) || val < 0 || val > 1) {\n console.error(`Invalid chaos-drop: ${dropStr} (must be 0-1)`);\n process.exit(1);\n }\n chaos.dropRate = val;\n }\n if (malformedStr !== undefined) {\n const val = parseFloat(malformedStr);\n if (isNaN(val) || val < 0 || val > 1) {\n console.error(`Invalid chaos-malformed: ${malformedStr} (must be 0-1)`);\n process.exit(1);\n }\n chaos.malformedRate = val;\n }\n if (disconnectStr !== undefined) {\n const val = parseFloat(disconnectStr);\n if (isNaN(val) || val < 0 || val > 1) {\n console.error(`Invalid chaos-disconnect: ${disconnectStr} (must be 0-1)`);\n process.exit(1);\n }\n chaos.disconnectRate = val;\n }\n }\n}\n\n// Parse record/proxy config from CLI flags\nlet record: RecordConfig | undefined;\nif (values.record || values[\"proxy-only\"]) {\n const providers: RecordConfig[\"providers\"] = {};\n if (values[\"provider-openai\"]) providers.openai = values[\"provider-openai\"];\n if (values[\"provider-anthropic\"]) providers.anthropic = values[\"provider-anthropic\"];\n if (values[\"provider-gemini\"]) providers.gemini = values[\"provider-gemini\"];\n if (values[\"provider-vertexai\"]) providers.vertexai = values[\"provider-vertexai\"];\n if (values[\"provider-bedrock\"]) providers.bedrock = values[\"provider-bedrock\"];\n if (values[\"provider-azure\"]) providers.azure = values[\"provider-azure\"];\n if (values[\"provider-ollama\"]) providers.ollama = values[\"provider-ollama\"];\n if (values[\"provider-cohere\"]) providers.cohere = values[\"provider-cohere\"];\n if (values[\"provider-openrouter\"]) providers.openrouter = values[\"provider-openrouter\"];\n if (values[\"provider-byteplus\"]) providers.byteplus = values[\"provider-byteplus\"];\n\n if (Object.keys(providers).length === 0) {\n console.error(\n `Error: --${values[\"proxy-only\"] ? \"proxy-only\" : \"record\"} requires at least one --provider-* flag`,\n );\n process.exit(1);\n }\n\n // For --record, the first --fixtures value is the base path for the recording\n // destination and must be a local filesystem path — writing to a URL is not supported.\n // For --proxy-only, unmatched requests are forwarded without saving, so no writable\n // destination is required; URL-only --fixtures is valid in that mode.\n const recordBase = fixtureValues[0];\n const recordBaseIsUrl = /^https?:\\/\\//i.test(recordBase);\n if (values.record && recordBaseIsUrl) {\n console.error(\n `Error: --record requires a local --fixtures path for the recording destination; got URL ${recordBase}`,\n );\n process.exit(1);\n }\n record = {\n providers,\n // aimock's own upstream keys, sourced from AIMOCK_PROVIDER_*_KEY env vars\n // (not CLI flags — secrets must not appear in `ps`). Injected on a\n // fixture-miss passthrough when the caller sent no/dummy credential.\n providerKeys: readProviderKeysFromEnv(),\n // In proxy-only mode with only URL sources, fixturePath is never consumed\n // (recorder.ts skips disk writes when proxyOnly is set). Leave it undefined\n // rather than resolving a URL string as a filesystem path.\n fixturePath: recordBaseIsUrl ? undefined : resolve(recordBase, \"recorded\"),\n proxyOnly: values[\"proxy-only\"],\n recordFullModelVersion: values[\"record-full-model-version\"],\n upstreamTimeoutMs,\n bodyTimeoutMs,\n maxProxyBufferBytes,\n maxProxyBufferFrames,\n };\n} else {\n // These flags configure upstream proxying — without --record or\n // --proxy-only they would be parsed and then silently dropped. Routed\n // through the constructed logger so --log-level is respected.\n const droppedProviderFlags = (\n [\n \"provider-openai\",\n \"provider-anthropic\",\n \"provider-gemini\",\n \"provider-vertexai\",\n \"provider-bedrock\",\n \"provider-azure\",\n \"provider-ollama\",\n \"provider-cohere\",\n \"provider-openrouter\",\n \"provider-byteplus\",\n ] as const\n ).filter((flag) => values[flag] !== undefined);\n if (droppedProviderFlags.length > 0) {\n logger.warn(\n `--${droppedProviderFlags.join(\"/--\")} only apply to --record/--proxy-only upstream proxying — ignored without one of those flags.`,\n );\n }\n if (\n upstreamTimeoutMs !== undefined ||\n bodyTimeoutMs !== undefined ||\n maxProxyBufferBytes !== undefined ||\n maxProxyBufferFrames !== undefined\n ) {\n logger.warn(\n \"--upstream-timeout-ms/--body-timeout-ms/--max-proxy-buffer-bytes/--max-proxy-buffer-frames only apply to --record/--proxy-only upstream proxying — ignored without one of those flags.\",\n );\n }\n}\n\n// Parse AG-UI record/proxy config from CLI flags\nlet aguiMount: { path: string; handler: AGUIMock } | undefined;\nif (values[\"agui-record\"] || values[\"agui-proxy-only\"]) {\n if (!values[\"agui-upstream\"]) {\n console.error(\"Error: --agui-record/--agui-proxy-only requires --agui-upstream\");\n process.exit(1);\n }\n // --agui-record writes recorded AG-UI fixtures to disk, so a URL source is unsupported.\n // --agui-proxy-only forwards without saving, so URL-only --fixtures is valid.\n const aguiBase = fixtureValues[0];\n const aguiBaseIsUrl = /^https?:\\/\\//i.test(aguiBase);\n if (values[\"agui-record\"] && aguiBaseIsUrl) {\n console.error(\n `Error: --agui-record requires a local --fixtures path for the recording destination; got URL ${aguiBase}`,\n );\n process.exit(1);\n }\n const agui = new AGUIMock();\n agui.enableRecording({\n upstream: values[\"agui-upstream\"],\n // In proxy-only mode with a URL-only --fixtures, the AG-UI recorder never\n // writes to disk (see agui-recorder.ts). Leave fixturePath undefined rather\n // than resolving a URL as a filesystem path.\n fixturePath: aguiBaseIsUrl ? undefined : resolve(aguiBase, \"agui-recorded\"),\n proxyOnly: values[\"agui-proxy-only\"],\n });\n aguiMount = { path: \"/agui\", handler: agui };\n}\n\ninterface ResolvedFixtureSource {\n source: string;\n path: string;\n isDir: boolean;\n}\n\nasync function resolveAllFixtureSources(): Promise<ResolvedFixtureSource[]> {\n const resolved: ResolvedFixtureSource[] = [];\n for (const value of fixtureValues) {\n let local;\n try {\n local = await resolveFixturesValue(value, {\n validateOnLoad,\n logger,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Failed to resolve --fixtures value \"${value}\": ${msg}`);\n process.exit(1);\n }\n if (!local.path) {\n // Remote fetch failed without validate-on-load and no cache — already warned; skip.\n continue;\n }\n try {\n const stat = statSync(local.path);\n resolved.push({ source: local.source, path: local.path, isDir: stat.isDirectory() });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n console.error(`Fixtures path not found: ${local.path}`);\n } else {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Failed to load fixtures from ${local.path}: ${msg}`);\n }\n process.exit(1);\n }\n }\n return resolved;\n}\n\nfunction loadSource(source: ResolvedFixtureSource): Fixture[] {\n return source.isDir\n ? loadFixturesFromDir(source.path, logger)\n : loadFixtureFile(source.path, logger);\n}\n\nasync function main() {\n const sources = await resolveAllFixtureSources();\n\n const fixtures: Fixture[] = [];\n for (const src of sources) {\n fixtures.push(...loadSource(src));\n }\n\n if (fixtures.length === 0) {\n if (validateOnLoad || values.strict) {\n console.error(\"Error: No fixtures loaded and validation/strict mode is enabled — aborting.\");\n process.exit(1);\n }\n console.warn(\"Warning: No fixtures loaded. The server will return 404 for all requests.\");\n }\n\n const sourceLabel = sources.map((s) => s.source).join(\", \") || \"<none>\";\n logger.info(`Loaded ${fixtures.length} fixture(s) from ${sourceLabel}`);\n\n // Validate fixtures if requested\n if (validateOnLoad) {\n const results = validateFixtures(fixtures);\n const errors = results.filter((r) => r.severity === \"error\");\n const warnings = results.filter((r) => r.severity === \"warning\");\n\n for (const w of warnings) {\n logger.warn(`Fixture ${w.fixtureIndex}: ${w.message}`);\n }\n for (const e of errors) {\n logger.error(`Fixture ${e.fixtureIndex}: ${e.message}`);\n }\n\n if (errors.length > 0) {\n console.error(`Validation failed: ${errors.length} error(s), ${warnings.length} warning(s)`);\n process.exit(1);\n }\n }\n\n const mounts = aguiMount ? [aguiMount] : undefined;\n\n const instance = await createServer(\n fixtures,\n {\n port,\n host,\n latency,\n chunkSize,\n replaySpeed,\n logLevel,\n chaos,\n metrics: values.metrics,\n record,\n strict: values.strict,\n journalMaxEntries: journalMax,\n fixtureCountsMaxTestIds: fixtureCountsMax,\n auth: resolveInboundAuth(selectInboundAuthSource(undefined)).publicConfig,\n },\n mounts,\n );\n\n // Only the first local source is watched — remote URL sources are fetched once\n // at boot and are not monitored. Declared before the readiness announcement so\n // shutdown can close it whenever the signal arrives.\n let watcher: { close: () => void } | null = null;\n\n function shutdown() {\n logger.info(\"Shutting down...\");\n if (watcher) watcher.close();\n instance.server.close(() => {\n process.exit(0);\n });\n }\n\n // Register BEFORE announcing readiness. Writes to a pipe are synchronous on Linux\n // (and to a file on every POSIX platform), so a supervisor that reacts to the line\n // below can deliver SIGTERM while this process is still mid-statement. Announcing\n // first left a window in which SIGTERM hit Node's default disposition, which\n // re-raises: the process died with a null exit code instead of shutting down.\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n logger.info(`aimock server listening on ${instance.url}`);\n\n // Start file watcher if requested.\n if (watchMode) {\n const primary = sources[0];\n if (!primary) {\n logger.warn(\"--watch requested but no resolvable fixture sources; skipping watcher\");\n } else {\n const loadFn = (): Fixture[] => loadSource(primary);\n watcher = watchFixtures(primary.path, fixtures, loadFn, {\n logger,\n validate: validateOnLoad,\n validateFn: validateFixtures,\n });\n logger.info(`Watching ${primary.path} for changes`);\n }\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { isDeepStrictEqual, parseArgs } from \"node:util\";\nimport { statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createServer } from \"./server.js\";\nimport { FixtureLoadError, validateFixtures } from \"./fixture-loader.js\";\nimport {\n loadFixtureFileWithServices,\n loadFixturesFromDirWithServices,\n type FixturesWithServices,\n} from \"./fixture-loader-services.js\";\nimport { McpFakesAddError } from \"./mcp-fakes.js\";\nimport { build, msg } from \"./message-text.js\";\nimport { Logger, type LogLevel } from \"./logger.js\";\nimport { watchFixtures } from \"./watcher.js\";\nimport { AGUIMock } from \"./agui-mock.js\";\nimport { parseChaosField, CHAOS_FIELDS, type ChaosField } from \"./chaos.js\";\nimport { resolveFixturesValue } from \"./fixtures-remote.js\";\nimport { readProviderKeysFromEnv } from \"./provider-auth.js\";\nimport { resolveInboundAuth, selectInboundAuthSource } from \"./api-key-auth.js\";\nimport type { Fixture, ChaosConfig, RecordConfig, McpFakeSource } from \"./types.js\";\n\nconst HELP = `\nUsage: aimock [options]\n\nOptions:\n -p, --port <number> Port to listen on (default: 4010)\n -h, --host <string> Host to bind to (default: 127.0.0.1)\n -f, --fixtures <value> Fixture source (repeatable). Accepts:\n - filesystem path to a directory or .json file (default: ./fixtures)\n - https:// or http:// URL to a .json fixture file\n -l, --latency <ms> Latency in ms between SSE chunks (default: 0)\n -c, --chunk-size <chars> Chunk size in characters (default: 20)\n -w, --watch Watch fixture path for changes and reload\n --log-level <level> Log verbosity: silent, warn, info, debug (default: info)\n --validate-on-load Validate fixture schemas at startup\n --metrics Enable Prometheus metrics at GET /metrics\n --record Record mode: proxy unmatched requests and save fixtures\n --record-full-model-version Record exact model version without date stripping (default: false)\n --proxy-only Proxy mode: forward unmatched requests without saving\n --strict Strict mode: fail on unmatched requests (overridable per-request via X-AIMock-Strict header)\n --journal-max <n> Max request entries retained in memory (default: 1000, 0 = unbounded)\n --fixture-counts-max <n> Max unique testIds retained in fixture match-count map (default: 500, 0 = unbounded)\n --provider-openai <url> Upstream URL for OpenAI (used with --record)\n --provider-anthropic <url> Upstream URL for Anthropic\n --provider-gemini <url> Upstream URL for Gemini\n --provider-vertexai <url> Upstream URL for Vertex AI\n --provider-bedrock <url> Upstream URL for Bedrock\n --provider-azure <url> Upstream URL for Azure OpenAI\n --provider-ollama <url> Upstream URL for Ollama\n --provider-cohere <url> Upstream URL for Cohere\n --provider-openrouter <url> Upstream URL for OpenRouter (video record proxy)\n --provider-byteplus <url> Upstream ORIGIN for BytePlus Ark (e.g. https://ark.ap-southeast.bytepluses.com — origin only, no /api/v3)\n --upstream-timeout-ms <ms> Idle timeout (ms) on upstream socket before response (default: 30000)\n --body-timeout-ms <ms> Idle timeout (ms) on upstream response body between chunks (default: 30000)\n --max-proxy-buffer-bytes <n> Cap (bytes) on in-memory proxy-path buffer; full body still relayed (default: 67108864)\n --max-proxy-buffer-frames <n> Cap (frames) on in-memory proxy-path per-frame state; full body still relayed (default: 5000000)\n --agui-record Enable AG-UI recording (proxy unmatched AG-UI requests)\n --agui-upstream <url> Upstream AG-UI agent URL (used with --agui-record)\n --agui-proxy-only AG-UI proxy mode: forward without saving\n --replay-speed <n> Replay speed multiplier (default: 1.0, 2.0 = 2x faster)\n --chaos-drop <rate> Probability (0-1) of dropping requests with 500\n --chaos-malformed <rate> Probability (0-1) of returning malformed JSON\n --chaos-disconnect <rate> Probability (0-1) of destroying connection\n --chaos-ratelimit <rate> Probability (0-1) of 429 with Retry-After\n --chaos-latency <ms> Delay (0-${CHAOS_FIELDS.latencyMs.max}ms) injected before handling\n AIMOCK_API_KEYS Comma-separated inbound test API keys (environment only)\n --help Show this help message\n`.trim();\n\nconst { values } = parseArgs({\n options: {\n port: { type: \"string\", short: \"p\", default: \"4010\" },\n host: { type: \"string\", short: \"h\", default: \"127.0.0.1\" },\n fixtures: { type: \"string\", short: \"f\", multiple: true },\n latency: { type: \"string\", short: \"l\", default: \"0\" },\n \"chunk-size\": { type: \"string\", short: \"c\", default: \"20\" },\n watch: { type: \"boolean\", short: \"w\", default: false },\n \"log-level\": { type: \"string\", default: \"info\" },\n \"validate-on-load\": { type: \"boolean\", default: false },\n metrics: { type: \"boolean\", default: false },\n record: { type: \"boolean\", default: false },\n \"record-full-model-version\": { type: \"boolean\", default: false },\n \"proxy-only\": { type: \"boolean\", default: false },\n strict: { type: \"boolean\", default: false },\n \"provider-openai\": { type: \"string\" },\n \"provider-anthropic\": { type: \"string\" },\n \"provider-gemini\": { type: \"string\" },\n \"provider-vertexai\": { type: \"string\" },\n \"provider-bedrock\": { type: \"string\" },\n \"provider-azure\": { type: \"string\" },\n \"provider-ollama\": { type: \"string\" },\n \"provider-cohere\": { type: \"string\" },\n \"provider-openrouter\": { type: \"string\" },\n \"provider-byteplus\": { type: \"string\" },\n \"upstream-timeout-ms\": { type: \"string\" },\n \"body-timeout-ms\": { type: \"string\" },\n \"max-proxy-buffer-bytes\": { type: \"string\" },\n \"max-proxy-buffer-frames\": { type: \"string\" },\n \"agui-record\": { type: \"boolean\", default: false },\n \"agui-upstream\": { type: \"string\" },\n \"agui-proxy-only\": { type: \"boolean\", default: false },\n \"replay-speed\": { type: \"string\", default: \"1.0\" },\n \"chaos-drop\": { type: \"string\" },\n \"chaos-malformed\": { type: \"string\" },\n \"chaos-disconnect\": { type: \"string\" },\n \"chaos-ratelimit\": { type: \"string\" },\n \"chaos-latency\": { type: \"string\" },\n \"journal-max\": { type: \"string\", default: \"1000\" },\n \"fixture-counts-max\": { type: \"string\", default: \"500\" },\n help: { type: \"boolean\", default: false },\n },\n strict: true,\n});\n\nif (values.help) {\n console.log(HELP);\n process.exit(0);\n}\n\nconst port = Number(values.port);\nconst host = values.host!;\nconst latency = Number(values.latency);\nconst chunkSize = Number(values[\"chunk-size\"]);\nconst fixtureValues: string[] =\n values.fixtures && values.fixtures.length > 0 ? values.fixtures : [\"./fixtures\"];\nconst watchMode = values.watch!;\nconst validateOnLoad = values[\"validate-on-load\"]!;\nconst logLevelStr = values[\"log-level\"]!;\n\nif (![\"silent\", \"warn\", \"info\", \"debug\"].includes(logLevelStr)) {\n console.error(`Invalid log-level: ${logLevelStr} (must be silent, warn, info, or debug)`);\n process.exit(1);\n}\nconst logLevel = logLevelStr as LogLevel;\n\nif (Number.isNaN(port) || port < 0 || port > 65535) {\n console.error(`Invalid port: ${values.port}`);\n process.exit(1);\n}\n\nif (Number.isNaN(latency) || latency < 0) {\n console.error(`Invalid latency: ${values.latency}`);\n process.exit(1);\n}\n\nif (Number.isNaN(chunkSize) || chunkSize < 1) {\n console.error(`Invalid chunk-size: ${values[\"chunk-size\"]}`);\n process.exit(1);\n}\n\nconst replaySpeed = Number(values[\"replay-speed\"]);\nif (Number.isNaN(replaySpeed) || replaySpeed <= 0) {\n console.error(\"--replay-speed must be a positive number\");\n process.exit(1);\n}\n\nconst journalMax = Number(values[\"journal-max\"]);\nif (Number.isNaN(journalMax) || !Number.isInteger(journalMax) || journalMax < 0) {\n console.error(\n `Invalid journal-max: ${values[\"journal-max\"]} (must be a non-negative integer; 0 = unbounded)`,\n );\n process.exit(1);\n}\n\nconst fixtureCountsMaxStr = values[\"fixture-counts-max\"];\nconst fixtureCountsMax = Number(fixtureCountsMaxStr);\nif (Number.isNaN(fixtureCountsMax) || !Number.isInteger(fixtureCountsMax) || fixtureCountsMax < 0) {\n console.error(\n `Invalid fixture-counts-max: ${fixtureCountsMaxStr} (must be a non-negative integer; 0 = unbounded)`,\n );\n process.exit(1);\n}\n\nconst upstreamTimeoutMsStr = values[\"upstream-timeout-ms\"];\nlet upstreamTimeoutMs: number | undefined;\nif (upstreamTimeoutMsStr !== undefined) {\n upstreamTimeoutMs = Number(upstreamTimeoutMsStr);\n if (!Number.isFinite(upstreamTimeoutMs) || upstreamTimeoutMs <= 0) {\n console.error(\n `Invalid upstream-timeout-ms: ${upstreamTimeoutMsStr} (must be a positive finite number)`,\n );\n process.exit(1);\n }\n}\n\nconst bodyTimeoutMsStr = values[\"body-timeout-ms\"];\nlet bodyTimeoutMs: number | undefined;\nif (bodyTimeoutMsStr !== undefined) {\n bodyTimeoutMs = Number(bodyTimeoutMsStr);\n if (!Number.isFinite(bodyTimeoutMs) || bodyTimeoutMs <= 0) {\n console.error(\n `Invalid body-timeout-ms: ${bodyTimeoutMsStr} (must be a positive finite number)`,\n );\n process.exit(1);\n }\n}\n\nconst maxProxyBufferBytesStr = values[\"max-proxy-buffer-bytes\"];\nlet maxProxyBufferBytes: number | undefined;\nif (maxProxyBufferBytesStr !== undefined) {\n maxProxyBufferBytes = Number(maxProxyBufferBytesStr);\n if (!Number.isFinite(maxProxyBufferBytes) || maxProxyBufferBytes <= 0) {\n console.error(\n `Invalid max-proxy-buffer-bytes: ${maxProxyBufferBytesStr} (must be a positive finite number)`,\n );\n process.exit(1);\n }\n}\n\nconst maxProxyBufferFramesStr = values[\"max-proxy-buffer-frames\"];\nlet maxProxyBufferFrames: number | undefined;\nif (maxProxyBufferFramesStr !== undefined) {\n maxProxyBufferFrames = Number(maxProxyBufferFramesStr);\n if (!Number.isFinite(maxProxyBufferFrames) || maxProxyBufferFrames <= 0) {\n console.error(\n `Invalid max-proxy-buffer-frames: ${maxProxyBufferFramesStr} (must be a positive finite number)`,\n );\n process.exit(1);\n }\n}\n\nconst logger = new Logger(logLevel);\n\n// Parse chaos config from CLI flags\nlet chaos: ChaosConfig | undefined;\n{\n const dropStr = values[\"chaos-drop\"];\n const malformedStr = values[\"chaos-malformed\"];\n const disconnectStr = values[\"chaos-disconnect\"];\n const ratelimitStr = values[\"chaos-ratelimit\"];\n const latencyStr = values[\"chaos-latency\"];\n\n if (\n dropStr !== undefined ||\n malformedStr !== undefined ||\n disconnectStr !== undefined ||\n ratelimitStr !== undefined ||\n latencyStr !== undefined\n ) {\n // Same parser, same TABLE, same reject-never-clamp policy as every other\n // chaos input (headers, fixture, server default, control API) — see\n // `ChaosConfig`. The bounds and the integer-vs-rate grammar are read out of\n // `CHAOS_FIELDS`, never re-typed here, so raising a cap in that one table\n // moves the flag with it instead of leaving `--chaos-latency` on the old\n // limit with no compile error.\n chaos = {};\n const flagFields: Array<{ flag: string; field: ChaosField; raw: string | undefined }> = [\n { flag: \"chaos-drop\", field: \"dropRate\", raw: dropStr },\n { flag: \"chaos-malformed\", field: \"malformedRate\", raw: malformedStr },\n { flag: \"chaos-disconnect\", field: \"disconnectRate\", raw: disconnectStr },\n { flag: \"chaos-ratelimit\", field: \"rateLimitRate\", raw: ratelimitStr },\n { flag: \"chaos-latency\", field: \"latencyMs\", raw: latencyStr },\n ];\n for (const { flag, field, raw } of flagFields) {\n if (raw === undefined) continue;\n const val = parseChaosField(field, raw);\n if (val === undefined) {\n console.error(`Invalid ${flag}: ${raw} (must be 0-${CHAOS_FIELDS[field].max})`);\n process.exit(1);\n }\n chaos[field] = val;\n }\n }\n}\n\n// Parse record/proxy config from CLI flags\nlet record: RecordConfig | undefined;\nif (values.record || values[\"proxy-only\"]) {\n const providers: RecordConfig[\"providers\"] = {};\n if (values[\"provider-openai\"]) providers.openai = values[\"provider-openai\"];\n if (values[\"provider-anthropic\"]) providers.anthropic = values[\"provider-anthropic\"];\n if (values[\"provider-gemini\"]) providers.gemini = values[\"provider-gemini\"];\n if (values[\"provider-vertexai\"]) providers.vertexai = values[\"provider-vertexai\"];\n if (values[\"provider-bedrock\"]) providers.bedrock = values[\"provider-bedrock\"];\n if (values[\"provider-azure\"]) providers.azure = values[\"provider-azure\"];\n if (values[\"provider-ollama\"]) providers.ollama = values[\"provider-ollama\"];\n if (values[\"provider-cohere\"]) providers.cohere = values[\"provider-cohere\"];\n if (values[\"provider-openrouter\"]) providers.openrouter = values[\"provider-openrouter\"];\n if (values[\"provider-byteplus\"]) providers.byteplus = values[\"provider-byteplus\"];\n\n if (Object.keys(providers).length === 0) {\n console.error(\n `Error: --${values[\"proxy-only\"] ? \"proxy-only\" : \"record\"} requires at least one --provider-* flag`,\n );\n process.exit(1);\n }\n\n // For --record, the first --fixtures value is the base path for the recording\n // destination and must be a local filesystem path — writing to a URL is not supported.\n // For --proxy-only, unmatched requests are forwarded without saving, so no writable\n // destination is required; URL-only --fixtures is valid in that mode.\n const recordBase = fixtureValues[0];\n const recordBaseIsUrl = /^https?:\\/\\//i.test(recordBase);\n if (values.record && recordBaseIsUrl) {\n console.error(\n `Error: --record requires a local --fixtures path for the recording destination; got URL ${recordBase}`,\n );\n process.exit(1);\n }\n record = {\n providers,\n // aimock's own upstream keys, sourced from AIMOCK_PROVIDER_*_KEY env vars\n // (not CLI flags — secrets must not appear in `ps`). Injected on a\n // fixture-miss passthrough when the caller sent no/dummy credential.\n providerKeys: readProviderKeysFromEnv(),\n // In proxy-only mode with only URL sources, fixturePath is never consumed\n // (recorder.ts skips disk writes when proxyOnly is set). Leave it undefined\n // rather than resolving a URL string as a filesystem path.\n fixturePath: recordBaseIsUrl ? undefined : resolve(recordBase, \"recorded\"),\n proxyOnly: values[\"proxy-only\"],\n recordFullModelVersion: values[\"record-full-model-version\"],\n upstreamTimeoutMs,\n bodyTimeoutMs,\n maxProxyBufferBytes,\n maxProxyBufferFrames,\n };\n} else {\n // These flags configure upstream proxying — without --record or\n // --proxy-only they would be parsed and then silently dropped. Routed\n // through the constructed logger so --log-level is respected.\n const droppedProviderFlags = (\n [\n \"provider-openai\",\n \"provider-anthropic\",\n \"provider-gemini\",\n \"provider-vertexai\",\n \"provider-bedrock\",\n \"provider-azure\",\n \"provider-ollama\",\n \"provider-cohere\",\n \"provider-openrouter\",\n \"provider-byteplus\",\n ] as const\n ).filter((flag) => values[flag] !== undefined);\n if (droppedProviderFlags.length > 0) {\n logger.warn(\n `--${droppedProviderFlags.join(\"/--\")} only apply to --record/--proxy-only upstream proxying — ignored without one of those flags.`,\n );\n }\n if (\n upstreamTimeoutMs !== undefined ||\n bodyTimeoutMs !== undefined ||\n maxProxyBufferBytes !== undefined ||\n maxProxyBufferFrames !== undefined\n ) {\n logger.warn(\n \"--upstream-timeout-ms/--body-timeout-ms/--max-proxy-buffer-bytes/--max-proxy-buffer-frames only apply to --record/--proxy-only upstream proxying — ignored without one of those flags.\",\n );\n }\n}\n\n// Parse AG-UI record/proxy config from CLI flags\nlet aguiMount: { path: string; handler: AGUIMock } | undefined;\nif (values[\"agui-record\"] || values[\"agui-proxy-only\"]) {\n if (!values[\"agui-upstream\"]) {\n console.error(\"Error: --agui-record/--agui-proxy-only requires --agui-upstream\");\n process.exit(1);\n }\n // --agui-record writes recorded AG-UI fixtures to disk, so a URL source is unsupported.\n // --agui-proxy-only forwards without saving, so URL-only --fixtures is valid.\n const aguiBase = fixtureValues[0];\n const aguiBaseIsUrl = /^https?:\\/\\//i.test(aguiBase);\n if (values[\"agui-record\"] && aguiBaseIsUrl) {\n console.error(\n `Error: --agui-record requires a local --fixtures path for the recording destination; got URL ${aguiBase}`,\n );\n process.exit(1);\n }\n const agui = new AGUIMock();\n agui.enableRecording({\n upstream: values[\"agui-upstream\"],\n // In proxy-only mode with a URL-only --fixtures, the AG-UI recorder never\n // writes to disk (see agui-recorder.ts). Leave fixturePath undefined rather\n // than resolving a URL as a filesystem path.\n fixturePath: aguiBaseIsUrl ? undefined : resolve(aguiBase, \"agui-recorded\"),\n proxyOnly: values[\"agui-proxy-only\"],\n });\n aguiMount = { path: \"/agui\", handler: agui };\n}\n\ninterface ResolvedFixtureSource {\n source: string;\n path: string;\n isDir: boolean;\n}\n\nasync function resolveAllFixtureSources(): Promise<ResolvedFixtureSource[]> {\n const resolved: ResolvedFixtureSource[] = [];\n for (const value of fixtureValues) {\n let local;\n try {\n local = await resolveFixturesValue(value, {\n validateOnLoad,\n logger,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Failed to resolve --fixtures value \"${value}\": ${msg}`);\n process.exit(1);\n }\n if (!local.path) {\n // Remote fetch failed without validate-on-load and no cache — already warned; skip.\n continue;\n }\n try {\n const stat = statSync(local.path);\n resolved.push({ source: local.source, path: local.path, isDir: stat.isDirectory() });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n console.error(`Fixtures path not found: ${local.path}`);\n } else {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Failed to load fixtures from ${local.path}: ${msg}`);\n }\n process.exit(1);\n }\n }\n return resolved;\n}\n\n// F3: the loaders that carry `mcpFakes`. A file's entry-id source (I6) is the\n// --fixtures value as given: the path, or the URL of a remote file (whose\n// `path` is the on-disk cache).\nfunction loadSource(source: ResolvedFixtureSource): FixturesWithServices {\n return source.isDir\n ? loadFixturesFromDirWithServices(source.path, logger)\n : loadFixtureFileWithServices(source.path, logger, undefined, source.source);\n}\n\n/** Each source's `mcpFakes` blocks, in load order, for the --watch comparison. */\nfunction fakesBySource(blocks: McpFakeSource[]): Map<string, unknown[]> {\n const bySource = new Map<string, unknown[]>();\n for (const block of blocks) {\n const list = bySource.get(block.source) ?? [];\n list.push([block.blockIndex, block.raw]);\n bySource.set(block.source, list);\n }\n return bySource;\n}\n\n/**\n * R9/F11: fakes are not reloaded by --watch, so a reload whose `mcpFakes`\n * differ from the boot set is rejected. Returns the first changed source.\n * A source in `unreadable` could not be read or parsed: it is not compared,\n * because its blocks are unknown, not removed.\n */\nfunction changedFakesSource(\n boot: McpFakeSource[],\n reload: McpFakeSource[],\n unreadable: ReadonlySet<string>,\n): string | null {\n const before = fakesBySource(boot);\n const after = fakesBySource(reload);\n for (const source of new Set([...before.keys(), ...after.keys()])) {\n if (unreadable.has(source)) continue;\n if (!isDeepStrictEqual(before.get(source), after.get(source))) return source;\n }\n return null;\n}\n\n/**\n * L3: a load error on stderr, whatever the log level: every error of a\n * `McpFakesAddError`, one per line, then its L8 warnings.\n */\nfunction printLoadError(err: FixtureLoadError): void {\n const errors = err instanceof McpFakesAddError ? err.errors : [err];\n for (const e of errors) console.error(e.message);\n if (err instanceof McpFakesAddError) {\n for (const w of err.warnings) console.warn(w.message);\n }\n}\n\n/** A --watch reload that `loadFn` rejected and already printed (L3, L7). */\nclass ReloadRejected extends Error {}\n\n/**\n * The --watch logger: as `Logger`, except that the watcher's own report of a\n * {@link ReloadRejected} reload is dropped, so the error is printed once.\n */\nclass WatchLogger extends Logger {\n override error(...args: unknown[]): void {\n if (args.some((a) => a instanceof ReloadRejected)) return;\n super.error(...args);\n }\n}\n\nasync function main() {\n const sources = await resolveAllFixtureSources();\n\n const fixtures: Fixture[] = [];\n const mcpFakes: McpFakeSource[] = [];\n // The primary source's blocks: what a --watch reload of it is compared with.\n let primaryFakes: McpFakeSource[] = [];\n for (const src of sources) {\n const loaded = loadSource(src);\n fixtures.push(...loaded.fixtures);\n mcpFakes.push(...loaded.mcpFakes);\n if (src === sources[0]) primaryFakes = loaded.mcpFakes;\n }\n\n if (fixtures.length === 0 && mcpFakes.length > 0) {\n // F5/L9: fake blocks count as loaded; only LLM requests have nothing to match.\n console.warn(build(msg`Warning: No LLM fixtures loaded; LLM requests will return 404`));\n } else if (fixtures.length === 0) {\n if (validateOnLoad || values.strict) {\n console.error(\"Error: No fixtures loaded and validation/strict mode is enabled — aborting.\");\n process.exit(1);\n }\n console.warn(\"Warning: No fixtures loaded. The server will return 404 for all requests.\");\n }\n\n const sourceLabel = sources.map((s) => s.source).join(\", \") || \"<none>\";\n logger.info(`Loaded ${fixtures.length} fixture(s) from ${sourceLabel}`);\n\n // Validate fixtures if requested\n if (validateOnLoad) {\n const results = validateFixtures(fixtures);\n const errors = results.filter((r) => r.severity === \"error\");\n const warnings = results.filter((r) => r.severity === \"warning\");\n\n for (const w of warnings) {\n logger.warn(`Fixture ${w.fixtureIndex}: ${w.message}`);\n }\n // L8 (shadowed mcpFakes entries) is not printed here: the hand-off in\n // createServer prints it on every start.\n for (const e of errors) {\n logger.error(`Fixture ${e.fixtureIndex}: ${e.message}`);\n }\n\n if (errors.length > 0) {\n console.error(`Validation failed: ${errors.length} error(s), ${warnings.length} warning(s)`);\n process.exit(1);\n }\n }\n\n const mounts = aguiMount ? [aguiMount] : undefined;\n\n const instance = await createServer(\n fixtures,\n {\n port,\n host,\n latency,\n chunkSize,\n replaySpeed,\n logLevel,\n chaos,\n metrics: values.metrics,\n record,\n strict: values.strict,\n journalMaxEntries: journalMax,\n fixtureCountsMaxTestIds: fixtureCountsMax,\n auth: resolveInboundAuth(selectInboundAuthSource(undefined)).publicConfig,\n },\n mounts,\n { search: [], rerank: [], moderation: [], mcpFakes },\n );\n\n // Only the first local source is watched — remote URL sources are fetched once\n // at boot and are not monitored. Declared before the readiness announcement so\n // shutdown can close it whenever the signal arrives.\n let watcher: { close: () => void } | null = null;\n\n function shutdown() {\n logger.info(\"Shutting down...\");\n if (watcher) watcher.close();\n instance.server.close(() => {\n process.exit(0);\n });\n }\n\n // Register BEFORE announcing readiness. Writes to a pipe are synchronous on Linux\n // (and to a file on every POSIX platform), so a supervisor that reacts to the line\n // below can deliver SIGTERM while this process is still mid-statement. Announcing\n // first left a window in which SIGTERM hit Node's default disposition, which\n // re-raises: the process died with a null exit code instead of shutting down.\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n logger.info(`aimock server listening on ${instance.url}`);\n\n // Start file watcher if requested.\n if (watchMode) {\n const primary = sources[0];\n if (!primary) {\n logger.warn(\"--watch requested but no resolvable fixture sources; skipping watcher\");\n } else {\n // A rejected reload is printed here, whatever the log level, then\n // thrown as ReloadRejected: watchFixtures keeps the previous fixtures,\n // and its WatchLogger does not print the error a second time.\n const reject = (err: FixtureLoadError): never => {\n printLoadError(err);\n throw new ReloadRejected(build(msg`--watch reload rejected`));\n };\n const loadFn = (): Fixture[] => {\n let loaded: FixturesWithServices;\n try {\n loaded = loadSource(primary);\n } catch (err) {\n // L3: a bad block in the reloaded file.\n if (err instanceof FixtureLoadError) return reject(err);\n throw err;\n }\n const changed = changedFakesSource(\n primaryFakes,\n loaded.mcpFakes,\n new Set(loaded.unreadable),\n );\n if (changed !== null) {\n // L7.\n return reject(\n new FixtureLoadError({\n rule: \"mcp-fakes/watch-reload-changed\",\n file: changed,\n detail: msg`mcpFakes changed on --watch reload; MCP fakes are not reloaded, so the whole reload is rejected and the previous fixtures stay loaded. Restart aimock to load the new fakes`,\n }),\n );\n }\n return loaded.fixtures;\n };\n watcher = watchFixtures(primary.path, fixtures, loadFn, {\n logger: new WatchLogger(logLevel),\n validate: validateOnLoad,\n validateFn: validateFixtures,\n });\n logger.info(`Watching ${primary.path} for changes`);\n }\n }\n}\n\nmain().catch((err) => {\n if (err instanceof FixtureLoadError) {\n printLoadError(err);\n } else {\n console.error(err);\n }\n process.exit(1);\n});\n"],"mappings":"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"}
package/dist/cohere.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  const require_helpers = require('./helpers.cjs');
2
- const require_router = require('./router.cjs');
3
2
  const require_sse_writer = require('./sse-writer.cjs');
4
- const require_interruption = require('./interruption.cjs');
5
3
  const require_chaos = require('./chaos.cjs');
4
+ const require_router = require('./router.cjs');
6
5
  const require_recorder = require('./recorder.cjs');
6
+ const require_interruption = require('./interruption.cjs');
7
7
 
8
8
  //#region src/cohere.ts
9
9
  const ZERO_USAGE = {
@@ -176,7 +176,8 @@ function buildCohereToolCallResponse(toolCalls, logger, reasoning, overrides) {
176
176
  };
177
177
  }
178
178
  function buildCohereContentWithToolCallsResponse(content, toolCalls, logger, reasoning, overrides, blocks) {
179
- const resolvedBlocks = blocks && blocks.length > 0 ? require_helpers.resolveFixtureBlocks(blocks) : void 0;
179
+ const outcome = blocks && blocks.length > 0 ? require_helpers.resolveFixtureBlockOutcome(blocks) : void 0;
180
+ const resolvedBlocks = outcome?.ordered;
180
181
  const cohereCalls = (resolvedBlocks ? resolvedBlocks.filter((b) => b.type === "toolCall").map((b) => ({
181
182
  name: b.name,
182
183
  arguments: b.arguments,
@@ -213,7 +214,7 @@ function buildCohereContentWithToolCallsResponse(content, toolCalls, logger, rea
213
214
  });
214
215
  return {
215
216
  id: overrides?.id ?? require_helpers.generateMessageId(),
216
- finish_reason: cohereFinishReason(overrides?.finishReason, "TOOL_CALL"),
217
+ finish_reason: cohereFinishReason(overrides?.finishReason, outcome && !outcome.hasToolCalls ? "COMPLETE" : "TOOL_CALL"),
217
218
  message: {
218
219
  role: "assistant",
219
220
  content: contentBlocks,
@@ -415,7 +416,8 @@ function buildCohereContentWithToolCallsStreamEvents(content, toolCalls, chunkSi
415
416
  contentIndex++;
416
417
  }
417
418
  if (blocks && blocks.length > 0) {
418
- const resolved = require_helpers.resolveFixtureBlocks(blocks);
419
+ const outcome = require_helpers.resolveFixtureBlockOutcome(blocks);
420
+ const resolved = outcome.ordered;
419
421
  let toolPlanEmitted = false;
420
422
  let toolIdx = 0;
421
423
  resolved.forEach((block) => {
@@ -482,7 +484,7 @@ function buildCohereContentWithToolCallsStreamEvents(content, toolCalls, chunkSi
482
484
  events.push({
483
485
  type: "message-end",
484
486
  delta: {
485
- finish_reason: cohereFinishReason(overrides?.finishReason, "TOOL_CALL"),
487
+ finish_reason: cohereFinishReason(overrides?.finishReason, outcome.hasToolCalls ? "TOOL_CALL" : "COMPLETE"),
486
488
  usage: cohereUsage(overrides)
487
489
  }
488
490
  });
@@ -640,7 +642,11 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
640
642
  } }));
641
643
  return;
642
644
  }
643
- const shapeError = require_helpers.validateChatMessages(cohereReq.messages) ?? require_helpers.validateToolsField(cohereReq.tools);
645
+ let shapeError = require_helpers.validateChatMessages(cohereReq.messages) ?? require_helpers.validateToolsField(cohereReq.tools);
646
+ if (!shapeError && Array.isArray(cohereReq.tools)) {
647
+ const nullFunctionIndex = cohereReq.tools.findIndex((tool) => "function" in tool && tool.function === null);
648
+ if (nullFunctionIndex !== -1) shapeError = `tools[${nullFunctionIndex}].function must be an object`;
649
+ }
644
650
  if (shapeError) {
645
651
  journal.add({
646
652
  method: req.method ?? "POST",
@@ -667,7 +673,7 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
667
673
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
668
674
  logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
669
675
  } else logger.debug(`No fixture matched for request`);
670
- if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, req.url, journal, {
676
+ if (await require_chaos.applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
671
677
  method: req.method ?? "POST",
672
678
  path: req.url ?? "/v2/chat",
673
679
  headers: require_helpers.flattenHeaders(req.headers),
@@ -983,13 +989,33 @@ async function handleCohereEmbed(req, res, raw, fixtures, journal, defaults, set
983
989
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
984
990
  logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
985
991
  } else logger.debug(`No fixture matched for request`);
986
- if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, req.url, journal, {
992
+ if (await require_chaos.applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
987
993
  method: req.method ?? "POST",
988
994
  path: req.url ?? "/v2/embed",
989
995
  headers: require_helpers.flattenHeaders(req.headers),
990
996
  body: syntheticReq
991
997
  }, fixture ? "fixture" : "proxy", defaults.registry, defaults.logger)) return;
992
998
  const embeddingTypes = embedReq.embedding_types && embedReq.embedding_types.length > 0 ? embedReq.embedding_types : ["float"];
999
+ function rejectInvalidEmbeddingTypes() {
1000
+ if (embedReq.embedding_types == null) return false;
1001
+ const isArray = Array.isArray(embedReq.embedding_types);
1002
+ if (isArray && embedReq.embedding_types.every((type) => typeof type === "string")) return false;
1003
+ journal.add({
1004
+ method: req.method ?? "POST",
1005
+ path: req.url ?? "/v2/embed",
1006
+ headers: require_helpers.flattenHeaders(req.headers),
1007
+ body: syntheticReq,
1008
+ response: {
1009
+ status: 400,
1010
+ fixture: fixture ?? null
1011
+ }
1012
+ });
1013
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
1014
+ message: isArray ? "Invalid request: embedding_types must be an array of strings" : "Invalid request: embedding_types must be an array",
1015
+ type: "invalid_request_error"
1016
+ } }));
1017
+ return true;
1018
+ }
993
1019
  if (fixture) {
994
1020
  const response = await require_helpers.resolveResponse(fixture, syntheticReq);
995
1021
  if (require_helpers.isErrorResponse(response)) {
@@ -1008,6 +1034,7 @@ async function handleCohereEmbed(req, res, raw, fixtures, journal, defaults, set
1008
1034
  return;
1009
1035
  }
1010
1036
  if (require_helpers.isEmbeddingResponse(response)) {
1037
+ if (rejectInvalidEmbeddingTypes()) return;
1011
1038
  journal.add({
1012
1039
  method: req.method ?? "POST",
1013
1040
  path: req.url ?? "/v2/embed",
@@ -1085,6 +1112,7 @@ async function handleCohereEmbed(req, res, raw, fixtures, journal, defaults, set
1085
1112
  return;
1086
1113
  }
1087
1114
  }
1115
+ if (rejectInvalidEmbeddingTypes()) return;
1088
1116
  logger.warn(`No embedding fixture matched for "${combinedInput.slice(0, 80)}" — returning deterministic fallback`);
1089
1117
  const dimensions = 1024;
1090
1118
  const vectors = embedReq.texts.map((text) => require_helpers.generateDeterministicEmbedding(text, dimensions));