@copilotkit/aimock 1.40.0 → 1.42.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 (251) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +28 -1
  4. package/README.md +5 -3
  5. package/dist/a2a-mock.cjs +18 -11
  6. package/dist/a2a-mock.cjs.map +1 -1
  7. package/dist/a2a-mock.d.cts +1 -0
  8. package/dist/a2a-mock.d.cts.map +1 -1
  9. package/dist/a2a-mock.d.ts +1 -0
  10. package/dist/a2a-mock.d.ts.map +1 -1
  11. package/dist/a2a-mock.js +18 -11
  12. package/dist/a2a-mock.js.map +1 -1
  13. package/dist/agui-handler.d.cts.map +1 -1
  14. package/dist/agui-handler.d.ts.map +1 -1
  15. package/dist/agui-mock.cjs +2 -0
  16. package/dist/agui-mock.cjs.map +1 -1
  17. package/dist/agui-mock.d.cts.map +1 -1
  18. package/dist/agui-mock.d.ts.map +1 -1
  19. package/dist/agui-mock.js +2 -0
  20. package/dist/agui-mock.js.map +1 -1
  21. package/dist/bedrock-converse.cjs +36 -2
  22. package/dist/bedrock-converse.cjs.map +1 -1
  23. package/dist/bedrock-converse.d.cts.map +1 -1
  24. package/dist/bedrock-converse.d.ts.map +1 -1
  25. package/dist/bedrock-converse.js +37 -3
  26. package/dist/bedrock-converse.js.map +1 -1
  27. package/dist/bedrock.cjs +50 -13
  28. package/dist/bedrock.cjs.map +1 -1
  29. package/dist/bedrock.d.cts.map +1 -1
  30. package/dist/bedrock.d.ts.map +1 -1
  31. package/dist/bedrock.js +51 -14
  32. package/dist/bedrock.js.map +1 -1
  33. package/dist/byteplus-video.cjs +954 -0
  34. package/dist/byteplus-video.cjs.map +1 -0
  35. package/dist/byteplus-video.d.cts +176 -0
  36. package/dist/byteplus-video.d.cts.map +1 -0
  37. package/dist/byteplus-video.d.ts +176 -0
  38. package/dist/byteplus-video.d.ts.map +1 -0
  39. package/dist/byteplus-video.js +947 -0
  40. package/dist/byteplus-video.js.map +1 -0
  41. package/dist/chaos.cjs +36 -7
  42. package/dist/chaos.cjs.map +1 -1
  43. package/dist/chaos.d.cts +8 -3
  44. package/dist/chaos.d.cts.map +1 -1
  45. package/dist/chaos.d.ts +8 -3
  46. package/dist/chaos.d.ts.map +1 -1
  47. package/dist/chaos.js +36 -8
  48. package/dist/chaos.js.map +1 -1
  49. package/dist/cli.cjs +5 -1
  50. package/dist/cli.cjs.map +1 -1
  51. package/dist/cli.js +5 -1
  52. package/dist/cli.js.map +1 -1
  53. package/dist/cohere.cjs +58 -5
  54. package/dist/cohere.cjs.map +1 -1
  55. package/dist/cohere.d.cts.map +1 -1
  56. package/dist/cohere.d.ts.map +1 -1
  57. package/dist/cohere.js +59 -6
  58. package/dist/cohere.js.map +1 -1
  59. package/dist/config-loader.d.cts.map +1 -1
  60. package/dist/elevenlabs-audio.cjs +36 -2
  61. package/dist/elevenlabs-audio.cjs.map +1 -1
  62. package/dist/elevenlabs-audio.d.cts.map +1 -1
  63. package/dist/elevenlabs-audio.d.ts.map +1 -1
  64. package/dist/elevenlabs-audio.js +37 -3
  65. package/dist/elevenlabs-audio.js.map +1 -1
  66. package/dist/embeddings.cjs +58 -3
  67. package/dist/embeddings.cjs.map +1 -1
  68. package/dist/embeddings.d.cts.map +1 -1
  69. package/dist/embeddings.d.ts.map +1 -1
  70. package/dist/embeddings.js +59 -4
  71. package/dist/embeddings.js.map +1 -1
  72. package/dist/fal-audio.cjs +38 -2
  73. package/dist/fal-audio.cjs.map +1 -1
  74. package/dist/fal-audio.d.cts.map +1 -1
  75. package/dist/fal-audio.d.ts.map +1 -1
  76. package/dist/fal-audio.js +39 -3
  77. package/dist/fal-audio.js.map +1 -1
  78. package/dist/fixture-loader.cjs +0 -0
  79. package/dist/fixture-loader.cjs.map +1 -1
  80. package/dist/fixture-loader.d.cts +9 -1
  81. package/dist/fixture-loader.d.cts.map +1 -1
  82. package/dist/fixture-loader.d.ts +9 -1
  83. package/dist/fixture-loader.d.ts.map +1 -1
  84. package/dist/fixture-loader.js +0 -0
  85. package/dist/fixture-loader.js.map +1 -1
  86. package/dist/gemini-embeddings.cjs +19 -1
  87. package/dist/gemini-embeddings.cjs.map +1 -1
  88. package/dist/gemini-embeddings.js +20 -2
  89. package/dist/gemini-embeddings.js.map +1 -1
  90. package/dist/gemini-interactions.cjs +15 -1
  91. package/dist/gemini-interactions.cjs.map +1 -1
  92. package/dist/gemini-interactions.d.cts.map +1 -1
  93. package/dist/gemini-interactions.d.ts.map +1 -1
  94. package/dist/gemini-interactions.js +16 -2
  95. package/dist/gemini-interactions.js.map +1 -1
  96. package/dist/gemini.cjs +70 -6
  97. package/dist/gemini.cjs.map +1 -1
  98. package/dist/gemini.d.cts.map +1 -1
  99. package/dist/gemini.d.ts.map +1 -1
  100. package/dist/gemini.js +71 -7
  101. package/dist/gemini.js.map +1 -1
  102. package/dist/grok-video.cjs +2 -2
  103. package/dist/grok-video.cjs.map +1 -1
  104. package/dist/grok-video.d.cts.map +1 -1
  105. package/dist/grok-video.d.ts.map +1 -1
  106. package/dist/grok-video.js +2 -2
  107. package/dist/grok-video.js.map +1 -1
  108. package/dist/helpers.cjs +160 -5
  109. package/dist/helpers.cjs.map +1 -1
  110. package/dist/helpers.d.cts.map +1 -1
  111. package/dist/helpers.d.ts.map +1 -1
  112. package/dist/helpers.js +152 -6
  113. package/dist/helpers.js.map +1 -1
  114. package/dist/images.cjs +24 -6
  115. package/dist/images.cjs.map +1 -1
  116. package/dist/images.d.cts +11 -3
  117. package/dist/images.d.cts.map +1 -1
  118. package/dist/images.d.ts +11 -3
  119. package/dist/images.d.ts.map +1 -1
  120. package/dist/images.js +25 -7
  121. package/dist/images.js.map +1 -1
  122. package/dist/index.cjs +7 -0
  123. package/dist/index.d.cts +2 -1
  124. package/dist/index.d.ts +2 -1
  125. package/dist/index.js +2 -1
  126. package/dist/llmock.cjs +15 -0
  127. package/dist/llmock.cjs.map +1 -1
  128. package/dist/llmock.d.cts +12 -0
  129. package/dist/llmock.d.cts.map +1 -1
  130. package/dist/llmock.d.ts +12 -0
  131. package/dist/llmock.d.ts.map +1 -1
  132. package/dist/llmock.js +16 -1
  133. package/dist/llmock.js.map +1 -1
  134. package/dist/mcp-mock.cjs +11 -0
  135. package/dist/mcp-mock.cjs.map +1 -1
  136. package/dist/mcp-mock.d.cts.map +1 -1
  137. package/dist/mcp-mock.d.ts.map +1 -1
  138. package/dist/mcp-mock.js +11 -0
  139. package/dist/mcp-mock.js.map +1 -1
  140. package/dist/messages.cjs +18 -1
  141. package/dist/messages.cjs.map +1 -1
  142. package/dist/messages.d.cts.map +1 -1
  143. package/dist/messages.d.ts.map +1 -1
  144. package/dist/messages.js +19 -2
  145. package/dist/messages.js.map +1 -1
  146. package/dist/metrics.cjs +20 -0
  147. package/dist/metrics.cjs.map +1 -1
  148. package/dist/metrics.d.cts.map +1 -1
  149. package/dist/metrics.d.ts.map +1 -1
  150. package/dist/metrics.js +19 -1
  151. package/dist/metrics.js.map +1 -1
  152. package/dist/moderation.cjs +67 -3
  153. package/dist/moderation.cjs.map +1 -1
  154. package/dist/moderation.d.cts +1 -0
  155. package/dist/moderation.d.cts.map +1 -1
  156. package/dist/moderation.d.ts +1 -0
  157. package/dist/moderation.d.ts.map +1 -1
  158. package/dist/moderation.js +68 -4
  159. package/dist/moderation.js.map +1 -1
  160. package/dist/ollama.cjs +75 -5
  161. package/dist/ollama.cjs.map +1 -1
  162. package/dist/ollama.d.cts.map +1 -1
  163. package/dist/ollama.d.ts.map +1 -1
  164. package/dist/ollama.js +76 -6
  165. package/dist/ollama.js.map +1 -1
  166. package/dist/openrouter-video.cjs +4 -4
  167. package/dist/openrouter-video.cjs.map +1 -1
  168. package/dist/openrouter-video.d.cts.map +1 -1
  169. package/dist/openrouter-video.d.ts.map +1 -1
  170. package/dist/openrouter-video.js +4 -4
  171. package/dist/openrouter-video.js.map +1 -1
  172. package/dist/provider-auth.cjs +2 -0
  173. package/dist/provider-auth.cjs.map +1 -1
  174. package/dist/provider-auth.js +2 -0
  175. package/dist/provider-auth.js.map +1 -1
  176. package/dist/rerank.cjs +65 -2
  177. package/dist/rerank.cjs.map +1 -1
  178. package/dist/rerank.d.cts +1 -1
  179. package/dist/rerank.d.cts.map +1 -1
  180. package/dist/rerank.d.ts +1 -1
  181. package/dist/rerank.d.ts.map +1 -1
  182. package/dist/rerank.js +66 -3
  183. package/dist/rerank.js.map +1 -1
  184. package/dist/responses.cjs +18 -1
  185. package/dist/responses.cjs.map +1 -1
  186. package/dist/responses.d.cts.map +1 -1
  187. package/dist/responses.d.ts.map +1 -1
  188. package/dist/responses.js +19 -2
  189. package/dist/responses.js.map +1 -1
  190. package/dist/router.cjs +1 -0
  191. package/dist/router.cjs.map +1 -1
  192. package/dist/router.d.cts.map +1 -1
  193. package/dist/router.d.ts.map +1 -1
  194. package/dist/router.js +1 -0
  195. package/dist/router.js.map +1 -1
  196. package/dist/search.cjs +67 -3
  197. package/dist/search.cjs.map +1 -1
  198. package/dist/search.d.cts +1 -0
  199. package/dist/search.d.cts.map +1 -1
  200. package/dist/search.d.ts +1 -0
  201. package/dist/search.d.ts.map +1 -1
  202. package/dist/search.js +68 -4
  203. package/dist/search.js.map +1 -1
  204. package/dist/server.cjs +406 -17
  205. package/dist/server.cjs.map +1 -1
  206. package/dist/server.d.cts +2 -0
  207. package/dist/server.d.cts.map +1 -1
  208. package/dist/server.d.ts +2 -0
  209. package/dist/server.d.ts.map +1 -1
  210. package/dist/server.js +410 -21
  211. package/dist/server.js.map +1 -1
  212. package/dist/speech.cjs +18 -1
  213. package/dist/speech.cjs.map +1 -1
  214. package/dist/speech.d.cts.map +1 -1
  215. package/dist/speech.d.ts.map +1 -1
  216. package/dist/speech.js +19 -2
  217. package/dist/speech.js.map +1 -1
  218. package/dist/stream-collapse.cjs +2 -1
  219. package/dist/stream-collapse.cjs.map +1 -1
  220. package/dist/stream-collapse.js +2 -1
  221. package/dist/stream-collapse.js.map +1 -1
  222. package/dist/transcription.cjs +21 -2
  223. package/dist/transcription.cjs.map +1 -1
  224. package/dist/transcription.d.cts.map +1 -1
  225. package/dist/transcription.d.ts.map +1 -1
  226. package/dist/transcription.js +21 -2
  227. package/dist/transcription.js.map +1 -1
  228. package/dist/types.d.cts +58 -5
  229. package/dist/types.d.cts.map +1 -1
  230. package/dist/types.d.ts +58 -5
  231. package/dist/types.d.ts.map +1 -1
  232. package/dist/vector-mock.cjs +88 -0
  233. package/dist/vector-mock.cjs.map +1 -1
  234. package/dist/vector-mock.d.cts.map +1 -1
  235. package/dist/vector-mock.d.ts.map +1 -1
  236. package/dist/vector-mock.js +89 -1
  237. package/dist/vector-mock.js.map +1 -1
  238. package/dist/vector-types.d.cts.map +1 -1
  239. package/dist/veo-video.cjs +2 -2
  240. package/dist/veo-video.cjs.map +1 -1
  241. package/dist/veo-video.d.cts.map +1 -1
  242. package/dist/veo-video.d.ts.map +1 -1
  243. package/dist/veo-video.js +2 -2
  244. package/dist/veo-video.js.map +1 -1
  245. package/dist/video.cjs +19 -2
  246. package/dist/video.cjs.map +1 -1
  247. package/dist/video.d.cts.map +1 -1
  248. package/dist/video.d.ts.map +1 -1
  249. package/dist/video.js +20 -3
  250. package/dist/video.js.map +1 -1
  251. package/package.json +2 -2
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.cjs","names":["Logger","readProviderKeysFromEnv","AGUIMock","resolveFixturesValue","loadFixturesFromDir","loadFixtureFile","validateFixtures","createServer","resolveInboundAuth","selectInboundAuthSource","watchFixtures"],"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 --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 \"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\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 ] 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.cjs","names":["Logger","readProviderKeysFromEnv","AGUIMock","resolveFixturesValue","loadFixturesFromDir","loadFixtureFile","validateFixtures","createServer","resolveInboundAuth","selectInboundAuthSource","watchFixtures"],"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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"}
package/dist/cli.js CHANGED
@@ -42,6 +42,7 @@ Options:
42
42
  --provider-ollama <url> Upstream URL for Ollama
43
43
  --provider-cohere <url> Upstream URL for Cohere
44
44
  --provider-openrouter <url> Upstream URL for OpenRouter (video record proxy)
45
+ --provider-byteplus <url> Upstream ORIGIN for BytePlus Ark (e.g. https://ark.ap-southeast.bytepluses.com — origin only, no /api/v3)
45
46
  --upstream-timeout-ms <ms> Idle timeout (ms) on upstream socket before response (default: 30000)
46
47
  --body-timeout-ms <ms> Idle timeout (ms) on upstream response body between chunks (default: 30000)
47
48
  --max-proxy-buffer-bytes <n> Cap (bytes) on in-memory proxy-path buffer; full body still relayed (default: 67108864)
@@ -125,6 +126,7 @@ const { values } = parseArgs({
125
126
  "provider-ollama": { type: "string" },
126
127
  "provider-cohere": { type: "string" },
127
128
  "provider-openrouter": { type: "string" },
129
+ "provider-byteplus": { type: "string" },
128
130
  "upstream-timeout-ms": { type: "string" },
129
131
  "body-timeout-ms": { type: "string" },
130
132
  "max-proxy-buffer-bytes": { type: "string" },
@@ -292,6 +294,7 @@ if (values.record || values["proxy-only"]) {
292
294
  if (values["provider-ollama"]) providers.ollama = values["provider-ollama"];
293
295
  if (values["provider-cohere"]) providers.cohere = values["provider-cohere"];
294
296
  if (values["provider-openrouter"]) providers.openrouter = values["provider-openrouter"];
297
+ if (values["provider-byteplus"]) providers.byteplus = values["provider-byteplus"];
295
298
  if (Object.keys(providers).length === 0) {
296
299
  console.error(`Error: --${values["proxy-only"] ? "proxy-only" : "record"} requires at least one --provider-* flag`);
297
300
  process.exit(1);
@@ -323,7 +326,8 @@ if (values.record || values["proxy-only"]) {
323
326
  "provider-azure",
324
327
  "provider-ollama",
325
328
  "provider-cohere",
326
- "provider-openrouter"
329
+ "provider-openrouter",
330
+ "provider-byteplus"
327
331
  ].filter((flag) => values[flag] !== void 0);
328
332
  if (droppedProviderFlags.length > 0) logger.warn(`--${droppedProviderFlags.join("/--")} only apply to --record/--proxy-only upstream proxying — ignored without one of those flags.`);
329
333
  if (upstreamTimeoutMs !== void 0 || bodyTimeoutMs !== void 0 || maxProxyBufferBytes !== void 0 || maxProxyBufferFrames !== void 0) logger.warn("--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.");
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 --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 \"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\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 ] 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 { 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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"}
package/dist/cohere.cjs CHANGED
@@ -44,8 +44,9 @@ function cohereUsage(overrides) {
44
44
  }
45
45
  /** Extract plain text from structured content (array of { type, text } parts) or passthrough string. */
46
46
  function extractTextContent(content) {
47
+ if (content === null || content === void 0) return "";
47
48
  if (typeof content === "string") return content;
48
- return content.filter((part) => part.type === "text" && part.text !== void 0).map((part) => part.text).join("");
49
+ return content.filter((part) => part !== null && part !== void 0 && part.type === "text" && part.text !== void 0).map((part) => part.text).join("");
49
50
  }
50
51
  /** Type guard: is this an OpenAI-style tool definition (has `function` key)? */
51
52
  function isOpenAIToolDef(t) {
@@ -605,6 +606,23 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
605
606
  } }));
606
607
  return;
607
608
  }
609
+ if (!require_helpers.isJsonObject(cohereReq)) {
610
+ journal.add({
611
+ method: req.method ?? "POST",
612
+ path: req.url ?? "/v2/chat",
613
+ headers: require_helpers.flattenHeaders(req.headers),
614
+ body: null,
615
+ response: {
616
+ status: 400,
617
+ fixture: null
618
+ }
619
+ });
620
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
621
+ message: "Request body must be a JSON object",
622
+ type: "invalid_request_error"
623
+ } }));
624
+ return;
625
+ }
608
626
  if (!cohereReq.model) {
609
627
  journal.add({
610
628
  method: req.method ?? "POST",
@@ -622,7 +640,8 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
622
640
  } }));
623
641
  return;
624
642
  }
625
- if (!cohereReq.messages || !Array.isArray(cohereReq.messages)) {
643
+ const shapeError = require_helpers.validateChatMessages(cohereReq.messages) ?? require_helpers.validateToolsField(cohereReq.tools);
644
+ if (shapeError) {
626
645
  journal.add({
627
646
  method: req.method ?? "POST",
628
647
  path: req.url ?? "/v2/chat",
@@ -634,7 +653,7 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
634
653
  }
635
654
  });
636
655
  require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
637
- message: "Invalid request: messages array is required",
656
+ message: `Invalid request: ${shapeError}`,
638
657
  type: "invalid_request_error"
639
658
  } }));
640
659
  return;
@@ -648,7 +667,7 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
648
667
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
649
668
  logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
650
669
  } else logger.debug(`No fixture matched for request`);
651
- if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
670
+ if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, req.url, journal, {
652
671
  method: req.method ?? "POST",
653
672
  path: req.url ?? "/v2/chat",
654
673
  headers: require_helpers.flattenHeaders(req.headers),
@@ -882,6 +901,23 @@ async function handleCohereEmbed(req, res, raw, fixtures, journal, defaults, set
882
901
  } }));
883
902
  return;
884
903
  }
904
+ if (!require_helpers.isJsonObject(embedReq)) {
905
+ journal.add({
906
+ method: req.method ?? "POST",
907
+ path: req.url ?? "/v2/embed",
908
+ headers: require_helpers.flattenHeaders(req.headers),
909
+ body: null,
910
+ response: {
911
+ status: 400,
912
+ fixture: null
913
+ }
914
+ });
915
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
916
+ message: "Request body must be a JSON object",
917
+ type: "invalid_request_error"
918
+ } }));
919
+ return;
920
+ }
885
921
  if (!embedReq.model) {
886
922
  journal.add({
887
923
  method: req.method ?? "POST",
@@ -916,6 +952,23 @@ async function handleCohereEmbed(req, res, raw, fixtures, journal, defaults, set
916
952
  } }));
917
953
  return;
918
954
  }
955
+ if (!embedReq.texts.every((text) => typeof text === "string")) {
956
+ journal.add({
957
+ method: req.method ?? "POST",
958
+ path: req.url ?? "/v2/embed",
959
+ headers: require_helpers.flattenHeaders(req.headers),
960
+ body: null,
961
+ response: {
962
+ status: 400,
963
+ fixture: null
964
+ }
965
+ });
966
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
967
+ message: "Invalid request: texts must be an array of strings",
968
+ type: "invalid_request_error"
969
+ } }));
970
+ return;
971
+ }
919
972
  const combinedInput = embedReq.texts.join(" ");
920
973
  const syntheticReq = {
921
974
  model: embedReq.model,
@@ -930,7 +983,7 @@ async function handleCohereEmbed(req, res, raw, fixtures, journal, defaults, set
930
983
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
931
984
  logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
932
985
  } else logger.debug(`No fixture matched for request`);
933
- if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
986
+ if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, req.url, journal, {
934
987
  method: req.method ?? "POST",
935
988
  path: req.url ?? "/v2/embed",
936
989
  headers: require_helpers.flattenHeaders(req.headers),