@blockrun/clawrouter 0.12.253 → 0.12.254

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
@@ -92320,13 +92320,16 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
92320
92320
  const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
92321
92321
  const globalController = new AbortController();
92322
92322
  const timeoutId = setTimeout(() => globalController.abort(), timeoutMs);
92323
+ const clientDisconnected = () => res.destroyed && !res.writableEnded;
92324
+ const abortError = () => clientDisconnected() ? new ClientDisconnectedError() : new Error(`Request timed out after ${timeoutMs}ms`);
92323
92325
  const onClientClose = () => {
92324
- if (!globalController.signal.aborted) {
92326
+ if (!res.writableEnded && !globalController.signal.aborted) {
92325
92327
  console.log(`[ClawRouter] Client disconnected \u2014 aborting upstream request`);
92326
92328
  globalController.abort();
92327
92329
  }
92328
92330
  };
92329
- req.on("close", onClientClose);
92331
+ res.on("close", onClientClose);
92332
+ if (res.destroyed) onClientClose();
92330
92333
  try {
92331
92334
  let modelsToTry;
92332
92335
  const excludeList = options.excludeModels ?? loadExcludeList();
@@ -92385,7 +92388,7 @@ data: [DONE]
92385
92388
  completed = true;
92386
92389
  deduplicator.removeInflight(dedupKey);
92387
92390
  clearTimeout(timeoutId);
92388
- req.removeListener("close", onClientClose);
92391
+ res.removeListener("close", onClientClose);
92389
92392
  return;
92390
92393
  }
92391
92394
  const contextExcluded = fullChain.filter((m) => !contextFiltered.includes(m));
@@ -92484,7 +92487,7 @@ data: [DONE]
92484
92487
  }
92485
92488
  deduplicator.removeInflight(dedupKey);
92486
92489
  clearTimeout(timeoutId);
92487
- req.removeListener("close", onClientClose);
92490
+ res.removeListener("close", onClientClose);
92488
92491
  return;
92489
92492
  }
92490
92493
  if (excluded.length > 0) {
@@ -92515,7 +92518,7 @@ data: [DONE]
92515
92518
  const tryModel = modelsToTry[i];
92516
92519
  const isLastAttempt = i === modelsToTry.length - 1;
92517
92520
  if (globalController.signal.aborted) {
92518
- throw new Error(`Request timed out after ${timeoutMs}ms`);
92521
+ throw abortError();
92519
92522
  }
92520
92523
  console.log(`[ClawRouter] Trying model ${i + 1}/${modelsToTry.length}: ${tryModel}`);
92521
92524
  const perAttemptTimeoutMs = timeoutForModel(tryModel);
@@ -92535,7 +92538,7 @@ data: [DONE]
92535
92538
  );
92536
92539
  clearTimeout(modelTimeoutId);
92537
92540
  if (globalController.signal.aborted) {
92538
- throw new Error(`Request timed out after ${timeoutMs}ms`);
92541
+ throw abortError();
92539
92542
  }
92540
92543
  if (!result.success && modelController.signal.aborted && !isLastAttempt) {
92541
92544
  console.log(
@@ -92747,7 +92750,7 @@ data: [DONE]
92747
92750
  break;
92748
92751
  }
92749
92752
  clearTimeout(timeoutId);
92750
- req.removeListener("close", onClientClose);
92753
+ res.removeListener("close", onClientClose);
92751
92754
  if (heartbeatInterval) {
92752
92755
  clearInterval(heartbeatInterval);
92753
92756
  heartbeatInterval = void 0;
@@ -93250,12 +93253,16 @@ data: [DONE]
93250
93253
  completed = true;
93251
93254
  } catch (err) {
93252
93255
  clearTimeout(timeoutId);
93253
- req.removeListener("close", onClientClose);
93256
+ res.removeListener("close", onClientClose);
93254
93257
  if (heartbeatInterval) {
93255
93258
  clearInterval(heartbeatInterval);
93256
93259
  heartbeatInterval = void 0;
93257
93260
  }
93258
93261
  deduplicator.removeInflight(dedupKey);
93262
+ if (err instanceof ClientDisconnectedError || err instanceof Error && err.name === "AbortError" && clientDisconnected()) {
93263
+ console.log(`[ClawRouter] Request cancelled \u2014 client disconnected`);
93264
+ return;
93265
+ }
93259
93266
  balanceMonitor.invalidate();
93260
93267
  if (err instanceof Error && err.name === "AbortError") {
93261
93268
  throw new Error(`Request timed out after ${timeoutMs}ms`, { cause: err });
@@ -93311,7 +93318,7 @@ data: [DONE]
93311
93318
  });
93312
93319
  }
93313
93320
  }
93314
- 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, 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;
93321
+ 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;
93315
93322
  var init_proxy = __esm({
93316
93323
  "src/proxy.ts"() {
93317
93324
  "use strict";
@@ -93391,6 +93398,12 @@ var init_proxy = __esm({
93391
93398
  PORT_RETRY_DELAY_MS = 1e3;
93392
93399
  MODEL_BODY_READ_TIMEOUT_MS = 3e5;
93393
93400
  ERROR_BODY_READ_TIMEOUT_MS = 3e4;
93401
+ ClientDisconnectedError = class extends Error {
93402
+ constructor() {
93403
+ super("Client disconnected");
93404
+ this.name = "ClientDisconnectedError";
93405
+ }
93406
+ };
93394
93407
  rateLimitedModels = /* @__PURE__ */ new Map();
93395
93408
  overloadedModels = /* @__PURE__ */ new Map();
93396
93409
  perProviderErrors = /* @__PURE__ */ new Map();
package/dist/index.js CHANGED
@@ -92140,13 +92140,16 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
92140
92140
  const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
92141
92141
  const globalController = new AbortController();
92142
92142
  const timeoutId = setTimeout(() => globalController.abort(), timeoutMs);
92143
+ const clientDisconnected = () => res.destroyed && !res.writableEnded;
92144
+ const abortError = () => clientDisconnected() ? new ClientDisconnectedError() : new Error(`Request timed out after ${timeoutMs}ms`);
92143
92145
  const onClientClose = () => {
92144
- if (!globalController.signal.aborted) {
92146
+ if (!res.writableEnded && !globalController.signal.aborted) {
92145
92147
  console.log(`[ClawRouter] Client disconnected \u2014 aborting upstream request`);
92146
92148
  globalController.abort();
92147
92149
  }
92148
92150
  };
92149
- req.on("close", onClientClose);
92151
+ res.on("close", onClientClose);
92152
+ if (res.destroyed) onClientClose();
92150
92153
  try {
92151
92154
  let modelsToTry;
92152
92155
  const excludeList = options.excludeModels ?? loadExcludeList();
@@ -92205,7 +92208,7 @@ data: [DONE]
92205
92208
  completed = true;
92206
92209
  deduplicator.removeInflight(dedupKey);
92207
92210
  clearTimeout(timeoutId);
92208
- req.removeListener("close", onClientClose);
92211
+ res.removeListener("close", onClientClose);
92209
92212
  return;
92210
92213
  }
92211
92214
  const contextExcluded = fullChain.filter((m) => !contextFiltered.includes(m));
@@ -92304,7 +92307,7 @@ data: [DONE]
92304
92307
  }
92305
92308
  deduplicator.removeInflight(dedupKey);
92306
92309
  clearTimeout(timeoutId);
92307
- req.removeListener("close", onClientClose);
92310
+ res.removeListener("close", onClientClose);
92308
92311
  return;
92309
92312
  }
92310
92313
  if (excluded.length > 0) {
@@ -92335,7 +92338,7 @@ data: [DONE]
92335
92338
  const tryModel = modelsToTry[i];
92336
92339
  const isLastAttempt = i === modelsToTry.length - 1;
92337
92340
  if (globalController.signal.aborted) {
92338
- throw new Error(`Request timed out after ${timeoutMs}ms`);
92341
+ throw abortError();
92339
92342
  }
92340
92343
  console.log(`[ClawRouter] Trying model ${i + 1}/${modelsToTry.length}: ${tryModel}`);
92341
92344
  const perAttemptTimeoutMs = timeoutForModel(tryModel);
@@ -92355,7 +92358,7 @@ data: [DONE]
92355
92358
  );
92356
92359
  clearTimeout(modelTimeoutId);
92357
92360
  if (globalController.signal.aborted) {
92358
- throw new Error(`Request timed out after ${timeoutMs}ms`);
92361
+ throw abortError();
92359
92362
  }
92360
92363
  if (!result.success && modelController.signal.aborted && !isLastAttempt) {
92361
92364
  console.log(
@@ -92567,7 +92570,7 @@ data: [DONE]
92567
92570
  break;
92568
92571
  }
92569
92572
  clearTimeout(timeoutId);
92570
- req.removeListener("close", onClientClose);
92573
+ res.removeListener("close", onClientClose);
92571
92574
  if (heartbeatInterval) {
92572
92575
  clearInterval(heartbeatInterval);
92573
92576
  heartbeatInterval = void 0;
@@ -93070,12 +93073,16 @@ data: [DONE]
93070
93073
  completed = true;
93071
93074
  } catch (err) {
93072
93075
  clearTimeout(timeoutId);
93073
- req.removeListener("close", onClientClose);
93076
+ res.removeListener("close", onClientClose);
93074
93077
  if (heartbeatInterval) {
93075
93078
  clearInterval(heartbeatInterval);
93076
93079
  heartbeatInterval = void 0;
93077
93080
  }
93078
93081
  deduplicator.removeInflight(dedupKey);
93082
+ if (err instanceof ClientDisconnectedError || err instanceof Error && err.name === "AbortError" && clientDisconnected()) {
93083
+ console.log(`[ClawRouter] Request cancelled \u2014 client disconnected`);
93084
+ return;
93085
+ }
93079
93086
  balanceMonitor.invalidate();
93080
93087
  if (err instanceof Error && err.name === "AbortError") {
93081
93088
  throw new Error(`Request timed out after ${timeoutMs}ms`, { cause: err });
@@ -93131,7 +93138,7 @@ data: [DONE]
93131
93138
  });
93132
93139
  }
93133
93140
  }
93134
- 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, 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;
93141
+ 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;
93135
93142
  var init_proxy = __esm({
93136
93143
  "src/proxy.ts"() {
93137
93144
  "use strict";
@@ -93211,6 +93218,12 @@ var init_proxy = __esm({
93211
93218
  PORT_RETRY_DELAY_MS = 1e3;
93212
93219
  MODEL_BODY_READ_TIMEOUT_MS = 3e5;
93213
93220
  ERROR_BODY_READ_TIMEOUT_MS = 3e4;
93221
+ ClientDisconnectedError = class extends Error {
93222
+ constructor() {
93223
+ super("Client disconnected");
93224
+ this.name = "ClientDisconnectedError";
93225
+ }
93226
+ };
93214
93227
  rateLimitedModels = /* @__PURE__ */ new Map();
93215
93228
  overloadedModels = /* @__PURE__ */ new Map();
93216
93229
  perProviderErrors = /* @__PURE__ */ new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/clawrouter",
3
- "version": "0.12.253",
3
+ "version": "0.12.254",
4
4
  "description": "Smart LLM router — save 88% on inference costs. 71 models (5 free), one wallet, x402 micropayments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",