@blockrun/clawrouter 0.8.10 → 0.8.12

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
@@ -1791,6 +1791,12 @@ async function getStats(days = 7) {
1791
1791
  }
1792
1792
  const totalSavings = totalBaselineCost - totalCost;
1793
1793
  const savingsPercentage = totalBaselineCost > 0 ? totalSavings / totalBaselineCost * 100 : 0;
1794
+ let entriesWithBaseline = 0;
1795
+ for (const day of dailyBreakdown) {
1796
+ if (day.totalBaselineCost !== day.totalCost) {
1797
+ entriesWithBaseline += day.totalRequests;
1798
+ }
1799
+ }
1794
1800
  return {
1795
1801
  period: days === 1 ? "today" : `last ${days} days`,
1796
1802
  totalRequests,
@@ -1802,8 +1808,10 @@ async function getStats(days = 7) {
1802
1808
  avgCostPerRequest: totalRequests > 0 ? totalCost / totalRequests : 0,
1803
1809
  byTier: byTierWithPercentage,
1804
1810
  byModel: byModelWithPercentage,
1805
- dailyBreakdown: dailyBreakdown.reverse()
1811
+ dailyBreakdown: dailyBreakdown.reverse(),
1806
1812
  // Oldest first for charts
1813
+ entriesWithBaseline
1814
+ // How many entries have valid baseline tracking
1807
1815
  };
1808
1816
  }
1809
1817
 
@@ -2215,6 +2223,42 @@ var HEALTH_CHECK_TIMEOUT_MS = 2e3;
2215
2223
  var RATE_LIMIT_COOLDOWN_MS = 6e4;
2216
2224
  var PORT_RETRY_ATTEMPTS = 5;
2217
2225
  var PORT_RETRY_DELAY_MS = 1e3;
2226
+ function transformPaymentError(errorBody) {
2227
+ try {
2228
+ const parsed = JSON.parse(errorBody);
2229
+ if (parsed.error === "Payment verification failed" && parsed.details) {
2230
+ const match = parsed.details.match(/Verification failed:\s*(\{.*\})/s);
2231
+ if (match) {
2232
+ const innerJson = JSON.parse(match[1]);
2233
+ if (innerJson.invalidReason === "insufficient_funds" && innerJson.invalidMessage) {
2234
+ const balanceMatch = innerJson.invalidMessage.match(
2235
+ /insufficient balance:\s*(\d+)\s*<\s*(\d+)/i
2236
+ );
2237
+ if (balanceMatch) {
2238
+ const currentMicros = parseInt(balanceMatch[1], 10);
2239
+ const requiredMicros = parseInt(balanceMatch[2], 10);
2240
+ const currentUSD = (currentMicros / 1e6).toFixed(6);
2241
+ const requiredUSD = (requiredMicros / 1e6).toFixed(6);
2242
+ const wallet = innerJson.payer || "unknown";
2243
+ const shortWallet = wallet.length > 12 ? `${wallet.slice(0, 6)}...${wallet.slice(-4)}` : wallet;
2244
+ return JSON.stringify({
2245
+ error: {
2246
+ message: `Insufficient USDC balance. Current: $${currentUSD}, Required: ~$${requiredUSD}`,
2247
+ type: "insufficient_funds",
2248
+ wallet,
2249
+ current_balance_usd: currentUSD,
2250
+ required_usd: requiredUSD,
2251
+ help: `Fund wallet ${shortWallet} with USDC on Base, or use free model: /model free`
2252
+ }
2253
+ });
2254
+ }
2255
+ }
2256
+ }
2257
+ }
2258
+ } catch {
2259
+ }
2260
+ return errorBody;
2261
+ }
2218
2262
  var rateLimitedModels = /* @__PURE__ */ new Map();
2219
2263
  function isRateLimited(modelId) {
2220
2264
  const hitTime = rateLimitedModels.get(modelId);
@@ -3083,10 +3127,18 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3083
3127
  options.onRouted?.(routingDecision);
3084
3128
  }
3085
3129
  if (!upstream) {
3086
- const errBody = lastError?.body || "All models in fallback chain failed";
3130
+ const rawErrBody = lastError?.body || "All models in fallback chain failed";
3087
3131
  const errStatus = lastError?.status || 502;
3132
+ const transformedErr = transformPaymentError(rawErrBody);
3088
3133
  if (headersSentEarly) {
3089
- const errEvent = `data: ${JSON.stringify({ error: { message: errBody, type: "provider_error", status: errStatus } })}
3134
+ let errPayload;
3135
+ try {
3136
+ const parsed = JSON.parse(transformedErr);
3137
+ errPayload = JSON.stringify(parsed);
3138
+ } catch {
3139
+ errPayload = JSON.stringify({ error: { message: rawErrBody, type: "provider_error", status: errStatus } });
3140
+ }
3141
+ const errEvent = `data: ${errPayload}
3090
3142
 
3091
3143
  `;
3092
3144
  safeWrite(res, errEvent);
@@ -3101,17 +3153,11 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3101
3153
  });
3102
3154
  } else {
3103
3155
  res.writeHead(errStatus, { "Content-Type": "application/json" });
3104
- res.end(
3105
- JSON.stringify({
3106
- error: { message: errBody, type: "provider_error" }
3107
- })
3108
- );
3156
+ res.end(transformedErr);
3109
3157
  deduplicator.complete(dedupKey, {
3110
3158
  status: errStatus,
3111
3159
  headers: { "content-type": "application/json" },
3112
- body: Buffer.from(
3113
- JSON.stringify({ error: { message: errBody, type: "provider_error" } })
3114
- ),
3160
+ body: Buffer.from(transformedErr),
3115
3161
  completedAt: Date.now()
3116
3162
  });
3117
3163
  }