@blockrun/clawrouter 0.12.268 → 0.12.270
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 +81 -45
- package/dist/cli.js +191 -71
- package/dist/index.d.ts +48 -5
- package/dist/index.js +190 -70
- package/package.json +1 -1
- package/skills/clawrouter/SKILL.md +8 -8
- package/skills/imagegen/SKILL.md +2 -2
- package/skills/phone/SKILL.md +3 -2
- package/skills/predexon/SKILL.md +1 -1
- package/skills/surf/SKILL.md +4 -4
package/dist/cli.js
CHANGED
|
@@ -32310,6 +32310,11 @@ function canonicalLimits(limits) {
|
|
|
32310
32310
|
Object.fromEntries(Object.entries(limits).sort(([a], [b]) => a < b ? -1 : 1))
|
|
32311
32311
|
);
|
|
32312
32312
|
}
|
|
32313
|
+
function getSharedSpendControl() {
|
|
32314
|
+
const host = sharedControlHost();
|
|
32315
|
+
host.__clawrouterSharedSpendControl ??= new SpendControl();
|
|
32316
|
+
return host.__clawrouterSharedSpendControl;
|
|
32317
|
+
}
|
|
32313
32318
|
function parseQuotedAmountUsd(selected) {
|
|
32314
32319
|
const raw = selected.amount ?? selected.maxAmountRequired;
|
|
32315
32320
|
if (typeof raw !== "string" || !CANONICAL_AMOUNT.test(raw)) {
|
|
@@ -32385,7 +32390,7 @@ function formatDuration(seconds) {
|
|
|
32385
32390
|
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
|
32386
32391
|
}
|
|
32387
32392
|
}
|
|
32388
|
-
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;
|
|
32393
|
+
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;
|
|
32389
32394
|
var init_spend_control = __esm({
|
|
32390
32395
|
"src/spend-control.ts"() {
|
|
32391
32396
|
"use strict";
|
|
@@ -32404,6 +32409,12 @@ var init_spend_control = __esm({
|
|
|
32404
32409
|
];
|
|
32405
32410
|
ADDRESS_LISTS = ["allowedPayees", "blockedPayees", "allowedAssets"];
|
|
32406
32411
|
EVM_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
32412
|
+
UnreadableSpendPolicyError = class extends Error {
|
|
32413
|
+
constructor(cause) {
|
|
32414
|
+
super(`[ClawRouter] Failed to load spending data: ${cause}`);
|
|
32415
|
+
this.name = "UnreadableSpendPolicyError";
|
|
32416
|
+
}
|
|
32417
|
+
};
|
|
32407
32418
|
MalformedSpendPolicyError = class extends Error {
|
|
32408
32419
|
constructor(key2) {
|
|
32409
32420
|
super(
|
|
@@ -32464,9 +32475,7 @@ var init_spend_control = __esm({
|
|
|
32464
32475
|
if (err instanceof MalformedSpendPolicyError) {
|
|
32465
32476
|
throw err;
|
|
32466
32477
|
}
|
|
32467
|
-
|
|
32468
|
-
`[ClawRouter] Failed to load spending data, starting fresh (any configured spend policy is NOT in effect until this file is repaired): ${err}`
|
|
32469
|
-
);
|
|
32478
|
+
throw new UnreadableSpendPolicyError(err);
|
|
32470
32479
|
}
|
|
32471
32480
|
return null;
|
|
32472
32481
|
}
|
|
@@ -32607,6 +32616,33 @@ var init_spend_control = __esm({
|
|
|
32607
32616
|
getPolicyFileError() {
|
|
32608
32617
|
return this.policyFileBroken;
|
|
32609
32618
|
}
|
|
32619
|
+
/**
|
|
32620
|
+
* Re-read limits from storage, keeping this instance's history and open
|
|
32621
|
+
* reservations. Used on an in-process proxy restart so a hand-edit to
|
|
32622
|
+
* spending.json made while the process was running still applies, without
|
|
32623
|
+
* resetting the rolling windows. Fails closed exactly like the constructor:
|
|
32624
|
+
* a malformed file refuses every payment until repaired, and a repaired
|
|
32625
|
+
* file clears that refusal.
|
|
32626
|
+
*/
|
|
32627
|
+
reloadLimits() {
|
|
32628
|
+
let data;
|
|
32629
|
+
try {
|
|
32630
|
+
data = this.storage.load();
|
|
32631
|
+
} catch (err) {
|
|
32632
|
+
if (err instanceof UnreadableSpendPolicyError) {
|
|
32633
|
+
console.error(`${err.message} \u2014 keeping the limits already in effect`);
|
|
32634
|
+
return;
|
|
32635
|
+
}
|
|
32636
|
+
if (!(err instanceof MalformedSpendPolicyError)) throw err;
|
|
32637
|
+
this.policyFileBroken = err.message;
|
|
32638
|
+
console.error(`[ClawRouter] ${err.message}`);
|
|
32639
|
+
return;
|
|
32640
|
+
}
|
|
32641
|
+
this.policyFileBroken = void 0;
|
|
32642
|
+
this.limits = data ? cloneLimits(data.limits) : {};
|
|
32643
|
+
this.diskLimits = cloneLimits(this.limits);
|
|
32644
|
+
this.limitsDirty = false;
|
|
32645
|
+
}
|
|
32610
32646
|
check(estimatedCost2, counterparty) {
|
|
32611
32647
|
if (this.policyFileBroken !== void 0) {
|
|
32612
32648
|
return {
|
|
@@ -32851,6 +32887,16 @@ var init_spend_control = __esm({
|
|
|
32851
32887
|
const records = [...this.history].reverse();
|
|
32852
32888
|
return limit ? records.slice(0, limit) : records;
|
|
32853
32889
|
}
|
|
32890
|
+
/**
|
|
32891
|
+
* Reset the session window. `sessionSpent`/`sessionCalls` are instance state
|
|
32892
|
+
* that is never persisted, so this used to happen implicitly: every
|
|
32893
|
+
* startProxy() built a fresh SpendControl. The process-wide ledger outlives
|
|
32894
|
+
* an in-process proxy restart, which would silently redefine `session` as
|
|
32895
|
+
* "since the gateway booted" — docs/configuration.md and the /policy help
|
|
32896
|
+
* both promise "session resets on restart". The restart path in index.ts
|
|
32897
|
+
* calls this to keep that promise. History and the rolling hourly/daily
|
|
32898
|
+
* windows are deliberately untouched.
|
|
32899
|
+
*/
|
|
32854
32900
|
resetSession() {
|
|
32855
32901
|
this.sessionSpent = 0;
|
|
32856
32902
|
this.sessionCalls = 0;
|
|
@@ -32872,10 +32918,13 @@ var init_spend_control = __esm({
|
|
|
32872
32918
|
this.storage.saveLimits(cloneLimits(this.limits), cloneLimits(this.diskLimits));
|
|
32873
32919
|
} catch (err) {
|
|
32874
32920
|
if (err instanceof SpendPolicyConflictError) {
|
|
32875
|
-
|
|
32876
|
-
|
|
32877
|
-
|
|
32878
|
-
|
|
32921
|
+
try {
|
|
32922
|
+
const current = this.storage.load();
|
|
32923
|
+
this.limits = cloneLimits(current?.limits ?? {});
|
|
32924
|
+
this.diskLimits = cloneLimits(this.limits);
|
|
32925
|
+
this.limitsDirty = false;
|
|
32926
|
+
} catch {
|
|
32927
|
+
}
|
|
32879
32928
|
}
|
|
32880
32929
|
throw err;
|
|
32881
32930
|
}
|
|
@@ -32893,6 +32942,12 @@ var init_spend_control = __esm({
|
|
|
32893
32942
|
try {
|
|
32894
32943
|
data = this.storage.load();
|
|
32895
32944
|
} catch (err) {
|
|
32945
|
+
if (err instanceof UnreadableSpendPolicyError) {
|
|
32946
|
+
console.error(
|
|
32947
|
+
`${err.message} \u2014 starting fresh (any configured spend policy is NOT in effect until this file is repaired)`
|
|
32948
|
+
);
|
|
32949
|
+
return;
|
|
32950
|
+
}
|
|
32896
32951
|
if (!(err instanceof MalformedSpendPolicyError)) throw err;
|
|
32897
32952
|
this.policyFileBroken = err.message;
|
|
32898
32953
|
console.error(`[ClawRouter] ${err.message}`);
|
|
@@ -32909,6 +32964,7 @@ var init_spend_control = __esm({
|
|
|
32909
32964
|
}
|
|
32910
32965
|
}
|
|
32911
32966
|
};
|
|
32967
|
+
sharedControlHost = () => process;
|
|
32912
32968
|
SpendPolicyError = class extends Error {
|
|
32913
32969
|
blockedBy;
|
|
32914
32970
|
blockedByPolicy;
|
|
@@ -32946,6 +33002,21 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
32946
33002
|
}
|
|
32947
33003
|
}
|
|
32948
33004
|
const cacheKey2 = `${urlPath}:${requestModel}`;
|
|
33005
|
+
const notifyAcceptedPayment = (response2, payload2) => {
|
|
33006
|
+
const settled = response2.headers.has("payment-response") || response2.headers.has("x-payment-response");
|
|
33007
|
+
if (!settled || !options?.onPayment) return;
|
|
33008
|
+
try {
|
|
33009
|
+
options.onPayment({
|
|
33010
|
+
model: requestModel,
|
|
33011
|
+
amount: payload2.accepted.amount,
|
|
33012
|
+
network: payload2.accepted.network
|
|
33013
|
+
});
|
|
33014
|
+
} catch (error) {
|
|
33015
|
+
console.error(
|
|
33016
|
+
`[ClawRouter] onPayment callback failed: ${error instanceof Error ? error.message : String(error)}`
|
|
33017
|
+
);
|
|
33018
|
+
}
|
|
33019
|
+
};
|
|
32949
33020
|
const estimateMicros = () => {
|
|
32950
33021
|
if (!options?.estimateAmount || !requestModel) return void 0;
|
|
32951
33022
|
const est = options.estimateAmount(requestModel, bodyLength, maxTokens);
|
|
@@ -32966,6 +33037,7 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
32966
33037
|
paymentInFlight = true;
|
|
32967
33038
|
const response2 = await baseFetch(preAuthRequest);
|
|
32968
33039
|
if (response2.status !== 402) {
|
|
33040
|
+
notifyAcceptedPayment(response2, payload2);
|
|
32969
33041
|
return response2;
|
|
32970
33042
|
}
|
|
32971
33043
|
paymentInFlight = false;
|
|
@@ -33012,7 +33084,9 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
33012
33084
|
for (const [key2, value] of Object.entries(paymentHeaders)) {
|
|
33013
33085
|
clonedRequest.headers.set(key2, value);
|
|
33014
33086
|
}
|
|
33015
|
-
|
|
33087
|
+
const paidResponse = await baseFetch(clonedRequest);
|
|
33088
|
+
notifyAcceptedPayment(paidResponse, payload);
|
|
33089
|
+
return paidResponse;
|
|
33016
33090
|
};
|
|
33017
33091
|
}
|
|
33018
33092
|
var DEFAULT_TTL_MS;
|
|
@@ -38467,8 +38541,8 @@ var init_models = __esm({
|
|
|
38467
38541
|
id: "openai/gpt-5.6-sol",
|
|
38468
38542
|
name: "GPT-5.6 Sol",
|
|
38469
38543
|
version: "5.6",
|
|
38470
|
-
inputPrice:
|
|
38471
|
-
outputPrice:
|
|
38544
|
+
inputPrice: 4,
|
|
38545
|
+
outputPrice: 20,
|
|
38472
38546
|
contextWindow: 105e4,
|
|
38473
38547
|
maxOutput: 128e3,
|
|
38474
38548
|
reasoning: true,
|
|
@@ -38509,8 +38583,8 @@ var init_models = __esm({
|
|
|
38509
38583
|
id: "openai/gpt-5.6-sol-pro",
|
|
38510
38584
|
name: "GPT-5.6 Sol Pro",
|
|
38511
38585
|
version: "5.6",
|
|
38512
|
-
inputPrice:
|
|
38513
|
-
outputPrice:
|
|
38586
|
+
inputPrice: 4,
|
|
38587
|
+
outputPrice: 20,
|
|
38514
38588
|
contextWindow: 105e4,
|
|
38515
38589
|
maxOutput: 128e3,
|
|
38516
38590
|
reasoning: true,
|
|
@@ -38948,8 +39022,8 @@ var init_models = __esm({
|
|
|
38948
39022
|
id: "google/gemini-3.6-flash",
|
|
38949
39023
|
name: "Gemini 3.6 Flash",
|
|
38950
39024
|
version: "3.6",
|
|
38951
|
-
inputPrice:
|
|
38952
|
-
outputPrice:
|
|
39025
|
+
inputPrice: 0.75,
|
|
39026
|
+
outputPrice: 3.75,
|
|
38953
39027
|
contextWindow: 1048576,
|
|
38954
39028
|
maxOutput: 65536,
|
|
38955
39029
|
reasoning: true,
|
|
@@ -39345,8 +39419,8 @@ var init_models = __esm({
|
|
|
39345
39419
|
id: "xai/grok-4.5",
|
|
39346
39420
|
name: "Grok 4.5",
|
|
39347
39421
|
version: "4.5",
|
|
39348
|
-
inputPrice: 2
|
|
39349
|
-
outputPrice:
|
|
39422
|
+
inputPrice: 2,
|
|
39423
|
+
outputPrice: 6,
|
|
39350
39424
|
contextWindow: 5e5,
|
|
39351
39425
|
maxOutput: 16384,
|
|
39352
39426
|
reasoning: true,
|
|
@@ -39360,8 +39434,8 @@ var init_models = __esm({
|
|
|
39360
39434
|
id: "xai/grok-4.3",
|
|
39361
39435
|
name: "Grok 4.3",
|
|
39362
39436
|
version: "4.3",
|
|
39363
|
-
inputPrice: 1.
|
|
39364
|
-
outputPrice:
|
|
39437
|
+
inputPrice: 1.25,
|
|
39438
|
+
outputPrice: 2.5,
|
|
39365
39439
|
contextWindow: 1e6,
|
|
39366
39440
|
maxOutput: 16384,
|
|
39367
39441
|
reasoning: true,
|
|
@@ -39373,8 +39447,8 @@ var init_models = __esm({
|
|
|
39373
39447
|
id: "xai/grok-build-0.1",
|
|
39374
39448
|
name: "Grok Build 0.1",
|
|
39375
39449
|
version: "0.1",
|
|
39376
|
-
inputPrice: 1
|
|
39377
|
-
outputPrice:
|
|
39450
|
+
inputPrice: 1,
|
|
39451
|
+
outputPrice: 2,
|
|
39378
39452
|
contextWindow: 256e3,
|
|
39379
39453
|
maxOutput: 16384,
|
|
39380
39454
|
agentic: true,
|
|
@@ -59980,13 +60054,18 @@ async function loadPaymentChain() {
|
|
|
59980
60054
|
const core = await readOptional(CORE_CHAIN_FILE);
|
|
59981
60055
|
if (core === "solana") return "solana";
|
|
59982
60056
|
if (core === "base") return "base";
|
|
59983
|
-
|
|
59984
|
-
|
|
59985
|
-
|
|
59986
|
-
|
|
59987
|
-
|
|
59988
|
-
|
|
59989
|
-
|
|
60057
|
+
const legacy = await readOptional(CHAIN_FILE);
|
|
60058
|
+
if (legacy === "solana") return "solana";
|
|
60059
|
+
if (legacy === "base") return "base";
|
|
60060
|
+
return await hasExistingWallet() ? "base" : "solana";
|
|
60061
|
+
}
|
|
60062
|
+
async function hasExistingWallet() {
|
|
60063
|
+
if (process["env"].BLOCKRUN_WALLET_KEY?.trim()) return true;
|
|
60064
|
+
const [core, legacy] = await Promise.all([
|
|
60065
|
+
readOptional(CORE_WALLET_FILE),
|
|
60066
|
+
readOptional(WALLET_FILE)
|
|
60067
|
+
]);
|
|
60068
|
+
return Boolean(core || legacy);
|
|
59990
60069
|
}
|
|
59991
60070
|
async function resolvePaymentChain() {
|
|
59992
60071
|
if (process["env"].CLAWROUTER_PAYMENT_CHAIN === "solana") return "solana";
|
|
@@ -60120,7 +60199,7 @@ var init_api_key = __esm({
|
|
|
60120
60199
|
HINTS = {
|
|
60121
60200
|
401: `Check BLOCKRUN_API_KEY, or mint a new key at ${PORTAL_KEYS_URL}.`,
|
|
60122
60201
|
402: `Top up your BlockRun credit at ${PORTAL_CREDITS_URL}.`,
|
|
60123
|
-
404: `api.blockrun.ai does not serve this endpoint
|
|
60202
|
+
404: `api.blockrun.ai does not serve this endpoint. Chat, Anthropic-shaped messages, images, speech, video and the partner APIs (Surf, Exa, prediction markets, phone lookup) all work on an API key. The wallet-only exceptions are the routes that bind a lease or a position to a payer address \u2014 buying/renewing/releasing phone numbers, and Polymarket trading \u2014 which need a wallet to own the thing being bought. For those, unset BLOCKRUN_API_KEY and run "clawrouter logout".`
|
|
60124
60203
|
};
|
|
60125
60204
|
}
|
|
60126
60205
|
});
|
|
@@ -91588,7 +91667,7 @@ async function startProxy(options) {
|
|
|
91588
91667
|
if (x402 && account) {
|
|
91589
91668
|
const evmPublicClient = createPublicClient({ chain: base, transport: http() });
|
|
91590
91669
|
const evmSigner = toClientEvmSigner(account, evmPublicClient);
|
|
91591
|
-
const spendControl = options.spendControl ??
|
|
91670
|
+
const spendControl = options.spendControl ?? getSharedSpendControl();
|
|
91592
91671
|
registerSpendPolicyHook(x402, spendControl);
|
|
91593
91672
|
registerExactEvmScheme(x402, { signer: evmSigner });
|
|
91594
91673
|
}
|
|
@@ -91620,7 +91699,11 @@ async function startProxy(options) {
|
|
|
91620
91699
|
// Per-request cost estimate so pre-auth is only reused when the cached
|
|
91621
91700
|
// payment still covers the (possibly larger) request — BlockRun prices per
|
|
91622
91701
|
// token, so one model can cost different amounts across requests.
|
|
91623
|
-
estimateAmount
|
|
91702
|
+
estimateAmount,
|
|
91703
|
+
// Only the wallet rail settles per call. The API-key rail is billed
|
|
91704
|
+
// server-side against account credit, so there is no per-request
|
|
91705
|
+
// settlement for an observer to report.
|
|
91706
|
+
onPayment: options.onPayment
|
|
91624
91707
|
});
|
|
91625
91708
|
let balanceMonitor;
|
|
91626
91709
|
if (options._balanceMonitorOverride) {
|
|
@@ -92500,7 +92583,8 @@ async function startProxy(options) {
|
|
|
92500
92583
|
}
|
|
92501
92584
|
return;
|
|
92502
92585
|
}
|
|
92503
|
-
|
|
92586
|
+
const isVerbatimGatewayPath = req.url === "/v1/messages" || (req.url?.startsWith("/v1/messages?") ?? false) || req.url === "/v1/audio/speech" || (req.url?.startsWith("/v1/audio/speech?") ?? false);
|
|
92587
|
+
if (isVerbatimGatewayPath || req.url?.match(
|
|
92504
92588
|
/^\/v1\/(?:partner|pm|exa|modal|stocks|usstock|crypto|fx|commodity|phone|voice|surf)\//
|
|
92505
92589
|
)) {
|
|
92506
92590
|
try {
|
|
@@ -92532,6 +92616,19 @@ async function startProxy(options) {
|
|
|
92532
92616
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
92533
92617
|
return;
|
|
92534
92618
|
}
|
|
92619
|
+
if (!req.url.includes("/chat/completions")) {
|
|
92620
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
92621
|
+
res.end(
|
|
92622
|
+
JSON.stringify({
|
|
92623
|
+
error: {
|
|
92624
|
+
message: `ClawRouter does not proxy ${req.url}. It serves /v1/chat/completions, /v1/messages, /v1/models, /v1/images/generations, /v1/images/image2image, /v1/videos/generations, /v1/audio/generations, /v1/audio/speech, and the partner prefixes /v1/{partner,pm,exa,modal,stocks,usstock,crypto,fx,commodity,phone,voice,surf}/. If BlockRun serves this endpoint, call it directly against the gateway.`,
|
|
92625
|
+
type: "invalid_request_error",
|
|
92626
|
+
code: "unsupported_endpoint"
|
|
92627
|
+
}
|
|
92628
|
+
})
|
|
92629
|
+
);
|
|
92630
|
+
return;
|
|
92631
|
+
}
|
|
92535
92632
|
try {
|
|
92536
92633
|
await proxyRequest(
|
|
92537
92634
|
req,
|
|
@@ -117892,9 +117989,7 @@ var init_client5 = __esm({
|
|
|
117892
117989
|
|
|
117893
117990
|
// src/polymarket/spend-policy.ts
|
|
117894
117991
|
function resolveSpendControl(deps) {
|
|
117895
|
-
|
|
117896
|
-
defaultControl ??= new SpendControl();
|
|
117897
|
-
return defaultControl;
|
|
117992
|
+
return deps?.spendControl ?? getSharedSpendControl();
|
|
117898
117993
|
}
|
|
117899
117994
|
function usdToMicros(usd) {
|
|
117900
117995
|
return String(Math.ceil(usd * 1e6));
|
|
@@ -117911,7 +118006,6 @@ async function signUnderSpendPolicy(deps, quote, action, sign4) {
|
|
|
117911
118006
|
throw err;
|
|
117912
118007
|
}
|
|
117913
118008
|
}
|
|
117914
|
-
var defaultControl;
|
|
117915
118009
|
var init_spend_policy = __esm({
|
|
117916
118010
|
"src/polymarket/spend-policy.ts"() {
|
|
117917
118011
|
"use strict";
|
|
@@ -227276,7 +227370,7 @@ var init_fund = __esm({
|
|
|
227276
227370
|
});
|
|
227277
227371
|
|
|
227278
227372
|
// src/polymarket/tool.ts
|
|
227279
|
-
function buildPolymarketTool() {
|
|
227373
|
+
function buildPolymarketTool(deps) {
|
|
227280
227374
|
return {
|
|
227281
227375
|
name: "blockrun_polymarket",
|
|
227282
227376
|
description: DESCRIPTION,
|
|
@@ -227361,26 +227455,32 @@ function buildPolymarketTool() {
|
|
|
227361
227455
|
result = await runSetup({ confirm });
|
|
227362
227456
|
break;
|
|
227363
227457
|
case "fund":
|
|
227364
|
-
result = await fundVault(
|
|
227365
|
-
|
|
227366
|
-
|
|
227367
|
-
|
|
227458
|
+
result = await fundVault(
|
|
227459
|
+
{
|
|
227460
|
+
amount_usd: params.amount_usd,
|
|
227461
|
+
confirm
|
|
227462
|
+
},
|
|
227463
|
+
deps
|
|
227464
|
+
);
|
|
227368
227465
|
break;
|
|
227369
227466
|
case "buy":
|
|
227370
227467
|
case "sell":
|
|
227371
|
-
result = await executeTrade(
|
|
227372
|
-
|
|
227373
|
-
|
|
227374
|
-
|
|
227375
|
-
|
|
227376
|
-
|
|
227377
|
-
|
|
227378
|
-
|
|
227379
|
-
|
|
227380
|
-
|
|
227381
|
-
|
|
227382
|
-
|
|
227383
|
-
|
|
227468
|
+
result = await executeTrade(
|
|
227469
|
+
{
|
|
227470
|
+
action,
|
|
227471
|
+
token_id: params.token_id,
|
|
227472
|
+
condition_id: params.condition_id,
|
|
227473
|
+
outcome: params.outcome,
|
|
227474
|
+
price: params.price,
|
|
227475
|
+
size: params.size,
|
|
227476
|
+
amount_usd: params.amount_usd,
|
|
227477
|
+
order_type: params.order_type,
|
|
227478
|
+
expires_at: params.expires_at,
|
|
227479
|
+
post_only: params.post_only,
|
|
227480
|
+
confirm
|
|
227481
|
+
},
|
|
227482
|
+
deps
|
|
227483
|
+
);
|
|
227384
227484
|
break;
|
|
227385
227485
|
case "orders":
|
|
227386
227486
|
result = await listOpenOrders({
|
|
@@ -227397,17 +227497,23 @@ function buildPolymarketTool() {
|
|
|
227397
227497
|
result = await listPositions();
|
|
227398
227498
|
break;
|
|
227399
227499
|
case "redeem":
|
|
227400
|
-
result = await redeemPosition(
|
|
227401
|
-
|
|
227402
|
-
|
|
227403
|
-
|
|
227500
|
+
result = await redeemPosition(
|
|
227501
|
+
{
|
|
227502
|
+
condition_id: params.condition_id,
|
|
227503
|
+
confirm
|
|
227504
|
+
},
|
|
227505
|
+
deps
|
|
227506
|
+
);
|
|
227404
227507
|
break;
|
|
227405
227508
|
case "withdraw":
|
|
227406
|
-
result = await withdrawFunds(
|
|
227407
|
-
|
|
227408
|
-
|
|
227409
|
-
|
|
227410
|
-
|
|
227509
|
+
result = await withdrawFunds(
|
|
227510
|
+
{
|
|
227511
|
+
amount_usd: params.amount_usd,
|
|
227512
|
+
to_address: params.to_address,
|
|
227513
|
+
confirm
|
|
227514
|
+
},
|
|
227515
|
+
deps
|
|
227516
|
+
);
|
|
227411
227517
|
break;
|
|
227412
227518
|
default:
|
|
227413
227519
|
throw new Error(`Unknown blockrun_polymarket action: ${action}`);
|
|
@@ -227823,6 +227929,7 @@ __export(index_exports, {
|
|
|
227823
227929
|
SolanaBalanceMonitor: () => SolanaBalanceMonitor,
|
|
227824
227930
|
SpendControl: () => SpendControl,
|
|
227825
227931
|
SpendPolicyError: () => SpendPolicyError,
|
|
227932
|
+
UnreadableSpendPolicyError: () => UnreadableSpendPolicyError,
|
|
227826
227933
|
VISIBLE_OPENCLAW_MODELS: () => VISIBLE_OPENCLAW_MODELS,
|
|
227827
227934
|
blockrunProvider: () => blockrunProvider,
|
|
227828
227935
|
buildImageGenerationProvider: () => buildImageGenerationProvider,
|
|
@@ -228403,12 +228510,18 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
228403
228510
|
`Cost cap: $${maxCostPerRunUsd.toFixed(2)} per session (mode: ${maxCostPerRunMode})`
|
|
228404
228511
|
);
|
|
228405
228512
|
}
|
|
228513
|
+
const sharedControl = getSharedSpendControl();
|
|
228514
|
+
sharedControl.reloadLimits();
|
|
228515
|
+
sharedControl.resetSession();
|
|
228406
228516
|
const proxy = await startProxy({
|
|
228407
228517
|
...apiKey ? { apiKey } : { wallet },
|
|
228408
228518
|
routingConfig,
|
|
228409
228519
|
maxCostPerRunUsd,
|
|
228410
228520
|
maxCostPerRunMode,
|
|
228411
|
-
|
|
228521
|
+
// The process-wide ledger: the polymarket tool and the /policy command
|
|
228522
|
+
// registered below use the same instance, so hourly/daily/session windows
|
|
228523
|
+
// cover every surface and a /policy write reaches the live signer.
|
|
228524
|
+
spendControl: sharedControl,
|
|
228412
228525
|
onReady: (port) => {
|
|
228413
228526
|
api.logger.info(`BlockRun ${apiKey ? "API-key" : "x402"} proxy listening on port ${port}`);
|
|
228414
228527
|
},
|
|
@@ -229151,7 +229264,7 @@ function createWalletCommand(api) {
|
|
|
229151
229264
|
}
|
|
229152
229265
|
};
|
|
229153
229266
|
}
|
|
229154
|
-
var activeProxyHandle,
|
|
229267
|
+
var activeProxyHandle, pendingConfiguredStartupApi, IMAGE_DIR2, AUDIO_DIR2, VIDEO_DIR2, plugin, index_default2;
|
|
229155
229268
|
var init_index = __esm({
|
|
229156
229269
|
"src/index.ts"() {
|
|
229157
229270
|
"use strict";
|
|
@@ -229170,10 +229283,10 @@ var init_index = __esm({
|
|
|
229170
229283
|
init_stats();
|
|
229171
229284
|
init_partners();
|
|
229172
229285
|
init_tool();
|
|
229286
|
+
init_spend_control();
|
|
229173
229287
|
init_stats2();
|
|
229174
229288
|
init_exclude();
|
|
229175
229289
|
init_policy();
|
|
229176
|
-
init_spend_control();
|
|
229177
229290
|
init_mcp_config();
|
|
229178
229291
|
init_openclaw_plugin_config();
|
|
229179
229292
|
init_openclaw_plugin_config();
|
|
@@ -229195,7 +229308,6 @@ var init_index = __esm({
|
|
|
229195
229308
|
init_response_cache();
|
|
229196
229309
|
init_partners();
|
|
229197
229310
|
activeProxyHandle = null;
|
|
229198
|
-
liveSpendControl = null;
|
|
229199
229311
|
pendingConfiguredStartupApi = null;
|
|
229200
229312
|
IMAGE_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "images");
|
|
229201
229313
|
AUDIO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "audio");
|
|
@@ -229511,7 +229623,7 @@ ${errText}`
|
|
|
229511
229623
|
api.registerCommand(createExcludeCommand());
|
|
229512
229624
|
api.registerCommand(
|
|
229513
229625
|
createPolicyCommand({
|
|
229514
|
-
liveControl: () =>
|
|
229626
|
+
liveControl: () => process.__clawrouterProxyStarted ? getSharedSpendControl() : void 0
|
|
229515
229627
|
})
|
|
229516
229628
|
);
|
|
229517
229629
|
if (shouldLogRegistration) {
|
|
@@ -229540,7 +229652,15 @@ ${errText}`
|
|
|
229540
229652
|
});
|
|
229541
229653
|
if (!isGatewayMode()) {
|
|
229542
229654
|
if (shouldLogRegistration) {
|
|
229543
|
-
|
|
229655
|
+
void (async () => {
|
|
229656
|
+
const keyResolution = await resolveApiKey().catch(() => void 0);
|
|
229657
|
+
if (keyResolution) {
|
|
229658
|
+
api.logger.info(
|
|
229659
|
+
`Using BlockRun API key ${maskApiKey(keyResolution.key)} (from ${keyResolution.source}) \u2014 billing account credit, no wallet`
|
|
229660
|
+
);
|
|
229661
|
+
return;
|
|
229662
|
+
}
|
|
229663
|
+
const { address: address2, source } = await resolveOrGenerateWalletKey();
|
|
229544
229664
|
if (source === "generated") {
|
|
229545
229665
|
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`);
|
|
229546
229666
|
api.logger.warn(` NEW WALLET GENERATED \u2014 BACK UP YOUR KEY NOW!`);
|
|
@@ -229557,7 +229677,7 @@ ${errText}`
|
|
|
229557
229677
|
} else {
|
|
229558
229678
|
api.logger.info(`Using wallet from BLOCKRUN_WALLET_KEY: ${address2}`);
|
|
229559
229679
|
}
|
|
229560
|
-
}).catch((err) => {
|
|
229680
|
+
})().catch((err) => {
|
|
229561
229681
|
api.logger.warn(
|
|
229562
229682
|
`Failed to initialize wallet: ${err instanceof Error ? err.message : String(err)}`
|
|
229563
229683
|
);
|
|
@@ -230089,7 +230209,7 @@ function createDoctorX402Client(opts) {
|
|
|
230089
230209
|
const publicClient = createPublicClient({ chain: base, transport: http() });
|
|
230090
230210
|
const evmSigner = toClientEvmSigner(account, publicClient);
|
|
230091
230211
|
const x402 = new x402Client();
|
|
230092
|
-
registerSpendPolicyHook(x402, opts.spendControl ??
|
|
230212
|
+
registerSpendPolicyHook(x402, opts.spendControl ?? getSharedSpendControl());
|
|
230093
230213
|
registerExactEvmScheme(x402, { signer: evmSigner });
|
|
230094
230214
|
return x402;
|
|
230095
230215
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -665,6 +665,14 @@ declare const PAYABLE_NETWORKS: readonly string[];
|
|
|
665
665
|
* pay, which is the one direction this file must never fail in. Callers
|
|
666
666
|
* classify on `instanceof`, not on the message text.
|
|
667
667
|
*/
|
|
668
|
+
/**
|
|
669
|
+
* spending.json exists but could not be read or parsed. Distinct from "no file
|
|
670
|
+
* yet" (load() returns null) so a reload can tell a failed read from an empty
|
|
671
|
+
* store and refuse to widen what the agent may pay.
|
|
672
|
+
*/
|
|
673
|
+
declare class UnreadableSpendPolicyError extends Error {
|
|
674
|
+
constructor(cause: unknown);
|
|
675
|
+
}
|
|
668
676
|
declare class MalformedSpendPolicyError extends Error {
|
|
669
677
|
constructor(key: string);
|
|
670
678
|
}
|
|
@@ -816,6 +824,15 @@ declare class SpendControl {
|
|
|
816
824
|
* reporting a change as applied.
|
|
817
825
|
*/
|
|
818
826
|
getPolicyFileError(): string | undefined;
|
|
827
|
+
/**
|
|
828
|
+
* Re-read limits from storage, keeping this instance's history and open
|
|
829
|
+
* reservations. Used on an in-process proxy restart so a hand-edit to
|
|
830
|
+
* spending.json made while the process was running still applies, without
|
|
831
|
+
* resetting the rolling windows. Fails closed exactly like the constructor:
|
|
832
|
+
* a malformed file refuses every payment until repaired, and a repaired
|
|
833
|
+
* file clears that refusal.
|
|
834
|
+
*/
|
|
835
|
+
reloadLimits(): void;
|
|
819
836
|
check(estimatedCost: number, counterparty?: CounterpartyInfo): CheckResult;
|
|
820
837
|
record(amount: number, metadata?: {
|
|
821
838
|
model?: string;
|
|
@@ -850,6 +867,16 @@ declare class SpendControl {
|
|
|
850
867
|
getRemaining(window: "hourly" | "daily" | "session"): number | null;
|
|
851
868
|
getStatus(): SpendingStatus;
|
|
852
869
|
getHistory(limit?: number): SpendRecord[];
|
|
870
|
+
/**
|
|
871
|
+
* Reset the session window. `sessionSpent`/`sessionCalls` are instance state
|
|
872
|
+
* that is never persisted, so this used to happen implicitly: every
|
|
873
|
+
* startProxy() built a fresh SpendControl. The process-wide ledger outlives
|
|
874
|
+
* an in-process proxy restart, which would silently redefine `session` as
|
|
875
|
+
* "since the gateway booted" — docs/configuration.md and the /policy help
|
|
876
|
+
* both promise "session resets on restart". The restart path in index.ts
|
|
877
|
+
* calls this to keep that promise. History and the rolling hourly/daily
|
|
878
|
+
* windows are deliberately untouched.
|
|
879
|
+
*/
|
|
853
880
|
resetSession(): void;
|
|
854
881
|
private cleanup;
|
|
855
882
|
private save;
|
|
@@ -1221,13 +1248,29 @@ declare function setupSolana(): Promise<{
|
|
|
1221
1248
|
declare function savePaymentChain(chain: "base" | "solana"): Promise<void>;
|
|
1222
1249
|
/**
|
|
1223
1250
|
* Load the persisted payment chain selection from disk.
|
|
1224
|
-
*
|
|
1225
|
-
*
|
|
1226
|
-
*
|
|
1251
|
+
*
|
|
1252
|
+
* Solana is the preferred rail, and since v0.12.246 wallet generation persists
|
|
1253
|
+
* `solana` outright — so a fresh install answers "solana" from its own chain
|
|
1254
|
+
* file, not from a default. This function only decides the case where no chain
|
|
1255
|
+
* file exists at all, and there the answer turns on whether a wallet already
|
|
1256
|
+
* does:
|
|
1257
|
+
*
|
|
1258
|
+
* no chain file, no wallet → "solana" (nothing to strand; prefer Solana)
|
|
1259
|
+
* no chain file, wallet on disk → "base" (a pre-v0.12.246 install)
|
|
1260
|
+
*
|
|
1261
|
+
* That second line is the one that must not move. An install predating the
|
|
1262
|
+
* Solana default has USDC sitting in its Base wallet and no Solana balance;
|
|
1263
|
+
* flipping it would point every request at a gateway its money is not on and
|
|
1264
|
+
* fail them all with an empty balance. The same reasoning covers a wallet
|
|
1265
|
+
* supplied through BLOCKRUN_WALLET_KEY, and a generated wallet whose Solana
|
|
1266
|
+
* derivation failed (no chain file is written in that case, on purpose — see
|
|
1267
|
+
* resolveOrGenerateWalletKey — and the wallet it did write keeps us on Base,
|
|
1268
|
+
* which is the only chain that proxy can actually sign for).
|
|
1227
1269
|
*/
|
|
1228
1270
|
declare function loadPaymentChain(): Promise<"base" | "solana">;
|
|
1229
1271
|
/**
|
|
1230
|
-
* Resolve payment chain: env var first → persisted file second →
|
|
1272
|
+
* Resolve payment chain: env var first → persisted file second → the
|
|
1273
|
+
* fresh-install preference (Solana) last. See loadPaymentChain.
|
|
1231
1274
|
*/
|
|
1232
1275
|
declare function resolvePaymentChain(): Promise<"base" | "solana">;
|
|
1233
1276
|
|
|
@@ -1814,4 +1857,4 @@ declare function buildImageGenerationProvider(): ImageGenerationProviderPlugin;
|
|
|
1814
1857
|
|
|
1815
1858
|
declare const plugin: OpenClawPluginDefinition;
|
|
1816
1859
|
|
|
1817
|
-
export { type AggregatedStats, BALANCE_THRESHOLDS, BLOCKRUN_MODELS, BLOCKRUN_PLUGIN_ID, type BalanceInfo, BalanceMonitor, CAIP2_BASE, CAIP2_SOLANA_MAINNET, type CachedLLMResponse, type CachedResponse, type CheckResult, type CounterpartyInfo, DEFAULT_RETRY_CONFIG, DEFAULT_SESSION_CONFIG, type DailyStats, type DerivedKeys, EmptyWalletError, FileSpendControlStorage, InMemorySpendControlStorage, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, MODEL_ALIASES, MalformedSpendPolicyError, OPENCLAW_MODELS, PARTNER_SERVICES, PAYABLE_NETWORKS, type PartnerServiceDefinition, type PartnerToolDefinition, type PaymentChain, type PolicyList, type ProxyHandle, type ProxyOptions, RequestDeduplicator, ResponseCache, type ResponseCacheConfig, type RetryConfig, RpcError, type SessionConfig, type SessionEntry, SessionStore, type SolanaBalanceInfo, SolanaBalanceMonitor, SpendControl, type SpendControlOptions, type SpendControlStorage, type SpendLimits, SpendPolicyError, type SpendRecord, type SpendWindow, type SpendingStatus, type SufficiencyResult, type UsageEntry, VISIBLE_OPENCLAW_MODELS, type WalletConfig, type WalletResolution, blockrunProvider, buildImageGenerationProvider, buildPartnerTools, buildProviderModels, clearStats, plugin as default, deriveAllKeys, deriveEvmKey, deriveSolanaKeyBytes, fetchWithRetry, formatDuration, formatStatsAscii, generateWalletMnemonic, getAgenticModels, getModelContextWindow, getPartnerService, getProxyPort, getSessionId, getStats, hashRequestContent, injectAuthProfile, injectModelsConfig, isAgenticModel, isBalanceError, isBlockrunWebSearchDisabled, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, isValidMnemonic, loadPaymentChain, logUsage, parseCallArgs, registerSpendPolicyHook, resolveModelAlias, resolvePaymentChain, savePaymentChain, setupSolana, startProxy, syncAgentModelCache };
|
|
1860
|
+
export { type AggregatedStats, BALANCE_THRESHOLDS, BLOCKRUN_MODELS, BLOCKRUN_PLUGIN_ID, type BalanceInfo, BalanceMonitor, CAIP2_BASE, CAIP2_SOLANA_MAINNET, type CachedLLMResponse, type CachedResponse, type CheckResult, type CounterpartyInfo, DEFAULT_RETRY_CONFIG, DEFAULT_SESSION_CONFIG, type DailyStats, type DerivedKeys, EmptyWalletError, FileSpendControlStorage, InMemorySpendControlStorage, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, MODEL_ALIASES, MalformedSpendPolicyError, OPENCLAW_MODELS, PARTNER_SERVICES, PAYABLE_NETWORKS, type PartnerServiceDefinition, type PartnerToolDefinition, type PaymentChain, type PolicyList, type ProxyHandle, type ProxyOptions, RequestDeduplicator, ResponseCache, type ResponseCacheConfig, type RetryConfig, RpcError, type SessionConfig, type SessionEntry, SessionStore, type SolanaBalanceInfo, SolanaBalanceMonitor, SpendControl, type SpendControlOptions, type SpendControlStorage, type SpendLimits, SpendPolicyError, type SpendRecord, type SpendWindow, type SpendingStatus, type SufficiencyResult, UnreadableSpendPolicyError, type UsageEntry, VISIBLE_OPENCLAW_MODELS, type WalletConfig, type WalletResolution, blockrunProvider, buildImageGenerationProvider, buildPartnerTools, buildProviderModels, clearStats, plugin as default, deriveAllKeys, deriveEvmKey, deriveSolanaKeyBytes, fetchWithRetry, formatDuration, formatStatsAscii, generateWalletMnemonic, getAgenticModels, getModelContextWindow, getPartnerService, getProxyPort, getSessionId, getStats, hashRequestContent, injectAuthProfile, injectModelsConfig, isAgenticModel, isBalanceError, isBlockrunWebSearchDisabled, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, isValidMnemonic, loadPaymentChain, logUsage, parseCallArgs, registerSpendPolicyHook, resolveModelAlias, resolvePaymentChain, savePaymentChain, setupSolana, startProxy, syncAgentModelCache };
|