@blockrun/clawrouter 0.12.268 → 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 +21 -0
- package/dist/cli.js +141 -48
- package/dist/index.d.ts +28 -1
- package/dist/index.js +140 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,23 @@ Or neither — <!-- br:models.free -->7<!-- /br:models.free --> models are free,
|
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
|
+
## What BlockRun is
|
|
44
|
+
|
|
45
|
+
> **BlockRun lets agents pay for the outcome — every LLM, tool and data source, best value per dollar.**
|
|
46
|
+
|
|
47
|
+
Not a seat, not a subscription, not a monthly minimum you keep paying while the agent idles. An agent asks for one thing — an answer, an image, a transcript, a price, a call placed — and pays for that one thing at the moment it happens. Nothing to sign up for, nothing to cancel.
|
|
48
|
+
|
|
49
|
+
| | |
|
|
50
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| **Every LLM** | <!-- br:models.chatVisible -->76<!-- /br:models.chatVisible --> chat models across 9 providers, behind one endpoint and one balance |
|
|
52
|
+
| **Every tool** | <!-- br:models.image -->9<!-- /br:models.image --> image models, <!-- br:models.video -->8<!-- /br:models.video --> video models, music, <!-- br:models.speech -->5<!-- /br:models.speech --> voices, img2img editing, and outbound phone calls that come back as transcripts |
|
|
53
|
+
| **Every data source** | web, news and neural search; prediction markets; live crypto and equity quotes; on-chain SQL over 100M+ labeled wallets; DEX routing; RPC across <!-- br:chains.rpc -->40<!-- /br:chains.rpc --> chains |
|
|
54
|
+
| **Best value per dollar** | <!-- br:clawrouter.dimensions -->15<!-- /br:clawrouter.dimensions -->-dimension local routing sends each request to the cheapest model that can actually do it — <!-- br:savings.autoVsBaselinePct -->84<!-- /br:savings.autoVsBaselinePct -->% below pinning Claude Opus 5 on the same traffic, computed from a published workload mix rather than estimated |
|
|
55
|
+
|
|
56
|
+
**ClawRouter is how an agent reaches it.** BlockRun is the gateway and the billing; ClawRouter is the MIT-licensed router that runs on your machine, decides which model each request deserves, and settles the bill — a wallet signature in USDC, or a credit-card-funded API key. The rest of this README is about ClawRouter.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
43
60
|
## Why ClawRouter exists
|
|
44
61
|
|
|
45
62
|
Every other LLM router was built for **human developers** — create an account, get an API key, pick a model from a dashboard, pay with a credit card.
|
|
@@ -868,6 +885,10 @@ Python plugin that wraps the ClawRouter proxy for `hermes-agent`. Same <!-- br:m
|
|
|
868
885
|
|
|
869
886
|
## Frequently Asked Questions
|
|
870
887
|
|
|
888
|
+
### What is BlockRun?
|
|
889
|
+
|
|
890
|
+
BlockRun lets agents pay for the outcome — every LLM, tool and data source, best value per dollar. One endpoint and one balance covering <!-- br:models.chatVisible -->76<!-- /br:models.chatVisible --> chat models plus image, video, music and speech generation, search, market data and multi-chain RPC, billed per call with USDC over [x402](https://x402.org) or a credit-card-funded API key — no seat, no subscription, no minimum. ClawRouter is the open-source local router agents use to reach it.
|
|
891
|
+
|
|
871
892
|
### What is ClawRouter?
|
|
872
893
|
|
|
873
894
|
ClawRouter is an open-source (MIT licensed) smart LLM router built for autonomous AI agents. It analyzes each request across <!-- br:clawrouter.dimensions -->15<!-- /br:clawrouter.dimensions --> dimensions and routes to the cheapest capable model in under 1ms, entirely locally — no external API calls needed for routing decisions.
|
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;
|
|
@@ -91588,7 +91662,7 @@ async function startProxy(options) {
|
|
|
91588
91662
|
if (x402 && account) {
|
|
91589
91663
|
const evmPublicClient = createPublicClient({ chain: base, transport: http() });
|
|
91590
91664
|
const evmSigner = toClientEvmSigner(account, evmPublicClient);
|
|
91591
|
-
const spendControl = options.spendControl ??
|
|
91665
|
+
const spendControl = options.spendControl ?? getSharedSpendControl();
|
|
91592
91666
|
registerSpendPolicyHook(x402, spendControl);
|
|
91593
91667
|
registerExactEvmScheme(x402, { signer: evmSigner });
|
|
91594
91668
|
}
|
|
@@ -91620,7 +91694,11 @@ async function startProxy(options) {
|
|
|
91620
91694
|
// Per-request cost estimate so pre-auth is only reused when the cached
|
|
91621
91695
|
// payment still covers the (possibly larger) request — BlockRun prices per
|
|
91622
91696
|
// token, so one model can cost different amounts across requests.
|
|
91623
|
-
estimateAmount
|
|
91697
|
+
estimateAmount,
|
|
91698
|
+
// Only the wallet rail settles per call. The API-key rail is billed
|
|
91699
|
+
// server-side against account credit, so there is no per-request
|
|
91700
|
+
// settlement for an observer to report.
|
|
91701
|
+
onPayment: options.onPayment
|
|
91624
91702
|
});
|
|
91625
91703
|
let balanceMonitor;
|
|
91626
91704
|
if (options._balanceMonitorOverride) {
|
|
@@ -117892,9 +117970,7 @@ var init_client5 = __esm({
|
|
|
117892
117970
|
|
|
117893
117971
|
// src/polymarket/spend-policy.ts
|
|
117894
117972
|
function resolveSpendControl(deps) {
|
|
117895
|
-
|
|
117896
|
-
defaultControl ??= new SpendControl();
|
|
117897
|
-
return defaultControl;
|
|
117973
|
+
return deps?.spendControl ?? getSharedSpendControl();
|
|
117898
117974
|
}
|
|
117899
117975
|
function usdToMicros(usd) {
|
|
117900
117976
|
return String(Math.ceil(usd * 1e6));
|
|
@@ -117911,7 +117987,6 @@ async function signUnderSpendPolicy(deps, quote, action, sign4) {
|
|
|
117911
117987
|
throw err;
|
|
117912
117988
|
}
|
|
117913
117989
|
}
|
|
117914
|
-
var defaultControl;
|
|
117915
117990
|
var init_spend_policy = __esm({
|
|
117916
117991
|
"src/polymarket/spend-policy.ts"() {
|
|
117917
117992
|
"use strict";
|
|
@@ -227276,7 +227351,7 @@ var init_fund = __esm({
|
|
|
227276
227351
|
});
|
|
227277
227352
|
|
|
227278
227353
|
// src/polymarket/tool.ts
|
|
227279
|
-
function buildPolymarketTool() {
|
|
227354
|
+
function buildPolymarketTool(deps) {
|
|
227280
227355
|
return {
|
|
227281
227356
|
name: "blockrun_polymarket",
|
|
227282
227357
|
description: DESCRIPTION,
|
|
@@ -227361,26 +227436,32 @@ function buildPolymarketTool() {
|
|
|
227361
227436
|
result = await runSetup({ confirm });
|
|
227362
227437
|
break;
|
|
227363
227438
|
case "fund":
|
|
227364
|
-
result = await fundVault(
|
|
227365
|
-
|
|
227366
|
-
|
|
227367
|
-
|
|
227439
|
+
result = await fundVault(
|
|
227440
|
+
{
|
|
227441
|
+
amount_usd: params.amount_usd,
|
|
227442
|
+
confirm
|
|
227443
|
+
},
|
|
227444
|
+
deps
|
|
227445
|
+
);
|
|
227368
227446
|
break;
|
|
227369
227447
|
case "buy":
|
|
227370
227448
|
case "sell":
|
|
227371
|
-
result = await executeTrade(
|
|
227372
|
-
|
|
227373
|
-
|
|
227374
|
-
|
|
227375
|
-
|
|
227376
|
-
|
|
227377
|
-
|
|
227378
|
-
|
|
227379
|
-
|
|
227380
|
-
|
|
227381
|
-
|
|
227382
|
-
|
|
227383
|
-
|
|
227449
|
+
result = await executeTrade(
|
|
227450
|
+
{
|
|
227451
|
+
action,
|
|
227452
|
+
token_id: params.token_id,
|
|
227453
|
+
condition_id: params.condition_id,
|
|
227454
|
+
outcome: params.outcome,
|
|
227455
|
+
price: params.price,
|
|
227456
|
+
size: params.size,
|
|
227457
|
+
amount_usd: params.amount_usd,
|
|
227458
|
+
order_type: params.order_type,
|
|
227459
|
+
expires_at: params.expires_at,
|
|
227460
|
+
post_only: params.post_only,
|
|
227461
|
+
confirm
|
|
227462
|
+
},
|
|
227463
|
+
deps
|
|
227464
|
+
);
|
|
227384
227465
|
break;
|
|
227385
227466
|
case "orders":
|
|
227386
227467
|
result = await listOpenOrders({
|
|
@@ -227397,17 +227478,23 @@ function buildPolymarketTool() {
|
|
|
227397
227478
|
result = await listPositions();
|
|
227398
227479
|
break;
|
|
227399
227480
|
case "redeem":
|
|
227400
|
-
result = await redeemPosition(
|
|
227401
|
-
|
|
227402
|
-
|
|
227403
|
-
|
|
227481
|
+
result = await redeemPosition(
|
|
227482
|
+
{
|
|
227483
|
+
condition_id: params.condition_id,
|
|
227484
|
+
confirm
|
|
227485
|
+
},
|
|
227486
|
+
deps
|
|
227487
|
+
);
|
|
227404
227488
|
break;
|
|
227405
227489
|
case "withdraw":
|
|
227406
|
-
result = await withdrawFunds(
|
|
227407
|
-
|
|
227408
|
-
|
|
227409
|
-
|
|
227410
|
-
|
|
227490
|
+
result = await withdrawFunds(
|
|
227491
|
+
{
|
|
227492
|
+
amount_usd: params.amount_usd,
|
|
227493
|
+
to_address: params.to_address,
|
|
227494
|
+
confirm
|
|
227495
|
+
},
|
|
227496
|
+
deps
|
|
227497
|
+
);
|
|
227411
227498
|
break;
|
|
227412
227499
|
default:
|
|
227413
227500
|
throw new Error(`Unknown blockrun_polymarket action: ${action}`);
|
|
@@ -227823,6 +227910,7 @@ __export(index_exports, {
|
|
|
227823
227910
|
SolanaBalanceMonitor: () => SolanaBalanceMonitor,
|
|
227824
227911
|
SpendControl: () => SpendControl,
|
|
227825
227912
|
SpendPolicyError: () => SpendPolicyError,
|
|
227913
|
+
UnreadableSpendPolicyError: () => UnreadableSpendPolicyError,
|
|
227826
227914
|
VISIBLE_OPENCLAW_MODELS: () => VISIBLE_OPENCLAW_MODELS,
|
|
227827
227915
|
blockrunProvider: () => blockrunProvider,
|
|
227828
227916
|
buildImageGenerationProvider: () => buildImageGenerationProvider,
|
|
@@ -228403,12 +228491,18 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
228403
228491
|
`Cost cap: $${maxCostPerRunUsd.toFixed(2)} per session (mode: ${maxCostPerRunMode})`
|
|
228404
228492
|
);
|
|
228405
228493
|
}
|
|
228494
|
+
const sharedControl = getSharedSpendControl();
|
|
228495
|
+
sharedControl.reloadLimits();
|
|
228496
|
+
sharedControl.resetSession();
|
|
228406
228497
|
const proxy = await startProxy({
|
|
228407
228498
|
...apiKey ? { apiKey } : { wallet },
|
|
228408
228499
|
routingConfig,
|
|
228409
228500
|
maxCostPerRunUsd,
|
|
228410
228501
|
maxCostPerRunMode,
|
|
228411
|
-
|
|
228502
|
+
// The process-wide ledger: the polymarket tool and the /policy command
|
|
228503
|
+
// registered below use the same instance, so hourly/daily/session windows
|
|
228504
|
+
// cover every surface and a /policy write reaches the live signer.
|
|
228505
|
+
spendControl: sharedControl,
|
|
228412
228506
|
onReady: (port) => {
|
|
228413
228507
|
api.logger.info(`BlockRun ${apiKey ? "API-key" : "x402"} proxy listening on port ${port}`);
|
|
228414
228508
|
},
|
|
@@ -229151,7 +229245,7 @@ function createWalletCommand(api) {
|
|
|
229151
229245
|
}
|
|
229152
229246
|
};
|
|
229153
229247
|
}
|
|
229154
|
-
var activeProxyHandle,
|
|
229248
|
+
var activeProxyHandle, pendingConfiguredStartupApi, IMAGE_DIR2, AUDIO_DIR2, VIDEO_DIR2, plugin, index_default2;
|
|
229155
229249
|
var init_index = __esm({
|
|
229156
229250
|
"src/index.ts"() {
|
|
229157
229251
|
"use strict";
|
|
@@ -229170,10 +229264,10 @@ var init_index = __esm({
|
|
|
229170
229264
|
init_stats();
|
|
229171
229265
|
init_partners();
|
|
229172
229266
|
init_tool();
|
|
229267
|
+
init_spend_control();
|
|
229173
229268
|
init_stats2();
|
|
229174
229269
|
init_exclude();
|
|
229175
229270
|
init_policy();
|
|
229176
|
-
init_spend_control();
|
|
229177
229271
|
init_mcp_config();
|
|
229178
229272
|
init_openclaw_plugin_config();
|
|
229179
229273
|
init_openclaw_plugin_config();
|
|
@@ -229195,7 +229289,6 @@ var init_index = __esm({
|
|
|
229195
229289
|
init_response_cache();
|
|
229196
229290
|
init_partners();
|
|
229197
229291
|
activeProxyHandle = null;
|
|
229198
|
-
liveSpendControl = null;
|
|
229199
229292
|
pendingConfiguredStartupApi = null;
|
|
229200
229293
|
IMAGE_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "images");
|
|
229201
229294
|
AUDIO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "audio");
|
|
@@ -229511,7 +229604,7 @@ ${errText}`
|
|
|
229511
229604
|
api.registerCommand(createExcludeCommand());
|
|
229512
229605
|
api.registerCommand(
|
|
229513
229606
|
createPolicyCommand({
|
|
229514
|
-
liveControl: () =>
|
|
229607
|
+
liveControl: () => process.__clawrouterProxyStarted ? getSharedSpendControl() : void 0
|
|
229515
229608
|
})
|
|
229516
229609
|
);
|
|
229517
229610
|
if (shouldLogRegistration) {
|
|
@@ -230089,7 +230182,7 @@ function createDoctorX402Client(opts) {
|
|
|
230089
230182
|
const publicClient = createPublicClient({ chain: base, transport: http() });
|
|
230090
230183
|
const evmSigner = toClientEvmSigner(account, publicClient);
|
|
230091
230184
|
const x402 = new x402Client();
|
|
230092
|
-
registerSpendPolicyHook(x402, opts.spendControl ??
|
|
230185
|
+
registerSpendPolicyHook(x402, opts.spendControl ?? getSharedSpendControl());
|
|
230093
230186
|
registerExactEvmScheme(x402, { signer: evmSigner });
|
|
230094
230187
|
return x402;
|
|
230095
230188
|
}
|
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;
|
|
@@ -1814,4 +1841,4 @@ declare function buildImageGenerationProvider(): ImageGenerationProviderPlugin;
|
|
|
1814
1841
|
|
|
1815
1842
|
declare const plugin: OpenClawPluginDefinition;
|
|
1816
1843
|
|
|
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 };
|
|
1844
|
+
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 };
|
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;
|
|
@@ -91375,7 +91449,7 @@ async function startProxy(options) {
|
|
|
91375
91449
|
if (x402 && account) {
|
|
91376
91450
|
const evmPublicClient = createPublicClient({ chain: base, transport: http() });
|
|
91377
91451
|
const evmSigner = toClientEvmSigner(account, evmPublicClient);
|
|
91378
|
-
const spendControl = options.spendControl ??
|
|
91452
|
+
const spendControl = options.spendControl ?? getSharedSpendControl();
|
|
91379
91453
|
registerSpendPolicyHook(x402, spendControl);
|
|
91380
91454
|
registerExactEvmScheme(x402, { signer: evmSigner });
|
|
91381
91455
|
}
|
|
@@ -91407,7 +91481,11 @@ async function startProxy(options) {
|
|
|
91407
91481
|
// Per-request cost estimate so pre-auth is only reused when the cached
|
|
91408
91482
|
// payment still covers the (possibly larger) request — BlockRun prices per
|
|
91409
91483
|
// token, so one model can cost different amounts across requests.
|
|
91410
|
-
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
|
|
91411
91489
|
});
|
|
91412
91490
|
let balanceMonitor;
|
|
91413
91491
|
if (options._balanceMonitorOverride) {
|
|
@@ -117475,9 +117553,7 @@ var init_client5 = __esm({
|
|
|
117475
117553
|
|
|
117476
117554
|
// src/polymarket/spend-policy.ts
|
|
117477
117555
|
function resolveSpendControl(deps) {
|
|
117478
|
-
|
|
117479
|
-
defaultControl ??= new SpendControl();
|
|
117480
|
-
return defaultControl;
|
|
117556
|
+
return deps?.spendControl ?? getSharedSpendControl();
|
|
117481
117557
|
}
|
|
117482
117558
|
function usdToMicros(usd) {
|
|
117483
117559
|
return String(Math.ceil(usd * 1e6));
|
|
@@ -117494,7 +117570,6 @@ async function signUnderSpendPolicy(deps, quote, action, sign4) {
|
|
|
117494
117570
|
throw err;
|
|
117495
117571
|
}
|
|
117496
117572
|
}
|
|
117497
|
-
var defaultControl;
|
|
117498
117573
|
var init_spend_policy = __esm({
|
|
117499
117574
|
"src/polymarket/spend-policy.ts"() {
|
|
117500
117575
|
"use strict";
|
|
@@ -226859,7 +226934,7 @@ var init_fund = __esm({
|
|
|
226859
226934
|
});
|
|
226860
226935
|
|
|
226861
226936
|
// src/polymarket/tool.ts
|
|
226862
|
-
function buildPolymarketTool() {
|
|
226937
|
+
function buildPolymarketTool(deps) {
|
|
226863
226938
|
return {
|
|
226864
226939
|
name: "blockrun_polymarket",
|
|
226865
226940
|
description: DESCRIPTION,
|
|
@@ -226944,26 +227019,32 @@ function buildPolymarketTool() {
|
|
|
226944
227019
|
result = await runSetup({ confirm });
|
|
226945
227020
|
break;
|
|
226946
227021
|
case "fund":
|
|
226947
|
-
result = await fundVault(
|
|
226948
|
-
|
|
226949
|
-
|
|
226950
|
-
|
|
227022
|
+
result = await fundVault(
|
|
227023
|
+
{
|
|
227024
|
+
amount_usd: params.amount_usd,
|
|
227025
|
+
confirm
|
|
227026
|
+
},
|
|
227027
|
+
deps
|
|
227028
|
+
);
|
|
226951
227029
|
break;
|
|
226952
227030
|
case "buy":
|
|
226953
227031
|
case "sell":
|
|
226954
|
-
result = await executeTrade(
|
|
226955
|
-
|
|
226956
|
-
|
|
226957
|
-
|
|
226958
|
-
|
|
226959
|
-
|
|
226960
|
-
|
|
226961
|
-
|
|
226962
|
-
|
|
226963
|
-
|
|
226964
|
-
|
|
226965
|
-
|
|
226966
|
-
|
|
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
|
+
);
|
|
226967
227048
|
break;
|
|
226968
227049
|
case "orders":
|
|
226969
227050
|
result = await listOpenOrders({
|
|
@@ -226980,17 +227061,23 @@ function buildPolymarketTool() {
|
|
|
226980
227061
|
result = await listPositions();
|
|
226981
227062
|
break;
|
|
226982
227063
|
case "redeem":
|
|
226983
|
-
result = await redeemPosition(
|
|
226984
|
-
|
|
226985
|
-
|
|
226986
|
-
|
|
227064
|
+
result = await redeemPosition(
|
|
227065
|
+
{
|
|
227066
|
+
condition_id: params.condition_id,
|
|
227067
|
+
confirm
|
|
227068
|
+
},
|
|
227069
|
+
deps
|
|
227070
|
+
);
|
|
226987
227071
|
break;
|
|
226988
227072
|
case "withdraw":
|
|
226989
|
-
result = await withdrawFunds(
|
|
226990
|
-
|
|
226991
|
-
|
|
226992
|
-
|
|
226993
|
-
|
|
227073
|
+
result = await withdrawFunds(
|
|
227074
|
+
{
|
|
227075
|
+
amount_usd: params.amount_usd,
|
|
227076
|
+
to_address: params.to_address,
|
|
227077
|
+
confirm
|
|
227078
|
+
},
|
|
227079
|
+
deps
|
|
227080
|
+
);
|
|
226994
227081
|
break;
|
|
226995
227082
|
default:
|
|
226996
227083
|
throw new Error(`Unknown blockrun_polymarket action: ${action}`);
|
|
@@ -228110,12 +228197,18 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
228110
228197
|
`Cost cap: $${maxCostPerRunUsd.toFixed(2)} per session (mode: ${maxCostPerRunMode})`
|
|
228111
228198
|
);
|
|
228112
228199
|
}
|
|
228200
|
+
const sharedControl = getSharedSpendControl();
|
|
228201
|
+
sharedControl.reloadLimits();
|
|
228202
|
+
sharedControl.resetSession();
|
|
228113
228203
|
const proxy = await startProxy({
|
|
228114
228204
|
...apiKey ? { apiKey } : { wallet },
|
|
228115
228205
|
routingConfig,
|
|
228116
228206
|
maxCostPerRunUsd,
|
|
228117
228207
|
maxCostPerRunMode,
|
|
228118
|
-
|
|
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,
|
|
228119
228212
|
onReady: (port) => {
|
|
228120
228213
|
api.logger.info(`BlockRun ${apiKey ? "API-key" : "x402"} proxy listening on port ${port}`);
|
|
228121
228214
|
},
|
|
@@ -228858,7 +228951,7 @@ function createWalletCommand(api) {
|
|
|
228858
228951
|
}
|
|
228859
228952
|
};
|
|
228860
228953
|
}
|
|
228861
|
-
var activeProxyHandle,
|
|
228954
|
+
var activeProxyHandle, pendingConfiguredStartupApi, IMAGE_DIR2, AUDIO_DIR2, VIDEO_DIR2, plugin, index_default2;
|
|
228862
228955
|
var init_index = __esm({
|
|
228863
228956
|
"src/index.ts"() {
|
|
228864
228957
|
init_provider();
|
|
@@ -228876,10 +228969,10 @@ var init_index = __esm({
|
|
|
228876
228969
|
init_stats();
|
|
228877
228970
|
init_partners();
|
|
228878
228971
|
init_tool();
|
|
228972
|
+
init_spend_control();
|
|
228879
228973
|
init_stats2();
|
|
228880
228974
|
init_exclude();
|
|
228881
228975
|
init_policy();
|
|
228882
|
-
init_spend_control();
|
|
228883
228976
|
init_mcp_config();
|
|
228884
228977
|
init_openclaw_plugin_config();
|
|
228885
228978
|
init_openclaw_plugin_config();
|
|
@@ -228901,7 +228994,6 @@ var init_index = __esm({
|
|
|
228901
228994
|
init_response_cache();
|
|
228902
228995
|
init_partners();
|
|
228903
228996
|
activeProxyHandle = null;
|
|
228904
|
-
liveSpendControl = null;
|
|
228905
228997
|
pendingConfiguredStartupApi = null;
|
|
228906
228998
|
IMAGE_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "images");
|
|
228907
228999
|
AUDIO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "audio");
|
|
@@ -229217,7 +229309,7 @@ ${errText}`
|
|
|
229217
229309
|
api.registerCommand(createExcludeCommand());
|
|
229218
229310
|
api.registerCommand(
|
|
229219
229311
|
createPolicyCommand({
|
|
229220
|
-
liveControl: () =>
|
|
229312
|
+
liveControl: () => process.__clawrouterProxyStarted ? getSharedSpendControl() : void 0
|
|
229221
229313
|
})
|
|
229222
229314
|
);
|
|
229223
229315
|
if (shouldLogRegistration) {
|
|
@@ -229415,6 +229507,7 @@ export {
|
|
|
229415
229507
|
SolanaBalanceMonitor,
|
|
229416
229508
|
SpendControl,
|
|
229417
229509
|
SpendPolicyError,
|
|
229510
|
+
UnreadableSpendPolicyError,
|
|
229418
229511
|
VISIBLE_OPENCLAW_MODELS,
|
|
229419
229512
|
blockrunProvider,
|
|
229420
229513
|
buildImageGenerationProvider,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/clawrouter",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.269",
|
|
4
4
|
"description": "Smart LLM router — save 84% on inference costs. 76 models (7 free). Pay by credit card (API key) or USDC (x402 wallet).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|