@blockrun/clawrouter 0.12.25 → 0.12.27
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 +131 -0
- package/dist/cli.js.map +1 -1
- package/dist/index.js +140 -9
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/skills/clawrouter/SKILL.md +48 -0
- package/skills/imagegen/SKILL.md +80 -0
- package/skills/release/SKILL.md +182 -0
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
// src/proxy.ts
|
|
4
4
|
import { createServer } from "http";
|
|
5
5
|
import { finished } from "stream";
|
|
6
|
+
import { homedir as homedir4 } from "os";
|
|
7
|
+
import { join as join5 } from "path";
|
|
8
|
+
import { mkdir as mkdir3, writeFile as writeFile2, readFile, stat as fsStat } from "fs/promises";
|
|
6
9
|
import { createPublicClient as createPublicClient2, http as http2 } from "viem";
|
|
7
10
|
import { base as base2 } from "viem/chains";
|
|
8
11
|
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
@@ -5032,6 +5035,7 @@ ${lines.join("\n")}`;
|
|
|
5032
5035
|
// src/proxy.ts
|
|
5033
5036
|
var BLOCKRUN_API = "https://blockrun.ai/api";
|
|
5034
5037
|
var BLOCKRUN_SOLANA_API = "https://sol.blockrun.ai/api";
|
|
5038
|
+
var IMAGE_DIR = join5(homedir4(), ".openclaw", "blockrun", "images");
|
|
5035
5039
|
var AUTO_MODEL = "blockrun/auto";
|
|
5036
5040
|
var ROUTING_PROFILES = /* @__PURE__ */ new Set([
|
|
5037
5041
|
"blockrun/free",
|
|
@@ -5131,6 +5135,58 @@ function transformPaymentError(errorBody) {
|
|
|
5131
5135
|
}
|
|
5132
5136
|
}
|
|
5133
5137
|
}
|
|
5138
|
+
if (parsed.error === "Payment verification failed" && parsed.code === "PAYMENT_INVALID" && parsed.debug) {
|
|
5139
|
+
const debugLower = parsed.debug.toLowerCase();
|
|
5140
|
+
const wallet = parsed.payer || "unknown";
|
|
5141
|
+
const shortWallet = wallet.length > 12 ? `${wallet.slice(0, 6)}...${wallet.slice(-4)}` : wallet;
|
|
5142
|
+
if (debugLower.includes("insufficient")) {
|
|
5143
|
+
return JSON.stringify({
|
|
5144
|
+
error: {
|
|
5145
|
+
message: "Insufficient Solana USDC balance.",
|
|
5146
|
+
type: "insufficient_funds",
|
|
5147
|
+
wallet,
|
|
5148
|
+
help: `Fund wallet ${shortWallet} with USDC on Solana, or switch to Base: /wallet base`
|
|
5149
|
+
}
|
|
5150
|
+
});
|
|
5151
|
+
}
|
|
5152
|
+
if (debugLower.includes("transaction_simulation_failed") || debugLower.includes("simulation")) {
|
|
5153
|
+
console.error(`[ClawRouter] Solana transaction simulation failed: ${parsed.debug}`);
|
|
5154
|
+
return JSON.stringify({
|
|
5155
|
+
error: {
|
|
5156
|
+
message: "Solana payment simulation failed. Retrying with a different model.",
|
|
5157
|
+
type: "transaction_simulation_failed",
|
|
5158
|
+
help: "This is usually temporary. If it persists, try: /model free"
|
|
5159
|
+
}
|
|
5160
|
+
});
|
|
5161
|
+
}
|
|
5162
|
+
if (debugLower.includes("invalid signature") || debugLower.includes("invalid_signature")) {
|
|
5163
|
+
return JSON.stringify({
|
|
5164
|
+
error: {
|
|
5165
|
+
message: "Solana payment signature invalid.",
|
|
5166
|
+
type: "invalid_payload",
|
|
5167
|
+
help: "Try again. If this persists, reinstall ClawRouter: curl -fsSL https://blockrun.ai/ClawRouter-update | bash"
|
|
5168
|
+
}
|
|
5169
|
+
});
|
|
5170
|
+
}
|
|
5171
|
+
if (debugLower.includes("expired")) {
|
|
5172
|
+
return JSON.stringify({
|
|
5173
|
+
error: {
|
|
5174
|
+
message: "Solana payment expired. Retrying.",
|
|
5175
|
+
type: "expired",
|
|
5176
|
+
help: "This is usually temporary."
|
|
5177
|
+
}
|
|
5178
|
+
});
|
|
5179
|
+
}
|
|
5180
|
+
console.error(`[ClawRouter] Solana payment verification failed: ${parsed.debug} payer=${wallet}`);
|
|
5181
|
+
return JSON.stringify({
|
|
5182
|
+
error: {
|
|
5183
|
+
message: `Solana payment verification failed: ${parsed.debug}`,
|
|
5184
|
+
type: "payment_invalid",
|
|
5185
|
+
wallet,
|
|
5186
|
+
help: "Try again or switch to Base: /wallet base"
|
|
5187
|
+
}
|
|
5188
|
+
});
|
|
5189
|
+
}
|
|
5134
5190
|
if (parsed.error === "Settlement failed" || parsed.error === "Payment settlement failed" || parsed.details?.includes("Settlement failed") || parsed.details?.includes("transaction_simulation_failed")) {
|
|
5135
5191
|
const details = parsed.details || "";
|
|
5136
5192
|
const gasError = details.includes("unable to estimate gas");
|
|
@@ -5813,6 +5869,81 @@ async function startProxy(options) {
|
|
|
5813
5869
|
res.end(JSON.stringify({ object: "list", data: models }));
|
|
5814
5870
|
return;
|
|
5815
5871
|
}
|
|
5872
|
+
if (req.url?.startsWith("/images/") && req.method === "GET") {
|
|
5873
|
+
const filename = req.url.slice("/images/".length).split("?")[0].replace(/[^a-zA-Z0-9._-]/g, "");
|
|
5874
|
+
if (!filename) {
|
|
5875
|
+
res.writeHead(400);
|
|
5876
|
+
res.end("Bad request");
|
|
5877
|
+
return;
|
|
5878
|
+
}
|
|
5879
|
+
const filePath = join5(IMAGE_DIR, filename);
|
|
5880
|
+
try {
|
|
5881
|
+
const s = await fsStat(filePath);
|
|
5882
|
+
if (!s.isFile()) throw new Error("not a file");
|
|
5883
|
+
const ext = filename.split(".").pop()?.toLowerCase() ?? "png";
|
|
5884
|
+
const mime = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", webp: "image/webp", gif: "image/gif" };
|
|
5885
|
+
const data = await readFile(filePath);
|
|
5886
|
+
res.writeHead(200, { "Content-Type": mime[ext] ?? "application/octet-stream", "Content-Length": data.length });
|
|
5887
|
+
res.end(data);
|
|
5888
|
+
} catch {
|
|
5889
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
5890
|
+
res.end(JSON.stringify({ error: "Image not found" }));
|
|
5891
|
+
}
|
|
5892
|
+
return;
|
|
5893
|
+
}
|
|
5894
|
+
if (req.url === "/v1/images/generations" && req.method === "POST") {
|
|
5895
|
+
const chunks = [];
|
|
5896
|
+
for await (const chunk of req) {
|
|
5897
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
5898
|
+
}
|
|
5899
|
+
const reqBody = Buffer.concat(chunks);
|
|
5900
|
+
try {
|
|
5901
|
+
const upstream = await payFetch(`${apiBase}/v1/images/generations`, {
|
|
5902
|
+
method: "POST",
|
|
5903
|
+
headers: { "content-type": "application/json", "user-agent": USER_AGENT },
|
|
5904
|
+
body: reqBody
|
|
5905
|
+
});
|
|
5906
|
+
const text = await upstream.text();
|
|
5907
|
+
if (!upstream.ok) {
|
|
5908
|
+
res.writeHead(upstream.status, { "Content-Type": "application/json" });
|
|
5909
|
+
res.end(text);
|
|
5910
|
+
return;
|
|
5911
|
+
}
|
|
5912
|
+
let result;
|
|
5913
|
+
try {
|
|
5914
|
+
result = JSON.parse(text);
|
|
5915
|
+
} catch {
|
|
5916
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5917
|
+
res.end(text);
|
|
5918
|
+
return;
|
|
5919
|
+
}
|
|
5920
|
+
if (result.data?.length) {
|
|
5921
|
+
await mkdir3(IMAGE_DIR, { recursive: true });
|
|
5922
|
+
const port2 = server.address()?.port ?? 8402;
|
|
5923
|
+
for (const img of result.data) {
|
|
5924
|
+
const m = img.url?.match(/^data:(image\/\w+);base64,(.+)$/);
|
|
5925
|
+
if (m) {
|
|
5926
|
+
const [, mimeType, b64] = m;
|
|
5927
|
+
const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
|
|
5928
|
+
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
5929
|
+
await writeFile2(join5(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
|
|
5930
|
+
img.url = `http://localhost:${port2}/images/${filename}`;
|
|
5931
|
+
console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
|
|
5932
|
+
}
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5935
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5936
|
+
res.end(JSON.stringify(result));
|
|
5937
|
+
} catch (err) {
|
|
5938
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5939
|
+
console.error(`[ClawRouter] Image generation error: ${msg}`);
|
|
5940
|
+
if (!res.headersSent) {
|
|
5941
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
5942
|
+
res.end(JSON.stringify({ error: "Image generation failed", details: msg }));
|
|
5943
|
+
}
|
|
5944
|
+
}
|
|
5945
|
+
return;
|
|
5946
|
+
}
|
|
5816
5947
|
if (req.url?.match(/^\/v1\/(?:x|partner)\//)) {
|
|
5817
5948
|
try {
|
|
5818
5949
|
await proxyPartnerRequest(req, res, apiBase, payFetch);
|