@blockrun/clawrouter 0.6.9 → 0.7.0

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/index.js CHANGED
@@ -2324,6 +2324,8 @@ var DEFAULT_PORT = 8402;
2324
2324
  var MAX_FALLBACK_ATTEMPTS = 3;
2325
2325
  var HEALTH_CHECK_TIMEOUT_MS = 2e3;
2326
2326
  var RATE_LIMIT_COOLDOWN_MS = 6e4;
2327
+ var PORT_RETRY_ATTEMPTS = 5;
2328
+ var PORT_RETRY_DELAY_MS = 1e3;
2327
2329
  var rateLimitedModels = /* @__PURE__ */ new Map();
2328
2330
  function isRateLimited(modelId) {
2329
2331
  const hitTime = rateLimitedModels.get(modelId);
@@ -2613,7 +2615,7 @@ async function startProxy(options) {
2613
2615
  if (existingWallet) {
2614
2616
  const account2 = privateKeyToAccount2(options.walletKey);
2615
2617
  const balanceMonitor2 = new BalanceMonitor(account2.address);
2616
- const baseUrl = `http://127.0.0.1:${listenPort}`;
2618
+ const baseUrl2 = `http://127.0.0.1:${listenPort}`;
2617
2619
  if (existingWallet !== account2.address) {
2618
2620
  console.warn(
2619
2621
  `[ClawRouter] Existing proxy on port ${listenPort} uses wallet ${existingWallet}, but current config uses ${account2.address}. Reusing existing proxy.`
@@ -2622,7 +2624,7 @@ async function startProxy(options) {
2622
2624
  options.onReady?.(listenPort);
2623
2625
  return {
2624
2626
  port: listenPort,
2625
- baseUrl,
2627
+ baseUrl: baseUrl2,
2626
2628
  walletAddress: existingWallet,
2627
2629
  balanceMonitor: balanceMonitor2,
2628
2630
  close: async () => {
@@ -2748,80 +2750,123 @@ async function startProxy(options) {
2748
2750
  }
2749
2751
  }
2750
2752
  });
2751
- return new Promise((resolve, reject) => {
2752
- server.on("error", (err) => {
2753
- if (err.code === "EADDRINUSE") {
2754
- const baseUrl = `http://127.0.0.1:${listenPort}`;
2753
+ const tryListen = (attempt) => {
2754
+ return new Promise((resolveAttempt, rejectAttempt) => {
2755
+ const onError = async (err) => {
2756
+ server.removeListener("error", onError);
2757
+ if (err.code === "EADDRINUSE") {
2758
+ const existingWallet2 = await checkExistingProxy(listenPort);
2759
+ if (existingWallet2) {
2760
+ console.log(`[ClawRouter] Existing proxy detected on port ${listenPort}, reusing`);
2761
+ rejectAttempt({ code: "REUSE_EXISTING", wallet: existingWallet2 });
2762
+ return;
2763
+ }
2764
+ if (attempt < PORT_RETRY_ATTEMPTS) {
2765
+ console.log(
2766
+ `[ClawRouter] Port ${listenPort} in TIME_WAIT, retrying in ${PORT_RETRY_DELAY_MS}ms (attempt ${attempt}/${PORT_RETRY_ATTEMPTS})`
2767
+ );
2768
+ rejectAttempt({ code: "RETRY", attempt });
2769
+ return;
2770
+ }
2771
+ console.error(
2772
+ `[ClawRouter] Port ${listenPort} still in use after ${PORT_RETRY_ATTEMPTS} attempts`
2773
+ );
2774
+ rejectAttempt(err);
2775
+ return;
2776
+ }
2777
+ rejectAttempt(err);
2778
+ };
2779
+ server.once("error", onError);
2780
+ server.listen(listenPort, "127.0.0.1", () => {
2781
+ server.removeListener("error", onError);
2782
+ resolveAttempt();
2783
+ });
2784
+ });
2785
+ };
2786
+ let lastError;
2787
+ for (let attempt = 1; attempt <= PORT_RETRY_ATTEMPTS; attempt++) {
2788
+ try {
2789
+ await tryListen(attempt);
2790
+ break;
2791
+ } catch (err) {
2792
+ const error = err;
2793
+ if (error.code === "REUSE_EXISTING" && error.wallet) {
2794
+ const baseUrl2 = `http://127.0.0.1:${listenPort}`;
2755
2795
  options.onReady?.(listenPort);
2756
- resolve({
2796
+ return {
2757
2797
  port: listenPort,
2758
- baseUrl,
2759
- walletAddress: account.address,
2798
+ baseUrl: baseUrl2,
2799
+ walletAddress: error.wallet,
2760
2800
  balanceMonitor,
2761
2801
  close: async () => {
2762
2802
  }
2763
- });
2764
- return;
2803
+ };
2765
2804
  }
2766
- reject(err);
2805
+ if (error.code === "RETRY") {
2806
+ await new Promise((r) => setTimeout(r, PORT_RETRY_DELAY_MS));
2807
+ continue;
2808
+ }
2809
+ lastError = err;
2810
+ break;
2811
+ }
2812
+ }
2813
+ if (lastError) {
2814
+ throw lastError;
2815
+ }
2816
+ const addr = server.address();
2817
+ const port = addr.port;
2818
+ const baseUrl = `http://127.0.0.1:${port}`;
2819
+ options.onReady?.(port);
2820
+ server.on("error", (err) => {
2821
+ console.error(`[ClawRouter] Server runtime error: ${err.message}`);
2822
+ options.onError?.(err);
2823
+ });
2824
+ server.on("clientError", (err, socket) => {
2825
+ console.error(`[ClawRouter] Client error: ${err.message}`);
2826
+ if (socket.writable && !socket.destroyed) {
2827
+ socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
2828
+ }
2829
+ });
2830
+ server.on("connection", (socket) => {
2831
+ connections.add(socket);
2832
+ socket.setTimeout(3e5);
2833
+ socket.on("timeout", () => {
2834
+ console.error(`[ClawRouter] Socket timeout, destroying connection`);
2835
+ socket.destroy();
2767
2836
  });
2768
- server.listen(listenPort, "127.0.0.1", () => {
2769
- const addr = server.address();
2770
- const port = addr.port;
2771
- const baseUrl = `http://127.0.0.1:${port}`;
2772
- options.onReady?.(port);
2773
- server.on("error", (err) => {
2774
- console.error(`[ClawRouter] Server runtime error: ${err.message}`);
2775
- options.onError?.(err);
2776
- });
2777
- server.on("clientError", (err, socket) => {
2778
- console.error(`[ClawRouter] Client error: ${err.message}`);
2779
- if (socket.writable && !socket.destroyed) {
2780
- socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
2781
- }
2782
- });
2783
- server.on("connection", (socket) => {
2784
- connections.add(socket);
2785
- socket.setTimeout(3e5);
2786
- socket.on("timeout", () => {
2787
- console.error(`[ClawRouter] Socket timeout, destroying connection`);
2788
- socket.destroy();
2789
- });
2790
- socket.on("end", () => {
2791
- });
2792
- socket.on("error", (err) => {
2793
- console.error(`[ClawRouter] Socket error: ${err.message}`);
2794
- });
2795
- socket.on("close", () => {
2796
- connections.delete(socket);
2797
- });
2798
- });
2799
- resolve({
2800
- port,
2801
- baseUrl,
2802
- walletAddress: account.address,
2803
- balanceMonitor,
2804
- close: () => new Promise((res, rej) => {
2805
- const timeout = setTimeout(() => {
2806
- rej(new Error("[ClawRouter] Close timeout after 4s"));
2807
- }, 4e3);
2808
- sessionStore.close();
2809
- for (const socket of connections) {
2810
- socket.destroy();
2811
- }
2812
- connections.clear();
2813
- server.close((err) => {
2814
- clearTimeout(timeout);
2815
- if (err) {
2816
- rej(err);
2817
- } else {
2818
- res();
2819
- }
2820
- });
2821
- })
2822
- });
2837
+ socket.on("end", () => {
2838
+ });
2839
+ socket.on("error", (err) => {
2840
+ console.error(`[ClawRouter] Socket error: ${err.message}`);
2841
+ });
2842
+ socket.on("close", () => {
2843
+ connections.delete(socket);
2823
2844
  });
2824
2845
  });
2846
+ return {
2847
+ port,
2848
+ baseUrl,
2849
+ walletAddress: account.address,
2850
+ balanceMonitor,
2851
+ close: () => new Promise((res, rej) => {
2852
+ const timeout = setTimeout(() => {
2853
+ rej(new Error("[ClawRouter] Close timeout after 4s"));
2854
+ }, 4e3);
2855
+ sessionStore.close();
2856
+ for (const socket of connections) {
2857
+ socket.destroy();
2858
+ }
2859
+ connections.clear();
2860
+ server.close((err) => {
2861
+ clearTimeout(timeout);
2862
+ if (err) {
2863
+ rej(err);
2864
+ } else {
2865
+ res();
2866
+ }
2867
+ });
2868
+ })
2869
+ };
2825
2870
  }
2826
2871
  async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxTokens, payFetch, balanceMonitor, signal) {
2827
2872
  let requestBody = body;