@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/cli.js
CHANGED
|
@@ -45,7 +45,12 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
45
45
|
const getHeader = (name) => response.headers.get(name);
|
|
46
46
|
let body;
|
|
47
47
|
try {
|
|
48
|
-
const responseText = await
|
|
48
|
+
const responseText = await Promise.race([
|
|
49
|
+
response.text(),
|
|
50
|
+
new Promise(
|
|
51
|
+
(_, reject) => setTimeout(() => reject(new Error("Body read timeout")), 6e4)
|
|
52
|
+
)
|
|
53
|
+
]);
|
|
49
54
|
if (responseText) body = JSON.parse(responseText);
|
|
50
55
|
} catch {
|
|
51
56
|
}
|
|
@@ -4982,6 +4987,27 @@ var HEALTH_CHECK_TIMEOUT_MS = 2e3;
|
|
|
4982
4987
|
var RATE_LIMIT_COOLDOWN_MS = 6e4;
|
|
4983
4988
|
var PORT_RETRY_ATTEMPTS = 5;
|
|
4984
4989
|
var PORT_RETRY_DELAY_MS = 1e3;
|
|
4990
|
+
var BODY_READ_TIMEOUT_MS = 6e4;
|
|
4991
|
+
async function readBodyWithTimeout(body, timeoutMs = BODY_READ_TIMEOUT_MS) {
|
|
4992
|
+
if (!body) return [];
|
|
4993
|
+
const reader = body.getReader();
|
|
4994
|
+
const chunks = [];
|
|
4995
|
+
try {
|
|
4996
|
+
while (true) {
|
|
4997
|
+
const result = await Promise.race([
|
|
4998
|
+
reader.read(),
|
|
4999
|
+
new Promise(
|
|
5000
|
+
(_, reject) => setTimeout(() => reject(new Error("Body read timeout")), timeoutMs)
|
|
5001
|
+
)
|
|
5002
|
+
]);
|
|
5003
|
+
if (result.done) break;
|
|
5004
|
+
chunks.push(result.value);
|
|
5005
|
+
}
|
|
5006
|
+
} finally {
|
|
5007
|
+
reader.releaseLock();
|
|
5008
|
+
}
|
|
5009
|
+
return chunks;
|
|
5010
|
+
}
|
|
4985
5011
|
function transformPaymentError(errorBody) {
|
|
4986
5012
|
try {
|
|
4987
5013
|
const parsed = JSON.parse(errorBody);
|
|
@@ -5021,9 +5047,21 @@ function transformPaymentError(errorBody) {
|
|
|
5021
5047
|
}
|
|
5022
5048
|
});
|
|
5023
5049
|
}
|
|
5050
|
+
if (innerJson.invalidReason === "transaction_simulation_failed") {
|
|
5051
|
+
console.error(
|
|
5052
|
+
`[ClawRouter] Solana transaction simulation failed: ${innerJson.invalidMessage || "unknown"}`
|
|
5053
|
+
);
|
|
5054
|
+
return JSON.stringify({
|
|
5055
|
+
error: {
|
|
5056
|
+
message: "Solana payment simulation failed. Retrying with a different model.",
|
|
5057
|
+
type: "transaction_simulation_failed",
|
|
5058
|
+
help: "This is usually temporary. If it persists, check your Solana USDC balance or try: /model free"
|
|
5059
|
+
}
|
|
5060
|
+
});
|
|
5061
|
+
}
|
|
5024
5062
|
}
|
|
5025
5063
|
}
|
|
5026
|
-
if (parsed.error === "Settlement failed" || parsed.details?.includes("Settlement failed")) {
|
|
5064
|
+
if (parsed.error === "Settlement failed" || parsed.error === "Payment settlement failed" || parsed.details?.includes("Settlement failed") || parsed.details?.includes("transaction_simulation_failed")) {
|
|
5027
5065
|
const details = parsed.details || "";
|
|
5028
5066
|
const gasError = details.includes("unable to estimate gas");
|
|
5029
5067
|
return JSON.stringify({
|
|
@@ -5469,15 +5507,9 @@ async function proxyPartnerRequest(req, res, apiBase, payFetch) {
|
|
|
5469
5507
|
});
|
|
5470
5508
|
res.writeHead(upstream.status, responseHeaders);
|
|
5471
5509
|
if (upstream.body) {
|
|
5472
|
-
const
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
const { done, value } = await reader.read();
|
|
5476
|
-
if (done) break;
|
|
5477
|
-
safeWrite(res, Buffer.from(value));
|
|
5478
|
-
}
|
|
5479
|
-
} finally {
|
|
5480
|
-
reader.releaseLock();
|
|
5510
|
+
const chunks = await readBodyWithTimeout(upstream.body);
|
|
5511
|
+
for (const chunk of chunks) {
|
|
5512
|
+
safeWrite(res, Buffer.from(chunk));
|
|
5481
5513
|
}
|
|
5482
5514
|
}
|
|
5483
5515
|
res.end();
|
|
@@ -5507,16 +5539,23 @@ async function uploadDataUriToHost(dataUri) {
|
|
|
5507
5539
|
const form = new FormData();
|
|
5508
5540
|
form.append("reqtype", "fileupload");
|
|
5509
5541
|
form.append("fileToUpload", blob, `image.${ext}`);
|
|
5510
|
-
const
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5542
|
+
const uploadController = new AbortController();
|
|
5543
|
+
const uploadTimeout = setTimeout(() => uploadController.abort(), 3e4);
|
|
5544
|
+
try {
|
|
5545
|
+
const resp = await fetch("https://catbox.moe/user/api.php", {
|
|
5546
|
+
method: "POST",
|
|
5547
|
+
body: form,
|
|
5548
|
+
signal: uploadController.signal
|
|
5549
|
+
});
|
|
5550
|
+
if (!resp.ok) throw new Error(`catbox.moe upload failed: HTTP ${resp.status}`);
|
|
5551
|
+
const result = await resp.text();
|
|
5552
|
+
if (result.startsWith("https://")) {
|
|
5553
|
+
return result.trim();
|
|
5554
|
+
}
|
|
5555
|
+
throw new Error(`catbox.moe upload failed: ${result}`);
|
|
5556
|
+
} finally {
|
|
5557
|
+
clearTimeout(uploadTimeout);
|
|
5518
5558
|
}
|
|
5519
|
-
throw new Error(`catbox.moe upload failed: ${result}`);
|
|
5520
5559
|
}
|
|
5521
5560
|
async function startProxy(options) {
|
|
5522
5561
|
const walletKey = typeof options.wallet === "string" ? options.wallet : options.wallet.key;
|
|
@@ -5918,7 +5957,8 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
5918
5957
|
signal
|
|
5919
5958
|
});
|
|
5920
5959
|
if (response.status !== 200) {
|
|
5921
|
-
const
|
|
5960
|
+
const errorBodyChunks = await readBodyWithTimeout(response.body);
|
|
5961
|
+
const errorBody = Buffer.concat(errorBodyChunks).toString();
|
|
5922
5962
|
const isProviderErr = isProviderError(response.status, errorBody);
|
|
5923
5963
|
return {
|
|
5924
5964
|
success: false,
|
|
@@ -5930,7 +5970,8 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
5930
5970
|
const contentType = response.headers.get("content-type") || "";
|
|
5931
5971
|
if (contentType.includes("json") || contentType.includes("text")) {
|
|
5932
5972
|
try {
|
|
5933
|
-
const
|
|
5973
|
+
const clonedChunks = await readBodyWithTimeout(response.clone().body);
|
|
5974
|
+
const responseBody = Buffer.concat(clonedChunks).toString();
|
|
5934
5975
|
const degradedReason = detectDegradedSuccessResponse(responseBody);
|
|
5935
5976
|
if (degradedReason) {
|
|
5936
5977
|
return {
|
|
@@ -6730,8 +6771,22 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6730
6771
|
if (result.isProviderError && !isLastAttempt) {
|
|
6731
6772
|
if (result.errorStatus === 429) {
|
|
6732
6773
|
markRateLimited(tryModel);
|
|
6774
|
+
try {
|
|
6775
|
+
const parsed = JSON.parse(result.errorBody || "{}");
|
|
6776
|
+
if (parsed.update_available) {
|
|
6777
|
+
console.log("");
|
|
6778
|
+
console.log(
|
|
6779
|
+
`\x1B[33m\u2B06\uFE0F ClawRouter ${parsed.update_available} available (you have ${VERSION})\x1B[0m`
|
|
6780
|
+
);
|
|
6781
|
+
console.log(
|
|
6782
|
+
` Run: \x1B[36mcurl -fsSL ${parsed.update_url || "https://blockrun.ai/ClawRouter-update"} | bash\x1B[0m`
|
|
6783
|
+
);
|
|
6784
|
+
console.log("");
|
|
6785
|
+
}
|
|
6786
|
+
} catch {
|
|
6787
|
+
}
|
|
6733
6788
|
}
|
|
6734
|
-
const isPaymentErr = /payment.*verification.*failed|insufficient.*funds/i.test(
|
|
6789
|
+
const isPaymentErr = /payment.*verification.*failed|payment.*settlement.*failed|insufficient.*funds|transaction_simulation_failed/i.test(
|
|
6735
6790
|
result.errorBody || ""
|
|
6736
6791
|
);
|
|
6737
6792
|
if (isPaymentErr && tryModel !== FREE_MODEL) {
|
|
@@ -6836,17 +6891,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6836
6891
|
const responseChunks = [];
|
|
6837
6892
|
if (headersSentEarly) {
|
|
6838
6893
|
if (upstream.body) {
|
|
6839
|
-
const
|
|
6840
|
-
const chunks = [];
|
|
6841
|
-
try {
|
|
6842
|
-
while (true) {
|
|
6843
|
-
const { done, value } = await reader.read();
|
|
6844
|
-
if (done) break;
|
|
6845
|
-
chunks.push(value);
|
|
6846
|
-
}
|
|
6847
|
-
} finally {
|
|
6848
|
-
reader.releaseLock();
|
|
6849
|
-
}
|
|
6894
|
+
const chunks = await readBodyWithTimeout(upstream.body);
|
|
6850
6895
|
const jsonBody = Buffer.concat(chunks);
|
|
6851
6896
|
const jsonStr = jsonBody.toString();
|
|
6852
6897
|
try {
|
|
@@ -6985,15 +7030,9 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6985
7030
|
}
|
|
6986
7031
|
const bodyParts = [];
|
|
6987
7032
|
if (upstream.body) {
|
|
6988
|
-
const
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
const { done, value } = await reader.read();
|
|
6992
|
-
if (done) break;
|
|
6993
|
-
bodyParts.push(Buffer.from(value));
|
|
6994
|
-
}
|
|
6995
|
-
} finally {
|
|
6996
|
-
reader.releaseLock();
|
|
7033
|
+
const chunks = await readBodyWithTimeout(upstream.body);
|
|
7034
|
+
for (const chunk of chunks) {
|
|
7035
|
+
bodyParts.push(Buffer.from(chunk));
|
|
6997
7036
|
}
|
|
6998
7037
|
}
|
|
6999
7038
|
let responseBody = Buffer.concat(bodyParts);
|