@blockrun/clawrouter 0.12.271 → 0.12.273
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 +569 -118
- package/dist/index.js +261 -65
- 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();
|
|
@@ -91054,7 +91138,12 @@ async function checkExistingProxy(port) {
|
|
|
91054
91138
|
const data = await response.json();
|
|
91055
91139
|
const authMode = data.authMode === "api-key" ? "api-key" : "wallet";
|
|
91056
91140
|
if (data.status === "ok" && (data.wallet || authMode === "api-key")) {
|
|
91057
|
-
return {
|
|
91141
|
+
return {
|
|
91142
|
+
wallet: data.wallet ?? "",
|
|
91143
|
+
paymentChain: data.paymentChain,
|
|
91144
|
+
authMode,
|
|
91145
|
+
apiKeyLabel: typeof data.apiKey === "string" ? data.apiKey : void 0
|
|
91146
|
+
};
|
|
91058
91147
|
}
|
|
91059
91148
|
}
|
|
91060
91149
|
return void 0;
|
|
@@ -91499,6 +91588,39 @@ function blockrunRequestId(response) {
|
|
|
91499
91588
|
}
|
|
91500
91589
|
return void 0;
|
|
91501
91590
|
}
|
|
91591
|
+
function gatewayRemainingCreditUsd(response) {
|
|
91592
|
+
const raw = response?.headers.get("x-blockrun-credit-remaining-usd");
|
|
91593
|
+
if (raw === null || raw === void 0) return void 0;
|
|
91594
|
+
const trimmed = raw.trim();
|
|
91595
|
+
if (trimmed === "") return void 0;
|
|
91596
|
+
const value = Number(trimmed);
|
|
91597
|
+
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
91598
|
+
return value;
|
|
91599
|
+
}
|
|
91600
|
+
function settledMediaCostUsd(upstream, body, x402AmountUsd, estimate) {
|
|
91601
|
+
const header = gatewaySettledCostUsd(upstream);
|
|
91602
|
+
if (header !== void 0) return header;
|
|
91603
|
+
const price = body?.price?.amount;
|
|
91604
|
+
if (typeof price === "string" || typeof price === "number") {
|
|
91605
|
+
const parsed = Number(price);
|
|
91606
|
+
if (Number.isFinite(parsed) && parsed >= 0) return parsed;
|
|
91607
|
+
}
|
|
91608
|
+
if (typeof x402AmountUsd === "number" && x402AmountUsd > 0) return x402AmountUsd;
|
|
91609
|
+
return estimate;
|
|
91610
|
+
}
|
|
91611
|
+
function noteRemainingCredit(response) {
|
|
91612
|
+
const remaining = gatewayRemainingCreditUsd(response);
|
|
91613
|
+
if (remaining === void 0) return;
|
|
91614
|
+
if (remaining > LOW_CREDIT_USD) {
|
|
91615
|
+
lowCreditWarned = false;
|
|
91616
|
+
return;
|
|
91617
|
+
}
|
|
91618
|
+
if (lowCreditWarned) return;
|
|
91619
|
+
lowCreditWarned = true;
|
|
91620
|
+
console.warn(
|
|
91621
|
+
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}`
|
|
91622
|
+
);
|
|
91623
|
+
}
|
|
91502
91624
|
function gatewaySettledCostUsd(response) {
|
|
91503
91625
|
const raw = response?.headers.get("x-blockrun-cost-usd");
|
|
91504
91626
|
if (raw === null || raw === void 0) return void 0;
|
|
@@ -91508,7 +91630,7 @@ function gatewaySettledCostUsd(response) {
|
|
|
91508
91630
|
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
91509
91631
|
return value;
|
|
91510
91632
|
}
|
|
91511
|
-
async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPaymentUsd) {
|
|
91633
|
+
async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPaymentUsd, accountPassthrough = false, authMode = "wallet") {
|
|
91512
91634
|
const startTime = Date.now();
|
|
91513
91635
|
const upstreamUrl = `${apiBase}${req.url}`;
|
|
91514
91636
|
const isBlockrunExa = req.url?.startsWith("/v1/exa/") ?? false;
|
|
@@ -91547,16 +91669,32 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
|
|
|
91547
91669
|
return;
|
|
91548
91670
|
responseHeaders[key2] = value;
|
|
91549
91671
|
});
|
|
91672
|
+
if (accountPassthrough || authMode === "api-key") {
|
|
91673
|
+
responseHeaders["cache-control"] = "no-store";
|
|
91674
|
+
}
|
|
91550
91675
|
res.writeHead(upstream.status, responseHeaders);
|
|
91551
|
-
if (
|
|
91552
|
-
|
|
91553
|
-
|
|
91554
|
-
|
|
91676
|
+
if (accountPassthrough) {
|
|
91677
|
+
if (upstream.body) {
|
|
91678
|
+
await pipeline(
|
|
91679
|
+
Readable.fromWeb(upstream.body),
|
|
91680
|
+
res,
|
|
91681
|
+
{ signal: clientAbort.signal }
|
|
91682
|
+
);
|
|
91683
|
+
} else {
|
|
91684
|
+
res.end();
|
|
91555
91685
|
}
|
|
91686
|
+
} else {
|
|
91687
|
+
if (upstream.body) {
|
|
91688
|
+
const chunks = await readBodyWithTimeout(upstream.body, ERROR_BODY_READ_TIMEOUT_MS);
|
|
91689
|
+
for (const chunk of chunks) {
|
|
91690
|
+
safeWrite(res, Buffer.from(chunk));
|
|
91691
|
+
}
|
|
91692
|
+
}
|
|
91693
|
+
res.end();
|
|
91556
91694
|
}
|
|
91557
|
-
res.end();
|
|
91558
91695
|
const latencyMs = Date.now() - startTime;
|
|
91559
91696
|
console.log(`[ClawRouter] ${requestLabel} response: ${upstream.status} (${latencyMs}ms)`);
|
|
91697
|
+
noteRemainingCredit(upstream);
|
|
91560
91698
|
const settledCostUsd = gatewaySettledCostUsd(upstream);
|
|
91561
91699
|
const paidAmount = settledCostUsd ?? getActualPaymentUsd();
|
|
91562
91700
|
const requestCost = settledCostUsd !== void 0 ? settledCostUsd : resolvePhoneTelemetryCost({
|
|
@@ -91660,7 +91798,8 @@ async function startProxy(options) {
|
|
|
91660
91798
|
const walletKey = options.wallet === void 0 ? void 0 : typeof options.wallet === "string" ? options.wallet : options.wallet.key;
|
|
91661
91799
|
const solanaPrivateKeyBytes = options.wallet === void 0 || typeof options.wallet === "string" ? void 0 : options.wallet.solanaPrivateKeyBytes;
|
|
91662
91800
|
const paymentChain = options.paymentChain ?? await resolvePaymentChain();
|
|
91663
|
-
const
|
|
91801
|
+
const requestedApiBase = options.apiBase ?? (authMode === "api-key" ? BLOCKRUN_API_KEY_API : paymentChain === "solana" && solanaPrivateKeyBytes ? BLOCKRUN_SOLANA_API : BLOCKRUN_API);
|
|
91802
|
+
const apiBase = authMode === "api-key" ? normalizeApiKeyBase(requestedApiBase) : requestedApiBase;
|
|
91664
91803
|
if (authMode === "api-key") {
|
|
91665
91804
|
console.log(`[ClawRouter] Auth: BlockRun API key ${maskApiKey(apiKey)} (${apiBase})`);
|
|
91666
91805
|
console.log(`[ClawRouter] Billing: account credit \u2014 top up at ${PORTAL_CREDITS_URL}`);
|
|
@@ -91687,13 +91826,20 @@ async function startProxy(options) {
|
|
|
91687
91826
|
);
|
|
91688
91827
|
}
|
|
91689
91828
|
if (authMode === "api-key") {
|
|
91829
|
+
const runningLabel = existingProxy.apiKeyLabel;
|
|
91830
|
+
const ourLabel = maskApiKey(apiKey);
|
|
91831
|
+
if (runningLabel !== ourLabel) {
|
|
91832
|
+
throw new Error(
|
|
91833
|
+
`Existing proxy on port ${listenPort} is billing ${runningLabel ?? "an unidentified BlockRun account"}, but this process is configured for ${ourLabel}. Reusing it would charge the other account. Stop that proxy first, or use a different port.`
|
|
91834
|
+
);
|
|
91835
|
+
}
|
|
91690
91836
|
options.onReady?.(listenPort);
|
|
91691
91837
|
return {
|
|
91692
91838
|
port: listenPort,
|
|
91693
91839
|
baseUrl: baseUrl2,
|
|
91694
91840
|
walletAddress: "",
|
|
91695
91841
|
authMode,
|
|
91696
|
-
apiKeyLabel:
|
|
91842
|
+
apiKeyLabel: ourLabel,
|
|
91697
91843
|
balanceMonitor: new ApiKeyBalanceMonitor(),
|
|
91698
91844
|
// No-op: we didn't start this proxy, so we shouldn't close it
|
|
91699
91845
|
close: async () => {
|
|
@@ -91789,7 +91935,7 @@ async function startProxy(options) {
|
|
|
91789
91935
|
if (store) store.amountUsd = amountUsd;
|
|
91790
91936
|
console.log(`[ClawRouter] Payment signed on ${chain3} (${network}) \u2014 $${amountUsd.toFixed(6)}`);
|
|
91791
91937
|
});
|
|
91792
|
-
const payFetch = authMode === "api-key" ? createApiKeyFetch(apiKey) : createPayFetchWithPreAuth(fetch, x402, void 0, {
|
|
91938
|
+
const payFetch = authMode === "api-key" ? createApiKeyFetch(apiKey, fetch, apiBase) : createPayFetchWithPreAuth(fetch, x402, void 0, {
|
|
91793
91939
|
skipPreAuth: paymentChain === "solana",
|
|
91794
91940
|
// Per-request cost estimate so pre-auth is only reused when the cached
|
|
91795
91941
|
// payment still covers the (possibly larger) request — BlockRun prices per
|
|
@@ -92293,9 +92439,15 @@ async function startProxy(options) {
|
|
|
92293
92439
|
}
|
|
92294
92440
|
}
|
|
92295
92441
|
}
|
|
92296
|
-
const imgActualCost =
|
|
92442
|
+
const imgActualCost = settledMediaCostUsd(
|
|
92443
|
+
upstream,
|
|
92444
|
+
result,
|
|
92445
|
+
paymentStore.getStore()?.amountUsd,
|
|
92446
|
+
imgCost
|
|
92447
|
+
);
|
|
92297
92448
|
logUsage({
|
|
92298
92449
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92450
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
92299
92451
|
model: imgModel,
|
|
92300
92452
|
tier: "IMAGE",
|
|
92301
92453
|
cost: imgActualCost,
|
|
@@ -92364,12 +92516,15 @@ async function startProxy(options) {
|
|
|
92364
92516
|
return;
|
|
92365
92517
|
}
|
|
92366
92518
|
try {
|
|
92367
|
-
|
|
92519
|
+
let upstream = await payFetch(`${apiBase}/v1/images/image2image`, {
|
|
92368
92520
|
method: "POST",
|
|
92369
92521
|
headers: { "content-type": "application/json", "user-agent": USER_AGENT },
|
|
92370
92522
|
body: reqBody,
|
|
92371
92523
|
signal: clientAbort.signal
|
|
92372
92524
|
});
|
|
92525
|
+
if (authMode === "api-key") {
|
|
92526
|
+
upstream = await pollApiKeyJob(upstream, payFetch, apiBase, clientAbort.signal);
|
|
92527
|
+
}
|
|
92373
92528
|
const text = await upstream.text();
|
|
92374
92529
|
if (!upstream.ok) {
|
|
92375
92530
|
res.writeHead(upstream.status, { "Content-Type": "application/json" });
|
|
@@ -92416,9 +92571,15 @@ async function startProxy(options) {
|
|
|
92416
92571
|
}
|
|
92417
92572
|
}
|
|
92418
92573
|
}
|
|
92419
|
-
const img2imgActualCost =
|
|
92574
|
+
const img2imgActualCost = settledMediaCostUsd(
|
|
92575
|
+
upstream,
|
|
92576
|
+
result,
|
|
92577
|
+
paymentStore.getStore()?.amountUsd,
|
|
92578
|
+
img2imgCost
|
|
92579
|
+
);
|
|
92420
92580
|
logUsage({
|
|
92421
92581
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92582
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
92422
92583
|
model: img2imgModel,
|
|
92423
92584
|
tier: "IMAGE",
|
|
92424
92585
|
cost: img2imgActualCost,
|
|
@@ -92458,12 +92619,15 @@ async function startProxy(options) {
|
|
|
92458
92619
|
} catch {
|
|
92459
92620
|
}
|
|
92460
92621
|
try {
|
|
92461
|
-
|
|
92622
|
+
let upstream = await payFetch(`${apiBase}/v1/audio/generations`, {
|
|
92462
92623
|
method: "POST",
|
|
92463
92624
|
headers: { "content-type": "application/json", "user-agent": USER_AGENT },
|
|
92464
92625
|
body: reqBody,
|
|
92465
92626
|
signal: clientAbort.signal
|
|
92466
92627
|
});
|
|
92628
|
+
if (authMode === "api-key") {
|
|
92629
|
+
upstream = await pollApiKeyJob(upstream, payFetch, apiBase, clientAbort.signal);
|
|
92630
|
+
}
|
|
92467
92631
|
const text = await upstream.text();
|
|
92468
92632
|
if (!upstream.ok) {
|
|
92469
92633
|
res.writeHead(upstream.status, { "Content-Type": "application/json" });
|
|
@@ -92502,9 +92666,15 @@ async function startProxy(options) {
|
|
|
92502
92666
|
}
|
|
92503
92667
|
}
|
|
92504
92668
|
}
|
|
92505
|
-
const audioActualCost =
|
|
92669
|
+
const audioActualCost = settledMediaCostUsd(
|
|
92670
|
+
upstream,
|
|
92671
|
+
result,
|
|
92672
|
+
paymentStore.getStore()?.amountUsd,
|
|
92673
|
+
0.15
|
|
92674
|
+
);
|
|
92506
92675
|
logUsage({
|
|
92507
92676
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92677
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
92508
92678
|
model: audioModel,
|
|
92509
92679
|
tier: "AUDIO",
|
|
92510
92680
|
cost: audioActualCost,
|
|
@@ -92662,7 +92832,12 @@ async function startProxy(options) {
|
|
|
92662
92832
|
}
|
|
92663
92833
|
}
|
|
92664
92834
|
}
|
|
92665
|
-
const videoActualCost =
|
|
92835
|
+
const videoActualCost = settledMediaCostUsd(
|
|
92836
|
+
void 0,
|
|
92837
|
+
submitResult,
|
|
92838
|
+
paymentStore.getStore()?.amountUsd,
|
|
92839
|
+
estimateVideoCost(videoModel, videoDuration, videoHasImageInput)
|
|
92840
|
+
);
|
|
92666
92841
|
logUsage({
|
|
92667
92842
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92668
92843
|
model: videoModel,
|
|
@@ -92696,7 +92871,9 @@ async function startProxy(options) {
|
|
|
92696
92871
|
res,
|
|
92697
92872
|
apiBase,
|
|
92698
92873
|
payFetch,
|
|
92699
|
-
() => paymentStore.getStore()?.amountUsd ?? 0
|
|
92874
|
+
() => paymentStore.getStore()?.amountUsd ?? 0,
|
|
92875
|
+
false,
|
|
92876
|
+
authMode
|
|
92700
92877
|
);
|
|
92701
92878
|
} catch (err) {
|
|
92702
92879
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -92719,6 +92896,33 @@ async function startProxy(options) {
|
|
|
92719
92896
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
92720
92897
|
return;
|
|
92721
92898
|
}
|
|
92899
|
+
if (authMode === "api-key" && /^\/v1\//.test(req.url ?? "") && !/^\/v1\/(?:chat\/completions|messages)(?:\?|$)/.test(req.url ?? "")) {
|
|
92900
|
+
try {
|
|
92901
|
+
await proxyPaidApiRequest(
|
|
92902
|
+
req,
|
|
92903
|
+
res,
|
|
92904
|
+
apiBase,
|
|
92905
|
+
payFetch,
|
|
92906
|
+
() => paymentStore.getStore()?.amountUsd ?? 0,
|
|
92907
|
+
true,
|
|
92908
|
+
authMode
|
|
92909
|
+
);
|
|
92910
|
+
} catch (err) {
|
|
92911
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
92912
|
+
options.onError?.(error);
|
|
92913
|
+
if (!res.headersSent) {
|
|
92914
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
92915
|
+
res.end(
|
|
92916
|
+
JSON.stringify({
|
|
92917
|
+
error: { message: `Account API error: ${error.message}`, type: "account_error" }
|
|
92918
|
+
})
|
|
92919
|
+
);
|
|
92920
|
+
} else if (!res.writableEnded) {
|
|
92921
|
+
res.end();
|
|
92922
|
+
}
|
|
92923
|
+
}
|
|
92924
|
+
return;
|
|
92925
|
+
}
|
|
92722
92926
|
if (!req.url.includes("/chat/completions")) {
|
|
92723
92927
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
92724
92928
|
res.end(
|
|
@@ -93402,9 +93606,15 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
93402
93606
|
responseText = lines.join("\n");
|
|
93403
93607
|
}
|
|
93404
93608
|
console.log(`[ClawRouter] /imagegen success: ${images.length} image(s) generated`);
|
|
93405
|
-
const imagegenActualCost =
|
|
93609
|
+
const imagegenActualCost = settledMediaCostUsd(
|
|
93610
|
+
imageResponse,
|
|
93611
|
+
imageResult,
|
|
93612
|
+
paymentStore.getStore()?.amountUsd,
|
|
93613
|
+
estimateImageCost(imageModel, imageSize, 1)
|
|
93614
|
+
);
|
|
93406
93615
|
logUsage({
|
|
93407
93616
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
93617
|
+
...blockrunRequestId(imageResponse) ? { requestId: blockrunRequestId(imageResponse) } : {},
|
|
93408
93618
|
model: imageModel,
|
|
93409
93619
|
tier: "IMAGE",
|
|
93410
93620
|
cost: imagegenActualCost,
|
|
@@ -93628,9 +93838,15 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
93628
93838
|
responseText = lines.join("\n");
|
|
93629
93839
|
}
|
|
93630
93840
|
console.log(`[ClawRouter] /img2img success: ${images.length} image(s)`);
|
|
93631
|
-
const img2imgActualCost2 =
|
|
93841
|
+
const img2imgActualCost2 = settledMediaCostUsd(
|
|
93842
|
+
img2imgResponse,
|
|
93843
|
+
img2imgResult,
|
|
93844
|
+
paymentStore.getStore()?.amountUsd,
|
|
93845
|
+
estimateImageCost(img2imgModel, img2imgSize, 1)
|
|
93846
|
+
);
|
|
93632
93847
|
logUsage({
|
|
93633
93848
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
93849
|
+
...blockrunRequestId(img2imgResponse) ? { requestId: blockrunRequestId(img2imgResponse) } : {},
|
|
93634
93850
|
model: img2imgModel,
|
|
93635
93851
|
tier: "IMAGE",
|
|
93636
93852
|
cost: img2imgActualCost2,
|
|
@@ -94686,6 +94902,7 @@ data: [DONE]
|
|
|
94686
94902
|
}
|
|
94687
94903
|
}
|
|
94688
94904
|
upstreamRequestId = blockrunRequestId(upstream);
|
|
94905
|
+
noteRemainingCredit(upstream);
|
|
94689
94906
|
if (!upstream) {
|
|
94690
94907
|
const attemptSummary = failedAttempts.length > 0 ? failedAttempts.map((a) => `${a.model} (${a.reason})`).join(", ") : "unknown";
|
|
94691
94908
|
const structuredMessage = failedAttempts.length > 0 ? `All ${failedAttempts.length} models failed. Tried: ${attemptSummary}` : "All models in fallback chain failed";
|
|
@@ -95226,7 +95443,7 @@ data: [DONE]
|
|
|
95226
95443
|
});
|
|
95227
95444
|
}
|
|
95228
95445
|
}
|
|
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;
|
|
95446
|
+
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
95447
|
var init_proxy = __esm({
|
|
95231
95448
|
"src/proxy.ts"() {
|
|
95232
95449
|
"use strict";
|
|
@@ -95488,6 +95705,8 @@ var init_proxy = __esm({
|
|
|
95488
95705
|
"phone/numbers/release": 0,
|
|
95489
95706
|
"voice/call": 0.54
|
|
95490
95707
|
};
|
|
95708
|
+
LOW_CREDIT_USD = 1;
|
|
95709
|
+
lowCreditWarned = false;
|
|
95491
95710
|
}
|
|
95492
95711
|
});
|
|
95493
95712
|
|
|
@@ -96528,13 +96747,13 @@ ${USAGE}`
|
|
|
96528
96747
|
${USAGE}`);
|
|
96529
96748
|
}
|
|
96530
96749
|
if (raw === "clear") return { kind: "limit", window: target, usd: void 0 };
|
|
96531
|
-
const
|
|
96532
|
-
if (!USD.test(raw) || !Number.isFinite(
|
|
96750
|
+
const usd2 = Number(raw);
|
|
96751
|
+
if (!USD.test(raw) || !Number.isFinite(usd2) || usd2 <= 0) {
|
|
96533
96752
|
return fail(
|
|
96534
96753
|
`Rejected amount "${raw}": must be a positive decimal USD value such as 0.10 or 5`
|
|
96535
96754
|
);
|
|
96536
96755
|
}
|
|
96537
|
-
return { kind: "limit", window: target, usd };
|
|
96756
|
+
return { kind: "limit", window: target, usd: usd2 };
|
|
96538
96757
|
}
|
|
96539
96758
|
if (!isListAction(action)) return fail(`Unknown subcommand "${sub}"
|
|
96540
96759
|
${USAGE}`);
|
|
@@ -96679,6 +96898,194 @@ var init_policy = __esm({
|
|
|
96679
96898
|
}
|
|
96680
96899
|
});
|
|
96681
96900
|
|
|
96901
|
+
// src/reconcile.ts
|
|
96902
|
+
var reconcile_exports = {};
|
|
96903
|
+
__export(reconcile_exports, {
|
|
96904
|
+
formatReconcile: () => formatReconcile,
|
|
96905
|
+
joinRows: () => joinRows,
|
|
96906
|
+
loadGatewayRows: () => loadGatewayRows,
|
|
96907
|
+
loadLocalRows: () => loadLocalRows,
|
|
96908
|
+
reconcile: () => reconcile
|
|
96909
|
+
});
|
|
96910
|
+
import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
|
|
96911
|
+
import { join as join12 } from "path";
|
|
96912
|
+
import { homedir as homedir9 } from "os";
|
|
96913
|
+
async function loadLocalRows(since) {
|
|
96914
|
+
let files;
|
|
96915
|
+
try {
|
|
96916
|
+
files = (await readdir3(LOG_DIR3)).filter((f) => f.endsWith(".jsonl"));
|
|
96917
|
+
} catch {
|
|
96918
|
+
return { rows: [], unkeyed: 0 };
|
|
96919
|
+
}
|
|
96920
|
+
const rows = [];
|
|
96921
|
+
let unkeyed = 0;
|
|
96922
|
+
for (const file of files) {
|
|
96923
|
+
let text;
|
|
96924
|
+
try {
|
|
96925
|
+
text = await readFile3(join12(LOG_DIR3, file), "utf8");
|
|
96926
|
+
} catch {
|
|
96927
|
+
continue;
|
|
96928
|
+
}
|
|
96929
|
+
for (const line of text.split("\n")) {
|
|
96930
|
+
if (!line.trim()) continue;
|
|
96931
|
+
let entry;
|
|
96932
|
+
try {
|
|
96933
|
+
entry = JSON.parse(line);
|
|
96934
|
+
} catch {
|
|
96935
|
+
continue;
|
|
96936
|
+
}
|
|
96937
|
+
if (!entry.timestamp || new Date(entry.timestamp) < since) continue;
|
|
96938
|
+
if (!entry.requestId) {
|
|
96939
|
+
unkeyed++;
|
|
96940
|
+
continue;
|
|
96941
|
+
}
|
|
96942
|
+
rows.push({
|
|
96943
|
+
requestId: entry.requestId,
|
|
96944
|
+
model: entry.model,
|
|
96945
|
+
cost: typeof entry.cost === "number" ? entry.cost : 0,
|
|
96946
|
+
timestamp: entry.timestamp
|
|
96947
|
+
});
|
|
96948
|
+
}
|
|
96949
|
+
}
|
|
96950
|
+
return { rows, unkeyed };
|
|
96951
|
+
}
|
|
96952
|
+
async function loadGatewayRows(apiKey, from15, maxPages = 40) {
|
|
96953
|
+
const rows = [];
|
|
96954
|
+
const unavailableDays = /* @__PURE__ */ new Set();
|
|
96955
|
+
let cursor;
|
|
96956
|
+
for (let page = 0; page < maxPages; page++) {
|
|
96957
|
+
const result = await fetchUsagePage(apiKey, { from: from15, limit: 500, cursor });
|
|
96958
|
+
if (!result) return page === 0 ? void 0 : { rows, unavailableDays: [...unavailableDays] };
|
|
96959
|
+
rows.push(...result.rows);
|
|
96960
|
+
result.unavailableDays.forEach((d) => unavailableDays.add(d));
|
|
96961
|
+
if (!result.nextCursor) break;
|
|
96962
|
+
cursor = result.nextCursor;
|
|
96963
|
+
}
|
|
96964
|
+
return { rows, unavailableDays: [...unavailableDays] };
|
|
96965
|
+
}
|
|
96966
|
+
function joinRows(localRows, gatewayRows, unkeyed = 0, unavailableDays = []) {
|
|
96967
|
+
const localById = new Map(localRows.map((r2) => [r2.requestId, r2]));
|
|
96968
|
+
const seen = /* @__PURE__ */ new Set();
|
|
96969
|
+
const matched = [];
|
|
96970
|
+
const chargedNotRecorded = [];
|
|
96971
|
+
const pending = [];
|
|
96972
|
+
let gatewayTotalUsd = 0;
|
|
96973
|
+
for (const remote of gatewayRows) {
|
|
96974
|
+
if (remote.costState === "pending") {
|
|
96975
|
+
pending.push(remote);
|
|
96976
|
+
continue;
|
|
96977
|
+
}
|
|
96978
|
+
gatewayTotalUsd += remote.costUsd ?? 0;
|
|
96979
|
+
const local = localById.get(remote.requestId);
|
|
96980
|
+
if (!local) {
|
|
96981
|
+
chargedNotRecorded.push(remote);
|
|
96982
|
+
continue;
|
|
96983
|
+
}
|
|
96984
|
+
seen.add(remote.requestId);
|
|
96985
|
+
matched.push({ local, remote, deltaUsd: local.cost - (remote.costUsd ?? 0) });
|
|
96986
|
+
}
|
|
96987
|
+
const recordedNotCharged = localRows.filter((r2) => !seen.has(r2.requestId));
|
|
96988
|
+
const localTotalUsd = localRows.reduce((sum, r2) => sum + r2.cost, 0);
|
|
96989
|
+
return {
|
|
96990
|
+
matched,
|
|
96991
|
+
chargedNotRecorded,
|
|
96992
|
+
recordedNotCharged,
|
|
96993
|
+
pending,
|
|
96994
|
+
localTotalUsd,
|
|
96995
|
+
gatewayTotalUsd,
|
|
96996
|
+
unavailableDays,
|
|
96997
|
+
unkeyedLocalCount: unkeyed
|
|
96998
|
+
};
|
|
96999
|
+
}
|
|
97000
|
+
async function reconcile(apiKey, days) {
|
|
97001
|
+
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
|
|
97002
|
+
const [{ rows: localRows, unkeyed }, gateway] = await Promise.all([
|
|
97003
|
+
loadLocalRows(since),
|
|
97004
|
+
loadGatewayRows(apiKey, since.toISOString())
|
|
97005
|
+
]);
|
|
97006
|
+
if (!gateway) throw new Error("Could not read the BlockRun usage ledger (GET /v1/usage).");
|
|
97007
|
+
return joinRows(localRows, gateway.rows, unkeyed, gateway.unavailableDays);
|
|
97008
|
+
}
|
|
97009
|
+
function formatReconcile(r2, days) {
|
|
97010
|
+
const out = [];
|
|
97011
|
+
const mismatched = r2.matched.filter((m) => Math.abs(m.deltaUsd) > CENT_EPSILON);
|
|
97012
|
+
out.push(`
|
|
97013
|
+
BlockRun reconciliation \u2014 last ${days} day${days === 1 ? "" : "s"}
|
|
97014
|
+
`);
|
|
97015
|
+
out.push(
|
|
97016
|
+
` Gateway charged: ${usd(r2.gatewayTotalUsd)} (${r2.matched.length + r2.chargedNotRecorded.length} settled calls)`
|
|
97017
|
+
);
|
|
97018
|
+
out.push(` Journal recorded: ${usd(r2.localTotalUsd)}`);
|
|
97019
|
+
out.push(
|
|
97020
|
+
` Matched: ${r2.matched.length}, of which ${mismatched.length} disagree on amount`
|
|
97021
|
+
);
|
|
97022
|
+
if (mismatched.length > 0) {
|
|
97023
|
+
out.push(`
|
|
97024
|
+
Amount mismatches (journal vs gateway):`);
|
|
97025
|
+
for (const m of mismatched.slice(0, 10)) {
|
|
97026
|
+
out.push(
|
|
97027
|
+
` ${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)}`
|
|
97028
|
+
);
|
|
97029
|
+
}
|
|
97030
|
+
if (mismatched.length > 10) out.push(` \u2026and ${mismatched.length - 10} more`);
|
|
97031
|
+
}
|
|
97032
|
+
if (r2.chargedNotRecorded.length > 0) {
|
|
97033
|
+
const total = r2.chargedNotRecorded.reduce((s3, x) => s3 + (x.costUsd ?? 0), 0);
|
|
97034
|
+
out.push(
|
|
97035
|
+
`
|
|
97036
|
+
\u26A0 Charged but NOT in this machine's journal: ${r2.chargedNotRecorded.length} call(s), ${usd(total)}`
|
|
97037
|
+
);
|
|
97038
|
+
out.push(
|
|
97039
|
+
` Expected if the same key is used elsewhere \u2014 another machine, another BlockRun product,`
|
|
97040
|
+
);
|
|
97041
|
+
out.push(` or a call made before this proxy started recording request ids.`);
|
|
97042
|
+
for (const x of r2.chargedNotRecorded.slice(0, 10)) {
|
|
97043
|
+
out.push(
|
|
97044
|
+
` ${x.requestId.slice(0, 8)} ${x.timestamp.slice(0, 19)} ${x.endpoint.padEnd(26)} ${usd(x.costUsd ?? 0)}`
|
|
97045
|
+
);
|
|
97046
|
+
}
|
|
97047
|
+
if (r2.chargedNotRecorded.length > 10) {
|
|
97048
|
+
out.push(` \u2026and ${r2.chargedNotRecorded.length - 10} more`);
|
|
97049
|
+
}
|
|
97050
|
+
}
|
|
97051
|
+
if (r2.recordedNotCharged.length > 0) {
|
|
97052
|
+
out.push(
|
|
97053
|
+
`
|
|
97054
|
+
Recorded locally with no settled ledger row: ${r2.recordedNotCharged.length} call(s)`
|
|
97055
|
+
);
|
|
97056
|
+
out.push(` Usually benign \u2014 free models, cache hits, and calls still pending pricing.`);
|
|
97057
|
+
}
|
|
97058
|
+
if (r2.pending.length > 0) {
|
|
97059
|
+
out.push(`
|
|
97060
|
+
Pending pricing (excluded from totals): ${r2.pending.length} call(s)`);
|
|
97061
|
+
out.push(` Usage recorded, charge not final. These can still be repriced.`);
|
|
97062
|
+
}
|
|
97063
|
+
if (r2.unkeyedLocalCount > 0) {
|
|
97064
|
+
out.push(`
|
|
97065
|
+
Journal entries with no request id: ${r2.unkeyedLocalCount} (not reconcilable)`);
|
|
97066
|
+
}
|
|
97067
|
+
if (r2.unavailableDays.length > 0) {
|
|
97068
|
+
out.push(`
|
|
97069
|
+
\u26A0 Gateway could not list these days: ${r2.unavailableDays.join(", ")}`);
|
|
97070
|
+
out.push(` Totals above are incomplete for that period.`);
|
|
97071
|
+
}
|
|
97072
|
+
out.push(`
|
|
97073
|
+
Note: the top-up fee (5.5% + $0.30) is charged at purchase, not per call,`);
|
|
97074
|
+
out.push(` so the sum above is less than your card was charged by exactly those fees.
|
|
97075
|
+
`);
|
|
97076
|
+
return out.join("\n");
|
|
97077
|
+
}
|
|
97078
|
+
var LOG_DIR3, CENT_EPSILON, usd;
|
|
97079
|
+
var init_reconcile = __esm({
|
|
97080
|
+
"src/reconcile.ts"() {
|
|
97081
|
+
"use strict";
|
|
97082
|
+
init_api_key();
|
|
97083
|
+
LOG_DIR3 = join12(homedir9(), ".openclaw", "blockrun", "logs");
|
|
97084
|
+
CENT_EPSILON = 1e-9;
|
|
97085
|
+
usd = (n) => Math.abs(n) < 0.01 ? `$${n.toFixed(6)}` : `$${n.toFixed(2)}`;
|
|
97086
|
+
}
|
|
97087
|
+
});
|
|
97088
|
+
|
|
96682
97089
|
// src/provider.ts
|
|
96683
97090
|
function setActiveProxy(proxy) {
|
|
96684
97091
|
activeProxy = proxy;
|
|
@@ -112521,7 +112928,7 @@ var init_readBlob = __esm({
|
|
|
112521
112928
|
|
|
112522
112929
|
// node_modules/axios/lib/helpers/formDataToStream.js
|
|
112523
112930
|
import util2 from "util";
|
|
112524
|
-
import { Readable } from "stream";
|
|
112931
|
+
import { Readable as Readable2 } from "stream";
|
|
112525
112932
|
var BOUNDARY_ALPHABET, textEncoder, CRLF, CRLF_BYTES, CRLF_BYTES_COUNT, FormDataPart, formDataToStream, formDataToStream_default;
|
|
112526
112933
|
var init_formDataToStream = __esm({
|
|
112527
112934
|
"node_modules/axios/lib/helpers/formDataToStream.js"() {
|
|
@@ -112601,7 +113008,7 @@ var init_formDataToStream = __esm({
|
|
|
112601
113008
|
computedHeaders["Content-Length"] = contentLength;
|
|
112602
113009
|
}
|
|
112603
113010
|
headersHandler && headersHandler(computedHeaders);
|
|
112604
|
-
return
|
|
113011
|
+
return Readable2.from(
|
|
112605
113012
|
(async function* () {
|
|
112606
113013
|
for (const part of parts) {
|
|
112607
113014
|
yield boundaryBytes;
|
|
@@ -117665,8 +118072,8 @@ var init_constants3 = __esm({
|
|
|
117665
118072
|
|
|
117666
118073
|
// src/polymarket/wallet-adapter.ts
|
|
117667
118074
|
import fs2 from "fs";
|
|
117668
|
-
import { homedir as
|
|
117669
|
-
import { join as
|
|
118075
|
+
import { homedir as homedir10 } from "os";
|
|
118076
|
+
import { join as join13 } from "path";
|
|
117670
118077
|
function getOrCreateWalletKey() {
|
|
117671
118078
|
const envKey = process.env.BLOCKRUN_WALLET_KEY?.trim();
|
|
117672
118079
|
if (envKey && /^0x[0-9a-fA-F]{64}$/.test(envKey)) return envKey;
|
|
@@ -117714,7 +118121,7 @@ var init_wallet_adapter = __esm({
|
|
|
117714
118121
|
init_esm();
|
|
117715
118122
|
init_chains();
|
|
117716
118123
|
init_constants3();
|
|
117717
|
-
WALLET_FILE2 =
|
|
118124
|
+
WALLET_FILE2 = join13(homedir10(), ".openclaw", "blockrun", "wallet.key");
|
|
117718
118125
|
BASE_RPC_URLS = [
|
|
117719
118126
|
"https://mainnet.base.org",
|
|
117720
118127
|
"https://base.llamarpc.com",
|
|
@@ -118118,8 +118525,8 @@ var init_client5 = __esm({
|
|
|
118118
118525
|
function resolveSpendControl(deps) {
|
|
118119
118526
|
return deps?.spendControl ?? getSharedSpendControl();
|
|
118120
118527
|
}
|
|
118121
|
-
function usdToMicros(
|
|
118122
|
-
return String(Math.ceil(
|
|
118528
|
+
function usdToMicros(usd2) {
|
|
118529
|
+
return String(Math.ceil(usd2 * 1e6));
|
|
118123
118530
|
}
|
|
118124
118531
|
async function signUnderSpendPolicy(deps, quote, action, sign4) {
|
|
118125
118532
|
const control = resolveSpendControl(deps);
|
|
@@ -118148,13 +118555,13 @@ function getSessionLedger() {
|
|
|
118148
118555
|
perAgent: Object.fromEntries(ledger.perAgent)
|
|
118149
118556
|
};
|
|
118150
118557
|
}
|
|
118151
|
-
function reserveBet(
|
|
118152
|
-
ledger.totalUsd +=
|
|
118153
|
-
if (agentId) ledger.perAgent.set(agentId, (ledger.perAgent.get(agentId) ?? 0) +
|
|
118558
|
+
function reserveBet(usd2, agentId) {
|
|
118559
|
+
ledger.totalUsd += usd2;
|
|
118560
|
+
if (agentId) ledger.perAgent.set(agentId, (ledger.perAgent.get(agentId) ?? 0) + usd2);
|
|
118154
118561
|
}
|
|
118155
|
-
function releaseBet(
|
|
118156
|
-
ledger.totalUsd -=
|
|
118157
|
-
if (agentId) ledger.perAgent.set(agentId, (ledger.perAgent.get(agentId) ?? 0) -
|
|
118562
|
+
function releaseBet(usd2, agentId) {
|
|
118563
|
+
ledger.totalUsd -= usd2;
|
|
118564
|
+
if (agentId) ledger.perAgent.set(agentId, (ledger.perAgent.get(agentId) ?? 0) - usd2);
|
|
118158
118565
|
}
|
|
118159
118566
|
function commitBet() {
|
|
118160
118567
|
ledger.count += 1;
|
|
@@ -228116,9 +228523,9 @@ import {
|
|
|
228116
228523
|
unlinkSync
|
|
228117
228524
|
} from "fs";
|
|
228118
228525
|
import { readFile as readFileAsync } from "fs/promises";
|
|
228119
|
-
import { homedir as
|
|
228526
|
+
import { homedir as homedir15 } from "os";
|
|
228120
228527
|
import { randomUUID } from "crypto";
|
|
228121
|
-
import { join as
|
|
228528
|
+
import { join as join18 } from "path";
|
|
228122
228529
|
async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
228123
228530
|
const start = Date.now();
|
|
228124
228531
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -228160,8 +228567,8 @@ function isBlockrunWebSearchDisabled(config) {
|
|
|
228160
228567
|
return cfg.tools?.web?.search?.enabled === false;
|
|
228161
228568
|
}
|
|
228162
228569
|
function injectModelsConfig(logger48, options = {}) {
|
|
228163
|
-
const configDir =
|
|
228164
|
-
const configPath =
|
|
228570
|
+
const configDir = join18(homedir15(), ".openclaw");
|
|
228571
|
+
const configPath = join18(configDir, "openclaw.json");
|
|
228165
228572
|
let config = {};
|
|
228166
228573
|
let needsWrite = false;
|
|
228167
228574
|
if (!existsSync4(configDir)) {
|
|
@@ -228382,7 +228789,7 @@ function injectModelsConfig(logger48, options = {}) {
|
|
|
228382
228789
|
}
|
|
228383
228790
|
function syncAgentModelCache(logger48, options = {}) {
|
|
228384
228791
|
if (!isGatewayMode() && !options.forceWrite) return;
|
|
228385
|
-
const agentsDir =
|
|
228792
|
+
const agentsDir = join18(homedir15(), ".openclaw", "agents");
|
|
228386
228793
|
if (!existsSync4(agentsDir)) return;
|
|
228387
228794
|
let agentDirs;
|
|
228388
228795
|
try {
|
|
@@ -228392,7 +228799,7 @@ function syncAgentModelCache(logger48, options = {}) {
|
|
|
228392
228799
|
}
|
|
228393
228800
|
const expectedIds = VISIBLE_OPENCLAW_MODELS.map((m) => m.id);
|
|
228394
228801
|
for (const agent of agentDirs) {
|
|
228395
|
-
const cachePath =
|
|
228802
|
+
const cachePath = join18(agentsDir, agent, "agent", "models.json");
|
|
228396
228803
|
if (!existsSync4(cachePath)) continue;
|
|
228397
228804
|
try {
|
|
228398
228805
|
const raw = readTextFileSync(cachePath).trim();
|
|
@@ -228421,7 +228828,7 @@ function syncAgentModelCache(logger48, options = {}) {
|
|
|
228421
228828
|
}
|
|
228422
228829
|
}
|
|
228423
228830
|
function injectAuthProfile(logger48) {
|
|
228424
|
-
const agentsDir =
|
|
228831
|
+
const agentsDir = join18(homedir15(), ".openclaw", "agents");
|
|
228425
228832
|
if (!existsSync4(agentsDir)) {
|
|
228426
228833
|
try {
|
|
228427
228834
|
mkdirSync3(agentsDir, { recursive: true });
|
|
@@ -228438,9 +228845,9 @@ function injectAuthProfile(logger48) {
|
|
|
228438
228845
|
agents = ["main", ...agents];
|
|
228439
228846
|
}
|
|
228440
228847
|
for (const agentId of agents) {
|
|
228441
|
-
const authDir =
|
|
228442
|
-
const authPath =
|
|
228443
|
-
const sqlitePath =
|
|
228848
|
+
const authDir = join18(agentsDir, agentId, "agent");
|
|
228849
|
+
const authPath = join18(authDir, "auth-profiles.json");
|
|
228850
|
+
const sqlitePath = join18(authDir, "openclaw-agent.sqlite");
|
|
228444
228851
|
if (existsSync4(sqlitePath)) {
|
|
228445
228852
|
removeInjectedAuthPlaceholder(authPath, logger48, agentId);
|
|
228446
228853
|
continue;
|
|
@@ -228588,12 +228995,12 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
228588
228995
|
proc.__clawrouterStartupPhase = "starting";
|
|
228589
228996
|
}
|
|
228590
228997
|
const pluginApiKey = api.pluginConfig?.apiKey;
|
|
228591
|
-
|
|
228592
|
-
|
|
228593
|
-
|
|
228594
|
-
`pluginConfig.apiKey is set but invalid (expected brk_...) \u2014 ignoring it and looking elsewhere`
|
|
228998
|
+
if (typeof pluginApiKey === "string" && pluginApiKey.trim() && !isValidApiKey(pluginApiKey)) {
|
|
228999
|
+
throw new Error(
|
|
229000
|
+
`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
229001
|
);
|
|
228596
229002
|
}
|
|
229003
|
+
const resolvedApiKey = typeof pluginApiKey === "string" && isValidApiKey(pluginApiKey) ? { key: pluginApiKey.trim(), source: "config" } : await resolveApiKey();
|
|
228597
229004
|
const apiKey = resolvedApiKey?.key;
|
|
228598
229005
|
const configKey = api.pluginConfig?.walletKey;
|
|
228599
229006
|
let wallet;
|
|
@@ -228943,7 +229350,7 @@ function buildImageGenerationProvider() {
|
|
|
228943
229350
|
(result.data ?? []).map(async (img) => {
|
|
228944
229351
|
const filename = img.url?.split("/images/").pop();
|
|
228945
229352
|
if (!filename) throw new Error(`Unexpected image URL format: ${img.url}`);
|
|
228946
|
-
const filePath =
|
|
229353
|
+
const filePath = join18(IMAGE_DIR2, filename);
|
|
228947
229354
|
const buffer2 = await readFileAsync(filePath);
|
|
228948
229355
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "png";
|
|
228949
229356
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
|
|
@@ -228995,7 +229402,7 @@ function buildMusicGenerationProvider() {
|
|
|
228995
229402
|
(result.data ?? []).map(async (track) => {
|
|
228996
229403
|
const filename = track.url?.split("/audio/").pop();
|
|
228997
229404
|
if (!filename) throw new Error(`Unexpected audio URL format: ${track.url}`);
|
|
228998
|
-
const filePath =
|
|
229405
|
+
const filePath = join18(AUDIO_DIR2, filename);
|
|
228999
229406
|
const buffer2 = await readFileAsync(filePath);
|
|
229000
229407
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "mp3";
|
|
229001
229408
|
const mimeType = ext === "wav" ? "audio/wav" : "audio/mpeg";
|
|
@@ -229071,7 +229478,7 @@ function buildVideoGenerationProvider() {
|
|
|
229071
229478
|
(result.data ?? []).map(async (clip) => {
|
|
229072
229479
|
const filename = clip.url?.split("/videos/").pop();
|
|
229073
229480
|
if (!filename) throw new Error(`Unexpected video URL format: ${clip.url}`);
|
|
229074
|
-
const filePath =
|
|
229481
|
+
const filePath = join18(VIDEO_DIR2, filename);
|
|
229075
229482
|
const buffer2 = await readFileAsync(filePath);
|
|
229076
229483
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "mp4";
|
|
229077
229484
|
const mimeType = ext === "webm" ? "video/webm" : ext === "mov" ? "video/quicktime" : "video/mp4";
|
|
@@ -229436,9 +229843,9 @@ var init_index = __esm({
|
|
|
229436
229843
|
init_partners();
|
|
229437
229844
|
activeProxyHandle = null;
|
|
229438
229845
|
pendingConfiguredStartupApi = null;
|
|
229439
|
-
IMAGE_DIR2 =
|
|
229440
|
-
AUDIO_DIR2 =
|
|
229441
|
-
VIDEO_DIR2 =
|
|
229846
|
+
IMAGE_DIR2 = join18(homedir15(), ".openclaw", "blockrun", "images");
|
|
229847
|
+
AUDIO_DIR2 = join18(homedir15(), ".openclaw", "blockrun", "audio");
|
|
229848
|
+
VIDEO_DIR2 = join18(homedir15(), ".openclaw", "blockrun", "videos");
|
|
229442
229849
|
plugin = {
|
|
229443
229850
|
// NOT "clawrouter". OpenClaw bundles its own plugin under that id since the
|
|
229444
229851
|
// 2026.7.1 line (vendored at dist/extensions/clawrouter, provider `clawrouter/*`,
|
|
@@ -229780,7 +230187,18 @@ ${errText}`
|
|
|
229780
230187
|
if (!isGatewayMode()) {
|
|
229781
230188
|
if (shouldLogRegistration) {
|
|
229782
230189
|
void (async () => {
|
|
229783
|
-
|
|
230190
|
+
let keyResolution;
|
|
230191
|
+
try {
|
|
230192
|
+
keyResolution = await resolveApiKey();
|
|
230193
|
+
} catch (error) {
|
|
230194
|
+
api.logger.error(
|
|
230195
|
+
`BlockRun API key is configured but unusable: ${error instanceof Error ? error.message : String(error)}`
|
|
230196
|
+
);
|
|
230197
|
+
api.logger.error(
|
|
230198
|
+
`Not falling back to a wallet \u2014 fix the key, or run "clawrouter logout" to pay from the wallet deliberately.`
|
|
230199
|
+
);
|
|
230200
|
+
return;
|
|
230201
|
+
}
|
|
229784
230202
|
if (keyResolution) {
|
|
229785
230203
|
api.logger.info(
|
|
229786
230204
|
`Using BlockRun API key ${maskApiKey(keyResolution.key)} (from ${keyResolution.source}) \u2014 billing account credit, no wallet`
|
|
@@ -229863,7 +230281,7 @@ ${errText}`
|
|
|
229863
230281
|
}
|
|
229864
230282
|
resetProxyStartupState();
|
|
229865
230283
|
try {
|
|
229866
|
-
const configPath =
|
|
230284
|
+
const configPath = join18(homedir15(), ".openclaw", "openclaw.json");
|
|
229867
230285
|
if (existsSync4(configPath)) {
|
|
229868
230286
|
const config = JSON.parse(readTextFileSync(configPath));
|
|
229869
230287
|
if (config.models?.providers?.blockrun) {
|
|
@@ -229905,11 +230323,11 @@ ${errText}`
|
|
|
229905
230323
|
api.logger.warn(`Config cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
229906
230324
|
}
|
|
229907
230325
|
try {
|
|
229908
|
-
const agentsDir =
|
|
230326
|
+
const agentsDir = join18(homedir15(), ".openclaw", "agents");
|
|
229909
230327
|
if (existsSync4(agentsDir)) {
|
|
229910
230328
|
for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
229911
230329
|
if (!entry.isDirectory()) continue;
|
|
229912
|
-
const authPath =
|
|
230330
|
+
const authPath = join18(agentsDir, entry.name, "agent", "auth-profiles.json");
|
|
229913
230331
|
if (!existsSync4(authPath)) continue;
|
|
229914
230332
|
try {
|
|
229915
230333
|
const store = JSON.parse(readTextFileSync(authPath));
|
|
@@ -230725,6 +231143,27 @@ Use: clawrouter share <id> --as=<feishu|slack|discord|telegram|whatsapp|plain>
|
|
|
230725
231143
|
process.exit(1);
|
|
230726
231144
|
}
|
|
230727
231145
|
}
|
|
231146
|
+
async function cmdReconcile(days) {
|
|
231147
|
+
const resolved = await resolveApiKey().catch((error) => {
|
|
231148
|
+
console.error(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
|
|
231149
|
+
process.exit(1);
|
|
231150
|
+
});
|
|
231151
|
+
if (!resolved) {
|
|
231152
|
+
console.error(`\u2717 Reconciliation needs a BlockRun API key \u2014 it compares against your account`);
|
|
231153
|
+
console.error(` ledger. Wallet spend settles on-chain instead; see "clawrouter stats".`);
|
|
231154
|
+
console.error(` Sign in at ${PORTAL_URL} and run: clawrouter login brk_live_...`);
|
|
231155
|
+
process.exit(1);
|
|
231156
|
+
}
|
|
231157
|
+
try {
|
|
231158
|
+
const { reconcile: reconcile2, formatReconcile: formatReconcile2 } = await Promise.resolve().then(() => (init_reconcile(), reconcile_exports));
|
|
231159
|
+
const result = await reconcile2(resolved.key, days);
|
|
231160
|
+
console.log(formatReconcile2(result, days));
|
|
231161
|
+
process.exitCode = result.chargedNotRecorded.length > 0 ? 2 : 0;
|
|
231162
|
+
} catch (error) {
|
|
231163
|
+
console.error(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
|
|
231164
|
+
process.exit(1);
|
|
231165
|
+
}
|
|
231166
|
+
}
|
|
230728
231167
|
async function cmdStatus(port) {
|
|
230729
231168
|
try {
|
|
230730
231169
|
const data = await queryProxy("/health?full=true", port);
|
|
@@ -231002,8 +231441,8 @@ async function cmdSetup() {
|
|
|
231002
231441
|
unlinkSync: unlinkSync2,
|
|
231003
231442
|
writeFileSync: writeFileSync4
|
|
231004
231443
|
} = await import("fs");
|
|
231005
|
-
const { dirname: dirname4, join:
|
|
231006
|
-
const { homedir:
|
|
231444
|
+
const { dirname: dirname4, join: join19 } = await import("path");
|
|
231445
|
+
const { homedir: homedir16 } = await import("os");
|
|
231007
231446
|
const { injectModelsConfig: injectModelsConfig2, injectAuthProfile: injectAuthProfile2, syncAgentModelCache: syncAgentModelCache2, VISIBLE_OPENCLAW_MODELS: VISIBLE_OPENCLAW_MODELS2 } = await Promise.resolve().then(() => (init_index(), index_exports));
|
|
231008
231447
|
const { BLOCKRUN_PLUGIN_ID: BLOCKRUN_PLUGIN_ID2, prepareBlockRunPluginConfig: prepareBlockRunPluginConfig2 } = await Promise.resolve().then(() => (init_openclaw_plugin_config(), openclaw_plugin_config_exports));
|
|
231009
231448
|
console.log("\u{1F99E} ClawRouter setup\n");
|
|
@@ -231018,19 +231457,19 @@ async function cmdSetup() {
|
|
|
231018
231457
|
} catch {
|
|
231019
231458
|
}
|
|
231020
231459
|
if (process.env.npm_config_prefix) {
|
|
231021
|
-
candidates.push(
|
|
231460
|
+
candidates.push(join19(process.env.npm_config_prefix, "bin", "openclaw"));
|
|
231022
231461
|
}
|
|
231023
231462
|
try {
|
|
231024
231463
|
const npmRoot = execSync("npm root -g", {
|
|
231025
231464
|
encoding: "utf8",
|
|
231026
231465
|
stdio: ["ignore", "pipe", "ignore"]
|
|
231027
231466
|
}).trim();
|
|
231028
|
-
if (npmRoot) candidates.push(
|
|
231467
|
+
if (npmRoot) candidates.push(join19(dirname4(dirname4(npmRoot)), "bin", "openclaw"));
|
|
231029
231468
|
} catch {
|
|
231030
231469
|
}
|
|
231031
231470
|
candidates.push(
|
|
231032
|
-
|
|
231033
|
-
|
|
231471
|
+
join19(homedir16(), ".npm-global", "bin", "openclaw"),
|
|
231472
|
+
join19(homedir16(), ".local", "bin", "openclaw"),
|
|
231034
231473
|
"/usr/local/bin/openclaw"
|
|
231035
231474
|
);
|
|
231036
231475
|
return candidates.find((candidate) => existsSync5(candidate)) ?? "";
|
|
@@ -231042,7 +231481,7 @@ async function cmdSetup() {
|
|
|
231042
231481
|
process.exit(1);
|
|
231043
231482
|
}
|
|
231044
231483
|
console.log(` \u2713 Found openclaw at ${openclawPath}`);
|
|
231045
|
-
const configPath =
|
|
231484
|
+
const configPath = join19(homedir16(), ".openclaw", "openclaw.json");
|
|
231046
231485
|
const configInitiallyExisted = existsSync5(configPath);
|
|
231047
231486
|
const configRollbackBackup = configInitiallyExisted ? `${configPath}.before-${BLOCKRUN_PLUGIN_ID2}.${Date.now()}` : void 0;
|
|
231048
231487
|
if (configRollbackBackup) copyFileSync2(configPath, configRollbackBackup);
|
|
@@ -231050,23 +231489,23 @@ async function cmdSetup() {
|
|
|
231050
231489
|
return configRollbackBackup;
|
|
231051
231490
|
};
|
|
231052
231491
|
let installAttempted = false;
|
|
231053
|
-
const openclawRoot =
|
|
231492
|
+
const openclawRoot = join19(homedir16(), ".openclaw");
|
|
231054
231493
|
const findManagedBlockRunInstalls = () => {
|
|
231055
231494
|
const candidates = [
|
|
231056
|
-
|
|
231057
|
-
|
|
231058
|
-
|
|
231495
|
+
join19(openclawRoot, "extensions", BLOCKRUN_PLUGIN_ID2),
|
|
231496
|
+
join19(openclawRoot, "extensions", "clawrouter"),
|
|
231497
|
+
join19(openclawRoot, "npm", "node_modules", "@blockrun", "clawrouter")
|
|
231059
231498
|
];
|
|
231060
|
-
const projectsDir =
|
|
231499
|
+
const projectsDir = join19(openclawRoot, "npm", "projects");
|
|
231061
231500
|
try {
|
|
231062
231501
|
for (const project of readdirSync2(projectsDir)) {
|
|
231063
|
-
candidates.push(
|
|
231502
|
+
candidates.push(join19(projectsDir, project, "node_modules", "@blockrun", "clawrouter"));
|
|
231064
231503
|
}
|
|
231065
231504
|
} catch {
|
|
231066
231505
|
}
|
|
231067
231506
|
return [...new Set(candidates)].filter((candidate) => {
|
|
231068
231507
|
try {
|
|
231069
|
-
const metadata = JSON.parse(readFileSync4(
|
|
231508
|
+
const metadata = JSON.parse(readFileSync4(join19(candidate, "package.json"), "utf8"));
|
|
231070
231509
|
return metadata.name === "@blockrun/clawrouter";
|
|
231071
231510
|
} catch {
|
|
231072
231511
|
return false;
|
|
@@ -231074,7 +231513,7 @@ async function cmdSetup() {
|
|
|
231074
231513
|
});
|
|
231075
231514
|
};
|
|
231076
231515
|
const initialPluginDirs = findManagedBlockRunInstalls();
|
|
231077
|
-
const setupRollbackRoot =
|
|
231516
|
+
const setupRollbackRoot = join19(
|
|
231078
231517
|
openclawRoot,
|
|
231079
231518
|
"blockrun",
|
|
231080
231519
|
"setup-rollbacks",
|
|
@@ -231084,7 +231523,7 @@ async function cmdSetup() {
|
|
|
231084
231523
|
if (initialPluginDirs.length > 0) {
|
|
231085
231524
|
mkdirSync4(setupRollbackRoot, { recursive: true, mode: 448 });
|
|
231086
231525
|
for (const [index2, pluginDir] of initialPluginDirs.entries()) {
|
|
231087
|
-
const backup =
|
|
231526
|
+
const backup = join19(setupRollbackRoot, String(index2));
|
|
231088
231527
|
cpSync(pluginDir, backup, { recursive: true });
|
|
231089
231528
|
pluginRollbackBackups.set(pluginDir, backup);
|
|
231090
231529
|
}
|
|
@@ -231129,8 +231568,8 @@ async function cmdSetup() {
|
|
|
231129
231568
|
if (existsSync5(configPath)) {
|
|
231130
231569
|
try {
|
|
231131
231570
|
const config = JSON.parse(readFileSync4(configPath, "utf8"));
|
|
231132
|
-
const legacyPackagePath =
|
|
231133
|
-
|
|
231571
|
+
const legacyPackagePath = join19(
|
|
231572
|
+
homedir16(),
|
|
231134
231573
|
".openclaw",
|
|
231135
231574
|
"extensions",
|
|
231136
231575
|
"clawrouter",
|
|
@@ -231301,6 +231740,8 @@ function parseArgs(args) {
|
|
|
231301
231740
|
queryModels: false,
|
|
231302
231741
|
queryStats: false,
|
|
231303
231742
|
queryStatsDays: 7,
|
|
231743
|
+
reconcile: false,
|
|
231744
|
+
reconcileDays: 7,
|
|
231304
231745
|
queryCache: false,
|
|
231305
231746
|
setup: false,
|
|
231306
231747
|
share: false,
|
|
@@ -231335,6 +231776,12 @@ function parseArgs(args) {
|
|
|
231335
231776
|
result.queryStatsDays = Math.min(parseInt(args[i + 2], 10) || 7, 30);
|
|
231336
231777
|
i += 2;
|
|
231337
231778
|
}
|
|
231779
|
+
} else if (arg === "reconcile") {
|
|
231780
|
+
result.reconcile = true;
|
|
231781
|
+
if (args[i + 1] === "--days" && args[i + 2]) {
|
|
231782
|
+
result.reconcileDays = Math.min(parseInt(args[i + 2], 10) || 7, 90);
|
|
231783
|
+
i += 2;
|
|
231784
|
+
}
|
|
231338
231785
|
} else if (arg === "cache") {
|
|
231339
231786
|
result.queryCache = true;
|
|
231340
231787
|
} else if (arg === "doctor" || arg === "--doctor") {
|
|
@@ -231560,6 +232007,10 @@ async function main() {
|
|
|
231560
232007
|
await cmdStats(queryPort, args.queryStatsDays);
|
|
231561
232008
|
process.exit(0);
|
|
231562
232009
|
}
|
|
232010
|
+
if (args.reconcile) {
|
|
232011
|
+
await cmdReconcile(args.reconcileDays);
|
|
232012
|
+
process.exit(0);
|
|
232013
|
+
}
|
|
231563
232014
|
if (args.queryCache) {
|
|
231564
232015
|
await cmdCache(queryPort);
|
|
231565
232016
|
process.exit(0);
|