@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/index.js
CHANGED
|
@@ -783,8 +783,8 @@ var init_models = __esm({
|
|
|
783
783
|
id: "openai/gpt-5.6-sol",
|
|
784
784
|
name: "GPT-5.6 Sol",
|
|
785
785
|
version: "5.6",
|
|
786
|
-
inputPrice:
|
|
787
|
-
outputPrice:
|
|
786
|
+
inputPrice: 4,
|
|
787
|
+
outputPrice: 20,
|
|
788
788
|
contextWindow: 105e4,
|
|
789
789
|
maxOutput: 128e3,
|
|
790
790
|
reasoning: true,
|
|
@@ -825,8 +825,8 @@ var init_models = __esm({
|
|
|
825
825
|
id: "openai/gpt-5.6-sol-pro",
|
|
826
826
|
name: "GPT-5.6 Sol Pro",
|
|
827
827
|
version: "5.6",
|
|
828
|
-
inputPrice:
|
|
829
|
-
outputPrice:
|
|
828
|
+
inputPrice: 4,
|
|
829
|
+
outputPrice: 20,
|
|
830
830
|
contextWindow: 105e4,
|
|
831
831
|
maxOutput: 128e3,
|
|
832
832
|
reasoning: true,
|
|
@@ -1264,8 +1264,8 @@ var init_models = __esm({
|
|
|
1264
1264
|
id: "google/gemini-3.6-flash",
|
|
1265
1265
|
name: "Gemini 3.6 Flash",
|
|
1266
1266
|
version: "3.6",
|
|
1267
|
-
inputPrice:
|
|
1268
|
-
outputPrice:
|
|
1267
|
+
inputPrice: 0.75,
|
|
1268
|
+
outputPrice: 3.75,
|
|
1269
1269
|
contextWindow: 1048576,
|
|
1270
1270
|
maxOutput: 65536,
|
|
1271
1271
|
reasoning: true,
|
|
@@ -1661,8 +1661,8 @@ var init_models = __esm({
|
|
|
1661
1661
|
id: "xai/grok-4.5",
|
|
1662
1662
|
name: "Grok 4.5",
|
|
1663
1663
|
version: "4.5",
|
|
1664
|
-
inputPrice: 2
|
|
1665
|
-
outputPrice:
|
|
1664
|
+
inputPrice: 2,
|
|
1665
|
+
outputPrice: 6,
|
|
1666
1666
|
contextWindow: 5e5,
|
|
1667
1667
|
maxOutput: 16384,
|
|
1668
1668
|
reasoning: true,
|
|
@@ -1676,8 +1676,8 @@ var init_models = __esm({
|
|
|
1676
1676
|
id: "xai/grok-4.3",
|
|
1677
1677
|
name: "Grok 4.3",
|
|
1678
1678
|
version: "4.3",
|
|
1679
|
-
inputPrice: 1.
|
|
1680
|
-
outputPrice:
|
|
1679
|
+
inputPrice: 1.25,
|
|
1680
|
+
outputPrice: 2.5,
|
|
1681
1681
|
contextWindow: 1e6,
|
|
1682
1682
|
maxOutput: 16384,
|
|
1683
1683
|
reasoning: true,
|
|
@@ -1689,8 +1689,8 @@ var init_models = __esm({
|
|
|
1689
1689
|
id: "xai/grok-build-0.1",
|
|
1690
1690
|
name: "Grok Build 0.1",
|
|
1691
1691
|
version: "0.1",
|
|
1692
|
-
inputPrice: 1
|
|
1693
|
-
outputPrice:
|
|
1692
|
+
inputPrice: 1,
|
|
1693
|
+
outputPrice: 2,
|
|
1694
1694
|
contextWindow: 256e3,
|
|
1695
1695
|
maxOutput: 16384,
|
|
1696
1696
|
agentic: true,
|
|
@@ -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;
|
|
@@ -59787,13 +59861,18 @@ async function loadPaymentChain() {
|
|
|
59787
59861
|
const core = await readOptional(CORE_CHAIN_FILE);
|
|
59788
59862
|
if (core === "solana") return "solana";
|
|
59789
59863
|
if (core === "base") return "base";
|
|
59790
|
-
|
|
59791
|
-
|
|
59792
|
-
|
|
59793
|
-
|
|
59794
|
-
|
|
59795
|
-
|
|
59796
|
-
|
|
59864
|
+
const legacy = await readOptional(CHAIN_FILE);
|
|
59865
|
+
if (legacy === "solana") return "solana";
|
|
59866
|
+
if (legacy === "base") return "base";
|
|
59867
|
+
return await hasExistingWallet() ? "base" : "solana";
|
|
59868
|
+
}
|
|
59869
|
+
async function hasExistingWallet() {
|
|
59870
|
+
if (process["env"].BLOCKRUN_WALLET_KEY?.trim()) return true;
|
|
59871
|
+
const [core, legacy] = await Promise.all([
|
|
59872
|
+
readOptional(CORE_WALLET_FILE),
|
|
59873
|
+
readOptional(WALLET_FILE)
|
|
59874
|
+
]);
|
|
59875
|
+
return Boolean(core || legacy);
|
|
59797
59876
|
}
|
|
59798
59877
|
async function resolvePaymentChain() {
|
|
59799
59878
|
if (process["env"].CLAWROUTER_PAYMENT_CHAIN === "solana") return "solana";
|
|
@@ -59907,7 +59986,7 @@ var init_api_key = __esm({
|
|
|
59907
59986
|
HINTS = {
|
|
59908
59987
|
401: `Check BLOCKRUN_API_KEY, or mint a new key at ${PORTAL_KEYS_URL}.`,
|
|
59909
59988
|
402: `Top up your BlockRun credit at ${PORTAL_CREDITS_URL}.`,
|
|
59910
|
-
404: `api.blockrun.ai does not serve this endpoint
|
|
59989
|
+
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".`
|
|
59911
59990
|
};
|
|
59912
59991
|
}
|
|
59913
59992
|
});
|
|
@@ -91375,7 +91454,7 @@ async function startProxy(options) {
|
|
|
91375
91454
|
if (x402 && account) {
|
|
91376
91455
|
const evmPublicClient = createPublicClient({ chain: base, transport: http() });
|
|
91377
91456
|
const evmSigner = toClientEvmSigner(account, evmPublicClient);
|
|
91378
|
-
const spendControl = options.spendControl ??
|
|
91457
|
+
const spendControl = options.spendControl ?? getSharedSpendControl();
|
|
91379
91458
|
registerSpendPolicyHook(x402, spendControl);
|
|
91380
91459
|
registerExactEvmScheme(x402, { signer: evmSigner });
|
|
91381
91460
|
}
|
|
@@ -91407,7 +91486,11 @@ async function startProxy(options) {
|
|
|
91407
91486
|
// Per-request cost estimate so pre-auth is only reused when the cached
|
|
91408
91487
|
// payment still covers the (possibly larger) request — BlockRun prices per
|
|
91409
91488
|
// token, so one model can cost different amounts across requests.
|
|
91410
|
-
estimateAmount
|
|
91489
|
+
estimateAmount,
|
|
91490
|
+
// Only the wallet rail settles per call. The API-key rail is billed
|
|
91491
|
+
// server-side against account credit, so there is no per-request
|
|
91492
|
+
// settlement for an observer to report.
|
|
91493
|
+
onPayment: options.onPayment
|
|
91411
91494
|
});
|
|
91412
91495
|
let balanceMonitor;
|
|
91413
91496
|
if (options._balanceMonitorOverride) {
|
|
@@ -92287,7 +92370,8 @@ async function startProxy(options) {
|
|
|
92287
92370
|
}
|
|
92288
92371
|
return;
|
|
92289
92372
|
}
|
|
92290
|
-
|
|
92373
|
+
const isVerbatimGatewayPath = req.url === "/v1/messages" || (req.url?.startsWith("/v1/messages?") ?? false) || req.url === "/v1/audio/speech" || (req.url?.startsWith("/v1/audio/speech?") ?? false);
|
|
92374
|
+
if (isVerbatimGatewayPath || req.url?.match(
|
|
92291
92375
|
/^\/v1\/(?:partner|pm|exa|modal|stocks|usstock|crypto|fx|commodity|phone|voice|surf)\//
|
|
92292
92376
|
)) {
|
|
92293
92377
|
try {
|
|
@@ -92319,6 +92403,19 @@ async function startProxy(options) {
|
|
|
92319
92403
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
92320
92404
|
return;
|
|
92321
92405
|
}
|
|
92406
|
+
if (!req.url.includes("/chat/completions")) {
|
|
92407
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
92408
|
+
res.end(
|
|
92409
|
+
JSON.stringify({
|
|
92410
|
+
error: {
|
|
92411
|
+
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.`,
|
|
92412
|
+
type: "invalid_request_error",
|
|
92413
|
+
code: "unsupported_endpoint"
|
|
92414
|
+
}
|
|
92415
|
+
})
|
|
92416
|
+
);
|
|
92417
|
+
return;
|
|
92418
|
+
}
|
|
92322
92419
|
try {
|
|
92323
92420
|
await proxyRequest(
|
|
92324
92421
|
req,
|
|
@@ -117475,9 +117572,7 @@ var init_client5 = __esm({
|
|
|
117475
117572
|
|
|
117476
117573
|
// src/polymarket/spend-policy.ts
|
|
117477
117574
|
function resolveSpendControl(deps) {
|
|
117478
|
-
|
|
117479
|
-
defaultControl ??= new SpendControl();
|
|
117480
|
-
return defaultControl;
|
|
117575
|
+
return deps?.spendControl ?? getSharedSpendControl();
|
|
117481
117576
|
}
|
|
117482
117577
|
function usdToMicros(usd) {
|
|
117483
117578
|
return String(Math.ceil(usd * 1e6));
|
|
@@ -117494,7 +117589,6 @@ async function signUnderSpendPolicy(deps, quote, action, sign4) {
|
|
|
117494
117589
|
throw err;
|
|
117495
117590
|
}
|
|
117496
117591
|
}
|
|
117497
|
-
var defaultControl;
|
|
117498
117592
|
var init_spend_policy = __esm({
|
|
117499
117593
|
"src/polymarket/spend-policy.ts"() {
|
|
117500
117594
|
"use strict";
|
|
@@ -226859,7 +226953,7 @@ var init_fund = __esm({
|
|
|
226859
226953
|
});
|
|
226860
226954
|
|
|
226861
226955
|
// src/polymarket/tool.ts
|
|
226862
|
-
function buildPolymarketTool() {
|
|
226956
|
+
function buildPolymarketTool(deps) {
|
|
226863
226957
|
return {
|
|
226864
226958
|
name: "blockrun_polymarket",
|
|
226865
226959
|
description: DESCRIPTION,
|
|
@@ -226944,26 +227038,32 @@ function buildPolymarketTool() {
|
|
|
226944
227038
|
result = await runSetup({ confirm });
|
|
226945
227039
|
break;
|
|
226946
227040
|
case "fund":
|
|
226947
|
-
result = await fundVault(
|
|
226948
|
-
|
|
226949
|
-
|
|
226950
|
-
|
|
227041
|
+
result = await fundVault(
|
|
227042
|
+
{
|
|
227043
|
+
amount_usd: params.amount_usd,
|
|
227044
|
+
confirm
|
|
227045
|
+
},
|
|
227046
|
+
deps
|
|
227047
|
+
);
|
|
226951
227048
|
break;
|
|
226952
227049
|
case "buy":
|
|
226953
227050
|
case "sell":
|
|
226954
|
-
result = await executeTrade(
|
|
226955
|
-
|
|
226956
|
-
|
|
226957
|
-
|
|
226958
|
-
|
|
226959
|
-
|
|
226960
|
-
|
|
226961
|
-
|
|
226962
|
-
|
|
226963
|
-
|
|
226964
|
-
|
|
226965
|
-
|
|
226966
|
-
|
|
227051
|
+
result = await executeTrade(
|
|
227052
|
+
{
|
|
227053
|
+
action,
|
|
227054
|
+
token_id: params.token_id,
|
|
227055
|
+
condition_id: params.condition_id,
|
|
227056
|
+
outcome: params.outcome,
|
|
227057
|
+
price: params.price,
|
|
227058
|
+
size: params.size,
|
|
227059
|
+
amount_usd: params.amount_usd,
|
|
227060
|
+
order_type: params.order_type,
|
|
227061
|
+
expires_at: params.expires_at,
|
|
227062
|
+
post_only: params.post_only,
|
|
227063
|
+
confirm
|
|
227064
|
+
},
|
|
227065
|
+
deps
|
|
227066
|
+
);
|
|
226967
227067
|
break;
|
|
226968
227068
|
case "orders":
|
|
226969
227069
|
result = await listOpenOrders({
|
|
@@ -226980,17 +227080,23 @@ function buildPolymarketTool() {
|
|
|
226980
227080
|
result = await listPositions();
|
|
226981
227081
|
break;
|
|
226982
227082
|
case "redeem":
|
|
226983
|
-
result = await redeemPosition(
|
|
226984
|
-
|
|
226985
|
-
|
|
226986
|
-
|
|
227083
|
+
result = await redeemPosition(
|
|
227084
|
+
{
|
|
227085
|
+
condition_id: params.condition_id,
|
|
227086
|
+
confirm
|
|
227087
|
+
},
|
|
227088
|
+
deps
|
|
227089
|
+
);
|
|
226987
227090
|
break;
|
|
226988
227091
|
case "withdraw":
|
|
226989
|
-
result = await withdrawFunds(
|
|
226990
|
-
|
|
226991
|
-
|
|
226992
|
-
|
|
226993
|
-
|
|
227092
|
+
result = await withdrawFunds(
|
|
227093
|
+
{
|
|
227094
|
+
amount_usd: params.amount_usd,
|
|
227095
|
+
to_address: params.to_address,
|
|
227096
|
+
confirm
|
|
227097
|
+
},
|
|
227098
|
+
deps
|
|
227099
|
+
);
|
|
226994
227100
|
break;
|
|
226995
227101
|
default:
|
|
226996
227102
|
throw new Error(`Unknown blockrun_polymarket action: ${action}`);
|
|
@@ -228110,12 +228216,18 @@ async function startProxyInBackground(api, startupGeneration) {
|
|
|
228110
228216
|
`Cost cap: $${maxCostPerRunUsd.toFixed(2)} per session (mode: ${maxCostPerRunMode})`
|
|
228111
228217
|
);
|
|
228112
228218
|
}
|
|
228219
|
+
const sharedControl = getSharedSpendControl();
|
|
228220
|
+
sharedControl.reloadLimits();
|
|
228221
|
+
sharedControl.resetSession();
|
|
228113
228222
|
const proxy = await startProxy({
|
|
228114
228223
|
...apiKey ? { apiKey } : { wallet },
|
|
228115
228224
|
routingConfig,
|
|
228116
228225
|
maxCostPerRunUsd,
|
|
228117
228226
|
maxCostPerRunMode,
|
|
228118
|
-
|
|
228227
|
+
// The process-wide ledger: the polymarket tool and the /policy command
|
|
228228
|
+
// registered below use the same instance, so hourly/daily/session windows
|
|
228229
|
+
// cover every surface and a /policy write reaches the live signer.
|
|
228230
|
+
spendControl: sharedControl,
|
|
228119
228231
|
onReady: (port) => {
|
|
228120
228232
|
api.logger.info(`BlockRun ${apiKey ? "API-key" : "x402"} proxy listening on port ${port}`);
|
|
228121
228233
|
},
|
|
@@ -228858,7 +228970,7 @@ function createWalletCommand(api) {
|
|
|
228858
228970
|
}
|
|
228859
228971
|
};
|
|
228860
228972
|
}
|
|
228861
|
-
var activeProxyHandle,
|
|
228973
|
+
var activeProxyHandle, pendingConfiguredStartupApi, IMAGE_DIR2, AUDIO_DIR2, VIDEO_DIR2, plugin, index_default2;
|
|
228862
228974
|
var init_index = __esm({
|
|
228863
228975
|
"src/index.ts"() {
|
|
228864
228976
|
init_provider();
|
|
@@ -228876,10 +228988,10 @@ var init_index = __esm({
|
|
|
228876
228988
|
init_stats();
|
|
228877
228989
|
init_partners();
|
|
228878
228990
|
init_tool();
|
|
228991
|
+
init_spend_control();
|
|
228879
228992
|
init_stats2();
|
|
228880
228993
|
init_exclude();
|
|
228881
228994
|
init_policy();
|
|
228882
|
-
init_spend_control();
|
|
228883
228995
|
init_mcp_config();
|
|
228884
228996
|
init_openclaw_plugin_config();
|
|
228885
228997
|
init_openclaw_plugin_config();
|
|
@@ -228901,7 +229013,6 @@ var init_index = __esm({
|
|
|
228901
229013
|
init_response_cache();
|
|
228902
229014
|
init_partners();
|
|
228903
229015
|
activeProxyHandle = null;
|
|
228904
|
-
liveSpendControl = null;
|
|
228905
229016
|
pendingConfiguredStartupApi = null;
|
|
228906
229017
|
IMAGE_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "images");
|
|
228907
229018
|
AUDIO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "audio");
|
|
@@ -229217,7 +229328,7 @@ ${errText}`
|
|
|
229217
229328
|
api.registerCommand(createExcludeCommand());
|
|
229218
229329
|
api.registerCommand(
|
|
229219
229330
|
createPolicyCommand({
|
|
229220
|
-
liveControl: () =>
|
|
229331
|
+
liveControl: () => process.__clawrouterProxyStarted ? getSharedSpendControl() : void 0
|
|
229221
229332
|
})
|
|
229222
229333
|
);
|
|
229223
229334
|
if (shouldLogRegistration) {
|
|
@@ -229246,7 +229357,15 @@ ${errText}`
|
|
|
229246
229357
|
});
|
|
229247
229358
|
if (!isGatewayMode()) {
|
|
229248
229359
|
if (shouldLogRegistration) {
|
|
229249
|
-
|
|
229360
|
+
void (async () => {
|
|
229361
|
+
const keyResolution = await resolveApiKey().catch(() => void 0);
|
|
229362
|
+
if (keyResolution) {
|
|
229363
|
+
api.logger.info(
|
|
229364
|
+
`Using BlockRun API key ${maskApiKey(keyResolution.key)} (from ${keyResolution.source}) \u2014 billing account credit, no wallet`
|
|
229365
|
+
);
|
|
229366
|
+
return;
|
|
229367
|
+
}
|
|
229368
|
+
const { address: address2, source } = await resolveOrGenerateWalletKey();
|
|
229250
229369
|
if (source === "generated") {
|
|
229251
229370
|
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`);
|
|
229252
229371
|
api.logger.warn(` NEW WALLET GENERATED \u2014 BACK UP YOUR KEY NOW!`);
|
|
@@ -229263,7 +229382,7 @@ ${errText}`
|
|
|
229263
229382
|
} else {
|
|
229264
229383
|
api.logger.info(`Using wallet from BLOCKRUN_WALLET_KEY: ${address2}`);
|
|
229265
229384
|
}
|
|
229266
|
-
}).catch((err) => {
|
|
229385
|
+
})().catch((err) => {
|
|
229267
229386
|
api.logger.warn(
|
|
229268
229387
|
`Failed to initialize wallet: ${err instanceof Error ? err.message : String(err)}`
|
|
229269
229388
|
);
|
|
@@ -229415,6 +229534,7 @@ export {
|
|
|
229415
229534
|
SolanaBalanceMonitor,
|
|
229416
229535
|
SpendControl,
|
|
229417
229536
|
SpendPolicyError,
|
|
229537
|
+
UnreadableSpendPolicyError,
|
|
229418
229538
|
VISIBLE_OPENCLAW_MODELS,
|
|
229419
229539
|
blockrunProvider,
|
|
229420
229540
|
buildImageGenerationProvider,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/clawrouter",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.270",
|
|
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",
|
|
@@ -53,7 +53,7 @@ metadata:
|
|
|
53
53
|
|
|
54
54
|
# ClawRouter
|
|
55
55
|
|
|
56
|
-
Hosted-gateway LLM router that saves <!-- br:savings.autoVsBaselinePct -->84<!-- /br:savings.autoVsBaselinePct -->% on inference costs by forwarding each request to the blockrun.ai gateway, which picks the cheapest model capable of handling it across <!-- br:models.chatVisible -->76<!-- /br:models.chatVisible --> models from 9 providers (<!-- br:models.free -->7<!-- /br:models.free --> free open-weight models). All billing flows through one USDC wallet
|
|
56
|
+
Hosted-gateway LLM router that saves <!-- br:savings.autoVsBaselinePct -->84<!-- /br:savings.autoVsBaselinePct -->% on inference costs by forwarding each request to the blockrun.ai gateway, which picks the cheapest model capable of handling it across <!-- br:models.chatVisible -->76<!-- /br:models.chatVisible --> models from 9 providers (<!-- br:models.free -->7<!-- /br:models.free --> free open-weight models). All billing flows through one BlockRun credential — either a USDC wallet paying x402 per call, or a BlockRun API key drawing on card-funded account credit. Either way you do not hold provider API keys.
|
|
57
57
|
|
|
58
58
|
**This is not a local-inference tool.** ClawRouter is a thin local proxy. Your prompts are sent over HTTPS to the blockrun.ai gateway for model execution. If your workload requires inference that never leaves your machine, use a local runtime like Ollama — ClawRouter is not the right tool for that use case.
|
|
59
59
|
|
|
@@ -69,7 +69,7 @@ Your app → localhost proxy (ClawRouter) → https://blockrun.ai/api (or sol.b
|
|
|
69
69
|
Response → back through proxy → your app
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
**Sent to blockrun.ai on every request:** the model name, the full prompt/messages body, sampling params (temperature, max_tokens, tools, etc.), and an `X-PAYMENT` header containing a signed x402 USDC micropayment.
|
|
72
|
+
**Sent to blockrun.ai on every request:** the model name, the full prompt/messages body, sampling params (temperature, max_tokens, tools, etc.), and a payment credential — an `X-PAYMENT` header containing a signed x402 USDC micropayment in wallet mode, or an `Authorization: Bearer brk_live_…` header in API-key mode.
|
|
73
73
|
|
|
74
74
|
**Not sent:** your wallet private key (only the detached payment signature is sent), any other local files, environment variables, or OpenClaw config beyond what's needed for this request.
|
|
75
75
|
|
|
@@ -81,12 +81,12 @@ ClawRouter does **not** collect or forward third-party provider API keys. You do
|
|
|
81
81
|
|
|
82
82
|
**What `models.providers.blockrun` stores (fully enumerated):**
|
|
83
83
|
|
|
84
|
-
| Field | Sensitive
|
|
85
|
-
| ----------- |
|
|
86
|
-
| `walletKey` |
|
|
87
|
-
| `solanaKey` | Yes
|
|
88
|
-
| `gateway` | No
|
|
89
|
-
| `routing` | No
|
|
84
|
+
| Field | Sensitive | Purpose |
|
|
85
|
+
| ----------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `walletKey` | Only in wallet mode | EVM private key used to sign USDC micropayments via x402. **Auto-generated locally on first run** — no user input required. Never transmitted over the network; only detached payment signatures are sent. Not used, read, or generated at all when a BlockRun API key is configured. |
|
|
87
|
+
| `solanaKey` | Yes | Solana keypair (BIP-44 `m/44'/501'/0'/0'`). Auto-derived from the same local mnemonic via `@scure/bip32` + `@scure/bip39`. |
|
|
88
|
+
| `gateway` | No | Gateway URL. Defaults: `https://sol.blockrun.ai/api` (Solana, default chain for new installs) · `https://blockrun.ai/api` (Base, default for pre-existing installs). |
|
|
89
|
+
| `routing` | No | Optional override of the router-core config (tier chains, `strategy: "rules"` rollback, `shadow` comparison, scorer keywords). |
|
|
90
90
|
|
|
91
91
|
**How and where keys are stored:**
|
|
92
92
|
|
package/skills/imagegen/SKILL.md
CHANGED
|
@@ -137,8 +137,8 @@ Response is identical to generation:
|
|
|
137
137
|
|
|
138
138
|
## Notes
|
|
139
139
|
|
|
140
|
-
- Payment is automatic
|
|
141
|
-
- If the call fails with a payment error,
|
|
140
|
+
- Payment is automatic, and which rail it uses depends on how ClawRouter was started: an x402 USDC micropayment from the user's wallet, or a draw on account credit if a BlockRun API key is configured. Either way the agent does nothing.
|
|
141
|
+
- If the call fails with a payment error, check `GET http://localhost:8402/health` and read `authMode` before telling the user how to fix it: `wallet` → fund the wallet at [blockrun.ai](https://blockrun.ai); `api-key` → top up account credit at [user.blockrun.ai/dashboard/credits](https://user.blockrun.ai/dashboard/credits). Naming the wrong one sends the user to a page that cannot fix their error.
|
|
142
142
|
- Google models may return base64 internally — ClawRouter uploads automatically and returns a hosted URL
|
|
143
143
|
- OpenAI image models enforce OpenAI content policy; use `nano-banana` or `grok-imagine` for more flexibility
|
|
144
144
|
- Image editing is only available with `gpt-image-1`; generation supports all listed models
|
package/skills/phone/SKILL.md
CHANGED
|
@@ -200,8 +200,9 @@ GET `http://localhost:8402/v1/voice/call/{call_id}`. Returns:
|
|
|
200
200
|
|
|
201
201
|
## Notes
|
|
202
202
|
|
|
203
|
-
- Payment is automatic
|
|
204
|
-
- If a call fails with a 402,
|
|
203
|
+
- Payment is automatic on every call — an x402 USDC micropayment from the user's wallet, or a draw on account credit if a BlockRun API key is configured.
|
|
204
|
+
- If a call fails with a 402, check `GET http://localhost:8402/health` and read `authMode` first: `wallet` → fund the wallet at [blockrun.ai](https://blockrun.ai); `api-key` → top up at [user.blockrun.ai/dashboard/credits](https://user.blockrun.ai/dashboard/credits).
|
|
205
|
+
- Number leases are bound to the **wallet's** payer address, so `phone numbers list/buy/renew/release` and the `from` parameter are wallet-mode features. In API-key mode the gateway answers those routes with a raw `402` x402 challenge (verified 2026-09-05) because there is no payer address to bind a lease to. Lookups, fraud checks and outbound calls all work on an API key; just let the server auto-pick `from`.
|
|
205
206
|
- Phone numbers are real, regulated resources — numbers bought are reachable from any phone within ~60 seconds of purchase
|
|
206
207
|
- Bland.ai's emergency-number blocklist is enforced server-side; ClawRouter does not duplicate it but trusts upstream
|
|
207
208
|
- Recordings and transcripts are retained by Bland.ai; ClawRouter does not download them locally (returns the upstream URL)
|
package/skills/predexon/SKILL.md
CHANGED
|
@@ -33,7 +33,7 @@ metadata: { "openclaw": { "emoji": "📊", "requires": { "config": ["models.prov
|
|
|
33
33
|
|
|
34
34
|
**IMPORTANT: Always use this skill for any Polymarket, Kalshi, Limitless, Opinion, Predict.Fun, or prediction market request. Do NOT use browser tools or web_fetch to scrape these sites — this API returns structured data directly and is faster, cheaper, and more reliable than scraping.**
|
|
35
35
|
|
|
36
|
-
Real-time prediction market data (Polymarket, Kalshi, Limitless, Opinion, Predict.Fun, dFlow, Binance, UMA oracle) via BlockRun's
|
|
36
|
+
Real-time prediction market data (Polymarket, Kalshi, Limitless, Opinion, Predict.Fun, dFlow, Binance, UMA oracle) via BlockRun's gateway. Payment is automatic — an x402 USDC micropayment from the user's wallet, or a draw on BlockRun account credit if an API key is configured (both verified 2026-09-05).
|
|
37
37
|
|
|
38
38
|
**All responses are wrapped:** `{ "data": { ... } }` — always read from `response.data`.
|
|
39
39
|
|
package/skills/surf/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: surf
|
|
3
|
-
description: Use this skill — NOT browser or web_fetch — for ALL Surf crypto-data calls. 83 endpoints at localhost:8402/v1/surf/* covering CEX/DEX markets, on-chain SQL over 80+ ClickHouse tables (Ethereum, Base, Arbitrum, BSC, TRON, HyperEVM, Tempo), 100M+ labeled wallets, prediction markets (Polymarket + Kalshi), social/CT intelligence, news, project + DeFi metrics, token analytics, unified search, VC fund intelligence.
|
|
3
|
+
description: Use this skill — NOT browser or web_fetch — for ALL Surf crypto-data calls. 83 endpoints at localhost:8402/v1/surf/* covering CEX/DEX markets, on-chain SQL over 80+ ClickHouse tables (Ethereum, Base, Arbitrum, BSC, TRON, HyperEVM, Tempo), 100M+ labeled wallets, prediction markets (Polymarket + Kalshi), social/CT intelligence, news, project + DeFi metrics, token analytics, unified search, VC fund intelligence. Paid through ClawRouter — x402 USDC from the local wallet, or BlockRun account credit if an API key is configured. No Surf account or Surf API key required either way.
|
|
4
4
|
triggers:
|
|
5
5
|
- "blockrun surf"
|
|
6
6
|
- "surf crypto api"
|
|
@@ -24,7 +24,7 @@ license: MIT
|
|
|
24
24
|
|
|
25
25
|
# Surf — Unified Crypto Data API (via ClawRouter)
|
|
26
26
|
|
|
27
|
-
Surf bundles **83 endpoints across 12 domains** into one paid HTTP API. ClawRouter exposes them at `http://127.0.0.1:8402/v1/surf/*`, paid
|
|
27
|
+
Surf bundles **83 endpoints across 12 domains** into one paid HTTP API. ClawRouter exposes them at `http://127.0.0.1:8402/v1/surf/*`, paid the same way ClawRouter pays for LLM calls — an x402 USDC micropayment from the local wallet, or a draw on BlockRun account credit if an API key is configured (verified working on both, 2026-09-05). **No Surf account and no Surf API key either way.** Upstream lives at `api.asksurf.ai/gateway/v1` — ClawRouter forwards transparently.
|
|
28
28
|
|
|
29
29
|
**Pricing tiers (per call):**
|
|
30
30
|
|
|
@@ -34,9 +34,9 @@ Surf bundles **83 endpoints across 12 domains** into one paid HTTP API. ClawRout
|
|
|
34
34
|
|
|
35
35
|
> The legacy **surf-1.5 chat** surface is intentionally NOT exposed yet — it's held until per-token settlement is wired. Trying `/v1/surf/chat/completions` returns 404 ("Unknown Surf endpoint"), no payment is taken.
|
|
36
36
|
|
|
37
|
-
All requests use GET unless the table below says otherwise. Path parameters that look like `?symbol=` are query params on a GET. POST endpoints take a JSON body. ClawRouter
|
|
37
|
+
All requests use GET unless the table below says otherwise. Path parameters that look like `?symbol=` are query params on a GET. POST endpoints take a JSON body. ClawRouter attaches payment transparently — an x402 header in wallet mode, a bearer token in API-key mode.
|
|
38
38
|
|
|
39
|
-
**Required-param pre-check.** 56 of the 83 endpoints have required query params (e.g. `pair`, `symbol`, `address`, `chain`, `q`, `interval`). The route validates them **before settlement** — call with missing params and you get `400 { missing_params, all_required, docs }` and
|
|
39
|
+
**Required-param pre-check.** 56 of the 83 endpoints have required query params (e.g. `pair`, `symbol`, `address`, `chain`, `q`, `interval`). The route validates them **before settlement** — call with missing params and you get `400 { missing_params, all_required, docs }` and you are NOT charged. Check each row in the catalog below for the correct param name (e.g. mindshare is `q` + `interval`, not `project` + `window`).
|
|
40
40
|
|
|
41
41
|
## When to use this skill
|
|
42
42
|
|