@copilotkit/aimock 1.22.0 → 1.23.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 (189) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +37 -0
  4. package/README.md +10 -10
  5. package/dist/agui-types.d.ts.map +1 -1
  6. package/dist/aimock-cli.cjs +0 -0
  7. package/dist/aimock-cli.js +0 -0
  8. package/dist/bedrock-converse.cjs +62 -22
  9. package/dist/bedrock-converse.cjs.map +1 -1
  10. package/dist/bedrock-converse.d.cts.map +1 -1
  11. package/dist/bedrock-converse.d.ts.map +1 -1
  12. package/dist/bedrock-converse.js +62 -22
  13. package/dist/bedrock-converse.js.map +1 -1
  14. package/dist/bedrock.cjs +59 -20
  15. package/dist/bedrock.cjs.map +1 -1
  16. package/dist/bedrock.d.cts.map +1 -1
  17. package/dist/bedrock.d.ts.map +1 -1
  18. package/dist/bedrock.js +59 -20
  19. package/dist/bedrock.js.map +1 -1
  20. package/dist/cli.cjs +8 -2
  21. package/dist/cli.cjs.map +1 -1
  22. package/dist/cli.js +8 -2
  23. package/dist/cli.js.map +1 -1
  24. package/dist/cohere.cjs +29 -9
  25. package/dist/cohere.cjs.map +1 -1
  26. package/dist/cohere.d.cts.map +1 -1
  27. package/dist/cohere.d.ts.map +1 -1
  28. package/dist/cohere.js +30 -10
  29. package/dist/cohere.js.map +1 -1
  30. package/dist/config-loader.d.cts.map +1 -1
  31. package/dist/constants.cjs +8 -0
  32. package/dist/constants.cjs.map +1 -0
  33. package/dist/constants.d.cts +8 -0
  34. package/dist/constants.d.cts.map +1 -0
  35. package/dist/constants.d.ts +8 -0
  36. package/dist/constants.d.ts.map +1 -0
  37. package/dist/constants.js +7 -0
  38. package/dist/constants.js.map +1 -0
  39. package/dist/elevenlabs-audio.cjs +41 -18
  40. package/dist/elevenlabs-audio.cjs.map +1 -1
  41. package/dist/elevenlabs-audio.d.cts.map +1 -1
  42. package/dist/elevenlabs-audio.d.ts.map +1 -1
  43. package/dist/elevenlabs-audio.js +42 -19
  44. package/dist/elevenlabs-audio.js.map +1 -1
  45. package/dist/embeddings.cjs +19 -17
  46. package/dist/embeddings.cjs.map +1 -1
  47. package/dist/embeddings.d.cts.map +1 -1
  48. package/dist/embeddings.d.ts.map +1 -1
  49. package/dist/embeddings.js +20 -18
  50. package/dist/embeddings.js.map +1 -1
  51. package/dist/fal-audio.cjs +128 -39
  52. package/dist/fal-audio.cjs.map +1 -1
  53. package/dist/fal-audio.d.cts.map +1 -1
  54. package/dist/fal-audio.d.ts.map +1 -1
  55. package/dist/fal-audio.js +129 -40
  56. package/dist/fal-audio.js.map +1 -1
  57. package/dist/fal.cjs +25 -8
  58. package/dist/fal.cjs.map +1 -1
  59. package/dist/fal.d.cts.map +1 -1
  60. package/dist/fal.d.ts.map +1 -1
  61. package/dist/fal.js +26 -9
  62. package/dist/fal.js.map +1 -1
  63. package/dist/fixture-loader.cjs +11 -2
  64. package/dist/fixture-loader.cjs.map +1 -1
  65. package/dist/fixture-loader.d.cts.map +1 -1
  66. package/dist/fixture-loader.d.ts.map +1 -1
  67. package/dist/fixture-loader.js +11 -2
  68. package/dist/fixture-loader.js.map +1 -1
  69. package/dist/gemini-interactions.cjs +29 -7
  70. package/dist/gemini-interactions.cjs.map +1 -1
  71. package/dist/gemini-interactions.js +28 -8
  72. package/dist/gemini-interactions.js.map +1 -1
  73. package/dist/gemini.cjs +45 -19
  74. package/dist/gemini.cjs.map +1 -1
  75. package/dist/gemini.d.cts.map +1 -1
  76. package/dist/gemini.d.ts.map +1 -1
  77. package/dist/gemini.js +45 -19
  78. package/dist/gemini.js.map +1 -1
  79. package/dist/helpers.cjs +52 -8
  80. package/dist/helpers.cjs.map +1 -1
  81. package/dist/helpers.d.cts +6 -0
  82. package/dist/helpers.d.cts.map +1 -1
  83. package/dist/helpers.d.ts +6 -0
  84. package/dist/helpers.d.ts.map +1 -1
  85. package/dist/helpers.js +52 -9
  86. package/dist/helpers.js.map +1 -1
  87. package/dist/images.cjs +26 -8
  88. package/dist/images.cjs.map +1 -1
  89. package/dist/images.d.cts.map +1 -1
  90. package/dist/images.d.ts.map +1 -1
  91. package/dist/images.js +27 -9
  92. package/dist/images.js.map +1 -1
  93. package/dist/index.cjs +2 -1
  94. package/dist/index.d.cts +2 -1
  95. package/dist/index.d.ts +2 -1
  96. package/dist/index.js +2 -1
  97. package/dist/journal.cjs +17 -7
  98. package/dist/journal.cjs.map +1 -1
  99. package/dist/journal.d.cts +2 -3
  100. package/dist/journal.d.cts.map +1 -1
  101. package/dist/journal.d.ts +2 -3
  102. package/dist/journal.d.ts.map +1 -1
  103. package/dist/journal.js +15 -4
  104. package/dist/journal.js.map +1 -1
  105. package/dist/messages.cjs +33 -12
  106. package/dist/messages.cjs.map +1 -1
  107. package/dist/messages.d.cts.map +1 -1
  108. package/dist/messages.d.ts.map +1 -1
  109. package/dist/messages.js +33 -12
  110. package/dist/messages.js.map +1 -1
  111. package/dist/model-utils.cjs +11 -0
  112. package/dist/model-utils.cjs.map +1 -0
  113. package/dist/model-utils.js +10 -0
  114. package/dist/model-utils.js.map +1 -0
  115. package/dist/ollama.cjs +59 -18
  116. package/dist/ollama.cjs.map +1 -1
  117. package/dist/ollama.d.cts.map +1 -1
  118. package/dist/ollama.d.ts.map +1 -1
  119. package/dist/ollama.js +60 -19
  120. package/dist/ollama.js.map +1 -1
  121. package/dist/recorder.cjs +30 -11
  122. package/dist/recorder.cjs.map +1 -1
  123. package/dist/recorder.d.cts.map +1 -1
  124. package/dist/recorder.d.ts.map +1 -1
  125. package/dist/recorder.js +30 -11
  126. package/dist/recorder.js.map +1 -1
  127. package/dist/responses.cjs +61 -52
  128. package/dist/responses.cjs.map +1 -1
  129. package/dist/responses.d.cts +1 -1
  130. package/dist/responses.d.cts.map +1 -1
  131. package/dist/responses.d.ts +1 -1
  132. package/dist/responses.d.ts.map +1 -1
  133. package/dist/responses.js +62 -53
  134. package/dist/responses.js.map +1 -1
  135. package/dist/router.cjs +7 -3
  136. package/dist/router.cjs.map +1 -1
  137. package/dist/router.js +7 -3
  138. package/dist/router.js.map +1 -1
  139. package/dist/server.cjs +64 -180
  140. package/dist/server.cjs.map +1 -1
  141. package/dist/server.d.cts.map +1 -1
  142. package/dist/server.d.ts.map +1 -1
  143. package/dist/server.js +40 -156
  144. package/dist/server.js.map +1 -1
  145. package/dist/speech.cjs +26 -8
  146. package/dist/speech.cjs.map +1 -1
  147. package/dist/speech.d.cts.map +1 -1
  148. package/dist/speech.d.ts.map +1 -1
  149. package/dist/speech.js +27 -9
  150. package/dist/speech.js.map +1 -1
  151. package/dist/transcription.cjs +57 -19
  152. package/dist/transcription.cjs.map +1 -1
  153. package/dist/transcription.d.cts.map +1 -1
  154. package/dist/transcription.d.ts.map +1 -1
  155. package/dist/transcription.js +58 -20
  156. package/dist/transcription.js.map +1 -1
  157. package/dist/types.d.cts +19 -2
  158. package/dist/types.d.cts.map +1 -1
  159. package/dist/types.d.ts +19 -2
  160. package/dist/types.d.ts.map +1 -1
  161. package/dist/video.cjs +50 -14
  162. package/dist/video.cjs.map +1 -1
  163. package/dist/video.d.cts +8 -1
  164. package/dist/video.d.cts.map +1 -1
  165. package/dist/video.d.ts +8 -1
  166. package/dist/video.d.ts.map +1 -1
  167. package/dist/video.js +51 -15
  168. package/dist/video.js.map +1 -1
  169. package/dist/ws-gemini-live.cjs +34 -27
  170. package/dist/ws-gemini-live.cjs.map +1 -1
  171. package/dist/ws-gemini-live.d.cts +2 -2
  172. package/dist/ws-gemini-live.d.cts.map +1 -1
  173. package/dist/ws-gemini-live.d.ts.map +1 -1
  174. package/dist/ws-gemini-live.js +34 -27
  175. package/dist/ws-gemini-live.js.map +1 -1
  176. package/dist/ws-realtime.cjs +776 -175
  177. package/dist/ws-realtime.cjs.map +1 -1
  178. package/dist/ws-realtime.d.cts +2 -2
  179. package/dist/ws-realtime.d.cts.map +1 -1
  180. package/dist/ws-realtime.d.ts.map +1 -1
  181. package/dist/ws-realtime.js +776 -175
  182. package/dist/ws-realtime.js.map +1 -1
  183. package/dist/ws-responses.cjs +48 -12
  184. package/dist/ws-responses.cjs.map +1 -1
  185. package/dist/ws-responses.d.cts.map +1 -1
  186. package/dist/ws-responses.d.ts.map +1 -1
  187. package/dist/ws-responses.js +49 -13
  188. package/dist/ws-responses.js.map +1 -1
  189. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"bedrock.js","names":[],"sources":["../src/bedrock.ts"],"sourcesContent":["/**\n * AWS Bedrock Claude endpoint support — invoke and invoke-with-response-stream.\n *\n * Handles four Bedrock endpoint families (split across two modules):\n *\n * This file (bedrock.ts):\n * - POST /model/{modelId}/invoke — non-streaming invoke\n * - POST /model/{modelId}/invoke-with-response-stream — binary EventStream streaming\n *\n * bedrock-converse.ts:\n * - POST /model/{modelId}/converse — Converse API (non-streaming)\n * - POST /model/{modelId}/converse-stream — Converse API (EventStream streaming)\n *\n * Translates incoming Bedrock Claude format into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * the appropriate Bedrock response format (JSON for invoke, AWS Event Stream\n * binary encoding for streaming).\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ResponseOverrides,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateMessageId,\n generateToolUseId,\n extractOverrides,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { writeEventStream } from \"./aws-event-stream.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Bedrock Claude request types ────────────────────────────────────────────\n\ninterface BedrockContentBlock {\n type: \"text\" | \"tool_use\" | \"tool_result\" | \"image\" | \"document\";\n text?: string;\n id?: string;\n name?: string;\n input?: unknown;\n tool_use_id?: string;\n content?: string | BedrockContentBlock[];\n is_error?: boolean;\n}\n\ninterface BedrockMessage {\n role: \"user\" | \"assistant\";\n content: string | BedrockContentBlock[];\n}\n\ninterface BedrockToolDef {\n name: string;\n description?: string;\n input_schema?: object;\n}\n\ninterface BedrockRequest {\n anthropic_version?: string;\n messages: BedrockMessage[];\n system?: string | BedrockContentBlock[];\n tools?: BedrockToolDef[];\n tool_choice?: unknown;\n max_tokens: number;\n temperature?: number;\n [key: string]: unknown;\n}\n\n// ─── Bedrock stop_reason mapping ───────────────────────────────────────────\n\nfunction bedrockStopReason(\n overrideFinishReason: string | undefined,\n defaultReason: string,\n): string {\n if (!overrideFinishReason) return defaultReason;\n if (overrideFinishReason === \"stop\") return \"end_turn\";\n if (overrideFinishReason === \"tool_calls\") return \"tool_use\";\n if (overrideFinishReason === \"length\") return \"max_tokens\";\n return overrideFinishReason;\n}\n\n/**\n * Build a Bedrock-style usage object from optional overrides.\n *\n * When no overrides are provided (the common case for mock fixtures),\n * returns all-zero token counts. This is intentional — aimock does not\n * attempt to estimate token usage from fixture content. Callers that\n * need realistic usage numbers should set `usage` in their fixture's\n * response overrides.\n */\nfunction bedrockUsage(overrides?: ResponseOverrides): {\n input_tokens: number;\n output_tokens: number;\n} {\n if (!overrides?.usage) return { input_tokens: 0, output_tokens: 0 };\n return {\n input_tokens: overrides.usage.input_tokens ?? overrides.usage.prompt_tokens ?? 0,\n output_tokens: overrides.usage.output_tokens ?? overrides.usage.completion_tokens ?? 0,\n };\n}\n\n// ─── Input conversion: Bedrock → ChatCompletionRequest ──────────────────────\n\nfunction extractTextContent(content: string | BedrockContentBlock[]): string {\n if (typeof content === \"string\") return content;\n return content\n .filter((b) => b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\");\n}\n\nexport function bedrockToCompletionRequest(\n req: BedrockRequest,\n modelId: string,\n logger?: Logger,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // system field → system message\n if (req.system) {\n const systemText =\n typeof req.system === \"string\"\n ? req.system\n : req.system\n .filter((b) => b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\");\n if (systemText) {\n messages.push({ role: \"system\", content: systemText });\n }\n }\n\n for (const msg of req.messages) {\n if (msg.role === \"user\") {\n // Check for tool_result blocks\n if (typeof msg.content !== \"string\" && Array.isArray(msg.content)) {\n // Warn about non-text content blocks that will be dropped (image, document, etc.)\n const unsupportedBlocks = msg.content.filter(\n (b) => b.type !== \"text\" && b.type !== \"tool_result\",\n );\n if (unsupportedBlocks.length > 0 && logger) {\n const types = [...new Set(unsupportedBlocks.map((b) => b.type))].join(\", \");\n logger.warn(\n `Bedrock user message contains unsupported content block types [${types}] — these will be dropped during conversion`,\n );\n }\n\n const toolResults = msg.content.filter((b) => b.type === \"tool_result\");\n const textBlocks = msg.content.filter((b) => b.type === \"text\");\n\n if (toolResults.length > 0) {\n for (const tr of toolResults) {\n const resultContent =\n typeof tr.content === \"string\"\n ? tr.content\n : Array.isArray(tr.content)\n ? tr.content\n .filter((b) => b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\")\n : \"\";\n messages.push({\n role: \"tool\",\n content: resultContent,\n tool_call_id: tr.tool_use_id,\n });\n }\n if (textBlocks.length > 0) {\n messages.push({\n role: \"user\",\n content: textBlocks.map((b) => b.text ?? \"\").join(\"\"),\n });\n }\n continue;\n }\n }\n messages.push({\n role: \"user\",\n content: extractTextContent(msg.content),\n });\n } else if (msg.role === \"assistant\") {\n if (typeof msg.content === \"string\") {\n messages.push({ role: \"assistant\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const toolUseBlocks = msg.content.filter((b) => b.type === \"tool_use\");\n const textContent = extractTextContent(msg.content);\n\n if (toolUseBlocks.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent ?? null,\n tool_calls: toolUseBlocks.map((b, index) => {\n if (!b.id && logger) {\n logger.warn(\n `Bedrock assistant tool_use block at index ${index} is missing an id — using deterministic fallback \"tool_use_${index}\"`,\n );\n }\n return {\n id: b.id ?? `tool_use_${index}`,\n type: \"function\" as const,\n function: {\n name: b.name ?? \"\",\n arguments: typeof b.input === \"string\" ? b.input : JSON.stringify(b.input ?? {}),\n },\n };\n }),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent ?? null });\n }\n } else {\n messages.push({ role: \"assistant\", content: null });\n }\n } else {\n if (logger) {\n logger.warn(\n `Bedrock message has unexpected role \"${(msg as { role: string }).role}\" — skipping`,\n );\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.input_schema,\n },\n }));\n }\n\n return {\n model: modelId,\n messages,\n stream: false,\n temperature: req.temperature,\n tools,\n };\n}\n\n// ─── Response builders ──────────────────────────────────────────────────────\n\nfunction buildBedrockTextResponse(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): object {\n const contentBlocks: object[] = [];\n if (reasoning) {\n contentBlocks.push({ type: \"thinking\", thinking: reasoning, signature: \"\" });\n }\n contentBlocks.push({ type: \"text\", text: content });\n\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: \"assistant\",\n content: contentBlocks,\n model: overrides?.model ?? model,\n stop_reason: bedrockStopReason(overrides?.finishReason, \"end_turn\"),\n stop_sequence: null,\n usage: bedrockUsage(overrides),\n };\n}\n\nfunction buildBedrockToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: \"assistant\",\n content: toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n type: \"tool_use\",\n id: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n };\n }),\n model: overrides?.model ?? model,\n stop_reason: bedrockStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n usage: bedrockUsage(overrides),\n };\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleBedrock(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/invoke`;\n\n let bedrockReq: BedrockRequest;\n try {\n bedrockReq = JSON.parse(raw) as BedrockRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!bedrockReq.messages || !Array.isArray(bedrockReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = bedrockToCompletionRequest(bedrockReq, modelId, logger);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n logger.debug(`No fixture matched for request`);\n }\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const strictStatus = effectiveStrict ? 503 : 404;\n const strictMessage = effectiveStrict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (effectiveStrict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n // Bedrock Claude error format: { type: \"error\", error: { type, message } }\n // Uses ?? (nullish coalescing) intentionally — preserves explicit empty-string types from fixtures.\n const anthropicError = {\n type: \"error\",\n error: {\n type: response.error.type ?? \"api_error\",\n message: response.error.message,\n },\n };\n writeErrorResponse(res, status, JSON.stringify(anthropicError));\n return;\n }\n\n // Content + tool calls response\n if (isContentWithToolCallsResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const textBody = buildBedrockTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n overrides,\n );\n const toolBody = buildBedrockToolCallResponse(\n response.toolCalls,\n completionReq.model,\n logger,\n overrides,\n );\n // Merge: take the text response as base, append tool_use blocks, set stop_reason to tool_use\n const merged = {\n ...(textBody as Record<string, unknown>),\n content: [\n ...((textBody as Record<string, unknown>).content as object[]),\n ...((toolBody as Record<string, unknown>).content as object[]),\n ],\n stop_reason: bedrockStopReason(overrides?.finishReason, \"tool_use\"),\n };\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(merged));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildBedrockTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (\"webSearches\" in response) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildBedrockToolCallResponse(\n response.toolCalls,\n completionReq.model,\n logger,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n\n// ─── Streaming event builders ───────────────────────────────────────────────\n\nconst BEDROCK_INVOKE_STREAM_EVENT_TYPE = \"chunk\";\n\nfunction buildBedrockInvokeMessageStart(\n model: string,\n overrides?: ResponseOverrides,\n): { eventType: string; payload: object } {\n return {\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"message_start\",\n message: {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: \"assistant\",\n content: [],\n model: overrides?.model ?? model,\n stop_reason: null,\n stop_sequence: null,\n usage: bedrockUsage(overrides),\n },\n },\n };\n}\n\nfunction buildBedrockInvokeMessageDelta(stopReason: string): {\n eventType: string;\n payload: object;\n} {\n return {\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"message_delta\",\n delta: { stop_reason: stopReason, stop_sequence: null },\n usage: { output_tokens: 0 },\n },\n };\n}\n\nfunction buildBedrockInvokeMessageStop(): { eventType: string; payload: object } {\n return {\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"message_stop\" },\n };\n}\n\nfunction parseToolArgumentsForStream(toolCall: ToolCall, logger: Logger): string {\n try {\n const parsed = JSON.parse(toolCall.arguments || \"{}\");\n return JSON.stringify(parsed);\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${toolCall.name}\": ${toolCall.arguments}`,\n );\n return \"{}\";\n }\n}\n\nexport function buildBedrockStreamTextEvents(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): Array<{ eventType: string; payload: object }> {\n const events: Array<{ eventType: string; payload: object }> = [];\n\n events.push(buildBedrockInvokeMessageStart(model, overrides));\n\n // Thinking block (emitted before text when reasoning is present)\n if (reasoning) {\n const blockIndex = 0;\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n },\n });\n }\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature: \"\" },\n },\n });\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: blockIndex },\n });\n }\n\n // Text block\n const textBlockIndex = reasoning ? 1 : 0;\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: textBlockIndex,\n content_block: { type: \"text\", text: \"\" },\n },\n });\n\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: textBlockIndex,\n delta: { type: \"text_delta\", text: slice },\n },\n });\n }\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: textBlockIndex },\n });\n\n events.push(\n buildBedrockInvokeMessageDelta(bedrockStopReason(overrides?.finishReason, \"end_turn\")),\n );\n events.push(buildBedrockInvokeMessageStop());\n\n return events;\n}\n\nexport function buildBedrockStreamContentWithToolCallsEvents(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): Array<{ eventType: string; payload: object }> {\n const events: Array<{ eventType: string; payload: object }> = [];\n\n events.push(buildBedrockInvokeMessageStart(model, overrides));\n\n let blockIndex = 0;\n\n // Thinking block (emitted before text when reasoning is present)\n if (reasoning) {\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n },\n });\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n },\n });\n }\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature: \"\" },\n },\n });\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: blockIndex },\n });\n blockIndex++;\n }\n\n // Text block\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"text\", text: \"\" },\n },\n });\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"text_delta\", text: slice },\n },\n });\n }\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: blockIndex },\n });\n blockIndex++;\n\n // Tool call blocks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const toolUseId = tc.id || generateToolUseId();\n const currentBlock = blockIndex + tcIdx;\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: currentBlock,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: tc.name,\n input: {},\n },\n },\n });\n\n const argsStr = parseToolArgumentsForStream(tc, logger);\n\n for (let i = 0; i < argsStr.length; i += chunkSize) {\n const slice = argsStr.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: currentBlock,\n delta: { type: \"input_json_delta\", partial_json: slice },\n },\n });\n }\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: currentBlock },\n });\n }\n\n events.push(\n buildBedrockInvokeMessageDelta(bedrockStopReason(overrides?.finishReason, \"tool_use\")),\n );\n events.push(buildBedrockInvokeMessageStop());\n\n return events;\n}\n\nexport function buildBedrockStreamToolCallEvents(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n overrides?: ResponseOverrides,\n): Array<{ eventType: string; payload: object }> {\n const events: Array<{ eventType: string; payload: object }> = [];\n\n events.push(buildBedrockInvokeMessageStart(model, overrides));\n\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const toolUseId = tc.id || generateToolUseId();\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: tcIdx,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: tc.name,\n input: {},\n },\n },\n });\n\n const argsStr = parseToolArgumentsForStream(tc, logger);\n\n for (let i = 0; i < argsStr.length; i += chunkSize) {\n const slice = argsStr.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: tcIdx,\n delta: { type: \"input_json_delta\", partial_json: slice },\n },\n });\n }\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: tcIdx },\n });\n }\n\n events.push(\n buildBedrockInvokeMessageDelta(bedrockStopReason(overrides?.finishReason, \"tool_use\")),\n );\n events.push(buildBedrockInvokeMessageStop());\n\n return events;\n}\n\n// ─── Streaming request handler ──────────────────────────────────────────────\n\nexport async function handleBedrockStream(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/invoke-with-response-stream`;\n\n let bedrockReq: BedrockRequest;\n try {\n bedrockReq = JSON.parse(raw) as BedrockRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!bedrockReq.messages || !Array.isArray(bedrockReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = bedrockToCompletionRequest(bedrockReq, modelId, logger);\n completionReq.stream = true;\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n logger.debug(`No fixture matched for request`);\n }\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const strictStatus = effectiveStrict ? 503 : 404;\n const strictMessage = effectiveStrict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (effectiveStrict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n // Bedrock Claude error format: { type: \"error\", error: { type, message } }\n // Uses ?? (nullish coalescing) intentionally — preserves explicit empty-string types from fixtures.\n const anthropicError = {\n type: \"error\",\n error: {\n type: response.error.type ?? \"api_error\",\n message: response.error.message,\n },\n };\n writeErrorResponse(res, status, JSON.stringify(anthropicError));\n return;\n }\n\n // Content + tool calls response — stream as Event Stream\n if (isContentWithToolCallsResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamContentWithToolCallsEvents(\n response.content,\n response.toolCalls,\n completionReq.model,\n chunkSize,\n logger,\n response.reasoning,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Text response — stream as Event Stream\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamTextEvents(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response — stream as Event Stream\n if (isToolCallResponse(response)) {\n if (\"webSearches\" in response) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamToolCallEvents(\n response.toolCalls,\n completionReq.model,\n chunkSize,\n logger,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bedrock.js","names":[],"sources":["../src/bedrock.ts"],"sourcesContent":["/**\n * AWS Bedrock Claude endpoint support — invoke and invoke-with-response-stream.\n *\n * Handles four Bedrock endpoint families (split across two modules):\n *\n * This file (bedrock.ts):\n * - POST /model/{modelId}/invoke — non-streaming invoke\n * - POST /model/{modelId}/invoke-with-response-stream — binary EventStream streaming\n *\n * bedrock-converse.ts:\n * - POST /model/{modelId}/converse — Converse API (non-streaming)\n * - POST /model/{modelId}/converse-stream — Converse API (EventStream streaming)\n *\n * Translates incoming Bedrock Claude format into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * the appropriate Bedrock response format (JSON for invoke, AWS Event Stream\n * binary encoding for streaming).\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ResponseOverrides,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateMessageId,\n generateToolUseId,\n extractOverrides,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { writeEventStream } from \"./aws-event-stream.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Bedrock Claude request types ────────────────────────────────────────────\n\ninterface BedrockContentBlock {\n type: \"text\" | \"tool_use\" | \"tool_result\" | \"image\" | \"document\";\n text?: string;\n id?: string;\n name?: string;\n input?: unknown;\n tool_use_id?: string;\n content?: string | BedrockContentBlock[];\n is_error?: boolean;\n}\n\ninterface BedrockMessage {\n role: \"user\" | \"assistant\";\n content: string | BedrockContentBlock[];\n}\n\ninterface BedrockToolDef {\n name: string;\n description?: string;\n input_schema?: object;\n}\n\ninterface BedrockRequest {\n anthropic_version?: string;\n messages: BedrockMessage[];\n system?: string | BedrockContentBlock[];\n tools?: BedrockToolDef[];\n tool_choice?: unknown;\n max_tokens: number;\n temperature?: number;\n [key: string]: unknown;\n}\n\n// ─── Bedrock stop_reason mapping ───────────────────────────────────────────\n\nfunction bedrockStopReason(\n overrideFinishReason: string | undefined,\n defaultReason: string,\n): string {\n if (!overrideFinishReason) return defaultReason;\n if (overrideFinishReason === \"stop\") return \"end_turn\";\n if (overrideFinishReason === \"tool_calls\") return \"tool_use\";\n if (overrideFinishReason === \"length\") return \"max_tokens\";\n return overrideFinishReason;\n}\n\n/**\n * Build a Bedrock-style usage object from optional overrides.\n *\n * When no overrides are provided (the common case for mock fixtures),\n * returns all-zero token counts. This is intentional — aimock does not\n * attempt to estimate token usage from fixture content. Callers that\n * need realistic usage numbers should set `usage` in their fixture's\n * response overrides.\n */\nfunction bedrockUsage(overrides?: ResponseOverrides): {\n input_tokens: number;\n output_tokens: number;\n} {\n if (!overrides?.usage) return { input_tokens: 0, output_tokens: 0 };\n return {\n input_tokens: overrides.usage.input_tokens ?? overrides.usage.prompt_tokens ?? 0,\n output_tokens: overrides.usage.output_tokens ?? overrides.usage.completion_tokens ?? 0,\n };\n}\n\n// ─── Input conversion: Bedrock → ChatCompletionRequest ──────────────────────\n\nfunction extractTextContent(content: string | BedrockContentBlock[]): string {\n if (typeof content === \"string\") return content;\n return content\n .filter((b) => b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\");\n}\n\nexport function bedrockToCompletionRequest(\n req: BedrockRequest,\n modelId: string,\n logger?: Logger,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // system field → system message\n if (req.system) {\n const systemText =\n typeof req.system === \"string\"\n ? req.system\n : req.system\n .filter((b) => b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\");\n if (systemText) {\n messages.push({ role: \"system\", content: systemText });\n }\n }\n\n for (const msg of req.messages) {\n if (msg.role === \"user\") {\n // Check for tool_result blocks\n if (typeof msg.content !== \"string\" && Array.isArray(msg.content)) {\n // Warn about non-text content blocks that will be dropped (image, document, etc.)\n const unsupportedBlocks = msg.content.filter(\n (b) => b.type !== \"text\" && b.type !== \"tool_result\",\n );\n if (unsupportedBlocks.length > 0 && logger) {\n const types = [...new Set(unsupportedBlocks.map((b) => b.type))].join(\", \");\n logger.warn(\n `Bedrock user message contains unsupported content block types [${types}] — these will be dropped during conversion`,\n );\n }\n\n const toolResults = msg.content.filter((b) => b.type === \"tool_result\");\n const textBlocks = msg.content.filter((b) => b.type === \"text\");\n\n if (toolResults.length > 0) {\n for (const tr of toolResults) {\n const resultContent =\n typeof tr.content === \"string\"\n ? tr.content\n : Array.isArray(tr.content)\n ? tr.content\n .filter((b) => b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\")\n : \"\";\n messages.push({\n role: \"tool\",\n content: resultContent,\n tool_call_id: tr.tool_use_id,\n });\n }\n if (textBlocks.length > 0) {\n messages.push({\n role: \"user\",\n content: textBlocks.map((b) => b.text ?? \"\").join(\"\"),\n });\n }\n continue;\n }\n }\n messages.push({\n role: \"user\",\n content: extractTextContent(msg.content),\n });\n } else if (msg.role === \"assistant\") {\n if (typeof msg.content === \"string\") {\n messages.push({ role: \"assistant\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const toolUseBlocks = msg.content.filter((b) => b.type === \"tool_use\");\n const textContent = extractTextContent(msg.content);\n\n if (toolUseBlocks.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: toolUseBlocks.map((b, index) => {\n if (!b.id && logger) {\n logger.warn(\n `Bedrock assistant tool_use block at index ${index} is missing an id — using deterministic fallback \"tool_use_${index}\"`,\n );\n }\n return {\n id: b.id ?? `tool_use_${index}`,\n type: \"function\" as const,\n function: {\n name: b.name ?? \"\",\n arguments: typeof b.input === \"string\" ? b.input : JSON.stringify(b.input ?? {}),\n },\n };\n }),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent || null });\n }\n } else {\n messages.push({ role: \"assistant\", content: null });\n }\n } else {\n if (logger) {\n logger.warn(\n `Bedrock message has unexpected role \"${(msg as { role: string }).role}\" — skipping`,\n );\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.input_schema,\n },\n }));\n }\n\n return {\n model: modelId,\n messages,\n stream: false,\n temperature: req.temperature,\n max_tokens: req.max_tokens,\n tools,\n };\n}\n\n// ─── Response builders ──────────────────────────────────────────────────────\n\nfunction buildBedrockTextResponse(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): object {\n const contentBlocks: object[] = [];\n if (reasoning) {\n contentBlocks.push({ type: \"thinking\", thinking: reasoning, signature: \"\" });\n }\n contentBlocks.push({ type: \"text\", text: content });\n\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: \"assistant\",\n content: contentBlocks,\n model: overrides?.model ?? model,\n stop_reason: bedrockStopReason(overrides?.finishReason, \"end_turn\"),\n stop_sequence: null,\n usage: bedrockUsage(overrides),\n };\n}\n\nfunction buildBedrockToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: \"assistant\",\n content: toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n type: \"tool_use\",\n id: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n };\n }),\n model: overrides?.model ?? model,\n stop_reason: bedrockStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n usage: bedrockUsage(overrides),\n };\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleBedrock(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/invoke`;\n\n let bedrockReq: BedrockRequest;\n try {\n bedrockReq = JSON.parse(raw) as BedrockRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!bedrockReq.messages || !Array.isArray(bedrockReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = bedrockToCompletionRequest(bedrockReq, modelId, logger);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n logger.debug(`No fixture matched for request`);\n }\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n if (effectiveStrict) {\n const strictStatus = 503;\n const strictMessage = \"Strict mode: no fixture matched\";\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({\n error: {\n message: \"No fixture matched\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n // Bedrock Claude error format: { type: \"error\", error: { type, message } }\n // Uses ?? (nullish coalescing) intentionally — preserves explicit empty-string types from fixtures.\n const anthropicError = {\n type: \"error\",\n error: {\n type: response.error.type ?? \"api_error\",\n message: response.error.message,\n },\n };\n writeErrorResponse(res, status, JSON.stringify(anthropicError));\n return;\n }\n\n // Content + tool calls response\n if (isContentWithToolCallsResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const textBody = buildBedrockTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n overrides,\n );\n const toolBody = buildBedrockToolCallResponse(\n response.toolCalls,\n completionReq.model,\n logger,\n overrides,\n );\n // Merge: take the text response as base, append tool_use blocks, set stop_reason to tool_use\n const merged = {\n ...(textBody as Record<string, unknown>),\n content: [\n ...((textBody as Record<string, unknown>).content as object[]),\n ...((toolBody as Record<string, unknown>).content as object[]),\n ],\n stop_reason: bedrockStopReason(overrides?.finishReason, \"tool_use\"),\n };\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(merged));\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildBedrockTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const body = buildBedrockToolCallResponse(\n response.toolCalls,\n completionReq.model,\n logger,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n\n// ─── Streaming event builders ───────────────────────────────────────────────\n\nconst BEDROCK_INVOKE_STREAM_EVENT_TYPE = \"chunk\";\n\nfunction buildBedrockInvokeMessageStart(\n model: string,\n overrides?: ResponseOverrides,\n): { eventType: string; payload: object } {\n return {\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"message_start\",\n message: {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: \"assistant\",\n content: [],\n model: overrides?.model ?? model,\n stop_reason: null,\n stop_sequence: null,\n usage: bedrockUsage(overrides),\n },\n },\n };\n}\n\nfunction buildBedrockInvokeMessageDelta(stopReason: string): {\n eventType: string;\n payload: object;\n} {\n return {\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"message_delta\",\n delta: { stop_reason: stopReason, stop_sequence: null },\n usage: { output_tokens: 0 },\n },\n };\n}\n\nfunction buildBedrockInvokeMessageStop(): { eventType: string; payload: object } {\n return {\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"message_stop\" },\n };\n}\n\nfunction parseToolArgumentsForStream(toolCall: ToolCall, logger: Logger): string {\n try {\n const parsed = JSON.parse(toolCall.arguments || \"{}\");\n return JSON.stringify(parsed);\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${toolCall.name}\": ${toolCall.arguments}`,\n );\n return \"{}\";\n }\n}\n\nexport function buildBedrockStreamTextEvents(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): Array<{ eventType: string; payload: object }> {\n const events: Array<{ eventType: string; payload: object }> = [];\n\n events.push(buildBedrockInvokeMessageStart(model, overrides));\n\n // Thinking block (emitted before text when reasoning is present)\n if (reasoning) {\n const blockIndex = 0;\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n },\n });\n }\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature: \"\" },\n },\n });\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: blockIndex },\n });\n }\n\n // Text block\n const textBlockIndex = reasoning ? 1 : 0;\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: textBlockIndex,\n content_block: { type: \"text\", text: \"\" },\n },\n });\n\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: textBlockIndex,\n delta: { type: \"text_delta\", text: slice },\n },\n });\n }\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: textBlockIndex },\n });\n\n events.push(\n buildBedrockInvokeMessageDelta(bedrockStopReason(overrides?.finishReason, \"end_turn\")),\n );\n events.push(buildBedrockInvokeMessageStop());\n\n return events;\n}\n\nexport function buildBedrockStreamContentWithToolCallsEvents(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): Array<{ eventType: string; payload: object }> {\n const events: Array<{ eventType: string; payload: object }> = [];\n\n events.push(buildBedrockInvokeMessageStart(model, overrides));\n\n let blockIndex = 0;\n\n // Thinking block (emitted before text when reasoning is present)\n if (reasoning) {\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n },\n });\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n },\n });\n }\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature: \"\" },\n },\n });\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: blockIndex },\n });\n blockIndex++;\n }\n\n // Text block\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"text\", text: \"\" },\n },\n });\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"text_delta\", text: slice },\n },\n });\n }\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: blockIndex },\n });\n blockIndex++;\n\n // Tool call blocks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const toolUseId = tc.id || generateToolUseId();\n const currentBlock = blockIndex + tcIdx;\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: currentBlock,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: tc.name,\n input: {},\n },\n },\n });\n\n const argsStr = parseToolArgumentsForStream(tc, logger);\n\n for (let i = 0; i < argsStr.length; i += chunkSize) {\n const slice = argsStr.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: currentBlock,\n delta: { type: \"input_json_delta\", partial_json: slice },\n },\n });\n }\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: currentBlock },\n });\n }\n\n events.push(\n buildBedrockInvokeMessageDelta(bedrockStopReason(overrides?.finishReason, \"tool_use\")),\n );\n events.push(buildBedrockInvokeMessageStop());\n\n return events;\n}\n\nexport function buildBedrockStreamToolCallEvents(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n overrides?: ResponseOverrides,\n): Array<{ eventType: string; payload: object }> {\n const events: Array<{ eventType: string; payload: object }> = [];\n\n events.push(buildBedrockInvokeMessageStart(model, overrides));\n\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const toolUseId = tc.id || generateToolUseId();\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_start\",\n index: tcIdx,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: tc.name,\n input: {},\n },\n },\n });\n\n const argsStr = parseToolArgumentsForStream(tc, logger);\n\n for (let i = 0; i < argsStr.length; i += chunkSize) {\n const slice = argsStr.slice(i, i + chunkSize);\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: {\n type: \"content_block_delta\",\n index: tcIdx,\n delta: { type: \"input_json_delta\", partial_json: slice },\n },\n });\n }\n\n events.push({\n eventType: BEDROCK_INVOKE_STREAM_EVENT_TYPE,\n payload: { type: \"content_block_stop\", index: tcIdx },\n });\n }\n\n events.push(\n buildBedrockInvokeMessageDelta(bedrockStopReason(overrides?.finishReason, \"tool_use\")),\n );\n events.push(buildBedrockInvokeMessageStop());\n\n return events;\n}\n\n// ─── Streaming request handler ──────────────────────────────────────────────\n\nexport async function handleBedrockStream(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n modelId: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? `/model/${modelId}/invoke-with-response-stream`;\n\n let bedrockReq: BedrockRequest;\n try {\n bedrockReq = JSON.parse(raw) as BedrockRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!bedrockReq.messages || !Array.isArray(bedrockReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const completionReq = bedrockToCompletionRequest(bedrockReq, modelId, logger);\n completionReq.stream = true;\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n logger.debug(`No fixture matched for request`);\n }\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n if (effectiveStrict) {\n const strictStatus = 503;\n const strictMessage = \"Strict mode: no fixture matched\";\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"bedrock\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({\n error: {\n message: \"No fixture matched\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n // Bedrock Claude error format: { type: \"error\", error: { type, message } }\n // Uses ?? (nullish coalescing) intentionally — preserves explicit empty-string types from fixtures.\n const anthropicError = {\n type: \"error\",\n error: {\n type: response.error.type ?? \"api_error\",\n message: response.error.message,\n },\n };\n writeErrorResponse(res, status, JSON.stringify(anthropicError));\n return;\n }\n\n // Content + tool calls response — stream as Event Stream\n if (isContentWithToolCallsResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamContentWithToolCallsEvents(\n response.content,\n response.toolCalls,\n completionReq.model,\n chunkSize,\n logger,\n response.reasoning,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Text response — stream as Event Stream\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamTextEvents(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response — stream as Event Stream\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Bedrock API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildBedrockStreamToolCallEvents(\n response.toolCalls,\n completionReq.model,\n chunkSize,\n logger,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeEventStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
package/dist/cli.cjs CHANGED
@@ -23,10 +23,11 @@ Options:
23
23
  -l, --latency <ms> Latency in ms between SSE chunks (default: 0)
24
24
  -c, --chunk-size <chars> Chunk size in characters (default: 20)
25
25
  -w, --watch Watch fixture path for changes and reload
26
- --log-level <level> Log verbosity: silent, info, debug (default: info)
26
+ --log-level <level> Log verbosity: silent, warn, info, debug (default: info)
27
27
  --validate-on-load Validate fixture schemas at startup
28
28
  --metrics Enable Prometheus metrics at GET /metrics
29
29
  --record Record mode: proxy unmatched requests and save fixtures
30
+ --record-full-model-version Record exact model version without date stripping (default: false)
30
31
  --proxy-only Proxy mode: forward unmatched requests without saving
31
32
  --strict Strict mode: fail on unmatched requests (overridable per-request via X-AIMock-Strict header)
32
33
  --journal-max <n> Max request entries retained in memory (default: 1000, 0 = unbounded)
@@ -95,6 +96,10 @@ const { values } = (0, node_util.parseArgs)({
95
96
  type: "boolean",
96
97
  default: false
97
98
  },
99
+ "record-full-model-version": {
100
+ type: "boolean",
101
+ default: false
102
+ },
98
103
  "proxy-only": {
99
104
  type: "boolean",
100
105
  default: false
@@ -241,7 +246,8 @@ if (values.record || values["proxy-only"]) {
241
246
  record = {
242
247
  providers,
243
248
  fixturePath: recordBaseIsUrl ? void 0 : (0, node_path.resolve)(recordBase, "recorded"),
244
- proxyOnly: values["proxy-only"]
249
+ proxyOnly: values["proxy-only"],
250
+ recordFullModelVersion: values["record-full-model-version"]
245
251
  };
246
252
  }
247
253
  let aguiMount;
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, 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 --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 --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 --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 \"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 \"agui-record\": { type: \"boolean\", default: false },\n \"agui-upstream\": { type: \"string\" },\n \"agui-proxy-only\": { type: \"boolean\", default: false },\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 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 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 };\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 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 --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 --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 \"agui-record\": { type: \"boolean\", default: false },\n \"agui-upstream\": { type: \"string\" },\n \"agui-proxy-only\": { type: \"boolean\", default: false },\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 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 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 };\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 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
@@ -22,10 +22,11 @@ Options:
22
22
  -l, --latency <ms> Latency in ms between SSE chunks (default: 0)
23
23
  -c, --chunk-size <chars> Chunk size in characters (default: 20)
24
24
  -w, --watch Watch fixture path for changes and reload
25
- --log-level <level> Log verbosity: silent, info, debug (default: info)
25
+ --log-level <level> Log verbosity: silent, warn, info, debug (default: info)
26
26
  --validate-on-load Validate fixture schemas at startup
27
27
  --metrics Enable Prometheus metrics at GET /metrics
28
28
  --record Record mode: proxy unmatched requests and save fixtures
29
+ --record-full-model-version Record exact model version without date stripping (default: false)
29
30
  --proxy-only Proxy mode: forward unmatched requests without saving
30
31
  --strict Strict mode: fail on unmatched requests (overridable per-request via X-AIMock-Strict header)
31
32
  --journal-max <n> Max request entries retained in memory (default: 1000, 0 = unbounded)
@@ -94,6 +95,10 @@ const { values } = parseArgs({
94
95
  type: "boolean",
95
96
  default: false
96
97
  },
98
+ "record-full-model-version": {
99
+ type: "boolean",
100
+ default: false
101
+ },
97
102
  "proxy-only": {
98
103
  type: "boolean",
99
104
  default: false
@@ -240,7 +245,8 @@ if (values.record || values["proxy-only"]) {
240
245
  record = {
241
246
  providers,
242
247
  fixturePath: recordBaseIsUrl ? void 0 : resolve(recordBase, "recorded"),
243
- proxyOnly: values["proxy-only"]
248
+ proxyOnly: values["proxy-only"],
249
+ recordFullModelVersion: values["record-full-model-version"]
244
250
  };
245
251
  }
246
252
  let aguiMount;
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, 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 --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 --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 --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 \"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 \"agui-record\": { type: \"boolean\", default: false },\n \"agui-upstream\": { type: \"string\" },\n \"agui-proxy-only\": { type: \"boolean\", default: false },\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 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 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 };\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 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.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 --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 --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 \"agui-record\": { type: \"boolean\", default: false },\n \"agui-upstream\": { type: \"string\" },\n \"agui-proxy-only\": { type: \"boolean\", default: false },\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 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 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 };\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 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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"}