{"version":3,"file":"config-loader.cjs","names":["fs","Logger","LLMock","path","MCPMock","A2AMock","AGUIMock","buildTextResponse","VectorMock"],"sources":["../src/config-loader.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { LLMock } from \"./llmock.js\";\nimport { MCPMock } from \"./mcp-mock.js\";\nimport { A2AMock } from \"./a2a-mock.js\";\nimport { AGUIMock } from \"./agui-mock.js\";\nimport type { ChaosConfig, RecordConfig } from \"./types.js\";\nimport type { MCPToolDefinition, MCPPromptDefinition } from \"./mcp-types.js\";\nimport type { A2AAgentDefinition, A2APart, A2AArtifact, A2AStreamEvent } from \"./a2a-types.js\";\nimport type { AGUIEvent } from \"./agui-types.js\";\nimport { buildTextResponse } from \"./agui-handler.js\";\nimport { VectorMock } from \"./vector-mock.js\";\nimport type { QueryResult } from \"./vector-types.js\";\nimport { Logger } from \"./logger.js\";\n\nexport interface MCPConfigTool extends MCPToolDefinition {\n  result?: string;\n}\n\nexport interface MCPConfigResource {\n  uri: string;\n  name: string;\n  mimeType?: string;\n  description?: string;\n  text?: string;\n  blob?: string;\n}\n\nexport interface MCPConfigPrompt extends MCPPromptDefinition {\n  result?: {\n    messages: Array<{ role: string; content: { type: string; text: string } }>;\n  };\n}\n\nexport interface MCPConfig {\n  path?: string;\n  serverInfo?: { name: string; version: string };\n  tools?: MCPConfigTool[];\n  resources?: MCPConfigResource[];\n  prompts?: MCPConfigPrompt[];\n}\n\nexport interface A2AConfigPattern {\n  pattern: string;\n  parts?: A2APart[];\n  artifacts?: A2AArtifact[];\n  events?: A2AStreamEvent[];\n  delayMs?: number;\n}\n\nexport interface A2AConfigAgent extends A2AAgentDefinition {\n  messages?: A2AConfigPattern[];\n  tasks?: A2AConfigPattern[];\n  streamingTasks?: A2AConfigPattern[];\n}\n\nexport interface A2AConfig {\n  path?: string;\n  agents?: A2AConfigAgent[];\n}\n\nexport interface AGUIConfigFixture {\n  match: { message?: string; toolCallId?: string; toolName?: string; stateKey?: string };\n  text?: string; // shorthand: uses buildTextResponse\n  events?: AGUIEvent[]; // raw events\n  delayMs?: number;\n}\n\nexport interface AGUIConfig {\n  path?: string; // mount path, default \"/agui\"\n  fixtures?: AGUIConfigFixture[];\n}\n\nexport interface VectorConfigCollection {\n  name: string;\n  dimension: number;\n  vectors?: Array<{\n    id: string;\n    values: number[];\n    metadata?: Record<string, unknown>;\n  }>;\n  queryResults?: QueryResult[];\n}\n\nexport interface VectorConfig {\n  path?: string;\n  collections?: VectorConfigCollection[];\n}\n\nexport interface AimockConfig {\n  llm?: {\n    fixtures?: string;\n    chaos?: ChaosConfig;\n    record?: RecordConfig;\n  };\n  mcp?: MCPConfig;\n  a2a?: A2AConfig;\n  agui?: AGUIConfig;\n  vector?: VectorConfig;\n  services?: { search?: boolean; rerank?: boolean; moderate?: boolean };\n  metrics?: boolean;\n  strict?: boolean;\n  port?: number;\n  host?: string;\n}\n\nexport function loadConfig(configPath: string): AimockConfig {\n  const raw = fs.readFileSync(configPath, \"utf-8\");\n  return JSON.parse(raw) as AimockConfig;\n}\n\nexport async function startFromConfig(\n  config: AimockConfig,\n  overrides?: { port?: number; host?: string },\n): Promise<{ llmock: LLMock; url: string }> {\n  const logger = new Logger(\"info\");\n\n  // Load fixtures if specified\n  const llmock = new LLMock({\n    port: overrides?.port ?? config.port ?? 0,\n    host: overrides?.host ?? config.host ?? \"127.0.0.1\",\n    chaos: config.llm?.chaos,\n    record: config.llm?.record,\n    metrics: config.metrics,\n    strict: config.strict,\n  });\n\n  if (config.llm?.fixtures) {\n    const fixturePath = path.resolve(config.llm.fixtures);\n    const stat = fs.statSync(fixturePath);\n    if (stat.isDirectory()) {\n      llmock.loadFixtureDir(fixturePath);\n    } else {\n      llmock.loadFixtureFile(fixturePath);\n    }\n  }\n\n  // MCP\n  if (config.mcp) {\n    const mcpConfig = config.mcp;\n    const mcp = new MCPMock({\n      serverInfo: mcpConfig.serverInfo,\n    });\n\n    if (mcpConfig.tools) {\n      for (const tool of mcpConfig.tools) {\n        const { result, ...def } = tool;\n        mcp.addTool(def);\n        if (result !== undefined) {\n          mcp.onToolCall(def.name, () => result);\n        }\n      }\n    }\n\n    if (mcpConfig.resources) {\n      for (const res of mcpConfig.resources) {\n        mcp.addResource(\n          { uri: res.uri, name: res.name, mimeType: res.mimeType, description: res.description },\n          res.text !== undefined || res.blob !== undefined\n            ? { text: res.text, blob: res.blob, mimeType: res.mimeType }\n            : undefined,\n        );\n      }\n    }\n\n    if (mcpConfig.prompts) {\n      for (const prompt of mcpConfig.prompts) {\n        const { result, ...def } = prompt;\n        if (result) {\n          mcp.addPrompt(def, () => result as import(\"./mcp-types.js\").MCPPromptResult);\n        } else {\n          mcp.addPrompt(def);\n        }\n      }\n    }\n\n    const mcpPath = mcpConfig.path ?? \"/mcp\";\n    llmock.mount(mcpPath, mcp);\n    logger.info(`MCPMock mounted at ${mcpPath}`);\n  }\n\n  // A2A\n  if (config.a2a) {\n    const a2aConfig = config.a2a;\n    const a2a = new A2AMock();\n\n    if (a2aConfig.agents) {\n      for (const agentConfig of a2aConfig.agents) {\n        const { messages, tasks, streamingTasks, ...def } = agentConfig;\n        a2a.registerAgent(def);\n\n        if (messages) {\n          for (const m of messages) {\n            a2a.onMessage(def.name, m.pattern, m.parts ?? [{ text: \"\" }]);\n          }\n        }\n\n        if (tasks) {\n          for (const t of tasks) {\n            a2a.onTask(def.name, t.pattern, t.artifacts ?? []);\n          }\n        }\n\n        if (streamingTasks) {\n          for (const s of streamingTasks) {\n            a2a.onStreamingTask(def.name, s.pattern, s.events ?? [], s.delayMs);\n          }\n        }\n      }\n    }\n\n    const a2aPath = a2aConfig.path ?? \"/a2a\";\n    llmock.mount(a2aPath, a2a);\n    logger.info(`A2AMock mounted at ${a2aPath}`);\n  }\n\n  // AG-UI\n  if (config.agui) {\n    const aguiConfig = config.agui;\n    const agui = new AGUIMock();\n\n    if (aguiConfig.fixtures) {\n      for (const f of aguiConfig.fixtures) {\n        if (f.match.toolCallId && f.text && !f.events) {\n          logger.warn(\n            `AG-UI fixture uses text shorthand with toolCallId — text shorthand ignores toolCallId matching; use events[] instead (match: ${JSON.stringify(f.match)})`,\n          );\n        }\n        if (f.text) {\n          if (f.match.message !== undefined) {\n            agui.onMessage(f.match.message, f.text, { delayMs: f.delayMs });\n          } else {\n            // No message pattern — register via addFixture so it only matches\n            // on other criteria (toolCallId, toolName, stateKey) instead of\n            // becoming a catch-all that matches every request.\n            agui.addFixture({\n              match: {\n                toolCallId: f.match.toolCallId,\n                toolName: f.match.toolName,\n                stateKey: f.match.stateKey,\n              },\n              events: buildTextResponse(f.text),\n              delayMs: f.delayMs,\n            });\n          }\n        } else if (f.events) {\n          agui.addFixture({\n            match: {\n              message: f.match.message,\n              toolCallId: f.match.toolCallId,\n              toolName: f.match.toolName,\n              stateKey: f.match.stateKey,\n            },\n            events: f.events,\n            delayMs: f.delayMs,\n          });\n        } else {\n          logger.warn(\n            `AG-UI fixture has neither text nor events — it will be skipped (match: ${JSON.stringify(f.match)})`,\n          );\n        }\n      }\n    }\n\n    const aguiPath = aguiConfig.path ?? \"/agui\";\n    llmock.mount(aguiPath, agui);\n    logger.info(`AGUIMock mounted at ${aguiPath}`);\n  }\n\n  // Vector\n  if (config.vector) {\n    const vectorConfig = config.vector;\n    const vector = new VectorMock();\n\n    if (vectorConfig.collections) {\n      for (const col of vectorConfig.collections) {\n        vector.addCollection(col.name, { dimension: col.dimension });\n\n        if (col.vectors && col.vectors.length > 0) {\n          vector.upsert(col.name, col.vectors);\n        }\n\n        if (col.queryResults) {\n          vector.onQuery(col.name, col.queryResults);\n        }\n      }\n    }\n\n    const vectorPath = vectorConfig.path ?? \"/vector\";\n    llmock.mount(vectorPath, vector);\n    logger.info(`VectorMock mounted at ${vectorPath}`);\n  }\n\n  // Services — configure default catch-all responses\n  if (config.services) {\n    if (config.services.search) {\n      llmock.onSearch(/.*/, []);\n      logger.info(\"Search service enabled with default empty results\");\n    }\n    if (config.services.rerank) {\n      llmock.onRerank(/.*/, []);\n      logger.info(\"Rerank service enabled with default empty results\");\n    }\n    if (config.services.moderate) {\n      llmock.onModerate(/.*/, { flagged: false, categories: {} });\n      logger.info(\"Moderation service enabled with default unflagged results\");\n    }\n  }\n\n  const url = await llmock.start();\n  return { llmock, url };\n}\n"],"mappings":"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"}