(options: ProxyOptions)
| 1888 | * Returns a handle with the assigned port, base URL, and a close function. |
| 1889 | */ |
| 1890 | export async function startProxy(options: ProxyOptions): Promise<ProxyHandle> { |
| 1891 | // Apply upstream proxy (SOCKS5/HTTP) before any outgoing requests |
| 1892 | const upstreamProxy = await applyUpstreamProxy(options.upstreamProxy); |
| 1893 | if (upstreamProxy) { |
| 1894 | console.log(`[ClawRouter] Upstream proxy: ${upstreamProxy}`); |
| 1895 | } |
| 1896 | |
| 1897 | // Normalize wallet config: string = EVM-only, object = full resolution |
| 1898 | const walletKey = typeof options.wallet === "string" ? options.wallet : options.wallet.key; |
| 1899 | const solanaPrivateKeyBytes = |
| 1900 | typeof options.wallet === "string" ? undefined : options.wallet.solanaPrivateKeyBytes; |
| 1901 | |
| 1902 | // Payment chain: options > env var > persisted file > default "base". |
| 1903 | // No dynamic switching — user selects chain via /wallet solana or /wallet base. |
| 1904 | const paymentChain = options.paymentChain ?? (await resolvePaymentChain()); |
| 1905 | const apiBase = |
| 1906 | options.apiBase ?? |
| 1907 | (paymentChain === "solana" && solanaPrivateKeyBytes ? BLOCKRUN_SOLANA_API : BLOCKRUN_API); |
| 1908 | if (paymentChain === "solana" && !solanaPrivateKeyBytes) { |
| 1909 | console.warn( |
| 1910 | `[ClawRouter] ⚠ Payment chain is Solana but no mnemonic found — falling back to Base (EVM).`, |
| 1911 | ); |
| 1912 | console.warn( |
| 1913 | `[ClawRouter] To fix: run "npx @blockrun/clawrouter wallet recover" if your mnemonic exists,`, |
| 1914 | ); |
| 1915 | console.warn(`[ClawRouter] or run "npx @blockrun/clawrouter chain base" to switch to EVM.`); |
| 1916 | } else if (paymentChain === "solana") { |
| 1917 | console.log(`[ClawRouter] Payment chain: Solana (${BLOCKRUN_SOLANA_API})`); |
| 1918 | } |
| 1919 | |
| 1920 | // Determine port: options.port > env var > default |
| 1921 | const listenPort = options.port ?? getProxyPort(); |
| 1922 | |
| 1923 | // Check if a proxy is already running on this port |
| 1924 | const existingProxy = await checkExistingProxy(listenPort); |
| 1925 | if (existingProxy) { |
| 1926 | // Proxy already running — reuse it instead of failing with EADDRINUSE |
| 1927 | const account = privateKeyToAccount(walletKey as `0x${string}`); |
| 1928 | const baseUrl = `http://127.0.0.1:${listenPort}`; |
| 1929 | |
| 1930 | // Verify the existing proxy is using the same wallet (or warn if different) |
| 1931 | if (existingProxy.wallet !== account.address) { |
| 1932 | console.warn( |
| 1933 | `[ClawRouter] Existing proxy on port ${listenPort} uses wallet ${existingProxy.wallet}, but current config uses ${account.address}. Reusing existing proxy.`, |
| 1934 | ); |
| 1935 | } |
| 1936 | |
| 1937 | // Verify the existing proxy is using the same payment chain |
| 1938 | if (existingProxy.paymentChain) { |
| 1939 | if (existingProxy.paymentChain !== paymentChain) { |
| 1940 | throw new Error( |
| 1941 | `Existing proxy on port ${listenPort} is using ${existingProxy.paymentChain} but ${paymentChain} was requested. ` + |
| 1942 | `Stop the existing proxy first or use a different port.`, |
| 1943 | ); |
| 1944 | } |
| 1945 | } else if (paymentChain !== "base") { |
| 1946 | // Old proxy doesn't report chain — assume Base. Reject if Solana was requested. |
| 1947 | console.warn( |
no test coverage detected