@blockrun/clawrouter 0.10.5 → 0.10.7
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/dist/cli.js +197 -10
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +80 -1
- package/dist/index.js +286 -9
- package/dist/index.js.map +1 -1
- package/package.json +22 -22
- package/scripts/reinstall.sh +44 -0
- package/scripts/update.sh +168 -0
package/dist/index.d.ts
CHANGED
|
@@ -738,6 +738,10 @@ type UsageEntry = {
|
|
|
738
738
|
baselineCost: number;
|
|
739
739
|
savings: number;
|
|
740
740
|
latencyMs: number;
|
|
741
|
+
/** Partner service ID (e.g., "x_users_lookup") — only set for partner API calls */
|
|
742
|
+
partnerId?: string;
|
|
743
|
+
/** Partner service name (e.g., "AttentionVC") — only set for partner API calls */
|
|
744
|
+
service?: string;
|
|
741
745
|
};
|
|
742
746
|
/**
|
|
743
747
|
* Log a usage entry as a JSON line.
|
|
@@ -991,6 +995,81 @@ declare function getStats(days?: number): Promise<AggregatedStats>;
|
|
|
991
995
|
*/
|
|
992
996
|
declare function formatStatsAscii(stats: AggregatedStats): string;
|
|
993
997
|
|
|
998
|
+
/**
|
|
999
|
+
* Partner Service Registry
|
|
1000
|
+
*
|
|
1001
|
+
* Defines available partner APIs that can be called through ClawRouter's proxy.
|
|
1002
|
+
* Partners provide specialized data (Twitter/X, etc.) via x402 micropayments.
|
|
1003
|
+
* The same wallet used for LLM calls pays for partner API calls — zero extra setup.
|
|
1004
|
+
*/
|
|
1005
|
+
type PartnerServiceParam = {
|
|
1006
|
+
name: string;
|
|
1007
|
+
type: "string" | "string[]" | "number";
|
|
1008
|
+
description: string;
|
|
1009
|
+
required: boolean;
|
|
1010
|
+
};
|
|
1011
|
+
type PartnerServiceDefinition = {
|
|
1012
|
+
/** Unique service ID used in tool names: blockrun_{id} */
|
|
1013
|
+
id: string;
|
|
1014
|
+
/** Human-readable name */
|
|
1015
|
+
name: string;
|
|
1016
|
+
/** Partner providing this service */
|
|
1017
|
+
partner: string;
|
|
1018
|
+
/** Short description for tool listing */
|
|
1019
|
+
description: string;
|
|
1020
|
+
/** Proxy path (relative to /v1) */
|
|
1021
|
+
proxyPath: string;
|
|
1022
|
+
/** HTTP method */
|
|
1023
|
+
method: "GET" | "POST";
|
|
1024
|
+
/** Parameters for the tool's JSON Schema */
|
|
1025
|
+
params: PartnerServiceParam[];
|
|
1026
|
+
/** Pricing info for display */
|
|
1027
|
+
pricing: {
|
|
1028
|
+
perUnit: string;
|
|
1029
|
+
unit: string;
|
|
1030
|
+
minimum: string;
|
|
1031
|
+
maximum: string;
|
|
1032
|
+
};
|
|
1033
|
+
/** Example usage for help text */
|
|
1034
|
+
example: {
|
|
1035
|
+
input: Record<string, unknown>;
|
|
1036
|
+
description: string;
|
|
1037
|
+
};
|
|
1038
|
+
};
|
|
1039
|
+
/**
|
|
1040
|
+
* All registered partner services.
|
|
1041
|
+
* New partners are added here — the rest of the system picks them up automatically.
|
|
1042
|
+
*/
|
|
1043
|
+
declare const PARTNER_SERVICES: PartnerServiceDefinition[];
|
|
1044
|
+
/**
|
|
1045
|
+
* Get a partner service by ID.
|
|
1046
|
+
*/
|
|
1047
|
+
declare function getPartnerService(id: string): PartnerServiceDefinition | undefined;
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* Partner Tool Builder
|
|
1051
|
+
*
|
|
1052
|
+
* Converts partner service definitions into OpenClaw tool definitions.
|
|
1053
|
+
* Each tool's execute() calls through the local proxy which handles
|
|
1054
|
+
* x402 payment transparently using the same wallet.
|
|
1055
|
+
*/
|
|
1056
|
+
/** OpenClaw tool definition shape (duck-typed) */
|
|
1057
|
+
type PartnerToolDefinition = {
|
|
1058
|
+
name: string;
|
|
1059
|
+
description: string;
|
|
1060
|
+
inputSchema: {
|
|
1061
|
+
type: "object";
|
|
1062
|
+
properties: Record<string, unknown>;
|
|
1063
|
+
required: string[];
|
|
1064
|
+
};
|
|
1065
|
+
execute: (args: Record<string, unknown>) => Promise<unknown>;
|
|
1066
|
+
};
|
|
1067
|
+
/**
|
|
1068
|
+
* Build OpenClaw tool definitions for all registered partner services.
|
|
1069
|
+
* @param proxyBaseUrl - Local proxy base URL (e.g., "http://127.0.0.1:8402")
|
|
1070
|
+
*/
|
|
1071
|
+
declare function buildPartnerTools(proxyBaseUrl: string): PartnerToolDefinition[];
|
|
1072
|
+
|
|
994
1073
|
/**
|
|
995
1074
|
* @blockrun/clawrouter
|
|
996
1075
|
*
|
|
@@ -1012,4 +1091,4 @@ declare function formatStatsAscii(stats: AggregatedStats): string;
|
|
|
1012
1091
|
|
|
1013
1092
|
declare const plugin: OpenClawPluginDefinition;
|
|
1014
1093
|
|
|
1015
|
-
export { type AggregatedStats, BALANCE_THRESHOLDS, BLOCKRUN_MODELS, type BalanceInfo, BalanceMonitor, type CachedLLMResponse, type CachedPaymentParams, type CachedResponse, DEFAULT_RETRY_CONFIG, DEFAULT_ROUTING_CONFIG, DEFAULT_SESSION_CONFIG, type DailyStats, EmptyWalletError, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, MODEL_ALIASES, OPENCLAW_MODELS, PaymentCache, type PaymentFetchResult, type PreAuthParams, type ProxyHandle, type ProxyOptions, RequestDeduplicator, ResponseCache, type ResponseCacheConfig, type RetryConfig, type RoutingConfig, type RoutingDecision, RpcError, type SessionConfig, type SessionEntry, SessionStore, type SufficiencyResult, type Tier, type UsageEntry, blockrunProvider, buildProviderModels, calculateModelCost, createPaymentFetch, plugin as default, fetchWithRetry, formatStatsAscii, getAgenticModels, getFallbackChain, getFallbackChainFiltered, getModelContextWindow, getProxyPort, getSessionId, getStats, isAgenticModel, isBalanceError, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, logUsage, resolveModelAlias, route, startProxy };
|
|
1094
|
+
export { type AggregatedStats, BALANCE_THRESHOLDS, BLOCKRUN_MODELS, type BalanceInfo, BalanceMonitor, type CachedLLMResponse, type CachedPaymentParams, type CachedResponse, DEFAULT_RETRY_CONFIG, DEFAULT_ROUTING_CONFIG, DEFAULT_SESSION_CONFIG, type DailyStats, EmptyWalletError, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, MODEL_ALIASES, OPENCLAW_MODELS, PARTNER_SERVICES, type PartnerServiceDefinition, type PartnerToolDefinition, PaymentCache, type PaymentFetchResult, type PreAuthParams, type ProxyHandle, type ProxyOptions, RequestDeduplicator, ResponseCache, type ResponseCacheConfig, type RetryConfig, type RoutingConfig, type RoutingDecision, RpcError, type SessionConfig, type SessionEntry, SessionStore, type SufficiencyResult, type Tier, type UsageEntry, blockrunProvider, buildPartnerTools, buildProviderModels, calculateModelCost, createPaymentFetch, plugin as default, fetchWithRetry, formatStatsAscii, getAgenticModels, getFallbackChain, getFallbackChainFiltered, getModelContextWindow, getPartnerService, getProxyPort, getSessionId, getStats, isAgenticModel, isBalanceError, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, logUsage, resolveModelAlias, route, startProxy };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,126 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/partners/registry.ts
|
|
12
|
+
function getPartnerService(id) {
|
|
13
|
+
return PARTNER_SERVICES.find((s) => s.id === id);
|
|
14
|
+
}
|
|
15
|
+
var PARTNER_SERVICES;
|
|
16
|
+
var init_registry = __esm({
|
|
17
|
+
"src/partners/registry.ts"() {
|
|
18
|
+
"use strict";
|
|
19
|
+
PARTNER_SERVICES = [
|
|
20
|
+
{
|
|
21
|
+
id: "x_users_lookup",
|
|
22
|
+
name: "Twitter/X User Lookup",
|
|
23
|
+
partner: "AttentionVC",
|
|
24
|
+
description: "ALWAYS use this tool to look up real-time Twitter/X user profiles. Call this when the user asks about any Twitter/X account, username, handle, follower count, verification status, bio, or profile. Do NOT answer Twitter/X user questions from memory \u2014 always fetch live data with this tool. Returns: follower count, verification badge, bio, location, join date. Accepts up to 100 usernames per request (without @ prefix).",
|
|
25
|
+
proxyPath: "/x/users/lookup",
|
|
26
|
+
method: "POST",
|
|
27
|
+
params: [
|
|
28
|
+
{
|
|
29
|
+
name: "usernames",
|
|
30
|
+
type: "string[]",
|
|
31
|
+
description: 'Array of Twitter/X usernames to look up (without @ prefix). Example: ["elonmusk", "naval"]',
|
|
32
|
+
required: true
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
pricing: {
|
|
36
|
+
perUnit: "$0.001",
|
|
37
|
+
unit: "user",
|
|
38
|
+
minimum: "$0.01 (10 users)",
|
|
39
|
+
maximum: "$0.10 (100 users)"
|
|
40
|
+
},
|
|
41
|
+
example: {
|
|
42
|
+
input: { usernames: ["elonmusk", "naval", "balaboris"] },
|
|
43
|
+
description: "Look up 3 Twitter/X user profiles"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// src/partners/tools.ts
|
|
51
|
+
function buildTool(service, proxyBaseUrl) {
|
|
52
|
+
const properties = {};
|
|
53
|
+
const required = [];
|
|
54
|
+
for (const param of service.params) {
|
|
55
|
+
const prop = {
|
|
56
|
+
description: param.description
|
|
57
|
+
};
|
|
58
|
+
if (param.type === "string[]") {
|
|
59
|
+
prop.type = "array";
|
|
60
|
+
prop.items = { type: "string" };
|
|
61
|
+
} else {
|
|
62
|
+
prop.type = param.type;
|
|
63
|
+
}
|
|
64
|
+
properties[param.name] = prop;
|
|
65
|
+
if (param.required) {
|
|
66
|
+
required.push(param.name);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
name: `blockrun_${service.id}`,
|
|
71
|
+
description: [
|
|
72
|
+
service.description,
|
|
73
|
+
"",
|
|
74
|
+
`Partner: ${service.partner}`,
|
|
75
|
+
`Pricing: ${service.pricing.perUnit} per ${service.pricing.unit} (min: ${service.pricing.minimum}, max: ${service.pricing.maximum})`
|
|
76
|
+
].join("\n"),
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties,
|
|
80
|
+
required
|
|
81
|
+
},
|
|
82
|
+
execute: async (args) => {
|
|
83
|
+
const url = `${proxyBaseUrl}/v1${service.proxyPath}`;
|
|
84
|
+
const response = await fetch(url, {
|
|
85
|
+
method: service.method,
|
|
86
|
+
headers: { "Content-Type": "application/json" },
|
|
87
|
+
body: JSON.stringify(args)
|
|
88
|
+
});
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
const errText = await response.text().catch(() => "");
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Partner API error (${response.status}): ${errText || response.statusText}`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return response.json();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function buildPartnerTools(proxyBaseUrl) {
|
|
100
|
+
return PARTNER_SERVICES.map((service) => buildTool(service, proxyBaseUrl));
|
|
101
|
+
}
|
|
102
|
+
var init_tools = __esm({
|
|
103
|
+
"src/partners/tools.ts"() {
|
|
104
|
+
"use strict";
|
|
105
|
+
init_registry();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// src/partners/index.ts
|
|
110
|
+
var partners_exports = {};
|
|
111
|
+
__export(partners_exports, {
|
|
112
|
+
PARTNER_SERVICES: () => PARTNER_SERVICES,
|
|
113
|
+
buildPartnerTools: () => buildPartnerTools,
|
|
114
|
+
getPartnerService: () => getPartnerService
|
|
115
|
+
});
|
|
116
|
+
var init_partners = __esm({
|
|
117
|
+
"src/partners/index.ts"() {
|
|
118
|
+
"use strict";
|
|
119
|
+
init_registry();
|
|
120
|
+
init_tools();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
1
124
|
// src/models.ts
|
|
2
125
|
var MODEL_ALIASES = {
|
|
3
126
|
// Claude - use newest versions (4.6)
|
|
@@ -1094,7 +1217,7 @@ function calibrateConfidence(distance, steepness) {
|
|
|
1094
1217
|
}
|
|
1095
1218
|
|
|
1096
1219
|
// src/router/selector.ts
|
|
1097
|
-
var BASELINE_MODEL_ID = "anthropic/claude-opus-4
|
|
1220
|
+
var BASELINE_MODEL_ID = "anthropic/claude-opus-4.6";
|
|
1098
1221
|
function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPricing, estimatedInputTokens, maxOutputTokens, routingProfile) {
|
|
1099
1222
|
const tierConfig = tierConfigs[tier];
|
|
1100
1223
|
const model = tierConfig.primary;
|
|
@@ -2635,7 +2758,7 @@ function formatStatsAscii(stats) {
|
|
|
2635
2758
|
lines.push(`\u2551 Avg Latency: ${stats.avgLatencyMs.toFixed(0)}ms`.padEnd(61) + "\u2551");
|
|
2636
2759
|
lines.push("\u2560\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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563");
|
|
2637
2760
|
lines.push("\u2551 Routing by Tier: \u2551");
|
|
2638
|
-
const knownTiers = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"];
|
|
2761
|
+
const knownTiers = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING", "DIRECT"];
|
|
2639
2762
|
const allTiers = Object.keys(stats.byTier);
|
|
2640
2763
|
const otherTiers = allTiers.filter((t) => !knownTiers.includes(t));
|
|
2641
2764
|
const tierOrder = [...knownTiers.filter((t) => stats.byTier[t]), ...otherTiers];
|
|
@@ -4745,6 +4868,63 @@ function estimateAmount(modelId, bodyLength, maxTokens) {
|
|
|
4745
4868
|
const amountMicros = Math.max(100, Math.ceil(costUsd * 1.2 * 1e6));
|
|
4746
4869
|
return amountMicros.toString();
|
|
4747
4870
|
}
|
|
4871
|
+
async function proxyPartnerRequest(req, res, apiBase, payFetch) {
|
|
4872
|
+
const startTime = Date.now();
|
|
4873
|
+
const upstreamUrl = `${apiBase}${req.url}`;
|
|
4874
|
+
const bodyChunks = [];
|
|
4875
|
+
for await (const chunk of req) {
|
|
4876
|
+
bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
4877
|
+
}
|
|
4878
|
+
const body = Buffer.concat(bodyChunks);
|
|
4879
|
+
const headers = {};
|
|
4880
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
4881
|
+
if (key === "host" || key === "connection" || key === "transfer-encoding" || key === "content-length")
|
|
4882
|
+
continue;
|
|
4883
|
+
if (typeof value === "string") headers[key] = value;
|
|
4884
|
+
}
|
|
4885
|
+
if (!headers["content-type"]) headers["content-type"] = "application/json";
|
|
4886
|
+
headers["user-agent"] = USER_AGENT;
|
|
4887
|
+
console.log(`[ClawRouter] Partner request: ${req.method} ${req.url}`);
|
|
4888
|
+
const upstream = await payFetch(upstreamUrl, {
|
|
4889
|
+
method: req.method ?? "POST",
|
|
4890
|
+
headers,
|
|
4891
|
+
body: body.length > 0 ? new Uint8Array(body) : void 0
|
|
4892
|
+
});
|
|
4893
|
+
const responseHeaders = {};
|
|
4894
|
+
upstream.headers.forEach((value, key) => {
|
|
4895
|
+
if (key === "transfer-encoding" || key === "connection" || key === "content-encoding") return;
|
|
4896
|
+
responseHeaders[key] = value;
|
|
4897
|
+
});
|
|
4898
|
+
res.writeHead(upstream.status, responseHeaders);
|
|
4899
|
+
if (upstream.body) {
|
|
4900
|
+
const reader = upstream.body.getReader();
|
|
4901
|
+
try {
|
|
4902
|
+
while (true) {
|
|
4903
|
+
const { done, value } = await reader.read();
|
|
4904
|
+
if (done) break;
|
|
4905
|
+
safeWrite(res, Buffer.from(value));
|
|
4906
|
+
}
|
|
4907
|
+
} finally {
|
|
4908
|
+
reader.releaseLock();
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
res.end();
|
|
4912
|
+
const latencyMs = Date.now() - startTime;
|
|
4913
|
+
console.log(`[ClawRouter] Partner response: ${upstream.status} (${latencyMs}ms)`);
|
|
4914
|
+
logUsage({
|
|
4915
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4916
|
+
model: "partner",
|
|
4917
|
+
tier: "PARTNER",
|
|
4918
|
+
cost: 0,
|
|
4919
|
+
// Actual cost handled by x402 settlement
|
|
4920
|
+
baselineCost: 0,
|
|
4921
|
+
savings: 0,
|
|
4922
|
+
latencyMs,
|
|
4923
|
+
partnerId: (req.url?.split("?")[0] ?? "").replace(/^\/v1\//, "").replace(/\//g, "_") || "unknown",
|
|
4924
|
+
service: "partner"
|
|
4925
|
+
}).catch(() => {
|
|
4926
|
+
});
|
|
4927
|
+
}
|
|
4748
4928
|
async function startProxy(options) {
|
|
4749
4929
|
const apiBase = options.apiBase ?? BLOCKRUN_API;
|
|
4750
4930
|
const listenPort = options.port ?? getProxyPort();
|
|
@@ -4855,6 +5035,23 @@ async function startProxy(options) {
|
|
|
4855
5035
|
res.end(JSON.stringify({ object: "list", data: models }));
|
|
4856
5036
|
return;
|
|
4857
5037
|
}
|
|
5038
|
+
if (req.url?.match(/^\/v1\/(?:x|partner)\//)) {
|
|
5039
|
+
try {
|
|
5040
|
+
await proxyPartnerRequest(req, res, apiBase, payFetch);
|
|
5041
|
+
} catch (err) {
|
|
5042
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
5043
|
+
options.onError?.(error);
|
|
5044
|
+
if (!res.headersSent) {
|
|
5045
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
5046
|
+
res.end(
|
|
5047
|
+
JSON.stringify({
|
|
5048
|
+
error: { message: `Partner proxy error: ${error.message}`, type: "partner_error" }
|
|
5049
|
+
})
|
|
5050
|
+
);
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5053
|
+
return;
|
|
5054
|
+
}
|
|
4858
5055
|
if (!req.url?.startsWith("/v1")) {
|
|
4859
5056
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
4860
5057
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
@@ -5717,10 +5914,11 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5717
5914
|
}
|
|
5718
5915
|
throw err;
|
|
5719
5916
|
}
|
|
5720
|
-
|
|
5917
|
+
const logModel = routingDecision?.model ?? modelId;
|
|
5918
|
+
if (logModel) {
|
|
5721
5919
|
const estimatedInputTokens = Math.ceil(body.length / 4);
|
|
5722
5920
|
const accurateCosts = calculateModelCost(
|
|
5723
|
-
|
|
5921
|
+
logModel,
|
|
5724
5922
|
routerOpts.modelPricing,
|
|
5725
5923
|
estimatedInputTokens,
|
|
5726
5924
|
maxTokens,
|
|
@@ -5730,8 +5928,8 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5730
5928
|
const baselineWithBuffer = accurateCosts.baselineCost * 1.2;
|
|
5731
5929
|
const entry = {
|
|
5732
5930
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5733
|
-
model:
|
|
5734
|
-
tier: routingDecision
|
|
5931
|
+
model: logModel,
|
|
5932
|
+
tier: routingDecision?.tier ?? "DIRECT",
|
|
5735
5933
|
cost: costWithBuffer,
|
|
5736
5934
|
baselineCost: baselineWithBuffer,
|
|
5737
5935
|
savings: accurateCosts.savings,
|
|
@@ -5756,12 +5954,24 @@ async function loadSavedWallet() {
|
|
|
5756
5954
|
console.log(`[ClawRouter] \u2713 Loaded existing wallet from ${WALLET_FILE}`);
|
|
5757
5955
|
return key;
|
|
5758
5956
|
}
|
|
5759
|
-
console.
|
|
5957
|
+
console.error(`[ClawRouter] \u2717 CRITICAL: Wallet file exists but has invalid format!`);
|
|
5958
|
+
console.error(`[ClawRouter] File: ${WALLET_FILE}`);
|
|
5959
|
+
console.error(`[ClawRouter] Expected: 0x followed by 64 hex characters (66 chars total)`);
|
|
5960
|
+
console.error(`[ClawRouter] To fix: restore your backup key or set BLOCKRUN_WALLET_KEY env var`);
|
|
5961
|
+
throw new Error(
|
|
5962
|
+
`Wallet file at ${WALLET_FILE} is corrupted or has wrong format. Refusing to auto-generate new wallet to protect existing funds. Restore your backup key or set BLOCKRUN_WALLET_KEY environment variable.`
|
|
5963
|
+
);
|
|
5760
5964
|
} catch (err) {
|
|
5761
5965
|
if (err.code !== "ENOENT") {
|
|
5966
|
+
if (err instanceof Error && err.message.includes("Refusing to auto-generate")) {
|
|
5967
|
+
throw err;
|
|
5968
|
+
}
|
|
5762
5969
|
console.error(
|
|
5763
5970
|
`[ClawRouter] \u2717 Failed to read wallet file: ${err instanceof Error ? err.message : String(err)}`
|
|
5764
5971
|
);
|
|
5972
|
+
throw new Error(
|
|
5973
|
+
`Cannot read wallet file at ${WALLET_FILE}: ${err instanceof Error ? err.message : String(err)}. Refusing to auto-generate new wallet to protect existing funds. Fix file permissions or set BLOCKRUN_WALLET_KEY environment variable.`
|
|
5974
|
+
);
|
|
5765
5975
|
}
|
|
5766
5976
|
}
|
|
5767
5977
|
return void 0;
|
|
@@ -5782,6 +5992,20 @@ async function generateAndSaveWallet() {
|
|
|
5782
5992
|
`Failed to verify wallet file after creation: ${err instanceof Error ? err.message : String(err)}`
|
|
5783
5993
|
);
|
|
5784
5994
|
}
|
|
5995
|
+
console.log(`[ClawRouter]`);
|
|
5996
|
+
console.log(`[ClawRouter] \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`);
|
|
5997
|
+
console.log(`[ClawRouter] NEW WALLET GENERATED \u2014 BACK UP YOUR KEY NOW`);
|
|
5998
|
+
console.log(`[ClawRouter] \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`);
|
|
5999
|
+
console.log(`[ClawRouter] Address : ${account.address}`);
|
|
6000
|
+
console.log(`[ClawRouter] Key file: ${WALLET_FILE}`);
|
|
6001
|
+
console.log(`[ClawRouter]`);
|
|
6002
|
+
console.log(`[ClawRouter] To back up, run in OpenClaw:`);
|
|
6003
|
+
console.log(`[ClawRouter] /wallet export`);
|
|
6004
|
+
console.log(`[ClawRouter]`);
|
|
6005
|
+
console.log(`[ClawRouter] To restore on another machine:`);
|
|
6006
|
+
console.log(`[ClawRouter] export BLOCKRUN_WALLET_KEY=<your_key>`);
|
|
6007
|
+
console.log(`[ClawRouter] \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`);
|
|
6008
|
+
console.log(`[ClawRouter]`);
|
|
5785
6009
|
return { key, address: account.address };
|
|
5786
6010
|
}
|
|
5787
6011
|
async function resolveOrGenerateWalletKey() {
|
|
@@ -5869,6 +6093,7 @@ function isRetryable(errorOrResponse, config) {
|
|
|
5869
6093
|
}
|
|
5870
6094
|
|
|
5871
6095
|
// src/index.ts
|
|
6096
|
+
init_partners();
|
|
5872
6097
|
async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
5873
6098
|
const start = Date.now();
|
|
5874
6099
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -6132,7 +6357,12 @@ var activeProxyHandle = null;
|
|
|
6132
6357
|
async function startProxyInBackground(api) {
|
|
6133
6358
|
const { key: walletKey, address, source } = await resolveOrGenerateWalletKey();
|
|
6134
6359
|
if (source === "generated") {
|
|
6135
|
-
api.logger.
|
|
6360
|
+
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`);
|
|
6361
|
+
api.logger.warn(` NEW WALLET GENERATED \u2014 BACK UP YOUR KEY NOW!`);
|
|
6362
|
+
api.logger.warn(` Address : ${address}`);
|
|
6363
|
+
api.logger.warn(` Run /wallet export to get your private key`);
|
|
6364
|
+
api.logger.warn(` Losing this key = losing your USDC funds`);
|
|
6365
|
+
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`);
|
|
6136
6366
|
} else if (source === "saved") {
|
|
6137
6367
|
api.logger.info(`Using saved wallet: ${address}`);
|
|
6138
6368
|
} else {
|
|
@@ -6314,6 +6544,45 @@ var plugin = {
|
|
|
6314
6544
|
models: OPENCLAW_MODELS
|
|
6315
6545
|
};
|
|
6316
6546
|
api.logger.info("BlockRun provider registered (30+ models via x402)");
|
|
6547
|
+
try {
|
|
6548
|
+
const { buildPartnerTools: buildPartnerTools2, PARTNER_SERVICES: PARTNER_SERVICES2 } = await Promise.resolve().then(() => (init_partners(), partners_exports));
|
|
6549
|
+
const proxyBaseUrl = `http://127.0.0.1:${runtimePort}`;
|
|
6550
|
+
const partnerTools = buildPartnerTools2(proxyBaseUrl);
|
|
6551
|
+
for (const tool of partnerTools) {
|
|
6552
|
+
api.registerTool(tool);
|
|
6553
|
+
}
|
|
6554
|
+
if (partnerTools.length > 0) {
|
|
6555
|
+
api.logger.info(`Registered ${partnerTools.length} partner tool(s): ${partnerTools.map((t) => t.name).join(", ")}`);
|
|
6556
|
+
}
|
|
6557
|
+
api.registerCommand({
|
|
6558
|
+
name: "partners",
|
|
6559
|
+
description: "List available partner APIs and pricing",
|
|
6560
|
+
acceptsArgs: false,
|
|
6561
|
+
requireAuth: false,
|
|
6562
|
+
handler: async () => {
|
|
6563
|
+
if (PARTNER_SERVICES2.length === 0) {
|
|
6564
|
+
return { text: "No partner APIs available." };
|
|
6565
|
+
}
|
|
6566
|
+
const lines = [
|
|
6567
|
+
"**Partner APIs** (paid via your ClawRouter wallet)",
|
|
6568
|
+
""
|
|
6569
|
+
];
|
|
6570
|
+
for (const svc of PARTNER_SERVICES2) {
|
|
6571
|
+
lines.push(`**${svc.name}** (${svc.partner})`);
|
|
6572
|
+
lines.push(` ${svc.description}`);
|
|
6573
|
+
lines.push(` Tool: \`${`blockrun_${svc.id}`}\``);
|
|
6574
|
+
lines.push(` Pricing: ${svc.pricing.perUnit} per ${svc.pricing.unit} (min ${svc.pricing.minimum}, max ${svc.pricing.maximum})`);
|
|
6575
|
+
lines.push(` **How to use:** Ask "Look up Twitter user @elonmusk" or "Get info on these X accounts: @naval, @balajis"`);
|
|
6576
|
+
lines.push("");
|
|
6577
|
+
}
|
|
6578
|
+
return { text: lines.join("\n") };
|
|
6579
|
+
}
|
|
6580
|
+
});
|
|
6581
|
+
} catch (err) {
|
|
6582
|
+
api.logger.warn(
|
|
6583
|
+
`Failed to register partner tools: ${err instanceof Error ? err.message : String(err)}`
|
|
6584
|
+
);
|
|
6585
|
+
}
|
|
6317
6586
|
createWalletCommand().then((walletCommand) => {
|
|
6318
6587
|
api.registerCommand(walletCommand);
|
|
6319
6588
|
}).catch((err) => {
|
|
@@ -6349,7 +6618,12 @@ var plugin = {
|
|
|
6349
6618
|
if (!isGatewayMode()) {
|
|
6350
6619
|
resolveOrGenerateWalletKey().then(({ address, source }) => {
|
|
6351
6620
|
if (source === "generated") {
|
|
6352
|
-
api.logger.
|
|
6621
|
+
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`);
|
|
6622
|
+
api.logger.warn(` NEW WALLET GENERATED \u2014 BACK UP YOUR KEY NOW!`);
|
|
6623
|
+
api.logger.warn(` Address : ${address}`);
|
|
6624
|
+
api.logger.warn(` Run /wallet export to get your private key`);
|
|
6625
|
+
api.logger.warn(` Losing this key = losing your USDC funds`);
|
|
6626
|
+
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`);
|
|
6353
6627
|
} else if (source === "saved") {
|
|
6354
6628
|
api.logger.info(`Using saved wallet: ${address}`);
|
|
6355
6629
|
} else {
|
|
@@ -6388,12 +6662,14 @@ export {
|
|
|
6388
6662
|
InsufficientFundsError,
|
|
6389
6663
|
MODEL_ALIASES,
|
|
6390
6664
|
OPENCLAW_MODELS,
|
|
6665
|
+
PARTNER_SERVICES,
|
|
6391
6666
|
PaymentCache,
|
|
6392
6667
|
RequestDeduplicator,
|
|
6393
6668
|
ResponseCache,
|
|
6394
6669
|
RpcError,
|
|
6395
6670
|
SessionStore,
|
|
6396
6671
|
blockrunProvider,
|
|
6672
|
+
buildPartnerTools,
|
|
6397
6673
|
buildProviderModels,
|
|
6398
6674
|
calculateModelCost,
|
|
6399
6675
|
createPaymentFetch,
|
|
@@ -6404,6 +6680,7 @@ export {
|
|
|
6404
6680
|
getFallbackChain,
|
|
6405
6681
|
getFallbackChainFiltered,
|
|
6406
6682
|
getModelContextWindow,
|
|
6683
|
+
getPartnerService,
|
|
6407
6684
|
getProxyPort,
|
|
6408
6685
|
getSessionId,
|
|
6409
6686
|
getStats,
|