@blockrun/clawrouter 0.11.13 → 0.12.0
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/README.md +6 -6
- package/dist/cli.js +1540 -192
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +131 -32
- package/dist/index.js +1929 -393
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,55 +8,6 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/wallet.ts
|
|
12
|
-
var wallet_exports = {};
|
|
13
|
-
__export(wallet_exports, {
|
|
14
|
-
deriveAllKeys: () => deriveAllKeys,
|
|
15
|
-
deriveEvmKey: () => deriveEvmKey,
|
|
16
|
-
deriveSolanaKeyBytes: () => deriveSolanaKeyBytes,
|
|
17
|
-
generateWalletMnemonic: () => generateWalletMnemonic,
|
|
18
|
-
isValidMnemonic: () => isValidMnemonic
|
|
19
|
-
});
|
|
20
|
-
import { HDKey } from "@scure/bip32";
|
|
21
|
-
import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from "@scure/bip39";
|
|
22
|
-
import { wordlist as english } from "@scure/bip39/wordlists/english";
|
|
23
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
24
|
-
function generateWalletMnemonic() {
|
|
25
|
-
return generateMnemonic(english, 256);
|
|
26
|
-
}
|
|
27
|
-
function isValidMnemonic(mnemonic) {
|
|
28
|
-
return validateMnemonic(mnemonic, english);
|
|
29
|
-
}
|
|
30
|
-
function deriveEvmKey(mnemonic) {
|
|
31
|
-
const seed = mnemonicToSeedSync(mnemonic);
|
|
32
|
-
const hdKey = HDKey.fromMasterSeed(seed);
|
|
33
|
-
const derived = hdKey.derive(ETH_DERIVATION_PATH);
|
|
34
|
-
if (!derived.privateKey) throw new Error("Failed to derive EVM private key");
|
|
35
|
-
const hex = `0x${Buffer.from(derived.privateKey).toString("hex")}`;
|
|
36
|
-
const account = privateKeyToAccount(hex);
|
|
37
|
-
return { privateKey: hex, address: account.address };
|
|
38
|
-
}
|
|
39
|
-
function deriveSolanaKeyBytes(mnemonic) {
|
|
40
|
-
const seed = mnemonicToSeedSync(mnemonic);
|
|
41
|
-
const hdKey = HDKey.fromMasterSeed(seed);
|
|
42
|
-
const derived = hdKey.derive(SOLANA_DERIVATION_PATH);
|
|
43
|
-
if (!derived.privateKey) throw new Error("Failed to derive Solana private key");
|
|
44
|
-
return new Uint8Array(derived.privateKey);
|
|
45
|
-
}
|
|
46
|
-
function deriveAllKeys(mnemonic) {
|
|
47
|
-
const { privateKey: evmPrivateKey, address: evmAddress } = deriveEvmKey(mnemonic);
|
|
48
|
-
const solanaPrivateKeyBytes = deriveSolanaKeyBytes(mnemonic);
|
|
49
|
-
return { mnemonic, evmPrivateKey, evmAddress, solanaPrivateKeyBytes };
|
|
50
|
-
}
|
|
51
|
-
var ETH_DERIVATION_PATH, SOLANA_DERIVATION_PATH;
|
|
52
|
-
var init_wallet = __esm({
|
|
53
|
-
"src/wallet.ts"() {
|
|
54
|
-
"use strict";
|
|
55
|
-
ETH_DERIVATION_PATH = "m/44'/60'/0'/0/0";
|
|
56
|
-
SOLANA_DERIVATION_PATH = "m/44'/501'/0'/0'";
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
11
|
// src/solana-balance.ts
|
|
61
12
|
var solana_balance_exports = {};
|
|
62
13
|
__export(solana_balance_exports, {
|
|
@@ -104,6 +55,28 @@ var init_solana_balance = __esm({
|
|
|
104
55
|
this.invalidate();
|
|
105
56
|
return this.checkBalance();
|
|
106
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Check if balance is sufficient for an estimated cost.
|
|
60
|
+
*/
|
|
61
|
+
async checkSufficient(estimatedCostMicros) {
|
|
62
|
+
const info = await this.checkBalance();
|
|
63
|
+
if (info.balance >= estimatedCostMicros) {
|
|
64
|
+
return { sufficient: true, info };
|
|
65
|
+
}
|
|
66
|
+
const shortfall = estimatedCostMicros - info.balance;
|
|
67
|
+
return {
|
|
68
|
+
sufficient: false,
|
|
69
|
+
info,
|
|
70
|
+
shortfall: this.formatUSDC(shortfall)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Format USDC amount (in micros) as "$X.XX".
|
|
75
|
+
*/
|
|
76
|
+
formatUSDC(amountMicros) {
|
|
77
|
+
const dollars = Number(amountMicros) / 1e6;
|
|
78
|
+
return `$${dollars.toFixed(2)}`;
|
|
79
|
+
}
|
|
107
80
|
getWalletAddress() {
|
|
108
81
|
return this.walletAddress;
|
|
109
82
|
}
|
|
@@ -122,7 +95,10 @@ var init_solana_balance = __esm({
|
|
|
122
95
|
}
|
|
123
96
|
return total;
|
|
124
97
|
} catch (err) {
|
|
125
|
-
throw new Error(
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Failed to fetch Solana USDC balance: ${err instanceof Error ? err.message : String(err)}`,
|
|
100
|
+
{ cause: err }
|
|
101
|
+
);
|
|
126
102
|
} finally {
|
|
127
103
|
clearTimeout(timer);
|
|
128
104
|
}
|
|
@@ -141,125 +117,834 @@ var init_solana_balance = __esm({
|
|
|
141
117
|
}
|
|
142
118
|
});
|
|
143
119
|
|
|
144
|
-
//
|
|
145
|
-
function
|
|
146
|
-
return
|
|
120
|
+
// node_modules/@noble/hashes/esm/utils.js
|
|
121
|
+
function isBytes(a) {
|
|
122
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
123
|
+
}
|
|
124
|
+
function anumber(n) {
|
|
125
|
+
if (!Number.isSafeInteger(n) || n < 0)
|
|
126
|
+
throw new Error("positive integer expected, got " + n);
|
|
127
|
+
}
|
|
128
|
+
function abytes(b, ...lengths) {
|
|
129
|
+
if (!isBytes(b))
|
|
130
|
+
throw new Error("Uint8Array expected");
|
|
131
|
+
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
132
|
+
throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
|
|
133
|
+
}
|
|
134
|
+
function ahash(h) {
|
|
135
|
+
if (typeof h !== "function" || typeof h.create !== "function")
|
|
136
|
+
throw new Error("Hash should be wrapped by utils.createHasher");
|
|
137
|
+
anumber(h.outputLen);
|
|
138
|
+
anumber(h.blockLen);
|
|
139
|
+
}
|
|
140
|
+
function aexists(instance, checkFinished = true) {
|
|
141
|
+
if (instance.destroyed)
|
|
142
|
+
throw new Error("Hash instance has been destroyed");
|
|
143
|
+
if (checkFinished && instance.finished)
|
|
144
|
+
throw new Error("Hash#digest() has already been called");
|
|
145
|
+
}
|
|
146
|
+
function aoutput(out, instance) {
|
|
147
|
+
abytes(out);
|
|
148
|
+
const min = instance.outputLen;
|
|
149
|
+
if (out.length < min) {
|
|
150
|
+
throw new Error("digestInto() expects output buffer of length at least " + min);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function clean(...arrays) {
|
|
154
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
155
|
+
arrays[i].fill(0);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function createView(arr) {
|
|
159
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
147
160
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
function utf8ToBytes(str) {
|
|
162
|
+
if (typeof str !== "string")
|
|
163
|
+
throw new Error("string expected");
|
|
164
|
+
return new Uint8Array(new TextEncoder().encode(str));
|
|
165
|
+
}
|
|
166
|
+
function toBytes(data) {
|
|
167
|
+
if (typeof data === "string")
|
|
168
|
+
data = utf8ToBytes(data);
|
|
169
|
+
abytes(data);
|
|
170
|
+
return data;
|
|
171
|
+
}
|
|
172
|
+
function createHasher(hashCons) {
|
|
173
|
+
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
|
|
174
|
+
const tmp = hashCons();
|
|
175
|
+
hashC.outputLen = tmp.outputLen;
|
|
176
|
+
hashC.blockLen = tmp.blockLen;
|
|
177
|
+
hashC.create = () => hashCons();
|
|
178
|
+
return hashC;
|
|
179
|
+
}
|
|
180
|
+
var Hash;
|
|
181
|
+
var init_utils = __esm({
|
|
182
|
+
"node_modules/@noble/hashes/esm/utils.js"() {
|
|
151
183
|
"use strict";
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
184
|
+
Hash = class {
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// node_modules/@noble/hashes/esm/hmac.js
|
|
190
|
+
var HMAC, hmac;
|
|
191
|
+
var init_hmac = __esm({
|
|
192
|
+
"node_modules/@noble/hashes/esm/hmac.js"() {
|
|
193
|
+
"use strict";
|
|
194
|
+
init_utils();
|
|
195
|
+
HMAC = class extends Hash {
|
|
196
|
+
constructor(hash, _key) {
|
|
197
|
+
super();
|
|
198
|
+
this.finished = false;
|
|
199
|
+
this.destroyed = false;
|
|
200
|
+
ahash(hash);
|
|
201
|
+
const key = toBytes(_key);
|
|
202
|
+
this.iHash = hash.create();
|
|
203
|
+
if (typeof this.iHash.update !== "function")
|
|
204
|
+
throw new Error("Expected instance of class which extends utils.Hash");
|
|
205
|
+
this.blockLen = this.iHash.blockLen;
|
|
206
|
+
this.outputLen = this.iHash.outputLen;
|
|
207
|
+
const blockLen = this.blockLen;
|
|
208
|
+
const pad = new Uint8Array(blockLen);
|
|
209
|
+
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
|
210
|
+
for (let i = 0; i < pad.length; i++)
|
|
211
|
+
pad[i] ^= 54;
|
|
212
|
+
this.iHash.update(pad);
|
|
213
|
+
this.oHash = hash.create();
|
|
214
|
+
for (let i = 0; i < pad.length; i++)
|
|
215
|
+
pad[i] ^= 54 ^ 92;
|
|
216
|
+
this.oHash.update(pad);
|
|
217
|
+
clean(pad);
|
|
218
|
+
}
|
|
219
|
+
update(buf) {
|
|
220
|
+
aexists(this);
|
|
221
|
+
this.iHash.update(buf);
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
digestInto(out) {
|
|
225
|
+
aexists(this);
|
|
226
|
+
abytes(out, this.outputLen);
|
|
227
|
+
this.finished = true;
|
|
228
|
+
this.iHash.digestInto(out);
|
|
229
|
+
this.oHash.update(out);
|
|
230
|
+
this.oHash.digestInto(out);
|
|
231
|
+
this.destroy();
|
|
232
|
+
}
|
|
233
|
+
digest() {
|
|
234
|
+
const out = new Uint8Array(this.oHash.outputLen);
|
|
235
|
+
this.digestInto(out);
|
|
236
|
+
return out;
|
|
237
|
+
}
|
|
238
|
+
_cloneInto(to) {
|
|
239
|
+
to || (to = Object.create(Object.getPrototypeOf(this), {}));
|
|
240
|
+
const { oHash, iHash, finished: finished2, destroyed, blockLen, outputLen } = this;
|
|
241
|
+
to = to;
|
|
242
|
+
to.finished = finished2;
|
|
243
|
+
to.destroyed = destroyed;
|
|
244
|
+
to.blockLen = blockLen;
|
|
245
|
+
to.outputLen = outputLen;
|
|
246
|
+
to.oHash = oHash._cloneInto(to.oHash);
|
|
247
|
+
to.iHash = iHash._cloneInto(to.iHash);
|
|
248
|
+
return to;
|
|
249
|
+
}
|
|
250
|
+
clone() {
|
|
251
|
+
return this._cloneInto();
|
|
252
|
+
}
|
|
253
|
+
destroy() {
|
|
254
|
+
this.destroyed = true;
|
|
255
|
+
this.oHash.destroy();
|
|
256
|
+
this.iHash.destroy();
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest();
|
|
260
|
+
hmac.create = (hash, key) => new HMAC(hash, key);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// node_modules/@noble/hashes/esm/_md.js
|
|
265
|
+
function setBigUint64(view, byteOffset, value, isLE) {
|
|
266
|
+
if (typeof view.setBigUint64 === "function")
|
|
267
|
+
return view.setBigUint64(byteOffset, value, isLE);
|
|
268
|
+
const _32n2 = BigInt(32);
|
|
269
|
+
const _u32_max = BigInt(4294967295);
|
|
270
|
+
const wh = Number(value >> _32n2 & _u32_max);
|
|
271
|
+
const wl = Number(value & _u32_max);
|
|
272
|
+
const h = isLE ? 4 : 0;
|
|
273
|
+
const l = isLE ? 0 : 4;
|
|
274
|
+
view.setUint32(byteOffset + h, wh, isLE);
|
|
275
|
+
view.setUint32(byteOffset + l, wl, isLE);
|
|
276
|
+
}
|
|
277
|
+
var HashMD, SHA512_IV;
|
|
278
|
+
var init_md = __esm({
|
|
279
|
+
"node_modules/@noble/hashes/esm/_md.js"() {
|
|
280
|
+
"use strict";
|
|
281
|
+
init_utils();
|
|
282
|
+
HashMD = class extends Hash {
|
|
283
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
284
|
+
super();
|
|
285
|
+
this.finished = false;
|
|
286
|
+
this.length = 0;
|
|
287
|
+
this.pos = 0;
|
|
288
|
+
this.destroyed = false;
|
|
289
|
+
this.blockLen = blockLen;
|
|
290
|
+
this.outputLen = outputLen;
|
|
291
|
+
this.padOffset = padOffset;
|
|
292
|
+
this.isLE = isLE;
|
|
293
|
+
this.buffer = new Uint8Array(blockLen);
|
|
294
|
+
this.view = createView(this.buffer);
|
|
295
|
+
}
|
|
296
|
+
update(data) {
|
|
297
|
+
aexists(this);
|
|
298
|
+
data = toBytes(data);
|
|
299
|
+
abytes(data);
|
|
300
|
+
const { view, buffer, blockLen } = this;
|
|
301
|
+
const len = data.length;
|
|
302
|
+
for (let pos = 0; pos < len; ) {
|
|
303
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
304
|
+
if (take === blockLen) {
|
|
305
|
+
const dataView = createView(data);
|
|
306
|
+
for (; blockLen <= len - pos; pos += blockLen)
|
|
307
|
+
this.process(dataView, pos);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
311
|
+
this.pos += take;
|
|
312
|
+
pos += take;
|
|
313
|
+
if (this.pos === blockLen) {
|
|
314
|
+
this.process(view, 0);
|
|
315
|
+
this.pos = 0;
|
|
166
316
|
}
|
|
167
|
-
],
|
|
168
|
-
pricing: {
|
|
169
|
-
perUnit: "$0.001",
|
|
170
|
-
unit: "user",
|
|
171
|
-
minimum: "$0.01 (10 users)",
|
|
172
|
-
maximum: "$0.10 (100 users)"
|
|
173
|
-
},
|
|
174
|
-
example: {
|
|
175
|
-
input: { usernames: ["elonmusk", "naval", "balaboris"] },
|
|
176
|
-
description: "Look up 3 Twitter/X user profiles"
|
|
177
317
|
}
|
|
318
|
+
this.length += data.length;
|
|
319
|
+
this.roundClean();
|
|
320
|
+
return this;
|
|
178
321
|
}
|
|
179
|
-
|
|
322
|
+
digestInto(out) {
|
|
323
|
+
aexists(this);
|
|
324
|
+
aoutput(out, this);
|
|
325
|
+
this.finished = true;
|
|
326
|
+
const { buffer, view, blockLen, isLE } = this;
|
|
327
|
+
let { pos } = this;
|
|
328
|
+
buffer[pos++] = 128;
|
|
329
|
+
clean(this.buffer.subarray(pos));
|
|
330
|
+
if (this.padOffset > blockLen - pos) {
|
|
331
|
+
this.process(view, 0);
|
|
332
|
+
pos = 0;
|
|
333
|
+
}
|
|
334
|
+
for (let i = pos; i < blockLen; i++)
|
|
335
|
+
buffer[i] = 0;
|
|
336
|
+
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
|
|
337
|
+
this.process(view, 0);
|
|
338
|
+
const oview = createView(out);
|
|
339
|
+
const len = this.outputLen;
|
|
340
|
+
if (len % 4)
|
|
341
|
+
throw new Error("_sha2: outputLen should be aligned to 32bit");
|
|
342
|
+
const outLen = len / 4;
|
|
343
|
+
const state = this.get();
|
|
344
|
+
if (outLen > state.length)
|
|
345
|
+
throw new Error("_sha2: outputLen bigger than state");
|
|
346
|
+
for (let i = 0; i < outLen; i++)
|
|
347
|
+
oview.setUint32(4 * i, state[i], isLE);
|
|
348
|
+
}
|
|
349
|
+
digest() {
|
|
350
|
+
const { buffer, outputLen } = this;
|
|
351
|
+
this.digestInto(buffer);
|
|
352
|
+
const res = buffer.slice(0, outputLen);
|
|
353
|
+
this.destroy();
|
|
354
|
+
return res;
|
|
355
|
+
}
|
|
356
|
+
_cloneInto(to) {
|
|
357
|
+
to || (to = new this.constructor());
|
|
358
|
+
to.set(...this.get());
|
|
359
|
+
const { blockLen, buffer, length, finished: finished2, destroyed, pos } = this;
|
|
360
|
+
to.destroyed = destroyed;
|
|
361
|
+
to.finished = finished2;
|
|
362
|
+
to.length = length;
|
|
363
|
+
to.pos = pos;
|
|
364
|
+
if (length % blockLen)
|
|
365
|
+
to.buffer.set(buffer);
|
|
366
|
+
return to;
|
|
367
|
+
}
|
|
368
|
+
clone() {
|
|
369
|
+
return this._cloneInto();
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
SHA512_IV = /* @__PURE__ */ Uint32Array.from([
|
|
373
|
+
1779033703,
|
|
374
|
+
4089235720,
|
|
375
|
+
3144134277,
|
|
376
|
+
2227873595,
|
|
377
|
+
1013904242,
|
|
378
|
+
4271175723,
|
|
379
|
+
2773480762,
|
|
380
|
+
1595750129,
|
|
381
|
+
1359893119,
|
|
382
|
+
2917565137,
|
|
383
|
+
2600822924,
|
|
384
|
+
725511199,
|
|
385
|
+
528734635,
|
|
386
|
+
4215389547,
|
|
387
|
+
1541459225,
|
|
388
|
+
327033209
|
|
389
|
+
]);
|
|
180
390
|
}
|
|
181
391
|
});
|
|
182
392
|
|
|
183
|
-
//
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
393
|
+
// node_modules/@noble/hashes/esm/_u64.js
|
|
394
|
+
function fromBig(n, le = false) {
|
|
395
|
+
if (le)
|
|
396
|
+
return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
|
|
397
|
+
return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
|
398
|
+
}
|
|
399
|
+
function split(lst, le = false) {
|
|
400
|
+
const len = lst.length;
|
|
401
|
+
let Ah = new Uint32Array(len);
|
|
402
|
+
let Al = new Uint32Array(len);
|
|
403
|
+
for (let i = 0; i < len; i++) {
|
|
404
|
+
const { h, l } = fromBig(lst[i], le);
|
|
405
|
+
[Ah[i], Al[i]] = [h, l];
|
|
406
|
+
}
|
|
407
|
+
return [Ah, Al];
|
|
408
|
+
}
|
|
409
|
+
function add(Ah, Al, Bh, Bl) {
|
|
410
|
+
const l = (Al >>> 0) + (Bl >>> 0);
|
|
411
|
+
return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 };
|
|
412
|
+
}
|
|
413
|
+
var U32_MASK64, _32n, shrSH, shrSL, rotrSH, rotrSL, rotrBH, rotrBL, add3L, add3H, add4L, add4H, add5L, add5H;
|
|
414
|
+
var init_u64 = __esm({
|
|
415
|
+
"node_modules/@noble/hashes/esm/_u64.js"() {
|
|
416
|
+
"use strict";
|
|
417
|
+
U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
|
|
418
|
+
_32n = /* @__PURE__ */ BigInt(32);
|
|
419
|
+
shrSH = (h, _l, s) => h >>> s;
|
|
420
|
+
shrSL = (h, l, s) => h << 32 - s | l >>> s;
|
|
421
|
+
rotrSH = (h, l, s) => h >>> s | l << 32 - s;
|
|
422
|
+
rotrSL = (h, l, s) => h << 32 - s | l >>> s;
|
|
423
|
+
rotrBH = (h, l, s) => h << 64 - s | l >>> s - 32;
|
|
424
|
+
rotrBL = (h, l, s) => h >>> s - 32 | l << 64 - s;
|
|
425
|
+
add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
|
|
426
|
+
add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0;
|
|
427
|
+
add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);
|
|
428
|
+
add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0;
|
|
429
|
+
add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);
|
|
430
|
+
add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0;
|
|
201
431
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// node_modules/@noble/hashes/esm/sha2.js
|
|
435
|
+
var K512, SHA512_Kh, SHA512_Kl, SHA512_W_H, SHA512_W_L, SHA512, sha512;
|
|
436
|
+
var init_sha2 = __esm({
|
|
437
|
+
"node_modules/@noble/hashes/esm/sha2.js"() {
|
|
438
|
+
"use strict";
|
|
439
|
+
init_md();
|
|
440
|
+
init_u64();
|
|
441
|
+
init_utils();
|
|
442
|
+
K512 = /* @__PURE__ */ (() => split([
|
|
443
|
+
"0x428a2f98d728ae22",
|
|
444
|
+
"0x7137449123ef65cd",
|
|
445
|
+
"0xb5c0fbcfec4d3b2f",
|
|
446
|
+
"0xe9b5dba58189dbbc",
|
|
447
|
+
"0x3956c25bf348b538",
|
|
448
|
+
"0x59f111f1b605d019",
|
|
449
|
+
"0x923f82a4af194f9b",
|
|
450
|
+
"0xab1c5ed5da6d8118",
|
|
451
|
+
"0xd807aa98a3030242",
|
|
452
|
+
"0x12835b0145706fbe",
|
|
453
|
+
"0x243185be4ee4b28c",
|
|
454
|
+
"0x550c7dc3d5ffb4e2",
|
|
455
|
+
"0x72be5d74f27b896f",
|
|
456
|
+
"0x80deb1fe3b1696b1",
|
|
457
|
+
"0x9bdc06a725c71235",
|
|
458
|
+
"0xc19bf174cf692694",
|
|
459
|
+
"0xe49b69c19ef14ad2",
|
|
460
|
+
"0xefbe4786384f25e3",
|
|
461
|
+
"0x0fc19dc68b8cd5b5",
|
|
462
|
+
"0x240ca1cc77ac9c65",
|
|
463
|
+
"0x2de92c6f592b0275",
|
|
464
|
+
"0x4a7484aa6ea6e483",
|
|
465
|
+
"0x5cb0a9dcbd41fbd4",
|
|
466
|
+
"0x76f988da831153b5",
|
|
467
|
+
"0x983e5152ee66dfab",
|
|
468
|
+
"0xa831c66d2db43210",
|
|
469
|
+
"0xb00327c898fb213f",
|
|
470
|
+
"0xbf597fc7beef0ee4",
|
|
471
|
+
"0xc6e00bf33da88fc2",
|
|
472
|
+
"0xd5a79147930aa725",
|
|
473
|
+
"0x06ca6351e003826f",
|
|
474
|
+
"0x142929670a0e6e70",
|
|
475
|
+
"0x27b70a8546d22ffc",
|
|
476
|
+
"0x2e1b21385c26c926",
|
|
477
|
+
"0x4d2c6dfc5ac42aed",
|
|
478
|
+
"0x53380d139d95b3df",
|
|
479
|
+
"0x650a73548baf63de",
|
|
480
|
+
"0x766a0abb3c77b2a8",
|
|
481
|
+
"0x81c2c92e47edaee6",
|
|
482
|
+
"0x92722c851482353b",
|
|
483
|
+
"0xa2bfe8a14cf10364",
|
|
484
|
+
"0xa81a664bbc423001",
|
|
485
|
+
"0xc24b8b70d0f89791",
|
|
486
|
+
"0xc76c51a30654be30",
|
|
487
|
+
"0xd192e819d6ef5218",
|
|
488
|
+
"0xd69906245565a910",
|
|
489
|
+
"0xf40e35855771202a",
|
|
490
|
+
"0x106aa07032bbd1b8",
|
|
491
|
+
"0x19a4c116b8d2d0c8",
|
|
492
|
+
"0x1e376c085141ab53",
|
|
493
|
+
"0x2748774cdf8eeb99",
|
|
494
|
+
"0x34b0bcb5e19b48a8",
|
|
495
|
+
"0x391c0cb3c5c95a63",
|
|
496
|
+
"0x4ed8aa4ae3418acb",
|
|
497
|
+
"0x5b9cca4f7763e373",
|
|
498
|
+
"0x682e6ff3d6b2b8a3",
|
|
499
|
+
"0x748f82ee5defb2fc",
|
|
500
|
+
"0x78a5636f43172f60",
|
|
501
|
+
"0x84c87814a1f0ab72",
|
|
502
|
+
"0x8cc702081a6439ec",
|
|
503
|
+
"0x90befffa23631e28",
|
|
504
|
+
"0xa4506cebde82bde9",
|
|
505
|
+
"0xbef9a3f7b2c67915",
|
|
506
|
+
"0xc67178f2e372532b",
|
|
507
|
+
"0xca273eceea26619c",
|
|
508
|
+
"0xd186b8c721c0c207",
|
|
509
|
+
"0xeada7dd6cde0eb1e",
|
|
510
|
+
"0xf57d4f7fee6ed178",
|
|
511
|
+
"0x06f067aa72176fba",
|
|
512
|
+
"0x0a637dc5a2c898a6",
|
|
513
|
+
"0x113f9804bef90dae",
|
|
514
|
+
"0x1b710b35131c471b",
|
|
515
|
+
"0x28db77f523047d84",
|
|
516
|
+
"0x32caab7b40c72493",
|
|
517
|
+
"0x3c9ebe0a15c9bebc",
|
|
518
|
+
"0x431d67c49c100d4c",
|
|
519
|
+
"0x4cc5d4becb3e42b6",
|
|
520
|
+
"0x597f299cfc657e2a",
|
|
521
|
+
"0x5fcb6fab3ad6faec",
|
|
522
|
+
"0x6c44198c4a475817"
|
|
523
|
+
].map((n) => BigInt(n))))();
|
|
524
|
+
SHA512_Kh = /* @__PURE__ */ (() => K512[0])();
|
|
525
|
+
SHA512_Kl = /* @__PURE__ */ (() => K512[1])();
|
|
526
|
+
SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);
|
|
527
|
+
SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);
|
|
528
|
+
SHA512 = class extends HashMD {
|
|
529
|
+
constructor(outputLen = 64) {
|
|
530
|
+
super(128, outputLen, 16, false);
|
|
531
|
+
this.Ah = SHA512_IV[0] | 0;
|
|
532
|
+
this.Al = SHA512_IV[1] | 0;
|
|
533
|
+
this.Bh = SHA512_IV[2] | 0;
|
|
534
|
+
this.Bl = SHA512_IV[3] | 0;
|
|
535
|
+
this.Ch = SHA512_IV[4] | 0;
|
|
536
|
+
this.Cl = SHA512_IV[5] | 0;
|
|
537
|
+
this.Dh = SHA512_IV[6] | 0;
|
|
538
|
+
this.Dl = SHA512_IV[7] | 0;
|
|
539
|
+
this.Eh = SHA512_IV[8] | 0;
|
|
540
|
+
this.El = SHA512_IV[9] | 0;
|
|
541
|
+
this.Fh = SHA512_IV[10] | 0;
|
|
542
|
+
this.Fl = SHA512_IV[11] | 0;
|
|
543
|
+
this.Gh = SHA512_IV[12] | 0;
|
|
544
|
+
this.Gl = SHA512_IV[13] | 0;
|
|
545
|
+
this.Hh = SHA512_IV[14] | 0;
|
|
546
|
+
this.Hl = SHA512_IV[15] | 0;
|
|
227
547
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
548
|
+
// prettier-ignore
|
|
549
|
+
get() {
|
|
550
|
+
const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
|
551
|
+
return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];
|
|
552
|
+
}
|
|
553
|
+
// prettier-ignore
|
|
554
|
+
set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) {
|
|
555
|
+
this.Ah = Ah | 0;
|
|
556
|
+
this.Al = Al | 0;
|
|
557
|
+
this.Bh = Bh | 0;
|
|
558
|
+
this.Bl = Bl | 0;
|
|
559
|
+
this.Ch = Ch | 0;
|
|
560
|
+
this.Cl = Cl | 0;
|
|
561
|
+
this.Dh = Dh | 0;
|
|
562
|
+
this.Dl = Dl | 0;
|
|
563
|
+
this.Eh = Eh | 0;
|
|
564
|
+
this.El = El | 0;
|
|
565
|
+
this.Fh = Fh | 0;
|
|
566
|
+
this.Fl = Fl | 0;
|
|
567
|
+
this.Gh = Gh | 0;
|
|
568
|
+
this.Gl = Gl | 0;
|
|
569
|
+
this.Hh = Hh | 0;
|
|
570
|
+
this.Hl = Hl | 0;
|
|
571
|
+
}
|
|
572
|
+
process(view, offset) {
|
|
573
|
+
for (let i = 0; i < 16; i++, offset += 4) {
|
|
574
|
+
SHA512_W_H[i] = view.getUint32(offset);
|
|
575
|
+
SHA512_W_L[i] = view.getUint32(offset += 4);
|
|
576
|
+
}
|
|
577
|
+
for (let i = 16; i < 80; i++) {
|
|
578
|
+
const W15h = SHA512_W_H[i - 15] | 0;
|
|
579
|
+
const W15l = SHA512_W_L[i - 15] | 0;
|
|
580
|
+
const s0h = rotrSH(W15h, W15l, 1) ^ rotrSH(W15h, W15l, 8) ^ shrSH(W15h, W15l, 7);
|
|
581
|
+
const s0l = rotrSL(W15h, W15l, 1) ^ rotrSL(W15h, W15l, 8) ^ shrSL(W15h, W15l, 7);
|
|
582
|
+
const W2h = SHA512_W_H[i - 2] | 0;
|
|
583
|
+
const W2l = SHA512_W_L[i - 2] | 0;
|
|
584
|
+
const s1h = rotrSH(W2h, W2l, 19) ^ rotrBH(W2h, W2l, 61) ^ shrSH(W2h, W2l, 6);
|
|
585
|
+
const s1l = rotrSL(W2h, W2l, 19) ^ rotrBL(W2h, W2l, 61) ^ shrSL(W2h, W2l, 6);
|
|
586
|
+
const SUMl = add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);
|
|
587
|
+
const SUMh = add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);
|
|
588
|
+
SHA512_W_H[i] = SUMh | 0;
|
|
589
|
+
SHA512_W_L[i] = SUMl | 0;
|
|
590
|
+
}
|
|
591
|
+
let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
|
592
|
+
for (let i = 0; i < 80; i++) {
|
|
593
|
+
const sigma1h = rotrSH(Eh, El, 14) ^ rotrSH(Eh, El, 18) ^ rotrBH(Eh, El, 41);
|
|
594
|
+
const sigma1l = rotrSL(Eh, El, 14) ^ rotrSL(Eh, El, 18) ^ rotrBL(Eh, El, 41);
|
|
595
|
+
const CHIh = Eh & Fh ^ ~Eh & Gh;
|
|
596
|
+
const CHIl = El & Fl ^ ~El & Gl;
|
|
597
|
+
const T1ll = add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);
|
|
598
|
+
const T1h = add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);
|
|
599
|
+
const T1l = T1ll | 0;
|
|
600
|
+
const sigma0h = rotrSH(Ah, Al, 28) ^ rotrBH(Ah, Al, 34) ^ rotrBH(Ah, Al, 39);
|
|
601
|
+
const sigma0l = rotrSL(Ah, Al, 28) ^ rotrBL(Ah, Al, 34) ^ rotrBL(Ah, Al, 39);
|
|
602
|
+
const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch;
|
|
603
|
+
const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl;
|
|
604
|
+
Hh = Gh | 0;
|
|
605
|
+
Hl = Gl | 0;
|
|
606
|
+
Gh = Fh | 0;
|
|
607
|
+
Gl = Fl | 0;
|
|
608
|
+
Fh = Eh | 0;
|
|
609
|
+
Fl = El | 0;
|
|
610
|
+
({ h: Eh, l: El } = add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));
|
|
611
|
+
Dh = Ch | 0;
|
|
612
|
+
Dl = Cl | 0;
|
|
613
|
+
Ch = Bh | 0;
|
|
614
|
+
Cl = Bl | 0;
|
|
615
|
+
Bh = Ah | 0;
|
|
616
|
+
Bl = Al | 0;
|
|
617
|
+
const All = add3L(T1l, sigma0l, MAJl);
|
|
618
|
+
Ah = add3H(All, T1h, sigma0h, MAJh);
|
|
619
|
+
Al = All | 0;
|
|
620
|
+
}
|
|
621
|
+
({ h: Ah, l: Al } = add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
|
|
622
|
+
({ h: Bh, l: Bl } = add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
|
|
623
|
+
({ h: Ch, l: Cl } = add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
|
|
624
|
+
({ h: Dh, l: Dl } = add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
|
|
625
|
+
({ h: Eh, l: El } = add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
|
|
626
|
+
({ h: Fh, l: Fl } = add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
|
|
627
|
+
({ h: Gh, l: Gl } = add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
|
|
628
|
+
({ h: Hh, l: Hl } = add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));
|
|
629
|
+
this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);
|
|
630
|
+
}
|
|
631
|
+
roundClean() {
|
|
632
|
+
clean(SHA512_W_H, SHA512_W_L);
|
|
633
|
+
}
|
|
634
|
+
destroy() {
|
|
635
|
+
clean(this.buffer);
|
|
636
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
sha512 = /* @__PURE__ */ createHasher(() => new SHA512());
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// node_modules/@noble/hashes/esm/sha512.js
|
|
644
|
+
var sha5122;
|
|
645
|
+
var init_sha512 = __esm({
|
|
646
|
+
"node_modules/@noble/hashes/esm/sha512.js"() {
|
|
647
|
+
"use strict";
|
|
648
|
+
init_sha2();
|
|
649
|
+
sha5122 = sha512;
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// src/wallet.ts
|
|
654
|
+
var wallet_exports = {};
|
|
655
|
+
__export(wallet_exports, {
|
|
656
|
+
deriveAllKeys: () => deriveAllKeys,
|
|
657
|
+
deriveEvmKey: () => deriveEvmKey,
|
|
658
|
+
deriveSolanaKeyBytes: () => deriveSolanaKeyBytes,
|
|
659
|
+
deriveSolanaKeyBytesLegacy: () => deriveSolanaKeyBytesLegacy,
|
|
660
|
+
generateWalletMnemonic: () => generateWalletMnemonic,
|
|
661
|
+
isValidMnemonic: () => isValidMnemonic
|
|
662
|
+
});
|
|
663
|
+
import { HDKey } from "@scure/bip32";
|
|
664
|
+
import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from "@scure/bip39";
|
|
665
|
+
import { wordlist as english } from "@scure/bip39/wordlists/english";
|
|
666
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
667
|
+
function generateWalletMnemonic() {
|
|
668
|
+
return generateMnemonic(english, 256);
|
|
240
669
|
}
|
|
241
|
-
function
|
|
242
|
-
return
|
|
670
|
+
function isValidMnemonic(mnemonic) {
|
|
671
|
+
return validateMnemonic(mnemonic, english);
|
|
672
|
+
}
|
|
673
|
+
function deriveEvmKey(mnemonic) {
|
|
674
|
+
const seed = mnemonicToSeedSync(mnemonic);
|
|
675
|
+
const hdKey = HDKey.fromMasterSeed(seed);
|
|
676
|
+
const derived = hdKey.derive(ETH_DERIVATION_PATH);
|
|
677
|
+
if (!derived.privateKey) throw new Error("Failed to derive EVM private key");
|
|
678
|
+
const hex = `0x${Buffer.from(derived.privateKey).toString("hex")}`;
|
|
679
|
+
const account = privateKeyToAccount(hex);
|
|
680
|
+
return { privateKey: hex, address: account.address };
|
|
243
681
|
}
|
|
244
|
-
|
|
245
|
-
|
|
682
|
+
function deriveSolanaKeyBytes(mnemonic) {
|
|
683
|
+
const seed = mnemonicToSeedSync(mnemonic);
|
|
684
|
+
let I = hmac(sha5122, "ed25519 seed", seed);
|
|
685
|
+
let key = I.slice(0, 32);
|
|
686
|
+
let chainCode = I.slice(32);
|
|
687
|
+
for (const index of SOLANA_HARDENED_INDICES) {
|
|
688
|
+
const data = new Uint8Array(37);
|
|
689
|
+
data[0] = 0;
|
|
690
|
+
data.set(key, 1);
|
|
691
|
+
data[33] = index >>> 24 & 255;
|
|
692
|
+
data[34] = index >>> 16 & 255;
|
|
693
|
+
data[35] = index >>> 8 & 255;
|
|
694
|
+
data[36] = index & 255;
|
|
695
|
+
I = hmac(sha5122, chainCode, data);
|
|
696
|
+
key = I.slice(0, 32);
|
|
697
|
+
chainCode = I.slice(32);
|
|
698
|
+
}
|
|
699
|
+
return new Uint8Array(key);
|
|
700
|
+
}
|
|
701
|
+
function deriveSolanaKeyBytesLegacy(mnemonic) {
|
|
702
|
+
const seed = mnemonicToSeedSync(mnemonic);
|
|
703
|
+
const hdKey = HDKey.fromMasterSeed(seed);
|
|
704
|
+
const derived = hdKey.derive("m/44'/501'/0'/0'");
|
|
705
|
+
if (!derived.privateKey) throw new Error("Failed to derive legacy Solana private key");
|
|
706
|
+
return new Uint8Array(derived.privateKey);
|
|
707
|
+
}
|
|
708
|
+
function deriveAllKeys(mnemonic) {
|
|
709
|
+
const { privateKey: evmPrivateKey, address: evmAddress } = deriveEvmKey(mnemonic);
|
|
710
|
+
const solanaPrivateKeyBytes = deriveSolanaKeyBytes(mnemonic);
|
|
711
|
+
return { mnemonic, evmPrivateKey, evmAddress, solanaPrivateKeyBytes };
|
|
712
|
+
}
|
|
713
|
+
var ETH_DERIVATION_PATH, SOLANA_HARDENED_INDICES;
|
|
714
|
+
var init_wallet = __esm({
|
|
715
|
+
"src/wallet.ts"() {
|
|
246
716
|
"use strict";
|
|
247
|
-
|
|
717
|
+
init_hmac();
|
|
718
|
+
init_sha512();
|
|
719
|
+
ETH_DERIVATION_PATH = "m/44'/60'/0'/0/0";
|
|
720
|
+
SOLANA_HARDENED_INDICES = [
|
|
721
|
+
44 + 2147483648,
|
|
722
|
+
501 + 2147483648,
|
|
723
|
+
0 + 2147483648,
|
|
724
|
+
0 + 2147483648
|
|
725
|
+
];
|
|
248
726
|
}
|
|
249
727
|
});
|
|
250
728
|
|
|
251
|
-
// src/
|
|
252
|
-
var
|
|
253
|
-
__export(
|
|
254
|
-
|
|
255
|
-
buildPartnerTools: () => buildPartnerTools,
|
|
256
|
-
getPartnerService: () => getPartnerService
|
|
729
|
+
// src/solana-sweep.ts
|
|
730
|
+
var solana_sweep_exports = {};
|
|
731
|
+
__export(solana_sweep_exports, {
|
|
732
|
+
sweepSolanaWallet: () => sweepSolanaWallet
|
|
257
733
|
});
|
|
258
|
-
|
|
259
|
-
|
|
734
|
+
import {
|
|
735
|
+
address as solAddress2,
|
|
736
|
+
createSolanaRpc as createSolanaRpc2,
|
|
737
|
+
createSolanaRpcSubscriptions,
|
|
738
|
+
createKeyPairSignerFromPrivateKeyBytes,
|
|
739
|
+
pipe,
|
|
740
|
+
createTransactionMessage,
|
|
741
|
+
setTransactionMessageFeePayer,
|
|
742
|
+
setTransactionMessageLifetimeUsingBlockhash,
|
|
743
|
+
appendTransactionMessageInstructions,
|
|
744
|
+
signTransactionMessageWithSigners,
|
|
745
|
+
getSignatureFromTransaction,
|
|
746
|
+
sendAndConfirmTransactionFactory,
|
|
747
|
+
getProgramDerivedAddress,
|
|
748
|
+
getAddressEncoder
|
|
749
|
+
} from "@solana/kit";
|
|
750
|
+
async function getAssociatedTokenAddress(owner, mint) {
|
|
751
|
+
const encoder = getAddressEncoder();
|
|
752
|
+
const [ata] = await getProgramDerivedAddress({
|
|
753
|
+
programAddress: ASSOCIATED_TOKEN_PROGRAM,
|
|
754
|
+
seeds: [encoder.encode(owner), encoder.encode(TOKEN_PROGRAM), encoder.encode(mint)]
|
|
755
|
+
});
|
|
756
|
+
return ata;
|
|
757
|
+
}
|
|
758
|
+
function buildCreateAtaIdempotentInstruction(payer, ata, owner, mint) {
|
|
759
|
+
return {
|
|
760
|
+
programAddress: ASSOCIATED_TOKEN_PROGRAM,
|
|
761
|
+
accounts: [
|
|
762
|
+
{
|
|
763
|
+
address: payer,
|
|
764
|
+
role: 3
|
|
765
|
+
/* writable signer */
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
address: ata,
|
|
769
|
+
role: 1
|
|
770
|
+
/* writable */
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
address: owner,
|
|
774
|
+
role: 0
|
|
775
|
+
/* readonly */
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
address: mint,
|
|
779
|
+
role: 0
|
|
780
|
+
/* readonly */
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
address: SYSTEM_PROGRAM,
|
|
784
|
+
role: 0
|
|
785
|
+
/* readonly */
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
address: TOKEN_PROGRAM,
|
|
789
|
+
role: 0
|
|
790
|
+
/* readonly */
|
|
791
|
+
}
|
|
792
|
+
],
|
|
793
|
+
data: new Uint8Array([1])
|
|
794
|
+
// instruction index 1 = CreateIdempotent
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function buildTokenTransferInstruction(source, destination, authority, amount) {
|
|
798
|
+
const data = new Uint8Array(9);
|
|
799
|
+
data[0] = 3;
|
|
800
|
+
const view = new DataView(data.buffer, data.byteOffset);
|
|
801
|
+
view.setBigUint64(1, amount, true);
|
|
802
|
+
return {
|
|
803
|
+
programAddress: TOKEN_PROGRAM,
|
|
804
|
+
accounts: [
|
|
805
|
+
{
|
|
806
|
+
address: source,
|
|
807
|
+
role: 1
|
|
808
|
+
/* writable */
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
address: destination,
|
|
812
|
+
role: 1
|
|
813
|
+
/* writable */
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
address: authority,
|
|
817
|
+
role: 2
|
|
818
|
+
/* signer */
|
|
819
|
+
}
|
|
820
|
+
],
|
|
821
|
+
data
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
async function sweepSolanaWallet(oldKeyBytes, newAddress, rpcUrl) {
|
|
825
|
+
const url = rpcUrl || process["env"].CLAWROUTER_SOLANA_RPC_URL || SOLANA_DEFAULT_RPC2;
|
|
826
|
+
const rpc = createSolanaRpc2(url);
|
|
827
|
+
const oldSigner = await createKeyPairSignerFromPrivateKeyBytes(oldKeyBytes);
|
|
828
|
+
const oldAddress = oldSigner.address;
|
|
829
|
+
const mint = solAddress2(SOLANA_USDC_MINT2);
|
|
830
|
+
const newOwner = solAddress2(newAddress);
|
|
831
|
+
let solBalance;
|
|
832
|
+
try {
|
|
833
|
+
const solResp = await rpc.getBalance(solAddress2(oldAddress)).send();
|
|
834
|
+
solBalance = solResp.value;
|
|
835
|
+
} catch (err) {
|
|
836
|
+
return {
|
|
837
|
+
error: `Failed to check SOL balance: ${err instanceof Error ? err.message : String(err)}`,
|
|
838
|
+
oldAddress,
|
|
839
|
+
newAddress
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
let usdcBalance = 0n;
|
|
843
|
+
let oldTokenAccount;
|
|
844
|
+
try {
|
|
845
|
+
const response = await rpc.getTokenAccountsByOwner(
|
|
846
|
+
solAddress2(oldAddress),
|
|
847
|
+
{ mint },
|
|
848
|
+
{ encoding: "jsonParsed" }
|
|
849
|
+
).send();
|
|
850
|
+
if (response.value.length > 0) {
|
|
851
|
+
for (const account of response.value) {
|
|
852
|
+
const parsed = account.account.data;
|
|
853
|
+
const amount = BigInt(parsed.parsed.info.tokenAmount.amount);
|
|
854
|
+
if (amount > 0n) {
|
|
855
|
+
usdcBalance += amount;
|
|
856
|
+
oldTokenAccount = account.pubkey;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
} catch (err) {
|
|
861
|
+
return {
|
|
862
|
+
error: `Failed to check USDC balance: ${err instanceof Error ? err.message : String(err)}`,
|
|
863
|
+
oldAddress,
|
|
864
|
+
newAddress
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
if (usdcBalance === 0n) {
|
|
868
|
+
return {
|
|
869
|
+
error: "No USDC found in old wallet. Nothing to sweep.",
|
|
870
|
+
oldAddress,
|
|
871
|
+
newAddress,
|
|
872
|
+
solBalance,
|
|
873
|
+
usdcBalance: 0n
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
const MIN_SOL_FOR_GAS = 5000000n;
|
|
877
|
+
if (solBalance < MIN_SOL_FOR_GAS) {
|
|
878
|
+
const needed = Number(MIN_SOL_FOR_GAS - solBalance) / 1e9;
|
|
879
|
+
return {
|
|
880
|
+
error: `Insufficient SOL for transaction fees. Send ~${needed.toFixed(4)} SOL to ${oldAddress} to cover gas. Current SOL balance: ${(Number(solBalance) / 1e9).toFixed(6)} SOL`,
|
|
881
|
+
oldAddress,
|
|
882
|
+
newAddress,
|
|
883
|
+
solBalance,
|
|
884
|
+
usdcBalance
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
if (!oldTokenAccount) {
|
|
888
|
+
return {
|
|
889
|
+
error: "Could not find USDC token account in old wallet.",
|
|
890
|
+
oldAddress,
|
|
891
|
+
newAddress
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
try {
|
|
895
|
+
const newAta = await getAssociatedTokenAddress(newOwner, mint);
|
|
896
|
+
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
897
|
+
const createAtaIx = buildCreateAtaIdempotentInstruction(
|
|
898
|
+
oldSigner.address,
|
|
899
|
+
newAta,
|
|
900
|
+
newOwner,
|
|
901
|
+
mint
|
|
902
|
+
);
|
|
903
|
+
const transferIx = buildTokenTransferInstruction(
|
|
904
|
+
solAddress2(oldTokenAccount),
|
|
905
|
+
newAta,
|
|
906
|
+
oldSigner.address,
|
|
907
|
+
usdcBalance
|
|
908
|
+
);
|
|
909
|
+
const txMessage = pipe(
|
|
910
|
+
createTransactionMessage({ version: 0 }),
|
|
911
|
+
(msg) => setTransactionMessageFeePayer(oldSigner.address, msg),
|
|
912
|
+
(msg) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, msg),
|
|
913
|
+
(msg) => appendTransactionMessageInstructions([createAtaIx, transferIx], msg)
|
|
914
|
+
);
|
|
915
|
+
const signedTx = await signTransactionMessageWithSigners(txMessage);
|
|
916
|
+
const txSignature = getSignatureFromTransaction(signedTx);
|
|
917
|
+
const wsUrl = url.replace("https://", "wss://").replace("http://", "ws://");
|
|
918
|
+
const rpcSubscriptions = createSolanaRpcSubscriptions(wsUrl);
|
|
919
|
+
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
|
|
920
|
+
await sendAndConfirm(signedTx, { commitment: "confirmed" });
|
|
921
|
+
const dollars = Number(usdcBalance) / 1e6;
|
|
922
|
+
return {
|
|
923
|
+
transferred: `$${dollars.toFixed(2)}`,
|
|
924
|
+
transferredMicros: usdcBalance,
|
|
925
|
+
txSignature,
|
|
926
|
+
oldAddress,
|
|
927
|
+
newAddress
|
|
928
|
+
};
|
|
929
|
+
} catch (err) {
|
|
930
|
+
return {
|
|
931
|
+
error: `Transaction failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
932
|
+
oldAddress,
|
|
933
|
+
newAddress,
|
|
934
|
+
solBalance,
|
|
935
|
+
usdcBalance
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
var SOLANA_USDC_MINT2, SOLANA_DEFAULT_RPC2, TOKEN_PROGRAM, ASSOCIATED_TOKEN_PROGRAM, SYSTEM_PROGRAM;
|
|
940
|
+
var init_solana_sweep = __esm({
|
|
941
|
+
"src/solana-sweep.ts"() {
|
|
260
942
|
"use strict";
|
|
261
|
-
|
|
262
|
-
|
|
943
|
+
SOLANA_USDC_MINT2 = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
944
|
+
SOLANA_DEFAULT_RPC2 = "https://api.mainnet-beta.solana.com";
|
|
945
|
+
TOKEN_PROGRAM = "TokenkegQfeN4jV6bme4LphiJbfPe2VopRsimuVSoZ5K";
|
|
946
|
+
ASSOCIATED_TOKEN_PROGRAM = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
|
|
947
|
+
SYSTEM_PROGRAM = "11111111111111111111111111111111";
|
|
263
948
|
}
|
|
264
949
|
});
|
|
265
950
|
|
|
@@ -307,6 +992,8 @@ var MODEL_ALIASES = {
|
|
|
307
992
|
// Google
|
|
308
993
|
gemini: "google/gemini-2.5-pro",
|
|
309
994
|
flash: "google/gemini-2.5-flash",
|
|
995
|
+
"gemini-3.1-pro-preview": "google/gemini-3.1-pro",
|
|
996
|
+
"google/gemini-3.1-pro-preview": "google/gemini-3.1-pro",
|
|
310
997
|
// xAI
|
|
311
998
|
grok: "xai/grok-3",
|
|
312
999
|
"grok-fast": "xai/grok-4-fast-reasoning",
|
|
@@ -380,7 +1067,8 @@ var BLOCKRUN_MODELS = [
|
|
|
380
1067
|
maxOutput: 128e3,
|
|
381
1068
|
reasoning: true,
|
|
382
1069
|
vision: true,
|
|
383
|
-
agentic: true
|
|
1070
|
+
agentic: true,
|
|
1071
|
+
toolCalling: true
|
|
384
1072
|
},
|
|
385
1073
|
{
|
|
386
1074
|
id: "openai/gpt-5-mini",
|
|
@@ -389,7 +1077,8 @@ var BLOCKRUN_MODELS = [
|
|
|
389
1077
|
inputPrice: 0.25,
|
|
390
1078
|
outputPrice: 2,
|
|
391
1079
|
contextWindow: 2e5,
|
|
392
|
-
maxOutput: 65536
|
|
1080
|
+
maxOutput: 65536,
|
|
1081
|
+
toolCalling: true
|
|
393
1082
|
},
|
|
394
1083
|
{
|
|
395
1084
|
id: "openai/gpt-5-nano",
|
|
@@ -398,7 +1087,8 @@ var BLOCKRUN_MODELS = [
|
|
|
398
1087
|
inputPrice: 0.05,
|
|
399
1088
|
outputPrice: 0.4,
|
|
400
1089
|
contextWindow: 128e3,
|
|
401
|
-
maxOutput: 32768
|
|
1090
|
+
maxOutput: 32768,
|
|
1091
|
+
toolCalling: true
|
|
402
1092
|
},
|
|
403
1093
|
{
|
|
404
1094
|
id: "openai/gpt-5.2-pro",
|
|
@@ -408,7 +1098,8 @@ var BLOCKRUN_MODELS = [
|
|
|
408
1098
|
outputPrice: 168,
|
|
409
1099
|
contextWindow: 4e5,
|
|
410
1100
|
maxOutput: 128e3,
|
|
411
|
-
reasoning: true
|
|
1101
|
+
reasoning: true,
|
|
1102
|
+
toolCalling: true
|
|
412
1103
|
},
|
|
413
1104
|
// OpenAI Codex Family
|
|
414
1105
|
{
|
|
@@ -419,7 +1110,8 @@ var BLOCKRUN_MODELS = [
|
|
|
419
1110
|
outputPrice: 14,
|
|
420
1111
|
contextWindow: 128e3,
|
|
421
1112
|
maxOutput: 32e3,
|
|
422
|
-
agentic: true
|
|
1113
|
+
agentic: true,
|
|
1114
|
+
toolCalling: true
|
|
423
1115
|
},
|
|
424
1116
|
// OpenAI GPT-4 Family
|
|
425
1117
|
{
|
|
@@ -430,7 +1122,8 @@ var BLOCKRUN_MODELS = [
|
|
|
430
1122
|
outputPrice: 8,
|
|
431
1123
|
contextWindow: 128e3,
|
|
432
1124
|
maxOutput: 16384,
|
|
433
|
-
vision: true
|
|
1125
|
+
vision: true,
|
|
1126
|
+
toolCalling: true
|
|
434
1127
|
},
|
|
435
1128
|
{
|
|
436
1129
|
id: "openai/gpt-4.1-mini",
|
|
@@ -439,7 +1132,8 @@ var BLOCKRUN_MODELS = [
|
|
|
439
1132
|
inputPrice: 0.4,
|
|
440
1133
|
outputPrice: 1.6,
|
|
441
1134
|
contextWindow: 128e3,
|
|
442
|
-
maxOutput: 16384
|
|
1135
|
+
maxOutput: 16384,
|
|
1136
|
+
toolCalling: true
|
|
443
1137
|
},
|
|
444
1138
|
{
|
|
445
1139
|
id: "openai/gpt-4.1-nano",
|
|
@@ -448,7 +1142,8 @@ var BLOCKRUN_MODELS = [
|
|
|
448
1142
|
inputPrice: 0.1,
|
|
449
1143
|
outputPrice: 0.4,
|
|
450
1144
|
contextWindow: 128e3,
|
|
451
|
-
maxOutput: 16384
|
|
1145
|
+
maxOutput: 16384,
|
|
1146
|
+
toolCalling: true
|
|
452
1147
|
},
|
|
453
1148
|
{
|
|
454
1149
|
id: "openai/gpt-4o",
|
|
@@ -459,7 +1154,8 @@ var BLOCKRUN_MODELS = [
|
|
|
459
1154
|
contextWindow: 128e3,
|
|
460
1155
|
maxOutput: 16384,
|
|
461
1156
|
vision: true,
|
|
462
|
-
agentic: true
|
|
1157
|
+
agentic: true,
|
|
1158
|
+
toolCalling: true
|
|
463
1159
|
},
|
|
464
1160
|
{
|
|
465
1161
|
id: "openai/gpt-4o-mini",
|
|
@@ -468,7 +1164,8 @@ var BLOCKRUN_MODELS = [
|
|
|
468
1164
|
inputPrice: 0.15,
|
|
469
1165
|
outputPrice: 0.6,
|
|
470
1166
|
contextWindow: 128e3,
|
|
471
|
-
maxOutput: 16384
|
|
1167
|
+
maxOutput: 16384,
|
|
1168
|
+
toolCalling: true
|
|
472
1169
|
},
|
|
473
1170
|
// OpenAI O-series (Reasoning)
|
|
474
1171
|
{
|
|
@@ -479,7 +1176,8 @@ var BLOCKRUN_MODELS = [
|
|
|
479
1176
|
outputPrice: 60,
|
|
480
1177
|
contextWindow: 2e5,
|
|
481
1178
|
maxOutput: 1e5,
|
|
482
|
-
reasoning: true
|
|
1179
|
+
reasoning: true,
|
|
1180
|
+
toolCalling: true
|
|
483
1181
|
},
|
|
484
1182
|
{
|
|
485
1183
|
id: "openai/o1-mini",
|
|
@@ -489,7 +1187,8 @@ var BLOCKRUN_MODELS = [
|
|
|
489
1187
|
outputPrice: 4.4,
|
|
490
1188
|
contextWindow: 128e3,
|
|
491
1189
|
maxOutput: 65536,
|
|
492
|
-
reasoning: true
|
|
1190
|
+
reasoning: true,
|
|
1191
|
+
toolCalling: true
|
|
493
1192
|
},
|
|
494
1193
|
{
|
|
495
1194
|
id: "openai/o3",
|
|
@@ -499,7 +1198,8 @@ var BLOCKRUN_MODELS = [
|
|
|
499
1198
|
outputPrice: 8,
|
|
500
1199
|
contextWindow: 2e5,
|
|
501
1200
|
maxOutput: 1e5,
|
|
502
|
-
reasoning: true
|
|
1201
|
+
reasoning: true,
|
|
1202
|
+
toolCalling: true
|
|
503
1203
|
},
|
|
504
1204
|
{
|
|
505
1205
|
id: "openai/o3-mini",
|
|
@@ -509,7 +1209,8 @@ var BLOCKRUN_MODELS = [
|
|
|
509
1209
|
outputPrice: 4.4,
|
|
510
1210
|
contextWindow: 128e3,
|
|
511
1211
|
maxOutput: 65536,
|
|
512
|
-
reasoning: true
|
|
1212
|
+
reasoning: true,
|
|
1213
|
+
toolCalling: true
|
|
513
1214
|
},
|
|
514
1215
|
{
|
|
515
1216
|
id: "openai/o4-mini",
|
|
@@ -519,7 +1220,8 @@ var BLOCKRUN_MODELS = [
|
|
|
519
1220
|
outputPrice: 4.4,
|
|
520
1221
|
contextWindow: 128e3,
|
|
521
1222
|
maxOutput: 65536,
|
|
522
|
-
reasoning: true
|
|
1223
|
+
reasoning: true,
|
|
1224
|
+
toolCalling: true
|
|
523
1225
|
},
|
|
524
1226
|
// Anthropic - all Claude models excel at agentic workflows
|
|
525
1227
|
// Use newest versions (4.6) with full provider prefix
|
|
@@ -531,7 +1233,9 @@ var BLOCKRUN_MODELS = [
|
|
|
531
1233
|
outputPrice: 5,
|
|
532
1234
|
contextWindow: 2e5,
|
|
533
1235
|
maxOutput: 8192,
|
|
534
|
-
|
|
1236
|
+
vision: true,
|
|
1237
|
+
agentic: true,
|
|
1238
|
+
toolCalling: true
|
|
535
1239
|
},
|
|
536
1240
|
{
|
|
537
1241
|
id: "anthropic/claude-sonnet-4.6",
|
|
@@ -542,7 +1246,9 @@ var BLOCKRUN_MODELS = [
|
|
|
542
1246
|
contextWindow: 2e5,
|
|
543
1247
|
maxOutput: 64e3,
|
|
544
1248
|
reasoning: true,
|
|
545
|
-
|
|
1249
|
+
vision: true,
|
|
1250
|
+
agentic: true,
|
|
1251
|
+
toolCalling: true
|
|
546
1252
|
},
|
|
547
1253
|
{
|
|
548
1254
|
id: "anthropic/claude-opus-4.6",
|
|
@@ -553,19 +1259,22 @@ var BLOCKRUN_MODELS = [
|
|
|
553
1259
|
contextWindow: 2e5,
|
|
554
1260
|
maxOutput: 32e3,
|
|
555
1261
|
reasoning: true,
|
|
556
|
-
|
|
1262
|
+
vision: true,
|
|
1263
|
+
agentic: true,
|
|
1264
|
+
toolCalling: true
|
|
557
1265
|
},
|
|
558
1266
|
// Google
|
|
559
1267
|
{
|
|
560
|
-
id: "google/gemini-3.1-pro
|
|
561
|
-
name: "Gemini 3.1 Pro
|
|
1268
|
+
id: "google/gemini-3.1-pro",
|
|
1269
|
+
name: "Gemini 3.1 Pro",
|
|
562
1270
|
version: "3.1",
|
|
563
1271
|
inputPrice: 2,
|
|
564
1272
|
outputPrice: 12,
|
|
565
1273
|
contextWindow: 105e4,
|
|
566
1274
|
maxOutput: 65536,
|
|
567
1275
|
reasoning: true,
|
|
568
|
-
vision: true
|
|
1276
|
+
vision: true,
|
|
1277
|
+
toolCalling: true
|
|
569
1278
|
},
|
|
570
1279
|
{
|
|
571
1280
|
id: "google/gemini-3-pro-preview",
|
|
@@ -576,7 +1285,8 @@ var BLOCKRUN_MODELS = [
|
|
|
576
1285
|
contextWindow: 105e4,
|
|
577
1286
|
maxOutput: 65536,
|
|
578
1287
|
reasoning: true,
|
|
579
|
-
vision: true
|
|
1288
|
+
vision: true,
|
|
1289
|
+
toolCalling: true
|
|
580
1290
|
},
|
|
581
1291
|
{
|
|
582
1292
|
id: "google/gemini-3-flash-preview",
|
|
@@ -586,7 +1296,8 @@ var BLOCKRUN_MODELS = [
|
|
|
586
1296
|
outputPrice: 3,
|
|
587
1297
|
contextWindow: 1e6,
|
|
588
1298
|
maxOutput: 65536,
|
|
589
|
-
vision: true
|
|
1299
|
+
vision: true,
|
|
1300
|
+
toolCalling: true
|
|
590
1301
|
},
|
|
591
1302
|
{
|
|
592
1303
|
id: "google/gemini-2.5-pro",
|
|
@@ -597,7 +1308,8 @@ var BLOCKRUN_MODELS = [
|
|
|
597
1308
|
contextWindow: 105e4,
|
|
598
1309
|
maxOutput: 65536,
|
|
599
1310
|
reasoning: true,
|
|
600
|
-
vision: true
|
|
1311
|
+
vision: true,
|
|
1312
|
+
toolCalling: true
|
|
601
1313
|
},
|
|
602
1314
|
{
|
|
603
1315
|
id: "google/gemini-2.5-flash",
|
|
@@ -606,7 +1318,9 @@ var BLOCKRUN_MODELS = [
|
|
|
606
1318
|
inputPrice: 0.3,
|
|
607
1319
|
outputPrice: 2.5,
|
|
608
1320
|
contextWindow: 1e6,
|
|
609
|
-
maxOutput: 65536
|
|
1321
|
+
maxOutput: 65536,
|
|
1322
|
+
vision: true,
|
|
1323
|
+
toolCalling: true
|
|
610
1324
|
},
|
|
611
1325
|
{
|
|
612
1326
|
id: "google/gemini-2.5-flash-lite",
|
|
@@ -615,7 +1329,8 @@ var BLOCKRUN_MODELS = [
|
|
|
615
1329
|
inputPrice: 0.1,
|
|
616
1330
|
outputPrice: 0.4,
|
|
617
1331
|
contextWindow: 1e6,
|
|
618
|
-
maxOutput: 65536
|
|
1332
|
+
maxOutput: 65536,
|
|
1333
|
+
toolCalling: true
|
|
619
1334
|
},
|
|
620
1335
|
// DeepSeek
|
|
621
1336
|
{
|
|
@@ -625,7 +1340,8 @@ var BLOCKRUN_MODELS = [
|
|
|
625
1340
|
inputPrice: 0.28,
|
|
626
1341
|
outputPrice: 0.42,
|
|
627
1342
|
contextWindow: 128e3,
|
|
628
|
-
maxOutput: 8192
|
|
1343
|
+
maxOutput: 8192,
|
|
1344
|
+
toolCalling: true
|
|
629
1345
|
},
|
|
630
1346
|
{
|
|
631
1347
|
id: "deepseek/deepseek-reasoner",
|
|
@@ -635,7 +1351,8 @@ var BLOCKRUN_MODELS = [
|
|
|
635
1351
|
outputPrice: 0.42,
|
|
636
1352
|
contextWindow: 128e3,
|
|
637
1353
|
maxOutput: 8192,
|
|
638
|
-
reasoning: true
|
|
1354
|
+
reasoning: true,
|
|
1355
|
+
toolCalling: true
|
|
639
1356
|
},
|
|
640
1357
|
// Moonshot / Kimi - optimized for agentic workflows
|
|
641
1358
|
{
|
|
@@ -648,7 +1365,8 @@ var BLOCKRUN_MODELS = [
|
|
|
648
1365
|
maxOutput: 8192,
|
|
649
1366
|
reasoning: true,
|
|
650
1367
|
vision: true,
|
|
651
|
-
agentic: true
|
|
1368
|
+
agentic: true,
|
|
1369
|
+
toolCalling: true
|
|
652
1370
|
},
|
|
653
1371
|
// xAI / Grok
|
|
654
1372
|
{
|
|
@@ -659,7 +1377,8 @@ var BLOCKRUN_MODELS = [
|
|
|
659
1377
|
outputPrice: 15,
|
|
660
1378
|
contextWindow: 131072,
|
|
661
1379
|
maxOutput: 16384,
|
|
662
|
-
reasoning: true
|
|
1380
|
+
reasoning: true,
|
|
1381
|
+
toolCalling: true
|
|
663
1382
|
},
|
|
664
1383
|
// grok-3-fast removed - too expensive ($5/$25), use grok-4-fast instead
|
|
665
1384
|
{
|
|
@@ -669,7 +1388,8 @@ var BLOCKRUN_MODELS = [
|
|
|
669
1388
|
inputPrice: 0.3,
|
|
670
1389
|
outputPrice: 0.5,
|
|
671
1390
|
contextWindow: 131072,
|
|
672
|
-
maxOutput: 16384
|
|
1391
|
+
maxOutput: 16384,
|
|
1392
|
+
toolCalling: true
|
|
673
1393
|
},
|
|
674
1394
|
// xAI Grok 4 Family - Ultra-cheap fast models
|
|
675
1395
|
{
|
|
@@ -680,7 +1400,8 @@ var BLOCKRUN_MODELS = [
|
|
|
680
1400
|
outputPrice: 0.5,
|
|
681
1401
|
contextWindow: 131072,
|
|
682
1402
|
maxOutput: 16384,
|
|
683
|
-
reasoning: true
|
|
1403
|
+
reasoning: true,
|
|
1404
|
+
toolCalling: true
|
|
684
1405
|
},
|
|
685
1406
|
{
|
|
686
1407
|
id: "xai/grok-4-fast-non-reasoning",
|
|
@@ -689,7 +1410,8 @@ var BLOCKRUN_MODELS = [
|
|
|
689
1410
|
inputPrice: 0.2,
|
|
690
1411
|
outputPrice: 0.5,
|
|
691
1412
|
contextWindow: 131072,
|
|
692
|
-
maxOutput: 16384
|
|
1413
|
+
maxOutput: 16384,
|
|
1414
|
+
toolCalling: true
|
|
693
1415
|
},
|
|
694
1416
|
{
|
|
695
1417
|
id: "xai/grok-4-1-fast-reasoning",
|
|
@@ -699,7 +1421,8 @@ var BLOCKRUN_MODELS = [
|
|
|
699
1421
|
outputPrice: 0.5,
|
|
700
1422
|
contextWindow: 131072,
|
|
701
1423
|
maxOutput: 16384,
|
|
702
|
-
reasoning: true
|
|
1424
|
+
reasoning: true,
|
|
1425
|
+
toolCalling: true
|
|
703
1426
|
},
|
|
704
1427
|
{
|
|
705
1428
|
id: "xai/grok-4-1-fast-non-reasoning",
|
|
@@ -708,7 +1431,8 @@ var BLOCKRUN_MODELS = [
|
|
|
708
1431
|
inputPrice: 0.2,
|
|
709
1432
|
outputPrice: 0.5,
|
|
710
1433
|
contextWindow: 131072,
|
|
711
|
-
maxOutput: 16384
|
|
1434
|
+
maxOutput: 16384,
|
|
1435
|
+
toolCalling: true
|
|
712
1436
|
},
|
|
713
1437
|
{
|
|
714
1438
|
id: "xai/grok-code-fast-1",
|
|
@@ -717,9 +1441,10 @@ var BLOCKRUN_MODELS = [
|
|
|
717
1441
|
inputPrice: 0.2,
|
|
718
1442
|
outputPrice: 1.5,
|
|
719
1443
|
contextWindow: 131072,
|
|
720
|
-
maxOutput: 16384
|
|
721
|
-
|
|
722
|
-
//
|
|
1444
|
+
maxOutput: 16384
|
|
1445
|
+
// toolCalling intentionally omitted: outputs tool calls as plain text JSON,
|
|
1446
|
+
// not OpenAI-compatible structured function calls. Will be skipped when
|
|
1447
|
+
// request has tools to prevent the "talking to itself" bug.
|
|
723
1448
|
},
|
|
724
1449
|
{
|
|
725
1450
|
id: "xai/grok-4-0709",
|
|
@@ -729,7 +1454,8 @@ var BLOCKRUN_MODELS = [
|
|
|
729
1454
|
outputPrice: 1.5,
|
|
730
1455
|
contextWindow: 131072,
|
|
731
1456
|
maxOutput: 16384,
|
|
732
|
-
reasoning: true
|
|
1457
|
+
reasoning: true,
|
|
1458
|
+
toolCalling: true
|
|
733
1459
|
},
|
|
734
1460
|
{
|
|
735
1461
|
id: "xai/grok-2-vision",
|
|
@@ -739,7 +1465,8 @@ var BLOCKRUN_MODELS = [
|
|
|
739
1465
|
outputPrice: 10,
|
|
740
1466
|
contextWindow: 131072,
|
|
741
1467
|
maxOutput: 16384,
|
|
742
|
-
vision: true
|
|
1468
|
+
vision: true,
|
|
1469
|
+
toolCalling: true
|
|
743
1470
|
},
|
|
744
1471
|
// MiniMax
|
|
745
1472
|
{
|
|
@@ -751,7 +1478,8 @@ var BLOCKRUN_MODELS = [
|
|
|
751
1478
|
contextWindow: 204800,
|
|
752
1479
|
maxOutput: 16384,
|
|
753
1480
|
reasoning: true,
|
|
754
|
-
agentic: true
|
|
1481
|
+
agentic: true,
|
|
1482
|
+
toolCalling: true
|
|
755
1483
|
},
|
|
756
1484
|
// NVIDIA - Free/cheap models
|
|
757
1485
|
{
|
|
@@ -762,6 +1490,8 @@ var BLOCKRUN_MODELS = [
|
|
|
762
1490
|
outputPrice: 0,
|
|
763
1491
|
contextWindow: 128e3,
|
|
764
1492
|
maxOutput: 16384
|
|
1493
|
+
// toolCalling intentionally omitted: free model, structured function
|
|
1494
|
+
// calling support unverified. Excluded from tool-heavy routing paths.
|
|
765
1495
|
},
|
|
766
1496
|
{
|
|
767
1497
|
id: "nvidia/kimi-k2.5",
|
|
@@ -770,7 +1500,8 @@ var BLOCKRUN_MODELS = [
|
|
|
770
1500
|
inputPrice: 0.55,
|
|
771
1501
|
outputPrice: 2.5,
|
|
772
1502
|
contextWindow: 262144,
|
|
773
|
-
maxOutput: 16384
|
|
1503
|
+
maxOutput: 16384,
|
|
1504
|
+
toolCalling: true
|
|
774
1505
|
}
|
|
775
1506
|
];
|
|
776
1507
|
function toOpenClawModel(m) {
|
|
@@ -815,6 +1546,16 @@ function isAgenticModel(modelId) {
|
|
|
815
1546
|
function getAgenticModels() {
|
|
816
1547
|
return BLOCKRUN_MODELS.filter((m) => m.agentic).map((m) => m.id);
|
|
817
1548
|
}
|
|
1549
|
+
function supportsToolCalling(modelId) {
|
|
1550
|
+
const normalized = modelId.replace("blockrun/", "");
|
|
1551
|
+
const model = BLOCKRUN_MODELS.find((m) => m.id === normalized);
|
|
1552
|
+
return model?.toolCalling ?? false;
|
|
1553
|
+
}
|
|
1554
|
+
function supportsVision(modelId) {
|
|
1555
|
+
const normalized = modelId.replace("blockrun/", "");
|
|
1556
|
+
const model = BLOCKRUN_MODELS.find((m) => m.id === normalized);
|
|
1557
|
+
return model?.vision ?? false;
|
|
1558
|
+
}
|
|
818
1559
|
function getModelContextWindow(modelId) {
|
|
819
1560
|
const normalized = modelId.replace("blockrun/", "");
|
|
820
1561
|
const model = BLOCKRUN_MODELS.find((m) => m.id === normalized);
|
|
@@ -861,13 +1602,13 @@ import { x402Client } from "@x402/fetch";
|
|
|
861
1602
|
// src/payment-preauth.ts
|
|
862
1603
|
import { x402HTTPClient } from "@x402/fetch";
|
|
863
1604
|
var DEFAULT_TTL_MS = 36e5;
|
|
864
|
-
function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS) {
|
|
1605
|
+
function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, options) {
|
|
865
1606
|
const httpClient = new x402HTTPClient(client);
|
|
866
1607
|
const cache = /* @__PURE__ */ new Map();
|
|
867
1608
|
return async (input, init) => {
|
|
868
1609
|
const request = new Request(input, init);
|
|
869
1610
|
const urlPath = new URL(request.url).pathname;
|
|
870
|
-
const cached = cache.get(urlPath);
|
|
1611
|
+
const cached = !options?.skipPreAuth ? cache.get(urlPath) : void 0;
|
|
871
1612
|
if (cached && Date.now() - cached.cachedAt < ttlMs) {
|
|
872
1613
|
try {
|
|
873
1614
|
const payload2 = await client.createPaymentPayload(cached.paymentRequired);
|
|
@@ -903,7 +1644,8 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS) {
|
|
|
903
1644
|
cache.set(urlPath, { paymentRequired, cachedAt: Date.now() });
|
|
904
1645
|
} catch (error) {
|
|
905
1646
|
throw new Error(
|
|
906
|
-
`Failed to parse payment requirements: ${error instanceof Error ? error.message : "Unknown error"}
|
|
1647
|
+
`Failed to parse payment requirements: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1648
|
+
{ cause: error }
|
|
907
1649
|
);
|
|
908
1650
|
}
|
|
909
1651
|
const payload = await client.createPaymentPayload(paymentRequired);
|
|
@@ -1007,20 +1749,18 @@ function scoreAgenticTask(text, keywords) {
|
|
|
1007
1749
|
};
|
|
1008
1750
|
}
|
|
1009
1751
|
function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
1010
|
-
const text = `${systemPrompt ?? ""} ${prompt}`.toLowerCase();
|
|
1011
1752
|
const userText = prompt.toLowerCase();
|
|
1012
1753
|
const dimensions = [
|
|
1013
|
-
//
|
|
1754
|
+
// Token count uses total estimated tokens (system + user) — context size matters for model selection
|
|
1014
1755
|
scoreTokenCount(estimatedTokens, config.tokenCountThresholds),
|
|
1015
1756
|
scoreKeywordMatch(
|
|
1016
|
-
|
|
1757
|
+
userText,
|
|
1017
1758
|
config.codeKeywords,
|
|
1018
1759
|
"codePresence",
|
|
1019
1760
|
"code",
|
|
1020
1761
|
{ low: 1, high: 2 },
|
|
1021
1762
|
{ none: 0, low: 0.5, high: 1 }
|
|
1022
1763
|
),
|
|
1023
|
-
// Reasoning markers use USER prompt only — system prompt "step by step" shouldn't trigger reasoning
|
|
1024
1764
|
scoreKeywordMatch(
|
|
1025
1765
|
userText,
|
|
1026
1766
|
config.reasoningKeywords,
|
|
@@ -1030,7 +1770,7 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1030
1770
|
{ none: 0, low: 0.7, high: 1 }
|
|
1031
1771
|
),
|
|
1032
1772
|
scoreKeywordMatch(
|
|
1033
|
-
|
|
1773
|
+
userText,
|
|
1034
1774
|
config.technicalKeywords,
|
|
1035
1775
|
"technicalTerms",
|
|
1036
1776
|
"technical",
|
|
@@ -1038,7 +1778,7 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1038
1778
|
{ none: 0, low: 0.5, high: 1 }
|
|
1039
1779
|
),
|
|
1040
1780
|
scoreKeywordMatch(
|
|
1041
|
-
|
|
1781
|
+
userText,
|
|
1042
1782
|
config.creativeKeywords,
|
|
1043
1783
|
"creativeMarkers",
|
|
1044
1784
|
"creative",
|
|
@@ -1046,18 +1786,18 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1046
1786
|
{ none: 0, low: 0.5, high: 0.7 }
|
|
1047
1787
|
),
|
|
1048
1788
|
scoreKeywordMatch(
|
|
1049
|
-
|
|
1789
|
+
userText,
|
|
1050
1790
|
config.simpleKeywords,
|
|
1051
1791
|
"simpleIndicators",
|
|
1052
1792
|
"simple",
|
|
1053
1793
|
{ low: 1, high: 2 },
|
|
1054
1794
|
{ none: 0, low: -1, high: -1 }
|
|
1055
1795
|
),
|
|
1056
|
-
scoreMultiStep(
|
|
1796
|
+
scoreMultiStep(userText),
|
|
1057
1797
|
scoreQuestionComplexity(prompt),
|
|
1058
1798
|
// 6 new dimensions
|
|
1059
1799
|
scoreKeywordMatch(
|
|
1060
|
-
|
|
1800
|
+
userText,
|
|
1061
1801
|
config.imperativeVerbs,
|
|
1062
1802
|
"imperativeVerbs",
|
|
1063
1803
|
"imperative",
|
|
@@ -1065,7 +1805,7 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1065
1805
|
{ none: 0, low: 0.3, high: 0.5 }
|
|
1066
1806
|
),
|
|
1067
1807
|
scoreKeywordMatch(
|
|
1068
|
-
|
|
1808
|
+
userText,
|
|
1069
1809
|
config.constraintIndicators,
|
|
1070
1810
|
"constraintCount",
|
|
1071
1811
|
"constraints",
|
|
@@ -1073,7 +1813,7 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1073
1813
|
{ none: 0, low: 0.3, high: 0.7 }
|
|
1074
1814
|
),
|
|
1075
1815
|
scoreKeywordMatch(
|
|
1076
|
-
|
|
1816
|
+
userText,
|
|
1077
1817
|
config.outputFormatKeywords,
|
|
1078
1818
|
"outputFormat",
|
|
1079
1819
|
"format",
|
|
@@ -1081,7 +1821,7 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1081
1821
|
{ none: 0, low: 0.4, high: 0.7 }
|
|
1082
1822
|
),
|
|
1083
1823
|
scoreKeywordMatch(
|
|
1084
|
-
|
|
1824
|
+
userText,
|
|
1085
1825
|
config.referenceKeywords,
|
|
1086
1826
|
"referenceComplexity",
|
|
1087
1827
|
"references",
|
|
@@ -1089,7 +1829,7 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1089
1829
|
{ none: 0, low: 0.3, high: 0.5 }
|
|
1090
1830
|
),
|
|
1091
1831
|
scoreKeywordMatch(
|
|
1092
|
-
|
|
1832
|
+
userText,
|
|
1093
1833
|
config.negationKeywords,
|
|
1094
1834
|
"negationComplexity",
|
|
1095
1835
|
"negation",
|
|
@@ -1097,7 +1837,7 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1097
1837
|
{ none: 0, low: 0.3, high: 0.5 }
|
|
1098
1838
|
),
|
|
1099
1839
|
scoreKeywordMatch(
|
|
1100
|
-
|
|
1840
|
+
userText,
|
|
1101
1841
|
config.domainSpecificKeywords,
|
|
1102
1842
|
"domainSpecificity",
|
|
1103
1843
|
"domain-specific",
|
|
@@ -1129,7 +1869,8 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1129
1869
|
tier: "REASONING",
|
|
1130
1870
|
confidence: Math.max(confidence2, 0.85),
|
|
1131
1871
|
signals,
|
|
1132
|
-
agenticScore
|
|
1872
|
+
agenticScore,
|
|
1873
|
+
dimensions
|
|
1133
1874
|
};
|
|
1134
1875
|
}
|
|
1135
1876
|
const { simpleMedium, mediumComplex, complexReasoning } = config.tierBoundaries;
|
|
@@ -1153,9 +1894,9 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
|
|
|
1153
1894
|
}
|
|
1154
1895
|
const confidence = calibrateConfidence(distanceFromBoundary, config.confidenceSteepness);
|
|
1155
1896
|
if (confidence < config.confidenceThreshold) {
|
|
1156
|
-
return { score: weightedScore, tier: null, confidence, signals, agenticScore };
|
|
1897
|
+
return { score: weightedScore, tier: null, confidence, signals, agenticScore, dimensions };
|
|
1157
1898
|
}
|
|
1158
|
-
return { score: weightedScore, tier, confidence, signals, agenticScore };
|
|
1899
|
+
return { score: weightedScore, tier, confidence, signals, agenticScore, dimensions };
|
|
1159
1900
|
}
|
|
1160
1901
|
function calibrateConfidence(distance, steepness) {
|
|
1161
1902
|
return 1 / (1 + Math.exp(-steepness * distance));
|
|
@@ -1163,7 +1904,9 @@ function calibrateConfidence(distance, steepness) {
|
|
|
1163
1904
|
|
|
1164
1905
|
// src/router/selector.ts
|
|
1165
1906
|
var BASELINE_MODEL_ID = "anthropic/claude-opus-4.6";
|
|
1166
|
-
|
|
1907
|
+
var BASELINE_INPUT_PRICE = 5;
|
|
1908
|
+
var BASELINE_OUTPUT_PRICE = 25;
|
|
1909
|
+
function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPricing, estimatedInputTokens, maxOutputTokens, routingProfile, agenticScore) {
|
|
1167
1910
|
const tierConfig = tierConfigs[tier];
|
|
1168
1911
|
const model = tierConfig.primary;
|
|
1169
1912
|
const pricing = modelPricing.get(model);
|
|
@@ -1173,8 +1916,8 @@ function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPric
|
|
|
1173
1916
|
const outputCost = maxOutputTokens / 1e6 * outputPrice;
|
|
1174
1917
|
const costEstimate = inputCost + outputCost;
|
|
1175
1918
|
const opusPricing = modelPricing.get(BASELINE_MODEL_ID);
|
|
1176
|
-
const opusInputPrice = opusPricing?.inputPrice ??
|
|
1177
|
-
const opusOutputPrice = opusPricing?.outputPrice ??
|
|
1919
|
+
const opusInputPrice = opusPricing?.inputPrice ?? BASELINE_INPUT_PRICE;
|
|
1920
|
+
const opusOutputPrice = opusPricing?.outputPrice ?? BASELINE_OUTPUT_PRICE;
|
|
1178
1921
|
const baselineInput = estimatedInputTokens / 1e6 * opusInputPrice;
|
|
1179
1922
|
const baselineOutput = maxOutputTokens / 1e6 * opusOutputPrice;
|
|
1180
1923
|
const baselineCost = baselineInput + baselineOutput;
|
|
@@ -1187,7 +1930,8 @@ function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPric
|
|
|
1187
1930
|
reasoning,
|
|
1188
1931
|
costEstimate,
|
|
1189
1932
|
baselineCost,
|
|
1190
|
-
savings
|
|
1933
|
+
savings,
|
|
1934
|
+
...agenticScore !== void 0 && { agenticScore }
|
|
1191
1935
|
};
|
|
1192
1936
|
}
|
|
1193
1937
|
function getFallbackChain(tier, tierConfigs) {
|
|
@@ -1202,14 +1946,24 @@ function calculateModelCost(model, modelPricing, estimatedInputTokens, maxOutput
|
|
|
1202
1946
|
const outputCost = maxOutputTokens / 1e6 * outputPrice;
|
|
1203
1947
|
const costEstimate = inputCost + outputCost;
|
|
1204
1948
|
const opusPricing = modelPricing.get(BASELINE_MODEL_ID);
|
|
1205
|
-
const opusInputPrice = opusPricing?.inputPrice ??
|
|
1206
|
-
const opusOutputPrice = opusPricing?.outputPrice ??
|
|
1949
|
+
const opusInputPrice = opusPricing?.inputPrice ?? BASELINE_INPUT_PRICE;
|
|
1950
|
+
const opusOutputPrice = opusPricing?.outputPrice ?? BASELINE_OUTPUT_PRICE;
|
|
1207
1951
|
const baselineInput = estimatedInputTokens / 1e6 * opusInputPrice;
|
|
1208
1952
|
const baselineOutput = maxOutputTokens / 1e6 * opusOutputPrice;
|
|
1209
1953
|
const baselineCost = baselineInput + baselineOutput;
|
|
1210
1954
|
const savings = routingProfile === "premium" ? 0 : baselineCost > 0 ? Math.max(0, (baselineCost - costEstimate) / baselineCost) : 0;
|
|
1211
1955
|
return { costEstimate, baselineCost, savings };
|
|
1212
1956
|
}
|
|
1957
|
+
function filterByToolCalling(models, hasTools, supportsToolCalling2) {
|
|
1958
|
+
if (!hasTools) return models;
|
|
1959
|
+
const filtered = models.filter(supportsToolCalling2);
|
|
1960
|
+
return filtered.length > 0 ? filtered : models;
|
|
1961
|
+
}
|
|
1962
|
+
function filterByVision(models, hasVision, supportsVision2) {
|
|
1963
|
+
if (!hasVision) return models;
|
|
1964
|
+
const filtered = models.filter(supportsVision2);
|
|
1965
|
+
return filtered.length > 0 ? filtered : models;
|
|
1966
|
+
}
|
|
1213
1967
|
function getFallbackChainFiltered(tier, tierConfigs, estimatedTotalTokens, getContextWindow) {
|
|
1214
1968
|
const fullChain = getFallbackChain(tier, tierConfigs);
|
|
1215
1969
|
const filtered = fullChain.filter((modelId) => {
|
|
@@ -2265,18 +3019,18 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
2265
3019
|
]
|
|
2266
3020
|
},
|
|
2267
3021
|
MEDIUM: {
|
|
2268
|
-
primary: "
|
|
2269
|
-
//
|
|
3022
|
+
primary: "moonshot/kimi-k2.5",
|
|
3023
|
+
// $0.50/$2.40 - strong tool use, proper function call format
|
|
2270
3024
|
fallback: [
|
|
3025
|
+
"deepseek/deepseek-chat",
|
|
2271
3026
|
"google/gemini-2.5-flash-lite",
|
|
2272
3027
|
// 1M context, ultra cheap ($0.10/$0.40)
|
|
2273
|
-
"deepseek/deepseek-chat",
|
|
2274
3028
|
"xai/grok-4-1-fast-non-reasoning"
|
|
2275
3029
|
// Upgraded Grok 4.1
|
|
2276
3030
|
]
|
|
2277
3031
|
},
|
|
2278
3032
|
COMPLEX: {
|
|
2279
|
-
primary: "google/gemini-3.1-pro
|
|
3033
|
+
primary: "google/gemini-3.1-pro",
|
|
2280
3034
|
// Newest Gemini 3.1 - upgraded from 3.0
|
|
2281
3035
|
fallback: [
|
|
2282
3036
|
"google/gemini-2.5-flash-lite",
|
|
@@ -2336,7 +3090,7 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
2336
3090
|
fallback: [
|
|
2337
3091
|
"anthropic/claude-haiku-4.5",
|
|
2338
3092
|
"google/gemini-2.5-flash-lite",
|
|
2339
|
-
"
|
|
3093
|
+
"deepseek/deepseek-chat"
|
|
2340
3094
|
]
|
|
2341
3095
|
},
|
|
2342
3096
|
MEDIUM: {
|
|
@@ -2356,7 +3110,7 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
2356
3110
|
"openai/gpt-5.2-codex",
|
|
2357
3111
|
"anthropic/claude-opus-4.6",
|
|
2358
3112
|
"anthropic/claude-sonnet-4.6",
|
|
2359
|
-
"google/gemini-3.1-pro
|
|
3113
|
+
"google/gemini-3.1-pro",
|
|
2360
3114
|
// Newest Gemini
|
|
2361
3115
|
"google/gemini-3-pro-preview",
|
|
2362
3116
|
"moonshot/kimi-k2.5"
|
|
@@ -2387,9 +3141,13 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
2387
3141
|
]
|
|
2388
3142
|
},
|
|
2389
3143
|
MEDIUM: {
|
|
2390
|
-
primary: "
|
|
2391
|
-
//
|
|
2392
|
-
fallback: [
|
|
3144
|
+
primary: "moonshot/kimi-k2.5",
|
|
3145
|
+
// $0.50/$2.40 - strong tool use, handles function calls correctly
|
|
3146
|
+
fallback: [
|
|
3147
|
+
"anthropic/claude-haiku-4.5",
|
|
3148
|
+
"deepseek/deepseek-chat",
|
|
3149
|
+
"xai/grok-4-1-fast-non-reasoning"
|
|
3150
|
+
]
|
|
2393
3151
|
},
|
|
2394
3152
|
COMPLEX: {
|
|
2395
3153
|
primary: "anthropic/claude-sonnet-4.6",
|
|
@@ -2397,7 +3155,7 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
2397
3155
|
"anthropic/claude-opus-4.6",
|
|
2398
3156
|
// Latest Opus - best agentic
|
|
2399
3157
|
"openai/gpt-5.2",
|
|
2400
|
-
"google/gemini-3.1-pro
|
|
3158
|
+
"google/gemini-3.1-pro",
|
|
2401
3159
|
// Newest Gemini
|
|
2402
3160
|
"google/gemini-3-pro-preview",
|
|
2403
3161
|
"xai/grok-4-0709"
|
|
@@ -2429,7 +3187,7 @@ function route(prompt, systemPrompt, maxOutputTokens, options) {
|
|
|
2429
3187
|
const ruleResult = classifyByRules(prompt, systemPrompt, estimatedTokens, config.scoring);
|
|
2430
3188
|
const { routingProfile } = options;
|
|
2431
3189
|
let tierConfigs;
|
|
2432
|
-
let profileSuffix
|
|
3190
|
+
let profileSuffix;
|
|
2433
3191
|
if (routingProfile === "eco" && config.ecoTiers) {
|
|
2434
3192
|
tierConfigs = config.ecoTiers;
|
|
2435
3193
|
profileSuffix = " | eco";
|
|
@@ -2444,6 +3202,7 @@ function route(prompt, systemPrompt, maxOutputTokens, options) {
|
|
|
2444
3202
|
tierConfigs = useAgenticTiers ? config.agenticTiers : config.tiers;
|
|
2445
3203
|
profileSuffix = useAgenticTiers ? " | agentic" : "";
|
|
2446
3204
|
}
|
|
3205
|
+
const agenticScoreValue = ruleResult.agenticScore;
|
|
2447
3206
|
if (estimatedTokens > config.overrides.maxTokensForceComplex) {
|
|
2448
3207
|
return selectModel(
|
|
2449
3208
|
"COMPLEX",
|
|
@@ -2454,7 +3213,8 @@ function route(prompt, systemPrompt, maxOutputTokens, options) {
|
|
|
2454
3213
|
modelPricing,
|
|
2455
3214
|
estimatedTokens,
|
|
2456
3215
|
maxOutputTokens,
|
|
2457
|
-
routingProfile
|
|
3216
|
+
routingProfile,
|
|
3217
|
+
agenticScoreValue
|
|
2458
3218
|
);
|
|
2459
3219
|
}
|
|
2460
3220
|
const hasStructuredOutput = systemPrompt ? /json|structured|schema/i.test(systemPrompt) : false;
|
|
@@ -2488,7 +3248,8 @@ function route(prompt, systemPrompt, maxOutputTokens, options) {
|
|
|
2488
3248
|
modelPricing,
|
|
2489
3249
|
estimatedTokens,
|
|
2490
3250
|
maxOutputTokens,
|
|
2491
|
-
routingProfile
|
|
3251
|
+
routingProfile,
|
|
3252
|
+
agenticScoreValue
|
|
2492
3253
|
);
|
|
2493
3254
|
}
|
|
2494
3255
|
|
|
@@ -3243,6 +4004,9 @@ var BalanceMonitor = class {
|
|
|
3243
4004
|
}
|
|
3244
4005
|
};
|
|
3245
4006
|
|
|
4007
|
+
// src/proxy.ts
|
|
4008
|
+
init_solana_balance();
|
|
4009
|
+
|
|
3246
4010
|
// src/auth.ts
|
|
3247
4011
|
import { writeFile, mkdir as mkdir2 } from "fs/promises";
|
|
3248
4012
|
init_wallet();
|
|
@@ -3263,7 +4027,9 @@ async function loadSavedWallet() {
|
|
|
3263
4027
|
console.error(`[ClawRouter] \u2717 CRITICAL: Wallet file exists but has invalid format!`);
|
|
3264
4028
|
console.error(`[ClawRouter] File: ${WALLET_FILE}`);
|
|
3265
4029
|
console.error(`[ClawRouter] Expected: 0x followed by 64 hex characters (66 chars total)`);
|
|
3266
|
-
console.error(
|
|
4030
|
+
console.error(
|
|
4031
|
+
`[ClawRouter] To fix: restore your backup key or set BLOCKRUN_WALLET_KEY env var`
|
|
4032
|
+
);
|
|
3267
4033
|
throw new Error(
|
|
3268
4034
|
`Wallet file at ${WALLET_FILE} is corrupted or has wrong format. Refusing to auto-generate new wallet to protect existing funds. Restore your backup key or set BLOCKRUN_WALLET_KEY environment variable.`
|
|
3269
4035
|
);
|
|
@@ -3276,7 +4042,8 @@ async function loadSavedWallet() {
|
|
|
3276
4042
|
`[ClawRouter] \u2717 Failed to read wallet file: ${err instanceof Error ? err.message : String(err)}`
|
|
3277
4043
|
);
|
|
3278
4044
|
throw new Error(
|
|
3279
|
-
`Cannot read wallet file at ${WALLET_FILE}: ${err instanceof Error ? err.message : String(err)}. Refusing to auto-generate new wallet to protect existing funds. Fix file permissions or set BLOCKRUN_WALLET_KEY environment variable
|
|
4045
|
+
`Cannot read wallet file at ${WALLET_FILE}: ${err instanceof Error ? err.message : String(err)}. Refusing to auto-generate new wallet to protect existing funds. Fix file permissions or set BLOCKRUN_WALLET_KEY environment variable.`,
|
|
4046
|
+
{ cause: err }
|
|
3280
4047
|
);
|
|
3281
4048
|
}
|
|
3282
4049
|
}
|
|
@@ -3321,7 +4088,8 @@ async function generateAndSaveWallet() {
|
|
|
3321
4088
|
console.log(`[ClawRouter] Wallet saved and verified at ${WALLET_FILE}`);
|
|
3322
4089
|
} catch (err) {
|
|
3323
4090
|
throw new Error(
|
|
3324
|
-
`Failed to verify wallet file after creation: ${err instanceof Error ? err.message : String(err)}
|
|
4091
|
+
`Failed to verify wallet file after creation: ${err instanceof Error ? err.message : String(err)}`,
|
|
4092
|
+
{ cause: err }
|
|
3325
4093
|
);
|
|
3326
4094
|
}
|
|
3327
4095
|
console.log(`[ClawRouter]`);
|
|
@@ -3347,6 +4115,29 @@ async function generateAndSaveWallet() {
|
|
|
3347
4115
|
solanaPrivateKeyBytes: derived.solanaPrivateKeyBytes
|
|
3348
4116
|
};
|
|
3349
4117
|
}
|
|
4118
|
+
async function logMigrationWarning(legacyKeyBytes, newKeyBytes) {
|
|
4119
|
+
try {
|
|
4120
|
+
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await import("@solana/kit");
|
|
4121
|
+
const [oldSigner, newSigner] = await Promise.all([
|
|
4122
|
+
createKeyPairSignerFromPrivateKeyBytes2(legacyKeyBytes),
|
|
4123
|
+
createKeyPairSignerFromPrivateKeyBytes2(newKeyBytes)
|
|
4124
|
+
]);
|
|
4125
|
+
console.log(`[ClawRouter]`);
|
|
4126
|
+
console.log(`[ClawRouter] \u26A0 SOLANA WALLET MIGRATION DETECTED`);
|
|
4127
|
+
console.log(`[ClawRouter] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
|
|
4128
|
+
console.log(`[ClawRouter] Old address (secp256k1): ${oldSigner.address}`);
|
|
4129
|
+
console.log(`[ClawRouter] New address (SLIP-10): ${newSigner.address}`);
|
|
4130
|
+
console.log(`[ClawRouter]`);
|
|
4131
|
+
console.log(`[ClawRouter] Your Solana wallet derivation has been fixed to use`);
|
|
4132
|
+
console.log(`[ClawRouter] SLIP-10 Ed25519 (Phantom/Solflare compatible).`);
|
|
4133
|
+
console.log(`[ClawRouter]`);
|
|
4134
|
+
console.log(`[ClawRouter] If you had funds in the old wallet, run:`);
|
|
4135
|
+
console.log(`[ClawRouter] /wallet migrate-solana`);
|
|
4136
|
+
console.log(`[ClawRouter] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
|
|
4137
|
+
console.log(`[ClawRouter]`);
|
|
4138
|
+
} catch {
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
3350
4141
|
async function resolveOrGenerateWalletKey() {
|
|
3351
4142
|
const saved = await loadSavedWallet();
|
|
3352
4143
|
if (saved) {
|
|
@@ -3354,13 +4145,19 @@ async function resolveOrGenerateWalletKey() {
|
|
|
3354
4145
|
const mnemonic = await loadMnemonic();
|
|
3355
4146
|
if (mnemonic) {
|
|
3356
4147
|
const solanaKeyBytes = deriveSolanaKeyBytes(mnemonic);
|
|
3357
|
-
|
|
4148
|
+
const result2 = {
|
|
3358
4149
|
key: saved,
|
|
3359
4150
|
address: account.address,
|
|
3360
4151
|
source: "saved",
|
|
3361
4152
|
mnemonic,
|
|
3362
4153
|
solanaPrivateKeyBytes: solanaKeyBytes
|
|
3363
4154
|
};
|
|
4155
|
+
const legacyKeyBytes = deriveSolanaKeyBytesLegacy(mnemonic);
|
|
4156
|
+
if (Buffer.from(legacyKeyBytes).toString("hex") !== Buffer.from(solanaKeyBytes).toString("hex")) {
|
|
4157
|
+
result2.legacySolanaKeyBytes = legacyKeyBytes;
|
|
4158
|
+
await logMigrationWarning(legacyKeyBytes, solanaKeyBytes);
|
|
4159
|
+
}
|
|
4160
|
+
return result2;
|
|
3364
4161
|
}
|
|
3365
4162
|
return { key: saved, address: account.address, source: "saved" };
|
|
3366
4163
|
}
|
|
@@ -3370,13 +4167,19 @@ async function resolveOrGenerateWalletKey() {
|
|
|
3370
4167
|
const mnemonic = await loadMnemonic();
|
|
3371
4168
|
if (mnemonic) {
|
|
3372
4169
|
const solanaKeyBytes = deriveSolanaKeyBytes(mnemonic);
|
|
3373
|
-
|
|
4170
|
+
const result2 = {
|
|
3374
4171
|
key: envKey,
|
|
3375
4172
|
address: account.address,
|
|
3376
4173
|
source: "env",
|
|
3377
4174
|
mnemonic,
|
|
3378
4175
|
solanaPrivateKeyBytes: solanaKeyBytes
|
|
3379
4176
|
};
|
|
4177
|
+
const legacyKeyBytes = deriveSolanaKeyBytesLegacy(mnemonic);
|
|
4178
|
+
if (Buffer.from(legacyKeyBytes).toString("hex") !== Buffer.from(solanaKeyBytes).toString("hex")) {
|
|
4179
|
+
result2.legacySolanaKeyBytes = legacyKeyBytes;
|
|
4180
|
+
await logMigrationWarning(legacyKeyBytes, solanaKeyBytes);
|
|
4181
|
+
}
|
|
4182
|
+
return result2;
|
|
3380
4183
|
}
|
|
3381
4184
|
return { key: envKey, address: account.address, source: "env" };
|
|
3382
4185
|
}
|
|
@@ -3392,9 +4195,7 @@ async function resolveOrGenerateWalletKey() {
|
|
|
3392
4195
|
async function setupSolana() {
|
|
3393
4196
|
const existing = await loadMnemonic();
|
|
3394
4197
|
if (existing) {
|
|
3395
|
-
throw new Error(
|
|
3396
|
-
"Solana wallet already set up. Mnemonic file exists at " + MNEMONIC_FILE
|
|
3397
|
-
);
|
|
4198
|
+
throw new Error("Solana wallet already set up. Mnemonic file exists at " + MNEMONIC_FILE);
|
|
3398
4199
|
}
|
|
3399
4200
|
const savedKey = await loadSavedWallet();
|
|
3400
4201
|
if (!savedKey) {
|
|
@@ -4175,8 +4976,9 @@ function shouldCompress(messages) {
|
|
|
4175
4976
|
}
|
|
4176
4977
|
|
|
4177
4978
|
// src/session.ts
|
|
4979
|
+
import { createHash as createHash3 } from "crypto";
|
|
4178
4980
|
var DEFAULT_SESSION_CONFIG = {
|
|
4179
|
-
enabled:
|
|
4981
|
+
enabled: true,
|
|
4180
4982
|
timeoutMs: 30 * 60 * 1e3,
|
|
4181
4983
|
// 30 minutes
|
|
4182
4984
|
headerName: "x-session-id"
|
|
@@ -4231,7 +5033,10 @@ var SessionStore = class {
|
|
|
4231
5033
|
tier,
|
|
4232
5034
|
createdAt: now,
|
|
4233
5035
|
lastUsedAt: now,
|
|
4234
|
-
requestCount: 1
|
|
5036
|
+
requestCount: 1,
|
|
5037
|
+
recentHashes: [],
|
|
5038
|
+
strikes: 0,
|
|
5039
|
+
escalated: false
|
|
4235
5040
|
});
|
|
4236
5041
|
}
|
|
4237
5042
|
}
|
|
@@ -4283,6 +5088,43 @@ var SessionStore = class {
|
|
|
4283
5088
|
}
|
|
4284
5089
|
}
|
|
4285
5090
|
}
|
|
5091
|
+
/**
|
|
5092
|
+
* Record a request content hash and detect repetitive patterns.
|
|
5093
|
+
* Returns true if escalation should be triggered (3+ consecutive similar requests).
|
|
5094
|
+
*/
|
|
5095
|
+
recordRequestHash(sessionId, hash) {
|
|
5096
|
+
const entry = this.sessions.get(sessionId);
|
|
5097
|
+
if (!entry) return false;
|
|
5098
|
+
const prev = entry.recentHashes;
|
|
5099
|
+
if (prev.length > 0 && prev[prev.length - 1] === hash) {
|
|
5100
|
+
entry.strikes++;
|
|
5101
|
+
} else {
|
|
5102
|
+
entry.strikes = 0;
|
|
5103
|
+
}
|
|
5104
|
+
entry.recentHashes.push(hash);
|
|
5105
|
+
if (entry.recentHashes.length > 3) {
|
|
5106
|
+
entry.recentHashes.shift();
|
|
5107
|
+
}
|
|
5108
|
+
return entry.strikes >= 2 && !entry.escalated;
|
|
5109
|
+
}
|
|
5110
|
+
/**
|
|
5111
|
+
* Escalate session to next tier. Returns the new model/tier or null if already at max.
|
|
5112
|
+
*/
|
|
5113
|
+
escalateSession(sessionId, tierConfigs) {
|
|
5114
|
+
const entry = this.sessions.get(sessionId);
|
|
5115
|
+
if (!entry) return null;
|
|
5116
|
+
const TIER_ORDER = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"];
|
|
5117
|
+
const currentIdx = TIER_ORDER.indexOf(entry.tier);
|
|
5118
|
+
if (currentIdx < 0 || currentIdx >= TIER_ORDER.length - 1) return null;
|
|
5119
|
+
const nextTier = TIER_ORDER[currentIdx + 1];
|
|
5120
|
+
const nextConfig = tierConfigs[nextTier];
|
|
5121
|
+
if (!nextConfig) return null;
|
|
5122
|
+
entry.model = nextConfig.primary;
|
|
5123
|
+
entry.tier = nextTier;
|
|
5124
|
+
entry.strikes = 0;
|
|
5125
|
+
entry.escalated = true;
|
|
5126
|
+
return { model: nextConfig.primary, tier: nextTier };
|
|
5127
|
+
}
|
|
4286
5128
|
/**
|
|
4287
5129
|
* Stop the cleanup interval.
|
|
4288
5130
|
*/
|
|
@@ -4303,6 +5145,17 @@ function getSessionId(headers, headerName = DEFAULT_SESSION_CONFIG.headerName) {
|
|
|
4303
5145
|
}
|
|
4304
5146
|
return void 0;
|
|
4305
5147
|
}
|
|
5148
|
+
function deriveSessionId(messages) {
|
|
5149
|
+
const firstUser = messages.find((m) => m.role === "user");
|
|
5150
|
+
if (!firstUser) return void 0;
|
|
5151
|
+
const content = typeof firstUser.content === "string" ? firstUser.content : JSON.stringify(firstUser.content);
|
|
5152
|
+
return createHash3("sha256").update(content).digest("hex").slice(0, 8);
|
|
5153
|
+
}
|
|
5154
|
+
function hashRequestContent(lastUserContent, toolCallNames) {
|
|
5155
|
+
const normalized = lastUserContent.replace(/\s+/g, " ").trim().slice(0, 500);
|
|
5156
|
+
const toolSuffix = toolCallNames?.length ? `|tools:${toolCallNames.sort().join(",")}` : "";
|
|
5157
|
+
return createHash3("sha256").update(normalized + toolSuffix).digest("hex").slice(0, 12);
|
|
5158
|
+
}
|
|
4306
5159
|
|
|
4307
5160
|
// src/updater.ts
|
|
4308
5161
|
var NPM_REGISTRY = "https://registry.npmjs.org/@blockrun/clawrouter/latest";
|
|
@@ -5060,13 +5913,36 @@ async function proxyPartnerRequest(req, res, apiBase, payFetch) {
|
|
|
5060
5913
|
}).catch(() => {
|
|
5061
5914
|
});
|
|
5062
5915
|
}
|
|
5916
|
+
async function uploadDataUriToHost(dataUri) {
|
|
5917
|
+
const match = dataUri.match(/^data:(image\/\w+);base64,(.+)$/);
|
|
5918
|
+
if (!match) throw new Error("Invalid data URI format");
|
|
5919
|
+
const [, mimeType, b64Data] = match;
|
|
5920
|
+
const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
|
|
5921
|
+
const buffer = Buffer.from(b64Data, "base64");
|
|
5922
|
+
const blob = new Blob([buffer], { type: mimeType });
|
|
5923
|
+
const form = new FormData();
|
|
5924
|
+
form.append("reqtype", "fileupload");
|
|
5925
|
+
form.append("fileToUpload", blob, `image.${ext}`);
|
|
5926
|
+
const resp = await fetch("https://catbox.moe/user/api.php", {
|
|
5927
|
+
method: "POST",
|
|
5928
|
+
body: form
|
|
5929
|
+
});
|
|
5930
|
+
if (!resp.ok) throw new Error(`catbox.moe upload failed: HTTP ${resp.status}`);
|
|
5931
|
+
const result = await resp.text();
|
|
5932
|
+
if (result.startsWith("https://")) {
|
|
5933
|
+
return result.trim();
|
|
5934
|
+
}
|
|
5935
|
+
throw new Error(`catbox.moe upload failed: ${result}`);
|
|
5936
|
+
}
|
|
5063
5937
|
async function startProxy(options) {
|
|
5064
5938
|
const walletKey = typeof options.wallet === "string" ? options.wallet : options.wallet.key;
|
|
5065
5939
|
const solanaPrivateKeyBytes = typeof options.wallet === "string" ? void 0 : options.wallet.solanaPrivateKeyBytes;
|
|
5066
5940
|
const paymentChain = options.paymentChain ?? await resolvePaymentChain();
|
|
5067
5941
|
const apiBase = options.apiBase ?? (paymentChain === "solana" && solanaPrivateKeyBytes ? BLOCKRUN_SOLANA_API : BLOCKRUN_API);
|
|
5068
5942
|
if (paymentChain === "solana" && !solanaPrivateKeyBytes) {
|
|
5069
|
-
console.warn(
|
|
5943
|
+
console.warn(
|
|
5944
|
+
`[ClawRouter] Payment chain is Solana but no Solana keys provided. Using Base (EVM).`
|
|
5945
|
+
);
|
|
5070
5946
|
} else if (paymentChain === "solana") {
|
|
5071
5947
|
console.log(`[ClawRouter] Payment chain: Solana (${BLOCKRUN_SOLANA_API})`);
|
|
5072
5948
|
}
|
|
@@ -5074,7 +5950,6 @@ async function startProxy(options) {
|
|
|
5074
5950
|
const existingProxy = await checkExistingProxy(listenPort);
|
|
5075
5951
|
if (existingProxy) {
|
|
5076
5952
|
const account2 = privateKeyToAccount3(walletKey);
|
|
5077
|
-
const balanceMonitor2 = new BalanceMonitor(account2.address);
|
|
5078
5953
|
const baseUrl2 = `http://127.0.0.1:${listenPort}`;
|
|
5079
5954
|
if (existingProxy.wallet !== account2.address) {
|
|
5080
5955
|
console.warn(
|
|
@@ -5088,17 +5963,20 @@ async function startProxy(options) {
|
|
|
5088
5963
|
);
|
|
5089
5964
|
}
|
|
5090
5965
|
} else if (paymentChain !== "base") {
|
|
5091
|
-
console.warn(
|
|
5966
|
+
console.warn(
|
|
5967
|
+
`[ClawRouter] Existing proxy on port ${listenPort} does not report paymentChain (pre-v0.11 instance). Assuming Base.`
|
|
5968
|
+
);
|
|
5092
5969
|
throw new Error(
|
|
5093
5970
|
`Existing proxy on port ${listenPort} is a pre-v0.11 instance (assumed Base) but ${paymentChain} was requested. Stop the existing proxy first or use a different port.`
|
|
5094
5971
|
);
|
|
5095
5972
|
}
|
|
5096
5973
|
let reuseSolanaAddress;
|
|
5097
5974
|
if (solanaPrivateKeyBytes) {
|
|
5098
|
-
const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit");
|
|
5099
|
-
const solanaSigner = await
|
|
5975
|
+
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await import("@solana/kit");
|
|
5976
|
+
const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes2(solanaPrivateKeyBytes);
|
|
5100
5977
|
reuseSolanaAddress = solanaSigner.address;
|
|
5101
5978
|
}
|
|
5979
|
+
const balanceMonitor2 = paymentChain === "solana" && reuseSolanaAddress ? new SolanaBalanceMonitor(reuseSolanaAddress) : new BalanceMonitor(account2.address);
|
|
5102
5980
|
options.onReady?.(listenPort);
|
|
5103
5981
|
return {
|
|
5104
5982
|
port: listenPort,
|
|
@@ -5118,8 +5996,8 @@ async function startProxy(options) {
|
|
|
5118
5996
|
let solanaAddress;
|
|
5119
5997
|
if (solanaPrivateKeyBytes) {
|
|
5120
5998
|
const { registerExactSvmScheme } = await import("@x402/svm/exact/client");
|
|
5121
|
-
const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit");
|
|
5122
|
-
const solanaSigner = await
|
|
5999
|
+
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await import("@solana/kit");
|
|
6000
|
+
const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes2(solanaPrivateKeyBytes);
|
|
5123
6001
|
solanaAddress = solanaSigner.address;
|
|
5124
6002
|
registerExactSvmScheme(x402, { signer: solanaSigner });
|
|
5125
6003
|
console.log(`[ClawRouter] Solana x402 scheme registered: ${solanaAddress}`);
|
|
@@ -5129,8 +6007,10 @@ async function startProxy(options) {
|
|
|
5129
6007
|
const chain = network.startsWith("eip155") ? "Base (EVM)" : network.startsWith("solana") ? "Solana" : network;
|
|
5130
6008
|
console.log(`[ClawRouter] Payment signed on ${chain} (${network})`);
|
|
5131
6009
|
});
|
|
5132
|
-
const payFetch = createPayFetchWithPreAuth(fetch, x402
|
|
5133
|
-
|
|
6010
|
+
const payFetch = createPayFetchWithPreAuth(fetch, x402, void 0, {
|
|
6011
|
+
skipPreAuth: paymentChain === "solana"
|
|
6012
|
+
});
|
|
6013
|
+
const balanceMonitor = paymentChain === "solana" && solanaAddress ? new SolanaBalanceMonitor(solanaAddress) : new BalanceMonitor(account.address);
|
|
5134
6014
|
const routingConfig = mergeRoutingConfig(options.routingConfig);
|
|
5135
6015
|
const modelPricing = buildModelPricing();
|
|
5136
6016
|
const routerOpts = {
|
|
@@ -5284,7 +6164,11 @@ async function startProxy(options) {
|
|
|
5284
6164
|
const existingProxy2 = await checkExistingProxy(listenPort);
|
|
5285
6165
|
if (existingProxy2) {
|
|
5286
6166
|
console.log(`[ClawRouter] Existing proxy detected on port ${listenPort}, reusing`);
|
|
5287
|
-
rejectAttempt({
|
|
6167
|
+
rejectAttempt({
|
|
6168
|
+
code: "REUSE_EXISTING",
|
|
6169
|
+
wallet: existingProxy2.wallet,
|
|
6170
|
+
existingChain: existingProxy2.paymentChain
|
|
6171
|
+
});
|
|
5288
6172
|
return;
|
|
5289
6173
|
}
|
|
5290
6174
|
if (attempt < PORT_RETRY_ATTEMPTS) {
|
|
@@ -5319,7 +6203,8 @@ async function startProxy(options) {
|
|
|
5319
6203
|
if (error.code === "REUSE_EXISTING" && error.wallet) {
|
|
5320
6204
|
if (error.existingChain && error.existingChain !== paymentChain) {
|
|
5321
6205
|
throw new Error(
|
|
5322
|
-
`Existing proxy on port ${listenPort} is using ${error.existingChain} but ${paymentChain} was requested. Stop the existing proxy first or use a different port
|
|
6206
|
+
`Existing proxy on port ${listenPort} is using ${error.existingChain} but ${paymentChain} was requested. Stop the existing proxy first or use a different port.`,
|
|
6207
|
+
{ cause: err }
|
|
5323
6208
|
);
|
|
5324
6209
|
}
|
|
5325
6210
|
const baseUrl2 = `http://127.0.0.1:${listenPort}`;
|
|
@@ -5427,15 +6312,12 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
5427
6312
|
} catch {
|
|
5428
6313
|
}
|
|
5429
6314
|
try {
|
|
5430
|
-
const response = await payFetch(
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
signal
|
|
5437
|
-
}
|
|
5438
|
-
);
|
|
6315
|
+
const response = await payFetch(upstreamUrl, {
|
|
6316
|
+
method,
|
|
6317
|
+
headers,
|
|
6318
|
+
body: requestBody.length > 0 ? new Uint8Array(requestBody) : void 0,
|
|
6319
|
+
signal
|
|
6320
|
+
});
|
|
5439
6321
|
if (response.status !== 200) {
|
|
5440
6322
|
const errorBody = await response.text();
|
|
5441
6323
|
const isProviderErr = isProviderError(response.status, errorBody);
|
|
@@ -5483,14 +6365,19 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5483
6365
|
}
|
|
5484
6366
|
let body = Buffer.concat(bodyChunks);
|
|
5485
6367
|
const originalContextSizeKB = Math.ceil(body.length / 1024);
|
|
6368
|
+
const debugMode = req.headers["x-clawrouter-debug"] !== "false";
|
|
5486
6369
|
let routingDecision;
|
|
6370
|
+
let hasTools = false;
|
|
6371
|
+
let hasVision = false;
|
|
5487
6372
|
let isStreaming = false;
|
|
5488
6373
|
let modelId = "";
|
|
5489
6374
|
let maxTokens = 4096;
|
|
5490
6375
|
let routingProfile = null;
|
|
5491
6376
|
let accumulatedContent = "";
|
|
6377
|
+
let responseInputTokens;
|
|
5492
6378
|
const isChatCompletion = req.url?.includes("/chat/completions");
|
|
5493
6379
|
const sessionId = getSessionId(req.headers);
|
|
6380
|
+
let effectiveSessionId = sessionId;
|
|
5494
6381
|
if (isChatCompletion && body.length > 0) {
|
|
5495
6382
|
try {
|
|
5496
6383
|
const parsed = JSON.parse(body.toString());
|
|
@@ -5498,10 +6385,12 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5498
6385
|
modelId = parsed.model || "";
|
|
5499
6386
|
maxTokens = parsed.max_tokens || 4096;
|
|
5500
6387
|
let bodyModified = false;
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
6388
|
+
const parsedMessages = Array.isArray(parsed.messages) ? parsed.messages : [];
|
|
6389
|
+
const lastUserMsg = [...parsedMessages].reverse().find((m) => m.role === "user");
|
|
6390
|
+
const rawLastContent = lastUserMsg?.content;
|
|
6391
|
+
const lastContent = typeof rawLastContent === "string" ? rawLastContent : Array.isArray(rawLastContent) ? rawLastContent.filter((b) => b.type === "text").map((b) => b.text ?? "").join(" ") : "";
|
|
6392
|
+
if (sessionId && parsedMessages.length > 0) {
|
|
6393
|
+
const messages = parsedMessages;
|
|
5505
6394
|
if (sessionJournal.needsContext(lastContent)) {
|
|
5506
6395
|
const journalText = sessionJournal.format(sessionId);
|
|
5507
6396
|
if (journalText) {
|
|
@@ -5512,15 +6401,312 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5512
6401
|
content: journalText + "\n\n" + messages[sysIdx].content
|
|
5513
6402
|
};
|
|
5514
6403
|
} else {
|
|
5515
|
-
messages.unshift({ role: "system", content: journalText });
|
|
6404
|
+
messages.unshift({ role: "system", content: journalText });
|
|
6405
|
+
}
|
|
6406
|
+
parsed.messages = messages;
|
|
6407
|
+
bodyModified = true;
|
|
6408
|
+
console.log(
|
|
6409
|
+
`[ClawRouter] Injected session journal (${journalText.length} chars) for session ${sessionId.slice(0, 8)}...`
|
|
6410
|
+
);
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
}
|
|
6414
|
+
if (lastContent.startsWith("/debug")) {
|
|
6415
|
+
const debugPrompt = lastContent.slice("/debug".length).trim() || "hello";
|
|
6416
|
+
const messages = parsed.messages;
|
|
6417
|
+
const systemMsg = messages?.find((m) => m.role === "system");
|
|
6418
|
+
const systemPrompt = typeof systemMsg?.content === "string" ? systemMsg.content : void 0;
|
|
6419
|
+
const fullText = `${systemPrompt ?? ""} ${debugPrompt}`;
|
|
6420
|
+
const estimatedTokens = Math.ceil(fullText.length / 4);
|
|
6421
|
+
const normalizedModel2 = typeof parsed.model === "string" ? parsed.model.trim().toLowerCase() : "";
|
|
6422
|
+
const profileName = normalizedModel2.replace("blockrun/", "");
|
|
6423
|
+
const debugProfile = ["free", "eco", "auto", "premium"].includes(profileName) ? profileName : "auto";
|
|
6424
|
+
const scoring = classifyByRules(
|
|
6425
|
+
debugPrompt,
|
|
6426
|
+
systemPrompt,
|
|
6427
|
+
estimatedTokens,
|
|
6428
|
+
DEFAULT_ROUTING_CONFIG.scoring
|
|
6429
|
+
);
|
|
6430
|
+
const debugRouting = route(debugPrompt, systemPrompt, maxTokens, {
|
|
6431
|
+
...routerOpts,
|
|
6432
|
+
routingProfile: debugProfile
|
|
6433
|
+
});
|
|
6434
|
+
const dimLines = (scoring.dimensions ?? []).map((d) => {
|
|
6435
|
+
const nameStr = (d.name + ":").padEnd(24);
|
|
6436
|
+
const scoreStr = d.score.toFixed(2).padStart(6);
|
|
6437
|
+
const sigStr = d.signal ? ` [${d.signal}]` : "";
|
|
6438
|
+
return ` ${nameStr}${scoreStr}${sigStr}`;
|
|
6439
|
+
}).join("\n");
|
|
6440
|
+
const sess = sessionId ? sessionStore.getSession(sessionId) : void 0;
|
|
6441
|
+
const sessLine = sess ? `Session: ${sessionId.slice(0, 8)}... \u2192 pinned: ${sess.model} (${sess.requestCount} requests)` : sessionId ? `Session: ${sessionId.slice(0, 8)}... \u2192 no pinned model` : "Session: none";
|
|
6442
|
+
const { simpleMedium, mediumComplex, complexReasoning } = DEFAULT_ROUTING_CONFIG.scoring.tierBoundaries;
|
|
6443
|
+
const debugText = [
|
|
6444
|
+
"ClawRouter Debug",
|
|
6445
|
+
"",
|
|
6446
|
+
`Profile: ${debugProfile} | Tier: ${debugRouting.tier} | Model: ${debugRouting.model}`,
|
|
6447
|
+
`Confidence: ${debugRouting.confidence.toFixed(2)} | Cost: $${debugRouting.costEstimate.toFixed(4)} | Savings: ${(debugRouting.savings * 100).toFixed(0)}%`,
|
|
6448
|
+
`Reasoning: ${debugRouting.reasoning}`,
|
|
6449
|
+
"",
|
|
6450
|
+
`Scoring (weighted: ${scoring.score.toFixed(3)})`,
|
|
6451
|
+
dimLines,
|
|
6452
|
+
"",
|
|
6453
|
+
`Tier Boundaries: SIMPLE <${simpleMedium.toFixed(2)} | MEDIUM <${mediumComplex.toFixed(2)} | COMPLEX <${complexReasoning.toFixed(2)} | REASONING >=${complexReasoning.toFixed(2)}`,
|
|
6454
|
+
"",
|
|
6455
|
+
sessLine
|
|
6456
|
+
].join("\n");
|
|
6457
|
+
const completionId = `chatcmpl-debug-${Date.now()}`;
|
|
6458
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
6459
|
+
const syntheticResponse = {
|
|
6460
|
+
id: completionId,
|
|
6461
|
+
object: "chat.completion",
|
|
6462
|
+
created: timestamp,
|
|
6463
|
+
model: "clawrouter/debug",
|
|
6464
|
+
choices: [
|
|
6465
|
+
{
|
|
6466
|
+
index: 0,
|
|
6467
|
+
message: { role: "assistant", content: debugText },
|
|
6468
|
+
finish_reason: "stop"
|
|
6469
|
+
}
|
|
6470
|
+
],
|
|
6471
|
+
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
6472
|
+
};
|
|
6473
|
+
if (isStreaming) {
|
|
6474
|
+
res.writeHead(200, {
|
|
6475
|
+
"Content-Type": "text/event-stream",
|
|
6476
|
+
"Cache-Control": "no-cache",
|
|
6477
|
+
Connection: "keep-alive"
|
|
6478
|
+
});
|
|
6479
|
+
const sseChunk = {
|
|
6480
|
+
id: completionId,
|
|
6481
|
+
object: "chat.completion.chunk",
|
|
6482
|
+
created: timestamp,
|
|
6483
|
+
model: "clawrouter/debug",
|
|
6484
|
+
choices: [
|
|
6485
|
+
{
|
|
6486
|
+
index: 0,
|
|
6487
|
+
delta: { role: "assistant", content: debugText },
|
|
6488
|
+
finish_reason: null
|
|
6489
|
+
}
|
|
6490
|
+
]
|
|
6491
|
+
};
|
|
6492
|
+
const sseDone = {
|
|
6493
|
+
id: completionId,
|
|
6494
|
+
object: "chat.completion.chunk",
|
|
6495
|
+
created: timestamp,
|
|
6496
|
+
model: "clawrouter/debug",
|
|
6497
|
+
choices: [{ index: 0, delta: {}, finish_reason: "stop" }]
|
|
6498
|
+
};
|
|
6499
|
+
res.write(`data: ${JSON.stringify(sseChunk)}
|
|
6500
|
+
|
|
6501
|
+
`);
|
|
6502
|
+
res.write(`data: ${JSON.stringify(sseDone)}
|
|
6503
|
+
|
|
6504
|
+
`);
|
|
6505
|
+
res.write("data: [DONE]\n\n");
|
|
6506
|
+
res.end();
|
|
6507
|
+
} else {
|
|
6508
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6509
|
+
res.end(JSON.stringify(syntheticResponse));
|
|
6510
|
+
}
|
|
6511
|
+
console.log(`[ClawRouter] /debug command \u2192 ${debugRouting.tier} | ${debugRouting.model}`);
|
|
6512
|
+
return;
|
|
6513
|
+
}
|
|
6514
|
+
if (lastContent.startsWith("/imagegen")) {
|
|
6515
|
+
const imageArgs = lastContent.slice("/imagegen".length).trim();
|
|
6516
|
+
let imageModel = "google/nano-banana";
|
|
6517
|
+
let imageSize = "1024x1024";
|
|
6518
|
+
let imagePrompt = imageArgs;
|
|
6519
|
+
const modelMatch = imageArgs.match(/--model\s+(\S+)/);
|
|
6520
|
+
if (modelMatch) {
|
|
6521
|
+
const raw = modelMatch[1];
|
|
6522
|
+
const IMAGE_MODEL_ALIASES = {
|
|
6523
|
+
"dall-e-3": "openai/dall-e-3",
|
|
6524
|
+
dalle3: "openai/dall-e-3",
|
|
6525
|
+
dalle: "openai/dall-e-3",
|
|
6526
|
+
"gpt-image": "openai/gpt-image-1",
|
|
6527
|
+
"gpt-image-1": "openai/gpt-image-1",
|
|
6528
|
+
flux: "black-forest/flux-1.1-pro",
|
|
6529
|
+
"flux-pro": "black-forest/flux-1.1-pro",
|
|
6530
|
+
banana: "google/nano-banana",
|
|
6531
|
+
"nano-banana": "google/nano-banana",
|
|
6532
|
+
"banana-pro": "google/nano-banana-pro",
|
|
6533
|
+
"nano-banana-pro": "google/nano-banana-pro"
|
|
6534
|
+
};
|
|
6535
|
+
imageModel = IMAGE_MODEL_ALIASES[raw] ?? raw;
|
|
6536
|
+
imagePrompt = imagePrompt.replace(/--model\s+\S+/, "").trim();
|
|
6537
|
+
}
|
|
6538
|
+
const sizeMatch = imageArgs.match(/--size\s+(\d+x\d+)/);
|
|
6539
|
+
if (sizeMatch) {
|
|
6540
|
+
imageSize = sizeMatch[1];
|
|
6541
|
+
imagePrompt = imagePrompt.replace(/--size\s+\d+x\d+/, "").trim();
|
|
6542
|
+
}
|
|
6543
|
+
if (!imagePrompt) {
|
|
6544
|
+
const errorText = [
|
|
6545
|
+
"Usage: /imagegen <prompt>",
|
|
6546
|
+
"",
|
|
6547
|
+
"Options:",
|
|
6548
|
+
" --model <model> Model to use (default: nano-banana)",
|
|
6549
|
+
" --size <WxH> Image size (default: 1024x1024)",
|
|
6550
|
+
"",
|
|
6551
|
+
"Models:",
|
|
6552
|
+
" nano-banana Google Gemini Flash \u2014 $0.05/image",
|
|
6553
|
+
" banana-pro Google Gemini Pro \u2014 $0.10/image (up to 4K)",
|
|
6554
|
+
" dall-e-3 OpenAI DALL-E 3 \u2014 $0.04/image",
|
|
6555
|
+
" gpt-image OpenAI GPT Image 1 \u2014 $0.02/image",
|
|
6556
|
+
" flux Black Forest Flux 1.1 Pro \u2014 $0.04/image",
|
|
6557
|
+
"",
|
|
6558
|
+
"Examples:",
|
|
6559
|
+
" /imagegen a cat wearing sunglasses",
|
|
6560
|
+
" /imagegen --model dall-e-3 a futuristic city at sunset",
|
|
6561
|
+
" /imagegen --model banana-pro --size 2048x2048 mountain landscape"
|
|
6562
|
+
].join("\n");
|
|
6563
|
+
const completionId = `chatcmpl-image-${Date.now()}`;
|
|
6564
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
6565
|
+
if (isStreaming) {
|
|
6566
|
+
res.writeHead(200, {
|
|
6567
|
+
"Content-Type": "text/event-stream",
|
|
6568
|
+
"Cache-Control": "no-cache",
|
|
6569
|
+
Connection: "keep-alive"
|
|
6570
|
+
});
|
|
6571
|
+
res.write(
|
|
6572
|
+
`data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: { role: "assistant", content: errorText }, finish_reason: null }] })}
|
|
6573
|
+
|
|
6574
|
+
`
|
|
6575
|
+
);
|
|
6576
|
+
res.write(
|
|
6577
|
+
`data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: {}, finish_reason: "stop" }] })}
|
|
6578
|
+
|
|
6579
|
+
`
|
|
6580
|
+
);
|
|
6581
|
+
res.write("data: [DONE]\n\n");
|
|
6582
|
+
res.end();
|
|
6583
|
+
} else {
|
|
6584
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6585
|
+
res.end(
|
|
6586
|
+
JSON.stringify({
|
|
6587
|
+
id: completionId,
|
|
6588
|
+
object: "chat.completion",
|
|
6589
|
+
created: timestamp,
|
|
6590
|
+
model: "clawrouter/image",
|
|
6591
|
+
choices: [
|
|
6592
|
+
{
|
|
6593
|
+
index: 0,
|
|
6594
|
+
message: { role: "assistant", content: errorText },
|
|
6595
|
+
finish_reason: "stop"
|
|
6596
|
+
}
|
|
6597
|
+
],
|
|
6598
|
+
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
6599
|
+
})
|
|
6600
|
+
);
|
|
6601
|
+
}
|
|
6602
|
+
console.log(`[ClawRouter] /imagegen command \u2192 showing usage help`);
|
|
6603
|
+
return;
|
|
6604
|
+
}
|
|
6605
|
+
console.log(
|
|
6606
|
+
`[ClawRouter] /imagegen command \u2192 ${imageModel} (${imageSize}): ${imagePrompt.slice(0, 80)}...`
|
|
6607
|
+
);
|
|
6608
|
+
try {
|
|
6609
|
+
const imageUpstreamUrl = `${apiBase}/v1/images/generations`;
|
|
6610
|
+
const imageBody = JSON.stringify({
|
|
6611
|
+
model: imageModel,
|
|
6612
|
+
prompt: imagePrompt,
|
|
6613
|
+
size: imageSize,
|
|
6614
|
+
n: 1
|
|
6615
|
+
});
|
|
6616
|
+
const imageResponse = await payFetch(imageUpstreamUrl, {
|
|
6617
|
+
method: "POST",
|
|
6618
|
+
headers: { "content-type": "application/json", "user-agent": USER_AGENT },
|
|
6619
|
+
body: imageBody
|
|
6620
|
+
});
|
|
6621
|
+
const imageResult = await imageResponse.json();
|
|
6622
|
+
let responseText;
|
|
6623
|
+
if (!imageResponse.ok || imageResult.error) {
|
|
6624
|
+
const errMsg = typeof imageResult.error === "string" ? imageResult.error : imageResult.error?.message ?? `HTTP ${imageResponse.status}`;
|
|
6625
|
+
responseText = `Image generation failed: ${errMsg}`;
|
|
6626
|
+
console.log(`[ClawRouter] /imagegen error: ${errMsg}`);
|
|
6627
|
+
} else {
|
|
6628
|
+
const images = imageResult.data ?? [];
|
|
6629
|
+
if (images.length === 0) {
|
|
6630
|
+
responseText = "Image generation returned no results.";
|
|
6631
|
+
} else {
|
|
6632
|
+
const lines = [];
|
|
6633
|
+
for (const img of images) {
|
|
6634
|
+
if (img.url) {
|
|
6635
|
+
if (img.url.startsWith("data:")) {
|
|
6636
|
+
try {
|
|
6637
|
+
const hostedUrl = await uploadDataUriToHost(img.url);
|
|
6638
|
+
lines.push(hostedUrl);
|
|
6639
|
+
} catch (uploadErr) {
|
|
6640
|
+
console.error(
|
|
6641
|
+
`[ClawRouter] /imagegen: failed to upload data URI: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
|
|
6642
|
+
);
|
|
6643
|
+
lines.push(
|
|
6644
|
+
"Image generated but upload failed. Try again or use --model dall-e-3."
|
|
6645
|
+
);
|
|
6646
|
+
}
|
|
6647
|
+
} else {
|
|
6648
|
+
lines.push(img.url);
|
|
6649
|
+
}
|
|
6650
|
+
}
|
|
6651
|
+
if (img.revised_prompt) lines.push(`Revised prompt: ${img.revised_prompt}`);
|
|
6652
|
+
}
|
|
6653
|
+
lines.push("", `Model: ${imageModel} | Size: ${imageSize}`);
|
|
6654
|
+
responseText = lines.join("\n");
|
|
5516
6655
|
}
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
6656
|
+
console.log(`[ClawRouter] /imagegen success: ${images.length} image(s) generated`);
|
|
6657
|
+
}
|
|
6658
|
+
const completionId = `chatcmpl-image-${Date.now()}`;
|
|
6659
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
6660
|
+
if (isStreaming) {
|
|
6661
|
+
res.writeHead(200, {
|
|
6662
|
+
"Content-Type": "text/event-stream",
|
|
6663
|
+
"Cache-Control": "no-cache",
|
|
6664
|
+
Connection: "keep-alive"
|
|
6665
|
+
});
|
|
6666
|
+
res.write(
|
|
6667
|
+
`data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: { role: "assistant", content: responseText }, finish_reason: null }] })}
|
|
6668
|
+
|
|
6669
|
+
`
|
|
6670
|
+
);
|
|
6671
|
+
res.write(
|
|
6672
|
+
`data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: {}, finish_reason: "stop" }] })}
|
|
6673
|
+
|
|
6674
|
+
`
|
|
6675
|
+
);
|
|
6676
|
+
res.write("data: [DONE]\n\n");
|
|
6677
|
+
res.end();
|
|
6678
|
+
} else {
|
|
6679
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6680
|
+
res.end(
|
|
6681
|
+
JSON.stringify({
|
|
6682
|
+
id: completionId,
|
|
6683
|
+
object: "chat.completion",
|
|
6684
|
+
created: timestamp,
|
|
6685
|
+
model: "clawrouter/image",
|
|
6686
|
+
choices: [
|
|
6687
|
+
{
|
|
6688
|
+
index: 0,
|
|
6689
|
+
message: { role: "assistant", content: responseText },
|
|
6690
|
+
finish_reason: "stop"
|
|
6691
|
+
}
|
|
6692
|
+
],
|
|
6693
|
+
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
6694
|
+
})
|
|
6695
|
+
);
|
|
6696
|
+
}
|
|
6697
|
+
} catch (err) {
|
|
6698
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
6699
|
+
console.error(`[ClawRouter] /imagegen error: ${errMsg}`);
|
|
6700
|
+
if (!res.headersSent) {
|
|
6701
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
6702
|
+
res.end(
|
|
6703
|
+
JSON.stringify({
|
|
6704
|
+
error: { message: `Image generation failed: ${errMsg}`, type: "image_error" }
|
|
6705
|
+
})
|
|
5521
6706
|
);
|
|
5522
6707
|
}
|
|
5523
6708
|
}
|
|
6709
|
+
return;
|
|
5524
6710
|
}
|
|
5525
6711
|
if (parsed.stream === true) {
|
|
5526
6712
|
parsed.stream = false;
|
|
@@ -5562,54 +6748,118 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5562
6748
|
latencyMs: 0
|
|
5563
6749
|
});
|
|
5564
6750
|
} else {
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
const
|
|
6751
|
+
effectiveSessionId = getSessionId(req.headers) ?? deriveSessionId(parsedMessages);
|
|
6752
|
+
const existingSession = effectiveSessionId ? sessionStore.getSession(effectiveSessionId) : void 0;
|
|
6753
|
+
const rawPrompt = lastUserMsg?.content;
|
|
6754
|
+
const prompt = typeof rawPrompt === "string" ? rawPrompt : Array.isArray(rawPrompt) ? rawPrompt.filter((b) => b.type === "text").map((b) => b.text ?? "").join(" ") : "";
|
|
6755
|
+
const systemMsg = parsedMessages.find((m) => m.role === "system");
|
|
6756
|
+
const systemPrompt = typeof systemMsg?.content === "string" ? systemMsg.content : void 0;
|
|
6757
|
+
const tools = parsed.tools;
|
|
6758
|
+
hasTools = Array.isArray(tools) && tools.length > 0;
|
|
6759
|
+
if (hasTools && tools) {
|
|
6760
|
+
console.log(`[ClawRouter] Tools detected (${tools.length}), agentic mode via keywords`);
|
|
6761
|
+
}
|
|
6762
|
+
hasVision = parsedMessages.some((m) => {
|
|
6763
|
+
if (Array.isArray(m.content)) {
|
|
6764
|
+
return m.content.some((p) => p.type === "image_url");
|
|
6765
|
+
}
|
|
6766
|
+
return false;
|
|
6767
|
+
});
|
|
6768
|
+
if (hasVision) {
|
|
6769
|
+
console.log(`[ClawRouter] Vision content detected, filtering to vision-capable models`);
|
|
6770
|
+
}
|
|
6771
|
+
routingDecision = route(prompt, systemPrompt, maxTokens, {
|
|
6772
|
+
...routerOpts,
|
|
6773
|
+
routingProfile: routingProfile ?? void 0
|
|
6774
|
+
});
|
|
5569
6775
|
if (existingSession) {
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
6776
|
+
const tierRank = {
|
|
6777
|
+
SIMPLE: 0,
|
|
6778
|
+
MEDIUM: 1,
|
|
6779
|
+
COMPLEX: 2,
|
|
6780
|
+
REASONING: 3
|
|
6781
|
+
};
|
|
6782
|
+
const existingRank = tierRank[existingSession.tier] ?? 0;
|
|
6783
|
+
const newRank = tierRank[routingDecision.tier] ?? 0;
|
|
6784
|
+
if (newRank > existingRank) {
|
|
6785
|
+
console.log(
|
|
6786
|
+
`[ClawRouter] Session ${effectiveSessionId?.slice(0, 8)}... upgrading: ${existingSession.tier} \u2192 ${routingDecision.tier} (${routingDecision.model})`
|
|
6787
|
+
);
|
|
6788
|
+
parsed.model = routingDecision.model;
|
|
6789
|
+
modelId = routingDecision.model;
|
|
6790
|
+
bodyModified = true;
|
|
6791
|
+
if (effectiveSessionId) {
|
|
6792
|
+
sessionStore.setSession(
|
|
6793
|
+
effectiveSessionId,
|
|
6794
|
+
routingDecision.model,
|
|
6795
|
+
routingDecision.tier
|
|
6796
|
+
);
|
|
5586
6797
|
}
|
|
5587
|
-
}
|
|
5588
|
-
const systemMsg = messages?.find((m) => m.role === "system");
|
|
5589
|
-
const prompt = typeof lastUserMsg?.content === "string" ? lastUserMsg.content : "";
|
|
5590
|
-
const systemPrompt = typeof systemMsg?.content === "string" ? systemMsg.content : void 0;
|
|
5591
|
-
const tools = parsed.tools;
|
|
5592
|
-
const hasTools = Array.isArray(tools) && tools.length > 0;
|
|
5593
|
-
if (hasTools && tools) {
|
|
6798
|
+
} else {
|
|
5594
6799
|
console.log(
|
|
5595
|
-
`[ClawRouter]
|
|
6800
|
+
`[ClawRouter] Session ${effectiveSessionId?.slice(0, 8)}... keeping pinned model: ${existingSession.model} (${existingSession.tier} >= ${routingDecision.tier})`
|
|
6801
|
+
);
|
|
6802
|
+
parsed.model = existingSession.model;
|
|
6803
|
+
modelId = existingSession.model;
|
|
6804
|
+
bodyModified = true;
|
|
6805
|
+
sessionStore.touchSession(effectiveSessionId);
|
|
6806
|
+
routingDecision = {
|
|
6807
|
+
...routingDecision,
|
|
6808
|
+
model: existingSession.model,
|
|
6809
|
+
tier: existingSession.tier
|
|
6810
|
+
};
|
|
6811
|
+
}
|
|
6812
|
+
const lastAssistantMsg = [...parsedMessages].reverse().find((m) => m.role === "assistant");
|
|
6813
|
+
const assistantToolCalls = lastAssistantMsg?.tool_calls;
|
|
6814
|
+
const toolCallNames = Array.isArray(assistantToolCalls) ? assistantToolCalls.map((tc) => tc.function?.name).filter((n) => Boolean(n)) : void 0;
|
|
6815
|
+
const contentHash = hashRequestContent(prompt, toolCallNames);
|
|
6816
|
+
const shouldEscalate = sessionStore.recordRequestHash(effectiveSessionId, contentHash);
|
|
6817
|
+
if (shouldEscalate) {
|
|
6818
|
+
const activeTierConfigs = (() => {
|
|
6819
|
+
if (routingDecision.reasoning?.includes("agentic") && routerOpts.config.agenticTiers) {
|
|
6820
|
+
return routerOpts.config.agenticTiers;
|
|
6821
|
+
}
|
|
6822
|
+
if (routingProfile === "eco" && routerOpts.config.ecoTiers) {
|
|
6823
|
+
return routerOpts.config.ecoTiers;
|
|
6824
|
+
}
|
|
6825
|
+
if (routingProfile === "premium" && routerOpts.config.premiumTiers) {
|
|
6826
|
+
return routerOpts.config.premiumTiers;
|
|
6827
|
+
}
|
|
6828
|
+
return routerOpts.config.tiers;
|
|
6829
|
+
})();
|
|
6830
|
+
const escalation = sessionStore.escalateSession(
|
|
6831
|
+
effectiveSessionId,
|
|
6832
|
+
activeTierConfigs
|
|
5596
6833
|
);
|
|
6834
|
+
if (escalation) {
|
|
6835
|
+
console.log(
|
|
6836
|
+
`[ClawRouter] \u26A1 3-strike escalation: ${existingSession.model} \u2192 ${escalation.model} (${existingSession.tier} \u2192 ${escalation.tier})`
|
|
6837
|
+
);
|
|
6838
|
+
parsed.model = escalation.model;
|
|
6839
|
+
modelId = escalation.model;
|
|
6840
|
+
routingDecision = {
|
|
6841
|
+
...routingDecision,
|
|
6842
|
+
model: escalation.model,
|
|
6843
|
+
tier: escalation.tier
|
|
6844
|
+
};
|
|
6845
|
+
}
|
|
5597
6846
|
}
|
|
5598
|
-
|
|
5599
|
-
...routerOpts,
|
|
5600
|
-
routingProfile: routingProfile ?? void 0
|
|
5601
|
-
});
|
|
6847
|
+
} else {
|
|
5602
6848
|
parsed.model = routingDecision.model;
|
|
5603
6849
|
modelId = routingDecision.model;
|
|
5604
6850
|
bodyModified = true;
|
|
5605
|
-
if (
|
|
5606
|
-
sessionStore.setSession(
|
|
6851
|
+
if (effectiveSessionId) {
|
|
6852
|
+
sessionStore.setSession(
|
|
6853
|
+
effectiveSessionId,
|
|
6854
|
+
routingDecision.model,
|
|
6855
|
+
routingDecision.tier
|
|
6856
|
+
);
|
|
5607
6857
|
console.log(
|
|
5608
|
-
`[ClawRouter] Session ${
|
|
6858
|
+
`[ClawRouter] Session ${effectiveSessionId.slice(0, 8)}... pinned to model: ${routingDecision.model}`
|
|
5609
6859
|
);
|
|
5610
6860
|
}
|
|
5611
|
-
options.onRouted?.(routingDecision);
|
|
5612
6861
|
}
|
|
6862
|
+
options.onRouted?.(routingDecision);
|
|
5613
6863
|
}
|
|
5614
6864
|
}
|
|
5615
6865
|
if (bodyModified) {
|
|
@@ -5702,6 +6952,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5702
6952
|
}
|
|
5703
6953
|
deduplicator.markInflight(dedupKey);
|
|
5704
6954
|
let estimatedCostMicros;
|
|
6955
|
+
let balanceFallbackNotice;
|
|
5705
6956
|
const isFreeModel = modelId === FREE_MODEL;
|
|
5706
6957
|
if (modelId && !options.skipBalanceCheck && !isFreeModel) {
|
|
5707
6958
|
const estimated = estimateAmount(modelId, body.length, maxTokens);
|
|
@@ -5712,12 +6963,17 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5712
6963
|
if (sufficiency.info.isEmpty || !sufficiency.sufficient) {
|
|
5713
6964
|
const originalModel = modelId;
|
|
5714
6965
|
console.log(
|
|
5715
|
-
`[ClawRouter] Wallet ${sufficiency.info.isEmpty ? "empty" : "insufficient"} (
|
|
6966
|
+
`[ClawRouter] Wallet ${sufficiency.info.isEmpty ? "empty" : "insufficient"} (${sufficiency.info.balanceUSD}), falling back to free model: ${FREE_MODEL} (requested: ${originalModel})`
|
|
5716
6967
|
);
|
|
5717
6968
|
modelId = FREE_MODEL;
|
|
5718
6969
|
const parsed = JSON.parse(body.toString());
|
|
5719
6970
|
parsed.model = FREE_MODEL;
|
|
5720
6971
|
body = Buffer.from(JSON.stringify(parsed));
|
|
6972
|
+
balanceFallbackNotice = sufficiency.info.isEmpty ? `> **\u26A0\uFE0F Wallet empty** \u2014 using free model. Fund your wallet to use ${originalModel}.
|
|
6973
|
+
|
|
6974
|
+
` : `> **\u26A0\uFE0F Insufficient balance** (${sufficiency.info.balanceUSD}) \u2014 using free model instead of ${originalModel}.
|
|
6975
|
+
|
|
6976
|
+
`;
|
|
5721
6977
|
options.onLowBalance?.({
|
|
5722
6978
|
balanceUSD: sufficiency.info.balanceUSD,
|
|
5723
6979
|
walletAddress: sufficiency.info.walletAddress
|
|
@@ -5781,8 +7037,18 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5781
7037
|
if (routingDecision) {
|
|
5782
7038
|
const estimatedInputTokens = Math.ceil(body.length / 4);
|
|
5783
7039
|
const estimatedTotalTokens = estimatedInputTokens + maxTokens;
|
|
5784
|
-
const
|
|
5785
|
-
|
|
7040
|
+
const tierConfigs = (() => {
|
|
7041
|
+
if (routingDecision.reasoning?.includes("agentic") && routerOpts.config.agenticTiers) {
|
|
7042
|
+
return routerOpts.config.agenticTiers;
|
|
7043
|
+
}
|
|
7044
|
+
if (routingProfile === "eco" && routerOpts.config.ecoTiers) {
|
|
7045
|
+
return routerOpts.config.ecoTiers;
|
|
7046
|
+
}
|
|
7047
|
+
if (routingProfile === "premium" && routerOpts.config.premiumTiers) {
|
|
7048
|
+
return routerOpts.config.premiumTiers;
|
|
7049
|
+
}
|
|
7050
|
+
return routerOpts.config.tiers;
|
|
7051
|
+
})();
|
|
5786
7052
|
const fullChain = getFallbackChain(routingDecision.tier, tierConfigs);
|
|
5787
7053
|
const contextFiltered = getFallbackChainFiltered(
|
|
5788
7054
|
routingDecision.tier,
|
|
@@ -5796,14 +7062,27 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5796
7062
|
`[ClawRouter] Context filter (~${estimatedTotalTokens} tokens): excluded ${contextExcluded.join(", ")}`
|
|
5797
7063
|
);
|
|
5798
7064
|
}
|
|
5799
|
-
|
|
7065
|
+
const toolFiltered = filterByToolCalling(contextFiltered, hasTools, supportsToolCalling);
|
|
7066
|
+
const toolExcluded = contextFiltered.filter((m) => !toolFiltered.includes(m));
|
|
7067
|
+
if (toolExcluded.length > 0) {
|
|
7068
|
+
console.log(
|
|
7069
|
+
`[ClawRouter] Tool-calling filter: excluded ${toolExcluded.join(", ")} (no structured function call support)`
|
|
7070
|
+
);
|
|
7071
|
+
}
|
|
7072
|
+
const visionFiltered = filterByVision(toolFiltered, hasVision, supportsVision);
|
|
7073
|
+
const visionExcluded = toolFiltered.filter((m) => !visionFiltered.includes(m));
|
|
7074
|
+
if (visionExcluded.length > 0) {
|
|
7075
|
+
console.log(
|
|
7076
|
+
`[ClawRouter] Vision filter: excluded ${visionExcluded.join(", ")} (no vision support)`
|
|
7077
|
+
);
|
|
7078
|
+
}
|
|
7079
|
+
modelsToTry = visionFiltered.slice(0, MAX_FALLBACK_ATTEMPTS);
|
|
5800
7080
|
modelsToTry = prioritizeNonRateLimited(modelsToTry);
|
|
5801
7081
|
} else {
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
}
|
|
7082
|
+
modelsToTry = modelId ? [modelId] : [];
|
|
7083
|
+
}
|
|
7084
|
+
if (!modelsToTry.includes(FREE_MODEL)) {
|
|
7085
|
+
modelsToTry.push(FREE_MODEL);
|
|
5807
7086
|
}
|
|
5808
7087
|
let upstream;
|
|
5809
7088
|
let lastError;
|
|
@@ -5837,6 +7116,17 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5837
7116
|
if (result.errorStatus === 429) {
|
|
5838
7117
|
markRateLimited(tryModel);
|
|
5839
7118
|
}
|
|
7119
|
+
const isPaymentErr = /payment.*verification.*failed|insufficient.*funds/i.test(
|
|
7120
|
+
result.errorBody || ""
|
|
7121
|
+
);
|
|
7122
|
+
if (isPaymentErr && tryModel !== FREE_MODEL) {
|
|
7123
|
+
const freeIdx = modelsToTry.indexOf(FREE_MODEL);
|
|
7124
|
+
if (freeIdx > i + 1) {
|
|
7125
|
+
console.log(`[ClawRouter] Payment error \u2014 skipping to free model: ${FREE_MODEL}`);
|
|
7126
|
+
i = freeIdx - 1;
|
|
7127
|
+
continue;
|
|
7128
|
+
}
|
|
7129
|
+
}
|
|
5840
7130
|
console.log(
|
|
5841
7131
|
`[ClawRouter] Provider error from ${tryModel}, trying fallback: ${result.errorBody?.slice(0, 100)}`
|
|
5842
7132
|
);
|
|
@@ -5854,6 +7144,12 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5854
7144
|
clearInterval(heartbeatInterval);
|
|
5855
7145
|
heartbeatInterval = void 0;
|
|
5856
7146
|
}
|
|
7147
|
+
if (debugMode && headersSentEarly && routingDecision) {
|
|
7148
|
+
const debugComment = `: x-clawrouter-debug profile=${routingProfile ?? "auto"} tier=${routingDecision.tier} model=${actualModelUsed} agentic=${routingDecision.agenticScore?.toFixed(2) ?? "n/a"} confidence=${routingDecision.confidence.toFixed(2)} reasoning=${routingDecision.reasoning}
|
|
7149
|
+
|
|
7150
|
+
`;
|
|
7151
|
+
safeWrite(res, debugComment);
|
|
7152
|
+
}
|
|
5857
7153
|
if (routingDecision && actualModelUsed !== routingDecision.model) {
|
|
5858
7154
|
const estimatedInputTokens = Math.ceil(body.length / 4);
|
|
5859
7155
|
const newCosts = calculateModelCost(
|
|
@@ -5872,6 +7168,12 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5872
7168
|
savings: newCosts.savings
|
|
5873
7169
|
};
|
|
5874
7170
|
options.onRouted?.(routingDecision);
|
|
7171
|
+
if (effectiveSessionId) {
|
|
7172
|
+
sessionStore.setSession(effectiveSessionId, actualModelUsed, routingDecision.tier);
|
|
7173
|
+
console.log(
|
|
7174
|
+
`[ClawRouter] Session ${effectiveSessionId.slice(0, 8)}... updated pin to fallback: ${actualModelUsed}`
|
|
7175
|
+
);
|
|
7176
|
+
}
|
|
5875
7177
|
}
|
|
5876
7178
|
if (!upstream) {
|
|
5877
7179
|
const rawErrBody = lastError?.body || "All models in fallback chain failed";
|
|
@@ -5934,6 +7236,10 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5934
7236
|
const jsonStr = jsonBody.toString();
|
|
5935
7237
|
try {
|
|
5936
7238
|
const rsp = JSON.parse(jsonStr);
|
|
7239
|
+
if (rsp.usage && typeof rsp.usage === "object") {
|
|
7240
|
+
const u = rsp.usage;
|
|
7241
|
+
if (typeof u.prompt_tokens === "number") responseInputTokens = u.prompt_tokens;
|
|
7242
|
+
}
|
|
5937
7243
|
const baseChunk = {
|
|
5938
7244
|
id: rsp.id ?? `chatcmpl-${Date.now()}`,
|
|
5939
7245
|
object: "chat.completion.chunk",
|
|
@@ -5959,6 +7265,25 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5959
7265
|
`;
|
|
5960
7266
|
safeWrite(res, roleData);
|
|
5961
7267
|
responseChunks.push(Buffer.from(roleData));
|
|
7268
|
+
if (balanceFallbackNotice) {
|
|
7269
|
+
const noticeChunk = {
|
|
7270
|
+
...baseChunk,
|
|
7271
|
+
choices: [
|
|
7272
|
+
{
|
|
7273
|
+
index,
|
|
7274
|
+
delta: { content: balanceFallbackNotice },
|
|
7275
|
+
logprobs: null,
|
|
7276
|
+
finish_reason: null
|
|
7277
|
+
}
|
|
7278
|
+
]
|
|
7279
|
+
};
|
|
7280
|
+
const noticeData = `data: ${JSON.stringify(noticeChunk)}
|
|
7281
|
+
|
|
7282
|
+
`;
|
|
7283
|
+
safeWrite(res, noticeData);
|
|
7284
|
+
responseChunks.push(Buffer.from(noticeData));
|
|
7285
|
+
balanceFallbackNotice = void 0;
|
|
7286
|
+
}
|
|
5962
7287
|
if (content) {
|
|
5963
7288
|
const contentChunk = {
|
|
5964
7289
|
...baseChunk,
|
|
@@ -6033,23 +7358,46 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6033
7358
|
});
|
|
6034
7359
|
responseHeaders["x-context-used-kb"] = String(originalContextSizeKB);
|
|
6035
7360
|
responseHeaders["x-context-limit-kb"] = String(CONTEXT_LIMIT_KB);
|
|
6036
|
-
|
|
7361
|
+
if (debugMode && routingDecision) {
|
|
7362
|
+
responseHeaders["x-clawrouter-profile"] = routingProfile ?? "auto";
|
|
7363
|
+
responseHeaders["x-clawrouter-tier"] = routingDecision.tier;
|
|
7364
|
+
responseHeaders["x-clawrouter-model"] = actualModelUsed;
|
|
7365
|
+
responseHeaders["x-clawrouter-confidence"] = routingDecision.confidence.toFixed(2);
|
|
7366
|
+
responseHeaders["x-clawrouter-reasoning"] = routingDecision.reasoning;
|
|
7367
|
+
if (routingDecision.agenticScore !== void 0) {
|
|
7368
|
+
responseHeaders["x-clawrouter-agentic-score"] = routingDecision.agenticScore.toFixed(2);
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7371
|
+
const bodyParts = [];
|
|
6037
7372
|
if (upstream.body) {
|
|
6038
7373
|
const reader = upstream.body.getReader();
|
|
6039
7374
|
try {
|
|
6040
7375
|
while (true) {
|
|
6041
7376
|
const { done, value } = await reader.read();
|
|
6042
7377
|
if (done) break;
|
|
6043
|
-
|
|
6044
|
-
safeWrite(res, chunk);
|
|
6045
|
-
responseChunks.push(chunk);
|
|
7378
|
+
bodyParts.push(Buffer.from(value));
|
|
6046
7379
|
}
|
|
6047
7380
|
} finally {
|
|
6048
7381
|
reader.releaseLock();
|
|
6049
7382
|
}
|
|
6050
7383
|
}
|
|
7384
|
+
let responseBody = Buffer.concat(bodyParts);
|
|
7385
|
+
if (balanceFallbackNotice && responseBody.length > 0) {
|
|
7386
|
+
try {
|
|
7387
|
+
const parsed = JSON.parse(responseBody.toString());
|
|
7388
|
+
if (parsed.choices?.[0]?.message?.content !== void 0) {
|
|
7389
|
+
parsed.choices[0].message.content = balanceFallbackNotice + parsed.choices[0].message.content;
|
|
7390
|
+
responseBody = Buffer.from(JSON.stringify(parsed));
|
|
7391
|
+
}
|
|
7392
|
+
} catch {
|
|
7393
|
+
}
|
|
7394
|
+
balanceFallbackNotice = void 0;
|
|
7395
|
+
}
|
|
7396
|
+
responseHeaders["content-length"] = String(responseBody.length);
|
|
7397
|
+
res.writeHead(upstream.status, responseHeaders);
|
|
7398
|
+
safeWrite(res, responseBody);
|
|
7399
|
+
responseChunks.push(responseBody);
|
|
6051
7400
|
res.end();
|
|
6052
|
-
const responseBody = Buffer.concat(responseChunks);
|
|
6053
7401
|
deduplicator.complete(dedupKey, {
|
|
6054
7402
|
status: upstream.status,
|
|
6055
7403
|
headers: responseHeaders,
|
|
@@ -6072,6 +7420,10 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6072
7420
|
if (rspJson.choices?.[0]?.message?.content) {
|
|
6073
7421
|
accumulatedContent = rspJson.choices[0].message.content;
|
|
6074
7422
|
}
|
|
7423
|
+
if (rspJson.usage && typeof rspJson.usage === "object") {
|
|
7424
|
+
if (typeof rspJson.usage.prompt_tokens === "number")
|
|
7425
|
+
responseInputTokens = rspJson.usage.prompt_tokens;
|
|
7426
|
+
}
|
|
6075
7427
|
} catch {
|
|
6076
7428
|
}
|
|
6077
7429
|
}
|
|
@@ -6097,7 +7449,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6097
7449
|
deduplicator.removeInflight(dedupKey);
|
|
6098
7450
|
balanceMonitor.invalidate();
|
|
6099
7451
|
if (err instanceof Error && err.name === "AbortError") {
|
|
6100
|
-
throw new Error(`Request timed out after ${timeoutMs}ms
|
|
7452
|
+
throw new Error(`Request timed out after ${timeoutMs}ms`, { cause: err });
|
|
6101
7453
|
}
|
|
6102
7454
|
throw err;
|
|
6103
7455
|
}
|
|
@@ -6120,7 +7472,8 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6120
7472
|
cost: costWithBuffer,
|
|
6121
7473
|
baselineCost: baselineWithBuffer,
|
|
6122
7474
|
savings: accurateCosts.savings,
|
|
6123
|
-
latencyMs: Date.now() - startTime
|
|
7475
|
+
latencyMs: Date.now() - startTime,
|
|
7476
|
+
...responseInputTokens !== void 0 && { inputTokens: responseInputTokens }
|
|
6124
7477
|
};
|
|
6125
7478
|
logUsage(entry).catch(() => {
|
|
6126
7479
|
});
|
|
@@ -6139,6 +7492,103 @@ import {
|
|
|
6139
7492
|
import { homedir as homedir5 } from "os";
|
|
6140
7493
|
import { join as join6 } from "path";
|
|
6141
7494
|
import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
7495
|
+
|
|
7496
|
+
// src/partners/registry.ts
|
|
7497
|
+
var PARTNER_SERVICES = [
|
|
7498
|
+
{
|
|
7499
|
+
id: "x_users_lookup",
|
|
7500
|
+
name: "Twitter/X User Lookup",
|
|
7501
|
+
partner: "AttentionVC",
|
|
7502
|
+
description: "ALWAYS use this tool to look up real-time Twitter/X user profiles. Call this when the user asks about any Twitter/X account, username, handle, follower count, verification status, bio, or profile. Do NOT answer Twitter/X user questions from memory \u2014 always fetch live data with this tool. Returns: follower count, verification badge, bio, location, join date. Accepts up to 100 usernames per request (without @ prefix).",
|
|
7503
|
+
proxyPath: "/x/users/lookup",
|
|
7504
|
+
method: "POST",
|
|
7505
|
+
params: [
|
|
7506
|
+
{
|
|
7507
|
+
name: "usernames",
|
|
7508
|
+
type: "string[]",
|
|
7509
|
+
description: 'Array of Twitter/X usernames to look up (without @ prefix). Example: ["elonmusk", "naval"]',
|
|
7510
|
+
required: true
|
|
7511
|
+
}
|
|
7512
|
+
],
|
|
7513
|
+
pricing: {
|
|
7514
|
+
perUnit: "$0.001",
|
|
7515
|
+
unit: "user",
|
|
7516
|
+
minimum: "$0.01 (10 users)",
|
|
7517
|
+
maximum: "$0.10 (100 users)"
|
|
7518
|
+
},
|
|
7519
|
+
example: {
|
|
7520
|
+
input: { usernames: ["elonmusk", "naval", "balaboris"] },
|
|
7521
|
+
description: "Look up 3 Twitter/X user profiles"
|
|
7522
|
+
}
|
|
7523
|
+
}
|
|
7524
|
+
];
|
|
7525
|
+
function getPartnerService(id) {
|
|
7526
|
+
return PARTNER_SERVICES.find((s) => s.id === id);
|
|
7527
|
+
}
|
|
7528
|
+
|
|
7529
|
+
// src/partners/tools.ts
|
|
7530
|
+
function buildTool(service, proxyBaseUrl) {
|
|
7531
|
+
const properties = {};
|
|
7532
|
+
const required = [];
|
|
7533
|
+
for (const param of service.params) {
|
|
7534
|
+
const prop = {
|
|
7535
|
+
description: param.description
|
|
7536
|
+
};
|
|
7537
|
+
if (param.type === "string[]") {
|
|
7538
|
+
prop.type = "array";
|
|
7539
|
+
prop.items = { type: "string" };
|
|
7540
|
+
} else {
|
|
7541
|
+
prop.type = param.type;
|
|
7542
|
+
}
|
|
7543
|
+
properties[param.name] = prop;
|
|
7544
|
+
if (param.required) {
|
|
7545
|
+
required.push(param.name);
|
|
7546
|
+
}
|
|
7547
|
+
}
|
|
7548
|
+
return {
|
|
7549
|
+
name: `blockrun_${service.id}`,
|
|
7550
|
+
description: [
|
|
7551
|
+
service.description,
|
|
7552
|
+
"",
|
|
7553
|
+
`Partner: ${service.partner}`,
|
|
7554
|
+
`Pricing: ${service.pricing.perUnit} per ${service.pricing.unit} (min: ${service.pricing.minimum}, max: ${service.pricing.maximum})`
|
|
7555
|
+
].join("\n"),
|
|
7556
|
+
parameters: {
|
|
7557
|
+
type: "object",
|
|
7558
|
+
properties,
|
|
7559
|
+
required
|
|
7560
|
+
},
|
|
7561
|
+
execute: async (_toolCallId, params) => {
|
|
7562
|
+
const url = `${proxyBaseUrl}/v1${service.proxyPath}`;
|
|
7563
|
+
const response = await fetch(url, {
|
|
7564
|
+
method: service.method,
|
|
7565
|
+
headers: { "Content-Type": "application/json" },
|
|
7566
|
+
body: JSON.stringify(params)
|
|
7567
|
+
});
|
|
7568
|
+
if (!response.ok) {
|
|
7569
|
+
const errText = await response.text().catch(() => "");
|
|
7570
|
+
throw new Error(
|
|
7571
|
+
`Partner API error (${response.status}): ${errText || response.statusText}`
|
|
7572
|
+
);
|
|
7573
|
+
}
|
|
7574
|
+
const data = await response.json();
|
|
7575
|
+
return {
|
|
7576
|
+
content: [
|
|
7577
|
+
{
|
|
7578
|
+
type: "text",
|
|
7579
|
+
text: JSON.stringify(data, null, 2)
|
|
7580
|
+
}
|
|
7581
|
+
],
|
|
7582
|
+
details: data
|
|
7583
|
+
};
|
|
7584
|
+
}
|
|
7585
|
+
};
|
|
7586
|
+
}
|
|
7587
|
+
function buildPartnerTools(proxyBaseUrl) {
|
|
7588
|
+
return PARTNER_SERVICES.map((service) => buildTool(service, proxyBaseUrl));
|
|
7589
|
+
}
|
|
7590
|
+
|
|
7591
|
+
// src/index.ts
|
|
6142
7592
|
init_solana_balance();
|
|
6143
7593
|
|
|
6144
7594
|
// src/spend-control.ts
|
|
@@ -6391,6 +7841,7 @@ function formatDuration(seconds) {
|
|
|
6391
7841
|
|
|
6392
7842
|
// src/index.ts
|
|
6393
7843
|
init_wallet();
|
|
7844
|
+
init_solana_sweep();
|
|
6394
7845
|
|
|
6395
7846
|
// src/retry.ts
|
|
6396
7847
|
var DEFAULT_RETRY_CONFIG = {
|
|
@@ -6449,7 +7900,6 @@ function isRetryable(errorOrResponse, config) {
|
|
|
6449
7900
|
}
|
|
6450
7901
|
|
|
6451
7902
|
// src/index.ts
|
|
6452
|
-
init_partners();
|
|
6453
7903
|
async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
6454
7904
|
const start = Date.now();
|
|
6455
7905
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -6586,56 +8036,48 @@ function injectModelsConfig(logger) {
|
|
|
6586
8036
|
logger.info("Set default model to blockrun/auto (first install)");
|
|
6587
8037
|
needsWrite = true;
|
|
6588
8038
|
}
|
|
6589
|
-
const
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
8039
|
+
const TOP_MODELS = [
|
|
8040
|
+
"auto",
|
|
8041
|
+
"free",
|
|
8042
|
+
"eco",
|
|
8043
|
+
"premium",
|
|
8044
|
+
"anthropic/claude-sonnet-4.6",
|
|
8045
|
+
"anthropic/claude-opus-4.6",
|
|
8046
|
+
"anthropic/claude-haiku-4.5",
|
|
8047
|
+
"openai/gpt-5.2",
|
|
8048
|
+
"openai/gpt-4o",
|
|
8049
|
+
"openai/o3",
|
|
8050
|
+
"google/gemini-3.1-pro",
|
|
8051
|
+
"google/gemini-3-flash-preview",
|
|
8052
|
+
"deepseek/deepseek-chat",
|
|
8053
|
+
"moonshot/kimi-k2.5",
|
|
8054
|
+
"xai/grok-3",
|
|
8055
|
+
"minimax/minimax-m2.5"
|
|
6606
8056
|
];
|
|
6607
|
-
|
|
6608
|
-
"blockrun/nvidia",
|
|
6609
|
-
"blockrun/gpt",
|
|
6610
|
-
"blockrun/o3",
|
|
6611
|
-
"blockrun/grok",
|
|
6612
|
-
"blockrun/mini",
|
|
6613
|
-
"blockrun/flash"
|
|
6614
|
-
// removed from picker - use gemini instead
|
|
6615
|
-
];
|
|
6616
|
-
if (!defaults.models) {
|
|
8057
|
+
if (!defaults.models || typeof defaults.models !== "object" || Array.isArray(defaults.models)) {
|
|
6617
8058
|
defaults.models = {};
|
|
6618
8059
|
needsWrite = true;
|
|
6619
8060
|
}
|
|
6620
8061
|
const allowlist = defaults.models;
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
8062
|
+
const topSet = new Set(TOP_MODELS.map((id) => `blockrun/${id}`));
|
|
8063
|
+
for (const key of Object.keys(allowlist)) {
|
|
8064
|
+
if (key.startsWith("blockrun/") && !topSet.has(key)) {
|
|
8065
|
+
delete allowlist[key];
|
|
6625
8066
|
needsWrite = true;
|
|
6626
8067
|
}
|
|
6627
8068
|
}
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
const
|
|
6631
|
-
if (!
|
|
6632
|
-
allowlist[
|
|
6633
|
-
|
|
6634
|
-
} else if (existing.alias !== m.alias) {
|
|
6635
|
-
existing.alias = m.alias;
|
|
6636
|
-
needsWrite = true;
|
|
8069
|
+
let addedCount = 0;
|
|
8070
|
+
for (const id of TOP_MODELS) {
|
|
8071
|
+
const key = `blockrun/${id}`;
|
|
8072
|
+
if (!allowlist[key]) {
|
|
8073
|
+
allowlist[key] = {};
|
|
8074
|
+
addedCount++;
|
|
6637
8075
|
}
|
|
6638
8076
|
}
|
|
8077
|
+
if (addedCount > 0) {
|
|
8078
|
+
needsWrite = true;
|
|
8079
|
+
logger.info(`Added ${addedCount} models to allowlist (${TOP_MODELS.length} total)`);
|
|
8080
|
+
}
|
|
6639
8081
|
if (needsWrite) {
|
|
6640
8082
|
try {
|
|
6641
8083
|
const tmpPath = `${configPath}.tmp.${process.pid}`;
|
|
@@ -6754,18 +8196,19 @@ async function startProxyInBackground(api) {
|
|
|
6754
8196
|
activeProxyHandle = proxy;
|
|
6755
8197
|
api.logger.info(`ClawRouter ready \u2014 smart routing enabled`);
|
|
6756
8198
|
api.logger.info(`Pricing: Simple ~$0.001 | Code ~$0.01 | Complex ~$0.05 | Free: $0`);
|
|
6757
|
-
const
|
|
6758
|
-
|
|
8199
|
+
const currentChain = await resolvePaymentChain();
|
|
8200
|
+
const displayAddress = currentChain === "solana" && proxy.solanaAddress ? proxy.solanaAddress : wallet.address;
|
|
8201
|
+
proxy.balanceMonitor.checkBalance().then((balance) => {
|
|
6759
8202
|
if (balance.isEmpty) {
|
|
6760
|
-
api.logger.info(`Wallet: ${
|
|
8203
|
+
api.logger.info(`Wallet: ${displayAddress} | Balance: $0.00`);
|
|
6761
8204
|
api.logger.info(`Using FREE model. Fund wallet for premium models.`);
|
|
6762
8205
|
} else if (balance.isLow) {
|
|
6763
|
-
api.logger.info(`Wallet: ${
|
|
8206
|
+
api.logger.info(`Wallet: ${displayAddress} | Balance: ${balance.balanceUSD} (low)`);
|
|
6764
8207
|
} else {
|
|
6765
|
-
api.logger.info(`Wallet: ${
|
|
8208
|
+
api.logger.info(`Wallet: ${displayAddress} | Balance: ${balance.balanceUSD}`);
|
|
6766
8209
|
}
|
|
6767
8210
|
}).catch(() => {
|
|
6768
|
-
api.logger.info(`Wallet: ${
|
|
8211
|
+
api.logger.info(`Wallet: ${displayAddress} | Balance: (checking...)`);
|
|
6769
8212
|
});
|
|
6770
8213
|
}
|
|
6771
8214
|
async function createStatsCommand() {
|
|
@@ -6839,8 +8282,8 @@ Run \`openclaw plugins install @blockrun/clawrouter\` to generate a wallet.`,
|
|
|
6839
8282
|
hasMnemonic = true;
|
|
6840
8283
|
const { deriveSolanaKeyBytes: deriveSolanaKeyBytes2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
|
|
6841
8284
|
const solKeyBytes = deriveSolanaKeyBytes2(mnemonic);
|
|
6842
|
-
const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit");
|
|
6843
|
-
const signer = await
|
|
8285
|
+
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await import("@solana/kit");
|
|
8286
|
+
const signer = await createKeyPairSignerFromPrivateKeyBytes2(solKeyBytes);
|
|
6844
8287
|
lines.push(
|
|
6845
8288
|
"",
|
|
6846
8289
|
"**Solana:**",
|
|
@@ -6881,8 +8324,8 @@ Run \`openclaw plugins install @blockrun/clawrouter\` to generate a wallet.`,
|
|
|
6881
8324
|
await savePaymentChain("solana");
|
|
6882
8325
|
const { deriveSolanaKeyBytes: deriveSolanaKeyBytes2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
|
|
6883
8326
|
const solKeyBytes = deriveSolanaKeyBytes2(existingMnemonic);
|
|
6884
|
-
const { createKeyPairSignerFromPrivateKeyBytes:
|
|
6885
|
-
const signer2 = await
|
|
8327
|
+
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes3 } = await import("@solana/kit");
|
|
8328
|
+
const signer2 = await createKeyPairSignerFromPrivateKeyBytes3(solKeyBytes);
|
|
6886
8329
|
solanaAddr = signer2.address;
|
|
6887
8330
|
return {
|
|
6888
8331
|
text: [
|
|
@@ -6896,8 +8339,8 @@ Run \`openclaw plugins install @blockrun/clawrouter\` to generate a wallet.`,
|
|
|
6896
8339
|
}
|
|
6897
8340
|
const { solanaPrivateKeyBytes } = await setupSolana();
|
|
6898
8341
|
await savePaymentChain("solana");
|
|
6899
|
-
const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit");
|
|
6900
|
-
const signer = await
|
|
8342
|
+
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await import("@solana/kit");
|
|
8343
|
+
const signer = await createKeyPairSignerFromPrivateKeyBytes2(solanaPrivateKeyBytes);
|
|
6901
8344
|
return {
|
|
6902
8345
|
text: [
|
|
6903
8346
|
"**Solana Wallet Set Up**",
|
|
@@ -6918,6 +8361,88 @@ Run \`openclaw plugins install @blockrun/clawrouter\` to generate a wallet.`,
|
|
|
6918
8361
|
};
|
|
6919
8362
|
}
|
|
6920
8363
|
}
|
|
8364
|
+
if (subcommand === "migrate-solana") {
|
|
8365
|
+
try {
|
|
8366
|
+
if (!existsSync2(MNEMONIC_FILE)) {
|
|
8367
|
+
return {
|
|
8368
|
+
text: "No mnemonic file found. Solana wallet not set up \u2014 nothing to migrate.",
|
|
8369
|
+
isError: true
|
|
8370
|
+
};
|
|
8371
|
+
}
|
|
8372
|
+
const mnemonic = readTextFileSync(MNEMONIC_FILE).trim();
|
|
8373
|
+
if (!mnemonic) {
|
|
8374
|
+
return { text: "Mnemonic file is empty.", isError: true };
|
|
8375
|
+
}
|
|
8376
|
+
const { deriveSolanaKeyBytes: deriveSolanaKeyBytes2, deriveSolanaKeyBytesLegacy: deriveSolanaKeyBytesLegacy2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
|
|
8377
|
+
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await import("@solana/kit");
|
|
8378
|
+
const legacyKeyBytes = deriveSolanaKeyBytesLegacy2(mnemonic);
|
|
8379
|
+
const newKeyBytes = deriveSolanaKeyBytes2(mnemonic);
|
|
8380
|
+
const [oldSigner, newSigner] = await Promise.all([
|
|
8381
|
+
createKeyPairSignerFromPrivateKeyBytes2(legacyKeyBytes),
|
|
8382
|
+
createKeyPairSignerFromPrivateKeyBytes2(newKeyBytes)
|
|
8383
|
+
]);
|
|
8384
|
+
if (oldSigner.address === newSigner.address) {
|
|
8385
|
+
return { text: "Legacy and new Solana addresses are the same. No migration needed." };
|
|
8386
|
+
}
|
|
8387
|
+
let oldUsdcText = "unknown";
|
|
8388
|
+
try {
|
|
8389
|
+
const { SolanaBalanceMonitor: SolanaBalanceMonitor2 } = await Promise.resolve().then(() => (init_solana_balance(), solana_balance_exports));
|
|
8390
|
+
const monitor = new SolanaBalanceMonitor2(oldSigner.address);
|
|
8391
|
+
const balance = await monitor.checkBalance();
|
|
8392
|
+
oldUsdcText = balance.balanceUSD;
|
|
8393
|
+
if (balance.isEmpty) {
|
|
8394
|
+
return {
|
|
8395
|
+
text: [
|
|
8396
|
+
"**Solana Migration Status**",
|
|
8397
|
+
"",
|
|
8398
|
+
`Old wallet (secp256k1): \`${oldSigner.address}\``,
|
|
8399
|
+
` USDC: $0.00`,
|
|
8400
|
+
"",
|
|
8401
|
+
`New wallet (SLIP-10): \`${newSigner.address}\``,
|
|
8402
|
+
"",
|
|
8403
|
+
"No USDC in old wallet. Nothing to sweep.",
|
|
8404
|
+
"Your new SLIP-10 address is Phantom/Solflare compatible."
|
|
8405
|
+
].join("\n")
|
|
8406
|
+
};
|
|
8407
|
+
}
|
|
8408
|
+
} catch {
|
|
8409
|
+
}
|
|
8410
|
+
const { sweepSolanaWallet: sweepSolanaWallet2 } = await Promise.resolve().then(() => (init_solana_sweep(), solana_sweep_exports));
|
|
8411
|
+
const result = await sweepSolanaWallet2(legacyKeyBytes, newSigner.address);
|
|
8412
|
+
if ("error" in result) {
|
|
8413
|
+
return {
|
|
8414
|
+
text: [
|
|
8415
|
+
"**Solana Migration Failed**",
|
|
8416
|
+
"",
|
|
8417
|
+
`Old wallet: \`${result.oldAddress}\` (USDC: ${oldUsdcText})`,
|
|
8418
|
+
`New wallet: \`${result.newAddress || newSigner.address}\``,
|
|
8419
|
+
"",
|
|
8420
|
+
`Error: ${result.error}`
|
|
8421
|
+
].join("\n"),
|
|
8422
|
+
isError: true
|
|
8423
|
+
};
|
|
8424
|
+
}
|
|
8425
|
+
return {
|
|
8426
|
+
text: [
|
|
8427
|
+
"**Solana Migration Complete**",
|
|
8428
|
+
"",
|
|
8429
|
+
`Swept **${result.transferred}** USDC from old to new wallet.`,
|
|
8430
|
+
"",
|
|
8431
|
+
`Old wallet: \`${result.oldAddress}\``,
|
|
8432
|
+
`New wallet: \`${result.newAddress}\``,
|
|
8433
|
+
`TX: https://solscan.io/tx/${result.txSignature}`,
|
|
8434
|
+
"",
|
|
8435
|
+
"Your new SLIP-10 address is Phantom/Solflare compatible.",
|
|
8436
|
+
"You can recover it from your 24-word mnemonic in any standard wallet."
|
|
8437
|
+
].join("\n")
|
|
8438
|
+
};
|
|
8439
|
+
} catch (err) {
|
|
8440
|
+
return {
|
|
8441
|
+
text: `Migration failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
8442
|
+
isError: true
|
|
8443
|
+
};
|
|
8444
|
+
}
|
|
8445
|
+
}
|
|
6921
8446
|
if (subcommand === "base") {
|
|
6922
8447
|
try {
|
|
6923
8448
|
await savePaymentChain("base");
|
|
@@ -6931,7 +8456,7 @@ Run \`openclaw plugins install @blockrun/clawrouter\` to generate a wallet.`,
|
|
|
6931
8456
|
};
|
|
6932
8457
|
}
|
|
6933
8458
|
}
|
|
6934
|
-
let evmBalanceText
|
|
8459
|
+
let evmBalanceText;
|
|
6935
8460
|
try {
|
|
6936
8461
|
const monitor = new BalanceMonitor(address);
|
|
6937
8462
|
const balance = await monitor.checkBalance();
|
|
@@ -6946,8 +8471,8 @@ Run \`openclaw plugins install @blockrun/clawrouter\` to generate a wallet.`,
|
|
|
6946
8471
|
const mnemonic = readTextFileSync(MNEMONIC_FILE).trim();
|
|
6947
8472
|
if (mnemonic) {
|
|
6948
8473
|
const solKeyBytes = deriveSolanaKeyBytes2(mnemonic);
|
|
6949
|
-
const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit");
|
|
6950
|
-
const signer = await
|
|
8474
|
+
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await import("@solana/kit");
|
|
8475
|
+
const signer = await createKeyPairSignerFromPrivateKeyBytes2(solKeyBytes);
|
|
6951
8476
|
const solAddr = signer.address;
|
|
6952
8477
|
let solBalanceText = "Balance: (checking...)";
|
|
6953
8478
|
try {
|
|
@@ -6989,7 +8514,8 @@ Run \`openclaw plugins install @blockrun/clawrouter\` to generate a wallet.`,
|
|
|
6989
8514
|
"\u2022 `/wallet export` - Export private key for backup",
|
|
6990
8515
|
!solanaSection ? "\u2022 `/wallet solana` - Enable Solana payments" : "",
|
|
6991
8516
|
solanaSection ? "\u2022 `/wallet base` - Switch to Base (EVM)" : "",
|
|
6992
|
-
solanaSection ? "\u2022 `/wallet solana` - Switch to Solana" : ""
|
|
8517
|
+
solanaSection ? "\u2022 `/wallet solana` - Switch to Solana" : "",
|
|
8518
|
+
solanaSection ? "\u2022 `/wallet migrate-solana` - Sweep funds from old Solana wallet" : ""
|
|
6993
8519
|
].filter(Boolean).join("\n")
|
|
6994
8520
|
};
|
|
6995
8521
|
}
|
|
@@ -7000,7 +8526,7 @@ var plugin = {
|
|
|
7000
8526
|
name: "ClawRouter",
|
|
7001
8527
|
description: "Smart LLM router \u2014 30+ models, x402 micropayments, 78% cost savings",
|
|
7002
8528
|
version: VERSION,
|
|
7003
|
-
|
|
8529
|
+
register(api) {
|
|
7004
8530
|
const isDisabled = process["env"].CLAWROUTER_DISABLED === "true" || process["env"].CLAWROUTER_DISABLED === "1";
|
|
7005
8531
|
if (isDisabled) {
|
|
7006
8532
|
api.logger.info("ClawRouter disabled (CLAWROUTER_DISABLED=true). Using default routing.");
|
|
@@ -7029,14 +8555,15 @@ var plugin = {
|
|
|
7029
8555
|
};
|
|
7030
8556
|
api.logger.info("BlockRun provider registered (30+ models via x402)");
|
|
7031
8557
|
try {
|
|
7032
|
-
const { buildPartnerTools: buildPartnerTools2, PARTNER_SERVICES: PARTNER_SERVICES2 } = await Promise.resolve().then(() => (init_partners(), partners_exports));
|
|
7033
8558
|
const proxyBaseUrl = `http://127.0.0.1:${runtimePort}`;
|
|
7034
|
-
const partnerTools =
|
|
8559
|
+
const partnerTools = buildPartnerTools(proxyBaseUrl);
|
|
7035
8560
|
for (const tool of partnerTools) {
|
|
7036
8561
|
api.registerTool(tool);
|
|
7037
8562
|
}
|
|
7038
8563
|
if (partnerTools.length > 0) {
|
|
7039
|
-
api.logger.info(
|
|
8564
|
+
api.logger.info(
|
|
8565
|
+
`Registered ${partnerTools.length} partner tool(s): ${partnerTools.map((t) => t.name).join(", ")}`
|
|
8566
|
+
);
|
|
7040
8567
|
}
|
|
7041
8568
|
api.registerCommand({
|
|
7042
8569
|
name: "partners",
|
|
@@ -7044,19 +8571,20 @@ var plugin = {
|
|
|
7044
8571
|
acceptsArgs: false,
|
|
7045
8572
|
requireAuth: false,
|
|
7046
8573
|
handler: async () => {
|
|
7047
|
-
if (
|
|
8574
|
+
if (PARTNER_SERVICES.length === 0) {
|
|
7048
8575
|
return { text: "No partner APIs available." };
|
|
7049
8576
|
}
|
|
7050
|
-
const lines = [
|
|
7051
|
-
|
|
7052
|
-
""
|
|
7053
|
-
];
|
|
7054
|
-
for (const svc of PARTNER_SERVICES2) {
|
|
8577
|
+
const lines = ["**Partner APIs** (paid via your ClawRouter wallet)", ""];
|
|
8578
|
+
for (const svc of PARTNER_SERVICES) {
|
|
7055
8579
|
lines.push(`**${svc.name}** (${svc.partner})`);
|
|
7056
8580
|
lines.push(` ${svc.description}`);
|
|
7057
8581
|
lines.push(` Tool: \`${`blockrun_${svc.id}`}\``);
|
|
7058
|
-
lines.push(
|
|
7059
|
-
|
|
8582
|
+
lines.push(
|
|
8583
|
+
` Pricing: ${svc.pricing.perUnit} per ${svc.pricing.unit} (min ${svc.pricing.minimum}, max ${svc.pricing.maximum})`
|
|
8584
|
+
);
|
|
8585
|
+
lines.push(
|
|
8586
|
+
` **How to use:** Ask "Look up Twitter user @elonmusk" or "Get info on these X accounts: @naval, @balajis"`
|
|
8587
|
+
);
|
|
7060
8588
|
lines.push("");
|
|
7061
8589
|
}
|
|
7062
8590
|
return { text: lines.join("\n") };
|
|
@@ -7163,6 +8691,7 @@ export {
|
|
|
7163
8691
|
deriveAllKeys,
|
|
7164
8692
|
deriveEvmKey,
|
|
7165
8693
|
deriveSolanaKeyBytes,
|
|
8694
|
+
deriveSolanaKeyBytesLegacy,
|
|
7166
8695
|
fetchWithRetry,
|
|
7167
8696
|
formatDuration,
|
|
7168
8697
|
formatStatsAscii,
|
|
@@ -7175,6 +8704,7 @@ export {
|
|
|
7175
8704
|
getProxyPort,
|
|
7176
8705
|
getSessionId,
|
|
7177
8706
|
getStats,
|
|
8707
|
+
hashRequestContent,
|
|
7178
8708
|
isAgenticModel,
|
|
7179
8709
|
isBalanceError,
|
|
7180
8710
|
isEmptyWalletError,
|
|
@@ -7189,6 +8719,12 @@ export {
|
|
|
7189
8719
|
route,
|
|
7190
8720
|
savePaymentChain,
|
|
7191
8721
|
setupSolana,
|
|
7192
|
-
startProxy
|
|
8722
|
+
startProxy,
|
|
8723
|
+
sweepSolanaWallet
|
|
7193
8724
|
};
|
|
8725
|
+
/*! Bundled license information:
|
|
8726
|
+
|
|
8727
|
+
@noble/hashes/esm/utils.js:
|
|
8728
|
+
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
8729
|
+
*/
|
|
7194
8730
|
//# sourceMappingURL=index.js.map
|