@t54-labs/clawcredit-blockrun-sdk 0.1.2 → 0.1.4

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.
@@ -108,6 +108,34 @@ function createClawCreditFetch(config) {
108
108
 
109
109
  // src/server.ts
110
110
  import { createServer } from "http";
111
+ import { appendFileSync, mkdirSync } from "fs";
112
+ import { dirname } from "path";
113
+ import { randomUUID } from "crypto";
114
+ var VALID_ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool", "function"]);
115
+ var ROLE_MAPPINGS = {
116
+ developer: "system",
117
+ model: "assistant"
118
+ };
119
+ function normalizeMessageRoles(messages) {
120
+ if (messages.length === 0) return messages;
121
+ let hasChanges = false;
122
+ const normalized = messages.map((msg) => {
123
+ const role = msg.role;
124
+ if (typeof role !== "string") {
125
+ hasChanges = true;
126
+ return { ...msg, role: "user" };
127
+ }
128
+ if (VALID_ROLES.has(role)) return msg;
129
+ const mappedRole = ROLE_MAPPINGS[role];
130
+ if (mappedRole) {
131
+ hasChanges = true;
132
+ return { ...msg, role: mappedRole };
133
+ }
134
+ hasChanges = true;
135
+ return { ...msg, role: "user" };
136
+ });
137
+ return hasChanges ? normalized : messages;
138
+ }
111
139
  function readBody(req) {
112
140
  return new Promise((resolve, reject) => {
113
141
  const chunks = [];
@@ -135,18 +163,128 @@ function extractMaxTokens(body) {
135
163
  function normalizePayloadForGateway(body) {
136
164
  try {
137
165
  const parsed = JSON.parse(body.toString("utf-8"));
166
+ const requestedStream = parsed.stream === true;
138
167
  if (parsed.stream === true) parsed.stream = false;
139
- return Buffer.from(JSON.stringify(parsed));
168
+ if (Array.isArray(parsed.messages)) {
169
+ parsed.messages = normalizeMessageRoles(parsed.messages);
170
+ }
171
+ return { body: Buffer.from(JSON.stringify(parsed)), requestedStream };
140
172
  } catch {
141
- return body;
173
+ return { body, requestedStream: false };
142
174
  }
143
175
  }
176
+ function convertJsonCompletionToSseBody(responseBody) {
177
+ let payload = null;
178
+ try {
179
+ payload = JSON.parse(responseBody);
180
+ } catch {
181
+ payload = null;
182
+ }
183
+ if (!payload || !Array.isArray(payload.choices) || payload.choices.length === 0) {
184
+ return `data: ${responseBody}
185
+
186
+ data: [DONE]
187
+
188
+ `;
189
+ }
190
+ const created = payload.created ?? Math.floor(Date.now() / 1e3);
191
+ const baseChunk = {
192
+ id: payload.id ?? `chatcmpl-${Date.now()}`,
193
+ object: "chat.completion.chunk",
194
+ created,
195
+ model: payload.model ?? "unknown",
196
+ system_fingerprint: null
197
+ };
198
+ const out = [];
199
+ for (const choice of payload.choices) {
200
+ const index = Number.isFinite(choice.index) ? Number(choice.index) : 0;
201
+ const role = choice.message?.role ?? choice.delta?.role ?? "assistant";
202
+ const content = choice.message?.content ?? choice.delta?.content ?? "";
203
+ const toolCalls = choice.message?.tool_calls ?? choice.delta?.tool_calls;
204
+ out.push(
205
+ `data: ${JSON.stringify({
206
+ ...baseChunk,
207
+ choices: [{ index, delta: { role }, logprobs: null, finish_reason: null }]
208
+ })}
209
+
210
+ `
211
+ );
212
+ if (typeof content === "string" && content.length > 0) {
213
+ out.push(
214
+ `data: ${JSON.stringify({
215
+ ...baseChunk,
216
+ choices: [{ index, delta: { content }, logprobs: null, finish_reason: null }]
217
+ })}
218
+
219
+ `
220
+ );
221
+ }
222
+ if (Array.isArray(toolCalls) && toolCalls.length > 0) {
223
+ out.push(
224
+ `data: ${JSON.stringify({
225
+ ...baseChunk,
226
+ choices: [{ index, delta: { tool_calls: toolCalls }, logprobs: null, finish_reason: null }]
227
+ })}
228
+
229
+ `
230
+ );
231
+ }
232
+ out.push(
233
+ `data: ${JSON.stringify({
234
+ ...baseChunk,
235
+ choices: [
236
+ {
237
+ index,
238
+ delta: {},
239
+ logprobs: null,
240
+ finish_reason: Array.isArray(toolCalls) && toolCalls.length > 0 ? "tool_calls" : choice.finish_reason ?? "stop"
241
+ }
242
+ ]
243
+ })}
244
+
245
+ `
246
+ );
247
+ }
248
+ out.push("data: [DONE]\n\n");
249
+ return out.join("");
250
+ }
251
+ function parseJsonIfPossible(text) {
252
+ try {
253
+ return JSON.parse(text);
254
+ } catch {
255
+ return text;
256
+ }
257
+ }
258
+ function safeHeadersObject(headers) {
259
+ const out = {};
260
+ headers.forEach((value, key) => {
261
+ out[key] = value;
262
+ });
263
+ return out;
264
+ }
265
+ function createCaptureWriter() {
266
+ const enabled = process.env.GATEWAY_CAPTURE === "1" || process.env.GATEWAY_CAPTURE === "true";
267
+ const file = (process.env.GATEWAY_CAPTURE_FILE || "/tmp/clawcredit-blockrun-gateway/.run/capture.jsonl").trim();
268
+ if (!enabled) {
269
+ return (_entry) => {
270
+ };
271
+ }
272
+ mkdirSync(dirname(file), { recursive: true });
273
+ return (entry) => {
274
+ try {
275
+ appendFileSync(file, `${JSON.stringify(entry)}
276
+ `, "utf-8");
277
+ } catch {
278
+ }
279
+ };
280
+ }
144
281
  async function startGateway(options) {
145
282
  const host = options.host ?? "127.0.0.1";
146
283
  const port = options.port ?? 3402;
147
284
  const apiBase = (options.blockrunApiBase ?? "https://blockrun.ai/api").replace(/\/+$/, "");
148
285
  const defaultAmountUsd = Number.isFinite(options.defaultAmountUsd) ? Number(options.defaultAmountUsd) : 0.1;
149
286
  const payFetch = createClawCreditFetch(options.clawCredit);
287
+ const writeCapture = createCaptureWriter();
150
288
  const server = createServer(async (req, res) => {
151
289
  if (req.url === "/health") {
152
290
  return sendJson(res, 200, {
@@ -159,8 +297,13 @@ async function startGateway(options) {
159
297
  return sendJson(res, 404, { error: "Not found" });
160
298
  }
161
299
  try {
300
+ const requestId = randomUUID();
301
+ const startedAt = Date.now();
162
302
  const body = await readBody(req);
163
- const normalized = normalizePayloadForGateway(body);
303
+ const normalizedReq = normalizePayloadForGateway(body);
304
+ const normalized = normalizedReq.body;
305
+ const requestedStream = normalizedReq.requestedStream;
306
+ const normalizedText = normalized.toString("utf-8");
164
307
  const maxTokens = extractMaxTokens(normalized);
165
308
  const estimatedMicros = String(
166
309
  Math.max(1e4, Math.round(defaultAmountUsd * 1e6 + maxTokens * 8))
@@ -176,6 +319,20 @@ async function startGateway(options) {
176
319
  if (!headers.has("content-type")) {
177
320
  headers.set("content-type", "application/json");
178
321
  }
322
+ writeCapture({
323
+ kind: "request",
324
+ requestId,
325
+ at: (/* @__PURE__ */ new Date()).toISOString(),
326
+ source: {
327
+ userAgent: req.headers["user-agent"] || null,
328
+ xOpenClawSession: req.headers["x-openclaw-session-id"] || null
329
+ },
330
+ method: "POST",
331
+ target: upstreamUrl,
332
+ estimatedMicros,
333
+ headers: safeHeadersObject(headers),
334
+ body: parseJsonIfPossible(normalizedText)
335
+ });
179
336
  const upstream = await payFetch(
180
337
  upstreamUrl,
181
338
  {
@@ -186,12 +343,36 @@ async function startGateway(options) {
186
343
  { estimatedAmount: estimatedMicros }
187
344
  );
188
345
  const responseBody = await upstream.text();
346
+ writeCapture({
347
+ kind: "response",
348
+ requestId,
349
+ at: (/* @__PURE__ */ new Date()).toISOString(),
350
+ durationMs: Date.now() - startedAt,
351
+ status: upstream.status,
352
+ headers: safeHeadersObject(upstream.headers),
353
+ body: parseJsonIfPossible(responseBody)
354
+ });
355
+ if (requestedStream && upstream.ok) {
356
+ const sseBody = convertJsonCompletionToSseBody(responseBody);
357
+ res.writeHead(200, {
358
+ "content-type": "text/event-stream",
359
+ "cache-control": "no-cache",
360
+ connection: "keep-alive"
361
+ });
362
+ res.end(sseBody);
363
+ return;
364
+ }
189
365
  res.writeHead(upstream.status, {
190
366
  "content-type": upstream.headers.get("content-type") || "application/json"
191
367
  });
192
368
  res.end(responseBody);
193
369
  } catch (err) {
194
370
  const msg = err instanceof Error ? err.message : String(err);
371
+ writeCapture({
372
+ kind: "error",
373
+ at: (/* @__PURE__ */ new Date()).toISOString(),
374
+ message: msg
375
+ });
195
376
  sendJson(res, 502, { error: msg });
196
377
  }
197
378
  });
@@ -214,4 +395,4 @@ export {
214
395
  createClawCreditFetch,
215
396
  startGateway
216
397
  };
217
- //# sourceMappingURL=chunk-K57GMQDY.js.map
398
+ //# sourceMappingURL=chunk-ZMPGMK4C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/clawcredit.ts","../src/server.ts"],"sourcesContent":["import { ClawCredit, withTrace } from \"@t54-labs/clawcredit-sdk\";\n\nconst DEFAULT_SERVICE_URL = \"https://api.claw.credit\";\n\nexport type ClawCreditConfig = {\n baseUrl?: string;\n apiToken: string;\n chain: string;\n asset: string;\n agent?: string;\n agentId?: string;\n};\n\nexport type PreAuthParams = {\n estimatedAmount: string;\n};\n\ntype SdkClient = {\n pay: (args: {\n transaction: {\n recipient: string;\n amount: number;\n chain: string;\n asset: string;\n amount_unit?: \"human\" | \"atomic\";\n };\n request_body: Record<string, unknown>;\n context?: {\n reasoning_process?: string;\n current_task?: string;\n };\n idempotencyKey?: string;\n }) => Promise<{ merchant_response?: unknown }>;\n};\n\nfunction headersToObject(headersInit?: HeadersInit): Record<string, string> {\n if (!headersInit) return {};\n const headers = new Headers(headersInit);\n const out: Record<string, string> = {};\n headers.forEach((value, key) => {\n const lower = key.toLowerCase();\n if (lower === \"host\" || lower === \"content-length\" || lower === \"connection\") return;\n out[key] = value;\n });\n return out;\n}\n\nfunction parseJsonBody(body: RequestInit[\"body\"]): unknown {\n if (body == null) return undefined;\n\n let raw = \"\";\n if (typeof body === \"string\") {\n raw = body;\n } else if (body instanceof Uint8Array) {\n raw = Buffer.from(body).toString(\"utf-8\");\n } else if (body instanceof ArrayBuffer) {\n raw = Buffer.from(body).toString(\"utf-8\");\n } else {\n return undefined;\n }\n\n if (!raw.trim()) return undefined;\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n}\n\nfunction microsToUsd(estimatedAmount?: string): number {\n const micros = Number(estimatedAmount ?? \"\");\n if (!Number.isFinite(micros) || micros <= 0) return 0.01;\n return Number((micros / 1_000_000).toFixed(6));\n}\n\nfunction inferStatusCode(err: unknown): number {\n const msg = err instanceof Error ? err.message : String(err);\n const match = msg.match(/ClawCredit API Error:\\s*(\\d{3})\\s*-/i);\n if (match) return parseInt(match[1], 10);\n if (/payment required/i.test(msg)) return 402;\n if (/prequalification_pending/i.test(msg)) return 403;\n if (/unauthorized/i.test(msg)) return 401;\n return 502;\n}\n\nexport function createClawCreditFetch(config: ClawCreditConfig) {\n const serviceUrl = (config.baseUrl || DEFAULT_SERVICE_URL).replace(/\\/+$/, \"\");\n const chain = config.chain.toUpperCase();\n const asset = config.asset;\n const apiToken = config.apiToken.trim();\n\n if (!apiToken) {\n throw new Error(\"CLAWCREDIT_API_TOKEN is required for claw.credit payment mode\");\n }\n\n const credit = new ClawCredit({\n serviceUrl,\n apiToken,\n agent: config.agent,\n agentId: config.agentId,\n }) as SdkClient;\n\n return async (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ): Promise<Response> => {\n const upstreamUrl =\n typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n const method = (init?.method || \"POST\").toUpperCase();\n const headers = headersToObject(init?.headers);\n const idempotencyKey = new Headers(init?.headers).get(\"idempotency-key\") || undefined;\n const requestBody = parseJsonBody(init?.body);\n const amountUsd = microsToUsd(preAuth?.estimatedAmount);\n\n try {\n const result = await withTrace(async () =>\n credit.pay({\n transaction: {\n recipient: upstreamUrl,\n amount: amountUsd,\n chain,\n asset,\n },\n request_body: {\n http: {\n url: upstreamUrl,\n method,\n headers,\n },\n body: requestBody,\n },\n context: {\n current_task: \"blockrun_inference_via_clawcredit_blockrun_gateway\",\n reasoning_process: \"Pay BlockRun inference through claw.credit SDK\",\n },\n idempotencyKey,\n }),\n );\n\n const merchantResponse =\n result && typeof result === \"object\" && \"merchant_response\" in result\n ? (result as { merchant_response?: unknown }).merchant_response\n : result;\n\n return new Response(JSON.stringify(merchantResponse ?? {}), {\n status: 200,\n headers: { \"content-type\": \"application/json\" },\n });\n } catch (err) {\n const status = inferStatusCode(err);\n const message = err instanceof Error ? err.message : String(err);\n return new Response(JSON.stringify({ error: message }), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n };\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { appendFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { createClawCreditFetch, type ClawCreditConfig } from \"./clawcredit.js\";\n\nexport type GatewayOptions = {\n port?: number;\n host?: string;\n blockrunApiBase?: string;\n clawCredit: ClawCreditConfig;\n defaultAmountUsd?: number;\n};\n\nexport type GatewayInstance = {\n port: number;\n baseUrl: string;\n close: () => Promise<void>;\n};\n\ntype CompletionRequest = {\n model?: string;\n stream?: boolean;\n max_tokens?: number;\n messages?: Array<Record<string, unknown>>;\n};\n\ntype NormalizedGatewayRequest = {\n body: Buffer;\n requestedStream: boolean;\n};\n\ntype CompletionResponseChoice = {\n index?: number;\n message?: {\n role?: string;\n content?: string;\n tool_calls?: unknown[];\n };\n delta?: {\n role?: string;\n content?: string;\n tool_calls?: unknown[];\n };\n finish_reason?: string | null;\n};\n\ntype CompletionResponse = {\n id?: string;\n object?: string;\n created?: number;\n model?: string;\n choices?: CompletionResponseChoice[];\n};\n\nconst VALID_ROLES = new Set([\"system\", \"user\", \"assistant\", \"tool\", \"function\"]);\nconst ROLE_MAPPINGS: Record<string, string> = {\n developer: \"system\",\n model: \"assistant\",\n};\n\nfunction normalizeMessageRoles(messages: Array<Record<string, unknown>>): Array<Record<string, unknown>> {\n if (messages.length === 0) return messages;\n\n let hasChanges = false;\n const normalized = messages.map((msg) => {\n const role = msg.role;\n if (typeof role !== \"string\") {\n hasChanges = true;\n return { ...msg, role: \"user\" };\n }\n if (VALID_ROLES.has(role)) return msg;\n\n const mappedRole = ROLE_MAPPINGS[role];\n if (mappedRole) {\n hasChanges = true;\n return { ...msg, role: mappedRole };\n }\n\n hasChanges = true;\n return { ...msg, role: \"user\" };\n });\n\n return hasChanges ? normalized : messages;\n}\n\nfunction readBody(req: IncomingMessage): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));\n req.on(\"end\", () => resolve(Buffer.concat(chunks)));\n req.on(\"error\", reject);\n });\n}\n\nfunction sendJson(res: ServerResponse, status: number, payload: unknown): void {\n const text = JSON.stringify(payload);\n res.writeHead(status, {\n \"content-type\": \"application/json\",\n \"content-length\": Buffer.byteLength(text).toString(),\n });\n res.end(text);\n}\n\nfunction extractMaxTokens(body: Buffer): number {\n try {\n const parsed = JSON.parse(body.toString(\"utf-8\")) as CompletionRequest;\n return parsed.max_tokens && Number.isFinite(parsed.max_tokens) ? parsed.max_tokens : 512;\n } catch {\n return 512;\n }\n}\n\nfunction normalizePayloadForGateway(body: Buffer): NormalizedGatewayRequest {\n try {\n const parsed = JSON.parse(body.toString(\"utf-8\")) as CompletionRequest;\n const requestedStream = parsed.stream === true;\n if (parsed.stream === true) parsed.stream = false;\n if (Array.isArray(parsed.messages)) {\n parsed.messages = normalizeMessageRoles(parsed.messages);\n }\n return { body: Buffer.from(JSON.stringify(parsed)), requestedStream };\n } catch {\n return { body, requestedStream: false };\n }\n}\n\nfunction convertJsonCompletionToSseBody(responseBody: string): string {\n let payload: CompletionResponse | null = null;\n try {\n payload = JSON.parse(responseBody) as CompletionResponse;\n } catch {\n payload = null;\n }\n\n if (!payload || !Array.isArray(payload.choices) || payload.choices.length === 0) {\n return `data: ${responseBody}\\n\\ndata: [DONE]\\n\\n`;\n }\n\n const created = payload.created ?? Math.floor(Date.now() / 1000);\n const baseChunk = {\n id: payload.id ?? `chatcmpl-${Date.now()}`,\n object: \"chat.completion.chunk\",\n created,\n model: payload.model ?? \"unknown\",\n system_fingerprint: null,\n };\n\n const out: string[] = [];\n for (const choice of payload.choices) {\n const index = Number.isFinite(choice.index) ? Number(choice.index) : 0;\n const role = choice.message?.role ?? choice.delta?.role ?? \"assistant\";\n const content = choice.message?.content ?? choice.delta?.content ?? \"\";\n const toolCalls = choice.message?.tool_calls ?? choice.delta?.tool_calls;\n\n out.push(\n `data: ${JSON.stringify({\n ...baseChunk,\n choices: [{ index, delta: { role }, logprobs: null, finish_reason: null }],\n })}\\n\\n`,\n );\n\n if (typeof content === \"string\" && content.length > 0) {\n out.push(\n `data: ${JSON.stringify({\n ...baseChunk,\n choices: [{ index, delta: { content }, logprobs: null, finish_reason: null }],\n })}\\n\\n`,\n );\n }\n\n if (Array.isArray(toolCalls) && toolCalls.length > 0) {\n out.push(\n `data: ${JSON.stringify({\n ...baseChunk,\n choices: [{ index, delta: { tool_calls: toolCalls }, logprobs: null, finish_reason: null }],\n })}\\n\\n`,\n );\n }\n\n out.push(\n `data: ${JSON.stringify({\n ...baseChunk,\n choices: [\n {\n index,\n delta: {},\n logprobs: null,\n finish_reason:\n Array.isArray(toolCalls) && toolCalls.length > 0\n ? \"tool_calls\"\n : (choice.finish_reason ?? \"stop\"),\n },\n ],\n })}\\n\\n`,\n );\n }\n\n out.push(\"data: [DONE]\\n\\n\");\n return out.join(\"\");\n}\n\nfunction parseJsonIfPossible(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction safeHeadersObject(headers: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n headers.forEach((value, key) => {\n out[key] = value;\n });\n return out;\n}\n\nfunction createCaptureWriter() {\n const enabled =\n process.env.GATEWAY_CAPTURE === \"1\" || process.env.GATEWAY_CAPTURE === \"true\";\n const file =\n (process.env.GATEWAY_CAPTURE_FILE || \"/tmp/clawcredit-blockrun-gateway/.run/capture.jsonl\").trim();\n\n if (!enabled) {\n return (_entry: unknown) => {};\n }\n\n mkdirSync(dirname(file), { recursive: true });\n return (entry: unknown) => {\n try {\n appendFileSync(file, `${JSON.stringify(entry)}\\n`, \"utf-8\");\n } catch {\n // Best-effort debug capture only.\n }\n };\n}\n\nexport async function startGateway(options: GatewayOptions): Promise<GatewayInstance> {\n const host = options.host ?? \"127.0.0.1\";\n const port = options.port ?? 3402;\n const apiBase = (options.blockrunApiBase ?? \"https://blockrun.ai/api\").replace(/\\/+$/, \"\");\n const defaultAmountUsd = Number.isFinite(options.defaultAmountUsd)\n ? Number(options.defaultAmountUsd)\n : 0.1;\n const payFetch = createClawCreditFetch(options.clawCredit);\n const writeCapture = createCaptureWriter();\n\n const server = createServer(async (req, res) => {\n if (req.url === \"/health\") {\n return sendJson(res, 200, {\n status: \"ok\",\n service: \"clawcredit-blockrun-gateway\",\n payment_mode: \"clawcredit\",\n });\n }\n\n if (req.url !== \"/v1/chat/completions\" || req.method !== \"POST\") {\n return sendJson(res, 404, { error: \"Not found\" });\n }\n\n try {\n const requestId = randomUUID();\n const startedAt = Date.now();\n const body = await readBody(req);\n const normalizedReq = normalizePayloadForGateway(body);\n const normalized = normalizedReq.body;\n const requestedStream = normalizedReq.requestedStream;\n const normalizedText = normalized.toString(\"utf-8\");\n const maxTokens = extractMaxTokens(normalized);\n\n const estimatedMicros = String(\n Math.max(10_000, Math.round(defaultAmountUsd * 1_000_000 + maxTokens * 8)),\n );\n\n const upstreamUrl = `${apiBase}/v1/chat/completions`;\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value !== \"string\") continue;\n const lower = key.toLowerCase();\n if (lower === \"host\" || lower === \"content-length\" || lower === \"connection\") continue;\n headers.set(key, value);\n }\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n\n writeCapture({\n kind: \"request\",\n requestId,\n at: new Date().toISOString(),\n source: {\n userAgent: req.headers[\"user-agent\"] || null,\n xOpenClawSession: req.headers[\"x-openclaw-session-id\"] || null,\n },\n method: \"POST\",\n target: upstreamUrl,\n estimatedMicros,\n headers: safeHeadersObject(headers),\n body: parseJsonIfPossible(normalizedText),\n });\n\n const upstream = await payFetch(\n upstreamUrl,\n {\n method: \"POST\",\n headers,\n body: new Uint8Array(normalized),\n },\n { estimatedAmount: estimatedMicros },\n );\n\n const responseBody = await upstream.text();\n writeCapture({\n kind: \"response\",\n requestId,\n at: new Date().toISOString(),\n durationMs: Date.now() - startedAt,\n status: upstream.status,\n headers: safeHeadersObject(upstream.headers),\n body: parseJsonIfPossible(responseBody),\n });\n if (requestedStream && upstream.ok) {\n const sseBody = convertJsonCompletionToSseBody(responseBody);\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n });\n res.end(sseBody);\n return;\n }\n\n res.writeHead(upstream.status, {\n \"content-type\": upstream.headers.get(\"content-type\") || \"application/json\",\n });\n res.end(responseBody);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n writeCapture({\n kind: \"error\",\n at: new Date().toISOString(),\n message: msg,\n });\n sendJson(res, 502, { error: msg });\n }\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.removeListener(\"error\", reject);\n resolve();\n });\n });\n\n const actualPort = (server.address() as AddressInfo).port;\n return {\n port: actualPort,\n baseUrl: `http://${host}:${actualPort}`,\n close: () => new Promise<void>((resolve, reject) => server.close((err) => (err ? reject(err) : resolve()))),\n };\n}\n"],"mappings":";AAAA,SAAS,YAAY,iBAAiB;AAEtC,IAAM,sBAAsB;AAiC5B,SAAS,gBAAgB,aAAmD;AAC1E,MAAI,CAAC,YAAa,QAAO,CAAC;AAC1B,QAAM,UAAU,IAAI,QAAQ,WAAW;AACvC,QAAM,MAA8B,CAAC;AACrC,UAAQ,QAAQ,CAAC,OAAO,QAAQ;AAC9B,UAAM,QAAQ,IAAI,YAAY;AAC9B,QAAI,UAAU,UAAU,UAAU,oBAAoB,UAAU,aAAc;AAC9E,QAAI,GAAG,IAAI;AAAA,EACb,CAAC;AACD,SAAO;AACT;AAEA,SAAS,cAAc,MAAoC;AACzD,MAAI,QAAQ,KAAM,QAAO;AAEzB,MAAI,MAAM;AACV,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM;AAAA,EACR,WAAW,gBAAgB,YAAY;AACrC,UAAM,OAAO,KAAK,IAAI,EAAE,SAAS,OAAO;AAAA,EAC1C,WAAW,gBAAgB,aAAa;AACtC,UAAM,OAAO,KAAK,IAAI,EAAE,SAAS,OAAO;AAAA,EAC1C,OAAO;AACL,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,IAAI,KAAK,EAAG,QAAO;AACxB,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,iBAAkC;AACrD,QAAM,SAAS,OAAO,mBAAmB,EAAE;AAC3C,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,EAAG,QAAO;AACpD,SAAO,QAAQ,SAAS,KAAW,QAAQ,CAAC,CAAC;AAC/C;AAEA,SAAS,gBAAgB,KAAsB;AAC7C,QAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,QAAM,QAAQ,IAAI,MAAM,sCAAsC;AAC9D,MAAI,MAAO,QAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AACvC,MAAI,oBAAoB,KAAK,GAAG,EAAG,QAAO;AAC1C,MAAI,4BAA4B,KAAK,GAAG,EAAG,QAAO;AAClD,MAAI,gBAAgB,KAAK,GAAG,EAAG,QAAO;AACtC,SAAO;AACT;AAEO,SAAS,sBAAsB,QAA0B;AAC9D,QAAM,cAAc,OAAO,WAAW,qBAAqB,QAAQ,QAAQ,EAAE;AAC7E,QAAM,QAAQ,OAAO,MAAM,YAAY;AACvC,QAAM,QAAQ,OAAO;AACrB,QAAM,WAAW,OAAO,SAAS,KAAK;AAEtC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AAEA,QAAM,SAAS,IAAI,WAAW;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,OAAO,OAAO;AAAA,IACd,SAAS,OAAO;AAAA,EAClB,CAAC;AAED,SAAO,OACL,OACA,MACA,YACsB;AACtB,UAAM,cACJ,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,OAAO,MAAM;AAChF,UAAM,UAAU,MAAM,UAAU,QAAQ,YAAY;AACpD,UAAM,UAAU,gBAAgB,MAAM,OAAO;AAC7C,UAAM,iBAAiB,IAAI,QAAQ,MAAM,OAAO,EAAE,IAAI,iBAAiB,KAAK;AAC5E,UAAM,cAAc,cAAc,MAAM,IAAI;AAC5C,UAAM,YAAY,YAAY,SAAS,eAAe;AAEtD,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QAAU,YAC7B,OAAO,IAAI;AAAA,UACT,aAAa;AAAA,YACX,WAAW;AAAA,YACX,QAAQ;AAAA,YACR;AAAA,YACA;AAAA,UACF;AAAA,UACA,cAAc;AAAA,YACZ,MAAM;AAAA,cACJ,KAAK;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,YACA,MAAM;AAAA,UACR;AAAA,UACA,SAAS;AAAA,YACP,cAAc;AAAA,YACd,mBAAmB;AAAA,UACrB;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAEA,YAAM,mBACJ,UAAU,OAAO,WAAW,YAAY,uBAAuB,SAC1D,OAA2C,oBAC5C;AAEN,aAAO,IAAI,SAAS,KAAK,UAAU,oBAAoB,CAAC,CAAC,GAAG;AAAA,QAC1D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAChD,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS,gBAAgB,GAAG;AAClC,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,QAAQ,CAAC,GAAG;AAAA,QACtD;AAAA,QACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAChD,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AC9JA,SAAS,oBAA+D;AAExE,SAAS,gBAAgB,iBAAiB;AAC1C,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAoD3B,IAAM,cAAc,oBAAI,IAAI,CAAC,UAAU,QAAQ,aAAa,QAAQ,UAAU,CAAC;AAC/E,IAAM,gBAAwC;AAAA,EAC5C,WAAW;AAAA,EACX,OAAO;AACT;AAEA,SAAS,sBAAsB,UAA0E;AACvG,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,MAAI,aAAa;AACjB,QAAM,aAAa,SAAS,IAAI,CAAC,QAAQ;AACvC,UAAM,OAAO,IAAI;AACjB,QAAI,OAAO,SAAS,UAAU;AAC5B,mBAAa;AACb,aAAO,EAAE,GAAG,KAAK,MAAM,OAAO;AAAA,IAChC;AACA,QAAI,YAAY,IAAI,IAAI,EAAG,QAAO;AAElC,UAAM,aAAa,cAAc,IAAI;AACrC,QAAI,YAAY;AACd,mBAAa;AACb,aAAO,EAAE,GAAG,KAAK,MAAM,WAAW;AAAA,IACpC;AAEA,iBAAa;AACb,WAAO,EAAE,GAAG,KAAK,MAAM,OAAO;AAAA,EAChC,CAAC;AAED,SAAO,aAAa,aAAa;AACnC;AAEA,SAAS,SAAS,KAAuC;AACvD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAmB,CAAC;AAC1B,QAAI,GAAG,QAAQ,CAAC,UAAU,OAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK,CAAC,CAAC;AAC1F,QAAI,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC;AAClD,QAAI,GAAG,SAAS,MAAM;AAAA,EACxB,CAAC;AACH;AAEA,SAAS,SAAS,KAAqB,QAAgB,SAAwB;AAC7E,QAAM,OAAO,KAAK,UAAU,OAAO;AACnC,MAAI,UAAU,QAAQ;AAAA,IACpB,gBAAgB;AAAA,IAChB,kBAAkB,OAAO,WAAW,IAAI,EAAE,SAAS;AAAA,EACrD,CAAC;AACD,MAAI,IAAI,IAAI;AACd;AAEA,SAAS,iBAAiB,MAAsB;AAC9C,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,KAAK,SAAS,OAAO,CAAC;AAChD,WAAO,OAAO,cAAc,OAAO,SAAS,OAAO,UAAU,IAAI,OAAO,aAAa;AAAA,EACvF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,2BAA2B,MAAwC;AAC1E,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,KAAK,SAAS,OAAO,CAAC;AAChD,UAAM,kBAAkB,OAAO,WAAW;AAC1C,QAAI,OAAO,WAAW,KAAM,QAAO,SAAS;AAC5C,QAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG;AAClC,aAAO,WAAW,sBAAsB,OAAO,QAAQ;AAAA,IACzD;AACA,WAAO,EAAE,MAAM,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC,GAAG,gBAAgB;AAAA,EACtE,QAAQ;AACN,WAAO,EAAE,MAAM,iBAAiB,MAAM;AAAA,EACxC;AACF;AAEA,SAAS,+BAA+B,cAA8B;AACpE,MAAI,UAAqC;AACzC,MAAI;AACF,cAAU,KAAK,MAAM,YAAY;AAAA,EACnC,QAAQ;AACN,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,WAAW,CAAC,MAAM,QAAQ,QAAQ,OAAO,KAAK,QAAQ,QAAQ,WAAW,GAAG;AAC/E,WAAO,SAAS,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAC9B;AAEA,QAAM,UAAU,QAAQ,WAAW,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAC/D,QAAM,YAAY;AAAA,IAChB,IAAI,QAAQ,MAAM,YAAY,KAAK,IAAI,CAAC;AAAA,IACxC,QAAQ;AAAA,IACR;AAAA,IACA,OAAO,QAAQ,SAAS;AAAA,IACxB,oBAAoB;AAAA,EACtB;AAEA,QAAM,MAAgB,CAAC;AACvB,aAAW,UAAU,QAAQ,SAAS;AACpC,UAAM,QAAQ,OAAO,SAAS,OAAO,KAAK,IAAI,OAAO,OAAO,KAAK,IAAI;AACrE,UAAM,OAAO,OAAO,SAAS,QAAQ,OAAO,OAAO,QAAQ;AAC3D,UAAM,UAAU,OAAO,SAAS,WAAW,OAAO,OAAO,WAAW;AACpE,UAAM,YAAY,OAAO,SAAS,cAAc,OAAO,OAAO;AAE9D,QAAI;AAAA,MACF,SAAS,KAAK,UAAU;AAAA,QACtB,GAAG;AAAA,QACH,SAAS,CAAC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,UAAU,MAAM,eAAe,KAAK,CAAC;AAAA,MAC3E,CAAC,CAAC;AAAA;AAAA;AAAA,IACJ;AAEA,QAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,GAAG;AACrD,UAAI;AAAA,QACF,SAAS,KAAK,UAAU;AAAA,UACtB,GAAG;AAAA,UACH,SAAS,CAAC,EAAE,OAAO,OAAO,EAAE,QAAQ,GAAG,UAAU,MAAM,eAAe,KAAK,CAAC;AAAA,QAC9E,CAAC,CAAC;AAAA;AAAA;AAAA,MACJ;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,SAAS,KAAK,UAAU,SAAS,GAAG;AACpD,UAAI;AAAA,QACF,SAAS,KAAK,UAAU;AAAA,UACtB,GAAG;AAAA,UACH,SAAS,CAAC,EAAE,OAAO,OAAO,EAAE,YAAY,UAAU,GAAG,UAAU,MAAM,eAAe,KAAK,CAAC;AAAA,QAC5F,CAAC,CAAC;AAAA;AAAA;AAAA,MACJ;AAAA,IACF;AAEA,QAAI;AAAA,MACF,SAAS,KAAK,UAAU;AAAA,QACtB,GAAG;AAAA,QACH,SAAS;AAAA,UACP;AAAA,YACE;AAAA,YACA,OAAO,CAAC;AAAA,YACR,UAAU;AAAA,YACV,eACE,MAAM,QAAQ,SAAS,KAAK,UAAU,SAAS,IAC3C,eACC,OAAO,iBAAiB;AAAA,UACjC;AAAA,QACF;AAAA,MACF,CAAC,CAAC;AAAA;AAAA;AAAA,IACJ;AAAA,EACF;AAEA,MAAI,KAAK,kBAAkB;AAC3B,SAAO,IAAI,KAAK,EAAE;AACpB;AAEA,SAAS,oBAAoB,MAAuB;AAClD,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,SAA0C;AACnE,QAAM,MAA8B,CAAC;AACrC,UAAQ,QAAQ,CAAC,OAAO,QAAQ;AAC9B,QAAI,GAAG,IAAI;AAAA,EACb,CAAC;AACD,SAAO;AACT;AAEA,SAAS,sBAAsB;AAC7B,QAAM,UACJ,QAAQ,IAAI,oBAAoB,OAAO,QAAQ,IAAI,oBAAoB;AACzE,QAAM,QACH,QAAQ,IAAI,wBAAwB,uDAAuD,KAAK;AAEnG,MAAI,CAAC,SAAS;AACZ,WAAO,CAAC,WAAoB;AAAA,IAAC;AAAA,EAC/B;AAEA,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,SAAO,CAAC,UAAmB;AACzB,QAAI;AACF,qBAAe,MAAM,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,GAAM,OAAO;AAAA,IAC5D,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEA,eAAsB,aAAa,SAAmD;AACpF,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,WAAW,QAAQ,mBAAmB,2BAA2B,QAAQ,QAAQ,EAAE;AACzF,QAAM,mBAAmB,OAAO,SAAS,QAAQ,gBAAgB,IAC7D,OAAO,QAAQ,gBAAgB,IAC/B;AACJ,QAAM,WAAW,sBAAsB,QAAQ,UAAU;AACzD,QAAM,eAAe,oBAAoB;AAEzC,QAAM,SAAS,aAAa,OAAO,KAAK,QAAQ;AAC9C,QAAI,IAAI,QAAQ,WAAW;AACzB,aAAO,SAAS,KAAK,KAAK;AAAA,QACxB,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,cAAc;AAAA,MAChB,CAAC;AAAA,IACH;AAEA,QAAI,IAAI,QAAQ,0BAA0B,IAAI,WAAW,QAAQ;AAC/D,aAAO,SAAS,KAAK,KAAK,EAAE,OAAO,YAAY,CAAC;AAAA,IAClD;AAEA,QAAI;AACF,YAAM,YAAY,WAAW;AAC7B,YAAM,YAAY,KAAK,IAAI;AAC3B,YAAM,OAAO,MAAM,SAAS,GAAG;AAC/B,YAAM,gBAAgB,2BAA2B,IAAI;AACrD,YAAM,aAAa,cAAc;AACjC,YAAM,kBAAkB,cAAc;AACtC,YAAM,iBAAiB,WAAW,SAAS,OAAO;AAClD,YAAM,YAAY,iBAAiB,UAAU;AAE7C,YAAM,kBAAkB;AAAA,QACtB,KAAK,IAAI,KAAQ,KAAK,MAAM,mBAAmB,MAAY,YAAY,CAAC,CAAC;AAAA,MAC3E;AAEA,YAAM,cAAc,GAAG,OAAO;AAE9B,YAAM,UAAU,IAAI,QAAQ;AAC5B,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,OAAO,GAAG;AACtD,YAAI,OAAO,UAAU,SAAU;AAC/B,cAAM,QAAQ,IAAI,YAAY;AAC9B,YAAI,UAAU,UAAU,UAAU,oBAAoB,UAAU,aAAc;AAC9E,gBAAQ,IAAI,KAAK,KAAK;AAAA,MACxB;AACA,UAAI,CAAC,QAAQ,IAAI,cAAc,GAAG;AAChC,gBAAQ,IAAI,gBAAgB,kBAAkB;AAAA,MAChD;AAEA,mBAAa;AAAA,QACX,MAAM;AAAA,QACN;AAAA,QACA,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC3B,QAAQ;AAAA,UACN,WAAW,IAAI,QAAQ,YAAY,KAAK;AAAA,UACxC,kBAAkB,IAAI,QAAQ,uBAAuB,KAAK;AAAA,QAC5D;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,QACA,SAAS,kBAAkB,OAAO;AAAA,QAClC,MAAM,oBAAoB,cAAc;AAAA,MAC1C,CAAC;AAED,YAAM,WAAW,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR;AAAA,UACA,MAAM,IAAI,WAAW,UAAU;AAAA,QACjC;AAAA,QACA,EAAE,iBAAiB,gBAAgB;AAAA,MACrC;AAEA,YAAM,eAAe,MAAM,SAAS,KAAK;AACzC,mBAAa;AAAA,QACX,MAAM;AAAA,QACN;AAAA,QACA,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC3B,YAAY,KAAK,IAAI,IAAI;AAAA,QACzB,QAAQ,SAAS;AAAA,QACjB,SAAS,kBAAkB,SAAS,OAAO;AAAA,QAC3C,MAAM,oBAAoB,YAAY;AAAA,MACxC,CAAC;AACD,UAAI,mBAAmB,SAAS,IAAI;AAClC,cAAM,UAAU,+BAA+B,YAAY;AAC3D,YAAI,UAAU,KAAK;AAAA,UACjB,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,UACjB,YAAY;AAAA,QACd,CAAC;AACD,YAAI,IAAI,OAAO;AACf;AAAA,MACF;AAEA,UAAI,UAAU,SAAS,QAAQ;AAAA,QAC7B,gBAAgB,SAAS,QAAQ,IAAI,cAAc,KAAK;AAAA,MAC1D,CAAC;AACD,UAAI,IAAI,YAAY;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,mBAAa;AAAA,QACX,MAAM;AAAA,QACN,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC3B,SAAS;AAAA,MACX,CAAC;AACD,eAAS,KAAK,KAAK,EAAE,OAAO,IAAI,CAAC;AAAA,IACnC;AAAA,EACF,CAAC;AAED,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAO,KAAK,SAAS,MAAM;AAC3B,WAAO,OAAO,MAAM,MAAM,MAAM;AAC9B,aAAO,eAAe,SAAS,MAAM;AACrC,cAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AAED,QAAM,aAAc,OAAO,QAAQ,EAAkB;AACrD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,UAAU,IAAI,IAAI,UAAU;AAAA,IACrC,OAAO,MAAM,IAAI,QAAc,CAAC,SAAS,WAAW,OAAO,MAAM,CAAC,QAAS,MAAM,OAAO,GAAG,IAAI,QAAQ,CAAE,CAAC;AAAA,EAC5G;AACF;","names":[]}
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startGateway
4
- } from "./chunk-K57GMQDY.js";
4
+ } from "./chunk-ZMPGMK4C.js";
5
5
 
6
6
  // src/cli.ts
7
7
  var DEFAULT_ASSET = "USDC";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createClawCreditFetch,
3
3
  startGateway
4
- } from "./chunk-K57GMQDY.js";
4
+ } from "./chunk-ZMPGMK4C.js";
5
5
  export {
6
6
  createClawCreditFetch,
7
7
  startGateway
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t54-labs/clawcredit-blockrun-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "SDK and standalone OpenAI-compatible gateway for BlockRun inference paid via claw.credit",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/clawcredit.ts","../src/server.ts"],"sourcesContent":["import { ClawCredit, withTrace } from \"@t54-labs/clawcredit-sdk\";\n\nconst DEFAULT_SERVICE_URL = \"https://api.claw.credit\";\n\nexport type ClawCreditConfig = {\n baseUrl?: string;\n apiToken: string;\n chain: string;\n asset: string;\n agent?: string;\n agentId?: string;\n};\n\nexport type PreAuthParams = {\n estimatedAmount: string;\n};\n\ntype SdkClient = {\n pay: (args: {\n transaction: {\n recipient: string;\n amount: number;\n chain: string;\n asset: string;\n amount_unit?: \"human\" | \"atomic\";\n };\n request_body: Record<string, unknown>;\n context?: {\n reasoning_process?: string;\n current_task?: string;\n };\n idempotencyKey?: string;\n }) => Promise<{ merchant_response?: unknown }>;\n};\n\nfunction headersToObject(headersInit?: HeadersInit): Record<string, string> {\n if (!headersInit) return {};\n const headers = new Headers(headersInit);\n const out: Record<string, string> = {};\n headers.forEach((value, key) => {\n const lower = key.toLowerCase();\n if (lower === \"host\" || lower === \"content-length\" || lower === \"connection\") return;\n out[key] = value;\n });\n return out;\n}\n\nfunction parseJsonBody(body: RequestInit[\"body\"]): unknown {\n if (body == null) return undefined;\n\n let raw = \"\";\n if (typeof body === \"string\") {\n raw = body;\n } else if (body instanceof Uint8Array) {\n raw = Buffer.from(body).toString(\"utf-8\");\n } else if (body instanceof ArrayBuffer) {\n raw = Buffer.from(body).toString(\"utf-8\");\n } else {\n return undefined;\n }\n\n if (!raw.trim()) return undefined;\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n}\n\nfunction microsToUsd(estimatedAmount?: string): number {\n const micros = Number(estimatedAmount ?? \"\");\n if (!Number.isFinite(micros) || micros <= 0) return 0.01;\n return Number((micros / 1_000_000).toFixed(6));\n}\n\nfunction inferStatusCode(err: unknown): number {\n const msg = err instanceof Error ? err.message : String(err);\n const match = msg.match(/ClawCredit API Error:\\s*(\\d{3})\\s*-/i);\n if (match) return parseInt(match[1], 10);\n if (/payment required/i.test(msg)) return 402;\n if (/prequalification_pending/i.test(msg)) return 403;\n if (/unauthorized/i.test(msg)) return 401;\n return 502;\n}\n\nexport function createClawCreditFetch(config: ClawCreditConfig) {\n const serviceUrl = (config.baseUrl || DEFAULT_SERVICE_URL).replace(/\\/+$/, \"\");\n const chain = config.chain.toUpperCase();\n const asset = config.asset;\n const apiToken = config.apiToken.trim();\n\n if (!apiToken) {\n throw new Error(\"CLAWCREDIT_API_TOKEN is required for claw.credit payment mode\");\n }\n\n const credit = new ClawCredit({\n serviceUrl,\n apiToken,\n agent: config.agent,\n agentId: config.agentId,\n }) as SdkClient;\n\n return async (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ): Promise<Response> => {\n const upstreamUrl =\n typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n const method = (init?.method || \"POST\").toUpperCase();\n const headers = headersToObject(init?.headers);\n const idempotencyKey = new Headers(init?.headers).get(\"idempotency-key\") || undefined;\n const requestBody = parseJsonBody(init?.body);\n const amountUsd = microsToUsd(preAuth?.estimatedAmount);\n\n try {\n const result = await withTrace(async () =>\n credit.pay({\n transaction: {\n recipient: upstreamUrl,\n amount: amountUsd,\n chain,\n asset,\n },\n request_body: {\n http: {\n url: upstreamUrl,\n method,\n headers,\n },\n body: requestBody,\n },\n context: {\n current_task: \"blockrun_inference_via_clawcredit_blockrun_gateway\",\n reasoning_process: \"Pay BlockRun inference through claw.credit SDK\",\n },\n idempotencyKey,\n }),\n );\n\n const merchantResponse =\n result && typeof result === \"object\" && \"merchant_response\" in result\n ? (result as { merchant_response?: unknown }).merchant_response\n : result;\n\n return new Response(JSON.stringify(merchantResponse ?? {}), {\n status: 200,\n headers: { \"content-type\": \"application/json\" },\n });\n } catch (err) {\n const status = inferStatusCode(err);\n const message = err instanceof Error ? err.message : String(err);\n return new Response(JSON.stringify({ error: message }), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n };\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { createClawCreditFetch, type ClawCreditConfig } from \"./clawcredit.js\";\n\nexport type GatewayOptions = {\n port?: number;\n host?: string;\n blockrunApiBase?: string;\n clawCredit: ClawCreditConfig;\n defaultAmountUsd?: number;\n};\n\nexport type GatewayInstance = {\n port: number;\n baseUrl: string;\n close: () => Promise<void>;\n};\n\ntype CompletionRequest = {\n model?: string;\n stream?: boolean;\n max_tokens?: number;\n};\n\nfunction readBody(req: IncomingMessage): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));\n req.on(\"end\", () => resolve(Buffer.concat(chunks)));\n req.on(\"error\", reject);\n });\n}\n\nfunction sendJson(res: ServerResponse, status: number, payload: unknown): void {\n const text = JSON.stringify(payload);\n res.writeHead(status, {\n \"content-type\": \"application/json\",\n \"content-length\": Buffer.byteLength(text).toString(),\n });\n res.end(text);\n}\n\nfunction extractMaxTokens(body: Buffer): number {\n try {\n const parsed = JSON.parse(body.toString(\"utf-8\")) as CompletionRequest;\n return parsed.max_tokens && Number.isFinite(parsed.max_tokens) ? parsed.max_tokens : 512;\n } catch {\n return 512;\n }\n}\n\nfunction normalizePayloadForGateway(body: Buffer): Buffer {\n try {\n const parsed = JSON.parse(body.toString(\"utf-8\")) as CompletionRequest;\n if (parsed.stream === true) parsed.stream = false;\n return Buffer.from(JSON.stringify(parsed));\n } catch {\n return body;\n }\n}\n\nexport async function startGateway(options: GatewayOptions): Promise<GatewayInstance> {\n const host = options.host ?? \"127.0.0.1\";\n const port = options.port ?? 3402;\n const apiBase = (options.blockrunApiBase ?? \"https://blockrun.ai/api\").replace(/\\/+$/, \"\");\n const defaultAmountUsd = Number.isFinite(options.defaultAmountUsd)\n ? Number(options.defaultAmountUsd)\n : 0.1;\n const payFetch = createClawCreditFetch(options.clawCredit);\n\n const server = createServer(async (req, res) => {\n if (req.url === \"/health\") {\n return sendJson(res, 200, {\n status: \"ok\",\n service: \"clawcredit-blockrun-gateway\",\n payment_mode: \"clawcredit\",\n });\n }\n\n if (req.url !== \"/v1/chat/completions\" || req.method !== \"POST\") {\n return sendJson(res, 404, { error: \"Not found\" });\n }\n\n try {\n const body = await readBody(req);\n const normalized = normalizePayloadForGateway(body);\n const maxTokens = extractMaxTokens(normalized);\n\n const estimatedMicros = String(\n Math.max(10_000, Math.round(defaultAmountUsd * 1_000_000 + maxTokens * 8)),\n );\n\n const upstreamUrl = `${apiBase}/v1/chat/completions`;\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value !== \"string\") continue;\n const lower = key.toLowerCase();\n if (lower === \"host\" || lower === \"content-length\" || lower === \"connection\") continue;\n headers.set(key, value);\n }\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n\n const upstream = await payFetch(\n upstreamUrl,\n {\n method: \"POST\",\n headers,\n body: new Uint8Array(normalized),\n },\n { estimatedAmount: estimatedMicros },\n );\n\n const responseBody = await upstream.text();\n res.writeHead(upstream.status, {\n \"content-type\": upstream.headers.get(\"content-type\") || \"application/json\",\n });\n res.end(responseBody);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n sendJson(res, 502, { error: msg });\n }\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.removeListener(\"error\", reject);\n resolve();\n });\n });\n\n const actualPort = (server.address() as AddressInfo).port;\n return {\n port: actualPort,\n baseUrl: `http://${host}:${actualPort}`,\n close: () => new Promise<void>((resolve, reject) => server.close((err) => (err ? reject(err) : resolve()))),\n };\n}\n"],"mappings":"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","names":[]}