@blockrun/clawrouter 0.12.10 → 0.12.11

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
@@ -1662,7 +1662,12 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
1662
1662
  const getHeader = (name) => response.headers.get(name);
1663
1663
  let body;
1664
1664
  try {
1665
- const responseText = await response.text();
1665
+ const responseText = await Promise.race([
1666
+ response.text(),
1667
+ new Promise(
1668
+ (_, reject) => setTimeout(() => reject(new Error("Body read timeout")), 6e4)
1669
+ )
1670
+ ]);
1666
1671
  if (responseText) body = JSON.parse(responseText);
1667
1672
  } catch {
1668
1673
  }
@@ -5466,6 +5471,27 @@ var HEALTH_CHECK_TIMEOUT_MS = 2e3;
5466
5471
  var RATE_LIMIT_COOLDOWN_MS = 6e4;
5467
5472
  var PORT_RETRY_ATTEMPTS = 5;
5468
5473
  var PORT_RETRY_DELAY_MS = 1e3;
5474
+ var BODY_READ_TIMEOUT_MS = 6e4;
5475
+ async function readBodyWithTimeout(body, timeoutMs = BODY_READ_TIMEOUT_MS) {
5476
+ if (!body) return [];
5477
+ const reader = body.getReader();
5478
+ const chunks = [];
5479
+ try {
5480
+ while (true) {
5481
+ const result = await Promise.race([
5482
+ reader.read(),
5483
+ new Promise(
5484
+ (_, reject) => setTimeout(() => reject(new Error("Body read timeout")), timeoutMs)
5485
+ )
5486
+ ]);
5487
+ if (result.done) break;
5488
+ chunks.push(result.value);
5489
+ }
5490
+ } finally {
5491
+ reader.releaseLock();
5492
+ }
5493
+ return chunks;
5494
+ }
5469
5495
  function transformPaymentError(errorBody) {
5470
5496
  try {
5471
5497
  const parsed = JSON.parse(errorBody);
@@ -5505,9 +5531,21 @@ function transformPaymentError(errorBody) {
5505
5531
  }
5506
5532
  });
5507
5533
  }
5534
+ if (innerJson.invalidReason === "transaction_simulation_failed") {
5535
+ console.error(
5536
+ `[ClawRouter] Solana transaction simulation failed: ${innerJson.invalidMessage || "unknown"}`
5537
+ );
5538
+ return JSON.stringify({
5539
+ error: {
5540
+ message: "Solana payment simulation failed. Retrying with a different model.",
5541
+ type: "transaction_simulation_failed",
5542
+ help: "This is usually temporary. If it persists, check your Solana USDC balance or try: /model free"
5543
+ }
5544
+ });
5545
+ }
5508
5546
  }
5509
5547
  }
5510
- if (parsed.error === "Settlement failed" || parsed.details?.includes("Settlement failed")) {
5548
+ if (parsed.error === "Settlement failed" || parsed.error === "Payment settlement failed" || parsed.details?.includes("Settlement failed") || parsed.details?.includes("transaction_simulation_failed")) {
5511
5549
  const details = parsed.details || "";
5512
5550
  const gasError = details.includes("unable to estimate gas");
5513
5551
  return JSON.stringify({
@@ -5953,15 +5991,9 @@ async function proxyPartnerRequest(req, res, apiBase, payFetch) {
5953
5991
  });
5954
5992
  res.writeHead(upstream.status, responseHeaders);
5955
5993
  if (upstream.body) {
5956
- const reader = upstream.body.getReader();
5957
- try {
5958
- while (true) {
5959
- const { done, value } = await reader.read();
5960
- if (done) break;
5961
- safeWrite(res, Buffer.from(value));
5962
- }
5963
- } finally {
5964
- reader.releaseLock();
5994
+ const chunks = await readBodyWithTimeout(upstream.body);
5995
+ for (const chunk of chunks) {
5996
+ safeWrite(res, Buffer.from(chunk));
5965
5997
  }
5966
5998
  }
5967
5999
  res.end();
@@ -5991,16 +6023,23 @@ async function uploadDataUriToHost(dataUri) {
5991
6023
  const form = new FormData();
5992
6024
  form.append("reqtype", "fileupload");
5993
6025
  form.append("fileToUpload", blob, `image.${ext}`);
5994
- const resp = await fetch("https://catbox.moe/user/api.php", {
5995
- method: "POST",
5996
- body: form
5997
- });
5998
- if (!resp.ok) throw new Error(`catbox.moe upload failed: HTTP ${resp.status}`);
5999
- const result = await resp.text();
6000
- if (result.startsWith("https://")) {
6001
- return result.trim();
6026
+ const uploadController = new AbortController();
6027
+ const uploadTimeout = setTimeout(() => uploadController.abort(), 3e4);
6028
+ try {
6029
+ const resp = await fetch("https://catbox.moe/user/api.php", {
6030
+ method: "POST",
6031
+ body: form,
6032
+ signal: uploadController.signal
6033
+ });
6034
+ if (!resp.ok) throw new Error(`catbox.moe upload failed: HTTP ${resp.status}`);
6035
+ const result = await resp.text();
6036
+ if (result.startsWith("https://")) {
6037
+ return result.trim();
6038
+ }
6039
+ throw new Error(`catbox.moe upload failed: ${result}`);
6040
+ } finally {
6041
+ clearTimeout(uploadTimeout);
6002
6042
  }
6003
- throw new Error(`catbox.moe upload failed: ${result}`);
6004
6043
  }
6005
6044
  async function startProxy(options) {
6006
6045
  const walletKey = typeof options.wallet === "string" ? options.wallet : options.wallet.key;
@@ -6402,7 +6441,8 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
6402
6441
  signal
6403
6442
  });
6404
6443
  if (response.status !== 200) {
6405
- const errorBody = await response.text();
6444
+ const errorBodyChunks = await readBodyWithTimeout(response.body);
6445
+ const errorBody = Buffer.concat(errorBodyChunks).toString();
6406
6446
  const isProviderErr = isProviderError(response.status, errorBody);
6407
6447
  return {
6408
6448
  success: false,
@@ -6414,7 +6454,8 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
6414
6454
  const contentType = response.headers.get("content-type") || "";
6415
6455
  if (contentType.includes("json") || contentType.includes("text")) {
6416
6456
  try {
6417
- const responseBody = await response.clone().text();
6457
+ const clonedChunks = await readBodyWithTimeout(response.clone().body);
6458
+ const responseBody = Buffer.concat(clonedChunks).toString();
6418
6459
  const degradedReason = detectDegradedSuccessResponse(responseBody);
6419
6460
  if (degradedReason) {
6420
6461
  return {
@@ -7215,7 +7256,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
7215
7256
  if (result.errorStatus === 429) {
7216
7257
  markRateLimited(tryModel);
7217
7258
  }
7218
- const isPaymentErr = /payment.*verification.*failed|insufficient.*funds/i.test(
7259
+ const isPaymentErr = /payment.*verification.*failed|payment.*settlement.*failed|insufficient.*funds|transaction_simulation_failed/i.test(
7219
7260
  result.errorBody || ""
7220
7261
  );
7221
7262
  if (isPaymentErr && tryModel !== FREE_MODEL) {
@@ -7320,17 +7361,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
7320
7361
  const responseChunks = [];
7321
7362
  if (headersSentEarly) {
7322
7363
  if (upstream.body) {
7323
- const reader = upstream.body.getReader();
7324
- const chunks = [];
7325
- try {
7326
- while (true) {
7327
- const { done, value } = await reader.read();
7328
- if (done) break;
7329
- chunks.push(value);
7330
- }
7331
- } finally {
7332
- reader.releaseLock();
7333
- }
7364
+ const chunks = await readBodyWithTimeout(upstream.body);
7334
7365
  const jsonBody = Buffer.concat(chunks);
7335
7366
  const jsonStr = jsonBody.toString();
7336
7367
  try {
@@ -7469,15 +7500,9 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
7469
7500
  }
7470
7501
  const bodyParts = [];
7471
7502
  if (upstream.body) {
7472
- const reader = upstream.body.getReader();
7473
- try {
7474
- while (true) {
7475
- const { done, value } = await reader.read();
7476
- if (done) break;
7477
- bodyParts.push(Buffer.from(value));
7478
- }
7479
- } finally {
7480
- reader.releaseLock();
7503
+ const chunks = await readBodyWithTimeout(upstream.body);
7504
+ for (const chunk of chunks) {
7505
+ bodyParts.push(Buffer.from(chunk));
7481
7506
  }
7482
7507
  }
7483
7508
  let responseBody = Buffer.concat(bodyParts);