@copilotkit/aimock 1.41.0 → 1.42.1
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +18 -1
- package/README.md +2 -1
- package/dist/a2a-mock.cjs +18 -11
- package/dist/a2a-mock.cjs.map +1 -1
- package/dist/a2a-mock.d.cts +1 -0
- package/dist/a2a-mock.d.cts.map +1 -1
- package/dist/a2a-mock.d.ts +1 -0
- package/dist/a2a-mock.d.ts.map +1 -1
- package/dist/a2a-mock.js +18 -11
- package/dist/a2a-mock.js.map +1 -1
- package/dist/agui-handler.d.ts.map +1 -1
- package/dist/agui-mock.cjs +2 -0
- package/dist/agui-mock.cjs.map +1 -1
- package/dist/agui-mock.d.cts.map +1 -1
- package/dist/agui-mock.d.ts.map +1 -1
- package/dist/agui-mock.js +2 -0
- package/dist/agui-mock.js.map +1 -1
- package/dist/bedrock-converse.cjs +36 -2
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +37 -3
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +50 -13
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +51 -14
- package/dist/bedrock.js.map +1 -1
- package/dist/byteplus-video.cjs +2 -2
- package/dist/byteplus-video.cjs.map +1 -1
- package/dist/byteplus-video.d.cts.map +1 -1
- package/dist/byteplus-video.d.ts.map +1 -1
- package/dist/byteplus-video.js +2 -2
- package/dist/byteplus-video.js.map +1 -1
- package/dist/chaos.cjs +36 -7
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts +8 -3
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts +8 -3
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +36 -8
- package/dist/chaos.js.map +1 -1
- package/dist/cohere.cjs +58 -5
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +59 -6
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/elevenlabs-audio.cjs +36 -2
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.d.cts.map +1 -1
- package/dist/elevenlabs-audio.d.ts.map +1 -1
- package/dist/elevenlabs-audio.js +37 -3
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/embeddings.cjs +58 -3
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +59 -4
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +38 -2
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.d.cts.map +1 -1
- package/dist/fal-audio.d.ts.map +1 -1
- package/dist/fal-audio.js +39 -3
- package/dist/fal-audio.js.map +1 -1
- package/dist/fixture-loader.cjs +0 -0
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +9 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +9 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +0 -0
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +19 -1
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +20 -2
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +15 -1
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +16 -2
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +70 -6
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +71 -7
- package/dist/gemini.js.map +1 -1
- package/dist/grok-video.cjs +2 -2
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.d.cts.map +1 -1
- package/dist/grok-video.d.ts.map +1 -1
- package/dist/grok-video.js +2 -2
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +168 -7
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +160 -8
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +22 -4
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +23 -5
- package/dist/images.js.map +1 -1
- package/dist/llmock.cjs +15 -0
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +12 -0
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +12 -0
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +16 -1
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-mock.cjs +11 -0
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +11 -0
- package/dist/mcp-mock.js.map +1 -1
- package/dist/messages.cjs +18 -1
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +19 -2
- package/dist/messages.js.map +1 -1
- package/dist/moderation.cjs +67 -3
- package/dist/moderation.cjs.map +1 -1
- package/dist/moderation.d.cts +1 -0
- package/dist/moderation.d.cts.map +1 -1
- package/dist/moderation.d.ts +1 -0
- package/dist/moderation.d.ts.map +1 -1
- package/dist/moderation.js +68 -4
- package/dist/moderation.js.map +1 -1
- package/dist/ollama.cjs +75 -5
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +76 -6
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-video.cjs +4 -4
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.d.cts.map +1 -1
- package/dist/openrouter-video.d.ts.map +1 -1
- package/dist/openrouter-video.js +4 -4
- package/dist/openrouter-video.js.map +1 -1
- package/dist/rerank.cjs +65 -2
- package/dist/rerank.cjs.map +1 -1
- package/dist/rerank.d.cts +1 -1
- package/dist/rerank.d.cts.map +1 -1
- package/dist/rerank.d.ts +1 -1
- package/dist/rerank.d.ts.map +1 -1
- package/dist/rerank.js +66 -3
- package/dist/rerank.js.map +1 -1
- package/dist/responses.cjs +18 -1
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +19 -2
- package/dist/responses.js.map +1 -1
- package/dist/search.cjs +67 -3
- package/dist/search.cjs.map +1 -1
- package/dist/search.d.cts +1 -0
- package/dist/search.d.cts.map +1 -1
- package/dist/search.d.ts +1 -0
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +68 -4
- package/dist/search.js.map +1 -1
- package/dist/server.cjs +317 -13
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +320 -16
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +18 -1
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.d.cts.map +1 -1
- package/dist/speech.d.ts.map +1 -1
- package/dist/speech.js +19 -2
- package/dist/speech.js.map +1 -1
- package/dist/transcription.cjs +21 -2
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +21 -2
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +39 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +39 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-mock.cjs +88 -0
- package/dist/vector-mock.cjs.map +1 -1
- package/dist/vector-mock.d.cts.map +1 -1
- package/dist/vector-mock.d.ts.map +1 -1
- package/dist/vector-mock.js +89 -1
- package/dist/vector-mock.js.map +1 -1
- package/dist/vector-types.d.cts.map +1 -1
- package/dist/vector-types.d.ts.map +1 -1
- package/dist/veo-video.cjs +2 -2
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.d.cts.map +1 -1
- package/dist/veo-video.d.ts.map +1 -1
- package/dist/veo-video.js +2 -2
- package/dist/veo-video.js.map +1 -1
- package/dist/video.cjs +19 -2
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +20 -3
- package/dist/video.js.map +1 -1
- package/package.json +1 -1
package/dist/search.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","names":[],"sources":["../src/search.ts"],"sourcesContent":["/**\n * Web Search API support for LLMock.\n *\n * Handles POST /search requests (Tavily-compatible). Matches fixtures by\n * comparing the request `query` field against registered patterns. First\n * match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport {
|
|
1
|
+
{"version":3,"file":"search.js","names":[],"sources":["../src/search.ts"],"sourcesContent":["/**\n * Web Search API support for LLMock.\n *\n * Handles POST /search requests (Tavily-compatible). Matches fixtures by\n * comparing the request `query` field against registered patterns. First\n * match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport {\n flattenHeaders,\n isJsonObject,\n matchesPattern,\n normalizeTextInput,\n resolveStrictMode,\n strictNoMatchLogLine,\n strictNoMatchMessage,\n strictOverrideField,\n} from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ─── Search types ─────────────────────────────────────────────────────────\n\nexport interface SearchResult {\n title: string;\n url: string;\n content: string;\n score?: number;\n}\n\nexport interface SearchFixture {\n match: string | RegExp;\n results: SearchResult[];\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nexport async function handleSearch(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: SearchFixture[],\n journal: Journal,\n defaults: { logger: Logger; strict?: boolean },\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let body: { query?: unknown; max_results?: number };\n try {\n body = JSON.parse(raw) as { query?: unknown; max_results?: number };\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`) before\n // touching fields — otherwise `body.query` throws a TypeError that surfaces\n // as a 500 instead of a 400.\n if (!isJsonObject(body)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Request body must be a JSON object\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const rawQuery: unknown = body.query ?? \"\";\n const normalizedQuery = normalizeTextInput(rawQuery);\n if (normalizedQuery === null) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Invalid parameter: 'query' must be a string or an array of strings\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n // `query` is the normalized string used for matching; the response echoes\n // `rawQuery` so an array payload round-trips exactly as it did before.\n const query = normalizedQuery;\n const maxResults = body.max_results;\n\n // Find first matching fixture\n let matchedResults: SearchResult[] = [];\n let matchedFixture: SearchFixture | null = null;\n\n for (const fixture of fixtures) {\n if (matchesPattern(query, fixture.match)) {\n matchedFixture = fixture;\n matchedResults = fixture.results;\n break;\n }\n }\n\n if (matchedFixture) {\n logger.debug(`Search fixture matched for query \"${query.slice(0, 80)}\"`);\n } else {\n logger.debug(`No search fixture matched for query \"${query.slice(0, 80)}\" — returning empty`);\n }\n\n // Strict mode turns the lenient default (empty results) into a no-match\n // error, like every other fixture-matching handler. These fixtures carry no\n // sequence/turn state, so the skipped count is always 0.\n if (!matchedFixture && resolveStrictMode(defaults.strict, req.headers)) {\n const strictMessage = strictNoMatchMessage(0);\n logger.error(strictNoMatchLogLine(req.method ?? \"POST\", req.url ?? \"/search\", 0));\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n res.writeHead(503, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n // Apply max_results limit\n if (maxResults !== undefined && maxResults > 0) {\n matchedResults = matchedResults.slice(0, maxResults);\n }\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: {\n status: 200,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n query: rawQuery,\n results: matchedResults,\n images: [],\n response_time: 0,\n answer: null,\n }),\n );\n}\n"],"mappings":"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"}
|
package/dist/server.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
|
+
const require_constants = require('./constants.cjs');
|
|
2
3
|
const require_api_key_auth = require('./api-key-auth.cjs');
|
|
3
4
|
const require_helpers = require('./helpers.cjs');
|
|
4
5
|
const require_logger = require('./logger.cjs');
|
|
@@ -127,7 +128,8 @@ const DEFAULT_MODELS = [
|
|
|
127
128
|
const CORS_HEADERS = {
|
|
128
129
|
"Access-Control-Allow-Origin": "*",
|
|
129
130
|
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
130
|
-
"Access-Control-Allow-Headers": "*"
|
|
131
|
+
"Access-Control-Allow-Headers": "*",
|
|
132
|
+
"Access-Control-Expose-Headers": "X-Total-Count"
|
|
131
133
|
};
|
|
132
134
|
function setCorsHeaders(res) {
|
|
133
135
|
for (const [key, value] of Object.entries(CORS_HEADERS)) res.setHeader(key, value);
|
|
@@ -145,11 +147,24 @@ function handleNotFound(res, message) {
|
|
|
145
147
|
} }));
|
|
146
148
|
}
|
|
147
149
|
const CONTROL_PREFIX = "/__aimock";
|
|
150
|
+
/** The complete `GET /__aimock/fixtures` query-param vocabulary; anything else 400s. */
|
|
151
|
+
const FIXTURES_PARAMS = new Set(["include"]);
|
|
152
|
+
/** The complete `GET /__aimock/journal` query-param vocabulary; anything else 400s. */
|
|
153
|
+
const JOURNAL_PARAMS = new Set([
|
|
154
|
+
"limit",
|
|
155
|
+
"offset",
|
|
156
|
+
"path",
|
|
157
|
+
"method",
|
|
158
|
+
"status",
|
|
159
|
+
"service",
|
|
160
|
+
"testId"
|
|
161
|
+
]);
|
|
148
162
|
/**
|
|
149
163
|
* Perform a full reset: clear the fixtures array, the journal (entries *and*
|
|
150
164
|
* per-test fixture match-counts, i.e. sequence position), the video and fal.ai
|
|
151
|
-
* job/queue state,
|
|
152
|
-
* the
|
|
165
|
+
* job/queue state, the Gemini interaction/event-id counters, and any runtime
|
|
166
|
+
* chaos override (reverting to the construction-time chaos config), then
|
|
167
|
+
* re-zero the `aimock_fixtures_loaded` gauge.
|
|
153
168
|
*
|
|
154
169
|
* `targets` is `null` when no server is running (an in-process `reset()` before
|
|
155
170
|
* `start()`). The process-global generation state is reset either way, since it
|
|
@@ -166,6 +181,7 @@ function performFullReset(fixtures, targets) {
|
|
|
166
181
|
require_gemini_interactions.resetEventIdCounter();
|
|
167
182
|
if (!targets) return;
|
|
168
183
|
targets.journal.clear();
|
|
184
|
+
targets.defaults.chaos = void 0;
|
|
169
185
|
targets.videoStates.clear();
|
|
170
186
|
targets.openRouterVideoJobs.clear();
|
|
171
187
|
targets.veoVideoJobs.clear();
|
|
@@ -174,10 +190,103 @@ function performFullReset(fixtures, targets) {
|
|
|
174
190
|
if (targets.defaults.registry) targets.defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
175
191
|
}
|
|
176
192
|
/**
|
|
193
|
+
* JSON-safe redaction for control-API inspection output. Functions (fixture
|
|
194
|
+
* `predicate`s, response factories) and RegExps do not survive
|
|
195
|
+
* JSON.stringify as anything useful, so they become marker strings.
|
|
196
|
+
*/
|
|
197
|
+
function redactForInspection(value) {
|
|
198
|
+
if (typeof value === "function") return "[function]";
|
|
199
|
+
if (value instanceof RegExp) return String(value);
|
|
200
|
+
if (Array.isArray(value)) return value.map(redactForInspection);
|
|
201
|
+
if (value !== null && typeof value === "object") {
|
|
202
|
+
const out = {};
|
|
203
|
+
for (const [key, entry] of Object.entries(value)) out[key] = redactForInspection(entry);
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* One-line kind label for a fixture response used by
|
|
210
|
+
* `GET /__aimock/fixtures?include=fixtures` (e.g. "text", "error").
|
|
211
|
+
* Response factories are reported as "factory".
|
|
212
|
+
*
|
|
213
|
+
* Discriminates with the same `is*Response` guards the request handlers use,
|
|
214
|
+
* in the same order — NOT by inspecting keys. Every response shape extends
|
|
215
|
+
* `ResponseOverrides` (`id`/`created`/`model`/`usage`), so a recorded fixture's
|
|
216
|
+
* first key is usually an envelope override rather than the discriminant. The
|
|
217
|
+
* audio-before-content/toolCalls order is the ORDERING CONTRACT documented on
|
|
218
|
+
* `AudioResponse` in types.ts: those shapes structurally overlap.
|
|
219
|
+
*/
|
|
220
|
+
function fixtureResponseKind(response) {
|
|
221
|
+
if (require_helpers.isResponseFactory(response)) return "factory";
|
|
222
|
+
if (require_helpers.isErrorResponse(response)) return "error";
|
|
223
|
+
if (require_helpers.isAudioResponse(response)) return "audio";
|
|
224
|
+
if (require_helpers.isVideoResponse(response)) return "video";
|
|
225
|
+
if (require_helpers.isTranscriptionResponse(response)) return "transcription";
|
|
226
|
+
if (require_helpers.isImageResponse(response)) return "image";
|
|
227
|
+
if (require_helpers.isEmbeddingResponse(response)) return "embedding";
|
|
228
|
+
if (require_helpers.isJSONResponse(response)) return "json";
|
|
229
|
+
if (require_helpers.isContentWithToolCallsResponse(response)) return "contentWithToolCalls";
|
|
230
|
+
if (require_helpers.isToolCallResponse(response)) return "toolCalls";
|
|
231
|
+
if (require_helpers.isTextResponse(response)) return "text";
|
|
232
|
+
return "unknown";
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Resolve a journal entry's testId exactly the way `getTestId` resolves an
|
|
236
|
+
* incoming request's: the `X-Test-Id` header wins, then `?testId=` parsed out
|
|
237
|
+
* of the query string, then `DEFAULT_TEST_ID`. The
|
|
238
|
+
* filter and the server's own per-test isolation must never disagree — and a
|
|
239
|
+
* raw `path.includes("testId=t1")` both prefix-collides with `t10` and matches
|
|
240
|
+
* unrelated params like `notTestId`.
|
|
241
|
+
*/
|
|
242
|
+
function journalEntryTestId(entry) {
|
|
243
|
+
const header = entry.headers["x-test-id"];
|
|
244
|
+
if (header) return header;
|
|
245
|
+
const qIdx = entry.path.indexOf("?");
|
|
246
|
+
if (qIdx !== -1) {
|
|
247
|
+
const queryValue = new URLSearchParams(entry.path.slice(qIdx + 1)).get("testId");
|
|
248
|
+
if (queryValue) return queryValue;
|
|
249
|
+
}
|
|
250
|
+
return require_constants.DEFAULT_TEST_ID;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The testId a chaos override is stored under, and read back with — resolved by
|
|
254
|
+
* `getTestId`, i.e. `X-Test-Id` header then `?testId=`, exactly as
|
|
255
|
+
* `resolveScopedDefaults` resolves the traffic being evaluated. A control call
|
|
256
|
+
* and the requests it is meant to affect can then never land in different
|
|
257
|
+
* scopes. `DEFAULT_TEST_ID` (neither header nor param) means the server-wide
|
|
258
|
+
* baseline.
|
|
259
|
+
*
|
|
260
|
+
* Returns `null` when `X-Test-Id` is PRESENT but blank. `String(testId ?? "")`
|
|
261
|
+
* in a harness produces that trivially, and silently treating it as "untagged"
|
|
262
|
+
* would install a server-wide baseline that fails every other test — the exact
|
|
263
|
+
* cross-test leak per-testId scoping exists to prevent. Callers must 400.
|
|
264
|
+
*/
|
|
265
|
+
function chaosScopeId(req) {
|
|
266
|
+
const headerValue = req.headers["x-test-id"];
|
|
267
|
+
const raw = Array.isArray(headerValue) ? headerValue[0] : headerValue;
|
|
268
|
+
if (typeof raw === "string" && raw.trim() === "") return null;
|
|
269
|
+
return require_helpers.getTestId(req);
|
|
270
|
+
}
|
|
271
|
+
/** 400 for a control call whose `X-Test-Id` is present but blank. */
|
|
272
|
+
function writeBlankTestId(res) {
|
|
273
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
274
|
+
res.end(JSON.stringify({ error: "Invalid 'X-Test-Id': header is present but empty" }));
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
/** The chaos config in effect for one testId: its override, else the baseline. */
|
|
278
|
+
function effectiveChaos(defaults, scopeId) {
|
|
279
|
+
const scoped = defaults.chaosByTestId?.get(scopeId);
|
|
280
|
+
if (scoped) return scoped;
|
|
281
|
+
const current = defaults.chaos;
|
|
282
|
+
if (!current) return {};
|
|
283
|
+
return require_chaos.isChaosScope(current) ? current.base ?? {} : current;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
177
286
|
* Handle requests under `/__aimock/`. Returns `true` if the request was
|
|
178
287
|
* handled, `false` if the path doesn't match the control prefix.
|
|
179
288
|
*/
|
|
180
|
-
async function handleControlAPI(req, res, pathname, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults) {
|
|
289
|
+
async function handleControlAPI(req, res, pathname, searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults) {
|
|
181
290
|
if (!pathname.startsWith(CONTROL_PREFIX)) return false;
|
|
182
291
|
const subPath = pathname.slice(9);
|
|
183
292
|
setCorsHeaders(res);
|
|
@@ -187,13 +296,148 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
|
|
|
187
296
|
return true;
|
|
188
297
|
}
|
|
189
298
|
if (subPath === "/journal" && req.method === "GET") {
|
|
190
|
-
|
|
191
|
-
|
|
299
|
+
const bad = (message) => {
|
|
300
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
301
|
+
res.end(JSON.stringify({ error: message }));
|
|
302
|
+
return true;
|
|
303
|
+
};
|
|
304
|
+
for (const key of searchParams.keys()) if (!JOURNAL_PARAMS.has(key)) return bad(`Unknown query parameter: '${key}'. Supported: ${[...JOURNAL_PARAMS].join(", ")}`);
|
|
305
|
+
const parseNonNegativeInt = (name, fallback) => {
|
|
306
|
+
const raw = searchParams.get(name);
|
|
307
|
+
if (raw === null) return fallback;
|
|
308
|
+
if (!/^\d+$/.test(raw.trim())) return null;
|
|
309
|
+
return Number(raw);
|
|
310
|
+
};
|
|
311
|
+
const limit = parseNonNegativeInt("limit", -1);
|
|
312
|
+
const offset = parseNonNegativeInt("offset", 0);
|
|
313
|
+
if (limit === null) return bad("Invalid 'limit': must be an integer >= 0");
|
|
314
|
+
if (offset === null) return bad("Invalid 'offset': must be an integer >= 0");
|
|
315
|
+
let statusFilter;
|
|
316
|
+
const statusRaw = searchParams.get("status");
|
|
317
|
+
if (statusRaw !== null) {
|
|
318
|
+
if (!/^\d+$/.test(statusRaw.trim())) return bad("Invalid 'status': must be an integer");
|
|
319
|
+
statusFilter = Number(statusRaw);
|
|
320
|
+
}
|
|
321
|
+
const pathFilter = searchParams.get("path");
|
|
322
|
+
const methodFilter = searchParams.get("method");
|
|
323
|
+
const serviceFilter = searchParams.get("service");
|
|
324
|
+
const testIdFilter = searchParams.get("testId");
|
|
325
|
+
let entries = journal.getAll();
|
|
326
|
+
if (methodFilter !== null) {
|
|
327
|
+
const want = methodFilter.toUpperCase();
|
|
328
|
+
entries = entries.filter((e) => e.method.toUpperCase() === want);
|
|
329
|
+
}
|
|
330
|
+
if (pathFilter !== null) entries = entries.filter((e) => e.path.includes(pathFilter));
|
|
331
|
+
if (statusFilter !== void 0) entries = entries.filter((e) => e.response.status === statusFilter);
|
|
332
|
+
if (serviceFilter !== null) entries = entries.filter((e) => e.service === serviceFilter);
|
|
333
|
+
if (testIdFilter !== null) entries = entries.filter((e) => journalEntryTestId(e) === testIdFilter);
|
|
334
|
+
const total = entries.length;
|
|
335
|
+
if (offset > 0) entries = entries.slice(offset);
|
|
336
|
+
if (limit >= 0) entries = entries.slice(0, limit);
|
|
337
|
+
res.writeHead(200, {
|
|
338
|
+
"Content-Type": "application/json",
|
|
339
|
+
"X-Total-Count": String(total)
|
|
340
|
+
});
|
|
341
|
+
res.end(JSON.stringify(entries));
|
|
192
342
|
return true;
|
|
193
343
|
}
|
|
194
344
|
if (subPath === "/fixtures" && req.method === "GET") {
|
|
345
|
+
for (const key of searchParams.keys()) if (!FIXTURES_PARAMS.has(key)) {
|
|
346
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
347
|
+
res.end(JSON.stringify({ error: `Unknown query parameter: '${key}'. Supported: ${[...FIXTURES_PARAMS].join(", ")}` }));
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
const include = searchParams.get("include");
|
|
351
|
+
if (include === null) {
|
|
352
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
353
|
+
res.end(JSON.stringify({ count: fixtures.length }));
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
if (include !== "fixtures") {
|
|
357
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
358
|
+
res.end(JSON.stringify({ error: "Invalid 'include': expected 'fixtures'" }));
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
362
|
+
res.end(JSON.stringify({
|
|
363
|
+
count: fixtures.length,
|
|
364
|
+
fixtures: fixtures.map((fixture, index) => ({
|
|
365
|
+
index,
|
|
366
|
+
match: redactForInspection(fixture.match),
|
|
367
|
+
responseKind: fixtureResponseKind(fixture.response),
|
|
368
|
+
...fixture.latency !== void 0 ? { latency: fixture.latency } : {},
|
|
369
|
+
...fixture.chaos !== void 0 ? { chaos: fixture.chaos } : {}
|
|
370
|
+
}))
|
|
371
|
+
}));
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
if (subPath === "/chaos" && req.method === "GET") {
|
|
375
|
+
const scopeId = chaosScopeId(req);
|
|
376
|
+
if (scopeId === null) return writeBlankTestId(res);
|
|
377
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
378
|
+
res.end(JSON.stringify({ chaos: effectiveChaos(defaults, scopeId) }));
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
if (subPath === "/chaos" && req.method === "DELETE") {
|
|
382
|
+
const scopeId = chaosScopeId(req);
|
|
383
|
+
if (scopeId === null) return writeBlankTestId(res);
|
|
384
|
+
defaults.chaosByTestId?.delete(scopeId);
|
|
385
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
386
|
+
res.end(JSON.stringify({ chaos: effectiveChaos(defaults, scopeId) }));
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
if (subPath === "/chaos" && req.method === "POST") {
|
|
390
|
+
const scopeId = chaosScopeId(req);
|
|
391
|
+
if (scopeId === null) return writeBlankTestId(res);
|
|
392
|
+
let raw;
|
|
393
|
+
try {
|
|
394
|
+
raw = await require_helpers.readBody(req);
|
|
395
|
+
} catch (err) {
|
|
396
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
397
|
+
defaults.logger.error(`POST /__aimock/chaos: failed to read body: ${msg}`);
|
|
398
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
399
|
+
res.end(JSON.stringify({ error: `Failed to read request body: ${msg}` }));
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
let parsed;
|
|
403
|
+
try {
|
|
404
|
+
parsed = JSON.parse(raw);
|
|
405
|
+
} catch (err) {
|
|
406
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
407
|
+
defaults.logger.error(`POST /__aimock/chaos: invalid JSON: ${msg}`);
|
|
408
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
409
|
+
res.end(JSON.stringify({ error: `Invalid JSON: ${msg}` }));
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
413
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
414
|
+
res.end(JSON.stringify({ error: "Invalid body: expected a JSON object" }));
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
const allowed = [
|
|
418
|
+
"dropRate",
|
|
419
|
+
"malformedRate",
|
|
420
|
+
"disconnectRate"
|
|
421
|
+
];
|
|
422
|
+
for (const key of Object.keys(parsed)) if (!allowed.includes(key)) {
|
|
423
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
424
|
+
res.end(JSON.stringify({ error: `Unknown chaos field: '${key}'` }));
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
const next = {};
|
|
428
|
+
for (const key of allowed) {
|
|
429
|
+
const value = parsed[key];
|
|
430
|
+
if (value === void 0) continue;
|
|
431
|
+
if (typeof value !== "number" || Number.isNaN(value) || value < 0 || value > 1) {
|
|
432
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
433
|
+
res.end(JSON.stringify({ error: `Invalid '${key}': must be a number between 0 and 1` }));
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
next[key] = value;
|
|
437
|
+
}
|
|
438
|
+
defaults.chaosByTestId?.set(scopeId, next);
|
|
195
439
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
196
|
-
res.end(JSON.stringify({
|
|
440
|
+
res.end(JSON.stringify({ chaos: effectiveChaos(defaults, scopeId) }));
|
|
197
441
|
return true;
|
|
198
442
|
}
|
|
199
443
|
if (subPath === "/fixtures" && req.method === "POST") {
|
|
@@ -302,8 +546,37 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
|
|
|
302
546
|
res.end(JSON.stringify({ error: `Invalid JSON: ${msg}` }));
|
|
303
547
|
return true;
|
|
304
548
|
}
|
|
549
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
550
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
551
|
+
res.end(JSON.stringify({ error: "Invalid body: expected a JSON object" }));
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
305
554
|
const status = parsed.status ?? 500;
|
|
555
|
+
if (!require_fixture_loader.isInjectableStatus(status)) {
|
|
556
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
557
|
+
res.end(JSON.stringify({ error: `Invalid 'status': must be ${require_fixture_loader.INJECTED_STATUS_RANGE}` }));
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
306
560
|
const errorBody = parsed.body;
|
|
561
|
+
if (errorBody !== void 0 && errorBody !== null) {
|
|
562
|
+
if (typeof errorBody !== "object" || Array.isArray(errorBody)) {
|
|
563
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
564
|
+
res.end(JSON.stringify({ error: "Invalid 'body': must be an object" }));
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
for (const field of [
|
|
568
|
+
"message",
|
|
569
|
+
"type",
|
|
570
|
+
"code"
|
|
571
|
+
]) {
|
|
572
|
+
const value = errorBody[field];
|
|
573
|
+
if (value !== void 0 && value !== null && typeof value !== "string") {
|
|
574
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
575
|
+
res.end(JSON.stringify({ error: `Invalid 'body.${field}': must be a string` }));
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
307
580
|
const errorFixture = {
|
|
308
581
|
match: { predicate: () => true },
|
|
309
582
|
response: {
|
|
@@ -363,7 +636,6 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
363
636
|
let body;
|
|
364
637
|
try {
|
|
365
638
|
body = JSON.parse(raw);
|
|
366
|
-
if (modelFallback && !body.model) body.model = modelFallback;
|
|
367
639
|
} catch (parseErr) {
|
|
368
640
|
const detail = parseErr instanceof Error ? parseErr.message : "unknown parse error";
|
|
369
641
|
journal.add({
|
|
@@ -384,6 +656,24 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
384
656
|
} }));
|
|
385
657
|
return;
|
|
386
658
|
}
|
|
659
|
+
if (!require_helpers.isJsonObject(body)) {
|
|
660
|
+
journal.add({
|
|
661
|
+
method: req.method ?? "POST",
|
|
662
|
+
path: req.url ?? COMPLETIONS_PATH,
|
|
663
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
664
|
+
body: null,
|
|
665
|
+
response: {
|
|
666
|
+
status: 400,
|
|
667
|
+
fixture: null
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
require_sse_writer.writeErrorResponse(res, 400, openRouter ? require_openrouter_chat.serializeOpenRouterError(400, "Request body must be a JSON object") : JSON.stringify({ error: {
|
|
671
|
+
message: "Request body must be a JSON object",
|
|
672
|
+
type: "invalid_request_error"
|
|
673
|
+
} }));
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (modelFallback && !body.model) body.model = modelFallback;
|
|
387
677
|
if (!Array.isArray(body.messages)) {
|
|
388
678
|
journal.add({
|
|
389
679
|
method: req.method ?? "POST",
|
|
@@ -475,7 +765,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
475
765
|
const snippet = typeof lastUserMsg?.content === "string" ? lastUserMsg.content.slice(0, 80) : "";
|
|
476
766
|
defaults.logger.debug(`No fixture matched for request (model=${body.model ?? "?"}, msg="${snippet}")`);
|
|
477
767
|
}
|
|
478
|
-
const chaosAction = require_chaos.evaluateChaos(fixture, defaults.chaos, req.headers, defaults.logger);
|
|
768
|
+
const chaosAction = require_chaos.evaluateChaos(fixture, defaults.chaos, req.headers, defaults.logger, req.url);
|
|
479
769
|
const chaosContext = {
|
|
480
770
|
method,
|
|
481
771
|
path,
|
|
@@ -772,13 +1062,27 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
772
1062
|
const logger = new require_logger.Logger(options?.logLevel ?? "silent");
|
|
773
1063
|
const registry = options?.metrics ? require_metrics.createMetricsRegistry() : void 0;
|
|
774
1064
|
const serverOptions = options ?? {};
|
|
1065
|
+
const chaosByTestId = /* @__PURE__ */ new Map();
|
|
775
1066
|
const defaults = {
|
|
776
1067
|
latency: serverOptions.latency ?? 0,
|
|
777
1068
|
chunkSize: Math.max(1, serverOptions.chunkSize ?? DEFAULT_CHUNK_SIZE),
|
|
778
1069
|
replaySpeed: serverOptions.replaySpeed ?? 1,
|
|
779
1070
|
logger,
|
|
1071
|
+
chaosByTestId,
|
|
780
1072
|
get chaos() {
|
|
781
|
-
return
|
|
1073
|
+
return {
|
|
1074
|
+
base: chaosByTestId.get(require_constants.DEFAULT_TEST_ID) ?? serverOptions.chaos,
|
|
1075
|
+
byTestId: chaosByTestId
|
|
1076
|
+
};
|
|
1077
|
+
},
|
|
1078
|
+
set chaos(value) {
|
|
1079
|
+
if (value === void 0) {
|
|
1080
|
+
chaosByTestId.clear();
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
const base = require_chaos.isChaosScope(value) ? value.base : value;
|
|
1084
|
+
if (base === void 0) chaosByTestId.delete(require_constants.DEFAULT_TEST_ID);
|
|
1085
|
+
else chaosByTestId.set(require_constants.DEFAULT_TEST_ID, base);
|
|
782
1086
|
},
|
|
783
1087
|
registry,
|
|
784
1088
|
get record() {
|
|
@@ -935,7 +1239,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
935
1239
|
require_api_key_auth.markAuthenticatedRequest(req, resolvedAuth.policy);
|
|
936
1240
|
}
|
|
937
1241
|
if (pathname.startsWith(CONTROL_PREFIX)) {
|
|
938
|
-
await handleControlAPI(req, res, pathname, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults);
|
|
1242
|
+
await handleControlAPI(req, res, pathname, parsedUrl.searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults);
|
|
939
1243
|
return;
|
|
940
1244
|
}
|
|
941
1245
|
if (mounts) {
|
|
@@ -1827,7 +2131,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1827
2131
|
try {
|
|
1828
2132
|
falBody = req.method === "POST" || req.method === "PUT" ? await require_helpers.readBody(req) : "";
|
|
1829
2133
|
const raw = falBody;
|
|
1830
|
-
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
|
|
2134
|
+
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger, req.url);
|
|
1831
2135
|
if (chaosAction) {
|
|
1832
2136
|
require_chaos.applyChaosAction(chaosAction, res, null, journal, {
|
|
1833
2137
|
method: req.method ?? "GET",
|
|
@@ -1869,7 +2173,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1869
2173
|
setCorsHeaders(res);
|
|
1870
2174
|
try {
|
|
1871
2175
|
const raw = req.method === "POST" || req.method === "PUT" ? falBody ?? await require_helpers.readBody(req) : "{}";
|
|
1872
|
-
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
|
|
2176
|
+
const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger, req.url);
|
|
1873
2177
|
if (chaosAction) {
|
|
1874
2178
|
require_chaos.applyChaosAction(chaosAction, res, null, journal, {
|
|
1875
2179
|
method: req.method ?? "GET",
|