@blockrun/clawrouter 0.12.60 → 0.12.61
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 +19 -0
- package/dist/cli.js +80 -24
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +204 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -681,6 +681,8 @@ type ProxyOptions = {
|
|
|
681
681
|
requestTimeoutMs?: number;
|
|
682
682
|
/** Skip balance checks (for testing only). Default: false */
|
|
683
683
|
skipBalanceCheck?: boolean;
|
|
684
|
+
/** Override the balance monitor with a mock (for testing only). */
|
|
685
|
+
_balanceMonitorOverride?: AnyBalanceMonitor;
|
|
684
686
|
/**
|
|
685
687
|
* Session persistence config. When enabled, maintains model selection
|
|
686
688
|
* across requests within a session to prevent mid-task model switching.
|
|
@@ -717,6 +719,12 @@ type ProxyOptions = {
|
|
|
717
719
|
* - 'strict': immediately return 429 once the session spend reaches the cap.
|
|
718
720
|
*/
|
|
719
721
|
maxCostPerRunMode?: "graceful" | "strict";
|
|
722
|
+
/**
|
|
723
|
+
* Set of model IDs to exclude from routing.
|
|
724
|
+
* Excluded models are filtered out of fallback chains.
|
|
725
|
+
* Loaded from ~/.openclaw/blockrun/exclude-models.json
|
|
726
|
+
*/
|
|
727
|
+
excludeModels?: Set<string>;
|
|
720
728
|
onReady?: (port: number) => void;
|
|
721
729
|
onError?: (error: Error) => void;
|
|
722
730
|
onPayment?: (info: {
|
|
@@ -913,6 +921,8 @@ type UsageEntry = {
|
|
|
913
921
|
latencyMs: number;
|
|
914
922
|
/** Input (prompt) tokens reported by the provider */
|
|
915
923
|
inputTokens?: number;
|
|
924
|
+
/** Output (completion) tokens reported by the provider */
|
|
925
|
+
outputTokens?: number;
|
|
916
926
|
/** Partner service ID (e.g., "x_users_lookup") — only set for partner API calls */
|
|
917
927
|
partnerId?: string;
|
|
918
928
|
/** Partner service name (e.g., "AttentionVC") — only set for partner API calls */
|
package/dist/index.js
CHANGED
|
@@ -32889,7 +32889,9 @@ var MODEL_ALIASES = {
|
|
|
32889
32889
|
nvidia: "nvidia/gpt-oss-120b",
|
|
32890
32890
|
"gpt-120b": "nvidia/gpt-oss-120b",
|
|
32891
32891
|
// MiniMax
|
|
32892
|
-
minimax: "minimax/minimax-m2.
|
|
32892
|
+
minimax: "minimax/minimax-m2.7",
|
|
32893
|
+
"minimax-m2.7": "minimax/minimax-m2.7",
|
|
32894
|
+
"minimax-m2.5": "minimax/minimax-m2.5",
|
|
32893
32895
|
// Z.AI GLM-5
|
|
32894
32896
|
glm: "zai/glm-5",
|
|
32895
32897
|
"glm-5": "zai/glm-5",
|
|
@@ -33395,6 +33397,18 @@ var BLOCKRUN_MODELS = [
|
|
|
33395
33397
|
toolCalling: true
|
|
33396
33398
|
},
|
|
33397
33399
|
// MiniMax
|
|
33400
|
+
{
|
|
33401
|
+
id: "minimax/minimax-m2.7",
|
|
33402
|
+
name: "MiniMax M2.7",
|
|
33403
|
+
version: "m2.7",
|
|
33404
|
+
inputPrice: 0.3,
|
|
33405
|
+
outputPrice: 1.2,
|
|
33406
|
+
contextWindow: 204800,
|
|
33407
|
+
maxOutput: 16384,
|
|
33408
|
+
reasoning: true,
|
|
33409
|
+
agentic: true,
|
|
33410
|
+
toolCalling: true
|
|
33411
|
+
},
|
|
33398
33412
|
{
|
|
33399
33413
|
id: "minimax/minimax-m2.5",
|
|
33400
33414
|
name: "MiniMax M2.5",
|
|
@@ -33541,10 +33555,10 @@ var blockrunProvider = {
|
|
|
33541
33555
|
// src/proxy.ts
|
|
33542
33556
|
import { createServer } from "http";
|
|
33543
33557
|
import { finished } from "stream";
|
|
33544
|
-
import { homedir as
|
|
33545
|
-
import { join as
|
|
33558
|
+
import { homedir as homedir5 } from "os";
|
|
33559
|
+
import { join as join8 } from "path";
|
|
33546
33560
|
import { mkdir as mkdir3, writeFile as writeFile2, readFile, stat as fsStat } from "fs/promises";
|
|
33547
|
-
import { readFileSync, existsSync } from "fs";
|
|
33561
|
+
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
33548
33562
|
|
|
33549
33563
|
// node_modules/viem/_esm/utils/getAction.js
|
|
33550
33564
|
function getAction(client, actionFn, name) {
|
|
@@ -43494,6 +43508,11 @@ function filterByVision(models, hasVision, supportsVision2) {
|
|
|
43494
43508
|
const filtered = models.filter(supportsVision2);
|
|
43495
43509
|
return filtered.length > 0 ? filtered : models;
|
|
43496
43510
|
}
|
|
43511
|
+
function filterByExcludeList(models, excludeList) {
|
|
43512
|
+
if (excludeList.size === 0) return models;
|
|
43513
|
+
const filtered = models.filter((m) => !excludeList.has(m));
|
|
43514
|
+
return filtered.length > 0 ? filtered : models;
|
|
43515
|
+
}
|
|
43497
43516
|
function getFallbackChainFiltered(tier, tierConfigs, estimatedTotalTokens, getContextWindow) {
|
|
43498
43517
|
const fullChain = getFallbackChain(tier, tierConfigs);
|
|
43499
43518
|
const filtered = fullChain.filter((modelId) => {
|
|
@@ -46839,6 +46858,54 @@ async function checkForUpdates() {
|
|
|
46839
46858
|
}
|
|
46840
46859
|
}
|
|
46841
46860
|
|
|
46861
|
+
// src/exclude-models.ts
|
|
46862
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
46863
|
+
import { join as join7, dirname as dirname2 } from "path";
|
|
46864
|
+
import { homedir as homedir4 } from "os";
|
|
46865
|
+
var DEFAULT_FILE_PATH = join7(
|
|
46866
|
+
homedir4(),
|
|
46867
|
+
".openclaw",
|
|
46868
|
+
"blockrun",
|
|
46869
|
+
"exclude-models.json"
|
|
46870
|
+
);
|
|
46871
|
+
function loadExcludeList(filePath = DEFAULT_FILE_PATH) {
|
|
46872
|
+
try {
|
|
46873
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
46874
|
+
const arr = JSON.parse(raw);
|
|
46875
|
+
if (Array.isArray(arr)) {
|
|
46876
|
+
return new Set(arr.filter((x) => typeof x === "string"));
|
|
46877
|
+
}
|
|
46878
|
+
return /* @__PURE__ */ new Set();
|
|
46879
|
+
} catch {
|
|
46880
|
+
return /* @__PURE__ */ new Set();
|
|
46881
|
+
}
|
|
46882
|
+
}
|
|
46883
|
+
function saveExcludeList(set, filePath) {
|
|
46884
|
+
const sorted = [...set].sort();
|
|
46885
|
+
const dir = dirname2(filePath);
|
|
46886
|
+
mkdirSync(dir, { recursive: true });
|
|
46887
|
+
writeFileSync(filePath, JSON.stringify(sorted, null, 2) + "\n", "utf-8");
|
|
46888
|
+
}
|
|
46889
|
+
function addExclusion(model, filePath = DEFAULT_FILE_PATH) {
|
|
46890
|
+
const resolved = resolveModelAlias(model);
|
|
46891
|
+
const set = loadExcludeList(filePath);
|
|
46892
|
+
set.add(resolved);
|
|
46893
|
+
saveExcludeList(set, filePath);
|
|
46894
|
+
return resolved;
|
|
46895
|
+
}
|
|
46896
|
+
function removeExclusion(model, filePath = DEFAULT_FILE_PATH) {
|
|
46897
|
+
const resolved = resolveModelAlias(model);
|
|
46898
|
+
const set = loadExcludeList(filePath);
|
|
46899
|
+
const had = set.delete(resolved);
|
|
46900
|
+
if (had) {
|
|
46901
|
+
saveExcludeList(set, filePath);
|
|
46902
|
+
}
|
|
46903
|
+
return had;
|
|
46904
|
+
}
|
|
46905
|
+
function clearExclusions(filePath = DEFAULT_FILE_PATH) {
|
|
46906
|
+
saveExcludeList(/* @__PURE__ */ new Set(), filePath);
|
|
46907
|
+
}
|
|
46908
|
+
|
|
46842
46909
|
// src/config.ts
|
|
46843
46910
|
var DEFAULT_PORT = 8402;
|
|
46844
46911
|
var PROXY_PORT = (() => {
|
|
@@ -47023,7 +47090,7 @@ ${lines.join("\n")}`;
|
|
|
47023
47090
|
// src/proxy.ts
|
|
47024
47091
|
var BLOCKRUN_API = "https://blockrun.ai/api";
|
|
47025
47092
|
var BLOCKRUN_SOLANA_API = "https://sol.blockrun.ai/api";
|
|
47026
|
-
var IMAGE_DIR =
|
|
47093
|
+
var IMAGE_DIR = join8(homedir5(), ".openclaw", "blockrun", "images");
|
|
47027
47094
|
var AUTO_MODEL = "blockrun/auto";
|
|
47028
47095
|
var ROUTING_PROFILES = /* @__PURE__ */ new Set([
|
|
47029
47096
|
"blockrun/free",
|
|
@@ -47691,7 +47758,7 @@ async function proxyPartnerRequest(req, res, apiBase, payFetch) {
|
|
|
47691
47758
|
});
|
|
47692
47759
|
}
|
|
47693
47760
|
function readImageFileAsDataUri(filePath) {
|
|
47694
|
-
const resolved = filePath.startsWith("~/") ?
|
|
47761
|
+
const resolved = filePath.startsWith("~/") ? join8(homedir5(), filePath.slice(2)) : filePath;
|
|
47695
47762
|
if (!existsSync(resolved)) {
|
|
47696
47763
|
throw new Error(`Image file not found: ${resolved}`);
|
|
47697
47764
|
}
|
|
@@ -47703,7 +47770,7 @@ function readImageFileAsDataUri(filePath) {
|
|
|
47703
47770
|
webp: "image/webp"
|
|
47704
47771
|
};
|
|
47705
47772
|
const mime = mimeMap[ext] ?? "image/png";
|
|
47706
|
-
const data =
|
|
47773
|
+
const data = readFileSync2(resolved);
|
|
47707
47774
|
return `data:${mime};base64,${data.toString("base64")}`;
|
|
47708
47775
|
}
|
|
47709
47776
|
async function uploadDataUriToHost(dataUri) {
|
|
@@ -47821,7 +47888,9 @@ async function startProxy(options) {
|
|
|
47821
47888
|
skipPreAuth: paymentChain === "solana"
|
|
47822
47889
|
});
|
|
47823
47890
|
let balanceMonitor;
|
|
47824
|
-
if (
|
|
47891
|
+
if (options._balanceMonitorOverride) {
|
|
47892
|
+
balanceMonitor = options._balanceMonitorOverride;
|
|
47893
|
+
} else if (paymentChain === "solana" && solanaAddress) {
|
|
47825
47894
|
const { SolanaBalanceMonitor: SolanaBalanceMonitor2 } = await Promise.resolve().then(() => (init_solana_balance(), solana_balance_exports));
|
|
47826
47895
|
balanceMonitor = new SolanaBalanceMonitor2(solanaAddress);
|
|
47827
47896
|
} else {
|
|
@@ -47946,7 +48015,7 @@ async function startProxy(options) {
|
|
|
47946
48015
|
res.end("Bad request");
|
|
47947
48016
|
return;
|
|
47948
48017
|
}
|
|
47949
|
-
const filePath =
|
|
48018
|
+
const filePath = join8(IMAGE_DIR, filename);
|
|
47950
48019
|
try {
|
|
47951
48020
|
const s3 = await fsStat(filePath);
|
|
47952
48021
|
if (!s3.isFile()) throw new Error("not a file");
|
|
@@ -48005,7 +48074,7 @@ async function startProxy(options) {
|
|
|
48005
48074
|
const [, mimeType, b64] = dataUriMatch;
|
|
48006
48075
|
const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
|
|
48007
48076
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
48008
|
-
await writeFile2(
|
|
48077
|
+
await writeFile2(join8(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
|
|
48009
48078
|
img.url = `http://localhost:${port2}/images/${filename}`;
|
|
48010
48079
|
console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
|
|
48011
48080
|
} else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
|
|
@@ -48016,7 +48085,7 @@ async function startProxy(options) {
|
|
|
48016
48085
|
const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
|
|
48017
48086
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
48018
48087
|
const buf = Buffer.from(await imgResp.arrayBuffer());
|
|
48019
|
-
await writeFile2(
|
|
48088
|
+
await writeFile2(join8(IMAGE_DIR, filename), buf);
|
|
48020
48089
|
img.url = `http://localhost:${port2}/images/${filename}`;
|
|
48021
48090
|
console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
|
|
48022
48091
|
}
|
|
@@ -48105,7 +48174,7 @@ async function startProxy(options) {
|
|
|
48105
48174
|
const [, mimeType, b64] = dataUriMatch;
|
|
48106
48175
|
const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
|
|
48107
48176
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
48108
|
-
await writeFile2(
|
|
48177
|
+
await writeFile2(join8(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
|
|
48109
48178
|
img.url = `http://localhost:${port2}/images/${filename}`;
|
|
48110
48179
|
console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
|
|
48111
48180
|
} else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
|
|
@@ -48116,7 +48185,7 @@ async function startProxy(options) {
|
|
|
48116
48185
|
const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
|
|
48117
48186
|
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
|
|
48118
48187
|
const buf = Buffer.from(await imgResp.arrayBuffer());
|
|
48119
|
-
await writeFile2(
|
|
48188
|
+
await writeFile2(join8(IMAGE_DIR, filename), buf);
|
|
48120
48189
|
img.url = `http://localhost:${port2}/images/${filename}`;
|
|
48121
48190
|
console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
|
|
48122
48191
|
}
|
|
@@ -48428,6 +48497,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
48428
48497
|
let budgetDowngradeHeaderMode;
|
|
48429
48498
|
let accumulatedContent = "";
|
|
48430
48499
|
let responseInputTokens;
|
|
48500
|
+
let responseOutputTokens;
|
|
48431
48501
|
const isChatCompletion = req.url?.includes("/chat/completions");
|
|
48432
48502
|
const sessionId = getSessionId(req.headers);
|
|
48433
48503
|
let effectiveSessionId = sessionId;
|
|
@@ -49361,6 +49431,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
49361
49431
|
const timeoutId = setTimeout(() => globalController.abort(), timeoutMs);
|
|
49362
49432
|
try {
|
|
49363
49433
|
let modelsToTry;
|
|
49434
|
+
const excludeList = options.excludeModels ?? loadExcludeList();
|
|
49364
49435
|
if (routingDecision) {
|
|
49365
49436
|
const estimatedInputTokens = Math.ceil(body.length / 4);
|
|
49366
49437
|
const estimatedTotalTokens = estimatedInputTokens + maxTokens;
|
|
@@ -49378,8 +49449,15 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
49378
49449
|
`[ClawRouter] Context filter (~${estimatedTotalTokens} tokens): excluded ${contextExcluded.join(", ")}`
|
|
49379
49450
|
);
|
|
49380
49451
|
}
|
|
49381
|
-
|
|
49382
|
-
const
|
|
49452
|
+
const excludeFiltered = filterByExcludeList(contextFiltered, excludeList);
|
|
49453
|
+
const excludeExcluded = contextFiltered.filter((m) => !excludeFiltered.includes(m));
|
|
49454
|
+
if (excludeExcluded.length > 0) {
|
|
49455
|
+
console.log(
|
|
49456
|
+
`[ClawRouter] Exclude filter: excluded ${excludeExcluded.join(", ")} (user preference)`
|
|
49457
|
+
);
|
|
49458
|
+
}
|
|
49459
|
+
let toolFiltered = filterByToolCalling(excludeFiltered, hasTools, supportsToolCalling);
|
|
49460
|
+
const toolExcluded = excludeFiltered.filter((m) => !toolFiltered.includes(m));
|
|
49383
49461
|
if (toolExcluded.length > 0) {
|
|
49384
49462
|
console.log(
|
|
49385
49463
|
`[ClawRouter] Tool-calling filter: excluded ${toolExcluded.join(", ")} (no structured function call support)`
|
|
@@ -49412,7 +49490,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
49412
49490
|
} else {
|
|
49413
49491
|
modelsToTry = modelId ? [modelId] : [];
|
|
49414
49492
|
}
|
|
49415
|
-
if (!hasTools && !modelsToTry.includes(FREE_MODEL)) {
|
|
49493
|
+
if (!hasTools && !modelsToTry.includes(FREE_MODEL) && !excludeList.has(FREE_MODEL)) {
|
|
49416
49494
|
modelsToTry.push(FREE_MODEL);
|
|
49417
49495
|
}
|
|
49418
49496
|
if (options.maxCostPerRunUsd && effectiveSessionId && !isFreeModel && (options.maxCostPerRunMode ?? "graceful") === "graceful") {
|
|
@@ -49720,6 +49798,7 @@ data: [DONE]
|
|
|
49720
49798
|
if (rsp.usage && typeof rsp.usage === "object") {
|
|
49721
49799
|
const u = rsp.usage;
|
|
49722
49800
|
if (typeof u.prompt_tokens === "number") responseInputTokens = u.prompt_tokens;
|
|
49801
|
+
if (typeof u.completion_tokens === "number") responseOutputTokens = u.completion_tokens;
|
|
49723
49802
|
}
|
|
49724
49803
|
const baseChunk = {
|
|
49725
49804
|
id: rsp.id ?? `chatcmpl-${Date.now()}`,
|
|
@@ -49933,6 +50012,8 @@ data: [DONE]
|
|
|
49933
50012
|
if (rspJson.usage && typeof rspJson.usage === "object") {
|
|
49934
50013
|
if (typeof rspJson.usage.prompt_tokens === "number")
|
|
49935
50014
|
responseInputTokens = rspJson.usage.prompt_tokens;
|
|
50015
|
+
if (typeof rspJson.usage.completion_tokens === "number")
|
|
50016
|
+
responseOutputTokens = rspJson.usage.completion_tokens;
|
|
49936
50017
|
}
|
|
49937
50018
|
} catch {
|
|
49938
50019
|
}
|
|
@@ -49965,25 +50046,25 @@ data: [DONE]
|
|
|
49965
50046
|
}
|
|
49966
50047
|
const logModel = routingDecision?.model ?? modelId;
|
|
49967
50048
|
if (logModel) {
|
|
49968
|
-
const
|
|
50049
|
+
const actualInputTokens = responseInputTokens ?? Math.ceil(body.length / 4);
|
|
50050
|
+
const actualOutputTokens = responseOutputTokens ?? maxTokens;
|
|
49969
50051
|
const accurateCosts = calculateModelCost(
|
|
49970
50052
|
logModel,
|
|
49971
50053
|
routerOpts.modelPricing,
|
|
49972
|
-
|
|
49973
|
-
|
|
50054
|
+
actualInputTokens,
|
|
50055
|
+
actualOutputTokens,
|
|
49974
50056
|
routingProfile ?? void 0
|
|
49975
50057
|
);
|
|
49976
|
-
const costWithBuffer = accurateCosts.costEstimate * 1.2;
|
|
49977
|
-
const baselineWithBuffer = accurateCosts.baselineCost * 1.2;
|
|
49978
50058
|
const entry = {
|
|
49979
50059
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49980
50060
|
model: logModel,
|
|
49981
50061
|
tier: routingDecision?.tier ?? "DIRECT",
|
|
49982
|
-
cost:
|
|
49983
|
-
baselineCost:
|
|
50062
|
+
cost: accurateCosts.costEstimate,
|
|
50063
|
+
baselineCost: accurateCosts.baselineCost,
|
|
49984
50064
|
savings: accurateCosts.savings,
|
|
49985
50065
|
latencyMs: Date.now() - startTime,
|
|
49986
|
-
...responseInputTokens !== void 0 && { inputTokens: responseInputTokens }
|
|
50066
|
+
...responseInputTokens !== void 0 && { inputTokens: responseInputTokens },
|
|
50067
|
+
...responseOutputTokens !== void 0 && { outputTokens: responseOutputTokens }
|
|
49987
50068
|
};
|
|
49988
50069
|
logUsage(entry).catch(() => {
|
|
49989
50070
|
});
|
|
@@ -49992,15 +50073,15 @@ data: [DONE]
|
|
|
49992
50073
|
|
|
49993
50074
|
// src/index.ts
|
|
49994
50075
|
import {
|
|
49995
|
-
writeFileSync as
|
|
50076
|
+
writeFileSync as writeFileSync3,
|
|
49996
50077
|
existsSync as existsSync3,
|
|
49997
50078
|
readdirSync,
|
|
49998
|
-
mkdirSync as
|
|
50079
|
+
mkdirSync as mkdirSync3,
|
|
49999
50080
|
copyFileSync,
|
|
50000
50081
|
renameSync
|
|
50001
50082
|
} from "fs";
|
|
50002
|
-
import { homedir as
|
|
50003
|
-
import { join as
|
|
50083
|
+
import { homedir as homedir7 } from "os";
|
|
50084
|
+
import { join as join10 } from "path";
|
|
50004
50085
|
init_accounts();
|
|
50005
50086
|
|
|
50006
50087
|
// src/partners/registry.ts
|
|
@@ -50104,8 +50185,8 @@ init_solana_balance();
|
|
|
50104
50185
|
// src/spend-control.ts
|
|
50105
50186
|
import * as fs from "fs";
|
|
50106
50187
|
import * as path from "path";
|
|
50107
|
-
import { homedir as
|
|
50108
|
-
var WALLET_DIR2 = path.join(
|
|
50188
|
+
import { homedir as homedir6 } from "os";
|
|
50189
|
+
var WALLET_DIR2 = path.join(homedir6(), ".openclaw", "blockrun");
|
|
50109
50190
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
50110
50191
|
var DAY_MS = 24 * HOUR_MS;
|
|
50111
50192
|
var FileSpendControlStorage = class {
|
|
@@ -50430,13 +50511,13 @@ function isGatewayMode() {
|
|
|
50430
50511
|
return args.includes("gateway");
|
|
50431
50512
|
}
|
|
50432
50513
|
function injectModelsConfig(logger) {
|
|
50433
|
-
const configDir =
|
|
50434
|
-
const configPath =
|
|
50514
|
+
const configDir = join10(homedir7(), ".openclaw");
|
|
50515
|
+
const configPath = join10(configDir, "openclaw.json");
|
|
50435
50516
|
let config = {};
|
|
50436
50517
|
let needsWrite = false;
|
|
50437
50518
|
if (!existsSync3(configDir)) {
|
|
50438
50519
|
try {
|
|
50439
|
-
|
|
50520
|
+
mkdirSync3(configDir, { recursive: true });
|
|
50440
50521
|
logger.info("Created OpenClaw config directory");
|
|
50441
50522
|
} catch (err) {
|
|
50442
50523
|
logger.info(
|
|
@@ -50597,7 +50678,7 @@ function injectModelsConfig(logger) {
|
|
|
50597
50678
|
if (needsWrite) {
|
|
50598
50679
|
try {
|
|
50599
50680
|
const tmpPath = `${configPath}.tmp.${process.pid}`;
|
|
50600
|
-
|
|
50681
|
+
writeFileSync3(tmpPath, JSON.stringify(config, null, 2));
|
|
50601
50682
|
renameSync(tmpPath, configPath);
|
|
50602
50683
|
logger.info("Smart routing enabled (blockrun/auto)");
|
|
50603
50684
|
} catch (err) {
|
|
@@ -50606,10 +50687,10 @@ function injectModelsConfig(logger) {
|
|
|
50606
50687
|
}
|
|
50607
50688
|
}
|
|
50608
50689
|
function injectAuthProfile(logger) {
|
|
50609
|
-
const agentsDir =
|
|
50690
|
+
const agentsDir = join10(homedir7(), ".openclaw", "agents");
|
|
50610
50691
|
if (!existsSync3(agentsDir)) {
|
|
50611
50692
|
try {
|
|
50612
|
-
|
|
50693
|
+
mkdirSync3(agentsDir, { recursive: true });
|
|
50613
50694
|
} catch (err) {
|
|
50614
50695
|
logger.info(
|
|
50615
50696
|
`Could not create agents dir: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -50623,11 +50704,11 @@ function injectAuthProfile(logger) {
|
|
|
50623
50704
|
agents = ["main", ...agents];
|
|
50624
50705
|
}
|
|
50625
50706
|
for (const agentId of agents) {
|
|
50626
|
-
const authDir =
|
|
50627
|
-
const authPath =
|
|
50707
|
+
const authDir = join10(agentsDir, agentId, "agent");
|
|
50708
|
+
const authPath = join10(authDir, "auth-profiles.json");
|
|
50628
50709
|
if (!existsSync3(authDir)) {
|
|
50629
50710
|
try {
|
|
50630
|
-
|
|
50711
|
+
mkdirSync3(authDir, { recursive: true });
|
|
50631
50712
|
} catch {
|
|
50632
50713
|
continue;
|
|
50633
50714
|
}
|
|
@@ -50655,7 +50736,7 @@ function injectAuthProfile(logger) {
|
|
|
50655
50736
|
key: "x402-proxy-handles-auth"
|
|
50656
50737
|
};
|
|
50657
50738
|
try {
|
|
50658
|
-
|
|
50739
|
+
writeFileSync3(authPath, JSON.stringify(store, null, 2));
|
|
50659
50740
|
logger.info(`Injected BlockRun auth profile for agent: ${agentId}`);
|
|
50660
50741
|
} catch (err) {
|
|
50661
50742
|
logger.info(
|
|
@@ -50719,6 +50800,10 @@ async function startProxyInBackground(api) {
|
|
|
50719
50800
|
});
|
|
50720
50801
|
setActiveProxy(proxy);
|
|
50721
50802
|
activeProxyHandle = proxy;
|
|
50803
|
+
const startupExclusions = loadExcludeList();
|
|
50804
|
+
if (startupExclusions.size > 0) {
|
|
50805
|
+
api.logger.info(`Model exclusions active (${startupExclusions.size}): ${[...startupExclusions].join(", ")}`);
|
|
50806
|
+
}
|
|
50722
50807
|
api.logger.info(`ClawRouter ready \u2014 smart routing enabled`);
|
|
50723
50808
|
api.logger.info(`Pricing: Simple ~$0.001 | Code ~$0.01 | Complex ~$0.05 | Free: $0`);
|
|
50724
50809
|
const currentChain = await resolvePaymentChain();
|
|
@@ -50778,6 +50863,78 @@ async function createStatsCommand() {
|
|
|
50778
50863
|
}
|
|
50779
50864
|
};
|
|
50780
50865
|
}
|
|
50866
|
+
async function createExcludeCommand() {
|
|
50867
|
+
return {
|
|
50868
|
+
name: "exclude",
|
|
50869
|
+
description: "Manage excluded models \u2014 /exclude add|remove|clear <model>",
|
|
50870
|
+
acceptsArgs: true,
|
|
50871
|
+
requireAuth: true,
|
|
50872
|
+
handler: async (ctx) => {
|
|
50873
|
+
const args = ctx.args?.trim() || "";
|
|
50874
|
+
const parts = args.split(/\s+/);
|
|
50875
|
+
const subcommand = parts[0]?.toLowerCase() || "";
|
|
50876
|
+
const modelArg = parts.slice(1).join(" ").trim();
|
|
50877
|
+
if (!subcommand) {
|
|
50878
|
+
const list = loadExcludeList();
|
|
50879
|
+
if (list.size === 0) {
|
|
50880
|
+
return {
|
|
50881
|
+
text: "No models excluded.\n\nUsage:\n /exclude add <model> \u2014 block a model\n /exclude remove <model> \u2014 unblock\n /exclude clear \u2014 remove all"
|
|
50882
|
+
};
|
|
50883
|
+
}
|
|
50884
|
+
const models = [...list].sort().map((m) => ` \u2022 ${m}`).join("\n");
|
|
50885
|
+
return {
|
|
50886
|
+
text: `Excluded models (${list.size}):
|
|
50887
|
+
${models}
|
|
50888
|
+
|
|
50889
|
+
Use /exclude remove <model> to unblock.`
|
|
50890
|
+
};
|
|
50891
|
+
}
|
|
50892
|
+
if (subcommand === "add") {
|
|
50893
|
+
if (!modelArg) {
|
|
50894
|
+
return { text: "Usage: /exclude add <model>\nExample: /exclude add nvidia/gpt-oss-120b", isError: true };
|
|
50895
|
+
}
|
|
50896
|
+
const resolved = addExclusion(modelArg);
|
|
50897
|
+
const list = loadExcludeList();
|
|
50898
|
+
return {
|
|
50899
|
+
text: `Excluded: ${resolved}
|
|
50900
|
+
|
|
50901
|
+
Active exclusions (${list.size}):
|
|
50902
|
+
${[...list].sort().map((m) => ` \u2022 ${m}`).join("\n")}`
|
|
50903
|
+
};
|
|
50904
|
+
}
|
|
50905
|
+
if (subcommand === "remove") {
|
|
50906
|
+
if (!modelArg) {
|
|
50907
|
+
return { text: "Usage: /exclude remove <model>", isError: true };
|
|
50908
|
+
}
|
|
50909
|
+
const removed = removeExclusion(modelArg);
|
|
50910
|
+
if (!removed) {
|
|
50911
|
+
return { text: `Model "${modelArg}" was not in the exclude list.` };
|
|
50912
|
+
}
|
|
50913
|
+
const list = loadExcludeList();
|
|
50914
|
+
return {
|
|
50915
|
+
text: `Unblocked: ${modelArg}
|
|
50916
|
+
|
|
50917
|
+
Active exclusions (${list.size}):
|
|
50918
|
+
${list.size > 0 ? [...list].sort().map((m) => ` \u2022 ${m}`).join("\n") : " (none)"}`
|
|
50919
|
+
};
|
|
50920
|
+
}
|
|
50921
|
+
if (subcommand === "clear") {
|
|
50922
|
+
clearExclusions();
|
|
50923
|
+
return { text: "All model exclusions cleared." };
|
|
50924
|
+
}
|
|
50925
|
+
return {
|
|
50926
|
+
text: `Unknown subcommand: ${subcommand}
|
|
50927
|
+
|
|
50928
|
+
Usage:
|
|
50929
|
+
/exclude \u2014 show list
|
|
50930
|
+
/exclude add <model>
|
|
50931
|
+
/exclude remove <model>
|
|
50932
|
+
/exclude clear`,
|
|
50933
|
+
isError: true
|
|
50934
|
+
};
|
|
50935
|
+
}
|
|
50936
|
+
};
|
|
50937
|
+
}
|
|
50781
50938
|
async function createWalletCommand() {
|
|
50782
50939
|
return {
|
|
50783
50940
|
name: "wallet",
|
|
@@ -51090,6 +51247,13 @@ var plugin = {
|
|
|
51090
51247
|
`Failed to register /stats command: ${err instanceof Error ? err.message : String(err)}`
|
|
51091
51248
|
);
|
|
51092
51249
|
});
|
|
51250
|
+
createExcludeCommand().then((excludeCommand) => {
|
|
51251
|
+
api.registerCommand(excludeCommand);
|
|
51252
|
+
}).catch((err) => {
|
|
51253
|
+
api.logger.warn(
|
|
51254
|
+
`Failed to register /exclude command: ${err instanceof Error ? err.message : String(err)}`
|
|
51255
|
+
);
|
|
51256
|
+
});
|
|
51093
51257
|
api.registerService({
|
|
51094
51258
|
id: "clawrouter-proxy",
|
|
51095
51259
|
start: () => {
|