@blockrun/clawrouter 0.6.3 → 0.6.6

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/dist/index.js CHANGED
@@ -26,6 +26,7 @@ var MODEL_ALIASES = {
26
26
  // NVIDIA (free)
27
27
  nvidia: "nvidia/gpt-oss-120b",
28
28
  "gpt-120b": "nvidia/gpt-oss-120b",
29
+ "gpt-20b": "nvidia/gpt-oss-20b",
29
30
  free: "nvidia/gpt-oss-120b"
30
31
  };
31
32
  function resolveModelAlias(model) {
@@ -168,7 +169,15 @@ var BLOCKRUN_MODELS = [
168
169
  maxOutput: 65536,
169
170
  reasoning: true
170
171
  },
171
- // o4-mini: Placeholder removed - model not yet released by OpenAI
172
+ {
173
+ id: "openai/o4-mini",
174
+ name: "o4-mini",
175
+ inputPrice: 1.1,
176
+ outputPrice: 4.4,
177
+ contextWindow: 128e3,
178
+ maxOutput: 65536,
179
+ reasoning: true
180
+ },
172
181
  // Anthropic - all Claude models excel at agentic workflows
173
182
  {
174
183
  id: "anthropic/claude-haiku-4.5",
@@ -365,7 +374,15 @@ var BLOCKRUN_MODELS = [
365
374
  inputPrice: 0,
366
375
  outputPrice: 0,
367
376
  contextWindow: 128e3,
368
- maxOutput: 8192
377
+ maxOutput: 16384
378
+ },
379
+ {
380
+ id: "nvidia/gpt-oss-20b",
381
+ name: "NVIDIA GPT-OSS 20B",
382
+ inputPrice: 0,
383
+ outputPrice: 0,
384
+ contextWindow: 128e3,
385
+ maxOutput: 16384
369
386
  },
370
387
  {
371
388
  id: "nvidia/kimi-k2.5",
@@ -373,7 +390,7 @@ var BLOCKRUN_MODELS = [
373
390
  inputPrice: 1e-3,
374
391
  outputPrice: 1e-3,
375
392
  contextWindow: 262144,
376
- maxOutput: 8192
393
+ maxOutput: 16384
377
394
  }
378
395
  ];
379
396
  function toOpenClawModel(m) {
@@ -450,6 +467,7 @@ var blockrunProvider = {
450
467
 
451
468
  // src/proxy.ts
452
469
  import { createServer } from "http";
470
+ import { finished } from "stream";
453
471
  import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
454
472
 
455
473
  // src/x402.ts
@@ -2348,6 +2366,15 @@ function prioritizeNonRateLimited(models) {
2348
2366
  }
2349
2367
  return [...available, ...rateLimited];
2350
2368
  }
2369
+ function canWrite(res) {
2370
+ return !res.writableEnded && !res.destroyed && res.socket !== null && !res.socket.destroyed && res.socket.writable;
2371
+ }
2372
+ function safeWrite(res, data) {
2373
+ if (!canWrite(res)) {
2374
+ return false;
2375
+ }
2376
+ return res.write(data);
2377
+ }
2351
2378
  var BALANCE_CHECK_BUFFER = 1.5;
2352
2379
  function getProxyPort() {
2353
2380
  const envPort = process.env.BLOCKRUN_PROXY_PORT;
@@ -2556,7 +2583,24 @@ async function startProxy(options) {
2556
2583
  };
2557
2584
  const deduplicator = new RequestDeduplicator();
2558
2585
  const sessionStore = new SessionStore(options.sessionConfig);
2586
+ const connections = /* @__PURE__ */ new Set();
2559
2587
  const server = createServer(async (req, res) => {
2588
+ req.on("error", (err) => {
2589
+ console.error(`[ClawRouter] Request stream error: ${err.message}`);
2590
+ });
2591
+ res.on("error", (err) => {
2592
+ console.error(`[ClawRouter] Response stream error: ${err.message}`);
2593
+ });
2594
+ finished(res, (err) => {
2595
+ if (err && err.code !== "ERR_STREAM_DESTROYED") {
2596
+ console.error(`[ClawRouter] Response finished with error: ${err.message}`);
2597
+ }
2598
+ });
2599
+ finished(req, (err) => {
2600
+ if (err && err.code !== "ERR_STREAM_DESTROYED") {
2601
+ console.error(`[ClawRouter] Request finished with error: ${err.message}`);
2602
+ }
2603
+ });
2560
2604
  if (req.url === "/health" || req.url?.startsWith("/health?")) {
2561
2605
  const url = new URL(req.url, "http://localhost");
2562
2606
  const full = url.searchParams.get("full") === "true";
@@ -2669,14 +2713,54 @@ async function startProxy(options) {
2669
2713
  const port = addr.port;
2670
2714
  const baseUrl = `http://127.0.0.1:${port}`;
2671
2715
  options.onReady?.(port);
2716
+ server.on("error", (err) => {
2717
+ console.error(`[ClawRouter] Server runtime error: ${err.message}`);
2718
+ options.onError?.(err);
2719
+ });
2720
+ server.on("clientError", (err, socket) => {
2721
+ console.error(`[ClawRouter] Client error: ${err.message}`);
2722
+ if (socket.writable && !socket.destroyed) {
2723
+ socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
2724
+ }
2725
+ });
2726
+ server.on("connection", (socket) => {
2727
+ connections.add(socket);
2728
+ socket.setTimeout(3e5);
2729
+ socket.on("timeout", () => {
2730
+ console.error(`[ClawRouter] Socket timeout, destroying connection`);
2731
+ socket.destroy();
2732
+ });
2733
+ socket.on("end", () => {
2734
+ });
2735
+ socket.on("error", (err) => {
2736
+ console.error(`[ClawRouter] Socket error: ${err.message}`);
2737
+ });
2738
+ socket.on("close", () => {
2739
+ connections.delete(socket);
2740
+ });
2741
+ });
2672
2742
  resolve({
2673
2743
  port,
2674
2744
  baseUrl,
2675
2745
  walletAddress: account.address,
2676
2746
  balanceMonitor,
2677
2747
  close: () => new Promise((res, rej) => {
2748
+ const timeout = setTimeout(() => {
2749
+ rej(new Error("[ClawRouter] Close timeout after 4s"));
2750
+ }, 4e3);
2678
2751
  sessionStore.close();
2679
- server.close((err) => err ? rej(err) : res());
2752
+ for (const socket of connections) {
2753
+ socket.destroy();
2754
+ }
2755
+ connections.clear();
2756
+ server.close((err) => {
2757
+ clearTimeout(timeout);
2758
+ if (err) {
2759
+ rej(err);
2760
+ } else {
2761
+ res();
2762
+ }
2763
+ });
2680
2764
  })
2681
2765
  });
2682
2766
  });
@@ -2884,10 +2968,13 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
2884
2968
  connection: "keep-alive"
2885
2969
  });
2886
2970
  headersSentEarly = true;
2887
- res.write(": heartbeat\n\n");
2971
+ safeWrite(res, ": heartbeat\n\n");
2888
2972
  heartbeatInterval = setInterval(() => {
2889
- if (!res.writableEnded) {
2890
- res.write(": heartbeat\n\n");
2973
+ if (canWrite(res)) {
2974
+ safeWrite(res, ": heartbeat\n\n");
2975
+ } else {
2976
+ clearInterval(heartbeatInterval);
2977
+ heartbeatInterval = void 0;
2891
2978
  }
2892
2979
  }, HEARTBEAT_INTERVAL_MS);
2893
2980
  }
@@ -3005,8 +3092,8 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3005
3092
  const errEvent = `data: ${JSON.stringify({ error: { message: errBody, type: "provider_error", status: errStatus } })}
3006
3093
 
3007
3094
  `;
3008
- res.write(errEvent);
3009
- res.write("data: [DONE]\n\n");
3095
+ safeWrite(res, errEvent);
3096
+ safeWrite(res, "data: [DONE]\n\n");
3010
3097
  res.end();
3011
3098
  const errBuf = Buffer.from(errEvent + "data: [DONE]\n\n");
3012
3099
  deduplicator.complete(dedupKey, {
@@ -3071,7 +3158,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3071
3158
  const roleData = `data: ${JSON.stringify(roleChunk)}
3072
3159
 
3073
3160
  `;
3074
- res.write(roleData);
3161
+ safeWrite(res, roleData);
3075
3162
  responseChunks.push(Buffer.from(roleData));
3076
3163
  if (content) {
3077
3164
  const contentChunk = {
@@ -3081,7 +3168,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3081
3168
  const contentData = `data: ${JSON.stringify(contentChunk)}
3082
3169
 
3083
3170
  `;
3084
- res.write(contentData);
3171
+ safeWrite(res, contentData);
3085
3172
  responseChunks.push(Buffer.from(contentData));
3086
3173
  }
3087
3174
  const toolCalls = choice.message?.tool_calls ?? choice.delta?.tool_calls;
@@ -3100,7 +3187,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3100
3187
  const toolCallData = `data: ${JSON.stringify(toolCallChunk)}
3101
3188
 
3102
3189
  `;
3103
- res.write(toolCallData);
3190
+ safeWrite(res, toolCallData);
3104
3191
  responseChunks.push(Buffer.from(toolCallData));
3105
3192
  }
3106
3193
  const finishChunk = {
@@ -3117,7 +3204,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3117
3204
  const finishData = `data: ${JSON.stringify(finishChunk)}
3118
3205
 
3119
3206
  `;
3120
- res.write(finishData);
3207
+ safeWrite(res, finishData);
3121
3208
  responseChunks.push(Buffer.from(finishData));
3122
3209
  }
3123
3210
  }
@@ -3125,11 +3212,11 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3125
3212
  const sseData = `data: ${jsonStr}
3126
3213
 
3127
3214
  `;
3128
- res.write(sseData);
3215
+ safeWrite(res, sseData);
3129
3216
  responseChunks.push(Buffer.from(sseData));
3130
3217
  }
3131
3218
  }
3132
- res.write("data: [DONE]\n\n");
3219
+ safeWrite(res, "data: [DONE]\n\n");
3133
3220
  responseChunks.push(Buffer.from("data: [DONE]\n\n"));
3134
3221
  res.end();
3135
3222
  deduplicator.complete(dedupKey, {
@@ -3152,8 +3239,9 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3152
3239
  while (true) {
3153
3240
  const { done, value } = await reader.read();
3154
3241
  if (done) break;
3155
- res.write(value);
3156
- responseChunks.push(Buffer.from(value));
3242
+ const chunk = Buffer.from(value);
3243
+ safeWrite(res, chunk);
3244
+ responseChunks.push(chunk);
3157
3245
  }
3158
3246
  } finally {
3159
3247
  reader.releaseLock();