@blockrun/clawrouter 0.12.10 → 0.12.12
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/cli.js +82 -43
- package/dist/cli.js.map +1 -1
- package/dist/index.js +82 -43
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
|
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
|
|
5957
|
-
|
|
5958
|
-
|
|
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
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
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
|
|
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
|
|
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 {
|
|
@@ -7214,8 +7255,22 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7214
7255
|
if (result.isProviderError && !isLastAttempt) {
|
|
7215
7256
|
if (result.errorStatus === 429) {
|
|
7216
7257
|
markRateLimited(tryModel);
|
|
7258
|
+
try {
|
|
7259
|
+
const parsed = JSON.parse(result.errorBody || "{}");
|
|
7260
|
+
if (parsed.update_available) {
|
|
7261
|
+
console.log("");
|
|
7262
|
+
console.log(
|
|
7263
|
+
`\x1B[33m\u2B06\uFE0F ClawRouter ${parsed.update_available} available (you have ${VERSION})\x1B[0m`
|
|
7264
|
+
);
|
|
7265
|
+
console.log(
|
|
7266
|
+
` Run: \x1B[36mcurl -fsSL ${parsed.update_url || "https://blockrun.ai/ClawRouter-update"} | bash\x1B[0m`
|
|
7267
|
+
);
|
|
7268
|
+
console.log("");
|
|
7269
|
+
}
|
|
7270
|
+
} catch {
|
|
7271
|
+
}
|
|
7217
7272
|
}
|
|
7218
|
-
const isPaymentErr = /payment.*verification.*failed|insufficient.*funds/i.test(
|
|
7273
|
+
const isPaymentErr = /payment.*verification.*failed|payment.*settlement.*failed|insufficient.*funds|transaction_simulation_failed/i.test(
|
|
7219
7274
|
result.errorBody || ""
|
|
7220
7275
|
);
|
|
7221
7276
|
if (isPaymentErr && tryModel !== FREE_MODEL) {
|
|
@@ -7320,17 +7375,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7320
7375
|
const responseChunks = [];
|
|
7321
7376
|
if (headersSentEarly) {
|
|
7322
7377
|
if (upstream.body) {
|
|
7323
|
-
const
|
|
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
|
-
}
|
|
7378
|
+
const chunks = await readBodyWithTimeout(upstream.body);
|
|
7334
7379
|
const jsonBody = Buffer.concat(chunks);
|
|
7335
7380
|
const jsonStr = jsonBody.toString();
|
|
7336
7381
|
try {
|
|
@@ -7469,15 +7514,9 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7469
7514
|
}
|
|
7470
7515
|
const bodyParts = [];
|
|
7471
7516
|
if (upstream.body) {
|
|
7472
|
-
const
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
const { done, value } = await reader.read();
|
|
7476
|
-
if (done) break;
|
|
7477
|
-
bodyParts.push(Buffer.from(value));
|
|
7478
|
-
}
|
|
7479
|
-
} finally {
|
|
7480
|
-
reader.releaseLock();
|
|
7517
|
+
const chunks = await readBodyWithTimeout(upstream.body);
|
|
7518
|
+
for (const chunk of chunks) {
|
|
7519
|
+
bodyParts.push(Buffer.from(chunk));
|
|
7481
7520
|
}
|
|
7482
7521
|
}
|
|
7483
7522
|
let responseBody = Buffer.concat(bodyParts);
|