@crvouga/mockingbird-service-bedrock 3.2.0 → 3.4.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.
package/CHANGELOG.md CHANGED
@@ -1,14 +1,28 @@
1
1
  # Changelog — @crvouga/mockingbird-service-bedrock
2
2
 
3
- ## 3.2.0 (2026-10-09)
3
+ ## 3.4.0 (2026-10-15)
4
4
 
5
5
  ### Features
6
6
 
7
- - match Emulate provider coverage ([16b65ce](https://github.com/crvouga/mockingbird/commit/16b65ce791fa216f28ec1521df636f57771d9c5e))
7
+ - finish durable run lifecycle ([725ea1a](https://github.com/crvouga/mockingbird/commit/725ea1a966dac65434e98f06a9c33aca0d6f32d7))
8
+ - add management and portable archives ([0d2a6dd](https://github.com/crvouga/mockingbird/commit/0d2a6dd0f9fecb3a1ea7c1638f6639787395c2f4))
9
+
10
+ ### Fixes and improvements
8
11
 
9
- ### Dependencies
12
+ - correlate cross-namespace operations ([3457a4b](https://github.com/crvouga/mockingbird/commit/3457a4b7dbfd9ad9c8a10671a5744b7c8425d000))
13
+ - harden HTTP stream handling ([674307a](https://github.com/crvouga/mockingbird/commit/674307a869f57907567d462c4f506c3b0a237e13))
14
+
15
+ ## 3.3.0 (2026-10-12)
16
+
17
+ ### Features
10
18
 
11
- - `@crvouga/mockingbird-service-sqlite`
19
+ - unify the emulator control plane ([2068547](https://github.com/crvouga/mockingbird/commit/2068547bedde4e2e2331c1657c1a78120e84428b))
20
+
21
+ ## 3.2.0 (2026-10-08)
22
+
23
+ ### Features
24
+
25
+ - match Emulate provider coverage ([16b65ce](https://github.com/crvouga/mockingbird/commit/16b65ce791fa216f28ec1521df636f57771d9c5e))
12
26
 
13
27
  ## 3.1.0 (2026-10-08)
14
28
 
package/DISCOVERY.md CHANGED
@@ -35,6 +35,7 @@ repository; live parity is an additional oracle check, not a substitute for the
35
35
 
36
36
  - `GET /__admin/health`
37
37
  - `GET /__admin`
38
+ - `GET /__admin/controls`
38
39
  - `GET /__admin/state`
39
40
  - `GET /__admin/requests`
40
41
  - `GET /__admin/metrics`
@@ -45,6 +46,28 @@ For HTTP services, use `x-mockingbird-namespace` (or the documented credential/p
45
46
  parallel tests do not share state. Admin state, journal, metrics and fault-preset endpoints are
46
47
  designed for assertions and diagnosis by consuming test suites.
47
48
 
49
+ ## Admin controls
50
+
51
+ Every HTTP emulator takes the same controls, and each means the same thing at boot
52
+ (`createRuntime({ adminControls })`, `serve --admin-controls`), over `/__admin`, and in a request header:
53
+
54
+ | Control | Admin API | Request header | Interface |
55
+ | --- | --- | --- | --- |
56
+ | Isolate state by key | `GET /__admin/namespaces` | `x-mockingbird-namespace: <key>` | [Namespaces](https://mockingbird.chrisvouga.dev/docs/admin-controls/namespaces) |
57
+ | Reset to boot | `POST /__admin/reset` | `x-mockingbird-reset: true` | [Reset](https://mockingbird.chrisvouga.dev/docs/admin-controls/reset) |
58
+ | Snapshot and restore | `POST /__admin/snapshots`, `POST /__admin/snapshots/<id>/restore` | `x-mockingbird-snapshot: <name>`, `x-mockingbird-restore: <name>` | [Snapshot and restore](https://mockingbird.chrisvouga.dev/docs/admin-controls/snapshot-and-restore) |
59
+ | Move the clock | `POST /__admin/clock` | `x-mockingbird-time: <instant>`, `x-mockingbird-advance: <duration>` | [Clock](https://mockingbird.chrisvouga.dev/docs/admin-controls/clock) |
60
+ | Read or branch from old state | `GET /__admin/timeline` | `x-mockingbird-at: <checkpoint>`, `x-mockingbird-branch: <name>` | [Checkpoints and branches](https://mockingbird.chrisvouga.dev/docs/admin-controls/checkpoints-and-branches) |
61
+ | Inject a fault | `POST /__admin/faults` | `x-mockingbird-fault: <fault>` | [Faults](https://mockingbird.chrisvouga.dev/docs/admin-controls/faults) |
62
+ | Latency and timeouts | `PUT /__admin/latency` | `x-mockingbird-delay: <duration>`, `x-mockingbird-timeout: <duration>` | [Latency and timeouts](https://mockingbird.chrisvouga.dev/docs/admin-controls/latency-and-timeouts) |
63
+ | Replay from a seed | `PUT /__admin/controls` | `x-mockingbird-seed: <seed>` | [Seed and determinism](https://mockingbird.chrisvouga.dev/docs/admin-controls/seed-and-determinism) |
64
+ | See what happened | `GET /__admin/requests`, `GET /__admin/state` | | [Journal, metrics and state](https://mockingbird.chrisvouga.dev/docs/admin-controls/journal-and-state) |
65
+
66
+ `GET /__admin` lists every route and header with a one-line summary, and `GET /__admin/controls`
67
+ is the namespace's current control document. The same seed and the same requests give the same
68
+ bytes. Setting controls as an emulator starts is [Boot](https://mockingbird.chrisvouga.dev/docs/admin-controls/boot); every route and header is in
69
+ the [Reference](https://mockingbird.chrisvouga.dev/docs/admin-controls/reference). The full guide: <https://mockingbird.chrisvouga.dev/docs/admin-controls>.
70
+
48
71
  ## Report a mismatch or missing capability
49
72
 
50
73
  Follow the [agent reporting contract](https://github.com/crvouga/mockingbird/blob/main/docs/REPORTING_ISSUES.md). Include package version,
package/README.md CHANGED
@@ -37,10 +37,12 @@ export AWS_ENDPOINT_URL_BEDROCK_AGENTCORE=http://127.0.0.1:8796 # AgentCore Inv
37
37
  ```
38
38
 
39
39
  ```ts
40
+ import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime"
40
41
  import { createServer } from "@crvouga/mockingbird-service-bedrock/server"
41
42
 
42
- const bedrock = await createServer({ port: 8796 })
43
- await fetch(`${bedrock.url}/__admin/scripts`, {
43
+ // Test setup: start the emulator and script the model's turns.
44
+ const emulator = await createServer()
45
+ await fetch(`${emulator.url}/__admin/scripts`, {
44
46
  method: "PUT",
45
47
  headers: { "content-type": "application/json" },
46
48
  body: JSON.stringify({
@@ -64,8 +66,29 @@ await fetch(`${bedrock.url}/__admin/scripts`, {
64
66
  ],
65
67
  }),
66
68
  })
67
- // …the backend's streamText turn now emits the approval card; approving resumes the turn.
68
- await bedrock.close()
69
+
70
+ // The AWS SDK client your application uses; only the endpoint changes.
71
+ const bedrock = new BedrockRuntimeClient({
72
+ endpoint: emulator.url,
73
+ region: "us-east-1",
74
+ credentials: { accessKeyId: "fixture", secretAccessKey: "fixture" },
75
+ })
76
+
77
+ // Application code: the chat turn now asks for the tool, which the app shows as an approval card.
78
+ const turn = await bedrock.send(
79
+ new ConverseCommand({
80
+ modelId: "global.anthropic.claude-sonnet-4-6",
81
+ messages: [{ role: "user", content: [{ text: "I feel dizzy" }] }],
82
+ toolConfig: {
83
+ tools: [{ toolSpec: { name: "report_rx_symptom", inputSchema: { json: { type: "object" } } } }],
84
+ },
85
+ }),
86
+ )
87
+ // turn.stopReason === "tool_use"
88
+ // turn.output?.message?.content?.[0]?.toolUse?.name === "report_rx_symptom"
89
+ // Sending the tool result back resumes the turn: "I've flagged that for your care team."
90
+
91
+ await emulator.close() // after the test
69
92
  ```
70
93
 
71
94
  ### Protocols
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createRuntime,
3
3
  parseScript
4
- } from "./chunk-B7FB27RN.js";
4
+ } from "./chunk-UU3IC2CF.js";
5
5
 
6
6
  // src/h2c.ts
7
7
  import {
@@ -279,6 +279,7 @@ var serveTarget = {
279
279
  ...defaultText !== void 0 ? { settings: { defaultText } } : {},
280
280
  ...common.adminPrefix !== void 0 ? { adminPrefix: common.adminPrefix } : {},
281
281
  ...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {},
282
+ ...common.adminControls !== void 0 ? { adminControls: common.adminControls } : {},
282
283
  ...common.seed !== void 0 ? { seed: common.seed } : {},
283
284
  ...common.onLog ? { onLog: common.onLog } : {}
284
285
  });
@@ -297,4 +298,4 @@ export {
297
298
  createServer,
298
299
  serveTarget
299
300
  };
300
- //# sourceMappingURL=chunk-DZ24BLVS.js.map
301
+ //# sourceMappingURL=chunk-CHZFUB2R.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/h2c.ts", "../src/server.ts"],
4
- "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * One port that speaks both cleartext HTTP/2 (prior knowledge, \"h2c\") and HTTP/1.1.\n *\n * AWS SDK v3 clients for Bedrock Runtime, Polly and Transcribe Streaming default to\n * `NodeHttp2Handler`, so against an `http://` endpoint they open an h2c connection and\n * send the HTTP/2 connection preface straight away; bidirectional operations (Nova Sonic,\n * `StartSpeechSynthesisStream`, `StartStreamTranscription`) need HTTP/2 duplex. The AI SDK,\n * AgentCore and Transcribe batch clients speak HTTP/1.1. A front `node:net` server reads\n * the first bytes of each connection and hands it to an internal HTTP/2 or HTTP/1.1\n * server; both turn requests into Fetch `Request`s whose bodies stream in as they arrive,\n * and stream responses back chunk by chunk.\n */\nimport {\n createServer as createHttp1Server,\n type IncomingMessage,\n type ServerResponse,\n} from \"node:http\"\nimport {\n createServer as createHttp2Server,\n constants as http2Constants,\n type IncomingHttpHeaders,\n type ServerHttp2Stream,\n} from \"node:http2\"\nimport { type AddressInfo, connect, createServer as createNetServer, type Socket } from \"node:net\"\nimport { Readable } from \"node:stream\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\n\nexport type H2cListenOptions = {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`. */\n host?: string\n}\n\n/** A running dual-protocol listener. */\nexport type H2cListening = {\n url: string\n port: number\n host: string\n close(): Promise<void>\n}\n\nconst PREFACE = \"PRI * HTTP/2.0\"\n\n/** Headers HTTP/2 forbids on a response (RFC 9113 \u00A78.2.2). */\nconst CONNECTION_HEADERS = new Set([\n \"connection\",\n \"keep-alive\",\n \"proxy-connection\",\n \"transfer-encoding\",\n \"upgrade\",\n \"host\",\n])\n\nconst toWebBody = (stream: Readable): ReadableStream<Uint8Array> =>\n Readable.toWeb(stream) as unknown as ReadableStream<Uint8Array>\n\nconst isDrop = (error: unknown) => (error as { code?: string } | null)?.code === \"MOCKINGBIRD_DROP\"\n\nconst internalError = (error: unknown) =>\n JSON.stringify({\n error: {\n type: \"mockingbird_internal\",\n message: error instanceof Error ? error.message : String(error),\n },\n })\n\n/** Write a Fetch response body to a Node writable, respecting backpressure. */\nconst pump = async (\n body: ReadableStream<Uint8Array>,\n write: (chunk: Uint8Array) => boolean,\n drained: () => Promise<void>,\n closed: () => boolean,\n): Promise<void> => {\n const reader = body.getReader()\n try {\n for (;;) {\n if (closed()) {\n await reader.cancel().catch(() => undefined)\n return\n }\n const { done, value } = await reader.read()\n if (done) return\n if (!write(value)) await drained()\n }\n } finally {\n reader.releaseLock()\n }\n}\n\nconst handleHttp1 = async (\n api: FetchAPI,\n base: string,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> => {\n const method = req.method ?? \"GET\"\n const url = new URL((req.url ?? \"/\").replace(/^\\/+/, \"/\"), `http://${req.headers.host ?? base}`)\n const headers = new Headers()\n for (const [name, value] of Object.entries(req.headers)) {\n if (value === undefined) continue\n for (const each of Array.isArray(value) ? value : [value]) headers.append(name, each)\n }\n const aborted = new AbortController()\n res.once(\"close\", () => {\n if (!res.writableFinished) aborted.abort()\n })\n const hasBody = method !== \"GET\" && method !== \"HEAD\"\n const request = new Request(url, {\n method,\n headers,\n signal: aborted.signal,\n ...(hasBody ? { body: toWebBody(req), duplex: \"half\" } : {}),\n } as RequestInit)\n let response: Response\n try {\n response = await api.fetch(request)\n } catch (error) {\n if (isDrop(error)) {\n req.socket.destroy()\n return\n }\n res.writeHead(500, { \"content-type\": \"application/json\" })\n res.end(internalError(error))\n return\n }\n const out: Record<string, string | string[]> = Object.fromEntries(response.headers)\n const cookies = response.headers.getSetCookie()\n if (cookies.length > 0) out[\"set-cookie\"] = cookies\n res.writeHead(response.status, out)\n if (!response.body) {\n res.end()\n return\n }\n res.flushHeaders()\n try {\n await pump(\n response.body,\n (chunk) => res.write(chunk),\n () => new Promise((resolve) => res.once(\"drain\", resolve)),\n () => res.destroyed,\n )\n res.end()\n } catch {\n res.destroy()\n }\n}\n\nconst handleHttp2 = async (\n api: FetchAPI,\n base: string,\n stream: ServerHttp2Stream,\n incoming: IncomingHttpHeaders,\n): Promise<void> => {\n const method = String(incoming[\":method\"] ?? \"GET\")\n const authority = String(incoming[\":authority\"] ?? incoming.host ?? base)\n const url = new URL(String(incoming[\":path\"] ?? \"/\").replace(/^\\/+/, \"/\"), `http://${authority}`)\n const headers = new Headers()\n for (const [name, value] of Object.entries(incoming)) {\n if (name.startsWith(\":\") || value === undefined) continue\n for (const each of Array.isArray(value) ? value : [value]) headers.append(name, String(each))\n }\n const aborted = new AbortController()\n stream.once(\"close\", () => {\n if (!stream.writableFinished) aborted.abort()\n })\n stream.on(\"error\", () => undefined)\n const hasBody = method !== \"GET\" && method !== \"HEAD\"\n const request = new Request(url, {\n method,\n headers,\n signal: aborted.signal,\n ...(hasBody ? { body: toWebBody(stream), duplex: \"half\" } : {}),\n } as RequestInit)\n let response: Response\n try {\n response = await api.fetch(request)\n } catch (error) {\n if (isDrop(error)) {\n stream.close(http2Constants.NGHTTP2_INTERNAL_ERROR)\n return\n }\n if (stream.destroyed) return\n stream.respond({ \":status\": 500, \"content-type\": \"application/json\" })\n stream.end(internalError(error))\n return\n }\n if (stream.destroyed || stream.closed) return\n const out: Record<string, string | string[] | number> = { \":status\": response.status }\n for (const [name, value] of response.headers) {\n if (!CONNECTION_HEADERS.has(name)) out[name] = value\n }\n if (!response.body) {\n stream.respond(out, { endStream: true })\n return\n }\n stream.respond(out)\n try {\n await pump(\n response.body,\n (chunk) => stream.write(chunk),\n () => new Promise((resolve) => stream.once(\"drain\", resolve)),\n () => stream.destroyed || stream.closed,\n )\n if (!stream.destroyed) stream.end()\n } catch {\n if (!stream.destroyed) stream.close(http2Constants.NGHTTP2_INTERNAL_ERROR)\n }\n}\n\nconst listenInternal = (server: {\n listen: (...args: unknown[]) => unknown\n once: (...args: never[]) => unknown\n address(): AddressInfo | string | null\n}) =>\n new Promise<number>((resolve, reject) => {\n ;(server.once as (event: string, listener: (error: Error) => void) => void)(\"error\", reject)\n server.listen(0, \"127.0.0.1\", () => resolve((server.address() as AddressInfo).port))\n })\n\n/**\n * Serve `api` over h2c and HTTP/1.1 on one port. Each accepted connection is sniffed for\n * the HTTP/2 preface and relayed to an internal loopback server for that protocol.\n */\nexport const listenH2c = async (\n api: FetchAPI,\n options: H2cListenOptions = {},\n): Promise<H2cListening> => {\n const host = options.host ?? \"127.0.0.1\"\n const shown = host.includes(\":\") ? `[${host}]` : host\n let base = `${shown}:${options.port ?? 0}`\n const h1 = createHttp1Server((req, res) => {\n void handleHttp1(api, base, req, res)\n })\n const h2 = createHttp2Server()\n h2.on(\"stream\", (stream, headers) => {\n void handleHttp2(api, base, stream, headers)\n })\n h2.on(\"sessionError\", () => undefined)\n const h1Port = await listenInternal(h1 as never)\n const h2Port = await listenInternal(h2 as never)\n const sockets = new Set<Socket>()\n const track = (socket: Socket) => {\n sockets.add(socket)\n socket.once(\"close\", () => sockets.delete(socket))\n }\n const front = createNetServer((socket) => {\n track(socket)\n socket.setNoDelay(true)\n let seen = Buffer.alloc(0)\n const onData = (chunk: Buffer) => {\n seen = Buffer.concat([seen, chunk])\n // \"PRI\" opens only the HTTP/2 preface; no HTTP/1.1 method starts that way.\n if (seen.length < 3) return\n socket.off(\"data\", onData)\n socket.pause()\n const http2 = seen.subarray(0, 3).toString(\"latin1\") === PREFACE.slice(0, 3)\n const upstream = connect(http2 ? h2Port : h1Port, \"127.0.0.1\")\n track(upstream)\n upstream.setNoDelay(true)\n const destroy = () => {\n socket.destroy()\n upstream.destroy()\n }\n socket.on(\"error\", destroy)\n upstream.on(\"error\", destroy)\n socket.once(\"close\", () => upstream.destroy())\n upstream.once(\"close\", () => socket.destroy())\n upstream.write(seen)\n socket.pipe(upstream)\n upstream.pipe(socket)\n socket.resume()\n }\n socket.on(\"data\", onData)\n socket.on(\"error\", () => socket.destroy())\n })\n await new Promise<void>((resolve, reject) => {\n front.once(\"error\", reject)\n front.listen(options.port ?? 0, host, () => resolve())\n })\n const port = (front.address() as AddressInfo).port\n base = `${shown}:${port}`\n const closeServer = (server: { close: (cb: (error?: Error) => void) => unknown }) =>\n new Promise<void>((resolve) => {\n server.close(() => resolve())\n })\n return {\n url: `http://${shown}:${port}`,\n port,\n host,\n close: async () => {\n const closing = [\n closeServer(front as never),\n closeServer(h1 as never),\n closeServer(h2 as never),\n ]\n for (const socket of sockets) socket.destroy()\n h1.closeAllConnections?.()\n await Promise.all(closing)\n },\n }\n}\n", "/// <reference types=\"node\" />\nimport type { ServeTarget } from \"@crvouga/mockingbird-adapter-node\"\nimport { type H2cListening, listenH2c } from \"./h2c.js\"\nimport { type BedrockRuntime, type BedrockRuntimeOptions, createRuntime } from \"./runtime.js\"\nimport { parseScript, type Script } from \"./scripts.js\"\n\n/** Port `mockingbird-bedrock serve` listens on when none is given. */\nexport const DEFAULT_PORT = 8796\n\nexport type BedrockServerOptions = BedrockRuntimeOptions & {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`. */\n host?: string\n}\n\nexport type BedrockServer = H2cListening & { runtime: BedrockRuntime }\n\n/**\n * Serve the Bedrock mock on one port that speaks both h2c (the AWS SDK's default\n * `NodeHttp2Handler`, and Nova Sonic's duplex stream) and HTTP/1.1 (the AI SDK, AgentCore).\n */\nexport const createServer = async (options: BedrockServerOptions = {}): Promise<BedrockServer> => {\n const { port, host, ...rest } = options\n const runtime = createRuntime(rest)\n const listening = await listenH2c(runtime, {\n port: port ?? 0,\n ...(host !== undefined ? { host } : {}),\n })\n return { ...listening, runtime }\n}\n\nconst text = (value: string | boolean | undefined) =>\n typeof value === \"string\" ? value : undefined\n\n/** Scripts from `--scripts <file.json>` (Node only). */\nconst loadScripts = async (path: string): Promise<Script[]> => {\n const { readFile } = await import(\"node:fs/promises\")\n const raw = JSON.parse(await readFile(path, \"utf8\")) as unknown\n const list = Array.isArray(raw) ? raw : (raw as { scripts?: unknown[] }).scripts\n if (!Array.isArray(list)) throw new Error(`${path}: expected {\"scripts\": [...]}`)\n return list.map((each, index) => {\n const parsed = parseScript(each, index)\n if (typeof parsed === \"string\") throw new Error(`${path}: ${parsed}`)\n return parsed\n })\n}\n\n/**\n * How `serve` builds the Bedrock mock from flags. `serve --config` in another service's CLI\n * listens over HTTP/1.1 only; `mockingbird-bedrock serve` listens with h2c as well.\n */\nexport const serveTarget: ServeTarget = {\n name: \"bedrock\",\n defaultPort: DEFAULT_PORT,\n options: {\n scripts: {\n type: \"string\",\n value: \"<file.json>\",\n description:\n 'Scripts every namespace starts with ({\"scripts\": [...]}, as PUT /__admin/scripts)',\n },\n \"default-text\": {\n type: \"string\",\n value: \"<text>\",\n description: 'What an unscripted chat call answers (default \"OK.\")',\n },\n },\n create: async (values, common) => {\n const scriptsPath = text(values.scripts)\n const defaultText = text(values[\"default-text\"])\n return createRuntime({\n ...(scriptsPath ? { scripts: await loadScripts(scriptsPath) } : {}),\n ...(defaultText !== undefined ? { settings: { defaultText } } : {}),\n ...(common.adminPrefix !== undefined ? { adminPrefix: common.adminPrefix } : {}),\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(common.onLog ? { onLog: common.onLog } : {}),\n }) as never\n },\n banner: () => [\n \"point the app at it: AWS_ENDPOINT_URL_BEDROCK_RUNTIME / AWS_ENDPOINT_URL_BEDROCK_AGENTCORE = this URL\",\n \"protocols: h2c (prior knowledge) and HTTP/1.1 on the same port\",\n \"namespaces: x-mockingbird-namespace, /__admin/ns/<name>/\u2026, or PUT /__admin/credentials {<AWS_ACCESS_KEY_ID>: <ns>}\",\n \"scripts: PUT /__admin/scripts {scripts: [{id, match, turns}]}\",\n ],\n}\n\nexport type { H2cListening, H2cListenOptions } from \"./h2c.js\"\nexport { listenH2c } from \"./h2c.js\"\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * One port that speaks both cleartext HTTP/2 (prior knowledge, \"h2c\") and HTTP/1.1.\n *\n * AWS SDK v3 clients for Bedrock Runtime, Polly and Transcribe Streaming default to\n * `NodeHttp2Handler`, so against an `http://` endpoint they open an h2c connection and\n * send the HTTP/2 connection preface straight away; bidirectional operations (Nova Sonic,\n * `StartSpeechSynthesisStream`, `StartStreamTranscription`) need HTTP/2 duplex. The AI SDK,\n * AgentCore and Transcribe batch clients speak HTTP/1.1. A front `node:net` server reads\n * the first bytes of each connection and hands it to an internal HTTP/2 or HTTP/1.1\n * server; both turn requests into Fetch `Request`s whose bodies stream in as they arrive,\n * and stream responses back chunk by chunk.\n */\nimport {\n createServer as createHttp1Server,\n type IncomingMessage,\n type ServerResponse,\n} from \"node:http\"\nimport {\n createServer as createHttp2Server,\n constants as http2Constants,\n type IncomingHttpHeaders,\n type ServerHttp2Stream,\n} from \"node:http2\"\nimport { type AddressInfo, connect, createServer as createNetServer, type Socket } from \"node:net\"\nimport { Readable } from \"node:stream\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\n\nexport type H2cListenOptions = {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`. */\n host?: string\n}\n\n/** A running dual-protocol listener. */\nexport type H2cListening = {\n url: string\n port: number\n host: string\n close(): Promise<void>\n}\n\nconst PREFACE = \"PRI * HTTP/2.0\"\n\n/** Headers HTTP/2 forbids on a response (RFC 9113 \u00A78.2.2). */\nconst CONNECTION_HEADERS = new Set([\n \"connection\",\n \"keep-alive\",\n \"proxy-connection\",\n \"transfer-encoding\",\n \"upgrade\",\n \"host\",\n])\n\nconst toWebBody = (stream: Readable): ReadableStream<Uint8Array> =>\n Readable.toWeb(stream) as unknown as ReadableStream<Uint8Array>\n\nconst isDrop = (error: unknown) => (error as { code?: string } | null)?.code === \"MOCKINGBIRD_DROP\"\n\nconst internalError = (error: unknown) =>\n JSON.stringify({\n error: {\n type: \"mockingbird_internal\",\n message: error instanceof Error ? error.message : String(error),\n },\n })\n\n/** Write a Fetch response body to a Node writable, respecting backpressure. */\nconst pump = async (\n body: ReadableStream<Uint8Array>,\n write: (chunk: Uint8Array) => boolean,\n drained: () => Promise<void>,\n closed: () => boolean,\n): Promise<void> => {\n const reader = body.getReader()\n try {\n for (;;) {\n if (closed()) {\n await reader.cancel().catch(() => undefined)\n return\n }\n const { done, value } = await reader.read()\n if (done) return\n if (!write(value)) await drained()\n }\n } finally {\n reader.releaseLock()\n }\n}\n\nconst handleHttp1 = async (\n api: FetchAPI,\n base: string,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> => {\n const method = req.method ?? \"GET\"\n const url = new URL((req.url ?? \"/\").replace(/^\\/+/, \"/\"), `http://${req.headers.host ?? base}`)\n const headers = new Headers()\n for (const [name, value] of Object.entries(req.headers)) {\n if (value === undefined) continue\n for (const each of Array.isArray(value) ? value : [value]) headers.append(name, each)\n }\n const aborted = new AbortController()\n res.once(\"close\", () => {\n if (!res.writableFinished) aborted.abort()\n })\n const hasBody = method !== \"GET\" && method !== \"HEAD\"\n const request = new Request(url, {\n method,\n headers,\n signal: aborted.signal,\n ...(hasBody ? { body: toWebBody(req), duplex: \"half\" } : {}),\n } as RequestInit)\n let response: Response\n try {\n response = await api.fetch(request)\n } catch (error) {\n if (isDrop(error)) {\n req.socket.destroy()\n return\n }\n res.writeHead(500, { \"content-type\": \"application/json\" })\n res.end(internalError(error))\n return\n }\n const out: Record<string, string | string[]> = Object.fromEntries(response.headers)\n const cookies = response.headers.getSetCookie()\n if (cookies.length > 0) out[\"set-cookie\"] = cookies\n res.writeHead(response.status, out)\n if (!response.body) {\n res.end()\n return\n }\n res.flushHeaders()\n try {\n await pump(\n response.body,\n (chunk) => res.write(chunk),\n () => new Promise((resolve) => res.once(\"drain\", resolve)),\n () => res.destroyed,\n )\n res.end()\n } catch {\n res.destroy()\n }\n}\n\nconst handleHttp2 = async (\n api: FetchAPI,\n base: string,\n stream: ServerHttp2Stream,\n incoming: IncomingHttpHeaders,\n): Promise<void> => {\n const method = String(incoming[\":method\"] ?? \"GET\")\n const authority = String(incoming[\":authority\"] ?? incoming.host ?? base)\n const url = new URL(String(incoming[\":path\"] ?? \"/\").replace(/^\\/+/, \"/\"), `http://${authority}`)\n const headers = new Headers()\n for (const [name, value] of Object.entries(incoming)) {\n if (name.startsWith(\":\") || value === undefined) continue\n for (const each of Array.isArray(value) ? value : [value]) headers.append(name, String(each))\n }\n const aborted = new AbortController()\n stream.once(\"close\", () => {\n if (!stream.writableFinished) aborted.abort()\n })\n stream.on(\"error\", () => undefined)\n const hasBody = method !== \"GET\" && method !== \"HEAD\"\n const request = new Request(url, {\n method,\n headers,\n signal: aborted.signal,\n ...(hasBody ? { body: toWebBody(stream), duplex: \"half\" } : {}),\n } as RequestInit)\n let response: Response\n try {\n response = await api.fetch(request)\n } catch (error) {\n if (isDrop(error)) {\n stream.close(http2Constants.NGHTTP2_INTERNAL_ERROR)\n return\n }\n if (stream.destroyed) return\n stream.respond({ \":status\": 500, \"content-type\": \"application/json\" })\n stream.end(internalError(error))\n return\n }\n if (stream.destroyed || stream.closed) return\n const out: Record<string, string | string[] | number> = { \":status\": response.status }\n for (const [name, value] of response.headers) {\n if (!CONNECTION_HEADERS.has(name)) out[name] = value\n }\n if (!response.body) {\n stream.respond(out, { endStream: true })\n return\n }\n stream.respond(out)\n try {\n await pump(\n response.body,\n (chunk) => stream.write(chunk),\n () => new Promise((resolve) => stream.once(\"drain\", resolve)),\n () => stream.destroyed || stream.closed,\n )\n if (!stream.destroyed) stream.end()\n } catch {\n if (!stream.destroyed) stream.close(http2Constants.NGHTTP2_INTERNAL_ERROR)\n }\n}\n\nconst listenInternal = (server: {\n listen: (...args: unknown[]) => unknown\n once: (...args: never[]) => unknown\n address(): AddressInfo | string | null\n}) =>\n new Promise<number>((resolve, reject) => {\n ;(server.once as (event: string, listener: (error: Error) => void) => void)(\"error\", reject)\n server.listen(0, \"127.0.0.1\", () => resolve((server.address() as AddressInfo).port))\n })\n\n/**\n * Serve `api` over h2c and HTTP/1.1 on one port. Each accepted connection is sniffed for\n * the HTTP/2 preface and relayed to an internal loopback server for that protocol.\n */\nexport const listenH2c = async (\n api: FetchAPI,\n options: H2cListenOptions = {},\n): Promise<H2cListening> => {\n const host = options.host ?? \"127.0.0.1\"\n const shown = host.includes(\":\") ? `[${host}]` : host\n let base = `${shown}:${options.port ?? 0}`\n const h1 = createHttp1Server((req, res) => {\n void handleHttp1(api, base, req, res)\n })\n const h2 = createHttp2Server()\n h2.on(\"stream\", (stream, headers) => {\n void handleHttp2(api, base, stream, headers)\n })\n h2.on(\"sessionError\", () => undefined)\n const h1Port = await listenInternal(h1 as never)\n const h2Port = await listenInternal(h2 as never)\n const sockets = new Set<Socket>()\n const track = (socket: Socket) => {\n sockets.add(socket)\n socket.once(\"close\", () => sockets.delete(socket))\n }\n const front = createNetServer((socket) => {\n track(socket)\n socket.setNoDelay(true)\n let seen = Buffer.alloc(0)\n const onData = (chunk: Buffer) => {\n seen = Buffer.concat([seen, chunk])\n // \"PRI\" opens only the HTTP/2 preface; no HTTP/1.1 method starts that way.\n if (seen.length < 3) return\n socket.off(\"data\", onData)\n socket.pause()\n const http2 = seen.subarray(0, 3).toString(\"latin1\") === PREFACE.slice(0, 3)\n const upstream = connect(http2 ? h2Port : h1Port, \"127.0.0.1\")\n track(upstream)\n upstream.setNoDelay(true)\n const destroy = () => {\n socket.destroy()\n upstream.destroy()\n }\n socket.on(\"error\", destroy)\n upstream.on(\"error\", destroy)\n socket.once(\"close\", () => upstream.destroy())\n upstream.once(\"close\", () => socket.destroy())\n upstream.write(seen)\n socket.pipe(upstream)\n upstream.pipe(socket)\n socket.resume()\n }\n socket.on(\"data\", onData)\n socket.on(\"error\", () => socket.destroy())\n })\n await new Promise<void>((resolve, reject) => {\n front.once(\"error\", reject)\n front.listen(options.port ?? 0, host, () => resolve())\n })\n const port = (front.address() as AddressInfo).port\n base = `${shown}:${port}`\n const closeServer = (server: { close: (cb: (error?: Error) => void) => unknown }) =>\n new Promise<void>((resolve) => {\n server.close(() => resolve())\n })\n return {\n url: `http://${shown}:${port}`,\n port,\n host,\n close: async () => {\n const closing = [\n closeServer(front as never),\n closeServer(h1 as never),\n closeServer(h2 as never),\n ]\n for (const socket of sockets) socket.destroy()\n h1.closeAllConnections?.()\n await Promise.all(closing)\n },\n }\n}\n", "/// <reference types=\"node\" />\nimport type { ServeTarget } from \"@crvouga/mockingbird-adapter-node\"\nimport { type H2cListening, listenH2c } from \"./h2c.js\"\nimport { type BedrockRuntime, type BedrockRuntimeOptions, createRuntime } from \"./runtime.js\"\nimport { parseScript, type Script } from \"./scripts.js\"\n\n/** Port `mockingbird-bedrock serve` listens on when none is given. */\nexport const DEFAULT_PORT = 8796\n\nexport type BedrockServerOptions = BedrockRuntimeOptions & {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`. */\n host?: string\n}\n\nexport type BedrockServer = H2cListening & { runtime: BedrockRuntime }\n\n/**\n * Serve the Bedrock mock on one port that speaks both h2c (the AWS SDK's default\n * `NodeHttp2Handler`, and Nova Sonic's duplex stream) and HTTP/1.1 (the AI SDK, AgentCore).\n */\nexport const createServer = async (options: BedrockServerOptions = {}): Promise<BedrockServer> => {\n const { port, host, ...rest } = options\n const runtime = createRuntime(rest)\n const listening = await listenH2c(runtime, {\n port: port ?? 0,\n ...(host !== undefined ? { host } : {}),\n })\n return { ...listening, runtime }\n}\n\nconst text = (value: string | boolean | undefined) =>\n typeof value === \"string\" ? value : undefined\n\n/** Scripts from `--scripts <file.json>` (Node only). */\nconst loadScripts = async (path: string): Promise<Script[]> => {\n const { readFile } = await import(\"node:fs/promises\")\n const raw = JSON.parse(await readFile(path, \"utf8\")) as unknown\n const list = Array.isArray(raw) ? raw : (raw as { scripts?: unknown[] }).scripts\n if (!Array.isArray(list)) throw new Error(`${path}: expected {\"scripts\": [...]}`)\n return list.map((each, index) => {\n const parsed = parseScript(each, index)\n if (typeof parsed === \"string\") throw new Error(`${path}: ${parsed}`)\n return parsed\n })\n}\n\n/**\n * How `serve` builds the Bedrock mock from flags. `serve --config` in another service's CLI\n * listens over HTTP/1.1 only; `mockingbird-bedrock serve` listens with h2c as well.\n */\nexport const serveTarget: ServeTarget = {\n name: \"bedrock\",\n defaultPort: DEFAULT_PORT,\n options: {\n scripts: {\n type: \"string\",\n value: \"<file.json>\",\n description:\n 'Scripts every namespace starts with ({\"scripts\": [...]}, as PUT /__admin/scripts)',\n },\n \"default-text\": {\n type: \"string\",\n value: \"<text>\",\n description: 'What an unscripted chat call answers (default \"OK.\")',\n },\n },\n create: async (values, common) => {\n const scriptsPath = text(values.scripts)\n const defaultText = text(values[\"default-text\"])\n return createRuntime({\n ...(scriptsPath ? { scripts: await loadScripts(scriptsPath) } : {}),\n ...(defaultText !== undefined ? { settings: { defaultText } } : {}),\n ...(common.adminPrefix !== undefined ? { adminPrefix: common.adminPrefix } : {}),\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n ...(common.adminControls !== undefined ? { adminControls: common.adminControls } : {}),\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(common.onLog ? { onLog: common.onLog } : {}),\n }) as never\n },\n banner: () => [\n \"point the app at it: AWS_ENDPOINT_URL_BEDROCK_RUNTIME / AWS_ENDPOINT_URL_BEDROCK_AGENTCORE = this URL\",\n \"protocols: h2c (prior knowledge) and HTTP/1.1 on the same port\",\n \"namespaces: x-mockingbird-namespace, /__admin/ns/<name>/\u2026, or PUT /__admin/credentials {<AWS_ACCESS_KEY_ID>: <ns>}\",\n \"scripts: PUT /__admin/scripts {scripts: [{id, match, turns}]}\",\n ],\n}\n\nexport type { H2cListening, H2cListenOptions } from \"./h2c.js\"\nexport { listenH2c } from \"./h2c.js\"\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }