@blockrun/clawrouter 0.12.270 → 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 +43 -7
- package/dist/cli.js +700 -127
- package/dist/index.d.ts +17 -0
- package/dist/index.js +385 -74
- package/docs/configuration.md +17 -1
- package/package.json +1 -1
- package/skills/surf/SKILL.md +32 -5
package/dist/cli.js
CHANGED
|
@@ -40713,6 +40713,18 @@ function isBalanceError(error) {
|
|
|
40713
40713
|
function isRpcError(error) {
|
|
40714
40714
|
return error instanceof Error && error.code === "RPC_ERROR";
|
|
40715
40715
|
}
|
|
40716
|
+
function describeFetchError(error) {
|
|
40717
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
40718
|
+
const cause = error?.cause;
|
|
40719
|
+
if (!(cause instanceof Error)) return message;
|
|
40720
|
+
const detail = cause;
|
|
40721
|
+
const host = detail.hostname || detail.address;
|
|
40722
|
+
const where = host && detail.port ? `${host}:${detail.port}` : host;
|
|
40723
|
+
const parts = [detail.code, where, detail.code ? void 0 : detail.message].filter(
|
|
40724
|
+
(part) => typeof part === "string" && part !== ""
|
|
40725
|
+
);
|
|
40726
|
+
return parts.length > 0 ? `${message} (${parts.join(" ")})` : message;
|
|
40727
|
+
}
|
|
40716
40728
|
var InsufficientFundsError2, EmptyWalletError, RpcError2;
|
|
40717
40729
|
var init_errors5 = __esm({
|
|
40718
40730
|
"src/errors.ts"() {
|
|
@@ -55984,7 +55996,7 @@ var require_websocket = __commonJS({
|
|
|
55984
55996
|
var net3 = __require("net");
|
|
55985
55997
|
var tls2 = __require("tls");
|
|
55986
55998
|
var { randomBytes: randomBytes9, createHash: createHash4 } = __require("crypto");
|
|
55987
|
-
var { Duplex, Readable:
|
|
55999
|
+
var { Duplex, Readable: Readable3 } = __require("stream");
|
|
55988
56000
|
var { URL: URL3 } = __require("url");
|
|
55989
56001
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
55990
56002
|
var Receiver2 = require_receiver();
|
|
@@ -60107,16 +60119,20 @@ function maskApiKey(key2) {
|
|
|
60107
60119
|
async function readOptional2(path5) {
|
|
60108
60120
|
try {
|
|
60109
60121
|
return (await readTextFile(path5)).trim() || void 0;
|
|
60110
|
-
} catch {
|
|
60111
|
-
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
|
+
);
|
|
60112
60128
|
}
|
|
60113
60129
|
}
|
|
60114
60130
|
async function resolveApiKey() {
|
|
60115
60131
|
const envKey = process["env"].BLOCKRUN_API_KEY?.trim();
|
|
60116
60132
|
if (envKey) {
|
|
60117
60133
|
if (isValidApiKey(envKey)) return { key: envKey, source: "env" };
|
|
60118
|
-
|
|
60119
|
-
`
|
|
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.`
|
|
60120
60136
|
);
|
|
60121
60137
|
}
|
|
60122
60138
|
for (const [path5, source] of [
|
|
@@ -60126,8 +60142,8 @@ async function resolveApiKey() {
|
|
|
60126
60142
|
const stored = await readOptional2(path5);
|
|
60127
60143
|
if (stored === void 0) continue;
|
|
60128
60144
|
if (isValidApiKey(stored)) return { key: stored, source };
|
|
60129
|
-
|
|
60130
|
-
|
|
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.`
|
|
60131
60147
|
);
|
|
60132
60148
|
}
|
|
60133
60149
|
return void 0;
|
|
@@ -60152,16 +60168,129 @@ async function clearApiKey() {
|
|
|
60152
60168
|
}
|
|
60153
60169
|
return { removed, envStillSet: Boolean(process["env"].BLOCKRUN_API_KEY?.trim()) };
|
|
60154
60170
|
}
|
|
60155
|
-
function
|
|
60171
|
+
async function fetchCreditBalance(apiKey, timeoutMs = 5e3) {
|
|
60172
|
+
try {
|
|
60173
|
+
const controller = new AbortController();
|
|
60174
|
+
const timer2 = setTimeout(() => controller.abort(), timeoutMs);
|
|
60175
|
+
const res = await fetch(`${BLOCKRUN_API_KEY_API}/v1/credits`, {
|
|
60176
|
+
headers: { authorization: `Bearer ${apiKey}` },
|
|
60177
|
+
signal: controller.signal
|
|
60178
|
+
});
|
|
60179
|
+
clearTimeout(timer2);
|
|
60180
|
+
if (!res.ok) return void 0;
|
|
60181
|
+
const b = await res.json();
|
|
60182
|
+
const num2 = (v) => typeof v === "number" ? v : null;
|
|
60183
|
+
return {
|
|
60184
|
+
accountId: typeof b.account_id === "string" ? b.account_id : void 0,
|
|
60185
|
+
billingMode: typeof b.billing_mode === "string" ? b.billing_mode : void 0,
|
|
60186
|
+
currency: typeof b.currency === "string" ? b.currency : "USD",
|
|
60187
|
+
grantedUsd: num2(b.granted_usd),
|
|
60188
|
+
spentUsd: num2(b.spent_usd),
|
|
60189
|
+
remainingUsd: num2(b.remaining_usd),
|
|
60190
|
+
blocked: b.blocked === true,
|
|
60191
|
+
blockedReason: typeof b.blocked_reason === "string" ? b.blocked_reason : void 0
|
|
60192
|
+
};
|
|
60193
|
+
} catch {
|
|
60194
|
+
return void 0;
|
|
60195
|
+
}
|
|
60196
|
+
}
|
|
60197
|
+
function formatCreditBalance(b) {
|
|
60198
|
+
const money = (v) => v === null ? void 0 : `$${v.toFixed(v < 0.01 && v > 0 ? 6 : 2)}`;
|
|
60199
|
+
const remaining = money(b.remainingUsd);
|
|
60200
|
+
const spent = money(b.spentUsd);
|
|
60201
|
+
if (remaining) return `${remaining} remaining${spent ? ` (spent ${spent})` : ""}`;
|
|
60202
|
+
const mode = b.billingMode === "ungated" ? "no prepaid limit" : b.billingMode ?? "unknown";
|
|
60203
|
+
return `${spent ? `spent ${spent}` : "unknown"} \u2014 ${mode}`;
|
|
60204
|
+
}
|
|
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));
|
|
60156
60251
|
return async (input, init2) => {
|
|
60157
|
-
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
|
+
}
|
|
60158
60264
|
headers.set("authorization", `Bearer ${apiKey}`);
|
|
60159
|
-
|
|
60160
|
-
|
|
60161
|
-
|
|
60265
|
+
const response = await baseFetch(request2 ? new Request(url2, request2) : url2.href, {
|
|
60266
|
+
...init2,
|
|
60267
|
+
headers,
|
|
60268
|
+
redirect: "error"
|
|
60269
|
+
});
|
|
60162
60270
|
return response.ok ? response : explainApiKeyFailure(response);
|
|
60163
60271
|
};
|
|
60164
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
|
+
}
|
|
60165
60294
|
async function explainApiKeyFailure(response) {
|
|
60166
60295
|
const hint = HINTS[response.status];
|
|
60167
60296
|
if (!hint) return response;
|
|
@@ -60195,7 +60324,7 @@ var init_api_key = __esm({
|
|
|
60195
60324
|
PORTAL_URL = "https://user.blockrun.ai";
|
|
60196
60325
|
PORTAL_KEYS_URL = `${PORTAL_URL}/dashboard/keys`;
|
|
60197
60326
|
PORTAL_CREDITS_URL = `${PORTAL_URL}/dashboard/credits`;
|
|
60198
|
-
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";
|
|
60199
60328
|
HINTS = {
|
|
60200
60329
|
401: `Check BLOCKRUN_API_KEY, or mint a new key at ${PORTAL_KEYS_URL}.`,
|
|
60201
60330
|
402: `Top up your BlockRun credit at ${PORTAL_CREDITS_URL}.`,
|
|
@@ -69341,7 +69470,7 @@ var require_client_h2 = __commonJS({
|
|
|
69341
69470
|
"node_modules/undici/lib/dispatcher/client-h2.js"(exports, module) {
|
|
69342
69471
|
"use strict";
|
|
69343
69472
|
var assert10 = __require("assert");
|
|
69344
|
-
var { pipeline } = __require("stream");
|
|
69473
|
+
var { pipeline: pipeline2 } = __require("stream");
|
|
69345
69474
|
var util5 = require_util();
|
|
69346
69475
|
var {
|
|
69347
69476
|
RequestContentLengthMismatchError,
|
|
@@ -70488,7 +70617,7 @@ var require_client_h2 = __commonJS({
|
|
|
70488
70617
|
}
|
|
70489
70618
|
function writeStream(abort, socket, expectsPayload, h2stream, body, client, request2, contentLength) {
|
|
70490
70619
|
assert10(contentLength !== 0 || client[kRunning] === 0, "stream body cannot be pipelined");
|
|
70491
|
-
const pipe2 =
|
|
70620
|
+
const pipe2 = pipeline2(
|
|
70492
70621
|
body,
|
|
70493
70622
|
h2stream,
|
|
70494
70623
|
(err) => {
|
|
@@ -73643,7 +73772,7 @@ var require_readable = __commonJS({
|
|
|
73643
73772
|
"use strict";
|
|
73644
73773
|
var assert10 = __require("assert");
|
|
73645
73774
|
var { addAbortListener } = __require("events");
|
|
73646
|
-
var { Readable:
|
|
73775
|
+
var { Readable: Readable3 } = __require("stream");
|
|
73647
73776
|
var { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = require_errors();
|
|
73648
73777
|
var util5 = require_util();
|
|
73649
73778
|
var { ReadableStreamFrom } = require_util();
|
|
@@ -73657,7 +73786,7 @@ var require_readable = __commonJS({
|
|
|
73657
73786
|
var kBytesRead = /* @__PURE__ */ Symbol("kBytesRead");
|
|
73658
73787
|
var noop2 = () => {
|
|
73659
73788
|
};
|
|
73660
|
-
var BodyReadable = class extends
|
|
73789
|
+
var BodyReadable = class extends Readable3 {
|
|
73661
73790
|
/**
|
|
73662
73791
|
* @param {object} opts
|
|
73663
73792
|
* @param {(this: Readable, size: number) => void} opts.resume
|
|
@@ -74056,7 +74185,7 @@ var require_api_request = __commonJS({
|
|
|
74056
74185
|
"use strict";
|
|
74057
74186
|
var assert10 = __require("assert");
|
|
74058
74187
|
var { AsyncResource } = __require("async_hooks");
|
|
74059
|
-
var { Readable:
|
|
74188
|
+
var { Readable: Readable3 } = require_readable();
|
|
74060
74189
|
var { InvalidArgumentError, RequestAbortedError } = require_errors();
|
|
74061
74190
|
var util5 = require_util();
|
|
74062
74191
|
function noop2() {
|
|
@@ -74142,7 +74271,7 @@ var require_api_request = __commonJS({
|
|
|
74142
74271
|
const parsedHeaders = headers;
|
|
74143
74272
|
const contentType = parsedHeaders?.["content-type"];
|
|
74144
74273
|
const contentLength = parsedHeaders?.["content-length"];
|
|
74145
|
-
const res = new
|
|
74274
|
+
const res = new Readable3({
|
|
74146
74275
|
resume: () => controller.resume(),
|
|
74147
74276
|
abort: (reason) => controller.abort(reason),
|
|
74148
74277
|
contentType,
|
|
@@ -74518,7 +74647,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74518
74647
|
"node_modules/undici/lib/api/api-pipeline.js"(exports, module) {
|
|
74519
74648
|
"use strict";
|
|
74520
74649
|
var {
|
|
74521
|
-
Readable:
|
|
74650
|
+
Readable: Readable3,
|
|
74522
74651
|
Duplex,
|
|
74523
74652
|
PassThrough
|
|
74524
74653
|
} = __require("stream");
|
|
@@ -74535,7 +74664,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74535
74664
|
function noop2() {
|
|
74536
74665
|
}
|
|
74537
74666
|
var kResume = /* @__PURE__ */ Symbol("resume");
|
|
74538
|
-
var PipelineRequest = class extends
|
|
74667
|
+
var PipelineRequest = class extends Readable3 {
|
|
74539
74668
|
constructor() {
|
|
74540
74669
|
super({ autoDestroy: true });
|
|
74541
74670
|
this[kResume] = null;
|
|
@@ -74553,7 +74682,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74553
74682
|
callback(err);
|
|
74554
74683
|
}
|
|
74555
74684
|
};
|
|
74556
|
-
var PipelineResponse = class extends
|
|
74685
|
+
var PipelineResponse = class extends Readable3 {
|
|
74557
74686
|
constructor(resume2) {
|
|
74558
74687
|
super({ autoDestroy: true });
|
|
74559
74688
|
this[kResume] = resume2;
|
|
@@ -74707,7 +74836,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74707
74836
|
util5.destroy(ret, err);
|
|
74708
74837
|
}
|
|
74709
74838
|
};
|
|
74710
|
-
function
|
|
74839
|
+
function pipeline2(opts, handler) {
|
|
74711
74840
|
try {
|
|
74712
74841
|
const pipelineHandler = new PipelineHandler(opts, handler);
|
|
74713
74842
|
this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler);
|
|
@@ -74716,7 +74845,7 @@ var require_api_pipeline = __commonJS({
|
|
|
74716
74845
|
return new PassThrough().destroy(err);
|
|
74717
74846
|
}
|
|
74718
74847
|
}
|
|
74719
|
-
module.exports =
|
|
74848
|
+
module.exports = pipeline2;
|
|
74720
74849
|
}
|
|
74721
74850
|
});
|
|
74722
74851
|
|
|
@@ -76352,7 +76481,7 @@ var require_snapshot_utils = __commonJS({
|
|
|
76352
76481
|
var require_snapshot_recorder = __commonJS({
|
|
76353
76482
|
"node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
76354
76483
|
"use strict";
|
|
76355
|
-
var { writeFile: writeFile4, readFile:
|
|
76484
|
+
var { writeFile: writeFile4, readFile: readFile4, mkdir: mkdir6 } = __require("fs/promises");
|
|
76356
76485
|
var { dirname: dirname4, resolve } = __require("path");
|
|
76357
76486
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("timers");
|
|
76358
76487
|
var { InvalidArgumentError, UndiciError } = require_errors();
|
|
@@ -76569,7 +76698,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
76569
76698
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
76570
76699
|
}
|
|
76571
76700
|
try {
|
|
76572
|
-
const data = await
|
|
76701
|
+
const data = await readFile4(resolve(path5), "utf8");
|
|
76573
76702
|
const parsed = JSON.parse(data);
|
|
76574
76703
|
if (Array.isArray(parsed)) {
|
|
76575
76704
|
this.#snapshots.clear();
|
|
@@ -79834,7 +79963,7 @@ var require_cache2 = __commonJS({
|
|
|
79834
79963
|
"node_modules/undici/lib/interceptor/cache.js"(exports, module) {
|
|
79835
79964
|
"use strict";
|
|
79836
79965
|
var assert10 = __require("assert");
|
|
79837
|
-
var { Readable:
|
|
79966
|
+
var { Readable: Readable3 } = __require("stream");
|
|
79838
79967
|
var util5 = require_util();
|
|
79839
79968
|
var CacheHandler = require_cache_handler();
|
|
79840
79969
|
var MemoryCacheStore = require_memory_cache_store();
|
|
@@ -80010,7 +80139,7 @@ var require_cache2 = __commonJS({
|
|
|
80010
80139
|
return dispatch(opts, new CacheHandler(globalOpts, cacheKey2, handler));
|
|
80011
80140
|
}
|
|
80012
80141
|
function sendCachedValue(handler, opts, result, age, context, isStale2) {
|
|
80013
|
-
const stream4 = util5.isStream(result.body) ? result.body :
|
|
80142
|
+
const stream4 = util5.isStream(result.body) ? result.body : Readable3.from(result.body ?? []);
|
|
80014
80143
|
assert10(!stream4.destroyed, "stream should not be destroyed");
|
|
80015
80144
|
assert10(!stream4.readableDidRead, "stream should not be readableDidRead");
|
|
80016
80145
|
const controller = {
|
|
@@ -80255,7 +80384,7 @@ var require_decompress = __commonJS({
|
|
|
80255
80384
|
"node_modules/undici/lib/interceptor/decompress.js"(exports, module) {
|
|
80256
80385
|
"use strict";
|
|
80257
80386
|
var { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = __require("zlib");
|
|
80258
|
-
var { pipeline } = __require("stream");
|
|
80387
|
+
var { pipeline: pipeline2 } = __require("stream");
|
|
80259
80388
|
var DecoratorHandler = require_decorator_handler();
|
|
80260
80389
|
var supportedEncodings = {
|
|
80261
80390
|
gzip: createGunzip,
|
|
@@ -80365,7 +80494,7 @@ var require_decompress = __commonJS({
|
|
|
80365
80494
|
#setupMultipleDecompressors(controller) {
|
|
80366
80495
|
const lastDecompressor = this.#decompressors[this.#decompressors.length - 1];
|
|
80367
80496
|
this.#setupDecompressorEvents(lastDecompressor, controller);
|
|
80368
|
-
|
|
80497
|
+
pipeline2(this.#decompressors, (err) => {
|
|
80369
80498
|
if (err) {
|
|
80370
80499
|
super.onResponseError(controller, err);
|
|
80371
80500
|
return;
|
|
@@ -83159,7 +83288,7 @@ var require_fetch = __commonJS({
|
|
|
83159
83288
|
subresourceSet
|
|
83160
83289
|
} = require_constants4();
|
|
83161
83290
|
var EE = __require("events");
|
|
83162
|
-
var { Readable:
|
|
83291
|
+
var { Readable: Readable3, pipeline: pipeline2, finished: finished2, isErrored, isReadable } = __require("stream");
|
|
83163
83292
|
var { addAbortListener, bufferToLowerCasedHeaderName } = require_util();
|
|
83164
83293
|
var { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require_data_url();
|
|
83165
83294
|
var { getGlobalDispatcher } = require_global2();
|
|
@@ -84135,7 +84264,7 @@ var require_fetch = __commonJS({
|
|
|
84135
84264
|
const headersList = new HeadersList();
|
|
84136
84265
|
appendHeadersListFromResponseHeaders(headersList, headers, rawHeaders);
|
|
84137
84266
|
const location = headersList.get("location", true);
|
|
84138
|
-
this.body = new
|
|
84267
|
+
this.body = new Readable3({ read: () => controller.resume() });
|
|
84139
84268
|
const willFollow = location && request2.redirect === "follow" && redirectStatusSet.has(status);
|
|
84140
84269
|
const decoders = [];
|
|
84141
84270
|
if (request2.method !== "HEAD" && request2.method !== "CONNECT" && !nullBodyStatus.includes(status) && !willFollow) {
|
|
@@ -84183,7 +84312,7 @@ var require_fetch = __commonJS({
|
|
|
84183
84312
|
status,
|
|
84184
84313
|
statusText,
|
|
84185
84314
|
headersList,
|
|
84186
|
-
body: decoders.length ?
|
|
84315
|
+
body: decoders.length ? pipeline2(this.body, ...decoders, (err) => {
|
|
84187
84316
|
if (err) {
|
|
84188
84317
|
this.onResponseError(controller, err);
|
|
84189
84318
|
}
|
|
@@ -87995,7 +88124,7 @@ ${value}`;
|
|
|
87995
88124
|
var require_eventsource = __commonJS({
|
|
87996
88125
|
"node_modules/undici/lib/web/eventsource/eventsource.js"(exports, module) {
|
|
87997
88126
|
"use strict";
|
|
87998
|
-
var { pipeline } = __require("stream");
|
|
88127
|
+
var { pipeline: pipeline2 } = __require("stream");
|
|
87999
88128
|
var { fetching } = require_fetch();
|
|
88000
88129
|
var { webidl } = require_webidl();
|
|
88001
88130
|
var { EventSourceStream } = require_eventsource_stream();
|
|
@@ -88145,7 +88274,7 @@ var require_eventsource = __commonJS({
|
|
|
88145
88274
|
));
|
|
88146
88275
|
}
|
|
88147
88276
|
});
|
|
88148
|
-
|
|
88277
|
+
pipeline2(
|
|
88149
88278
|
response.body.stream,
|
|
88150
88279
|
eventSourceStream,
|
|
88151
88280
|
(error) => {
|
|
@@ -90673,11 +90802,30 @@ var init_client3 = __esm({
|
|
|
90673
90802
|
}
|
|
90674
90803
|
});
|
|
90675
90804
|
|
|
90805
|
+
// node_modules/@x402/svm/dist/esm/v1/index.mjs
|
|
90806
|
+
var v1_exports = {};
|
|
90807
|
+
__export(v1_exports, {
|
|
90808
|
+
ExactSvmSchemeV1: () => ExactSvmSchemeV1,
|
|
90809
|
+
NETWORKS: () => NETWORKS2
|
|
90810
|
+
});
|
|
90811
|
+
var init_v1 = __esm({
|
|
90812
|
+
"node_modules/@x402/svm/dist/esm/v1/index.mjs"() {
|
|
90813
|
+
"use strict";
|
|
90814
|
+
init_chunk_WWACQNRQ();
|
|
90815
|
+
init_chunk_MNNIECS4();
|
|
90816
|
+
init_chunk_WIPN332D();
|
|
90817
|
+
init_chunk_FVTMOTG6();
|
|
90818
|
+
init_chunk_FJ6REVHH();
|
|
90819
|
+
init_chunk_QEBTC3LJ();
|
|
90820
|
+
}
|
|
90821
|
+
});
|
|
90822
|
+
|
|
90676
90823
|
// src/proxy.ts
|
|
90677
90824
|
import { AsyncLocalStorage } from "async_hooks";
|
|
90678
90825
|
import { createHmac } from "crypto";
|
|
90679
90826
|
import { createServer } from "http";
|
|
90680
|
-
import { finished } from "stream";
|
|
90827
|
+
import { finished, Readable } from "stream";
|
|
90828
|
+
import { pipeline } from "stream/promises";
|
|
90681
90829
|
import { homedir as homedir8 } from "os";
|
|
90682
90830
|
import { join as join11 } from "path";
|
|
90683
90831
|
import { mkdir as mkdir5, writeFile as writeFile3, readFile as readFile2, stat as fsStat } from "fs/promises";
|
|
@@ -90686,10 +90834,10 @@ async function loadGatewayCatalog(apiBase, apiKey) {
|
|
|
90686
90834
|
try {
|
|
90687
90835
|
const controller = new AbortController();
|
|
90688
90836
|
const timer2 = setTimeout(() => controller.abort(), GATEWAY_CATALOG_TIMEOUT_MS);
|
|
90689
|
-
const res = await fetch
|
|
90690
|
-
|
|
90691
|
-
|
|
90692
|
-
|
|
90837
|
+
const res = await (apiKey ? createApiKeyFetch(apiKey, fetch, apiBase) : fetch)(
|
|
90838
|
+
`${apiBase}/v1/models`,
|
|
90839
|
+
{ signal: controller.signal }
|
|
90840
|
+
);
|
|
90693
90841
|
clearTimeout(timer2);
|
|
90694
90842
|
if (!res.ok) return;
|
|
90695
90843
|
const body = await res.json();
|
|
@@ -91427,7 +91575,57 @@ function estimateImageCost(model, size5, n = 1) {
|
|
|
91427
91575
|
const pricePerImage = sizePrice ?? pricing.default;
|
|
91428
91576
|
return pricePerImage * n * 1.05;
|
|
91429
91577
|
}
|
|
91430
|
-
|
|
91578
|
+
function blockrunRequestId(response) {
|
|
91579
|
+
if (!response) return void 0;
|
|
91580
|
+
for (const name of ["x-blockrun-request-id", "x-request-id", "request-id"]) {
|
|
91581
|
+
const value = response.headers.get(name)?.trim();
|
|
91582
|
+
if (value) return value;
|
|
91583
|
+
}
|
|
91584
|
+
return void 0;
|
|
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
|
+
}
|
|
91619
|
+
function gatewaySettledCostUsd(response) {
|
|
91620
|
+
const raw = response?.headers.get("x-blockrun-cost-usd");
|
|
91621
|
+
if (raw === null || raw === void 0) return void 0;
|
|
91622
|
+
const trimmed = raw.trim();
|
|
91623
|
+
if (trimmed === "") return void 0;
|
|
91624
|
+
const value = Number(trimmed);
|
|
91625
|
+
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
91626
|
+
return value;
|
|
91627
|
+
}
|
|
91628
|
+
async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPaymentUsd, accountPassthrough = false, authMode = "wallet") {
|
|
91431
91629
|
const startTime = Date.now();
|
|
91432
91630
|
const upstreamUrl = `${apiBase}${req.url}`;
|
|
91433
91631
|
const isBlockrunExa = req.url?.startsWith("/v1/exa/") ?? false;
|
|
@@ -91466,18 +91664,35 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
|
|
|
91466
91664
|
return;
|
|
91467
91665
|
responseHeaders[key2] = value;
|
|
91468
91666
|
});
|
|
91667
|
+
if (accountPassthrough || authMode === "api-key") {
|
|
91668
|
+
responseHeaders["cache-control"] = "no-store";
|
|
91669
|
+
}
|
|
91469
91670
|
res.writeHead(upstream.status, responseHeaders);
|
|
91470
|
-
if (
|
|
91471
|
-
|
|
91472
|
-
|
|
91473
|
-
|
|
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();
|
|
91474
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
|
+
}
|
|
91687
|
+
}
|
|
91688
|
+
res.end();
|
|
91475
91689
|
}
|
|
91476
|
-
res.end();
|
|
91477
91690
|
const latencyMs = Date.now() - startTime;
|
|
91478
91691
|
console.log(`[ClawRouter] ${requestLabel} response: ${upstream.status} (${latencyMs}ms)`);
|
|
91479
|
-
|
|
91480
|
-
const
|
|
91692
|
+
noteRemainingCredit(upstream);
|
|
91693
|
+
const settledCostUsd = gatewaySettledCostUsd(upstream);
|
|
91694
|
+
const paidAmount = settledCostUsd ?? getActualPaymentUsd();
|
|
91695
|
+
const requestCost = settledCostUsd !== void 0 ? settledCostUsd : resolvePhoneTelemetryCost({
|
|
91481
91696
|
paidAmount,
|
|
91482
91697
|
isPhone,
|
|
91483
91698
|
upstreamStatus: upstream.status,
|
|
@@ -91486,6 +91701,7 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
|
|
|
91486
91701
|
});
|
|
91487
91702
|
logUsage({
|
|
91488
91703
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
91704
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
91489
91705
|
model: isBlockrunExa ? "blockrun-exa" : isModalSandbox ? "modal-sandbox" : isPhone ? (req.url ?? "").replace(/^\/v1\//, "").split("?")[0] : isSurf ? (req.url ?? "").replace(/^\/v1\//, "").split("?")[0] : "partner",
|
|
91490
91706
|
tier: isPhone ? "PHONE" : isSurf ? "SURF" : "PARTNER",
|
|
91491
91707
|
cost: requestCost,
|
|
@@ -91577,7 +91793,8 @@ async function startProxy(options) {
|
|
|
91577
91793
|
const walletKey = options.wallet === void 0 ? void 0 : typeof options.wallet === "string" ? options.wallet : options.wallet.key;
|
|
91578
91794
|
const solanaPrivateKeyBytes = options.wallet === void 0 || typeof options.wallet === "string" ? void 0 : options.wallet.solanaPrivateKeyBytes;
|
|
91579
91795
|
const paymentChain = options.paymentChain ?? await resolvePaymentChain();
|
|
91580
|
-
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;
|
|
91581
91798
|
if (authMode === "api-key") {
|
|
91582
91799
|
console.log(`[ClawRouter] Auth: BlockRun API key ${maskApiKey(apiKey)} (${apiBase})`);
|
|
91583
91800
|
console.log(`[ClawRouter] Billing: account credit \u2014 top up at ${PORTAL_CREDITS_URL}`);
|
|
@@ -91677,8 +91894,20 @@ async function startProxy(options) {
|
|
|
91677
91894
|
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await Promise.resolve().then(() => (init_index_node37(), index_node_exports));
|
|
91678
91895
|
const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes2(solanaPrivateKeyBytes);
|
|
91679
91896
|
solanaAddress = solanaSigner.address;
|
|
91897
|
+
const solanaRpcUrl = process["env"].CLAWROUTER_SOLANA_RPC_URL;
|
|
91680
91898
|
registerExactSvmScheme2(x402, { signer: solanaSigner });
|
|
91899
|
+
if (solanaRpcUrl) {
|
|
91900
|
+
const { ExactSvmScheme: ExactSvmScheme2 } = await Promise.resolve().then(() => (init_client3(), client_exports));
|
|
91901
|
+
const { ExactSvmSchemeV1: ExactSvmSchemeV12, NETWORKS: SVM_V1_NETWORKS } = await Promise.resolve().then(() => (init_v1(), v1_exports));
|
|
91902
|
+
x402.register("solana:*", new ExactSvmScheme2(solanaSigner, { rpcUrl: solanaRpcUrl }));
|
|
91903
|
+
for (const network of SVM_V1_NETWORKS) {
|
|
91904
|
+
x402.registerV1(network, new ExactSvmSchemeV12(solanaSigner, { rpcUrl: solanaRpcUrl }));
|
|
91905
|
+
}
|
|
91906
|
+
}
|
|
91681
91907
|
console.log(`[ClawRouter] Solana wallet: ${solanaAddress}`);
|
|
91908
|
+
if (solanaRpcUrl) {
|
|
91909
|
+
console.log(`[ClawRouter] Solana RPC (payment signing + balance): ${solanaRpcUrl}`);
|
|
91910
|
+
}
|
|
91682
91911
|
}
|
|
91683
91912
|
x402?.onAfterPaymentCreation(async (context) => {
|
|
91684
91913
|
if (!context.selectedRequirements.network.startsWith("eip155")) return;
|
|
@@ -91694,7 +91923,7 @@ async function startProxy(options) {
|
|
|
91694
91923
|
if (store) store.amountUsd = amountUsd;
|
|
91695
91924
|
console.log(`[ClawRouter] Payment signed on ${chain3} (${network}) \u2014 $${amountUsd.toFixed(6)}`);
|
|
91696
91925
|
});
|
|
91697
|
-
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, {
|
|
91698
91927
|
skipPreAuth: paymentChain === "solana",
|
|
91699
91928
|
// Per-request cost estimate so pre-auth is only reused when the cached
|
|
91700
91929
|
// payment still covers the (possibly larger) request — BlockRun prices per
|
|
@@ -91778,7 +92007,15 @@ async function startProxy(options) {
|
|
|
91778
92007
|
)
|
|
91779
92008
|
]);
|
|
91780
92009
|
if (authMode === "api-key") {
|
|
91781
|
-
|
|
92010
|
+
const credit = await fetchCreditBalance(apiKey, BALANCE_CHECK_TIMEOUT_MS);
|
|
92011
|
+
response.balance = credit?.remainingUsd ?? null;
|
|
92012
|
+
response.creditSpent = credit?.spentUsd ?? null;
|
|
92013
|
+
response.creditGranted = credit?.grantedUsd ?? null;
|
|
92014
|
+
response.billingMode = credit?.billingMode ?? null;
|
|
92015
|
+
response.creditSummary = credit ? formatCreditBalance(credit) : null;
|
|
92016
|
+
response.blocked = credit?.blocked ?? false;
|
|
92017
|
+
if (credit?.blockedReason) response.blockedReason = credit.blockedReason;
|
|
92018
|
+
response.isEmpty = credit?.remainingUsd !== null && (credit?.remainingUsd ?? 1) <= 0;
|
|
91782
92019
|
response.billing = "BlockRun account credit";
|
|
91783
92020
|
response.topUpUrl = PORTAL_CREDITS_URL;
|
|
91784
92021
|
} else {
|
|
@@ -92190,9 +92427,15 @@ async function startProxy(options) {
|
|
|
92190
92427
|
}
|
|
92191
92428
|
}
|
|
92192
92429
|
}
|
|
92193
|
-
const imgActualCost =
|
|
92430
|
+
const imgActualCost = settledMediaCostUsd(
|
|
92431
|
+
upstream,
|
|
92432
|
+
result,
|
|
92433
|
+
paymentStore.getStore()?.amountUsd,
|
|
92434
|
+
imgCost
|
|
92435
|
+
);
|
|
92194
92436
|
logUsage({
|
|
92195
92437
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92438
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
92196
92439
|
model: imgModel,
|
|
92197
92440
|
tier: "IMAGE",
|
|
92198
92441
|
cost: imgActualCost,
|
|
@@ -92261,12 +92504,15 @@ async function startProxy(options) {
|
|
|
92261
92504
|
return;
|
|
92262
92505
|
}
|
|
92263
92506
|
try {
|
|
92264
|
-
|
|
92507
|
+
let upstream = await payFetch(`${apiBase}/v1/images/image2image`, {
|
|
92265
92508
|
method: "POST",
|
|
92266
92509
|
headers: { "content-type": "application/json", "user-agent": USER_AGENT },
|
|
92267
92510
|
body: reqBody,
|
|
92268
92511
|
signal: clientAbort.signal
|
|
92269
92512
|
});
|
|
92513
|
+
if (authMode === "api-key") {
|
|
92514
|
+
upstream = await pollApiKeyJob(upstream, payFetch, apiBase, clientAbort.signal);
|
|
92515
|
+
}
|
|
92270
92516
|
const text = await upstream.text();
|
|
92271
92517
|
if (!upstream.ok) {
|
|
92272
92518
|
res.writeHead(upstream.status, { "Content-Type": "application/json" });
|
|
@@ -92313,9 +92559,15 @@ async function startProxy(options) {
|
|
|
92313
92559
|
}
|
|
92314
92560
|
}
|
|
92315
92561
|
}
|
|
92316
|
-
const img2imgActualCost =
|
|
92562
|
+
const img2imgActualCost = settledMediaCostUsd(
|
|
92563
|
+
upstream,
|
|
92564
|
+
result,
|
|
92565
|
+
paymentStore.getStore()?.amountUsd,
|
|
92566
|
+
img2imgCost
|
|
92567
|
+
);
|
|
92317
92568
|
logUsage({
|
|
92318
92569
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92570
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
92319
92571
|
model: img2imgModel,
|
|
92320
92572
|
tier: "IMAGE",
|
|
92321
92573
|
cost: img2imgActualCost,
|
|
@@ -92355,12 +92607,15 @@ async function startProxy(options) {
|
|
|
92355
92607
|
} catch {
|
|
92356
92608
|
}
|
|
92357
92609
|
try {
|
|
92358
|
-
|
|
92610
|
+
let upstream = await payFetch(`${apiBase}/v1/audio/generations`, {
|
|
92359
92611
|
method: "POST",
|
|
92360
92612
|
headers: { "content-type": "application/json", "user-agent": USER_AGENT },
|
|
92361
92613
|
body: reqBody,
|
|
92362
92614
|
signal: clientAbort.signal
|
|
92363
92615
|
});
|
|
92616
|
+
if (authMode === "api-key") {
|
|
92617
|
+
upstream = await pollApiKeyJob(upstream, payFetch, apiBase, clientAbort.signal);
|
|
92618
|
+
}
|
|
92364
92619
|
const text = await upstream.text();
|
|
92365
92620
|
if (!upstream.ok) {
|
|
92366
92621
|
res.writeHead(upstream.status, { "Content-Type": "application/json" });
|
|
@@ -92399,9 +92654,15 @@ async function startProxy(options) {
|
|
|
92399
92654
|
}
|
|
92400
92655
|
}
|
|
92401
92656
|
}
|
|
92402
|
-
const audioActualCost =
|
|
92657
|
+
const audioActualCost = settledMediaCostUsd(
|
|
92658
|
+
upstream,
|
|
92659
|
+
result,
|
|
92660
|
+
paymentStore.getStore()?.amountUsd,
|
|
92661
|
+
0.15
|
|
92662
|
+
);
|
|
92403
92663
|
logUsage({
|
|
92404
92664
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92665
|
+
...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
|
|
92405
92666
|
model: audioModel,
|
|
92406
92667
|
tier: "AUDIO",
|
|
92407
92668
|
cost: audioActualCost,
|
|
@@ -92559,7 +92820,12 @@ async function startProxy(options) {
|
|
|
92559
92820
|
}
|
|
92560
92821
|
}
|
|
92561
92822
|
}
|
|
92562
|
-
const videoActualCost =
|
|
92823
|
+
const videoActualCost = settledMediaCostUsd(
|
|
92824
|
+
void 0,
|
|
92825
|
+
submitResult,
|
|
92826
|
+
paymentStore.getStore()?.amountUsd,
|
|
92827
|
+
estimateVideoCost(videoModel, videoDuration, videoHasImageInput)
|
|
92828
|
+
);
|
|
92563
92829
|
logUsage({
|
|
92564
92830
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92565
92831
|
model: videoModel,
|
|
@@ -92593,7 +92859,9 @@ async function startProxy(options) {
|
|
|
92593
92859
|
res,
|
|
92594
92860
|
apiBase,
|
|
92595
92861
|
payFetch,
|
|
92596
|
-
() => paymentStore.getStore()?.amountUsd ?? 0
|
|
92862
|
+
() => paymentStore.getStore()?.amountUsd ?? 0,
|
|
92863
|
+
false,
|
|
92864
|
+
authMode
|
|
92597
92865
|
);
|
|
92598
92866
|
} catch (err) {
|
|
92599
92867
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -92616,6 +92884,33 @@ async function startProxy(options) {
|
|
|
92616
92884
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
92617
92885
|
return;
|
|
92618
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
|
+
}
|
|
92619
92914
|
if (!req.url.includes("/chat/completions")) {
|
|
92620
92915
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
92621
92916
|
res.end(
|
|
@@ -92883,7 +93178,8 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
92883
93178
|
errorBody,
|
|
92884
93179
|
errorStatus: response.status,
|
|
92885
93180
|
isProviderError: category !== null,
|
|
92886
|
-
errorCategory: category ?? void 0
|
|
93181
|
+
errorCategory: category ?? void 0,
|
|
93182
|
+
upstreamRequestId: blockrunRequestId(response)
|
|
92887
93183
|
};
|
|
92888
93184
|
}
|
|
92889
93185
|
const contentType = response.headers.get("content-type") || "";
|
|
@@ -92920,9 +93216,12 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
92920
93216
|
errorCategory: "payment_error"
|
|
92921
93217
|
};
|
|
92922
93218
|
}
|
|
93219
|
+
const networkMsg = describeFetchError(err);
|
|
93220
|
+
const solanaSigningHint = upstreamUrl.startsWith(BLOCKRUN_SOLANA_API) && networkMsg.startsWith("fetch failed") ? " \u2014 on the Solana rail this may be the RPC that signs the payment rather than the gateway; if api.mainnet-beta.solana.com is unreachable from this host, set CLAWROUTER_SOLANA_RPC_URL to your own endpoint" : "";
|
|
93221
|
+
console.error(`[ClawRouter] ${modelId} network error: ${networkMsg}${solanaSigningHint}`);
|
|
92923
93222
|
return {
|
|
92924
93223
|
success: false,
|
|
92925
|
-
errorBody:
|
|
93224
|
+
errorBody: `${networkMsg}${solanaSigningHint}`,
|
|
92926
93225
|
errorStatus: 500,
|
|
92927
93226
|
isProviderError: true
|
|
92928
93227
|
// Network errors are retryable
|
|
@@ -92955,6 +93254,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
92955
93254
|
let accumulatedContent = "";
|
|
92956
93255
|
let responseInputTokens;
|
|
92957
93256
|
let responseOutputTokens;
|
|
93257
|
+
let upstreamRequestId;
|
|
92958
93258
|
let requestHadError = false;
|
|
92959
93259
|
let requestSummaryForStore = "";
|
|
92960
93260
|
const isChatCompletion = req.url?.includes("/chat/completions");
|
|
@@ -93294,9 +93594,15 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
93294
93594
|
responseText = lines.join("\n");
|
|
93295
93595
|
}
|
|
93296
93596
|
console.log(`[ClawRouter] /imagegen success: ${images.length} image(s) generated`);
|
|
93297
|
-
const imagegenActualCost =
|
|
93597
|
+
const imagegenActualCost = settledMediaCostUsd(
|
|
93598
|
+
imageResponse,
|
|
93599
|
+
imageResult,
|
|
93600
|
+
paymentStore.getStore()?.amountUsd,
|
|
93601
|
+
estimateImageCost(imageModel, imageSize, 1)
|
|
93602
|
+
);
|
|
93298
93603
|
logUsage({
|
|
93299
93604
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
93605
|
+
...blockrunRequestId(imageResponse) ? { requestId: blockrunRequestId(imageResponse) } : {},
|
|
93300
93606
|
model: imageModel,
|
|
93301
93607
|
tier: "IMAGE",
|
|
93302
93608
|
cost: imagegenActualCost,
|
|
@@ -93520,9 +93826,15 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
93520
93826
|
responseText = lines.join("\n");
|
|
93521
93827
|
}
|
|
93522
93828
|
console.log(`[ClawRouter] /img2img success: ${images.length} image(s)`);
|
|
93523
|
-
const img2imgActualCost2 =
|
|
93829
|
+
const img2imgActualCost2 = settledMediaCostUsd(
|
|
93830
|
+
img2imgResponse,
|
|
93831
|
+
img2imgResult,
|
|
93832
|
+
paymentStore.getStore()?.amountUsd,
|
|
93833
|
+
estimateImageCost(img2imgModel, img2imgSize, 1)
|
|
93834
|
+
);
|
|
93524
93835
|
logUsage({
|
|
93525
93836
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
93837
|
+
...blockrunRequestId(img2imgResponse) ? { requestId: blockrunRequestId(img2imgResponse) } : {},
|
|
93526
93838
|
model: img2imgModel,
|
|
93527
93839
|
tier: "IMAGE",
|
|
93528
93840
|
cost: img2imgActualCost2,
|
|
@@ -94361,7 +94673,8 @@ data: [DONE]
|
|
|
94361
94673
|
}
|
|
94362
94674
|
lastError = {
|
|
94363
94675
|
body: result.errorBody || "Unknown error",
|
|
94364
|
-
status: result.errorStatus || 500
|
|
94676
|
+
status: result.errorStatus || 500,
|
|
94677
|
+
requestId: result.upstreamRequestId
|
|
94365
94678
|
};
|
|
94366
94679
|
failedAttempts.push({
|
|
94367
94680
|
model: tryModel,
|
|
@@ -94433,7 +94746,8 @@ data: [DONE]
|
|
|
94433
94746
|
}
|
|
94434
94747
|
lastError = {
|
|
94435
94748
|
body: retryResult.errorBody || lastError?.body || "Unknown error",
|
|
94436
|
-
status: retryResult.errorStatus || lastError?.status || 500
|
|
94749
|
+
status: retryResult.errorStatus || lastError?.status || 500,
|
|
94750
|
+
requestId: retryResult.upstreamRequestId ?? lastError?.requestId
|
|
94437
94751
|
};
|
|
94438
94752
|
failedAttempts.push({
|
|
94439
94753
|
model: tryModel,
|
|
@@ -94575,10 +94889,14 @@ data: [DONE]
|
|
|
94575
94889
|
}
|
|
94576
94890
|
}
|
|
94577
94891
|
}
|
|
94892
|
+
upstreamRequestId = blockrunRequestId(upstream);
|
|
94893
|
+
noteRemainingCredit(upstream);
|
|
94578
94894
|
if (!upstream) {
|
|
94579
94895
|
const attemptSummary = failedAttempts.length > 0 ? failedAttempts.map((a) => `${a.model} (${a.reason})`).join(", ") : "unknown";
|
|
94580
94896
|
const structuredMessage = failedAttempts.length > 0 ? `All ${failedAttempts.length} models failed. Tried: ${attemptSummary}` : "All models in fallback chain failed";
|
|
94581
|
-
console.log(
|
|
94897
|
+
console.log(
|
|
94898
|
+
`[ClawRouter] ${structuredMessage}${lastError?.requestId ? ` | gateway request id: ${lastError.requestId}` : ""}`
|
|
94899
|
+
);
|
|
94582
94900
|
const rawErrBody = lastError?.body || structuredMessage;
|
|
94583
94901
|
const errStatus = lastError?.status || 502;
|
|
94584
94902
|
const transformedErr = transformPaymentError(rawErrBody);
|
|
@@ -94616,7 +94934,8 @@ data: [DONE]
|
|
|
94616
94934
|
res.writeHead(errStatus, {
|
|
94617
94935
|
"Content-Type": "application/json",
|
|
94618
94936
|
"x-context-used-kb": String(originalContextSizeKB),
|
|
94619
|
-
"x-context-limit-kb": String(CONTEXT_LIMIT_KB)
|
|
94937
|
+
"x-context-limit-kb": String(CONTEXT_LIMIT_KB),
|
|
94938
|
+
...lastError?.requestId ? { "x-blockrun-request-id": lastError.requestId } : {}
|
|
94620
94939
|
});
|
|
94621
94940
|
res.end(transformedErr);
|
|
94622
94941
|
deduplicator.complete(dedupKey, {
|
|
@@ -95080,12 +95399,24 @@ data: [DONE]
|
|
|
95080
95399
|
maxTokens,
|
|
95081
95400
|
routingProfile ?? void 0
|
|
95082
95401
|
);
|
|
95083
|
-
|
|
95402
|
+
const apiKeyPricing = options.apiKey ? routerOpts.modelPricing.get(logModel) : void 0;
|
|
95403
|
+
if (FREE_MODELS.has(logModel)) {
|
|
95404
|
+
logCost = 0;
|
|
95405
|
+
logSavings = 1;
|
|
95406
|
+
} else if (apiKeyPricing) {
|
|
95407
|
+
const inTokens = responseInputTokens ?? chargedInputTokens;
|
|
95408
|
+
const outTokens = responseOutputTokens ?? 0;
|
|
95409
|
+
logCost = (inTokens * (apiKeyPricing.inputPrice ?? 0) + outTokens * (apiKeyPricing.outputPrice ?? 0)) / 1e6;
|
|
95410
|
+
logSavings = costs.baselineCost > 0 ? Math.max(0, (costs.baselineCost - logCost) / costs.baselineCost) : 0;
|
|
95411
|
+
} else {
|
|
95412
|
+
logCost = costs.costEstimate;
|
|
95413
|
+
logSavings = costs.savings;
|
|
95414
|
+
}
|
|
95084
95415
|
logBaseline = costs.baselineCost;
|
|
95085
|
-
logSavings = FREE_MODELS.has(logModel) ? 1 : costs.savings;
|
|
95086
95416
|
}
|
|
95087
95417
|
const entry = {
|
|
95088
95418
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
95419
|
+
...upstreamRequestId ? { requestId: upstreamRequestId } : {},
|
|
95089
95420
|
model: logModel,
|
|
95090
95421
|
tier: routingDecision?.tier ?? "DIRECT",
|
|
95091
95422
|
cost: logCost,
|
|
@@ -95100,7 +95431,7 @@ data: [DONE]
|
|
|
95100
95431
|
});
|
|
95101
95432
|
}
|
|
95102
95433
|
}
|
|
95103
|
-
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;
|
|
95104
95435
|
var init_proxy = __esm({
|
|
95105
95436
|
"src/proxy.ts"() {
|
|
95106
95437
|
"use strict";
|
|
@@ -95125,6 +95456,7 @@ var init_proxy = __esm({
|
|
|
95125
95456
|
init_api_key();
|
|
95126
95457
|
init_spend_control();
|
|
95127
95458
|
init_compression();
|
|
95459
|
+
init_errors5();
|
|
95128
95460
|
init_version4();
|
|
95129
95461
|
init_session();
|
|
95130
95462
|
init_updater();
|
|
@@ -95361,6 +95693,8 @@ var init_proxy = __esm({
|
|
|
95361
95693
|
"phone/numbers/release": 0,
|
|
95362
95694
|
"voice/call": 0.54
|
|
95363
95695
|
};
|
|
95696
|
+
LOW_CREDIT_USD = 1;
|
|
95697
|
+
lowCreditWarned = false;
|
|
95364
95698
|
}
|
|
95365
95699
|
});
|
|
95366
95700
|
|
|
@@ -96401,13 +96735,13 @@ ${USAGE}`
|
|
|
96401
96735
|
${USAGE}`);
|
|
96402
96736
|
}
|
|
96403
96737
|
if (raw === "clear") return { kind: "limit", window: target, usd: void 0 };
|
|
96404
|
-
const
|
|
96405
|
-
if (!USD.test(raw) || !Number.isFinite(
|
|
96738
|
+
const usd2 = Number(raw);
|
|
96739
|
+
if (!USD.test(raw) || !Number.isFinite(usd2) || usd2 <= 0) {
|
|
96406
96740
|
return fail(
|
|
96407
96741
|
`Rejected amount "${raw}": must be a positive decimal USD value such as 0.10 or 5`
|
|
96408
96742
|
);
|
|
96409
96743
|
}
|
|
96410
|
-
return { kind: "limit", window: target, usd };
|
|
96744
|
+
return { kind: "limit", window: target, usd: usd2 };
|
|
96411
96745
|
}
|
|
96412
96746
|
if (!isListAction(action)) return fail(`Unknown subcommand "${sub}"
|
|
96413
96747
|
${USAGE}`);
|
|
@@ -96552,6 +96886,194 @@ var init_policy = __esm({
|
|
|
96552
96886
|
}
|
|
96553
96887
|
});
|
|
96554
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
|
+
|
|
96555
97077
|
// src/provider.ts
|
|
96556
97078
|
function setActiveProxy(proxy) {
|
|
96557
97079
|
activeProxy = proxy;
|
|
@@ -112394,7 +112916,7 @@ var init_readBlob = __esm({
|
|
|
112394
112916
|
|
|
112395
112917
|
// node_modules/axios/lib/helpers/formDataToStream.js
|
|
112396
112918
|
import util2 from "util";
|
|
112397
|
-
import { Readable } from "stream";
|
|
112919
|
+
import { Readable as Readable2 } from "stream";
|
|
112398
112920
|
var BOUNDARY_ALPHABET, textEncoder, CRLF, CRLF_BYTES, CRLF_BYTES_COUNT, FormDataPart, formDataToStream, formDataToStream_default;
|
|
112399
112921
|
var init_formDataToStream = __esm({
|
|
112400
112922
|
"node_modules/axios/lib/helpers/formDataToStream.js"() {
|
|
@@ -112474,7 +112996,7 @@ var init_formDataToStream = __esm({
|
|
|
112474
112996
|
computedHeaders["Content-Length"] = contentLength;
|
|
112475
112997
|
}
|
|
112476
112998
|
headersHandler && headersHandler(computedHeaders);
|
|
112477
|
-
return
|
|
112999
|
+
return Readable2.from(
|
|
112478
113000
|
(async function* () {
|
|
112479
113001
|
for (const part of parts) {
|
|
112480
113002
|
yield boundaryBytes;
|
|
@@ -117538,8 +118060,8 @@ var init_constants3 = __esm({
|
|
|
117538
118060
|
|
|
117539
118061
|
// src/polymarket/wallet-adapter.ts
|
|
117540
118062
|
import fs2 from "fs";
|
|
117541
|
-
import { homedir as
|
|
117542
|
-
import { join as
|
|
118063
|
+
import { homedir as homedir10 } from "os";
|
|
118064
|
+
import { join as join13 } from "path";
|
|
117543
118065
|
function getOrCreateWalletKey() {
|
|
117544
118066
|
const envKey = process.env.BLOCKRUN_WALLET_KEY?.trim();
|
|
117545
118067
|
if (envKey && /^0x[0-9a-fA-F]{64}$/.test(envKey)) return envKey;
|
|
@@ -117587,7 +118109,7 @@ var init_wallet_adapter = __esm({
|
|
|
117587
118109
|
init_esm();
|
|
117588
118110
|
init_chains();
|
|
117589
118111
|
init_constants3();
|
|
117590
|
-
WALLET_FILE2 =
|
|
118112
|
+
WALLET_FILE2 = join13(homedir10(), ".openclaw", "blockrun", "wallet.key");
|
|
117591
118113
|
BASE_RPC_URLS = [
|
|
117592
118114
|
"https://mainnet.base.org",
|
|
117593
118115
|
"https://base.llamarpc.com",
|
|
@@ -117991,8 +118513,8 @@ var init_client5 = __esm({
|
|
|
117991
118513
|
function resolveSpendControl(deps) {
|
|
117992
118514
|
return deps?.spendControl ?? getSharedSpendControl();
|
|
117993
118515
|
}
|
|
117994
|
-
function usdToMicros(
|
|
117995
|
-
return String(Math.ceil(
|
|
118516
|
+
function usdToMicros(usd2) {
|
|
118517
|
+
return String(Math.ceil(usd2 * 1e6));
|
|
117996
118518
|
}
|
|
117997
118519
|
async function signUnderSpendPolicy(deps, quote, action, sign4) {
|
|
117998
118520
|
const control = resolveSpendControl(deps);
|
|
@@ -118021,13 +118543,13 @@ function getSessionLedger() {
|
|
|
118021
118543
|
perAgent: Object.fromEntries(ledger.perAgent)
|
|
118022
118544
|
};
|
|
118023
118545
|
}
|
|
118024
|
-
function reserveBet(
|
|
118025
|
-
ledger.totalUsd +=
|
|
118026
|
-
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);
|
|
118027
118549
|
}
|
|
118028
|
-
function releaseBet(
|
|
118029
|
-
ledger.totalUsd -=
|
|
118030
|
-
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);
|
|
118031
118553
|
}
|
|
118032
118554
|
function commitBet() {
|
|
118033
118555
|
ledger.count += 1;
|
|
@@ -227989,9 +228511,9 @@ import {
|
|
|
227989
228511
|
unlinkSync
|
|
227990
228512
|
} from "fs";
|
|
227991
228513
|
import { readFile as readFileAsync } from "fs/promises";
|
|
227992
|
-
import { homedir as
|
|
228514
|
+
import { homedir as homedir15 } from "os";
|
|
227993
228515
|
import { randomUUID } from "crypto";
|
|
227994
|
-
import { join as
|
|
228516
|
+
import { join as join18 } from "path";
|
|
227995
228517
|
async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
227996
228518
|
const start = Date.now();
|
|
227997
228519
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -228033,8 +228555,8 @@ function isBlockrunWebSearchDisabled(config) {
|
|
|
228033
228555
|
return cfg.tools?.web?.search?.enabled === false;
|
|
228034
228556
|
}
|
|
228035
228557
|
function injectModelsConfig(logger48, options = {}) {
|
|
228036
|
-
const configDir =
|
|
228037
|
-
const configPath =
|
|
228558
|
+
const configDir = join18(homedir15(), ".openclaw");
|
|
228559
|
+
const configPath = join18(configDir, "openclaw.json");
|
|
228038
228560
|
let config = {};
|
|
228039
228561
|
let needsWrite = false;
|
|
228040
228562
|
if (!existsSync4(configDir)) {
|
|
@@ -228255,7 +228777,7 @@ function injectModelsConfig(logger48, options = {}) {
|
|
|
228255
228777
|
}
|
|
228256
228778
|
function syncAgentModelCache(logger48, options = {}) {
|
|
228257
228779
|
if (!isGatewayMode() && !options.forceWrite) return;
|
|
228258
|
-
const agentsDir =
|
|
228780
|
+
const agentsDir = join18(homedir15(), ".openclaw", "agents");
|
|
228259
228781
|
if (!existsSync4(agentsDir)) return;
|
|
228260
228782
|
let agentDirs;
|
|
228261
228783
|
try {
|
|
@@ -228265,7 +228787,7 @@ function syncAgentModelCache(logger48, options = {}) {
|
|
|
228265
228787
|
}
|
|
228266
228788
|
const expectedIds = VISIBLE_OPENCLAW_MODELS.map((m) => m.id);
|
|
228267
228789
|
for (const agent of agentDirs) {
|
|
228268
|
-
const cachePath =
|
|
228790
|
+
const cachePath = join18(agentsDir, agent, "agent", "models.json");
|
|
228269
228791
|
if (!existsSync4(cachePath)) continue;
|
|
228270
228792
|
try {
|
|
228271
228793
|
const raw = readTextFileSync(cachePath).trim();
|
|
@@ -228294,7 +228816,7 @@ function syncAgentModelCache(logger48, options = {}) {
|
|
|
228294
228816
|
}
|
|
228295
228817
|
}
|
|
228296
228818
|
function injectAuthProfile(logger48) {
|
|
228297
|
-
const agentsDir =
|
|
228819
|
+
const agentsDir = join18(homedir15(), ".openclaw", "agents");
|
|
228298
228820
|
if (!existsSync4(agentsDir)) {
|
|
228299
228821
|
try {
|
|
228300
228822
|
mkdirSync3(agentsDir, { recursive: true });
|
|
@@ -228311,9 +228833,9 @@ function injectAuthProfile(logger48) {
|
|
|
228311
228833
|
agents = ["main", ...agents];
|
|
228312
228834
|
}
|
|
228313
228835
|
for (const agentId of agents) {
|
|
228314
|
-
const authDir =
|
|
228315
|
-
const authPath =
|
|
228316
|
-
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");
|
|
228317
228839
|
if (existsSync4(sqlitePath)) {
|
|
228318
228840
|
removeInjectedAuthPlaceholder(authPath, logger48, agentId);
|
|
228319
228841
|
continue;
|
|
@@ -228461,12 +228983,12 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
228461
228983
|
proc.__clawrouterStartupPhase = "starting";
|
|
228462
228984
|
}
|
|
228463
228985
|
const pluginApiKey = api.pluginConfig?.apiKey;
|
|
228464
|
-
|
|
228465
|
-
|
|
228466
|
-
|
|
228467
|
-
`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.`
|
|
228468
228989
|
);
|
|
228469
228990
|
}
|
|
228991
|
+
const resolvedApiKey = typeof pluginApiKey === "string" && isValidApiKey(pluginApiKey) ? { key: pluginApiKey.trim(), source: "config" } : await resolveApiKey();
|
|
228470
228992
|
const apiKey = resolvedApiKey?.key;
|
|
228471
228993
|
const configKey = api.pluginConfig?.walletKey;
|
|
228472
228994
|
let wallet;
|
|
@@ -228816,7 +229338,7 @@ function buildImageGenerationProvider() {
|
|
|
228816
229338
|
(result.data ?? []).map(async (img) => {
|
|
228817
229339
|
const filename = img.url?.split("/images/").pop();
|
|
228818
229340
|
if (!filename) throw new Error(`Unexpected image URL format: ${img.url}`);
|
|
228819
|
-
const filePath =
|
|
229341
|
+
const filePath = join18(IMAGE_DIR2, filename);
|
|
228820
229342
|
const buffer2 = await readFileAsync(filePath);
|
|
228821
229343
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "png";
|
|
228822
229344
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
|
|
@@ -228868,7 +229390,7 @@ function buildMusicGenerationProvider() {
|
|
|
228868
229390
|
(result.data ?? []).map(async (track) => {
|
|
228869
229391
|
const filename = track.url?.split("/audio/").pop();
|
|
228870
229392
|
if (!filename) throw new Error(`Unexpected audio URL format: ${track.url}`);
|
|
228871
|
-
const filePath =
|
|
229393
|
+
const filePath = join18(AUDIO_DIR2, filename);
|
|
228872
229394
|
const buffer2 = await readFileAsync(filePath);
|
|
228873
229395
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "mp3";
|
|
228874
229396
|
const mimeType = ext === "wav" ? "audio/wav" : "audio/mpeg";
|
|
@@ -228944,7 +229466,7 @@ function buildVideoGenerationProvider() {
|
|
|
228944
229466
|
(result.data ?? []).map(async (clip) => {
|
|
228945
229467
|
const filename = clip.url?.split("/videos/").pop();
|
|
228946
229468
|
if (!filename) throw new Error(`Unexpected video URL format: ${clip.url}`);
|
|
228947
|
-
const filePath =
|
|
229469
|
+
const filePath = join18(VIDEO_DIR2, filename);
|
|
228948
229470
|
const buffer2 = await readFileAsync(filePath);
|
|
228949
229471
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "mp4";
|
|
228950
229472
|
const mimeType = ext === "webm" ? "video/webm" : ext === "mov" ? "video/quicktime" : "video/mp4";
|
|
@@ -229309,9 +229831,9 @@ var init_index = __esm({
|
|
|
229309
229831
|
init_partners();
|
|
229310
229832
|
activeProxyHandle = null;
|
|
229311
229833
|
pendingConfiguredStartupApi = null;
|
|
229312
|
-
IMAGE_DIR2 =
|
|
229313
|
-
AUDIO_DIR2 =
|
|
229314
|
-
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");
|
|
229315
229837
|
plugin = {
|
|
229316
229838
|
// NOT "clawrouter". OpenClaw bundles its own plugin under that id since the
|
|
229317
229839
|
// 2026.7.1 line (vendored at dist/extensions/clawrouter, provider `clawrouter/*`,
|
|
@@ -229653,7 +230175,18 @@ ${errText}`
|
|
|
229653
230175
|
if (!isGatewayMode()) {
|
|
229654
230176
|
if (shouldLogRegistration) {
|
|
229655
230177
|
void (async () => {
|
|
229656
|
-
|
|
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
|
+
}
|
|
229657
230190
|
if (keyResolution) {
|
|
229658
230191
|
api.logger.info(
|
|
229659
230192
|
`Using BlockRun API key ${maskApiKey(keyResolution.key)} (from ${keyResolution.source}) \u2014 billing account credit, no wallet`
|
|
@@ -229736,7 +230269,7 @@ ${errText}`
|
|
|
229736
230269
|
}
|
|
229737
230270
|
resetProxyStartupState();
|
|
229738
230271
|
try {
|
|
229739
|
-
const configPath =
|
|
230272
|
+
const configPath = join18(homedir15(), ".openclaw", "openclaw.json");
|
|
229740
230273
|
if (existsSync4(configPath)) {
|
|
229741
230274
|
const config = JSON.parse(readTextFileSync(configPath));
|
|
229742
230275
|
if (config.models?.providers?.blockrun) {
|
|
@@ -229778,11 +230311,11 @@ ${errText}`
|
|
|
229778
230311
|
api.logger.warn(`Config cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
229779
230312
|
}
|
|
229780
230313
|
try {
|
|
229781
|
-
const agentsDir =
|
|
230314
|
+
const agentsDir = join18(homedir15(), ".openclaw", "agents");
|
|
229782
230315
|
if (existsSync4(agentsDir)) {
|
|
229783
230316
|
for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
229784
230317
|
if (!entry.isDirectory()) continue;
|
|
229785
|
-
const authPath =
|
|
230318
|
+
const authPath = join18(agentsDir, entry.name, "agent", "auth-profiles.json");
|
|
229786
230319
|
if (!existsSync4(authPath)) continue;
|
|
229787
230320
|
try {
|
|
229788
230321
|
const store = JSON.parse(readTextFileSync(authPath));
|
|
@@ -230598,6 +231131,27 @@ Use: clawrouter share <id> --as=<feishu|slack|discord|telegram|whatsapp|plain>
|
|
|
230598
231131
|
process.exit(1);
|
|
230599
231132
|
}
|
|
230600
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
|
+
}
|
|
230601
231155
|
async function cmdStatus(port) {
|
|
230602
231156
|
try {
|
|
230603
231157
|
const data = await queryProxy("/health?full=true", port);
|
|
@@ -230609,6 +231163,12 @@ ClawRouter Status (port ${port})
|
|
|
230609
231163
|
console.log(` Auth: BlockRun API key ${data.apiKey}`);
|
|
230610
231164
|
console.log(` Gateway: ${data.gateway}`);
|
|
230611
231165
|
console.log(` Billing: account credit \u2014 top up at ${PORTAL_CREDITS_URL}`);
|
|
231166
|
+
if (data.creditSummary) console.log(` Credit: ${data.creditSummary}`);
|
|
231167
|
+
if (data.blocked) {
|
|
231168
|
+
console.log(` \u26A0 Account is blocked${data.blockedReason ? `: ${data.blockedReason}` : ""}`);
|
|
231169
|
+
} else if (data.isEmpty) {
|
|
231170
|
+
console.log(` \u26A0 Credit exhausted \u2014 top up at ${PORTAL_CREDITS_URL}`);
|
|
231171
|
+
}
|
|
230612
231172
|
} else {
|
|
230613
231173
|
console.log(` Auth: wallet (x402)`);
|
|
230614
231174
|
console.log(` Payment Chain: ${data.paymentChain}`);
|
|
@@ -230636,6 +231196,7 @@ ClawRouter Account
|
|
|
230636
231196
|
console.log(` Auth: BlockRun API key ${data.apiKey}`);
|
|
230637
231197
|
console.log(` Gateway: ${data.gateway}`);
|
|
230638
231198
|
console.log(` Billing: account credit (no wallet, no chain)`);
|
|
231199
|
+
if (data.creditSummary) console.log(` Credit: ${data.creditSummary}`);
|
|
230639
231200
|
console.log(` Top up: ${PORTAL_CREDITS_URL}`);
|
|
230640
231201
|
console.log(` Usage: ${PORTAL_URL}/dashboard`);
|
|
230641
231202
|
console.log();
|
|
@@ -230868,8 +231429,8 @@ async function cmdSetup() {
|
|
|
230868
231429
|
unlinkSync: unlinkSync2,
|
|
230869
231430
|
writeFileSync: writeFileSync4
|
|
230870
231431
|
} = await import("fs");
|
|
230871
|
-
const { dirname: dirname4, join:
|
|
230872
|
-
const { homedir:
|
|
231432
|
+
const { dirname: dirname4, join: join19 } = await import("path");
|
|
231433
|
+
const { homedir: homedir16 } = await import("os");
|
|
230873
231434
|
const { injectModelsConfig: injectModelsConfig2, injectAuthProfile: injectAuthProfile2, syncAgentModelCache: syncAgentModelCache2, VISIBLE_OPENCLAW_MODELS: VISIBLE_OPENCLAW_MODELS2 } = await Promise.resolve().then(() => (init_index(), index_exports));
|
|
230874
231435
|
const { BLOCKRUN_PLUGIN_ID: BLOCKRUN_PLUGIN_ID2, prepareBlockRunPluginConfig: prepareBlockRunPluginConfig2 } = await Promise.resolve().then(() => (init_openclaw_plugin_config(), openclaw_plugin_config_exports));
|
|
230875
231436
|
console.log("\u{1F99E} ClawRouter setup\n");
|
|
@@ -230884,19 +231445,19 @@ async function cmdSetup() {
|
|
|
230884
231445
|
} catch {
|
|
230885
231446
|
}
|
|
230886
231447
|
if (process.env.npm_config_prefix) {
|
|
230887
|
-
candidates.push(
|
|
231448
|
+
candidates.push(join19(process.env.npm_config_prefix, "bin", "openclaw"));
|
|
230888
231449
|
}
|
|
230889
231450
|
try {
|
|
230890
231451
|
const npmRoot = execSync("npm root -g", {
|
|
230891
231452
|
encoding: "utf8",
|
|
230892
231453
|
stdio: ["ignore", "pipe", "ignore"]
|
|
230893
231454
|
}).trim();
|
|
230894
|
-
if (npmRoot) candidates.push(
|
|
231455
|
+
if (npmRoot) candidates.push(join19(dirname4(dirname4(npmRoot)), "bin", "openclaw"));
|
|
230895
231456
|
} catch {
|
|
230896
231457
|
}
|
|
230897
231458
|
candidates.push(
|
|
230898
|
-
|
|
230899
|
-
|
|
231459
|
+
join19(homedir16(), ".npm-global", "bin", "openclaw"),
|
|
231460
|
+
join19(homedir16(), ".local", "bin", "openclaw"),
|
|
230900
231461
|
"/usr/local/bin/openclaw"
|
|
230901
231462
|
);
|
|
230902
231463
|
return candidates.find((candidate) => existsSync5(candidate)) ?? "";
|
|
@@ -230908,7 +231469,7 @@ async function cmdSetup() {
|
|
|
230908
231469
|
process.exit(1);
|
|
230909
231470
|
}
|
|
230910
231471
|
console.log(` \u2713 Found openclaw at ${openclawPath}`);
|
|
230911
|
-
const configPath =
|
|
231472
|
+
const configPath = join19(homedir16(), ".openclaw", "openclaw.json");
|
|
230912
231473
|
const configInitiallyExisted = existsSync5(configPath);
|
|
230913
231474
|
const configRollbackBackup = configInitiallyExisted ? `${configPath}.before-${BLOCKRUN_PLUGIN_ID2}.${Date.now()}` : void 0;
|
|
230914
231475
|
if (configRollbackBackup) copyFileSync2(configPath, configRollbackBackup);
|
|
@@ -230916,23 +231477,23 @@ async function cmdSetup() {
|
|
|
230916
231477
|
return configRollbackBackup;
|
|
230917
231478
|
};
|
|
230918
231479
|
let installAttempted = false;
|
|
230919
|
-
const openclawRoot =
|
|
231480
|
+
const openclawRoot = join19(homedir16(), ".openclaw");
|
|
230920
231481
|
const findManagedBlockRunInstalls = () => {
|
|
230921
231482
|
const candidates = [
|
|
230922
|
-
|
|
230923
|
-
|
|
230924
|
-
|
|
231483
|
+
join19(openclawRoot, "extensions", BLOCKRUN_PLUGIN_ID2),
|
|
231484
|
+
join19(openclawRoot, "extensions", "clawrouter"),
|
|
231485
|
+
join19(openclawRoot, "npm", "node_modules", "@blockrun", "clawrouter")
|
|
230925
231486
|
];
|
|
230926
|
-
const projectsDir =
|
|
231487
|
+
const projectsDir = join19(openclawRoot, "npm", "projects");
|
|
230927
231488
|
try {
|
|
230928
231489
|
for (const project of readdirSync2(projectsDir)) {
|
|
230929
|
-
candidates.push(
|
|
231490
|
+
candidates.push(join19(projectsDir, project, "node_modules", "@blockrun", "clawrouter"));
|
|
230930
231491
|
}
|
|
230931
231492
|
} catch {
|
|
230932
231493
|
}
|
|
230933
231494
|
return [...new Set(candidates)].filter((candidate) => {
|
|
230934
231495
|
try {
|
|
230935
|
-
const metadata = JSON.parse(readFileSync4(
|
|
231496
|
+
const metadata = JSON.parse(readFileSync4(join19(candidate, "package.json"), "utf8"));
|
|
230936
231497
|
return metadata.name === "@blockrun/clawrouter";
|
|
230937
231498
|
} catch {
|
|
230938
231499
|
return false;
|
|
@@ -230940,7 +231501,7 @@ async function cmdSetup() {
|
|
|
230940
231501
|
});
|
|
230941
231502
|
};
|
|
230942
231503
|
const initialPluginDirs = findManagedBlockRunInstalls();
|
|
230943
|
-
const setupRollbackRoot =
|
|
231504
|
+
const setupRollbackRoot = join19(
|
|
230944
231505
|
openclawRoot,
|
|
230945
231506
|
"blockrun",
|
|
230946
231507
|
"setup-rollbacks",
|
|
@@ -230950,7 +231511,7 @@ async function cmdSetup() {
|
|
|
230950
231511
|
if (initialPluginDirs.length > 0) {
|
|
230951
231512
|
mkdirSync4(setupRollbackRoot, { recursive: true, mode: 448 });
|
|
230952
231513
|
for (const [index2, pluginDir] of initialPluginDirs.entries()) {
|
|
230953
|
-
const backup =
|
|
231514
|
+
const backup = join19(setupRollbackRoot, String(index2));
|
|
230954
231515
|
cpSync(pluginDir, backup, { recursive: true });
|
|
230955
231516
|
pluginRollbackBackups.set(pluginDir, backup);
|
|
230956
231517
|
}
|
|
@@ -230995,8 +231556,8 @@ async function cmdSetup() {
|
|
|
230995
231556
|
if (existsSync5(configPath)) {
|
|
230996
231557
|
try {
|
|
230997
231558
|
const config = JSON.parse(readFileSync4(configPath, "utf8"));
|
|
230998
|
-
const legacyPackagePath =
|
|
230999
|
-
|
|
231559
|
+
const legacyPackagePath = join19(
|
|
231560
|
+
homedir16(),
|
|
231000
231561
|
".openclaw",
|
|
231001
231562
|
"extensions",
|
|
231002
231563
|
"clawrouter",
|
|
@@ -231167,6 +231728,8 @@ function parseArgs(args) {
|
|
|
231167
231728
|
queryModels: false,
|
|
231168
231729
|
queryStats: false,
|
|
231169
231730
|
queryStatsDays: 7,
|
|
231731
|
+
reconcile: false,
|
|
231732
|
+
reconcileDays: 7,
|
|
231170
231733
|
queryCache: false,
|
|
231171
231734
|
setup: false,
|
|
231172
231735
|
share: false,
|
|
@@ -231201,6 +231764,12 @@ function parseArgs(args) {
|
|
|
231201
231764
|
result.queryStatsDays = Math.min(parseInt(args[i + 2], 10) || 7, 30);
|
|
231202
231765
|
i += 2;
|
|
231203
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
|
+
}
|
|
231204
231773
|
} else if (arg === "cache") {
|
|
231205
231774
|
result.queryCache = true;
|
|
231206
231775
|
} else if (arg === "doctor" || arg === "--doctor") {
|
|
@@ -231426,6 +231995,10 @@ async function main() {
|
|
|
231426
231995
|
await cmdStats(queryPort, args.queryStatsDays);
|
|
231427
231996
|
process.exit(0);
|
|
231428
231997
|
}
|
|
231998
|
+
if (args.reconcile) {
|
|
231999
|
+
await cmdReconcile(args.reconcileDays);
|
|
232000
|
+
process.exit(0);
|
|
232001
|
+
}
|
|
231429
232002
|
if (args.queryCache) {
|
|
231430
232003
|
await cmdCache(queryPort);
|
|
231431
232004
|
process.exit(0);
|