@blockrun/clawrouter 0.12.256 → 0.12.258

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 CHANGED
@@ -27426,8 +27426,8 @@ var init_parseUtil = __esm({
27426
27426
  init_errors4();
27427
27427
  init_en();
27428
27428
  makeIssue = (params) => {
27429
- const { data, path: path6, errorMaps, issueData } = params;
27430
- const fullPath = [...path6, ...issueData.path || []];
27429
+ const { data, path: path5, errorMaps, issueData } = params;
27430
+ const fullPath = [...path5, ...issueData.path || []];
27431
27431
  const fullIssue = {
27432
27432
  ...issueData,
27433
27433
  path: fullPath
@@ -27738,11 +27738,11 @@ var init_types = __esm({
27738
27738
  init_parseUtil();
27739
27739
  init_util();
27740
27740
  ParseInputLazyPath = class {
27741
- constructor(parent, value, path6, key2) {
27741
+ constructor(parent, value, path5, key2) {
27742
27742
  this._cachedPath = [];
27743
27743
  this.parent = parent;
27744
27744
  this.data = value;
27745
- this._path = path6;
27745
+ this._path = path5;
27746
27746
  this._key = key2;
27747
27747
  }
27748
27748
  get path() {
@@ -32235,6 +32235,613 @@ var init_max_tokens = __esm({
32235
32235
  }
32236
32236
  });
32237
32237
 
32238
+ // src/fs-read.ts
32239
+ import { open } from "fs/promises";
32240
+ import { openSync, readSync, closeSync, fstatSync } from "fs";
32241
+ async function readTextFile(filePath) {
32242
+ const fh = await open(filePath, "r");
32243
+ try {
32244
+ const size5 = (await fh.stat()).size;
32245
+ const buf = Buffer.alloc(size5);
32246
+ let offset = 0;
32247
+ while (offset < size5) {
32248
+ const { bytesRead } = await fh.read(buf, offset, size5 - offset, offset);
32249
+ if (bytesRead === 0) break;
32250
+ offset += bytesRead;
32251
+ }
32252
+ return buf.subarray(0, offset).toString("utf-8");
32253
+ } finally {
32254
+ await fh.close();
32255
+ }
32256
+ }
32257
+ function readTextFileSync(filePath) {
32258
+ const fd = openSync(filePath, "r");
32259
+ try {
32260
+ const size5 = fstatSync(fd).size;
32261
+ const buf = Buffer.alloc(size5);
32262
+ let offset = 0;
32263
+ while (offset < size5) {
32264
+ const bytesRead = readSync(fd, buf, offset, size5 - offset, offset);
32265
+ if (bytesRead === 0) break;
32266
+ offset += bytesRead;
32267
+ }
32268
+ return buf.subarray(0, offset).toString("utf-8");
32269
+ } finally {
32270
+ closeSync(fd);
32271
+ }
32272
+ }
32273
+ var init_fs_read = __esm({
32274
+ "src/fs-read.ts"() {
32275
+ "use strict";
32276
+ }
32277
+ });
32278
+
32279
+ // src/spend-control.ts
32280
+ import * as fs from "fs";
32281
+ import * as path from "path";
32282
+ import { homedir } from "os";
32283
+ function normalizePayee(value) {
32284
+ return EVM_ADDRESS.test(value) ? value.toLowerCase() : value;
32285
+ }
32286
+ function isAddressList(list) {
32287
+ return ADDRESS_LISTS.includes(list);
32288
+ }
32289
+ function normalizePolicyValues(list, values) {
32290
+ return isAddressList(list) ? values.map(normalizePayee) : [...values];
32291
+ }
32292
+ function isValidPolicyValues(values) {
32293
+ return Array.isArray(values) && values.every((v) => typeof v === "string" && v.length > 0);
32294
+ }
32295
+ function isPolicyList(value) {
32296
+ return POLICY_LISTS.includes(value);
32297
+ }
32298
+ function cloneLimits(limits) {
32299
+ const clone = { ...limits };
32300
+ for (const key2 of POLICY_LISTS) {
32301
+ const val = limits[key2];
32302
+ if (val !== void 0) {
32303
+ clone[key2] = [...val];
32304
+ }
32305
+ }
32306
+ return clone;
32307
+ }
32308
+ function parseQuotedAmountUsd(selected) {
32309
+ const raw = selected.amount ?? selected.maxAmountRequired;
32310
+ if (typeof raw !== "string" || !CANONICAL_AMOUNT.test(raw)) {
32311
+ return void 0;
32312
+ }
32313
+ const micros = Number(raw);
32314
+ if (!Number.isSafeInteger(micros)) {
32315
+ return void 0;
32316
+ }
32317
+ return micros / 1e6;
32318
+ }
32319
+ function assertSpendPolicyAllows(control, selected) {
32320
+ const quoted = parseQuotedAmountUsd(selected);
32321
+ if (quoted === void 0 && control.hasAmountLimits()) {
32322
+ throw new SpendPolicyError(
32323
+ `Payment quote carries no usable amount (${JSON.stringify(
32324
+ selected.amount ?? selected.maxAmountRequired
32325
+ )}); refusing to sign against a configured spend limit`
32326
+ );
32327
+ }
32328
+ const estimatedCost2 = quoted ?? 0;
32329
+ const result = control.check(estimatedCost2, {
32330
+ payTo: selected.payTo,
32331
+ network: selected.network,
32332
+ asset: selected.asset
32333
+ });
32334
+ if (!result.allowed) {
32335
+ throw new SpendPolicyError(result.reason ?? "blocked by spend policy", {
32336
+ blockedBy: result.blockedBy,
32337
+ blockedByPolicy: result.blockedByPolicy
32338
+ });
32339
+ }
32340
+ if (!control.hasAggregateLimits()) {
32341
+ return void 0;
32342
+ }
32343
+ return control.reserve(estimatedCost2);
32344
+ }
32345
+ function registerSpendPolicyHook(x402, control) {
32346
+ const reservations = /* @__PURE__ */ new WeakMap();
32347
+ x402.onBeforePaymentCreation(async (ctx) => {
32348
+ const selected = ctx.selectedRequirements;
32349
+ const reservationId = assertSpendPolicyAllows(control, selected);
32350
+ if (reservationId !== void 0) {
32351
+ reservations.set(ctx.selectedRequirements, reservationId);
32352
+ }
32353
+ });
32354
+ x402.onAfterPaymentCreation(async (ctx) => {
32355
+ const key2 = ctx.selectedRequirements;
32356
+ const id2 = reservations.get(key2);
32357
+ if (id2 !== void 0) {
32358
+ reservations.delete(key2);
32359
+ control.settleReservation(id2, { action: "x402 payment" });
32360
+ }
32361
+ });
32362
+ x402.onPaymentCreationFailure(async (ctx) => {
32363
+ const key2 = ctx.selectedRequirements;
32364
+ const id2 = reservations.get(key2);
32365
+ if (id2 !== void 0) {
32366
+ reservations.delete(key2);
32367
+ control.releaseReservation(id2);
32368
+ }
32369
+ });
32370
+ }
32371
+ function formatDuration(seconds) {
32372
+ if (seconds < 60) {
32373
+ return `${seconds}s`;
32374
+ } else if (seconds < 3600) {
32375
+ const mins = Math.ceil(seconds / 60);
32376
+ return `${mins} min`;
32377
+ } else {
32378
+ const hours = Math.floor(seconds / 3600);
32379
+ const mins = Math.ceil(seconds % 3600 / 60);
32380
+ return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
32381
+ }
32382
+ }
32383
+ var WALLET_DIR, HOUR_MS, DAY_MS, CAIP2_BASE, CAIP2_SOLANA_MAINNET, POLICY_LISTS, ADDRESS_LISTS, EVM_ADDRESS, MalformedSpendPolicyError, FileSpendControlStorage, InMemorySpendControlStorage, RESERVATION_TTL_MS, SpendControl, SpendPolicyError, CANONICAL_AMOUNT;
32384
+ var init_spend_control = __esm({
32385
+ "src/spend-control.ts"() {
32386
+ "use strict";
32387
+ init_fs_read();
32388
+ WALLET_DIR = path.join(homedir(), ".openclaw", "blockrun");
32389
+ HOUR_MS = 60 * 60 * 1e3;
32390
+ DAY_MS = 24 * HOUR_MS;
32391
+ CAIP2_BASE = "eip155:8453";
32392
+ CAIP2_SOLANA_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d";
32393
+ POLICY_LISTS = [
32394
+ "allowedPayees",
32395
+ "blockedPayees",
32396
+ "allowedNetworks",
32397
+ "allowedAssets"
32398
+ ];
32399
+ ADDRESS_LISTS = ["allowedPayees", "blockedPayees", "allowedAssets"];
32400
+ EVM_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
32401
+ MalformedSpendPolicyError = class extends Error {
32402
+ constructor(key2) {
32403
+ super(
32404
+ `[ClawRouter] refusing to load spending.json: ${key2} is malformed; a corrupted policy file must not widen what the agent may pay`
32405
+ );
32406
+ this.name = "MalformedSpendPolicyError";
32407
+ }
32408
+ };
32409
+ FileSpendControlStorage = class {
32410
+ spendingFile;
32411
+ constructor() {
32412
+ this.spendingFile = path.join(WALLET_DIR, "spending.json");
32413
+ }
32414
+ load() {
32415
+ try {
32416
+ if (fs.existsSync(this.spendingFile)) {
32417
+ const data = JSON.parse(readTextFileSync(this.spendingFile));
32418
+ const rawLimits = data.limits ?? {};
32419
+ const rawHistory = data.history ?? [];
32420
+ const limits = {};
32421
+ for (const key2 of ["perRequest", "hourly", "daily", "session"]) {
32422
+ const val = rawLimits[key2];
32423
+ if (typeof val === "number" && val > 0 && Number.isFinite(val)) {
32424
+ limits[key2] = val;
32425
+ }
32426
+ }
32427
+ for (const key2 of POLICY_LISTS) {
32428
+ if (!Object.prototype.hasOwnProperty.call(rawLimits, key2)) continue;
32429
+ const val = rawLimits[key2];
32430
+ if (!isValidPolicyValues(val)) {
32431
+ throw new MalformedSpendPolicyError(key2);
32432
+ }
32433
+ if (val.length === 0) continue;
32434
+ limits[key2] = normalizePolicyValues(key2, val);
32435
+ }
32436
+ const history = [];
32437
+ if (Array.isArray(rawHistory)) {
32438
+ for (const r2 of rawHistory) {
32439
+ if (typeof r2?.timestamp === "number" && typeof r2?.amount === "number" && Number.isFinite(r2.timestamp) && Number.isFinite(r2.amount) && r2.amount >= 0) {
32440
+ history.push({
32441
+ timestamp: r2.timestamp,
32442
+ amount: r2.amount,
32443
+ model: typeof r2.model === "string" ? r2.model : void 0,
32444
+ action: typeof r2.action === "string" ? r2.action : void 0
32445
+ });
32446
+ }
32447
+ }
32448
+ }
32449
+ return { limits, history };
32450
+ }
32451
+ } catch (err) {
32452
+ if (err instanceof MalformedSpendPolicyError) {
32453
+ throw err;
32454
+ }
32455
+ console.error(
32456
+ `[ClawRouter] Failed to load spending data, starting fresh (any configured spend policy is NOT in effect until this file is repaired): ${err}`
32457
+ );
32458
+ }
32459
+ return null;
32460
+ }
32461
+ save(data) {
32462
+ try {
32463
+ if (!fs.existsSync(WALLET_DIR)) {
32464
+ fs.mkdirSync(WALLET_DIR, { recursive: true, mode: 448 });
32465
+ }
32466
+ const tmp = `${this.spendingFile}.${process.pid}.tmp`;
32467
+ fs.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
32468
+ fs.renameSync(tmp, this.spendingFile);
32469
+ } catch (err) {
32470
+ console.error(`[ClawRouter] Failed to save spending data: ${err}`);
32471
+ }
32472
+ }
32473
+ /**
32474
+ * Persist history while leaving the stored limits exactly as they are on
32475
+ * disk. Recording spend must not rewrite policy: the proxy reads limits once
32476
+ * at startup, so writing its in-memory copy back on every payment would
32477
+ * erase an operator's hand-edit to spending.json seconds after they made it.
32478
+ */
32479
+ saveHistory(history) {
32480
+ let storedLimits = {};
32481
+ try {
32482
+ const current = this.load();
32483
+ if (current) storedLimits = current.limits;
32484
+ } catch {
32485
+ return;
32486
+ }
32487
+ this.save({ limits: storedLimits, history });
32488
+ }
32489
+ };
32490
+ InMemorySpendControlStorage = class {
32491
+ data = null;
32492
+ load() {
32493
+ return this.data ? {
32494
+ limits: cloneLimits(this.data.limits),
32495
+ history: this.data.history.map((r2) => ({ ...r2 }))
32496
+ } : null;
32497
+ }
32498
+ save(data) {
32499
+ this.data = {
32500
+ limits: cloneLimits(data.limits),
32501
+ history: data.history.map((r2) => ({ ...r2 }))
32502
+ };
32503
+ }
32504
+ };
32505
+ RESERVATION_TTL_MS = 2 * 60 * 1e3;
32506
+ SpendControl = class {
32507
+ limits = {};
32508
+ history = [];
32509
+ sessionSpent = 0;
32510
+ sessionCalls = 0;
32511
+ pending = /* @__PURE__ */ new Map();
32512
+ reservationSeq = 0;
32513
+ /** Limits we loaded and have not changed; history-only saves must not clobber operator edits. */
32514
+ limitsDirty = false;
32515
+ /** Set when spending.json held an unusable policy list: refuse every payment. */
32516
+ policyFileBroken;
32517
+ storage;
32518
+ now;
32519
+ constructor(options) {
32520
+ this.storage = options?.storage ?? new FileSpendControlStorage();
32521
+ this.now = options?.now ?? (() => Date.now());
32522
+ this.load();
32523
+ }
32524
+ setLimit(window2, amount) {
32525
+ if (!Number.isFinite(amount) || amount <= 0) {
32526
+ throw new Error("Limit must be a finite positive number");
32527
+ }
32528
+ this.limits[window2] = amount;
32529
+ this.limitsDirty = true;
32530
+ this.save();
32531
+ }
32532
+ clearLimit(window2) {
32533
+ delete this.limits[window2];
32534
+ this.limitsDirty = true;
32535
+ this.save();
32536
+ }
32537
+ setPolicy(list, values) {
32538
+ if (!isPolicyList(list)) {
32539
+ throw new Error(`Unknown policy list: ${String(list)}`);
32540
+ }
32541
+ if (!isValidPolicyValues(values) || values.length === 0) {
32542
+ throw new Error("Policy list must be a non-empty array of non-empty strings");
32543
+ }
32544
+ this.limits[list] = normalizePolicyValues(list, values);
32545
+ this.limitsDirty = true;
32546
+ this.save();
32547
+ }
32548
+ clearPolicy(list) {
32549
+ if (!isPolicyList(list)) {
32550
+ throw new Error(`Unknown policy list: ${String(list)}`);
32551
+ }
32552
+ delete this.limits[list];
32553
+ this.limitsDirty = true;
32554
+ this.save();
32555
+ }
32556
+ getLimits() {
32557
+ return cloneLimits(this.limits);
32558
+ }
32559
+ check(estimatedCost2, counterparty) {
32560
+ if (this.policyFileBroken !== void 0) {
32561
+ return {
32562
+ allowed: false,
32563
+ reason: `Spend policy is unreadable, refusing all payments: ${this.policyFileBroken}`
32564
+ };
32565
+ }
32566
+ const payeePolicySet = this.limits.blockedPayees && this.limits.blockedPayees.length > 0 || this.limits.allowedPayees && this.limits.allowedPayees.length > 0;
32567
+ if (payeePolicySet) {
32568
+ if (counterparty?.payTo === void 0) {
32569
+ return {
32570
+ allowed: false,
32571
+ blockedByPolicy: this.limits.blockedPayees?.length ? "blockedPayees" : "allowedPayees",
32572
+ reason: "Payee policy is configured but no payTo was provided to check()"
32573
+ };
32574
+ }
32575
+ const payTo = normalizePayee(counterparty.payTo);
32576
+ if (this.limits.blockedPayees?.includes(payTo)) {
32577
+ return {
32578
+ allowed: false,
32579
+ blockedByPolicy: "blockedPayees",
32580
+ reason: `Payee is blocked by policy: ${counterparty.payTo}`
32581
+ };
32582
+ }
32583
+ if (this.limits.allowedPayees && this.limits.allowedPayees.length > 0 && !this.limits.allowedPayees.includes(payTo)) {
32584
+ return {
32585
+ allowed: false,
32586
+ blockedByPolicy: "allowedPayees",
32587
+ reason: `Payee is not in the configured allowlist: ${counterparty.payTo}`
32588
+ };
32589
+ }
32590
+ }
32591
+ if (this.limits.allowedNetworks && this.limits.allowedNetworks.length > 0) {
32592
+ if (counterparty?.network === void 0) {
32593
+ return {
32594
+ allowed: false,
32595
+ blockedByPolicy: "allowedNetworks",
32596
+ reason: "Network policy is configured but no network was provided to check()"
32597
+ };
32598
+ }
32599
+ if (!this.limits.allowedNetworks.includes(counterparty.network)) {
32600
+ return {
32601
+ allowed: false,
32602
+ blockedByPolicy: "allowedNetworks",
32603
+ reason: `Network is not in the configured allowlist: ${counterparty.network}`
32604
+ };
32605
+ }
32606
+ }
32607
+ if (this.limits.allowedAssets && this.limits.allowedAssets.length > 0) {
32608
+ if (counterparty?.asset === void 0) {
32609
+ return {
32610
+ allowed: false,
32611
+ blockedByPolicy: "allowedAssets",
32612
+ reason: "Asset policy is configured but no asset was provided to check()"
32613
+ };
32614
+ }
32615
+ if (!this.limits.allowedAssets.includes(normalizePayee(counterparty.asset))) {
32616
+ return {
32617
+ allowed: false,
32618
+ blockedByPolicy: "allowedAssets",
32619
+ reason: `Asset is not in the configured allowlist: ${counterparty.asset}`
32620
+ };
32621
+ }
32622
+ }
32623
+ const now2 = this.now();
32624
+ if (this.limits.perRequest !== void 0) {
32625
+ if (estimatedCost2 > this.limits.perRequest) {
32626
+ return {
32627
+ allowed: false,
32628
+ blockedBy: "perRequest",
32629
+ remaining: this.limits.perRequest,
32630
+ reason: `Per-request limit exceeded: $${estimatedCost2.toFixed(4)} > $${this.limits.perRequest.toFixed(2)} max`
32631
+ };
32632
+ }
32633
+ }
32634
+ if (this.limits.hourly !== void 0) {
32635
+ const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
32636
+ const remaining = this.limits.hourly - hourlySpent;
32637
+ if (estimatedCost2 > remaining) {
32638
+ const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - HOUR_MS);
32639
+ const resetIn = oldestInWindow ? Math.ceil((oldestInWindow.timestamp + HOUR_MS - now2) / 1e3) : 0;
32640
+ return {
32641
+ allowed: false,
32642
+ blockedBy: "hourly",
32643
+ remaining,
32644
+ reason: `Hourly limit exceeded: $${(hourlySpent + estimatedCost2).toFixed(2)} > $${this.limits.hourly.toFixed(2)} max`,
32645
+ resetIn
32646
+ };
32647
+ }
32648
+ }
32649
+ if (this.limits.daily !== void 0) {
32650
+ const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
32651
+ const remaining = this.limits.daily - dailySpent;
32652
+ if (estimatedCost2 > remaining) {
32653
+ const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - DAY_MS);
32654
+ const resetIn = oldestInWindow ? Math.ceil((oldestInWindow.timestamp + DAY_MS - now2) / 1e3) : 0;
32655
+ return {
32656
+ allowed: false,
32657
+ blockedBy: "daily",
32658
+ remaining,
32659
+ reason: `Daily limit exceeded: $${(dailySpent + estimatedCost2).toFixed(2)} > $${this.limits.daily.toFixed(2)} max`,
32660
+ resetIn
32661
+ };
32662
+ }
32663
+ }
32664
+ if (this.limits.session !== void 0) {
32665
+ const sessionSpent = this.sessionSpent + this.pendingTotal();
32666
+ const remaining = this.limits.session - sessionSpent;
32667
+ if (estimatedCost2 > remaining) {
32668
+ return {
32669
+ allowed: false,
32670
+ blockedBy: "session",
32671
+ remaining,
32672
+ reason: `Session limit exceeded: $${(sessionSpent + estimatedCost2).toFixed(2)} > $${this.limits.session.toFixed(2)} max`
32673
+ };
32674
+ }
32675
+ }
32676
+ return { allowed: true };
32677
+ }
32678
+ record(amount, metadata) {
32679
+ if (!Number.isFinite(amount) || amount < 0) {
32680
+ throw new Error("Record amount must be a non-negative finite number");
32681
+ }
32682
+ const record = {
32683
+ timestamp: this.now(),
32684
+ amount,
32685
+ model: metadata?.model,
32686
+ action: metadata?.action
32687
+ };
32688
+ this.history.push(record);
32689
+ this.sessionSpent += amount;
32690
+ this.sessionCalls += 1;
32691
+ this.cleanup();
32692
+ this.save();
32693
+ }
32694
+ /** True when any window that this module can compare an amount against is set. */
32695
+ hasAmountLimits() {
32696
+ return this.limits.perRequest !== void 0 || this.limits.hourly !== void 0 || this.limits.daily !== void 0 || this.limits.session !== void 0;
32697
+ }
32698
+ /** True when a window spans more than one request, so reservations matter. */
32699
+ hasAggregateLimits() {
32700
+ return this.limits.hourly !== void 0 || this.limits.daily !== void 0 || this.limits.session !== void 0;
32701
+ }
32702
+ /**
32703
+ * Hold `amount` against the aggregate windows before a payment is signed.
32704
+ *
32705
+ * Reservations live in memory only and are never persisted: an unsettled
32706
+ * reservation is not spend, and writing it to disk is what made a failed
32707
+ * signer permanently consume budget. They expire on their own so a caller
32708
+ * that never settles or releases (process killed mid-payment, a transport
32709
+ * that hangs past the payment timeout) cannot wedge the window shut.
32710
+ */
32711
+ reserve(amount) {
32712
+ if (!Number.isFinite(amount) || amount < 0) {
32713
+ throw new Error("Reservation amount must be a non-negative finite number");
32714
+ }
32715
+ const id2 = `${this.now()}-${this.reservationSeq += 1}`;
32716
+ this.pending.set(id2, { amount, expiresAt: this.now() + RESERVATION_TTL_MS });
32717
+ return id2;
32718
+ }
32719
+ /** Convert a reservation into recorded spend (the payment was signed). */
32720
+ settleReservation(id2, metadata) {
32721
+ const held = this.pending.get(id2);
32722
+ if (!held) return;
32723
+ this.pending.delete(id2);
32724
+ this.record(held.amount, metadata);
32725
+ }
32726
+ /** Drop a reservation without recording spend (the payment was never signed). */
32727
+ releaseReservation(id2) {
32728
+ this.pending.delete(id2);
32729
+ }
32730
+ /** Total currently held but not yet settled. */
32731
+ pendingTotal() {
32732
+ this.expireReservations();
32733
+ let total = 0;
32734
+ for (const held of this.pending.values()) {
32735
+ total += held.amount;
32736
+ }
32737
+ return total;
32738
+ }
32739
+ expireReservations() {
32740
+ const now2 = this.now();
32741
+ for (const [id2, held] of this.pending) {
32742
+ if (held.expiresAt <= now2) {
32743
+ this.pending.delete(id2);
32744
+ }
32745
+ }
32746
+ }
32747
+ getSpendingInWindow(from15, to) {
32748
+ const recorded = this.history.filter((r2) => r2.timestamp >= from15 && r2.timestamp <= to).reduce((sum, r2) => sum + r2.amount, 0);
32749
+ return recorded + (to >= this.now() ? this.pendingTotal() : 0);
32750
+ }
32751
+ getSpending(window2) {
32752
+ const now2 = this.now();
32753
+ switch (window2) {
32754
+ case "hourly":
32755
+ return this.getSpendingInWindow(now2 - HOUR_MS, now2);
32756
+ case "daily":
32757
+ return this.getSpendingInWindow(now2 - DAY_MS, now2);
32758
+ case "session":
32759
+ return this.sessionSpent + this.pendingTotal();
32760
+ }
32761
+ }
32762
+ getRemaining(window2) {
32763
+ const limit = this.limits[window2];
32764
+ if (limit === void 0) return null;
32765
+ return Math.max(0, limit - this.getSpending(window2));
32766
+ }
32767
+ getStatus() {
32768
+ const now2 = this.now();
32769
+ const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
32770
+ const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
32771
+ return {
32772
+ limits: cloneLimits(this.limits),
32773
+ spending: {
32774
+ hourly: hourlySpent,
32775
+ daily: dailySpent,
32776
+ session: this.sessionSpent
32777
+ },
32778
+ remaining: {
32779
+ hourly: this.limits.hourly !== void 0 ? this.limits.hourly - hourlySpent : null,
32780
+ daily: this.limits.daily !== void 0 ? this.limits.daily - dailySpent : null,
32781
+ session: this.limits.session !== void 0 ? this.limits.session - (this.sessionSpent + this.pendingTotal()) : null
32782
+ },
32783
+ calls: this.sessionCalls
32784
+ };
32785
+ }
32786
+ getHistory(limit) {
32787
+ const records = [...this.history].reverse();
32788
+ return limit ? records.slice(0, limit) : records;
32789
+ }
32790
+ resetSession() {
32791
+ this.sessionSpent = 0;
32792
+ this.sessionCalls = 0;
32793
+ }
32794
+ cleanup() {
32795
+ const cutoff = this.now() - DAY_MS;
32796
+ this.history = this.history.filter((r2) => r2.timestamp >= cutoff);
32797
+ }
32798
+ save() {
32799
+ if (this.policyFileBroken !== void 0) {
32800
+ return;
32801
+ }
32802
+ if (!this.limitsDirty && this.storage.saveHistory) {
32803
+ this.storage.saveHistory([...this.history]);
32804
+ return;
32805
+ }
32806
+ this.storage.save({
32807
+ limits: cloneLimits(this.limits),
32808
+ history: [...this.history]
32809
+ });
32810
+ }
32811
+ load() {
32812
+ let data;
32813
+ try {
32814
+ data = this.storage.load();
32815
+ } catch (err) {
32816
+ if (!(err instanceof MalformedSpendPolicyError)) throw err;
32817
+ this.policyFileBroken = err.message;
32818
+ console.error(`[ClawRouter] ${err.message}`);
32819
+ console.error(
32820
+ "[ClawRouter] All paid requests will be refused until spending.json is repaired. Free models are unaffected."
32821
+ );
32822
+ return;
32823
+ }
32824
+ if (data) {
32825
+ this.limits = cloneLimits(data.limits);
32826
+ this.history = data.history;
32827
+ this.cleanup();
32828
+ }
32829
+ }
32830
+ };
32831
+ SpendPolicyError = class extends Error {
32832
+ blockedBy;
32833
+ blockedByPolicy;
32834
+ constructor(reason, blocked) {
32835
+ super(`Payment creation aborted: ${reason}`);
32836
+ this.name = "SpendPolicyError";
32837
+ this.blockedBy = blocked?.blockedBy;
32838
+ this.blockedByPolicy = blocked?.blockedByPolicy;
32839
+ }
32840
+ };
32841
+ CANONICAL_AMOUNT = /^\d+$/;
32842
+ }
32843
+ });
32844
+
32238
32845
  // src/payment-preauth.ts
32239
32846
  function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, options) {
32240
32847
  const httpClient = new x402HTTPClient(client);
@@ -32279,8 +32886,11 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
32279
32886
  return response2;
32280
32887
  }
32281
32888
  cache2.delete(cacheKey2);
32282
- } catch {
32889
+ } catch (err) {
32283
32890
  cache2.delete(cacheKey2);
32891
+ if (err instanceof SpendPolicyError) {
32892
+ throw err;
32893
+ }
32284
32894
  }
32285
32895
  }
32286
32896
  const clonedRequest = request2.clone();
@@ -32324,6 +32934,7 @@ var init_payment_preauth = __esm({
32324
32934
  "use strict";
32325
32935
  init_esm4();
32326
32936
  init_max_tokens();
32937
+ init_spend_control();
32327
32938
  DEFAULT_TTL_MS = 36e5;
32328
32939
  }
32329
32940
  });
@@ -34422,6 +35033,13 @@ ${value.slice(-(scanLimit - prefixLength))}`;
34422
35033
  supportsTools: true,
34423
35034
  supportsVision: true
34424
35035
  },
35036
+ "cohere/north-mini-code": {
35037
+ // supportsTools: not probed — fails closed
35038
+ contextWindow: 256e3,
35039
+ maxOutputTokens: 16384,
35040
+ supportsTools: false,
35041
+ supportsVision: false
35042
+ },
34425
35043
  "deepseek/deepseek-chat": {
34426
35044
  contextWindow: 1048576,
34427
35045
  maxOutputTokens: 65536,
@@ -34512,8 +35130,15 @@ ${value.slice(-(scanLimit - prefixLength))}`;
34512
35130
  supportsTools: true,
34513
35131
  supportsVision: true
34514
35132
  },
34515
- "nvidia/mistral-nemotron": {
34516
- // supportsTools: gateway unavailable at probe time — fails closed
35133
+ "nvidia/llama-3.2-11b-vision": {
35134
+ // supportsTools: not probed — fails closed
35135
+ contextWindow: 128e3,
35136
+ maxOutputTokens: 16384,
35137
+ supportsTools: false,
35138
+ supportsVision: true
35139
+ },
35140
+ "nvidia/nemotron-3-nano-30b": {
35141
+ // supportsTools: not probed — fails closed
34517
35142
  contextWindow: 131072,
34518
35143
  maxOutputTokens: 16384,
34519
35144
  supportsTools: false,
@@ -34525,21 +35150,16 @@ ${value.slice(-(scanLimit - prefixLength))}`;
34525
35150
  supportsTools: false,
34526
35151
  supportsVision: true
34527
35152
  },
34528
- "nvidia/nemotron-nano-12b-v2-vl": {
34529
- // supportsTools: gateway unavailable at probe time — fails closed
34530
- contextWindow: 131072,
34531
- maxOutputTokens: 16384,
34532
- supportsTools: false,
34533
- supportsVision: true
34534
- },
34535
- "nvidia/nemotron-nano-9b-v2": {
34536
- contextWindow: 131072,
35153
+ "nvidia/nemotron-3-ultra-550b": {
35154
+ // supportsTools: not probed — fails closed
35155
+ contextWindow: 1e6,
34537
35156
  maxOutputTokens: 16384,
34538
35157
  supportsTools: false,
34539
35158
  supportsVision: false
34540
35159
  },
34541
- "nvidia/step-3.7-flash": {
34542
- contextWindow: 131072,
35160
+ "nvidia/nemotron-3.5-lightning": {
35161
+ // supportsTools: not probed — fails closed
35162
+ contextWindow: 1e6,
34543
35163
  maxOutputTokens: 16384,
34544
35164
  supportsTools: false,
34545
35165
  supportsVision: false
@@ -34599,13 +35219,6 @@ ${value.slice(-(scanLimit - prefixLength))}`;
34599
35219
  supportsTools: false,
34600
35220
  supportsVision: true
34601
35221
  },
34602
- "openai/gpt-5.3": {
34603
- // supportsTools: gateway unavailable at probe time — fails closed
34604
- contextWindow: 128e3,
34605
- maxOutputTokens: 128e3,
34606
- supportsTools: false,
34607
- supportsVision: true
34608
- },
34609
35222
  "openai/gpt-5.3-codex": {
34610
35223
  // supportsTools: gateway unavailable at probe time — fails closed; override: 2026-08-29 probe: every request (6 plain + 3 tool attempts) returned a gateway 500, so the probe measured an incident, not the model. Codex's function calling is established by the 2026-07 Terminal-Bench / tau2 calibration trajectories in portfolio.ts. Hosts observing the 500s should drop it with RouterOptions.unavailableModels rather than this snapshot claiming the model cannot call tools.
34611
35224
  contextWindow: 4e5,
@@ -34711,6 +35324,13 @@ ${value.slice(-(scanLimit - prefixLength))}`;
34711
35324
  supportsTools: true,
34712
35325
  supportsVision: false
34713
35326
  },
35327
+ "poolside/laguna-xs-2.1": {
35328
+ // supportsTools: not probed — fails closed
35329
+ contextWindow: 131072,
35330
+ maxOutputTokens: 16384,
35331
+ supportsTools: false,
35332
+ supportsVision: false
35333
+ },
34714
35334
  "qwen/qwen3.7-flash": {
34715
35335
  contextWindow: 1e6,
34716
35336
  maxOutputTokens: 65536,
@@ -36626,8 +37246,8 @@ ${value.slice(-(scanLimit - prefixLength))}`;
36626
37246
  // $0.20/$1.25, 1M ctx
36627
37247
  "google/gemini-2.5-flash-lite",
36628
37248
  // $0.10/$0.40
36629
- "nvidia/step-3.7-flash"
36630
- // FREE backstop — NVIDIA free tier (probed 2026-08-21)
37249
+ "nvidia/nemotron-3.5-lightning"
37250
+ // FREE backstop — NVIDIA free tier (probed 2026-08-30)
36631
37251
  ]
36632
37252
  },
36633
37253
  MEDIUM: {
@@ -36716,15 +37336,19 @@ ${value.slice(-(scanLimit - prefixLength))}`;
36716
37336
  // Eco tier configs - absolute cheapest (blockrun/eco)
36717
37337
  ecoTiers: {
36718
37338
  SIMPLE: {
36719
- primary: "nvidia/step-3.7-flash",
36720
- // FREE — NVIDIA free tier flagship
37339
+ primary: "nvidia/nemotron-3.5-lightning",
37340
+ // FREE — NVIDIA free tier flagship, 1M ctx
36721
37341
  fallback: [
36722
- "nvidia/nemotron-nano-9b-v2",
36723
- // FREE — compact + fast, high-volume light tasks
37342
+ "nvidia/nemotron-3-nano-30b",
37343
+ // FREE — fastest free model (~121 tok/s)
36724
37344
  // The free head keeps rotting with NVIDIA's hosting (deepseek-v4-flash
36725
37345
  // 410 2026-08-12, seed-oss-36b 410 2026-08-03, gpt-oss-120b/20b 400
36726
- // 2026-08-21). Each retirement retargets the two free rungs to the
36727
- // current free tier; the paid rungs below never move.
37346
+ // 2026-08-21, and on 2026-08-30 FOUR of the five visible free models at
37347
+ // once — step-3.7-flash, nemotron-nano-9b-v2 and nemotron-nano-12b-v2-vl
37348
+ // all 410, mistral-nemotron hung). Each retirement retargets the two
37349
+ // free rungs to the current free tier; the paid rungs below never move.
37350
+ // The head follows blockrun's own redirect of the model it replaces, so
37351
+ // the router and the gateway never name different models.
36728
37352
  "google/gemini-2.5-flash-lite",
36729
37353
  // $0.10/$0.40 — cheapest paid rung
36730
37354
  "zai/glm-5.3-flash",
@@ -36868,7 +37492,7 @@ ${value.slice(-(scanLimit - prefixLength))}`;
36868
37492
  // strongest open-weight reasoner
36869
37493
  "deepseek/deepseek-chat",
36870
37494
  // Cheap, reliable
36871
- "nvidia/step-3.7-flash"
37495
+ "nvidia/nemotron-3.5-lightning"
36872
37496
  // NVIDIA free ultimate backstop
36873
37497
  ]
36874
37498
  },
@@ -36972,7 +37596,7 @@ ${value.slice(-(scanLimit - prefixLength))}`;
36972
37596
  // retail high-risk 3/3
36973
37597
  "deepseek/deepseek-chat",
36974
37598
  // cheap, reliable
36975
- "nvidia/step-3.7-flash"
37599
+ "nvidia/nemotron-3.5-lightning"
36976
37600
  // NVIDIA free ultimate backstop
36977
37601
  ]
36978
37602
  },
@@ -37054,6 +37678,8 @@ var init_top_models = __esm({
37054
37678
  "xai/grok-4.5",
37055
37679
  "xai/grok-4.3",
37056
37680
  "xai/grok-build-0.1",
37681
+ "zai/glm-5.3",
37682
+ "zai/glm-5.3-flash",
37057
37683
  "zai/glm-5.2",
37058
37684
  "zai/glm-5.1",
37059
37685
  "zai/glm-5-turbo",
@@ -37065,11 +37691,13 @@ var init_top_models = __esm({
37065
37691
  "deepseek/deepseek-v4-pro",
37066
37692
  "deepseek/deepseek-chat",
37067
37693
  "deepseek/deepseek-reasoner",
37694
+ "free/nemotron-3.5-lightning",
37695
+ "free/nemotron-3-nano-30b",
37696
+ "free/laguna-xs-2.1",
37697
+ "free/north-mini-code",
37068
37698
  "free/nemotron-3-nano-omni-30b-a3b-reasoning",
37069
- "free/mistral-nemotron",
37070
- "free/step-3.7-flash",
37071
- "free/nemotron-nano-9b-v2",
37072
- "free/nemotron-nano-12b-v2-vl"
37699
+ "free/nemotron-3-ultra-550b",
37700
+ "free/llama-3.2-11b-vision"
37073
37701
  ];
37074
37702
  }
37075
37703
  });
@@ -37382,14 +38010,20 @@ var init_models = __esm({
37382
38010
  "xai/grok-3-fast": "xai/grok-4-fast-reasoning",
37383
38011
  // delisted (too expensive)
37384
38012
  // NVIDIA — backward compat aliases (nvidia/xxx → free/xxx)
37385
- // Default free model is step-3.7-flash — the same model @blockrun/router-core
37386
- // opens the eco SIMPLE tier on. It replaced gpt-oss-120b on 2026-08-29:
37387
- // gpt-oss-120b went dead upstream on 2026-08-16 (NVIDIA still LISTS it, but a
37388
- // completion hangs until the client gives up — blockrun #391 retargeted its
37389
- // whole free cascade off it, and the gateway now answers 400 Unknown model
37390
- // for the `free/` id). Pins that NAME gpt-oss stay routable below — the
37391
- // gateway server-redirects them — but nothing generic may land on it.
37392
- nvidia: "free/step-3.7-flash",
38013
+ // Default free model is nemotron-3.5-lightning — the same model
38014
+ // @blockrun/router-core opens the eco SIMPLE tier on. It replaced
38015
+ // step-3.7-flash on 2026-08-30, when NVIDIA retired FOUR of the five visible
38016
+ // free models in a single sweep (blockrun #448): step-3.7-flash,
38017
+ // nemotron-nano-9b-v2 and nemotron-nano-12b-v2-vl all published 410 Gone, and
38018
+ // mistral-nemotron went the quiet way — still listed, >150s and zero bytes.
38019
+ // We follow blockrun's own retarget of step-3.7-flash rather than picking a
38020
+ // different survivor, so the proxy and the gateway name the same model.
38021
+ //
38022
+ // gpt-oss-120b/20b RECOVERED on the same probe run and are reachable again,
38023
+ // but they stay withheld from blockrun's public catalog over NVIDIA's
38024
+ // prompt-retention terms — so pins that NAME gpt-oss stay routable below and
38025
+ // nothing generic may land on it.
38026
+ nvidia: "free/nemotron-3.5-lightning",
37393
38027
  "gpt-120b": "free/gpt-oss-120b",
37394
38028
  // names the model itself — gateway redirects
37395
38029
  "gpt-20b": "free/gpt-oss-20b",
@@ -37402,10 +38036,10 @@ var init_models = __esm({
37402
38036
  // straight to gpt-oss-120b. Ids naming v4-flash itself keep the real id (the
37403
38037
  // gateway redirects them); the v3.2/v4-pro ids follow blockrun's retarget
37404
38038
  // rather than chaining through a second dead model.
37405
- "nvidia/deepseek-v3.2": "free/gpt-oss-120b",
37406
- "free/deepseek-v3.2": "free/gpt-oss-120b",
37407
- "nvidia/deepseek-v4-pro": "free/gpt-oss-120b",
37408
- "free/deepseek-v4-pro": "free/gpt-oss-120b",
38039
+ "nvidia/deepseek-v3.2": "free/nemotron-3.5-lightning",
38040
+ "free/deepseek-v3.2": "free/nemotron-3.5-lightning",
38041
+ "nvidia/deepseek-v4-pro": "free/nemotron-3.5-lightning",
38042
+ "free/deepseek-v4-pro": "free/nemotron-3.5-lightning",
37409
38043
  "nvidia/deepseek-v4-flash": "free/deepseek-v4-flash",
37410
38044
  "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning": "free/nemotron-3-nano-omni-30b-a3b-reasoning",
37411
38045
  // qwen3-coder-480b retired (NVIDIA EOL 2026-06-14). Its server-side redirect used
@@ -37432,16 +38066,16 @@ var init_models = __esm({
37432
38066
  // Retired free IDs → successors (mirror blockrun's 2026-07-17 redirect map:
37433
38067
  // ultra-253b redirects to gpt-oss-120b; the two supers have NO server redirect —
37434
38068
  // they stay hidden-but-routable, so their pins pass through to the real ids).
37435
- "nvidia/nemotron-ultra-253b": "free/gpt-oss-120b",
38069
+ "nvidia/nemotron-ultra-253b": "free/nemotron-3.5-lightning",
37436
38070
  // mistral-large-3-675b un-retired 2026-06-14, then EOL'd for good 2026-07-28:
37437
38071
  // blockrun's re-probe got HTTP 410 Gone from NVIDIA on both passes (baa967b).
37438
38072
  // Pin stays routable — the gateway redirects it to gpt-oss-120b.
37439
38073
  "nvidia/mistral-large-3-675b": "free/mistral-large-3-675b",
37440
38074
  "nvidia/qwen3.5-122b-a10b": "free/qwen3.5-122b-a10b",
37441
38075
  // devstral-2-123b died upstream; blockrun redirects it to gpt-oss-120b (2026-07-17 map)
37442
- "nvidia/devstral-2-123b": "free/gpt-oss-120b",
37443
- "free/nemotron-ultra-253b": "free/gpt-oss-120b",
37444
- "free/devstral-2-123b": "free/gpt-oss-120b",
38076
+ "nvidia/devstral-2-123b": "free/nemotron-3.5-lightning",
38077
+ "free/nemotron-ultra-253b": "free/nemotron-3.5-lightning",
38078
+ "free/devstral-2-123b": "free/nemotron-3.5-lightning",
37445
38079
  // Explicit-ish pins — dead upstream since 2026-07-28, gateway redirects to gpt-oss-120b
37446
38080
  "mistral-large": "free/mistral-large-3-675b",
37447
38081
  "mistral-large-3-675b": "free/mistral-large-3-675b",
@@ -37452,74 +38086,124 @@ var init_models = __esm({
37452
38086
  // gateway's retarget (blockrun's /free-deepseek page points at gpt-oss-120b
37453
38087
  // too). Shorthands naming v4-flash itself stay on the real id — the gateway
37454
38088
  // redirects them, same treatment as seed-oss/mistral-large pins.
37455
- "deepseek-free": "free/gpt-oss-120b",
37456
- "deepseek-v4-pro": "free/gpt-oss-120b",
38089
+ "deepseek-free": "free/nemotron-3.5-lightning",
38090
+ "deepseek-v4-pro": "free/nemotron-3.5-lightning",
37457
38091
  // free shorthand; pro dead upstream (410)
37458
38092
  "deepseek-v4-flash": "free/deepseek-v4-flash",
37459
- "v4-pro": "free/gpt-oss-120b",
38093
+ "v4-pro": "free/nemotron-3.5-lightning",
37460
38094
  "v4-flash": "free/deepseek-v4-flash",
37461
- "mistral-free": "free/mistral-nemotron",
37462
- // mistral-large-3-675b EOL'd 2026-07-28 → last live free Mistral (blockrun demoted its /free-mistral primary the same way)
37463
- "glm-free": "free/gpt-oss-120b",
38095
+ // mistral-nemotron died 2026-08-30 and it was the LAST free Mistral anywhere —
38096
+ // blockrun's own /free-mistral page now says so plainly instead of naming one.
38097
+ // Point the generic shorthand at the free default rather than advertise a
38098
+ // Mistral we cannot serve.
38099
+ "mistral-free": "free/nemotron-3.5-lightning",
38100
+ "glm-free": "free/nemotron-3.5-lightning",
37464
38101
  // seed-oss-36b (the prior target) EOL'd 2026-08-03
37465
- "llama-free": "free/gpt-oss-120b",
37466
- // no live free Llama left (maverick died 2026-07)
38102
+ // A free Llama exists again: nemotron-super-49b (Llama-3.3-based) hit 410 on
38103
+ // 2026-08-30, and a 12-model sweep of what NVIDIA still serves found Llama 3.2
38104
+ // 11B Vision as the only one that finishes a real completion.
38105
+ "llama-free": "free/llama-3.2-11b-vision",
37467
38106
  // qwen3-coder-480b retired 2026-06-14 → seed-oss-36b, which then EOL'd 2026-08-03.
37468
38107
  // Follow the gateway's own retarget rather than chaining to a second dead model.
37469
- "qwen-coder": "free/step-3.7-flash",
38108
+ "qwen-coder": "free/nemotron-3.5-lightning",
37470
38109
  // no live free Qwen; follows the free default
37471
- "qwen-coder-free": "free/step-3.7-flash",
37472
- "qwen-thinking": "free/step-3.7-flash",
38110
+ "qwen-coder-free": "free/nemotron-3.5-lightning",
38111
+ "qwen-thinking": "free/nemotron-3.5-lightning",
37473
38112
  // qwen3-next died 2026-07-17; no live free Qwen left
37474
38113
  "qwen3-next": "free/qwen3-next-80b-a3b-instruct",
37475
38114
  // explicit-ish pin — gateway redirects
37476
38115
  "qwen3-next-80b": "free/qwen3-next-80b-a3b-instruct",
37477
- "mistral-small": "free/mistral-nemotron",
37478
- // closest live Mistral-family free model
38116
+ "mistral-small": "free/nemotron-3.5-lightning",
38117
+ // no free Mistral left upstream (2026-08-30)
37479
38118
  // New live free models (2026-06-14 BlockRun free-tier refresh)
37480
38119
  // seed-oss pins name the model itself — kept routable, the gateway redirects them.
37481
38120
  "seed-oss": "free/seed-oss-36b",
37482
38121
  "seed-oss-36b": "free/seed-oss-36b",
37483
- "coder-free": "free/step-3.7-flash",
37484
- // generic "a free coder" → follows the free default
38122
+ // A free coder exists again — two of them, both sub-second. north-mini-code is
38123
+ // the faster (607ms median) and 256K ctx against laguna's 131K.
38124
+ "coder-free": "free/north-mini-code",
37485
38125
  "mistral-nemotron": "free/mistral-nemotron",
37486
38126
  "step-flash": "free/step-3.7-flash",
37487
38127
  "step-3.7-flash": "free/step-3.7-flash",
37488
- "nemotron-nano-9b": "free/nemotron-nano-9b-v2",
37489
- "nemotron-nano": "free/nemotron-nano-9b-v2",
37490
- "nemotron-nano-vl": "free/nemotron-nano-12b-v2-vl",
37491
- "nano-vl": "free/nemotron-nano-12b-v2-vl",
38128
+ // nemotron-nano-9b-v2 hit 410 on 2026-08-30; the generic shorthands follow
38129
+ // blockrun's redirect to its replacement, while the 9b-naming pins above stay
38130
+ // on the real id (the gateway resolves them).
38131
+ "nemotron-nano-9b": "free/nemotron-3-nano-30b",
38132
+ "nemotron-nano": "free/nemotron-3-nano-30b",
38133
+ // nemotron-nano-12b-v2-vl hit 410 the same day; vision in, vision out — the
38134
+ // target is blockrun's own, and nano-omni is the only vision-capable free
38135
+ // model left.
38136
+ "nemotron-nano-vl": "free/nemotron-3-nano-omni-30b-a3b-reasoning",
38137
+ "nano-vl": "free/nemotron-3-nano-omni-30b-a3b-reasoning",
37492
38138
  // Vision-capable free models
37493
38139
  "nemotron-omni": "free/nemotron-3-nano-omni-30b-a3b-reasoning",
37494
38140
  "nano-omni": "free/nemotron-3-nano-omni-30b-a3b-reasoning",
37495
38141
  "vision-free": "free/nemotron-3-nano-omni-30b-a3b-reasoning",
37496
38142
  // Retired shorthand aliases redirect to live successors. The catch-all target
37497
38143
  // moved llama-4-maverick → gpt-oss-120b (2026-07-17) → step-3.7-flash
37498
- // (2026-08-29, gpt-oss-120b hung upstream; see the NVIDIA block above).
37499
- nemotron: "free/nemotron-3-nano-omni-30b-a3b-reasoning",
38144
+ // (2026-08-29) → nemotron-3.5-lightning (2026-08-30, blockrun #448).
38145
+ nemotron: "free/nemotron-3.5-lightning",
37500
38146
  // strongest live Nemotron
37501
- "nemotron-ultra": "free/step-3.7-flash",
37502
- "nemotron-253b": "free/step-3.7-flash",
37503
- "nemotron-super": "free/step-3.7-flash",
37504
- "nemotron-49b": "free/step-3.7-flash",
37505
- "nemotron-120b": "free/step-3.7-flash",
37506
- devstral: "free/step-3.7-flash",
38147
+ "nemotron-ultra": "free/nemotron-3.5-lightning",
38148
+ "nemotron-253b": "free/nemotron-3.5-lightning",
38149
+ "nemotron-super": "free/nemotron-3.5-lightning",
38150
+ "nemotron-49b": "free/nemotron-3.5-lightning",
38151
+ "nemotron-120b": "free/nemotron-3.5-lightning",
38152
+ devstral: "free/nemotron-3.5-lightning",
37507
38153
  // seed-oss-36b EOL'd 2026-08-03
37508
- "devstral-2": "free/step-3.7-flash",
38154
+ "devstral-2": "free/nemotron-3.5-lightning",
37509
38155
  maverick: "free/llama-4-maverick",
37510
38156
  // explicit-ish pin — gateway redirects
38157
+ // ── The 2026-08-30 free lineup (blockrun #448) ────────────────────────────
38158
+ // nvidia/* bridges for the four NVIDIA-hosted additions.
38159
+ "nvidia/nemotron-3.5-lightning": "free/nemotron-3.5-lightning",
38160
+ "nvidia/nemotron-3-nano-30b": "free/nemotron-3-nano-30b",
38161
+ "nvidia/nemotron-3-ultra-550b": "free/nemotron-3-ultra-550b",
38162
+ "nvidia/llama-3.2-11b-vision": "free/llama-3.2-11b-vision",
38163
+ // The two non-NVIDIA free models keep the `free/` picker convention; their
38164
+ // real upstream ids are accepted as pins and rewritten in toUpstreamModelId
38165
+ // (see FREE_UPSTREAM_OVERRIDES in proxy.ts).
38166
+ "cohere/north-mini-code": "free/north-mini-code",
38167
+ "poolside/laguna-xs-2.1": "free/laguna-xs-2.1",
38168
+ // Shorthands.
38169
+ lightning: "free/nemotron-3.5-lightning",
38170
+ "nemotron-lightning": "free/nemotron-3.5-lightning",
38171
+ "nemotron-3.5-lightning": "free/nemotron-3.5-lightning",
38172
+ "nano-30b": "free/nemotron-3-nano-30b",
38173
+ "nemotron-nano-30b": "free/nemotron-3-nano-30b",
38174
+ "ultra-550b": "free/nemotron-3-ultra-550b",
38175
+ "nemotron-ultra-550b": "free/nemotron-3-ultra-550b",
38176
+ "llama-vision": "free/llama-3.2-11b-vision",
38177
+ "llama-3.2-vision": "free/llama-3.2-11b-vision",
38178
+ "north-mini": "free/north-mini-code",
38179
+ "north-mini-code": "free/north-mini-code",
38180
+ laguna: "free/laguna-xs-2.1",
38181
+ "laguna-xs": "free/laguna-xs-2.1",
37511
38182
  // `free` = the free-tier default. Must equal router-core's ecoTiers.SIMPLE
37512
38183
  // primary and the head of proxy.ts FREE_MODELS so `/model free`, the eco
37513
38184
  // profile and the budget-cap free fallback all agree on one live model.
37514
- free: "free/step-3.7-flash",
38185
+ free: "free/nemotron-3.5-lightning",
37515
38186
  // MiniMax (minimax → current flagship: M3)
37516
38187
  minimax: "minimax/minimax-m3",
37517
38188
  "minimax-m3": "minimax/minimax-m3",
37518
38189
  "minimax-m2.7": "minimax/minimax-m2.7",
37519
38190
  "minimax-m2.5": "minimax/minimax-m2.5",
37520
38191
  // Z.AI GLM-5
37521
- glm: "zai/glm-5.2",
37522
- // bare alias tracks newest flagship (launched 2026-06-16)
38192
+ // Bare `glm` PROMOTED 5.2 → 5.3 (2026-08-30). Same rule as `opus` 4.8 → 5: the
38193
+ // cost tradeoff is zero — identical $1.40/$4.40 AND an identical 1M ctx /
38194
+ // 131072 maxOutput envelope, so nothing can bill or truncate differently for a
38195
+ // caller who never typed a version. blockrun's own copy already names glm-5.3
38196
+ // the Z.AI flagship, so leaving `glm` on 5.2 would make the proxy disagree
38197
+ // with the gateway it fronts. `glm-5.2` and every other version pin still
38198
+ // resolve to their own model.
38199
+ glm: "zai/glm-5.3",
38200
+ "glm-5.3": "zai/glm-5.3",
38201
+ "glm-5-3": "zai/glm-5.3",
38202
+ // GLM-5.3 Flash — Z.AI's first natively multimodal GLM-5. Also router-core's
38203
+ // eco MEDIUM and COMPLEX primary.
38204
+ "glm-5.3-flash": "zai/glm-5.3-flash",
38205
+ "glm-5-3-flash": "zai/glm-5.3-flash",
38206
+ "glm-flash": "zai/glm-5.3-flash",
37523
38207
  "glm-5.2": "zai/glm-5.2",
37524
38208
  "glm-5": "zai/glm-5",
37525
38209
  "glm-5.1": "zai/glm-5.1",
@@ -37581,10 +38265,10 @@ var init_models = __esm({
37581
38265
  },
37582
38266
  {
37583
38267
  id: "free",
37584
- name: "Free \u2192 Step 3.7 Flash",
38268
+ name: "Free \u2192 Nemotron 3.5 Lightning",
37585
38269
  inputPrice: 0,
37586
38270
  outputPrice: 0,
37587
- contextWindow: 131072,
38271
+ contextWindow: 1e6,
37588
38272
  maxOutput: 16384,
37589
38273
  reasoning: true
37590
38274
  },
@@ -37625,7 +38309,7 @@ var init_models = __esm({
37625
38309
  inputPrice: 0.25,
37626
38310
  outputPrice: 2,
37627
38311
  contextWindow: 2e5,
37628
- maxOutput: 65536,
38312
+ maxOutput: 128e3,
37629
38313
  toolCalling: true
37630
38314
  },
37631
38315
  {
@@ -37635,7 +38319,7 @@ var init_models = __esm({
37635
38319
  inputPrice: 0.05,
37636
38320
  outputPrice: 0.4,
37637
38321
  contextWindow: 128e3,
37638
- maxOutput: 32768,
38322
+ maxOutput: 128e3,
37639
38323
  toolCalling: true,
37640
38324
  deprecated: true,
37641
38325
  fallbackModel: "openai/gpt-5.4-nano"
@@ -37796,7 +38480,7 @@ var init_models = __esm({
37796
38480
  version: "5.4",
37797
38481
  inputPrice: 2.5,
37798
38482
  outputPrice: 15,
37799
- contextWindow: 4e5,
38483
+ contextWindow: 105e4,
37800
38484
  maxOutput: 128e3,
37801
38485
  reasoning: true,
37802
38486
  vision: true,
@@ -37821,7 +38505,7 @@ var init_models = __esm({
37821
38505
  version: "5.4",
37822
38506
  inputPrice: 30,
37823
38507
  outputPrice: 180,
37824
- contextWindow: 4e5,
38508
+ contextWindow: 105e4,
37825
38509
  maxOutput: 128e3,
37826
38510
  reasoning: true,
37827
38511
  vision: true,
@@ -37834,7 +38518,7 @@ var init_models = __esm({
37834
38518
  inputPrice: 0.2,
37835
38519
  outputPrice: 1.25,
37836
38520
  contextWindow: 105e4,
37837
- maxOutput: 32768,
38521
+ maxOutput: 128e3,
37838
38522
  toolCalling: true
37839
38523
  },
37840
38524
  // OpenAI GPT-5.3 Family
@@ -37845,7 +38529,7 @@ var init_models = __esm({
37845
38529
  inputPrice: 1.75,
37846
38530
  outputPrice: 14,
37847
38531
  contextWindow: 128e3,
37848
- maxOutput: 16e3,
38532
+ maxOutput: 128e3,
37849
38533
  reasoning: true,
37850
38534
  vision: true,
37851
38535
  agentic: true,
@@ -37871,7 +38555,7 @@ var init_models = __esm({
37871
38555
  inputPrice: 2,
37872
38556
  outputPrice: 8,
37873
38557
  contextWindow: 128e3,
37874
- maxOutput: 16384,
38558
+ maxOutput: 32768,
37875
38559
  vision: true,
37876
38560
  toolCalling: true
37877
38561
  },
@@ -37882,7 +38566,7 @@ var init_models = __esm({
37882
38566
  inputPrice: 0.4,
37883
38567
  outputPrice: 1.6,
37884
38568
  contextWindow: 128e3,
37885
- maxOutput: 16384,
38569
+ maxOutput: 32768,
37886
38570
  toolCalling: true
37887
38571
  },
37888
38572
  {
@@ -37892,7 +38576,7 @@ var init_models = __esm({
37892
38576
  inputPrice: 0.1,
37893
38577
  outputPrice: 0.4,
37894
38578
  contextWindow: 128e3,
37895
- maxOutput: 16384,
38579
+ maxOutput: 32768,
37896
38580
  toolCalling: true
37897
38581
  },
37898
38582
  {
@@ -37958,7 +38642,7 @@ var init_models = __esm({
37958
38642
  inputPrice: 1.1,
37959
38643
  outputPrice: 4.4,
37960
38644
  contextWindow: 128e3,
37961
- maxOutput: 65536,
38645
+ maxOutput: 1e5,
37962
38646
  reasoning: true,
37963
38647
  toolCalling: true
37964
38648
  },
@@ -37969,7 +38653,7 @@ var init_models = __esm({
37969
38653
  inputPrice: 1.1,
37970
38654
  outputPrice: 4.4,
37971
38655
  contextWindow: 128e3,
37972
- maxOutput: 65536,
38656
+ maxOutput: 1e5,
37973
38657
  reasoning: true,
37974
38658
  toolCalling: true
37975
38659
  },
@@ -37982,7 +38666,7 @@ var init_models = __esm({
37982
38666
  inputPrice: 1,
37983
38667
  outputPrice: 5,
37984
38668
  contextWindow: 2e5,
37985
- maxOutput: 8192,
38669
+ maxOutput: 64e3,
37986
38670
  vision: true,
37987
38671
  agentic: true,
37988
38672
  toolCalling: true
@@ -38006,8 +38690,8 @@ var init_models = __esm({
38006
38690
  version: "4.6",
38007
38691
  inputPrice: 3,
38008
38692
  outputPrice: 15,
38009
- contextWindow: 2e5,
38010
- maxOutput: 64e3,
38693
+ contextWindow: 1e6,
38694
+ maxOutput: 128e3,
38011
38695
  reasoning: true,
38012
38696
  vision: true,
38013
38697
  agentic: true,
@@ -38037,7 +38721,7 @@ var init_models = __esm({
38037
38721
  inputPrice: 5,
38038
38722
  outputPrice: 25,
38039
38723
  contextWindow: 2e5,
38040
- maxOutput: 32e3,
38724
+ maxOutput: 64e3,
38041
38725
  reasoning: true,
38042
38726
  vision: true,
38043
38727
  agentic: true,
@@ -38121,7 +38805,7 @@ var init_models = __esm({
38121
38805
  version: "3.1",
38122
38806
  inputPrice: 2,
38123
38807
  outputPrice: 12,
38124
- contextWindow: 105e4,
38808
+ contextWindow: 1048576,
38125
38809
  maxOutput: 65536,
38126
38810
  reasoning: true,
38127
38811
  vision: true,
@@ -38133,7 +38817,7 @@ var init_models = __esm({
38133
38817
  version: "3.0",
38134
38818
  inputPrice: 2,
38135
38819
  outputPrice: 12,
38136
- contextWindow: 105e4,
38820
+ contextWindow: 1048576,
38137
38821
  maxOutput: 65536,
38138
38822
  reasoning: true,
38139
38823
  vision: true,
@@ -38185,7 +38869,7 @@ var init_models = __esm({
38185
38869
  version: "3.0",
38186
38870
  inputPrice: 0.5,
38187
38871
  outputPrice: 3,
38188
- contextWindow: 1e6,
38872
+ contextWindow: 1048576,
38189
38873
  maxOutput: 65536,
38190
38874
  reasoning: true,
38191
38875
  vision: true
@@ -38196,7 +38880,7 @@ var init_models = __esm({
38196
38880
  version: "2.5",
38197
38881
  inputPrice: 1.25,
38198
38882
  outputPrice: 10,
38199
- contextWindow: 105e4,
38883
+ contextWindow: 1048576,
38200
38884
  maxOutput: 65536,
38201
38885
  reasoning: true,
38202
38886
  vision: true,
@@ -38208,7 +38892,7 @@ var init_models = __esm({
38208
38892
  version: "2.5",
38209
38893
  inputPrice: 0.3,
38210
38894
  outputPrice: 2.5,
38211
- contextWindow: 1e6,
38895
+ contextWindow: 1048576,
38212
38896
  maxOutput: 65536,
38213
38897
  vision: true,
38214
38898
  toolCalling: true
@@ -38219,7 +38903,7 @@ var init_models = __esm({
38219
38903
  version: "2.5",
38220
38904
  inputPrice: 0.1,
38221
38905
  outputPrice: 0.4,
38222
- contextWindow: 1e6,
38906
+ contextWindow: 1048576,
38223
38907
  maxOutput: 65536,
38224
38908
  toolCalling: true
38225
38909
  },
@@ -38229,8 +38913,8 @@ var init_models = __esm({
38229
38913
  version: "3.1",
38230
38914
  inputPrice: 0.25,
38231
38915
  outputPrice: 1.5,
38232
- contextWindow: 1e6,
38233
- maxOutput: 8192,
38916
+ contextWindow: 1048576,
38917
+ maxOutput: 65536,
38234
38918
  toolCalling: true
38235
38919
  },
38236
38920
  // DeepSeek — V4 family (2026-04-24). The legacy deepseek-chat/reasoner
@@ -38243,8 +38927,8 @@ var init_models = __esm({
38243
38927
  version: "4-flash",
38244
38928
  inputPrice: 0.14,
38245
38929
  outputPrice: 0.28,
38246
- contextWindow: 1e6,
38247
- maxOutput: 8192,
38930
+ contextWindow: 1048576,
38931
+ maxOutput: 65536,
38248
38932
  toolCalling: true
38249
38933
  },
38250
38934
  {
@@ -38253,8 +38937,8 @@ var init_models = __esm({
38253
38937
  version: "4-flash",
38254
38938
  inputPrice: 0.14,
38255
38939
  outputPrice: 0.28,
38256
- contextWindow: 1e6,
38257
- maxOutput: 8192,
38940
+ contextWindow: 1048576,
38941
+ maxOutput: 65536,
38258
38942
  reasoning: true,
38259
38943
  toolCalling: true
38260
38944
  },
@@ -38380,7 +39064,7 @@ var init_models = __esm({
38380
39064
  inputPrice: 0.6,
38381
39065
  outputPrice: 3,
38382
39066
  contextWindow: 262144,
38383
- maxOutput: 16384,
39067
+ maxOutput: 65536,
38384
39068
  reasoning: true,
38385
39069
  vision: true,
38386
39070
  agentic: true,
@@ -38393,7 +39077,7 @@ var init_models = __esm({
38393
39077
  inputPrice: 0.6,
38394
39078
  outputPrice: 3,
38395
39079
  contextWindow: 262144,
38396
- maxOutput: 8192,
39080
+ maxOutput: 16384,
38397
39081
  reasoning: true,
38398
39082
  vision: true,
38399
39083
  agentic: true,
@@ -38431,7 +39115,7 @@ var init_models = __esm({
38431
39115
  version: "4",
38432
39116
  inputPrice: 0.2,
38433
39117
  outputPrice: 0.5,
38434
- contextWindow: 131072,
39118
+ contextWindow: 2e6,
38435
39119
  maxOutput: 16384,
38436
39120
  reasoning: true,
38437
39121
  toolCalling: true
@@ -38442,7 +39126,7 @@ var init_models = __esm({
38442
39126
  version: "4",
38443
39127
  inputPrice: 0.2,
38444
39128
  outputPrice: 0.5,
38445
- contextWindow: 131072,
39129
+ contextWindow: 2e6,
38446
39130
  maxOutput: 16384,
38447
39131
  toolCalling: true
38448
39132
  },
@@ -38452,7 +39136,7 @@ var init_models = __esm({
38452
39136
  version: "4.1",
38453
39137
  inputPrice: 0.2,
38454
39138
  outputPrice: 0.5,
38455
- contextWindow: 131072,
39139
+ contextWindow: 2e6,
38456
39140
  maxOutput: 16384,
38457
39141
  reasoning: true,
38458
39142
  toolCalling: true
@@ -38463,7 +39147,7 @@ var init_models = __esm({
38463
39147
  version: "4.1",
38464
39148
  inputPrice: 0.2,
38465
39149
  outputPrice: 0.5,
38466
- contextWindow: 131072,
39150
+ contextWindow: 2e6,
38467
39151
  maxOutput: 16384,
38468
39152
  toolCalling: true
38469
39153
  },
@@ -38475,7 +39159,7 @@ var init_models = __esm({
38475
39159
  version: "4-0709",
38476
39160
  inputPrice: 3,
38477
39161
  outputPrice: 15,
38478
- contextWindow: 131072,
39162
+ contextWindow: 256e3,
38479
39163
  maxOutput: 16384,
38480
39164
  reasoning: true,
38481
39165
  toolCalling: true
@@ -38486,7 +39170,7 @@ var init_models = __esm({
38486
39170
  version: "2",
38487
39171
  inputPrice: 2,
38488
39172
  outputPrice: 10,
38489
- contextWindow: 131072,
39173
+ contextWindow: 32768,
38490
39174
  maxOutput: 16384,
38491
39175
  vision: true,
38492
39176
  toolCalling: true
@@ -38791,7 +39475,13 @@ var init_models = __esm({
38791
39475
  maxOutput: 16384
38792
39476
  },
38793
39477
  {
38794
- // Mistral × NVIDIA hybrid: strong instruction following, 131K context.
39478
+ // Mistral × NVIDIA hybrid, 131K context. DEAD 2026-08-30 — the QUIET kind:
39479
+ // NVIDIA still LISTS it but a completion returns zero bytes after >150s on
39480
+ // both of blockrun's probe passes (blockrun #448, the #391 shape). Upstream
39481
+ // hid it and redirects callers to a live workhorse. Entry kept so explicit
39482
+ // pins stay routable; off the picker and the FREE_MODELS cascade.
39483
+ // There is no free Mistral left on NVIDIA — do not point a generic
39484
+ // "a free mistral" shorthand at one.
38795
39485
  id: "free/mistral-nemotron",
38796
39486
  name: "[Free] Mistral Nemotron",
38797
39487
  version: "nemotron",
@@ -38801,7 +39491,13 @@ var init_models = __esm({
38801
39491
  maxOutput: 16384
38802
39492
  },
38803
39493
  {
38804
- // StepFun Step 3.7 Flash: reasoning-focused, 131K context.
39494
+ // StepFun Step 3.7 Flash: reasoning-focused, 131K context. EOL'd 2026-08-30
39495
+ // — NVIDIA published 410 Gone on both of blockrun's probe passes (#448), in
39496
+ // the same sweep that took nemotron-nano-9b-v2, nemotron-nano-12b-v2-vl and
39497
+ // (hidden) nemotron-super-49b. It had been ClawRouter's free default since
39498
+ // 2026-08-29; the gateway redirects it to nemotron-3.5-lightning, which is
39499
+ // exactly why nothing looked broken. Entry kept so explicit pins stay
39500
+ // routable; off the picker and the FREE_MODELS cascade.
38805
39501
  id: "free/step-3.7-flash",
38806
39502
  name: "[Free] StepFun Step 3.7 Flash",
38807
39503
  version: "3.7-flash",
@@ -38813,6 +39509,8 @@ var init_models = __esm({
38813
39509
  },
38814
39510
  {
38815
39511
  // NVIDIA Nemotron Nano 9B v2: fast lightweight generalist, 131K context.
39512
+ // EOL'd 2026-08-30 (410 Gone, same sweep). Gateway redirects it to
39513
+ // nemotron-3-nano-30b. Entry kept for pins; off the picker and the cascade.
38816
39514
  id: "free/nemotron-nano-9b-v2",
38817
39515
  name: "[Free] Nemotron Nano 9B v2",
38818
39516
  version: "nano-9b-v2",
@@ -38824,6 +39522,9 @@ var init_models = __esm({
38824
39522
  },
38825
39523
  {
38826
39524
  // NVIDIA Nemotron Nano 12B v2 VL: vision-language (text + image), 131K context.
39525
+ // EOL'd 2026-08-30 (410 Gone, same sweep). Gateway redirects it to
39526
+ // nemotron-3-nano-omni, the only vision-capable free model left. Entry kept
39527
+ // for pins; off the picker and the cascade.
38827
39528
  id: "free/nemotron-nano-12b-v2-vl",
38828
39529
  name: "[Free] Nemotron Nano 12B v2 VL",
38829
39530
  version: "nano-12b-v2-vl",
@@ -38834,9 +39535,155 @@ var init_models = __esm({
38834
39535
  reasoning: true,
38835
39536
  vision: true
38836
39537
  },
39538
+ // ── 2026-08-30 free-tier rebuild (blockrun #448) ─────────────────────────
39539
+ // NVIDIA retired FOUR of the five VISIBLE free models in one sweep. blockrun
39540
+ // replaced them and moved two survivors onto OpenRouter's $0 ":free" pool,
39541
+ // taking the visible free set from 5 to 7 and spanning three hosts for the
39542
+ // first time (NVIDIA, OpenRouter, and two non-NVIDIA makers).
39543
+ //
39544
+ // All seven live free models were tool-probed through the gateway on
39545
+ // 2026-08-30 and every one returned a structured `tool_calls` array with
39546
+ // valid JSON args and finish_reason: "tool_calls" (nemotron-3-nano-30b looked
39547
+ // like a textual leak at max_tokens:120 — that was truncation; at 300 it is
39548
+ // structured). The evidence is recorded here on purpose, but `toolCalling` is
39549
+ // deliberately NOT set: no free entry has ever carried it, filterByToolCalling
39550
+ // keeps the free tier out of tool-bearing requests, and the budget pre-check
39551
+ // at proxy.ts:5217 is written on the same assumption. Flipping the tier into
39552
+ // agentic eligibility is its own change with its own evidence bar.
39553
+ {
39554
+ // Nemotron 3.5 Lightning 30B-A3B — the new free default. blockrun's own
39555
+ // redirect target for the retired step-3.7-flash, so following it keeps the
39556
+ // proxy and the gateway naming the same model. Served from OpenRouter's $0
39557
+ // pool (4.9s median vs 16.3s direct) which also publishes a 1M window
39558
+ // against the 131K the NVIDIA NIM path gives; blockrun carries a hidden
39559
+ // "-nim" twin as its fallback. Probed 1.3s with tools.
39560
+ id: "free/nemotron-3.5-lightning",
39561
+ name: "[Free] Nemotron 3.5 Lightning",
39562
+ version: "3.5-lightning",
39563
+ inputPrice: 0,
39564
+ outputPrice: 0,
39565
+ contextWindow: 1e6,
39566
+ maxOutput: 16384,
39567
+ reasoning: true
39568
+ },
39569
+ {
39570
+ // Nemotron 3 Nano 30B-A3B — the fastest free model in the catalog
39571
+ // (~121 tok/s on a realistic workload, not a 16-token ping). Returns
39572
+ // reasoning_content. Also the tertiary rung of blockrun's own free cascade.
39573
+ id: "free/nemotron-3-nano-30b",
39574
+ name: "[Free] Nemotron 3 Nano 30B",
39575
+ version: "3-nano-30b",
39576
+ inputPrice: 0,
39577
+ outputPrice: 0,
39578
+ contextWindow: 131072,
39579
+ maxOutput: 16384,
39580
+ reasoning: true
39581
+ },
39582
+ {
39583
+ // Nemotron 3 Ultra 550B-A55B — the largest free model ever listed, 1M ctx,
39584
+ // reachable ONLY through OpenRouter's $0 pool (build.nvidia.com answers 503).
39585
+ // Deliberately LOW in the cascade: 16.8s on the tools probe, and blockrun
39586
+ // measured 3 of 15 calls coming back as an HTTP 200 carrying an upstream
39587
+ // 502/503 error object instead of choices.
39588
+ id: "free/nemotron-3-ultra-550b",
39589
+ name: "[Free] Nemotron 3 Ultra 550B",
39590
+ version: "3-ultra-550b",
39591
+ inputPrice: 0,
39592
+ outputPrice: 0,
39593
+ contextWindow: 1e6,
39594
+ maxOutput: 16384,
39595
+ reasoning: true
39596
+ },
39597
+ {
39598
+ // Meta Llama 3.2 11B Vision — restores a free Llama after nemotron-super-49b
39599
+ // (Llama-3.3-based) hit NVIDIA's 410 EOL. A 12-model sweep found exactly two
39600
+ // survivors and only the 11B finishes a real completion. Older than the rest
39601
+ // of the tier and deliberately so: it is a real Llama that actually answers.
39602
+ // Slowest in the tier (~18 tok/s) — last rung of the cascade.
39603
+ id: "free/llama-3.2-11b-vision",
39604
+ name: "[Free] Llama 3.2 11B Vision",
39605
+ version: "3.2-11b-vision",
39606
+ inputPrice: 0,
39607
+ outputPrice: 0,
39608
+ contextWindow: 128e3,
39609
+ maxOutput: 16384,
39610
+ vision: true
39611
+ },
39612
+ {
39613
+ // Cohere North Mini Code, on OpenRouter's $0 pool — 607ms median, the
39614
+ // fastest thing in the tier. Emits reasoning_content (content is clean once
39615
+ // the budget is large enough to finish, verified live).
39616
+ // NOTE: NOT an nvidia/* id upstream — see FREE_UPSTREAM_OVERRIDES in proxy.ts.
39617
+ id: "free/north-mini-code",
39618
+ name: "[Free] Cohere North Mini Code",
39619
+ version: "north-mini-code",
39620
+ inputPrice: 0,
39621
+ outputPrice: 0,
39622
+ contextWindow: 256e3,
39623
+ maxOutput: 16384,
39624
+ reasoning: true
39625
+ },
39626
+ {
39627
+ // Poolside Laguna XS 2.1 on OUR NVIDIA key (~161 tok/s). Deliberately not
39628
+ // the OpenRouter twin, which 429s on every attempt — so this rung and
39629
+ // north-mini-code sit on DIFFERENT capacity pools, which is why they are
39630
+ // adjacent in the cascade.
39631
+ // NOTE: NOT an nvidia/* id upstream — see FREE_UPSTREAM_OVERRIDES in proxy.ts.
39632
+ id: "free/laguna-xs-2.1",
39633
+ name: "[Free] Poolside Laguna XS 2.1",
39634
+ version: "xs-2.1",
39635
+ inputPrice: 0,
39636
+ outputPrice: 0,
39637
+ contextWindow: 131072,
39638
+ maxOutput: 16384
39639
+ },
38837
39640
  // Z.AI GLM-5 Models
38838
39641
  {
38839
- // Launched 2026-06-16 — Z.AI's newest flagship. 1M-token context,
39642
+ // Z.AI's flagship, live-probed by blockrun 2026-08-19 against api.z.ai
39643
+ // (real completion in 2s, content alongside reasoning tokens). $1.40/$4.40,
39644
+ // cached input $0.26, off the international USD price list.
39645
+ // maxOutput is 131072, NOT the 1M context: that is the hard ceiling the API
39646
+ // enforces (error 1210 above it), the same on every GLM-5 SKU.
39647
+ // Thinking is ALWAYS ON here and cannot be disabled.
39648
+ id: "zai/glm-5.3",
39649
+ name: "GLM-5.3",
39650
+ version: "5.3",
39651
+ inputPrice: 1.4,
39652
+ outputPrice: 4.4,
39653
+ contextWindow: 1e6,
39654
+ maxOutput: 131072,
39655
+ reasoning: true,
39656
+ toolCalling: true
39657
+ },
39658
+ {
39659
+ // Z.AI's first natively multimodal GLM-5 — 320B/18B MoE. blockrun probed
39660
+ // text, vision (base64 data URL → correct answer), tools
39661
+ // (finish_reason=tool_calls with well-formed arguments) and streaming live
39662
+ // on 2026-08-27 before listing it.
39663
+ //
39664
+ // MUST be in this catalog, not just router-core's: it is router-core's eco
39665
+ // MEDIUM and COMPLEX primary, and estimateAmount() returns undefined for an
39666
+ // id we do not carry — which makes the request skip the maxCostPerRun filter
39667
+ // AND never accumulate into session cost. An uncatalogued routing target is
39668
+ // a cost-cap hole, not a logging gap.
39669
+ //
39670
+ // Price is the LIST rate. Z.AI is running a 50% launch promo ($0.075/$0.25)
39671
+ // that ENDS 2026-09-09; listing the promo rate would put us under COGS the
39672
+ // morning it lapses. Thinking is always on, as on glm-5.3.
39673
+ id: "zai/glm-5.3-flash",
39674
+ name: "GLM-5.3 Flash",
39675
+ version: "5.3-flash",
39676
+ inputPrice: 0.15,
39677
+ outputPrice: 0.5,
39678
+ contextWindow: 1e6,
39679
+ maxOutput: 131072,
39680
+ reasoning: true,
39681
+ vision: true,
39682
+ toolCalling: true
39683
+ },
39684
+ {
39685
+ // Launched 2026-06-16. Was Z.AI's flagship until glm-5.3 (above) took the
39686
+ // slot on 2026-08-19. 1M-token context,
38840
39687
  // beats GPT-5.5 on long-horizon coding at a fraction of the cost.
38841
39688
  // Paid per-token at $1.40/$4.40 (same as glm-5.1, cached $0.26).
38842
39689
  id: "zai/glm-5.2",
@@ -38845,7 +39692,7 @@ var init_models = __esm({
38845
39692
  inputPrice: 1.4,
38846
39693
  outputPrice: 4.4,
38847
39694
  contextWindow: 1e6,
38848
- maxOutput: 262144,
39695
+ maxOutput: 131072,
38849
39696
  reasoning: true,
38850
39697
  toolCalling: true
38851
39698
  },
@@ -38908,8 +39755,8 @@ var init_models = __esm({
38908
39755
 
38909
39756
  // src/logger.ts
38910
39757
  import { appendFile, mkdir } from "fs/promises";
38911
- import { join as join2 } from "path";
38912
- import { homedir } from "os";
39758
+ import { join as join3 } from "path";
39759
+ import { homedir as homedir2 } from "os";
38913
39760
  async function ensureDir() {
38914
39761
  if (dirReady) return;
38915
39762
  await mkdir(LOG_DIR, { recursive: true });
@@ -38919,7 +39766,7 @@ async function logUsage(entry) {
38919
39766
  try {
38920
39767
  await ensureDir();
38921
39768
  const date = entry.timestamp.slice(0, 10);
38922
- const file = join2(LOG_DIR, `usage-${date}.jsonl`);
39769
+ const file = join3(LOG_DIR, `usage-${date}.jsonl`);
38923
39770
  await appendFile(file, JSON.stringify(entry) + "\n");
38924
39771
  } catch {
38925
39772
  }
@@ -38928,56 +39775,15 @@ var LOG_DIR, dirReady;
38928
39775
  var init_logger = __esm({
38929
39776
  "src/logger.ts"() {
38930
39777
  "use strict";
38931
- LOG_DIR = join2(homedir(), ".openclaw", "blockrun", "logs");
39778
+ LOG_DIR = join3(homedir2(), ".openclaw", "blockrun", "logs");
38932
39779
  dirReady = false;
38933
39780
  }
38934
39781
  });
38935
39782
 
38936
- // src/fs-read.ts
38937
- import { open } from "fs/promises";
38938
- import { openSync, readSync, closeSync, fstatSync } from "fs";
38939
- async function readTextFile(filePath) {
38940
- const fh = await open(filePath, "r");
38941
- try {
38942
- const size5 = (await fh.stat()).size;
38943
- const buf = Buffer.alloc(size5);
38944
- let offset = 0;
38945
- while (offset < size5) {
38946
- const { bytesRead } = await fh.read(buf, offset, size5 - offset, offset);
38947
- if (bytesRead === 0) break;
38948
- offset += bytesRead;
38949
- }
38950
- return buf.subarray(0, offset).toString("utf-8");
38951
- } finally {
38952
- await fh.close();
38953
- }
38954
- }
38955
- function readTextFileSync(filePath) {
38956
- const fd = openSync(filePath, "r");
38957
- try {
38958
- const size5 = fstatSync(fd).size;
38959
- const buf = Buffer.alloc(size5);
38960
- let offset = 0;
38961
- while (offset < size5) {
38962
- const bytesRead = readSync(fd, buf, offset, size5 - offset, offset);
38963
- if (bytesRead === 0) break;
38964
- offset += bytesRead;
38965
- }
38966
- return buf.subarray(0, offset).toString("utf-8");
38967
- } finally {
38968
- closeSync(fd);
38969
- }
38970
- }
38971
- var init_fs_read = __esm({
38972
- "src/fs-read.ts"() {
38973
- "use strict";
38974
- }
38975
- });
38976
-
38977
39783
  // src/version.ts
38978
39784
  import { createRequire } from "module";
38979
39785
  import { fileURLToPath } from "url";
38980
- import { dirname, join as join3 } from "path";
39786
+ import { dirname, join as join4 } from "path";
38981
39787
  function clientTag() {
38982
39788
  const raw = (process.env.CLAWROUTER_CLIENT ?? "").trim();
38983
39789
  if (!raw) return "";
@@ -38991,7 +39797,7 @@ var init_version4 = __esm({
38991
39797
  __filename2 = fileURLToPath(import.meta.url);
38992
39798
  __dirname = dirname(__filename2);
38993
39799
  require2 = createRequire(import.meta.url);
38994
- pkg = require2(join3(__dirname, "..", "package.json"));
39800
+ pkg = require2(join4(__dirname, "..", "package.json"));
38995
39801
  VERSION = pkg.version;
38996
39802
  USER_AGENT = `clawrouter/${VERSION}${clientTag()}`;
38997
39803
  }
@@ -38999,8 +39805,8 @@ var init_version4 = __esm({
38999
39805
 
39000
39806
  // src/stats.ts
39001
39807
  import { readdir, unlink } from "fs/promises";
39002
- import { join as join4 } from "path";
39003
- import { homedir as homedir2 } from "os";
39808
+ import { join as join5 } from "path";
39809
+ import { homedir as homedir3 } from "os";
39004
39810
  async function parseLogFile(filePath) {
39005
39811
  try {
39006
39812
  const content = await readTextFile(filePath);
@@ -39072,7 +39878,7 @@ async function getStats(days = 7) {
39072
39878
  let totalLatency = 0;
39073
39879
  for (const file of filesToRead) {
39074
39880
  const date = file.replace("usage-", "").replace(".jsonl", "");
39075
- const filePath = join4(LOG_DIR2, file);
39881
+ const filePath = join5(LOG_DIR2, file);
39076
39882
  const entries = await parseLogFile(filePath);
39077
39883
  if (entries.length === 0) continue;
39078
39884
  const dayStats = aggregateDay(date, entries);
@@ -39191,7 +39997,7 @@ async function formatRecentLogs(days = 1) {
39191
39997
  const filesToRead = logFiles.slice(0, days);
39192
39998
  const allEntries = [];
39193
39999
  for (const file of filesToRead) {
39194
- const entries = await parseLogFile(join4(LOG_DIR2, file));
40000
+ const entries = await parseLogFile(join5(LOG_DIR2, file));
39195
40001
  allEntries.push(...entries);
39196
40002
  }
39197
40003
  allEntries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
@@ -39236,7 +40042,7 @@ async function clearStats() {
39236
40042
  try {
39237
40043
  const files = await readdir(LOG_DIR2);
39238
40044
  const logFiles = files.filter((f) => f.startsWith("usage-") && f.endsWith(".jsonl"));
39239
- await Promise.all(logFiles.map((f) => unlink(join4(LOG_DIR2, f))));
40045
+ await Promise.all(logFiles.map((f) => unlink(join5(LOG_DIR2, f))));
39240
40046
  return { deletedFiles: logFiles.length };
39241
40047
  } catch {
39242
40048
  return { deletedFiles: 0 };
@@ -39248,7 +40054,7 @@ var init_stats = __esm({
39248
40054
  "use strict";
39249
40055
  init_fs_read();
39250
40056
  init_version4();
39251
- LOG_DIR2 = join4(homedir2(), ".openclaw", "blockrun", "logs");
40057
+ LOG_DIR2 = join5(homedir3(), ".openclaw", "blockrun", "logs");
39252
40058
  }
39253
40059
  });
39254
40060
 
@@ -43067,7 +43873,7 @@ function alphabet2(letters) {
43067
43873
  };
43068
43874
  }
43069
43875
  // @__NO_SIDE_EFFECTS__
43070
- function join5(separator = "") {
43876
+ function join6(separator = "") {
43071
43877
  astr2("join", separator);
43072
43878
  return {
43073
43879
  encode: (from15) => {
@@ -43177,7 +43983,7 @@ var genBase58, base58, createBase58check;
43177
43983
  var init_base3 = __esm({
43178
43984
  "node_modules/@scure/base/index.js"() {
43179
43985
  "use strict";
43180
- genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => /* @__PURE__ */ chain2(/* @__PURE__ */ radix3(58), /* @__PURE__ */ alphabet2(abc), /* @__PURE__ */ join5(""));
43986
+ genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => /* @__PURE__ */ chain2(/* @__PURE__ */ radix3(58), /* @__PURE__ */ alphabet2(abc), /* @__PURE__ */ join6(""));
43181
43987
  base58 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ genBase58("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"));
43182
43988
  createBase58check = (sha25610) => {
43183
43989
  afn2(sha25610);
@@ -43326,14 +44132,14 @@ var init_bip32 = __esm({
43326
44132
  }
43327
44133
  this.pubHash = hash160(this._publicKey);
43328
44134
  }
43329
- derive(path6) {
43330
- if (!/^[mM]'?/.test(path6)) {
44135
+ derive(path5) {
44136
+ if (!/^[mM]'?/.test(path5)) {
43331
44137
  throw new Error('Path must start with "m" or "M"');
43332
44138
  }
43333
- if (/^[mM]'?$/.test(path6)) {
44139
+ if (/^[mM]'?$/.test(path5)) {
43334
44140
  return this;
43335
44141
  }
43336
- const parts = path6.replace(/^[mM]'?\//, "").split("/");
44142
+ const parts = path5.replace(/^[mM]'?\//, "").split("/");
43337
44143
  let child = this;
43338
44144
  for (const c of parts) {
43339
44145
  const m = /^(\d+)('?)$/.exec(c);
@@ -51623,14 +52429,14 @@ function createSolanaJsonRpcIntegerOverflowError(methodName, keyPath, value) {
51623
52429
  } else {
51624
52430
  argumentLabel = `\`${keyPath[0].toString()}\``;
51625
52431
  }
51626
- const path6 = keyPath.length > 1 ? keyPath.slice(1).map((pathPart) => typeof pathPart === "number" ? `[${pathPart}]` : pathPart).join(".") : void 0;
52432
+ const path5 = keyPath.length > 1 ? keyPath.slice(1).map((pathPart) => typeof pathPart === "number" ? `[${pathPart}]` : pathPart).join(".") : void 0;
51627
52433
  const error = new SolanaError(SOLANA_ERROR__RPC__INTEGER_OVERFLOW, {
51628
52434
  argumentLabel,
51629
52435
  keyPath,
51630
52436
  methodName,
51631
- optionalPathLabel: path6 ? ` at path \`${path6}\`` : "",
52437
+ optionalPathLabel: path5 ? ` at path \`${path5}\`` : "",
51632
52438
  value,
51633
- ...path6 !== void 0 ? { path: path6 } : void 0
52439
+ ...path5 !== void 0 ? { path: path5 } : void 0
51634
52440
  });
51635
52441
  safeCaptureStackTrace(error, createSolanaJsonRpcIntegerOverflowError);
51636
52442
  return error;
@@ -56294,14 +57100,14 @@ function createSolanaJsonRpcIntegerOverflowError2(methodName, keyPath, value) {
56294
57100
  } else {
56295
57101
  argumentLabel = `\`${keyPath[0].toString()}\``;
56296
57102
  }
56297
- const path6 = keyPath.length > 1 ? keyPath.slice(1).map((pathPart) => typeof pathPart === "number" ? `[${pathPart}]` : pathPart).join(".") : void 0;
57103
+ const path5 = keyPath.length > 1 ? keyPath.slice(1).map((pathPart) => typeof pathPart === "number" ? `[${pathPart}]` : pathPart).join(".") : void 0;
56298
57104
  const error = new SolanaError(SOLANA_ERROR__RPC__INTEGER_OVERFLOW, {
56299
57105
  argumentLabel,
56300
57106
  keyPath,
56301
57107
  methodName,
56302
- optionalPathLabel: path6 ? ` at path \`${path6}\`` : "",
57108
+ optionalPathLabel: path5 ? ` at path \`${path5}\`` : "",
56303
57109
  value,
56304
- ...path6 !== void 0 ? { path: path6 } : void 0
57110
+ ...path5 !== void 0 ? { path: path5 } : void 0
56305
57111
  });
56306
57112
  safeCaptureStackTrace(error, createSolanaJsonRpcIntegerOverflowError2);
56307
57113
  return error;
@@ -58381,8 +59187,8 @@ __export(auth_exports, {
58381
59187
  setupSolana: () => setupSolana
58382
59188
  });
58383
59189
  import { writeFile, mkdir as mkdir2 } from "fs/promises";
58384
- import { join as join6 } from "path";
58385
- import { homedir as homedir3 } from "os";
59190
+ import { join as join7 } from "path";
59191
+ import { homedir as homedir4 } from "os";
58386
59192
  async function loadSavedWallet() {
58387
59193
  try {
58388
59194
  const key2 = (await readTextFile(WALLET_FILE)).trim();
@@ -58431,7 +59237,7 @@ async function loadMnemonic() {
58431
59237
  return void 0;
58432
59238
  }
58433
59239
  async function saveMnemonic(mnemonic) {
58434
- await mkdir2(WALLET_DIR, { recursive: true });
59240
+ await mkdir2(WALLET_DIR2, { recursive: true });
58435
59241
  await writeFile(MNEMONIC_FILE, mnemonic + "\n", { mode: 384 });
58436
59242
  }
58437
59243
  async function generateAndSaveWallet() {
@@ -58450,7 +59256,7 @@ Then run: npx @blockrun/clawrouter`
58450
59256
  }
58451
59257
  const mnemonic = generateWalletMnemonic();
58452
59258
  const derived = deriveAllKeys(mnemonic);
58453
- await mkdir2(WALLET_DIR, { recursive: true });
59259
+ await mkdir2(WALLET_DIR2, { recursive: true });
58454
59260
  await writeFile(WALLET_FILE, derived.evmPrivateKey + "\n", { mode: 384 });
58455
59261
  await writeFile(MNEMONIC_FILE, mnemonic + "\n", { mode: 384 });
58456
59262
  try {
@@ -58583,7 +59389,7 @@ async function recoverWalletFromMnemonic() {
58583
59389
  console.log(`[ClawRouter] wallet, recovery is safe to proceed.`);
58584
59390
  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`);
58585
59391
  console.log(`[ClawRouter]`);
58586
- await mkdir2(WALLET_DIR, { recursive: true });
59392
+ await mkdir2(WALLET_DIR2, { recursive: true });
58587
59393
  await writeFile(WALLET_FILE, derived.evmPrivateKey + "\n", { mode: 384 });
58588
59394
  console.log(`[ClawRouter] \u2713 wallet.key restored at ${WALLET_FILE}`);
58589
59395
  console.log(`[ClawRouter] Run: npx @blockrun/clawrouter`);
@@ -58609,7 +59415,7 @@ async function setupSolana() {
58609
59415
  return { mnemonic, solanaPrivateKeyBytes: solanaKeyBytes };
58610
59416
  }
58611
59417
  async function savePaymentChain(chain3) {
58612
- await mkdir2(WALLET_DIR, { recursive: true });
59418
+ await mkdir2(WALLET_DIR2, { recursive: true });
58613
59419
  await writeFile(CHAIN_FILE, chain3 + "\n", { mode: 384 });
58614
59420
  }
58615
59421
  async function loadPaymentChain() {
@@ -58626,17 +59432,17 @@ async function resolvePaymentChain() {
58626
59432
  if (process["env"].CLAWROUTER_PAYMENT_CHAIN === "base") return "base";
58627
59433
  return loadPaymentChain();
58628
59434
  }
58629
- var WALLET_DIR, WALLET_FILE, MNEMONIC_FILE, CHAIN_FILE;
59435
+ var WALLET_DIR2, WALLET_FILE, MNEMONIC_FILE, CHAIN_FILE;
58630
59436
  var init_auth = __esm({
58631
59437
  "src/auth.ts"() {
58632
59438
  "use strict";
58633
59439
  init_fs_read();
58634
59440
  init_accounts();
58635
59441
  init_wallet2();
58636
- WALLET_DIR = join6(homedir3(), ".openclaw", "blockrun");
58637
- WALLET_FILE = join6(WALLET_DIR, "wallet.key");
58638
- MNEMONIC_FILE = join6(WALLET_DIR, "mnemonic");
58639
- CHAIN_FILE = join6(WALLET_DIR, "payment-chain");
59442
+ WALLET_DIR2 = join7(homedir4(), ".openclaw", "blockrun");
59443
+ WALLET_FILE = join7(WALLET_DIR2, "wallet.key");
59444
+ MNEMONIC_FILE = join7(WALLET_DIR2, "mnemonic");
59445
+ CHAIN_FILE = join7(WALLET_DIR2, "payment-chain");
58640
59446
  }
58641
59447
  });
58642
59448
 
@@ -58795,7 +59601,7 @@ function generateCodebookHeader(usedCodes, pathMap = {}) {
58795
59601
  parts.push(`[Dict: ${codeEntries}]`);
58796
59602
  }
58797
59603
  if (Object.keys(pathMap).length > 0) {
58798
- const pathEntries = Object.entries(pathMap).map(([code, path6]) => `${code}=${path6}`).join(", ");
59604
+ const pathEntries = Object.entries(pathMap).map(([code, path5]) => `${code}=${path5}`).join(", ");
58799
59605
  parts.push(`[Paths: ${pathEntries}]`);
58800
59606
  }
58801
59607
  return parts.join("\n");
@@ -58943,8 +59749,8 @@ function extractPaths(messages) {
58943
59749
  }
58944
59750
  function findFrequentPrefixes(paths) {
58945
59751
  const prefixCounts = /* @__PURE__ */ new Map();
58946
- for (const path6 of paths) {
58947
- const parts = path6.split("/").filter(Boolean);
59752
+ for (const path5 of paths) {
59753
+ const parts = path5.split("/").filter(Boolean);
58948
59754
  for (let i = 2; i < parts.length; i++) {
58949
59755
  const prefix = "/" + parts.slice(0, i).join("/") + "/";
58950
59756
  prefixCounts.set(prefix, (prefixCounts.get(prefix) || 0) + 1);
@@ -59728,9 +60534,9 @@ var init_updater = __esm({
59728
60534
  });
59729
60535
 
59730
60536
  // src/exclude-models.ts
59731
- import { readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
59732
- import { join as join7, dirname as dirname2 } from "path";
59733
- import { homedir as homedir4 } from "os";
60537
+ import { readFileSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, statSync } from "fs";
60538
+ import { join as join8, dirname as dirname2 } from "path";
60539
+ import { homedir as homedir5 } from "os";
59734
60540
  function loadExcludeList(filePath = DEFAULT_FILE_PATH) {
59735
60541
  try {
59736
60542
  const mtimeMs = statSync(filePath).mtimeMs;
@@ -59751,8 +60557,8 @@ function loadExcludeList(filePath = DEFAULT_FILE_PATH) {
59751
60557
  function saveExcludeList(set, filePath) {
59752
60558
  const sorted = [...set].sort();
59753
60559
  const dir = dirname2(filePath);
59754
- mkdirSync(dir, { recursive: true });
59755
- writeFileSync(filePath, JSON.stringify(sorted, null, 2) + "\n", "utf-8");
60560
+ mkdirSync2(dir, { recursive: true });
60561
+ writeFileSync2(filePath, JSON.stringify(sorted, null, 2) + "\n", "utf-8");
59756
60562
  loadCache.delete(filePath);
59757
60563
  }
59758
60564
  function addExclusion(model, filePath = DEFAULT_FILE_PATH) {
@@ -59779,7 +60585,7 @@ var init_exclude_models = __esm({
59779
60585
  "src/exclude-models.ts"() {
59780
60586
  "use strict";
59781
60587
  init_models();
59782
- DEFAULT_FILE_PATH = join7(homedir4(), ".openclaw", "blockrun", "exclude-models.json");
60588
+ DEFAULT_FILE_PATH = join8(homedir5(), ".openclaw", "blockrun", "exclude-models.json");
59783
60589
  loadCache = /* @__PURE__ */ new Map();
59784
60590
  }
59785
60591
  });
@@ -61084,14 +61890,14 @@ var require_util = __commonJS({
61084
61890
  }
61085
61891
  const port = url2.port != null ? url2.port : url2.protocol === "https:" ? 443 : 80;
61086
61892
  let origin2 = url2.origin != null ? url2.origin : `${url2.protocol || ""}//${url2.hostname || ""}:${port}`;
61087
- let path6 = url2.path != null ? url2.path : `${url2.pathname || ""}${url2.search || ""}`;
61893
+ let path5 = url2.path != null ? url2.path : `${url2.pathname || ""}${url2.search || ""}`;
61088
61894
  if (origin2[origin2.length - 1] === "/") {
61089
61895
  origin2 = origin2.slice(0, origin2.length - 1);
61090
61896
  }
61091
- if (path6 && path6[0] !== "/") {
61092
- path6 = `/${path6}`;
61897
+ if (path5 && path5[0] !== "/") {
61898
+ path5 = `/${path5}`;
61093
61899
  }
61094
- return new URL(`${origin2}${path6}`);
61900
+ return new URL(`${origin2}${path5}`);
61095
61901
  }
61096
61902
  if (!isHttpOrHttpsPrefixed(url2.origin || url2.protocol)) {
61097
61903
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -61962,9 +62768,9 @@ var require_diagnostics = __commonJS({
61962
62768
  "undici:client:sendHeaders",
61963
62769
  (evt) => {
61964
62770
  const {
61965
- request: { method, path: path6, origin: origin2 }
62771
+ request: { method, path: path5, origin: origin2 }
61966
62772
  } = evt;
61967
- debugLog("sending request to %s %s%s", method, origin2, path6);
62773
+ debugLog("sending request to %s %s%s", method, origin2, path5);
61968
62774
  }
61969
62775
  );
61970
62776
  }
@@ -61982,14 +62788,14 @@ var require_diagnostics = __commonJS({
61982
62788
  "undici:request:headers",
61983
62789
  (evt) => {
61984
62790
  const {
61985
- request: { method, path: path6, origin: origin2 },
62791
+ request: { method, path: path5, origin: origin2 },
61986
62792
  response: { statusCode }
61987
62793
  } = evt;
61988
62794
  debugLog(
61989
62795
  "received response to %s %s%s - HTTP %d",
61990
62796
  method,
61991
62797
  origin2,
61992
- path6,
62798
+ path5,
61993
62799
  statusCode
61994
62800
  );
61995
62801
  }
@@ -61998,23 +62804,23 @@ var require_diagnostics = __commonJS({
61998
62804
  "undici:request:trailers",
61999
62805
  (evt) => {
62000
62806
  const {
62001
- request: { method, path: path6, origin: origin2 }
62807
+ request: { method, path: path5, origin: origin2 }
62002
62808
  } = evt;
62003
- debugLog("trailers received from %s %s%s", method, origin2, path6);
62809
+ debugLog("trailers received from %s %s%s", method, origin2, path5);
62004
62810
  }
62005
62811
  );
62006
62812
  diagnosticsChannel.subscribe(
62007
62813
  "undici:request:error",
62008
62814
  (evt) => {
62009
62815
  const {
62010
- request: { method, path: path6, origin: origin2 },
62816
+ request: { method, path: path5, origin: origin2 },
62011
62817
  error
62012
62818
  } = evt;
62013
62819
  debugLog(
62014
62820
  "request to %s %s%s errored - %s",
62015
62821
  method,
62016
62822
  origin2,
62017
- path6,
62823
+ path5,
62018
62824
  error.message
62019
62825
  );
62020
62826
  }
@@ -62169,7 +62975,7 @@ var require_request = __commonJS({
62169
62975
  };
62170
62976
  var Request2 = class {
62171
62977
  constructor(origin2, {
62172
- path: path6,
62978
+ path: path5,
62173
62979
  method,
62174
62980
  body,
62175
62981
  headers,
@@ -62186,11 +62992,11 @@ var require_request = __commonJS({
62186
62992
  maxRedirections,
62187
62993
  typeOfService
62188
62994
  }, handler) {
62189
- if (typeof path6 !== "string") {
62995
+ if (typeof path5 !== "string") {
62190
62996
  throw new InvalidArgumentError("path must be a string");
62191
- } else if (path6[0] !== "/" && !(path6.startsWith("http://") || path6.startsWith("https://")) && method !== "CONNECT") {
62997
+ } else if (path5[0] !== "/" && !(path5.startsWith("http://") || path5.startsWith("https://")) && method !== "CONNECT") {
62192
62998
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
62193
- } else if (invalidPathRegex.test(path6)) {
62999
+ } else if (invalidPathRegex.test(path5)) {
62194
63000
  throw new InvalidArgumentError("invalid request path");
62195
63001
  }
62196
63002
  if (typeof method !== "string") {
@@ -62265,7 +63071,7 @@ var require_request = __commonJS({
62265
63071
  this.completed = false;
62266
63072
  this.aborted = false;
62267
63073
  this.upgrade = upgrade || null;
62268
- this.path = query ? serializePathWithQuery(path6, query) : path6;
63074
+ this.path = query ? serializePathWithQuery(path5, query) : path5;
62269
63075
  this.origin = origin2;
62270
63076
  this.protocol = getProtocolFromUrlString(origin2);
62271
63077
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" || method === "QUERY" : idempotent;
@@ -67351,7 +68157,7 @@ var require_client_h1 = __commonJS({
67351
68157
  }
67352
68158
  }
67353
68159
  function writeH1(client, request2) {
67354
- const { method, path: path6, host, upgrade, blocking, reset } = request2;
68160
+ const { method, path: path5, host, upgrade, blocking, reset } = request2;
67355
68161
  let { body, headers, contentLength } = request2;
67356
68162
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
67357
68163
  if (util5.isFormDataLike(body)) {
@@ -67427,7 +68233,7 @@ var require_client_h1 = __commonJS({
67427
68233
  socket[kBlocking] = true;
67428
68234
  }
67429
68235
  setTypeOfService(socket, request2);
67430
- let header = `${method} ${path6} HTTP/1.1\r
68236
+ let header = `${method} ${path5} HTTP/1.1\r
67431
68237
  `;
67432
68238
  if (typeof host === "string") {
67433
68239
  header += `host: ${host}\r
@@ -68508,7 +69314,7 @@ var require_client_h2 = __commonJS({
68508
69314
  const headersTimeout = request2.headersTimeout ?? client[kHeadersTimeout];
68509
69315
  const bodyTimeout = request2.bodyTimeout ?? client[kBodyTimeout];
68510
69316
  const session = client[kHTTP2Session];
68511
- const { method, path: path6, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
69317
+ const { method, path: path5, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
68512
69318
  if (upgrade != null && upgrade !== "websocket") {
68513
69319
  util5.errorRequest(client, request2, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`));
68514
69320
  return false;
@@ -68571,7 +69377,7 @@ var require_client_h2 = __commonJS({
68571
69377
  }
68572
69378
  headers[HTTP2_HEADER_METHOD] = "CONNECT";
68573
69379
  headers[HTTP2_HEADER_PROTOCOL] = "websocket";
68574
- headers[HTTP2_HEADER_PATH] = path6;
69380
+ headers[HTTP2_HEADER_PATH] = path5;
68575
69381
  if (protocol === "ws:" || protocol === "wss:") {
68576
69382
  headers[HTTP2_HEADER_SCHEME] = protocol === "ws:" ? "http" : "https";
68577
69383
  } else {
@@ -68593,7 +69399,7 @@ var require_client_h2 = __commonJS({
68593
69399
  setupUpgradeStream(stream4, state);
68594
69400
  return true;
68595
69401
  }
68596
- headers[HTTP2_HEADER_PATH] = path6;
69402
+ headers[HTTP2_HEADER_PATH] = path5;
68597
69403
  headers[HTTP2_HEADER_SCHEME] = protocol === "http:" ? "http" : "https";
68598
69404
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
68599
69405
  let body = state.body;
@@ -71263,10 +72069,10 @@ var require_proxy_agent = __commonJS({
71263
72069
  };
71264
72070
  const {
71265
72071
  origin: origin2,
71266
- path: path6 = "/",
72072
+ path: path5 = "/",
71267
72073
  headers = {}
71268
72074
  } = opts;
71269
- opts.path = origin2 + path6;
72075
+ opts.path = origin2 + path5;
71270
72076
  if (!("host" in headers) && !("Host" in headers)) {
71271
72077
  const { host } = new URL(origin2);
71272
72078
  headers.host = host;
@@ -73531,20 +74337,20 @@ var require_mock_utils = __commonJS({
73531
74337
  }
73532
74338
  return normalizedQp;
73533
74339
  }
73534
- function safeUrl(path6) {
73535
- if (typeof path6 !== "string") {
73536
- return path6;
74340
+ function safeUrl(path5) {
74341
+ if (typeof path5 !== "string") {
74342
+ return path5;
73537
74343
  }
73538
- const pathSegments = path6.split("?", 3);
74344
+ const pathSegments = path5.split("?", 3);
73539
74345
  if (pathSegments.length !== 2) {
73540
- return path6;
74346
+ return path5;
73541
74347
  }
73542
74348
  const qp = new URLSearchParams(pathSegments.pop());
73543
74349
  qp.sort();
73544
74350
  return [...pathSegments, qp.toString()].join("?");
73545
74351
  }
73546
- function matchKey(mockDispatch2, { path: path6, method, body, headers }) {
73547
- const pathMatch = matchValue(mockDispatch2.path, path6);
74352
+ function matchKey(mockDispatch2, { path: path5, method, body, headers }) {
74353
+ const pathMatch = matchValue(mockDispatch2.path, path5);
73548
74354
  const methodMatch = matchValue(mockDispatch2.method, method);
73549
74355
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
73550
74356
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -73571,8 +74377,8 @@ var require_mock_utils = __commonJS({
73571
74377
  const basePath = key2.query ? serializePathWithQuery(key2.path, key2.query) : key2.path;
73572
74378
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
73573
74379
  const resolvedPathWithoutTrailingSlash = removeTrailingSlash(resolvedPath);
73574
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path6, ignoreTrailingSlash }) => {
73575
- return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path6)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path6), resolvedPath);
74380
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path5, ignoreTrailingSlash }) => {
74381
+ return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path5)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path5), resolvedPath);
73576
74382
  });
73577
74383
  if (matchedMockDispatches.length === 0) {
73578
74384
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
@@ -73611,22 +74417,22 @@ var require_mock_utils = __commonJS({
73611
74417
  mockDispatches.splice(index2, 1);
73612
74418
  }
73613
74419
  }
73614
- function removeTrailingSlash(path6) {
73615
- if (typeof path6 !== "string") {
73616
- return path6;
74420
+ function removeTrailingSlash(path5) {
74421
+ if (typeof path5 !== "string") {
74422
+ return path5;
73617
74423
  }
73618
- while (path6.endsWith("/")) {
73619
- path6 = path6.slice(0, -1);
74424
+ while (path5.endsWith("/")) {
74425
+ path5 = path5.slice(0, -1);
73620
74426
  }
73621
- if (path6.length === 0) {
73622
- path6 = "/";
74427
+ if (path5.length === 0) {
74428
+ path5 = "/";
73623
74429
  }
73624
- return path6;
74430
+ return path5;
73625
74431
  }
73626
74432
  function buildKey(opts) {
73627
- const { path: path6, method, body, headers, query } = opts;
74433
+ const { path: path5, method, body, headers, query } = opts;
73628
74434
  return {
73629
- path: path6,
74435
+ path: path5,
73630
74436
  method,
73631
74437
  body,
73632
74438
  headers,
@@ -74497,10 +75303,10 @@ var require_pending_interceptors_formatter = __commonJS({
74497
75303
  }
74498
75304
  format(pendingInterceptors) {
74499
75305
  const withPrettyHeaders = pendingInterceptors.map(
74500
- ({ method, path: path6, data: { statusCode }, persist, times, timesInvoked, origin: origin2 }) => ({
75306
+ ({ method, path: path5, data: { statusCode }, persist, times, timesInvoked, origin: origin2 }) => ({
74501
75307
  Method: method,
74502
75308
  Origin: origin2,
74503
- Path: path6,
75309
+ Path: path5,
74504
75310
  "Status code": statusCode,
74505
75311
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
74506
75312
  Invocations: timesInvoked,
@@ -74582,9 +75388,9 @@ var require_mock_agent = __commonJS({
74582
75388
  const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters];
74583
75389
  const dispatchOpts = { ...opts };
74584
75390
  if (acceptNonStandardSearchParameters && dispatchOpts.path) {
74585
- const [path6, searchParams] = dispatchOpts.path.split("?");
75391
+ const [path5, searchParams] = dispatchOpts.path.split("?");
74586
75392
  const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters);
74587
- dispatchOpts.path = `${path6}?${normalizedSearchParams}`;
75393
+ dispatchOpts.path = `${path5}?${normalizedSearchParams}`;
74588
75394
  }
74589
75395
  return this[kAgent].dispatch(dispatchOpts, handler);
74590
75396
  }
@@ -75000,12 +75806,12 @@ var require_snapshot_recorder = __commonJS({
75000
75806
  * @return {Promise<void>} - Resolves when snapshots are loaded
75001
75807
  */
75002
75808
  async loadSnapshots(filePath) {
75003
- const path6 = filePath || this.#snapshotPath;
75004
- if (!path6) {
75809
+ const path5 = filePath || this.#snapshotPath;
75810
+ if (!path5) {
75005
75811
  throw new InvalidArgumentError("Snapshot path is required");
75006
75812
  }
75007
75813
  try {
75008
- const data = await readFile3(resolve(path6), "utf8");
75814
+ const data = await readFile3(resolve(path5), "utf8");
75009
75815
  const parsed = JSON.parse(data);
75010
75816
  if (Array.isArray(parsed)) {
75011
75817
  this.#snapshots.clear();
@@ -75019,7 +75825,7 @@ var require_snapshot_recorder = __commonJS({
75019
75825
  if (error.code === "ENOENT") {
75020
75826
  this.#snapshots.clear();
75021
75827
  } else {
75022
- throw new UndiciError(`Failed to load snapshots from ${path6}`, { cause: error });
75828
+ throw new UndiciError(`Failed to load snapshots from ${path5}`, { cause: error });
75023
75829
  }
75024
75830
  }
75025
75831
  }
@@ -75030,11 +75836,11 @@ var require_snapshot_recorder = __commonJS({
75030
75836
  * @returns {Promise<void>} - Resolves when snapshots are saved
75031
75837
  */
75032
75838
  async saveSnapshots(filePath) {
75033
- const path6 = filePath || this.#snapshotPath;
75034
- if (!path6) {
75839
+ const path5 = filePath || this.#snapshotPath;
75840
+ if (!path5) {
75035
75841
  throw new InvalidArgumentError("Snapshot path is required");
75036
75842
  }
75037
- const resolvedPath = resolve(path6);
75843
+ const resolvedPath = resolve(path5);
75038
75844
  await mkdir5(dirname5(resolvedPath), { recursive: true });
75039
75845
  const data = Array.from(this.#snapshots.entries()).map(([hash5, snapshot]) => ({
75040
75846
  hash: hash5,
@@ -75671,15 +76477,15 @@ var require_redirect_handler = __commonJS({
75671
76477
  return;
75672
76478
  }
75673
76479
  const { origin: origin2, pathname, search } = util5.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
75674
- const path6 = search ? `${pathname}${search}` : pathname;
75675
- const redirectUrlString = `${origin2}${path6}`;
76480
+ const path5 = search ? `${pathname}${search}` : pathname;
76481
+ const redirectUrlString = `${origin2}${path5}`;
75676
76482
  for (const historyUrl of this.history) {
75677
76483
  if (historyUrl.toString() === redirectUrlString) {
75678
76484
  throw new InvalidArgumentError(`Redirect loop detected. Cannot redirect to ${origin2}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.`);
75679
76485
  }
75680
76486
  }
75681
76487
  this.opts.headers = cleanRequestHeaders(this.opts.headers, removeContentHeaders, this.opts.origin !== origin2, this.stripHeadersOnRedirect, this.stripHeadersOnCrossOriginRedirect);
75682
- this.opts.path = path6;
76488
+ this.opts.path = path5;
75683
76489
  this.opts.origin = origin2;
75684
76490
  this.opts.query = null;
75685
76491
  }
@@ -77507,10 +78313,10 @@ var require_cache_handler = __commonJS({
77507
78313
  }
77508
78314
  return locationUrl.pathname + locationUrl.search;
77509
78315
  }
77510
- function deleteCachedUri(store, cacheKey2, path6) {
78316
+ function deleteCachedUri(store, cacheKey2, path5) {
77511
78317
  deleteCachedValue(store, {
77512
78318
  ...cacheKey2,
77513
- path: path6
78319
+ path: path5
77514
78320
  });
77515
78321
  for (let i = 0; i < util5.safeHTTPMethods.length; i++) {
77516
78322
  const method = util5.safeHTTPMethods[i];
@@ -77518,7 +78324,7 @@ var require_cache_handler = __commonJS({
77518
78324
  deleteCachedValue(store, {
77519
78325
  ...cacheKey2,
77520
78326
  method,
77521
- path: path6
78327
+ path: path5
77522
78328
  });
77523
78329
  }
77524
78330
  }
@@ -77529,9 +78335,9 @@ var require_cache_handler = __commonJS({
77529
78335
  }
77530
78336
  const values = Array.isArray(headerValue) ? headerValue : [headerValue];
77531
78337
  for (let i = 0; i < values.length; i++) {
77532
- const path6 = getSameOriginPath(cacheKey2, values[i]);
77533
- if (path6 !== void 0) {
77534
- deleteCachedUri(store, cacheKey2, path6);
78338
+ const path5 = getSameOriginPath(cacheKey2, values[i]);
78339
+ if (path5 !== void 0) {
78340
+ deleteCachedUri(store, cacheKey2, path5);
77535
78341
  }
77536
78342
  }
77537
78343
  }
@@ -82528,13 +83334,13 @@ var require_fetch = __commonJS({
82528
83334
  function dispatch({ body }) {
82529
83335
  const url2 = requestCurrentURL(request2);
82530
83336
  const agent = fetchParams.controller.dispatcher;
82531
- const path6 = url2.pathname + url2.search;
83337
+ const path5 = url2.pathname + url2.search;
82532
83338
  const hasTrailingQuestionMark = url2.search.length === 0 && url2.href[url2.href.length - url2.hash.length - 1] === "?";
82533
83339
  return dispatchWithProtocolPreference(body);
82534
83340
  function dispatchWithProtocolPreference(body2, allowH2) {
82535
83341
  return new Promise((resolve, reject) => agent.dispatch(
82536
83342
  {
82537
- path: hasTrailingQuestionMark ? `${path6}?` : path6,
83343
+ path: hasTrailingQuestionMark ? `${path5}?` : path5,
82538
83344
  origin: url2.origin,
82539
83345
  method: request2.method,
82540
83346
  body: agent.isMockActive ? request2.body && (request2.body.source || request2.body.stream) : body2,
@@ -83446,9 +84252,9 @@ var require_util4 = __commonJS({
83446
84252
  }
83447
84253
  }
83448
84254
  }
83449
- function validateCookiePath(path6) {
83450
- for (let i = 0; i < path6.length; ++i) {
83451
- const code = path6.charCodeAt(i);
84255
+ function validateCookiePath(path5) {
84256
+ for (let i = 0; i < path5.length; ++i) {
84257
+ const code = path5.charCodeAt(i);
83452
84258
  if (code < 32 || // exclude CTLs (0-31)
83453
84259
  code > 126 || // exclude non-ascii and DEL
83454
84260
  code === 59) {
@@ -86820,11 +87626,11 @@ var require_undici = __commonJS({
86820
87626
  if (typeof opts.path !== "string") {
86821
87627
  throw new InvalidArgumentError("invalid opts.path");
86822
87628
  }
86823
- let path6 = opts.path;
87629
+ let path5 = opts.path;
86824
87630
  if (!opts.path.startsWith("/")) {
86825
- path6 = `/${path6}`;
87631
+ path5 = `/${path5}`;
86826
87632
  }
86827
- url2 = new URL(util5.parseOrigin(url2).origin + path6);
87633
+ url2 = new URL(util5.parseOrigin(url2).origin + path5);
86828
87634
  } else {
86829
87635
  if (!opts) {
86830
87636
  opts = typeof url2 === "object" ? url2 : {};
@@ -87370,8 +88176,8 @@ var init_textual_tool_calls = __esm({
87370
88176
 
87371
88177
  // src/response-store.ts
87372
88178
  import { appendFile as appendFile2, mkdir as mkdir3, readFile, readdir as readdir2 } from "fs/promises";
87373
- import { homedir as homedir5 } from "os";
87374
- import { join as join8 } from "path";
88179
+ import { homedir as homedir6 } from "os";
88180
+ import { join as join9 } from "path";
87375
88181
  import { randomBytes as randomBytes7 } from "crypto";
87376
88182
  function isEnabled() {
87377
88183
  const v = process.env.BLOCKRUN_RESPONSE_STORE;
@@ -87384,7 +88190,7 @@ async function ensureDir2() {
87384
88190
  }
87385
88191
  function dailyFile(date = /* @__PURE__ */ new Date()) {
87386
88192
  const iso = date.toISOString().slice(0, 10);
87387
- return join8(STORE_DIR, `responses-${iso}.jsonl`);
88193
+ return join9(STORE_DIR, `responses-${iso}.jsonl`);
87388
88194
  }
87389
88195
  function genId() {
87390
88196
  return `resp_${Date.now()}_${randomBytes7(3).toString("hex")}`;
@@ -87434,7 +88240,7 @@ async function listRecent(limit = 20, daysBack = 7) {
87434
88240
  d.setUTCDate(d.getUTCDate() - i);
87435
88241
  const iso = d.toISOString().slice(0, 10);
87436
88242
  const name = `responses-${iso}.jsonl`;
87437
- if (files.includes(name)) candidateFiles.push(join8(STORE_DIR, name));
88243
+ if (files.includes(name)) candidateFiles.push(join9(STORE_DIR, name));
87438
88244
  }
87439
88245
  const all3 = [];
87440
88246
  for (const f of candidateFiles) {
@@ -87463,7 +88269,7 @@ var STORE_DIR, dirReady2;
87463
88269
  var init_response_store = __esm({
87464
88270
  "src/response-store.ts"() {
87465
88271
  "use strict";
87466
- STORE_DIR = join8(homedir5(), ".openclaw", "blockrun", "responses");
88272
+ STORE_DIR = join9(homedir6(), ".openclaw", "blockrun", "responses");
87467
88273
  dirReady2 = false;
87468
88274
  }
87469
88275
  });
@@ -89113,10 +89919,10 @@ var init_client3 = __esm({
89113
89919
  import { AsyncLocalStorage } from "async_hooks";
89114
89920
  import { createServer } from "http";
89115
89921
  import { finished } from "stream";
89116
- import { homedir as homedir6 } from "os";
89117
- import { join as join9 } from "path";
89922
+ import { homedir as homedir7 } from "os";
89923
+ import { join as join10 } from "path";
89118
89924
  import { mkdir as mkdir4, writeFile as writeFile2, readFile as readFile2, stat as fsStat } from "fs/promises";
89119
- import { readFileSync as readFileSync2, existsSync } from "fs";
89925
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
89120
89926
  function pickFreeModel(excludeList) {
89121
89927
  for (const m of FREE_MODELS) {
89122
89928
  if (!excludeList?.has(m)) return m;
@@ -89124,6 +89930,8 @@ function pickFreeModel(excludeList) {
89124
89930
  return void 0;
89125
89931
  }
89126
89932
  function toUpstreamModelId(modelId) {
89933
+ const override = FREE_UPSTREAM_OVERRIDES[modelId];
89934
+ if (override) return override;
89127
89935
  if (modelId.startsWith("free/")) {
89128
89936
  return "nvidia/" + modelId.slice("free/".length);
89129
89937
  }
@@ -89882,8 +90690,8 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
89882
90690
  });
89883
90691
  }
89884
90692
  function readImageFileAsDataUri(filePath) {
89885
- const resolved = filePath.startsWith("~/") ? join9(homedir6(), filePath.slice(2)) : filePath;
89886
- if (!existsSync(resolved)) {
90693
+ const resolved = filePath.startsWith("~/") ? join10(homedir7(), filePath.slice(2)) : filePath;
90694
+ if (!existsSync2(resolved)) {
89887
90695
  throw new Error(`Image file not found: ${resolved}`);
89888
90696
  }
89889
90697
  const ext = resolved.split(".").pop()?.toLowerCase() ?? "png";
@@ -89997,6 +90805,8 @@ async function startProxy(options) {
89997
90805
  const evmPublicClient = createPublicClient({ chain: base, transport: http() });
89998
90806
  const evmSigner = toClientEvmSigner(account, evmPublicClient);
89999
90807
  const x402 = new x402Client();
90808
+ const spendControl = options.spendControl ?? new SpendControl();
90809
+ registerSpendPolicyHook(x402, spendControl);
90000
90810
  registerExactEvmScheme(x402, { signer: evmSigner });
90001
90811
  let solanaAddress;
90002
90812
  if (solanaPrivateKeyBytes) {
@@ -90165,8 +90975,8 @@ async function startProxy(options) {
90165
90975
  if (req.url?.startsWith("/share") && req.method === "GET") {
90166
90976
  try {
90167
90977
  const url2 = new URL(req.url, "http://localhost");
90168
- const path6 = url2.pathname;
90169
- if (path6 === "/share/list") {
90978
+ const path5 = url2.pathname;
90979
+ if (path5 === "/share/list") {
90170
90980
  const limit = Math.min(parseInt(url2.searchParams.get("limit") || "20", 10), 100);
90171
90981
  const entries = await listRecent(limit);
90172
90982
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -90184,7 +90994,7 @@ async function startProxy(options) {
90184
90994
  );
90185
90995
  return;
90186
90996
  }
90187
- if (path6 === "/share/last") {
90997
+ if (path5 === "/share/last") {
90188
90998
  const sessionId = url2.searchParams.get("sessionId") || void 0;
90189
90999
  const entry = await getLast(sessionId);
90190
91000
  if (!entry) {
@@ -90213,7 +91023,7 @@ async function startProxy(options) {
90213
91023
  res.end(JSON.stringify(entry));
90214
91024
  return;
90215
91025
  }
90216
- const idMatch = path6.match(/^\/share\/(resp_[A-Za-z0-9_]+)(?:\/render)?$/);
91026
+ const idMatch = path5.match(/^\/share\/(resp_[A-Za-z0-9_]+)(?:\/render)?$/);
90217
91027
  if (idMatch) {
90218
91028
  const id2 = idMatch[1];
90219
91029
  const entry = await getById(id2);
@@ -90259,7 +91069,7 @@ async function startProxy(options) {
90259
91069
  res.end("Bad request");
90260
91070
  return;
90261
91071
  }
90262
- const filePath = join9(IMAGE_DIR, filename);
91072
+ const filePath = join10(IMAGE_DIR, filename);
90263
91073
  try {
90264
91074
  const s3 = await fsStat(filePath);
90265
91075
  if (!s3.isFile()) throw new Error("not a file");
@@ -90290,7 +91100,7 @@ async function startProxy(options) {
90290
91100
  res.end("Bad request");
90291
91101
  return;
90292
91102
  }
90293
- const filePath = join9(AUDIO_DIR, filename);
91103
+ const filePath = join10(AUDIO_DIR, filename);
90294
91104
  try {
90295
91105
  const s3 = await fsStat(filePath);
90296
91106
  if (!s3.isFile()) throw new Error("not a file");
@@ -90320,7 +91130,7 @@ async function startProxy(options) {
90320
91130
  res.end("Bad request");
90321
91131
  return;
90322
91132
  }
90323
- const filePath = join9(VIDEO_DIR, filename);
91133
+ const filePath = join10(VIDEO_DIR, filename);
90324
91134
  try {
90325
91135
  const s3 = await fsStat(filePath);
90326
91136
  if (!s3.isFile()) throw new Error("not a file");
@@ -90461,7 +91271,7 @@ async function startProxy(options) {
90461
91271
  const [, mimeType, b64] = dataUriMatch;
90462
91272
  const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
90463
91273
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
90464
- await writeFile2(join9(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
91274
+ await writeFile2(join10(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
90465
91275
  img.url = `http://localhost:${port2}/images/${filename}`;
90466
91276
  console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
90467
91277
  } else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
@@ -90472,7 +91282,7 @@ async function startProxy(options) {
90472
91282
  const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
90473
91283
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
90474
91284
  const buf = Buffer.from(await imgResp.arrayBuffer());
90475
- await writeFile2(join9(IMAGE_DIR, filename), buf);
91285
+ await writeFile2(join10(IMAGE_DIR, filename), buf);
90476
91286
  img.url = `http://localhost:${port2}/images/${filename}`;
90477
91287
  console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
90478
91288
  }
@@ -90584,7 +91394,7 @@ async function startProxy(options) {
90584
91394
  const [, mimeType, b64] = dataUriMatch;
90585
91395
  const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
90586
91396
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
90587
- await writeFile2(join9(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
91397
+ await writeFile2(join10(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
90588
91398
  img.url = `http://localhost:${port2}/images/${filename}`;
90589
91399
  console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
90590
91400
  } else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
@@ -90595,7 +91405,7 @@ async function startProxy(options) {
90595
91405
  const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
90596
91406
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
90597
91407
  const buf = Buffer.from(await imgResp.arrayBuffer());
90598
- await writeFile2(join9(IMAGE_DIR, filename), buf);
91408
+ await writeFile2(join10(IMAGE_DIR, filename), buf);
90599
91409
  img.url = `http://localhost:${port2}/images/${filename}`;
90600
91410
  console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
90601
91411
  }
@@ -90681,7 +91491,7 @@ async function startProxy(options) {
90681
91491
  const ext = contentType.includes("wav") ? "wav" : "mp3";
90682
91492
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
90683
91493
  const buf = Buffer.from(await audioResp.arrayBuffer());
90684
- await writeFile2(join9(AUDIO_DIR, filename), buf);
91494
+ await writeFile2(join10(AUDIO_DIR, filename), buf);
90685
91495
  track.url = `http://localhost:${port2}/audio/${filename}`;
90686
91496
  console.log(`[ClawRouter] Audio saved \u2192 ${track.url}`);
90687
91497
  }
@@ -90841,7 +91651,7 @@ async function startProxy(options) {
90841
91651
  const ext = contentType.includes("webm") ? "webm" : contentType.includes("quicktime") ? "mov" : "mp4";
90842
91652
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
90843
91653
  const buf = Buffer.from(await videoResp.arrayBuffer());
90844
- await writeFile2(join9(VIDEO_DIR, filename), buf);
91654
+ await writeFile2(join10(VIDEO_DIR, filename), buf);
90845
91655
  clip.url = `http://localhost:${port2}/videos/${filename}`;
90846
91656
  console.log(`[ClawRouter] Video saved \u2192 ${clip.url}`);
90847
91657
  }
@@ -91181,6 +91991,17 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
91181
91991
  return { success: true, response };
91182
91992
  } catch (err) {
91183
91993
  const errorMsg = err instanceof Error ? err.message : String(err);
91994
+ if (err instanceof SpendPolicyError) {
91995
+ return {
91996
+ success: false,
91997
+ errorBody: JSON.stringify({
91998
+ error: { message: errorMsg, type: "spend_policy_denied", status: 403 }
91999
+ }),
92000
+ errorStatus: 403,
92001
+ isProviderError: false,
92002
+ errorCategory: "payment_error"
92003
+ };
92004
+ }
91184
92005
  return {
91185
92006
  success: false,
91186
92007
  errorBody: errorMsg,
@@ -93345,7 +94166,7 @@ data: [DONE]
93345
94166
  });
93346
94167
  }
93347
94168
  }
93348
- var paymentStore, BLOCKRUN_API, BLOCKRUN_SOLANA_API, IMAGE_DIR, AUDIO_DIR, VIDEO_DIR, AUTO_MODEL, ROUTING_PROFILES, FREE_MODELS, FREE_MODEL, MAX_MESSAGES, CONTEXT_LIMIT_KB, HEARTBEAT_INTERVAL_MS, BALANCE_CHECK_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, PER_MODEL_TIMEOUT_MS, REASONING_MODEL_TIMEOUT_MS, REASONING_MODEL_IDS, MAX_FALLBACK_ATTEMPTS, HEALTH_CHECK_TIMEOUT_MS, RATE_LIMIT_COOLDOWN_MS, OVERLOAD_COOLDOWN_MS, PORT_RETRY_ATTEMPTS, PORT_RETRY_DELAY_MS, MODEL_BODY_READ_TIMEOUT_MS, ERROR_BODY_READ_TIMEOUT_MS, ClientDisconnectedError, rateLimitedModels, overloadedModels, perProviderErrors, BALANCE_CHECK_BUFFER, BALANCE_PREFLIGHT_OUTPUT_TOKEN_CAP, PROVIDER_ERROR_PATTERNS, DEGRADED_RESPONSE_PATTERNS, DEGRADED_LOOP_PATTERNS, VALID_ROLES, ROLE_MAPPINGS, VALID_TOOL_ID_PATTERN, KIMI_BLOCK_RE, KIMI_TOKEN_RE, THINKING_TAG_RE, THINKING_BLOCK_RE, BLOCKRUN_MODEL_BY_ID, IMAGE_PRICING, IMAGE_MODEL_IDS, IMAGE_MODEL_SIZES, IMAGE_MODEL_ALIASES, VIDEO_PRICING, PHONE_PRICING;
94169
+ var paymentStore, BLOCKRUN_API, BLOCKRUN_SOLANA_API, IMAGE_DIR, AUDIO_DIR, VIDEO_DIR, AUTO_MODEL, ROUTING_PROFILES, FREE_MODELS, FREE_MODEL, FREE_UPSTREAM_OVERRIDES, MAX_MESSAGES, CONTEXT_LIMIT_KB, HEARTBEAT_INTERVAL_MS, BALANCE_CHECK_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, PER_MODEL_TIMEOUT_MS, REASONING_MODEL_TIMEOUT_MS, REASONING_MODEL_IDS, MAX_FALLBACK_ATTEMPTS, HEALTH_CHECK_TIMEOUT_MS, RATE_LIMIT_COOLDOWN_MS, OVERLOAD_COOLDOWN_MS, PORT_RETRY_ATTEMPTS, PORT_RETRY_DELAY_MS, MODEL_BODY_READ_TIMEOUT_MS, ERROR_BODY_READ_TIMEOUT_MS, ClientDisconnectedError, rateLimitedModels, overloadedModels, perProviderErrors, BALANCE_CHECK_BUFFER, BALANCE_PREFLIGHT_OUTPUT_TOKEN_CAP, PROVIDER_ERROR_PATTERNS, DEGRADED_RESPONSE_PATTERNS, DEGRADED_LOOP_PATTERNS, VALID_ROLES, ROLE_MAPPINGS, VALID_TOOL_ID_PATTERN, KIMI_BLOCK_RE, KIMI_TOKEN_RE, THINKING_TAG_RE, THINKING_BLOCK_RE, BLOCKRUN_MODEL_BY_ID, IMAGE_PRICING, IMAGE_MODEL_IDS, IMAGE_MODEL_SIZES, IMAGE_MODEL_ALIASES, VIDEO_PRICING, PHONE_PRICING;
93349
94170
  var init_proxy = __esm({
93350
94171
  "src/proxy.ts"() {
93351
94172
  "use strict";
@@ -93367,6 +94188,7 @@ var init_proxy = __esm({
93367
94188
  init_response_cache();
93368
94189
  init_balance();
93369
94190
  init_auth();
94191
+ init_spend_control();
93370
94192
  init_compression();
93371
94193
  init_version4();
93372
94194
  init_session();
@@ -93382,9 +94204,9 @@ var init_proxy = __esm({
93382
94204
  paymentStore = new AsyncLocalStorage();
93383
94205
  BLOCKRUN_API = "https://blockrun.ai/api";
93384
94206
  BLOCKRUN_SOLANA_API = "https://sol.blockrun.ai/api";
93385
- IMAGE_DIR = join9(homedir6(), ".openclaw", "blockrun", "images");
93386
- AUDIO_DIR = join9(homedir6(), ".openclaw", "blockrun", "audio");
93387
- VIDEO_DIR = join9(homedir6(), ".openclaw", "blockrun", "videos");
94207
+ IMAGE_DIR = join10(homedir7(), ".openclaw", "blockrun", "images");
94208
+ AUDIO_DIR = join10(homedir7(), ".openclaw", "blockrun", "audio");
94209
+ VIDEO_DIR = join10(homedir7(), ".openclaw", "blockrun", "videos");
93388
94210
  AUTO_MODEL = "blockrun/auto";
93389
94211
  ROUTING_PROFILES = /* @__PURE__ */ new Set([
93390
94212
  "blockrun/eco",
@@ -93395,18 +94217,26 @@ var init_proxy = __esm({
93395
94217
  "premium"
93396
94218
  ]);
93397
94219
  FREE_MODELS = /* @__PURE__ */ new Set([
93398
- "free/step-3.7-flash",
93399
- // reasoning-focused — free-tier flagship
93400
- "free/nemotron-nano-9b-v2",
93401
- // fast lightweight generalist (~0.7s)
93402
- "free/mistral-nemotron",
93403
- // strong instruction following
94220
+ "free/nemotron-3.5-lightning",
94221
+ // free-tier default — 1M ctx, thinking mode
94222
+ "free/nemotron-3-nano-30b",
94223
+ // fastest free model (~121 tok/s)
94224
+ "free/laguna-xs-2.1",
94225
+ // coding, ~161 tok/s — on our NVIDIA key
94226
+ "free/north-mini-code",
94227
+ // coding, 607ms median — OpenRouter $0 pool
93404
94228
  "free/nemotron-3-nano-omni-30b-a3b-reasoning",
93405
94229
  // vision (text/image/video/audio)
93406
- "free/nemotron-nano-12b-v2-vl"
93407
- // vision-language (text + image)
94230
+ "free/nemotron-3-ultra-550b",
94231
+ // largest free model, 1M ctx — but slow + flaky
94232
+ "free/llama-3.2-11b-vision"
94233
+ // only free Llama left; slowest, so last
93408
94234
  ]);
93409
- FREE_MODEL = "free/step-3.7-flash";
94235
+ FREE_MODEL = "free/nemotron-3.5-lightning";
94236
+ FREE_UPSTREAM_OVERRIDES = {
94237
+ "free/north-mini-code": "cohere/north-mini-code",
94238
+ "free/laguna-xs-2.1": "poolside/laguna-xs-2.1"
94239
+ };
93410
94240
  MAX_MESSAGES = 200;
93411
94241
  CONTEXT_LIMIT_KB = 5120;
93412
94242
  HEARTBEAT_INTERVAL_MS = 2e3;
@@ -94449,7 +95279,7 @@ function buildTool(service, proxyBaseUrl) {
94449
95279
  required
94450
95280
  },
94451
95281
  execute: async (_toolCallId, params) => {
94452
- let path6;
95282
+ let path5;
94453
95283
  const leftoverParams = {};
94454
95284
  let dynamicMethod = "GET";
94455
95285
  let dynamicBody = void 0;
@@ -94461,7 +95291,7 @@ function buildTool(service, proxyBaseUrl) {
94461
95291
  `predexon_endpoint_call: invalid path '${rawPath}' \u2014 must begin with '/pm/' and contain no '..'`
94462
95292
  );
94463
95293
  }
94464
- path6 = `/v1${normalized}`;
95294
+ path5 = `/v1${normalized}`;
94465
95295
  if (typeof params.method === "string") {
94466
95296
  const upper = params.method.toUpperCase();
94467
95297
  if (upper !== "GET" && upper !== "POST") {
@@ -94510,19 +95340,19 @@ function buildTool(service, proxyBaseUrl) {
94510
95340
  }
94511
95341
  }
94512
95342
  } else {
94513
- path6 = `/v1${service.proxyPath}`;
95343
+ path5 = `/v1${service.proxyPath}`;
94514
95344
  for (const [key2, value] of Object.entries(params)) {
94515
95345
  if (value === void 0 || value === null) continue;
94516
95346
  const placeholder = `:${key2}`;
94517
- if (path6.includes(placeholder)) {
94518
- path6 = path6.replace(placeholder, encodeURIComponent(String(value)));
95347
+ if (path5.includes(placeholder)) {
95348
+ path5 = path5.replace(placeholder, encodeURIComponent(String(value)));
94519
95349
  } else {
94520
95350
  leftoverParams[key2] = value;
94521
95351
  }
94522
95352
  }
94523
95353
  }
94524
95354
  const effectiveMethod = service.proxyPath === "/pm/__dynamic__" ? dynamicMethod : service.method;
94525
- let url2 = `${proxyBaseUrl}${path6}`;
95355
+ let url2 = `${proxyBaseUrl}${path5}`;
94526
95356
  if (effectiveMethod === "GET" && Object.keys(leftoverParams).length > 0) {
94527
95357
  const qs = new URLSearchParams();
94528
95358
  for (const [key2, value] of Object.entries(leftoverParams)) {
@@ -106201,11 +107031,11 @@ var require_mime_types = __commonJS({
106201
107031
  }
106202
107032
  return exts[0];
106203
107033
  }
106204
- function lookup(path6) {
106205
- if (!path6 || typeof path6 !== "string") {
107034
+ function lookup(path5) {
107035
+ if (!path5 || typeof path5 !== "string") {
106206
107036
  return false;
106207
107037
  }
106208
- var extension3 = extname("x." + path6).toLowerCase().substr(1);
107038
+ var extension3 = extname("x." + path5).toLowerCase().substr(1);
106209
107039
  if (!extension3) {
106210
107040
  return false;
106211
107041
  }
@@ -107320,7 +108150,7 @@ var require_form_data = __commonJS({
107320
108150
  "use strict";
107321
108151
  var CombinedStream = require_combined_stream();
107322
108152
  var util5 = __require("util");
107323
- var path6 = __require("path");
108153
+ var path5 = __require("path");
107324
108154
  var http5 = __require("http");
107325
108155
  var https2 = __require("https");
107326
108156
  var parseUrl3 = __require("url").parse;
@@ -107451,11 +108281,11 @@ var require_form_data = __commonJS({
107451
108281
  FormData3.prototype._getContentDisposition = function(value, options) {
107452
108282
  var filename;
107453
108283
  if (typeof options.filepath === "string") {
107454
- filename = path6.normalize(options.filepath).replace(/\\/g, "/");
108284
+ filename = path5.normalize(options.filepath).replace(/\\/g, "/");
107455
108285
  } else if (options.filename || value && (value.name || value.path)) {
107456
- filename = path6.basename(options.filename || value && (value.name || value.path));
108286
+ filename = path5.basename(options.filename || value && (value.name || value.path));
107457
108287
  } else if (value && value.readable && hasOwn(value, "httpVersion")) {
107458
- filename = path6.basename(value.client._httpMessage.path || "");
108288
+ filename = path5.basename(value.client._httpMessage.path || "");
107459
108289
  }
107460
108290
  if (filename) {
107461
108291
  return 'filename="' + escapeHeaderParam(filename) + '"';
@@ -107669,9 +108499,9 @@ function isVisitable(thing) {
107669
108499
  function removeBrackets(key2) {
107670
108500
  return utils_default.endsWith(key2, "[]") ? key2.slice(0, -2) : key2;
107671
108501
  }
107672
- function renderKey(path6, key2, dots) {
107673
- if (!path6) return key2;
107674
- return path6.concat(key2).map(function each(token, i) {
108502
+ function renderKey(path5, key2, dots) {
108503
+ if (!path5) return key2;
108504
+ return path5.concat(key2).map(function each(token, i) {
107675
108505
  token = removeBrackets(token);
107676
108506
  return !dots && i ? "[" + token + "]" : token;
107677
108507
  }).join(dots ? "." : "");
@@ -107754,13 +108584,13 @@ function toFormData(obj, formData, options) {
107754
108584
  return currentValue;
107755
108585
  });
107756
108586
  }
107757
- function defaultVisitor(value, key2, path6) {
108587
+ function defaultVisitor(value, key2, path5) {
107758
108588
  let arr = value;
107759
108589
  if (utils_default.isReactNative(formData) && utils_default.isReactNativeBlob(value)) {
107760
- formData.append(renderKey(path6, key2, dots), convertValue(value));
108590
+ formData.append(renderKey(path5, key2, dots), convertValue(value));
107761
108591
  return false;
107762
108592
  }
107763
- if (value && !path6 && typeof value === "object") {
108593
+ if (value && !path5 && typeof value === "object") {
107764
108594
  if (utils_default.endsWith(key2, "{}")) {
107765
108595
  key2 = metaTokens ? key2 : key2.slice(0, -2);
107766
108596
  value = stringifyWithDepthLimit(value, 1);
@@ -107779,7 +108609,7 @@ function toFormData(obj, formData, options) {
107779
108609
  if (isVisitable(value)) {
107780
108610
  return true;
107781
108611
  }
107782
- formData.append(renderKey(path6, key2, dots), convertValue(value));
108612
+ formData.append(renderKey(path5, key2, dots), convertValue(value));
107783
108613
  return false;
107784
108614
  }
107785
108615
  const exposedHelpers = Object.assign(predicates, {
@@ -107787,17 +108617,17 @@ function toFormData(obj, formData, options) {
107787
108617
  convertValue,
107788
108618
  isVisitable
107789
108619
  });
107790
- function build(value, path6, depth = 0) {
108620
+ function build(value, path5, depth = 0) {
107791
108621
  if (utils_default.isUndefined(value)) return;
107792
108622
  throwIfMaxDepthExceeded(depth);
107793
108623
  if (stack.indexOf(value) !== -1) {
107794
- throw new Error("Circular reference detected in " + path6.join("."));
108624
+ throw new Error("Circular reference detected in " + path5.join("."));
107795
108625
  }
107796
108626
  stack.push(value);
107797
108627
  utils_default.forEach(value, function each(el, key2) {
107798
- const result = !(utils_default.isUndefined(el) || el === null) && visitor.call(formData, el, utils_default.isString(key2) ? key2.trim() : key2, path6, exposedHelpers);
108628
+ const result = !(utils_default.isUndefined(el) || el === null) && visitor.call(formData, el, utils_default.isString(key2) ? key2.trim() : key2, path5, exposedHelpers);
107799
108629
  if (result === true) {
107800
- build(el, path6 ? path6.concat(key2) : [key2], depth + 1);
108630
+ build(el, path5 ? path5.concat(key2) : [key2], depth + 1);
107801
108631
  }
107802
108632
  });
107803
108633
  stack.pop();
@@ -108076,7 +108906,7 @@ var init_platform = __esm({
108076
108906
  // node_modules/axios/lib/helpers/toURLEncodedForm.js
108077
108907
  function toURLEncodedForm(data, options) {
108078
108908
  return toFormData_default(data, new platform_default.classes.URLSearchParams(), {
108079
- visitor: function(value, key2, path6, helpers) {
108909
+ visitor: function(value, key2, path5, helpers) {
108080
108910
  if (platform_default.isNode && utils_default.isBuffer(value)) {
108081
108911
  this.append(key2, value.toString("base64"));
108082
108912
  return false;
@@ -108105,14 +108935,14 @@ function throwIfDepthExceeded(index2) {
108105
108935
  }
108106
108936
  }
108107
108937
  function parsePropPath(name) {
108108
- const path6 = [];
108938
+ const path5 = [];
108109
108939
  const pattern = /[^.[\]]+|\[([^.[\]]*)]/g;
108110
108940
  let match;
108111
108941
  while ((match = pattern.exec(name)) !== null) {
108112
- throwIfDepthExceeded(path6.length);
108113
- path6.push(match[0] === "[]" ? "" : match[1] || match[0]);
108942
+ throwIfDepthExceeded(path5.length);
108943
+ path5.push(match[0] === "[]" ? "" : match[1] || match[0]);
108114
108944
  }
108115
- return path6;
108945
+ return path5;
108116
108946
  }
108117
108947
  function arrayToObject(arr) {
108118
108948
  const obj = {};
@@ -108127,12 +108957,12 @@ function arrayToObject(arr) {
108127
108957
  return obj;
108128
108958
  }
108129
108959
  function formDataToJSON(formData) {
108130
- function buildPath(path6, value, target, index2) {
108960
+ function buildPath(path5, value, target, index2) {
108131
108961
  throwIfDepthExceeded(index2);
108132
- let name = path6[index2++];
108962
+ let name = path5[index2++];
108133
108963
  if (name === "__proto__") return true;
108134
108964
  const isNumericKey = Number.isFinite(+name);
108135
- const isLast = index2 >= path6.length;
108965
+ const isLast = index2 >= path5.length;
108136
108966
  name = !name && utils_default.isArray(target) ? target.length : name;
108137
108967
  if (isLast) {
108138
108968
  if (utils_default.hasOwnProp(target, name)) {
@@ -108145,7 +108975,7 @@ function formDataToJSON(formData) {
108145
108975
  if (!utils_default.hasOwnProp(target, name) || !utils_default.isObject(target[name])) {
108146
108976
  target[name] = [];
108147
108977
  }
108148
- const result = buildPath(path6, value, target[name], index2);
108978
+ const result = buildPath(path5, value, target[name], index2);
108149
108979
  if (result && utils_default.isArray(target[name])) {
108150
108980
  target[name] = arrayToObject(target[name]);
108151
108981
  }
@@ -111692,9 +112522,9 @@ var init_http4 = __esm({
111692
112522
  auth = urlUsername + ":" + urlPassword;
111693
112523
  }
111694
112524
  auth && headers.delete("authorization");
111695
- let path6;
112525
+ let path5;
111696
112526
  try {
111697
- path6 = buildURL(
112527
+ path5 = buildURL(
111698
112528
  parsed.pathname + parsed.search,
111699
112529
  own2("params"),
111700
112530
  own2("paramsSerializer")
@@ -111713,7 +112543,7 @@ var init_http4 = __esm({
111713
112543
  false
111714
112544
  );
111715
112545
  const options = Object.assign(/* @__PURE__ */ Object.create(null), {
111716
- path: path6,
112546
+ path: path5,
111717
112547
  method,
111718
112548
  headers: toByteStringHeaderObject(headers),
111719
112549
  agents: { http: httpAgent, https: httpsAgent },
@@ -112130,14 +112960,14 @@ var init_cookies = __esm({
112130
112960
  cookies_default = platform_default.hasStandardBrowserEnv ? (
112131
112961
  // Standard browser envs support document.cookie
112132
112962
  {
112133
- write(name, value, expires, path6, domain, secure, sameSite) {
112963
+ write(name, value, expires, path5, domain, secure, sameSite) {
112134
112964
  if (typeof document === "undefined") return;
112135
112965
  const cookie = [`${name}=${encodeURIComponent(value)}`];
112136
112966
  if (utils_default.isNumber(expires)) {
112137
112967
  cookie.push(`expires=${new Date(expires).toUTCString()}`);
112138
112968
  }
112139
- if (utils_default.isString(path6)) {
112140
- cookie.push(`path=${path6}`);
112969
+ if (utils_default.isString(path5)) {
112970
+ cookie.push(`path=${path5}`);
112141
112971
  }
112142
112972
  if (utils_default.isString(domain)) {
112143
112973
  cookie.push(`domain=${domain}`);
@@ -115566,15 +116396,15 @@ var init_constants3 = __esm({
115566
116396
  });
115567
116397
 
115568
116398
  // src/polymarket/wallet-adapter.ts
115569
- import fs from "fs";
115570
- import { homedir as homedir7 } from "os";
115571
- import { join as join10 } from "path";
116399
+ import fs2 from "fs";
116400
+ import { homedir as homedir8 } from "os";
116401
+ import { join as join11 } from "path";
115572
116402
  function getOrCreateWalletKey() {
115573
116403
  const envKey = process.env.BLOCKRUN_WALLET_KEY?.trim();
115574
116404
  if (envKey && /^0x[0-9a-fA-F]{64}$/.test(envKey)) return envKey;
115575
116405
  let key2;
115576
116406
  try {
115577
- key2 = fs.readFileSync(WALLET_FILE2, "utf-8").trim();
116407
+ key2 = fs2.readFileSync(WALLET_FILE2, "utf-8").trim();
115578
116408
  } catch (err) {
115579
116409
  if (err.code === "ENOENT") {
115580
116410
  throw new Error(
@@ -115616,7 +116446,7 @@ var init_wallet_adapter = __esm({
115616
116446
  init_esm();
115617
116447
  init_chains();
115618
116448
  init_constants3();
115619
- WALLET_FILE2 = join10(homedir7(), ".openclaw", "blockrun", "wallet.key");
116449
+ WALLET_FILE2 = join11(homedir8(), ".openclaw", "blockrun", "wallet.key");
115620
116450
  BASE_RPC_URLS = [
115621
116451
  "https://mainnet.base.org",
115622
116452
  "https://base.llamarpc.com",
@@ -115627,21 +116457,21 @@ var init_wallet_adapter = __esm({
115627
116457
  });
115628
116458
 
115629
116459
  // src/polymarket/creds.ts
115630
- import fs2 from "fs";
115631
- import path from "path";
116460
+ import fs3 from "fs";
116461
+ import path2 from "path";
115632
116462
  import os from "os";
115633
116463
  function readJsonFile(file) {
115634
116464
  try {
115635
- if (!fs2.existsSync(file)) return null;
115636
- const raw = fs2.readFileSync(file, "utf-8").trim();
116465
+ if (!fs3.existsSync(file)) return null;
116466
+ const raw = fs3.readFileSync(file, "utf-8").trim();
115637
116467
  return raw ? JSON.parse(raw) : null;
115638
116468
  } catch {
115639
116469
  return null;
115640
116470
  }
115641
116471
  }
115642
116472
  function writeJsonFile(file, value) {
115643
- fs2.mkdirSync(BLOCKRUN_DIR, { recursive: true });
115644
- fs2.writeFileSync(file, JSON.stringify(value, null, 2), { mode: 384 });
116473
+ fs3.mkdirSync(BLOCKRUN_DIR, { recursive: true });
116474
+ fs3.writeFileSync(file, JSON.stringify(value, null, 2), { mode: 384 });
115645
116475
  }
115646
116476
  function credsKey(address2, sigType) {
115647
116477
  return `${address2.toLowerCase()}:${sigType}`;
@@ -115693,10 +116523,10 @@ var BLOCKRUN_DIR, CREDS_FILE, BUILDER_CREDS_FILE, STATE_FILE;
115693
116523
  var init_creds = __esm({
115694
116524
  "src/polymarket/creds.ts"() {
115695
116525
  "use strict";
115696
- BLOCKRUN_DIR = path.join(os.homedir(), ".openclaw", "blockrun");
115697
- CREDS_FILE = path.join(BLOCKRUN_DIR, ".polymarket-creds");
115698
- BUILDER_CREDS_FILE = path.join(BLOCKRUN_DIR, ".polymarket-builder-creds");
115699
- STATE_FILE = path.join(BLOCKRUN_DIR, ".polymarket.json");
116526
+ BLOCKRUN_DIR = path2.join(os.homedir(), ".openclaw", "blockrun");
116527
+ CREDS_FILE = path2.join(BLOCKRUN_DIR, ".polymarket-creds");
116528
+ BUILDER_CREDS_FILE = path2.join(BLOCKRUN_DIR, ".polymarket-builder-creds");
116529
+ STATE_FILE = path2.join(BLOCKRUN_DIR, ".polymarket.json");
115700
116530
  }
115701
116531
  });
115702
116532
 
@@ -116878,13 +117708,13 @@ function __disposeResources(env) {
116878
117708
  }
116879
117709
  return next();
116880
117710
  }
116881
- function __rewriteRelativeImportExtension(path6, preserveJsx) {
116882
- if (typeof path6 === "string" && /^\.\.?\//.test(path6)) {
116883
- return path6.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
117711
+ function __rewriteRelativeImportExtension(path5, preserveJsx) {
117712
+ if (typeof path5 === "string" && /^\.\.?\//.test(path5)) {
117713
+ return path5.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
116884
117714
  return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
116885
117715
  });
116886
117716
  }
116887
- return path6;
117717
+ return path5;
116888
117718
  }
116889
117719
  var extendStatics, __assign, __createBinding, __setModuleDefault, ownKeys, _SuppressedError, tslib_es6_default;
116890
117720
  var init_tslib_es6 = __esm({
@@ -149181,9 +150011,9 @@ var require_setupKzg = __commonJS({
149181
150011
  Object.defineProperty(exports, "__esModule", { value: true });
149182
150012
  exports.setupKzg = setupKzg;
149183
150013
  var defineKzg_js_1 = require_defineKzg();
149184
- function setupKzg(parameters, path6) {
150014
+ function setupKzg(parameters, path5) {
149185
150015
  try {
149186
- parameters.loadTrustedSetup(path6);
150016
+ parameters.loadTrustedSetup(path5);
149187
150017
  } catch (e7) {
149188
150018
  const error = e7;
149189
150019
  if (!error.message.includes("trusted setup is already loaded"))
@@ -197268,12 +198098,12 @@ var init_fragments = __esm({
197268
198098
  // node_modules/@ethersproject/abi/lib.esm/coders/abstract-coder.js
197269
198099
  function checkResultErrors(result) {
197270
198100
  const errors = [];
197271
- const checkErrors = function(path6, object) {
198101
+ const checkErrors = function(path5, object) {
197272
198102
  if (!Array.isArray(object)) {
197273
198103
  return;
197274
198104
  }
197275
198105
  for (let key2 in object) {
197276
- const childPath = path6.slice();
198106
+ const childPath = path5.slice();
197277
198107
  childPath.push(key2);
197278
198108
  try {
197279
198109
  checkErrors(childPath, object[key2]);
@@ -203091,8 +203921,8 @@ function createCommonjsModule(fn, basedir, module) {
203091
203921
  return module = {
203092
203922
  path: basedir,
203093
203923
  exports: {},
203094
- require: function(path6, base3) {
203095
- return commonjsRequire(path6, base3 === void 0 || base3 === null ? module.path : base3);
203924
+ require: function(path5, base3) {
203925
+ return commonjsRequire(path5, base3 === void 0 || base3 === null ? module.path : base3);
203096
203926
  }
203097
203927
  }, fn(module, module.exports), module.exports;
203098
203928
  }
@@ -206942,9 +207772,9 @@ var init_lib23 = __esm({
206942
207772
  if (index2 > 4294967295) {
206943
207773
  throw new Error("invalid index - " + String(index2));
206944
207774
  }
206945
- let path6 = this.path;
206946
- if (path6) {
206947
- path6 += "/" + (index2 & ~HardenedBit);
207775
+ let path5 = this.path;
207776
+ if (path5) {
207777
+ path5 += "/" + (index2 & ~HardenedBit);
206948
207778
  }
206949
207779
  const data = new Uint8Array(37);
206950
207780
  if (index2 & HardenedBit) {
@@ -206952,8 +207782,8 @@ var init_lib23 = __esm({
206952
207782
  throw new Error("cannot derive child of neutered node");
206953
207783
  }
206954
207784
  data.set(arrayify(this.privateKey), 1);
206955
- if (path6) {
206956
- path6 += "'";
207785
+ if (path5) {
207786
+ path5 += "'";
206957
207787
  }
206958
207788
  } else {
206959
207789
  data.set(arrayify(this.publicKey));
@@ -206972,21 +207802,21 @@ var init_lib23 = __esm({
206972
207802
  const ek = new SigningKey(hexlify(IL));
206973
207803
  Ki = ek._addPoint(this.publicKey);
206974
207804
  }
206975
- let mnemonicOrPath = path6;
207805
+ let mnemonicOrPath = path5;
206976
207806
  const srcMnemonic = this.mnemonic;
206977
207807
  if (srcMnemonic) {
206978
207808
  mnemonicOrPath = Object.freeze({
206979
207809
  phrase: srcMnemonic.phrase,
206980
- path: path6,
207810
+ path: path5,
206981
207811
  locale: srcMnemonic.locale || "en"
206982
207812
  });
206983
207813
  }
206984
207814
  return new _HDNode(_constructorGuard4, ki, Ki, this.fingerprint, bytes32(IR), index2, this.depth + 1, mnemonicOrPath);
206985
207815
  }
206986
- derivePath(path6) {
206987
- const components = path6.split("/");
207816
+ derivePath(path5) {
207817
+ const components = path5.split("/");
206988
207818
  if (components.length === 0 || components[0] === "m" && this.depth !== 0) {
206989
- throw new Error("invalid path - " + path6);
207819
+ throw new Error("invalid path - " + path5);
206990
207820
  }
206991
207821
  if (components[0] === "m") {
206992
207822
  components.shift();
@@ -207721,9 +208551,9 @@ function getPassword(password) {
207721
208551
  }
207722
208552
  return arrayify(password);
207723
208553
  }
207724
- function searchPath(object, path6) {
208554
+ function searchPath(object, path5) {
207725
208555
  let currentChild = object;
207726
- const comps = path6.toLowerCase().split("/");
208556
+ const comps = path5.toLowerCase().split("/");
207727
208557
  for (let i = 0; i < comps.length; i++) {
207728
208558
  let matchingChild = null;
207729
208559
  for (const key2 in currentChild) {
@@ -208372,12 +209202,12 @@ function _getAccount(data, key2) {
208372
209202
  const mnemonicIv = looseArrayify(searchPath(data, "x-ethers/mnemonicCounter"));
208373
209203
  const mnemonicCounter = new import_aes_js2.default.Counter(mnemonicIv);
208374
209204
  const mnemonicAesCtr = new import_aes_js2.default.ModeOfOperation.ctr(mnemonicKey, mnemonicCounter);
208375
- const path6 = searchPath(data, "x-ethers/path") || defaultPath;
209205
+ const path5 = searchPath(data, "x-ethers/path") || defaultPath;
208376
209206
  const locale = searchPath(data, "x-ethers/locale") || "en";
208377
209207
  const entropy = arrayify(mnemonicAesCtr.decrypt(mnemonicCiphertext));
208378
209208
  try {
208379
209209
  const mnemonic = entropyToMnemonic2(entropy, locale);
208380
- const node = HDNode.fromMnemonic(mnemonic, null, locale).derivePath(path6);
209210
+ const node = HDNode.fromMnemonic(mnemonic, null, locale).derivePath(path5);
208381
209211
  if (node.privateKey != account.privateKey) {
208382
209212
  throw new Error("mnemonic mismatch");
208383
209213
  }
@@ -208477,12 +209307,12 @@ function encrypt(account, password, options, progressCallback) {
208477
209307
  const privateKey = arrayify(account.privateKey);
208478
209308
  const passwordBytes = getPassword(password);
208479
209309
  let entropy = null;
208480
- let path6 = null;
209310
+ let path5 = null;
208481
209311
  let locale = null;
208482
209312
  if (hasMnemonic(account)) {
208483
209313
  const srcMnemonic = account.mnemonic;
208484
209314
  entropy = arrayify(mnemonicToEntropy2(srcMnemonic.phrase, srcMnemonic.locale || "en"));
208485
- path6 = srcMnemonic.path || defaultPath;
209315
+ path5 = srcMnemonic.path || defaultPath;
208486
209316
  locale = srcMnemonic.locale || "en";
208487
209317
  }
208488
209318
  let client = options.client;
@@ -208568,7 +209398,7 @@ function encrypt(account, password, options, progressCallback) {
208568
209398
  gethFilename: "UTC--" + timestamp + "--" + data.address,
208569
209399
  mnemonicCounter: hexlify(mnemonicIv).substring(2),
208570
209400
  mnemonicCiphertext: hexlify(mnemonicCiphertext).substring(2),
208571
- path: path6,
209401
+ path: path5,
208572
209402
  locale,
208573
209403
  version: "0.1"
208574
209404
  };
@@ -208862,11 +209692,11 @@ var init_lib26 = __esm({
208862
209692
  static fromEncryptedJsonSync(json, password) {
208863
209693
  return new _Wallet(decryptJsonWalletSync(json, password));
208864
209694
  }
208865
- static fromMnemonic(mnemonic, path6, wordlist3) {
208866
- if (!path6) {
208867
- path6 = defaultPath;
209695
+ static fromMnemonic(mnemonic, path5, wordlist3) {
209696
+ if (!path5) {
209697
+ path5 = defaultPath;
208868
209698
  }
208869
- return new _Wallet(HDNode.fromMnemonic(mnemonic, null, wordlist3).derivePath(path6));
209699
+ return new _Wallet(HDNode.fromMnemonic(mnemonic, null, wordlist3).derivePath(path5));
208870
209700
  }
208871
209701
  };
208872
209702
  }
@@ -214862,21 +215692,21 @@ var init_web3_provider = __esm({
214862
215692
  if (provider == null) {
214863
215693
  logger43.throwArgumentError("missing provider", "provider", provider);
214864
215694
  }
214865
- let path6 = null;
215695
+ let path5 = null;
214866
215696
  let jsonRpcFetchFunc = null;
214867
215697
  let subprovider = null;
214868
215698
  if (typeof provider === "function") {
214869
- path6 = "unknown:";
215699
+ path5 = "unknown:";
214870
215700
  jsonRpcFetchFunc = provider;
214871
215701
  } else {
214872
- path6 = provider.host || provider.path || "";
214873
- if (!path6 && provider.isMetaMask) {
214874
- path6 = "metamask";
215702
+ path5 = provider.host || provider.path || "";
215703
+ if (!path5 && provider.isMetaMask) {
215704
+ path5 = "metamask";
214875
215705
  }
214876
215706
  subprovider = provider;
214877
215707
  if (provider.request) {
214878
- if (path6 === "") {
214879
- path6 = "eip-1193:";
215708
+ if (path5 === "") {
215709
+ path5 = "eip-1193:";
214880
215710
  }
214881
215711
  jsonRpcFetchFunc = buildEip1193Fetcher(provider);
214882
215712
  } else if (provider.sendAsync) {
@@ -214886,11 +215716,11 @@ var init_web3_provider = __esm({
214886
215716
  } else {
214887
215717
  logger43.throwArgumentError("unsupported provider", "provider", provider);
214888
215718
  }
214889
- if (!path6) {
214890
- path6 = "unknown:";
215719
+ if (!path5) {
215720
+ path5 = "unknown:";
214891
215721
  }
214892
215722
  }
214893
- super(path6, network);
215723
+ super(path5, network);
214894
215724
  defineReadOnly(this, "jsonRpcFetchFunc", jsonRpcFetchFunc);
214895
215725
  defineReadOnly(this, "provider", subprovider);
214896
215726
  }
@@ -215728,7 +216558,7 @@ var require_axios = __commonJS({
215728
216558
  var https2 = __require("https");
215729
216559
  var http23 = __require("http2");
215730
216560
  var util5 = __require("util");
215731
- var path6 = __require("path");
216561
+ var path5 = __require("path");
215732
216562
  var followRedirects2 = require_follow_redirects();
215733
216563
  var zlib2 = __require("zlib");
215734
216564
  var stream4 = __require("stream");
@@ -216796,9 +217626,9 @@ var require_axios = __commonJS({
216796
217626
  function removeBrackets2(key2) {
216797
217627
  return utils$1.endsWith(key2, "[]") ? key2.slice(0, -2) : key2;
216798
217628
  }
216799
- function renderKey2(path7, key2, dots) {
216800
- if (!path7) return key2;
216801
- return path7.concat(key2).map(function each(token, i) {
217629
+ function renderKey2(path6, key2, dots) {
217630
+ if (!path6) return key2;
217631
+ return path6.concat(key2).map(function each(token, i) {
216802
217632
  token = removeBrackets2(token);
216803
217633
  return !dots && i ? "[" + token + "]" : token;
216804
217634
  }).join(dots ? "." : "");
@@ -216876,13 +217706,13 @@ var require_axios = __commonJS({
216876
217706
  return currentValue;
216877
217707
  });
216878
217708
  }
216879
- function defaultVisitor(value, key2, path7) {
217709
+ function defaultVisitor(value, key2, path6) {
216880
217710
  let arr = value;
216881
217711
  if (utils$1.isReactNative(formData) && utils$1.isReactNativeBlob(value)) {
216882
- formData.append(renderKey2(path7, key2, dots), convertValue(value));
217712
+ formData.append(renderKey2(path6, key2, dots), convertValue(value));
216883
217713
  return false;
216884
217714
  }
216885
- if (value && !path7 && typeof value === "object") {
217715
+ if (value && !path6 && typeof value === "object") {
216886
217716
  if (utils$1.endsWith(key2, "{}")) {
216887
217717
  key2 = metaTokens ? key2 : key2.slice(0, -2);
216888
217718
  value = stringifyWithDepthLimit(value, 1);
@@ -216901,7 +217731,7 @@ var require_axios = __commonJS({
216901
217731
  if (isVisitable2(value)) {
216902
217732
  return true;
216903
217733
  }
216904
- formData.append(renderKey2(path7, key2, dots), convertValue(value));
217734
+ formData.append(renderKey2(path6, key2, dots), convertValue(value));
216905
217735
  return false;
216906
217736
  }
216907
217737
  const exposedHelpers = Object.assign(predicates2, {
@@ -216909,17 +217739,17 @@ var require_axios = __commonJS({
216909
217739
  convertValue,
216910
217740
  isVisitable: isVisitable2
216911
217741
  });
216912
- function build(value, path7, depth = 0) {
217742
+ function build(value, path6, depth = 0) {
216913
217743
  if (utils$1.isUndefined(value)) return;
216914
217744
  throwIfMaxDepthExceeded(depth);
216915
217745
  if (stack.indexOf(value) !== -1) {
216916
- throw new Error("Circular reference detected in " + path7.join("."));
217746
+ throw new Error("Circular reference detected in " + path6.join("."));
216917
217747
  }
216918
217748
  stack.push(value);
216919
217749
  utils$1.forEach(value, function each(el, key2) {
216920
- const result = !(utils$1.isUndefined(el) || el === null) && visitor.call(formData, el, utils$1.isString(key2) ? key2.trim() : key2, path7, exposedHelpers);
217750
+ const result = !(utils$1.isUndefined(el) || el === null) && visitor.call(formData, el, utils$1.isString(key2) ? key2.trim() : key2, path6, exposedHelpers);
216921
217751
  if (result === true) {
216922
- build(el, path7 ? path7.concat(key2) : [key2], depth + 1);
217752
+ build(el, path6 ? path6.concat(key2) : [key2], depth + 1);
216923
217753
  }
216924
217754
  });
216925
217755
  stack.pop();
@@ -217108,7 +217938,7 @@ var require_axios = __commonJS({
217108
217938
  };
217109
217939
  function toURLEncodedForm2(data, options) {
217110
217940
  return toFormData3(data, new platform2.classes.URLSearchParams(), {
217111
- visitor: function(value, key2, path7, helpers) {
217941
+ visitor: function(value, key2, path6, helpers) {
217112
217942
  if (platform2.isNode && utils$1.isBuffer(value)) {
217113
217943
  this.append(key2, value.toString("base64"));
217114
217944
  return false;
@@ -217125,14 +217955,14 @@ var require_axios = __commonJS({
217125
217955
  }
217126
217956
  }
217127
217957
  function parsePropPath2(name) {
217128
- const path7 = [];
217958
+ const path6 = [];
217129
217959
  const pattern = /[^.[\]]+|\[([^.[\]]*)]/g;
217130
217960
  let match;
217131
217961
  while ((match = pattern.exec(name)) !== null) {
217132
- throwIfDepthExceeded2(path7.length);
217133
- path7.push(match[0] === "[]" ? "" : match[1] || match[0]);
217962
+ throwIfDepthExceeded2(path6.length);
217963
+ path6.push(match[0] === "[]" ? "" : match[1] || match[0]);
217134
217964
  }
217135
- return path7;
217965
+ return path6;
217136
217966
  }
217137
217967
  function arrayToObject2(arr) {
217138
217968
  const obj = {};
@@ -217147,12 +217977,12 @@ var require_axios = __commonJS({
217147
217977
  return obj;
217148
217978
  }
217149
217979
  function formDataToJSON2(formData) {
217150
- function buildPath(path7, value, target, index2) {
217980
+ function buildPath(path6, value, target, index2) {
217151
217981
  throwIfDepthExceeded2(index2);
217152
- let name = path7[index2++];
217982
+ let name = path6[index2++];
217153
217983
  if (name === "__proto__") return true;
217154
217984
  const isNumericKey = Number.isFinite(+name);
217155
- const isLast = index2 >= path7.length;
217985
+ const isLast = index2 >= path6.length;
217156
217986
  name = !name && utils$1.isArray(target) ? target.length : name;
217157
217987
  if (isLast) {
217158
217988
  if (utils$1.hasOwnProp(target, name)) {
@@ -217165,7 +217995,7 @@ var require_axios = __commonJS({
217165
217995
  if (!utils$1.hasOwnProp(target, name) || !utils$1.isObject(target[name])) {
217166
217996
  target[name] = [];
217167
217997
  }
217168
- const result = buildPath(path7, value, target[name], index2);
217998
+ const result = buildPath(path6, value, target[name], index2);
217169
217999
  if (result && utils$1.isArray(target[name])) {
217170
218000
  target[name] = arrayToObject2(target[name]);
217171
218001
  }
@@ -218776,8 +219606,8 @@ var require_axios = __commonJS({
218776
219606
  const allowedSocketPaths = own3("allowedSocketPaths");
218777
219607
  if (allowedSocketPaths != null) {
218778
219608
  const allowed = Array.isArray(allowedSocketPaths) ? allowedSocketPaths : [allowedSocketPaths];
218779
- const resolvedSocket = path6.resolve(socketPath);
218780
- const isAllowed = allowed.some((entry) => typeof entry === "string" && path6.resolve(entry) === resolvedSocket);
219609
+ const resolvedSocket = path5.resolve(socketPath);
219610
+ const isAllowed = allowed.some((entry) => typeof entry === "string" && path5.resolve(entry) === resolvedSocket);
218781
219611
  if (!isAllowed) {
218782
219612
  return reject(new AxiosError3(`socketPath "${socketPath}" is not permitted by allowedSocketPaths`, AxiosError3.ERR_BAD_OPTION_VALUE, config));
218783
219613
  }
@@ -219076,14 +219906,14 @@ var require_axios = __commonJS({
219076
219906
  var cookies = platform2.hasStandardBrowserEnv ? (
219077
219907
  // Standard browser envs support document.cookie
219078
219908
  {
219079
- write(name, value, expires, path7, domain, secure, sameSite) {
219909
+ write(name, value, expires, path6, domain, secure, sameSite) {
219080
219910
  if (typeof document === "undefined") return;
219081
219911
  const cookie = [`${name}=${encodeURIComponent(value)}`];
219082
219912
  if (utils$1.isNumber(expires)) {
219083
219913
  cookie.push(`expires=${new Date(expires).toUTCString()}`);
219084
219914
  }
219085
- if (utils$1.isString(path7)) {
219086
- cookie.push(`path=${path7}`);
219915
+ if (utils$1.isString(path6)) {
219916
+ cookie.push(`path=${path6}`);
219087
219917
  }
219088
219918
  if (utils$1.isString(domain)) {
219089
219919
  cookie.push(`domain=${domain}`);
@@ -223572,18 +224402,18 @@ var require_client2 = __commonJS({
223572
224402
  }
223573
224403
  console.log(`Transaction not found or not in given states, timing out!`);
223574
224404
  }
223575
- async sendAuthedRequest(method, path6, body) {
224405
+ async sendAuthedRequest(method, path5, body) {
223576
224406
  if (this.canBuilderAuth()) {
223577
- const builderHeaders = await this._generateBuilderHeaders(method, path6, body);
224407
+ const builderHeaders = await this._generateBuilderHeaders(method, path5, body);
223578
224408
  if (builderHeaders !== void 0) {
223579
- return this.send(path6, method, { headers: builderHeaders, data: body });
224409
+ return this.send(path5, method, { headers: builderHeaders, data: body });
223580
224410
  }
223581
224411
  }
223582
- return this.send(path6, method, { data: body });
224412
+ return this.send(path5, method, { data: body });
223583
224413
  }
223584
- async _generateBuilderHeaders(method, path6, body) {
224414
+ async _generateBuilderHeaders(method, path5, body) {
223585
224415
  if (this.builderConfig !== void 0) {
223586
- const builderHeaders = await this.builderConfig.generateBuilderHeaders(method, path6, body);
224416
+ const builderHeaders = await this.builderConfig.generateBuilderHeaders(method, path5, body);
223587
224417
  if (builderHeaders == void 0) {
223588
224418
  return void 0;
223589
224419
  }
@@ -223674,12 +224504,12 @@ var require_signer = __commonJS({
223674
224504
  constructor(creds) {
223675
224505
  this.creds = creds;
223676
224506
  }
223677
- createBuilderHeaderPayload(method, path6, body, timestamp) {
224507
+ createBuilderHeaderPayload(method, path5, body, timestamp) {
223678
224508
  let ts = Math.floor(Date.now() / 1e3);
223679
224509
  if (timestamp !== void 0) {
223680
224510
  ts = timestamp;
223681
224511
  }
223682
- const builderSig = (0, signing_1.buildHmacSignature)(this.creds.secret, ts, method, path6, body);
224512
+ const builderSig = (0, signing_1.buildHmacSignature)(this.creds.secret, ts, method, path5, body);
223683
224513
  return {
223684
224514
  POLY_BUILDER_API_KEY: this.creds.key,
223685
224515
  POLY_BUILDER_PASSPHRASE: this.creds.passphrase,
@@ -223768,17 +224598,17 @@ var require_config2 = __commonJS({
223768
224598
  * @param path
223769
224599
  * @param body
223770
224600
  */
223771
- async generateBuilderHeaders(method, path6, body, timestamp) {
224601
+ async generateBuilderHeaders(method, path5, body, timestamp) {
223772
224602
  this.ensureValid();
223773
224603
  const builderType = this.getBuilderType();
223774
224604
  if (builderType == types_1.BuilderType.LOCAL) {
223775
- return Promise.resolve(this.signer?.createBuilderHeaderPayload(method, path6, body, timestamp));
224605
+ return Promise.resolve(this.signer?.createBuilderHeaderPayload(method, path5, body, timestamp));
223776
224606
  }
223777
224607
  if (builderType == types_1.BuilderType.REMOTE) {
223778
224608
  const url2 = this.remoteBuilderConfig.url;
223779
224609
  const payload = {
223780
224610
  method,
223781
- path: path6,
224611
+ path: path5,
223782
224612
  body,
223783
224613
  timestamp
223784
224614
  };
@@ -224560,11 +225390,11 @@ var init_withdraw = __esm({
224560
225390
  });
224561
225391
 
224562
225392
  // node_modules/@blockrun/llm/dist/index.js
224563
- import * as path2 from "path";
225393
+ import * as path3 from "path";
224564
225394
  import * as os2 from "os";
224565
225395
  import * as path22 from "path";
224566
225396
  import * as os22 from "os";
224567
- import * as path3 from "path";
225397
+ import * as path32 from "path";
224568
225398
  import * as os3 from "os";
224569
225399
  import * as path4 from "path";
224570
225400
  import * as os4 from "os";
@@ -224738,7 +225568,7 @@ function resolveDefaultTimeout() {
224738
225568
  function sleep2(ms) {
224739
225569
  return new Promise((r2) => setTimeout(r2, ms));
224740
225570
  }
224741
- var BlockrunError, PaymentError, APIError, BASE_CHAIN_ID2, USDC_BASE2, USDC_DOMAIN, TRANSFER_TYPES, BLOCKRUN_SERVICE_CODE2, LOCALHOST_DOMAINS, BLOCKRUN_DIR2, COST_LOG_FILE, SDK_VERSION, USER_AGENT2, DEFAULT_TIMEOUT, PHONE_PRICES, DEFAULT_API_URL13, DEFAULT_TIMEOUT13, DEFAULT_POLL_INTERVAL_MS, DEFAULT_POLL_BUDGET_MS, MAX_SIGNED_AUTH_SECONDS, BlockrunClient, WALLET_DIR2, WALLET_FILE3, WALLET_DIR22, SOLANA_WALLET_FILE, CACHE_DIR, DATA_DIR, COST_LOG_FILE2, DEFAULT_TTL;
225571
+ var BlockrunError, PaymentError, APIError, BASE_CHAIN_ID2, USDC_BASE2, USDC_DOMAIN, TRANSFER_TYPES, BLOCKRUN_SERVICE_CODE2, LOCALHOST_DOMAINS, BLOCKRUN_DIR2, COST_LOG_FILE, SDK_VERSION, USER_AGENT2, DEFAULT_TIMEOUT, PHONE_PRICES, DEFAULT_API_URL13, DEFAULT_TIMEOUT13, DEFAULT_POLL_INTERVAL_MS, DEFAULT_POLL_BUDGET_MS, MAX_SIGNED_AUTH_SECONDS, BlockrunClient, WALLET_DIR3, WALLET_FILE3, WALLET_DIR22, SOLANA_WALLET_FILE, CACHE_DIR, DATA_DIR, COST_LOG_FILE2, DEFAULT_TTL;
224742
225572
  var init_dist7 = __esm({
224743
225573
  "node_modules/@blockrun/llm/dist/index.js"() {
224744
225574
  "use strict";
@@ -224786,8 +225616,8 @@ var init_dist7 = __esm({
224786
225616
  };
224787
225617
  BLOCKRUN_SERVICE_CODE2 = "blockrun";
224788
225618
  LOCALHOST_DOMAINS = ["localhost", "127.0.0.1"];
224789
- BLOCKRUN_DIR2 = path2.join(os2.homedir(), ".blockrun");
224790
- COST_LOG_FILE = path2.join(BLOCKRUN_DIR2, "cost_log.jsonl");
225619
+ BLOCKRUN_DIR2 = path3.join(os2.homedir(), ".blockrun");
225620
+ COST_LOG_FILE = path3.join(BLOCKRUN_DIR2, "cost_log.jsonl");
224791
225621
  SDK_VERSION = "3.10.0";
224792
225622
  USER_AGENT2 = `blockrun-ts/${SDK_VERSION}`;
224793
225623
  DEFAULT_TIMEOUT = resolveDefaultTimeout();
@@ -224832,15 +225662,15 @@ var init_dist7 = __esm({
224832
225662
  * `/api` is tolerated and stripped). Query params are URL-encoded; arrays
224833
225663
  * become repeated keys (`?a=1&a=2`); undefined/null are dropped.
224834
225664
  */
224835
- async get(path52, params) {
224836
- const url2 = this.buildUrl(path52, params);
225665
+ async get(path5, params) {
225666
+ const url2 = this.buildUrl(path5, params);
224837
225667
  return this.requestWithPayment(url2, "GET");
224838
225668
  }
224839
225669
  /**
224840
225670
  * POST a BlockRun endpoint with a JSON body.
224841
225671
  */
224842
- async post(path52, body) {
224843
- const url2 = this.buildUrl(path52);
225672
+ async post(path5, body) {
225673
+ const url2 = this.buildUrl(path5);
224844
225674
  return this.requestWithPayment(url2, "POST", body);
224845
225675
  }
224846
225676
  /**
@@ -224855,8 +225685,8 @@ var init_dist7 = __esm({
224855
225685
  * short-circuits and returns the body. Most long-running endpoints (image,
224856
225686
  * video, music, voice) return 202 with a poll_url.
224857
225687
  */
224858
- async poll(path52, body, options) {
224859
- const submitUrl = this.buildUrl(path52);
225688
+ async poll(path5, body, options) {
225689
+ const submitUrl = this.buildUrl(path5);
224860
225690
  const budgetMs = options?.budgetMs ?? DEFAULT_POLL_BUDGET_MS;
224861
225691
  const intervalMs = options?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
224862
225692
  const resp402 = await this.fetchWithTimeout(submitUrl, {
@@ -224955,8 +225785,8 @@ var init_dist7 = __esm({
224955
225785
  * process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
224956
225786
  * }
224957
225787
  */
224958
- async *stream(path52, body) {
224959
- const url2 = this.buildUrl(path52);
225788
+ async *stream(path5, body) {
225789
+ const url2 = this.buildUrl(path5);
224960
225790
  const requestBody = JSON.stringify(body ?? {});
224961
225791
  const resp402 = await this.fetchWithTimeout(url2, {
224962
225792
  method: "POST",
@@ -225022,8 +225852,8 @@ var init_dist7 = __esm({
225022
225852
  // --------------------------------------------------------------------
225023
225853
  // Internal: shared infrastructure
225024
225854
  // --------------------------------------------------------------------
225025
- buildUrl(path52, params) {
225026
- let normalized = path52.startsWith("/") ? path52 : `/${path52}`;
225855
+ buildUrl(path5, params) {
225856
+ let normalized = path5.startsWith("/") ? path5 : `/${path5}`;
225027
225857
  if (normalized.startsWith("/api/")) {
225028
225858
  normalized = normalized.slice(4);
225029
225859
  }
@@ -225171,10 +226001,10 @@ var init_dist7 = __esm({
225171
226001
  return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
225172
226002
  }
225173
226003
  };
225174
- WALLET_DIR2 = path22.join(os22.homedir(), ".blockrun");
225175
- WALLET_FILE3 = path22.join(WALLET_DIR2, ".session");
225176
- WALLET_DIR22 = path3.join(os3.homedir(), ".blockrun");
225177
- SOLANA_WALLET_FILE = path3.join(WALLET_DIR22, ".solana-session");
226004
+ WALLET_DIR3 = path22.join(os22.homedir(), ".blockrun");
226005
+ WALLET_FILE3 = path22.join(WALLET_DIR3, ".session");
226006
+ WALLET_DIR22 = path32.join(os3.homedir(), ".blockrun");
226007
+ SOLANA_WALLET_FILE = path32.join(WALLET_DIR22, ".solana-session");
225178
226008
  CACHE_DIR = path4.join(os4.homedir(), ".blockrun", "cache");
225179
226009
  DATA_DIR = path4.join(os4.homedir(), ".blockrun", "data");
225180
226010
  COST_LOG_FILE2 = path4.join(os4.homedir(), ".blockrun", "cost_log.jsonl");
@@ -225670,261 +226500,6 @@ var init_mcp_config = __esm({
225670
226500
  }
225671
226501
  });
225672
226502
 
225673
- // src/spend-control.ts
225674
- import * as fs3 from "fs";
225675
- import * as path5 from "path";
225676
- import { homedir as homedir12 } from "os";
225677
- function formatDuration(seconds) {
225678
- if (seconds < 60) {
225679
- return `${seconds}s`;
225680
- } else if (seconds < 3600) {
225681
- const mins = Math.ceil(seconds / 60);
225682
- return `${mins} min`;
225683
- } else {
225684
- const hours = Math.floor(seconds / 3600);
225685
- const mins = Math.ceil(seconds % 3600 / 60);
225686
- return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
225687
- }
225688
- }
225689
- var WALLET_DIR3, HOUR_MS, DAY_MS, FileSpendControlStorage, InMemorySpendControlStorage, SpendControl;
225690
- var init_spend_control = __esm({
225691
- "src/spend-control.ts"() {
225692
- "use strict";
225693
- init_fs_read();
225694
- WALLET_DIR3 = path5.join(homedir12(), ".openclaw", "blockrun");
225695
- HOUR_MS = 60 * 60 * 1e3;
225696
- DAY_MS = 24 * HOUR_MS;
225697
- FileSpendControlStorage = class {
225698
- spendingFile;
225699
- constructor() {
225700
- this.spendingFile = path5.join(WALLET_DIR3, "spending.json");
225701
- }
225702
- load() {
225703
- try {
225704
- if (fs3.existsSync(this.spendingFile)) {
225705
- const data = JSON.parse(readTextFileSync(this.spendingFile));
225706
- const rawLimits = data.limits ?? {};
225707
- const rawHistory = data.history ?? [];
225708
- const limits = {};
225709
- for (const key2 of ["perRequest", "hourly", "daily", "session"]) {
225710
- const val = rawLimits[key2];
225711
- if (typeof val === "number" && val > 0 && Number.isFinite(val)) {
225712
- limits[key2] = val;
225713
- }
225714
- }
225715
- const history = [];
225716
- if (Array.isArray(rawHistory)) {
225717
- for (const r2 of rawHistory) {
225718
- if (typeof r2?.timestamp === "number" && typeof r2?.amount === "number" && Number.isFinite(r2.timestamp) && Number.isFinite(r2.amount) && r2.amount >= 0) {
225719
- history.push({
225720
- timestamp: r2.timestamp,
225721
- amount: r2.amount,
225722
- model: typeof r2.model === "string" ? r2.model : void 0,
225723
- action: typeof r2.action === "string" ? r2.action : void 0
225724
- });
225725
- }
225726
- }
225727
- }
225728
- return { limits, history };
225729
- }
225730
- } catch (err) {
225731
- console.error(`[ClawRouter] Failed to load spending data, starting fresh: ${err}`);
225732
- }
225733
- return null;
225734
- }
225735
- save(data) {
225736
- try {
225737
- if (!fs3.existsSync(WALLET_DIR3)) {
225738
- fs3.mkdirSync(WALLET_DIR3, { recursive: true, mode: 448 });
225739
- }
225740
- fs3.writeFileSync(this.spendingFile, JSON.stringify(data, null, 2), {
225741
- mode: 384
225742
- });
225743
- } catch (err) {
225744
- console.error(`[ClawRouter] Failed to save spending data: ${err}`);
225745
- }
225746
- }
225747
- };
225748
- InMemorySpendControlStorage = class {
225749
- data = null;
225750
- load() {
225751
- return this.data ? {
225752
- limits: { ...this.data.limits },
225753
- history: this.data.history.map((r2) => ({ ...r2 }))
225754
- } : null;
225755
- }
225756
- save(data) {
225757
- this.data = {
225758
- limits: { ...data.limits },
225759
- history: data.history.map((r2) => ({ ...r2 }))
225760
- };
225761
- }
225762
- };
225763
- SpendControl = class {
225764
- limits = {};
225765
- history = [];
225766
- sessionSpent = 0;
225767
- sessionCalls = 0;
225768
- storage;
225769
- now;
225770
- constructor(options) {
225771
- this.storage = options?.storage ?? new FileSpendControlStorage();
225772
- this.now = options?.now ?? (() => Date.now());
225773
- this.load();
225774
- }
225775
- setLimit(window2, amount) {
225776
- if (!Number.isFinite(amount) || amount <= 0) {
225777
- throw new Error("Limit must be a finite positive number");
225778
- }
225779
- this.limits[window2] = amount;
225780
- this.save();
225781
- }
225782
- clearLimit(window2) {
225783
- delete this.limits[window2];
225784
- this.save();
225785
- }
225786
- getLimits() {
225787
- return { ...this.limits };
225788
- }
225789
- check(estimatedCost2) {
225790
- const now2 = this.now();
225791
- if (this.limits.perRequest !== void 0) {
225792
- if (estimatedCost2 > this.limits.perRequest) {
225793
- return {
225794
- allowed: false,
225795
- blockedBy: "perRequest",
225796
- remaining: this.limits.perRequest,
225797
- reason: `Per-request limit exceeded: $${estimatedCost2.toFixed(4)} > $${this.limits.perRequest.toFixed(2)} max`
225798
- };
225799
- }
225800
- }
225801
- if (this.limits.hourly !== void 0) {
225802
- const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
225803
- const remaining = this.limits.hourly - hourlySpent;
225804
- if (estimatedCost2 > remaining) {
225805
- const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - HOUR_MS);
225806
- const resetIn = oldestInWindow ? Math.ceil((oldestInWindow.timestamp + HOUR_MS - now2) / 1e3) : 0;
225807
- return {
225808
- allowed: false,
225809
- blockedBy: "hourly",
225810
- remaining,
225811
- reason: `Hourly limit exceeded: $${(hourlySpent + estimatedCost2).toFixed(2)} > $${this.limits.hourly.toFixed(2)} max`,
225812
- resetIn
225813
- };
225814
- }
225815
- }
225816
- if (this.limits.daily !== void 0) {
225817
- const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
225818
- const remaining = this.limits.daily - dailySpent;
225819
- if (estimatedCost2 > remaining) {
225820
- const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - DAY_MS);
225821
- const resetIn = oldestInWindow ? Math.ceil((oldestInWindow.timestamp + DAY_MS - now2) / 1e3) : 0;
225822
- return {
225823
- allowed: false,
225824
- blockedBy: "daily",
225825
- remaining,
225826
- reason: `Daily limit exceeded: $${(dailySpent + estimatedCost2).toFixed(2)} > $${this.limits.daily.toFixed(2)} max`,
225827
- resetIn
225828
- };
225829
- }
225830
- }
225831
- if (this.limits.session !== void 0) {
225832
- const remaining = this.limits.session - this.sessionSpent;
225833
- if (estimatedCost2 > remaining) {
225834
- return {
225835
- allowed: false,
225836
- blockedBy: "session",
225837
- remaining,
225838
- reason: `Session limit exceeded: $${(this.sessionSpent + estimatedCost2).toFixed(2)} > $${this.limits.session.toFixed(2)} max`
225839
- };
225840
- }
225841
- }
225842
- return { allowed: true };
225843
- }
225844
- record(amount, metadata) {
225845
- if (!Number.isFinite(amount) || amount < 0) {
225846
- throw new Error("Record amount must be a non-negative finite number");
225847
- }
225848
- const record = {
225849
- timestamp: this.now(),
225850
- amount,
225851
- model: metadata?.model,
225852
- action: metadata?.action
225853
- };
225854
- this.history.push(record);
225855
- this.sessionSpent += amount;
225856
- this.sessionCalls += 1;
225857
- this.cleanup();
225858
- this.save();
225859
- }
225860
- getSpendingInWindow(from15, to) {
225861
- return this.history.filter((r2) => r2.timestamp >= from15 && r2.timestamp <= to).reduce((sum, r2) => sum + r2.amount, 0);
225862
- }
225863
- getSpending(window2) {
225864
- const now2 = this.now();
225865
- switch (window2) {
225866
- case "hourly":
225867
- return this.getSpendingInWindow(now2 - HOUR_MS, now2);
225868
- case "daily":
225869
- return this.getSpendingInWindow(now2 - DAY_MS, now2);
225870
- case "session":
225871
- return this.sessionSpent;
225872
- }
225873
- }
225874
- getRemaining(window2) {
225875
- const limit = this.limits[window2];
225876
- if (limit === void 0) return null;
225877
- return Math.max(0, limit - this.getSpending(window2));
225878
- }
225879
- getStatus() {
225880
- const now2 = this.now();
225881
- const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
225882
- const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
225883
- return {
225884
- limits: { ...this.limits },
225885
- spending: {
225886
- hourly: hourlySpent,
225887
- daily: dailySpent,
225888
- session: this.sessionSpent
225889
- },
225890
- remaining: {
225891
- hourly: this.limits.hourly !== void 0 ? this.limits.hourly - hourlySpent : null,
225892
- daily: this.limits.daily !== void 0 ? this.limits.daily - dailySpent : null,
225893
- session: this.limits.session !== void 0 ? this.limits.session - this.sessionSpent : null
225894
- },
225895
- calls: this.sessionCalls
225896
- };
225897
- }
225898
- getHistory(limit) {
225899
- const records = [...this.history].reverse();
225900
- return limit ? records.slice(0, limit) : records;
225901
- }
225902
- resetSession() {
225903
- this.sessionSpent = 0;
225904
- this.sessionCalls = 0;
225905
- }
225906
- cleanup() {
225907
- const cutoff = this.now() - DAY_MS;
225908
- this.history = this.history.filter((r2) => r2.timestamp >= cutoff);
225909
- }
225910
- save() {
225911
- this.storage.save({
225912
- limits: { ...this.limits },
225913
- history: [...this.history]
225914
- });
225915
- }
225916
- load() {
225917
- const data = this.storage.load();
225918
- if (data) {
225919
- this.limits = data.limits;
225920
- this.history = data.history;
225921
- this.cleanup();
225922
- }
225923
- }
225924
- };
225925
- }
225926
- });
225927
-
225928
226503
  // src/retry.ts
225929
226504
  function sleep3(ms) {
225930
226505
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -225993,6 +226568,8 @@ __export(index_exports, {
225993
226568
  BALANCE_THRESHOLDS: () => BALANCE_THRESHOLDS,
225994
226569
  BLOCKRUN_MODELS: () => BLOCKRUN_MODELS,
225995
226570
  BalanceMonitor: () => BalanceMonitor,
226571
+ CAIP2_BASE: () => CAIP2_BASE,
226572
+ CAIP2_SOLANA_MAINNET: () => CAIP2_SOLANA_MAINNET,
225996
226573
  DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
225997
226574
  DEFAULT_ROUTING_CONFIG: () => DEFAULT_ROUTING_CONFIG,
225998
226575
  DEFAULT_SESSION_CONFIG: () => DEFAULT_SESSION_CONFIG,
@@ -226001,6 +226578,7 @@ __export(index_exports, {
226001
226578
  InMemorySpendControlStorage: () => InMemorySpendControlStorage,
226002
226579
  InsufficientFundsError: () => InsufficientFundsError2,
226003
226580
  MODEL_ALIASES: () => MODEL_ALIASES,
226581
+ MalformedSpendPolicyError: () => MalformedSpendPolicyError,
226004
226582
  OPENCLAW_MODELS: () => OPENCLAW_MODELS,
226005
226583
  PARTNER_SERVICES: () => PARTNER_SERVICES,
226006
226584
  RequestDeduplicator: () => RequestDeduplicator,
@@ -226009,6 +226587,7 @@ __export(index_exports, {
226009
226587
  SessionStore: () => SessionStore,
226010
226588
  SolanaBalanceMonitor: () => SolanaBalanceMonitor,
226011
226589
  SpendControl: () => SpendControl,
226590
+ SpendPolicyError: () => SpendPolicyError,
226012
226591
  VISIBLE_OPENCLAW_MODELS: () => VISIBLE_OPENCLAW_MODELS,
226013
226592
  blockrunProvider: () => blockrunProvider,
226014
226593
  buildImageGenerationProvider: () => buildImageGenerationProvider,
@@ -226048,6 +226627,7 @@ __export(index_exports, {
226048
226627
  loadPaymentChain: () => loadPaymentChain,
226049
226628
  logUsage: () => logUsage,
226050
226629
  parseCallArgs: () => parseCallArgs,
226630
+ registerSpendPolicyHook: () => registerSpendPolicyHook,
226051
226631
  resolveModelAlias: () => resolveModelAlias,
226052
226632
  resolvePaymentChain: () => resolvePaymentChain,
226053
226633
  route: () => route,
@@ -226062,7 +226642,7 @@ import {
226062
226642
  readdirSync,
226063
226643
  mkdirSync as mkdirSync3,
226064
226644
  copyFileSync,
226065
- renameSync
226645
+ renameSync as renameSync2
226066
226646
  } from "fs";
226067
226647
  import { readFile as readFileAsync } from "fs/promises";
226068
226648
  import { homedir as homedir13 } from "os";
@@ -226340,7 +226920,7 @@ function injectModelsConfig(logger48, options = {}) {
226340
226920
  try {
226341
226921
  const tmpPath = `${configPath}.tmp.${process.pid}`;
226342
226922
  writeFileSync3(tmpPath, JSON.stringify(config, null, 2));
226343
- renameSync(tmpPath, configPath);
226923
+ renameSync2(tmpPath, configPath);
226344
226924
  logger48.info("Smart routing enabled (blockrun/auto)");
226345
226925
  } catch (err) {
226346
226926
  logger48.info(`Failed to write config: ${err instanceof Error ? err.message : String(err)}`);
@@ -226376,7 +226956,7 @@ function syncAgentModelCache(logger48, options = {}) {
226376
226956
  entry.models = VISIBLE_OPENCLAW_MODELS;
226377
226957
  const tmpPath = `${cachePath}.tmp.${process.pid}`;
226378
226958
  writeFileSync3(tmpPath, JSON.stringify(cache2, null, 2));
226379
- renameSync(tmpPath, cachePath);
226959
+ renameSync2(tmpPath, cachePath);
226380
226960
  logger48.info(
226381
226961
  `Repaired ${agent} model cache: ${staleCount} \u2192 ${expectedIds.length} BlockRun models`
226382
226962
  );
@@ -227740,7 +228320,7 @@ ${errText}`
227740
228320
  }
227741
228321
  const tmpPath = `${configPath}.tmp.${process.pid}`;
227742
228322
  writeFileSync3(tmpPath, JSON.stringify(config, null, 2));
227743
- renameSync(tmpPath, configPath);
228323
+ renameSync2(tmpPath, configPath);
227744
228324
  api.logger.info("ClawRouter config cleaned up");
227745
228325
  }
227746
228326
  } catch (err) {
@@ -227835,7 +228415,7 @@ init_stats();
227835
228415
  init_proxy();
227836
228416
  init_version4();
227837
228417
  import { platform, arch, freemem, totalmem } from "os";
227838
- import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
228418
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
227839
228419
  function formatBytes(bytes) {
227840
228420
  const gb = bytes / (1024 * 1024 * 1024);
227841
228421
  return `${gb.toFixed(1)}GB`;
@@ -228124,7 +228704,7 @@ async function analyzeWithAI(diagnostics, userQuestion, model = "sonnet") {
228124
228704
  const paymentChain = diagnostics.wallet.paymentChain;
228125
228705
  if (paymentChain === "solana") {
228126
228706
  try {
228127
- if (!existsSync2(MNEMONIC_FILE)) {
228707
+ if (!existsSync3(MNEMONIC_FILE)) {
228128
228708
  throw new Error(`mnemonic file missing at ${MNEMONIC_FILE}`);
228129
228709
  }
228130
228710
  const mnemonic = readFileSync3(MNEMONIC_FILE, "utf8").trim();
@@ -228287,8 +228867,8 @@ Environment Variables:
228287
228867
  For more info: https://blockrun.ai/clawrouter.md
228288
228868
  `);
228289
228869
  }
228290
- async function queryProxy(path6, port) {
228291
- const res = await fetch(`http://127.0.0.1:${port}${path6}`);
228870
+ async function queryProxy(path5, port) {
228871
+ const res = await fetch(`http://127.0.0.1:${port}${path5}`);
228292
228872
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
228293
228873
  return res.json();
228294
228874
  }
@@ -228368,8 +228948,8 @@ Use: clawrouter share <id> --as=<feishu|slack|discord|telegram|whatsapp|plain>
228368
228948
  const { join: pjoin } = await import("path");
228369
228949
  const paths = [];
228370
228950
  for (const p of VALID_PRESETS) {
228371
- const path6 = idOrLast === "last" ? `/share/last?as=${p}` : `/share/${idOrLast}/render?as=${p}`;
228372
- const result = await queryProxy(path6, port);
228951
+ const path5 = idOrLast === "last" ? `/share/last?as=${p}` : `/share/${idOrLast}/render?as=${p}`;
228952
+ const result = await queryProxy(path5, port);
228373
228953
  const file = pjoin(tmpdir(), `claw-share-${result.id}-${p}.txt`);
228374
228954
  await writeFile3(file, result.rendered, "utf8");
228375
228955
  paths.push(file);
@@ -228390,8 +228970,8 @@ Use: clawrouter share <id> --as=<feishu|slack|discord|telegram|whatsapp|plain>
228390
228970
  process.exit(1);
228391
228971
  }
228392
228972
  try {
228393
- const path6 = idOrLast === "last" ? `/share/last?as=${chosenPreset}` : `/share/${idOrLast}/render?as=${chosenPreset}`;
228394
- const result = await queryProxy(path6, port);
228973
+ const path5 = idOrLast === "last" ? `/share/last?as=${chosenPreset}` : `/share/${idOrLast}/render?as=${chosenPreset}`;
228974
+ const result = await queryProxy(path5, port);
228395
228975
  process.stdout.write(result.rendered);
228396
228976
  if (!result.rendered.endsWith("\n")) process.stdout.write("\n");
228397
228977
  const clipErr = await copyToClipboard(result.rendered);
@@ -228511,8 +229091,8 @@ Usage Stats (last ${days} days)
228511
229091
  }
228512
229092
  async function cmdPhone(port, subcommand, numbersAction, arg, areaCode) {
228513
229093
  const base3 = `http://127.0.0.1:${port}`;
228514
- async function postJson(path6, body) {
228515
- const resp = await fetch(`${base3}${path6}`, {
229094
+ async function postJson(path5, body) {
229095
+ const resp = await fetch(`${base3}${path5}`, {
228516
229096
  method: "POST",
228517
229097
  headers: { "content-type": "application/json" },
228518
229098
  body: JSON.stringify(body)
@@ -228614,12 +229194,12 @@ Releasing ${arg}...
228614
229194
  console.error(`\u2717 Usage: clawrouter phone ${subcommand} <+E.164-number>`);
228615
229195
  process.exit(1);
228616
229196
  }
228617
- const path6 = subcommand === "fraud" ? "/v1/phone/lookup/fraud" : "/v1/phone/lookup";
229197
+ const path5 = subcommand === "fraud" ? "/v1/phone/lookup/fraud" : "/v1/phone/lookup";
228618
229198
  const price = subcommand === "fraud" ? "$0.05" : "$0.01";
228619
229199
  console.log(`
228620
229200
  Looking up ${arg}... (${price})
228621
229201
  `);
228622
- const result = await postJson(path6, { phoneNumber: arg });
229202
+ const result = await postJson(path5, { phoneNumber: arg });
228623
229203
  console.log(JSON.stringify(result, null, 2));
228624
229204
  console.log();
228625
229205
  }