@blockrun/clawrouter 0.12.267 → 0.12.269
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 +168 -21
- package/dist/cli.js +804 -204
- package/dist/index.d.ts +86 -12
- package/dist/index.js +465 -136
- package/docs/configuration.md +102 -9
- package/openclaw.plugin.json +9 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -34446,6 +34446,11 @@ function canonicalLimits(limits) {
|
|
|
34446
34446
|
Object.fromEntries(Object.entries(limits).sort(([a], [b]) => a < b ? -1 : 1))
|
|
34447
34447
|
);
|
|
34448
34448
|
}
|
|
34449
|
+
function getSharedSpendControl() {
|
|
34450
|
+
const host = sharedControlHost();
|
|
34451
|
+
host.__clawrouterSharedSpendControl ??= new SpendControl();
|
|
34452
|
+
return host.__clawrouterSharedSpendControl;
|
|
34453
|
+
}
|
|
34449
34454
|
function parseQuotedAmountUsd(selected) {
|
|
34450
34455
|
const raw = selected.amount ?? selected.maxAmountRequired;
|
|
34451
34456
|
if (typeof raw !== "string" || !CANONICAL_AMOUNT.test(raw)) {
|
|
@@ -34521,7 +34526,7 @@ function formatDuration(seconds) {
|
|
|
34521
34526
|
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
|
34522
34527
|
}
|
|
34523
34528
|
}
|
|
34524
|
-
var WALLET_DIR, HOUR_MS, DAY_MS, CAIP2_BASE, CAIP2_SOLANA_MAINNET, PAYABLE_NETWORKS, POLICY_LISTS, ADDRESS_LISTS, EVM_ADDRESS, MalformedSpendPolicyError, SpendPolicyConflictError, FileSpendControlStorage, InMemorySpendControlStorage, RESERVATION_TTL_MS, SpendControl, SpendPolicyError, CANONICAL_AMOUNT;
|
|
34529
|
+
var WALLET_DIR, HOUR_MS, DAY_MS, CAIP2_BASE, CAIP2_SOLANA_MAINNET, PAYABLE_NETWORKS, POLICY_LISTS, ADDRESS_LISTS, EVM_ADDRESS, UnreadableSpendPolicyError, MalformedSpendPolicyError, SpendPolicyConflictError, FileSpendControlStorage, InMemorySpendControlStorage, RESERVATION_TTL_MS, SpendControl, sharedControlHost, SpendPolicyError, CANONICAL_AMOUNT;
|
|
34525
34530
|
var init_spend_control = __esm({
|
|
34526
34531
|
"src/spend-control.ts"() {
|
|
34527
34532
|
"use strict";
|
|
@@ -34540,6 +34545,12 @@ var init_spend_control = __esm({
|
|
|
34540
34545
|
];
|
|
34541
34546
|
ADDRESS_LISTS = ["allowedPayees", "blockedPayees", "allowedAssets"];
|
|
34542
34547
|
EVM_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
34548
|
+
UnreadableSpendPolicyError = class extends Error {
|
|
34549
|
+
constructor(cause) {
|
|
34550
|
+
super(`[ClawRouter] Failed to load spending data: ${cause}`);
|
|
34551
|
+
this.name = "UnreadableSpendPolicyError";
|
|
34552
|
+
}
|
|
34553
|
+
};
|
|
34543
34554
|
MalformedSpendPolicyError = class extends Error {
|
|
34544
34555
|
constructor(key2) {
|
|
34545
34556
|
super(
|
|
@@ -34600,9 +34611,7 @@ var init_spend_control = __esm({
|
|
|
34600
34611
|
if (err instanceof MalformedSpendPolicyError) {
|
|
34601
34612
|
throw err;
|
|
34602
34613
|
}
|
|
34603
|
-
|
|
34604
|
-
`[ClawRouter] Failed to load spending data, starting fresh (any configured spend policy is NOT in effect until this file is repaired): ${err}`
|
|
34605
|
-
);
|
|
34614
|
+
throw new UnreadableSpendPolicyError(err);
|
|
34606
34615
|
}
|
|
34607
34616
|
return null;
|
|
34608
34617
|
}
|
|
@@ -34743,6 +34752,33 @@ var init_spend_control = __esm({
|
|
|
34743
34752
|
getPolicyFileError() {
|
|
34744
34753
|
return this.policyFileBroken;
|
|
34745
34754
|
}
|
|
34755
|
+
/**
|
|
34756
|
+
* Re-read limits from storage, keeping this instance's history and open
|
|
34757
|
+
* reservations. Used on an in-process proxy restart so a hand-edit to
|
|
34758
|
+
* spending.json made while the process was running still applies, without
|
|
34759
|
+
* resetting the rolling windows. Fails closed exactly like the constructor:
|
|
34760
|
+
* a malformed file refuses every payment until repaired, and a repaired
|
|
34761
|
+
* file clears that refusal.
|
|
34762
|
+
*/
|
|
34763
|
+
reloadLimits() {
|
|
34764
|
+
let data;
|
|
34765
|
+
try {
|
|
34766
|
+
data = this.storage.load();
|
|
34767
|
+
} catch (err) {
|
|
34768
|
+
if (err instanceof UnreadableSpendPolicyError) {
|
|
34769
|
+
console.error(`${err.message} \u2014 keeping the limits already in effect`);
|
|
34770
|
+
return;
|
|
34771
|
+
}
|
|
34772
|
+
if (!(err instanceof MalformedSpendPolicyError)) throw err;
|
|
34773
|
+
this.policyFileBroken = err.message;
|
|
34774
|
+
console.error(`[ClawRouter] ${err.message}`);
|
|
34775
|
+
return;
|
|
34776
|
+
}
|
|
34777
|
+
this.policyFileBroken = void 0;
|
|
34778
|
+
this.limits = data ? cloneLimits(data.limits) : {};
|
|
34779
|
+
this.diskLimits = cloneLimits(this.limits);
|
|
34780
|
+
this.limitsDirty = false;
|
|
34781
|
+
}
|
|
34746
34782
|
check(estimatedCost2, counterparty) {
|
|
34747
34783
|
if (this.policyFileBroken !== void 0) {
|
|
34748
34784
|
return {
|
|
@@ -34987,6 +35023,16 @@ var init_spend_control = __esm({
|
|
|
34987
35023
|
const records = [...this.history].reverse();
|
|
34988
35024
|
return limit ? records.slice(0, limit) : records;
|
|
34989
35025
|
}
|
|
35026
|
+
/**
|
|
35027
|
+
* Reset the session window. `sessionSpent`/`sessionCalls` are instance state
|
|
35028
|
+
* that is never persisted, so this used to happen implicitly: every
|
|
35029
|
+
* startProxy() built a fresh SpendControl. The process-wide ledger outlives
|
|
35030
|
+
* an in-process proxy restart, which would silently redefine `session` as
|
|
35031
|
+
* "since the gateway booted" — docs/configuration.md and the /policy help
|
|
35032
|
+
* both promise "session resets on restart". The restart path in index.ts
|
|
35033
|
+
* calls this to keep that promise. History and the rolling hourly/daily
|
|
35034
|
+
* windows are deliberately untouched.
|
|
35035
|
+
*/
|
|
34990
35036
|
resetSession() {
|
|
34991
35037
|
this.sessionSpent = 0;
|
|
34992
35038
|
this.sessionCalls = 0;
|
|
@@ -35008,10 +35054,13 @@ var init_spend_control = __esm({
|
|
|
35008
35054
|
this.storage.saveLimits(cloneLimits(this.limits), cloneLimits(this.diskLimits));
|
|
35009
35055
|
} catch (err) {
|
|
35010
35056
|
if (err instanceof SpendPolicyConflictError) {
|
|
35011
|
-
|
|
35012
|
-
|
|
35013
|
-
|
|
35014
|
-
|
|
35057
|
+
try {
|
|
35058
|
+
const current = this.storage.load();
|
|
35059
|
+
this.limits = cloneLimits(current?.limits ?? {});
|
|
35060
|
+
this.diskLimits = cloneLimits(this.limits);
|
|
35061
|
+
this.limitsDirty = false;
|
|
35062
|
+
} catch {
|
|
35063
|
+
}
|
|
35015
35064
|
}
|
|
35016
35065
|
throw err;
|
|
35017
35066
|
}
|
|
@@ -35029,6 +35078,12 @@ var init_spend_control = __esm({
|
|
|
35029
35078
|
try {
|
|
35030
35079
|
data = this.storage.load();
|
|
35031
35080
|
} catch (err) {
|
|
35081
|
+
if (err instanceof UnreadableSpendPolicyError) {
|
|
35082
|
+
console.error(
|
|
35083
|
+
`${err.message} \u2014 starting fresh (any configured spend policy is NOT in effect until this file is repaired)`
|
|
35084
|
+
);
|
|
35085
|
+
return;
|
|
35086
|
+
}
|
|
35032
35087
|
if (!(err instanceof MalformedSpendPolicyError)) throw err;
|
|
35033
35088
|
this.policyFileBroken = err.message;
|
|
35034
35089
|
console.error(`[ClawRouter] ${err.message}`);
|
|
@@ -35045,6 +35100,7 @@ var init_spend_control = __esm({
|
|
|
35045
35100
|
}
|
|
35046
35101
|
}
|
|
35047
35102
|
};
|
|
35103
|
+
sharedControlHost = () => process;
|
|
35048
35104
|
SpendPolicyError = class extends Error {
|
|
35049
35105
|
blockedBy;
|
|
35050
35106
|
blockedByPolicy;
|
|
@@ -35082,6 +35138,21 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
35082
35138
|
}
|
|
35083
35139
|
}
|
|
35084
35140
|
const cacheKey2 = `${urlPath}:${requestModel}`;
|
|
35141
|
+
const notifyAcceptedPayment = (response2, payload2) => {
|
|
35142
|
+
const settled = response2.headers.has("payment-response") || response2.headers.has("x-payment-response");
|
|
35143
|
+
if (!settled || !options?.onPayment) return;
|
|
35144
|
+
try {
|
|
35145
|
+
options.onPayment({
|
|
35146
|
+
model: requestModel,
|
|
35147
|
+
amount: payload2.accepted.amount,
|
|
35148
|
+
network: payload2.accepted.network
|
|
35149
|
+
});
|
|
35150
|
+
} catch (error) {
|
|
35151
|
+
console.error(
|
|
35152
|
+
`[ClawRouter] onPayment callback failed: ${error instanceof Error ? error.message : String(error)}`
|
|
35153
|
+
);
|
|
35154
|
+
}
|
|
35155
|
+
};
|
|
35085
35156
|
const estimateMicros = () => {
|
|
35086
35157
|
if (!options?.estimateAmount || !requestModel) return void 0;
|
|
35087
35158
|
const est = options.estimateAmount(requestModel, bodyLength, maxTokens);
|
|
@@ -35102,6 +35173,7 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
35102
35173
|
paymentInFlight = true;
|
|
35103
35174
|
const response2 = await baseFetch(preAuthRequest);
|
|
35104
35175
|
if (response2.status !== 402) {
|
|
35176
|
+
notifyAcceptedPayment(response2, payload2);
|
|
35105
35177
|
return response2;
|
|
35106
35178
|
}
|
|
35107
35179
|
paymentInFlight = false;
|
|
@@ -35148,7 +35220,9 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
35148
35220
|
for (const [key2, value] of Object.entries(paymentHeaders)) {
|
|
35149
35221
|
clonedRequest.headers.set(key2, value);
|
|
35150
35222
|
}
|
|
35151
|
-
|
|
35223
|
+
const paidResponse = await baseFetch(clonedRequest);
|
|
35224
|
+
notifyAcceptedPayment(paidResponse, payload);
|
|
35225
|
+
return paidResponse;
|
|
35152
35226
|
};
|
|
35153
35227
|
}
|
|
35154
35228
|
var DEFAULT_TTL_MS;
|
|
@@ -40559,7 +40633,7 @@ var init_errors5 = __esm({
|
|
|
40559
40633
|
});
|
|
40560
40634
|
|
|
40561
40635
|
// src/balance.ts
|
|
40562
|
-
var USDC_BASE, CACHE_TTL_MS, BALANCE_THRESHOLDS, BalanceMonitor;
|
|
40636
|
+
var USDC_BASE, CACHE_TTL_MS, BALANCE_THRESHOLDS, BalanceMonitor, ApiKeyBalanceMonitor;
|
|
40563
40637
|
var init_balance = __esm({
|
|
40564
40638
|
"src/balance.ts"() {
|
|
40565
40639
|
"use strict";
|
|
@@ -40688,6 +40762,38 @@ var init_balance = __esm({
|
|
|
40688
40762
|
};
|
|
40689
40763
|
}
|
|
40690
40764
|
};
|
|
40765
|
+
ApiKeyBalanceMonitor = class _ApiKeyBalanceMonitor {
|
|
40766
|
+
/** Large enough that every `balance >= estimatedCost` comparison passes. */
|
|
40767
|
+
static UNMETERED = BigInt(Number.MAX_SAFE_INTEGER);
|
|
40768
|
+
async checkBalance() {
|
|
40769
|
+
return {
|
|
40770
|
+
balance: _ApiKeyBalanceMonitor.UNMETERED,
|
|
40771
|
+
// Never a dollar figure: no caller may print this as if it were one.
|
|
40772
|
+
balanceUSD: "account credit",
|
|
40773
|
+
isLow: false,
|
|
40774
|
+
isEmpty: false,
|
|
40775
|
+
walletAddress: ""
|
|
40776
|
+
};
|
|
40777
|
+
}
|
|
40778
|
+
async checkSufficient() {
|
|
40779
|
+
return { sufficient: true, info: await this.checkBalance() };
|
|
40780
|
+
}
|
|
40781
|
+
/** No local cache to adjust — the server keeps the books. */
|
|
40782
|
+
deductEstimated() {
|
|
40783
|
+
}
|
|
40784
|
+
invalidate() {
|
|
40785
|
+
}
|
|
40786
|
+
async refresh() {
|
|
40787
|
+
return this.checkBalance();
|
|
40788
|
+
}
|
|
40789
|
+
formatUSDC(amountMicros) {
|
|
40790
|
+
return `$${(Number(amountMicros) / 1e6).toFixed(2)}`;
|
|
40791
|
+
}
|
|
40792
|
+
/** No wallet in this mode; callers render the account instead. */
|
|
40793
|
+
getWalletAddress() {
|
|
40794
|
+
return "";
|
|
40795
|
+
}
|
|
40796
|
+
};
|
|
40691
40797
|
}
|
|
40692
40798
|
});
|
|
40693
40799
|
|
|
@@ -59787,6 +59893,99 @@ var init_auth = __esm({
|
|
|
59787
59893
|
}
|
|
59788
59894
|
});
|
|
59789
59895
|
|
|
59896
|
+
// src/api-key.ts
|
|
59897
|
+
import { writeFile as writeFile2, mkdir as mkdir3, rm } from "fs/promises";
|
|
59898
|
+
import { join as join8 } from "path";
|
|
59899
|
+
import { homedir as homedir5 } from "os";
|
|
59900
|
+
function isValidApiKey(value) {
|
|
59901
|
+
if (typeof value !== "string") return false;
|
|
59902
|
+
return /^brk_[A-Za-z0-9_-]{8,}$/.test(value.trim());
|
|
59903
|
+
}
|
|
59904
|
+
function maskApiKey(key2) {
|
|
59905
|
+
const trimmed = key2.trim();
|
|
59906
|
+
if (trimmed.length <= 18) return `${trimmed.slice(0, 8)}\u2026`;
|
|
59907
|
+
return `${trimmed.slice(0, 14)}\u2026${trimmed.slice(-4)}`;
|
|
59908
|
+
}
|
|
59909
|
+
async function readOptional2(path5) {
|
|
59910
|
+
try {
|
|
59911
|
+
return (await readTextFile(path5)).trim() || void 0;
|
|
59912
|
+
} catch {
|
|
59913
|
+
return void 0;
|
|
59914
|
+
}
|
|
59915
|
+
}
|
|
59916
|
+
async function resolveApiKey() {
|
|
59917
|
+
const envKey = process["env"].BLOCKRUN_API_KEY?.trim();
|
|
59918
|
+
if (envKey) {
|
|
59919
|
+
if (isValidApiKey(envKey)) return { key: envKey, source: "env" };
|
|
59920
|
+
console.warn(
|
|
59921
|
+
`[ClawRouter] \u26A0 BLOCKRUN_API_KEY is set but does not look like a BlockRun key (expected brk_\u2026) \u2014 ignoring.`
|
|
59922
|
+
);
|
|
59923
|
+
}
|
|
59924
|
+
for (const [path5, source] of [
|
|
59925
|
+
[CORE_API_KEY_FILE, "core"],
|
|
59926
|
+
[API_KEY_FILE, "saved"]
|
|
59927
|
+
]) {
|
|
59928
|
+
const stored = await readOptional2(path5);
|
|
59929
|
+
if (stored === void 0) continue;
|
|
59930
|
+
if (isValidApiKey(stored)) return { key: stored, source };
|
|
59931
|
+
console.warn(
|
|
59932
|
+
`[ClawRouter] \u26A0 ${path5} does not contain a BlockRun key (expected brk_\u2026) \u2014 ignoring.`
|
|
59933
|
+
);
|
|
59934
|
+
}
|
|
59935
|
+
return void 0;
|
|
59936
|
+
}
|
|
59937
|
+
function createApiKeyFetch(apiKey, baseFetch = fetch) {
|
|
59938
|
+
return async (input, init2) => {
|
|
59939
|
+
const headers = new Headers(init2?.headers);
|
|
59940
|
+
headers.set("authorization", `Bearer ${apiKey}`);
|
|
59941
|
+
headers.delete("x-payment");
|
|
59942
|
+
headers.delete("x-api-key");
|
|
59943
|
+
const response = await baseFetch(input, { ...init2, headers });
|
|
59944
|
+
return response.ok ? response : explainApiKeyFailure(response);
|
|
59945
|
+
};
|
|
59946
|
+
}
|
|
59947
|
+
async function explainApiKeyFailure(response) {
|
|
59948
|
+
const hint = HINTS[response.status];
|
|
59949
|
+
if (!hint) return response;
|
|
59950
|
+
if (!(response.headers.get("content-type") ?? "").includes("json")) return response;
|
|
59951
|
+
let parsed;
|
|
59952
|
+
try {
|
|
59953
|
+
parsed = await response.clone().json();
|
|
59954
|
+
} catch {
|
|
59955
|
+
return response;
|
|
59956
|
+
}
|
|
59957
|
+
if (!parsed?.error || typeof parsed.error.message !== "string") return response;
|
|
59958
|
+
if (response.status === 404 && !/unsupported endpoint/i.test(parsed.error.message)) {
|
|
59959
|
+
return response;
|
|
59960
|
+
}
|
|
59961
|
+
parsed.error.message = `${parsed.error.message} ${hint}`;
|
|
59962
|
+
const headers = new Headers(response.headers);
|
|
59963
|
+
headers.delete("content-length");
|
|
59964
|
+
return new Response(JSON.stringify(parsed), {
|
|
59965
|
+
status: response.status,
|
|
59966
|
+
statusText: response.statusText,
|
|
59967
|
+
headers
|
|
59968
|
+
});
|
|
59969
|
+
}
|
|
59970
|
+
var API_KEY_FILE, CORE_API_KEY_FILE, PORTAL_URL, PORTAL_KEYS_URL, PORTAL_CREDITS_URL, BLOCKRUN_API_KEY_API, HINTS;
|
|
59971
|
+
var init_api_key = __esm({
|
|
59972
|
+
"src/api-key.ts"() {
|
|
59973
|
+
"use strict";
|
|
59974
|
+
init_fs_read();
|
|
59975
|
+
API_KEY_FILE = join8(homedir5(), ".openclaw", "blockrun", "api-key");
|
|
59976
|
+
CORE_API_KEY_FILE = join8(homedir5(), ".blockrun", ".api-key");
|
|
59977
|
+
PORTAL_URL = "https://user.blockrun.ai";
|
|
59978
|
+
PORTAL_KEYS_URL = `${PORTAL_URL}/dashboard/keys`;
|
|
59979
|
+
PORTAL_CREDITS_URL = `${PORTAL_URL}/dashboard/credits`;
|
|
59980
|
+
BLOCKRUN_API_KEY_API = process["env"].BLOCKRUN_API_BASE_URL?.replace(/\/+$/, "") || "https://api.blockrun.ai";
|
|
59981
|
+
HINTS = {
|
|
59982
|
+
401: `Check BLOCKRUN_API_KEY, or mint a new key at ${PORTAL_KEYS_URL}.`,
|
|
59983
|
+
402: `Top up your BlockRun credit at ${PORTAL_CREDITS_URL}.`,
|
|
59984
|
+
404: `api.blockrun.ai does not serve this endpoint yet \u2014 it currently carries chat and text completions. For media and partner APIs, run ClawRouter in wallet mode (unset BLOCKRUN_API_KEY and run "clawrouter logout").`
|
|
59985
|
+
};
|
|
59986
|
+
}
|
|
59987
|
+
});
|
|
59988
|
+
|
|
59790
59989
|
// src/compression/types.ts
|
|
59791
59990
|
var DEFAULT_COMPRESSION_CONFIG;
|
|
59792
59991
|
var init_types2 = __esm({
|
|
@@ -60876,8 +61075,8 @@ var init_updater = __esm({
|
|
|
60876
61075
|
|
|
60877
61076
|
// src/exclude-models.ts
|
|
60878
61077
|
import { readFileSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, statSync } from "fs";
|
|
60879
|
-
import { join as
|
|
60880
|
-
import { homedir as
|
|
61078
|
+
import { join as join9, dirname as dirname2 } from "path";
|
|
61079
|
+
import { homedir as homedir6 } from "os";
|
|
60881
61080
|
function loadExcludeList(filePath = DEFAULT_FILE_PATH) {
|
|
60882
61081
|
try {
|
|
60883
61082
|
const mtimeMs = statSync(filePath).mtimeMs;
|
|
@@ -60926,7 +61125,7 @@ var init_exclude_models = __esm({
|
|
|
60926
61125
|
"src/exclude-models.ts"() {
|
|
60927
61126
|
"use strict";
|
|
60928
61127
|
init_models();
|
|
60929
|
-
DEFAULT_FILE_PATH =
|
|
61128
|
+
DEFAULT_FILE_PATH = join9(homedir6(), ".openclaw", "blockrun", "exclude-models.json");
|
|
60930
61129
|
loadCache = /* @__PURE__ */ new Map();
|
|
60931
61130
|
}
|
|
60932
61131
|
});
|
|
@@ -75935,7 +76134,7 @@ var require_snapshot_utils = __commonJS({
|
|
|
75935
76134
|
var require_snapshot_recorder = __commonJS({
|
|
75936
76135
|
"node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
75937
76136
|
"use strict";
|
|
75938
|
-
var { writeFile:
|
|
76137
|
+
var { writeFile: writeFile4, readFile: readFile3, mkdir: mkdir6 } = __require("fs/promises");
|
|
75939
76138
|
var { dirname: dirname4, resolve } = __require("path");
|
|
75940
76139
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("timers");
|
|
75941
76140
|
var { InvalidArgumentError, UndiciError } = require_errors();
|
|
@@ -76182,12 +76381,12 @@ var require_snapshot_recorder = __commonJS({
|
|
|
76182
76381
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
76183
76382
|
}
|
|
76184
76383
|
const resolvedPath = resolve(path5);
|
|
76185
|
-
await
|
|
76384
|
+
await mkdir6(dirname4(resolvedPath), { recursive: true });
|
|
76186
76385
|
const data = Array.from(this.#snapshots.entries()).map(([hash5, snapshot]) => ({
|
|
76187
76386
|
hash: hash5,
|
|
76188
76387
|
snapshot
|
|
76189
76388
|
}));
|
|
76190
|
-
await
|
|
76389
|
+
await writeFile4(resolvedPath, JSON.stringify(data, null, 2), { flush: true });
|
|
76191
76390
|
}
|
|
76192
76391
|
/**
|
|
76193
76392
|
* Clears all recorded snapshots
|
|
@@ -88516,9 +88715,9 @@ var init_textual_tool_calls = __esm({
|
|
|
88516
88715
|
});
|
|
88517
88716
|
|
|
88518
88717
|
// src/response-store.ts
|
|
88519
|
-
import { appendFile as appendFile2, mkdir as
|
|
88520
|
-
import { homedir as
|
|
88521
|
-
import { join as
|
|
88718
|
+
import { appendFile as appendFile2, mkdir as mkdir4, readFile, readdir as readdir2 } from "fs/promises";
|
|
88719
|
+
import { homedir as homedir7 } from "os";
|
|
88720
|
+
import { join as join10 } from "path";
|
|
88522
88721
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
88523
88722
|
function isEnabled() {
|
|
88524
88723
|
const v = process.env.BLOCKRUN_RESPONSE_STORE;
|
|
@@ -88526,12 +88725,12 @@ function isEnabled() {
|
|
|
88526
88725
|
}
|
|
88527
88726
|
async function ensureDir2() {
|
|
88528
88727
|
if (dirReady2) return;
|
|
88529
|
-
await
|
|
88728
|
+
await mkdir4(STORE_DIR, { recursive: true });
|
|
88530
88729
|
dirReady2 = true;
|
|
88531
88730
|
}
|
|
88532
88731
|
function dailyFile(date = /* @__PURE__ */ new Date()) {
|
|
88533
88732
|
const iso = date.toISOString().slice(0, 10);
|
|
88534
|
-
return
|
|
88733
|
+
return join10(STORE_DIR, `responses-${iso}.jsonl`);
|
|
88535
88734
|
}
|
|
88536
88735
|
function genId() {
|
|
88537
88736
|
return `resp_${Date.now()}_${randomBytes7(3).toString("hex")}`;
|
|
@@ -88581,7 +88780,7 @@ async function listRecent(limit = 20, daysBack = 7) {
|
|
|
88581
88780
|
d.setUTCDate(d.getUTCDate() - i);
|
|
88582
88781
|
const iso = d.toISOString().slice(0, 10);
|
|
88583
88782
|
const name = `responses-${iso}.jsonl`;
|
|
88584
|
-
if (files.includes(name)) candidateFiles.push(
|
|
88783
|
+
if (files.includes(name)) candidateFiles.push(join10(STORE_DIR, name));
|
|
88585
88784
|
}
|
|
88586
88785
|
const all3 = [];
|
|
88587
88786
|
for (const f of candidateFiles) {
|
|
@@ -88610,7 +88809,7 @@ var STORE_DIR, dirReady2;
|
|
|
88610
88809
|
var init_response_store = __esm({
|
|
88611
88810
|
"src/response-store.ts"() {
|
|
88612
88811
|
"use strict";
|
|
88613
|
-
STORE_DIR =
|
|
88812
|
+
STORE_DIR = join10(homedir7(), ".openclaw", "blockrun", "responses");
|
|
88614
88813
|
dirReady2 = false;
|
|
88615
88814
|
}
|
|
88616
88815
|
});
|
|
@@ -90261,15 +90460,18 @@ import { AsyncLocalStorage } from "async_hooks";
|
|
|
90261
90460
|
import { createHmac } from "crypto";
|
|
90262
90461
|
import { createServer } from "http";
|
|
90263
90462
|
import { finished } from "stream";
|
|
90264
|
-
import { homedir as
|
|
90265
|
-
import { join as
|
|
90266
|
-
import { mkdir as
|
|
90463
|
+
import { homedir as homedir8 } from "os";
|
|
90464
|
+
import { join as join11 } from "path";
|
|
90465
|
+
import { mkdir as mkdir5, writeFile as writeFile3, readFile as readFile2, stat as fsStat } from "fs/promises";
|
|
90267
90466
|
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
90268
|
-
async function loadGatewayCatalog(apiBase) {
|
|
90467
|
+
async function loadGatewayCatalog(apiBase, apiKey) {
|
|
90269
90468
|
try {
|
|
90270
90469
|
const controller = new AbortController();
|
|
90271
90470
|
const timer2 = setTimeout(() => controller.abort(), GATEWAY_CATALOG_TIMEOUT_MS);
|
|
90272
|
-
const res = await fetch(`${apiBase}/v1/models`, {
|
|
90471
|
+
const res = await fetch(`${apiBase}/v1/models`, {
|
|
90472
|
+
signal: controller.signal,
|
|
90473
|
+
...apiKey ? { headers: { authorization: `Bearer ${apiKey}` } } : {}
|
|
90474
|
+
});
|
|
90273
90475
|
clearTimeout(timer2);
|
|
90274
90476
|
if (!res.ok) return;
|
|
90275
90477
|
const body = await res.json();
|
|
@@ -90568,8 +90770,9 @@ async function checkExistingProxy(port) {
|
|
|
90568
90770
|
clearTimeout(timeoutId);
|
|
90569
90771
|
if (response.ok) {
|
|
90570
90772
|
const data = await response.json();
|
|
90571
|
-
|
|
90572
|
-
|
|
90773
|
+
const authMode = data.authMode === "api-key" ? "api-key" : "wallet";
|
|
90774
|
+
if (data.status === "ok" && (data.wallet || authMode === "api-key")) {
|
|
90775
|
+
return { wallet: data.wallet ?? "", paymentChain: data.paymentChain, authMode };
|
|
90573
90776
|
}
|
|
90574
90777
|
}
|
|
90575
90778
|
return void 0;
|
|
@@ -91077,7 +91280,7 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
|
|
|
91077
91280
|
});
|
|
91078
91281
|
}
|
|
91079
91282
|
function readImageFileAsDataUri(filePath) {
|
|
91080
|
-
const resolved = filePath.startsWith("~/") ?
|
|
91283
|
+
const resolved = filePath.startsWith("~/") ? join11(homedir8(), filePath.slice(2)) : filePath;
|
|
91081
91284
|
if (!existsSync2(resolved)) {
|
|
91082
91285
|
throw new Error(`Image file not found: ${resolved}`);
|
|
91083
91286
|
}
|
|
@@ -91120,16 +91323,48 @@ async function uploadDataUriToHost(dataUri) {
|
|
|
91120
91323
|
clearTimeout(uploadTimeout);
|
|
91121
91324
|
}
|
|
91122
91325
|
}
|
|
91326
|
+
function warnIfSpendLimitsUnenforced(spendControl) {
|
|
91327
|
+
let configured;
|
|
91328
|
+
try {
|
|
91329
|
+
const limits = (spendControl ?? new SpendControl()).getStatus().limits;
|
|
91330
|
+
configured = ["perRequest", "hourly", "daily", "session"].filter((window2) => typeof limits[window2] === "number").map((window2) => `${window2}=$${limits[window2].toFixed(2)}`);
|
|
91331
|
+
} catch {
|
|
91332
|
+
return;
|
|
91333
|
+
}
|
|
91334
|
+
if (configured.length === 0) return;
|
|
91335
|
+
console.warn(
|
|
91336
|
+
`[ClawRouter] \u26A0 Spend limits (${configured.join(", ")}) are NOT enforced in API-key mode \u2014 they gate x402 signing, and nothing is signed here.`
|
|
91337
|
+
);
|
|
91338
|
+
console.warn(
|
|
91339
|
+
`[ClawRouter] Your BlockRun account balance is the cap instead. Use maxCostPerRun for a per-session limit that still applies.`
|
|
91340
|
+
);
|
|
91341
|
+
}
|
|
91123
91342
|
async function startProxy(options) {
|
|
91124
91343
|
const upstreamProxy = await applyUpstreamProxy(options.upstreamProxy);
|
|
91125
91344
|
if (upstreamProxy) {
|
|
91126
91345
|
console.log(`[ClawRouter] Upstream proxy: ${upstreamProxy}`);
|
|
91127
91346
|
}
|
|
91128
|
-
const
|
|
91129
|
-
|
|
91347
|
+
const apiKey = options.apiKey?.trim() || void 0;
|
|
91348
|
+
if (apiKey && !isValidApiKey(apiKey)) {
|
|
91349
|
+
throw new Error(
|
|
91350
|
+
`BlockRun API key is malformed (expected it to start with "brk_"). Mint one at https://user.blockrun.ai/dashboard/keys`
|
|
91351
|
+
);
|
|
91352
|
+
}
|
|
91353
|
+
const authMode = apiKey ? "api-key" : "wallet";
|
|
91354
|
+
if (!apiKey && !options.wallet) {
|
|
91355
|
+
throw new Error(
|
|
91356
|
+
`startProxy needs a credential: pass either a wallet (x402) or an apiKey (brk_\u2026, from https://user.blockrun.ai/dashboard/keys).`
|
|
91357
|
+
);
|
|
91358
|
+
}
|
|
91359
|
+
const walletKey = options.wallet === void 0 ? void 0 : typeof options.wallet === "string" ? options.wallet : options.wallet.key;
|
|
91360
|
+
const solanaPrivateKeyBytes = options.wallet === void 0 || typeof options.wallet === "string" ? void 0 : options.wallet.solanaPrivateKeyBytes;
|
|
91130
91361
|
const paymentChain = options.paymentChain ?? await resolvePaymentChain();
|
|
91131
|
-
const apiBase = options.apiBase ?? (paymentChain === "solana" && solanaPrivateKeyBytes ? BLOCKRUN_SOLANA_API : BLOCKRUN_API);
|
|
91132
|
-
if (
|
|
91362
|
+
const apiBase = options.apiBase ?? (authMode === "api-key" ? BLOCKRUN_API_KEY_API : paymentChain === "solana" && solanaPrivateKeyBytes ? BLOCKRUN_SOLANA_API : BLOCKRUN_API);
|
|
91363
|
+
if (authMode === "api-key") {
|
|
91364
|
+
console.log(`[ClawRouter] Auth: BlockRun API key ${maskApiKey(apiKey)} (${apiBase})`);
|
|
91365
|
+
console.log(`[ClawRouter] Billing: account credit \u2014 top up at ${PORTAL_CREDITS_URL}`);
|
|
91366
|
+
warnIfSpendLimitsUnenforced(options.spendControl);
|
|
91367
|
+
} else if (paymentChain === "solana" && !solanaPrivateKeyBytes) {
|
|
91133
91368
|
console.warn(
|
|
91134
91369
|
`[ClawRouter] \u26A0 Payment chain is Solana but no mnemonic found \u2014 falling back to Base (EVM).`
|
|
91135
91370
|
);
|
|
@@ -91140,12 +91375,31 @@ async function startProxy(options) {
|
|
|
91140
91375
|
} else if (paymentChain === "solana") {
|
|
91141
91376
|
console.log(`[ClawRouter] Payment chain: Solana (${BLOCKRUN_SOLANA_API})`);
|
|
91142
91377
|
}
|
|
91143
|
-
void loadGatewayCatalog(apiBase);
|
|
91378
|
+
void loadGatewayCatalog(apiBase, apiKey);
|
|
91144
91379
|
const listenPort = options.port ?? getProxyPort();
|
|
91145
91380
|
const existingProxy = options.allowExistingProxy === false ? void 0 : await checkExistingProxy(listenPort);
|
|
91146
91381
|
if (existingProxy) {
|
|
91147
|
-
const account2 = privateKeyToAccount(walletKey);
|
|
91148
91382
|
const baseUrl2 = `http://127.0.0.1:${listenPort}`;
|
|
91383
|
+
if (existingProxy.authMode !== authMode) {
|
|
91384
|
+
throw new Error(
|
|
91385
|
+
`Existing proxy on port ${listenPort} is authenticating with ${existingProxy.authMode === "api-key" ? "a BlockRun API key" : "a wallet"} but ${authMode === "api-key" ? "an API key" : "a wallet"} was requested. Stop the existing proxy first or use a different port.`
|
|
91386
|
+
);
|
|
91387
|
+
}
|
|
91388
|
+
if (authMode === "api-key") {
|
|
91389
|
+
options.onReady?.(listenPort);
|
|
91390
|
+
return {
|
|
91391
|
+
port: listenPort,
|
|
91392
|
+
baseUrl: baseUrl2,
|
|
91393
|
+
walletAddress: "",
|
|
91394
|
+
authMode,
|
|
91395
|
+
apiKeyLabel: maskApiKey(apiKey),
|
|
91396
|
+
balanceMonitor: new ApiKeyBalanceMonitor(),
|
|
91397
|
+
// No-op: we didn't start this proxy, so we shouldn't close it
|
|
91398
|
+
close: async () => {
|
|
91399
|
+
}
|
|
91400
|
+
};
|
|
91401
|
+
}
|
|
91402
|
+
const account2 = privateKeyToAccount(walletKey);
|
|
91149
91403
|
if (existingProxy.wallet !== account2.address) {
|
|
91150
91404
|
console.warn(
|
|
91151
91405
|
`[ClawRouter] Existing proxy on port ${listenPort} uses wallet ${existingProxy.wallet}, but current config uses ${account2.address}. Reusing existing proxy.`
|
|
@@ -91184,20 +91438,23 @@ async function startProxy(options) {
|
|
|
91184
91438
|
baseUrl: baseUrl2,
|
|
91185
91439
|
walletAddress: existingProxy.wallet,
|
|
91186
91440
|
solanaAddress: reuseSolanaAddress,
|
|
91441
|
+
authMode,
|
|
91187
91442
|
balanceMonitor: balanceMonitor2,
|
|
91188
91443
|
close: async () => {
|
|
91189
91444
|
}
|
|
91190
91445
|
};
|
|
91191
91446
|
}
|
|
91192
|
-
const account = privateKeyToAccount(walletKey);
|
|
91193
|
-
const
|
|
91194
|
-
|
|
91195
|
-
|
|
91196
|
-
|
|
91197
|
-
|
|
91198
|
-
|
|
91447
|
+
const account = walletKey ? privateKeyToAccount(walletKey) : void 0;
|
|
91448
|
+
const x402 = authMode === "wallet" ? new x402Client() : void 0;
|
|
91449
|
+
if (x402 && account) {
|
|
91450
|
+
const evmPublicClient = createPublicClient({ chain: base, transport: http() });
|
|
91451
|
+
const evmSigner = toClientEvmSigner(account, evmPublicClient);
|
|
91452
|
+
const spendControl = options.spendControl ?? getSharedSpendControl();
|
|
91453
|
+
registerSpendPolicyHook(x402, spendControl);
|
|
91454
|
+
registerExactEvmScheme(x402, { signer: evmSigner });
|
|
91455
|
+
}
|
|
91199
91456
|
let solanaAddress;
|
|
91200
|
-
if (solanaPrivateKeyBytes) {
|
|
91457
|
+
if (x402 && solanaPrivateKeyBytes) {
|
|
91201
91458
|
const { registerExactSvmScheme: registerExactSvmScheme2 } = await Promise.resolve().then(() => (init_client3(), client_exports));
|
|
91202
91459
|
const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await Promise.resolve().then(() => (init_index_node37(), index_node_exports));
|
|
91203
91460
|
const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes2(solanaPrivateKeyBytes);
|
|
@@ -91205,12 +91462,12 @@ async function startProxy(options) {
|
|
|
91205
91462
|
registerExactSvmScheme2(x402, { signer: solanaSigner });
|
|
91206
91463
|
console.log(`[ClawRouter] Solana wallet: ${solanaAddress}`);
|
|
91207
91464
|
}
|
|
91208
|
-
x402
|
|
91465
|
+
x402?.onAfterPaymentCreation(async (context) => {
|
|
91209
91466
|
if (!context.selectedRequirements.network.startsWith("eip155")) return;
|
|
91210
91467
|
const payload = context.paymentPayload;
|
|
91211
91468
|
payload.extensions = withBuilderCodeServiceCode(payload.extensions);
|
|
91212
91469
|
});
|
|
91213
|
-
x402
|
|
91470
|
+
x402?.onAfterPaymentCreation(async (context) => {
|
|
91214
91471
|
const network = context.selectedRequirements.network;
|
|
91215
91472
|
const chain3 = network.startsWith("eip155") ? "Base (EVM)" : network.startsWith("solana") ? "Solana" : network;
|
|
91216
91473
|
const amountMicros = parseInt(context.selectedRequirements.amount || "0", 10);
|
|
@@ -91219,16 +91476,22 @@ async function startProxy(options) {
|
|
|
91219
91476
|
if (store) store.amountUsd = amountUsd;
|
|
91220
91477
|
console.log(`[ClawRouter] Payment signed on ${chain3} (${network}) \u2014 $${amountUsd.toFixed(6)}`);
|
|
91221
91478
|
});
|
|
91222
|
-
const payFetch = createPayFetchWithPreAuth(fetch, x402, void 0, {
|
|
91479
|
+
const payFetch = authMode === "api-key" ? createApiKeyFetch(apiKey) : createPayFetchWithPreAuth(fetch, x402, void 0, {
|
|
91223
91480
|
skipPreAuth: paymentChain === "solana",
|
|
91224
91481
|
// Per-request cost estimate so pre-auth is only reused when the cached
|
|
91225
91482
|
// payment still covers the (possibly larger) request — BlockRun prices per
|
|
91226
91483
|
// token, so one model can cost different amounts across requests.
|
|
91227
|
-
estimateAmount
|
|
91484
|
+
estimateAmount,
|
|
91485
|
+
// Only the wallet rail settles per call. The API-key rail is billed
|
|
91486
|
+
// server-side against account credit, so there is no per-request
|
|
91487
|
+
// settlement for an observer to report.
|
|
91488
|
+
onPayment: options.onPayment
|
|
91228
91489
|
});
|
|
91229
91490
|
let balanceMonitor;
|
|
91230
91491
|
if (options._balanceMonitorOverride) {
|
|
91231
91492
|
balanceMonitor = options._balanceMonitorOverride;
|
|
91493
|
+
} else if (authMode === "api-key") {
|
|
91494
|
+
balanceMonitor = new ApiKeyBalanceMonitor();
|
|
91232
91495
|
} else if (paymentChain === "solana" && solanaAddress) {
|
|
91233
91496
|
const { SolanaBalanceMonitor: SolanaBalanceMonitor2 } = await Promise.resolve().then(() => (init_solana_balance(), solana_balance_exports));
|
|
91234
91497
|
balanceMonitor = new SolanaBalanceMonitor2(solanaAddress);
|
|
@@ -91270,11 +91533,17 @@ async function startProxy(options) {
|
|
|
91270
91533
|
const full = url2.searchParams.get("full") === "true";
|
|
91271
91534
|
const response = {
|
|
91272
91535
|
status: "ok",
|
|
91273
|
-
|
|
91274
|
-
paymentChain
|
|
91536
|
+
authMode
|
|
91275
91537
|
};
|
|
91276
|
-
if (
|
|
91277
|
-
response.
|
|
91538
|
+
if (authMode === "api-key") {
|
|
91539
|
+
response.apiKey = maskApiKey(apiKey);
|
|
91540
|
+
response.gateway = apiBase;
|
|
91541
|
+
} else {
|
|
91542
|
+
response.wallet = account.address;
|
|
91543
|
+
response.paymentChain = paymentChain;
|
|
91544
|
+
if (solanaAddress) {
|
|
91545
|
+
response.solana = solanaAddress;
|
|
91546
|
+
}
|
|
91278
91547
|
}
|
|
91279
91548
|
if (upstreamProxy) {
|
|
91280
91549
|
response.upstreamProxy = upstreamProxy;
|
|
@@ -91290,9 +91559,15 @@ async function startProxy(options) {
|
|
|
91290
91559
|
).unref()
|
|
91291
91560
|
)
|
|
91292
91561
|
]);
|
|
91293
|
-
|
|
91294
|
-
|
|
91295
|
-
|
|
91562
|
+
if (authMode === "api-key") {
|
|
91563
|
+
response.balance = null;
|
|
91564
|
+
response.billing = "BlockRun account credit";
|
|
91565
|
+
response.topUpUrl = PORTAL_CREDITS_URL;
|
|
91566
|
+
} else {
|
|
91567
|
+
response.balance = balanceInfo.balanceUSD;
|
|
91568
|
+
response.isLow = balanceInfo.isLow;
|
|
91569
|
+
response.isEmpty = balanceInfo.isEmpty;
|
|
91570
|
+
}
|
|
91296
91571
|
} catch {
|
|
91297
91572
|
response.balanceError = "Could not fetch balance";
|
|
91298
91573
|
}
|
|
@@ -91472,7 +91747,7 @@ async function startProxy(options) {
|
|
|
91472
91747
|
res.end("Bad request");
|
|
91473
91748
|
return;
|
|
91474
91749
|
}
|
|
91475
|
-
const filePath =
|
|
91750
|
+
const filePath = join11(IMAGE_DIR, filename);
|
|
91476
91751
|
try {
|
|
91477
91752
|
const s3 = await fsStat(filePath);
|
|
91478
91753
|
if (!s3.isFile()) throw new Error("not a file");
|
|
@@ -91503,7 +91778,7 @@ async function startProxy(options) {
|
|
|
91503
91778
|
res.end("Bad request");
|
|
91504
91779
|
return;
|
|
91505
91780
|
}
|
|
91506
|
-
const filePath =
|
|
91781
|
+
const filePath = join11(AUDIO_DIR, filename);
|
|
91507
91782
|
try {
|
|
91508
91783
|
const s3 = await fsStat(filePath);
|
|
91509
91784
|
if (!s3.isFile()) throw new Error("not a file");
|
|
@@ -91533,7 +91808,7 @@ async function startProxy(options) {
|
|
|
91533
91808
|
res.end("Bad request");
|
|
91534
91809
|
return;
|
|
91535
91810
|
}
|
|
91536
|
-
const filePath =
|
|
91811
|
+
const filePath = join11(VIDEO_DIR, filename);
|
|
91537
91812
|
try {
|
|
91538
91813
|
const s3 = await fsStat(filePath);
|
|
91539
91814
|
if (!s3.isFile()) throw new Error("not a file");
|
|
@@ -91666,7 +91941,7 @@ async function startProxy(options) {
|
|
|
91666
91941
|
}
|
|
91667
91942
|
}
|
|
91668
91943
|
if (result.data?.length) {
|
|
91669
|
-
await
|
|
91944
|
+
await mkdir5(IMAGE_DIR, { recursive: true });
|
|
91670
91945
|
const port2 = server.address()?.port ?? 8402;
|
|
91671
91946
|
for (const img of result.data) {
|
|
91672
91947
|
const dataUriMatch = img.url?.match(/^data:(image\/\w+);base64,(.+)$/);
|
|
@@ -91674,7 +91949,7 @@ async function startProxy(options) {
|
|
|
91674
91949
|
const [, mimeType, b64] = dataUriMatch;
|
|
91675
91950
|
const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
|
|
91676
91951
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
91677
|
-
await
|
|
91952
|
+
await writeFile3(join11(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
|
|
91678
91953
|
img.url = `http://localhost:${port2}/images/${filename}`;
|
|
91679
91954
|
console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
|
|
91680
91955
|
} else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
|
|
@@ -91685,7 +91960,7 @@ async function startProxy(options) {
|
|
|
91685
91960
|
const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
|
|
91686
91961
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
91687
91962
|
const buf = Buffer.from(await imgResp.arrayBuffer());
|
|
91688
|
-
await
|
|
91963
|
+
await writeFile3(join11(IMAGE_DIR, filename), buf);
|
|
91689
91964
|
img.url = `http://localhost:${port2}/images/${filename}`;
|
|
91690
91965
|
console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
|
|
91691
91966
|
}
|
|
@@ -91789,7 +92064,7 @@ async function startProxy(options) {
|
|
|
91789
92064
|
return;
|
|
91790
92065
|
}
|
|
91791
92066
|
if (result.data?.length) {
|
|
91792
|
-
await
|
|
92067
|
+
await mkdir5(IMAGE_DIR, { recursive: true });
|
|
91793
92068
|
const port2 = server.address()?.port ?? 8402;
|
|
91794
92069
|
for (const img of result.data) {
|
|
91795
92070
|
const dataUriMatch = img.url?.match(/^data:(image\/\w+);base64,(.+)$/);
|
|
@@ -91797,7 +92072,7 @@ async function startProxy(options) {
|
|
|
91797
92072
|
const [, mimeType, b64] = dataUriMatch;
|
|
91798
92073
|
const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
|
|
91799
92074
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
91800
|
-
await
|
|
92075
|
+
await writeFile3(join11(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
|
|
91801
92076
|
img.url = `http://localhost:${port2}/images/${filename}`;
|
|
91802
92077
|
console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
|
|
91803
92078
|
} else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
|
|
@@ -91808,7 +92083,7 @@ async function startProxy(options) {
|
|
|
91808
92083
|
const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
|
|
91809
92084
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
91810
92085
|
const buf = Buffer.from(await imgResp.arrayBuffer());
|
|
91811
|
-
await
|
|
92086
|
+
await writeFile3(join11(IMAGE_DIR, filename), buf);
|
|
91812
92087
|
img.url = `http://localhost:${port2}/images/${filename}`;
|
|
91813
92088
|
console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
|
|
91814
92089
|
}
|
|
@@ -91883,7 +92158,7 @@ async function startProxy(options) {
|
|
|
91883
92158
|
return;
|
|
91884
92159
|
}
|
|
91885
92160
|
if (result.data?.length) {
|
|
91886
|
-
await
|
|
92161
|
+
await mkdir5(AUDIO_DIR, { recursive: true });
|
|
91887
92162
|
const port2 = server.address()?.port ?? 8402;
|
|
91888
92163
|
for (const track of result.data) {
|
|
91889
92164
|
if (track.url?.startsWith("https://") || track.url?.startsWith("http://")) {
|
|
@@ -91894,7 +92169,7 @@ async function startProxy(options) {
|
|
|
91894
92169
|
const ext = contentType.includes("wav") ? "wav" : "mp3";
|
|
91895
92170
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
91896
92171
|
const buf = Buffer.from(await audioResp.arrayBuffer());
|
|
91897
|
-
await
|
|
92172
|
+
await writeFile3(join11(AUDIO_DIR, filename), buf);
|
|
91898
92173
|
track.url = `http://localhost:${port2}/audio/${filename}`;
|
|
91899
92174
|
console.log(`[ClawRouter] Audio saved \u2192 ${track.url}`);
|
|
91900
92175
|
}
|
|
@@ -92043,7 +92318,7 @@ async function startProxy(options) {
|
|
|
92043
92318
|
}
|
|
92044
92319
|
}
|
|
92045
92320
|
if (finalResult.data?.length) {
|
|
92046
|
-
await
|
|
92321
|
+
await mkdir5(VIDEO_DIR, { recursive: true });
|
|
92047
92322
|
const port2 = server.address()?.port ?? 8402;
|
|
92048
92323
|
for (const clip of finalResult.data) {
|
|
92049
92324
|
if (clip.url?.startsWith("https://") || clip.url?.startsWith("http://")) {
|
|
@@ -92054,7 +92329,7 @@ async function startProxy(options) {
|
|
|
92054
92329
|
const ext = contentType.includes("webm") ? "webm" : contentType.includes("quicktime") ? "mov" : "mp4";
|
|
92055
92330
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
92056
92331
|
const buf = Buffer.from(await videoResp.arrayBuffer());
|
|
92057
|
-
await
|
|
92332
|
+
await writeFile3(join11(VIDEO_DIR, filename), buf);
|
|
92058
92333
|
clip.url = `http://localhost:${port2}/videos/${filename}`;
|
|
92059
92334
|
console.log(`[ClawRouter] Video saved \u2192 ${clip.url}`);
|
|
92060
92335
|
}
|
|
@@ -92237,6 +92512,8 @@ async function startProxy(options) {
|
|
|
92237
92512
|
port: listenPort,
|
|
92238
92513
|
baseUrl: baseUrl2,
|
|
92239
92514
|
walletAddress: error.wallet,
|
|
92515
|
+
authMode,
|
|
92516
|
+
...apiKey ? { apiKeyLabel: maskApiKey(apiKey) } : {},
|
|
92240
92517
|
balanceMonitor,
|
|
92241
92518
|
close: async () => {
|
|
92242
92519
|
}
|
|
@@ -92287,8 +92564,10 @@ async function startProxy(options) {
|
|
|
92287
92564
|
return {
|
|
92288
92565
|
port,
|
|
92289
92566
|
baseUrl,
|
|
92290
|
-
walletAddress: account
|
|
92567
|
+
walletAddress: account?.address ?? "",
|
|
92291
92568
|
solanaAddress,
|
|
92569
|
+
authMode,
|
|
92570
|
+
...apiKey ? { apiKeyLabel: maskApiKey(apiKey) } : {},
|
|
92292
92571
|
balanceMonitor,
|
|
92293
92572
|
close: () => new Promise((res, rej) => {
|
|
92294
92573
|
const timeout = setTimeout(() => {
|
|
@@ -94611,6 +94890,7 @@ var init_proxy = __esm({
|
|
|
94611
94890
|
init_response_cache();
|
|
94612
94891
|
init_balance();
|
|
94613
94892
|
init_auth();
|
|
94893
|
+
init_api_key();
|
|
94614
94894
|
init_spend_control();
|
|
94615
94895
|
init_compression();
|
|
94616
94896
|
init_version4();
|
|
@@ -94627,10 +94907,10 @@ var init_proxy = __esm({
|
|
|
94627
94907
|
paymentStore = new AsyncLocalStorage();
|
|
94628
94908
|
BLOCKRUN_API = "https://blockrun.ai/api";
|
|
94629
94909
|
BLOCKRUN_SOLANA_API = "https://sol.blockrun.ai/api";
|
|
94630
|
-
DESKTOP_SERVICE_TOKEN_FILE = process.env.CLAWROUTER_DESKTOP_TOKEN_FILE ??
|
|
94631
|
-
IMAGE_DIR =
|
|
94632
|
-
AUDIO_DIR =
|
|
94633
|
-
VIDEO_DIR =
|
|
94910
|
+
DESKTOP_SERVICE_TOKEN_FILE = process.env.CLAWROUTER_DESKTOP_TOKEN_FILE ?? join11(homedir8(), ".clawrouter-desktop", "service-token");
|
|
94911
|
+
IMAGE_DIR = join11(homedir8(), ".openclaw", "blockrun", "images");
|
|
94912
|
+
AUDIO_DIR = join11(homedir8(), ".openclaw", "blockrun", "audio");
|
|
94913
|
+
VIDEO_DIR = join11(homedir8(), ".openclaw", "blockrun", "videos");
|
|
94634
94914
|
AUTO_MODEL = "blockrun/auto";
|
|
94635
94915
|
ROUTING_PROFILES = /* @__PURE__ */ new Set([
|
|
94636
94916
|
"blockrun/eco",
|
|
@@ -116822,8 +117102,8 @@ var init_constants3 = __esm({
|
|
|
116822
117102
|
|
|
116823
117103
|
// src/polymarket/wallet-adapter.ts
|
|
116824
117104
|
import fs2 from "fs";
|
|
116825
|
-
import { homedir as
|
|
116826
|
-
import { join as
|
|
117105
|
+
import { homedir as homedir9 } from "os";
|
|
117106
|
+
import { join as join12 } from "path";
|
|
116827
117107
|
function getOrCreateWalletKey() {
|
|
116828
117108
|
const envKey = process.env.BLOCKRUN_WALLET_KEY?.trim();
|
|
116829
117109
|
if (envKey && /^0x[0-9a-fA-F]{64}$/.test(envKey)) return envKey;
|
|
@@ -116871,7 +117151,7 @@ var init_wallet_adapter = __esm({
|
|
|
116871
117151
|
init_esm();
|
|
116872
117152
|
init_chains();
|
|
116873
117153
|
init_constants3();
|
|
116874
|
-
WALLET_FILE2 =
|
|
117154
|
+
WALLET_FILE2 = join12(homedir9(), ".openclaw", "blockrun", "wallet.key");
|
|
116875
117155
|
BASE_RPC_URLS = [
|
|
116876
117156
|
"https://mainnet.base.org",
|
|
116877
117157
|
"https://base.llamarpc.com",
|
|
@@ -117273,9 +117553,7 @@ var init_client5 = __esm({
|
|
|
117273
117553
|
|
|
117274
117554
|
// src/polymarket/spend-policy.ts
|
|
117275
117555
|
function resolveSpendControl(deps) {
|
|
117276
|
-
|
|
117277
|
-
defaultControl ??= new SpendControl();
|
|
117278
|
-
return defaultControl;
|
|
117556
|
+
return deps?.spendControl ?? getSharedSpendControl();
|
|
117279
117557
|
}
|
|
117280
117558
|
function usdToMicros(usd) {
|
|
117281
117559
|
return String(Math.ceil(usd * 1e6));
|
|
@@ -117292,7 +117570,6 @@ async function signUnderSpendPolicy(deps, quote, action, sign4) {
|
|
|
117292
117570
|
throw err;
|
|
117293
117571
|
}
|
|
117294
117572
|
}
|
|
117295
|
-
var defaultControl;
|
|
117296
117573
|
var init_spend_policy = __esm({
|
|
117297
117574
|
"src/polymarket/spend-policy.ts"() {
|
|
117298
117575
|
"use strict";
|
|
@@ -226657,7 +226934,7 @@ var init_fund = __esm({
|
|
|
226657
226934
|
});
|
|
226658
226935
|
|
|
226659
226936
|
// src/polymarket/tool.ts
|
|
226660
|
-
function buildPolymarketTool() {
|
|
226937
|
+
function buildPolymarketTool(deps) {
|
|
226661
226938
|
return {
|
|
226662
226939
|
name: "blockrun_polymarket",
|
|
226663
226940
|
description: DESCRIPTION,
|
|
@@ -226742,26 +227019,32 @@ function buildPolymarketTool() {
|
|
|
226742
227019
|
result = await runSetup({ confirm });
|
|
226743
227020
|
break;
|
|
226744
227021
|
case "fund":
|
|
226745
|
-
result = await fundVault(
|
|
226746
|
-
|
|
226747
|
-
|
|
226748
|
-
|
|
227022
|
+
result = await fundVault(
|
|
227023
|
+
{
|
|
227024
|
+
amount_usd: params.amount_usd,
|
|
227025
|
+
confirm
|
|
227026
|
+
},
|
|
227027
|
+
deps
|
|
227028
|
+
);
|
|
226749
227029
|
break;
|
|
226750
227030
|
case "buy":
|
|
226751
227031
|
case "sell":
|
|
226752
|
-
result = await executeTrade(
|
|
226753
|
-
|
|
226754
|
-
|
|
226755
|
-
|
|
226756
|
-
|
|
226757
|
-
|
|
226758
|
-
|
|
226759
|
-
|
|
226760
|
-
|
|
226761
|
-
|
|
226762
|
-
|
|
226763
|
-
|
|
226764
|
-
|
|
227032
|
+
result = await executeTrade(
|
|
227033
|
+
{
|
|
227034
|
+
action,
|
|
227035
|
+
token_id: params.token_id,
|
|
227036
|
+
condition_id: params.condition_id,
|
|
227037
|
+
outcome: params.outcome,
|
|
227038
|
+
price: params.price,
|
|
227039
|
+
size: params.size,
|
|
227040
|
+
amount_usd: params.amount_usd,
|
|
227041
|
+
order_type: params.order_type,
|
|
227042
|
+
expires_at: params.expires_at,
|
|
227043
|
+
post_only: params.post_only,
|
|
227044
|
+
confirm
|
|
227045
|
+
},
|
|
227046
|
+
deps
|
|
227047
|
+
);
|
|
226765
227048
|
break;
|
|
226766
227049
|
case "orders":
|
|
226767
227050
|
result = await listOpenOrders({
|
|
@@ -226778,17 +227061,23 @@ function buildPolymarketTool() {
|
|
|
226778
227061
|
result = await listPositions();
|
|
226779
227062
|
break;
|
|
226780
227063
|
case "redeem":
|
|
226781
|
-
result = await redeemPosition(
|
|
226782
|
-
|
|
226783
|
-
|
|
226784
|
-
|
|
227064
|
+
result = await redeemPosition(
|
|
227065
|
+
{
|
|
227066
|
+
condition_id: params.condition_id,
|
|
227067
|
+
confirm
|
|
227068
|
+
},
|
|
227069
|
+
deps
|
|
227070
|
+
);
|
|
226785
227071
|
break;
|
|
226786
227072
|
case "withdraw":
|
|
226787
|
-
result = await withdrawFunds(
|
|
226788
|
-
|
|
226789
|
-
|
|
226790
|
-
|
|
226791
|
-
|
|
227073
|
+
result = await withdrawFunds(
|
|
227074
|
+
{
|
|
227075
|
+
amount_usd: params.amount_usd,
|
|
227076
|
+
to_address: params.to_address,
|
|
227077
|
+
confirm
|
|
227078
|
+
},
|
|
227079
|
+
deps
|
|
227080
|
+
);
|
|
226792
227081
|
break;
|
|
226793
227082
|
default:
|
|
226794
227083
|
throw new Error(`Unknown blockrun_polymarket action: ${action}`);
|
|
@@ -227387,9 +227676,9 @@ import {
|
|
|
227387
227676
|
unlinkSync
|
|
227388
227677
|
} from "fs";
|
|
227389
227678
|
import { readFile as readFileAsync } from "fs/promises";
|
|
227390
|
-
import { homedir as
|
|
227679
|
+
import { homedir as homedir14 } from "os";
|
|
227391
227680
|
import { randomUUID } from "crypto";
|
|
227392
|
-
import { join as
|
|
227681
|
+
import { join as join17 } from "path";
|
|
227393
227682
|
async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
227394
227683
|
const start = Date.now();
|
|
227395
227684
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -227431,8 +227720,8 @@ function isBlockrunWebSearchDisabled(config) {
|
|
|
227431
227720
|
return cfg.tools?.web?.search?.enabled === false;
|
|
227432
227721
|
}
|
|
227433
227722
|
function injectModelsConfig(logger48, options = {}) {
|
|
227434
|
-
const configDir =
|
|
227435
|
-
const configPath =
|
|
227723
|
+
const configDir = join17(homedir14(), ".openclaw");
|
|
227724
|
+
const configPath = join17(configDir, "openclaw.json");
|
|
227436
227725
|
let config = {};
|
|
227437
227726
|
let needsWrite = false;
|
|
227438
227727
|
if (!existsSync3(configDir)) {
|
|
@@ -227653,7 +227942,7 @@ function injectModelsConfig(logger48, options = {}) {
|
|
|
227653
227942
|
}
|
|
227654
227943
|
function syncAgentModelCache(logger48, options = {}) {
|
|
227655
227944
|
if (!isGatewayMode() && !options.forceWrite) return;
|
|
227656
|
-
const agentsDir =
|
|
227945
|
+
const agentsDir = join17(homedir14(), ".openclaw", "agents");
|
|
227657
227946
|
if (!existsSync3(agentsDir)) return;
|
|
227658
227947
|
let agentDirs;
|
|
227659
227948
|
try {
|
|
@@ -227663,7 +227952,7 @@ function syncAgentModelCache(logger48, options = {}) {
|
|
|
227663
227952
|
}
|
|
227664
227953
|
const expectedIds = VISIBLE_OPENCLAW_MODELS.map((m) => m.id);
|
|
227665
227954
|
for (const agent of agentDirs) {
|
|
227666
|
-
const cachePath =
|
|
227955
|
+
const cachePath = join17(agentsDir, agent, "agent", "models.json");
|
|
227667
227956
|
if (!existsSync3(cachePath)) continue;
|
|
227668
227957
|
try {
|
|
227669
227958
|
const raw = readTextFileSync(cachePath).trim();
|
|
@@ -227692,7 +227981,7 @@ function syncAgentModelCache(logger48, options = {}) {
|
|
|
227692
227981
|
}
|
|
227693
227982
|
}
|
|
227694
227983
|
function injectAuthProfile(logger48) {
|
|
227695
|
-
const agentsDir =
|
|
227984
|
+
const agentsDir = join17(homedir14(), ".openclaw", "agents");
|
|
227696
227985
|
if (!existsSync3(agentsDir)) {
|
|
227697
227986
|
try {
|
|
227698
227987
|
mkdirSync3(agentsDir, { recursive: true });
|
|
@@ -227709,9 +227998,9 @@ function injectAuthProfile(logger48) {
|
|
|
227709
227998
|
agents = ["main", ...agents];
|
|
227710
227999
|
}
|
|
227711
228000
|
for (const agentId of agents) {
|
|
227712
|
-
const authDir =
|
|
227713
|
-
const authPath =
|
|
227714
|
-
const sqlitePath =
|
|
228001
|
+
const authDir = join17(agentsDir, agentId, "agent");
|
|
228002
|
+
const authPath = join17(authDir, "auth-profiles.json");
|
|
228003
|
+
const sqlitePath = join17(authDir, "openclaw-agent.sqlite");
|
|
227715
228004
|
if (existsSync3(sqlitePath)) {
|
|
227716
228005
|
removeInjectedAuthPlaceholder(authPath, logger48, agentId);
|
|
227717
228006
|
continue;
|
|
@@ -227858,9 +228147,21 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
227858
228147
|
if (startupGeneration !== void 0 && isProxyStartupCurrent(startupGeneration, proc)) {
|
|
227859
228148
|
proc.__clawrouterStartupPhase = "starting";
|
|
227860
228149
|
}
|
|
228150
|
+
const pluginApiKey = api.pluginConfig?.apiKey;
|
|
228151
|
+
const resolvedApiKey = typeof pluginApiKey === "string" && isValidApiKey(pluginApiKey) ? { key: pluginApiKey.trim(), source: "config" } : await resolveApiKey();
|
|
228152
|
+
if (typeof pluginApiKey === "string" && !isValidApiKey(pluginApiKey)) {
|
|
228153
|
+
api.logger.warn(
|
|
228154
|
+
`pluginConfig.apiKey is set but invalid (expected brk_...) \u2014 ignoring it and looking elsewhere`
|
|
228155
|
+
);
|
|
228156
|
+
}
|
|
228157
|
+
const apiKey = resolvedApiKey?.key;
|
|
227861
228158
|
const configKey = api.pluginConfig?.walletKey;
|
|
227862
228159
|
let wallet;
|
|
227863
|
-
if (
|
|
228160
|
+
if (apiKey) {
|
|
228161
|
+
api.logger.info(
|
|
228162
|
+
`Using BlockRun API key ${maskApiKey(apiKey)} (from ${resolvedApiKey.source}) \u2014 billing account credit, no wallet`
|
|
228163
|
+
);
|
|
228164
|
+
} else if (typeof configKey === "string" && /^0x[0-9a-fA-F]{64}$/.test(configKey)) {
|
|
227864
228165
|
const account = privateKeyToAccount(configKey);
|
|
227865
228166
|
wallet = { key: configKey, address: account.address, source: "config" };
|
|
227866
228167
|
} else {
|
|
@@ -227871,7 +228172,8 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
227871
228172
|
}
|
|
227872
228173
|
wallet = await resolveOrGenerateWalletKey();
|
|
227873
228174
|
}
|
|
227874
|
-
if (wallet
|
|
228175
|
+
if (!wallet) {
|
|
228176
|
+
} else if (wallet.source === "generated") {
|
|
227875
228177
|
api.logger.warn(`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
|
|
227876
228178
|
api.logger.warn(` NEW WALLET GENERATED \u2014 BACK UP YOUR KEY NOW!`);
|
|
227877
228179
|
api.logger.warn(` Address : ${wallet.address}`);
|
|
@@ -227895,14 +228197,20 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
227895
228197
|
`Cost cap: $${maxCostPerRunUsd.toFixed(2)} per session (mode: ${maxCostPerRunMode})`
|
|
227896
228198
|
);
|
|
227897
228199
|
}
|
|
228200
|
+
const sharedControl = getSharedSpendControl();
|
|
228201
|
+
sharedControl.reloadLimits();
|
|
228202
|
+
sharedControl.resetSession();
|
|
227898
228203
|
const proxy = await startProxy({
|
|
227899
|
-
wallet,
|
|
228204
|
+
...apiKey ? { apiKey } : { wallet },
|
|
227900
228205
|
routingConfig,
|
|
227901
228206
|
maxCostPerRunUsd,
|
|
227902
228207
|
maxCostPerRunMode,
|
|
227903
|
-
|
|
228208
|
+
// The process-wide ledger: the polymarket tool and the /policy command
|
|
228209
|
+
// registered below use the same instance, so hourly/daily/session windows
|
|
228210
|
+
// cover every surface and a /policy write reaches the live signer.
|
|
228211
|
+
spendControl: sharedControl,
|
|
227904
228212
|
onReady: (port) => {
|
|
227905
|
-
api.logger.info(`BlockRun x402 proxy listening on port ${port}`);
|
|
228213
|
+
api.logger.info(`BlockRun ${apiKey ? "API-key" : "x402"} proxy listening on port ${port}`);
|
|
227906
228214
|
},
|
|
227907
228215
|
onError: (error) => {
|
|
227908
228216
|
api.logger.error(`BlockRun proxy error: ${error.message}`);
|
|
@@ -227948,6 +228256,10 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
227948
228256
|
}
|
|
227949
228257
|
api.logger.info(`ClawRouter ready \u2014 smart routing enabled`);
|
|
227950
228258
|
api.logger.info(`Pricing: Simple ~$0.001 | Code ~$0.01 | Complex ~$0.05 | Free: $0`);
|
|
228259
|
+
if (apiKey) {
|
|
228260
|
+
api.logger.info(`Billing: BlockRun account credit \u2014 top up at ${PORTAL_CREDITS_URL}`);
|
|
228261
|
+
return true;
|
|
228262
|
+
}
|
|
227951
228263
|
const currentChain = await resolvePaymentChain();
|
|
227952
228264
|
const displayAddress = currentChain === "solana" && proxy.solanaAddress ? proxy.solanaAddress : wallet.address;
|
|
227953
228265
|
const network = currentChain === "solana" ? "Solana" : "Base";
|
|
@@ -228191,7 +228503,7 @@ function buildImageGenerationProvider() {
|
|
|
228191
228503
|
(result.data ?? []).map(async (img) => {
|
|
228192
228504
|
const filename = img.url?.split("/images/").pop();
|
|
228193
228505
|
if (!filename) throw new Error(`Unexpected image URL format: ${img.url}`);
|
|
228194
|
-
const filePath =
|
|
228506
|
+
const filePath = join17(IMAGE_DIR2, filename);
|
|
228195
228507
|
const buffer2 = await readFileAsync(filePath);
|
|
228196
228508
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "png";
|
|
228197
228509
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
|
|
@@ -228243,7 +228555,7 @@ function buildMusicGenerationProvider() {
|
|
|
228243
228555
|
(result.data ?? []).map(async (track) => {
|
|
228244
228556
|
const filename = track.url?.split("/audio/").pop();
|
|
228245
228557
|
if (!filename) throw new Error(`Unexpected audio URL format: ${track.url}`);
|
|
228246
|
-
const filePath =
|
|
228558
|
+
const filePath = join17(AUDIO_DIR2, filename);
|
|
228247
228559
|
const buffer2 = await readFileAsync(filePath);
|
|
228248
228560
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "mp3";
|
|
228249
228561
|
const mimeType = ext === "wav" ? "audio/wav" : "audio/mpeg";
|
|
@@ -228319,7 +228631,7 @@ function buildVideoGenerationProvider() {
|
|
|
228319
228631
|
(result.data ?? []).map(async (clip) => {
|
|
228320
228632
|
const filename = clip.url?.split("/videos/").pop();
|
|
228321
228633
|
if (!filename) throw new Error(`Unexpected video URL format: ${clip.url}`);
|
|
228322
|
-
const filePath =
|
|
228634
|
+
const filePath = join17(VIDEO_DIR2, filename);
|
|
228323
228635
|
const buffer2 = await readFileAsync(filePath);
|
|
228324
228636
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "mp4";
|
|
228325
228637
|
const mimeType = ext === "webm" ? "video/webm" : ext === "mov" ? "video/quicktime" : "video/mp4";
|
|
@@ -228372,6 +228684,22 @@ function createWalletCommand(api) {
|
|
|
228372
228684
|
requireAuth: true,
|
|
228373
228685
|
handler: async (ctx) => {
|
|
228374
228686
|
const subcommand = ctx.args?.trim().toLowerCase() || "status";
|
|
228687
|
+
const activeApiKey = (await resolveApiKey())?.key;
|
|
228688
|
+
if (activeApiKey) {
|
|
228689
|
+
return {
|
|
228690
|
+
text: [
|
|
228691
|
+
"**BlockRun account** (API key)",
|
|
228692
|
+
"",
|
|
228693
|
+
`**Key:** \`${maskApiKey(activeApiKey)}\``,
|
|
228694
|
+
"**Billing:** account credit \u2014 no wallet, no payment chain, no gas.",
|
|
228695
|
+
"",
|
|
228696
|
+
`**Add credit:** ${PORTAL_CREDITS_URL}`,
|
|
228697
|
+
`**Usage & keys:** ${PORTAL_KEYS_URL}`,
|
|
228698
|
+
"",
|
|
228699
|
+
"To pay with a USDC wallet over x402 instead, run `clawrouter logout` and restart."
|
|
228700
|
+
].join("\n")
|
|
228701
|
+
};
|
|
228702
|
+
}
|
|
228375
228703
|
let wallet;
|
|
228376
228704
|
try {
|
|
228377
228705
|
wallet = subcommand === "status" || subcommand === "export" ? await resolveExistingWalletKey() : await resolveOrGenerateWalletKey();
|
|
@@ -228623,11 +228951,12 @@ function createWalletCommand(api) {
|
|
|
228623
228951
|
}
|
|
228624
228952
|
};
|
|
228625
228953
|
}
|
|
228626
|
-
var activeProxyHandle,
|
|
228954
|
+
var activeProxyHandle, pendingConfiguredStartupApi, IMAGE_DIR2, AUDIO_DIR2, VIDEO_DIR2, plugin, index_default2;
|
|
228627
228955
|
var init_index = __esm({
|
|
228628
228956
|
"src/index.ts"() {
|
|
228629
228957
|
init_provider();
|
|
228630
228958
|
init_proxy();
|
|
228959
|
+
init_api_key();
|
|
228631
228960
|
init_web_search_provider();
|
|
228632
228961
|
init_auth();
|
|
228633
228962
|
init_balance();
|
|
@@ -228640,10 +228969,10 @@ var init_index = __esm({
|
|
|
228640
228969
|
init_stats();
|
|
228641
228970
|
init_partners();
|
|
228642
228971
|
init_tool();
|
|
228972
|
+
init_spend_control();
|
|
228643
228973
|
init_stats2();
|
|
228644
228974
|
init_exclude();
|
|
228645
228975
|
init_policy();
|
|
228646
|
-
init_spend_control();
|
|
228647
228976
|
init_mcp_config();
|
|
228648
228977
|
init_openclaw_plugin_config();
|
|
228649
228978
|
init_openclaw_plugin_config();
|
|
@@ -228665,11 +228994,10 @@ var init_index = __esm({
|
|
|
228665
228994
|
init_response_cache();
|
|
228666
228995
|
init_partners();
|
|
228667
228996
|
activeProxyHandle = null;
|
|
228668
|
-
liveSpendControl = null;
|
|
228669
228997
|
pendingConfiguredStartupApi = null;
|
|
228670
|
-
IMAGE_DIR2 =
|
|
228671
|
-
AUDIO_DIR2 =
|
|
228672
|
-
VIDEO_DIR2 =
|
|
228998
|
+
IMAGE_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "images");
|
|
228999
|
+
AUDIO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "audio");
|
|
229000
|
+
VIDEO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "videos");
|
|
228673
229001
|
plugin = {
|
|
228674
229002
|
// NOT "clawrouter". OpenClaw bundles its own plugin under that id since the
|
|
228675
229003
|
// 2026.7.1 line (vendored at dist/extensions/clawrouter, provider `clawrouter/*`,
|
|
@@ -228981,7 +229309,7 @@ ${errText}`
|
|
|
228981
229309
|
api.registerCommand(createExcludeCommand());
|
|
228982
229310
|
api.registerCommand(
|
|
228983
229311
|
createPolicyCommand({
|
|
228984
|
-
liveControl: () =>
|
|
229312
|
+
liveControl: () => process.__clawrouterProxyStarted ? getSharedSpendControl() : void 0
|
|
228985
229313
|
})
|
|
228986
229314
|
);
|
|
228987
229315
|
if (shouldLogRegistration) {
|
|
@@ -229086,7 +229414,7 @@ ${errText}`
|
|
|
229086
229414
|
}
|
|
229087
229415
|
resetProxyStartupState();
|
|
229088
229416
|
try {
|
|
229089
|
-
const configPath =
|
|
229417
|
+
const configPath = join17(homedir14(), ".openclaw", "openclaw.json");
|
|
229090
229418
|
if (existsSync3(configPath)) {
|
|
229091
229419
|
const config = JSON.parse(readTextFileSync(configPath));
|
|
229092
229420
|
if (config.models?.providers?.blockrun) {
|
|
@@ -229128,11 +229456,11 @@ ${errText}`
|
|
|
229128
229456
|
api.logger.warn(`Config cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
229129
229457
|
}
|
|
229130
229458
|
try {
|
|
229131
|
-
const agentsDir =
|
|
229459
|
+
const agentsDir = join17(homedir14(), ".openclaw", "agents");
|
|
229132
229460
|
if (existsSync3(agentsDir)) {
|
|
229133
229461
|
for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
229134
229462
|
if (!entry.isDirectory()) continue;
|
|
229135
|
-
const authPath =
|
|
229463
|
+
const authPath = join17(agentsDir, entry.name, "agent", "auth-profiles.json");
|
|
229136
229464
|
if (!existsSync3(authPath)) continue;
|
|
229137
229465
|
try {
|
|
229138
229466
|
const store = JSON.parse(readTextFileSync(authPath));
|
|
@@ -229179,6 +229507,7 @@ export {
|
|
|
229179
229507
|
SolanaBalanceMonitor,
|
|
229180
229508
|
SpendControl,
|
|
229181
229509
|
SpendPolicyError,
|
|
229510
|
+
UnreadableSpendPolicyError,
|
|
229182
229511
|
VISIBLE_OPENCLAW_MODELS,
|
|
229183
229512
|
blockrunProvider,
|
|
229184
229513
|
buildImageGenerationProvider,
|