@copilotkit/aimock 1.29.0 → 1.31.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 (207) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +34 -0
  4. package/README.md +1 -1
  5. package/dist/agui-types.d.ts.map +1 -1
  6. package/dist/bedrock-converse.cjs +63 -31
  7. package/dist/bedrock-converse.cjs.map +1 -1
  8. package/dist/bedrock-converse.d.cts.map +1 -1
  9. package/dist/bedrock-converse.d.ts.map +1 -1
  10. package/dist/bedrock-converse.js +65 -33
  11. package/dist/bedrock-converse.js.map +1 -1
  12. package/dist/bedrock.cjs +95 -33
  13. package/dist/bedrock.cjs.map +1 -1
  14. package/dist/bedrock.d.cts.map +1 -1
  15. package/dist/bedrock.d.ts.map +1 -1
  16. package/dist/bedrock.js +97 -35
  17. package/dist/bedrock.js.map +1 -1
  18. package/dist/cli.cjs +18 -1
  19. package/dist/cli.cjs.map +1 -1
  20. package/dist/cli.js +18 -1
  21. package/dist/cli.js.map +1 -1
  22. package/dist/cohere.cjs +49 -15
  23. package/dist/cohere.cjs.map +1 -1
  24. package/dist/cohere.d.cts.map +1 -1
  25. package/dist/cohere.d.ts.map +1 -1
  26. package/dist/cohere.js +51 -17
  27. package/dist/cohere.js.map +1 -1
  28. package/dist/config-loader.d.ts.map +1 -1
  29. package/dist/elevenlabs-audio.cjs +8 -4
  30. package/dist/elevenlabs-audio.cjs.map +1 -1
  31. package/dist/elevenlabs-audio.d.cts.map +1 -1
  32. package/dist/elevenlabs-audio.d.ts.map +1 -1
  33. package/dist/elevenlabs-audio.js +10 -6
  34. package/dist/elevenlabs-audio.js.map +1 -1
  35. package/dist/embeddings.cjs +4 -3
  36. package/dist/embeddings.cjs.map +1 -1
  37. package/dist/embeddings.d.cts.map +1 -1
  38. package/dist/embeddings.d.ts.map +1 -1
  39. package/dist/embeddings.js +6 -5
  40. package/dist/embeddings.js.map +1 -1
  41. package/dist/fal-audio.cjs +18 -8
  42. package/dist/fal-audio.cjs.map +1 -1
  43. package/dist/fal-audio.d.cts.map +1 -1
  44. package/dist/fal-audio.d.ts.map +1 -1
  45. package/dist/fal-audio.js +22 -12
  46. package/dist/fal-audio.js.map +1 -1
  47. package/dist/fal.cjs +55 -46
  48. package/dist/fal.cjs.map +1 -1
  49. package/dist/fal.d.cts +8 -0
  50. package/dist/fal.d.cts.map +1 -1
  51. package/dist/fal.d.ts +8 -0
  52. package/dist/fal.d.ts.map +1 -1
  53. package/dist/fal.js +58 -49
  54. package/dist/fal.js.map +1 -1
  55. package/dist/gemini-embeddings.cjs +4 -3
  56. package/dist/gemini-embeddings.cjs.map +1 -1
  57. package/dist/gemini-embeddings.js +6 -5
  58. package/dist/gemini-embeddings.js.map +1 -1
  59. package/dist/gemini-interactions.cjs +3 -3
  60. package/dist/gemini-interactions.cjs.map +1 -1
  61. package/dist/gemini-interactions.d.cts.map +1 -1
  62. package/dist/gemini-interactions.d.ts.map +1 -1
  63. package/dist/gemini-interactions.js +5 -5
  64. package/dist/gemini-interactions.js.map +1 -1
  65. package/dist/gemini.cjs +55 -24
  66. package/dist/gemini.cjs.map +1 -1
  67. package/dist/gemini.d.cts.map +1 -1
  68. package/dist/gemini.d.ts.map +1 -1
  69. package/dist/gemini.js +57 -26
  70. package/dist/gemini.js.map +1 -1
  71. package/dist/helpers.cjs +120 -2
  72. package/dist/helpers.cjs.map +1 -1
  73. package/dist/helpers.d.cts +43 -3
  74. package/dist/helpers.d.cts.map +1 -1
  75. package/dist/helpers.d.ts +43 -3
  76. package/dist/helpers.d.ts.map +1 -1
  77. package/dist/helpers.js +117 -3
  78. package/dist/helpers.js.map +1 -1
  79. package/dist/images.cjs +12 -6
  80. package/dist/images.cjs.map +1 -1
  81. package/dist/images.d.cts.map +1 -1
  82. package/dist/images.d.ts.map +1 -1
  83. package/dist/images.js +14 -8
  84. package/dist/images.js.map +1 -1
  85. package/dist/index.cjs +13 -2
  86. package/dist/index.d.cts +4 -3
  87. package/dist/index.d.ts +4 -3
  88. package/dist/index.js +6 -5
  89. package/dist/llmock.cjs +2 -1
  90. package/dist/llmock.cjs.map +1 -1
  91. package/dist/llmock.d.cts.map +1 -1
  92. package/dist/llmock.d.ts.map +1 -1
  93. package/dist/llmock.js +2 -1
  94. package/dist/llmock.js.map +1 -1
  95. package/dist/messages.cjs +325 -85
  96. package/dist/messages.cjs.map +1 -1
  97. package/dist/messages.d.cts.map +1 -1
  98. package/dist/messages.d.ts.map +1 -1
  99. package/dist/messages.js +327 -87
  100. package/dist/messages.js.map +1 -1
  101. package/dist/metrics.cjs +9 -0
  102. package/dist/metrics.cjs.map +1 -1
  103. package/dist/metrics.d.cts.map +1 -1
  104. package/dist/metrics.d.ts.map +1 -1
  105. package/dist/metrics.js +7 -1
  106. package/dist/metrics.js.map +1 -1
  107. package/dist/model-utils.cjs +68 -0
  108. package/dist/model-utils.cjs.map +1 -1
  109. package/dist/model-utils.js +68 -1
  110. package/dist/model-utils.js.map +1 -1
  111. package/dist/ollama.cjs +58 -21
  112. package/dist/ollama.cjs.map +1 -1
  113. package/dist/ollama.d.cts.map +1 -1
  114. package/dist/ollama.d.ts.map +1 -1
  115. package/dist/ollama.js +60 -23
  116. package/dist/ollama.js.map +1 -1
  117. package/dist/openrouter-video.cjs +1609 -0
  118. package/dist/openrouter-video.cjs.map +1 -0
  119. package/dist/openrouter-video.d.cts +154 -0
  120. package/dist/openrouter-video.d.cts.map +1 -0
  121. package/dist/openrouter-video.d.ts +154 -0
  122. package/dist/openrouter-video.d.ts.map +1 -0
  123. package/dist/openrouter-video.js +1601 -0
  124. package/dist/openrouter-video.js.map +1 -0
  125. package/dist/recorder.cjs +97 -15
  126. package/dist/recorder.cjs.map +1 -1
  127. package/dist/recorder.d.cts.map +1 -1
  128. package/dist/recorder.d.ts.map +1 -1
  129. package/dist/recorder.js +96 -17
  130. package/dist/recorder.js.map +1 -1
  131. package/dist/responses.cjs +26 -12
  132. package/dist/responses.cjs.map +1 -1
  133. package/dist/responses.d.cts +1 -1
  134. package/dist/responses.d.cts.map +1 -1
  135. package/dist/responses.d.ts +1 -1
  136. package/dist/responses.d.ts.map +1 -1
  137. package/dist/responses.js +28 -14
  138. package/dist/responses.js.map +1 -1
  139. package/dist/router.cjs +37 -8
  140. package/dist/router.cjs.map +1 -1
  141. package/dist/router.d.cts +30 -1
  142. package/dist/router.d.cts.map +1 -1
  143. package/dist/router.d.ts +30 -1
  144. package/dist/router.d.ts.map +1 -1
  145. package/dist/router.js +37 -9
  146. package/dist/router.js.map +1 -1
  147. package/dist/server.cjs +173 -19
  148. package/dist/server.cjs.map +1 -1
  149. package/dist/server.d.cts +2 -0
  150. package/dist/server.d.cts.map +1 -1
  151. package/dist/server.d.ts +2 -0
  152. package/dist/server.d.ts.map +1 -1
  153. package/dist/server.js +176 -22
  154. package/dist/server.js.map +1 -1
  155. package/dist/speech.cjs +4 -2
  156. package/dist/speech.cjs.map +1 -1
  157. package/dist/speech.d.cts.map +1 -1
  158. package/dist/speech.d.ts.map +1 -1
  159. package/dist/speech.js +6 -4
  160. package/dist/speech.js.map +1 -1
  161. package/dist/stream-collapse.cjs +44 -1
  162. package/dist/stream-collapse.cjs.map +1 -1
  163. package/dist/stream-collapse.d.cts +28 -0
  164. package/dist/stream-collapse.d.cts.map +1 -1
  165. package/dist/stream-collapse.d.ts +28 -0
  166. package/dist/stream-collapse.d.ts.map +1 -1
  167. package/dist/stream-collapse.js +44 -2
  168. package/dist/stream-collapse.js.map +1 -1
  169. package/dist/transcription.cjs +4 -2
  170. package/dist/transcription.cjs.map +1 -1
  171. package/dist/transcription.d.cts.map +1 -1
  172. package/dist/transcription.d.ts.map +1 -1
  173. package/dist/transcription.js +6 -4
  174. package/dist/transcription.js.map +1 -1
  175. package/dist/types.d.cts +123 -8
  176. package/dist/types.d.cts.map +1 -1
  177. package/dist/types.d.ts +123 -8
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/vector-types.d.cts.map +1 -1
  180. package/dist/vector-types.d.ts.map +1 -1
  181. package/dist/video.cjs +4 -2
  182. package/dist/video.cjs.map +1 -1
  183. package/dist/video.d.cts.map +1 -1
  184. package/dist/video.d.ts.map +1 -1
  185. package/dist/video.js +6 -4
  186. package/dist/video.js.map +1 -1
  187. package/dist/ws-gemini-live.cjs +4 -3
  188. package/dist/ws-gemini-live.cjs.map +1 -1
  189. package/dist/ws-gemini-live.d.cts +2 -2
  190. package/dist/ws-gemini-live.d.cts.map +1 -1
  191. package/dist/ws-gemini-live.d.ts.map +1 -1
  192. package/dist/ws-gemini-live.js +6 -5
  193. package/dist/ws-gemini-live.js.map +1 -1
  194. package/dist/ws-realtime.cjs +4 -3
  195. package/dist/ws-realtime.cjs.map +1 -1
  196. package/dist/ws-realtime.d.cts +2 -2
  197. package/dist/ws-realtime.d.cts.map +1 -1
  198. package/dist/ws-realtime.d.ts.map +1 -1
  199. package/dist/ws-realtime.js +6 -5
  200. package/dist/ws-realtime.js.map +1 -1
  201. package/dist/ws-responses.cjs +8 -6
  202. package/dist/ws-responses.cjs.map +1 -1
  203. package/dist/ws-responses.d.cts.map +1 -1
  204. package/dist/ws-responses.d.ts.map +1 -1
  205. package/dist/ws-responses.js +10 -8
  206. package/dist/ws-responses.js.map +1 -1
  207. package/package.json +2 -2
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.cjs","names":["Logger","AGUIMock","resolveFixturesValue","loadFixturesFromDir","loadFixtureFile","validateFixtures","createServer","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 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 --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 --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 --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 \"upstream-timeout-ms\": { type: \"string\" },\n \"body-timeout-ms\": { 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 or omitted = 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 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\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 // 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 };\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 },\n mounts,\n );\n\n logger.info(`aimock server listening on ${instance.url}`);\n\n // Start file watcher if requested. Only the first local source is watched —\n // remote URL sources are fetched once at boot and are not monitored.\n let watcher: { close: () => void } | null = null;\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 function shutdown() {\n logger.info(\"Shutting down...\");\n if (watcher) watcher.close();\n instance.server.close(() => {\n process.exit(0);\n });\n }\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\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","AGUIMock","resolveFixturesValue","loadFixturesFromDir","loadFixtureFile","validateFixtures","createServer","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 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 --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 --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 \"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 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 // 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 };\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 (upstreamTimeoutMs !== undefined || bodyTimeoutMs !== undefined) {\n logger.warn(\n \"--upstream-timeout-ms/--body-timeout-ms 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 },\n mounts,\n );\n\n logger.info(`aimock server listening on ${instance.url}`);\n\n // Start file watcher if requested. Only the first local source is watched —\n // remote URL sources are fetched once at boot and are not monitored.\n let watcher: { close: () => void } | null = null;\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 function shutdown() {\n logger.info(\"Shutting down...\");\n if (watcher) watcher.close();\n instance.server.close(() => {\n process.exit(0);\n });\n }\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\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
@@ -39,6 +39,7 @@ Options:
39
39
  --provider-azure <url> Upstream URL for Azure OpenAI
40
40
  --provider-ollama <url> Upstream URL for Ollama
41
41
  --provider-cohere <url> Upstream URL for Cohere
42
+ --provider-openrouter <url> Upstream URL for OpenRouter (video record proxy)
42
43
  --upstream-timeout-ms <ms> Idle timeout (ms) on upstream socket before response (default: 30000)
43
44
  --body-timeout-ms <ms> Idle timeout (ms) on upstream response body between chunks (default: 30000)
44
45
  --agui-record Enable AG-UI recording (proxy unmatched AG-UI requests)
@@ -118,6 +119,7 @@ const { values } = parseArgs({
118
119
  "provider-azure": { type: "string" },
119
120
  "provider-ollama": { type: "string" },
120
121
  "provider-cohere": { type: "string" },
122
+ "provider-openrouter": { type: "string" },
121
123
  "upstream-timeout-ms": { type: "string" },
122
124
  "body-timeout-ms": { type: "string" },
123
125
  "agui-record": {
@@ -192,7 +194,7 @@ if (Number.isNaN(replaySpeed) || replaySpeed <= 0) {
192
194
  }
193
195
  const journalMax = Number(values["journal-max"]);
194
196
  if (Number.isNaN(journalMax) || !Number.isInteger(journalMax) || journalMax < 0) {
195
- console.error(`Invalid journal-max: ${values["journal-max"]} (must be a non-negative integer; 0 or omitted = unbounded)`);
197
+ console.error(`Invalid journal-max: ${values["journal-max"]} (must be a non-negative integer; 0 = unbounded)`);
196
198
  process.exit(1);
197
199
  }
198
200
  const fixtureCountsMaxStr = values["fixture-counts-max"];
@@ -264,6 +266,7 @@ if (values.record || values["proxy-only"]) {
264
266
  if (values["provider-azure"]) providers.azure = values["provider-azure"];
265
267
  if (values["provider-ollama"]) providers.ollama = values["provider-ollama"];
266
268
  if (values["provider-cohere"]) providers.cohere = values["provider-cohere"];
269
+ if (values["provider-openrouter"]) providers.openrouter = values["provider-openrouter"];
267
270
  if (Object.keys(providers).length === 0) {
268
271
  console.error(`Error: --${values["proxy-only"] ? "proxy-only" : "record"} requires at least one --provider-* flag`);
269
272
  process.exit(1);
@@ -282,6 +285,20 @@ if (values.record || values["proxy-only"]) {
282
285
  upstreamTimeoutMs,
283
286
  bodyTimeoutMs
284
287
  };
288
+ } else {
289
+ const droppedProviderFlags = [
290
+ "provider-openai",
291
+ "provider-anthropic",
292
+ "provider-gemini",
293
+ "provider-vertexai",
294
+ "provider-bedrock",
295
+ "provider-azure",
296
+ "provider-ollama",
297
+ "provider-cohere",
298
+ "provider-openrouter"
299
+ ].filter((flag) => values[flag] !== void 0);
300
+ if (droppedProviderFlags.length > 0) logger.warn(`--${droppedProviderFlags.join("/--")} only apply to --record/--proxy-only upstream proxying — ignored without one of those flags.`);
301
+ if (upstreamTimeoutMs !== void 0 || bodyTimeoutMs !== void 0) logger.warn("--upstream-timeout-ms/--body-timeout-ms only apply to --record/--proxy-only upstream proxying — ignored without one of those flags.");
285
302
  }
286
303
  let aguiMount;
287
304
  if (values["agui-record"] || values["agui-proxy-only"]) {
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 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 --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 --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 --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 \"upstream-timeout-ms\": { type: \"string\" },\n \"body-timeout-ms\": { 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 or omitted = 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 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\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 // 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 };\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 },\n mounts,\n );\n\n logger.info(`aimock server listening on ${instance.url}`);\n\n // Start file watcher if requested. Only the first local source is watched —\n // remote URL sources are fetched once at boot and are not monitored.\n let watcher: { close: () => void } | null = null;\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 function shutdown() {\n logger.info(\"Shutting down...\");\n if (watcher) watcher.close();\n instance.server.close(() => {\n process.exit(0);\n });\n }\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;AAYA,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuCX,MAAM;AAER,MAAM,EAAE,WAAW,UAAU;CAC3B,SAAS;EACP,MAAM;GAAE,MAAM;GAAU,OAAO;GAAK,SAAS;GAAQ;EACrD,MAAM;GAAE,MAAM;GAAU,OAAO;GAAK,SAAS;GAAa;EAC1D,UAAU;GAAE,MAAM;GAAU,OAAO;GAAK,UAAU;GAAM;EACxD,SAAS;GAAE,MAAM;GAAU,OAAO;GAAK,SAAS;GAAK;EACrD,cAAc;GAAE,MAAM;GAAU,OAAO;GAAK,SAAS;GAAM;EAC3D,OAAO;GAAE,MAAM;GAAW,OAAO;GAAK,SAAS;GAAO;EACtD,aAAa;GAAE,MAAM;GAAU,SAAS;GAAQ;EAChD,oBAAoB;GAAE,MAAM;GAAW,SAAS;GAAO;EACvD,SAAS;GAAE,MAAM;GAAW,SAAS;GAAO;EAC5C,QAAQ;GAAE,MAAM;GAAW,SAAS;GAAO;EAC3C,6BAA6B;GAAE,MAAM;GAAW,SAAS;GAAO;EAChE,cAAc;GAAE,MAAM;GAAW,SAAS;GAAO;EACjD,QAAQ;GAAE,MAAM;GAAW,SAAS;GAAO;EAC3C,mBAAmB,EAAE,MAAM,UAAU;EACrC,sBAAsB,EAAE,MAAM,UAAU;EACxC,mBAAmB,EAAE,MAAM,UAAU;EACrC,qBAAqB,EAAE,MAAM,UAAU;EACvC,oBAAoB,EAAE,MAAM,UAAU;EACtC,kBAAkB,EAAE,MAAM,UAAU;EACpC,mBAAmB,EAAE,MAAM,UAAU;EACrC,mBAAmB,EAAE,MAAM,UAAU;EACrC,uBAAuB,EAAE,MAAM,UAAU;EACzC,mBAAmB,EAAE,MAAM,UAAU;EACrC,eAAe;GAAE,MAAM;GAAW,SAAS;GAAO;EAClD,iBAAiB,EAAE,MAAM,UAAU;EACnC,mBAAmB;GAAE,MAAM;GAAW,SAAS;GAAO;EACtD,gBAAgB;GAAE,MAAM;GAAU,SAAS;GAAO;EAClD,cAAc,EAAE,MAAM,UAAU;EAChC,mBAAmB,EAAE,MAAM,UAAU;EACrC,oBAAoB,EAAE,MAAM,UAAU;EACtC,eAAe;GAAE,MAAM;GAAU,SAAS;GAAQ;EAClD,sBAAsB;GAAE,MAAM;GAAU,SAAS;GAAO;EACxD,MAAM;GAAE,MAAM;GAAW,SAAS;GAAO;EAC1C;CACD,QAAQ;CACT,CAAC;AAEF,IAAI,OAAO,MAAM;AACf,SAAQ,IAAI,KAAK;AACjB,SAAQ,KAAK,EAAE;;AAGjB,MAAM,OAAO,OAAO,OAAO,KAAK;AAChC,MAAM,OAAO,OAAO;AACpB,MAAM,UAAU,OAAO,OAAO,QAAQ;AACtC,MAAM,YAAY,OAAO,OAAO,cAAc;AAC9C,MAAM,gBACJ,OAAO,YAAY,OAAO,SAAS,SAAS,IAAI,OAAO,WAAW,CAAC,aAAa;AAClF,MAAM,YAAY,OAAO;AACzB,MAAM,iBAAiB,OAAO;AAC9B,MAAM,cAAc,OAAO;AAE3B,IAAI,CAAC;CAAC;CAAU;CAAQ;CAAQ;CAAQ,CAAC,SAAS,YAAY,EAAE;AAC9D,SAAQ,MAAM,sBAAsB,YAAY,yCAAyC;AACzF,SAAQ,KAAK,EAAE;;AAEjB,MAAM,WAAW;AAEjB,IAAI,OAAO,MAAM,KAAK,IAAI,OAAO,KAAK,OAAO,OAAO;AAClD,SAAQ,MAAM,iBAAiB,OAAO,OAAO;AAC7C,SAAQ,KAAK,EAAE;;AAGjB,IAAI,OAAO,MAAM,QAAQ,IAAI,UAAU,GAAG;AACxC,SAAQ,MAAM,oBAAoB,OAAO,UAAU;AACnD,SAAQ,KAAK,EAAE;;AAGjB,IAAI,OAAO,MAAM,UAAU,IAAI,YAAY,GAAG;AAC5C,SAAQ,MAAM,uBAAuB,OAAO,gBAAgB;AAC5D,SAAQ,KAAK,EAAE;;AAGjB,MAAM,cAAc,OAAO,OAAO,gBAAgB;AAClD,IAAI,OAAO,MAAM,YAAY,IAAI,eAAe,GAAG;AACjD,SAAQ,MAAM,2CAA2C;AACzD,SAAQ,KAAK,EAAE;;AAGjB,MAAM,aAAa,OAAO,OAAO,eAAe;AAChD,IAAI,OAAO,MAAM,WAAW,IAAI,CAAC,OAAO,UAAU,WAAW,IAAI,aAAa,GAAG;AAC/E,SAAQ,MACN,wBAAwB,OAAO,eAAe,6DAC/C;AACD,SAAQ,KAAK,EAAE;;AAGjB,MAAM,sBAAsB,OAAO;AACnC,MAAM,mBAAmB,OAAO,oBAAoB;AACpD,IAAI,OAAO,MAAM,iBAAiB,IAAI,CAAC,OAAO,UAAU,iBAAiB,IAAI,mBAAmB,GAAG;AACjG,SAAQ,MACN,+BAA+B,oBAAoB,kDACpD;AACD,SAAQ,KAAK,EAAE;;AAGjB,MAAM,uBAAuB,OAAO;AACpC,IAAI;AACJ,IAAI,yBAAyB,QAAW;AACtC,qBAAoB,OAAO,qBAAqB;AAChD,KAAI,CAAC,OAAO,SAAS,kBAAkB,IAAI,qBAAqB,GAAG;AACjE,UAAQ,MACN,gCAAgC,qBAAqB,qCACtD;AACD,UAAQ,KAAK,EAAE;;;AAInB,MAAM,mBAAmB,OAAO;AAChC,IAAI;AACJ,IAAI,qBAAqB,QAAW;AAClC,iBAAgB,OAAO,iBAAiB;AACxC,KAAI,CAAC,OAAO,SAAS,cAAc,IAAI,iBAAiB,GAAG;AACzD,UAAQ,MACN,4BAA4B,iBAAiB,qCAC9C;AACD,UAAQ,KAAK,EAAE;;;AAInB,MAAM,SAAS,IAAI,OAAO,SAAS;AAGnC,IAAI;AACJ;CACE,MAAM,UAAU,OAAO;CACvB,MAAM,eAAe,OAAO;CAC5B,MAAM,gBAAgB,OAAO;AAE7B,KAAI,YAAY,UAAa,iBAAiB,UAAa,kBAAkB,QAAW;AACtF,UAAQ,EAAE;AACV,MAAI,YAAY,QAAW;GACzB,MAAM,MAAM,WAAW,QAAQ;AAC/B,OAAI,MAAM,IAAI,IAAI,MAAM,KAAK,MAAM,GAAG;AACpC,YAAQ,MAAM,uBAAuB,QAAQ,gBAAgB;AAC7D,YAAQ,KAAK,EAAE;;AAEjB,SAAM,WAAW;;AAEnB,MAAI,iBAAiB,QAAW;GAC9B,MAAM,MAAM,WAAW,aAAa;AACpC,OAAI,MAAM,IAAI,IAAI,MAAM,KAAK,MAAM,GAAG;AACpC,YAAQ,MAAM,4BAA4B,aAAa,gBAAgB;AACvE,YAAQ,KAAK,EAAE;;AAEjB,SAAM,gBAAgB;;AAExB,MAAI,kBAAkB,QAAW;GAC/B,MAAM,MAAM,WAAW,cAAc;AACrC,OAAI,MAAM,IAAI,IAAI,MAAM,KAAK,MAAM,GAAG;AACpC,YAAQ,MAAM,6BAA6B,cAAc,gBAAgB;AACzE,YAAQ,KAAK,EAAE;;AAEjB,SAAM,iBAAiB;;;;AAM7B,IAAI;AACJ,IAAI,OAAO,UAAU,OAAO,eAAe;CACzC,MAAM,YAAuC,EAAE;AAC/C,KAAI,OAAO,mBAAoB,WAAU,SAAS,OAAO;AACzD,KAAI,OAAO,sBAAuB,WAAU,YAAY,OAAO;AAC/D,KAAI,OAAO,mBAAoB,WAAU,SAAS,OAAO;AACzD,KAAI,OAAO,qBAAsB,WAAU,WAAW,OAAO;AAC7D,KAAI,OAAO,oBAAqB,WAAU,UAAU,OAAO;AAC3D,KAAI,OAAO,kBAAmB,WAAU,QAAQ,OAAO;AACvD,KAAI,OAAO,mBAAoB,WAAU,SAAS,OAAO;AACzD,KAAI,OAAO,mBAAoB,WAAU,SAAS,OAAO;AAEzD,KAAI,OAAO,KAAK,UAAU,CAAC,WAAW,GAAG;AACvC,UAAQ,MACN,YAAY,OAAO,gBAAgB,eAAe,SAAS,0CAC5D;AACD,UAAQ,KAAK,EAAE;;CAOjB,MAAM,aAAa,cAAc;CACjC,MAAM,kBAAkB,gBAAgB,KAAK,WAAW;AACxD,KAAI,OAAO,UAAU,iBAAiB;AACpC,UAAQ,MACN,2FAA2F,aAC5F;AACD,UAAQ,KAAK,EAAE;;AAEjB,UAAS;EACP;EAIA,aAAa,kBAAkB,SAAY,QAAQ,YAAY,WAAW;EAC1E,WAAW,OAAO;EAClB,wBAAwB,OAAO;EAC/B;EACA;EACD;;AAIH,IAAI;AACJ,IAAI,OAAO,kBAAkB,OAAO,oBAAoB;AACtD,KAAI,CAAC,OAAO,kBAAkB;AAC5B,UAAQ,MAAM,kEAAkE;AAChF,UAAQ,KAAK,EAAE;;CAIjB,MAAM,WAAW,cAAc;CAC/B,MAAM,gBAAgB,gBAAgB,KAAK,SAAS;AACpD,KAAI,OAAO,kBAAkB,eAAe;AAC1C,UAAQ,MACN,gGAAgG,WACjG;AACD,UAAQ,KAAK,EAAE;;CAEjB,MAAM,OAAO,IAAI,UAAU;AAC3B,MAAK,gBAAgB;EACnB,UAAU,OAAO;EAIjB,aAAa,gBAAgB,SAAY,QAAQ,UAAU,gBAAgB;EAC3E,WAAW,OAAO;EACnB,CAAC;AACF,aAAY;EAAE,MAAM;EAAS,SAAS;EAAM;;AAS9C,eAAe,2BAA6D;CAC1E,MAAM,WAAoC,EAAE;AAC5C,MAAK,MAAM,SAAS,eAAe;EACjC,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,qBAAqB,OAAO;IACxC;IACA;IACD,CAAC;WACK,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,WAAQ,MAAM,uCAAuC,MAAM,KAAK,MAAM;AACtE,WAAQ,KAAK,EAAE;;AAEjB,MAAI,CAAC,MAAM,KAET;AAEF,MAAI;GACF,MAAM,OAAO,SAAS,MAAM,KAAK;AACjC,YAAS,KAAK;IAAE,QAAQ,MAAM;IAAQ,MAAM,MAAM;IAAM,OAAO,KAAK,aAAa;IAAE,CAAC;WAC7E,KAAK;AACZ,OAAK,IAA8B,SAAS,SAC1C,SAAQ,MAAM,4BAA4B,MAAM,OAAO;QAClD;IACL,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,YAAQ,MAAM,gCAAgC,MAAM,KAAK,IAAI,MAAM;;AAErE,WAAQ,KAAK,EAAE;;;AAGnB,QAAO;;AAGT,SAAS,WAAW,QAA0C;AAC5D,QAAO,OAAO,QACV,oBAAoB,OAAO,MAAM,OAAO,GACxC,gBAAgB,OAAO,MAAM,OAAO;;AAG1C,eAAe,OAAO;CACpB,MAAM,UAAU,MAAM,0BAA0B;CAEhD,MAAM,WAAsB,EAAE;AAC9B,MAAK,MAAM,OAAO,QAChB,UAAS,KAAK,GAAG,WAAW,IAAI,CAAC;AAGnC,KAAI,SAAS,WAAW,GAAG;AACzB,MAAI,kBAAkB,OAAO,QAAQ;AACnC,WAAQ,MAAM,8EAA8E;AAC5F,WAAQ,KAAK,EAAE;;AAEjB,UAAQ,KAAK,4EAA4E;;CAG3F,MAAM,cAAc,QAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI;AAC/D,QAAO,KAAK,UAAU,SAAS,OAAO,mBAAmB,cAAc;AAGvE,KAAI,gBAAgB;EAClB,MAAM,UAAU,iBAAiB,SAAS;EAC1C,MAAM,SAAS,QAAQ,QAAQ,MAAM,EAAE,aAAa,QAAQ;EAC5D,MAAM,WAAW,QAAQ,QAAQ,MAAM,EAAE,aAAa,UAAU;AAEhE,OAAK,MAAM,KAAK,SACd,QAAO,KAAK,WAAW,EAAE,aAAa,IAAI,EAAE,UAAU;AAExD,OAAK,MAAM,KAAK,OACd,QAAO,MAAM,WAAW,EAAE,aAAa,IAAI,EAAE,UAAU;AAGzD,MAAI,OAAO,SAAS,GAAG;AACrB,WAAQ,MAAM,sBAAsB,OAAO,OAAO,aAAa,SAAS,OAAO,aAAa;AAC5F,WAAQ,KAAK,EAAE;;;CAInB,MAAM,SAAS,YAAY,CAAC,UAAU,GAAG;CAEzC,MAAM,WAAW,MAAM,aACrB,UACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA,SAAS,OAAO;EAChB;EACA,QAAQ,OAAO;EACf,mBAAmB;EACnB,yBAAyB;EAC1B,EACD,OACD;AAED,QAAO,KAAK,8BAA8B,SAAS,MAAM;CAIzD,IAAI,UAAwC;AAC5C,KAAI,WAAW;EACb,MAAM,UAAU,QAAQ;AACxB,MAAI,CAAC,QACH,QAAO,KAAK,wEAAwE;OAC/E;GACL,MAAM,eAA0B,WAAW,QAAQ;AACnD,aAAU,cAAc,QAAQ,MAAM,UAAU,QAAQ;IACtD;IACA,UAAU;IACV,YAAY;IACb,CAAC;AACF,UAAO,KAAK,YAAY,QAAQ,KAAK,cAAc;;;CAIvD,SAAS,WAAW;AAClB,SAAO,KAAK,mBAAmB;AAC/B,MAAI,QAAS,SAAQ,OAAO;AAC5B,WAAS,OAAO,YAAY;AAC1B,WAAQ,KAAK,EAAE;IACf;;AAGJ,SAAQ,GAAG,UAAU,SAAS;AAC9B,SAAQ,GAAG,WAAW,SAAS;;AAGjC,MAAM,CAAC,OAAO,QAAQ;AACpB,SAAQ,MAAM,IAAI;AAClB,SAAQ,KAAK,EAAE;EACf"}
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 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 --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 --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 \"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 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 // 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 };\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 (upstreamTimeoutMs !== undefined || bodyTimeoutMs !== undefined) {\n logger.warn(\n \"--upstream-timeout-ms/--body-timeout-ms 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 },\n mounts,\n );\n\n logger.info(`aimock server listening on ${instance.url}`);\n\n // Start file watcher if requested. Only the first local source is watched —\n // remote URL sources are fetched once at boot and are not monitored.\n let watcher: { close: () => void } | null = null;\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 function shutdown() {\n logger.info(\"Shutting down...\");\n if (watcher) watcher.close();\n instance.server.close(() => {\n process.exit(0);\n });\n }\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\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
@@ -137,7 +137,7 @@ function buildCohereTextResponse(content, reasoning, overrides) {
137
137
  usage: cohereUsage(overrides)
138
138
  };
139
139
  }
140
- function buildCohereToolCallResponse(toolCalls, logger, overrides) {
140
+ function buildCohereToolCallResponse(toolCalls, logger, reasoning, overrides) {
141
141
  const cohereCalls = toolCalls.map((tc) => {
142
142
  let argsJson;
143
143
  try {
@@ -156,12 +156,17 @@ function buildCohereToolCallResponse(toolCalls, logger, overrides) {
156
156
  }
157
157
  };
158
158
  });
159
+ const contentBlocks = [];
160
+ if (reasoning) contentBlocks.push({
161
+ type: "text",
162
+ text: reasoning
163
+ });
159
164
  return {
160
165
  id: overrides?.id ?? require_helpers.generateMessageId(),
161
166
  finish_reason: cohereFinishReason(overrides?.finishReason, "TOOL_CALL"),
162
167
  message: {
163
168
  role: "assistant",
164
- content: [],
169
+ content: contentBlocks,
165
170
  tool_calls: cohereCalls,
166
171
  tool_plan: "",
167
172
  citations: []
@@ -277,7 +282,7 @@ function buildCohereTextStreamEvents(content, chunkSize, reasoning, overrides) {
277
282
  });
278
283
  return events;
279
284
  }
280
- function buildCohereToolCallStreamEvents(toolCalls, chunkSize, logger, overrides) {
285
+ function buildCohereToolCallStreamEvents(toolCalls, chunkSize, logger, reasoning, overrides) {
281
286
  const msgId = overrides?.id ?? require_helpers.generateMessageId();
282
287
  const events = [];
283
288
  events.push({
@@ -291,6 +296,28 @@ function buildCohereToolCallStreamEvents(toolCalls, chunkSize, logger, overrides
291
296
  citations: []
292
297
  } }
293
298
  });
299
+ if (reasoning) {
300
+ events.push({
301
+ type: "content-start",
302
+ index: 0,
303
+ delta: { message: { content: { type: "text" } } }
304
+ });
305
+ for (let i = 0; i < reasoning.length; i += chunkSize) {
306
+ const slice = reasoning.slice(i, i + chunkSize);
307
+ events.push({
308
+ type: "content-delta",
309
+ index: 0,
310
+ delta: { message: { content: {
311
+ type: "text",
312
+ text: slice
313
+ } } }
314
+ });
315
+ }
316
+ events.push({
317
+ type: "content-end",
318
+ index: 0
319
+ });
320
+ }
294
321
  events.push({
295
322
  type: "tool-plan-delta",
296
323
  delta: { message: { tool_plan: "I will use the requested tool." } }
@@ -534,7 +561,7 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
534
561
  completionReq._endpointType = "chat";
535
562
  completionReq._context = require_helpers.getContext(req);
536
563
  const testId = require_helpers.getTestId(req);
537
- const fixture = require_router.matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
564
+ const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
538
565
  if (fixture) {
539
566
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
540
567
  logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
@@ -548,8 +575,8 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
548
575
  if (!fixture) {
549
576
  if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
550
577
  const strictStatus = 503;
551
- const strictMessage = "Strict mode: no fixture matched";
552
- logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${req.url ?? "/v2/chat"}`);
578
+ const strictMessage = require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn);
579
+ logger.error(require_helpers.strictNoMatchLogLine(req.method ?? "POST", req.url ?? "/v2/chat", skippedBySequenceOrTurn));
553
580
  journal.add({
554
581
  method: req.method ?? "POST",
555
582
  path: req.url ?? "/v2/chat",
@@ -623,6 +650,8 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
623
650
  if (require_helpers.isContentWithToolCallsResponse(response)) {
624
651
  if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Cohere v2 Chat API — ignoring");
625
652
  const overrides = require_helpers.extractOverrides(response);
653
+ const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
654
+ const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, cohereReq.model, effectiveStrict, logger);
626
655
  const journalEntry = journal.add({
627
656
  method: req.method ?? "POST",
628
657
  path: req.url ?? "/v2/chat",
@@ -634,11 +663,11 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
634
663
  }
635
664
  });
636
665
  if (cohereReq.stream !== true) {
637
- const body = buildCohereContentWithToolCallsResponse(response.content, response.toolCalls, logger, response.reasoning, overrides);
666
+ const body = buildCohereContentWithToolCallsResponse(response.content, response.toolCalls, logger, effReasoning, overrides);
638
667
  res.writeHead(200, { "Content-Type": "application/json" });
639
668
  res.end(JSON.stringify(body));
640
669
  } else {
641
- const events = buildCohereContentWithToolCallsStreamEvents(response.content, response.toolCalls, chunkSize, logger, response.reasoning, overrides);
670
+ const events = buildCohereContentWithToolCallsStreamEvents(response.content, response.toolCalls, chunkSize, logger, effReasoning, overrides);
642
671
  const interruption = require_interruption.createInterruptionSignal(fixture);
643
672
  if (!await writeCohereSSEStream(res, events, {
644
673
  latency,
@@ -659,6 +688,8 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
659
688
  if (require_helpers.isTextResponse(response)) {
660
689
  if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Cohere v2 Chat API — ignoring");
661
690
  const overrides = require_helpers.extractOverrides(response);
691
+ const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
692
+ const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, cohereReq.model, effectiveStrict, logger);
662
693
  const journalEntry = journal.add({
663
694
  method: req.method ?? "POST",
664
695
  path: req.url ?? "/v2/chat",
@@ -670,11 +701,11 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
670
701
  }
671
702
  });
672
703
  if (cohereReq.stream !== true) {
673
- const body = buildCohereTextResponse(response.content, response.reasoning, overrides);
704
+ const body = buildCohereTextResponse(response.content, effReasoning, overrides);
674
705
  res.writeHead(200, { "Content-Type": "application/json" });
675
706
  res.end(JSON.stringify(body));
676
707
  } else {
677
- const events = buildCohereTextStreamEvents(response.content, chunkSize, response.reasoning, overrides);
708
+ const events = buildCohereTextStreamEvents(response.content, chunkSize, effReasoning, overrides);
678
709
  const interruption = require_interruption.createInterruptionSignal(fixture);
679
710
  if (!await writeCohereSSEStream(res, events, {
680
711
  latency,
@@ -695,6 +726,8 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
695
726
  if (require_helpers.isToolCallResponse(response)) {
696
727
  if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Cohere v2 Chat API — ignoring");
697
728
  const overrides = require_helpers.extractOverrides(response);
729
+ const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
730
+ const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, cohereReq.model, effectiveStrict, logger);
698
731
  const journalEntry = journal.add({
699
732
  method: req.method ?? "POST",
700
733
  path: req.url ?? "/v2/chat",
@@ -706,11 +739,11 @@ async function handleCohere(req, res, raw, fixtures, journal, defaults, setCorsH
706
739
  }
707
740
  });
708
741
  if (cohereReq.stream !== true) {
709
- const body = buildCohereToolCallResponse(response.toolCalls, logger, overrides);
742
+ const body = buildCohereToolCallResponse(response.toolCalls, logger, effReasoning, overrides);
710
743
  res.writeHead(200, { "Content-Type": "application/json" });
711
744
  res.end(JSON.stringify(body));
712
745
  } else {
713
- const events = buildCohereToolCallStreamEvents(response.toolCalls, chunkSize, logger, overrides);
746
+ const events = buildCohereToolCallStreamEvents(response.toolCalls, chunkSize, logger, effReasoning, overrides);
714
747
  const interruption = require_interruption.createInterruptionSignal(fixture);
715
748
  if (!await writeCohereSSEStream(res, events, {
716
749
  latency,
@@ -810,7 +843,7 @@ async function handleCohereEmbed(req, res, raw, fixtures, journal, defaults, set
810
843
  _context: require_helpers.getContext(req)
811
844
  };
812
845
  const testId = require_helpers.getTestId(req);
813
- const fixture = require_router.matchFixture(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
846
+ const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
814
847
  if (fixture) {
815
848
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
816
849
  logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
@@ -880,7 +913,8 @@ async function handleCohereEmbed(req, res, raw, fixtures, journal, defaults, set
880
913
  return;
881
914
  }
882
915
  if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
883
- logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${req.url ?? "/v2/embed"}`);
916
+ const strictMessage = require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn);
917
+ logger.error(require_helpers.strictNoMatchLogLine(req.method ?? "POST", req.url ?? "/v2/embed", skippedBySequenceOrTurn));
884
918
  journal.add({
885
919
  method: req.method ?? "POST",
886
920
  path: req.url ?? "/v2/embed",
@@ -893,7 +927,7 @@ async function handleCohereEmbed(req, res, raw, fixtures, journal, defaults, set
893
927
  }
894
928
  });
895
929
  require_sse_writer.writeErrorResponse(res, 503, JSON.stringify({ error: {
896
- message: "Strict mode: no fixture matched",
930
+ message: strictMessage,
897
931
  type: "invalid_request_error"
898
932
  } }));
899
933
  return;