@blockrun/clawrouter 0.12.271 → 0.12.272
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 +31 -3
- package/dist/cli.js +555 -116
- package/dist/index.js +247 -63
- package/package.json +1 -1
- package/skills/surf/SKILL.md +14 -3
package/dist/cli.js
CHANGED
|
@@ -55996,7 +55996,7 @@ var require_websocket = __commonJS({
|
|
|
55996
55996
|
var net3 = __require("net");
|
|
55997
55997
|
var tls2 = __require("tls");
|
|
55998
55998
|
var { randomBytes: randomBytes9, createHash: createHash4 } = __require("crypto");
|
|
55999
|
-
var { Duplex, Readable:
|
|
55999
|
+
var { Duplex, Readable: Readable3 } = __require("stream");
|
|
56000
56000
|
var { URL: URL3 } = __require("url");
|
|
56001
56001
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
56002
56002
|
var Receiver2 = require_receiver();
|
|
@@ -60119,16 +60119,20 @@ function maskApiKey(key2) {
|
|
|
60119
60119
|
async function readOptional2(path5) {
|
|
60120
60120
|
try {
|
|
60121
60121
|
return (await readTextFile(path5)).trim() || void 0;
|
|
60122
|
-
} catch {
|
|
60123
|
-
return void 0;
|
|
60122
|
+
} catch (error) {
|
|
60123
|
+
if (error.code === "ENOENT") return void 0;
|
|
60124
|
+
throw new Error(
|
|
60125
|
+
`Cannot read BlockRun API key file ${path5}; refusing to fall back to another account or wallet.`,
|
|
60126
|
+
{ cause: error }
|
|
60127
|
+
);
|
|
60124
60128
|
}
|
|
60125
60129
|
}
|
|
60126
60130
|
async function resolveApiKey() {
|
|
60127
60131
|
const envKey = process["env"].BLOCKRUN_API_KEY?.trim();
|
|
60128
60132
|
if (envKey) {
|
|
60129
60133
|
if (isValidApiKey(envKey)) return { key: envKey, source: "env" };
|
|
60130
|
-
|
|
60131
|
-
`
|
|
60134
|
+
throw new Error(
|
|
60135
|
+
`BLOCKRUN_API_KEY is malformed (expected brk_\u2026). Mint one at ${PORTAL_KEYS_URL}, or unset it to pay from the wallet. Refusing to fall back silently.`
|
|
60132
60136
|
);
|
|
60133
60137
|
}
|
|
60134
60138
|
for (const [path5, source] of [
|
|
@@ -60138,8 +60142,8 @@ async function resolveApiKey() {
|
|
|
60138
60142
|
const stored = await readOptional2(path5);
|
|
60139
60143
|
if (stored === void 0) continue;
|
|
60140
60144
|
if (isValidApiKey(stored)) return { key: stored, source };
|
|
60141
|
-
|
|
60142
|
-
|
|
60145
|
+
throw new Error(
|
|
60146
|
+
`${path5} does not contain a BlockRun key (expected brk_\u2026). Run "clawrouter login" with a valid key, or "clawrouter logout" to return to wallet billing. Refusing to fall back silently: the next source could bill a different account or spend from a wallet.`
|
|
60143
60147
|
);
|
|
60144
60148
|
}
|
|
60145
60149
|
return void 0;
|
|
@@ -60198,16 +60202,95 @@ function formatCreditBalance(b) {
|
|
|
60198
60202
|
const mode = b.billingMode === "ungated" ? "no prepaid limit" : b.billingMode ?? "unknown";
|
|
60199
60203
|
return `${spent ? `spent ${spent}` : "unknown"} \u2014 ${mode}`;
|
|
60200
60204
|
}
|
|
60201
|
-
function
|
|
60205
|
+
async function fetchUsagePage(apiKey, opts = {}) {
|
|
60206
|
+
const params = new URLSearchParams();
|
|
60207
|
+
if (opts.from) params.set("from", opts.from);
|
|
60208
|
+
if (opts.to) params.set("to", opts.to);
|
|
60209
|
+
if (opts.limit) params.set("limit", String(opts.limit));
|
|
60210
|
+
if (opts.cursor) params.set("cursor", opts.cursor);
|
|
60211
|
+
const query = params.toString();
|
|
60212
|
+
try {
|
|
60213
|
+
const res = await fetch(`${BLOCKRUN_API_KEY_API}/v1/usage${query ? `?${query}` : ""}`, {
|
|
60214
|
+
headers: { authorization: `Bearer ${apiKey}` },
|
|
60215
|
+
signal: AbortSignal.timeout(3e4)
|
|
60216
|
+
});
|
|
60217
|
+
if (!res.ok) return void 0;
|
|
60218
|
+
const body = await res.json();
|
|
60219
|
+
return {
|
|
60220
|
+
rows: (body.data ?? []).map((r2) => ({
|
|
60221
|
+
requestId: String(r2.request_id ?? ""),
|
|
60222
|
+
timestamp: String(r2.timestamp ?? ""),
|
|
60223
|
+
endpoint: String(r2.endpoint ?? ""),
|
|
60224
|
+
model: typeof r2.model === "string" ? r2.model : null,
|
|
60225
|
+
kind: String(r2.kind ?? ""),
|
|
60226
|
+
inputTokens: typeof r2.input_tokens === "number" ? r2.input_tokens : 0,
|
|
60227
|
+
outputTokens: typeof r2.output_tokens === "number" ? r2.output_tokens : 0,
|
|
60228
|
+
costUsd: typeof r2.cost_usd === "number" ? r2.cost_usd : null,
|
|
60229
|
+
costState: String(r2.cost_state ?? ""),
|
|
60230
|
+
status: typeof r2.status === "number" ? r2.status : 0
|
|
60231
|
+
})),
|
|
60232
|
+
nextCursor: body.next_cursor ?? null,
|
|
60233
|
+
unavailableDays: body.unavailable_days ?? []
|
|
60234
|
+
};
|
|
60235
|
+
} catch {
|
|
60236
|
+
return void 0;
|
|
60237
|
+
}
|
|
60238
|
+
}
|
|
60239
|
+
function normalizeApiKeyBase(raw) {
|
|
60240
|
+
const base4 = raw.replace(/\/+$/, "").replace(/\/v1$/, "");
|
|
60241
|
+
const url2 = new URL(base4);
|
|
60242
|
+
if (url2.username || url2.password || url2.search || url2.hash || url2.protocol !== "https:" && !(url2.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname))) {
|
|
60243
|
+
throw new Error(
|
|
60244
|
+
"BlockRun account API URL requires HTTPS (except localhost) and no credentials, query or fragment."
|
|
60245
|
+
);
|
|
60246
|
+
}
|
|
60247
|
+
return base4;
|
|
60248
|
+
}
|
|
60249
|
+
function createApiKeyFetch(apiKey, baseFetch = fetch, apiBase = BLOCKRUN_API_KEY_API) {
|
|
60250
|
+
const base4 = new URL(normalizeApiKeyBase(apiBase));
|
|
60202
60251
|
return async (input, init2) => {
|
|
60203
|
-
const
|
|
60252
|
+
const request2 = input instanceof Request ? input : void 0;
|
|
60253
|
+
const url2 = new URL(request2?.url ?? String(input), `${base4}/`);
|
|
60254
|
+
if (url2.origin !== base4.origin || url2.username || url2.password) {
|
|
60255
|
+
throw new Error("Refusing to forward a BlockRun account key to another origin.");
|
|
60256
|
+
}
|
|
60257
|
+
if (base4.pathname === "/" && url2.pathname.startsWith("/api/v1/")) {
|
|
60258
|
+
url2.pathname = url2.pathname.slice(4);
|
|
60259
|
+
}
|
|
60260
|
+
const headers = new Headers(init2?.headers ?? request2?.headers);
|
|
60261
|
+
for (const name of [...headers.keys()]) {
|
|
60262
|
+
if (/payment/i.test(name) || name.toLowerCase() === "x-api-key") headers.delete(name);
|
|
60263
|
+
}
|
|
60204
60264
|
headers.set("authorization", `Bearer ${apiKey}`);
|
|
60205
|
-
|
|
60206
|
-
|
|
60207
|
-
|
|
60265
|
+
const response = await baseFetch(request2 ? new Request(url2, request2) : url2.href, {
|
|
60266
|
+
...init2,
|
|
60267
|
+
headers,
|
|
60268
|
+
redirect: "error"
|
|
60269
|
+
});
|
|
60208
60270
|
return response.ok ? response : explainApiKeyFailure(response);
|
|
60209
60271
|
};
|
|
60210
60272
|
}
|
|
60273
|
+
async function pollApiKeyJob(response, payFetch, apiBase, signal, intervalMs = 2e3) {
|
|
60274
|
+
if (response.status !== 202) return response;
|
|
60275
|
+
const initial = await response.clone().json();
|
|
60276
|
+
if (!initial.poll_url) throw new Error("Async account response missing poll_url");
|
|
60277
|
+
const pollUrl = new URL(initial.poll_url, `${normalizeApiKeyBase(apiBase)}/`).href;
|
|
60278
|
+
const abort = AbortSignal.any([signal, AbortSignal.timeout(15 * 6e4)]);
|
|
60279
|
+
while (!abort.aborted) {
|
|
60280
|
+
const polled = await payFetch(pollUrl, { signal: abort });
|
|
60281
|
+
if ([502, 503, 504, 522, 524].includes(polled.status)) {
|
|
60282
|
+
await polled.body?.cancel();
|
|
60283
|
+
} else {
|
|
60284
|
+
if (!polled.ok) return polled;
|
|
60285
|
+
const data = await polled.clone().json();
|
|
60286
|
+
if (data.status === "completed") return polled;
|
|
60287
|
+
if (data.status === "failed") return polled;
|
|
60288
|
+
await polled.body?.cancel();
|
|
60289
|
+
}
|
|
60290
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
60291
|
+
}
|
|
60292
|
+
throw new Error("Async account job did not complete before the client aborted or timed out.");
|
|
60293
|
+
}
|
|
60211
60294
|
async function explainApiKeyFailure(response) {
|
|
60212
60295
|
const hint = HINTS[response.status];
|
|
60213
60296
|
if (!hint) return response;
|
|
@@ -60241,7 +60324,7 @@ var init_api_key = __esm({
|
|
|
60241
60324
|
PORTAL_URL = "https://user.blockrun.ai";
|
|
60242
60325
|
PORTAL_KEYS_URL = `${PORTAL_URL}/dashboard/keys`;
|
|
60243
60326
|
PORTAL_CREDITS_URL = `${PORTAL_URL}/dashboard/credits`;
|
|
60244
|
-
BLOCKRUN_API_KEY_API = process["env"].BLOCKRUN_API_BASE_URL?.replace(/\/+$/, "") || "https://api.blockrun.ai";
|
|
60327
|
+
BLOCKRUN_API_KEY_API = process["env"].BLOCKRUN_API_BASE_URL?.replace(/\/+$/, "").replace(/\/v1$/, "") || "https://api.blockrun.ai";
|
|
60245
60328
|
HINTS = {
|
|
60246
60329
|
401: `Check BLOCKRUN_API_KEY, or mint a new key at ${PORTAL_KEYS_URL}.`,
|
|
60247
60330
|
402: `Top up your BlockRun credit at ${PORTAL_CREDITS_URL}.`,
|
|
@@ -69387,7 +69470,7 @@ var require_client_h2 = __commonJS({
|
|
|
69387
69470
|
"node_modules/undici/lib/dispatcher/client-h2.js"(exports, module) {
|
|
69388
69471
|
"use strict";
|
|
69389
69472
|
var assert10 = __require("assert");
|
|
69390
|
-
var { pipeline } = __require("stream");
|
|
69473
|
+
var { pipeline: pipeline2 } = __require("stream");
|
|
69391
69474
|
var util5 = require_util();
|
|
69392
69475
|
var {
|
|
69393
69476
|
RequestContentLengthMismatchError,
|
|
@@ -70534,7 +70617,7 @@ var require_client_h2 = __commonJS({
|
|
|
70534
70617
|
}
|
|
70535
70618
|
function writeStream(abort, socket, expectsPayload, h2stream, body, client, request2, contentLength) {
|
|
70536
70619
|
assert10(contentLength !== 0 || client[kRunning] === 0, "stream body cannot be pipelined");
|
|
70537
|
-
const pipe2 =
|
|
70620
|
+
const pipe2 = pipeline2(
|
|
70538
70621
|
body,
|
|
70539
70622
|
h2stream,
|
|
70540
70623
|
(err) => {
|
|
@@ -73689,7 +73772,7 @@ var require_readable = __commonJS({
|
|
|
73689
73772
|
"use strict";
|
|
73690
73773
|
var assert10 = __require("assert");
|
|
73691
73774
|
var { addAbortListener } = __require("events");
|
|
73692
|
-
var { Readable:
|
|
73775
|
+
var { Readable: Readable3 } = __require("stream");
|
|
73693
73776
|
var { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = require_errors();
|
|
73694
73777
|
var util5 = require_util();
|
|
73695
73778
|
var { ReadableStreamFrom } = require_util();
|
|
@@ -73703,7 +73786,7 @@ var require_readable = __commonJS({
|
|
|
73703
73786
|
var kBytesRead = /* @__PURE__ */ Symbol("kBytesRead");
|
|
73704
73787
|
var noop2 = () => {
|
|
73705
73788
|
};
|
|
73706
|
-
var BodyReadable = class extends
|
|
73789
|
+
var BodyReadable = class extends Readable3 {
|
|
73707
73790
|
/**
|
|
73708
73791
|
* @param {object} opts
|
|
73709
73792
|
* @param {(this: Readable, size: number) => void} opts.resume
|
|
@@ -74102,7 +74185,7 @@ var require_api_request = __commonJS({
|
|
|
74102
74185
|
"use strict";
|
|
74103
74186
|
var assert10 = __require("assert");
|
|
74104
74187
|
var { AsyncResource } = __require("async_hooks");
|
|
74105
|
-
var { Readable:
|
|
74188
|
+
var { Readable: Readable3 } = require_readable();
|
|
74106
74189
|
var { InvalidArgumentError, RequestAbortedError } = require_errors();
|
|
74107
74190
|
var util5 = require_util();
|
|
74108
74191
|
function noop2() {
|
|
@@ -74188,7 +74271,7 @@ var require_api_request = __commonJS({
|
|
|
74188
74271
|
const parsedHeaders = headers;
|
|
74189
74272
|
const contentType = parsedHeaders?.["content-type"];
|
|
74190
74273
|
const contentLength = parsedHeaders?.["content-length"];
|
|
74191
|
-
const res = new
|
|
74274
|
+
const res = new Readable3({
|
|
74192
74275
|
resume: () => controller.resume(),
|
|
74193
74276
|
abort: (reason) => controller.abort(reason),
|
|
74194
74277
|
contentType,
|
|
@@ -74564,7 +74647,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74564
74647
|
"node_modules/undici/lib/api/api-pipeline.js"(exports, module) {
|
|
74565
74648
|
"use strict";
|
|
74566
74649
|
var {
|
|
74567
|
-
Readable:
|
|
74650
|
+
Readable: Readable3,
|
|
74568
74651
|
Duplex,
|
|
74569
74652
|
PassThrough
|
|
74570
74653
|
} = __require("stream");
|
|
@@ -74581,7 +74664,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74581
74664
|
function noop2() {
|
|
74582
74665
|
}
|
|
74583
74666
|
var kResume = /* @__PURE__ */ Symbol("resume");
|
|
74584
|
-
var PipelineRequest = class extends
|
|
74667
|
+
var PipelineRequest = class extends Readable3 {
|
|
74585
74668
|
constructor() {
|
|
74586
74669
|
super({ autoDestroy: true });
|
|
74587
74670
|
this[kResume] = null;
|
|
@@ -74599,7 +74682,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74599
74682
|
callback(err);
|
|
74600
74683
|
}
|
|
74601
74684
|
};
|
|
74602
|
-
var PipelineResponse = class extends
|
|
74685
|
+
var PipelineResponse = class extends Readable3 {
|
|
74603
74686
|
constructor(resume2) {
|
|
74604
74687
|
super({ autoDestroy: true });
|
|
74605
74688
|
this[kResume] = resume2;
|
|
@@ -74753,7 +74836,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74753
74836
|
util5.destroy(ret, err);
|
|
74754
74837
|
}
|
|
74755
74838
|
};
|
|
74756
|
-
function
|
|
74839
|
+
function pipeline2(opts, handler) {
|
|
74757
74840
|
try {
|
|
74758
74841
|
const pipelineHandler = new PipelineHandler(opts, handler);
|
|
74759
74842
|
this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler);
|
|
@@ -74762,7 +74845,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74762
74845
|
return new PassThrough().destroy(err);
|
|
74763
74846
|
}
|
|
74764
74847
|
}
|
|
74765
|
-
module.exports =
|
|
74848
|
+
module.exports = pipeline2;
|
|
74766
74849
|
}
|
|
74767
74850
|
});
|
|
74768
74851
|
|
|
@@ -76398,7 +76481,7 @@ var require_snapshot_utils = __commonJS({
|
|
|
76398
76481
|
var require_snapshot_recorder = __commonJS({
|
|
76399
76482
|
"node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
76400
76483
|
"use strict";
|
|
76401
|
-
var { writeFile: writeFile4, readFile:
|
|
76484
|
+
var { writeFile: writeFile4, readFile: readFile4, mkdir: mkdir6 } = __require("fs/promises");
|
|
76402
76485
|
var { dirname: dirname4, resolve } = __require("path");
|
|
76403
76486
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("timers");
|
|
76404
76487
|
var { InvalidArgumentError, UndiciError } = require_errors();
|
|
@@ -76615,7 +76698,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
76615
76698
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
76616
76699
|
}
|
|
76617
76700
|
try {
|
|
76618
|
-
const data = await
|
|
76701
|
+
const data = await readFile4(resolve(path5), "utf8");
|
|
76619
76702
|
const parsed = JSON.parse(data);
|
|
76620
76703
|
if (Array.isArray(parsed)) {
|
|
76621
76704
|
this.#snapshots.clear();
|
|
@@ -79880,7 +79963,7 @@ var require_cache2 = __commonJS({
|
|
|
79880
79963
|
"node_modules/undici/lib/interceptor/cache.js"(exports, module) {
|
|
79881
79964
|
"use strict";
|
|
79882
79965
|
var assert10 = __require("assert");
|
|
79883
|
-
var { Readable:
|
|
79966
|
+
var { Readable: Readable3 } = __require("stream");
|
|
79884
79967
|
var util5 = require_util();
|
|
79885
79968
|
var CacheHandler = require_cache_handler();
|
|
79886
79969
|
var MemoryCacheStore = require_memory_cache_store();
|
|
@@ -80056,7 +80139,7 @@ var require_cache2 = __commonJS({
|
|
|
80056
80139
|
return dispatch(opts, new CacheHandler(globalOpts, cacheKey2, handler));
|
|
80057
80140
|
}
|
|
80058
80141
|
function sendCachedValue(handler, opts, result, age, context, isStale2) {
|
|
80059
|
-
const stream4 = util5.isStream(result.body) ? result.body :
|
|
80142
|
+
const stream4 = util5.isStream(result.body) ? result.body : Readable3.from(result.body ?? []);
|
|
80060
80143
|
assert10(!stream4.destroyed, "stream should not be destroyed");
|
|
80061
80144
|
assert10(!stream4.readableDidRead, "stream should not be readableDidRead");
|
|
80062
80145
|
const controller = {
|
|
@@ -80301,7 +80384,7 @@ var require_decompress = __commonJS({
|
|
|
80301
80384
|
"node_modules/undici/lib/interceptor/decompress.js"(exports, module) {
|
|
80302
80385
|
"use strict";
|
|
80303
80386
|
var { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = __require("zlib");
|
|
80304
|
-
var { pipeline } = __require("stream");
|
|
80387
|
+
var { pipeline: pipeline2 } = __require("stream");
|
|
80305
80388
|
var DecoratorHandler = require_decorator_handler();
|
|
80306
80389
|
var supportedEncodings = {
|
|
80307
80390
|
gzip: createGunzip,
|
|
@@ -80411,7 +80494,7 @@ var require_decompress = __commonJS({
|
|
|
80411
80494
|
#setupMultipleDecompressors(controller) {
|
|
80412
80495
|
const lastDecompressor = this.#decompressors[this.#decompressors.length - 1];
|
|
80413
80496
|
this.#setupDecompressorEvents(lastDecompressor, controller);
|
|
80414
|
-
|
|
80497
|
+
pipeline2(this.#decompressors, (err) => {
|
|
80415
80498
|
if (err) {
|
|
80416
80499
|
super.onResponseError(controller, err);
|
|
80417
80500
|
return;
|
|
@@ -83205,7 +83288,7 @@ var require_fetch = __commonJS({
|
|
|
83205
83288
|
subresourceSet
|
|
83206
83289
|
} = require_constants4();
|
|
83207
83290
|
var EE = __require("events");
|
|
83208
|
-
var { Readable:
|
|
83291
|
+
var { Readable: Readable3, pipeline: pipeline2, finished: finished2, isErrored, isReadable } = __require("stream");
|
|
83209
83292
|
var { addAbortListener, bufferToLowerCasedHeaderName } = require_util();
|
|
83210
83293
|
var { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require_data_url();
|
|
83211
83294
|
var { getGlobalDispatcher } = require_global2();
|
|
@@ -84181,7 +84264,7 @@ var require_fetch = __commonJS({
|
|
|
84181
84264
|
const headersList = new HeadersList();
|
|
84182
84265
|
appendHeadersListFromResponseHeaders(headersList, headers, rawHeaders);
|
|
84183
84266
|
const location = headersList.get("location", true);
|
|
84184
|
-
this.body = new
|
|
84267
|
+
this.body = new Readable3({ read: () => controller.resume() });
|
|
84185
84268
|
const willFollow = location && request2.redirect === "follow" && redirectStatusSet.has(status);
|
|
84186
84269
|
const decoders = [];
|
|
84187
84270
|
if (request2.method !== "HEAD" && request2.method !== "CONNECT" && !nullBodyStatus.includes(status) && !willFollow) {
|
|
@@ -84229,7 +84312,7 @@ var require_fetch = __commonJS({
|
|
|
84229
84312
|
status,
|
|
84230
84313
|
statusText,
|
|
84231
84314
|
headersList,
|
|
84232
|
-
body: decoders.length ?
|
|
84315
|
+
body: decoders.length ? pipeline2(this.body, ...decoders, (err) => {
|
|
84233
84316
|
if (err) {
|
|
84234
84317
|
this.onResponseError(controller, err);
|
|
84235
84318
|
}
|
|
@@ -88041,7 +88124,7 @@ ${value}`;
|
|
|
88041
88124
|
var require_eventsource = __commonJS({
|
|
88042
88125
|
"node_modules/undici/lib/web/eventsource/eventsource.js"(exports, module) {
|
|
88043
88126
|
"use strict";
|
|
88044
|
-
var { pipeline } = __require("stream");
|
|
88127
|
+
var { pipeline: pipeline2 } = __require("stream");
|
|
88045
88128
|
var { fetching } = require_fetch();
|
|
88046
88129
|
var { webidl } = require_webidl();
|
|
88047
88130
|
var { EventSourceStream } = require_eventsource_stream();
|
|
@@ -88191,7 +88274,7 @@ var require_eventsource = __commonJS({
|
|
|
88191
88274
|
));
|
|
88192
88275
|
}
|
|
88193
88276
|
});
|
|
88194
|
-
|
|
88277
|
+
pipeline2(
|
|
88195
88278
|
response.body.stream,
|
|
88196
88279
|
eventSourceStream,
|
|
88197
88280
|
(error) => {
|
|
@@ -90741,7 +90824,8 @@ var init_v1 = __esm({
|
|
|
90741
90824
|
import { AsyncLocalStorage } from "async_hooks";
|
|
90742
90825
|
import { createHmac } from "crypto";
|
|
90743
90826
|
import { createServer } from "http";
|
|
90744
|
-
import { finished } from "stream";
|
|
90827
|
+
import { finished, Readable } from "stream";
|
|
90828
|
+
import { pipeline } from "stream/promises";
|
|
90745
90829
|
import { homedir as homedir8 } from "os";
|
|
90746
90830
|
import { join as join11 } from "path";
|
|
90747
90831
|
import { mkdir as mkdir5, writeFile as writeFile3, readFile as readFile2, stat as fsStat } from "fs/promises";
|
|
@@ -90750,10 +90834,10 @@ async function loadGatewayCatalog(apiBase, apiKey) {
|
|
|
90750
90834
|
try {
|
|
90751
90835
|
const controller = new AbortController();
|
|
90752
90836
|
const timer2 = setTimeout(() => controller.abort(), GATEWAY_CATALOG_TIMEOUT_MS);
|
|
90753
|
-
const res = await fetch
|
|
90754
|
-
|
|
90755
|
-
|
|
90756
|
-
|
|
90837
|
+
const res = await (apiKey ? createApiKeyFetch(apiKey, fetch, apiBase) : fetch)(
|
|
90838
|
+
`${apiBase}/v1/models`,
|
|
90839
|
+
{ signal: controller.signal }
|
|
90840
|
+
);
|
|
90757
90841
|
clearTimeout(timer2);
|
|
90758
90842
|
if (!res.ok) return;
|
|
90759
90843
|
const body = await res.json();
|
|
@@ -91499,6 +91583,39 @@ function blockrunRequestId(response) {
|
|
|
91499
91583
|
}
|
|
91500
91584
|
return void 0;
|
|
91501
91585
|
}
|
|
91586
|
+
function gatewayRemainingCreditUsd(response) {
|
|
91587
|
+
const raw = response?.headers.get("x-blockrun-credit-remaining-usd");
|
|
91588
|
+
if (raw === null || raw === void 0) return void 0;
|
|
91589
|
+
const trimmed = raw.trim();
|
|
91590
|
+
if (trimmed === "") return void 0;
|
|
91591
|
+
const value = Number(trimmed);
|
|
91592
|
+
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
91593
|
+
return value;
|
|
91594
|
+
}
|
|
91595
|
+
function settledMediaCostUsd(upstream, body, x402AmountUsd, estimate) {
|
|
91596
|
+
const header = gatewaySettledCostUsd(upstream);
|
|
91597
|
+
if (header !== void 0) return header;
|
|
91598
|
+
const price = body?.price?.amount;
|
|
91599
|
+
if (typeof price === "string" || typeof price === "number") {
|
|
91600
|
+
const parsed = Number(price);
|
|
91601
|
+
if (Number.isFinite(parsed) && parsed >= 0) return parsed;
|
|
91602
|
+
}
|
|
91603
|
+
if (typeof x402AmountUsd === "number" && x402AmountUsd > 0) return x402AmountUsd;
|
|
91604
|
+
return estimate;
|
|
91605
|
+
}
|
|
91606
|
+
function noteRemainingCredit(response) {
|
|
91607
|
+
const remaining = gatewayRemainingCreditUsd(response);
|
|
91608
|
+
if (remaining === void 0) return;
|
|
91609
|
+
if (remaining > LOW_CREDIT_USD) {
|
|
91610
|
+
lowCreditWarned = false;
|
|
91611
|
+
return;
|
|
91612
|
+
}
|
|
91613
|
+
if (lowCreditWarned) return;
|
|
91614
|
+
lowCreditWarned = true;
|
|
91615
|
+
console.warn(
|
|
91616
|
+
remaining <= 0 ? `[ClawRouter] \u26A0 BlockRun account credit is exhausted \u2014 calls will fail with 402. Top up: ${PORTAL_CREDITS_URL}` : `[ClawRouter] \u26A0 BlockRun account credit low: $${remaining.toFixed(2)} remaining. Top up: ${PORTAL_CREDITS_URL}`
|
|
91617
|
+
);
|
|
91618
|
+
}
|
|
91502
91619
|
function gatewaySettledCostUsd(response) {
|
|
91503
91620
|
const raw = response?.headers.get("x-blockrun-cost-usd");
|
|
91504
91621
|
if (raw === null || raw === void 0) return void 0;
|
|
@@ -91508,7 +91625,7 @@ function gatewaySettledCostUsd(response) {
|
|
|
91508
91625
|
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
91509
91626
|
return value;
|
|
91510
91627
|
}
|
|
91511
|
-
async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPaymentUsd) {
|
|
91628
|
+
async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPaymentUsd, accountPassthrough = false, authMode = "wallet") {
|
|
91512
91629
|
const startTime = Date.now();
|
|
91513
91630
|
const upstreamUrl = `${apiBase}${req.url}`;
|
|
91514
91631
|
const isBlockrunExa = req.url?.startsWith("/v1/exa/") ?? false;
|
|
@@ -91547,16 +91664,32 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
|
|
|
91547
91664
|
return;
|
|
91548
91665
|
responseHeaders[key2] = value;
|
|
91549
91666
|
});
|
|
91667
|
+
if (accountPassthrough || authMode === "api-key") {
|
|
91668
|
+
responseHeaders["cache-control"] = "no-store";
|
|
91669
|
+
}
|
|
91550
91670
|
res.writeHead(upstream.status, responseHeaders);
|
|
91551
|
-
if (
|
|
91552
|
-
|
|
91553
|
-
|
|
91554
|
-
|
|
91671
|
+
if (accountPassthrough) {
|
|
91672
|
+
if (upstream.body) {
|
|
91673
|
+
await pipeline(
|
|
91674
|
+
Readable.fromWeb(upstream.body),
|
|
91675
|
+
res,
|
|
91676
|
+
{ signal: clientAbort.signal }
|
|
91677
|
+
);
|
|
91678
|
+
} else {
|
|
91679
|
+
res.end();
|
|
91680
|
+
}
|
|
91681
|
+
} else {
|
|
91682
|
+
if (upstream.body) {
|
|
91683
|
+
const chunks = await readBodyWithTimeout(upstream.body, ERROR_BODY_READ_TIMEOUT_MS);
|
|
91684
|
+
for (const chunk of chunks) {
|
|
91685
|
+
safeWrite(res, Buffer.from(chunk));
|
|
91686
|
+
}
|
|
91555
91687
|
}
|
|
91688
|
+
res.end();
|
|
91556
91689
|
}
|
|
91557
|
-
res.end();
|
|
91558
91690
|
const latencyMs = Date.now() - startTime;
|
|
91559
91691
|
console.log(`[ClawRouter] ${requestLabel} response: ${upstream.status} (${latencyMs}ms)`);
|
|
91692
|
+
noteRemainingCredit(upstream);
|
|
91560
91693
|
const settledCostUsd = gatewaySettledCostUsd(upstream);
|
|
91561
91694
|
const paidAmount = settledCostUsd ?? getActualPaymentUsd();
|
|
91562
91695
|
const requestCost = settledCostUsd !== void 0 ? settledCostUsd : resolvePhoneTelemetryCost({
|
|
@@ -91660,7 +91793,8 @@ async function startProxy(options) {
|
|
|
91660
91793
|
const walletKey = options.wallet === void 0 ? void 0 : typeof options.wallet === "string" ? options.wallet : options.wallet.key;
|
|
91661
91794
|
const solanaPrivateKeyBytes = options.wallet === void 0 || typeof options.wallet === "string" ? void 0 : options.wallet.solanaPrivateKeyBytes;
|
|
91662
91795
|
const paymentChain = options.paymentChain ?? await resolvePaymentChain();
|
|
91663
|
-
const
|
|
91796
|
+
const requestedApiBase = options.apiBase ?? (authMode === "api-key" ? BLOCKRUN_API_KEY_API : paymentChain === "solana" && solanaPrivateKeyBytes ? BLOCKRUN_SOLANA_API : BLOCKRUN_API);
|
|
91797
|
+
const apiBase = authMode === "api-key" ? normalizeApiKeyBase(requestedApiBase) : requestedApiBase;
|
|
91664
91798
|
if (authMode === "api-key") {
|
|
91665
91799
|
console.log(`[ClawRouter] Auth: BlockRun API key ${maskApiKey(apiKey)} (${apiBase})`);
|
|
91666
91800
|
console.log(`[ClawRouter] Billing: account credit \u2014 top up at ${PORTAL_CREDITS_URL}`);
|
|
@@ -91789,7 +91923,7 @@ async function startProxy(options) {
|
|
|
91789
91923
|
if (store) store.amountUsd = amountUsd;
|
|
91790
91924
|
console.log(`[ClawRouter] Payment signed on ${chain3} (${network}) \u2014 $${amountUsd.toFixed(6)}`);
|
|
91791
91925
|
});
|
|
91792
|
-
const payFetch = authMode === "api-key" ? createApiKeyFetch(apiKey) : createPayFetchWithPreAuth(fetch, x402, void 0, {
|
|
91926
|
+
const payFetch = authMode === "api-key" ? createApiKeyFetch(apiKey, fetch, apiBase) : createPayFetchWithPreAuth(fetch, x402, void 0, {
|
|
91793
91927
|
skipPreAuth: paymentChain === "solana",
|
|
91794
91928
|
// Per-request cost estimate so pre-auth is only reused when the cached
|
|
91795
91929
|
// payment still covers the (possibly larger) request — BlockRun prices per
|
|
@@ -92293,9 +92427,15 @@ async function startProxy(options) {
|
|
|
92293
92427
|
}
|
|
92294
92428
|
}
|
|
92295
92429
|
}
|
|
92296
|
-
const imgActualCost =
|
|
92430
|
+
const imgActualCost = settledMediaCostUsd(
|
|
92431
|
+
upstream,
|
|
92432
|
+
result,
|
|
92433
|
+
paymentStore.getStore()?.amountUsd,
|
|
92434
|
+
imgCost
|
|
92435
|
+
);
|
|
92297
92436
|
logUsage({
|
|
92298
92437
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92438
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
92299
92439
|
model: imgModel,
|
|
92300
92440
|
tier: "IMAGE",
|
|
92301
92441
|
cost: imgActualCost,
|
|
@@ -92364,12 +92504,15 @@ async function startProxy(options) {
|
|
|
92364
92504
|
return;
|
|
92365
92505
|
}
|
|
92366
92506
|
try {
|
|
92367
|
-
|
|
92507
|
+
let upstream = await payFetch(`${apiBase}/v1/images/image2image`, {
|
|
92368
92508
|
method: "POST",
|
|
92369
92509
|
headers: { "content-type": "application/json", "user-agent": USER_AGENT },
|
|
92370
92510
|
body: reqBody,
|
|
92371
92511
|
signal: clientAbort.signal
|
|
92372
92512
|
});
|
|
92513
|
+
if (authMode === "api-key") {
|
|
92514
|
+
upstream = await pollApiKeyJob(upstream, payFetch, apiBase, clientAbort.signal);
|
|
92515
|
+
}
|
|
92373
92516
|
const text = await upstream.text();
|
|
92374
92517
|
if (!upstream.ok) {
|
|
92375
92518
|
res.writeHead(upstream.status, { "Content-Type": "application/json" });
|
|
@@ -92416,9 +92559,15 @@ async function startProxy(options) {
|
|
|
92416
92559
|
}
|
|
92417
92560
|
}
|
|
92418
92561
|
}
|
|
92419
|
-
const img2imgActualCost =
|
|
92562
|
+
const img2imgActualCost = settledMediaCostUsd(
|
|
92563
|
+
upstream,
|
|
92564
|
+
result,
|
|
92565
|
+
paymentStore.getStore()?.amountUsd,
|
|
92566
|
+
img2imgCost
|
|
92567
|
+
);
|
|
92420
92568
|
logUsage({
|
|
92421
92569
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92570
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
92422
92571
|
model: img2imgModel,
|
|
92423
92572
|
tier: "IMAGE",
|
|
92424
92573
|
cost: img2imgActualCost,
|
|
@@ -92458,12 +92607,15 @@ async function startProxy(options) {
|
|
|
92458
92607
|
} catch {
|
|
92459
92608
|
}
|
|
92460
92609
|
try {
|
|
92461
|
-
|
|
92610
|
+
let upstream = await payFetch(`${apiBase}/v1/audio/generations`, {
|
|
92462
92611
|
method: "POST",
|
|
92463
92612
|
headers: { "content-type": "application/json", "user-agent": USER_AGENT },
|
|
92464
92613
|
body: reqBody,
|
|
92465
92614
|
signal: clientAbort.signal
|
|
92466
92615
|
});
|
|
92616
|
+
if (authMode === "api-key") {
|
|
92617
|
+
upstream = await pollApiKeyJob(upstream, payFetch, apiBase, clientAbort.signal);
|
|
92618
|
+
}
|
|
92467
92619
|
const text = await upstream.text();
|
|
92468
92620
|
if (!upstream.ok) {
|
|
92469
92621
|
res.writeHead(upstream.status, { "Content-Type": "application/json" });
|
|
@@ -92502,9 +92654,15 @@ async function startProxy(options) {
|
|
|
92502
92654
|
}
|
|
92503
92655
|
}
|
|
92504
92656
|
}
|
|
92505
|
-
const audioActualCost =
|
|
92657
|
+
const audioActualCost = settledMediaCostUsd(
|
|
92658
|
+
upstream,
|
|
92659
|
+
result,
|
|
92660
|
+
paymentStore.getStore()?.amountUsd,
|
|
92661
|
+
0.15
|
|
92662
|
+
);
|
|
92506
92663
|
logUsage({
|
|
92507
92664
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92665
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
92508
92666
|
model: audioModel,
|
|
92509
92667
|
tier: "AUDIO",
|
|
92510
92668
|
cost: audioActualCost,
|
|
@@ -92662,7 +92820,12 @@ async function startProxy(options) {
|
|
|
92662
92820
|
}
|
|
92663
92821
|
}
|
|
92664
92822
|
}
|
|
92665
|
-
const videoActualCost =
|
|
92823
|
+
const videoActualCost = settledMediaCostUsd(
|
|
92824
|
+
void 0,
|
|
92825
|
+
submitResult,
|
|
92826
|
+
paymentStore.getStore()?.amountUsd,
|
|
92827
|
+
estimateVideoCost(videoModel, videoDuration, videoHasImageInput)
|
|
92828
|
+
);
|
|
92666
92829
|
logUsage({
|
|
92667
92830
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92668
92831
|
model: videoModel,
|
|
@@ -92696,7 +92859,9 @@ async function startProxy(options) {
|
|
|
92696
92859
|
res,
|
|
92697
92860
|
apiBase,
|
|
92698
92861
|
payFetch,
|
|
92699
|
-
() => paymentStore.getStore()?.amountUsd ?? 0
|
|
92862
|
+
() => paymentStore.getStore()?.amountUsd ?? 0,
|
|
92863
|
+
false,
|
|
92864
|
+
authMode
|
|
92700
92865
|
);
|
|
92701
92866
|
} catch (err) {
|
|
92702
92867
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -92719,6 +92884,33 @@ async function startProxy(options) {
|
|
|
92719
92884
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
92720
92885
|
return;
|
|
92721
92886
|
}
|
|
92887
|
+
if (authMode === "api-key" && /^\/v1\//.test(req.url ?? "") && !/^\/v1\/(?:chat\/completions|messages)(?:\?|$)/.test(req.url ?? "")) {
|
|
92888
|
+
try {
|
|
92889
|
+
await proxyPaidApiRequest(
|
|
92890
|
+
req,
|
|
92891
|
+
res,
|
|
92892
|
+
apiBase,
|
|
92893
|
+
payFetch,
|
|
92894
|
+
() => paymentStore.getStore()?.amountUsd ?? 0,
|
|
92895
|
+
true,
|
|
92896
|
+
authMode
|
|
92897
|
+
);
|
|
92898
|
+
} catch (err) {
|
|
92899
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
92900
|
+
options.onError?.(error);
|
|
92901
|
+
if (!res.headersSent) {
|
|
92902
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
92903
|
+
res.end(
|
|
92904
|
+
JSON.stringify({
|
|
92905
|
+
error: { message: `Account API error: ${error.message}`, type: "account_error" }
|
|
92906
|
+
})
|
|
92907
|
+
);
|
|
92908
|
+
} else if (!res.writableEnded) {
|
|
92909
|
+
res.end();
|
|
92910
|
+
}
|
|
92911
|
+
}
|
|
92912
|
+
return;
|
|
92913
|
+
}
|
|
92722
92914
|
if (!req.url.includes("/chat/completions")) {
|
|
92723
92915
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
92724
92916
|
res.end(
|
|
@@ -93402,9 +93594,15 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
93402
93594
|
responseText = lines.join("\n");
|
|
93403
93595
|
}
|
|
93404
93596
|
console.log(`[ClawRouter] /imagegen success: ${images.length} image(s) generated`);
|
|
93405
|
-
const imagegenActualCost =
|
|
93597
|
+
const imagegenActualCost = settledMediaCostUsd(
|
|
93598
|
+
imageResponse,
|
|
93599
|
+
imageResult,
|
|
93600
|
+
paymentStore.getStore()?.amountUsd,
|
|
93601
|
+
estimateImageCost(imageModel, imageSize, 1)
|
|
93602
|
+
);
|
|
93406
93603
|
logUsage({
|
|
93407
93604
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
93605
|
+
...blockrunRequestId(imageResponse) ? { requestId: blockrunRequestId(imageResponse) } : {},
|
|
93408
93606
|
model: imageModel,
|
|
93409
93607
|
tier: "IMAGE",
|
|
93410
93608
|
cost: imagegenActualCost,
|
|
@@ -93628,9 +93826,15 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
93628
93826
|
responseText = lines.join("\n");
|
|
93629
93827
|
}
|
|
93630
93828
|
console.log(`[ClawRouter] /img2img success: ${images.length} image(s)`);
|
|
93631
|
-
const img2imgActualCost2 =
|
|
93829
|
+
const img2imgActualCost2 = settledMediaCostUsd(
|
|
93830
|
+
img2imgResponse,
|
|
93831
|
+
img2imgResult,
|
|
93832
|
+
paymentStore.getStore()?.amountUsd,
|
|
93833
|
+
estimateImageCost(img2imgModel, img2imgSize, 1)
|
|
93834
|
+
);
|
|
93632
93835
|
logUsage({
|
|
93633
93836
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
93837
|
+
...blockrunRequestId(img2imgResponse) ? { requestId: blockrunRequestId(img2imgResponse) } : {},
|
|
93634
93838
|
model: img2imgModel,
|
|
93635
93839
|
tier: "IMAGE",
|
|
93636
93840
|
cost: img2imgActualCost2,
|
|
@@ -94686,6 +94890,7 @@ data: [DONE]
|
|
|
94686
94890
|
}
|
|
94687
94891
|
}
|
|
94688
94892
|
upstreamRequestId = blockrunRequestId(upstream);
|
|
94893
|
+
noteRemainingCredit(upstream);
|
|
94689
94894
|
if (!upstream) {
|
|
94690
94895
|
const attemptSummary = failedAttempts.length > 0 ? failedAttempts.map((a) => `${a.model} (${a.reason})`).join(", ") : "unknown";
|
|
94691
94896
|
const structuredMessage = failedAttempts.length > 0 ? `All ${failedAttempts.length} models failed. Tried: ${attemptSummary}` : "All models in fallback chain failed";
|
|
@@ -95226,7 +95431,7 @@ data: [DONE]
|
|
|
95226
95431
|
});
|
|
95227
95432
|
}
|
|
95228
95433
|
}
|
|
95229
|
-
var paymentStore, BLOCKRUN_API, BLOCKRUN_SOLANA_API, DESKTOP_SERVICE_TOKEN_FILE, IMAGE_DIR, AUDIO_DIR, VIDEO_DIR, AUTO_MODEL, ROUTING_PROFILES, FREE_MODELS, gatewayModelIds, FREE_MODEL, FREE_UPSTREAM_OVERRIDES, MAX_MESSAGES, CONTEXT_LIMIT_KB, HEARTBEAT_INTERVAL_MS, BALANCE_CHECK_TIMEOUT_MS, GATEWAY_CATALOG_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, PER_MODEL_TIMEOUT_MS, REASONING_MODEL_TIMEOUT_MS, REASONING_MODEL_IDS, MAX_FALLBACK_ATTEMPTS, HEALTH_CHECK_TIMEOUT_MS, RATE_LIMIT_COOLDOWN_MS, OVERLOAD_COOLDOWN_MS, PORT_RETRY_ATTEMPTS, PORT_RETRY_DELAY_MS, MODEL_BODY_READ_TIMEOUT_MS, ERROR_BODY_READ_TIMEOUT_MS, ClientDisconnectedError, rateLimitedModels, overloadedModels, perProviderErrors, BALANCE_CHECK_BUFFER, BALANCE_PREFLIGHT_OUTPUT_TOKEN_CAP, PROVIDER_ERROR_PATTERNS, DEGRADED_RESPONSE_PATTERNS, DEGRADED_LOOP_PATTERNS, VALID_ROLES, ROLE_MAPPINGS, VALID_TOOL_ID_PATTERN, KIMI_BLOCK_RE, KIMI_TOKEN_RE, THINKING_TAG_RE, THINKING_BLOCK_RE, BLOCKRUN_MODEL_BY_ID, IMAGE_PRICING, IMAGE_MODEL_IDS, IMAGE_MODEL_SIZES, IMAGE_MODEL_ALIASES, VIDEO_PRICING, PHONE_PRICING;
|
|
95434
|
+
var paymentStore, BLOCKRUN_API, BLOCKRUN_SOLANA_API, DESKTOP_SERVICE_TOKEN_FILE, IMAGE_DIR, AUDIO_DIR, VIDEO_DIR, AUTO_MODEL, ROUTING_PROFILES, FREE_MODELS, gatewayModelIds, FREE_MODEL, FREE_UPSTREAM_OVERRIDES, MAX_MESSAGES, CONTEXT_LIMIT_KB, HEARTBEAT_INTERVAL_MS, BALANCE_CHECK_TIMEOUT_MS, GATEWAY_CATALOG_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, PER_MODEL_TIMEOUT_MS, REASONING_MODEL_TIMEOUT_MS, REASONING_MODEL_IDS, MAX_FALLBACK_ATTEMPTS, HEALTH_CHECK_TIMEOUT_MS, RATE_LIMIT_COOLDOWN_MS, OVERLOAD_COOLDOWN_MS, PORT_RETRY_ATTEMPTS, PORT_RETRY_DELAY_MS, MODEL_BODY_READ_TIMEOUT_MS, ERROR_BODY_READ_TIMEOUT_MS, ClientDisconnectedError, rateLimitedModels, overloadedModels, perProviderErrors, BALANCE_CHECK_BUFFER, BALANCE_PREFLIGHT_OUTPUT_TOKEN_CAP, PROVIDER_ERROR_PATTERNS, DEGRADED_RESPONSE_PATTERNS, DEGRADED_LOOP_PATTERNS, VALID_ROLES, ROLE_MAPPINGS, VALID_TOOL_ID_PATTERN, KIMI_BLOCK_RE, KIMI_TOKEN_RE, THINKING_TAG_RE, THINKING_BLOCK_RE, BLOCKRUN_MODEL_BY_ID, IMAGE_PRICING, IMAGE_MODEL_IDS, IMAGE_MODEL_SIZES, IMAGE_MODEL_ALIASES, VIDEO_PRICING, PHONE_PRICING, LOW_CREDIT_USD, lowCreditWarned;
|
|
95230
95435
|
var init_proxy = __esm({
|
|
95231
95436
|
"src/proxy.ts"() {
|
|
95232
95437
|
"use strict";
|
|
@@ -95488,6 +95693,8 @@ var init_proxy = __esm({
|
|
|
95488
95693
|
"phone/numbers/release": 0,
|
|
95489
95694
|
"voice/call": 0.54
|
|
95490
95695
|
};
|
|
95696
|
+
LOW_CREDIT_USD = 1;
|
|
95697
|
+
lowCreditWarned = false;
|
|
95491
95698
|
}
|
|
95492
95699
|
});
|
|
95493
95700
|
|
|
@@ -96528,13 +96735,13 @@ ${USAGE}`
|
|
|
96528
96735
|
${USAGE}`);
|
|
96529
96736
|
}
|
|
96530
96737
|
if (raw === "clear") return { kind: "limit", window: target, usd: void 0 };
|
|
96531
|
-
const
|
|
96532
|
-
if (!USD.test(raw) || !Number.isFinite(
|
|
96738
|
+
const usd2 = Number(raw);
|
|
96739
|
+
if (!USD.test(raw) || !Number.isFinite(usd2) || usd2 <= 0) {
|
|
96533
96740
|
return fail(
|
|
96534
96741
|
`Rejected amount "${raw}": must be a positive decimal USD value such as 0.10 or 5`
|
|
96535
96742
|
);
|
|
96536
96743
|
}
|
|
96537
|
-
return { kind: "limit", window: target, usd };
|
|
96744
|
+
return { kind: "limit", window: target, usd: usd2 };
|
|
96538
96745
|
}
|
|
96539
96746
|
if (!isListAction(action)) return fail(`Unknown subcommand "${sub}"
|
|
96540
96747
|
${USAGE}`);
|
|
@@ -96679,6 +96886,194 @@ var init_policy = __esm({
|
|
|
96679
96886
|
}
|
|
96680
96887
|
});
|
|
96681
96888
|
|
|
96889
|
+
// src/reconcile.ts
|
|
96890
|
+
var reconcile_exports = {};
|
|
96891
|
+
__export(reconcile_exports, {
|
|
96892
|
+
formatReconcile: () => formatReconcile,
|
|
96893
|
+
joinRows: () => joinRows,
|
|
96894
|
+
loadGatewayRows: () => loadGatewayRows,
|
|
96895
|
+
loadLocalRows: () => loadLocalRows,
|
|
96896
|
+
reconcile: () => reconcile
|
|
96897
|
+
});
|
|
96898
|
+
import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
|
|
96899
|
+
import { join as join12 } from "path";
|
|
96900
|
+
import { homedir as homedir9 } from "os";
|
|
96901
|
+
async function loadLocalRows(since) {
|
|
96902
|
+
let files;
|
|
96903
|
+
try {
|
|
96904
|
+
files = (await readdir3(LOG_DIR3)).filter((f) => f.endsWith(".jsonl"));
|
|
96905
|
+
} catch {
|
|
96906
|
+
return { rows: [], unkeyed: 0 };
|
|
96907
|
+
}
|
|
96908
|
+
const rows = [];
|
|
96909
|
+
let unkeyed = 0;
|
|
96910
|
+
for (const file of files) {
|
|
96911
|
+
let text;
|
|
96912
|
+
try {
|
|
96913
|
+
text = await readFile3(join12(LOG_DIR3, file), "utf8");
|
|
96914
|
+
} catch {
|
|
96915
|
+
continue;
|
|
96916
|
+
}
|
|
96917
|
+
for (const line of text.split("\n")) {
|
|
96918
|
+
if (!line.trim()) continue;
|
|
96919
|
+
let entry;
|
|
96920
|
+
try {
|
|
96921
|
+
entry = JSON.parse(line);
|
|
96922
|
+
} catch {
|
|
96923
|
+
continue;
|
|
96924
|
+
}
|
|
96925
|
+
if (!entry.timestamp || new Date(entry.timestamp) < since) continue;
|
|
96926
|
+
if (!entry.requestId) {
|
|
96927
|
+
unkeyed++;
|
|
96928
|
+
continue;
|
|
96929
|
+
}
|
|
96930
|
+
rows.push({
|
|
96931
|
+
requestId: entry.requestId,
|
|
96932
|
+
model: entry.model,
|
|
96933
|
+
cost: typeof entry.cost === "number" ? entry.cost : 0,
|
|
96934
|
+
timestamp: entry.timestamp
|
|
96935
|
+
});
|
|
96936
|
+
}
|
|
96937
|
+
}
|
|
96938
|
+
return { rows, unkeyed };
|
|
96939
|
+
}
|
|
96940
|
+
async function loadGatewayRows(apiKey, from15, maxPages = 40) {
|
|
96941
|
+
const rows = [];
|
|
96942
|
+
const unavailableDays = /* @__PURE__ */ new Set();
|
|
96943
|
+
let cursor;
|
|
96944
|
+
for (let page = 0; page < maxPages; page++) {
|
|
96945
|
+
const result = await fetchUsagePage(apiKey, { from: from15, limit: 500, cursor });
|
|
96946
|
+
if (!result) return page === 0 ? void 0 : { rows, unavailableDays: [...unavailableDays] };
|
|
96947
|
+
rows.push(...result.rows);
|
|
96948
|
+
result.unavailableDays.forEach((d) => unavailableDays.add(d));
|
|
96949
|
+
if (!result.nextCursor) break;
|
|
96950
|
+
cursor = result.nextCursor;
|
|
96951
|
+
}
|
|
96952
|
+
return { rows, unavailableDays: [...unavailableDays] };
|
|
96953
|
+
}
|
|
96954
|
+
function joinRows(localRows, gatewayRows, unkeyed = 0, unavailableDays = []) {
|
|
96955
|
+
const localById = new Map(localRows.map((r2) => [r2.requestId, r2]));
|
|
96956
|
+
const seen = /* @__PURE__ */ new Set();
|
|
96957
|
+
const matched = [];
|
|
96958
|
+
const chargedNotRecorded = [];
|
|
96959
|
+
const pending = [];
|
|
96960
|
+
let gatewayTotalUsd = 0;
|
|
96961
|
+
for (const remote of gatewayRows) {
|
|
96962
|
+
if (remote.costState === "pending") {
|
|
96963
|
+
pending.push(remote);
|
|
96964
|
+
continue;
|
|
96965
|
+
}
|
|
96966
|
+
gatewayTotalUsd += remote.costUsd ?? 0;
|
|
96967
|
+
const local = localById.get(remote.requestId);
|
|
96968
|
+
if (!local) {
|
|
96969
|
+
chargedNotRecorded.push(remote);
|
|
96970
|
+
continue;
|
|
96971
|
+
}
|
|
96972
|
+
seen.add(remote.requestId);
|
|
96973
|
+
matched.push({ local, remote, deltaUsd: local.cost - (remote.costUsd ?? 0) });
|
|
96974
|
+
}
|
|
96975
|
+
const recordedNotCharged = localRows.filter((r2) => !seen.has(r2.requestId));
|
|
96976
|
+
const localTotalUsd = localRows.reduce((sum, r2) => sum + r2.cost, 0);
|
|
96977
|
+
return {
|
|
96978
|
+
matched,
|
|
96979
|
+
chargedNotRecorded,
|
|
96980
|
+
recordedNotCharged,
|
|
96981
|
+
pending,
|
|
96982
|
+
localTotalUsd,
|
|
96983
|
+
gatewayTotalUsd,
|
|
96984
|
+
unavailableDays,
|
|
96985
|
+
unkeyedLocalCount: unkeyed
|
|
96986
|
+
};
|
|
96987
|
+
}
|
|
96988
|
+
async function reconcile(apiKey, days) {
|
|
96989
|
+
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
|
|
96990
|
+
const [{ rows: localRows, unkeyed }, gateway] = await Promise.all([
|
|
96991
|
+
loadLocalRows(since),
|
|
96992
|
+
loadGatewayRows(apiKey, since.toISOString())
|
|
96993
|
+
]);
|
|
96994
|
+
if (!gateway) throw new Error("Could not read the BlockRun usage ledger (GET /v1/usage).");
|
|
96995
|
+
return joinRows(localRows, gateway.rows, unkeyed, gateway.unavailableDays);
|
|
96996
|
+
}
|
|
96997
|
+
function formatReconcile(r2, days) {
|
|
96998
|
+
const out = [];
|
|
96999
|
+
const mismatched = r2.matched.filter((m) => Math.abs(m.deltaUsd) > CENT_EPSILON);
|
|
97000
|
+
out.push(`
|
|
97001
|
+
BlockRun reconciliation \u2014 last ${days} day${days === 1 ? "" : "s"}
|
|
97002
|
+
`);
|
|
97003
|
+
out.push(
|
|
97004
|
+
` Gateway charged: ${usd(r2.gatewayTotalUsd)} (${r2.matched.length + r2.chargedNotRecorded.length} settled calls)`
|
|
97005
|
+
);
|
|
97006
|
+
out.push(` Journal recorded: ${usd(r2.localTotalUsd)}`);
|
|
97007
|
+
out.push(
|
|
97008
|
+
` Matched: ${r2.matched.length}, of which ${mismatched.length} disagree on amount`
|
|
97009
|
+
);
|
|
97010
|
+
if (mismatched.length > 0) {
|
|
97011
|
+
out.push(`
|
|
97012
|
+
Amount mismatches (journal vs gateway):`);
|
|
97013
|
+
for (const m of mismatched.slice(0, 10)) {
|
|
97014
|
+
out.push(
|
|
97015
|
+
` ${m.remote.requestId.slice(0, 8)} ${(m.local.model || m.remote.endpoint).padEnd(28)} journal ${usd(m.local.cost)} gateway ${usd(m.remote.costUsd ?? 0)}`
|
|
97016
|
+
);
|
|
97017
|
+
}
|
|
97018
|
+
if (mismatched.length > 10) out.push(` \u2026and ${mismatched.length - 10} more`);
|
|
97019
|
+
}
|
|
97020
|
+
if (r2.chargedNotRecorded.length > 0) {
|
|
97021
|
+
const total = r2.chargedNotRecorded.reduce((s3, x) => s3 + (x.costUsd ?? 0), 0);
|
|
97022
|
+
out.push(
|
|
97023
|
+
`
|
|
97024
|
+
\u26A0 Charged but NOT in this machine's journal: ${r2.chargedNotRecorded.length} call(s), ${usd(total)}`
|
|
97025
|
+
);
|
|
97026
|
+
out.push(
|
|
97027
|
+
` Expected if the same key is used elsewhere \u2014 another machine, another BlockRun product,`
|
|
97028
|
+
);
|
|
97029
|
+
out.push(` or a call made before this proxy started recording request ids.`);
|
|
97030
|
+
for (const x of r2.chargedNotRecorded.slice(0, 10)) {
|
|
97031
|
+
out.push(
|
|
97032
|
+
` ${x.requestId.slice(0, 8)} ${x.timestamp.slice(0, 19)} ${x.endpoint.padEnd(26)} ${usd(x.costUsd ?? 0)}`
|
|
97033
|
+
);
|
|
97034
|
+
}
|
|
97035
|
+
if (r2.chargedNotRecorded.length > 10) {
|
|
97036
|
+
out.push(` \u2026and ${r2.chargedNotRecorded.length - 10} more`);
|
|
97037
|
+
}
|
|
97038
|
+
}
|
|
97039
|
+
if (r2.recordedNotCharged.length > 0) {
|
|
97040
|
+
out.push(
|
|
97041
|
+
`
|
|
97042
|
+
Recorded locally with no settled ledger row: ${r2.recordedNotCharged.length} call(s)`
|
|
97043
|
+
);
|
|
97044
|
+
out.push(` Usually benign \u2014 free models, cache hits, and calls still pending pricing.`);
|
|
97045
|
+
}
|
|
97046
|
+
if (r2.pending.length > 0) {
|
|
97047
|
+
out.push(`
|
|
97048
|
+
Pending pricing (excluded from totals): ${r2.pending.length} call(s)`);
|
|
97049
|
+
out.push(` Usage recorded, charge not final. These can still be repriced.`);
|
|
97050
|
+
}
|
|
97051
|
+
if (r2.unkeyedLocalCount > 0) {
|
|
97052
|
+
out.push(`
|
|
97053
|
+
Journal entries with no request id: ${r2.unkeyedLocalCount} (not reconcilable)`);
|
|
97054
|
+
}
|
|
97055
|
+
if (r2.unavailableDays.length > 0) {
|
|
97056
|
+
out.push(`
|
|
97057
|
+
\u26A0 Gateway could not list these days: ${r2.unavailableDays.join(", ")}`);
|
|
97058
|
+
out.push(` Totals above are incomplete for that period.`);
|
|
97059
|
+
}
|
|
97060
|
+
out.push(`
|
|
97061
|
+
Note: the top-up fee (5.5% + $0.30) is charged at purchase, not per call,`);
|
|
97062
|
+
out.push(` so the sum above is less than your card was charged by exactly those fees.
|
|
97063
|
+
`);
|
|
97064
|
+
return out.join("\n");
|
|
97065
|
+
}
|
|
97066
|
+
var LOG_DIR3, CENT_EPSILON, usd;
|
|
97067
|
+
var init_reconcile = __esm({
|
|
97068
|
+
"src/reconcile.ts"() {
|
|
97069
|
+
"use strict";
|
|
97070
|
+
init_api_key();
|
|
97071
|
+
LOG_DIR3 = join12(homedir9(), ".openclaw", "blockrun", "logs");
|
|
97072
|
+
CENT_EPSILON = 1e-9;
|
|
97073
|
+
usd = (n) => Math.abs(n) < 0.01 ? `$${n.toFixed(6)}` : `$${n.toFixed(2)}`;
|
|
97074
|
+
}
|
|
97075
|
+
});
|
|
97076
|
+
|
|
96682
97077
|
// src/provider.ts
|
|
96683
97078
|
function setActiveProxy(proxy) {
|
|
96684
97079
|
activeProxy = proxy;
|
|
@@ -112521,7 +112916,7 @@ var init_readBlob = __esm({
|
|
|
112521
112916
|
|
|
112522
112917
|
// node_modules/axios/lib/helpers/formDataToStream.js
|
|
112523
112918
|
import util2 from "util";
|
|
112524
|
-
import { Readable } from "stream";
|
|
112919
|
+
import { Readable as Readable2 } from "stream";
|
|
112525
112920
|
var BOUNDARY_ALPHABET, textEncoder, CRLF, CRLF_BYTES, CRLF_BYTES_COUNT, FormDataPart, formDataToStream, formDataToStream_default;
|
|
112526
112921
|
var init_formDataToStream = __esm({
|
|
112527
112922
|
"node_modules/axios/lib/helpers/formDataToStream.js"() {
|
|
@@ -112601,7 +112996,7 @@ var init_formDataToStream = __esm({
|
|
|
112601
112996
|
computedHeaders["Content-Length"] = contentLength;
|
|
112602
112997
|
}
|
|
112603
112998
|
headersHandler && headersHandler(computedHeaders);
|
|
112604
|
-
return
|
|
112999
|
+
return Readable2.from(
|
|
112605
113000
|
(async function* () {
|
|
112606
113001
|
for (const part of parts) {
|
|
112607
113002
|
yield boundaryBytes;
|
|
@@ -117665,8 +118060,8 @@ var init_constants3 = __esm({
|
|
|
117665
118060
|
|
|
117666
118061
|
// src/polymarket/wallet-adapter.ts
|
|
117667
118062
|
import fs2 from "fs";
|
|
117668
|
-
import { homedir as
|
|
117669
|
-
import { join as
|
|
118063
|
+
import { homedir as homedir10 } from "os";
|
|
118064
|
+
import { join as join13 } from "path";
|
|
117670
118065
|
function getOrCreateWalletKey() {
|
|
117671
118066
|
const envKey = process.env.BLOCKRUN_WALLET_KEY?.trim();
|
|
117672
118067
|
if (envKey && /^0x[0-9a-fA-F]{64}$/.test(envKey)) return envKey;
|
|
@@ -117714,7 +118109,7 @@ var init_wallet_adapter = __esm({
|
|
|
117714
118109
|
init_esm();
|
|
117715
118110
|
init_chains();
|
|
117716
118111
|
init_constants3();
|
|
117717
|
-
WALLET_FILE2 =
|
|
118112
|
+
WALLET_FILE2 = join13(homedir10(), ".openclaw", "blockrun", "wallet.key");
|
|
117718
118113
|
BASE_RPC_URLS = [
|
|
117719
118114
|
"https://mainnet.base.org",
|
|
117720
118115
|
"https://base.llamarpc.com",
|
|
@@ -118118,8 +118513,8 @@ var init_client5 = __esm({
|
|
|
118118
118513
|
function resolveSpendControl(deps) {
|
|
118119
118514
|
return deps?.spendControl ?? getSharedSpendControl();
|
|
118120
118515
|
}
|
|
118121
|
-
function usdToMicros(
|
|
118122
|
-
return String(Math.ceil(
|
|
118516
|
+
function usdToMicros(usd2) {
|
|
118517
|
+
return String(Math.ceil(usd2 * 1e6));
|
|
118123
118518
|
}
|
|
118124
118519
|
async function signUnderSpendPolicy(deps, quote, action, sign4) {
|
|
118125
118520
|
const control = resolveSpendControl(deps);
|
|
@@ -118148,13 +118543,13 @@ function getSessionLedger() {
|
|
|
118148
118543
|
perAgent: Object.fromEntries(ledger.perAgent)
|
|
118149
118544
|
};
|
|
118150
118545
|
}
|
|
118151
|
-
function reserveBet(
|
|
118152
|
-
ledger.totalUsd +=
|
|
118153
|
-
if (agentId) ledger.perAgent.set(agentId, (ledger.perAgent.get(agentId) ?? 0) +
|
|
118546
|
+
function reserveBet(usd2, agentId) {
|
|
118547
|
+
ledger.totalUsd += usd2;
|
|
118548
|
+
if (agentId) ledger.perAgent.set(agentId, (ledger.perAgent.get(agentId) ?? 0) + usd2);
|
|
118154
118549
|
}
|
|
118155
|
-
function releaseBet(
|
|
118156
|
-
ledger.totalUsd -=
|
|
118157
|
-
if (agentId) ledger.perAgent.set(agentId, (ledger.perAgent.get(agentId) ?? 0) -
|
|
118550
|
+
function releaseBet(usd2, agentId) {
|
|
118551
|
+
ledger.totalUsd -= usd2;
|
|
118552
|
+
if (agentId) ledger.perAgent.set(agentId, (ledger.perAgent.get(agentId) ?? 0) - usd2);
|
|
118158
118553
|
}
|
|
118159
118554
|
function commitBet() {
|
|
118160
118555
|
ledger.count += 1;
|
|
@@ -228116,9 +228511,9 @@ import {
|
|
|
228116
228511
|
unlinkSync
|
|
228117
228512
|
} from "fs";
|
|
228118
228513
|
import { readFile as readFileAsync } from "fs/promises";
|
|
228119
|
-
import { homedir as
|
|
228514
|
+
import { homedir as homedir15 } from "os";
|
|
228120
228515
|
import { randomUUID } from "crypto";
|
|
228121
|
-
import { join as
|
|
228516
|
+
import { join as join18 } from "path";
|
|
228122
228517
|
async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
228123
228518
|
const start = Date.now();
|
|
228124
228519
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -228160,8 +228555,8 @@ function isBlockrunWebSearchDisabled(config) {
|
|
|
228160
228555
|
return cfg.tools?.web?.search?.enabled === false;
|
|
228161
228556
|
}
|
|
228162
228557
|
function injectModelsConfig(logger48, options = {}) {
|
|
228163
|
-
const configDir =
|
|
228164
|
-
const configPath =
|
|
228558
|
+
const configDir = join18(homedir15(), ".openclaw");
|
|
228559
|
+
const configPath = join18(configDir, "openclaw.json");
|
|
228165
228560
|
let config = {};
|
|
228166
228561
|
let needsWrite = false;
|
|
228167
228562
|
if (!existsSync4(configDir)) {
|
|
@@ -228382,7 +228777,7 @@ function injectModelsConfig(logger48, options = {}) {
|
|
|
228382
228777
|
}
|
|
228383
228778
|
function syncAgentModelCache(logger48, options = {}) {
|
|
228384
228779
|
if (!isGatewayMode() && !options.forceWrite) return;
|
|
228385
|
-
const agentsDir =
|
|
228780
|
+
const agentsDir = join18(homedir15(), ".openclaw", "agents");
|
|
228386
228781
|
if (!existsSync4(agentsDir)) return;
|
|
228387
228782
|
let agentDirs;
|
|
228388
228783
|
try {
|
|
@@ -228392,7 +228787,7 @@ function syncAgentModelCache(logger48, options = {}) {
|
|
|
228392
228787
|
}
|
|
228393
228788
|
const expectedIds = VISIBLE_OPENCLAW_MODELS.map((m) => m.id);
|
|
228394
228789
|
for (const agent of agentDirs) {
|
|
228395
|
-
const cachePath =
|
|
228790
|
+
const cachePath = join18(agentsDir, agent, "agent", "models.json");
|
|
228396
228791
|
if (!existsSync4(cachePath)) continue;
|
|
228397
228792
|
try {
|
|
228398
228793
|
const raw = readTextFileSync(cachePath).trim();
|
|
@@ -228421,7 +228816,7 @@ function syncAgentModelCache(logger48, options = {}) {
|
|
|
228421
228816
|
}
|
|
228422
228817
|
}
|
|
228423
228818
|
function injectAuthProfile(logger48) {
|
|
228424
|
-
const agentsDir =
|
|
228819
|
+
const agentsDir = join18(homedir15(), ".openclaw", "agents");
|
|
228425
228820
|
if (!existsSync4(agentsDir)) {
|
|
228426
228821
|
try {
|
|
228427
228822
|
mkdirSync3(agentsDir, { recursive: true });
|
|
@@ -228438,9 +228833,9 @@ function injectAuthProfile(logger48) {
|
|
|
228438
228833
|
agents = ["main", ...agents];
|
|
228439
228834
|
}
|
|
228440
228835
|
for (const agentId of agents) {
|
|
228441
|
-
const authDir =
|
|
228442
|
-
const authPath =
|
|
228443
|
-
const sqlitePath =
|
|
228836
|
+
const authDir = join18(agentsDir, agentId, "agent");
|
|
228837
|
+
const authPath = join18(authDir, "auth-profiles.json");
|
|
228838
|
+
const sqlitePath = join18(authDir, "openclaw-agent.sqlite");
|
|
228444
228839
|
if (existsSync4(sqlitePath)) {
|
|
228445
228840
|
removeInjectedAuthPlaceholder(authPath, logger48, agentId);
|
|
228446
228841
|
continue;
|
|
@@ -228588,12 +228983,12 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
228588
228983
|
proc.__clawrouterStartupPhase = "starting";
|
|
228589
228984
|
}
|
|
228590
228985
|
const pluginApiKey = api.pluginConfig?.apiKey;
|
|
228591
|
-
|
|
228592
|
-
|
|
228593
|
-
|
|
228594
|
-
`pluginConfig.apiKey is set but invalid (expected brk_...) \u2014 ignoring it and looking elsewhere`
|
|
228986
|
+
if (typeof pluginApiKey === "string" && pluginApiKey.trim() && !isValidApiKey(pluginApiKey)) {
|
|
228987
|
+
throw new Error(
|
|
228988
|
+
`pluginConfig.apiKey is set but is not a BlockRun key (expected brk_\u2026). Fix it or remove it \u2014 refusing to fall back to another credential.`
|
|
228595
228989
|
);
|
|
228596
228990
|
}
|
|
228991
|
+
const resolvedApiKey = typeof pluginApiKey === "string" && isValidApiKey(pluginApiKey) ? { key: pluginApiKey.trim(), source: "config" } : await resolveApiKey();
|
|
228597
228992
|
const apiKey = resolvedApiKey?.key;
|
|
228598
228993
|
const configKey = api.pluginConfig?.walletKey;
|
|
228599
228994
|
let wallet;
|
|
@@ -228943,7 +229338,7 @@ function buildImageGenerationProvider() {
|
|
|
228943
229338
|
(result.data ?? []).map(async (img) => {
|
|
228944
229339
|
const filename = img.url?.split("/images/").pop();
|
|
228945
229340
|
if (!filename) throw new Error(`Unexpected image URL format: ${img.url}`);
|
|
228946
|
-
const filePath =
|
|
229341
|
+
const filePath = join18(IMAGE_DIR2, filename);
|
|
228947
229342
|
const buffer2 = await readFileAsync(filePath);
|
|
228948
229343
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "png";
|
|
228949
229344
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
|
|
@@ -228995,7 +229390,7 @@ function buildMusicGenerationProvider() {
|
|
|
228995
229390
|
(result.data ?? []).map(async (track) => {
|
|
228996
229391
|
const filename = track.url?.split("/audio/").pop();
|
|
228997
229392
|
if (!filename) throw new Error(`Unexpected audio URL format: ${track.url}`);
|
|
228998
|
-
const filePath =
|
|
229393
|
+
const filePath = join18(AUDIO_DIR2, filename);
|
|
228999
229394
|
const buffer2 = await readFileAsync(filePath);
|
|
229000
229395
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "mp3";
|
|
229001
229396
|
const mimeType = ext === "wav" ? "audio/wav" : "audio/mpeg";
|
|
@@ -229071,7 +229466,7 @@ function buildVideoGenerationProvider() {
|
|
|
229071
229466
|
(result.data ?? []).map(async (clip) => {
|
|
229072
229467
|
const filename = clip.url?.split("/videos/").pop();
|
|
229073
229468
|
if (!filename) throw new Error(`Unexpected video URL format: ${clip.url}`);
|
|
229074
|
-
const filePath =
|
|
229469
|
+
const filePath = join18(VIDEO_DIR2, filename);
|
|
229075
229470
|
const buffer2 = await readFileAsync(filePath);
|
|
229076
229471
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "mp4";
|
|
229077
229472
|
const mimeType = ext === "webm" ? "video/webm" : ext === "mov" ? "video/quicktime" : "video/mp4";
|
|
@@ -229436,9 +229831,9 @@ var init_index = __esm({
|
|
|
229436
229831
|
init_partners();
|
|
229437
229832
|
activeProxyHandle = null;
|
|
229438
229833
|
pendingConfiguredStartupApi = null;
|
|
229439
|
-
IMAGE_DIR2 =
|
|
229440
|
-
AUDIO_DIR2 =
|
|
229441
|
-
VIDEO_DIR2 =
|
|
229834
|
+
IMAGE_DIR2 = join18(homedir15(), ".openclaw", "blockrun", "images");
|
|
229835
|
+
AUDIO_DIR2 = join18(homedir15(), ".openclaw", "blockrun", "audio");
|
|
229836
|
+
VIDEO_DIR2 = join18(homedir15(), ".openclaw", "blockrun", "videos");
|
|
229442
229837
|
plugin = {
|
|
229443
229838
|
// NOT "clawrouter". OpenClaw bundles its own plugin under that id since the
|
|
229444
229839
|
// 2026.7.1 line (vendored at dist/extensions/clawrouter, provider `clawrouter/*`,
|
|
@@ -229780,7 +230175,18 @@ ${errText}`
|
|
|
229780
230175
|
if (!isGatewayMode()) {
|
|
229781
230176
|
if (shouldLogRegistration) {
|
|
229782
230177
|
void (async () => {
|
|
229783
|
-
|
|
230178
|
+
let keyResolution;
|
|
230179
|
+
try {
|
|
230180
|
+
keyResolution = await resolveApiKey();
|
|
230181
|
+
} catch (error) {
|
|
230182
|
+
api.logger.error(
|
|
230183
|
+
`BlockRun API key is configured but unusable: ${error instanceof Error ? error.message : String(error)}`
|
|
230184
|
+
);
|
|
230185
|
+
api.logger.error(
|
|
230186
|
+
`Not falling back to a wallet \u2014 fix the key, or run "clawrouter logout" to pay from the wallet deliberately.`
|
|
230187
|
+
);
|
|
230188
|
+
return;
|
|
230189
|
+
}
|
|
229784
230190
|
if (keyResolution) {
|
|
229785
230191
|
api.logger.info(
|
|
229786
230192
|
`Using BlockRun API key ${maskApiKey(keyResolution.key)} (from ${keyResolution.source}) \u2014 billing account credit, no wallet`
|
|
@@ -229863,7 +230269,7 @@ ${errText}`
|
|
|
229863
230269
|
}
|
|
229864
230270
|
resetProxyStartupState();
|
|
229865
230271
|
try {
|
|
229866
|
-
const configPath =
|
|
230272
|
+
const configPath = join18(homedir15(), ".openclaw", "openclaw.json");
|
|
229867
230273
|
if (existsSync4(configPath)) {
|
|
229868
230274
|
const config = JSON.parse(readTextFileSync(configPath));
|
|
229869
230275
|
if (config.models?.providers?.blockrun) {
|
|
@@ -229905,11 +230311,11 @@ ${errText}`
|
|
|
229905
230311
|
api.logger.warn(`Config cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
229906
230312
|
}
|
|
229907
230313
|
try {
|
|
229908
|
-
const agentsDir =
|
|
230314
|
+
const agentsDir = join18(homedir15(), ".openclaw", "agents");
|
|
229909
230315
|
if (existsSync4(agentsDir)) {
|
|
229910
230316
|
for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
229911
230317
|
if (!entry.isDirectory()) continue;
|
|
229912
|
-
const authPath =
|
|
230318
|
+
const authPath = join18(agentsDir, entry.name, "agent", "auth-profiles.json");
|
|
229913
230319
|
if (!existsSync4(authPath)) continue;
|
|
229914
230320
|
try {
|
|
229915
230321
|
const store = JSON.parse(readTextFileSync(authPath));
|
|
@@ -230725,6 +231131,27 @@ Use: clawrouter share <id> --as=<feishu|slack|discord|telegram|whatsapp|plain>
|
|
|
230725
231131
|
process.exit(1);
|
|
230726
231132
|
}
|
|
230727
231133
|
}
|
|
231134
|
+
async function cmdReconcile(days) {
|
|
231135
|
+
const resolved = await resolveApiKey().catch((error) => {
|
|
231136
|
+
console.error(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
|
|
231137
|
+
process.exit(1);
|
|
231138
|
+
});
|
|
231139
|
+
if (!resolved) {
|
|
231140
|
+
console.error(`\u2717 Reconciliation needs a BlockRun API key \u2014 it compares against your account`);
|
|
231141
|
+
console.error(` ledger. Wallet spend settles on-chain instead; see "clawrouter stats".`);
|
|
231142
|
+
console.error(` Sign in at ${PORTAL_URL} and run: clawrouter login brk_live_...`);
|
|
231143
|
+
process.exit(1);
|
|
231144
|
+
}
|
|
231145
|
+
try {
|
|
231146
|
+
const { reconcile: reconcile2, formatReconcile: formatReconcile2 } = await Promise.resolve().then(() => (init_reconcile(), reconcile_exports));
|
|
231147
|
+
const result = await reconcile2(resolved.key, days);
|
|
231148
|
+
console.log(formatReconcile2(result, days));
|
|
231149
|
+
process.exitCode = result.chargedNotRecorded.length > 0 ? 2 : 0;
|
|
231150
|
+
} catch (error) {
|
|
231151
|
+
console.error(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
|
|
231152
|
+
process.exit(1);
|
|
231153
|
+
}
|
|
231154
|
+
}
|
|
230728
231155
|
async function cmdStatus(port) {
|
|
230729
231156
|
try {
|
|
230730
231157
|
const data = await queryProxy("/health?full=true", port);
|
|
@@ -231002,8 +231429,8 @@ async function cmdSetup() {
|
|
|
231002
231429
|
unlinkSync: unlinkSync2,
|
|
231003
231430
|
writeFileSync: writeFileSync4
|
|
231004
231431
|
} = await import("fs");
|
|
231005
|
-
const { dirname: dirname4, join:
|
|
231006
|
-
const { homedir:
|
|
231432
|
+
const { dirname: dirname4, join: join19 } = await import("path");
|
|
231433
|
+
const { homedir: homedir16 } = await import("os");
|
|
231007
231434
|
const { injectModelsConfig: injectModelsConfig2, injectAuthProfile: injectAuthProfile2, syncAgentModelCache: syncAgentModelCache2, VISIBLE_OPENCLAW_MODELS: VISIBLE_OPENCLAW_MODELS2 } = await Promise.resolve().then(() => (init_index(), index_exports));
|
|
231008
231435
|
const { BLOCKRUN_PLUGIN_ID: BLOCKRUN_PLUGIN_ID2, prepareBlockRunPluginConfig: prepareBlockRunPluginConfig2 } = await Promise.resolve().then(() => (init_openclaw_plugin_config(), openclaw_plugin_config_exports));
|
|
231009
231436
|
console.log("\u{1F99E} ClawRouter setup\n");
|
|
@@ -231018,19 +231445,19 @@ async function cmdSetup() {
|
|
|
231018
231445
|
} catch {
|
|
231019
231446
|
}
|
|
231020
231447
|
if (process.env.npm_config_prefix) {
|
|
231021
|
-
candidates.push(
|
|
231448
|
+
candidates.push(join19(process.env.npm_config_prefix, "bin", "openclaw"));
|
|
231022
231449
|
}
|
|
231023
231450
|
try {
|
|
231024
231451
|
const npmRoot = execSync("npm root -g", {
|
|
231025
231452
|
encoding: "utf8",
|
|
231026
231453
|
stdio: ["ignore", "pipe", "ignore"]
|
|
231027
231454
|
}).trim();
|
|
231028
|
-
if (npmRoot) candidates.push(
|
|
231455
|
+
if (npmRoot) candidates.push(join19(dirname4(dirname4(npmRoot)), "bin", "openclaw"));
|
|
231029
231456
|
} catch {
|
|
231030
231457
|
}
|
|
231031
231458
|
candidates.push(
|
|
231032
|
-
|
|
231033
|
-
|
|
231459
|
+
join19(homedir16(), ".npm-global", "bin", "openclaw"),
|
|
231460
|
+
join19(homedir16(), ".local", "bin", "openclaw"),
|
|
231034
231461
|
"/usr/local/bin/openclaw"
|
|
231035
231462
|
);
|
|
231036
231463
|
return candidates.find((candidate) => existsSync5(candidate)) ?? "";
|
|
@@ -231042,7 +231469,7 @@ async function cmdSetup() {
|
|
|
231042
231469
|
process.exit(1);
|
|
231043
231470
|
}
|
|
231044
231471
|
console.log(` \u2713 Found openclaw at ${openclawPath}`);
|
|
231045
|
-
const configPath =
|
|
231472
|
+
const configPath = join19(homedir16(), ".openclaw", "openclaw.json");
|
|
231046
231473
|
const configInitiallyExisted = existsSync5(configPath);
|
|
231047
231474
|
const configRollbackBackup = configInitiallyExisted ? `${configPath}.before-${BLOCKRUN_PLUGIN_ID2}.${Date.now()}` : void 0;
|
|
231048
231475
|
if (configRollbackBackup) copyFileSync2(configPath, configRollbackBackup);
|
|
@@ -231050,23 +231477,23 @@ async function cmdSetup() {
|
|
|
231050
231477
|
return configRollbackBackup;
|
|
231051
231478
|
};
|
|
231052
231479
|
let installAttempted = false;
|
|
231053
|
-
const openclawRoot =
|
|
231480
|
+
const openclawRoot = join19(homedir16(), ".openclaw");
|
|
231054
231481
|
const findManagedBlockRunInstalls = () => {
|
|
231055
231482
|
const candidates = [
|
|
231056
|
-
|
|
231057
|
-
|
|
231058
|
-
|
|
231483
|
+
join19(openclawRoot, "extensions", BLOCKRUN_PLUGIN_ID2),
|
|
231484
|
+
join19(openclawRoot, "extensions", "clawrouter"),
|
|
231485
|
+
join19(openclawRoot, "npm", "node_modules", "@blockrun", "clawrouter")
|
|
231059
231486
|
];
|
|
231060
|
-
const projectsDir =
|
|
231487
|
+
const projectsDir = join19(openclawRoot, "npm", "projects");
|
|
231061
231488
|
try {
|
|
231062
231489
|
for (const project of readdirSync2(projectsDir)) {
|
|
231063
|
-
candidates.push(
|
|
231490
|
+
candidates.push(join19(projectsDir, project, "node_modules", "@blockrun", "clawrouter"));
|
|
231064
231491
|
}
|
|
231065
231492
|
} catch {
|
|
231066
231493
|
}
|
|
231067
231494
|
return [...new Set(candidates)].filter((candidate) => {
|
|
231068
231495
|
try {
|
|
231069
|
-
const metadata = JSON.parse(readFileSync4(
|
|
231496
|
+
const metadata = JSON.parse(readFileSync4(join19(candidate, "package.json"), "utf8"));
|
|
231070
231497
|
return metadata.name === "@blockrun/clawrouter";
|
|
231071
231498
|
} catch {
|
|
231072
231499
|
return false;
|
|
@@ -231074,7 +231501,7 @@ async function cmdSetup() {
|
|
|
231074
231501
|
});
|
|
231075
231502
|
};
|
|
231076
231503
|
const initialPluginDirs = findManagedBlockRunInstalls();
|
|
231077
|
-
const setupRollbackRoot =
|
|
231504
|
+
const setupRollbackRoot = join19(
|
|
231078
231505
|
openclawRoot,
|
|
231079
231506
|
"blockrun",
|
|
231080
231507
|
"setup-rollbacks",
|
|
@@ -231084,7 +231511,7 @@ async function cmdSetup() {
|
|
|
231084
231511
|
if (initialPluginDirs.length > 0) {
|
|
231085
231512
|
mkdirSync4(setupRollbackRoot, { recursive: true, mode: 448 });
|
|
231086
231513
|
for (const [index2, pluginDir] of initialPluginDirs.entries()) {
|
|
231087
|
-
const backup =
|
|
231514
|
+
const backup = join19(setupRollbackRoot, String(index2));
|
|
231088
231515
|
cpSync(pluginDir, backup, { recursive: true });
|
|
231089
231516
|
pluginRollbackBackups.set(pluginDir, backup);
|
|
231090
231517
|
}
|
|
@@ -231129,8 +231556,8 @@ async function cmdSetup() {
|
|
|
231129
231556
|
if (existsSync5(configPath)) {
|
|
231130
231557
|
try {
|
|
231131
231558
|
const config = JSON.parse(readFileSync4(configPath, "utf8"));
|
|
231132
|
-
const legacyPackagePath =
|
|
231133
|
-
|
|
231559
|
+
const legacyPackagePath = join19(
|
|
231560
|
+
homedir16(),
|
|
231134
231561
|
".openclaw",
|
|
231135
231562
|
"extensions",
|
|
231136
231563
|
"clawrouter",
|
|
@@ -231301,6 +231728,8 @@ function parseArgs(args) {
|
|
|
231301
231728
|
queryModels: false,
|
|
231302
231729
|
queryStats: false,
|
|
231303
231730
|
queryStatsDays: 7,
|
|
231731
|
+
reconcile: false,
|
|
231732
|
+
reconcileDays: 7,
|
|
231304
231733
|
queryCache: false,
|
|
231305
231734
|
setup: false,
|
|
231306
231735
|
share: false,
|
|
@@ -231335,6 +231764,12 @@ function parseArgs(args) {
|
|
|
231335
231764
|
result.queryStatsDays = Math.min(parseInt(args[i + 2], 10) || 7, 30);
|
|
231336
231765
|
i += 2;
|
|
231337
231766
|
}
|
|
231767
|
+
} else if (arg === "reconcile") {
|
|
231768
|
+
result.reconcile = true;
|
|
231769
|
+
if (args[i + 1] === "--days" && args[i + 2]) {
|
|
231770
|
+
result.reconcileDays = Math.min(parseInt(args[i + 2], 10) || 7, 90);
|
|
231771
|
+
i += 2;
|
|
231772
|
+
}
|
|
231338
231773
|
} else if (arg === "cache") {
|
|
231339
231774
|
result.queryCache = true;
|
|
231340
231775
|
} else if (arg === "doctor" || arg === "--doctor") {
|
|
@@ -231560,6 +231995,10 @@ async function main() {
|
|
|
231560
231995
|
await cmdStats(queryPort, args.queryStatsDays);
|
|
231561
231996
|
process.exit(0);
|
|
231562
231997
|
}
|
|
231998
|
+
if (args.reconcile) {
|
|
231999
|
+
await cmdReconcile(args.reconcileDays);
|
|
232000
|
+
process.exit(0);
|
|
232001
|
+
}
|
|
231563
232002
|
if (args.queryCache) {
|
|
231564
232003
|
await cmdCache(queryPort);
|
|
231565
232004
|
process.exit(0);
|